Site de práticas recomendadas em vários idiomas


179

Estou lutando com essa questão há alguns meses, mas não estava em uma situação em que precisava explorar todas as opções possíveis antes. No momento, sinto que é hora de conhecer as possibilidades e criar minha própria preferência pessoal para usar nos meus próximos projetos.

Deixe-me primeiro esboçar a situação que estou procurando

Estou prestes a atualizar / reconstruir um sistema de gerenciamento de conteúdo que já uso há bastante tempo. No entanto, sinto que a linguagem multilíngue é uma grande melhoria para este sistema. Antes eu não usava nenhuma estrutura, mas vou usar o Laraval4 para o próximo projeto. O Laravel parece ser a melhor escolha de uma maneira mais limpa de codificar PHP. Sidenote: Laraval4 should be no factor in your answer. Estou procurando maneiras gerais de tradução que sejam independentes de plataforma / estrutura.

O que deve ser traduzido

Como o sistema que estou procurando precisa ser o mais amigável possível, o método de gerenciamento da tradução deve estar dentro do CMS. Não deve haver necessidade de iniciar uma conexão FTP para modificar arquivos de tradução ou quaisquer modelos analisados ​​em html / php.

Além disso, estou procurando a maneira mais fácil de traduzir várias tabelas de banco de dados, talvez sem a necessidade de criar tabelas adicionais.

O que eu vim comigo mesmo

Como eu tenho procurado, lendo e experimentando as coisas já. Existem algumas opções que tenho. Mas ainda não sinto que cheguei a um método de melhores práticas para o que realmente estou procurando. No momento, é isso que eu criei, mas esse método também tem efeitos colaterais.

  1. Modelos Analisados ​​em PHP: o sistema de modelos deve ser analisado pelo PHP. Dessa forma, sou capaz de inserir os parâmetros traduzidos no HTML sem precisar abrir os modelos e modificá-los. Além disso, os modelos analisados ​​pelo PHP me dão a capacidade de ter 1 modelo para o site completo, em vez de ter uma subpasta para cada idioma (que eu já tinha antes). O método para atingir esse objetivo pode ser Smarty, TemplatePower, Laravel's Blade ou qualquer outro analisador de modelos. Como eu disse, isso deve ser independente da solução escrita.
  2. Orientado a banco de dados : talvez eu não precise mencionar isso novamente. Mas a solução deve ser orientada por banco de dados. O CMS tem como objetivo ser orientado a objetos e MVC, então eu precisaria pensar em uma estrutura de dados lógica para as strings. Como meus modelos seria estruturada: templates / Controller / view.php talvez esta estrutura faria mais sentido: Controller.View.parameter. A tabela do banco de dados teria esses campos por muito tempo com um valuecampo. Dentro dos modelos, poderíamos usar algum método de classificação como echo __('Controller.View.welcome', array('name', 'Joshua'))e o parâmetro contém Welcome, :name. Assim sendo o resultado Welcome, Joshua. Essa parece ser uma boa maneira de fazer isso, porque os parâmetros como: name são fáceis de entender pelo editor.
  3. Baixa carga de banco de dados : é claro que o sistema acima causaria cargas de banco de dados se essas cadeias estivessem sendo carregadas em movimento. Portanto, eu precisaria de um sistema de cache que renderize novamente os arquivos de idioma assim que forem editados / salvos no ambiente de administração. Como os arquivos são gerados, também é necessário um bom layout do sistema de arquivos. Acho que podemos ir com languages/en_EN/Controller/View.phpou .ini, o que melhor lhe convier. Talvez um .ini seja analisado mais rapidamente no final. Isso deve conter os dados no arquivo format parameter=value; . Eu acho que essa é a melhor maneira de fazer isso, pois cada View renderizada pode incluir seu próprio arquivo de idioma, se existir. Os parâmetros de idioma devem ser carregados em uma visualização específica e não em um escopo global para impedir que os parâmetros se substituam.
  4. Tradução da tabela de banco de dados : é com isso que estou mais preocupado. Estou procurando uma maneira de criar traduções de Notícias / Páginas / etc. o mais rápido possível. Ter duas tabelas para cada módulo (por exemplo Newse News_translations) é uma opção, mas parece muito trabalho para obter um bom sistema. Uma das coisas que eu vim com é baseado em um data versioningsistema de I escreveu: existe um nome de tabela de banco de dados Translations, esta tabela tem uma combinação única de language, tablenameeprimarykey. Por exemplo: en_En / News / 1 (referindo-se à versão em inglês do item de Notícias com ID = 1). Mas existem duas grandes desvantagens nesse método: primeiro, essa tabela tende a ficar bastante longa com muitos dados no banco de dados e, em segundo lugar, seria um trabalho muito bom usar essa configuração para pesquisar na tabela. Por exemplo, pesquisar a lesma de SEO do item seria uma pesquisa de texto completo, o que é bastante idiota. Mas, por outro lado: é uma maneira rápida de criar conteúdo traduzível em todas as tabelas muito rapidamente, mas não acredito que esse profissional supere os contras.
  5. Trabalho de front-end : também o front-end precisaria de algumas reflexões. É claro que armazenaríamos os idiomas disponíveis em um banco de dados e (de) ativamos os que precisamos. Dessa forma, o script pode gerar uma lista suspensa para selecionar um idioma e o back-end pode decidir automaticamente quais traduções podem ser feitas usando o CMS. O idioma escolhido (por exemplo, en_EN) seria usado ao obter o arquivo de idioma para uma visualização ou obter a tradução correta para um item de conteúdo no site.

Então, lá estão eles. Minhas idéias até agora. Eles ainda nem incluem opções de localização para datas, etc., mas como meu servidor suporta PHP5.3.2 +, a melhor opção é usar a extensão intl conforme explicado aqui: http://devzone.zend.com/1500/internationalization-in -php-53 / - mas isso seria útil em qualquer estádio de desenvolvimento posterior. Por enquanto, a questão principal é como ter as melhores práticas de tradução do conteúdo em um site.

Além de tudo que expliquei aqui, ainda tenho outra coisa que ainda não decidi, parece uma pergunta simples, mas na verdade está me dando dores de cabeça:

Tradução de URL? Devemos fazer isso ou não? e de que maneira?

Então .. se eu tiver esse URL: http://www.domain.com/about-use inglês é o meu idioma padrão. Esse URL deve ser traduzido para http://www.domain.com/over-onsquando eu escolher o holandês como meu idioma? Ou devemos seguir o caminho mais fácil e simplesmente alterar o conteúdo da página visível em /about. A última coisa que parece não ser uma opção válida, porque isso geraria várias versões do mesmo URL; a indexação do conteúdo falhará da maneira correta.

Outra opção está sendo usada http://www.domain.com/nl/about-us. Isso gera pelo menos um URL exclusivo para cada conteúdo. Também seria mais fácil ir para outro idioma, por exemplo, http://www.domain.com/en/about-use o URL fornecido é mais fácil de entender para os visitantes do Google e Humanos. Usando esta opção, o que fazemos com os idiomas padrão? O idioma padrão deve remover o idioma selecionado por padrão? Então, redirecionando http://www.domain.com/en/about-uspara http://www.domain.com/about-us... A meu ver, essa é a melhor solução, porque quando o CMS é configurado para apenas um idioma, não é necessário ter essa identificação no URL.

