'rainlab.translate::lang.plugin.name', 'description' => 'rainlab.translate::lang.plugin.description', 'author' => 'Alexey Bobkov, Samuel Georges', 'icon' => 'icon-language', 'homepage' => 'https://github.com/rainlab/translate-plugin' ]; } /** * register the plugin */ public function register() { // Load localized version of mail templates (akin to localized CMS content files) Event::listen('mailer.beforeAddContent', function ($mailer, $message, $view, $data, $raw, $plain) { return EventRegistry::instance()->findLocalizedMailViewContent($mailer, $message, $view, $data, $raw, $plain); }, 1); // Defer event with low priority to let others contribute before this registers. Event::listen('backend.form.extendFieldsBefore', function($widget) { EventRegistry::instance()->registerFormFieldReplacements($widget); }, -1); // Handle translated page URLs Page::extend(function($model) { if (!$model->propertyExists('translatable')) { $model->addDynamicProperty('translatable', []); } $model->translatable = array_merge($model->translatable, ['title', 'description', 'meta_title', 'meta_description']); if (!$model->isClassExtendedWith(\RainLab\Translate\Behaviors\TranslatablePageUrl::class)) { $model->extendClassWith(\RainLab\Translate\Behaviors\TranslatablePageUrl::class); } if (!$model->isClassExtendedWith(\RainLab\Translate\Behaviors\TranslatablePage::class)) { $model->extendClassWith(\RainLab\Translate\Behaviors\TranslatablePage::class); } }); // Extension logic for October CMS v1.0 if (!class_exists('System')) { $this->extendLegacyPlatform(); } // Extension logic for October CMS v2.0 else { Event::listen('cms.theme.createThemeDataModel', function($attributes) { return new \RainLab\Translate\Models\MLThemeData($attributes); }); Event::listen('cms.template.getTemplateToolbarSettingsButtons', function($extension, $dataHolder) { if ($dataHolder->templateType === 'page') { EventRegistry::instance()->extendEditorPageToolbar($dataHolder); } }); } // Register console commands $this->registerConsoleCommand('translate.scan', \Rainlab\Translate\Console\ScanCommand::class); // Register asset bundles $this->registerAssetBundles(); } /** * extendLegacyPlatform will add the legacy features expected in v1.0 */ protected function extendLegacyPlatform() { // Adds translation support to file models File::extend(function ($model) { if (!$model->propertyExists('translatable')) { $model->addDynamicProperty('translatable', []); } $model->translatable = array_merge($model->translatable, ['title', 'description']); if (!$model->isClassExtendedWith(\October\Rain\Database\Behaviors\Purgeable::class)) { $model->extendClassWith(\October\Rain\Database\Behaviors\Purgeable::class); } if (!$model->isClassExtendedWith(\RainLab\Translate\Behaviors\TranslatableModel::class)) { $model->extendClassWith(\RainLab\Translate\Behaviors\TranslatableModel::class); } }); // Adds translation support to theme settings ThemeData::extend(static function ($model) { if (!$model->propertyExists('translatable')) { $model->addDynamicProperty('translatable', []); } if (!$model->isClassExtendedWith(\October\Rain\Database\Behaviors\Purgeable::class)) { $model->extendClassWith(\October\Rain\Database\Behaviors\Purgeable::class); } if (!$model->isClassExtendedWith(\RainLab\Translate\Behaviors\TranslatableModel::class)) { $model->extendClassWith(\RainLab\Translate\Behaviors\TranslatableModel::class); } $model->bindEvent('model.afterFetch', static function() use ($model) { foreach ($model->getFormFields() as $id => $field) { if (!empty($field['translatable'])) { $model->translatable[] = $id; } } }); }); } /** * boot the plugin */ public function boot() { // Set the page context for translation caching with high priority. Event::listen('cms.page.init', function($controller, $page) { EventRegistry::instance()->setMessageContext($page); }, 100); // Populate MenuItem properties with localized values if available Event::listen('pages.menu.referencesGenerated', function (&$items) { $locale = App::getLocale(); $iterator = function ($menuItems) use (&$iterator, $locale) { $result = []; foreach ($menuItems as $item) { $localeFields = array_get($item->viewBag, "locale.$locale", []); foreach ($localeFields as $fieldName => $fieldValue) { if ($fieldValue) { $item->$fieldName = $fieldValue; } } if ($item->items) { $item->items = $iterator($item->items); } $result[] = $item; } return $result; }; $items = $iterator($items); }); // Import messages defined by the theme Event::listen('cms.theme.setActiveTheme', function($code) { EventRegistry::instance()->importMessagesFromTheme($code); }); // Adds language suffixes to content files. Event::listen('cms.page.beforeRenderContent', function($controller, $fileName) { return EventRegistry::instance() ->findTranslatedContentFile($controller, $fileName) ; }); // Prune localized content files from template list Event::listen('pages.content.templateList', function($widget, $templates) { return EventRegistry::instance() ->pruneTranslatedContentTemplates($templates) ; }); // Look at session for locale using middleware \Cms\Classes\CmsController::extend(function($controller) { $controller->middleware(\RainLab\Translate\Classes\LocaleMiddleware::class); }); // Append current locale to static page's cache keys $modifyKey = function (&$key) { $key = $key . '-' . Lang::getLocale(); }; Event::listen('pages.router.getCacheKey', $modifyKey); Event::listen('pages.page.getMenuCacheKey', $modifyKey); Event::listen('pages.snippet.getMapCacheKey', $modifyKey); Event::listen('pages.snippet.getPartialMapCacheKey', $modifyKey); if (class_exists('\RainLab\Pages\Classes\SnippetManager')) { $handler = function ($controller, $template, $type) { if (!$template->methodExists('getDirtyLocales')) { return; } // Get the locales that have changed $dirtyLocales = $template->getDirtyLocales(); if (!empty($dirtyLocales)) { $currentLocale = Lang::getLocale(); foreach ($dirtyLocales as $locale) { if (!$template->isTranslateDirty(null, $locale)) { continue; } // Clear the RainLab.Pages caches for each dirty locale App::setLocale($locale); \RainLab\Pages\Plugin::clearCache(); } // Restore the original locale for this request App::setLocale($currentLocale); } }; Event::listen('cms.template.save', $handler); Event::listen('pages.object.save', $handler); } } /** * registerComponents */ public function registerComponents() { return [ \RainLab\Translate\Components\LocalePicker::class => 'localePicker', \RainLab\Translate\Components\AlternateHrefLangElements::class => 'alternateHrefLangElements' ]; } /** * registerPermissions */ public function registerPermissions() { return [ 'rainlab.translate.manage_locales' => [ 'tab' => 'rainlab.translate::lang.plugin.tab', 'label' => 'rainlab.translate::lang.plugin.manage_locales' ], 'rainlab.translate.manage_messages' => [ 'tab' => 'rainlab.translate::lang.plugin.tab', 'label' => 'rainlab.translate::lang.plugin.manage_messages' ] ]; } /** * registerSettings */ public function registerSettings() { return [ 'locales' => [ 'label' => 'rainlab.translate::lang.locale.title', 'description' => 'rainlab.translate::lang.plugin.description', 'icon' => 'icon-language', 'url' => Backend::url('rainlab/translate/locales'), 'order' => 550, 'category' => 'rainlab.translate::lang.plugin.name', 'permissions' => ['rainlab.translate.manage_locales'], 'keywords' => 'translate', ], 'messages' => [ 'label' => 'rainlab.translate::lang.messages.title', 'description' => 'rainlab.translate::lang.messages.description', 'icon' => 'icon-list-alt', 'url' => Backend::url('rainlab/translate/messages'), 'order' => 551, 'category' => 'rainlab.translate::lang.plugin.name', 'permissions' => ['rainlab.translate.manage_messages'], 'keywords' => 'translate', ] ]; } /** * registerMarkupTags for Twig * @return array */ public function registerMarkupTags() { return [ 'filters' => [ '_' => [$this, 'translateString'], '__' => [$this, 'translatePlural'], 'transRaw' => [$this, 'translateRawString'], 'transRawPlural' => [$this, 'translateRawPlural'], 'localeUrl' => [$this, 'localeUrl'], ] ]; } /** * registerFormWidgets for multi-lingual */ public function registerFormWidgets() { $mediaFinderClass = class_exists('System') ? \RainLab\Translate\FormWidgets\MLMediaFinderv2::class : \RainLab\Translate\FormWidgets\MLMediaFinder::class; return [ \RainLab\Translate\FormWidgets\MLText::class => 'mltext', \RainLab\Translate\FormWidgets\MLTextarea::class => 'mltextarea', \RainLab\Translate\FormWidgets\MLRichEditor::class => 'mlricheditor', \RainLab\Translate\FormWidgets\MLMarkdownEditor::class => 'mlmarkdowneditor', \RainLab\Translate\FormWidgets\MLRepeater::class => 'mlrepeater', \RainLab\Translate\FormWidgets\MLNestedForm::class => 'mlnestedform', $mediaFinderClass => 'mlmediafinder', ]; } /** * registerAssetBundles for compilation */ protected function registerAssetBundles() { CombineAssets::registerCallback(function ($combiner) { $combiner->registerBundle('$/rainlab/translate/assets/less/messages.less'); $combiner->registerBundle('$/rainlab/translate/assets/less/multilingual.less'); }); } /** * localeUrl builds a localized URL */ public function localeUrl($url, $locale) { $translator = Translator::instance(); $parts = parse_url($url); $path = array_get($parts, 'path'); return http_build_url($parts, [ 'path' => '/' . $translator->getPathInLocale($path, $locale) ]); } /** * translateString */ public function translateString($string, $params = [], $locale = null) { return Message::trans($string, $params, $locale); } /** * translatePlural */ public function translatePlural($string, $count = 0, $params = [], $locale = null) { return Lang::choice(Message::trans($string, $params, $locale), $count, $params); } /** * translateRawString */ public function translateRawString($string, $params = [], $locale = null) { return Message::transRaw($string, $params, $locale); } /** * translateRawPlural */ public function translateRawPlural($string, $count = 0, $params = [], $locale = null) { return Lang::choice(Message::transRaw($string, $params, $locale), $count, $params); } }