Translations
Default messages are rendered from an English template table. vld::i18n::translate_error re-renders them with another table.
use vld::prelude::*;
use vld::i18n::{russian, translate_error};
let err = vld::string().min(5).parse(r#""ab""#).unwrap_err();
let ru = translate_error(&err, &russian());
println!("{}", ru.issues[0].message);
// Строка должна содержать не менее 5 символов
Built-in tables (vld::i18n):
| Function | Language | Plural rule |
|---|---|---|
english() | English (the defaults) | plural_en |
russian() | Russian | plural_ru (one / few / many) |
german() | German | plural_en |
spanish() | Spanish | plural_en |
Each table covers every template key and the type.<name> keys for type names.
Your own tables
MapResolver wraps a HashMap<String, String> of templates. Placeholders like {minimum} are filled from the issue's parameters.
use std::collections::HashMap;
use vld::i18n::{translate_error, MapResolver};
let mut t = HashMap::new();
t.insert("too_small.string".into(), "Mínimo {minimum} caracteres".into());
t.insert("invalid_string.email".into(), "Correo no válido".into());
let resolver = MapResolver::new(t);
let err = vld::string().min(5).email().parse(r#""ab""#).unwrap_err();
let es = translate_error(&err, &resolver);
assert_eq!(es.issues[0].message, "Mínimo 5 caracteres");
Start from a bundled table to override a few keys: english() returns a MapResolver with every default template.
FnResolver takes a closure instead, useful for lookups in Fluent, gettext or a database:
use vld::i18n::{translate_error, FnResolver};
let resolver = FnResolver::new(|key| match key {
"too_small" => Some("Zu kurz! Mindestens {minimum} Zeichen.".into()),
_ => None,
});
let de = translate_error(&err, &resolver);
Implement the MessageResolver trait directly for full control (resolve(key) plus an optional plural_category(n)).
Lookup order
For each issue the resolver is asked for the keys of IssueCode::message_keys(), most specific first (too_small.string, then too_small). The first template found wins; with no match the message is unchanged. Exclusive bounds use too_small_exclusive / too_big_exclusive, so an "at least" template is never applied to a "greater than" check. Union issues keep their [variant N] prefix.
Plurals
Size codes (too_small, too_big, invalid_length) with an integer bound try <key>_<category> first, where the category comes from the resolver's plural rule: too_small.string_one, invalid_length.string_few. The plain key is the fallback, so it should hold the other (English) or many (Russian) form.
let resolver = MapResolver::new(HashMap::from([
("too_small.string_one".to_string(), "Минимум {minimum} символ".to_string()),
("too_small.string_few".to_string(), "Минимум {minimum} символа".to_string()),
("too_small.string".to_string(), "Минимум {minimum} символов".to_string()),
]))
.with_plural_rule(vld::i18n::plural_ru);
Type names
invalid_type messages translate their {expected} / {received} through type.<name> keys (type.string, type.date, …) when the resolver has them.
Custom messages are kept
An issue whose message is not the default English one (from _msg builders, with_messages, .message(), type_error(), refine, …) is returned unchanged by translate_error.