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 (domain.com/en, domain.com/es 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" />
</perch:repeater>
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.