Skip to content

Translation custom loaders #4166

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Sep 16, 2014
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions components/map.rst.inc
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@

* :doc:`/components/translation/introduction`
* :doc:`/components/translation/usage`
* :doc:`/components/translation/custom_formats`

* :doc:`/components/yaml/index`

Expand Down
116 changes: 116 additions & 0 deletions components/translation/custom_formats.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
.. index::
single: Translation; Custom formats

Custom Formats
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer a title that tells more about the problem that's described, something like "Adding Custom Format Support to the Translator"

==============

Sometimes, you need to deal with custom formats for translation files. The
Translation component is flexible enough to support this, just creating a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[...] to support this. Just create a [...]

loader (to load translations) and, optionally, a dumper (to dump translations).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and what about an extractor?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

totally unrelated. The extractor does not care about the format of your translation files. It relies on the loader and dumper for it (it would be great to add a cookbook about adding extractors though)


Let's imagine you have a custom format where translation messages are defined
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We always avoid the first person perspective. So, I'd write something like "Image that you have a custom format [...]".

using one line for each translation and parenthesis to wrap the key and the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be "parentheses", shouldn't it?

message. A translation file would look like this:

.. code-block:: text

(welcome)(Bienvenido)
(goodbye)(Adios)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Little type: adiós ;)

(hello)(Hola)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since Symfony is french, I would prefer to have a french translation in here :)


Custom Loader
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I would prefer "Creating a Custom Loader"

-------------

To define a custom loader able to read this kind of files, you must create a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[...] loader that is able to read [...]

new class that implements the
:class:`Symfony\\Component\\Translation\\Loader\\LoaderInterface`. The
:method:`Symfony\\Component\\Translation\\Loader\\LoaderInterface::load`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think listing the methods of the interface is needed in this paragraph. Implementing an inteface involves implementing all its methods.

method will get a filename and parse it into an array. Then, it will
create the catalogue that will be returned::
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

catalog


use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Loader\LoaderInterface;

class MyFormatLoader implements LoaderInterface
{
public function load($resource, $locale, $domain = 'messages')
{
$messages = array();
$lines = file($resource);

foreach ($lines as $line) {
if (preg_match('/\(([^\)]+)\)\(([^\)]+)\)/', $line, $matches)) {
$messages[$matches[1]] = $matches[2];
}
}

$catalogue = new MessageCatalogue($locale);
$catalogue->add($messages, $domain);

return $catalogue;
}

}

Once created, it can be used as any other loader::

$translator = new Translator('es_ES');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should add a use statement for the Translator class.

$translator->addLoader('my_format', new MyFormatLoader());

$translator->addResource('my_format', __DIR__.'/translations/messages.txt', 'es_ES');

echo $translator->trans('welcome');

It will print *"Bienvenido"*.

Custom Dumper
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And following the same guidelines, this would be "Creating a Custom Dumper"

-------------

It is also possible to create a custom dumper for your format, useful when using
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[...] for your format which is useful [...]

the extraction commands. To do so, a new class implementing the
:class:`Symfony\\Component\\Translation\\Dumper\\DumperInterface`
interface must be created.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"interface" is also part of the interface name. So, it should be removed here.

To write the dump contents into a file, extending the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this new line will not be detected by Sphinx. You either have to add an empty line between the 2 sentences to create 2 paragraphs, or you can simply remove the line break.

:class:`Symfony\\Component\\Translation\\Dumper\\FileDumper` class
will save a few lines::

use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Dumper\FileDumper;

class MyFormatDumper extends FileDumper
{

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The blank line can be removed.

public function format(MessageCatalogue $messages, $domain = 'messages')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be protected.

{
$output = '';

foreach ($messages->all($domain) as $source => $target) {
$output .= sprintf("(%s)(%s)\n", $source, $target);
}

return $output;
}

protected function getExtension()
{
return 'txt';
}
}

The :method:`Symfony\\Component\\Translation\\Dumper\\FileDumper::format`
method creates the output string, that will be used by the
:method:`Symfony\\Component\\Translation\\Dumper\\FileDumper::dump` method
of the :class:`Symfony\\Component\\Translation\\Dumper\\FileDumper` class to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need for this class role. It was already introduced above the previous code block.

create the file. The dumper can be used like any other
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to break the line so early.

built-in dumper. In this example, the translation messages defined in the YAML file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the following example, [...]

are dumped into a text file with the custom format::

use Symfony\Component\Translation\Loader\YamlFileLoader;

include_once __DIR__. '/vendor/autoload.php';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove this line. We never include the autoloader explicitly in any example.


$loader = new YamlFileLoader();
$catalogue = $loader->load(__DIR__ . '/translations/messages.es_ES.yml' , 'es_ES');

$dumper = new MyFormatDumper();
$dumper->dump($catalogue, array('path' => __DIR__.'/dumps'));
1 change: 1 addition & 0 deletions components/translation/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ Translation

introduction
usage
custom_formats
5 changes: 3 additions & 2 deletions components/translation/introduction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,9 @@ The Translation component uses Loader classes to load catalogs. You can load
multiple resources for the same locale, which will then be combined into one
catalog.

The component comes with some default Loaders and you can create your own
Loader too. The default loaders are:
The component comes with some default loaders and you can
:doc:`create your own Loader too </components/translation/custom_formats>`. The
default loaders are:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not mention the custom loaders here, but add a sentence after the list of default loaders instead.


* :class:`Symfony\\Component\\Translation\\Loader\\ArrayLoader` - to load
catalogs from PHP arrays.
Expand Down