DH
David Hellmann
2020/12/20

CraftCMS: Smarter Translation Files

On my last Craft CMS client project at work I was a bit pissed about our translation file. Long story short: It's messy! We have a lot static translations in this project and it's a bit hard to manage all this translations in one big file. So our first approach was to use generic strings to translate rather than the thing to be translated itself. That looked like this:

      
        <?php
return [
    'translate.component.code.show' => 'Show',
    'translate.component.code.hide' => 'Hide',
    'translate.component.code.copy' => 'Copy',
    'translate.component.code.copied' => '✅ Copied!',
];
      
    

This helped us to have more clear strings and more context in our translation file. All our string starts with a translate. So it's really easy to search against all templates for translations. Great advantage! But, it does not solve the problem that you can have a really large file at the end. Yes, it's possible to work with a file like that but we don't like it.

A much better approach

So we thought about it how we can optimize here. It's quite simple. Place your files where they belong. That means we say goodbye to one large and messy file and say hello to component/page based translations. Let me explain with a small example. Our folder structure looks like this:

      
        ...
templates
-- _components
----- code
------- code.js
------- code.twig
------- translate.de.component.code.php
------- translate.en.component.code.php
...
translations
-- de
---- site.php
-- en
---- site.php
...
      
    

In our components folder, we find all our components. Each component can have a different set of files. If we have a template file (twig in our case) and we need static translations, we place a PHP file for each language in that folder too. Our naming for this file is:

TRANSLATE.LANG.CONTEXT.COMPONENT.php > translate.en.component.code.php

Let's look at a complete example

We have two sites (German, English) and in our example, we have a code component. That means we need two translation files.

templates/_components/code/translate.de.component.code.php

      
        <?php
return [
    'translate.component.code.show' => 'Zeigen',
    'translate.component.code.hide' => 'Verbergen',
    'translate.component.code.copy' => 'Kopieren',
    'translate.component.code.copied' => '✅ Kopiert!',
];
      
    

templates/_components/code/translate.en.component.code.php

      
        <?php
return [
    'translate.component.code.show' => 'Show',
    'translate.component.code.hide' => 'Hide',
    'translate.component.code.copy' => 'Copy',
    'translate.component.code.copied' => '✅ Copied!',
];
      
    

In our main translation file, we have to include this component translation file and we have to merge it all. In this example, we import our code file and two other components. We've also a globals array where we can place some global translations like weekdays or stuff like that. In the end, it looks like this:

translations/de/site.php

      
        <?php
// Vars
$page = '/../../templates/';
$lang = 'de';

// Global Translations
$globalTranslations = [
    'translate.global.days.monday' => 'Montag',
    'translate.global.days.tuesday' => 'Dienstag',
    'translate.global.days.wednesday' => 'Mittwoch',
    'translate.global.days.thursday' => 'Donnerstag',
    'translate.global.days.friday' => 'Freitag',
    'translate.global.days.saturday' => 'Samstag',
    'translate.global.days.sunday' => 'Sonntag',
];

// Comp Translations
$compTranslations = [];

// Get all Files
$translationFiles = glob(__DIR__ . $page . "**/*/translate." . $lang . "*.php");

// Merge Files
foreach ($translationFiles as $translationFile) {
    $data = include $translationFile;
    if (!is_array($data)) {
        continue;
    }
    $compTranslations[] = $data;
}


// Return All Translations
return array_merge(...$compTranslations, ...[$globalTranslations]);

      
    

translations/en/site.php

      
        <?php
// Vars
$page = '/../../templates/';
$lang = 'en';

// Global Translations
$globalTranslations = [
    'translate.global.days.monday' => 'Monday',
    'translate.global.days.tuesday' => 'Tuesday',
    'translate.global.days.wednesday' => 'Wednesday',
    'translate.global.days.thursday' => 'Thursday',
    'translate.global.days.friday' => 'Friday',
    'translate.global.days.saturday' => 'Saturday',
    'translate.global.days.sunday' => 'Sunday',
];

// Comp Translations
$compTranslations = [];

// Get all Files
$translationFiles = glob(__DIR__ . $page . "**/*/translate." . $lang . "*.php");

// Merge Files
foreach ($translationFiles as $translationFile) {
    $data = include $translationFile;
    if (!is_array($data)) {
        continue;
    }
    $compTranslations[] = $data;
}


// Return All Translations
return array_merge(...$compTranslations, ...[$globalTranslations]);

      
    

What do you think? Let me know!

comments powered by Disqus

Maybe interesting…

UP