186 votes

Les meilleures pratiques multi-langue site web

J'ai eu du mal avec cette question depuis quelques mois maintenant, mais je n'ai pas été dans une situation que j'avais besoin d'explorer toutes les options possibles avant. Maintenant, je me sens comme il est temps d'apprendre à connaître les possibilités et de créer mon propre préférence personnelle pour les utiliser dans mes projets à venir.

Permettez-moi d'abord de l'esquisse de la situation, je suis à la recherche d'

Je suis sur le point de mettre à niveau/réaménager un système de gestion de contenu qui, je l'ai utilisé pendant un bon moment maintenant. Cependant, je me sens multi-langue est une grande amélioration par rapport à ce système. Avant que je n'utilise pas toute les cadres, mais je vais utiliser Laraval4 pour le projet à venir. Laravel semble le meilleur choix d'une manière plus propre de code PHP. Sidenote: Laraval4 should be no factor in your answer. Je suis à la recherche de façons générales de la traduction qui sont de plate-forme/cadre indépendant.

Ce qui devrait être traduit

Comme le système, je suis à la recherche de besoins pour être aussi convivial que possible, la méthode de gestion de la traduction doit être à l'intérieur de la CMS. Il devrait y avoir aucun besoin pour démarrer une connexion FTP pour modifier les fichiers de traduction ou tout code html/php analysé les modèles.

Par ailleurs, je suis à la recherche de la façon la plus simple de traduire plusieurs tables de base de données peut-être sans la nécessité de procéder à d'autres tables.

Qu'ai-je venir avec moi

Comme je l'ai été à la recherche, de lecture et d'essayer des choses moi-même déjà. Il ya un couple d'options que j'ai. Mais je n'ai toujours pas l'impression que j'ai atteint une meilleure méthode pratique pour ce que je suis vraiment à la recherche. Maintenant, c'est ce que je suis venu avec, mais cette méthode a aussi des effets secondaires.

  1. PHP Analysé les Modèles: le modèle du système doit être interprété par PHP. De cette façon, je suis capable d'insérer la traduction des paramètres dans le code HTML sans avoir à ouvrir les modèles et les modifier. En outre, PHP analysé les modèles me donne la possibilité d'avoir 1 modèle de site web complet, au lieu d'avoir un sous-dossier pour chaque langue (que j'ai eu avant). La méthode pour atteindre cette cible peut être de Smarty, TemplatePower, Laravel de la Lame ou tout autre modèle de l'analyseur. Comme je l'ai dit, ce doit être indépendant de l'écrit solution.
  2. Base de données: peut-être que je n'ai pas besoin de le mentionner à nouveau. Mais la solution doit être piloté par base de données. Le CMS est destinée à être orientée objet et MVC, donc j'aurais besoin de penser à une logique de structure de données pour les cordes. Comme mes modèles serait structuré: templates/Controller/View.php peut-être que cette structure aurait plus de sens: Controller.View.parameter. La table de base de données ont ces champs avec un value champ. Dans les modèles que nous pourrions utiliser une sorte de méthode comme echo __('Controller.View.welcome', array('name', 'Joshua')) et le paramètre contient Welcome, :name. Ainsi, le résultat étant Welcome, Joshua. Cela semble une bonne façon de le faire, parce que les paramètres tels que :le nom est facile à comprendre par l'éditeur.
  3. Faible Charge de Base de données: bien sûr, le système ci-dessus serait la cause de charge de charge de base de données si ces chaînes sont en cours de chargement sur la route. Donc j'aurais besoin d'un système de cache qui re-rend la langue des fichiers dès qu'ils sont édités/enregistré dans l'administration de l'environnement. Parce que les fichiers sont générés, aussi un bon système de fichiers de mise en page est nécessaire. Je crois que l'on peut aller avec languages/en_EN/Controller/View.php ou .ini, qui vous convient le mieux. Peut-être un .ini est encore analysée plus rapide à la fin. Cette fould doit contenir les données dans l' format parameter=value; . Je suppose que c'est la meilleure façon de le faire, puisque chaque point de Vue qui est rendu peut inclure sa propre langue de fichier s'il existe. Les paramètres de langue, puis devrait être chargé d'un point de vue spécifique et non pas dans une portée globale pour prévenir les paramètres d'écraser les uns les autres.
  4. Table de base de données de traduction: c'est en fait la chose dont je suis le plus inquiet. Je suis à la recherche d'un moyen de créer des traductions des News/Pages/etc. aussi rapidement que possible. Avoir deux tables pour chaque module (par exemple, News et News_translations) est une option, mais il ressemble à beaucoup de travail pour obtenir un bon système. Une des choses que je suis venu avec est basé sur un data versioning système que j'ai écrit: il y a une table de base de données nom de l' Translations, cette table possède une combinaison unique d' language, tablename et primarykey. Par exemple: en_En / News / 1 (en se Référant à la version anglaise de l'article avec l'ID=1). Mais il y a 2 inconvénients majeurs de cette méthode: tout d'abord ce tableau tend à devenir assez longue avec un grand nombre de données dans la base de données et d'autre part qu'il serait un enfer d'un emploi pour utiliser cette configuration pour la recherche de la table. E. g. la recherche pour le SEO slug de l'objet serait une recherche en texte intégral, qui est assez bête. Mais d'un autre côté: c'est un moyen rapide de créer des chaînes traduisibles dans chaque table très rapide, mais je ne crois pas que ce pro overweights la con.
  5. Avant la fin de l': Aussi le front-end aurait besoin d'un peu de réflexion. Bien sûr, nous aurions stocker les langues disponibles dans une base de données et (de)active de ceux que nous avons besoin. De cette façon, le script peut générer une liste déroulante pour sélectionner une langue et le back-end peut décider automatiquement ce que les traductions peuvent être faites à l'aide de la CMS. La langue choisie (par exemple, en_EN) pourrait alors être utilisé lors de l'obtention de la langue de fichier d'une vue ou d'obtenir le droit de traduction d'un élément de contenu sur le site web.