E uma terceira opção é uma combinação das duas opções: usar o "idioma-identificação-menos" -URL ( http://www.domain.com/about-us) para o idioma principal. E use um URL com uma lesma de SEO traduzida para sub-idiomas: http://www.domain.com/nl/over-ons&http://www.domain.com/de/uber-uns

Espero que minha pergunta faça sua cabeça rachar, eles racharam a minha com certeza! Já me ajudou a resolver as coisas como uma pergunta aqui. Deu-me a possibilidade de revisar os métodos que usei antes e a idéia que estou tendo para o meu próximo CMS.

Eu gostaria de agradecer a você por ler esse monte de texto!

// Edit #1:

Esqueci de mencionar: a função __ () é um alias para traduzir uma determinada string. Nesse método, obviamente deve haver algum tipo de método de fallback em que o texto padrão é carregado quando ainda não há traduções disponíveis. Se a tradução estiver ausente, ela deve ser inserida ou o arquivo de tradução deve ser regenerado.


Respostas:


115

Premissa do tópico

Existem três aspectos distintos em um site multilíngue:

  • tradução de interface
  • conteúdo
  • roteamento de URL

Embora todos estejam interconectados de maneiras diferentes, do ponto de vista do CMS, eles são gerenciados usando diferentes elementos da interface do usuário e armazenados de maneira diferente. Você parece confiante em sua implementação e compreensão dos dois primeiros. A pergunta era sobre o último aspecto - "Tradução da URL? Devemos fazer isso ou não? E de que maneira?"

Do que o URL pode ser feito?

Uma coisa muito importante é que não se apaixone pelo IDN . Em vez disso, favorecer a transliteração (também: transcrição e romanização). Embora, à primeira vista, o IDN pareça uma opção viável para URLs internacionais, na verdade não funciona como anunciado por dois motivos:

  • alguns navegadores transformarão os caracteres não ASCII como 'ч'ou 'ž'em '%D1%87'e'%C5%BE'
  • se o usuário tiver temas personalizados, é provável que a fonte do tema não tenha símbolos para essas letras

Na verdade, tentei abordar os IDNs alguns anos atrás em um projeto baseado no Yii (quadro horrível, IMHO). Encontrei os dois problemas acima mencionados antes de raspar a solução. Além disso, suspeito que possa ser um vetor de ataque.

Opções disponíveis ... como eu as vejo.

Basicamente, você tem duas opções, que podem ser abstraídas como:

  • http://site.tld/[:query]: onde [:query]determina a escolha do idioma e do conteúdo

  • http://site.tld/[:language]/[:query]: onde [:language]parte do URL define a escolha do idioma e [:query]é usada apenas para identificar o conteúdo

A consulta é Α e Ω ..

Vamos dizer que você escolhe http://site.tld/[:query].

Nesse caso, você tem uma fonte primária de idioma: o conteúdo do [:query]segmento; e duas fontes adicionais:

  • valor $_COOKIE['lang']para esse navegador específico
  • lista de idiomas em HTTP Accept-Language (1) , (2) cabeçalho

Primeiro, você precisa corresponder a consulta a um dos padrões de roteamento definidos (se você escolher Laravel, leia aqui ). Na correspondência bem-sucedida de padrão, você precisará encontrar o idioma.

Você precisaria passar por todos os segmentos do padrão. Encontre as traduções em potencial para todos esses segmentos e determine qual idioma foi usado. As duas fontes adicionais (cookie e cabeçalho) seriam usadas para resolver conflitos de roteamento, quando (não "se") surgirem.

Tomemos por exemplo: http://site.tld/blog/novinka.

Isso é transliteração "блог, новинка", que em inglês significa aproximadamente "blog", "latest".

Como você já pode notar, em russo "блог" será transliterado como "blog". O que significa que, na primeira parte de [:query]você (na melhor das hipóteses ), a ['en', 'ru']lista de idiomas possíveis será finalizada . Então você toma o próximo segmento - "novinka". Isso pode ter apenas um idioma na lista de possibilidades: ['ru'].

Quando a lista possui um item, você encontrou o idioma com sucesso.

Mas se você terminar com 2 (exemplo: russo e ucraniano) ou mais possibilidades .. ou 0 possibilidades, conforme o caso. Você precisará usar cookies e / ou cabeçalho para encontrar a opção correta.

E se tudo mais falhar, você escolhe o idioma padrão do site.

Idioma como parâmetro

A alternativa é usar URL, que pode ser definido como http://site.tld/[:language]/[:query]. Nesse caso, ao traduzir a consulta, você não precisa adivinhar o idioma, porque nesse momento você já sabe qual usar.

Há também uma fonte secundária de linguagem: o valor do cookie. Mas aqui não faz sentido mexer no cabeçalho Accept-Language, porque você não está lidando com uma quantidade desconhecida de idiomas possíveis em caso de "partida a frio" (quando o usuário abre o site pela primeira vez com uma consulta personalizada).

Em vez disso, você tem três opções simples e priorizadas:

  1. se o [:language]segmento estiver definido, use-o
  2. se $_COOKIE['lang']estiver definido, use-o
  3. usar idioma padrão

Quando você possui o idioma, você simplesmente tenta traduzir a consulta e, se a tradução falhar, use o "valor padrão" para esse segmento específico (com base nos resultados do roteamento).

Aqui não é uma terceira opção?

Sim, tecnicamente você pode combinar ambas as abordagens, mas isso iria complicar o processo e só acomodar as pessoas que querem URL alterar manualmente de http://site.tld/en/newspara http://site.tld/de/newse esperar que a página de notícias para a mudança para Alemão.

Mas mesmo esse caso pode ser mitigado usando o valor do cookie (que conteria informações sobre a escolha anterior do idioma), para implementar com menos mágica e esperança.

Qual abordagem usar?

Como você já deve ter adivinhado, eu recomendaria http://site.tld/[:language]/[:query]como a opção mais sensata.

Também em situações reais, você teria a terceira parte principal no URL: "title". Como no nome do produto na loja online ou no título do artigo no site de notícias.

Exemplo: http://site.tld/en/news/article/121415/EU-as-global-reserve-currency

Nesse caso, '/news/article/121415'seria a consulta e o 'EU-as-global-reserve-currency'é title. Puramente para fins de SEO.

Isso pode ser feito no Laravel?

Meio, mas não por padrão.

Não estou muito familiarizado com isso, mas pelo que vi, o Laravel usa um mecanismo de roteamento baseado em padrões simples. Para implementar URLs multilíngues, você provavelmente precisará estender as classes principais , porque o roteamento multilíngue precisa acessar diferentes formas de armazenamento (banco de dados, cache e / ou arquivos de configuração).

Está roteado. E agora?

Como resultado, você terminaria com duas informações valiosas: idioma atual e segmentos de consulta traduzidos. Esses valores podem ser usados ​​para despachar para as classes que produzirão o resultado.

Basicamente, o seguinte URL: http://site.tld/ru/blog/novinka(ou a versão sem '/ru') é transformado em algo como

$parameters = [
   'language' => 'ru',
   'classname' => 'blog',
   'method' => 'latest',
];

Que você apenas usa para despachar:

$instance = new {$parameter['classname']};
$instance->{'get'.$parameters['method']}( $parameters );

.. ou alguma variação, dependendo da implementação específica.


1
Obrigado por mais um insight! Muito atencioso! Eu estava pensando em ter o parâmetro language no URL também. Isso simplesmente parece a melhor maneira de identificar um idioma específico, não apenas para o usuário, mas também para fins de SEO. Caso um usuário altere / en / news para / de / news, minha ideia era fazer um redirecionamento 301 (permanente) para / de / nachrichten, por exemplo. Só para ter certeza cada idioma tem apenas um URL exclusivo por página (de novo para fins de SEO)
Joshua - Pendo

Está ficando cada vez mais difícil selecionar a melhor resposta. Atualmente, existem cerca de 3/4 respostas que merecem pelo menos uma parte da recompensa cada. Combinados, eles se tornam uma resposta sólida para tudo o que eu queria esclarecer juntos :)
Joshua - Pendo

Aceitei sua resposta para fornecer pelo menos algum representante extra para a resposta detalhada que você deu na tradução de URL. Muito apreciado! No entanto, a recompensa é concedida à pessoa abaixo de você, pois ela respondeu todos os aspectos da minha pergunta de maneira independente da plataforma.
Joshua - Pendo

52

Implementando o i18n sem o desempenho atingido usando um pré-processador, conforme sugerido por Thomas Bley

No trabalho, recentemente implementamos o i18n em algumas de nossas propriedades, e uma das coisas com as quais continuamos lutando foi o impacto no desempenho de lidar com traduções on-the-fly, então descobri este ótimo post de Thomas Bley que inspirou a maneira como usamos o i18n para lidar com grandes cargas de tráfego com problemas mínimos de desempenho.

Em vez de chamar funções para todas as operações de tradução, que, como sabemos no PHP, são caras, definimos nossos arquivos de base com espaços reservados e usamos um pré-processador para armazenar em cache esses arquivos (armazenamos o tempo de modificação do arquivo para garantir que estamos servindo o conteúdo mais recente em todos os momentos).

Tags de tradução

