I started implementing i18n for my upcoming KO3 application, and implemented a quick patch so that I don't need to manually find and type translation strings.

What this code does

What the code below does is it checks whether the translation string exists, if not then it saves it into the translation file with the English equivalent. This updated version of the translation string file is saved into /application/i18n/languagename.php, and the old file is saved with a new name containing the current date and time.

Hope this helps!

How to set it up

First set the language using i18n::lang('xy-xx') in bootstrap.php.

Also, add the following as the last line in bootstrap.php:

// Write the updated language file, if necessary
i18n::write();
Finally, add the file /application/classes/i18n.php which overrides i18n::get():

/
  A patch for the Internationalization (i18n) class.
 
  @package    I18n
  @author Mikito Takada
 */

class I18n extends Kohana_I18n {
    // Cache of missing strings
    protected static $_cache_missing = array();

    /
      Returns translation of a string. If no translation exists, the original
      string will be returned. No parameters are replaced.
     
          $hello = I18n::get('Hello friends, my name is :name');
     
      @param   string   text to translate
      @param   string   target language
      @return  string
     /
    public static function get($string, $lang = NULL)
    {
        if ( ! $lang)
        {
            // Use the global target language
            $lang = I18n::$lang;
        }

        // Load the translation table for this language
        $table = I18n::load($lang);

        // Return the translated string if it exists
      if(isset($table[$string]))
      {
         return $table[$string];
      } else {
         // Translated string does not exist
         // Store the original string as missing - still makes sense to store the English string so that loading the untranslated file will work.
         I18n::$_cache_missing[$lang][$string] = $string;
         return $string;
      }
    }

   public static function write()
   {
      // something new must be added for anything to happen
      if(!empty(I18n::$_cache_missing)) {
         $contents = '<?php defined('SYSPATH') or die('No direct script access.');
/**
  Translation file in language: '.I18n::$lang.'
  Automatically generated from previous translation file.
 /
return '.varexport(array_merge(I18n::$_cache_missing[I18n::$lang], I18n::$_cache[I18n::$lang]), true).';';

         // save string to file
         $savepath = APPPATH.'/i18n/';
         $filename = I18n::$lang.'.php';
         // check that the path exists
         if(!file_exists($savepath)) {
            // if not, create directory
            mkdir($savepath, 0777, true);
         }
         // rename the old file - if the file size is different.
         if(file_exists($savepath.$filename) && ( filesize($savepath.$filename) != strlen($contents) ) ) {
            $result = rename($savepath.$filename, $savepath.I18n::$lang.''.date('Y_m_d_H_i_s').'.php');
            if(!$result) {
               // Rename failed! Don't write the file.
               return;
            }
         }
         // save the file
         file_put_contents($savepath.$filename, $contents);
      }
   }
}

Caveats and notes

There are two things that have to be taken into account:

  • First, this is would obviously be inefficient for a production site, since actual files are being rewritten on each request that finds new translation strings.
  • Why this is not a problem: My recommendation is that you shouldn't run this code in production mode, since there is no point and it is very easy to remove the code after developement is completed.
  • Second, this approach is less comprehensive than using something like the gettext tools that are available - those tools scan all of the source code, while my approach depends on run-time detection of new strings. This means that a small percentage of strings will not be found automatically (ex. rare errors that never get triggered).
  • Why this is not a problem: This approach will still get the vast majority of the strings without requiring any manual hunting for strings, so I think it'll save you quite a bit of time.

Comments

Beto: Hello,

I took the liberty to organize their class within a module, so I believe that is easier to reuse.

Of course, I kept developing the credits, I made only a small modification that I found interesting to make it more readable.

On my blog (Brazilian Portuguese) I wrote a bit more of a module called i18n-files.

http://beto.euqueroserummacaco.com/blog/kohana-modulo-i18n-files-traduzinho-seu-software-de-maneira-simples/

I hope this does not leave angry.

Mikito Takada: Hi Beto,

Good to hear that you found it useful, thanks for your blog post! I had a look at it via Google Translate...

Beto: Hello Mikito,

After all this time returned to his post to clarify some doubts. Your article is very good!

About the google translator .... he is really good is not it? Always saves me when I do not understand the text written in other languages.

Ando: Hey,

I can't seem to overload the Kohana i18n class, specifically.

Kohana seems to load the system's i18n and leave my classes/i18n.php untouced.

The filenames/ locations / class i18n extends Kohana_I18n all seem ok.

When manually require-ing the file: PHP Fatal error: Cannot redeclare class I18n in ...

Any ideas?

Nuno: nice man. thanks

jaydublu: I found that just adding I18n::write() into bootstrap.php didn't work - it was run before any new strings were collected.

But, looking at Beto's module I spotted that register_shutdown_function(array('I18n', 'write')); is a way that will work.