Search code examples
phpoctobercmsoctobercms-pluginsoctobercms-backend

October CMS - Translate Backend with RainLab Translate Plugin


On my application I have a custom plugin with a form of about 80 translatable fields. The form will be available in the backend (for admins) and in the frontend (for end users) with the same labels.

I would like to use the Rainlab Translate plugin to translate all the field's labels so the admins can easily change them to suit their needs.

The problem is that all the backend translations are stored in the file system (e.g /plugins/my-plugin/lang/en/lang.php).

Would it be possible to manage a custom plugin's translations with the Rainlab Translate plugin ? This will avoid having duplicates between the php translation files and the database.

I've installed Rainlab Translate plugin so far to translate the front-end in french and german.

Thank you guys!


Solution

  • I came up with a solution creating a custom Plugin that extends the October CMS Backend Translator service. I've added the following code to the boot() method of my plugin :

     // Custom Backend Translator that handles the Backend localisation with the Rainlab Translator plugin
        $this->app->singleton('translator', function ($app) {
            $loader = $app['translation.loader'];
    
            // When registering the translator component, we'll need to set the default
            // locale as well as the fallback locale. So, we'll grab the application
            // configuration so we can easily get both of these values from there.
            $locale = $app['config']['app.locale'];
            // extending October CMS Translator class
            $trans = new Translator($loader, $locale);
            // setting locale to message
            Message::$locale = $locale;
    
            $trans->setFallback($app['config']['app.fallback_locale']);
    
            return $trans;
        });
    

    I've create a Translator class that extends the default one and implements a database lookup for translations. It also has a fallback to the default array file based translations if no translation is found.

    <?php namespace Author\MyPlugin\Classes\Translation;
    
    use Author\MyPlugin\Classes\Translation\Message as Message;
    
    class Translator extends \October\Rain\Translation\Translator
    {
        /**
         * Get the translation for the given key.
         *
         * @param  string $key
         * @param  array $replace
         * @param  string $locale
         * @return string
         */
        public function get($key, array $replace = [], $locale = null)
        {
            // getting translation from database
            $message = Message::trans($key);
            // if there's a translation we return it
            if ($message != null && $message !== $key) {
                return $message;
            }
            // otherwise fallback to file array translations
            return parent::get($key, $replace, $locale);
        }
    }
    

    And the code of the Message class below:

    <?php namespace Author\MyPlugin\Classes\Translation;
    
    
    class Message extends \RainLab\Translate\Models\Message
    {
        /**
         * Creates or finds an untranslated message string.
         * @param  string $messageId
         * @return string
         */
        public static function get($messageId)
        {
            if (!self::$locale) {
                return $messageId;
            }
    
            if (!is_string($messageId)) {
                return null;
            }
    
            // we let full translation key for the backend
            if (!\App::runningInBackend()) {
                $messageCode = self::makeMessageCode($messageId);
            } else {
                $messageCode = $messageId;
            }
    
            /*
            * Found in cache
            */
            if (array_key_exists($messageCode, self::$cache)) {
                return self::$cache[$messageCode];
            }
    
            /*
             * Uncached item
             */
            $item = static::firstOrNew([
                'code' => $messageCode
            ]);
    
            /*
             * Create a default entry
             */
            if (!$item->exists) {
                $data = [static::DEFAULT_LOCALE => $messageId];
                $item->message_data = $item->message_data ?: $data;
            }
    
            /*
             * Schedule new cache and go
             */
            $msg = $item->forLocale(self::$locale, $messageId);
            self::$cache[$messageCode] = $msg;
            self::$hasNew = true;
    
            return $msg;
        }
    }
    

    Any feedback of this solution is welcome.