Thomas usa {tr}e {/tr}tags para definir onde as traduções começam e terminam. Devido ao fato de que estamos usando GALHO, não queremos usar {para evitar confusão por isso usamos [%tr%]e [%/tr%]em vez disso. Basicamente, isso se parece com isso:

`return [%tr%]formatted_value[%/tr%];`

Observe que Thomas sugere usar o inglês base no arquivo. Não fazemos isso porque não queremos modificar todos os arquivos de tradução se alterarmos o valor em inglês.

Os arquivos INI

Em seguida, criamos um arquivo INI para cada idioma, no formato placeholder = translated:

// lang/fr.ini
formatted_value = number_format($value * Model_Exchange::getEurRate(), 2, ',', ' ') . '€'

// lang/en_gb.ini
formatted_value = '£' . number_format($value * Model_Exchange::getStgRate())

// lang/en_us.ini
formatted_value = '$' . number_format($value)

Seria trivial para permitir que um usuário para modificar estes dentro do CMS, é só pegar os pares de chaves por um preg_spliton \nou =e fazendo o CMS apto a reescrever os arquivos INI.

O componente pré-processador

Essencialmente, Thomas sugere o uso de uma função de 'compilador' just-in-time (embora, na verdade, seja um pré-processador) como essa para pegar seus arquivos de tradução e criar arquivos PHP estáticos no disco. Dessa forma, nós armazenamos nossos arquivos traduzidos em cache, em vez de chamar uma função de tradução para cada sequência do arquivo:

// This function was written by Thomas Bley, not by me
function translate($file) {
  $cache_file = 'cache/'.LANG.'_'.basename($file).'_'.filemtime($file).'.php';
  // (re)build translation?
  if (!file_exists($cache_file)) {
    $lang_file = 'lang/'.LANG.'.ini';
    $lang_file_php = 'cache/'.LANG.'_'.filemtime($lang_file).'.php';

    // convert .ini file into .php file
    if (!file_exists($lang_file_php)) {
      file_put_contents($lang_file_php, '<?php $strings='.
        var_export(parse_ini_file($lang_file), true).';', LOCK_EX);
    }
    // translate .php into localized .php file
    $tr = function($match) use (&$lang_file_php) {
      static $strings = null;
      if ($strings===null) require($lang_file_php);
      return isset($strings[ $match[1] ]) ? $strings[ $match[1] ] : $match[1];
    };
    // replace all {t}abc{/t} by tr()
    file_put_contents($cache_file, preg_replace_callback(
      '/\[%tr%\](.*?)\[%\/tr%\]/', $tr, file_get_contents($file)), LOCK_EX);
  }
  return $cache_file;
}

Nota: Não verifiquei se o regex funciona, não o copiei do servidor da empresa, mas você pode ver como a operação funciona.

Como chamá-lo

Novamente, este exemplo é de Thomas Bley, não de mim:

// instead of
require("core/example.php");
echo (new example())->now();

// we write
define('LANG', 'en_us');
require(translate('core/example.php'));
echo (new example())->now();

Armazenamos o idioma em um cookie (ou variável de sessão, se não conseguimos obter um cookie) e, em seguida, recuperamos em cada solicitação. Você pode combinar isso com um opcional$_GET parâmetro para substituir o idioma, mas não sugiro subdomínio por idioma ou página por idioma, porque isso tornará mais difícil ver quais páginas são populares e reduzirá o valor de entrada links, pois você dificilmente os espalhará.

Por que usar esse método?

Gostamos deste método de pré-processamento por três razões:

  1. O enorme ganho de desempenho ao não chamar um monte de funções para conteúdo que raramente muda (com esse sistema, 100 mil visitantes em francês ainda acabam executando a substituição da tradução apenas uma vez).
  2. Ele não adiciona nenhuma carga ao nosso banco de dados, pois usa arquivos simples e é uma solução pura de PHP.
  3. A capacidade de usar expressões PHP em nossas traduções.

Obtendo o conteúdo traduzido do banco de dados

Nós apenas adicionamos uma coluna para o conteúdo em nosso banco de dados chamado language, então usamos um método acessador para a LANGconstante que definimos anteriormente, para que nossas chamadas SQL (usando ZF1, infelizmente) sejam assim:

$query = select()->from($this->_name)
                 ->where('language = ?', User::getLang())
                 ->where('id       = ?', $articleId)
                 ->limit(1);

Nossos artigos têm uma chave primária composta ide, languageportanto, o artigo 54pode existir em todos os idiomas. Nossos LANGpadrões são en_USse não especificado.

Tradução de URL Slug

Eu combinaria duas coisas aqui, uma é uma função no seu bootstrap que aceita um $_GETparâmetro para o idioma e substitui a variável de cookie, e outra é o roteamento que aceita várias lesmas. Então você pode fazer algo assim no seu roteamento:

"/wilkommen" => "/welcome/lang/de"
... etc ...

Eles podem ser armazenados em um arquivo simples que pode ser facilmente gravado no seu painel de administração. JSON ou XML pode fornecer uma boa estrutura para suportá-los.

Notas sobre algumas outras opções

Tradução On-The-Fly baseada em PHP

Não vejo que eles oferecem alguma vantagem sobre as traduções pré-processadas.

Traduções baseadas em front-end

Há muito tempo acho isso interessante, mas há algumas ressalvas. Por exemplo, você deve disponibilizar ao usuário toda a lista de frases em seu site que você planeja traduzir; isso pode ser problemático se houver áreas do site que você está mantendo oculto ou que não lhes permitiram acesso.

Você também deve assumir que todos os seus usuários estão dispostos e podem usar o Javascript em seu site, mas, pelas minhas estatísticas, cerca de 2,5% de nossos usuários estão executando sem ele (ou usando o Noscript para impedir que nossos sites o usem) .

Traduções Orientadas a Banco de Dados

As velocidades de conectividade do banco de dados do PHP não são nada sobre o que escrever, e isso aumenta a já alta sobrecarga de chamar uma função em cada frase para traduzir. Os problemas de desempenho e escalabilidade parecem impressionantes com essa abordagem.


Vejo que confundi você com "Traduções de front-end", o que eu quis dizer foi uma maneira de analisar as seqüências traduzidas na tela. Definitivamente, não estou procurando uma maneira de traduzi-lo no lado do cliente! O que eu quis dizer foi a maneira mais fácil de línguas interruptor no front-end, mas isso é, obviamente, usando um cookie ou uma configuração de usuário :)
Joshua - Pendo

Ah, e pelo Database-Driven eu estava mais focado no método de gerenciar todas as traduções, então minha solução ideal seria um back-end que grava traduções em um banco de dados seguido por uma função que gera o componente de pré-processamento que gera o PHP Arquivo. Why?: Simples .. Eu quero não quero ser incomoda por pequenas mudanças no texto, os usuários devem ser capazes de fazê-lo themselfs sem usar um editor de código e / ou ftp programm :)
Joshua - Pendo

@PENDO Eu sei que você não quis dizer traduções front-end, esse foi um comentário pouco velado para o usuário que sugeriu estruturas de tradução front-end usando JS. ;)
Glitch Desire

@PENDO Eu concordo, eu usaria o back-end como você sugeriu, mas em vez de um banco de dados eu usaria um arquivo simples por razões de desempenho. É claro, a sugestão do núcleo aqui é pré-renderizar os modelos em caso de mudança para que você possa substituir os .INIarquivos com uma tabela de banco de 3 colunas com placeholder, replacement, language. Tecla composta ativada placeholdere language. Depois, tenha mais 2-col com tempfile(caminho para o modelo) e modified(DATETIME).
Glitch Desejo

1
@PENDO Thanks. Coloquei 250 de volta e pretendo conceder a teresko em 24 horas quando o site permitir, pois você selecionou as duas respostas como corretas e acho que uma divisão melhor representaria suas intenções.
Glitch Desejo

15

Sugiro que você não invente uma roda e use a lista de abreviações de linguagens gettext e ISO. Você viu como o i18n / l10n foi implementado em CMSes ou estruturas populares?

Usando gettext, você terá uma ferramenta poderosa na qual muitos casos já estão implementados, como formas plurais de números. Em inglês, você tem apenas 2 opções: singular e plural. Mas em russo, por exemplo, existem três formas e não é tão simples quanto no inglês.

Muitos tradutores também já têm experiência para trabalhar com o gettext.

Dê uma olhada no CakePHP ou Drupal . Ambos multilíngues habilitados. CakePHP como exemplo de localização de interface e Drupal como exemplo de tradução de conteúdo.

Para l10n, usar banco de dados não é o caso. Serão toneladas de consultas. A abordagem padrão é obter todos os dados da l10n na memória no estágio inicial (ou durante a primeira chamada para a função i10n, se você preferir carregamento lento). Pode ler do arquivo .po ou do DB todos os dados de uma só vez. E do que apenas ler as strings solicitadas da matriz.

