This guide covers the optional result-code layer.
Use it when you want validators to emit stable codes and only resolve the final text when you build the result output. If you only need inline err, warn, or hint messages, you can skip this page.
Codes are useful when:
hint and an errThe coded-result API uses:
CodedMessageCatalogCodedMessageCatalog.globalICodedMessageCatalogResultCodeResultCodeDefinitiontype ResultCode = string | number;
interface ResultCodeDefinition {
hint?: string | Record<string, string>;
warn?: string | Record<string, string>;
err?: string | Record<string, string>;
}
Each level is optional. A single code can define one level or several levels.
If a level is declared as a single string, the catalog stores it as the English translation. These two registrations are equivalent:
catalog.register('person.name.missing', {
err: 'Name is required'
});
catalog.register('person.name.missing', {
err: {
en: 'Name is required'
}
});
Language maps are plain key-value objects. The package looks up the requested language first and then falls back to default when present. If no translation matches, the validator falls back to the original generated validator message.
Use the shared catalog when one process-wide registry is sufficient.
import { CodedMessageCatalog, FieldCheck } from '@samatawy/checks';
CodedMessageCatalog.global.register('person.name.missing', {
hint: {
en: 'Add the legal full name when available',
de: 'Ergaenze den vollstaendigen Namen, wenn verfuegbar'
},
err: {
en: 'Name is required',
de: 'Name ist erforderlich'
}
});
const result = new FieldCheck('name', {}).required({
code: 'person.name.missing'
}).result({ language: 'de' });
console.log(result.code);
console.log(result.hint);
console.log(result.err);
Expected result shape:
{
"field": "name",
"valid": false,
"code": "person.name.missing",
"hint": "Ergaenze den vollstaendigen Namen, wenn verfuegbar",
"err": "Name ist erforderlich"
}
Use a separate CodedMessageCatalog instance when different modules or tenants should not share the same registry.
import { CodedMessageCatalog, FieldCheck } from '@samatawy/checks';
const catalog = new CodedMessageCatalog();
catalog.register('person.name.missing', {
warn: {
en: 'Name is missing',
de: 'Name fehlt'
}
});
const result = new FieldCheck('name', {}).required({
code: 'person.name.missing',
catalog
}).result({
language: 'de',
catalog
});
If translators maintain external JSON files, load them at runtime through the Node-only entrypoint.
import { CodedMessageCatalog } from '@samatawy/checks';
import { loadCodedMessagesFromFile } from '@samatawy/checks/node';
const catalog = new CodedMessageCatalog();
await loadCodedMessagesFromFile(catalog, './messages.json');
Each file should be a JSON object keyed by result code. Under each code, use hint, warn, and err first. Each level can be either:
{ "en": "...", "de": "..." }{
"person.name.missing": {
"hint": {
"en": "Add the legal full name when available",
"de": "Ergaenze den vollstaendigen Namen, wenn verfuegbar"
},
"err": {
"en": "Name is required",
"de": "Name ist erforderlich"
}
},
"person.age.invalid": {
"err": "Age must be a number"
}
}
That means one file can contain only one language or several languages, depending on how you want translators to work. For example, a translator-owned German file can contain entries like "err": { "de": "..." }, while an English-maintained file can use the shorter "err": "..." form.
If you already have a base catalog in code, call the same method again to merge file-based translations into that catalog:
import { CodedMessageCatalog } from '@samatawy/checks';
import { loadCodedMessagesFromFile } from '@samatawy/checks/node';
const catalog = new CodedMessageCatalog();
catalog.register('person.name.missing', {
err: {
en: 'Name is required'
}
});
await loadCodedMessagesFromFile(catalog, './messages.de.json');
Checks keep the code while rules are being composed. Translation is applied when you call result(...).
If final result formatting runs without an explicit language, the catalog resolves coded messages using English by default.
That matters because the same check output can be formatted in more than one language:
const check = new FieldCheck('name', {}).required({
code: 'person.name.missing'
});
const german = check.result({ language: 'de' });
const english = check.result({ language: 'en' });
If a code exists in the catalog but the requested language does not, the package falls back to the original generated validator text for that level.
import { CodedMessageCatalog, FieldCheck } from '@samatawy/checks';
CodedMessageCatalog.global.register('person.name.missing', {
err: {
en: 'Name is required'
}
});
const check = new FieldCheck('name', {}).required({
code: 'person.name.missing'
});
const english = check.result({ language: 'en' });
const french = check.result({ language: 'fr' });
Here:
english.err is Name is requiredfrench.err falls back to Field name is requiredcode: 'person.name.missing'Codes also survive inside nested object and array results.
import { CodedMessageCatalog, ObjectCheck } from '@samatawy/checks';
CodedMessageCatalog.global.register('children.minor', {
err: {
en: 'All children must be minors'
}
});
const check = await ObjectCheck.for({
children: [{ age: 26 }]
}).check(person => [
person.optional('children').array().isTrueEach(child => {
if (child.age !== undefined && child.age >= 18) {
return false;
}
return true;
}, { code: 'children.minor' })
]);
const result = check.result({
raw: true,
flattened: true,
language: 'en'
}) as any;
console.log(result.raw.results[0].results[0].code);
console.log(result.errors);
Available CodedMessageCatalog methods:
register(code, definition) adds or replaces one coderegisterAll(source) copies codes from another cataloggetDefinition(code) returns a cloned definitiongetResult(code, language?) returns a resolved SingleResultlistCodes() returns the configured codesclear() empties the catalogconfigure(source) replaces the current catalog contents from another catalogDo not add codes just because the feature exists.
Inline messages are usually enough when: