Practical advice on avoiding common mistakes when internationalizing Craft CMS projects, including when to use translations versus CMS content, why placeholders matter, and how to handle HTML within translated strings.
Let’s wrap up the course with some practical advice on avoiding common mistakes when internationalizing Craft CMS projects, including when to use translations versus CMS content, why placeholders matter, and how to handle HTML within translated strings.
One of the most important decisions in any multilingual Craft CMS project is knowing when to use translations versus CMS content. Translations live in PHP source files and are not editable by content authors, which makes them ideal for UI strings that require placeholders, formatting logic, or pluralization rules. However, content like page headlines is often better served as a CMS field so that editors can customize and optimize it for SEO. A good rule of thumb is that if the string is a UI label or involves dynamic formatting, use a translation. If it is editorial content that editors should be able to change, put it in the control panel as a field.
A very common mistake in plugins and projects is concatenating translated prefixes or suffixes with dynamic values instead of using placeholders. For example, translating “Published on” as a standalone string and then appending a date after it works fine in English, but it locks the date into a fixed position within the sentence. Other languages may need the date at the beginning, in the middle, or formatted differently. By hard-coding the position, you force translators into a sentence structure that may be awkward or grammatically incorrect in their language. The solution is to always pass dynamic values as placeholders within the full translated string. This gives translators complete control over where each value appears, and they can even omit parameters entirely if their language does not need them.
Mixing HTML with translations introduces its own challenges. If you need semantic markup like a <time> element inside a translated string, you must use the |raw filter in Twig to prevent the HTML from being escaped. This works safely when the parameters are known safe values like dates, but it creates a cross-site scripting vulnerability if any user-provided data is passed through. External translators may also struggle with HTML syntax in translation strings. Alternative approaches include preparing the HTML separately and passing it as a parameter, or splitting the translation into parts, though both sacrifice some flexibility. There is no perfect solution, so you need to weigh the trade-offs between semantic correctness, translator experience, security, and the ability to customize formatting per language.
|raw filter to translations containing HTML prevents escaping but introduces cross-site scripting risks if any parameters contain user-provided data