Lost For Words. The case of the Craft CMS missing translations

Posted: 19th Sep 2021

Working with so many Craft CMS projects, we get to see it working perfectly most of the time, but occasionally something unexpected crops up and we have to go on a bug hunt.

The recent launch of one particular project Servd went without a hitch. Running as a Craft multi-site, it supports several languages for a variety of visitors. However, whilst testing the site's language switching functionality an issue was detected and we worked with the project's developers to get to the bottom of it.

The Problem

Upon testing the different languages supported by the site it was noted that one, es, was correctly showing static translations whilst another, zh-hant was not.

These static translations should be automatically included by Craft when a piece of text is run through the |t translate filter within the twig template. As one of the languages was working as expected, we knew that these twig filters were in place and working as expected.

We could also tell that Craft was detecting the language of all of the sites correctly because all other dynamic content was being translated as expected.


The first thing we checked was that the translation files themselves were in the correct location and had the correct name.

We could see on the filesystem folders for both es and zh-hant, both of which contained a site.php files which Craft should be using for its static translations. A brief look at the contents of these files also didn't reveal any obvious errors.

We double checked the permissions set on these files to make sure that they were readable by PHP (Servd configures all of the file permissions appropriately, but best to double check!).

We also considered whether there might be any issues with the hyphen character in the folder name. We double checked the character code that was being used in the URL against the folder name on the filesystem, which matched. No problem there.

In order to test to make sure it wasn't the contents of the translations file that was causing the problem we finally removed the zh-hant directory and copied the existing es folder in its place. Once this had been done the site still wasn't showing translations for zh-hant so we knew the problem wasn't with the translations files themselves. Something must be wrong with the way Craft is matching the language against the translations directories.

Craft's codebase is large and complex, so often the easiest way to figure out what its doing internally is to simply output some potentially problematic variables and see if they match what we expect.

So we created a new temporary twig template with the following contents:

{{ craft.app.language }}

Upon visiting our test template we received a very revealing output:


Craft was matching the zh-hant in the URL to its internal site object, but was using the partially uppercased version when trying to find the correct directory on the filesystem. This would only cause issues on strictly case-sensitive filesystems (like Linux on Servd) so could potentially go unnoticed during development.

The Fix

Simply rename the translations directories to match the exact casing for the language slug as seen in the site object's settings in the Craft Control Panel.

Once this update had been made, Craft was able to match the current language with the folder name and the site began displaying the translations as expected.

Deep Dive

Upon reflection, the cause of this issue was relatively simple. The site in Craft had been set up with a URL prefix of zh-hant and the language for the site had been set to zh-Hant - Chinese.

The zh-hant in the URL was correctly being used to find the appropriate Craft site to display, but an assumption had been made that the language code in the URL would also be appropriate to use for the translations directory name.

Craft was instead using the language slug which had been associated with the site, which was zh-Hant.

This assumption didn't cause any problems during local development due to the use of a case-insensitive filesystem, but once deployed to a case-sensitive filesystem it prevented Craft from finding the relevant translation files.

The original diagnosis was confused due to the use of es as the URL prefix for the other translated site, which coincidentally exactly matched the slug for the language which was also associated with that site: es - Spanish.