Retrieves the list of locale names as an associative array
public static locale_names ( ) : array | ||
return | array | List of locale names mapped by locale code |
public function fix_fluent_menu() { if (!class_exists('Fluent')) { return; } $conf = SiteConfig::current_site_config(); $localesNames = Fluent::locale_names(); if ($conf->hasExtension('ActiveLocalesExtension') && $conf->ActiveLocales) { $localesNames = $conf->ActiveLocalesNames(); } $locales = json_encode($localesNames); $locale = json_encode(Fluent::current_locale()); // If we have only one locale, set this one as default if (count($localesNames) === 1) { $locale = json_encode(key($localesNames)); } $param = json_encode(Fluent::config()->query_param); $buttonTitle = json_encode(_t('Fluent.ChangeLocale', 'Change Locale')); Requirements::block('FluentHeadScript'); Requirements::insertHeadTags(<<<EOT <script type="text/javascript"> //<![CDATA[ \tvar fluentLocales = {$locales}; \tvar fluentLocale = {$locale}; \tvar fluentParam = {$param}; \tvar fluentButtonTitle = {$buttonTitle}; //]]> </script> EOT , 'FluentHeadScriptSubsite'); }
public function init() { $dirName = basename(dirname(dirname(dirname(__FILE__)))); $locales = json_encode(Fluent::locale_names()); $locale = json_encode(Fluent::current_locale()); $param = json_encode(Fluent::config()->query_param); $buttonTitle = json_encode(_t('Fluent.ChangeLocale', 'Change Locale')); // Force the variables to be written to the head, to ensure these are available for other scripts to pick up. Requirements::insertHeadTags(<<<EOT <script type="text/javascript"> //<![CDATA[ \tvar fluentLocales = {$locales}; \tvar fluentLocale = {$locale}; \tvar fluentParam = {$param}; \tvar fluentButtonTitle = {$buttonTitle}; //]]> </script> EOT , 'FluentHeadScript'); Requirements::javascript("{$dirName}/javascript/fluent.js"); Requirements::css("{$dirName}/css/fluent.css"); }
/** * If only one locale is active * * @return string|boolean The active locale or false */ public function HasOnlyOneLocale() { $list = $this->owner->ActiveLocales; if (!$list) { $list = implode(',', array_keys(Fluent::locale_names())); } $list = trim($list, ','); if ($list && strpos($list, ',') === false) { return $list; } return false; }
public function run($request) { echo 'Run with ?clear=1 to clear empty database before running the task<br/>'; echo 'Run with ?overwrite=soft|hard to overwrite templates that exists in the cms. Soft will replace template if not modified by the user, hard will replace template even if modified by user.<br/>'; echo 'Run with ?templates=xxx,yyy to specify which template should be imported<br/>'; echo 'Run with ?subsite=all|subsiteID to create email templates in all subsites (including main site) or only in the chosen subsite (if a subsite is active, it will be used by default).<br/>'; echo 'Run with ?locales=fr,en to choose which locale to import.<br/>'; echo '<strong>Remember to flush the templates/translations if needed</strong><br/>'; echo '<hr/>'; $overwrite = $request->getVar('overwrite'); $clear = $request->getVar('clear'); $templatesToImport = $request->getVar('templates'); $importToSubsite = $request->getVar('subsite'); $chosenLocales = $request->getVar('locales'); // Normalize argument if ($overwrite && $overwrite != 'soft' && $overwrite != 'hard') { $overwrite = 'soft'; } $subsites = array(); if ($importToSubsite == 'all') { $subsites = Subsite::get()->map(); } else { if (is_numeric($importToSubsite)) { $subsites = array($importToSubsite => Subsite::get()->byID($importToSubsite)->Title); } } if (class_exists('Subsite') && Subsite::currentSubsiteID()) { DB::alteration_message("Importing to current subsite. Run from main site to import other subsites at once.", "created"); $subsites = array(); } if (!empty($subsites)) { DB::alteration_message("Importing to subsites : " . implode(',', array_values($subsites)), "created"); } if ($templatesToImport) { $templatesToImport = explode(',', $templatesToImport); } if ($clear == 1) { DB::alteration_message("Clear all email templates", "created"); $emailTemplates = EmailTemplate::get(); foreach ($emailTemplates as $emailTemplate) { $emailTemplate->delete(); } } $emailTemplateSingl = singleton('EmailTemplate'); $ignoredModules = self::config()->ignored_modules; if (!is_array($ignoredModules)) { $ignoredModules = array(); } $locales = null; if (class_exists('Fluent') && Fluent::locale_names()) { if ($emailTemplateSingl->hasExtension('FluentExtension')) { $locales = array_keys(Fluent::locale_names()); if ($chosenLocales) { $arr = explode(',', $chosenLocales); $locales = array(); foreach ($arr as $a) { if (strlen($a) == 2) { $a = i18n::get_locale_from_lang($a); } $locales[] = $a; } } } } $defaultLocale = i18n::get_locale(); $templates = SS_TemplateLoader::instance()->getManifest()->getTemplates(); foreach ($templates as $t) { $isOverwritten = false; // Emails in mysite/email are not properly marked as emails if (isset($t['mysite']) && isset($t['mysite']['email'])) { $t['email'] = $t['mysite']['email']; } // Should be in the /email folder if (!isset($t['email'])) { continue; } $filePath = $t['email']; $fileName = basename($filePath, '.ss'); // Should end with *Email if (!preg_match('/Email$/', $fileName)) { continue; } $relativeFilePath = str_replace(Director::baseFolder(), '', $filePath); $relativeFilePathParts = explode('/', trim($relativeFilePath, '/')); // Group by module $module = array_shift($relativeFilePathParts); // Ignore some modules if (in_array($module, $ignoredModules)) { continue; } array_shift($relativeFilePathParts); // remove /templates part $templateName = str_replace('.ss', '', implode('/', $relativeFilePathParts)); $templateTitle = basename($templateName); // Create a default code from template name $code = strtolower(preg_replace('/([a-zA-Z])(?=[A-Z])/', '$1-', $fileName)); $code = preg_replace('/-email$/', '', $code); if (!empty($templatesToImport) && !in_array($code, $templatesToImport)) { DB::alteration_message("Template with code <b>{$code}</b> was ignored.", "repaired"); continue; } $whereCode = array('Code' => $code); $emailTemplate = EmailTemplate::get()->filter($whereCode)->first(); // Check if it has been modified or not $templateModified = false; if ($emailTemplate) { $templateModified = $emailTemplate->Created != $emailTemplate->LastEdited; } if (!$overwrite && $emailTemplate) { DB::alteration_message("Template with code <b>{$code}</b> already exists. Choose overwrite if you want to import again.", "repaired"); continue; } if ($overwrite == 'soft' && $templateModified) { DB::alteration_message("Template with code <b>{$code}</b> has been modified by the user. Choose overwrite=hard to change.", "repaired"); continue; } // Create a default title from code $title = explode('-', $code); $title = array_map(function ($item) { return ucfirst($item); }, $title); $title = implode(' ', $title); // Get content of the email $content = file_get_contents($filePath); // Analyze content to find incompatibilities $errors = array(); if (strpos($content, '<% with') !== false) { $errors[] = 'Replace "with" blocks by plain calls to the variable'; } if (strpos($content, '<% if') !== false) { $errors[] = 'If/else logic is not supported. Please create one template by use case or abstract logic into the model'; } if (strpos($content, '<% loop') !== false) { $errors[] = 'Loops are not supported. Please create a helper method on the model to render the loop'; } if (strpos($content, '<% sprintf') !== false) { $errors[] = 'You should not use sprintf to escape content, please use plain _t calls'; } if (!empty($errors)) { echo "<div style='color:red'>Invalid syntax was found in '{$relativeFilePath}'. Please fix these errors before importing the template<ul>"; foreach ($errors as $error) { echo '<li>' . $error . '</li>'; } echo '</ul></div>'; continue; } // Parse language $collector = new i18nTextCollector(); $entities = $collector->collectFromTemplate($content, $fileName, $module); $translationTable = array(); foreach ($entities as $entity => $data) { if ($locales) { foreach ($locales as $locale) { i18n::set_locale($locale); if (!isset($translationTable[$entity])) { $translationTable[$entity] = array(); } $translationTable[$entity][$locale] = i18n::_t($entity); } i18n::set_locale($defaultLocale); } else { $translationTable[$entity] = array($defaultLocale => i18n::_t($entity)); } } $contentLocale = array(); foreach ($locales as $locale) { $contentLocale[$locale] = $content; } foreach ($translationTable as $entity => $translationData) { $escapedEntity = str_replace('.', '\\.', $entity); $baseTranslation = null; foreach ($translationData as $locale => $translation) { if (!$baseTranslation && $translation) { $baseTranslation = $translation; } if (!$translation) { $translation = $baseTranslation; } // This regex should match old and new style $count = 0; $contentLocale[$locale] = preg_replace("/<%(t | _t\\(')" . $escapedEntity . "( |').*?%>/ums", $translation, $contentLocale[$locale], -1, $count); if (!$count) { throw new Exception("Failed to replace {$escapedEntity} with translation {$translation}"); } } } if (!$emailTemplate) { $emailTemplate = new EmailTemplate(); } else { $isOverwritten = true; } // Scan for extra models based on convention preg_match_all('/\\$([a-zA-Z]+)\\./ms', $contentLocale[$defaultLocale], $matches); $extraModels = array(); if (!empty($matches) && !empty($matches[1])) { $arr = array_unique($matches[1]); foreach ($arr as $n) { if (strtolower($n) === 'siteconfig') { continue; } if (class_exists($n)) { $extraModels[$n] = $n; } } } // Apply content to email $this->assignContent($emailTemplate, $contentLocale[$defaultLocale]); if (!empty($locales)) { foreach ($locales as $locale) { $this->assignContent($emailTemplate, $contentLocale[$locale], $locale); } } // Title $emailTemplate->Title = $title; if (!empty($locales)) { // By convention, we store the translation under NameOfTheTemplateEmail.SUBJECT foreach ($locales as $locale) { i18n::set_locale($locale); $localeField = 'Title_' . $locale; $entity = $templateTitle . '.SUBJECT'; $translation = i18n::_t($entity); if (!$translation) { $translation = $title; DB::alteration_message("No title found in {$locale} for {$title}. You should define {$templateTitle}.SUBJECT", "error"); } $emailTemplate->{$localeField} = $translation; if (strpos($translation, '%s') !== false) { echo '<div style="color:red">There is a %s in the title that should be replaced in locale ' . $locale . '!</div>'; } if ($locale == $defaultLocale) { $emailTemplate->Title = $translation; } } i18n::set_locale($defaultLocale); } // Other properties $emailTemplate->Code = $code; $emailTemplate->Category = $module; if (class_exists('Subsite') && Subsite::currentSubsiteID()) { $emailTemplate->SubsiteID = Subsite::currentSubsiteID(); } $emailTemplate->setExtraModelsAsArray($extraModels); // Write to main site or current subsite $emailTemplate->write(); $this->resetLastEditedDate($emailTemplate->ID); // Loop through subsites if (!empty($importToSubsite)) { Subsite::$disable_subsite_filter = true; foreach ($subsites as $subsiteID => $subsiteTitle) { $whereCode['SubsiteID'] = $subsiteID; $subsiteEmailTemplate = EmailTemplate::get()->filter($whereCode)->first(); $emailTemplateCopy = $emailTemplate; $emailTemplateCopy->SubsiteID = $subsiteID; if ($subsiteEmailTemplate) { $emailTemplateCopy->ID = $subsiteEmailTemplate->ID; } else { $emailTemplateCopy->ID = 0; // New } $emailTemplateCopy->write(); $this->resetLastEditedDate($emailTemplateCopy->ID); } } if ($isOverwritten) { DB::alteration_message("Overwrote <b>{$emailTemplate->Code}</b>", "created"); } else { DB::alteration_message("Imported <b>{$emailTemplate->Code}</b>", "created"); } } }
/** * Adds a UI message to indicate whether you're editing in the default locale or not * * @param FieldList $fields * @return $this */ protected function addLocaleIndicatorMessage(FieldList $fields) { if (Fluent::config()->disable_current_locale_message) { return $this; } // If the field is already present, don't add it a second time if ($fields->fieldByName('CurrentLocaleMessage')) { return $this; } $localeNames = Fluent::locale_names(); $isDefaultLocale = Fluent::default_locale() === Fluent::current_locale(); $messageClass = $isDefaultLocale ? 'good' : 'notice'; $message = $isDefaultLocale ? _t('Fluent.DefaultLocale', 'This is the default locale') : _t('Fluent.DefaultLocaleIs', 'The default locale is') . ' ' . $localeNames[Fluent::default_locale()]; $fields->unshift(LiteralField::create('CurrentLocaleMessage', sprintf('<p class="message %s">' . _t('Fluent.EditingIn', 'Please note: You are editing in') . ' %s. %s.</p>', $messageClass, $localeNames[Fluent::current_locale()], $message))); return $this; }
public function run($request) { echo 'Run with ?clear=1 to clear empty database before running the task<br/>'; echo 'Run with ?overwrite=1 to overwrite templates that exists in the cms<br/>'; echo 'Run with ?templates=xxx,yyy to specify which template should be imported<br/>'; echo 'Run with ?subsite=1 to create email templates in all subsites as well. Overwriting is based on main site.<br/>'; echo '<hr/>'; $overwrite = $request->getVar('overwrite'); $clear = $request->getVar('clear'); $templatesToImport = $request->getVar('templates'); $importToSubsite = $request->getVar('subsite'); $subsites = array(); if ($importToSubsite) { $subsites = Subsite::get()->map(); } if ($templatesToImport) { $templatesToImport = explode(',', $templatesToImport); } if ($clear == 1) { echo '<strong>Clear all email templates</strong><br/>'; $emailTemplates = EmailTemplate::get(); foreach ($emailTemplates as $emailTemplate) { $emailTemplate->delete(); } } $o = singleton('EmailTemplate'); $ignoredModules = self::config()->ignored_modules; if (!is_array($ignoredModules)) { $ignoredModules = array(); } $locales = null; if (class_exists('Fluent') && Fluent::locale_names()) { if ($o->hasExtension('FluentExtension')) { $locales = array_keys(Fluent::locale_names()); } } $defaultLocale = i18n::get_locale(); $templates = SS_TemplateLoader::instance()->getManifest()->getTemplates(); foreach ($templates as $t) { $isOverwritten = false; // Emails in mysite/email are not properly marked as emails if (isset($t['mysite']) && isset($t['mysite']['email'])) { $t['email'] = $t['mysite']['email']; } // Should be in the /email folder if (!isset($t['email'])) { continue; } $filePath = $t['email']; $fileName = basename($filePath, '.ss'); // Should end with *Email if (!preg_match('/Email$/', $fileName)) { continue; } $relativeFilePath = str_replace(Director::baseFolder(), '', $filePath); $relativeFilePathParts = explode('/', trim($relativeFilePath, '/')); // Group by module $module = array_shift($relativeFilePathParts); // Ignore some modules if (in_array($module, $ignoredModules)) { continue; } array_shift($relativeFilePathParts); // remove /templates part $templateName = str_replace('.ss', '', implode('/', $relativeFilePathParts)); $templateTitle = basename($templateName); // Create a default code from template name $code = strtolower(preg_replace('/([a-zA-Z])(?=[A-Z])/', '$1-', $fileName)); $code = preg_replace('/-email$/', '', $code); if (!empty($templatesToImport) && !in_array($code, $templatesToImport)) { echo "<div style='color:blue'>Template with code '{$code}' was ignored.</div>"; continue; } $emailTemplate = EmailTemplate::get()->filter('Code', $code)->first(); if (!$overwrite && $emailTemplate) { echo "<div style='color:blue'>Template with code '{$code}' already exists.</div>"; continue; } // Create a default title from code $title = explode('-', $code); $title = array_map(function ($item) { return ucfirst($item); }, $title); $title = implode(' ', $title); // Get content of the email $content = file_get_contents($filePath); // Analyze content to find incompatibilities $errors = array(); if (strpos($content, '<% with') !== false) { $errors[] = 'Replace "with" blocks by plain calls to the variable'; } if (strpos($content, '<% if') !== false) { $errors[] = 'If/else logic is not supported. Please create one template by use case or abstract logic into the model'; } if (strpos($content, '<% loop') !== false) { $errors[] = 'Loops are not supported. Please create a helper method on the model to render the loop'; } if (strpos($content, '<% sprintf') !== false) { $errors[] = 'You should not use sprintf to escape content, please use plain _t calls'; } if (!empty($errors)) { echo "<div style='color:red'>Invalid syntax was found in '{$relativeFilePath}'. Please fix these errors before importing the template<ul>"; foreach ($errors as $error) { echo '<li>' . $error . '</li>'; } echo '</ul></div>'; continue; } // Parse language $collector = new i18nTextCollector(); $entities = $collector->collectFromTemplate($content, $fileName, $module); $translationTable = array(); foreach ($entities as $entity => $data) { if ($locales) { foreach ($locales as $locale) { i18n::set_locale($locale); if (!isset($translationTable[$entity])) { $translationTable[$entity] = array(); } $translationTable[$entity][$locale] = i18n::_t($entity); } i18n::set_locale($defaultLocale); } else { $translationTable[$entity] = array($defaultLocale => i18n::_t($entity)); } } $contentLocale = array(); foreach ($locales as $locale) { $contentLocale[$locale] = $content; } foreach ($translationTable as $entity => $translationData) { $escapedEntity = str_replace('.', '\\.', $entity); $baseTranslation = null; foreach ($translationData as $locale => $translation) { if (!$baseTranslation && $translation) { $baseTranslation = $translation; } if (!$translation) { $translation = $baseTranslation; } // This regex should match old and new style $count = 0; $contentLocale[$locale] = preg_replace("/<%(t | _t\\(')" . $escapedEntity . "( |').*?%>/ums", $translation, $contentLocale[$locale], -1, $count); if (!$count) { throw new Exception("Failed to replace {$escapedEntity} with translation {$translation}"); } } } if (!$emailTemplate) { $emailTemplate = new EmailTemplate(); } else { $isOverwritten = true; } // Scan for extra models based on convention preg_match_all('/\\$([a-zA-Z]+)\\./ms', $contentLocale[$defaultLocale], $matches); $extraModels = array(); if (!empty($matches) && !empty($matches[1])) { $arr = array_unique($matches[1]); foreach ($arr as $n) { if (strtolower($n) === 'siteconfig') { continue; } if (class_exists($n)) { $extraModels[$n] = $n; } } } // Apply content to email $this->assignContent($emailTemplate, $contentLocale[$defaultLocale]); if (!empty($locales)) { foreach ($locales as $locale) { $this->assignContent($emailTemplate, $contentLocale[$locale], $locale); } } // Title $emailTemplate->Title = $title; if (!empty($locales)) { // By convention, we store the translation under NameOfTheTemplateEmail.SUBJECT foreach ($locales as $locale) { i18n::set_locale($locale); $localeField = 'Title_' . $locale; $entity = $templateTitle . '.SUBJECT'; $translation = i18n::_t($entity); if (!$translation) { $translation = $title; } $emailTemplate->{$localeField} = $translation; if (strpos($translation, '%s') !== false) { echo '<div style="color:red">There is a %s in the title that should be replaced in locale ' . $locale . '!</div>'; } if ($locale == $defaultLocale) { $emailTemplate->Title = $translation; } } i18n::set_locale($defaultLocale); } // Other properties $emailTemplate->Code = $code; $emailTemplate->Category = $module; $emailTemplate->setExtraModelsAsArray($extraModels); $emailTemplate->write(); $subsiteImport = ''; // Loop through subsites if ($importToSubsite) { $subsiteImport .= ' => Main site and subsites'; Subsite::$disable_subsite_filter = true; foreach ($subsites as $subsiteID => $subsiteTitle) { $subsiteEmailTemplate = EmailTemplate::get()->filter(array('Code' => $code, 'SubsiteID' => $subsiteID))->first(); $emailTemplateCopy = $emailTemplate; $emailTemplateCopy->SubsiteID = $subsiteID; if ($subsiteEmailTemplate) { $emailTemplateCopy->ID = $subsiteEmailTemplate->ID; } else { $emailTemplateCopy->ID = 0; // New } $emailTemplateCopy->write(); } } if ($isOverwritten) { echo "<div style='color:orange'>Overwrote {$emailTemplate->Code}{$subsiteImport}</div>"; } else { echo "<div style='color:green'>Imported {$emailTemplate->Code}{$subsiteImport}</div>"; } } }
public function collect($restrictToModules = null, $mergeWithExisting = true) { $clearUnused = $this->getClearUnused(); $glob = glob($this->basePath . '/*', GLOB_ONLYDIR); $modules = array_map(function ($item) { return basename($item); }, $glob); $themeFolders = array(); // A master string tables array (one master per module) $entitiesByModule = array(); // Scan themes foreach ($modules as $index => $module) { if ($module != 'themes') { continue; } else { $themes = scandir($this->basePath . "/themes"); if (count($themes)) { foreach ($themes as $theme) { if (is_dir($this->basePath . "/themes/" . $theme) && substr($theme, 0, 1) != '.' && is_dir($this->basePath . "/themes/" . $theme . "/templates")) { $themeFolders[] = 'themes/' . $theme; } } } $themesInd = $index; } } if (isset($themesInd)) { unset($modules[$themesInd]); } $modules = array_merge($modules, $themeFolders); foreach ($modules as $module) { if ($restrictToModules && !in_array($module, $restrictToModules)) { continue; } // Only search for calls in folder with a _config.php file (which means they are modules, including // themes folder) $isValidModuleFolder = is_dir("{$this->basePath}/{$module}") && is_file("{$this->basePath}/{$module}/_config.php") && substr($module, 0, 1) != '.' || substr($module, 0, 7) == 'themes/' && is_dir("{$this->basePath}/{$module}"); if (!$isValidModuleFolder) { continue; } // we store the master string tables $processedEntities = $this->processModule($module); if (isset($entitiesByModule[$module])) { $entitiesByModule[$module] = array_merge_recursive($entitiesByModule[$module], $processedEntities); } else { $entitiesByModule[$module] = $processedEntities; } // extract all entities for "foreign" modules (fourth argument) foreach ($entitiesByModule[$module] as $fullName => $spec) { if (isset($spec[2]) && $spec[2] && $spec[2] != $module) { $othermodule = $spec[2]; if (!isset($entitiesByModule[$othermodule])) { $entitiesByModule[$othermodule] = array(); } unset($spec[2]); $entitiesByModule[$othermodule][$fullName] = $spec; unset($entitiesByModule[$module][$fullName]); } } if ($mergeWithExisting) { $locale = $this->defaultLocale; // If we pass simple lang instead of locale, Zend_Translate will complain if (strlen($locale) == 2) { if (class_exists('Fluent')) { $fluentLocales = Fluent::locale_names(); foreach ($fluentLocales as $fluentLocale => $name) { if (substr($fluentLocale, 0, 2) == $locale) { $locale = $fluentLocale; } } } else { $locale = i18n::get_locale_from_lang($locale); } } $adapter = Injector::inst()->create('i18nRailsYamlAdapter'); $masterFile = "{$this->basePath}/{$module}/lang/" . $adapter->getFilenameForLocale($this->defaultLocale); if (!file_exists($masterFile)) { continue; } $adapter->addTranslation(array('content' => $masterFile, 'locale' => $this->defaultLocale, 'reload' => true, 'clear' => true)); //do not overwrite by interverting $messages = array_map(function ($v) { return array($v); }, $adapter->getMessages($this->defaultLocale)); $entitiesByModule[$module] = array_merge($entitiesByModule[$module], $messages); //clear by diffing if ($clearUnused) { $unusedEntities = array_diff(array_keys($messages), array_keys($processedEntities)); foreach ($unusedEntities as $unusedEntity) { if (strpos($unusedEntity, '.db_') !== false) { continue; } if (strpos($unusedEntity, '.has_one_') !== false) { continue; } if (strpos($unusedEntity, '.has_many_') !== false) { continue; } if (strpos($unusedEntity, '.many_many') !== false) { continue; } if (strpos($unusedEntity, '.belongs_many_many') !== false) { continue; } echo '<div style="color:red">Removed translations for ' . $unusedEntity . '</div>'; unset($entitiesByModule[$module][$unusedEntity]); } } } } // Restrict modules we update to just the specified ones (if any passed) if ($restrictToModules && count($restrictToModules)) { foreach (array_diff(array_keys($entitiesByModule), $restrictToModules) as $module) { unset($entitiesByModule[$module]); } } return $entitiesByModule; }