Donc, ils sont là. Mes idées jusqu'à présent. Ils n'ont même pas inclure les options de localisation pour les dates etc, mais comme mon serveur prend en charge PHP5.3.2+, la meilleure option est d'utiliser l'extension intl, comme expliqué ici: http://devzone.zend.com/1500/internationalization-in-php-53/ - mais ce serait de l'utiliser à tout stade de développement. Pour l'instant, la question principale est de savoir comment avoir le meilleur practics de traduction du contenu dans un site web.

En plus de tout ce que j'ai expliqué ici, j'ai encore une autre chose que je n'ai pas encore décidé, ça ressemble à une question simple, mais en fait ça a été de me donner des maux de tête:

Traduction de l'URL? Faut-il le faire ou pas? et de quelle manière?

Si.. si j'ai cette url: http://www.domain.com/about-us et l'anglais est ma langue par défaut. Si cette URL est traduit en http://www.domain.com/over-ons lorsque je choisis le néerlandais comme langue? Ou devons-nous aller à la route facile et il suffit de changer le contenu de la page qui est visible à l' /about. La dernière chose ne semble pas une option valide parce que ce serait de générer plusieurs versions de la même URL, ce indexation du contenu va échouer le droit chemin.

Une autre option est d'utiliser http://www.domain.com/nl/about-us à la place. Cela génère au moins une URL unique pour chaque contenu. Aussi ce serait plus facile d'aller à une autre langue, par exemple l' http://www.domain.com/en/about-us et l'URL fournie est plus facile à comprendre pour les deux Google et les visiteurs de l'Homme. L'utilisation de cette option, que faisons-nous avec les langues par défaut? Si la langue par défaut de supprimer la langue sélectionnée par défaut? Afin de rediriger http://www.domain.com/en/about-us de http://www.domain.com/about-us ... À mes yeux c'est la meilleure solution, car quand le CMS est le programme d'installation pour une seule langue, il n'est pas nécessaire d'avoir cette identification de la langue dans l'URL.

