Multilingual Form Validation
Trongate makes it easy to support form validation in multiple languages. The framework stores error messages in language-specific files and you can easily switch between languages on demand.
How It Works
Trongate's validation system loads error messages from language files based on the current language setting. When a validation error occurs, the framework displays the translated message for the selected language.
Key Concept: Language files are separate from your application code. You can add support for new languages simply by creating new language files - no changes to your validation rules are needed.
Language File Structure
Language files are stored in the following directory structure:
modules/validation/language/
├── en/
│ └── validation_errors.php
├── fr/
│ └── validation_errors.php
├── es/
│ └── validation_errors.php
└── de/
└── validation_errors.php
Each language uses a two-character ISO language code:
en - English
fr - French
es - Spanish
de - German
it - Italian
pt - Portuguese
ja - Japanese
zh - Chinese
Language File Format
Each language file contains a PHP array of error messages. Keys match the validation rule names, and values are the translated error messages:
<?php
/**
* English validation error messages for Trongate v2
*/
$validation_errors = [
'required_error' => 'The [label] field is required.',
'valid_email_error' => 'The [label] field must contain a valid email address.',
'min_length_error' => 'The [label] field must be at least [param] characters long.',
'max_length_error' => 'The [label] field cannot exceed [param] characters.',
// ... more error messages
];
Placeholder Substitution
Error messages use two types of placeholders that are automatically replaced:
| Placeholder |
Replaced With |
Example |
[label] |
The field label from set_rules() |
"The email address field is required." |
[param] |
Rule parameter (e.g., length, value) |
"The username field must be at least 3 characters long." |
Setting the Validation Language
Use set_language() to switch the validation language:
public function create(): void {
// Set validation language to French
$this->validation->set_language('fr');
// Define validation rules (same as always)
$this->validation->set_rules('email', 'email address', 'required|valid_email');
// Load and display form...
}
When validation fails, error messages will be displayed in French.
Pro Tip: Language preference is stored in the session ($_SESSION['validation_lang']), so it persists across requests. Once you set a language, it remains active until you change it or reset it.
Resetting to Default Language
Use reset_language() to revert validation messages to the default language (English):
public function create(): void {
// Reset to default language (English)
$this->validation->reset_language();
// Validation messages will now be in English
$this->validation->set_rules('email', 'email address', 'required|valid_email');
}
This method removes the language preference from the session, allowing the framework to use the default language.
Real-World Example: Multi-Language Site
Here's a complete example of a multilingual registration form:
class Users extends Trongate {
public function create(): void {
// Get the user's preferred language from a GET parameter, cookie, or session
$lang = post('lang') ?? $_COOKIE['preferred_lang'] ?? 'en';
// Validate language code (security: only allow valid language codes)
$allowed_languages = ['en', 'fr', 'es', 'de'];
if (!in_array($lang, $allowed_languages)) {
$lang = 'en';
}
// Set validation language
$this->validation->set_language($lang);
// Define validation rules in the selected language
$this->validation->set_rules('first_name', 'first name', 'required|min_length[2]');
$this->validation->set_rules('last_name', 'last name', 'required|min_length[2]');
$this->validation->set_rules('email', 'email address', 'required|valid_email|callback_email_unique');
$this->validation->set_rules('password', 'password', 'required|min_length[8]');
$this->validation->set_rules('confirm_password', 'password confirmation', 'required|matches[password]');
// Prepare form data
$update_id = segment(3, 'int');
$submit = post('submit');
if (($update_id === 0) || ($submit === 'Submit')) {
$data = $this->model->get_data_from_post();
} else {
$data = $this->model->get_data_from_db($update_id);
}
$data['lang'] = $lang;
$data['headline'] = ($update_id === 0) ? 'Create Account' : 'Update Profile';
$data['form_location'] = str_replace('/create', '/submit', current_url());
$this->view('create', $data);
}
public function submit(): void {
// Get the language again (maintain consistency)
$lang = post('lang') ?? 'en';
$allowed_languages = ['en', 'fr', 'es', 'de'];
if (!in_array($lang, $allowed_languages)) {
$lang = 'en';
}
// Set the same language for validation
$this->validation->set_language($lang);
// Define validation rules
$this->validation->set_rules('first_name', 'first name', 'required|min_length[2]');
$this->validation->set_rules('last_name', 'last name', 'required|min_length[2]');
$this->validation->set_rules('email', 'email address', 'required|valid_email|callback_email_unique');
$this->validation->set_rules('password', 'password', 'required|min_length[8]');
$this->validation->set_rules('confirm_password', 'password confirmation', 'required|matches[password]');
if ($this->validation->run() === true) {
// Save user
$data = $this->model->get_data_from_post();
$update_id = segment(3, 'int');
if ($update_id === 0) {
$this->db->insert($data, 'users');
set_flashdata('Account created successfully');
} else {
$this->db->update($update_id, $data, 'users');
set_flashdata('Profile updated successfully');
}
redirect('users/login');
} else {
// Validation failed - redisplay form with errors in the same language
$this->create();
}
}
public function email_unique(string $email): string|bool {
block_url('users/email_unique');
$existing = $this->db->get_one_where('email', $email, 'users');
if ($existing !== false) {
return 'This email address is already in use.';
}
return true;
}
}
In the view file, pass the language as a hidden field so it persists through form submission:
<?php
echo form_open($form_location, ['class' => 'highlight-errors']);
// Pass language as hidden field so it's submitted with the form
echo form_hidden('lang', $lang);
echo form_label('First Name');
echo validation_errors('first_name');
echo form_input('first_name', $first_name);
echo form_label('Last Name');
echo validation_errors('last_name');
echo form_input('last_name', $last_name);
echo form_label('Email Address');
echo validation_errors('email');
echo form_email('email', $email);
echo form_label('Password');
echo validation_errors('password');
echo form_password('password');
echo form_label('Confirm Password');
echo validation_errors('confirm_password');
echo form_password('confirm_password');
echo form_submit('submit', 'Register');
echo form_close();
?>
Adding a New Language
To add support for a new language, follow these steps:
Step 1: Create the Language Directory
Create a new directory in the validation module:
modules/validation/language/pt/
Step 2: Create the Language File
Create validation_errors.php in that directory with translated error messages:
<?php
/**
* Mensagens de erro de validação em português para Trongate v2
*/
$validation_errors = [
'required_error' => 'O campo [label] é obrigatório.',
'valid_email_error' => 'O campo [label] deve conter um endereço de e-mail válido.',
'min_length_error' => 'O campo [label] deve ter pelo menos [param] caracteres.',
'max_length_error' => 'O campo [label] não pode exceder [param] caracteres.',
// ... include all error message keys
];
Step 3: Use the New Language
Now you can use the new language in your validation:
$this->validation->set_language('pt'); // Portuguese
$this->validation->set_rules('email', 'endereço de e-mail', 'required|valid_email');
Important: Ensure all error message keys in your new language file match the keys in the default English file. If a key is missing, the framework will display "The [label] field failed validation."
Language Fallback Behavior
The validation system handles missing language files gracefully:
- First, it looks for a language file matching the requested language code (e.g.,
fr/validation_errors.php)
- If that file doesn't exist, it falls back to the default English language file (
en/validation_errors.php)
- If a specific error message key is missing from a language file, it displays a generic error message
Best Practice: Always keep your English language file complete with all possible error messages. This ensures a graceful fallback if a translated language file is incomplete or missing.
Complete List of Language Strings
Here are all the validation error message keys you should include in your language files:
Standard Validation Rules
required_error
integer_error
numeric_error
decimal_error
valid_email_error
valid_url_error
valid_ip_error
valid_date_error
valid_time_error
valid_datetime_local_error
valid_month_error
valid_week_error
min_length_error
max_length_error
exact_length_error
greater_than_error
less_than_error
matches_error
File Validation Rules
allowed_types_error
max_size_error
min_size_error
Image Validation Rules
is_image_error
max_width_error
min_width_error
max_height_error
min_height_error
exact_width_error
exact_height_error
square_error
Upload and Security Rules
upload_failed_error
not_an_image_error
invalid_file_error
security_threat_error
Best Practices
- Validate the language code: Always whitelist acceptable language codes before setting a language. This prevents invalid language codes from being used.
- Pass language as a hidden field: When using forms, include the language as a hidden field so it persists through validation errors.
- Use ISO 639-1 codes: Stick to two-character language codes (en, fr, es, etc.) for consistency and compatibility.
- Keep language files in sync: When you add a new validation rule, update all language files with translations for that rule's error message.
- Create a language constant (optional): Define a constant like
DEFAULT_VALIDATION_LANG for your application's default language.
- Store user language preference: Save the user's language preference in their account or a cookie for personalization.
- Use natural field labels: Use lowercase, natural language for field labels so error messages read naturally in all languages: "email address" not "email_address".
- Test all languages: Verify that error messages display correctly in each supported language before deploying.
- Include comprehensive translations: Don't leave error messages in English if you're supporting other languages - translate all messages for a professional user experience.
Custom Error Messages in Different Languages
When using custom validation callbacks, define error messages in your language files and simply return the key - the framework will handle the rest.
For example, in a language file such as fr/validation_errors.php you could add:
'email_already_exists_error' => 'Cette adresse e-mail est déjà utilisée.',
Then, your validation callback could be written like so:
// In your callback:
public function email_unique(string $email): string|bool {
block_url('users/email_unique');
$existing = $this->db->get_one_where('email', $email, 'users');
if ($existing !== false) {
return 'email_already_exists_error'; // Framework looks this up in language file
}
return true;
}
In the scenario described above, Trongate will:
- Get the currently loaded language.
- Attempt to read a language file associated with the currently loaded language.
- Attempt to find and return an error message from the language file whose key matches the returned value from the form validation callback.
If a matching error message cannot be found, the returned string will be rendered in the same manner as would happen with a normal custom form validation callback.
Configuration Constants
If you are building a website that does not have English as the default language, you can define a default validation language in your application configuration:
// In any config file (e.g., config/config.php or config/site_owner.php):
define('VALIDATION_LANG', 'fr');
The framework will use this default language if no language has been explicitly set and no language preference exists in the session.
Session Behavior
Language preference is stored in the session ($_SESSION['validation_lang']):
- When you call
set_language('fr'), it sets $_SESSION['validation_lang'] = 'fr'
- This preference persists for the entire session (or until changed)
- When you call
reset_language(), it unsets the session variable
- When validating, the framework checks the session variable for the current language setting
Session Security: Since language preference is stored in the user's session, it's safe from tampering. However, you should still validate language codes on the server side before using them.
Common Scenarios
Scenario 1: User Selects Language from Dropdown
public function submit(): void {
$lang = post('language_select', true);
// Whitelist allowed languages
$allowed = ['en', 'fr', 'es', 'de'];
if (!in_array($lang, $allowed)) {
$lang = 'en';
}
// Set language and save to user profile
$this->validation->set_language($lang);
$user_id = auth_user('id');
$this->db->update($user_id, ['preferred_language' => $lang], 'users');
set_flashdata('Language preference updated');
redirect('users/profile');
}
Scenario 2: Load User's Preferred Language Automatically
public function create(): void {
$user_id = auth_user('id');
$user = $this->db->get_one_where('id', $user_id, 'users');
// Set language based on user's preference
if ($user && !empty($user->preferred_language)) {
$this->validation->set_language($user->preferred_language);
}
// Define validation rules (in user's preferred language)
$this->validation->set_rules('email', 'email address', 'required|valid_email');
// ... rest of method
}
Scenario 3: Multilingual API Response
public function api_create(): void {
$lang = post('language', true) ?? 'en';
// Validate language
$allowed = ['en', 'fr', 'es'];
if (!in_array($lang, $allowed)) {
$lang = 'en';
}
$this->validation->set_language($lang);
$this->validation->set_rules('email', 'email address', 'required|valid_email');
$this->validation->set_rules('name', 'name', 'required|min_length[2]');
if ($this->validation->run() === false) {
validation_errors(422); // Returns JSON errors in the selected language
}
echo json_encode(['success' => true]);
}