Implemented domains in translations. Fix #116 and #177

This commit is contained in:
Franck Allimant
2014-04-20 16:31:06 +02:00
parent 9b29d510f2
commit a135a68e6c
23 changed files with 1262 additions and 938 deletions

View File

@@ -12,6 +12,8 @@
namespace Thelia\Command;
use Thelia\Model\Module;
/**
* base class for module commands
*
@@ -36,7 +38,7 @@ abstract class BaseModuleGenerate extends ContainerAwareCommand
'Controller',
'EventListeners',
'I18n',
'AdminIncludes',
Module::ADMIN_INCLUDES_DIRECTORY_NAME,
'templates',
);

View File

@@ -23,11 +23,11 @@ return array(
'Alpha code 3 *' => 'Code Alpha 3 *',
'Amount removed from the cart' => 'Montant déduit du panier',
'Apply exchange rates on price in %sym' => 'Appliquer le taux de change sur le prix en %sym',
'Area' => 'Zone',
'Attribute ID:Attribute AV ID' => 'Déclinaison ID : Valeur de déclinaison ID',
'Auth mode' => 'Mode d\'authentification',
'Available quantity' => 'Quantité disponible',
'Available quantity *' => 'Quantité disponible *',
'Available shipping zones' => 'Zones de livraison disponibles',
'Bad tax list JSON' => 'Mauvais JSON de la liste des taxes',
'Business ID' => 'ID du business',
'Cannot find a default country. Please define one.' => 'Impossible de trouver un pays par défaut. Veuillez en définir un.',
@@ -64,6 +64,7 @@ return array(
'Emergency' => 'Urgence',
'Enable remote SMTP use' => 'Activer l\'utilisation d\'un serveur SMTP distant.',
'Encryption' => 'Chiffrement',
'Error occured while processing order ref. %ref, ID %id: %err' => 'Un erreur est survenue paedant le traitement de la commande ref. %ref, ID %id; %err',
'Errors' => 'Erreurs',
'Failed to update language definition: %ex' => 'Erreur lors de la mise à jour de la définition de la langue : %ex',
'Fax' => 'Fax',
@@ -91,6 +92,8 @@ return array(
'Log level *' => 'Niveau de log *',
'Login' => 'Connexion',
'Login failed. Please check your username and password.' => 'Erreur de login. Veuillez vérifier votre login et votre mot de passe.',
'Loop must implements \'PropelSearchLoopInterface\' to be timestampable' => 'Les classes \'boucle\' doivent implémenter \'PropelSearchLoopInterface\' pour être horodatables',
'Loop must implements \'PropelSearchLoopInterface\' to be versionable' => 'Les classes \'boucle\' doivent implémenter \'PropelSearchLoopInterface\' pour être versonnables',
'Make this address as my primary address' => 'Choisir cette adresse comme adresse par défaut',
'Message subject' => 'Sujet',
'Meta Description' => 'Meta description',
@@ -105,9 +108,13 @@ return array(
'New Password' => 'Nouveau mot de passe.',
'No %obj was created.' => 'aucun %obj n\'a été créé.',
'No %obj was updated.' => 'Aucun %obj mis à jour',
'No module found for code \'%item\'' => 'Aucun module trouvé pour \'%item\' ',
'No, I am a new customer.' => 'Non, je suis un nouveau client.',
'Not found' => 'Non trouvé.',
'Notices' => 'Notices',
'Order address ID not found' => 'ID de l\'adresse de la commande non trouvé',
'Order ref. %ref is now unpaid.' => 'La commande %ref, ID %id est désormais non payée',
'Order ref. %ref, ID %id has been successfully paid.' => 'La commande ref. %ref, ID %id a été correctement payée',
'Page Title' => 'Titre de la page',
'Parent category *' => 'Catégorie parente *',
'Parent folder *' => 'Dossier parent *',
@@ -129,6 +136,8 @@ return array(
'Prevent variable modification or deletion, except for super-admin' => 'Prévenir la suppression ou la modification de variables, excepté pour les super-administrateurs.',
'Price' => 'Prix',
'Price currency *' => 'Devise *',
'Processing cancelation of payment for order ref. %ref' => 'Traitement de l\'annulation du paiement de la commande %ref, ID %id',
'Processing confirmation of order ref. %ref, ID %id' => 'Traitement de la confirmation de la commande %ref, ID %id',
'Prodcut ID *' => 'ID du produit *',
'Product ID' => 'ID produit',
'Product ID *' => 'ID produit *',
@@ -147,6 +156,7 @@ return array(
'Profile ID not found' => 'ID du profil non trouvé',
'Profile `code` already exists' => 'Le `code` du profil existe déjà',
'Purpose *' => 'Objet',
'Quantity' => 'Quantité',
'Rate from € *' => 'Taux à partie de l\'€ *',
'Redirecting ...' => 'Redirection ...',
'Redirecting to %url' => 'Redirection vers %url',
@@ -162,11 +172,13 @@ return array(
'Rotated Text File' => 'Rotation du fichier texte',
'Sale price excluding taxes' => 'Prix de vente Hors Taxes',
'Sale price including taxes' => 'Prix de vente Toutes Taxes Comprises',
'Shipping zone name' => 'Nom de la zone de livraison',
'Show redirections *' => 'Montrer les redirections *',
'Sorry, an error occured: %msg' => 'Désolé, une erreur est survenue : %msg',
'Sorry, you are not allowed to perform this action.' => 'Désolé, vous n\'êtes pas autorisé à réaliser cette action.',
'Sorry, you\'re not allowed to perform this action' => 'Désolé, vous n\'êtes pas autorisé à réaliser cette action.',
'Source IP' => 'IP source',
'Stats on %month/%year' => 'Statisticues pour %month/%year ',
'Store configuration failed.' => 'Erreur de configuration du magasin.',
'Store email address' => 'Adresse mail du magasin',
'Store logs into text file' => 'Conserver les logs dans des fichiers texte',
@@ -205,13 +217,18 @@ return array(
'Title *' => 'Titre *',
'Title ID not found' => 'ID de la civilité non trouvé',
'Type' => 'Type',
'Unavailable' => 'Non disponible.',
'Undefined loop argument "%name"' => 'Argument de boucle invalide: "%name" ',
'Undefined search mode \'%mode\'' => 'Mode de recherche \'%mode\' invalide',
'Undefined translation type: %item' => 'Type de traduction inconnu:%item ',
'Unknown order ID: %id' => 'La commande ID %id est inconnue.',
'Unsupported magic method %name. only getArgname() is supported.' => 'La méthode magique %name n\'est pas supportée. Seule get<argname>() est supporté..',
'Username' => 'Nom d\'utilisateur',
'Username *' => 'Nom d\'utilisateur *',
'Value' => 'Valeur',
'Value *' => 'Valeur *',
'Warnings' => 'Avertissements',
'Weight' => 'Poids',
'Weight *' => 'Poids *',
'Yes, I have a password :' => 'Oui, j\'ai un mot de passe :',
'You are already registered!' => 'Vous êtes déjà enregistré !',
'Your Email Address' => 'Votre adresse mail',
@@ -226,7 +243,6 @@ return array(
'password must be composed of at least 4 characters' => 'le mot de passe doit être composé d\'au moins 4 caractères',
'payment module %s is not a Thelia\Module\PaymentModuleInterface' => 'Le module de paiement %s n\'est pas une instance de Thelia\Module\PaymentModuleInterface ',
'quantity value is not valid' => 'la valeur de la quantité n\'est pas valide',
'shipping area name' => 'Nom de la zone de livraison',
'this product id does not exists : %d' => 'l\'id du produit %d n\'existe pas',
'time format' => 'Format d\'heure',
);

View File

@@ -14,6 +14,7 @@ namespace Thelia\Controller\Admin;
use Thelia\Core\Security\Resource\AdminResources;
use Thelia\Core\Security\AccessManager;
use Thelia\Model\Module;
use Thelia\Model\ModuleQuery;
use Thelia\Core\Template\TemplateHelper;
use Thelia\Core\Template\TemplateDefinition;
@@ -25,13 +26,45 @@ use Thelia\Tools\URL;
*/
class TranslationsController extends BaseAdminController
{
/**
* @param string $item_name the modume code
* @return Module the module object
* @throws \InvalidArgumentException if module was not found
*/
protected function getModule($item_name) {
if (null !== $module = ModuleQuery::create()->findPk($item_name))
return $module;
throw new \InvalidArgumentException(
$this->getTranslator()->trans("No module found for code '%item'", ['%item' => $item_name])
);
}
protected function getModuleTemplateNames(Module $module, $template_type) {
$templates =
TemplateHelper::getInstance()->getList(
$template_type,
$module->getAbsoluteTemplateBasePath()
);
$names = [];
foreach($templates as $template) $names[] = $template->getName();
return $names;
}
protected function renderTemplate()
{
// Get related strings, if all input data are here
$item_to_translate = $this->getRequest()->get('item_to_translate');
$item_id = $this->getRequest()->get('item_id', '');
$item_name = $this->getRequest()->get('item_name', '');
if ($item_to_translate == 'mo' && ! empty($item_name))
$module_part = $this->getRequest()->get('module_part', '');
else
$module_part = false;
$all_strings = array();
@@ -41,51 +74,106 @@ class TranslationsController extends BaseAdminController
$templateArguments = array(
'item_to_translate' => $item_to_translate,
'item_id' => $item_id,
'item_name' => $item_name,
'module_part' => $module_part,
'view_missing_traductions_only' => $this->getRequest()->get('view_missing_traductions_only', 0),
'max_input_vars_warning' => false,
);
// Find the i18n directory, and the directory to examine.
if (! empty($item_id) || $item_to_translate == 'co') {
if (! empty($item_name) || $item_to_translate == 'co') {
switch ($item_to_translate) {
// Module core
case 'mo' :
if (null !== $module = ModuleQuery::create()->findPk($item_id)) {
$module = $this->getModule($item_name);
if ($module_part == 'core') {
$directory = $module->getAbsoluteBaseDir();
$domain = $module->getTranslationDomain();
$i18n_directory = $module->getAbsoluteI18nPath();
$walkMode = TemplateHelper::WALK_MODE_PHP;
}
else if ($module_part == 'admin-includes') {
$directory = $module->getAbsoluteAdminIncludesPath();
$domain = $module->getAdminIncludesTranslationDomain();
$i18n_directory = $module->getAbsoluteAdminIncludesI18nPath();
$walkMode = TemplateHelper::WALK_MODE_TEMPLATE;
}
else if (! empty($module_part)) {
// Front or back office template, form of $module_part is [bo|fo].subdir-name
list($type, $subdir) = explode('.', $module_part);
if ($type == 'bo') {
$directory = $module->getAbsoluteBackOfficeTemplatePath($subdir);
$domain = $module->getBackOfficeTemplateTranslationDomain($subdir);
$i18n_directory = $module->getAbsoluteBackOfficeI18nTemplatePath($subdir);
}
else if ($type == 'fo') {
$directory = $module->getAbsoluteFrontOfficeTemplatePath($subdir);
$domain = $module->getFrontOfficeTemplateTranslationDomain($subdir);
$i18n_directory = $module->getAbsoluteFrontOfficeI18nTemplatePath($subdir);
}
else {
throw new \InvalidArgumentException("Undefined module template type: '$type'.");
}
$walkMode = TemplateHelper::WALK_MODE_TEMPLATE;
}
// List front and back office templates defined by this module
$templateArguments['back_office_templates'] =
$this->getModuleTemplateNames($module, TemplateDefinition::BACK_OFFICE);
$templateArguments['front_office_templates'] =
$this->getModuleTemplateNames($module, TemplateDefinition::FRONT_OFFICE);
break;
// Thelia Core
case 'co' :
$directory = THELIA_ROOT . 'core/lib/Thelia';
$domain = 'core';
$i18n_directory = THELIA_ROOT . 'core/lib/Thelia/Config/I18n';
$walkMode = TemplateHelper::WALK_MODE_PHP;
break;
// Front-office template
case 'fo' :
$template = new TemplateDefinition($item_id, TemplateDefinition::FRONT_OFFICE);
$template = new TemplateDefinition($item_name, TemplateDefinition::FRONT_OFFICE);
break;
// Back-office template
case 'bo' :
$template = new TemplateDefinition($item_id, TemplateDefinition::BACK_OFFICE);
$template = new TemplateDefinition($item_name, TemplateDefinition::BACK_OFFICE);
break;
// PDF templates
case 'pf' :
$template = new TemplateDefinition($item_id, TemplateDefinition::PDF);
$template = new TemplateDefinition($item_name, TemplateDefinition::PDF);
break;
// Email templates
case 'ma' :
$template = new TemplateDefinition($item_id, TemplateDefinition::EMAIL);
$template = new TemplateDefinition($item_name, TemplateDefinition::EMAIL);
break;
default:
/*
throw new \InvalidArgumentException(
$this->getTranslator()->trans("Undefined translation type: %item", ['%item' => $item_to_translate])
);
*/
}
if ($template) {
$directory = $template->getAbsolutePath();
$i18n_directory = $template->getAbsoluteI18nPath();
$domain = $template->getTranslationDomain();
}
// Load strings to translate
@@ -102,11 +190,11 @@ class TranslationsController extends BaseAdminController
if (! empty($texts)) {
$file = sprintf("%s/%s.php", $i18n_directory, $this->getCurrentEditionLocale());
$file = sprintf("%s".DS."%s.php", $i18n_directory, $this->getCurrentEditionLocale());
$translations = $this->getRequest()->get('translation', array());
TemplateHelper::getInstance()->writeTranslation($file, $texts, $translations);
TemplateHelper::getInstance()->writeTranslation($file, $texts, $translations, true);
if ($save_mode == 'stay')
$this->redirectToRoute("admin.configuration.translations", $templateArguments);
@@ -122,6 +210,7 @@ class TranslationsController extends BaseAdminController
$walkMode,
$this->getTranslator(),
$this->getCurrentEditionLocale(),
$domain,
$all_strings
);

View File

@@ -166,6 +166,7 @@ class Module extends BaseI18nLoop implements PropelSearchLoopInterface
public function parseResults(LoopResult $loopResult)
{
/** @var \Thelia\Model\Module $module */
foreach ($loopResult->getResultDataCollection() as $module) {
$loopResultRow = new LoopResultRow($module);
$loopResultRow->set("ID", $module->getId())
@@ -199,13 +200,13 @@ class Module extends BaseI18nLoop implements PropelSearchLoopInterface
/* if not ; test if it uses admin inclusion : module_configuration.html */
if (false === $hasConfigurationInterface) {
if (file_exists( sprintf("%s/AdminIncludes/%s.html", $module->getAbsoluteBaseDir(), "module_configuration"))) {
if (file_exists($module->getAbsoluteAdminIncludesPath() . DS . "module_configuration.html")) {
$hasConfigurationInterface = true;
}
}
} else {
// Make a quick and dirty test on the module's routing.xml file
$routing = @file_get_contents($module->getAbsoluteBaseDir() . DS . "Config" . DS . "routing.xml");
$routing = @file_get_contents($module->getAbsoluteConfigPath() . DS . "routing.xml");
if ($routing && strpos($routing, '/admin/module/') !== false) {
$hasConfigurationInterface = true;

View File

@@ -80,8 +80,8 @@ class AdminUtilities extends AbstractSmartyPlugin
<a href="{url path='/admin/configuration/currencies/positionDown' currency_id=$ID}"><i class="icon-arrow-down"></i></a>
*/
if ($permissions == null || $this->securityContext->isGranted(
"ADMIN",
if ($this->securityContext->isGranted(
array("ADMIN"),
$resource === null ? array() : array($resource),
$module === null ? array() : array($module),
array($access))

View File

@@ -63,13 +63,14 @@ class Module extends AbstractSmartyPlugin
$modules = ModuleQuery::getActivated();
/** @var \Thelia\Model\Module $module */
foreach ($modules as $module) {
if (null !== $moduleLimit && $moduleLimit != $module->getCode()) {
continue;
}
$file = sprintf("%s/AdminIncludes/%s.html", $module->getAbsoluteBaseDir(), $location);
$file = $module->getAbsoluteAdminIncludesPath() . DS . $location . '.html';
if (file_exists($file)) {

View File

@@ -19,41 +19,59 @@ use Symfony\Component\Translation\TranslatorInterface;
class Translation extends AbstractSmartyPlugin
{
protected $translator;
protected $defaultTranslationDomain = '';
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
/**
* Set the default translation domain
*
* @param array $params
* @param \Smarty_Internal_Template $smarty
* @return string
*/
public function setDefaultTranslationDomain($params, &$smarty)
{
$this->defaultTranslationDomain = $this->getParam($params, 'domain');
}
/**
* Process translate function
*
* @param unknown $params
* @param unknown $smarty
* @param array $params
* @param \Smarty_Internal_Template $smarty
* @return string
*/
public function translate($params, &$smarty)
{
// All parameters other than 'l' are supposed to be variables. Build an array of var => value pairs
// All parameters other than 'l' and 'd' are supposed to be variables. Build an array of var => value pairs
// and pass it to the translator
$vars = array();
foreach ($params as $name => $value) {
if ($name != 'l') $vars["%$name"] = $value;
if ($name != 'l' && $name != 'd') $vars["%$name"] = $value;
}
return $this->translator->trans($this->getParam($params, 'l'), $vars);
return $this->translator->trans(
$this->getParam($params, 'l'),
$vars,
$this->getParam($params, 'd', $this->defaultTranslationDomain)
);
}
/**
* Define the various smarty plugins hendled by this class
* Define the various smarty plugins handled by this class
*
* @return an array of smarty plugin descriptors
* @return SmartyPluginDescriptor[] an array of smarty plugin descriptors
*/
public function getPluginDescriptors()
{
return array(
new SmartyPluginDescriptor('function', 'intl', $this, 'translate'),
new SmartyPluginDescriptor('function', 'default_translation_domain', $this, 'setDefaultTranslationDomain'),
);
}
}

View File

@@ -46,6 +46,8 @@ class TemplateDefinition
*/
protected $type;
protected $translationDomainPrefix;
public function __construct($name, $type)
{
$this->name = $name;
@@ -54,22 +56,31 @@ class TemplateDefinition
switch ($type) {
case TemplateDefinition::FRONT_OFFICE:
$this->path = self::FRONT_OFFICE_SUBDIR . DS . $name;
$this->translationDomainPrefix = 'fo.';
break;
case TemplateDefinition::BACK_OFFICE:
$this->path = self::BACK_OFFICE_SUBDIR . DS . $name;
$this->translationDomainPrefix = 'bo.';
break;
case TemplateDefinition::PDF:
$this->path = self::PDF_SUBDIR . DS . $name;
$this->translationDomainPrefix = 'pdf.';
break;
case TemplateDefinition::EMAIL:
$this->path = self::EMAIL_SUBDIR . DS . $name;
$this->translationDomainPrefix = 'email.';
break;
default:
$this->path = $name;
$this->translationDomainPrefix = 'generic.';
break;
}
}
public function getTranslationDomain() {
return $this->translationDomainPrefix . strtolower($this->getName());
}
public function getName()
{
return $this->name;

View File

@@ -12,6 +12,7 @@
namespace Thelia\Core\Template;
use Symfony\Component\Filesystem\Filesystem;
use Thelia\Model\ConfigQuery;
use Thelia\Log\Tlog;
use Thelia\Core\Translation\Translator;
@@ -96,9 +97,10 @@ class TemplateHelper
* Return a list of existing templates for a given template type
*
* @param int $templateType the template type
* @return An array of \Thelia\Core\Template\TemplateDefinition
* @param string the template base (module or core, default to core).
* @return TemplateDefinition[] of \Thelia\Core\Template\TemplateDefinition
*/
public function getList($templateType)
public function getList($templateType, $base = THELIA_TEMPLATE_DIR)
{
$list = $exclude = array();
@@ -108,8 +110,9 @@ class TemplateHelper
if ($templateType == $type) {
$baseDir = THELIA_TEMPLATE_DIR.$subdir;
$baseDir = rtrim($base, DS).DS.$subdir;
try {
// Every subdir of the basedir is supposed to be a template.
$di = new \DirectoryIterator($baseDir);
@@ -122,11 +125,15 @@ class TemplateHelper
$list[] = new TemplateDefinition($file->getFilename(), $templateType);
}
}
catch (\UnexpectedValueException $ex) {
// Ignore the exception and continue
}
}
}
return $list;
}
}
}
protected function normalizePath($path)
{
@@ -152,11 +159,12 @@ class TemplateHelper
* @param string $walkMode type of file scanning: WALK_MODE_PHP or WALK_MODE_TEMPLATE
* @param \Thelia\Core\Translation\Translator $translator the current translator
* @param string $currentLocale the current locale
* @param string $domain the translation domain (fontoffice, backoffice, module, etc...)
* @param array $strings the list of strings
* @throws \InvalidArgumentException if $walkMode contains an invalid value
* @return number the total number of translatable texts
*/
public function walkDir($directory, $walkMode, Translator $translator, $currentLocale, &$strings)
public function walkDir($directory, $walkMode, Translator $translator, $currentLocale, $domain, &$strings)
{
$num_texts = 0;
@@ -165,7 +173,7 @@ class TemplateHelper
$allowed_exts = array('php');
} elseif ($walkMode == self::WALK_MODE_TEMPLATE) {
$prefix = '\{intl[\s]l=';
$prefix = "\\{intl(?:.*?)l=";
$allowed_exts = array('html', 'tpl', 'xml');
} else {
@@ -178,11 +186,12 @@ class TemplateHelper
Tlog::getInstance()->debug("Walking in $directory, in mode $walkMode");
/** @var \DirectoryIterator $fileInfo */
foreach (new \DirectoryIterator($directory) as $fileInfo) {
if ($fileInfo->isDot()) continue;
if ($fileInfo->isDir()) $num_texts += $this->walkDir($fileInfo->getPathName(), $walkMode, $translator, $currentLocale, $strings);
if ($fileInfo->isDir()) $num_texts += $this->walkDir($fileInfo->getPathName(), $walkMode, $translator, $currentLocale, $domain, $strings);
if ($fileInfo->isFile()) {
@@ -219,7 +228,7 @@ class TemplateHelper
$strings[$hash] = array(
'files' => array($short_path),
'text' => $match,
'translation' => $translator->trans($match, array(), 'messages', $currentLocale, false),
'translation' => $translator->trans($match, array(), $domain, $currentLocale, false),
'dollar' => strstr($match, '$') !== false
);
}
@@ -233,13 +242,24 @@ class TemplateHelper
return $num_texts;
} catch (\UnexpectedValueException $ex) {
echo $ex;
// Directory does not exists => ignore/
}
}
public function writeTranslation($file, $texts, $translations)
public function writeTranslation($file, $texts, $translations, $createIfNotExists = false)
{
$fs = new Filesystem();
if (! $fs->exists($file) && true === $createIfNotExists) {
$dir = dirname($file);
if (! $fs->exists($file)) {
$fs->mkdir($dir);
}
}
if ($fp = @fopen($file, 'w')) {
fwrite($fp, '<' . "?php\n\n");

View File

@@ -34,6 +34,7 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Config\DatabaseConfiguration;
use Thelia\Config\DefinePropel;
use Thelia\Core\Template\ParserInterface;
use Thelia\Core\Template\TemplateDefinition;
use Thelia\Core\DependencyInjection\Loader\XmlFileLoader;
@@ -43,6 +44,7 @@ use Propel\Runtime\Propel;
use Propel\Runtime\Connection\ConnectionManagerSingle;
use Thelia\Core\Template\TemplateHelper;
use Thelia\Log\Tlog;
use Thelia\Model\Module;
class Thelia extends Kernel
{
@@ -93,7 +95,7 @@ class Thelia extends Kernel
/**
* Add all module's standard templates to the parser environment
*
* @param TheliaParser $parser the parser
* @param ParserInterface $parser the parser
* @param Module $module the Module.
*/
protected function addStandardModuleTemplatesToParserEnvironment($parser, $module)
@@ -108,7 +110,7 @@ class Thelia extends Kernel
/**
* Add a module template directory to the parser environment
*
* @param TheliaParser $parser the parser
* @param ParserInterface $parser the parser
* @param Module $module the Module.
* @param string $templateType the template type (one of the TemplateDefinition type constants)
* @param string $templateSubdirName the template subdirectory name (one of the TemplateDefinition::XXX_SUBDIR constants)
@@ -168,8 +170,11 @@ class Thelia extends Kernel
$modules = \Thelia\Model\ModuleQuery::getActivated();
$translationDirs = array();
/** @var ParserInterface $parser */
$parser = $container->getDefinition('thelia.parser');
/** @var Module $module */
foreach ($modules as $module) {
try {
@@ -190,14 +195,21 @@ class Thelia extends Kernel
} else {
$container->addCompilerPass($compiler);
}
}
$loader = new XmlFileLoader($container, new FileLocator($module->getAbsoluteConfigPath()));
$loader->load("config.xml");
$moduleDomain = strtolower($module->getCode());
// Core module translation
if (is_dir($dir = $module->getAbsoluteI18nPath())) {
$translationDirs[] = $dir;
$translationDirs[$module->getTranslationDomain()] = $dir;
}
// Admin includes translation
if (is_dir($dir = $module->getAbsoluteAdminIncludesI18nPath())) {
$translationDirs[$module->getAdminIncludesTranslationDomain()] = $dir;
}
$this->addStandardModuleTemplatesToParserEnvironment($parser, $module);
@@ -207,16 +219,16 @@ class Thelia extends Kernel
}
}
// Load translation from templates
// core translation
$translationDirs[] = THELIA_ROOT . "core/lib/Thelia/Config/I18n";
// Load core translation
$translationDirs['core'] = THELIA_ROOT . "core/lib/Thelia/Config/I18n";
// Standard templates (front, back, pdf, mail)
$th = TemplateHelper::getInstance();
/** @var TemplateDefinition $templateDefinition */
foreach ($th->getStandardTemplateDefinitions() as $templateDefinition) {
if (is_dir($dir = $templateDefinition->getAbsoluteI18nPath())) {
$translationDirs[] = $dir;
$translationDirs[$templateDefinition->getTranslationDomain()] = $dir;
}
}
@@ -230,14 +242,19 @@ class Thelia extends Kernel
{
$translator = $container->getDefinition('thelia.translator');
foreach ($dirs as $domain => $dir) {
$finder = Finder::create()
->files()
->depth(0)
->in($dirs);
->in($dir);
/** @var \DirectoryIterator $file */
foreach ($finder as $file) {
list($locale, $format) = explode('.', $file->getBaseName(), 2);
$translator->addMethodCall('addResource', array($format, (string) $file, $locale));
$translator->addMethodCall('addResource', array($format, (string) $file, $locale, $domain));
}
}
}

View File

@@ -14,6 +14,7 @@ namespace Thelia\Core\Translation;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Translation\Translator as BaseTranslator;
use Thelia\Log\Tlog;
class Translator extends BaseTranslator
{
@@ -63,7 +64,7 @@ class Translator extends BaseTranslator
*
* @api
*/
public function trans($id, array $parameters = array(), $domain = 'messages', $locale = null, $return_default_if_not_available = true)
public function trans($id, array $parameters = array(), $domain = 'core', $locale = null, $return_default_if_not_available = true)
{
if (null === $locale) {
$locale = $this->getLocale();
@@ -73,11 +74,19 @@ class Translator extends BaseTranslator
$this->loadCatalogue($locale);
}
if ($this->catalogues[$locale]->has((string) $id, $domain))
if (! $this->catalogues[$locale]->has((string) $id, $domain)) {
}
if ($this->catalogues[$locale]->has((string) $id, $domain)) {
return parent::trans($id, $parameters, $domain, $locale);
else if ($return_default_if_not_available)
} else {
Tlog::getInstance()->addWarning("Undefined translation: locale: $locale, domain: $domain, ID: $id");
if ($return_default_if_not_available)
return strtr($id, $parameters);
else
return '';
}
}
}

View File

@@ -3,20 +3,77 @@
namespace Thelia\Model;
use Propel\Runtime\Connection\ConnectionInterface;
use Thelia\Core\Template\TemplateDefinition;
use Thelia\Model\Base\Module as BaseModule;
use Thelia\Model\Tools\ModelEventDispatcherTrait;
use Thelia\Model\Tools\PositionManagementTrait;
class Module extends BaseModule
{
use ModelEventDispatcherTrait;
use \Thelia\Model\Tools\PositionManagementTrait;
use PositionManagementTrait;
const ADMIN_INCLUDES_DIRECTORY_NAME = "AdminIncludes";
public function postSave(ConnectionInterface $con = null)
{
ModuleQuery::resetActivated();
}
public function getTranslationDomain() {
return strtolower($this->getCode());
}
public function getAdminIncludesTranslationDomain() {
return $this->getTranslationDomain().'.ai';
}
public function getAbsoluteBackOfficeTemplatePath($subdir)
{
return sprintf("%s".DS."%s".DS."%s",
$this->getAbsoluteTemplateBasePath(),
TemplateDefinition::BACK_OFFICE_SUBDIR,
$subdir
);
}
public function getAbsoluteBackOfficeI18nTemplatePath($subdir)
{
return sprintf("%s".DS."%s".DS."%s",
$this->getAbsoluteI18nPath(),
TemplateDefinition::BACK_OFFICE_SUBDIR,
$subdir
);
}
public function getBackOfficeTemplateTranslationDomain($templateName) {
return $this->getTranslationDomain(). '.bo.' . $templateName;
}
public function getAbsoluteFrontOfficeTemplatePath($subdir)
{
return sprintf("%s".DS."%s".DS."%s",
$this->getAbsoluteTemplateBasePath(),
TemplateDefinition::FRONT_OFFICE_SUBDIR,
$subdir
);
}
public function getAbsoluteFrontOfficeI18nTemplatePath($subdir)
{
return sprintf("%s".DS."%s".DS."%s",
$this->getAbsoluteI18nPath(),
TemplateDefinition::FRONT_OFFICE_SUBDIR,
$subdir
);
}
public function getFrontOfficeTemplateTranslationDomain($templateName) {
return $this->getTranslationDomain(). '.fo.' . $templateName;
}
/**
* @return the module's base directory path, relative to THELIA_MODULE_DIR
*/
@@ -65,14 +122,41 @@ class Module extends BaseModule
return THELIA_MODULE_DIR . $this->getI18nPath();
}
/**
* @return the module's AdminIncludes absolute directory path
*/
public function getAbsoluteAdminIncludesPath()
{
return $this->getAbsoluteBaseDir() . DS . self::ADMIN_INCLUDES_DIRECTORY_NAME;
}
/**
* @return the module's AdminIncludes i18N absolute directory path
*/
public function getAbsoluteAdminIncludesI18nPath()
{
return THELIA_MODULE_DIR . $this->getI18nPath() . DS . self::ADMIN_INCLUDES_DIRECTORY_NAME;
}
/**
* Return the absolute path to the module's template directory
*
* @return string a path
*/
public function getAbsoluteTemplateBasePath()
{
return $this->getAbsoluteBaseDir() . DS . 'templates';
}
/**
* Return the absolute path to one of the module's template directories
*
* @param int $templateSubdirName the name of the, probably one of TemplateDefinition::xxx_SUBDIR constants
* @return string a path
*/
public function getAbsoluteTemplateDirectoryPath($templateSubdirName)
{
return sprintf("%s%stemplates%s%s", $this->getAbsoluteBaseDir(), DS, DS, $templateSubdirName);
return $this->getAbsoluteTemplateBasePath() .DS. $templateSubdirName;
}
/**

View File

@@ -10,6 +10,9 @@
{* -- Declare assets directory, relative to template base directory --------- *}
{declare_assets directory='assets'}
{* Set the default translation domain, that will be used by {intl} when the 'd' parameter is not set *}
{default_translation_domain domain='bo.default'}
<!DOCTYPE html>
<html lang="{$lang_code}">
<head>

View File

@@ -1,3 +1,6 @@
{* Set the default translation domain, that will be used by {intl} when the 'd' parameter is not set *}
{default_translation_domain domain='bo.default'}
{* Update an Address *}
{form name="thelia.lang.update"}

View File

@@ -1,3 +1,6 @@
{* Set the default translation domain, that will be used by {intl} when the 'd' parameter is not set *}
{default_translation_domain domain='bo.default'}
{loop name="product_edit" type="product" visible="*" id=$product_id backend_context="1" lang=$edit_language_id}
<div class="form-container">

View File

@@ -1,3 +1,6 @@
{* Set the default translation domain, that will be used by {intl} when the 'd' parameter is not set *}
{default_translation_domain domain='bo.default'}
{loop name="product_edit" type="product" visible="*" id=$product_id backend_context="1" lang=$edit_language_id}
<div class="form-container">

View File

@@ -1,3 +1,6 @@
{* Set the default translation domain, that will be used by {intl} when the 'd' parameter is not set *}
{default_translation_domain domain='bo.default'}
<div class="form-group">
{ifloop rel="free_attributes"}
<form method="POST" action="{url path='/admin/configuration/templates/attributes/add'}">

View File

@@ -1,3 +1,6 @@
{* Set the default translation domain, that will be used by {intl} when the 'd' parameter is not set *}
{default_translation_domain domain='bo.default'}
<div class="form-group">
{ifloop rel="free_features"}
<form method="POST" action="{url path='/admin/configuration/templates/features/add'}">

View File

@@ -1,4 +1,7 @@
{* this temlate is loaded via Ajax in the login page, to prevent login page slowdown *}
{* this template is loaded via Ajax in the login page, to prevent login page slowdown *}
{* Set the default translation domain, that will be used by {intl} when the 'd' parameter is not set *}
{default_translation_domain domain='bo.default'}
<div class="panel-group" id="accordion">
{loop type="feed" name="thelia_feeds" url="http://thelia.net/Flux-rss.html?id_rubrique=8" limit="3"}

View File

@@ -1,8 +1,11 @@
#order
# Maximum number of lines in lists
# --------------------------------
max_displayed_orders = 20
max_displayed_customers = 20
#order status
# order status - seems ununsed ?
# ------------------------------
order_not_paid = 'warning'
order_paid = 'success'
order_processing = 'primary'

View File

@@ -39,7 +39,7 @@
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label class="control-label" for="item_to_translate">{intl l='Select which items you want to translate'}</label>
<label class="control-label" for="item_to_translate">{intl l='Items to translate'}</label>
<select id="item_to_translate" required="required" name="item_to_translate" class="submit-on-change form-control">
<option value="">{intl l='Please select items to translate'}</option>
@@ -56,12 +56,12 @@
{if $item_to_translate == 'mo'}
<div class="col-md-4 item-id-selector">
<div class="form-group">
<label class="control-label" for="item_id">{intl l='Select the module you want to translate'}</label>
<label class="control-label" for="item_name">{intl l='Module you want to translate'}</label>
<select id="item_id" required="required" name="item_id" class="submit-on-change form-control">
<select id="item_name" required="required" name="item_name" class="submit-on-change form-control">
<option value="">{intl l='Please select the module to translate'}</option>
{loop type="module" name="translate-module" backend_context=1}
<option value="{$ID}" {if $item_id == $ID}selected="selected"{/if}>{$TITLE}</option>
<option value="{$ID}" {if $item_name == $ID}selected="selected"{/if}>{$TITLE}</option>
{/loop}
</select>
</div>
@@ -69,12 +69,12 @@
{else if $item_to_translate == 'fo'}
<div class="col-md-4 item-id-selector">
<div class="form-group">
<label class="control-label" for="item_id">{intl l='Select the front-office template you want to translate'}</label>
<label class="control-label" for="item_name">{intl l='Front-office template you want to translate'}</label>
<select id="item_id" required="required" name="item_id" class="submit-on-change form-control">
<select id="item_name" required="required" name="item_name" class="submit-on-change form-control">
<option value="">{intl l='Please select the F.O. template to translate'}</option>
{loop type="template" name="translate-fo-template" template-type="front-office" backend_context=1}
<option value="{$NAME}" {if $item_id == $NAME}selected="selected"{/if}>{$NAME}</option>
<option value="{$NAME}" {if $item_name == $NAME}selected="selected"{/if}>{$NAME}</option>
{/loop}
</select>
</div>
@@ -82,12 +82,12 @@
{else if $item_to_translate == 'bo'}
<div class="col-md-4 item-id-selector">
<div class="form-group">
<label class="control-label" for="item_id">{intl l='Select the back-office template you want to translate'}</label>
<label class="control-label" for="item_name">{intl l='Back-office template you want to translate'}</label>
<select id="item_id" required="required" name="item_id" class="submit-on-change form-control">
<select id="item_name" required="required" name="item_name" class="submit-on-change form-control">
<option value="">{intl l='Please select the B.O. template to translate'}</option>
{loop type="template" name="translate-fo-template" template-type="back-office" backend_context=1}
<option value="{$NAME}" {if $item_id == $NAME}selected="selected"{/if}>{$NAME}</option>
<option value="{$NAME}" {if $item_name == $NAME}selected="selected"{/if}>{$NAME}</option>
{/loop}
</select>
</div>
@@ -95,12 +95,12 @@
{else if $item_to_translate == 'ma'}
<div class="col-md-4 item-id-selector">
<div class="form-group">
<label class="control-label" for="item_id">{intl l='Select the E-mail template you want to translate'}</label>
<label class="control-label" for="item_name">{intl l='E-mail template you want to translate'}</label>
<select id="item_id" required="required" name="item_id" class="submit-on-change form-control">
<select id="item_name" required="required" name="item_name" class="submit-on-change form-control">
<option value="">{intl l='Please select the E-mail template to translate'}</option>
{loop type="template" name="translate-fo-template" template-type="email" backend_context=1}
<option value="{$NAME}" {if $item_id == $NAME}selected="selected"{/if}>{$NAME}</option>
<option value="{$NAME}" {if $item_name == $NAME}selected="selected"{/if}>{$NAME}</option>
{/loop}
</select>
</div>
@@ -108,24 +108,46 @@
{else if $item_to_translate == 'pf'}
<div class="col-md-4 item-id-selector">
<div class="form-group">
<label class="control-label" for="item_id">{intl l='Select the PDF template you want to translate'}</label>
<label class="control-label" for="item_name">{intl l='PDF template you want to translate'}</label>
<select id="item_id" required="required" name="item_id" class="submit-on-change form-control">
<select id="item_name" required="required" name="item_name" class="submit-on-change form-control">
<option value="">{intl l='Please select the PDF template to translate'}</option>
{loop type="template" name="translate-pdf-template" template-type="pdf" backend_context=1}
<option value="{$NAME}" {if $item_id == $NAME}selected="selected"{/if}>{$NAME}</option>
<option value="{$NAME}" {if $item_name == $NAME}selected="selected"{/if}>{$NAME}</option>
{/loop}
</select>
</div>
</div>
{/if}
<div class="col-md-4">
{if $item_to_translate == 'mo' && $item_name != ''}
<div class="col-md-4 item-id-selector">
<div class="form-group">
<label class="control-label" for="module_part">{intl l='Module component'}</label>
<select id="module_part" required="required" name="module_part" class="submit-on-change form-control">
<option value="">{intl l='Please select the module component'}</option>
<option value="core" {if $module_part == 'core'}selected="selected"{/if}>Core files</option>
<option value="admin-includes" {if $module_part == 'admin-includes'}selected="selected"{/if}>Administration includes template</option>
{foreach $back_office_templates as $template}
{$option_value = "bo.{$template}"}
<option value="{$option_value}" {if $module_part == $option_value}selected="selected"{/if}>Back-office template "{$template}"</option>
{/foreach}
{foreach $front_office_templates as $template}
{$option_value = "fo.{$template}"}
<option value="{$option_value}" {if $module_part == $option_value}selected="selected"{/if}>Front-office template "{$template}"</option>
{/foreach}
</select>
</div>
</div>
{/if}
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label class="control-label">&nbsp;</label>
<label class="checkbox control-label">
<input class="submit-on-change" type="checkbox" name="view_missing_traductions_only" value="1" {if $view_missing_traductions_only}checked="checked"{/if}> {intl l='View only missing translations'}
<input class="submit-on-change" type="checkbox" name="view_missing_traductions_only" value="1" {if $view_missing_traductions_only}checked="checked"{/if}> {intl l='View only missing translations.'}
</label>
</div>
</div>
@@ -270,6 +292,11 @@
$('.item-id-selector').hide();
});
$('#item_name').change(function() {
$('#module_part').val('');
});
$('.submit-on-change').change(function() {
$('#translation_form').submit();
});

View File

@@ -1,3 +1,6 @@
{* Set the default translation domain, that will be used by {intl} when the 'd' parameter is not set *}
{default_translation_domain domain='fo.default'}
{form name="thelia.order.delivery"}
{loop type="delivery" name="deliveries" force_return="true" country=$country}

View File

@@ -19,6 +19,8 @@ GNU General Public License : http://www.gnu.org/licenses/
{* Declare assets directory, relative to template base directory *}
{declare_assets directory='assets'}
{* Set the default translation domain, that will be used by {intl} when the 'd' parameter is not set *}
{default_translation_domain domain='fo.default'}
{block name="no-return-functions"}{/block}
{assign var="store_name" value="{config key="store_name"}"}
{if not $store_name}{assign var="store_name" value="{intl l='Thelia V2'}"}{/if}