PHP Bilingual Error System Ideas I am building a php based mobile website that must be in both Spanish and English. This is my first bilingual site so I've been having to re-think a lot of coding practices I currently use to accommodate multiple languages. 

I'm using a custom MVC pattern and currently I'm trying to use the same controller and model for both Spanish and English version of the site and just change the view for each language. 

Since I'm using same model for both Spanish and English I have created an error system
in the model that supports both languages see sample below. 

The error list will be different depending on the model but I have 1 model with almost 30 errors and 60 translations. Its a big Form with a lot of input field validations.

Do you think it would be better to keep it as 1 large nested array or split the array into something like $english_errorids and $spanish_errorids

Open to any ideas you guys may have, it currently works as is I'm just trying to make sure I'm doing this the right way.

 /**
 * Created by PhpStorm.
 * User: wmcgee
 * Date: 11/19/14
 * Time: 12:05 PM
 */

 class SampleModel
 {
     #region Error System
     /** 
      * List of all recorded errors
      * @var array 
      */
     public $errors;

     /**
      * Can be set to either ENG or SPAN
      * @var string
      */
     public $lang = "ENG";

     /**
      * List of errors with translations 
      * @var array 
      */
     public $errorids = array(
         10 =&gt; array('ENG'=&gt;"Vehicle Condition Missing", 'SPAN'=&gt;"Condicion de vehiculo falta"),
         11 =&gt; array('ENG'=&gt;"Signature Missing",         'SPAN'=&gt;"Falta su firma "),
     );

     /**
      * Records a translated error based on what lang is set and
      * what error id was passed into recorded errors list.
      * 
      * @param $id int
      * @param bool $override if true id is treated as an 
      *                       error message and is recorded
      *                       in error list.
      */
     public function recordError($id, $override = false)
     {
         //- Allow manual error messages not in translation list to be added
         if( $override ){
             $this-&gt;errors[] = $id;
             return;
         }

         $this-&gt;errors[] = $this-&gt;errorids[$id][$this-&gt;lang];
     }

    /**
     * Returns errors formatted in HTML
     * @return HTML
     */
    public function getErrors()
    {
        if( sizeof($this-&gt;errors) == 0){ return ''; }

        $msg = '&lt;p class="errorBox"&gt;&lt;b&gt;Report Errors&lt;/b&gt;&lt;br&gt;';

        foreach($this-&gt;errors as $error)
        {
            $msg .= "&amp;bull; " . $error . "&lt;br&gt;";
        }

        $msg .= "&lt;/p&gt;";

        return $msg;
    }
     #endregion
 }
I have had this problem several times and have tried different solutions. This is my current solution.

I use an associative array as a key-value list of message strings. Then I set the errors to a specific key name and fetch the corresponding message in a appropriately named file like english.php etc. This file can be all types of formats, but I will stick with PHP. 

This has two main advantages:


Human readable key names
A central place all message string are stored, which in turn makes reuse of messages easier.


An example would be:

/*
* File: english.php
* This must return an associative array. This array can be multidimentional to better
* group different messages.
*/
$lang = [];
$lang['field_required'] = 'The field is required.';
$lang['success'] = 'The post was successfully created.';

// A grouped entry.
$lang['registration']['email_unavailable'] = 'The provided email is already in use.';

/*
 * A message string that requires some input that cannot be determined beforehand can 
 * be solved by using the sprintf() or vsprintf() functions with the latter using an
 * array as an argument.
 */
$lang['shopping']['cart']['items']['amount_of_items'] = 'There are %d item(s) in your shopping cart.';

return $lang;

/*
 * File: bootstrapper.php (example)
 * 
 * Some centrally available file. The $languages variable should be provided to
 * classes using dependency injection or made available otherwise.
 */
$current_language = 'english'; // Detected in a class or some other code.

$file = "path/to/language/{$current_language}.php"; // This file must return an array.

/*
 * Include the currently supported language file.
 * You can also make a list of supported languages before trying to load the file.
 */
if(!is_file($file) || !is_readable($file)) {
    // Do something here...
}

$languages = include $file;

/* 
 * (OPTIONAL)
 * Here you can check if the language file returned the expected format. 
 * Example: if(!is_array($languages)) { // do something }
 */

/*
 * Some model or controller (if you're validating input before sending it to the
 * model).
 * Here the language key is the same for all languages and only the value is 
 * different.
 */
 $field_id = 'username';
 $field = filter_input(INPUT_POST, $field_id, FILTER_SANITIZE_STRING); // Just as good practice

 if(!check_required_field_function($field)) {
     $errors[$field_id] = $languages['field_required'];
 }

 /*
  * File: some view.php
  * Since this file properly contains some HTML I have used an alternative PHP syntax
  */
 &lt;?php if(isset($errors) &amp;&amp; !empty($errors)): ?&gt;

     &lt;p&gt;The following errors occurred.&lt;/p&gt;
     &lt;ul&gt;         

     &lt;?php foreach($errors as $error): ?&gt;

         &lt;li&gt;&lt;?php echo htmlspecialchars($error, ENT_QUOTES, 'UTF-8'); ?&gt;&lt;/li&gt; 

     &lt;? endforeach; ?&gt;

     &lt;/ul&gt;

 &lt;?php endif; ?&gt;


The output escaping isn't stictly related to this question, but I added it as good practice.

This approach can properly be refactored a lot. Please let me know if you have any questions or something you would point out.

This is a very simple outline of the strategy I use. Hope it helps.