The Power of t() in Craft 3

Have you ever had trouble working with:

  • date formats: 05/06/2018 vs 06/05/2018 vs 2018. 06. 05.
  • number formats: 2,100.99 vs 2 100,99
  • currencies: $100 vs 100 EUR
  • time zones: GMT UTC DST CET PST
  • plural forms: 1 hour, 2 hours vs صاحِب أَصْحاب صُحْبان (arabic has three forms of plural, roughly one, few and many)

These are annoying, trivial but tedious to solve with the basic PHP tools at hand: number_format, looking up the weird syntax of sprintf, converting time from timestamps to DateTime objects, praying strtotime will work correctly, remembering the magic letters of date formatting…

If you have PHP’s intl extension (based on the ICU library) installed, there’s an easier way. You probably have it already, it’s a recommended extension for Craft 3. It’s especially helpful if you’re working on multilingual sites, but it’s handy just for its string formatting capabilities too.

Did you know you can have named parameters in Craft’s tTwig filter?

{{ 'Hello {name}!' | t({ name: 'Zoltan' }) }}
=> Hello Zoltan!

You can also use simple arrays instead of keyed hashes, like so:

{{ 'Hello {0}!' | t(['Zoltan']) }}
=> Hello Zoltan!

{0} signifies the first parameter. Now, let’s add some formatting modifiers:

{{ 'Total: {amt,number}' | t({amt: 2510.99}) }}
en-US => Total: 2,510.99
hu-HU => Összesen: 2 510,99

The number formatter will format the number according to the locale of the current site. Be sure to use a specific locale if possible, use en_USfor the United States or en_GB for the UK instead of just en — it will provide the correct number, date and currency formats.

{{ 'We are {n,spellout} in line' | t({n: 138}) }} 
en-US => We are one hundred thirty-eight
hu-HU => Százharmincnyolc vagyunk
{{ 'You are the {n,spellout,%spellout-ordinal} in line' | t({n: 47}) }} 
en-US => You are the forty-seventh in line
hu-HU => Negyvenhetedik vagy a sorban.

Some languages have feminine/masculine forms, and ICU knows about them:

{{ 'Eres {n,ordinal,%digits-ordinal-feminine}' | t({n: 1}) }} 
es-ES => Eres 1.ª

On to dates, ICU has built in short, medium and long forms:

{{ '{d,date,short}' | t({d: date().timestamp}) }} 
en-US => 9/22/18
hu-HU => 2018. 09. 22.
{{ '{d,date,medium}' | t({d: date().timestamp}) }}
en-US => Sep 22, 2018
hu-HU => 2018. szept. 22.
{{ '{d,date,long}' | t({d: date().timestamp}) }}
en-US => September 22, 2018
hu-HU => 2018. szeptember 22.

The translation string must exist for it to take effect; even if it’s just '{d,date,short'} => '{d,date,short}' — if it doesn’t, it will use the default locale.

If you don’t like the presets, you can specify your own format, and you can change the format per language just by translating the string:

in your translations/hu-HU/site.php

return [
'{d,date,MM/dd/yy hh:mm z}' => '{d,date,yyyy. MM. dd. HH:mm z}',
];

in your twig file

{{ '{d,date,MM/dd/yy hh:mm z}' | t({d: date().timestamp}) }}
en-US => 09/22/18 06:19 PDT
hu-HU => 2018. 09. 22. 06:19 GMT-7

You can even get human readable durations, if it’s supported in your language:

{{ 'Last updated: {0, duration,%with-words} ago' | t([15024]) }} 
en-US => Last updated: 4 hours, 10 minutes, 24 seconds ago
hu-HU => Utoljára frissítve: 15024 (not supported in hungarian)

These modifiers are basically a domain specific language, you can do complex things such as this contrived example:

{{ 'You {likeCount,plural,
offset: 1
=0{did not like this}
=1{liked this}
one{and one other person liked this}
other{and # others liked this}
} ' | t ({likeCount: 2}) }}
=> You and one other person liked this

Of course, you can use the Craft::t function in your plugins, too:

Craft::t('app', 'Total: {amt,number}', [ 'amt' => 99.99 ])

To read more about Yii’s static message translation, see the Yii i18n documentation.

To find out which formatting modifiers are available for your languages, visit the intl.rmcreative.ru site.

If you’ve enjoyed this teaser, come see my Multilingual Craft talk at the 2018 DotAll conference!