Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix email not translated when installing a new language #16294

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
53 changes: 9 additions & 44 deletions classes/Context.php
Expand Up @@ -25,12 +25,11 @@
*/
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
use PrestaShop\PrestaShop\Core\Localization\Locale;
use PrestaShopBundle\Translation\Loader\SqlTranslationLoader;
use PrestaShopBundle\Translation\TranslatorComponent as Translator;
use PrestaShopBundle\Translation\TranslatorLanguageLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Translation\Loader\XliffFileLoader;

/**
* Class ContextCore.
Expand Down Expand Up @@ -405,7 +404,7 @@ public function getTranslatorFromLocale($locale)

// In case we have at least 1 translated message, we return the current translator.
// If some translations are missing, clear cache
if ($locale === '' || count($translator->getCatalogue($locale)->all())) {
if ($locale === '' || null === $locale || count($translator->getCatalogue($locale)->all())) {
return $translator;
}

Expand All @@ -420,49 +419,15 @@ public function getTranslatorFromLocale($locale)
(new Filesystem())->remove($cache_file);
}

$adminContext = defined('_PS_ADMIN_DIR_');
$translator->addLoader('xlf', new XliffFileLoader());

$sqlTranslationLoader = new SqlTranslationLoader();
if (null !== $this->shop) {
$sqlTranslationLoader->setTheme($this->shop->theme);
}

$translator->addLoader('db', $sqlTranslationLoader);
$notName = $adminContext ? '^Shop*' : '^Admin*';

$finder = Finder::create()
->files()
->name('*.' . $locale . '.xlf')
->notName($notName)
->in($this->getTranslationResourcesDirectories());
$translator->clearLanguage($locale);

foreach ($finder as $file) {
list($domain, $locale, $format) = explode('.', $file->getBasename(), 3);

$translator->addResource($format, $file, $locale, $domain);
if (!$this->language instanceof PrestashopBundle\Install\Language) {
$translator->addResource('db', $domain . '.' . $locale . '.db', $locale, $domain);
}
}
$adminContext = defined('_PS_ADMIN_DIR_');
// Do not load DB translations when $this->language is PrestashopBundle\Install\Language
// because it means that we're looking for the installer translations, so we're not yet connected to the DB
$withDB = !$this->language instanceof PrestashopBundle\Install\Language;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this check? In which case don't you want the database to be loaded? Maybe it's worth a comment here

Besides what other type can $this->language be? \Language? So it means you only disable the db loading during install process? Why?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to load DB translations when $this->language in an instance of PrestashopBundle\Install\Language because that's only translations for the web installer and when we load the installer language we didn't configured the DB yet...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oooookay, this makes sense but maybe you can add a comment explaining that, because it's not that intuitive at first ^^

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🆙

$theme = $this->shop !== null ? $this->shop->theme : null;
(new TranslatorLanguageLoader($adminContext))->loadLanguage($translator, $locale, $withDB, $theme);

return $translator;
}

/**
* @return array
*/
protected function getTranslationResourcesDirectories()
{
$locations = array(_PS_ROOT_DIR_ . '/app/Resources/translations');

if (null !== $this->shop) {
$activeThemeLocation = _PS_ROOT_DIR_ . '/themes/' . $this->shop->theme_name . '/translations';
if (is_dir($activeThemeLocation)) {
$locations[] = $activeThemeLocation;
}
}

return $locations;
}
}
9 changes: 7 additions & 2 deletions classes/Language.php
Expand Up @@ -29,6 +29,7 @@
use PrestaShop\PrestaShop\Core\Exception\CoreException;
use PrestaShop\PrestaShop\Core\Domain\MailTemplate\Command\GenerateThemeMailTemplatesCommand;
use PrestaShop\PrestaShop\Core\CommandBus\CommandBusInterface;
use PrestaShopBundle\Translation\TranslatorLanguageLoader;