Et une troisième option est une combinaison de deux options: à l'aide de la "langue-identification-moins"-URL (http://www.domain.com/about-us) pour la langue principale. Et l'utilisation d'une URL avec une traduction SEO slug pour les sous-langues: http://www.domain.com/nl/over-ons & http://www.domain.com/de/uber-uns

J'espère que ma question reçoit vos têtes à la fissuration, ils ont craqué le mien, c'est sûr! Il ne m'aide déjà des choses à travailler comme une question ici. M'a donné la possibilité d'examiner les méthodes que j'ai utilisé avant et à l'idée que je vais avoir pour mon prochain CMS.

Je vous remercie déjà d'avoir pris le temps de lire ce tas de texte!

// Edit #1:

J'ai oublié de mentionner: le __() la fonction est un alias de traduire une chaîne donnée. Dans cette méthode, il y a évidemment devrait être une sorte de repli de la méthode où le texte par défaut est chargé lorsqu'il n'y a pas de traductions disponibles pour le moment. Si la traduction est manquant, il devrait être inséré ou le fichier de traduction doit être régénéré.

120voto

tereško Points 32847

Sujet du postulat

Il y a trois aspects distincts dans un site multilingue:

  • traduction de l'interface
  • contenu
  • le routage d'url

Alors qu'ils sont tous interconnectés de différentes façons, à partir du CMS point de vue qu'ils sont gérés à l'aide de différents éléments de l'INTERFACE utilisateur et stockées de manière différente. Vous semblez être confiant dans votre mise en œuvre et la compréhension des deux premières. La question était sur ce dernier aspect - "Traduction de l'URL? Faut-il le faire ou pas? et de quelle façon?"

Ce que l'URL peut être fait?

Une chose très importante est de ne pas obtenir la fantaisie avec IDN. Au lieu faveur de la translittération (aussi: la transcription et la romanisation). Même si au premier abord IDN semble option viable pour les international de l'Url, ça ne fonctionne pas comme annoncé, pour deux raisons:

  • certains navigateurs vous tournez le non-ASCII caractères comme 'ч' ou 'ž' en '%D1%87' et '%C5%BE'
  • si l'utilisateur a des thèmes personnalisés, le thème de la police est très probable, pour ne pas avoir les symboles de ces lettres

J'ai effectivement essayé d'IDN approche il ya quelques années dans un Yii en fonction du projet (horrible cadre, à mon humble avis). J'ai rencontré deux problèmes mentionnés ci-dessus avant de grattage de cette solution. Aussi, je soupçonne qu'il pourrait être un vecteur d'attaque.

Options disponibles ... comme je les vois.

En gros, vous avez deux choix, qui pourrait être réduit à:

  • http://site.tld/[:query]: où en [:query] détermine à la fois la langue et le contenu choix

  • http://site.tld/[:language]/[:query]: où en [:language] de la partie de l'URL définit le choix de la langue et de l' [:query] est uniquement utilisée pour identifier le contenu

La requête est Α et Ω ..

Disons que vous choisissez http://site.tld/[:query].

Dans ce cas, vous avez une source primaire de la langue: le contenu de [:query] segment; et deux autres sources:

  • valeur en $_COOKIE['lang'] pour le navigateur
  • liste des langues dans HTTP Accept-Language (1), (2) d'en-tête

Tout d'abord, vous avez besoin pour correspondre à la requête de l'un des régimes de modèles de routage (si votre choix est Laravel, puis lire ici). Sur le succès de la correspondance de motif, vous devez ensuite trouver la langue.

Vous devez aller à travers tous les segments de la répétition. Trouver le potentiel des traductions pour l'ensemble de ces segments et de déterminer la langue utilisée. Les deux autres sources (cookie et en-tête), soit pour résoudre des conflits de routage, quand (et non pas "si"), elles surviennent.

