Keeping it clean and tidy with multilingual Perch builds

I saw this forum post and realised that the blog post I had about this had been languishing in drafts for a very long time. So here’s a quick version of it to actually get it out the door.

The premise

We have a multi-language site that uses a branching technique (, etc) and we can use the url structure to grab the current language at any time. That’s our $lang variable.

Ideally I want to keep Perch content templates as clean and tidy as possible for both site managers and site developers. Specifically this means that:

  • The edit forms for any item are kept to a manageable length
  • The edit forms for any item are easily understood
  • More languages can be added relatively easily and quickly
  • Templates should be as clean and language independent as possible

A quick example

Let’s say we have a site in English, French and Spanish and we have a Collection edit template (I like to have an edit template to gather the data and a separate templates to output the data with all my required markup) that looks like this:

<perch:content id="heading_en" type="text" />
<perch:content id="description_en" type="textarea" />

<perch:content id="heading_fr" type="text" />
<perch:content id="description_fr" type="textarea" />

<perch:content id="heading_es" type="text" />
<perch:content id="description_es" type="textarea" />

Each field is identified by a language marker (en, fr, es). It’s important that this is planned to match with the values expected for the $lang variable.

Avoiding conditionals with localised content

When outputting content (and as the forum post mentions) it can be unwieldy to use a bunch of conditional statements in content templates., especially with a large set of languages.

Instead of saying, “if it’s English, use this, if it’s French use this, etc”, it’s nice to have a localised version to use. And we can create that with a simple callback which uses our $lang variable.

perch_collection('My Collection', [
        'template'   => '_collection-output.html',
        'each'  =>  function($item) use($lang) {
            $item["heading_localised" = $item['heading_' . $lang];
            $item["description_localised"] = $item['description_' . $lang];
            return $item;

And now the _collection.output.html template will have available two new localised items which will always match the current language. But with no conditionals or added verbosity required.

<perch:content id="heading_localised" type="text" />
<perch:content id="description_localised" type="textarea" />

Repeaters can also be similarly localised. For example, if we have a repeater of Features in the Collection edit template as follows:

<perch:repeater id="features" label=“Features" >
<perch:content type="text" id="feature_en" label="Feature EN" />
<perch:content type="text" id="feature_es"  label="Feature ES" />
<perch:content type="text" id="feature_fr"  label="Feature FR" />

The callback can also create a localised repeater:

perch_collection('My Collection', [
        'template'   => '_collection-output.html',
        'each'  =>  function($item) use($lang) {
            if (is_array($item["features"])) {
                    foreach ($item["features"] as &$feature_item) {
                        $feature_item["feature_localised"] = $feature_item['feature_' . $lang];

And then within the repeater in the _collection-output.html template there is a feature_localised item available to use.

With a little more wrangling, Categories can also be processed in the callback and things like a localised catTitle and catPath can be passed through.

I’ve used this approach on quite a few projects and I really like the clarity of the templates that it gives and the ease of adding further languages as required.

Another approach

Since I first used the above approach, I’ve seen that there is a third party Perch app from a member of the great Perch community which looks really interesting and addresses the same issue. It looks like it might output slightly more verbose templates than producing a single localised field, but of course the php needed for the callbacks in the examples here is more verbose than the regular functions. I like the idea of an app to handle this though and definitely need to take a closer look.