Magento’s Custom Variables Anywhere

Magento’s wiki states that: “CMS content cannot simply be translated in the same way as catalog content. Duplicate pages, blocks, banners and polls must be created for each language.”

The problem is that separate CMS pages would have different URL keys. This creates several complications:

1. Having separate pages with the same content isn’t advised. It’s not so much the false belief that Google frowns upon websites that contain duplicate content (it’s only dissallowed at a disproportional scale that would indicate this is used as a grey-hat SEO trick). It’s more a matter of directing more users to the same pages so as to promote their relevance in Google’s eyes.

2. Locale-dependant url codes make it hard to link to them in other places in the store. For example, a footer link to such a url means you’ll need a locale-based footer block too.

3. If the only difference between store views are those CMS pages, then what’s the point of having to create a separate sitemap.xml file for each locale?

To address these issues in my own store, I hacked Magento’s core to handle custom variables everywhere, and not just through its WYSIWYG editor. The code below is an unintrucive version that uses Magento’s event observers to react to a page rendering and scan for Magento’s custom variables syntax.

For those of you not interested in the tenchnical details, I created a packaged extension that supplies this functionality. It’s available to download for free on my professional website.

For those interested in the technicalities, you’ll notice below that I hook into the response object instead of parsing the individual blocks’ toHtml() methods. It seems surprising because individual blocks are cached in Magento CE, whereas whole pages are not, and it would have saved some parsing time if I were to hook into a method that only runs when there’s no cache available. The thing is, I tested it using Magento’s profiler and it simply doesn’t work as expected. If anyone has any insights as to why, I’d really appreciate a comment.

Either way, to get this working on your own store, create the XML file for the extension here:

ROOT/app/etc/modules/Namespace_Module.xml


<?xml version="1.0"?>
<config>
  <modules>
    <Namespace_Module>
      <active>true</active>
      <codePool>local</codePool>
    </Namespace_Module>
  </modules>
</config> 

Then create the XML file for your extension’s configurations, in which we’ll register our event observer, here:

ROOT/app/code/local/Namespace/Module/etc/config.xml


<?xml version="1.0"?>
<config>
  <modules>
    <Namespace_Module>
      <version>0.1.0</version>
    </Namespace_Module>
  </modules> 
  <global>
    <helpers>
      <variables>
        <class>Namespace_Module_Helper</class>
      </variables>
    </helpers>
    <events>
      <controller_front_send_response_before>
        <observers>
          <namespace_module>
            <type>singleton</type>
            <class>
              Namespace_Module_Helper_Observer
            </class>
            <method>parseCustomVars</method>
          </namespace_module>
        </observers>
      </controller_front_send_response_before>  
    </events>
  </global>
</config>

Last but not least, create the actual observer function inside the helper class, here:

ROOT/app/code/local/Namespace/Module/Helper/Observer.php


<?php
class Namespace_Module_Helper_Observer extends Mage_Core_Helper_Abstract
{
  public function parseCustomVars($observer)
  {
    $response = $observer->getEvent()->getFront()->getResponse();
    $html = $response->getBody();

    $callback = function($matches) {
      $var = Mage::getModel('core/variable');
      $var->setStoreId(Mage::app()->getStore()->getId());
      return $var->loadByCode($matches[1])->getValue('html');
    };

    if (!$this->isAdmin()) {
      $html = preg_replace_callback(
        "/{{customVar code=(.*)}}/U",
        $callback,
        $html
      );
    }
    $response->setBody($html);
    Mage::app()->setResponse($response);

    return $this;
  }

  public function isAdmin()
  {
    if(Mage::app()->getStore()->isAdmin())
    {
      return true;
    }

    if(Mage::getDesign()->getArea() == 'adminhtml')
    {
      return true;
    }

    return false;
  }
}

The isAdmin() function is copied from somewhere. I don’t remember where but I thank it’s author for the code.

This is pretty much it. With this code in place, you can type in {{customVar code=anything}} anywhere in the administration area – configuration menus, CMS page titles, category names, etc, and it’ll parse that into the corresponding value on the frontend.

Hope this helps someone. Please share and don’t forget to test this before deploying as it was only tested on the Magento CE ver. 1.7.0.0.

| Magento’s architecture requires the use of separate CMS pages, with separate URL keys for each language. In this post, I suggest a hack that enables using custom variables in the CMS’s title and content fields instead. Custom variables are translatable, and I’ve been successful at using them as a way of creating language-independent CMS pages.