Prenons, par exemple: http://site.tld/blog/novinka.

C'est la translittération de l' "блог, новинка", qui en anglais signifie approximativement "blog", "latest".

Comme vous pouvez déjà remarquer, en russe "блог" sera transcrit comme "blog". Ce qui signifie que pour la première partie de l' [:query] vous (dans le meilleur des cas) à la fin avec l' ['en', 'ru'] liste de langues. Ensuite, vous prenez segment suivant - "novinka". Qui peut avoir qu'une seule langue sur la liste des possibilités: ['ru'].

Lorsque la liste contient un seul élément, vous avez réussi à trouvé le langage.

Mais si vous vous retrouvez avec 2 (exemple: le russe et l'ukrainien) ou plus de possibilités .. ou 0 possibilités, comme un cas peut être. Vous devrez utiliser des cookies et/ou d'en-tête pour trouver la bonne option.

Et si tout le reste échoue, vous choisissez la langue par défaut du site.

La langue en tant que paramètre

L'alternative est d'utiliser l'URL, qui peut être définie comme http://site.tld/[:language]/[:query]. Dans ce cas, lors de la traduction de la requête, vous n'avez pas besoin de deviner la langue, parce que vous savez déjà.

Il est aussi une source secondaire de langue: la valeur du cookie. Mais ici, il est inutile de vous embêter avec-tête Accept-Language, parce que vous n'êtes pas affaire avec une quantité inconnue de langues possibles en cas de "démarrage à froid" (lorsque l'utilisateur premier temps, il ouvre le site avec de requête personnalisés).

Au lieu de cela vous avez 3 simple, les priorités options:

  1. si [:language] segment est défini, utilisez
  2. si $_COOKIE['lang'] est défini, utilisez
  3. utiliser la langue par défaut

