Search for a command to run...
Last updated June 9, 2026
Ctrovalidate provides a centralized, isomorphic Translator system in the ctrovalidate-core package. It allows you to manage error messages for multiple languages and dynamically switch locales across your entire application.
The translator instance is shared across all packages (core, react, vue, svelte, browser). When you change the locale in the core, the adapters react to it.
[!IMPORTANT] Reactivity Note: Changing the locale does NOT automatically re-render existing error messages. You must trigger a re-validation (e.g.,
validateForm()) to update the UI with new translations.
import { translator } from 'ctrovalidate-core';
// Switch to French
translator.setLocale('fr');You can add support for new languages by providing a dictionary object that maps rule names to translated messages.
// Register Spanish
translator.addMessages('es', {
required: '¡Este campo es obligatorio!',
email: 'Por favor, introduce un correo electrónico válido.',
minLength: 'Debe tener al menos {0} caracteres.'
});
// Activate it
translator.setLocale('es');Messages support dynamic placeholders using the {n} syntax, where n corresponds to the index of the rule parameter.
minLength:8Must be at least {0} characters.Must be at least 8 characters.For rules with multiple parameters (like between:18,65):
Value must be between {0} and {1}.Value must be between 18 and 65.Ctrovalidate supports two complementary approaches for managing locales:
Use translator.setLocale() to change the language for all validation across your entire application.
import { translator } from 'ctrovalidate-core';
// Switch globally
translator.setLocale('es');
// All hooks will now use Spanish messages (if registered)[!IMPORTANT] Reactivity Note: Changing the global locale does NOT automatically re-render existing error messages. You must trigger a re-validation (e.g.,
validate()) to update the UI with new translations.
Pass a locale option directly to the hook to override the global locale for that specific form instance.
import { useCtrovalidate } from 'ctrovalidate-react';
const { errors, validate } = useCtrovalidate({
schema: { email: 'required|email' },
locale: 'fr', // This form always uses French, regardless of global locale
});Use Cases:
Here is a verified pattern for switching languages dynamically in a React application using the global approach.
import { useCtrovalidate } from 'ctrovalidate-react';
import { translator } from 'ctrovalidate-core';
// 1. Setup dictionaries once (e.g., in App.tsx)
translator.addMessages('es', {
required: 'Requerido',
email: 'Email inválido'
});
function MyForm() {
const { validateForm, errors } = useCtrovalidate({ /* ... */ });
const switchLanguage = async (lang) => {
// 2. Set the global locale
translator.setLocale(lang);
// 3. IMPORTANT: Re-validate to update current errors
await validate();
};
return (
<button onClick={() => switchLanguage('es')}>Español</button>
);
}While global i18n is powerful, you often need to override messages for a specific field without touching the global dictionary.
<input
name="username"
data-ctrovalidate-rules="required"
data-ctrovalidate-message="Your username is required to login."
/>You can pass a messages object to the hook options to override specific rules for that form instance.
useCtrovalidate({
schema: { ... },
messages: {
'required': 'Custom required message for this form',
'email': 'Please enter a valid email address'
}
});Message Resolution (highest priority first):
messages[ruleName] — from setCustomMessages(), hook messages option, or data-ctrovalidate-{ruleName}-message attributemessages['*'] — catch-all from setCustomMessages(), hook messages, or data-ctrovalidate-message attributetranslator.translate(ruleName, params, locale) — resolves via locale dictionary:
locale override or translator.currentLocale'default' key, then 'Invalid input.'Note: The locale option does not add a separate priority level — it controls which locale dictionary is used during translation.
i18n/ directory to facilitate contribution and maintenance.translator.setLocale() accordingly.locale option when you need a specific form to always use a certain language, regardless of global settings.