Updated: 06/08/2011 1:36pm
The straightforward way to translate strings in SilverStripe is using the _t( ... ) method on them. However there some situations where you can't use it - or at least not in that straightforward way.
From the PHP manual: 'static properties may only be initialized using a literal or constant; expressions are not allowed.' One way to overcome this in SilverStripe is the use of a getter:
static $SomeVar = 'some value'; function getSomeVar() { return _t('ThisClass.SOMEVAR', $this->stat('SomeVar')); }
Now, if you add $SomeVar in your template, SilverStripe will use getSomeVar() to parse it.
Sometimes you won't know in advance what value a static var will have. Suppose there are three values to choose from. You'd need three possible translations that you want the textcollector to find. Enter the provideI18nEntities() method:
class MyExample extends DataObject implements i18nEntityProvider { static $TimeOfDay = 'morning'; // default // prepare a couple of strings for translation function provideI18nEntities() { $entities = parent::provideI18nEntities $entities['MyExample.morning'] = array('morning'); $entities['MyExample.afternoon'] = array('afternoon'); $entities['MyExample.evening'] = array('evening'); return $entities; } }
Running the textcollector would result in the following entry in the en_US.php language file:
$lang['en_US']['MyExample']['morning'] = 'morning'; $lang['en_US']['MyExample']['afternoon'] = 'afternoon'; $lang['en_US']['MyExample']['evening'] = 'evening';
And then again: the getter, that will select the right translation, based on the current value of the variable:
function getTimeOfDay() { return _t("MyExample." . $this->stat('TimeOfDay'), $value); }
This can now be referred to from the template as:
The time of day is: $TimeOfDay
In some situations custom getters won't work - especially when SilverStripe uses scaffolding to create forms. But now there's another hook: SilverStripe will call on the DataObject::FieldLabels() method to provide it with an array of fields and their labels. So to provide the correct translation for a label, we need to override the FieldLabels() method in our custom DataObject class. Something like:
class MyExample extends DataObject implements i18nEntityProvider { static $db = array( 'Name' => 'Varchar', ... ); function fieldLabels($includerelations = true) { $labels = parent::fieldLabels($includerelations); // add a translation to the $labels array for each db field // if $includerelations == true (and it normally is) you need to add // the db_ prefix, cause that's what SS will look for foreach($this->stat('db') as $key => $value) { $labels[$key] = _t("MyExample.db_{$key}", $key); } return $labels; }
Now you need to make sure that these translations again exist in the language file, so you must to provide them for the textcollector. Note: in scaffolding, SilverStripe wants the keys for these translations to have a prefix refering to the type of relation: db, has_one, has_many etc.:
function provideI18nEntities() { $entities = parent::provideI18nEntities(); // provide translations for $db fields foreach($this->stat('db') as $key => $value) { $entities["MyExample.db_{$key}"] = array($key); } // provide translations for $has_one fields foreach($this->stat('has_one') as $key => $value) { $entities["MyExample.has_one_{$key}"] = array($key); } return $entities; }
Effectively, a field called 'Name' and a has_one relation called 'Image' will end up in the language file like so:
$lang['en_US']['MyExample']['db_Name'] = 'Name'; $lang['en_US']['MyExample']['has_one_Image'] = 'Image';
Special labels for fields like $summary_fields and $searchable_fields are treated in a similar fashion as normal field labels. But in this case there's no need for relational prefixes. And since you know in advance which fields you're using, you can also skip the provideI18nEntities() method and provide translations as straight strings.
Mind these fields can refer to fields within related objects or even to functions, so they need not be real database fields at all. Something like this will work (textcollector will spot these translations):
static $summary_fields = array( 'Name', // db Name field 'Image.ID', // ID of a has_one Image 'MyFunc' // return value of some function ); function fieldLabels($includerelations = true) { $labels = parent::fieldLabels($includerelations); $labels['Name'] = _t('MyExample.NAME', 'Last name'); $labels['Image.ID'] = _t('MyExample.NAME', 'Image ID'); $labels['MyFunc'] = _t('MyExample.MYFUNC', 'Function result'); return $labels; }
Default values are the values that form fields are set to when a form is first opened for a new record. These values need to be set before the form is created. Normally you would do something like this:
static $db = array( 'Name' => 'Varchar' ); static $defaults = array( 'Name' => 'Enter your name here' );
And again: you can't just use the _t( ... ) method here. But that's where the PopulateDefaults() method comes to the rescue! SilverStripe will call it to collect its default values and we can hook into this function to define our translations:
static $db = array( 'Name' => 'Varchar', ); public function populateDefaults(){ parent::populateDefaults(); // create a default value for the 'Name' field $this->Name = _t('MyExample.DEFAULT_NAME', 'Enter your name here'); }
Note: a default value is only offered when an object is newly created in the CMS, it is skipped on opening previously stored records - for obvious reasons...