Skip to content

Commit

Permalink
Merge pull request #6558 from open-sausages/pulls/4.0/i18n-symfony
Browse files Browse the repository at this point in the history
API Replace i18n message localisation with symfony/translation
  • Loading branch information
dhensby authored Jan 26, 2017
2 parents 90072e2 + de02a3f commit c81959c
Show file tree
Hide file tree
Showing 73 changed files with 2,577 additions and 4,417 deletions.
9 changes: 4 additions & 5 deletions .upgrade.yml
Original file line number Diff line number Diff line change
Expand Up @@ -454,11 +454,10 @@ mappings:
i18nRailsYamlAdapter: SilverStripe\i18n\i18nRailsYamlAdapter
i18nSSLegacyAdapter: SilverStripe\i18n\i18nSSLegacyAdapter
i18nSSLegacyAdapter_Iterator: SilverStripe\i18n\i18nSSLegacyAdapter_Iterator
i18nTextCollector: SilverStripe\i18n\i18nTextCollector
i18nTextCollector_Writer: SilverStripe\i18n\i18nTextCollector_Writer
i18nTextCollector_Writer_Php: SilverStripe\i18n\i18nTextCollector_Writer_Php
i18nTextCollector_Writer_RailsYaml: SilverStripe\i18n\i18nTextCollector_Writer_RailsYaml
i18nTextCollector_Parser: SilverStripe\i18n\i18nTextCollector_Parser
i18nTextCollector: SilverStripe\i18n\TextCollection\i18nTextCollector
i18nTextCollector_Writer: SilverStripe\i18n\Messages\Writer
i18nTextCollector_Writer_RailsYaml: SilverStripe\i18n\Messages\YamlWriter
i18nTextCollector_Parser: SilverStripe\i18n\TextCollection\Parser
i18nTranslateAdapterInterface: SilverStripe\i18n\i18nTranslateAdapterInterface
SilverStripe\Framework\Logging\DebugViewFriendlyErrorFormatter: SilverStripe\Logging\DebugViewFriendlyErrorFormatter
SilverStripe\Framework\Logging\DetailedErrorFormatter: SilverStripe\Logging\DetailedErrorFormatter
Expand Down
42 changes: 41 additions & 1 deletion _config/i18n.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,47 @@ SilverStripe\i18n\i18n:
module_priority:
- other_modules
---
name: i18nMessages
---
SilverStripe\Core\Injector\Injector:
# Custom yml loader for localisation messages
SilverStripe\i18n\Messages\Reader:
class: SilverStripe\i18n\Messages\YamlReader
SilverStripe\i18n\Messages\Writer:
class: SilverStripe\i18n\Messages\YamlWriter
Symfony\Component\Translation\Loader\LoaderInterface:
class: SilverStripe\i18n\Messages\Symfony\ModuleYamlLoader
properties:
Reader: %$SilverStripe\i18n\Messages\Reader
# Ensure our cache respects ModuleYamlLoader's self-invalidation
# @see DirectoryListResource::isFresh()
# Note: This could be replaced with a more aggressive cache if necessary on a live environment
Symfony\Component\Config\ConfigCacheFactoryInterface:
class: Symfony\Component\Config\ResourceCheckerConfigCacheFactory
constructor:
0: [ %$Symfony\Component\Config\Resource\SelfCheckingResourceChecker ]
# Create default translator with standard cache path and our custom loader
Symfony\Component\Translation\TranslatorInterface:
class: Symfony\Component\Translation\Translator
constructor:
0: 'en'
1: null
2: `TEMP_FOLDER`
properties:
ConfigCacheFactory: %$Symfony\Component\Config\ConfigCacheFactoryInterface
calls:
FallbackLocales: [ setFallbackLocales, [['en']]]
Loader: [ addLoader, ['ss', %$Symfony\Component\Translation\Loader\LoaderInterface ]]
# Set this translator as our message provider for silverstripe's i18n
SilverStripe\i18n\Messages\MessageProvider:
class: SilverStripe\i18n\Messages\Symfony\SymfonyMessageProvider
properties:
Translator: %$Symfony\Component\Translation\TranslatorInterface
---
Name: textcollector
---
SilverStripe\Core\Injector\Injector:
SilverStripe\i18n\i18nTextCollector_Writer: SilverStripe\i18n\i18nTextCollector_Writer_RailsYaml
SilverStripe\i18n\TextCollection\i18nTextCollector:
properties:
Reader: %$SilverStripe\i18n\Messages\Reader
Writer: %$SilverStripe\i18n\Messages\Writer
5 changes: 4 additions & 1 deletion admin/code/CMSMenu.php
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,10 @@ public function provideI18nEntities()
foreach ($cmsClasses as $cmsClass) {
$defaultTitle = LeftAndMain::menu_title($cmsClass, false);
$ownerModule = i18n::get_owner_module($cmsClass);
$entities["{$cmsClass}.MENUTITLE"] = array($defaultTitle, 'Menu title', $ownerModule);
$entities["{$cmsClass}.MENUTITLE"] = [
'default' => $defaultTitle,
'module' => $ownerModule
];
}
return $entities;
}
Expand Down
2 changes: 1 addition & 1 deletion admin/code/ModelAdmin.php
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ public function ImportForm()
}