Se você precisar implementar uma ferramenta online para traduzir a interface, poderá ter todos esses dados no banco de dados, mas ainda assim salvar todos os dados no arquivo para trabalhar com ele. Para reduzir a quantidade de dados na memória, você pode dividir todas as suas mensagens / strings traduzidas em grupos e carregar apenas os grupos necessários, se for possível.

Então você está totalmente certo no seu # 3. Com uma exceção: geralmente é um arquivo grande e não um arquivo por controlador. Porque é melhor para o desempenho abrir um arquivo. Você provavelmente sabe que alguns aplicativos da Web carregados compilam todo o código PHP em um arquivo para evitar operações de arquivo ao incluir / exigir a chamada.

Sobre URLs. O Google sugere indiretamente o uso da tradução:

para indicar claramente o conteúdo em francês: http://example.ca/fr/vélo-de-montagne.html

Também acho que você precisa redirecionar o usuário para o prefixo do idioma padrão, por exemplo, http://examlpe.com/about-us será redirecionado para http://examlpe.com/en/about-us Mas se o seu site usar apenas um idioma, você não precisa de prefixos.

Confira: http://www.audiomicro.com/trailer-hit-impact-psychodrama-sound-effects-836925 http://nl.audiomicro.com/aanhangwagen-hit-effect-psychodrama-geluidseffecten-836925 http: / /de.audiomicro.com/anhanger-hit-auswirkungen-psychodrama-sound-effekte-836925

Traduzir conteúdo é uma tarefa mais difícil. Eu acho que haverá algumas diferenças com diferentes tipos de conteúdo, por exemplo, artigos, itens de menu etc. Mas na quarta posição você está no caminho certo. Dê uma olhada no Drupal para ter mais idéias. Ele possui um esquema de banco de dados suficientemente claro e uma interface suficientemente boa para a tradução. Como você criando um artigo e selecionando o idioma para ele. E depois você pode traduzi-lo para outros idiomas.

Interface de tradução do Drupal

Eu acho que não é problema com lesmas de URL. Você pode apenas criar uma tabela separada para lesmas e será a decisão certa. Também usando índices corretos, não é problema consultar a tabela, mesmo com grande quantidade de dados. E não foi a pesquisa de texto completo, mas a correspondência de string se usará o tipo de dados varchar para slug e você também poderá ter um índice nesse campo.

PS Desculpe, meu inglês está longe de ser perfeito.


Obrigado pelo tempo necessário para responder à minha pergunta. Seu inglês é bom o suficiente para eu entender! Eu vou marcar você com +1 por seus esforços!
Joshua # Pendo # 11/13

Yaroslav, mais uma vez, obrigado pela sua resposta. No entanto, eu fui com duas outras respostas que, onde um pouco mais completas, explicam os métodos usados por trás do código, em vez de apontar que ele já está lá.
Joshua - Pendo

2
Sem problemas. Na verdade, isso responde mais completo e interessante para ler para mim também. Mas espero que você tenha recebido algo útil da minha resposta também.
Yaroslav #

12

Depende da quantidade de conteúdo que seu site possui. No começo, usei um banco de dados como todas as outras pessoas aqui, mas pode levar muito tempo para criar scripts para todo o funcionamento de um banco de dados. Não digo que esse seja um método ideal e, especialmente, se você tiver muito texto, mas se quiser fazê-lo rapidamente sem usar um banco de dados, esse método poderá funcionar, no entanto, você não poderá permitir que os usuários insiram dados que será usado como arquivos de tradução. Mas se você adicionar as traduções, ele funcionará:

Digamos que você tenha este texto:

Welcome!

Você pode inserir isso em um banco de dados com traduções, mas também pode fazer isso:

$welcome = array(
"English"=>"Welcome!",
"German"=>"Willkommen!",
"French"=>"Bienvenue!",
"Turkish"=>"Hoşgeldiniz!",
"Russian"=>"Добро пожаловать!",
"Dutch"=>"Welkom!",
"Swedish"=>"Välkommen!",
"Basque"=>"Ongietorri!",
"Spanish"=>"Bienvenito!"
"Welsh"=>"Croeso!");

Agora, se o seu site usa um cookie, você tem isso, por exemplo:

$_COOKIE['language'];

Para facilitar, vamos transformá-lo em um código que pode ser facilmente usado:

$language=$_COOKIE['language'];

Se o seu idioma de cookies for galês e você tiver este código:

echo $welcome[$language];

O resultado disso será:

Croeso!

Se você precisar adicionar muitas traduções ao seu site e um banco de dados estiver consumindo muito, o uso de uma matriz pode ser a solução ideal.


1
Este não é o lugar nem perto da resposta que eu estava pedindo. Além disso, em vez de ter todos os idiomas disponíveis em cada página, é melhor criar arquivos como o lang.en.phpque será incluído e usar o $lang['welcome']que é declarado em cada arquivo.
Joshua - Pendo

7

Vou sugerir que você não dependa realmente do banco de dados para tradução. Isso pode ser uma tarefa muito complicada e um problema extremo no caso de codificação de dados.

Eu tive um problema semelhante há um tempo atrás e escrevi a seguinte aula para resolver meu problema

Objeto: Localidade \ Localidade