Lorsque vous avez la langue, vous essayez simplement de traduire la requête, et si la traduction échoue, utilisez la "valeur par défaut" pour que le segment particulier (basé sur le résultat d'acheminement).

N'est-ce pas ici une troisième option?

Oui, techniquement, vous pouvez combiner les deux approches, mais qui ne ferait que compliquer le processus et ne peuvent accueillir que des gens qui veulent changer manuellement l'URL de l' http://site.tld/en/news de http://site.tld/de/news et attendre que la page de news pour le changement de l'allemand.

Mais même dans ce cas probable pourrait être atténué à l'aide de cookie (qui contiendrait des informations sur les précédentes choix de la langue), à mettre en œuvre avec moins de magie et d'espoir.

L'approche à utiliser?

Comme vous l'avez peut-être déjà deviné, je vous recommande http://site.tld/[:language]/[:query] comme la plus raisonnable option.

Aussi dans la vraie parole est d'avoir 3ème partie importante dans l'URL: "titre". Comme dans le nom du produit dans la boutique en ligne ou le titre de l'article dans les news du site.

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

Dans ce cas - '/news/article/121415' serait la requête, et l' 'EU-as-global-reserve-currency' est le titre. Purement à des fins de SEO.

Peut-il être fait dans Laravel?

Un peu, mais pas par défaut.

Je ne suis pas trop familier avec elle, mais de ce que j'ai vu, Laravel utilise le modèle simple basé sur le mécanisme de routage. Pour mettre en œuvre Url multilingues, vous aurez probablement à étendre la classe de base(es), car multilingue de routage doivent avoir accès à différentes formes de stockage (base de données, le cache et/ou des fichiers de configuration).

Il est acheminé. Que faire maintenant?

Comme un résultat de tout vous vous retrouvez avec deux précieux éléments d'information: actualité langue et traduit les segments de la requête. Ces valeurs peuvent ensuite être utilisés pour l'envoi de la ou des classe(s) qui va produire le résultat.

Fondamentalement, l'URL suivante: http://site.tld/ru/blog/novinka (ou la version sans '/ru') est transformé en quelque chose comme

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

Dont vous venez d'utiliser pour l'expédition:

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

.. ou de la variation, en fonction notamment de la mise en œuvre.

52voto

Alula Errorpone Points 6304

La mise en œuvre de l'i18n Sans L'impact sur les Performances à l'Aide d'un Pré-Processeur, comme suggéré par Thomas Bley

Au travail, nous nous sommes récemment allés à travers la mise en œuvre de l'i18n sur un couple de nos propriétés, et l'une des choses que nous avons gardé en difficulté a été l'impact sur les performances de traiter à la volée de la traduction, puis j'ai découvert ce super blog post par Thomas Bley qui a inspiré la façon dont nous utilisons i18n pour supporter de grosses charges de trafic avec un minimum de problèmes de performances.

Au lieu d'appeler des fonctions pour chaque opération de traduction, qui comme nous le savons en PHP est cher, nous définissons notre base de fichiers avec des espaces réservés, puis d'utiliser un pré-processeur pour mettre en cache les fichiers (nous stockons la date de modification du fichier pour s'assurer que nous servons le plus récent contenu à tout moment).

La Traduction Des Balises

Thomas utilise {tr} et {/tr} balises pour définir où les traductions de début et de fin. En raison du fait que nous sommes à l'aide de RAMEAUX, nous ne voulons pas utiliser { afin d'éviter la confusion, de sorte que nous utilisons [%tr%] et [%/tr%] à la place. En gros, cela ressemble à ceci:

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

Notez que Thomas suggère l'utilisation de la base de l'anglais dans le fichier. Nous ne le faisons pas parce que nous ne voulons pas avoir à modifier tous les fichiers de traduction si l'on change la valeur en anglais.

Les Fichiers INI

Ensuite, nous créons un fichier INI pour chaque langue, dans le format 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)

Il serait trivial de permettre à un utilisateur de modifier les présentes à l'intérieur de la CMS, juste obtenir les paires de clés, par un preg_split sur \n ou = et de faire de la CMS en mesure d'écrire dans les fichiers INI.

Le Pré-Processeur

Essentiellement, Thomas suggère d'utiliser un juste-à-temps "compilateur" (même si, en vérité, c'est un préprocesseur) la fonction de ce genre pour prendre vos fichiers de traduction et de créer statique PHP les fichiers sur le disque. De cette façon, nous avons essentiellement cache nos fichiers traduits au lieu d'appeler une fonction de traduction pour chaque chaîne de caractères dans le fichier:

// 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;
}

Note: je n'ai pas de vérifier que la regex fonctionne, je n'ai pas le copier à partir de notre serveur de l'entreprise, mais vous pouvez voir le fonctionnement de l'opération.

Comment Appeler

Encore une fois, cet exemple est de Thomas Bley, pas de moi:

// 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();

Nous stockons la langue dans un cookie (ou variable de session si nous ne pouvons pas obtenir un cookie) et ensuite le retirer à chaque requête. Vous pouvez combiner cela avec une option $_GET paramètre pour changer la langue, mais je vous suggère de ne pas sous-domaine par langue ou de pages par langue, parce que ça va rendre plus difficile pour voir quelles pages sont les plus populaires et permettra de réduire la valeur de liens entrants que vous aurez plus de peine à se répandre.

Pourquoi utiliser cette méthode?

Nous aimons cette méthode de prétraitement pour trois raisons:

  1. L'énorme gain de performance de ne pas appeler tout un tas de fonctions pour le contenu qui change rarement (avec ce système, 100k visiteurs en français sera toujours seulement finir la traduction de remplacement une fois).
  2. Il ne pas ajouter de charge de notre base de données, il utilise de simples plat de fichiers et est une pure solution PHP.
  3. La possibilité d'utiliser PHP expressions à l'intérieur de nos traductions.

Traduits De La Base De Données De Contenu

