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; namespace Thelia\Command;
use Thelia\Model\Module;
/** /**
* base class for module commands * base class for module commands
* *
@@ -36,7 +38,7 @@ abstract class BaseModuleGenerate extends ContainerAwareCommand
'Controller', 'Controller',
'EventListeners', 'EventListeners',
'I18n', 'I18n',
'AdminIncludes', Module::ADMIN_INCLUDES_DIRECTORY_NAME,
'templates', 'templates',
); );

View File

@@ -23,11 +23,11 @@ return array(
'Alpha code 3 *' => 'Code Alpha 3 *', 'Alpha code 3 *' => 'Code Alpha 3 *',
'Amount removed from the cart' => 'Montant déduit du panier', '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', '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', 'Attribute ID:Attribute AV ID' => 'Déclinaison ID : Valeur de déclinaison ID',
'Auth mode' => 'Mode d\'authentification', 'Auth mode' => 'Mode d\'authentification',
'Available quantity' => 'Quantité disponible', 'Available quantity' => 'Quantité disponible',
'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', 'Bad tax list JSON' => 'Mauvais JSON de la liste des taxes',
'Business ID' => 'ID du business', '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.', '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', 'Emergency' => 'Urgence',
'Enable remote SMTP use' => 'Activer l\'utilisation d\'un serveur SMTP distant.', 'Enable remote SMTP use' => 'Activer l\'utilisation d\'un serveur SMTP distant.',
'Encryption' => 'Chiffrement', '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', 'Errors' => 'Erreurs',
'Failed to update language definition: %ex' => 'Erreur lors de la mise à jour de la définition de la langue : %ex', 'Failed to update language definition: %ex' => 'Erreur lors de la mise à jour de la définition de la langue : %ex',
'Fax' => 'Fax', 'Fax' => 'Fax',
@@ -91,6 +92,8 @@ return array(
'Log level *' => 'Niveau de log *', 'Log level *' => 'Niveau de log *',
'Login' => 'Connexion', 'Login' => 'Connexion',
'Login failed. Please check your username and password.' => 'Erreur de login. Veuillez vérifier votre login et votre mot de passe.', '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', 'Make this address as my primary address' => 'Choisir cette adresse comme adresse par défaut',
'Message subject' => 'Sujet', 'Message subject' => 'Sujet',
'Meta Description' => 'Meta description', 'Meta Description' => 'Meta description',
@@ -105,9 +108,13 @@ return array(
'New Password' => 'Nouveau mot de passe.', 'New Password' => 'Nouveau mot de passe.',
'No %obj was created.' => 'aucun %obj n\'a été créé.', 'No %obj was created.' => 'aucun %obj n\'a été créé.',
'No %obj was updated.' => 'Aucun %obj mis à jour', '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.', 'No, I am a new customer.' => 'Non, je suis un nouveau client.',
'Not found' => 'Non trouvé.',
'Notices' => 'Notices', 'Notices' => 'Notices',
'Order address ID not found' => 'ID de l\'adresse de la commande non trouvé', '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', 'Page Title' => 'Titre de la page',
'Parent category *' => 'Catégorie parente *', 'Parent category *' => 'Catégorie parente *',
'Parent folder *' => 'Dossier parent *', '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.', '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' => 'Prix',
'Price currency *' => 'Devise *', '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 *', 'Prodcut ID *' => 'ID du produit *',
'Product ID' => 'ID produit', 'Product ID' => 'ID 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 ID not found' => 'ID du profil non trouvé',
'Profile `code` already exists' => 'Le `code` du profil existe déjà', 'Profile `code` already exists' => 'Le `code` du profil existe déjà',
'Purpose *' => 'Objet', 'Purpose *' => 'Objet',
'Quantity' => 'Quantité',
'Rate from € *' => 'Taux à partie de l\'€ *', 'Rate from € *' => 'Taux à partie de l\'€ *',
'Redirecting ...' => 'Redirection ...', 'Redirecting ...' => 'Redirection ...',
'Redirecting to %url' => 'Redirection vers %url', 'Redirecting to %url' => 'Redirection vers %url',
@@ -162,11 +172,13 @@ return array(
'Rotated Text File' => 'Rotation du fichier texte', 'Rotated Text File' => 'Rotation du fichier texte',
'Sale price excluding taxes' => 'Prix de vente Hors Taxes', 'Sale price excluding taxes' => 'Prix de vente Hors Taxes',
'Sale price including taxes' => 'Prix de vente Toutes Taxes Comprises', 'Sale price including taxes' => 'Prix de vente Toutes Taxes Comprises',
'Shipping zone name' => 'Nom de la zone de livraison',
'Show redirections *' => 'Montrer les redirections *', 'Show redirections *' => 'Montrer les redirections *',
'Sorry, an error occured: %msg' => 'Désolé, une erreur est survenue : %msg', '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 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.', 'Sorry, you\'re not allowed to perform this action' => 'Désolé, vous n\'êtes pas autorisé à réaliser cette action.',
'Source IP' => 'IP source', 'Source IP' => 'IP source',
'Stats on %month/%year' => 'Statisticues pour %month/%year ',
'Store configuration failed.' => 'Erreur de configuration du magasin.', 'Store configuration failed.' => 'Erreur de configuration du magasin.',
'Store email address' => 'Adresse mail du magasin', 'Store email address' => 'Adresse mail du magasin',
'Store logs into text file' => 'Conserver les logs dans des fichiers texte', 'Store logs into text file' => 'Conserver les logs dans des fichiers texte',
@@ -205,13 +217,18 @@ return array(
'Title *' => 'Titre *', 'Title *' => 'Titre *',
'Title ID not found' => 'ID de la civilité non trouvé', 'Title ID not found' => 'ID de la civilité non trouvé',
'Type' => 'Type', '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',
'Username *' => 'Nom d\'utilisateur *', 'Username *' => 'Nom d\'utilisateur *',
'Value' => 'Valeur', 'Value' => 'Valeur',
'Value *' => 'Valeur *', 'Value *' => 'Valeur *',
'Warnings' => 'Avertissements', 'Warnings' => 'Avertissements',
'Weight' => 'Poids', 'Weight' => 'Poids',
'Weight *' => 'Poids *',
'Yes, I have a password :' => 'Oui, j\'ai un mot de passe :', 'Yes, I have a password :' => 'Oui, j\'ai un mot de passe :',
'You are already registered!' => 'Vous êtes déjà enregistré !', 'You are already registered!' => 'Vous êtes déjà enregistré !',
'Your Email Address' => 'Votre adresse mail', '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', '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 ', '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', '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', 'this product id does not exists : %d' => 'l\'id du produit %d n\'existe pas',
'time format' => 'Format d\'heure', '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\Resource\AdminResources;
use Thelia\Core\Security\AccessManager; use Thelia\Core\Security\AccessManager;
use Thelia\Model\Module;
use Thelia\Model\ModuleQuery; use Thelia\Model\ModuleQuery;
use Thelia\Core\Template\TemplateHelper; use Thelia\Core\Template\TemplateHelper;
use Thelia\Core\Template\TemplateDefinition; use Thelia\Core\Template\TemplateDefinition;
@@ -25,13 +26,45 @@ use Thelia\Tools\URL;
*/ */
class TranslationsController extends BaseAdminController 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() protected function renderTemplate()
{ {
// Get related strings, if all input data are here // Get related strings, if all input data are here
$item_to_translate = $this->getRequest()->get('item_to_translate'); $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(); $all_strings = array();
@@ -41,51 +74,106 @@ class TranslationsController extends BaseAdminController
$templateArguments = array( $templateArguments = array(
'item_to_translate' => $item_to_translate, '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), 'view_missing_traductions_only' => $this->getRequest()->get('view_missing_traductions_only', 0),
'max_input_vars_warning' => false, 'max_input_vars_warning' => false,
); );
// Find the i18n directory, and the directory to examine. // 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) { switch ($item_to_translate) {
// Module core
case 'mo' : case 'mo' :
if (null !== $module = ModuleQuery::create()->findPk($item_id)) { $module = $this->getModule($item_name);
if ($module_part == 'core') {
$directory = $module->getAbsoluteBaseDir(); $directory = $module->getAbsoluteBaseDir();
$domain = $module->getTranslationDomain();
$i18n_directory = $module->getAbsoluteI18nPath(); $i18n_directory = $module->getAbsoluteI18nPath();
$walkMode = TemplateHelper::WALK_MODE_PHP; $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; break;
// Thelia Core
case 'co' : case 'co' :
$directory = THELIA_ROOT . 'core/lib/Thelia'; $directory = THELIA_ROOT . 'core/lib/Thelia';
$domain = 'core';
$i18n_directory = THELIA_ROOT . 'core/lib/Thelia/Config/I18n'; $i18n_directory = THELIA_ROOT . 'core/lib/Thelia/Config/I18n';
$walkMode = TemplateHelper::WALK_MODE_PHP; $walkMode = TemplateHelper::WALK_MODE_PHP;
break; break;
// Front-office template
case 'fo' : case 'fo' :
$template = new TemplateDefinition($item_id, TemplateDefinition::FRONT_OFFICE); $template = new TemplateDefinition($item_name, TemplateDefinition::FRONT_OFFICE);
break; break;
// Back-office template
case 'bo' : case 'bo' :
$template = new TemplateDefinition($item_id, TemplateDefinition::BACK_OFFICE); $template = new TemplateDefinition($item_name, TemplateDefinition::BACK_OFFICE);
break; break;
// PDF templates
case 'pf' : case 'pf' :
$template = new TemplateDefinition($item_id, TemplateDefinition::PDF); $template = new TemplateDefinition($item_name, TemplateDefinition::PDF);
break; break;
// Email templates
case 'ma' : case 'ma' :
$template = new TemplateDefinition($item_id, TemplateDefinition::EMAIL); $template = new TemplateDefinition($item_name, TemplateDefinition::EMAIL);
break; break;
default:
/*
throw new \InvalidArgumentException(
$this->getTranslator()->trans("Undefined translation type: %item", ['%item' => $item_to_translate])
);
*/
} }
if ($template) { if ($template) {
$directory = $template->getAbsolutePath(); $directory = $template->getAbsolutePath();
$i18n_directory = $template->getAbsoluteI18nPath(); $i18n_directory = $template->getAbsoluteI18nPath();
$domain = $template->getTranslationDomain();
} }
// Load strings to translate // Load strings to translate
@@ -102,11 +190,11 @@ class TranslationsController extends BaseAdminController
if (! empty($texts)) { 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()); $translations = $this->getRequest()->get('translation', array());
TemplateHelper::getInstance()->writeTranslation($file, $texts, $translations); TemplateHelper::getInstance()->writeTranslation($file, $texts, $translations, true);
if ($save_mode == 'stay') if ($save_mode == 'stay')
$this->redirectToRoute("admin.configuration.translations", $templateArguments); $this->redirectToRoute("admin.configuration.translations", $templateArguments);
@@ -122,6 +210,7 @@ class TranslationsController extends BaseAdminController
$walkMode, $walkMode,
$this->getTranslator(), $this->getTranslator(),
$this->getCurrentEditionLocale(), $this->getCurrentEditionLocale(),
$domain,
$all_strings $all_strings
); );