<?php

  namespace Locale;

  class Locale{

// Following array stolen from Zend Framework
public $country_to_locale = array(
    'AD' => 'ca_AD',
    'AE' => 'ar_AE',
    'AF' => 'fa_AF',
    'AG' => 'en_AG',
    'AI' => 'en_AI',
    'AL' => 'sq_AL',
    'AM' => 'hy_AM',
    'AN' => 'pap_AN',
    'AO' => 'pt_AO',
    'AQ' => 'und_AQ',
    'AR' => 'es_AR',
    'AS' => 'sm_AS',
    'AT' => 'de_AT',
    'AU' => 'en_AU',
    'AW' => 'nl_AW',
    'AX' => 'sv_AX',
    'AZ' => 'az_Latn_AZ',
    'BA' => 'bs_BA',
    'BB' => 'en_BB',
    'BD' => 'bn_BD',
    'BE' => 'nl_BE',
    'BF' => 'mos_BF',
    'BG' => 'bg_BG',
    'BH' => 'ar_BH',
    'BI' => 'rn_BI',
    'BJ' => 'fr_BJ',
    'BL' => 'fr_BL',
    'BM' => 'en_BM',
    'BN' => 'ms_BN',
    'BO' => 'es_BO',
    'BR' => 'pt_BR',
    'BS' => 'en_BS',
    'BT' => 'dz_BT',
    'BV' => 'und_BV',
    'BW' => 'en_BW',
    'BY' => 'be_BY',
    'BZ' => 'en_BZ',
    'CA' => 'en_CA',
    'CC' => 'ms_CC',
    'CD' => 'sw_CD',
    'CF' => 'fr_CF',
    'CG' => 'fr_CG',
    'CH' => 'de_CH',
    'CI' => 'fr_CI',
    'CK' => 'en_CK',
    'CL' => 'es_CL',
    'CM' => 'fr_CM',
    'CN' => 'zh_Hans_CN',
    'CO' => 'es_CO',
    'CR' => 'es_CR',
    'CU' => 'es_CU',
    'CV' => 'kea_CV',
    'CX' => 'en_CX',
    'CY' => 'el_CY',
    'CZ' => 'cs_CZ',
    'DE' => 'de_DE',
    'DJ' => 'aa_DJ',
    'DK' => 'da_DK',
    'DM' => 'en_DM',
    'DO' => 'es_DO',
    'DZ' => 'ar_DZ',
    'EC' => 'es_EC',
    'EE' => 'et_EE',
    'EG' => 'ar_EG',
    'EH' => 'ar_EH',
    'ER' => 'ti_ER',
    'ES' => 'es_ES',
    'ET' => 'en_ET',
    'FI' => 'fi_FI',
    'FJ' => 'hi_FJ',
    'FK' => 'en_FK',
    'FM' => 'chk_FM',
    'FO' => 'fo_FO',
    'FR' => 'fr_FR',
    'GA' => 'fr_GA',
    'GB' => 'en_GB',
    'GD' => 'en_GD',
    'GE' => 'ka_GE',
    'GF' => 'fr_GF',
    'GG' => 'en_GG',
    'GH' => 'ak_GH',
    'GI' => 'en_GI',
    'GL' => 'iu_GL',
    'GM' => 'en_GM',
    'GN' => 'fr_GN',
    'GP' => 'fr_GP',
    'GQ' => 'fan_GQ',
    'GR' => 'el_GR',
    'GS' => 'und_GS',
    'GT' => 'es_GT',
    'GU' => 'en_GU',
    'GW' => 'pt_GW',
    'GY' => 'en_GY',
    'HK' => 'zh_Hant_HK',
    'HM' => 'und_HM',
    'HN' => 'es_HN',
    'HR' => 'hr_HR',
    'HT' => 'ht_HT',
    'HU' => 'hu_HU',
    'ID' => 'id_ID',
    'IE' => 'en_IE',
    'IL' => 'he_IL',
    'IM' => 'en_IM',
    'IN' => 'hi_IN',
    'IO' => 'und_IO',
    'IQ' => 'ar_IQ',
    'IR' => 'fa_IR',
    'IS' => 'is_IS',
    'IT' => 'it_IT',
    'JE' => 'en_JE',
    'JM' => 'en_JM',
    'JO' => 'ar_JO',
    'JP' => 'ja_JP',
    'KE' => 'en_KE',
    'KG' => 'ky_Cyrl_KG',
    'KH' => 'km_KH',
    'KI' => 'en_KI',
    'KM' => 'ar_KM',
    'KN' => 'en_KN',
    'KP' => 'ko_KP',
    'KR' => 'ko_KR',
    'KW' => 'ar_KW',
    'KY' => 'en_KY',
    'KZ' => 'ru_KZ',
    'LA' => 'lo_LA',
    'LB' => 'ar_LB',
    'LC' => 'en_LC',
    'LI' => 'de_LI',
    'LK' => 'si_LK',
    'LR' => 'en_LR',
    'LS' => 'st_LS',
    'LT' => 'lt_LT',
    'LU' => 'fr_LU',
    'LV' => 'lv_LV',
    'LY' => 'ar_LY',
    'MA' => 'ar_MA',
    'MC' => 'fr_MC',
    'MD' => 'ro_MD',
    'ME' => 'sr_Latn_ME',
    'MF' => 'fr_MF',
    'MG' => 'mg_MG',
    'MH' => 'mh_MH',
    'MK' => 'mk_MK',
    'ML' => 'bm_ML',
    'MM' => 'my_MM',
    'MN' => 'mn_Cyrl_MN',
    'MO' => 'zh_Hant_MO',
    'MP' => 'en_MP',
    'MQ' => 'fr_MQ',
    'MR' => 'ar_MR',
    'MS' => 'en_MS',
    'MT' => 'mt_MT',
    'MU' => 'mfe_MU',
    'MV' => 'dv_MV',
    'MW' => 'ny_MW',
    'MX' => 'es_MX',
    'MY' => 'ms_MY',
    'MZ' => 'pt_MZ',
    'NA' => 'kj_NA',
    'NC' => 'fr_NC',
    'NE' => 'ha_Latn_NE',
    'NF' => 'en_NF',
    'NG' => 'en_NG',
    'NI' => 'es_NI',
    'NL' => 'nl_NL',
    'NO' => 'nb_NO',
    'NP' => 'ne_NP',
    'NR' => 'en_NR',
    'NU' => 'niu_NU',
    'NZ' => 'en_NZ',
    'OM' => 'ar_OM',
    'PA' => 'es_PA',
    'PE' => 'es_PE',
    'PF' => 'fr_PF',
    'PG' => 'tpi_PG',
    'PH' => 'fil_PH',
    'PK' => 'ur_PK',
    'PL' => 'pl_PL',
    'PM' => 'fr_PM',
    'PN' => 'en_PN',
    'PR' => 'es_PR',
    'PS' => 'ar_PS',
    'PT' => 'pt_PT',
    'PW' => 'pau_PW',
    'PY' => 'gn_PY',
    'QA' => 'ar_QA',
    'RE' => 'fr_RE',
    'RO' => 'ro_RO',
    'RS' => 'sr_Cyrl_RS',
    'RU' => 'ru_RU',
    'RW' => 'rw_RW',
    'SA' => 'ar_SA',
    'SB' => 'en_SB',
    'SC' => 'crs_SC',
    'SD' => 'ar_SD',
    'SE' => 'sv_SE',
    'SG' => 'en_SG',
    'SH' => 'en_SH',
    'SI' => 'sl_SI',
    'SJ' => 'nb_SJ',
    'SK' => 'sk_SK',
    'SL' => 'kri_SL',
    'SM' => 'it_SM',
    'SN' => 'fr_SN',
    'SO' => 'sw_SO',
    'SR' => 'srn_SR',
    'ST' => 'pt_ST',
    'SV' => 'es_SV',
    'SY' => 'ar_SY',
    'SZ' => 'en_SZ',
    'TC' => 'en_TC',
    'TD' => 'fr_TD',
    'TF' => 'und_TF',
    'TG' => 'fr_TG',
    'TH' => 'th_TH',
    'TJ' => 'tg_Cyrl_TJ',
    'TK' => 'tkl_TK',
    'TL' => 'pt_TL',
    'TM' => 'tk_TM',
    'TN' => 'ar_TN',
    'TO' => 'to_TO',
    'TR' => 'tr_TR',
    'TT' => 'en_TT',
    'TV' => 'tvl_TV',
    'TW' => 'zh_Hant_TW',
    'TZ' => 'sw_TZ',
    'UA' => 'uk_UA',
    'UG' => 'sw_UG',
    'UM' => 'en_UM',
    'US' => 'en_US',
    'UY' => 'es_UY',
    'UZ' => 'uz_Cyrl_UZ',
    'VA' => 'it_VA',
    'VC' => 'en_VC',
    'VE' => 'es_VE',
    'VG' => 'en_VG',
    'VI' => 'en_VI',
    'VN' => 'vn_VN',
    'VU' => 'bi_VU',
    'WF' => 'wls_WF',
    'WS' => 'sm_WS',
    'YE' => 'ar_YE',
    'YT' => 'swb_YT',
    'ZA' => 'en_ZA',
    'ZM' => 'en_ZM',
    'ZW' => 'sn_ZW'
);

/**
 * Store the transaltion for specific languages
 *
 * @var array
 */
protected $translation = array();

/**
 * Current locale
 *
 * @var string
 */
protected $locale;

/**
 * Default locale
 *
 * @var string
 */
protected $default_locale;

/**
 *
 * @var string
 */
protected $locale_dir;

/**
 * Construct.
 *
 *
 * @param string $locale_dir            
 */
public function __construct($locale_dir)
{
    $this->locale_dir = $locale_dir;
}

/**
 * Set the user define localte
 *
 * @param string $locale            
 */
public function setLocale($locale = null)
{
    $this->locale = $locale;

    return $this;
}

/**
 * Get the user define locale
 *
 * @return string
 */
public function getLocale()
{
    return $this->locale;
}

/**
 * Get the Default locale
 *
 * @return string
 */
public function getDefaultLocale()
{
    return $this->default_locale;
}

/**
 * Set the default locale
 *
 * @param string $locale            
 */
public function setDefaultLocale($locale)
{
    $this->default_locale = $locale;

    return $this;
}

/**
 * Determine if transltion exist or translation key exist
 *
 * @param string $locale            
 * @param string $key            
 * @return boolean
 */
public function hasTranslation($locale, $key = null)
{
    if (null == $key && isset($this->translation[$locale])) {
        return true;
    } elseif (isset($this->translation[$locale][$key])) {
        return true;
    }

    return false;
}

/**
 * Get the transltion for required locale or transtion for key
 *
 * @param string $locale            
 * @param string $key            
 * @return array
 */
public function getTranslation($locale, $key = null)
{
    if (null == $key && $this->hasTranslation($locale)) {
        return $this->translation[$locale];
    } elseif ($this->hasTranslation($locale, $key)) {
        return $this->translation[$locale][$key];
    }

    return array();
}

/**
 * Set the transtion for required locale
 *
 * @param string $locale
 *            Language code
 * @param string $trans
 *            translations array
 */
public function setTranslation($locale, $trans = array())
{
    $this->translation[$locale] = $trans;
}

/**
 * Remove transltions for required locale
 *
 * @param string $locale            
 */
public function removeTranslation($locale = null)
{
    if (null === $locale) {
        unset($this->translation);
    } else {
        unset($this->translation[$locale]);
    }
}

/**
 * Initialize locale
 *
 * @param string $locale            
 */
public function init($locale = null, $default_locale = null)
{
    // check if previously set locale exist or not
    $this->init_locale();
    if ($this->locale != null) {
        return;
    }

    if ($locale == null || (! preg_match('#^[a-z]+_[a-zA-Z_]+$#', $locale) && ! preg_match('#^[a-z]+_[a-zA-Z]+_[a-zA-Z_]+$#', $locale))) {
        $this->detectLocale();
    } else {
        $this->locale = $locale;
    }

    $this->init_locale();
}

/**
 * Attempt to autodetect locale
 *
 * @return void
 */
private function detectLocale()
{
    $locale = false;

    // GeoIP
    if (function_exists('geoip_country_code_by_name') && isset($_SERVER['REMOTE_ADDR'])) {

        $country = geoip_country_code_by_name($_SERVER['REMOTE_ADDR']);

        if ($country) {

            $locale = isset($this->country_to_locale[$country]) ? $this->country_to_locale[$country] : false;
        }
    }

    // Try detecting locale from browser headers
    if (! $locale) {

        if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {

            $languages = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);

            foreach ($languages as $lang) {

                $lang = str_replace('-', '_', trim($lang));

                if (strpos($lang, '_') === false) {

                    if (isset($this->country_to_locale[strtoupper($lang)])) {

                        $locale = $this->country_to_locale[strtoupper($lang)];
                    }
                } else {

                    $lang = explode('_', $lang);

                    if (count($lang) == 3) {
                        // language_Encoding_COUNTRY
                        $this->locale = strtolower($lang[0]) . ucfirst($lang[1]) . strtoupper($lang[2]);
                    } else {
                        // language_COUNTRY
                        $this->locale = strtolower($lang[0]) . strtoupper($lang[1]);
                    }

                    return;
                }
            }
        }
    }

    // Resort to default locale specified in config file
    if (! $locale) {
        $this->locale = $this->default_locale;
    }
}

