Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 91 additions & 1 deletion num2words/lang_HR.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,97 @@ def pluralize(self, number, forms):
return forms[form]

def to_ordinal(self, number):
raise NotImplementedError()
"""Masculine nominative singular ordinal (the default ordinal form).

Croatian ordinals decline by gender, case, and number — this method
returns the unmarked masculine-nominative-singular form (prvi, drugi,
peti, sedamnaesti, dvadeseti, stoti, tisućiti, ...). Compound numbers
replace only the last cardinal word with its ordinal counterpart.
"""
if int(number) != number:
raise ValueError("ordinals only defined for integers")
n = int(number)
if n in self._ORDINAL_EXACT:
return self._ORDINAL_EXACT[n]
cardinal_words = self._int2word(n).split()
last = cardinal_words[-1]
if last not in self._ORDINAL_LAST_MASC:
raise NotImplementedError(
"Croatian ordinal not defined for last word %r in %r" % (
last, " ".join(cardinal_words)
)
)
cardinal_words[-1] = self._ORDINAL_LAST_MASC[last]
return " ".join(cardinal_words)

def to_year(self, value, **kwargs):
"""Year form used before 'godine' — feminine-genitive declension.

Croatian convention reads "1986. godine" as
"tisuću devetsto osamdeset šeste godine" (last word = feminine
genitive ordinal). The leading "jedna tisuća" of cardinal years
1000-1999 collapses to "tisuću".
"""
if int(value) != value:
raise ValueError("years only defined for integers")
n = int(value)
words = self._int2word(n).split()
# Collapse "jedna tisuća ..." → "tisuću ..." for years 1000-1999
if len(words) >= 2 and words[0] == "jedna" and words[1] == "tisuća":
words = ["tisuću"] + words[2:]
last = words[-1]
if last in self._YEAR_LAST_FEM_GEN:
words[-1] = self._YEAR_LAST_FEM_GEN[last]
return " ".join(words)

# Cardinal-last-word → masculine-nominative ordinal
_ORDINAL_LAST_MASC = {
"jedan": "prvi", "dva": "drugi", "tri": "treći", "četiri": "četvrti",
"pet": "peti", "šest": "šesti", "sedam": "sedmi", "osam": "osmi",
"devet": "deveti",
"deset": "deseti",
"jedanaest": "jedanaesti", "dvanaest": "dvanaesti",
"trinaest": "trinaesti", "četrnaest": "četrnaesti",
"petnaest": "petnaesti", "šesnaest": "šesnaesti",
"sedamnaest": "sedamnaesti", "osamnaest": "osamnaesti",
"devetnaest": "devetnaesti",
"dvadeset": "dvadeseti", "trideset": "trideseti",
"četrdeset": "četrdeseti", "pedeset": "pedeseti",
"šezdeset": "šezdeseti", "sedamdeset": "sedamdeseti",
"osamdeset": "osamdeseti", "devedeset": "devedeseti",
"sto": "stoti", "dvjesto": "dvjestoti", "tristo": "tristoti",
"četiristo": "četiristoti", "petsto": "petstoti", "šesto": "šestoti",
"sedamsto": "sedamstoti", "osamsto": "osamstoti",
"devetsto": "devetstoti",
}

# Cardinal-last-word → feminine-genitive ordinal (year form, before "godine")
_YEAR_LAST_FEM_GEN = {
"jedan": "prve", "dva": "druge", "tri": "treće", "četiri": "četvrte",
"pet": "pete", "šest": "šeste", "sedam": "sedme", "osam": "osme",
"devet": "devete",
"deset": "desete",
"jedanaest": "jedanaeste", "dvanaest": "dvanaeste",
"trinaest": "trinaeste", "četrnaest": "četrnaeste",
"petnaest": "petnaeste", "šesnaest": "šesnaeste",
"sedamnaest": "sedamnaeste", "osamnaest": "osamnaeste",
"devetnaest": "devetnaeste",
"dvadeset": "dvadesete", "trideset": "tridesete",
"četrdeset": "četrdesete", "pedeset": "pedesete",
"šezdeset": "šezdesete", "sedamdeset": "sedamdesete",
"osamdeset": "osamdesete", "devedeset": "devedesete",
"sto": "stote", "dvjesto": "dvjestote", "tristo": "tristote",
"četiristo": "četiristote", "petsto": "petstote", "šesto": "šestote",
"sedamsto": "sedamstote", "osamsto": "osamstote",
"devetsto": "devetstote",
}