View File

@@ -166,6 +166,7 @@ class Module extends BaseI18nLoop implements PropelSearchLoopInterface
public function parseResults(LoopResult $loopResult) public function parseResults(LoopResult $loopResult)
{ {
/** @var \Thelia\Model\Module $module */
foreach ($loopResult->getResultDataCollection() as $module) { foreach ($loopResult->getResultDataCollection() as $module) {
$loopResultRow = new LoopResultRow($module); $loopResultRow = new LoopResultRow($module);
$loopResultRow->set("ID", $module->getId()) $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 not ; test if it uses admin inclusion : module_configuration.html */
if (false === $hasConfigurationInterface) { 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; $hasConfigurationInterface = true;
} }
} }
} else { } else {
// Make a quick and dirty test on the module's routing.xml file // 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) { if ($routing && strpos($routing, '/admin/module/') !== false) {
$hasConfigurationInterface = true; $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> <a href="{url path='/admin/configuration/currencies/positionDown' currency_id=$ID}"><i class="icon-arrow-down"></i></a>
*/ */
if ($permissions == null || $this->securityContext->isGranted( if ($this->securityContext->isGranted(
"ADMIN", array("ADMIN"),
$resource === null ? array() : array($resource), $resource === null ? array() : array($resource),
$module === null ? array() : array($module), $module === null ? array() : array($module),
array($access)) array($access))

View File

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

View File

@@ -19,41 +19,59 @@ use Symfony\Component\Translation\TranslatorInterface;
class Translation extends AbstractSmartyPlugin class Translation extends AbstractSmartyPlugin
{ {
protected $translator; protected $translator;
protected $defaultTranslationDomain = '';
public function __construct(TranslatorInterface $translator) public function __construct(TranslatorInterface $translator)
{ {
$this->translator = $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 * Process translate function
* *
* @param unknown $params * @param array $params
* @param unknown $smarty * @param \Smarty_Internal_Template $smarty
* @return string * @return string
*/ */
public function translate($params, &$smarty) 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 // and pass it to the translator
$vars = array(); $vars = array();
foreach ($params as $name => $value) { 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() public function getPluginDescriptors()
{ {
return array( return array(
new SmartyPluginDescriptor('function', 'intl', $this, 'translate'), 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 $type;
protected $translationDomainPrefix;
public function __construct($name, $type) public function __construct($name, $type)
{ {
$this->name = $name; $this->name = $name;
@@ -54,22 +56,31 @@ class TemplateDefinition
switch ($type) { switch ($type) {
case TemplateDefinition::FRONT_OFFICE: case TemplateDefinition::FRONT_OFFICE:
$this->path = self::FRONT_OFFICE_SUBDIR . DS . $name; $this->path = self::FRONT_OFFICE_SUBDIR . DS . $name;
$this->translationDomainPrefix = 'fo.';
break; break;
case TemplateDefinition::BACK_OFFICE: case TemplateDefinition::BACK_OFFICE:
$this->path = self::BACK_OFFICE_SUBDIR . DS . $name; $this->path = self::BACK_OFFICE_SUBDIR . DS . $name;
$this->translationDomainPrefix = 'bo.';
break; break;
case TemplateDefinition::PDF: case TemplateDefinition::PDF:
$this->path = self::PDF_SUBDIR . DS . $name; $this->path = self::PDF_SUBDIR . DS . $name;
$this->translationDomainPrefix = 'pdf.';
break; break;
case TemplateDefinition::EMAIL: case TemplateDefinition::EMAIL:
$this->path = self::EMAIL_SUBDIR . DS . $name; $this->path = self::EMAIL_SUBDIR . DS . $name;
$this->translationDomainPrefix = 'email.';
break; break;
default: default:
$this->path = $name; $this->path = $name;
$this->translationDomainPrefix = 'generic.';
break; break;
} }
} }
public function getTranslationDomain() {
return $this->translationDomainPrefix . strtolower($this->getName());
}
public function getName() public function getName()
{ {
return $this->name; return $this->name;

View File

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

View File

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

View File

@@ -1,95 +1,179 @@
<?php <?php
namespace Thelia\Model; namespace Thelia\Model;
use Propel\Runtime\Connection\ConnectionInterface; use Propel\Runtime\Connection\ConnectionInterface;
use Thelia\Model\Base\Module as BaseModule; use Thelia\Core\Template\TemplateDefinition;
use Thelia\Model\Tools\ModelEventDispatcherTrait; use Thelia\Model\Base\Module as BaseModule;
use Thelia\Model\Tools\ModelEventDispatcherTrait;
class Module extends BaseModule use Thelia\Model\Tools\PositionManagementTrait;
{
use ModelEventDispatcherTrait; class Module extends BaseModule
{
use \Thelia\Model\Tools\PositionManagementTrait; use ModelEventDispatcherTrait;
public function postSave(ConnectionInterface $con = null) use PositionManagementTrait;
{
ModuleQuery::resetActivated(); const ADMIN_INCLUDES_DIRECTORY_NAME = "AdminIncludes";
}
/** public function postSave(ConnectionInterface $con = null)
* @return the module's base directory path, relative to THELIA_MODULE_DIR {
*/ ModuleQuery::resetActivated();
public function getBaseDir() }
{
return ucfirst($this->getCode()); public function getTranslationDomain() {
} return strtolower($this->getCode());
}
/**
* @return the module's base directory path, relative to THELIA_MODULE_DIR public function getAdminIncludesTranslationDomain() {
*/ return $this->getTranslationDomain().'.ai';
public function getAbsoluteBaseDir() }
{
return THELIA_MODULE_DIR . $this->getBaseDir(); public function getAbsoluteBackOfficeTemplatePath($subdir)
} {
return sprintf("%s".DS."%s".DS."%s",
/** $this->getAbsoluteTemplateBasePath(),
* @return the module's config directory path, relative to THELIA_MODULE_DIR TemplateDefinition::BACK_OFFICE_SUBDIR,
*/ $subdir
public function getConfigPath() );
{ }
return $this->getBaseDir() . DS . "Config";
} public function getAbsoluteBackOfficeI18nTemplatePath($subdir)
{
/** return sprintf("%s".DS."%s".DS."%s",
* @return the module's config absolute directory path $this->getAbsoluteI18nPath(),
*/ TemplateDefinition::BACK_OFFICE_SUBDIR,
public function getAbsoluteConfigPath() $subdir
{ );
return THELIA_MODULE_DIR . $this->getConfigPath(); }
}
public function getBackOfficeTemplateTranslationDomain($templateName) {
/** return $this->getTranslationDomain(). '.bo.' . $templateName;
* @return the module's i18N directory path, relative to THELIA_MODULE_DIR }
*/
public function getI18nPath() public function getAbsoluteFrontOfficeTemplatePath($subdir)
{ {
return $this->getBaseDir() . DS . "I18n"; return sprintf("%s".DS."%s".DS."%s",
} $this->getAbsoluteTemplateBasePath(),
TemplateDefinition::FRONT_OFFICE_SUBDIR,
/** $subdir
* @return the module's i18N absolute directory path );
*/ }
public function getAbsoluteI18nPath()
{ public function getAbsoluteFrontOfficeI18nTemplatePath($subdir)
return THELIA_MODULE_DIR . $this->getI18nPath(); {
} return sprintf("%s".DS."%s".DS."%s",
$this->getAbsoluteI18nPath(),
/** TemplateDefinition::FRONT_OFFICE_SUBDIR,
* Return the absolute path to one of the module's template directories $subdir
* );
* @param int $templateSubdirName the name of the, probably one of TemplateDefinition::xxx_SUBDIR constants }
*/
public function getAbsoluteTemplateDirectoryPath($templateSubdirName) public function getFrontOfficeTemplateTranslationDomain($templateName) {
{ return $this->getTranslationDomain(). '.fo.' . $templateName;
return sprintf("%s%stemplates%s%s", $this->getAbsoluteBaseDir(), DS, DS, $templateSubdirName); }
}
/**
/** * @return the module's base directory path, relative to THELIA_MODULE_DIR
* Calculate next position relative to module type */
*/ public function getBaseDir()
protected function addCriteriaToPositionQuery($query) {
{ return ucfirst($this->getCode());
$query->filterByType($this->getType()); }
}
/**
/** * @return the module's base directory path, relative to THELIA_MODULE_DIR
* {@inheritDoc} */
*/ public function getAbsoluteBaseDir()
public function preInsert(ConnectionInterface $con = null) {
{ return THELIA_MODULE_DIR . $this->getBaseDir();
$this->setPosition($this->getNextPosition()); }
return true; /**
} * @return the module's config directory path, relative to THELIA_MODULE_DIR
} */
public function getConfigPath()
{
return $this->getBaseDir() . DS . "Config";
}
/**
* @return the module's config absolute directory path
*/
public function getAbsoluteConfigPath()
{
return THELIA_MODULE_DIR . $this->getConfigPath();
}
/**
* @return the module's i18N directory path, relative to THELIA_MODULE_DIR
*/
public function getI18nPath()
{
return $this->getBaseDir() . DS . "I18n";
}
/**
* @return the module's i18N absolute directory path
*/
public function getAbsoluteI18nPath()
{
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 $this->getAbsoluteTemplateBasePath() .DS. $templateSubdirName;
}
/**
* Calculate next position relative to module type
*/
protected function addCriteriaToPositionQuery($query)
{
$query->filterByType($this->getType());
}
/**
* {@inheritDoc}
*/
public function preInsert(ConnectionInterface $con = null)
{
$this->setPosition($this->getNextPosition());
return true;
}
}

View File

@@ -10,6 +10,9 @@
{* -- Declare assets directory, relative to template base directory --------- *} {* -- Declare assets directory, relative to template base directory --------- *}
{declare_assets directory='assets'} {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> <!DOCTYPE html>
<html lang="{$lang_code}"> <html lang="{$lang_code}">
<head> <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 *} {* Update an Address *}
{form name="thelia.lang.update"} {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} {loop name="product_edit" type="product" visible="*" id=$product_id backend_context="1" lang=$edit_language_id}
<div class="form-container"> <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} {loop name="product_edit" type="product" visible="*" id=$product_id backend_context="1" lang=$edit_language_id}
<div class="form-container"> <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"> <div class="form-group">
{ifloop rel="free_attributes"} {ifloop rel="free_attributes"}
<form method="POST" action="{url path='/admin/configuration/templates/attributes/add'}"> <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"> <div class="form-group">
{ifloop rel="free_features"} {ifloop rel="free_features"}
<form method="POST" action="{url path='/admin/configuration/templates/features/add'}"> <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"> <div class="panel-group" id="accordion">
{loop type="feed" name="thelia_feeds" url="http://thelia.net/Flux-rss.html?id_rubrique=8" limit="3"} {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_orders = 20
max_displayed_customers = 20 max_displayed_customers = 20
#order status # order status - seems ununsed ?
# ------------------------------
order_not_paid = 'warning' order_not_paid = 'warning'
order_paid = 'success' order_paid = 'success'
order_processing = 'primary' order_processing = 'primary'

View File

@@ -1,320 +1,347 @@
{extends file="admin-layout.tpl"} {extends file="admin-layout.tpl"}
{block name="page-title"}{intl l='Translations'}{/block} {block name="page-title"}{intl l='Translations'}{/block}
{block name="check-resource"}admin.configuration.variable{/block} {block name="check-resource"}admin.configuration.variable{/block}
{block name="check-access"}update{/block} {block name="check-access"}update{/block}
{block name="main-content"} {block name="main-content"}
<div class="variables translations"> <div class="variables translations">
<div id="wrapper" class="container"> <div id="wrapper" class="container">
<ul class="breadcrumb"> <ul class="breadcrumb">
<li><a href="{url path='/admin/home'}">{intl l="Home"}</a></li> <li><a href="{url path='/admin/home'}">{intl l="Home"}</a></li>
<li><a href="{url path='/admin/configuration'}">{intl l="Configuration"}</a></li> <li><a href="{url path='/admin/configuration'}">{intl l="Configuration"}</a></li>
<li><a href="{url path='/admin/configuration/translations'}">{intl l="Translations"}</a></li> <li><a href="{url path='/admin/configuration/translations'}">{intl l="Translations"}</a></li>
</ul> </ul>
<div class="row"> <div class="row">
<div class="col-md-12 general-block-decorator"> <div class="col-md-12 general-block-decorator">
<div class="row"> <div class="row">
<div class="col-md-12 title title-without-tabs"> <div class="col-md-12 title title-without-tabs">
{intl l="Translation"} {intl l="Translation"}
</div> </div>
<div class="form-container"> <div class="form-container">
<div class="col-md-12"> <div class="col-md-12">
<form method="POST" id="translation_form" action="{url path='/admin/configuration/translations/update'}"> <form method="POST" id="translation_form" action="{url path='/admin/configuration/translations/update'}">
<fieldset> <fieldset>
{include {include
file = "includes/inner-form-toolbar.html" file = "includes/inner-form-toolbar.html"
close_url = {url path='/admin/configuration'} close_url = {url path='/admin/configuration'}
} }
<input type="hidden" name="edit_language_id" value="{$edit_language_id}" /> <input type="hidden" name="edit_language_id" value="{$edit_language_id}" />
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-4">
<div class="form-group"> <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"> <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> <option value="">{intl l='Please select items to translate'}</option>
<option value="fo" {if $item_to_translate=='fo'}selected="selected"{/if}>{intl l='Front-office templates'}</option> <option value="fo" {if $item_to_translate=='fo'}selected="selected"{/if}>{intl l='Front-office templates'}</option>
<option value="pf" {if $item_to_translate=='pf'}selected="selected"{/if}>{intl l='PDF templates'}</option> <option value="pf" {if $item_to_translate=='pf'}selected="selected"{/if}>{intl l='PDF templates'}</option>
<option value="ma" {if $item_to_translate=='ma'}selected="selected"{/if}>{intl l='E-mail templates'}</option> <option value="ma" {if $item_to_translate=='ma'}selected="selected"{/if}>{intl l='E-mail templates'}</option>
<option value="bo" {if $item_to_translate=='bo'}selected="selected"{/if}>{intl l='Back-office templates'}</option> <option value="bo" {if $item_to_translate=='bo'}selected="selected"{/if}>{intl l='Back-office templates'}</option>
<option value="mo" {if $item_to_translate=='mo'}selected="selected"{/if}>{intl l='Modules'}</option> <option value="mo" {if $item_to_translate=='mo'}selected="selected"{/if}>{intl l='Modules'}</option>
<option value="co" {if $item_to_translate=='co'}selected="selected"{/if}>{intl l='Thelia core'}</option> <option value="co" {if $item_to_translate=='co'}selected="selected"{/if}>{intl l='Thelia core'}</option>
</select> </select>
</div> </div>
</div> </div>
{if $item_to_translate == 'mo'} {if $item_to_translate == 'mo'}
<div class="col-md-4 item-id-selector"> <div class="col-md-4 item-id-selector">
<div class="form-group"> <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> <option value="">{intl l='Please select the module to translate'}</option>
{loop type="module" name="translate-module" backend_context=1} {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} {/loop}
</select> </select>
</div> </div>
</div> </div>
{else if $item_to_translate == 'fo'} {else if $item_to_translate == 'fo'}
<div class="col-md-4 item-id-selector"> <div class="col-md-4 item-id-selector">
<div class="form-group"> <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> <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} {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} {/loop}
</select> </select>
</div> </div>
</div> </div>
{else if $item_to_translate == 'bo'} {else if $item_to_translate == 'bo'}
<div class="col-md-4 item-id-selector"> <div class="col-md-4 item-id-selector">
<div class="form-group"> <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> <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} {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} {/loop}
</select> </select>
</div> </div>
</div> </div>
{else if $item_to_translate == 'ma'} {else if $item_to_translate == 'ma'}
<div class="col-md-4 item-id-selector"> <div class="col-md-4 item-id-selector">
<div class="form-group"> <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> <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} {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} {/loop}
</select> </select>
</div> </div>
</div> </div>
{else if $item_to_translate == 'pf'} {else if $item_to_translate == 'pf'}
<div class="col-md-4 item-id-selector"> <div class="col-md-4 item-id-selector">
<div class="form-group"> <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> <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} {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} {/loop}
</select> </select>
</div> </div>
</div> </div>
{/if} {/if}
{if $item_to_translate == 'mo' && $item_name != ''}
<div class="col-md-4"> <div class="col-md-4 item-id-selector">
<div class="form-group"> <div class="form-group">
<label class="control-label">&nbsp;</label> <label class="control-label" for="module_part">{intl l='Module component'}</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'} <select id="module_part" required="required" name="module_part" class="submit-on-change form-control">
</label> <option value="">{intl l='Please select the module component'}</option>
</div> <option value="core" {if $module_part == 'core'}selected="selected"{/if}>Core files</option>
</div> <option value="admin-includes" {if $module_part == 'admin-includes'}selected="selected"{/if}>Administration includes template</option>
</div> {foreach $back_office_templates as $template}
</fieldset> {$option_value = "bo.{$template}"}
<option value="{$option_value}" {if $module_part == $option_value}selected="selected"{/if}>Back-office template "{$template}"</option>
{* -- STRINGS TO TRANSLATE --------------------------------------------- *} {/foreach}
{foreach $front_office_templates as $template}
{$currfile = false} {$option_value = "fo.{$template}"}
{$close_last_panel = false} <option value="{$option_value}" {if $module_part == $option_value}selected="selected"{/if}>Front-office template "{$template}"</option>
{/foreach}
{$idx = 0} </select>
</div>
{foreach $all_strings as $info} </div>
{/if}
<input type="hidden" id="text_{$idx}" name="text[]" value="{$info.text}" /> </div>
{$not_translated = empty($info.translation)} <div class="row">
<div class="col-md-6">
{if $view_missing_traductions_only != 1 || $not_translated } <div class="form-group">
<label class="checkbox control-label">
{* Create a liste of files names *} <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>
{if count($info.files) > 1} </div>
{$label = {intl l='In pages:'}} </div>
{capture name="banner" assign=file_names} </div>
<ul> </fieldset>
{foreach $info.files as $file}
<li>{$file}</li> {* -- STRINGS TO TRANSLATE --------------------------------------------- *}
{/foreach}
</ul> {$currfile = false}
{/capture} {$close_last_panel = false}
{else}
{$label = {intl l='In page'}} {$idx = 0}
{$file_names = $info.files.0}
{/if} {foreach $all_strings as $info}
{* Display current file liste, if required *} <input type="hidden" id="text_{$idx}" name="text[]" value="{$info.text}" />
{if $file_names != $currfile} {$not_translated = empty($info.translation)}
{if $currfile !== false} {if $view_missing_traductions_only != 1 || $not_translated }
{* Close current panel *}
</tbody></table></div> {* Create a liste of files names *}
{$close_last_panel = false}
{/if} {if count($info.files) > 1}
{$label = {intl l='In pages:'}}
{$currfile = $file_names} {capture name="banner" assign=file_names}
<ul>
<div class="panel panel-default"> {foreach $info.files as $file}
<div class="panel-heading"> <li>{$file}</li>
{loop name="lang_list" type="lang" id=$edit_language_id} {/foreach}
<img src="{image file="assets/img/flags/{$CODE}.png"}" alt="{$TITLE}" /> </ul>
{/loop} {/capture}
{else}
{$label} {$file_names nofilter} {$label = {intl l='In page'}}
{$file_names = $info.files.0}
</div> {/if}
<table class="table table-condensed table-hover"> {* Display current file liste, if required *}
<tbody>
{$close_last_panel = true} {if $file_names != $currfile}
{/if}
{if $currfile !== false}
<tr> {* Close current panel *}
<td class="col-md-6"> </tbody></table></div>
{$info.text} {$close_last_panel = false}
{/if}
{if $info.dollar} {$currfile = $file_names}
<div>
<span class="label label-warning">{intl l='Warning'}</span> <div class="panel panel-default">
{intl l='Il seems that this string contains a Smarty variable (&#36;). If \'s the case, it cannot be transleted properly.'} <div class="panel-heading">
</div> {loop name="lang_list" type="lang" id=$edit_language_id}
{/if} <img src="{image file="assets/img/flags/{$CODE}.png"}" alt="{$TITLE}" />
</td> {/loop}
<td class="col-md-6" > {$label} {$file_names nofilter}
<div class="input-group {if $not_translated}has-error{/if}">
<span class="input-group-addon"><a href="#" data-text-num="{$idx}" class="copy-translation" title="{intl l='Copy source text in input field'}"><span class="glyphicon glyphicon-chevron-right"></span></a></span> </div>
<input type="text" id="translation_{$idx}" class="translation_field form-control" name="translation[]" value="{$info.translation}" />
</div> <table class="table table-condensed table-hover">
</td> <tbody>
</tr> {$close_last_panel = true}
{/if}
{else}
<tr>
{* Text is not displayed, put it in a hidden field *} <td class="col-md-6">
<input type="hidden" id="translation_{$idx}" name="translation[]" value="{$info.translation}" /> {$info.text}
{/if}
{if $info.dollar}
{$idx = $idx + 1} <div>
{/foreach} <span class="label label-warning">{intl l='Warning'}</span>
{intl l='Il seems that this string contains a Smarty variable (&#36;). If \'s the case, it cannot be transleted properly.'}
{if isset($all_strings) } </div>
{/if}
{if empty($all_strings) } </td>
<div class="alert alert-info">
{intl l='<strong>Did not found any text to translate</strong>. It\'s probably normal. If not, please be sure to use Smarty\'s "intl" function in templates, or the Translator::trans() method in PHP files.'} <td class="col-md-6" >
</div> <div class="input-group {if $not_translated}has-error{/if}">
{else if $view_missing_traductions_only == 1 && $currfile == false } <span class="input-group-addon"><a href="#" data-text-num="{$idx}" class="copy-translation" title="{intl l='Copy source text in input field'}"><span class="glyphicon glyphicon-chevron-right"></span></a></span>
<div class="alert alert-success"> <input type="text" id="translation_{$idx}" class="translation_field form-control" name="translation[]" value="{$info.translation}" />
{intl l='<strong>Congratulations</strong>, all text is now translated !'} </div>
</div> </td>
{/if} </tr>
{if $close_last_panel} {else}
{* close the last panel *}
</tbody></table></div> {* Text is not displayed, put it in a hidden field *}
{/if} <input type="hidden" id="translation_{$idx}" name="translation[]" value="{$info.translation}" />
{/if}
{/if}
{if $max_input_vars_warning}
<div class="alert alert-danger"> {$idx = $idx + 1}
{intl l='<strong>Cannot translate all fields.</strong> According to your PHP configuration, forms cannot contains more than %current_max_input_vars input fields, but at least %required_max_input_vars are required. Please change the value of max_input_vars in your PHP configuration of change the translation file by hand.' {/foreach}
current_max_input_vars=$current_max_input_vars
required_max_input_vars=$required_max_input_vars {if isset($all_strings) }
}
</div> {if empty($all_strings) }
{/if} <div class="alert alert-info">
{intl l='<strong>Did not found any text to translate</strong>. It\'s probably normal. If not, please be sure to use Smarty\'s "intl" function in templates, or the Translator::trans() method in PHP files.'}
</form> </div>
</div> {else if $view_missing_traductions_only == 1 && $currfile == false }
</div> <div class="alert alert-success">
</div> {intl l='<strong>Congratulations</strong>, all text is now translated !'}
</div> </div>
{/if}
</div>
</div> {if $close_last_panel}
</div> {* close the last panel *}
{/block} </tbody></table></div>
{/if}
{block name="javascript-initialization"} {/if}
<script>
$(function() { {if $max_input_vars_warning}
<div class="alert alert-danger">
var translation_changed = false; {intl l='<strong>Cannot translate all fields.</strong> According to your PHP configuration, forms cannot contains more than %current_max_input_vars input fields, but at least %required_max_input_vars are required. Please change the value of max_input_vars in your PHP configuration of change the translation file by hand.'
current_max_input_vars=$current_max_input_vars
$('#item_to_translate').change(function() { required_max_input_vars=$required_max_input_vars
$('#item-id').val(''); }
$('.item-id-selector').hide(); </div>
}); {/if}
$('.submit-on-change').change(function() { </form>
$('#translation_form').submit(); </div>
}); </div>
</div>
$('input.translation_field').change(function() { translation_changed = true; }); </div>
$('.copy-translation').click(function(ev) { </div>
var num = $(this).data('text-num'); </div>
</div>
$('#translation_' + num).val($('#text_' + num).val()); {/block}
translation_changed = true; {block name="javascript-initialization"}
<script>
ev.preventDefault(); $(function() {
})
var translation_changed = false;
$('#translation_form').submit(function(ev) {
$('#item_to_translate').change(function() {
if (translation_changed $('#item-id').val('');
&& $('.item-id-selector').hide();
! confirm("{intl l='Some of your translations are not saved. Continue anyway ?'}") ) { });
ev.preventDefault(); $('#item_name').change(function() {
} $('#module_part').val('');
}); });
// Intercept language changes, to reload string when changing language
$('.form-submit-button').click(function(ev) { $('.submit-on-change').change(function() {
// For a submit throught the save buttons, do not show the change warning. $('#translation_form').submit();
translation_changed = false; });
});
$('input.translation_field').change(function() { translation_changed = true; });
// Intercept language changes, to reload string when changing language
$('.language-change-button').click(function(ev) { $('.copy-translation').click(function(ev) {
var num = $(this).data('text-num');
$('input[name=edit_language_id]').val($(this).data('language-id'));
$('#translation_' + num).val($('#text_' + num).val());
$('#translation_form').submit();
translation_changed = true;
ev.preventDefault();
}); ev.preventDefault();
}); })
</script>
{/block} $('#translation_form').submit(function(ev) {
{block name="javascript-last-call"} if (translation_changed
{module_include location='translations-js'} &&
{/block} ! confirm("{intl l='Some of your translations are not saved. Continue anyway ?'}") ) {
ev.preventDefault();
}
});
// Intercept language changes, to reload string when changing language
$('.form-submit-button').click(function(ev) {
// For a submit throught the save buttons, do not show the change warning.
translation_changed = false;
});
// Intercept language changes, to reload string when changing language
$('.language-change-button').click(function(ev) {
$('input[name=edit_language_id]').val($(this).data('language-id'));
$('#translation_form').submit();
ev.preventDefault();
});
});
</script>
{/block}
{block name="javascript-last-call"}
{module_include location='translations-js'}
{/block}

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"} {form name="thelia.order.delivery"}
{loop type="delivery" name="deliveries" force_return="true" country=$country} {loop type="delivery" name="deliveries" force_return="true" country=$country}

View File

@@ -1,449 +1,451 @@
<!doctype html> <!doctype html>
<!-- <!--
______ __ __ ______ __ __ ______ ______ __ __ ______ __ __ ______
/\__ _\ /\ \_\ \ /\ ___\ /\ \ /\ \ /\ __ \ /\__ _\ /\ \_\ \ /\ ___\ /\ \ /\ \ /\ __ \
\/_/\ \/ \ \ __ \ \ \ __\ \ \ \____ \ \ \ \ \ __ \ \/_/\ \/ \ \ __ \ \ \ __\ \ \ \____ \ \ \ \ \ __ \
\ \_\ \ \_\ \_\ \ \_____\ \ \_____\ \ \_\ \ \_\ \_\ \ \_\ \ \_\ \_\ \ \_____\ \ \_____\ \ \_\ \ \_\ \_\
\/_/ \/_/\/_/ \/_____/ \/_____/ \/_/ \/_/\/_/ \/_/ \/_/\/_/ \/_____/ \/_____/ \/_/ \/_/\/_/
Copyright (c) OpenStudio Copyright (c) OpenStudio
email : info@thelia.net email : info@thelia.net
web : http://www.thelia.net web : http://www.thelia.net
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the the Free Software Foundation; either version 3 of the
GNU General Public License : http://www.gnu.org/licenses/ GNU General Public License : http://www.gnu.org/licenses/
--> -->
{* Declare assets directory, relative to template base directory *} {* Declare assets directory, relative to template base directory *}
{declare_assets directory='assets'} {declare_assets directory='assets'}
{block name="no-return-functions"}{/block} {* Set the default translation domain, that will be used by {intl} when the 'd' parameter is not set *}
{assign var="store_name" value="{config key="store_name"}"} {default_translation_domain domain='fo.default'}
{if not $store_name}{assign var="store_name" value="{intl l='Thelia V2'}"}{/if} {block name="no-return-functions"}{/block}
{assign var="store_name" value="{config key="store_name"}"}
{* paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither *} {if not $store_name}{assign var="store_name" value="{intl l='Thelia V2'}"}{/if}
<!--[if lt IE 7 ]><html class="no-js oldie ie6" lang="{lang attr="code"}"> <![endif]-->
<!--[if IE 7 ]><html class="no-js oldie ie7" lang="{lang attr="code"}"> <![endif]--> {* paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither *}
<!--[if IE 8 ]><html class="no-js oldie ie8" lang="{lang attr="code"}"> <![endif]--> <!--[if lt IE 7 ]><html class="no-js oldie ie6" lang="{lang attr="code"}"> <![endif]-->
<!--[if (gte IE 9)|!(IE)]><!--><html lang="{lang attr="code"}" class="no-js"> <!--<![endif]--> <!--[if IE 7 ]><html class="no-js oldie ie7" lang="{lang attr="code"}"> <![endif]-->
<head> <!--[if IE 8 ]><html class="no-js oldie ie8" lang="{lang attr="code"}"> <![endif]-->
<!--[if (gte IE 9)|!(IE)]><!--><html lang="{lang attr="code"}" class="no-js"> <!--<![endif]-->
{* Test if javascript is enabled *} <head>
<script>(function(H) { H.className=H.className.replace(/\bno-js\b/,'js') } )(document.documentElement);</script>
{* Test if javascript is enabled *}
<meta charset="utf-8"> <script>(function(H) { H.className=H.className.replace(/\bno-js\b/,'js') } )(document.documentElement);</script>
{* Page Title *} <meta charset="utf-8">
<title>{block name="page-title"}{strip}{if $page_title}{$page_title}{elseif $breadcrumbs}{foreach from=$breadcrumbs|array_reverse item=breadcrumb}{$breadcrumb.title|unescape} - {/foreach}{$store_name}{/if}{/strip}{/block}</title>
{* Page Title *}
{* Meta Tags *} <title>{block name="page-title"}{strip}{if $page_title}{$page_title}{elseif $breadcrumbs}{foreach from=$breadcrumbs|array_reverse item=breadcrumb}{$breadcrumb.title|unescape} - {/foreach}{$store_name}{/if}{/strip}{/block}</title>
<meta name="generator" content="{intl l='Thelia V2'}">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> {* Meta Tags *}
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="generator" content="{intl l='Thelia V2'}">
{block name="meta"} <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" content="{if $page_description}{$page_description}{else}{$store_name}{/if}"> <meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta name="robots" content="noindex,nofollow"> {block name="meta"}
{/block} <meta name="description" content="{if $page_description}{$page_description}{else}{$store_name}{/if}">
<meta name="robots" content="noindex,nofollow">
{* Stylesheets *} {/block}
{stylesheets file='assets/css/styles.css'}
<link rel="stylesheet" href="{$asset_url}"> {* Stylesheets *}
{/stylesheets} {stylesheets file='assets/css/styles.css'}
<link rel="stylesheet" href="{$asset_url}">
{block name="stylesheet"}{/block} {/stylesheets}
{* Favicon *} {block name="stylesheet"}{/block}
{images file='assets/img/favicon.ico'}<link rel="shortcut icon" type="image/x-icon" href="{$asset_url}">{/images}
{images file='assets/img/favicon.png'}<link rel="icon" type="image/png" href="{$asset_url}" />{/images} {* Favicon *}
{images file='assets/img/favicon.ico'}<link rel="shortcut icon" type="image/x-icon" href="{$asset_url}">{/images}
{* HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries *} {images file='assets/img/favicon.png'}<link rel="icon" type="image/png" href="{$asset_url}" />{/images}
<!--[if lt IE 9]>
<script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script> {* HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries *}
<script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script> <!--[if lt IE 9]>
<![endif]--> <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
</head> <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
<![endif]-->
<body class="{block name="body-class"}{/block}" itemscope itemtype="http://schema.org/WebPage"> </head>
<!-- Accessibility --> <body class="{block name="body-class"}{/block}" itemscope itemtype="http://schema.org/WebPage">
<a class="sr-only" href="#content">{intl l="Skip to content"}</a>
<!-- Accessibility -->
<div class="page" role="document"> <a class="sr-only" href="#content">{intl l="Skip to content"}</a>
<div class="header-container" itemscope itemtype="http://schema.org/WPHeader"> <div class="page" role="document">
<div class="navbar" itemscope itemtype="http://schema.org/SiteNavigationElement"> <div class="header-container" itemscope itemtype="http://schema.org/WPHeader">
<div class="container">
<div class="navbar" itemscope itemtype="http://schema.org/SiteNavigationElement">
<div class="navbar-header"> <div class="container">
<!-- .navbar-toggle is used as the toggle for collapsed navbar content -->
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".nav-main"> <div class="navbar-header">
<span class="sr-only">{intl l="Toggle navigation"}</span> <!-- .navbar-toggle is used as the toggle for collapsed navbar content -->
<span class="icon-bar"></span> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".nav-main">
<span class="icon-bar"></span> <span class="sr-only">{intl l="Toggle navigation"}</span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> <span class="icon-bar"></span>
<a class="navbar-brand" href="{navigate to="index"}">{$store_name}</a> <span class="icon-bar"></span>
</div> </button>
<a class="navbar-brand" href="{navigate to="index"}">{$store_name}</a>
<!-- Place everything within .nav-collapse to hide it until above 768px --> </div>
<nav class="navbar-collapse collapse nav-main" role="navigation" aria-label="{intl l="Main Navigation"}">
{nocache} <!-- Place everything within .nav-collapse to hide it until above 768px -->
<ul class="nav navbar-nav navbar-cart navbar-right"> <nav class="navbar-collapse collapse nav-main" role="navigation" aria-label="{intl l="Main Navigation"}">
{loop type="auth" name="customer_info_block" role="CUSTOMER"} {nocache}
<li><a href="{url path="/logout"}" class="logout">{intl l="Log out!"}</a></li> <ul class="nav navbar-nav navbar-cart navbar-right">
<li><a href="{url path="/account"}" class="account">{intl l="My Account"}</a></li> {loop type="auth" name="customer_info_block" role="CUSTOMER"}
{/loop} <li><a href="{url path="/logout"}" class="logout">{intl l="Log out!"}</a></li>
{elseloop rel="customer_info_block"} <li><a href="{url path="/account"}" class="account">{intl l="My Account"}</a></li>
<li><a href="{url path="/register"}" class="register">{intl l="Register!"}</a></li> {/loop}
<li class="dropdown"> {elseloop rel="customer_info_block"}
<a href="{url path="/login"}" class="login">{intl l="Log In!"}</a> <li><a href="{url path="/register"}" class="register">{intl l="Register!"}</a></li>
<div class="dropdown-menu"> <li class="dropdown">
{form name="thelia.front.customer.login"} <a href="{url path="/login"}" class="login">{intl l="Log In!"}</a>
<form id="form-login-mini" action="{url path="/login"}" method="post" {form_enctype form=$form}> <div class="dropdown-menu">
{form_hidden_fields form=$form} {form name="thelia.front.customer.login"}
{form_field form=$form field="success_url"} <form id="form-login-mini" action="{url path="/login"}" method="post" {form_enctype form=$form}>
<input type="hidden" name="{$name}" value="{navigate to="current"}"> {form_hidden_fields form=$form}
{/form_field} {form_field form=$form field="success_url"}
{form_field form=$form field="email"} <input type="hidden" name="{$name}" value="{navigate to="current"}">
<div class="form-group group-email"> {/form_field}
<label for="{$label_attr.for}-mini">{intl l="Email address"}</label> {form_field form=$form field="email"}
<input type="email" name="{$name}" id="{$label_attr.for}-mini" class="form-control" maxlength="255" aria-required="true" required> <div class="form-group group-email">
</div> <label for="{$label_attr.for}-mini">{intl l="Email address"}</label>
{/form_field} <input type="email" name="{$name}" id="{$label_attr.for}-mini" class="form-control" maxlength="255" aria-required="true" required>
{form_field form=$form field="password"} </div>
<div class="form-group group-password"> {/form_field}
<label for="{$label_attr.for}-mini">{intl l="Password"}</label> {form_field form=$form field="password"}
<input type="password" name="{$name}" id="{$label_attr.for}-mini" class="form-control" maxlength="255" aria-required="true" required> <div class="form-group group-password">
</div> <label for="{$label_attr.for}-mini">{intl l="Password"}</label>
{/form_field} <input type="password" name="{$name}" id="{$label_attr.for}-mini" class="form-control" maxlength="255" aria-required="true" required>
{form_field form=$form field="account"} </div>
<input type="hidden" name="{$name}" value="1"> {/form_field}
{/form_field} {form_field form=$form field="account"}
<div class="group-btn"> <input type="hidden" name="{$name}" value="1">
<button type="submit" class="btn btn-login-mini">{intl l="Sign In"}</button> {/form_field}
<a href="{url path="/register"}" class="btn btn-register-mini">{intl l="Register"}</a> <div class="group-btn">
</div> <button type="submit" class="btn btn-login-mini">{intl l="Sign In"}</button>
</form> <a href="{url path="/register"}" class="btn btn-register-mini">{intl l="Register"}</a>
{/form} </div>
</div> </form>
</li> {/form}
{/elseloop} </div>
{include file="includes/mini-cart.html" nocache} </li>
</ul> {/elseloop}
{/nocache} {include file="includes/mini-cart.html" nocache}
<ul class="nav navbar-nav navbar-categories"> </ul>
<li><a href="{navigate to="index"}" class="home">{intl l="Home"}</a></li> {/nocache}
{loop type="category" name="category.navigation" parent="0"} <ul class="nav navbar-nav navbar-categories">
<li><a href="{$URL}">{$TITLE}</a></li> <li><a href="{navigate to="index"}" class="home">{intl l="Home"}</a></li>
{/loop} {loop type="category" name="category.navigation" parent="0"}
</ul> <li><a href="{$URL}">{$TITLE}</a></li>
</nav> {/loop}
</div> </ul>
</div> </nav>
</div>
</div>
<header class="container" role="banner">
<div class="header">
<h1 class="logo"> <header class="container" role="banner">
<a href="{navigate to="index"}" title="{$store_name}"> <div class="header">
{images file='assets/img/logo.gif'}<img src="{$asset_url}" alt="{$store_name}">{/images} <h1 class="logo">
</a> <a href="{navigate to="index"}" title="{$store_name}">
</h1> {images file='assets/img/logo.gif'}<img src="{$asset_url}" alt="{$store_name}">{/images}
</a>
<div class="language-container"> </h1>
<div class="search-container"> <div class="language-container">
<form id="form-search" action="{url path="/search"}" method="get" role="search" aria-labelledby="search-label">
<label id="search-label" for="q">{intl l="Search a product"}</label> <div class="search-container">
<div class="input-group"> <form id="form-search" action="{url path="/search"}" method="get" role="search" aria-labelledby="search-label">
<input type="search" name="q" id="q" placeholder="{intl l="Search..."}" class="form-control" autocomplete="off" aria-required="true" required pattern=".{ldelim}2,{rdelim}" title="{intl l="Minimum 2 characters."}"> <label id="search-label" for="q">{intl l="Search a product"}</label>
<div class="input-group-btn"> <div class="input-group">
<button type="submit" class="btn btn-search"><i class="icon-search"></i> <span>{intl l="Search"}</span></button> <input type="search" name="q" id="q" placeholder="{intl l="Search..."}" class="form-control" autocomplete="off" aria-required="true" required pattern=".{ldelim}2,{rdelim}" title="{intl l="Minimum 2 characters."}">
</div> <div class="input-group-btn">
</div> <button type="submit" class="btn btn-search"><i class="icon-search"></i> <span>{intl l="Search"}</span></button>
</form> </div>
</div> </div>
</form>
<div class="language-switch" aria-labelledby="language-label" role="form"> </div>
<span id="language-label" class="dropdown-label">{intl l="Language:"}</span>
<a class="current dropdown-toggle" data-toggle="dropdown" href="{url path="/language"}">{lang attr="title"}</a> <div class="language-switch" aria-labelledby="language-label" role="form">
<ul class="select dropdown-menu"> <span id="language-label" class="dropdown-label">{intl l="Language:"}</span>
{loop type="lang" name="lang_available" exclude="{lang attr="id"}"} <a class="current dropdown-toggle" data-toggle="dropdown" href="{url path="/language"}">{lang attr="title"}</a>
<li><a href="{url path="{navigate to="current"}" lang={$CODE}}">{$TITLE}</a></li> <ul class="select dropdown-menu">
{/loop} {loop type="lang" name="lang_available" exclude="{lang attr="id"}"}
</ul> <li><a href="{url path="{navigate to="current"}" lang={$CODE}}">{$TITLE}</a></li>
</div> {/loop}
</ul>
<div class="currency-switch" aria-labelledby="currency-label" role="form"> </div>
<span id="currency-label" class="dropdown-label">{intl l="Currency:"}</span>
<a class="current dropdown-toggle" data-toggle="dropdown" href="{url path="/currency"}">{currency attr="code"}</a> <div class="currency-switch" aria-labelledby="currency-label" role="form">
<ul class="select dropdown-menu"> <span id="currency-label" class="dropdown-label">{intl l="Currency:"}</span>
{loop type="currency" name="currency_available" exclude="{currency attr="id"}" } <a class="current dropdown-toggle" data-toggle="dropdown" href="{url path="/currency"}">{currency attr="code"}</a>
<li><a href="{url path="{navigate to="current"}" currency={$ISOCODE}}">{$SYMBOL} - {$NAME}</a></li> <ul class="select dropdown-menu">
{/loop} {loop type="currency" name="currency_available" exclude="{currency attr="id"}" }
</ul> <li><a href="{url path="{navigate to="current"}" currency={$ISOCODE}}">{$SYMBOL} - {$NAME}</a></li>
</div> {/loop}
</div> </ul>
</div> </div>
</div>
</header><!-- /.header --> </div>
</div><!-- /.header-container --> </header><!-- /.header -->
<main class="main-container" role="main"> </div><!-- /.header-container -->
<div class="container">
{block name="breadcrumb"}{include file="misc/breadcrumb.tpl"}{/block} <main class="main-container" role="main">
<div id="content">{block name="main-content"}{/block}</div> <div class="container">
</div><!-- /.container --> {block name="breadcrumb"}{include file="misc/breadcrumb.tpl"}{/block}
</main><!-- /.main-container --> <div id="content">{block name="main-content"}{/block}</div>
</div><!-- /.container -->
<section class="footer-container" itemscope itemtype="http://schema.org/WPFooter"> </main><!-- /.main-container -->
<section class="footer-banner"> <section class="footer-container" itemscope itemtype="http://schema.org/WPFooter">
<div class="container">
<div class="banner banner-col-3"> <section class="footer-banner">
<div class="col"> <div class="container">
<span class="icon-truck icon-flip-horizontal"></span> <div class="banner banner-col-3">
{intl l="Free shipping"} <small>{intl l="Orders over $50"}</small> <div class="col">
</div> <span class="icon-truck icon-flip-horizontal"></span>
<div class="col"> {intl l="Free shipping"} <small>{intl l="Orders over $50"}</small>
<span class="icon-credit-card"></span> </div>
{intl l="Secure payment"} <small>{intl l="Multi-payment platform"}</small> <div class="col">
</div> <span class="icon-credit-card"></span>
<div class="col"> {intl l="Secure payment"} <small>{intl l="Multi-payment platform"}</small>
<span class="icon-info"></span> </div>
{intl l="Need help ?"} <small>{intl l="Questions ? See our F.A.Q."}</small> <div class="col">
</div> <span class="icon-info"></span>
</div> {intl l="Need help ?"} <small>{intl l="Questions ? See our F.A.Q."}</small>
</div> </div>
</section><!-- /.footer-banner --> </div>
</div>
<section class="footer-block"> </section><!-- /.footer-banner -->
<div class="container">
<div class="blocks block-col-4"> <section class="footer-block">
<div class="col"> <div class="container">
<section class="block block-links"> <div class="blocks block-col-4">
<div class="block-heading"><h3 class="block-title">{intl l="Latest articles"}</h3></div> <div class="col">
<div class="block-content"> <section class="block block-links">
{ifloop rel="blog.articles"} <div class="block-heading"><h3 class="block-title">{intl l="Latest articles"}</h3></div>
<ul> <div class="block-content">
{loop type="content" name="blog.articles" folder="1" limit="3"} {ifloop rel="blog.articles"}
<li> <ul>
<a href="{$URL}"> {loop type="content" name="blog.articles" folder="1" limit="3"}
<h4 class="block-subtitle">{$TITLE}</h4> <li>
<p>{$CHAPO}</p> <a href="{$URL}">
</a> <h4 class="block-subtitle">{$TITLE}</h4>
</li> <p>{$CHAPO}</p>
{/loop} </a>
</ul> </li>
{/ifloop} {/loop}
{elseloop rel="blog.articles"} </ul>
<ul> {/ifloop}
<li>{intl l="No articles currently"}</li> {elseloop rel="blog.articles"}
</ul> <ul>
{/elseloop} <li>{intl l="No articles currently"}</li>
</div> </ul>
</section> {/elseloop}
</div> </div>
<div class="col"> </section>
<section class="block block-default"> </div>
<div class="block-heading"><h3 class="block-title">{intl l="Useful links"}</h3></div> <div class="col">
<div class="block-content"> <section class="block block-default">
<ul> <div class="block-heading"><h3 class="block-title">{intl l="Useful links"}</h3></div>
{loop name="footer_links" type="content" folder="2"} <div class="block-content">
<li><a href="{$URL}">{$TITLE}</a></li> <ul>
{/loop} {loop name="footer_links" type="content" folder="2"}
{loop type="auth" name="customer_is_logged" role="CUSTOMER"} <li><a href="{$URL}">{$TITLE}</a></li>
<li><a href="{url path="/logout"}" class="logout">{intl l="Log out!"}</a></li> {/loop}
<li><a href="{url path="/account"}" class="account">{intl l="My Account"}</a></li> {loop type="auth" name="customer_is_logged" role="CUSTOMER"}
{/loop} <li><a href="{url path="/logout"}" class="logout">{intl l="Log out!"}</a></li>
{elseloop rel="customer_is_logged"} <li><a href="{url path="/account"}" class="account">{intl l="My Account"}</a></li>
<li><a href="{url path="/login"}">{intl l="Login"}</a></li> {/loop}
<li><a href="{url path="/register"}">{intl l="Register"}</a></li> {elseloop rel="customer_is_logged"}
{/elseloop} <li><a href="{url path="/login"}">{intl l="Login"}</a></li>
<li><a href="{url path="/cart"}">{intl l="Cart"}</a></li> <li><a href="{url path="/register"}">{intl l="Register"}</a></li>
<li><a href="{url path="/order/delivery"}">{intl l="Checkout"}</a></li> {/elseloop}
</ul> <li><a href="{url path="/cart"}">{intl l="Cart"}</a></li>
</div> <li><a href="{url path="/order/delivery"}">{intl l="Checkout"}</a></li>
</section> </ul>
</div> </div>
<div class="col"> </section>
<section class="block block-social"> </div>
<div class="block-heading"><h3 class="block-title">{intl l="Follow us"}</h3></div> <div class="col">
<div class="block-content"> <section class="block block-social">
<p>{intl l="Follow us introduction"}</p> <div class="block-heading"><h3 class="block-title">{intl l="Follow us"}</h3></div>
<ul role="presentation"> <div class="block-content">
<li> <p>{intl l="Follow us introduction"}</p>
<a href="http://facebook.com" rel="nofollow" class="facebook" data-toggle="tooltip" data-placement="top" title="{intl l="Facebook"}" target="_blank"> <ul role="presentation">
<span class="icon-stack"> <li>
<span class="icon-circle icon-stack-base"></span> <a href="http://facebook.com" rel="nofollow" class="facebook" data-toggle="tooltip" data-placement="top" title="{intl l="Facebook"}" target="_blank">
<span class="icon-facebook icon-light"></span> <span class="icon-stack">
</span> <span class="icon-circle icon-stack-base"></span>
<span class="visible-print">{intl l="Facebook"}</span> <span class="icon-facebook icon-light"></span>
</a> </span>
</li> <span class="visible-print">{intl l="Facebook"}</span>
<li> </a>
<a href="https://twitter.com" rel="nofollow" class="twitter" data-toggle="tooltip" data-placement="top" title="{intl l="Twitter"}" target="_blank"> </li>
<span class="icon-stack"> <li>
<span class="icon-circle icon-stack-base"></span> <a href="https://twitter.com" rel="nofollow" class="twitter" data-toggle="tooltip" data-placement="top" title="{intl l="Twitter"}" target="_blank">
<span class="icon-twitter icon-light"></span> <span class="icon-stack">
</span> <span class="icon-circle icon-stack-base"></span>
<span class="visible-print">{intl l="Twitter"}</span> <span class="icon-twitter icon-light"></span>
</a> </span>
</li> <span class="visible-print">{intl l="Twitter"}</span>
<li> </a>
<a href="http://instagram.com" rel="nofollow" class="instagram" data-toggle="tooltip" data-placement="top" title="{intl l="Instagram"}" target="_blank"> </li>
<span class="icon-stack"> <li>
<span class="icon-circle icon-stack-base"></span> <a href="http://instagram.com" rel="nofollow" class="instagram" data-toggle="tooltip" data-placement="top" title="{intl l="Instagram"}" target="_blank">
<span class="icon-instagram icon-light"></span> <span class="icon-stack">
</span> <span class="icon-circle icon-stack-base"></span>
<span class="visible-print">{intl l="Instagram"}</span> <span class="icon-instagram icon-light"></span>
</a> </span>
</li> <span class="visible-print">{intl l="Instagram"}</span>
<li> </a>
<a href="http://www.google.com" rel="nofollow" class="google-plus" data-toggle="tooltip" data-placement="top" title="{intl l="Google+"}" target="_blank"> </li>
<span class="icon-stack"> <li>
<span class="icon-circle icon-stack-base"></span> <a href="http://www.google.com" rel="nofollow" class="google-plus" data-toggle="tooltip" data-placement="top" title="{intl l="Google+"}" target="_blank">
<span class="icon-google-plus icon-light"></span> <span class="icon-stack">
</span> <span class="icon-circle icon-stack-base"></span>
<span class="visible-print">{intl l="Google+"}</span> <span class="icon-google-plus icon-light"></span>
</a> </span>
</li> <span class="visible-print">{intl l="Google+"}</span>
<li> </a>
<a href="http://www.youtube.com" rel="nofollow" class="youtube" data-toggle="tooltip" data-placement="top" title="{intl l="Youtube"}" target="_blank"> </li>
<span class="icon-stack"> <li>
<span class="icon-circle icon-stack-base"></span> <a href="http://www.youtube.com" rel="nofollow" class="youtube" data-toggle="tooltip" data-placement="top" title="{intl l="Youtube"}" target="_blank">
<span class="icon-youtube icon-light"></span> <span class="icon-stack">
</span> <span class="icon-circle icon-stack-base"></span>
<span class="visible-print">{intl l="Youtube"}</span> <span class="icon-youtube icon-light"></span>
</a> </span>
</li> <span class="visible-print">{intl l="Youtube"}</span>
<li> </a>
<a href="#rss" class="rss" rel="nofollow" data-toggle="tooltip" data-placement="top" title="{intl l="RSS"}" target="_blank"> </li>
<span class="icon-stack"> <li>
<span class="icon-circle icon-stack-base"></span> <a href="#rss" class="rss" rel="nofollow" data-toggle="tooltip" data-placement="top" title="{intl l="RSS"}" target="_blank">
<span class="icon-rss icon-light"></span> <span class="icon-stack">
</span> <span class="icon-circle icon-stack-base"></span>
<span class="visible-print">{intl l="RSS"}</span> <span class="icon-rss icon-light"></span>
</a> </span>
</li> <span class="visible-print">{intl l="RSS"}</span>
</ul> </a>
</div> </li>
</section> </ul>
</div>
<section class="block block-newsletter"> </section>
<div class="block-heading"><h3 class="block-title">{intl l="Newsletter"}</h3></div>
<div class="block-content"> <section class="block block-newsletter">
<p id="newsletter-describe">{intl l="Sign up to receive our latest news."}</p> <div class="block-heading"><h3 class="block-title">{intl l="Newsletter"}</h3></div>
{form name="thelia.front.newsletter"} <div class="block-content">
<form id="form-newsletter-mini" action="{url path="/newsletter"}" method="post"> <p id="newsletter-describe">{intl l="Sign up to receive our latest news."}</p>
{form_hidden_fields form=$form} {form name="thelia.front.newsletter"}
{form_field form=$form field="email"} <form id="form-newsletter-mini" action="{url path="/newsletter"}" method="post">
<div class="form-group"> {form_hidden_fields form=$form}
<label for="{$label_attr.for}-mini">{intl l="Email address"}</label> {form_field form=$form field="email"}
<input type="email" name="{$name}" id="{$label_attr.for}-mini" class="form-control" maxlength="255" placeholder="{intl l="Your email address"}" aria-describedby="newsletter-describe" {if $required} aria-required="true" required{/if} autocomplete="off"> <div class="form-group">
</div> <label for="{$label_attr.for}-mini">{intl l="Email address"}</label>
{/form_field} <input type="email" name="{$name}" id="{$label_attr.for}-mini" class="form-control" maxlength="255" placeholder="{intl l="Your email address"}" aria-describedby="newsletter-describe" {if $required} aria-required="true" required{/if} autocomplete="off">
<button type="submit" class="btn btn-subscribe">{intl l="Subscribe"}</button> </div>
</form> {/form_field}
{/form} <button type="submit" class="btn btn-subscribe">{intl l="Subscribe"}</button>
</div> </form>
</section> {/form}
</div> </div>
</section>
<div class="col"> </div>
<section class="block block-contact" itemscope itemtype="http://schema.org/Organization">
<div class="block-heading"><h3 class="block-title">{intl l="Contact Us"}</h3></div> <div class="col">
<div class="block-content"> <section class="block block-contact" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="{$store_name}"> <div class="block-heading"><h3 class="block-title">{intl l="Contact Us"}</h3></div>
<ul> <div class="block-content">
<li class="contact-address"> <meta itemprop="name" content="{$store_name}">
<address class="adr" itemprop="address" itemscope itemtype="http://schema.org/PostalAddress"> <ul>
<span class="street-address" itemprop="streetAddress">{config key="store_address1"} {config key="store_address2"} {config key="store_address3"}</span><br> <li class="contact-address">
<span class="postal-code" itemprop="postalCode">{config key="store_zipcode"}</span> <address class="adr" itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">
<span class="locality" itemprop="addressLocality"> <span class="street-address" itemprop="streetAddress">{config key="store_address1"} {config key="store_address2"} {config key="store_address3"}</span><br>
{config key="store_city"} <span class="postal-code" itemprop="postalCode">{config key="store_zipcode"}</span>
{if {config key="store_country"} } <span class="locality" itemprop="addressLocality">
{loop type="country" name="address.country.title" id={config key="store_country"}}, <span class="country-name">{$TITLE}</span>{/loop} {config key="store_city"}
{/if} {if {config key="store_country"} }
</span> {loop type="country" name="address.country.title" id={config key="store_country"}}, <span class="country-name">{$TITLE}</span>{/loop}
</address> {/if}
</li> </span>
{if {config key="store_phone"} } </address>
<li class="contact-phone"> </li>
<span class="tel" itemprop="telephone">{config key="store_phone"}</span> {if {config key="store_phone"} }
</li> <li class="contact-phone">
{/if} <span class="tel" itemprop="telephone">{config key="store_phone"}</span>
{if {config key="store_email"} } </li>
<li class="contact-email"> {/if}
{mailto address={config key="store_email"} encode="hex" extra='class="email" itemprop="email"'} {if {config key="store_email"} }
</li> <li class="contact-email">
{/if} {mailto address={config key="store_email"} encode="hex" extra='class="email" itemprop="email"'}
</ul> </li>
</div> {/if}
</section> </ul>
</div> </div>
</div> </section>
</div> </div>
</section><!-- /.footer-block --> </div>
</div>
</section><!-- /.footer-block -->
<footer class="footer-info" role="contentinfo">
<div class="container">
<div class="info"> <footer class="footer-info" role="contentinfo">
<nav class="nav-footer" role="navigation"> <div class="container">
<ul> <div class="info">
{loop name="footer_links" type="content" folder="2"} <nav class="nav-footer" role="navigation">
<li><a href="{$URL}">{$TITLE}</a></li> <ul>
{/loop} {loop name="footer_links" type="content" folder="2"}
{*<li><a href="#">Site Map</a></li> <li><a href="{$URL}">{$TITLE}</a></li>
<li><a href="#">Terms & Conditions</a></li>*} {/loop}
<li><a href="{url path="/contact"}">{intl l="Contact Us"}</a></li> {*<li><a href="#">Site Map</a></li>
</ul> <li><a href="#">Terms & Conditions</a></li>*}
</nav> <li><a href="{url path="/contact"}">{intl l="Contact Us"}</a></li>
</ul>
<section class="copyright">{intl l="Copyright"} &copy; <time datetime="{'Y-m-d'|date}">{'Y'|date}</time> <a href="http://thelia.net" rel="external">Thelia</a></section> </nav>
</div>
</div> <section class="copyright">{intl l="Copyright"} &copy; <time datetime="{'Y-m-d'|date}">{'Y'|date}</time> <a href="http://thelia.net" rel="external">Thelia</a></section>
</footer><!-- /.footer-info --> </div>
</div>
</section><!-- /.footer-container --> </footer><!-- /.footer-info -->
</div><!-- /.page --> </section><!-- /.footer-container -->
{block name="before-javascript-include"}{/block} </div><!-- /.page -->
<!-- JavaScript -->
<!--[if lt IE 9]><script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script> <![endif]--> {block name="before-javascript-include"}{/block}
<!--[if (gte IE 9)|!(IE)]><!--><script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script> <!--<![endif]--> <!-- JavaScript -->
<script> <!--[if lt IE 9]><script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script> <![endif]-->
if (typeof jQuery == 'undefined') { <!--[if (gte IE 9)|!(IE)]><!--><script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script> <!--<![endif]-->
{javascripts file='assets/js/libs/jquery.js'} <script>
document.write(unescape("%3Cscript src='{$asset_url}' %3E%3C/script%3E")); if (typeof jQuery == 'undefined') {
{/javascripts} {javascripts file='assets/js/libs/jquery.js'}
} document.write(unescape("%3Cscript src='{$asset_url}' %3E%3C/script%3E"));
</script> {/javascripts}
}
<script src="//ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.min.js"></script> </script>
<script src="//ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.min.js"></script>
{javascripts file='assets/js/bootstrap/bootstrap.js'}
<script src="{$asset_url}"></script>
{/javascripts} {javascripts file='assets/js/bootstrap/bootstrap.js'}
<script src="{$asset_url}"></script>
{javascripts file='assets/js/plugins/bootbox/bootbox.js'} {/javascripts}
<script src="{$asset_url}"></script>
{/javascripts} {javascripts file='assets/js/plugins/bootbox/bootbox.js'}
<script src="{$asset_url}"></script>
{block name="after-javascript-include"}{/block} {/javascripts}
{block name="javascript-initialization"}{/block} {block name="after-javascript-include"}{/block}
<!-- Custom scripts --> {block name="javascript-initialization"}{/block}
{javascripts file='assets/js/script.js'}
<script src="{$asset_url}"></script> <!-- Custom scripts -->
{/javascripts} {javascripts file='assets/js/script.js'}
</body> <script src="{$asset_url}"></script>
{/javascripts}
</body>
</html> </html>