/**
 * Check if config for selected locale exists
 *
 * @return void
 */
private function init_locale()
{
    if (! file_exists(sprintf('%s/%s.php', $this->locale_dir, $this->locale))) {
        $this->locale = $this->default_locale;
    }
}

/**
 * Load a Transtion into array
 *
 * @return void
 */
private function loadTranslation($locale = null, $force = false)
{
    if ($locale == null)
        $locale = $this->locale;

    if (! $this->hasTranslation($locale)) {
        $this->setTranslation($locale, include (sprintf('%s/%s.php', $this->locale_dir, $locale)));
    }
}

/**
 * Translate a key
 *
 * @param
 *            string Key to be translated
 * @param
 *            string optional arguments
 * @return string
 */
public function translate($key)
{
    $this->init();
    $this->loadTranslation($this->locale);

    if (! $this->hasTranslation($this->locale, $key)) {

        if ($this->locale !== $this->default_locale) {

            $this->loadTranslation($this->default_locale);

            if ($this->hasTranslation($this->default_locale, $key)) {

                $translation = $this->getTranslation($this->default_locale, $key);
            } else {
                // return key as it is or log error here
                return $key;
            }
        } else {
            return $key;
        }
    } else {
        $translation = $this->getTranslation($this->locale, $key);
    }
    // Replace arguments
    if (false !== strpos($translation, '{a:')) {
        $replace = array();
        $args = func_get_args();
        for ($i = 1, $max = count($args); $i < $max; $i ++) {
            $replace['{a:' . $i . '}'] = $args[$i];
        }
        // interpolate replacement values into the messsage then return
        return strtr($translation, $replace);
    }

    return $translation;
  }
}

Uso

 <?php
    ## /locale/en.php

    return array(
       'name' => 'Hello {a:1}'
       'name_full' => 'Hello {a:1} {a:2}'
   );

$locale = new Locale(__DIR__ . '/locale');
$locale->setLocale('en');// load en.php from locale dir
//want to work with auto detection comment $locale->setLocale('en');

echo $locale->translate('name', 'Foo');
echo $locale->translate('name', 'Foo', 'Bar');

Como funciona

{a:1}é substituído pelo 1º argumento passado ao método Locale::translate('key_name','arg1') {a:2}é substituído pelo 2º argumento passado ao métodoLocale::translate('key_name','arg1','arg2')

Como funciona a detecção

  • Por padrão, se geoipestiver instalado, ele retornará o código do país geoip_country_code_by_namee, se o geoip não estiver instalado, o fallback para o HTTP_ACCEPT_LANGUAGEcabeçalho

De que maneira um banco de dados seria confuso? Por causa dos possíveis caracteres em diferentes idiomas? Até agora, eu tenho principalmente sites em inglês, francês, holandês e alemão, então isso não é problema no momento. Obrigado pela resposta, mas como é apenas uma parte da resposta, ela não ganhará a recompensa.
Joshua - Pendo

Bem, acho que sua pergunta é útil apenas para você. Alguns caras considerariam o uso de idiomas como hindi, tailandês, chinês e árabe (esse idioma precisará de mais de 1 byte para representar os caracteres) em relação aos idiomas necessários. se você estiver usando o db, o utf8_general_ciagrupamento é a maneira apropriada de fazê-lo.
Shushant

Eu concordo, consegui me localizar um pouco. Obrigado por apontar, também caracteres multi-bit são importantes o suficiente para ser mencionado nesta questão :)
Joshua - Pendo

5

Apenas uma sub-resposta: use absolutamente os URLs traduzidos com um identificador de idioma à sua frente: http://www.domain.com/nl/over-ons As
soluções Hybride tendem a ficar complicadas, então eu continuaria com ele. Por quê? Porque o URL é essencial para o SEO.

Sobre a tradução do banco de dados: o número de idiomas é mais ou menos fixo? Ou melhor, imprevisível e dinâmico? Se for corrigido, eu adicionaria apenas novas colunas, caso contrário, usaria várias tabelas.

Mas geralmente, por que não usar Drupal? Eu sei que todo mundo quer criar seu próprio CMS porque é mais rápido, mais enxuto, etc. etc. Mas isso é realmente uma péssima ideia!


1
Obrigado pela sua resposta. O motivo pelo qual não quero usar o Drupal / Joomla é simples: quero ter certeza de que conheço todos os meandros do meu sistema, falhas pausáveis, como o código é criado (e importante: não é criado por 300 programadores juntos) . Eu tenho motivos mais que suficientes para não escolher o código aberto. Além disso, quero que minha empresa seja um fator importante para meus clientes, é uma coisa ruim que eles possam ir a qualquer outro desenvolvedor e me deixar para trás sem nada.
Joshua - Pendo

7
Eu acho que todas essas razões são disputadas em toneladas de artigos. Esperamos que seus clientes não o escolham exatamente porque você tem um CMS proprietário que ninguém mais pode manter. Mas, enfim, essa é uma discussão totalmente diferente.
Remy

1
Entendo o seu ponto de vista, ainda prefiro um sistema pelo qual conheço todos os detalhes e não sinto nada por confiar em alguém que funcione quando uso um plug-in.
Joshua - Pendo 10/10

1
Além disso, tenho a tendência de documentar meu trabalho suficientemente bem, já que sou um "exército de um homem" que trabalha para mim não deve ter dificuldade em conhecer o sistema.
Joshua - Pendo

A má idéia é escolher o Drupal e até o Google diz que eles não se importam se o URL é traduzido ou não. No entanto, ele deve conter um identificador de localidade.
undefinedman

5

Não vou tentar refinar as respostas já dadas. Em vez disso, vou falar sobre como minha própria estrutura PHP OOP lida com traduções.

Internamente, minha estrutura usa códigos como en, fr, es, cn e assim por diante. Um array contém os idiomas suportados pelo site: array ('en', 'fr', 'es', 'cn') O código do idioma é passado através de $ _GET (lang = fr) e, se não for passado ou não for válido, é definido como o primeiro idioma da matriz. Portanto, a qualquer momento durante a execução do programa e desde o início, o idioma atual é conhecido.

É útil entender o tipo de conteúdo que precisa ser traduzido em um aplicativo típico:

1) mensagens de erro de classes (ou código processual) 2) mensagens sem erro de classes (ou código processual) 3) conteúdo da página (geralmente armazenado em um banco de dados) 4) seqüências de caracteres em todo o site (como o nome do site) 5) script- seqüências específicas

O primeiro tipo é simples de entender. Basicamente, estamos falando de mensagens como "não foi possível conectar ao banco de dados ...". Essas mensagens precisam ser carregadas apenas quando ocorrer um erro. Minha classe de gerente recebe uma chamada das outras classes e, usando as informações passadas como parâmetros, simplesmente vai para a pasta da classe e recupera o arquivo de erro.

