Trongate PHP Framework Docs
Introduction
Quick Start
Basic Concepts
Understanding Routing
Intercepting Requests
Module Fundamentals
Database Operations
Templates
Helpers
Form Handling
Form Validation
Working With Files
Image Manipulation
Working With Dates & Times
Language Control
Security
Tips And Best Practices

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
<?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. The framework supports both square-bracket and curly-brace formats:

Placeholder Replaced With Example
[label] or {label} The field label from set_rules() "The email address field is required."
[param] or {param} Rule parameter (e.g., length, value) "The username field must be at least 3 characters long."

Note: The default English language file uses square brackets ([label], [param]), but both formats work interchangeably. When creating translations, you can use whichever style you prefer.

Setting the Validation Language

Use to switch the validation language:

PHP
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: The validation module uses Trongate's Language module to manage language preferences. When you call set_language('fr'), it sets $_SESSION['app_lang'] = 'fr' (and stores a cookie). This preference persists across requests and is shared with other parts of your application that use the Language module.

Resetting to Default Language

Use to revert validation messages to the default language (English):

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

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

View File
<?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
<?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:

PHP
$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 generic error message "The {label} field failed validation." (Note: The framework supports both {label} and [label] placeholders.)

Language Fallback Behavior

The validation system handles missing language files gracefully:

  1. First, it looks for a language file matching the requested language code (e.g., fr/validation_errors.php)
  2. If that file doesn't exist, it falls back to the default English language file (en/validation_errors.php)
  3. 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

  1. Validate the language code: Always whitelist acceptable language codes before setting a language. This prevents invalid language codes from being used.
  2. Pass language as a hidden field: When using forms, include the language as a hidden field so it persists through validation errors.
  3. Use ISO 639-1 codes: Stick to two-character language codes (en, fr, es, etc.) for consistency and compatibility.
  4. Keep language files in sync: When you add a new validation rule, update all language files with translations for that rule's error message.
  5. Create a language constant (optional): Define the constant APP_LANG in your configuration (e.g., config/config.php) to set your application's default language. The Language module checks this constant if no session or cookie preference exists.
  6. Store user language preference: Save the user's language preference in their account or a cookie for personalization.
  7. Use natural field labels: Use lowercase, natural language for field labels so error messages read naturally in all languages: "email address" not "email_address".
  8. Test all languages: Verify that error messages display correctly in each supported language before deploying.
  9. 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:

PHP
// 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:

  1. Get the currently loaded language.
  2. Attempt to read a language file associated with the currently loaded language.
  3. 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 application language in your configuration. The Language module (which validation uses) checks the constant APP_LANG:

PHP
// In any config file (e.g., config/config.php or config/site_owner.php):
define('APP_LANG', 'fr');

The framework will use this default language if no language has been explicitly set via set_language() and no language preference exists in the session or cookies.

Session Behavior

The validation module integrates with Trongate's Language module. Language preference is stored in the session ($_SESSION['app_lang']) and optionally in a cookie:

  • When you call set_language('fr'), it sets $_SESSION['app_lang'] = 'fr' and stores a cookie
  • This preference persists across browser sessions (via cookie) and is shared with other application components
  • When you call reset_language(), it removes the session variable and cookie
  • When validating, the framework checks the Language module 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

PHP
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 = $this->trongate_tokens->get_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

PHP
public function create(): void {
    $user_id = $this->trongate_tokens->get_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

PHP
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]);
}

We're continually improving the Trongate documentation. If anything is incorrect, unclear, incomplete, or could be better, we'd genuinely appreciate your input.

Share your thoughts in the Documentation Feedback.

Leave Feedback About This Page