@@ -183,7 +183,10 @@ defmodule Cldr.Number do
183183 The most commonly used formats in this category are to spell out the
184184 number in a locale's language. The applicable formats are `:spellout`,
185185 `:spellout_year` and `:ordinal`. A number can also be formatted as roman
186- numbers by using the format `:roman` or `:roman_lower`.
186+ numbers by using the format `:roman` or `:roman_lower`. If the format is
187+ an RBNF format then the options `:gender` and/or `:grammatical_case` may also
188+ be provided. If they are provided then they will be used to try to resolve
189+ an available RBNF rule for the given `:locale`.
187190
188191 * `currency`: is the currency for which the number is formatted. If `currency`
189192 is set and no `:format` is set, `:format` will be set to `:currency` as well.
@@ -320,6 +323,28 @@ defmodule Cldr.Number do
320323 element type. The wrapper function is required to return a string that is then
321324 inserted in the final formatted number.
322325
326+ ### RBNF with Grammatical Gender and Case
327+
328+ RBNF rules are used to format numbers - often to spell out a number
329+ or format an ordinal number. In certain locales, such formats require
330+ the specification of a grammatical gender (such as `:masculine`,
331+ `:feminine` or `:neuter`) and/or grammatical case.
332+
333+ Not all locales have RBNF rules, and those that do have RBNF rules
334+ may not have rules supporting grammatical gender or case. Therefore a
335+ fallback mechansms is required.
336+
337+ * First, an attempt is made to find an RBNF rule
338+ that combines the required format (such as `:spellout` or `:ordinal`) and
339+ the specified gender and gramnmatical case.
340+
341+ * If not found, an attempt to find a rule that is the combnation of the
342+ format plus the specified gender is made.
343+
344+ * Finally, of not otherwise found, an attempt is made to find the format
345+ without the requested gender or grammatical case.
346+
347+
323348 ### Returns
324349
325350 * `{:ok, string}` or
@@ -497,15 +522,16 @@ defmodule Cldr.Number do
497522 end
498523
499524 @ format_mapping [
500- { :ordinal , :digits_ordinal , Ordinal } ,
501- { :spellout , :spellout_numbering , Spellout } ,
502- { :spellout_verbose , :spellout_numbering_verbose , Spellout } ,
503- { :spellout_year , :spellout_numbering_year , Spellout }
525+ { :ordinal , :digits_ordinal } ,
526+ { :spellout , :spellout_numbering } ,
527+ { :spellout_verbose , :spellout_numbering_verbose } ,
528+ { :spellout_year , :spellout_numbering_year }
504529 ]
505530
506- for { format , function , module } <- @ format_mapping do
531+ for { format , expanded_format } <- @ format_mapping do
507532 defp to_string ( number , unquote ( format ) , backend , options ) do
508- evaluate_rule ( number , unquote ( module ) , unquote ( function ) , options . locale , backend )
533+ to_string ( number , unquote ( expanded_format ) , backend , options )
534+ # evaluate_rule(number, unquote(module), unquote(function), options.locale, backend)
509535 end
510536 end
511537
@@ -541,8 +567,8 @@ defmodule Cldr.Number do
541567
542568 # For executing arbitrary RBNF rules that might exist for a given locale
543569 defp to_string ( number , format , backend , options ) when is_atom ( format ) do
544- with { :ok , module , locale } <- find_rbnf_format_module ( options . locale , format , backend ) do
545- apply ( module , format , [ number , locale ] )
570+ with { :ok , module , function , locale } <- find_rbnf_format_module ( format , backend , options ) do
571+ apply ( module , function , [ number , locale ] )
546572 end
547573 end
548574
@@ -556,38 +582,63 @@ defmodule Cldr.Number do
556582 # Look for the RBNF rule in the given locale or in the
557583 # root locale (called "und")
558584
559- defp find_rbnf_format_module ( locale , format , backend ) do
585+ defp find_rbnf_format_module ( format , backend , % Cldr.Number.Format.Options { locale: locale } = options ) do
560586 root_locale = Map . put ( @ root_locale , :backend , backend )
587+ % Cldr.Number.Format.Options { gender: gender , grammatical_case: grammatical_case } = options
561588
562589 cond do
563- module = find_rbnf_module ( locale , format , backend ) -> { :ok , module , locale }
564- module = find_rbnf_module ( root_locale , format , backend ) -> { :ok , module , root_locale }
565- true -> { :error , Cldr.Rbnf . rbnf_rule_error ( locale , format ) }
590+ module_and_function = find_rbnf_module ( locale , format , gender , grammatical_case , backend ) ->
591+ { module , function } = module_and_function
592+ { :ok , module , function , locale }
593+ module_and_function = find_rbnf_module ( root_locale , format , gender , grammatical_case , backend ) ->
594+ { module , function } = module_and_function
595+ { :ok , module , function , root_locale }
596+ true ->
597+ { :error , Cldr.Rbnf . rbnf_rule_error ( locale , format ) }
566598 end
567599 end
568600
569- defp find_rbnf_module ( locale , format , backend ) do
601+ defp find_rbnf_module ( locale , format , nil , nil , backend ) do
570602 Enum . reduce_while ( Cldr.Rbnf . categories_for_locale! ( locale ) , nil , fn category , acc ->
571603 format_module = Module . concat ( [ backend , :Rbnf , category ] )
572604 rules = format_module . rule_sets ( locale )
573605
574606 if rules && format in rules do
575- { :halt , format_module }
607+ { :halt , { format_module , format } }
576608 else
577609 { :cont , acc }
578610 end
579611 end )
580612 end
581613
582- defp evaluate_rule ( number , module , function , locale , backend ) do
583- module = Module . concat ( [ backend , :Rbnf , module ] )
584- rule_sets = module . rule_sets ( locale )
614+ defp find_rbnf_module ( locale , format , gender , nil , backend ) do
615+ gendered_format = String . to_existing_atom ( "#{ format } _#{ gender } " )
585616
586- if rule_sets && function in rule_sets do
587- apply ( module , function , [ number , locale ] )
588- else
589- { :error , Cldr.Rbnf . rbnf_rule_error ( locale , function ) }
617+ case find_rbnf_module ( locale , gendered_format , nil , nil , backend ) do
618+ { format_module , format } ->
619+ { format_module , format }
620+
621+ nil ->
622+ find_rbnf_module ( locale , format , nil , nil , backend )
623+ end
624+
625+ rescue ArgumentError ->
626+ find_rbnf_module ( locale , format , nil , nil , backend )
627+ end
628+
629+ defp find_rbnf_module ( locale , format , gender , grammatical_case , backend ) do
630+ gendered_cased_format = String . to_existing_atom ( "#{ format } _#{ gender } _#{ grammatical_case } " )
631+
632+ case find_rbnf_module ( locale , gendered_cased_format , nil , nil , backend ) do
633+ { format_module , format } ->
634+ { format_module , format }
635+
636+ nil ->
637+ find_rbnf_module ( locale , format , gender , nil , backend )
590638 end
639+
640+ rescue ArgumentError ->
641+ find_rbnf_module ( locale , format , gender , nil , backend )
591642 end
592643
593644 @ doc """
0 commit comments