class LanguageCore extends ObjectModel
{
Expand Down Expand Up @@ -1391,7 +1392,11 @@ private static function updateMultilangFromClassForShop($tableName, $classObject
{
$shopDefaultLangId = Configuration::get('PS_LANG_DEFAULT', null, $shop->id_shop_group, $shop->id);
$shopDefaultLanguage = new Language($shopDefaultLangId);
$translatorDefaultShopLanguage = Context::getContext()->getTranslatorFromLocale($shopDefaultLanguage->locale);

$translator = SymfonyContainer::getInstance()->get('translator');
if (!$translator->isLanguageLoaded($shopDefaultLanguage->locale)) {
(new TranslatorLanguageLoader(true))->loadLanguage($translator, $shopDefaultLanguage->locale);
}

$shopFieldExists = $primary_key_exists = false;
$columns = Db::getInstance()->executeS('SHOW COLUMNS FROM `' . $tableName . '`');
Expand Down Expand Up @@ -1430,7 +1435,7 @@ private static function updateMultilangFromClassForShop($tableName, $classObject
continue;
}

$untranslated = $translatorDefaultShopLanguage->getSourceString($data[$toUpdate], $classObject->getDomain());
$untranslated = $translator->getSourceString($data[$toUpdate], $classObject->getDomain());
$translatedField = $classObject->getFieldValue($toUpdate, $untranslated);

if (!empty($translatedField) && $translatedField != $data[$toUpdate]) {
Expand Down
30 changes: 7 additions & 23 deletions classes/lang/DataLang.php
Expand Up @@ -23,7 +23,9 @@
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
use PrestaShopBundle\Translation\TranslatorComponent as Translator;
use PrestaShopBundle\Translation\TranslatorLanguageLoader;

class DataLangCore
{
Expand All @@ -46,30 +48,12 @@ public function __construct($locale)
{
$this->locale = $locale;

$legacyTranslator = Context::getContext()->getTranslator();
$legacyLocale = $legacyTranslator->getLocale();
$this->translator = SymfonyContainer::getInstance()->get('translator');
$isAdminContext = defined('_PS_ADMIN_DIR_');

if ($legacyLocale === $this->locale) {
$this->translator = $legacyTranslator;
} else {
$this->translator = new Translator(
$this->locale,
null,
_PS_CACHE_DIR_ . '/translations/' . $this->locale,
false
);

$this->translator->addLoader('xlf', new \Symfony\Component\Translation\Loader\XliffFileLoader());

$finder = \Symfony\Component\Finder\Finder::create()
->files()
->name('*.' . $this->locale . '.xlf')
->in((_PS_ROOT_DIR_ . '/app/Resources/translations'));

foreach ($finder as $file) {
list($domain, $locale, $format) = explode('.', $file->getBasename(), 3);
$this->translator->addResource($format, $file, $locale, $domain);
}
if (!$this->translator->isLanguageLoaded($this->locale)) {
(new TranslatorLanguageLoader($isAdminContext))->loadLanguage($this->translator, $this->locale);
$this->translator->getCatalogue($this->locale);
}
}

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Expand Up @@ -157,9 +157,9 @@
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache"
],
"test-all": [
"composer phpunit-admin",
"composer phpunit-legacy",
"composer unit-tests",
"composer phpunit-admin",
"composer phpunit-sf",
"composer integration-tests",
"composer phpunit-controllers",
Expand Down
7 changes: 4 additions & 3 deletions install-dev/controllers/console/process.php
Expand Up @@ -120,16 +120,17 @@ public function process()
if (!$this->processInstallDatabase()) {
$this->printErrors();
}

// Deferred Kernel Init
$this->initKernel();

if (!$this->processInstallDefaultData()) {
$this->printErrors();
}
if (!$this->processPopulateDatabase()) {
$this->printErrors();
}

// Deferred Kernel Init
$this->initKernel();

if (!$this->processConfigureShop()) {
$this->printErrors();
}
Expand Down
Expand Up @@ -33,7 +33,6 @@
use PrestaShop\PrestaShop\Core\MailTemplate\MailTemplateGenerator;
use PrestaShop\PrestaShop\Core\MailTemplate\ThemeCatalogInterface;
use PrestaShop\PrestaShop\Core\MailTemplate\ThemeInterface;
use Symfony\Component\Translation\Loader\ArrayLoader;
use Symfony\Component\Translation\TranslatorInterface;

/**
Expand Down Expand Up @@ -99,34 +98,9 @@ public function handle(GenerateThemeMailTemplatesCommand $command)
/** @var ThemeInterface $theme */
$theme = $this->themeCatalog->getByName($command->getThemeName());

$this->cleanTranslatorLocaleCache($command->getLanguage());

$coreMailsFolder = $command->getCoreMailsFolder() ?: $this->defaultCoreMailsFolder;
$modulesMailFolder = $command->getModulesMailFolder() ?: $this->defaultModulesMailFolder;

$this->generator->generateTemplates($theme, $language, $coreMailsFolder, $modulesMailFolder, $command->overwriteTemplates());
}

/**
* When installing a new Language, if it's a new one the Translator component can't manage it because its cache is
* already filled with the default one as fallback. We force the component to update its cache by adding a fake
* resource for this locale (this is the only way clean its local cache)
*
* @param string $locale
*/
private function cleanTranslatorLocaleCache($locale)
{
if (!method_exists($this->translator, 'addLoader')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be careful, I remember this check was here for a reason because sometimes the injected translator was not PrestaShopBundle\Translation\Translator but PrestaShopBundle\Translation\DataCollectorTranslator which doesn't have the method addRessource nor addLoader
Although since there have been modifications on the translator (we had 2 different components) by @matthieu-rolland maybe this fixed the issue

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fixed but it should be tested.

FYI you get DataCollectorTranslator when you're in dev mode (it tracks translation hits for the Symfony profiler)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, actually if I check if those methods exists it will return false because I got a DataCollectorTranslator but as DataCollectorTranslator is wrapping the Translator class, those methods are still available via the magic function __call that call the translator functions and make it works.

|| !method_exists($this->translator, 'addResource')
) {
return;
}

$this->translator->addLoader('array', new ArrayLoader());
$this->translator->addResource(
'array',
['Fake clean cache message' => 'Fake clean cache message'],
$locale
);
}
}
1 change: 1 addition & 0 deletions src/PrestaShopBundle/Translation/Translator.php
Expand Up @@ -35,6 +35,7 @@
class Translator extends BaseTranslator
{
use PrestaShopTranslatorTrait;
use TranslatorLanguageTrait;

/**
* {@inheritdoc}
Expand Down
1 change: 1 addition & 0 deletions src/PrestaShopBundle/Translation/TranslatorComponent.php
Expand Up @@ -35,4 +35,5 @@
class TranslatorComponent extends BaseTranslatorComponent
{
use PrestaShopTranslatorTrait;
use TranslatorLanguageTrait;
}
109 changes: 109 additions & 0 deletions src/PrestaShopBundle/Translation/TranslatorLanguageLoader.php
@@ -0,0 +1,109 @@
<?php

/**
* 2007-2019 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/

namespace PrestaShopBundle\Translation;

use PrestaShop\PrestaShop\Core\Addon\Theme\Theme;
use PrestaShopBundle\Translation\Loader\SqlTranslationLoader;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Translation\Loader\XliffFileLoader;
use Symfony\Component\Translation\TranslatorInterface;

class TranslatorLanguageLoader
{
const TRANSLATION_DIR = _PS_ROOT_DIR_ . '/app/Resources/translations';
/**
* @var bool
*/
private $isAdminContext;

/**
* TranslatorLanguageLoader constructor.
*
* @param $isAdminContext
*/
public function __construct($isAdminContext)
{
$this->isAdminContext = $isAdminContext;
}

/**
* Loads a language into a translator
*
* @param TranslatorInterface $translator Translator to modifiy
* @param string $locale Locale code for the language to load
* @param bool $withDB [default=true] Whether to load translations from the database or not
* @param Theme|null $theme [default=false] Currently active theme (Front office only)
*/
public function loadLanguage(TranslatorInterface $translator, $locale, $withDB = true, Theme $theme = null)
{
if (!$translator->isLanguageLoaded($locale)) {
$translator->addLoader('xlf', new XliffFileLoader());
eternoendless marked this conversation as resolved.
Show resolved Hide resolved

if ($withDB) {
$sqlTranslationLoader = new SqlTranslationLoader();
if (null !== $theme) {
$sqlTranslationLoader->setTheme($theme);
}
$translator->addLoader('db', $sqlTranslationLoader);
}

$finder = Finder::create()
->files()
->name('*.' . $locale . '.xlf')
->notName($this->isAdminContext ? '^Shop*' : '^Admin*')
->in($this->getTranslationResourcesDirectories($theme));

foreach ($finder as $file) {
list($domain, $locale, $format) = explode('.', $file->getBasename(), 3);
$translator->addResource($format, $file, $locale, $domain);
if ($withDB) {
$translator->addResource('db', $domain . '.' . $locale . '.db', $locale, $domain);
}
}
}
}

/**
* @param Theme|null $theme
*
* @return array
*/
protected function getTranslationResourcesDirectories(Theme $theme = null)
{
$locations = [self::TRANSLATION_DIR];

if (null !== $theme) {
$activeThemeLocation = $theme->getDirectory() . '/translations';
if (is_dir($activeThemeLocation)) {
$locations[] = $activeThemeLocation;
}
}

return $locations;
}
}