// lang_it.mbt — Italian number spelling (cardinal, year).
//
// Italian quirks handled here:
// * the tens word drops its final vowel before "uno" and "otto"
// (venti + uno -> "ventuno", trenta + otto -> "trentotto");
// * "tre" takes an accent when it ends a compound ("ventitré");
// * "cento" elides its final vowel before "otto" ("centotto"), but keeps it
// before other vowel-initial units ("centouno");
// * scale words: "mila" attaches to the preceding word ("duemila") while
// million-and-above are separate words ("due milioni").
// Scale words for a group value of exactly 1. Index = group power:
// 1 -> 10^3, 2 -> 10^6, 3 -> 10^9, 4 -> 10^12 ...
///|
let it_scale_sing : Array[String] = [
"", "mille", "milione", "miliardo", "bilione", "biliardo", "trilione", "triliardo",
]
// Scale words for group values >= 2. Index = group power.
///|
let it_scale_plur : Array[String] = [
"", "mila", "milioni", "miliardi", "bilioni", "biliardi", "trilioni", "triliardi",
]
// Hundreds: 2 -> "duecento", 3 -> "trecento", ... 9 -> "novecento".
///|
let it_hundreds : Array[String] = [
"", "", "duecento", "trecento", "quattrocento", "cinquecento", "seicento", "settecento",
"ottocento", "novecento",
]
// Units as they appear inside a compound. "tre" stays unaccented while a
// further morpheme follows ("ventitremila"); the accent is restored on the
// final word by `it_accent_final` ("ventitré").
///|
let it_units_compound : Array[String] = [
"", "uno", "due", "tre", "quattro", "cinque", "sei", "sette", "otto", "nove",
]
///|
pub fn lang_it() -> Lang {
{
variant: It,
zero: "zero",
ones: [
"zero", "uno", "due", "tre", "quattro", "cinque", "sei", "sette", "otto", "nove",
],
teens: [
"dieci", "undici", "dodici", "tredici", "quattordici", "quindici", "sedici",
"diciassette", "diciotto", "diciannove",
],
tens: [
"", "dieci", "venti", "trenta", "quaranta", "cinquanta", "sessanta", "settanta",
"ottanta", "novanta",
],
hundred: "cento",
thousands: it_scale_plur,
negative: "meno",
decimal: "virgola",
comma_sep: false,
}
}
///|
/// Drop the final vowel of a tens/hundreds word (venti -> "vent").
fn it_truncate(w : String) -> String {
if w.length() == 0 {
return w
}
w[0:w.length() - 1].to_owned()
}
///|
/// Restore the accent on a final "tre" glued to a preceding morpheme:
/// "ventitre" -> "ventitré", "milletre" -> "milletré". A standalone "tre"
/// (3, or after a space) stays unaccented.
fn it_accent_final(s : String) -> String {
let n = s.length()
if n > 3 && s[n - 3:n].to_owned() == "tre" && s[n - 4:n - 3].to_owned() != " " {
s[0:n - 3].to_owned() + "tré"
} else {
s
}
}
///|
fn Lang::below100_it(self : Lang, r : Int) -> String {
if r < 10 {
return self.ones[r]
}
if r < 20 {
return self.teens[r - 10]
}
let d = r / 10
let u = r % 10
if u == 0 {
return self.tens[d]
}
if u == 1 || u == 8 {
// venti + uno -> "ventuno"; trenta + otto -> "trentotto"
return it_truncate(self.tens[d]) + it_units_compound[u]
}
// tre carries an accent in final position: 23 -> "ventitré"
return self.tens[d] + it_units_compound[u]
}
///|
fn Lang::triple_it(self : Lang, t : Int) -> String {
if t == 0 {
return ""
}
let h = t / 100
let r = t % 100
if h == 0 {
return self.below100_it(r)
}
let prefix = if h == 1 { "cento" } else { it_hundreds[h] }
if r == 0 {
return prefix
}
let low = self.below100_it(r)
// The final vowel of "...cento" is dropped when the following part starts
// with "o": 108 -> "centotto", 180 -> "centottanta", 208 -> "duecentotto".
// It is kept before other vowels: 101 -> "centouno", 111 -> "centoundici".
if low[0:1].to_owned() == "o" {
return it_truncate(prefix) + low
}
return prefix + low
}
///|
fn Lang::cardinal_it(self : Lang, n : BigInt) -> String {
if n == BigInt::from_int(0) {
return self.zero
}
if !self.supports(n) {
return ""
}
let neg = n < BigInt::from_int(0)
let n = if neg { -n } else { n }
let groups = group3(n)
let parts : Array[String] = []
let powers : Array[Int] = []
for i = 0; i < groups.length(); i = i + 1 {
let (triple, power) = groups[i]
let t = triple.to_int()
if t == 0 {
continue
}
let mut s = self.triple_it(t)
if power > 0 {
if power == 1 {
// 1000 -> "mille"; 2000 -> "duemila" (attached, no space)
s = if t == 1 { "mille" } else { s + "mila" }
} else if t == 1 {
// 1000000 -> "un milione"
s = "un " + it_scale_sing[power]
} else {
// 2000000 -> "due milioni"
s = s + " " + it_scale_plur[power]
}
}
parts.push(s)
powers.push(power)
}
// Italian writes a number as one word; a space (and the conjunction "e")
// appears only after a million-or-larger scale word: 1234567 -> "un milione
// e duecentotrentaquattromilacinquecentosessantasette", 2345 ->
// "duemilatrecentoquarantacinque", 1001 -> "milleuno".
let mut joined = ""
for i = 0; i < parts.length(); i = i + 1 {
if i > 0 && powers[i - 1] >= 2 {
joined = joined + " e "
}
joined = joined + parts[i]
}
joined = it_accent_final(joined)
if neg {
"meno " + joined
} else {
joined
}
}
///|
/// Italian years are read as a plain cardinal ("2024" ->
/// "duemilaventiquattro"), so this simply delegates to `cardinal_it`.
pub fn year_it(n : BigInt) -> String {
lang_it().cardinal_it(n)
}