Nous venons d'ajouter une colonne pour le contenu dans notre base de données appelée language, puis nous utilisons une méthode d'accesseur pour l' LANG constante nous l'avons défini plus tôt, afin que nos appels SQL (à l'aide de ZF1, malheureusement) ressembler à ceci:

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

Nos articles ont un composé clé primaire sur id et language si l'article 54 peut exister dans toutes les langues. Notre LANG par défaut est en_US si non spécifié.

URL Slug Traduction

J'aimerais combiner deux choses ici, on est une fonction dans le fichier de démarrage qui accepte $_GET paramètre de langue et remplace la variable de cookie, et l'autre est de routage qui accepte plusieurs limaces. Ensuite, vous pouvez faire quelque chose comme ceci dans votre routage:

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

Celles-ci pourraient être stockées dans un fichier plat qui pourrait facilement être écrite à partir de votre panneau d'administration. JSON ou XML peut fournir une bonne structure pour les soutenir.

Notes Sur Quelques Autres Options

Basé sur PHP À La Volée de Traduction

Je ne peux pas voir que ceux-ci offrent aucun avantage par rapport aux pré-traitées à des traductions.

Front-end Basé sur les Traductions

J'ai longtemps trouvé ces intéressantes, mais il ya quelques mises en garde. Par exemple, vous avez à mettre à la disposition de l'utilisateur de l'ensemble de la liste des phrases sur votre site web que vous envisagez de traduire, cela pourrait être problématique si il y a des zones du site que vous êtes en gardant cachés ou n'autorisent pas l'accès à l'.

Il faudrait aussi supposer que tous vos utilisateurs sont prêts et en mesure d'utiliser Javascript sur votre site, mais à partir de mes statistiques, environ 2,5% de nos utilisateurs sont en cours d'exécution sans qu'il (ou à l'aide de Noscript pour bloquer nos sites de l'utiliser).

Sur Base De Données Des Traductions

PHP base de données des vitesses de connexion sont rien à écrire sur la maison, et cela s'ajoute à la déjà haut dans le ciel de l'appel d'une fonction sur chaque phrase à traduire. La performance et l'évolutivité des questions sembler écrasante avec cette approche.

16voto

Yaroslav Points 543

Je vous suggère de ne pas inventer une roue et utiliser gettext et ISO langues abbrevs liste. Avez-vous vu comment i18n/l10n mis en œuvre dans populaire des Sgc ou les cadres?

En utilisant gettext, vous disposez d'un outil puissant, où de nombreux cas, est déjà mis en place comme le pluriel des nombres. En anglais, vous n'avez que 2 options: le singulier et le pluriel. Mais en russie par exemple, il existe 3 formes et ce n'est pas aussi simple qu'en anglais.

Aussi de nombreux traducteurs ont déjà une expérience de travail avec gettext.

Jetez un oeil à CakePHP ou Drupal . Les deux multilingue activé. CakePHP comme exemple de traduction de l'interface et Drupal comme exemple de la traduction du contenu.

Pour l10n à l'aide de la base de données n'est pas du tout le cas. Il aura des tonnes de requêtes. L'approche Standard pour obtenir tous l10n données en mémoire au début (ou au cours du premier appel à la i10n fonction si vous préférez lazy loading). Il peut être lu à partir de .fichier po ou de DB toutes les données à la fois. Et que viens de lire demandé des chaînes de la matrice.

Si vous avez besoin de mettre en œuvre un outil en ligne pour traduire l'interface, vous pouvez avoir toutes les données en DB mais encore d'enregistrer toutes les données dans un fichier de travailler avec elle. Pour réduire la quantité de données dans la mémoire vous pouvez diviser tous vos messages traduits/cordes en groupes et de ne charger que les groupes dont vous avez besoin si ce sera possible.

Donc, vous avez tout à fait raison dans votre #3. À une exception près: d'habitude, c'est un gros fichier, pas un par-contrôleur de fichier. Parce qu'il est le meilleur pour les performances pour ouvrir un fichier. Vous savez sans doute que certains highloaded web apps compile tous les code PHP dans un fichier afin d'éviter les opérations de fichier lors de l'include ou require appelé.

Sur Les Url. Google indirectement suggèrent d'utiliser la traduction:

afin d'indiquer clairement le contenu en langue française: http://example.ca/fr/vélo-de-montagne.html

Je pense également que vous avez besoin de rediriger l'utilisateur par défaut préfixe de langue par exemple http://examlpe.com/about-us va redirige vers http://examlpe.com/en/about-us Mais si votre site n'utilise qu'une seule langue de sorte que vous n'avez pas besoin de préfixes.

Découvrez: 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

La traduction de contenu est plus difficile. Je pense que ça va être quelques différences avec les différents types de contenu, par exemple, les articles, les éléments de menu etc. Mais dans #4 vous êtes dans le droit chemin. Prendre un coup d'oeil dans Drupal pour avoir plus d'idées. Il ont assez clair DB schéma et assez bonne interface pour traduire. Comme vous la création de l'article et sélectionnez la langue. Et que vous pouvez par la suite de le traduire dans d'autres langues.

Drupal translation interface

Je pense qu'il n'est pas de problème avec l'URL de limaces. Vous pouvez simplement créer table séparée pour les limaces et ça sera la bonne décision. Également droit à l'aide d'indices, il n'est pas de problème pour la table de requête même avec l'énorme quantité de données. Et ce n'était pas de la recherche plein texte, mais la chaîne de match, si l'utilisation de type de données varchar pour la limace et vous pouvez avoir un index sur ce champ.

PS Désolé, mon anglais est loin d'être parfait.

12voto

user3749746 Points 18

Il dépend de la quantité de contenu de votre site. Au début, j'ai utilisé une base de données comme toutes les autres personnes ici, mais il peut être temps de faire un script de tous les rouages d'une base de données. Je ne dis pas que c'est une méthode idéale et surtout si vous avez beaucoup de texte, mais si vous voulez le faire rapidement sans l'aide d'une base de données, cette méthode pourrait fonctionner, cependant, vous ne pouvez pas permettre aux utilisateurs de saisir des données qui sera utilisé comme traduction des fichiers. Mais si vous ajoutez les traductions-vous, il va travailler:

Disons que vous avez de ce texte:

Welcome!

Vous pouvez entrer dans une base de données avec des traductions, mais vous pouvez également le faire:

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

Maintenant, si votre site utilise un cookie, vous avez ceci par exemple:

$_COOKIE['language'];

Pour le rendre facile de transformer en un code qui peut facilement être utilisé:

$language=$_COOKIE['language'];

Si votre cookie de langue est Gallois et vous avez ce morceau de code:

echo $welcome[$language];

Le résultat de ce sera:

Croeso!

Si vous avez besoin d'ajouter beaucoup de traductions de votre site web et une base de données est trop longue, à l'aide d'un tableau peut être une solution idéale.

8voto

Shushant Points 931

Je vous suggère de ne pas vraiment dépendre de la base de données pour la traduction, il pourrait être vraiment un désordre de la tâche et peut être d'une extrême problème dans le cas de l'encodage des données.

J'ai eu la même question tout à l'heure et à l'issue de la classe de résoudre mon problème

Objet: Paramètres\Paramètres Régionaux

<?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;
  }
}

L'utilisation de la

 <?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');

Comment ça marche

{a:1} est remplacé par le 1er argument passé à la méthode Locale::translate('key_name','arg1') {a:2} est remplacé par le 2e argument passé à la méthode Locale::translate('key_name','arg1','arg2')

Comment fonctionne la détection

  • Par défaut, si geoip est installé, puis ce sera le retour de code de pays en geoip_country_code_by_name et si geoip est pas installé le secours d' HTTP_ACCEPT_LANGUAGE - tête

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X