///|
/// Resolves messages from a catalog using requested and default locale
/// fallback candidates.
pub struct Translator {
catalog : Catalog
locale : Locale
default_locale : Locale
}
///|
/// Creates a translator for a catalog, requested locale, and default locale.
pub fn Translator::new(
catalog~ : Catalog,
locale~ : Locale,
default_locale~ : Locale,
) -> Translator {
{ catalog, locale, default_locale, }
}
///|
/// Translates a message key and interpolates named values. Missing messages
/// return a nonfatal `[[key]]` marker.
pub fn Translator::translate(
self : Translator,
key : String,
values? : ArrayView[(String, String)],
) -> String {
match self.lookup(key) {
Some(message) => interpolate(message, values.unwrap_or([]))
None => "[[" + key + "]]"
}
}
///|
/// Translates a plural message. English selects `.one` when `count == 1` and
/// `.other` otherwise; Chinese always selects `.other`. The `count` value is
/// always available as the `{count}` placeholder.
pub fn Translator::translate_count(
self : Translator,
key : String,
count : Int,
values? : ArrayView[(String, String)],
) -> String {
let plural_key = key + "." + self.plural_category(count)
let interpolation_values : Array[(String, String)] = [
("count", count.to_string()),
]
for value in values.unwrap_or([]) {
if value.0 != "count" {
interpolation_values.push(value)
}
}
match self.lookup(plural_key) {
Some(message) => interpolate(message, interpolation_values)
None => "[[" + key + "]]"
}
}
///|
fn Translator::lookup(self : Translator, key : String) -> String? {
for candidate in self.locale.fallback_candidates() {
match self.catalog.lookup(candidate, key) {
Some(message) => return Some(message)
None => ()
}
}
for candidate in self.default_locale.fallback_candidates() {
match self.catalog.lookup(candidate, key) {
Some(message) => return Some(message)
None => ()
}
}
None
}
///|
fn Translator::plural_category(self : Translator, count : Int) -> String {
if self.locale.language() == "zh" {
"other"
} else if self.locale.language() == "en" && count == 1 {
"one"
} else {
"other"
}
}
///|
fn interpolate(
template : String,
values : ArrayView[(String, String)],
) -> String {
let result = StringBuilder()
let chars = template.to_array()
let mut index = 0
while index < chars.length() {
let char = chars[index]
if char == '{' {
if index + 1 < chars.length() && chars[index + 1] == '{' {
result.write_char('{')
index += 2
} else {
match closing_brace(chars, index + 1) {
Some(end) => {
let name = String::from_array(chars[index + 1:end])
match lookup_value(values, name) {
Some(value) => result.write_string(value)
None => result.write_string("{" + name + "}")
}
index = end + 1
}
None => {
result.write_char(char)
index += 1
}
}
}
} else if char == '}' &&
index + 1 < chars.length() &&
chars[index + 1] == '}' {
result.write_char('}')
index += 2
} else {
result.write_char(char)
index += 1
}
}
result.to_string()
}
///|
fn closing_brace(chars : Array[Char], start : Int) -> Int? {
let mut index = start
while index < chars.length() {
if chars[index] == '}' {
return Some(index)
}
if chars[index] == '{' {
return None
}
index += 1
}
None
}
///|
fn lookup_value(values : ArrayView[(String, String)], name : String) -> String? {
for value in values {
if value.0 == name {
return Some(value.1)
}
}
None
}