O segundo tipo de mensagem de erro é mais parecido com as mensagens recebidas quando a validação de um formulário deu errado. ("Você não pode deixar ... em branco" ou "escolha uma senha com mais de 5 caracteres"). As seqüências de caracteres precisam ser carregadas antes da execução da classe.

Para o conteúdo da página real, eu uso uma tabela por idioma, cada tabela prefixada pelo código do idioma. Então en_content é a tabela com conteúdo em inglês, es_content é para a espanha, cn_content para a China e fr_content é o material francês.

O quarto tipo de string é relevante em todo o site. Isso é carregado por meio de um arquivo de configuração nomeado usando o código do idioma, ou seja, en_lang.php, es_lang.php e assim por diante. No arquivo de idioma global, você precisará carregar os idiomas traduzidos, como matriz ('inglês', 'chinês', 'espanhol', 'francês') no arquivo global e matriz em inglês ('Anglais', 'Chinois', ' Espagnol ',' Francais ') no arquivo francês. Portanto, quando você preenche um menu suspenso para seleção de idioma, ele está no idioma correto;)

Finalmente, você tem as seqüências específicas do script. Portanto, se você escrever um aplicativo de culinária, pode ser "O forno não estava quente o suficiente".

No meu ciclo de aplicação, o arquivo de idioma global é carregado primeiro. Lá você encontrará não apenas strings globais (como o "site de Jack"), mas também configurações para algumas das classes. Basicamente, qualquer coisa que depende da língua ou da cultura. Algumas das seqüências de caracteres incluem máscaras para datas (MMDDYYYY ou DDMMYYYY) ou códigos de idioma ISO. No arquivo de idioma principal, eu incluo strings para classes individuais, pois são poucas.

O segundo e último arquivo de idioma que é lido do disco é o arquivo de idioma do script. lang_en_home_welcome.php é o arquivo de idioma para o script home / welcome. Um script é definido por um modo (home) e uma ação (bem-vindo). Cada script possui sua própria pasta com arquivos de configuração e lang.

O script extrai o conteúdo do banco de dados nomeando a tabela de conteúdo, conforme explicado acima.

Se algo der errado, o gerente sabe onde obter o arquivo de erro dependente do idioma. Esse arquivo é carregado apenas em caso de erro.

Portanto, a conclusão é óbvia. Pense nos problemas de tradução antes de começar a desenvolver um aplicativo ou estrutura. Você também precisa de um fluxo de trabalho de desenvolvimento que incorpore traduções. Com minha estrutura, desenvolvo o site inteiro em inglês e depois traduzo todos os arquivos relevantes.

Apenas uma palavra final rápida sobre como as seqüências de tradução são implementadas. Minha estrutura possui um único global, o $ manager, que executa serviços disponíveis para qualquer outro serviço. Assim, por exemplo, o serviço de formulário obtém o serviço html e o usa para escrever o html. Um dos serviços no meu sistema é o serviço de tradutor. $ translator-> set ($ service, $ code, $ string) define uma string para o idioma atual. O arquivo de idioma é uma lista dessas declarações. O $ translator-> get ($ service, $ code) recupera uma string de tradução. O código $ pode ser numérico como 1 ou uma sequência como 'no_connection'. Não pode haver conflito entre serviços, porque cada um tem seu próprio espaço para nome na área de dados do tradutor.

Coloco isso aqui na esperança de que alguém salve a tarefa de reinventar a roda, como tive que fazer alguns anos atrás.


4

Eu tive o mesmo problema há um tempo atrás, antes de começar a usar o framework Symfony .

  1. Basta usar uma função __ () que possua os parâmetros pageId (ou objectId, objectTable descritos em # 2), idioma de destino e um parâmetro opcional do idioma de fallback (padrão). O idioma padrão pode ser definido em algumas configurações globais para ter uma maneira mais fácil de alterá-lo posteriormente.

  2. Para armazenar o conteúdo no banco de dados, usei a seguinte estrutura: (ID da página, idioma, conteúdo, variáveis).

    • O pageId seria um FK para a sua página que você deseja traduzir. se você tiver outros objetos, como notícias, galerias ou qualquer outra coisa, divida-o em 2 campos objectId, objectTable.

    • idioma - obviamente, ele armazenaria a string de idioma ISO EN_en, LT_lt, EN_us etc.

    • conteúdo - o texto que você deseja traduzir junto com os curingas para substituição de variável. Exemplo "Olá Sr. %% name %%. O saldo da sua conta é %% balance %%."

    • variáveis ​​- as variáveis ​​codificadas pelo json. O PHP fornece funções para analisá-las rapidamente. Exemplo "nome: Laurynas, saldo: 15,23".

    • você mencionou também campo lesma. você pode adicioná-lo livremente a esta tabela apenas para ter uma maneira rápida de procurá-lo.

  3. Suas chamadas ao banco de dados devem ser reduzidas ao mínimo com o cache das traduções. Ele deve ser armazenado na matriz PHP, porque é a estrutura mais rápida da linguagem PHP. Como você fará esse cache é com você. Pela minha experiência, você deve ter uma pasta para cada idioma suportado e uma matriz para cada pageId. O cache deve ser reconstruído após a atualização da tradução. SOMENTE a matriz alterada deve ser regenerada.

  4. acho que respondi isso em # 2

  5. sua ideia é perfeitamente lógica. este é bem simples e acho que não causará nenhum problema.

Os URLs devem ser traduzidos usando as lesmas armazenadas na tabela de tradução.

Palavras finais

é sempre bom pesquisar as melhores práticas, mas não reinvente a roda. basta pegar e usar os componentes de estruturas conhecidas e usá-los.

dê uma olhada no componente de tradução Symfony . Pode ser uma boa base de código para você.


Obrigado pelo comentário, marque +1 pelo tempo gasto. O Laravel (no meu caso) está usando algumas partes do Symfony, se não me engano, então você está absolutamente certo em não reinventar a roda. Comecei a esta pergunta (e recompensas) para obter alguns insights na forma como os outros fazem as traduções, estou começando a acreditar que há um monte de boas práticas lá fora :-)
Joshua - Pendo

1

Fiz perguntas repetidas vezes a mim mesmo, depois me perdi em idiomas formais ... mas, para ajudar um pouco, gostaria de compartilhar algumas descobertas:

Eu recomendo dar uma olhada no CMS avançado

Typo3para PHP (eu sei que há muitas coisas, mas essa é a que eu acho mais madura)

Plone no Python

Se você descobrir que a web em 2013 deve funcionar diferente, comece do zero. Isso significaria reunir uma equipe de pessoas altamente qualificadas / experientes para criar um novo CMS. Pode ser que você queira dar uma olhada no polímero para esse fim.

Se se trata de sites multilíngues de codificação / suporte a idiomas nativos, acho que todo programador deve ter uma idéia sobre o unicode. Se você não conhece o Unicode, certamente estragará seus dados. Não siga os milhares de códigos ISO. Eles só economizarão um pouco de memória. Mas você pode fazer literalmente tudo com o UTF-8 até armazenar caracteres chineses. Mas para isso, você precisará armazenar caracteres de 2 ou 4 bytes, o que o torna basicamente um utf-16 ou utf-32.

Se se trata de codificação de URL, novamente você não deve misturar codificações e esteja ciente de que, pelo menos para o nome de domínio, existem regras definidas por diferentes lobbies que fornecem aplicativos como um navegador. por exemplo, um domínio pode ser muito semelhante, como:

ьankofamerica.com ou bankofamerica.com samesamebutdifferent;)

Claro que você precisa do sistema de arquivos para trabalhar com todas as codificações. Outra vantagem para o unicode usando o sistema de arquivos utf-8.

Se é sobre traduções, pense na estrutura dos documentos. por exemplo, um livro ou um artigo. Você tem as docbookespecificações para entender sobre essas estruturas. Mas em HTML, trata-se apenas de blocos de conteúdo. Então, você gostaria de ter uma tradução nesse nível, também no nível da página da web ou no domínio. Portanto, se um bloco não existe, simplesmente não existe, se uma página da Web não existe, você será redirecionado para o nível de navegação superior. Se um domínio deve ser completamente diferente na estrutura de navegação, é uma estrutura completamente diferente para gerenciar. Isso já pode ser feito com o Typo3.

