Componente de traducción

Resumen
The component Phalcon\Translate offers multilingual capabilities to applications. Este componente le permite mostrar contenido en diferentes idiomas, basado en la elección de idioma del usuario, disponible en la aplicación.
Uso
Introducir traducciones en su aplicación es una tarea relativamente fácil. However, no two implementations are the same and of course the implementation will depend on the needs of your application. Some options available can be an automatic detection of the visitor’s language using the server headers (parsing the HTTP_ACCEPT_LANGUAGE
contents or using the getBestLanguage()
method of the Phalcon\Http\Request object).
<?php
use Phalcon\Mvc\Controller;
use Phalcon\Translate\Adapter\NativeArray;
use Phalcon\Translate\InterpolatorFactory;
use Phalcon\Translate\TranslateFactory;
/**
* @property Phalcon\Http\Request $request
* @property Phalcon\Mvc\View $view
*/
class UserController extends Controller
{
public function indexAction()
{
$this->view->name = 'Mike';
$this->view->t = $this->getTranslator();
}
/**
* @return NativeArray
*/
private function getTranslator(): NativeArray
{
$language = $this->request->getBestLanguage();
$messages = [];
$translationFile = 'app/messages/' . $language . '.php';
if (true !== file_exists($translationFile)) {
$translationFile = 'app/messages/en.php';
}
require $translationFile;
$interpolator = new InterpolatorFactory();
$factory = new TranslateFactory($interpolator);
return $factory->newInstance(
'array',
[
'content' => $messages,
]
);
}
}
El método getTranslator()
está disponible en el controlador para todas las acciones que lo requieran. You could of course introduce a caching mechanism to store the translation adapter in your cache (based on the language selected i.e. en.cache
, de.cache
etc.)
The t
variable is passed then in the view and with it, we can perform translations in the view layer.
<!-- welcome -->
<!-- String: hi => 'Hello' -->
<p><?php echo $t->_('hi'), ' ', $name; ?></p>
y para Volt:
<p>{{ t._('hi') }} {{ name }}</p>
Marcadores de Posición
El método _()
devolverá la cadena traducida de la clave pasada. En el ejemplo anterior, devolverá el valor almacenado para la clave hi
. El componente también puede analizar marcadores de posición usando [interpolation][#interpolation]. Therefore, for a translation of:
necesitará pasar la variable $name
en la llamada _()
y el componente realizará el reemplazo por usted.
<!-- welcome -->
<!-- String: hi-name => 'Hello %name%' -->
<p><?php echo $t->_('hi-name', ['name' => $name]); ?></p>
y para Volt:
<p>{{ t._('hi-name', ['name' => name]) }}</p>
Plugin
La implementación anterior se puede extender para ofrecer capacidades de traducción en toda la aplicación. Por supuesto, podemos mover el método getTranslator()
a un controlador base y cambiar su visibilidad a protected
. Sin embargo, podríamos querer usar traducciones en otros componentes que están fuera del alcance de un controlador.
Para solucionar esto, podemos implementar un nuevo componente como un Plugin y registrarlo en nuestro contenedor Di.
<?php
namespace MyApp;
use Phalcon\Di\Injectable;
use Phalcon\Translate\Adapter\NativeArray;
use Phalcon\Translate\InterpolatorFactory;
use Phalcon\Translate\TranslateFactory;
class Locale extends Injectable
{
/**
* @return NativeArray
*/
public function getTranslator(): NativeArray
{
// Ask browser what is the best language
$language = $this->request->getBestLanguage();
$messages = [];
$translationFile = 'app/messages/' . $language . '.php';
if (true !== file_exists($translationFile)) {
$translationFile = 'app/messages/en.php';
}
require $translationFile;
$interpolator = new InterpolatorFactory();
$factory = new TranslateFactory($interpolator);
return $factory->newInstance(
'array',
[
'content' => $messages,
]
);
}
}
Entonces podemos registrarlo en el contenedor Di, cuando configuramos los servicios durante el arranque:
<?php
use MyApp\Locale;
$container->set('locale', (new Locale())->getTranslator());
And now you can access the Locale
plugin from your controllers, and anywhere you need to.
<?php
use Phalcon\Mvc\Controller;
/**
* @property MyApp\Locale $locale
*/
class MyController extends Controller
{
public function indexAction()
{
$name = 'Mike';
$text = $this->locale->_(
'hi-name',
[
'name' => $name,
]
);
$this->view->text = $text;
}
}
o en una vista directamente
<?php echo $locale->_('hi-name', ['name' => 'Mike']);
y para Volt:
<p>{{ locale._('hi-name', ['name' => 'Mike']) }}</p>
Ruteo
Algunas aplicaciones usan la URL de la petición para distinguir el contenido basado en distintos idiomas, para ayudar con SEO. A sample URL is:
https://mozilla.org/es-ES/firefox/
Phalcon can implement this functionality by using a Router.
Translate Factory
Loads Translate Adapter class using adapter
option, the remaining options will be passed to the adapter constructor.
<?php
use Phalcon\Translate\InterpolatorFactory;
use Phalcon\Translate\TranslateFactory;
$interpolator = new InterpolatorFactory();
$factory = new TranslateFactory($interpolator);
$options = [
'content' => [
'hi' => 'Hello',
'bye' => 'Good Bye',
],
];
$translator = $factory->newInstance('array', $options);
Adaptadores
Este componente hace uso de adaptadores para leer los mensajes de traducción de diferentes fuentes de una forma unificada.
Vector Nativo
Este adaptador almacena las cadenas traducidas en un vector PHP. Este adaptador es claramente el más rápido ya que las cadenas se almacenan en memoria. Additionally, the fact that it uses PHP arrays makes maintenance easier. Las cadenas también se pueden almacenar en ficheros JSON que a su vez se pueden traducir al formato nativo de vector PHP cuando se consultan.
<?php
use Phalcon\Translate\InterpolatorFactory;
use Phalcon\Translate\TranslateFactory;
$interpolator = new InterpolatorFactory();
$factory = new TranslateFactory($interpolator);
$options = [
'content' => [
'hi' => 'Hello',
'bye' => 'Good Bye',
],
];
$translator = $factory->newInstance('array', $options);
El uso recomendado debería ser crear un fichero por idioma y almacenarlo en el sistema de ficheros. Después de eso, puede cargar el fichero relevante, basado en el idioma seleccionado. Una estructura de ejemplo puede ser:
app/messages/en.php
app/messages/es.php
app/messages/fr.php
app/messages/zh.php
o en formato JSON
app/messages/en.json
app/messages/es.json
app/messages/fr.json
app/messages/zh.json
Cada fichero contiene vectores PHP, donde la clave es la clave de la cadena traducida y el valor del mensaje traducido. Cada fichero contiene las mismas claves pero los valores son, por supuesto, el mensaje traducido en el idioma correspondiente.
<?php
// app/messages/en.php
$messages = [
'hi' => 'Hello',
'bye' => 'Good Bye',
'hi-name' => 'Hello %name%',
'song' => 'This song is %song%',
];
<?php
// app/messages/fr.php
$messages = [
'hi' => 'Bonjour',
'bye' => 'Au revoir',
'hi-name' => 'Bonjour %name%',
'song' => 'La chanson est %song%',
];
Crear este adaptador se puede conseguir usando la Fábrica de Traducción, pero lo puede instanciar directamente:
<?php
use Phalcon\Translate\InterpolatorFactory;
use Phalcon\Translate\Adapter\NativeArray;
$interpolator = new InterpolatorFactory();
$options = [
'content' => [
'hi' => 'Hello',
'bye' => 'Good Bye',
],
];
$translator = new NativeArray($interpolator, $options);
No Encontrado
Si se pasa la opción triggerError
y se establece a true
entonces el método notFound()
se llamará cuando una clave no se encuentre. El método lanzará un error.
<?php
use Phalcon\Translate\InterpolatorFactory;
use Phalcon\Translate\TranslateFactory;
$interpolator = new InterpolatorFactory();
$factory = new TranslateFactory($interpolator);
$options = [
'content' => [
'hi' => 'Hello',
'bye' => 'Good Bye',
],
'triggerError' => true,
];
$translator = $factory->newInstance('array', $options);
echo $translator->query('unknown');
El código anterior lanzará un error cuando intentemos acceder a una entrada desconocida
.
Csv
Si sus cadenas de traducción se almacenan en un fichero .csv
. The Phalcon\Translate\Adapter\Csv adapter accepts the interpolator factory and an array with options necessary for loading the translations. El vector de opciones acepta:
Opción |
Descripción |
content |
The location of the CSV file on the file system |
delimiter |
The delimiter the CSV file uses (optional - defaults to ; ) |
enclosure |
The character that surrounds the text (optional - defaults to " ) |
<?php
use Phalcon\Translate\InterpolatorFactory;
use Phalcon\Translate\TranslateFactory;
$interpolator = new InterpolatorFactory();
$factory = new TranslateFactory($interpolator);
// `sample-key`|`sample-translated-text`
$options = [
'content' => '/path/to/translation-file.csv',
'delimiter' => '|',
'enclosure' => '`',
];
$translator = $factory->newInstance('csv', $options);
En el ejemplo anterior puede ver el uso de delimiter
y enclosure
. En la mayoría de casos no necesitará proporcionar estas opciones pero en caso de que sus ficheros CSV sean algo diferentes, tiene la opción de indicar al adaptador cómo debe analizar los contenidos del fichero de traducción.
Crear este adaptador se puede conseguir usando la Fábrica de Traducción, pero lo puede instanciar directamente:
<?php
use Phalcon\Translate\InterpolatorFactory;
use Phalcon\Translate\Adapter\Csv;
$interpolator = new InterpolatorFactory();
$options = [
'content' => '/path/to/translation-file.csv',
'delimiter' => '|',
'enclosure' => '`',
];
$translator = new Csv($interpolator, $options);
Gettext
NOTE: This adapter requires the gettext PHP extension. Please make sure that your system has it installed so that you can take advantage of this adapter’s functionality
The gettext format has been around for years and many applications are using it because it has become a standard, and it is easy to use. The translations are stored in .po
and .mo
files, and content can be easily added or changed using online editors or tools such as POEdit. This adapter requires files to be in specific folders, so it can locate the translation files. El vector de opciones acepta:
Opción |
Descripción |
locale |
The language locale you need |
defaultDomain |
The domain for the files. Este es el nombre actual de los ficheros. Ambos ficheros po y mo deben tener el mismo nombre. |
directory |
The directory where the translation files are located |
category |
A LC* PHP variable defining what category should be used. Esto mapea a una carpeta (como se ve a continuación en el ejemplo de estructura de directorios). |
<?php
use Phalcon\Translate\InterpolatorFactory;
use Phalcon\Translate\TranslateFactory;
$interpolator = new InterpolatorFactory();
$factory = new TranslateFactory($interpolator);
$options = [
'locale' => 'de_DE.UTF-8',
'defaultDomain' => 'translations',
'directory' => '/path/to/application/locales',
'category' => LC_MESSAGES,
];
$translator = $factory->newInstance('gettext', $options);
Una estructura de directorios de ejemplo para los ficheros de traducción es:
translations/
en_US.UTF-8/
LC_MESSAGES/
translations.mo
translations.po
de_DE.UTF-8
LC_MESSAGES/
translations.mo
translations.po
Crear este adaptador se puede conseguir usando la Fábrica de Traducción, pero lo puede instanciar directamente:
<?php
use Phalcon\Translate\InterpolatorFactory;
use Phalcon\Translate\Adapter\Gettext;
$interpolator = new InterpolatorFactory();
$options = [
'locale' => 'de_DE.UTF-8',
'defaultDomain' => 'translations',
'directory' => '/path/to/application/locales',
'category' => LC_MESSAGES,
];
$translator = new Gettext($interpolator, $options);
Personalizado
The Phalcon\Translate\Adapter\AdapterInterface interface must be implemented in order to create your own translation adapters or extend the existing ones:
<?php
use Phalcon\Translate\Adapter\AdapterInterface;
class MyTranslateAdapter implements AdapterInterface
{
/**
* @param array $options
*/
public function __construct(array $options);
/**
* @param string $translateKey
* @param array $placeholders
*
* @return string
*/
public function t(string $translateKey, array $placeholders = []);
/**
* @param string $translateKey
* @param array $placeholders
*
* @return string
*/
public function _(
string $translateKey,
array $placeholders = []
): string;
/**
* @param string $index
* @param array $placeholders
*
* @return string
*/
public function query(string $index, array $placeholders = []): string;
/**
* @param string $index
* @return bool
*/
public function exists(string $index): bool;
}
There are more adapters available for these components in the Phalcon Incubator
Interpolación
En muchos casos, las cadenas traducidas se necesitan con datos. With interpolation, you can inject a variable from your code to the translated message at a specific place. El marcador de posición en el mensaje está encerrado entre caracteres %
.
Hello %name, good %time%!
Salut %name%, bien %time%!
Suponiendo que el contexto no cambiará basado en las cadenas de cada idioma, puede añadir estos marcadores de posición a sus cadenas traducidas. The Translation component with its adapters will then correctly perform the interpolation for you.
Cambiar el Interpolador
Para cambiar el interpolador que usa su adaptador, todo lo que tiene que hacer es pasar el nombre del interpolador en las opciones usando la clave defaultInterpolator
.
<?php
use Phalcon\Translate\InterpolatorFactory;
use Phalcon\Translate\TranslateFactory;
$interpolator = new InterpolatorFactory();
$factory = new TranslateFactory($interpolator);
$options = [
'defaultInterpolator' => 'indexedArray',
'content' => [
'hi-name' => 'Hello %1$s, it\'s %2$d o\'clock',
],
];
$translator = $factory->newInstance('array', $options);
AssociatedArray
Phalcon\Translate\Interpolator\AssociativeArray is the default interpolator. Le permite realizar un reemplazo de clave/valor de los marcadores de posición.
<?php
use Phalcon\Translate\InterpolatorFactory;
use Phalcon\Translate\TranslateFactory;
$interpolator = new InterpolatorFactory();
$factory = new TranslateFactory($interpolator);
$options = [
'content' => [
'hi-name' => 'Hello %name%, good %time% !',
],
];
$translator = $factory->newInstance('array', $options);
$name = 'Henry';
$translator->_(
'hi-name',
[
'name' => $name,
'time' => 'day',
]
); // Hello Henry, good day !
$translator->_(
'hi-name',
[
'name' => $name,
'time' => 'night',
]
); // Hello Henry, good night !
IndexedArray
Phalcon\Translate\Interpolator\IndexedArray is another option that you can use as the interpolator. This interpolator follows the sprintf convention.
<?php
use Phalcon\Translate\InterpolatorFactory;
use Phalcon\Translate\TranslateFactory;
$interpolator = new InterpolatorFactory();
$factory = new TranslateFactory($interpolator);
$options = [
'defaultInterpolator' => 'indexedArray',
'content' => [
'hi-name' => 'Hello %1$s, it\'s %2$d o\'clock',
],
];
$translator = $factory->newInstance('array', $options);
$name = 'Henry';
$translator->_(
'hi-name',
[
$name,
8,
]
); // Hello Henry, it's 8 o'clock
Interpoladores Personalizados
The Phalcon\Translate\Interpolator\InterpolatorInterface interface must be implemented in order to create your own interpolators or extend the existing ones:
Fábrica de Interpoladores
The Phalcon\Translate\InterpolatorFactory factory offers an easy way to create interpolators. It is an object required to be passed to the translation adapters and translate factory, so that in turn can create the relevant interpolation class that the adapter will use.
<?php
use Phalcon\Translate\InterpolatorFactory;
use Phalcon\Translate\TranslateFactory;
$interpolator = new InterpolatorFactory();
$factory = new TranslateFactory($interpolator);
$translator = $factory->newInstance(
'array',
[
'content' => [
'hi' => 'Hello',
'bye' => 'Good Bye',
],
]
);