///|
/// An indexed runtime translation catalog.
///
/// Construct catalogs with `Catalog::from_po` or `Catalog::from_mo`; fields are
/// intentionally private so duplicate-key and plural-rule checks cannot be
/// bypassed.
pub struct Catalog {
messages : Map[String, PoEntry]
rule : PluralRule
}
///|
fn catalog_key(msgid : String, context : String?) -> String {
match context {
Some(value) => value + "\u{0004}" + msgid
None => msgid
}
}
///|
fn validated_default_rule(rule : PluralRule) -> Unit raise GettextError {
if rule.nplurals <= 0 {
raise Validation(message="catalog plural rule must declare nplurals > 0")
}
ignore(parse_plural_ast(rule.expression))
}
///|
/// Build a runtime catalog from a parsed PO document.
///
/// Obsolete entries and the metadata header are excluded. Fuzzy entries are
/// excluded by default and can be explicitly included for preview tooling.
/// When no `Plural-Forms` header exists, the English rule (or
/// `default_rule`) is used.
pub fn Catalog::from_po(
document : PoFile,
default_rule? : PluralRule = PluralRule::english(),
include_fuzzy? : Bool = false,
) -> Catalog raise GettextError {
validated_default_rule(default_rule)
let metadata = document.metadata()
let rule = match metadata.get("Plural-Forms") {
Some(value) => parse_plural_forms(value)
None => default_rule
}
let messages : Map[String, PoEntry] = Map([])
for entry in document.entries {
if entry.is_header() ||
entry.obsolete ||
(!include_fuzzy && entry.is_fuzzy()) {
continue
}
let key = catalog_key(entry.msgid, entry.context)
if messages.contains(key) {
raise Validation(message="duplicate catalog key: \{entry.msgid}")
}
messages[key] = entry
}
{ messages, rule }
}
///|
/// Build a runtime catalog directly from GNU MO bytes.
pub fn Catalog::from_mo(bytes : Bytes) -> Catalog raise GettextError {
Catalog::from_po(parse_mo(bytes))
}
///|
/// Number of indexed message entries, excluding the metadata header.
pub fn Catalog::length(self : Catalog) -> Int {
self.messages.length()
}
///|
/// Return the catalog's validated plural rule.
pub fn Catalog::plural_rule(self : Catalog) -> PluralRule {
self.rule
}
///|
fn Catalog::find_singular(
self : Catalog,
msgid : String,
context : String?,
) -> String? {
match self.messages.get(catalog_key(msgid, context)) {
Some(entry) => entry.translation(0)
None => None
}
}
///|
fn Catalog::find_plural(
self : Catalog,
msgid : String,
context : String?,
n : Int,
) -> String? raise GettextError {
match self.messages.get(catalog_key(msgid, context)) {
Some(entry) => entry.translation(self.rule.select(n))
None => None
}
}
///|
/// Look up a singular message, returning the source `msgid` when neither this
/// catalog nor the optional fallback has a non-empty translation.
pub fn Catalog::gettext(
self : Catalog,
msgid : String,
fallback? : Catalog,
) -> String {
match self.find_singular(msgid, None) {
Some(value) => value
None =>
match fallback {
Some(other) => other.find_singular(msgid, None).unwrap_or(msgid)
None => msgid
}
}
}
///|
/// Look up a context-qualified singular message.
pub fn Catalog::pgettext(
self : Catalog,
context : String,
msgid : String,
fallback? : Catalog,
) -> String {
match self.find_singular(msgid, Some(context)) {
Some(value) => value
None =>
match fallback {
Some(other) =>
other.find_singular(msgid, Some(context)).unwrap_or(msgid)
None => msgid
}
}
}
///|
/// Look up a plural message using this catalog's `Plural-Forms` rule.
///
/// If no translation is available, the fallback catalog is tried. Source text
/// finally falls back to `singular` for `n == 1` and `plural` otherwise.
pub fn Catalog::ngettext(
self : Catalog,
singular : String,
plural : String,
n : Int,
fallback? : Catalog,
) -> String raise GettextError {
match self.find_plural(singular, None, n) {
Some(value) => value
None =>
match fallback {
Some(other) =>
other
.find_plural(singular, None, n)
.unwrap_or(if n == 1 { singular } else { plural })
None => if n == 1 { singular } else { plural }
}
}
}
///|
/// Look up a context-qualified plural message.
pub fn Catalog::npgettext(
self : Catalog,
context : String,
singular : String,
plural : String,
n : Int,
fallback? : Catalog,
) -> String raise GettextError {
match self.find_plural(singular, Some(context), n) {
Some(value) => value
None =>
match fallback {
Some(other) =>
other
.find_plural(singular, Some(context), n)
.unwrap_or(if n == 1 { singular } else { plural })
None => if n == 1 { singular } else { plural }
}
}
}