Skip to content

Latest commit

 

History

History
418 lines (298 loc) · 9.94 KB

File metadata and controls

418 lines (298 loc) · 9.94 KB

Internationalization (i18n)

← Back to Main Documentation

Navigation: Validations | Conditional Validation | Rules & Schemas | Custom Strategies | Error Handling | Internationalization(INTERNATIONALIZATION.md) | Benchmarks


DataVerify includes a built-in translation system for validation error messages. Built-in languages (English, French) are automatically loaded - just set the locale.

Table of Contents


Quick Start

use Gravity\DataVerify;

$data = new stdClass();
$data->email = 'invalid';

$dv = new DataVerify($data);
$dv->setLocale('fr');  // Switch to French

$dv->field('email')->required->email;

if (!$dv->verify()) {
    echo $dv->getErrors()[0]['message'];
    // "Le champ email doit être une adresse email valide"
}

Built-in Languages

Available Built-in Locales

DataVerify comes with built-in translations that are automatically loaded:

  • English (en) - Default locale
  • French (fr) - Ready to use
$dv = new DataVerify($data);

// English (default)
$dv->field('email')->required;
// "The field email is required"

// French
$dv->setLocale('fr');
$dv->field('email')->required;
// "Le champ email est requis"

Note: Built-in translations are in src/Locales/ (no dependencies).


Custom Translation Files

PHP Translation Files (Recommended)
<?php
// locales/es/validations.php
return [
    'validation.required' => 'El campo {field} es obligatorio',
    'validation.email' => 'El campo {field} debe ser un email válido',
    'validation.minLength' => 'El campo {field} debe tener al menos {min} caracteres',
];

Load and use:

$dv->loadLocale('es', __DIR__ . '/locales/es/validations.php');
$dv->setLocale('es');

Why PHP files? Fast, zero dependencies, native support.

YAML Files (Requires symfony/yaml)
# locales/es/validations.yml
validation.required: "El campo {field} es obligatorio"
validation.email: "El campo {field} debe ser un email válido"
$dv->loadLocale('es', __DIR__ . '/locales/es/validations.yml');
$dv->setLocale('es');

Note: Requires composer require symfony/yaml

Recommended File Structure
app/locales/
├── en/validations.php
├── fr/validations.php
└── es/validations.php

Load multiple files per locale:

$dv
    ->loadLocale('es', __DIR__ . '/locales/es/validations.php')
    ->loadLocale('es', __DIR__ . '/locales/es/custom.php')
    ->setLocale('es');

Inline Custom Translations

Adding Translations Directly in Code
$dv->addTranslations([
    'validation.strong_password' => 'The {field} must be at least {minLength} characters with uppercase, lowercase, number, and special character'
], 'en');

$dv->addTranslations([
    'validation.strong_password' => 'Le {field} doit contenir au moins {minLength} caractères avec majuscule, minuscule, chiffre et caractère spécial'
], 'fr');

Use for: Custom strategies, quick prototyping, one-off translations.

Overriding Built-in Messages
$dv->addTranslations([
    'validation.required' => '{field} is absolutely mandatory!'
], 'en');

$dv->field('email')->required;
// "email is absolutely mandatory!"

Translation Placeholders

Built-in Placeholders
Placeholder Used In Example
{field} All validations Field name or alias
{min} minLength, between, greaterThan Minimum value
{max} maxLength, between, lowerThan Maximum value
{mime} fileMime MIME type(s)
{value} Custom messages Actual field value
$dv->field('password')->minLength(8);
// "The field password must be at least 8 characters"
//                                         ^ {min} auto-replaced
Automatic Parameter Mapping

DataVerify automatically discovers parameter names from handler() methods and makes them available as placeholders.

use Gravity\Validations\ValidationStrategy;

class RangeStrategy extends ValidationStrategy
{
    public function getName(): string
    {
        return 'in_range';
    }
    
    protected function handler(
        mixed $value,
        int $min,    // Becomes {min} placeholder
        int $max     // Becomes {max} placeholder
    ): bool {
        return $value >= $min && $value <= $max;
    }
}

