Plugin de localização de idioma
1Apresentação
Este plugin permite duas coisas:
- Gerenciar URLs que são prefixadas por idioma. O plugin redirecionará automaticamente o usuário para a página solicitada com /en/ (por exemplo) no início da URL.
- Carregar automaticamente as traduções correspondentes ao idioma usado, para disponibilizar as strings traduzidas aos templates.
2Configuração
No arquivo etc/temma.php, adicione o pré-plugin e configure-o:
<?php
return [
// carregamento do plugin
'plugins' => [
'_pre' => [
'\Temma\Plugins\Language'
]
],
// configuração do plugin
'x-language' => [
// lista de idiomas suportados pelo site
'supported' => [ 'fr', 'en', 'de' ],
// idioma padrão, caso o navegador não seja compatível com
// nenhum dos idiomas listados acima
'default' => 'fr',
// (opcional) você pode indicar para não usar
// prefixos de template (veja abaixo)
'templatePrefix' => false,
// (opcional) você pode indicar as URLs para as quais
// o gerenciamento de idioma não é ativado
'protectedUrls' => [
'/robots.txt',
'/sitemap.xml',
],
// (opcional) defina como false para evitar tentar
// carregar o arquivo de tradução
'readTranslationFile' => false,
],
// configuração da visão Smarty
'x-smarty' => [
'pluginsDir' => '/path/to/project/lib/smarty-plugins'
]
];
-
Linhas 5 a 9: Configuração dos plugins.
- Linha 7: Ativação do pré-plugin que oferece suporte ao gerenciamento de idioma.
-
Linhas 11 a 29: Configuração do plugin.
- Linha 13: Lista de idiomas suportados pelo site.
- Linha 16: Definição do idioma padrão (usado se o navegador não for compatível com nenhum dos idiomas listados).
- Linhas 22 a 25: Definição da lista de URLs para as quais o gerenciamento de idioma é desativado.
- Linha 28: Desativação do carregamento do arquivo de tradução.
-
Linhas 31 a 33: Configuração da visão Smarty.
- Linha 32: Adição do diretório que contém os plugins Smarty fornecidos pelo Temma, para que o interpretador Smarty consiga encontrar a extensão (veja abaixo).
3Como funciona
No exemplo de configuração acima, indicamos que o site tem traduções em francês, inglês e alemão.
Quando um navegador solicita uma página, dois casos podem ocorrer:
- A URL solicitada não começa com /fr/, /en/ nem /de/. Nesse caso, o plugin redirecionará o usuário para a URL solicitada, adicionando o prefixo de idioma. O idioma escolhido será o primeiro suportado pelo navegador que também seja suportado pelo site (se nenhum for adequado, o idioma padrão do site será usado).
- A URL solicitada começa com um prefixo de idioma. Nesse caso, o plugin verificará se o idioma solicitado é realmente suportado pelo site (se não for o caso, voltamos ao caso anterior). Se o idioma for suportado, o plugin extrairá o idioma, e modificará todas as informações relativas ao controlador, à ação e aos parâmetros, para fingir que esse prefixo não existia na URL. Além disso, o plugin carrega o arquivo de tradução correspondente, e adiciona um prefixo ao caminho que leva aos arquivos de template.
Por exemplo, se o usuário solicitar a página /page/list/date/5, com um navegador que suporte francês, o plugin a redirecionará para /fr/page/list/date/5.
Ao chegar em /fr/page/list/date/5, o plugin extrairá o idioma (fr), e carregará o arquivo de tradução em francês. Em seguida, ele modificará várias coisas:
- O controlador solicitado passa a ser page (e não mais fr).
- A ação solicitada passa a ser list (e não mais page).
- A lista de parâmetros passa a ser
['date', '5'](e não mais['list', 'date', '5']).
As variáveis de template $CONTROLLER, $ACTION e $URL também são modificadas de acordo.
4Gerenciamento de métodos de requisição
Se a URL solicitada começar com a informação de idioma (e esse idioma for autorizado pela configuração), o processamento explicado acima sempre será realizado.
Por outro lado, se a informação de idioma estiver ausente, o redirecionamento (para a mesma URL à qual o prefixo de idioma é adicionado) não é realizado se a requisição tiver usado o método POST ou PUT. Nesse caso, o plugin não faz nada e o processamento continua normalmente.
5URLs protegidas
É possível definir uma lista de URLs que não serão gerenciadas pelo plugin de idioma. Isso é útil se, por exemplo, você tiver controladores que geram automaticamente o conteúdo dos arquivos robots.txt ou sitemap.xml.
Observe que as URLs devem começar com o caractere '/'.
6Prefixo no caminho dos templates
A menos que a variável de configuração templatePrefix tenha sido definida como false, o plugin modificará o caminho que leva aos arquivos de template, adicionando no início um diretório correspondente ao idioma solicitado.
Assim, se a URL for /fr/article/list, o template usado será (por padrão) o arquivo templates/fr/article/list.tpl
Se o controlador usar outro arquivo, o prefixo será adicionado do mesmo jeito.
Por exemplo, se o controlador contiver a seguinte linha:
$this->_template('cms/content/article_list.tpl');
O Temma usará o arquivo templates/fr/cms/content/article_list.tpl
7Arquivos de tradução
7.1Formato básico
O plugin carrega arquivos contendo as traduções. Esses arquivos devem ser colocados no diretório etc/lang/ do projeto. Assim como o arquivo de configuração, esses arquivos podem estar nos formatos PHP, JSON, INI, YAML ou NEON.
O formato PHP é recomendado, pois se beneficia do cache de OPCode (o arquivo não é relido a cada acesso). Mas você pode escolher um formato diferente, como o INI, por questão de simplicidade.
Os nomes dos arquivos dependem do idioma das traduções que contêm: fr.php, en.ini, es.json, etc.
Aqui está um exemplo de arquivo fr.php:
<?php
return [
'default' => [
'Controllers' => 'Contrôleurs',
'Model' => 'Modèle',
'Views' => 'Vues',
],
'header' => [
'title' => 'Temma : framework PHP simple et performant',
'Home' => 'Accueil',
'Download' => 'Télécharger',
'Community' => 'Communauté',
'Examples' => 'Exemples',
],
'footer' => [
'Designed by' => 'Conception par',
],
];
A mesma coisa no formato INI (arquivo fr.ini):
[default]
Controllers="Contrôleurs"
Model="Modèle"
Views="Vues"
[header]
title="Temma : framework PHP simple et performant"
Home="Accueil"
Download="Télécharger"
Community="Communauté"
Examples="Exemples"
[footer]
Designed by="Conception par"
No formato JSON (arquivo fr.json):
{
"default": {
"Controllers": "Contrôleurs",
"Model": "Modèle",
"Views": "Vues"
},
"header": {
"title": "Temma : framework PHP simple et performant",
"Home": "Accueil",
"Download": "Télécharger",
"Community": "Communauté",
"Examples": "Exemples"
},
"footer": {
"Designed by": "Conception par"
}
}
No formato YAML (arquivo fr.yaml) ou NEON (arquivo fr.neon):
default:
Controllers: Contrôleurs
Model: Modèle
Views: Vues
header:
title: "Temma : framework PHP simple et performant"
Home: Accueil
Download: Télécharger
Community: Communauté
Examples: Exemples
footer:
Designed by: Conception par
Como você pode ver, as strings traduzidas são organizadas por seções (aqui default, header e footer). Essas seções correspondem a domínios de tradução.
Aqui, novamente, as strings de base estão em inglês, e cada uma delas tem sua tradução para o francês.
Importante: se você tentar traduzir uma string, mas ela não for encontrada no arquivo de tradução, ela será usada como está.
No exemplo acima, podemos ver que as strings de base estão em inglês, e que uma tradução em francês foi fornecida.
Portanto, não será necessário fornecer um arquivo de tradução em inglês.
7.2Gerenciamento de contexto
É possível criar várias versões de uma string de caracteres. Cada uma dessas versões depende de um contexto personalizável. Esses contextos são usados, por exemplo, para gerenciar versões masculinas/femininas de um texto.
O contexto padrão é o primeiro definido.
Para definir contextos, basta associar um array associativo à chave de tradução.
Exemplo de arquivo fr.php:
<?php
return [
'default' => [
'Actor' => [
'male' => 'Acteur',
'female' => 'Actrice',
],
'Waiter' => [
'non-binary' => 'Serveur⋅euse',
'male' => 'Serveur',
'female' => 'Serveuse',
],
],
];
E o arquivo en.php correspondente:
<?php
return [
'job' => [
'Actor' => [
'male' => 'Actor',
'female' => 'Actress',
],
'Waiter' => [
'non-binary' => 'Waitstaff',
'male' => 'Waiter',
'female' => 'Waitress',
],
],
];
7.3Gerenciamento de contagem (singular/plural)
Os arquivos de tradução também podem incluir diferentes variações da mesma string de caracteres, dependendo do número de elementos. Isso vai além do simples singular/plural: pode ser usado quando não há elementos, ou quando há um número variável de elementos.
Assim como com os contextos, você precisa definir um array associativo, cujas chaves são o número de elementos correspondente à declinação. É possível ter chaves começando com < ou <= para definir intervalos.
O valor padrão é definido com a chave *. Se não for fornecida, o primeiro valor da lista será usado.
Exemplo de arquivo de tradução fr.php:
<?php
return [
'default' => [
'there are flowers' => [
'0' => "il n'y a pas de fleurs",
'1' => 'il y a une fleur',
'<=3' => 'il y a quelques fleurs',
'<10' => 'il y a des fleurs',
'*' => 'il y a plein de fleurs',
],
],
];
E sua versão em en.php:
<?php
return [
'default' => [
'there are flowers' => [
'0' => 'there are no flowers',
'1' => 'there is one flower',
'<=3' => 'there are a few flowers',
'<10' => 'there are some flowers',
'*' => 'there are lots of flowers',
],
],
];
7.4Contexto + contagem
Contextos e contagens podem ser usados em conjunto. Para isso, cada contexto deve conter uma contagem.
Aqui está um exemplo:
<?php
return [
'default' => [
'there are actors' => [
'male' => [
'0' => "il n'y a pas d'acteurs",
'1' => 'il y a un acteur',
'*' => 'il y a des acteurs',
],
'female' => [
'0' => "il n'y a pas d'actrices",
'1' => 'il y a une actrice',
'*' => 'il y a des actrices',
],
],
],
];
8Uso nos templates
Nos templates Smarty, duas variáveis adicionais são criadas pelo plugin:
- $lang: Contém o idioma atual (fr, en, etc.).
- $l10n: Contém a tabela de traduções. Não deve ser usada.
Você pode usar a variável $lang diretamente em seus templates:
{if $lang == 'fr'}
Bonjour tout le monde
{else}
Hello everyone
{/if}
Exibido em francês, isso ficará assim:
Bonjour tout le monde
Em inglês:
Hello everyone
Você também pode usar o modificador |l10n e a tag de bloco {l10n}...{/l10n}.
9Templates: modificador
Para traduzir strings, você pode usar o modificador l10n. Ele procura a tradução da string recebida como entrada, e a retorna após escapar quaisquer caracteres especiais.
<h1>{"Model"|l10n}</h1>
<h2>{"Views"|l10n}</h2>
{"zkwx<hyq"|l10n}
Exibido em francês:
<h1>Modèle</h1>
<h2>Vues</h2>
zkwx<hyq
Em inglês:
<h1>Model</h1>
<h2>Views</h2>
zkwx<hyq
- Linhas 1 e 2: As strings listadas na seção default do arquivo de tradução podem ser usadas diretamente.
- Linha 4: Se a string não for encontrada no arquivo de tradução, ela é usada como está. O caractere especial < é escapado como <.
9.1Modificador: domínio
Para especificar um domínio, adicione-o antes da string de tradução, usando o caractere de sustenido (#) para separar o domínio da string.
{"header#Home"|l10n}
{"header#Download"|l10n}
Exibido em francês, isso resultaria em:
Accueil
Télécharger
Em inglês:
Home
Download
9.2Modificador: contexto e contagem
O contexto e a contagem podem ser fornecidos após o domínio, separados por vírgulas.
Se o contexto não for fornecido, o primeiro listado no arquivo de tradução será usado. Se a contagem não for fornecida, a contagem padrão (*) será usada; se ela não existir, a primeira contagem definida será usada.
Exemplo de template:
{"default,female,1#there are actors"|l10n}
{",,3#there are actors"|l10n}
{"default,female#there are actors"|l10n}
O resultado em francês:
il y a une actrice
il y a des acteurs
il y a des actrices
- Linha 1: O domínio padrão é usado, o contexto female, e a contagem é definida como 1.
- Linha 2: O domínio não é especificado, então o contexto padrão é usado. O contexto não é especificado, então o primeiro definido é usado. A contagem é 3.
- Linha 3: Domínio padrão e contexto female são usados. A contagem não é especificada, então a contagem padrão (*) é usada.
9.3Modificador: parâmetros
Parâmetros podem ser fornecidos ao modificador, e serão usados para substituir partes do texto. Cada parâmetro ocupará o lugar de um marcador do tipo %1%, %2%, %3% e assim por diante.
Se o arquivo de tradução contiver as seguintes definições:
<?php
return [
'default' => [
'Hello %1%' => 'Bonjour %1%',
'%1% has %2% kids' => "%1% a %2% enfants"
]
];
Seu template pode conter:
{"Hello %1%"|l10n:'James'}
{"%1% has %2% kids"|l10n:'Alice':3}
Exibido em francês:
Bonjour James
Alice a 3 enfants
Os parâmetros podem, claro, ser usados ao mesmo tempo que domínio, contexto e contagem.
O domínio está disponível com o marcador %domain%.
O contexto está disponível com o marcador %ctx%.
A contagem está disponível com o marcador %count%.
Por exemplo, com o seguinte arquivo de tradução:
<?php
return [
'default' => [
'%1% has %count% kids' => [
'any' => [
'0' => "%1% n'a pas d'enfant",
'1' => "%1% a un enfant",
'*' => "%1% a %count% enfants"
],
'girls' => [
'0' => "%1% n'a pas de filles",
'1' => "%1% a une fille",
'*' => "%1% a %count% filles"
],
'boys' => [
'0' => "%1% n'a pas de garçons",
'1' => "%1% a un garçon",
'*' => "%1% a %count% garçons"
]
]
]
];
Se seu template contiver isto:
{",,3#%1% has %count% kids"|l10n:'Alice'}
{"default,boys,0#%1% has %count% kids"|l10n:'Bob'}
{",girls,1#%1% has %count% kids"|l10n:'Camille'}
O resultado será:
Alice a 3 enfants
Bob n'a pas de garçons
Camille a une fille
- Linha 1: O domínio não é especificado, então o domínio padrão será usado. O contexto não é especificado, então o primeiro definido (any) será usado. E usamos o valor 3 para a contagem.
- Linha 2: O domínio default é especificado. O contexto boys é especificado. A contagem é definida como 0.
- Linha 3: Domínio não especificado. O contexto girls é especificado. A contagem é 1.
10Templates: tag de bloco
Você também pode usar a tag de bloco Smarty {l10n}.
{l10n}Model{/l10n}
Exibido em francês:
Modèle
Diferentemente dos modificadores, os blocos de tradução não escapam caracteres especiais.
10.1Tag de bloco: domínio
O domínio padrão é sempre default. Outro domínio pode ser especificado usando o atributo _ (o caractere sublinhado) ou o atributo domain na tag de abertura.
{l10n _='header'}
Home
{/l10n}
{l10n domain='header'}
Download
{/l10n}
Exibido em francês:
Accueil
Télécharger
10.2Tag de bloco: contexto e contagem
É possível especificar contexto e contagem de duas formas diferentes.
A primeira é adicionar os atributos ctx e count
na tag de abertura.
A segunda é usar o atributo _ (caractere sublinhado),
adicionando o contexto e a contagem após o domínio, separados por vírgulas.
Se o contexto não for fornecido, o primeiro listado no arquivo de tradução será usado. Se a contagem não for fornecida, a contagem padrão (*) será usada; se ela não existir, a primeira contagem definida será usada.
Exemplo de template:
{l10n _='default,female,1'}
there are actors
{/l10n}
{l10n _=',,3'}
there are actors
{/l10n}
{l10n _='default,female'}
there are actors
{/l10n}
Exibido em francês:
il y a une actrice
il y a des acteurs
il y a des actrices
- Linha 1: O domínio padrão, o contexto female, e a contagem definida como 1 são usados.
- Linha 2: O domínio não é especificado, então o contexto padrão é usado. O contexto não é especificado, então o primeiro definido é usado. A contagem é 3.
- Linha 3: Domínio padrão e contexto female são usados. A contagem não é especificada, então a contagem padrão (*) é usada.
O atributo count pode ser um número ou um array. Se for um array, o número de seus elementos será usado como contagem.
Exemplo de template:
{$actors = ['Alice', 'Bernadette', 'Camille']}
{l10n ctx='female' count=$actors}
there are actors
{/l10n}
Exibido em francês:
il y a des actrices
10.3Tag de bloco: parâmetros
Assim como os modificadores, os blocos de tradução podem receber parâmetros. Esses parâmetros são passados como atributos na tag de abertura {l10n}, e usados nas strings no formato %name_parameter%.
Assim, é possível usar parâmetros como name="Luke" na tag (com o marcador %name% no template). Mas, para ser compatível com os parâmetros usados no modificador (veja acima), é recomendado nomear os parâmetros usando números a partir de 1. Por exemplo, o parâmetro 1="Luke" na tag, e o marcador %1% no template.
Se o arquivo de tradução contiver as seguintes definições:
<?php
return [
'default' => [
'Hello %1%' => "Bonjour %1%",
'%1% has %2% kids' => "%1% a %2% enfants"
]
];
Seu template pode conter:
{l10n 1='Marie'}
Hello %1%
{/l10n}
{l10n 1='Alice' 2='3'}
%1% has %2% kids
{/l10n}
Exibição em francês:
Bonjour Marie
Alice a 3 enfants
Os parâmetros podem, claro, ser usados ao mesmo tempo que domínio, contexto e contagem.
O domínio está disponível com o marcador %domain%.
O contexto está disponível com o marcador %ctx%.
A contagem está disponível com o marcador %count%.
Por exemplo, com o seguinte arquivo de tradução:
<?php
return [
'default' => [
'%1% has %count% kids' => [
'any' => [
'0' => "%1% n'a pas d'enfant",
'1' => "%1% a un enfant",
'*' => "%1% a %count% enfants"
],
'girls' => [
'0' => "%1% n'a pas de filles",
'1' => "%1% a une fille",
'*' => "%1% a %count% filles"
],
'boys' => [
'0' => "%1% n'a pas de garçons",
'1' => "%1% a un garçon",
'*' => "%1% a %count% garçons"
]
]
]
];
Se seu template contiver:
{l10n _=',,1' 1='Alice'}
%1% has %count% kids
{/l10n}
{l10n _='default,girls,0' 1='Bob'}
%1% has %count% kids
{/l10n}
{l10n _=',boys,3' 1='Camille'}
%1% has %count% kids
{/l10n}
Isso seria exatamente o mesmo que isto:
{l10n count=1 1='Alice'}
%1% has %count% kids
{/l10n}
{l10n domain='default' ctx='girls' count=0 1='Bob'}
%1% has %count% kids
{/l10n}
{l10n ctx='boys' count=['Huey', 'Dewey', 'Louie'] 1='Camille'}
%1% has %count% kids
{/l10n}
O resultado será:
Alice a um enfant
Bob n'a pas de filles
Camille a 3 garçons
- Linhas 1 a 3: O domínio não é especificado, então o domínio padrão é usado. O contexto não é especificado, então o primeiro definido (any) será usado. E usamos o valor 1 para a contagem.
- Linhas 4 a 6: O domínio default é especificado. O contexto girls é especificado. A contagem é definida como 0.
- Linhas 7 a 9: O domínio não é especificado. O contexto boys é especificado. Um array é fornecido para a contagem, contendo 3 elementos.
11Prefixo nos arquivos de erro
Quando este plugin é usado, é necessário traduzir os arquivos de erro definidos na seção errorPages do arquivo etc/temma.php (veja a documentação).
Os caminhos definidos na configuração recebem então um prefixo composto por um diretório error-pages, seguido de um diretório correspondente ao idioma usado.
Por exemplo, para o seguinte arquivo de configuração:
[
'errorPages' => [
'404' => 'error404.html'
]
]
Se solicitarmos uma página que não existe em francês (por exemplo, a URL /fr/sdsjnzeoizoueh), o Temma procurará o arquivo
www/error-pages/fr/error404.html
Se solicitarmos a mesma página em inglês (por exemplo, a URL /en/sdsjnzeoizoueh), o Temma procurará o arquivo
www/error-pages/en/error404.html