$fields = new FieldList(
new HiddenField('ClassName', _t('ModelAdmin.CLASSTYPE'), $this->modelClass),
new HiddenField('ClassName', false, $this->modelClass),
new FileField('_CsvFile', false)
);

Expand Down
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
"league/flysystem": "~1.0.12",
"symfony/yaml": "~2.7",
"embed/embed": "^2.6",
"swiftmailer/swiftmailer": "~5.4"
"swiftmailer/swiftmailer": "~5.4",
"symfony/config": "^2.8|^3",
"symfony/translation": "^2.8|^3"
},
"require-dev": {
"phpunit/PHPUnit": "~4.8",
Expand Down
15 changes: 15 additions & 0 deletions docs/en/02_Developer_Guides/05_Extending/05_Injector.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,21 @@ As well as properties, method calls can also be specified:
- [ pushHandler, [ %$DefaultHandler ] ]


## Using constants as variables

Any of the core constants can be used as a service argument by quoting with back ticks "`".


:::yaml
CachingService:
class: SilverStripe\Cache\CacheProvider
properties:
CacheDir: `TEMP_DIR`


Note: undefined variables will be replaced with null


## Factories

Some services require non-trivial construction which means they must be created by a factory class. To do this, create
Expand Down
108 changes: 74 additions & 34 deletions docs/en/02_Developer_Guides/13_i18n/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,68 @@ All strings passed through the `_t()` function will be collected in a separate l
The `_t()` function is the main gateway to localized text, and takes four parameters, all but the first being optional.
It can be used to translate strings in both PHP files and template files. The usage for each case is described below.

* **$entity:** Unique identifier, composed by a namespace and an entity name, with a dot separating them. Both are arbitrary names, although by convention we use the name of the containing class or template. Use this identifier to reference the same translation elsewhere in your code.
* **$string:** (optional) The original language string to be translated. Only needs to be declared once, and gets picked up the [text collector](#collecting-text).
* **$string:** (optional) Natural language comment (particularly short phrases and individual words)
are very context dependent. This parameter allows the developer to convey this information
to the translator.
* **$array::** (optional) An array of injecting variables into the second parameter
* **$entity:** Unique identifier, composed by a namespace and an entity name, with a dot
separating them. Both are arbitrary names, although by convention we use the name of
the containing class or template. Use this identifier to reference the same translation
elsewhere in your code.
* **$default:** The original language string to be translated. This should be declared
whenever used, and will get picked up the [text collector](#collecting-text).
* **$string:** (optional) Natural language comment (particularly short phrases and individual words)
are very context dependent. This parameter allows the developer to convey this information
to the translator.
* **$injection::** (optional) An array of injecting variables into the second parameter


## Pluralisation

i18n also supports locale-respective pluralisation rules. Many languages have more than two plural forms,
unlike English which has two only; One for the singular, and another for any other number.

More information on what forms these plurals can take for various locales can be found on the
[CLDR documentation](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html)

The ability to pluralise strings is provided through the `i18n::_t` method when supplied with a
`{count}` argument and `|` pipe-delimiter provided with the default string.

Plural forms can also be explicitly declared via the i18nEntityProvider interface in array-format
with both a 'one' and 'other' key (as per the CLDR for the default `en` language).

For instance, this is an example of how to correctly declare pluralisations for an object


:::php
class MyObject extends DataObject, implements i18nEntityProvider
{
public function provideI18nEntities()
{
return [
'MyObject.SINGULAR_NAME' => 'object',
'MyObject.PLURAL_NAME' => 'objects',
'MyObject.PLURALS' => [
'one' => 'An object',
'other' => '{count} objects',
],
];
}
}


In YML format this will be expressed as the below. This follows the
[ruby i18n convention](guides.rubyonrails.org/i18n.html#pluralization) for plural forms.


:::yaml
en:
MyObject:
SINGULAR_NAME: 'object'
PLURAL_NAME: 'objects'
PLURALS:
one: 'An object',
other: '{count} objects'


Note: i18nTextCollector support for pluralisation is not yet available.
Please ensure that any required plurals are exposed via provideI18nEntities.

#### Usage in PHP Files

Expand All @@ -180,15 +236,15 @@ to the translator.
// Simple string translation
_t('LeftAndMain.FILESIMAGES','Files & Images');

// Using the natural languate comment parameter to supply additional context information to translators
_t('LeftAndMain.HELLO','Site content','Menu title');

// Using injection to add variables into the translated strings.
_t('CMSMain.RESTORED',
"Restored {value} successfully",
'This is a message when restoring a broken part of the CMS',
array('value' => $itemRestored)
);

// Plurals are invoked via a `|` pipe-delimeter with a {count} argument
_t('MyObject.PLURALS', 'An object|{count} objects', [ 'count' => '$count ]);


#### Usage in Template Files

Expand All @@ -207,11 +263,12 @@ the PHP version of the function.
// Simple string translation
<%t Namespace.Entity "String to translate" %>

// Using the natural languate comment parameter to supply additional context information to translators
<%t SearchResults.NoResult "There are no results matching your query." is "A message displayed to users when the search produces no results." %>

// Using injection to add variables into the translated strings (note that $Name and $Greeting must be available in the current template scope).
<%t Header.Greeting "Hello {name} {greeting}" name=$Name greeting=$Greeting %>

// Plurals follow the same convention, required a `|` and `{count}` in the default string
<%t MyObject.PLURALS 'An item|{count} items' count=$Count %>


#### Caching in Template Files with locale switching

Expand Down Expand Up @@ -279,46 +336,29 @@ Each module can have one language table per locale, stored by convention in the
The translation is powered by [Zend_Translate](http://framework.zend.com/manual/current/en/modules/zend.i18n.translating.html),
which supports different translation adapters, dealing with different storage formats.

By default, SilverStripe 3.x uses a YAML format (through the [Zend_Translate_RailsYAML adapter](https://github.com/chillu/zend_translate_railsyaml)).
By default, SilverStripe uses a YAML format which is loaded via the
[symfony/translate](http://symfony.com/doc/current/translation.html) library.

Example: framework/lang/en.yml (extract)

en:
ImageUploader:
Attach: 'Attach %s'
Attach: 'Attach {title}'
UploadField:
NOTEADDFILES: 'You can add files once you have saved for the first time.'

Translation table: framework/lang/de.yml (extract)

de:
ImageUploader:
ATTACH: '%s anhängen'
ATTACH: '{title} anhängen'
UploadField:
NOTEADDFILES: 'Sie können Dateien hinzufügen sobald Sie das erste mal gespeichert haben'

Note that translations are cached across requests.
The cache can be cleared through the `?flush=1` query parameter,
or explicitly through `Zend_Translate::getCache()->clean(Zend_Cache::CLEANING_MODE_ALL)`.

<div class="hint" markdown='1'>
The format of language definitions has changed significantly in since version 2.x.
</div>

In order to enable usage of [version 2.x style language definitions](http://doc.silverstripe.org/framework/en/2.4/topics/i18n#language-tables-in-php) in 3.x, you need to register a legacy adapter
in your `mysite/_config.php`:

:::php
i18n::register_translator(
new Zend_Translate(array(
'adapter' => 'i18nSSLegacyAdapter',
'locale' => i18n::default_locale(),
'disableNotices' => true,
)),
'legacy',
9 // priority lower than standard translator
);

## Javascript Usage

The i18n system in JavaScript is similar to its PHP equivalent.
Expand Down
Loading

0 comments on commit c81959c

Please sign in to comment.