# Whole-number ordinals where last-word substitution doesn't apply
_ORDINAL_EXACT = {
1000: "tisućiti",
1_000_000: "milijunti",
1_000_000_000: "milijarditi",
}

def _cents_verbose(self, number, currency):
return self._int2word(
Expand Down
77 changes: 75 additions & 2 deletions tests/test_hr.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,81 @@ def test_floating_point(self):
)

def test_to_ordinal(self):
with self.assertRaises(NotImplementedError):
num2words(1, lang='hr', to='ordinal')
# ones — irregular forms
self.assertEqual("prvi", num2words(1, lang='hr', to='ordinal'))
self.assertEqual("drugi", num2words(2, lang='hr', to='ordinal'))
self.assertEqual("treći", num2words(3, lang='hr', to='ordinal'))
self.assertEqual("četvrti", num2words(4, lang='hr', to='ordinal'))
# ones — regular
self.assertEqual("peti", num2words(5, lang='hr', to='ordinal'))
self.assertEqual("šesti", num2words(6, lang='hr', to='ordinal'))
self.assertEqual("sedmi", num2words(7, lang='hr', to='ordinal'))
self.assertEqual("osmi", num2words(8, lang='hr', to='ordinal'))
self.assertEqual("deveti", num2words(9, lang='hr', to='ordinal'))
# tens
self.assertEqual("deseti", num2words(10, lang='hr', to='ordinal'))
self.assertEqual(
"sedamnaesti", num2words(17, lang='hr', to='ordinal')
)
self.assertEqual(
"dvadeseti", num2words(20, lang='hr', to='ordinal')
)
# compound — only last word becomes ordinal
self.assertEqual(
"dvadeset prvi", num2words(21, lang='hr', to='ordinal')
)
self.assertEqual(
"trideset peti", num2words(35, lang='hr', to='ordinal')
)
# hundreds
self.assertEqual("stoti", num2words(100, lang='hr', to='ordinal'))
self.assertEqual(
"sto prvi", num2words(101, lang='hr', to='ordinal')
)
self.assertEqual(
"sto dvadeset peti", num2words(125, lang='hr', to='ordinal')
)
# thousand — exact 10^k uses dedicated form
self.assertEqual(
"tisućiti", num2words(1000, lang='hr', to='ordinal')
)
self.assertEqual(
"milijunti", num2words(1_000_000, lang='hr', to='ordinal')
)
# year-shaped numbers as masculine ordinals
self.assertEqual(
"jedna tisuća devetsto osamdeset šesti",
num2words(1986, lang='hr', to='ordinal')
)
self.assertEqual(
"dvije tisuće dvadeset četvrti",
num2words(2024, lang='hr', to='ordinal')
)

def test_to_year(self):
# 1000-1999 collapse "jedna tisuća" → "tisuću" and apply fem-gen ending
self.assertEqual(
"tisuću devetsto osamdeset šeste",
num2words(1986, lang='hr', to='year')
)
self.assertEqual(
"tisuću devetsto četrdeset osme",
num2words(1948, lang='hr', to='year')
)
# 2000+ keeps "dvije tisuće ..." prefix; last word still feminine genitive
self.assertEqual(
"dvije tisuće dvadeset četvrte",
num2words(2024, lang='hr', to='year')
)
self.assertEqual(
"dvije tisuće trinaeste",
num2words(2013, lang='hr', to='year')
)
# Round years
self.assertEqual(
"dvije tisuće",
num2words(2000, lang='hr', to='year')
)

def test_to_currency(self):
self.assertEqual(
Expand Down