Se for sobre frameworks, os mais maduros que conheço, para fazer coisas gerais como MVC (chavão, eu realmente odeio! Como "performance" Se você quiser vender algo, use a palavra performance e featurerich e vende ... o que o inferno) é Zend . Provou ser uma boa coisa trazer padrões aos codificadores de php caos. Mas, o typo3 também possui um Framework além do CMS. Recentemente, foi reconstruído e agora é chamado flow3. As estruturas, obviamente, cobrem a abstração do banco de dados, modelos e conceitos para armazenamento em cache, mas têm pontos fortes individuais.

Se for sobre cache ... isso pode ser muito complicado / em várias camadas. No PHP, você pensa em accellerator, opcode, mas também html, httpd, mysql, xml, css, js ... qualquer tipo de cache. É claro que algumas partes devem ser armazenadas em cache e as partes dinâmicas, como as respostas do blog, não. Alguns devem ser solicitados pelo AJAX com URLs gerados. JSON, hashbangs etc.

Em seguida, você deseja que qualquer pequeno componente em seu site seja acessado ou gerenciado apenas por determinados usuários , de maneira conceitual que desempenha um grande papel.

Além disso, você gostaria de fazer estatísticas , talvez tenha distribuído um sistema / facebook de facebooks etc. qualquer software a ser construído sobre o seu sobre os principais cms ... então você precisa de diferentes tipos de bancos de dados em memória, bigdata, xml, qualquer que seja .

bem, acho que isso é o suficiente por enquanto. Se você nunca ouviu falar de typo3 / plone ou de estruturas mencionadas, você tem o suficiente para estudar. Nesse caminho, você encontrará muitas soluções para perguntas que ainda não foram feitas.

Se você pensa assim, vamos criar um novo CMS porque seu 2013 e php estão prestes a morrer de qualquer maneira, então você é bem-vindo para se juntar a qualquer outro grupo de desenvolvedores, espero que não se perca.

Boa sorte!

E aliás. e as pessoas não terão mais sites no futuro? e todos nós estaremos no google +? Espero que os desenvolvedores se tornem um pouco mais criativos e façam algo útil (a não ser assimilado pela borgle)

//// Edit /// Apenas um pouco de reflexão para o seu aplicativo existente:

Se você possui um php mysql CMS e deseja incorporar suporte a vários idiomas. você pode usar sua tabela com uma coluna adicional para qualquer idioma ou inserir a tradução com um ID de objeto e um ID de idioma na mesma tabela ou criar uma tabela idêntica para qualquer idioma e inserir objetos lá, e fazer uma união de seleção, se desejar para que todos sejam exibidos. Para o banco de dados, use utf8 general ci e, é claro, no front / back-end, use utf8 text / encoding. Eu usei segmentos de caminho de URL para URLs da maneira que você já explicou como

domain.org/en/about, você pode mapear o ID de idioma para sua tabela de conteúdo. de qualquer maneira, você precisa ter um mapa de parâmetros para seus URLs, para definir um parâmetro a ser mapeado a partir de um segmento de caminho em seu URL, que seria, por exemplo,

domain.org/en/about/employees/IT/administrators/

configuração de pesquisa

pageid | url

1 | /about/employees/../ ..

1 | /../about/employees../../

mapear parâmetros para o segmento de caminho da URL ""

$parameterlist[lang] = array(0=>"nl",1=>"en"); // default nl if 0
$parameterlist[branch] = array(1=>"IT",2=>"DESIGN"); // default nl if 0
$parameterlist[employertype] = array(1=>"admin",1=>"engineer"); //could be a sql result 

$websiteconfig[]=$userwhatever;
$websiteconfig[]=$parameterlist;
$someparameterlist[] = array("branch"=>$someid);
$someparameterlist[] = array("employertype"=>$someid);
function getURL($someparameterlist){ 
// todo foreach someparameter lookup pathsegment 
return path;
}

por dizer, isso já foi coberto no cargo superior.

E para não esquecer, você precisa "reescrever" o URL do seu arquivo php gerador que, na maioria dos casos, seria index.php


Obrigado pelo comentário, certamente há coisas em que devo pensar. Eu já uso a codificação utf8 há alguns anos, já lutei com caracteres uma vez ;-) Por outro lado, o tipo de CMS / Framework era para não ser um fator em sua resposta, pois eu estava procurando por um método independente de plataforma, como se estivéssemos codificando do zero.
Joshua - Pendo

se você realmente deseja codificar do zero, recomendo dar uma olhada em Dartlang e polímero. Como o dartlang está trabalhando no navegador e possui suporte de 32 e 64 bits, pode ser usado para a maioria dos propósitos no servidor e possui um compilador dart2js que realmente vale a pena ser estudado. Se as pessoas falam sobre independência de plataforma, pensam em java ... sabemos o que isso significa. Processo de compilação ... Eu acho que usaria JSON para intercâmbio. site gerado com clientes e hashbangs e servidores .. faça o que quiser para garantir a colaboração.
Dr. Dama

A lógica de geração de dados e geração de dados é a tarefa principal. Ninguém vai fazer isso aqui por você ... mas a idéia em si é o que conta. Como não me preocupo com lobbies, mas com o objetivo de fazer as coisas, espero que você possa criar modelos e compartilhar algumas coisas. Estou trabalhando em tarefas semelhantes agora. Mas ainda estou planejando. Estou considerando o Typo3 como um back-end e crie uma nova estrutura de cliente. O padrão multilíngue é resolvido no back-end e compartilha informações de maneira dedicada aos mecanismos de pesquisa / serviços da web. De qualquer forma todos os seus sensível ao contexto e uma tarefa de construção contínua
Dr. Dama

-1

Trabalho de banco de dados:

Criar 'idiomas' da tabela de idiomas:

Campos:

language_id(primary and auto increamented)

language_name

created_at

created_by

updated_at

updated_by

Crie uma tabela no banco de dados 'conteúdo':

Campos:

content_id(primary and auto incremented)

main_content

header_content

footer_content

leftsidebar_content

rightsidebar_content

language_id(foreign key: referenced to languages table)

created_at

created_by

updated_at

updated_by

Trabalho Final:

Quando o usuário seleciona qualquer idioma no menu suspenso ou em qualquer área, salve o ID do idioma selecionado na sessão, como,

$_SESSION['language']=1;

Agora, busque dados do 'conteúdo' da tabela do banco de dados com base no ID do idioma armazenado na sessão.

Detalhes podem ser encontrados aqui http://skillrow.com/multilingual-website-in-php-2/


1
Essa é uma maneira simples de integração de idiomas, então você tentou ler as postagens completas e obter respostas?
Joshua # Pendo

-2

Como uma pessoa que mora em Quebec, onde quase todo o site é francês e inglês ... eu tentei muitos, se não o mais, plug-in multilíngüe para o WP ... a única solução útil que funciona em todo o meu site é mQtranslate ... eu vivo e morro com isso!

https://wordpress.org/plugins/mqtranslate/


1
Sim, bem, WP não foi nenhum fator da questão. Isto poderia ter sido um comentário aswel
Joshua - Pendo

-3

E o WORDPRESS + MULTI-LANGUAGE SITE BASIS(plugin)? o site terá estrutura:

  • example.com/ eng / categoria1 / ....
  • example.com/ eng / my-page ....
  • example.com/ rus / category1 / ....
  • example.com/ rus / minha-página ....

O plugin fornece todas as frases da Interface for Translation, com lógica simples:

(ENG) my_title - "Hello user"
(SPA) my_title - "Holla usuario"

então pode ser emitido:
echo translate('my_title', LNG); // LNG is auto-detected

ps no entanto, verifique se o plug-in ainda está ativo.


3
e não é "Holla userio" em espanhol é "Hola Usuario"
bheatcoker

1
Lol Holla userio, isso foi engraçado!
Spkdrum

pela razão que eu não sabia espanhol (apenas exemplo usado), corra, corra para votar! :)
T.Todua

-5

Uma opção realmente simples que funciona com qualquer site em que você pode fazer upload de Javascript é www.multilingualizer.com

Permite colocar todo o texto de todos os idiomas em uma página e ocultar os idiomas que o usuário não precisa ver. Funciona bem.


Cuidado, SEO seria muito ruim! Além disso, você carrega todo o conteúdo enquanto precisa apenas de uma parte, o que é realmente uma prática ruim.
Hafenkranich 30/01

coisas estranhas que o site é apenas em inglês ... por que eles não usam sua solução?
Eduardo.lopes #
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.