// Translation automatically recognizes {min} and {max}
$dv->addTranslations([
    'validation.in_range' => 'The {field} must be between {min} and {max}'
], 'en');

// Usage
$dv->field('age')->in_range(18, 65);
// Error: "The field age must be between 18 and 65"
//         {min} and {max} auto-populated from parameters

How it works:

  • Reflection extracts parameter names from your handler() method
  • Parameter names become placeholders (e.g., $threshold{threshold})
  • Values are automatically mapped when validation is called

Best practice - Use standard names:

// ✅ Good - Matches conventions
protected function handler(mixed $value, int $min, int $max): bool
{
    // {min} and {max} in translations
}

// ❌ Works but unclear
protected function handler(mixed $value, int $firstLimit, int $secondLimit): bool
{
    // {firstLimit} and {secondLimit} - less intuitive
}

Standard names: {min}, {max}, {mime}, {format}, {allowed}, {forbidden}

Using Aliases
// Without alias
$dv->field('user_email')->required;
// "The field user_email is required"

// With alias
$dv->field('user_email')->alias('Email Address')->required;
// "The field Email Address is required"
Custom Error Messages (Bypass Translations)
$dv->field('email')
    ->required
    ->email
    ->errorMessage('Please provide a valid professional email address');

Custom Translator

Extending Translator Class

For advanced needs (database, API, cache):

use Gravity\Translation\Translator;

class DatabaseTranslator extends Translator
{
    private PDO $pdo;
    
    public function __construct(PDO $pdo, string $locale = 'en')
    {
        parent::__construct($locale);
        $this->pdo = $pdo;
    }
    
    public function trans(string $id, array $parameters = [], ?string $domain = null, ?string $locale = null): string
    {
        $locale = $locale ?? $this->getLocale();
        $domain = $domain ?? 'messages';
        
        // Load from database
        $message = $this->loadFromDb($id, $locale, $domain);
        
        if ($message === null) {
            // Fallback to parent (handles fallback locale)
            return parent::trans($id, $parameters, $domain, $locale);
        }
        
        // Use parent's placeholder replacement
        return $this->replacePlaceholders($message, $parameters);
    }
    
    private function loadFromDb(string $id, string $locale, string $domain): ?string
    {
        $stmt = $this->pdo->prepare(
            'SELECT message FROM translations WHERE id = ? AND locale = ? AND domain = ?'
        );
        $stmt->execute([$id, $locale, $domain]);
        return $stmt->fetchColumn() ?: null;
    }
}

$dv->setTranslator(new DatabaseTranslator($pdo, 'fr'));

Best Practices

Performance Tips

Load at bootstrap, switch on demand:

// bootstrap.php - Load once
$dv->loadLocale('es', __DIR__ . '/locales/es.php');
$dv->loadLocale('de', __DIR__ . '/locales/de.php');

// Later - instant switch (no I/O)
$dv->setLocale('es');
Organization

Use translation files over inline:

// ✅ Organized, reusable
$dv->loadLocale('es', __DIR__ . '/locales/es.php');

// ❌ Scattered, hard to maintain
$dv->addTranslations([...], 'es');

Leverage placeholders:

// ✅ Dynamic
'validation.between' => 'Between {min} and {max}'

// ❌ Hardcoded
'validation.between' => 'Between 1 and 100'
Custom Strategies

Use named parameters in handler():

use Gravity\Validations\ValidationStrategy;

// ✅ Clean - parameter names become placeholders
class MyStrategy extends ValidationStrategy
{
    protected function handler(mixed $value, int $min, int $max): bool
    {
        return $value >= $min && $value <= $max;
    }
}

// Translation
'validation.my_validation' => 'Between {min} and {max}'  // Clear and automatic

See Also


Navigation: Validations | Conditional Validation | Rules & Schemas | Custom Strategies | Error Handling | Internationalization | Benchmarks

← Back to Main Documentation