Errors

Translations

Bundled English, Russian, German and Spanish messages; MapResolver, FnResolver, plural rules and type names.

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):

FunctionLanguagePlural rule
english()English (the defaults)plural_en
russian()Russianplural_ru (one / few / many)
german()Germanplural_en
spanish()Spanishplural_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.