pub struct LangSl { /* private fields */ }Implementations§
Trait Implementations§
Source§impl Lang for LangSl
impl Lang for LangSl
Source§fn default_currency(&self) -> &str
fn default_currency(&self) -> &str
This language’s own to_currency(currency=...) default,
read from the live Python signature. Only 44 of 156 use EUR.
Source§fn default_separator(&self) -> &str
fn default_separator(&self) -> &str
This language’s own to_currency(separator=...) default,
read from the live Python signature. Base’s is “,”, but only
36 of 149 languages actually use it — most default to “ “ or a
conjunction, so inheriting Base’s comma silently corrupts them.
Source§fn to_cardinal_float(
&self,
value: &FloatValue,
_precision_override: Option<u32>,
) -> Result<String>
fn to_cardinal_float( &self, value: &FloatValue, _precision_override: Option<u32>, ) -> Result<String>
Port of Num2Word_SL.to_cardinal_float(self, value).
SL’s own one-arg override, NOT Num2Word_Base.to_cardinal_float (the
default this trait method would otherwise route to). It diverges twice:
-
Fraction as a single number. Where the base loops the padded fractional string digit by digit, SL emits it whole:
self.to_cardinal(int(post_str)). So12.34is “dvanajst vejica štiriintrideset” (34), never “… tri štiri”;12.345is “dvanajst vejica tristo petinštirideset” (345). -
Always float-casts, even Decimal. Python does
self.float2tuple(float(value))unconditionally, so the exact-Decimal arm ofbase.float2tupleis never taken here. This reintroduces issue #603 that the Decimal arm exists to avoid:Decimal( "98746251323029.99")casts to the f6498746251323029.98, so the fraction reads 98 (“osemindevetdeset”), not 99. LikewiseDecimal("1.10")casts to1.1— precision 1, “ena”, not the precision-2 “deset” the exact arm would give. Reproduced by takingto_f64()on theDecimalvariant and running the Float branch.
SL’s signature has no precision= parameter, so precision_override
is dead: even when the dispatcher pre-sets self.precision,
float2tuple immediately overwrites it from repr(float(value)).
Verified live — num2words(0.5, lang='sl', precision=5) is still
“nič vejica pet”. Ignored here to match.
Only non-integral values arrive: Num2Word_Base.to_cardinal sends
integral floats (1.0, 0.0) down its integer branch, so precision
is always >= 1 in practice and the fraction segment always fires.
Source§fn ordinal_float_entry(&self, value: &FloatValue) -> Result<String>
fn ordinal_float_entry(&self, value: &FloatValue) -> Result<String>
to_ordinal(float/Decimal).
verify_ordinal(value) runs first: a non-integral value raises
TypeError (errmsg_floatord), a negative one TypeError
(errmsg_negord) — note -0.0 passes both (int(-0.0) == -0.0 and
abs(-0.0) == -0.0 numerically). A surviving value is whole and
non-negative; num = int(value) then walks the same table/ordflag
machinery as the integer path, and to_cardinal(<whole float>)
inside the ordflag arm takes Base’s integer branch (the
assert int(value) == value passes), so the output is byte-identical
to to_ordinal(int(value)): 5.0 -> “peti”, 0.0/-0.0 -> “niči”,
1e+16 -> “desetbilijardti”.
Source§fn ordinal_num_float_entry(
&self,
value: &FloatValue,
repr_str: &str,
) -> Result<String>
fn ordinal_num_float_entry( &self, value: &FloatValue, repr_str: &str, ) -> Result<String>
to_ordinal_num(float/Decimal) — verify_ordinal(value), then
str(value) + "." with the original string form: 5.0 -> “5.0.”,
-0.0 -> “-0.0.”, 1e+16 -> “1e+16.”, Decimal("5.00") -> “5.00.”.
Non-integral or negative values raise TypeError exactly as above.
Source§fn pluralize(&self, n: &BigInt, forms: &[String]) -> Result<String>
fn pluralize(&self, n: &BigInt, forms: &[String]) -> Result<String>
Port of Num2Word_SL.pluralize(n, forms).
Python’s % on int floors, so a negative n wraps up into a
positive residue: (-99) % 100 == 1 selects forms[0]. Rust’s %
truncates — -99 % 100 == -99, which matches nothing and falls to
forms[3] — so mod_floor is required. The two agree on -1/-100
and disagree on -99/-98/-97, which is why the test pins -99.
Unreachable from to_currency (both operands are absolute values), but
pluralize is a public hook and the residue rule is the whole point.
Source§fn to_currency(
&self,
val: &CurrencyValue,
currency: &str,
_cents: bool,
_separator: Option<&str>,
_adjective: bool,
) -> Result<String>
fn to_currency( &self, val: &CurrencyValue, currency: &str, _cents: bool, _separator: Option<&str>, _adjective: bool, ) -> Result<String>
Port of Num2Word_SL.to_currency.
Three of the five parameters are dead upstream and so are ignored here:
separator— SL declaresseparator=" in"and then never reads it, using the thirdCURRENCY_FORMSelement instead. The generateddefault_separator()above still reports “ in“ because that is the live signature’s default; it simply has no effect. Passingseparator=" XX"changes nothing in Python. Verified live.cents— no_cents_tersecall exists on this path.adjective— noCURRENCY_ADJECTIVESlookup exists on this path.
§The int/float split
is_integer_input = isinstance(val, int) gates the cents segment: a
true int never prints cents, a float always does — including 1.0,
which is why currency:EUR of 1 is “ena evro” but of 1.0 is “ena
evro nič centov”. has_decimal is therefore unused here: SL does not
consult it, and the Int/Decimal variants already carry the only
distinction SL makes.
§Known gap: fractional cents
When (Decimal(str(val)) * 100) % 1 != 0, parse_currency_parts keeps
the subunit as a Decimal and Python renders it with
Num2Word_SL.to_cardinal_float — SL’s own one-arg override, which
emits the fractional digits as a single number
(to_cardinal(int(post_str))). cardinal_from_decimal’s default routes
to Num2Word_Base.to_cardinal_float, which emits them digit by digit.
The two agree whenever the residue is a single digit and diverge beyond
it:
to_currency(1.005, "EUR") "ena evro nič vejica pet centov" both
to_currency(1.0025, "EUR") "ena evro nič vejica petindvajset centov" Python
"ena evro nič vejica dve pet centov" hereMeasured, not assumed: diffed against the live interpreter over 1751 cases (17 currency codes x 51 ints incl. 10^100 and negatives x 50 floats, plus cheques). Exactly 8 diverge — 1.0025, 0.0001, 1e-07 and 3.14159 under EUR and USD — and all 8 are this one cause. No corpus row is among them; reaching the branch needs 4+ decimal places.
Closing it means porting SL’s to_cardinal_float, which needs
repr(float) semantics that this crate deliberately keeps on the Python
side (see currency.rs’s header): 1e-07 renders “nič vejica ena”
precisely because Python’s repr goes exponential and SL then reads the
zero-padded digits back as one number. That is the later float phase,
not this one — and note currency::cardinal_from_bigdecimal calls
default_to_cardinal_float directly rather than through
lang.to_cardinal_float, so porting SL’s override alone will not
reroute this hook.
Source§fn to_cheque(&self, _val: &BigDecimal, currency: &str) -> Result<String>
fn to_cheque(&self, _val: &BigDecimal, currency: &str) -> Result<String>
Num2Word_Base.to_cheque — inherited, and it cannot succeed for SL.
try:
cr1, _cr2 = self.CURRENCY_FORMS[currency]
except KeyError:
raise NotImplementedError(...)Every SL entry is a 3-tuple, so the unpack raises
ValueError: too many values to unpack (expected 2) — from inside the
try, where only KeyError is caught, so it propagates. An unknown
code raises KeyError on the subscript first and converts to
NotImplementedError as usual. So the implemented codes fail harder
than the unimplemented ones:
cheque:EUR 1234.56 ValueError cheque:GBP 1234.56 NotImplementedError
cheque:USD 1234.56 ValueError cheque:JPY 1234.56 NotImplementedErrorBoth arms are corpus rows and both are reproduced. val is untouched
because Python never gets far enough to look at it — the unpack is the
first statement after the subscript.
fn cards(&self) -> &Cards
fn maxval(&self) -> &BigInt
fn negword(&self) -> &str
fn pointword(&self) -> &str
Source§fn merge(&self, l: (&str, &BigInt), r: (&str, &BigInt)) -> (String, BigInt)
fn merge(&self, l: (&str, &BigInt), r: (&str, &BigInt)) -> (String, BigInt)
merge is abstract in Python (raise NotImplementedError).
Languages that override to_cardinal never reach it.fn to_cardinal(&self, value: &BigInt) -> Result<String>
fn to_ordinal(&self, value: &BigInt) -> Result<String>
fn to_ordinal_num(&self, value: &BigInt) -> Result<String>
fn to_year(&self, value: &BigInt) -> Result<String>
Source§fn currency_forms(&self, code: &str) -> Option<&CurrencyForms>
fn currency_forms(&self, code: &str) -> Option<&CurrencyForms>
CURRENCY_FORMS[code]. None -> NotImplementedError, as in Python.fn is_title(&self) -> bool
fn exclude_title(&self) -> &[String]
fn title(&self, value: &str) -> String
Source§fn currency_precision(&self, _code: &str) -> i64
fn currency_precision(&self, _code: &str) -> i64
CURRENCY_PRECISION.get(code, 100) — subunits per unit. 1000 for
3-decimal currencies (BHD/KWD/…), 1 for no-subunit currencies.fn money_verbose(&self, number: &BigInt, _currency: &str) -> Result<String>
fn cents_verbose(&self, number: &BigInt, _currency: &str) -> Result<String>
fn cents_terse(&self, number: &BigInt, currency: &str) -> Result<String>
Source§fn cardinal_from_decimal(&self, value: &BigDecimal) -> Result<String>
fn cardinal_from_decimal(&self, value: &BigDecimal) -> Result<String>
self.to_cardinal(float(right)), i.e. through the
float path, so the default routes there rather than raising.Source§fn default_adjective(&self) -> bool
fn default_adjective(&self) -> bool
to_currency(adjective=...) default. Base’s is
False; only Mongolian defaults to True.Source§fn to_fraction(
&self,
numerator: &BigInt,
denominator: &BigInt,
) -> Result<String>
fn to_fraction( &self, numerator: &BigInt, denominator: &BigInt, ) -> Result<String>
Num2Word_Base.to_fraction (issue #584). EN/DE/ES/FR/IT/PT and the
aero profiles override with idiomatic forms (half/quarter/Drittel).Source§fn cardinal_float_entry(
&self,
value: &FloatValue,
precision_override: Option<u32>,
) -> Result<String>
fn cardinal_float_entry( &self, value: &FloatValue, precision_override: Option<u32>, ) -> Result<String>
to_cardinal(float/Decimal) — the full entry, whole values included.Source§fn year_float_entry(&self, value: &FloatValue) -> Result<String>
fn year_float_entry(&self, value: &FloatValue) -> Result<String>
to_year(float/Decimal). Base’s to_year is to_cardinal.Source§fn str_to_number(&self, s: &str) -> Result<ParsedNumber>
fn str_to_number(&self, s: &str) -> Result<ParsedNumber>
converter.str_to_number. Base is Decimal(value); ES (“1ro” ->
ordinal handshake), PT_BR (“1.50” -> ‘ponto’), DV (TypeError instead
of InvalidOperation), BN (abs()) and ID override.Source§fn cardinal_with_pointword(
&self,
_value: &FloatValue,
_pointword: &str,
_precision_override: Option<u32>,
) -> Result<String>
fn cardinal_with_pointword( &self, _value: &FloatValue, _pointword: &str, _precision_override: Option<u32>, ) -> Result<String>
to_cardinal(value) with the pointword
swapped for this one call. Only reachable through
ParsedNumber::DecPoint, so only PT_BR implements it.fn to_cardinal_kw(&self, value: &BigInt, kw: &Kwargs) -> Result<String>
fn to_ordinal_kw(&self, value: &BigInt, kw: &Kwargs) -> Result<String>
fn to_ordinal_num_kw(&self, value: &BigInt, kw: &Kwargs) -> Result<String>
fn to_year_kw(&self, value: &BigInt, kw: &Kwargs) -> Result<String>
fn to_currency_kw( &self, val: &CurrencyValue, currency: &str, cents: bool, separator: Option<&str>, adjective: bool, kw: &Kwargs, ) -> Result<String>
fn to_cardinal_float_kw( &self, value: &FloatValue, precision_override: Option<u32>, kw: &Kwargs, ) -> Result<String>
Source§fn es_currency_ordinal_fires(&self) -> bool
fn es_currency_ordinal_fires(&self) -> bool
Num2Word_ES
apocopates 1 → “un euro” in to_currency, bypassing to_cardinal, so
the stash never fires (false, the default). The regional es_XX
classes inherit Base.to_currency, whose _money_verbose calls
to_cardinal(1) → the stash fires → “primero córdoba” (true).Source§fn neg_zero_decimal(&self, _to: &str) -> Option<Result<String>>
fn neg_zero_decimal(&self, _to: &str) -> Option<Result<String>>
Decimal('-0.0') for mode to, which BigDecimal cannot
represent (it has no signed zero). None — the default — means the
value coincides with float -0.0, so the binding’s Float{-0.0}
demotion is exact. The ~10 languages that render the Decimal form
differently (ce/cy “dim”/“ноль” via the integer path, pl its float
grammar with the negword, rm a TypeError) override this to serve it
natively instead of leaning on a Python fallback.Source§fn inf_result(&self, _negative: bool, _to: &str) -> Result<String>
fn inf_result(&self, _negative: bool, _to: &str) -> Result<String>
str_to_number produced Decimal('Infinity') / -Infinity. Base’s
integer path does int(Decimal('Infinity')) → OverflowError. The many
self-contained converters that feed the raw token to int("Infinity")
get ValueError instead and override this. to is the requested mode.Source§fn nan_result(&self, _to: &str) -> Result<String>
fn nan_result(&self, _to: &str) -> Result<String>
str_to_number produced Decimal('NaN'). Base’s int(NaN) raises
ValueError (caught into the float path, which raises ValueError again).
Languages whose parse raises decimal.InvalidOperation first override.Source§fn python_maxval(&self) -> Option<BigInt>
fn python_maxval(&self) -> Option<BigInt>
getattr(converter, "MAXVAL", None) — the maxval() API.
Engine languages get it from their card table (Base sets
MAXVAL = 1000 * top_card); self-contained ones mostly lack the
attribute (None). The zero sentinel in maxval() means “unset”.