A message plural-form selector for Standard ML with locale-aware
CLDR cardinal plural
categories. Maps a count to its plural category (one, few, many,
other, ...) under a named locale rule, and picks the matching pre-rendered
string.
datatype category = Zero | One | Two | Few | Many | Other
datatype rule = English | French | Slavic | Other'
val select : rule -> int -> category (* count -> CLDR category *)
val categoryName : category -> string (* "one", "few", "many", ... *)
val selectByName : rule -> int -> string (* categoryName o select *)
exception NoOther
val cardinal : rule -> int -> (category * string) list -> string
(* legacy: argument order is (n, one, other, few) *)
val plural : int -> string -> string -> string -> string| rule | categories |
|---|---|
English |
one (n = 1), other |
French |
one (n = 0 or 1), other |
Slavic |
one (n = 1), few (n mod 10 in 2-4 and n mod 100 not in 12-14), many |
Other' |
always other |
MsgFmt.select MsgFmt.English 1 (* One *)
MsgFmt.select MsgFmt.English 2 (* Other *)
MsgFmt.select MsgFmt.French 0 (* One *)
MsgFmt.select MsgFmt.Slavic 3 (* Few *)
MsgFmt.select MsgFmt.Slavic 5 (* Many *)
MsgFmt.select MsgFmt.Slavic 22 (* Few *)cardinal r n forms selects the string whose category matches select r n,
falling back to the Other entry (which is required; its absence raises
NoOther).
val forms = [(MsgFmt.One, "1 plik"),
(MsgFmt.Few, "%d pliki"),
(MsgFmt.Many, "%d plikow"),
(MsgFmt.Other,"%d plikow")]
MsgFmt.cardinal MsgFmt.Slavic 1 forms (* "1 plik" *)
MsgFmt.cardinal MsgFmt.Slavic 3 forms (* "%d pliki" *)
MsgFmt.cardinal MsgFmt.Slavic 7 forms (* "%d plikow" *)(* note the argument order: n, one, other, few *)
MsgFmt.plural 1 "1 file" "N files" "a few files" (* "1 file" *)
MsgFmt.plural 3 "1 file" "N files" "a few files" (* "a few files" *)
MsgFmt.plural 9 "1 file" "N files" "a few files" (* "N files" *)- Only cardinal plural rules for a handful of representative locales
(
English,French,Slavic/Polish-style) are built in; ordinal rules and the full CLDR locale set are out of scope. selectoperates on integer counts only — no decimal/fraction visible-digit operands (v,f,t), and no number interpolation,{count}placeholder substitution, gender, or message catalog. You supply already-formatted strings.
make example builds and runs examples/demo.sml, which
runs selectByName over representative counts for the English, French, and
Slavic rules, then shows cardinal picking a pre-rendered form and the
legacy plural helper (output is byte-identical under MLton and Poly/ML):
selectByName over counts [0,1,2,3,5,11]:
English: 0=other 1=one 2=other 3=other 5=other 11=other
French: 0=one 1=one 2=other 3=other 5=other 11=other
Slavic: 0=many 1=one 2=few 3=few 5=many 11=many
cardinal with pre-rendered forms (Slavic rule):
1 -> one item
2 -> a few items
5 -> many items
legacy plural helper plural(n, one, other, few):
plural 1 -> one
plural 2 -> few
plural 5 -> other
smlpkg add github.com/sjqtentacles/sml-msgfmt
smlpkg syncReference from your .mlb:
lib/github.com/sjqtentacles/sml-msgfmt/msgfmt.mlb
make test # MLton
make test-poly # Poly/ML
make all-tests # both
make cleansml.pkg
Makefile
lib/github.com/sjqtentacles/sml-msgfmt/
msgfmt.sig
msgfmt.sml CLDR cardinal categories + locale rules + selection
msgfmt.mlb
test/
test.sml category selection, cardinal, legacy plural
MIT. See LICENSE.