Site web multilingue des meilleures pratiques
J'ai été aux prises avec cette question pendant quelques mois maintenant, mais je n'ai pas été dans une situation où j'avais besoin d'explorer toutes les options possibles avant. En ce moment, j'ai l'impression qu'il est temps d'apprendre à connaître les possibilités et de créer mes propres préférences personnelles à utiliser dans mes prochains projets.
Laissez-moi d'abord esquisser la situation que je cherche
Je suis sur le point de mettre à niveau/redévelopper un système de gestion de contenu que j'utilise depuis un bon moment maintenant. Cependant, je me sens multi langue est une grande amélioration de ce système. Avant je n'ai pas utilisé de frameworks mais je vais utiliser Laraval4 pour le projet à venir. Laravel semble le meilleur choix d'un moyen plus propre de coder PHP. Sidenote: Laraval4 should be no factor in your answer
. Je cherche des moyens généraux de traduction indépendants de la plate-forme/du cadre.
Ce qui devrait être traduit
Comme le système que je recherche doit être aussi convivial que possible la méthode de gestion de la traduction doit être à l'intérieur de la CMS. Il ne devrait pas être nécessaire de démarrer une connexion FTP pour modifier des fichiers de traduction ou des modèles analysés html/php.
De plus, je cherche le moyen le plus simple de traduire plusieurs tables de base de données peut-être sans avoir besoin de créer des tables supplémentaires.
Qu'est-ce que je suis venu avec moi-même
Comme j'ai déjà cherché, lu et essayé des choses moi-même. Il y a quelques options que j'ai. Mais je n'ai toujours pas l'impression d'avoir atteint une méthode de meilleure pratique pour ce que je cherche vraiment. En ce moment, c'est ce que j'ai trouvé, mais cette méthode a aussi des effets secondaires.
- PHP a analysé les Modèles : le système de Modèles doit être analysé par PHP. De cette façon, je suis capable d'insérer les paramètres traduits dans le HTML sans avoir à ouvrir les modèles et à les modifier. En outre, les modèles analysés par PHP me donnent la possibilité d'avoir 1 modèle pour le 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 Smarty, TemplatePower, Laravel's Blade ou tout autre analyseur de modèle. Comme je l'ai dit, cela devrait être indépendant de la solution écrite.
-
database Driven : peut-être que je n'ai pas besoin de le mentionner à nouveau. Mais la solution devrait être pilotée par la base de données. Le CMS est destiné à être orienté objet et MVC, donc je devrais penser à une structure de données logique pour les chaînes. Comme mes modèles seraient structuré: modèles/Contrôleur/Vue.php peut-être que cette structure aurait le plus de sens:
Controller.View.parameter
. La table de base de données aurait ces champs un long avec un champvalue
. Dans les modèles, nous pourrions utiliser une méthode de tri commeecho __('Controller.View.welcome', array('name', 'Joshua'))
et le paramètre contientWelcome, :name
. Ainsi, le résultat étantWelcome, Joshua
. Cela semble un bon moyen de le faire, car les paramètres tels que: name sont faciles à comprendre par l'éditeur. -
Faible Charge de Base de données: bien sûr, le système ci-dessus entraînerait des charges de charge de la base de données si ces chaînes sont chargées en déplacement. Par conséquent, j'aurais besoin d'un système de mise en cache qui restitue les fichiers de langue dès qu'ils sont édités/enregistrés dans l'environnement d'administration. Étant donné que les fichiers sont générés, une bonne disposition du système de fichiers est également nécessaire. Je suppose que nous pouvons aller avec
languages/en_EN/Controller/View.php
ou .ini, tout ce qui vous convient le mieux. Peut-être un .ini est même analysé plus rapidement à la fin. Ce fould devrait contenir les données dans leformat parameter=value;
. Je suppose que c'est la meilleure façon de le faire, puisque chaque vue qui est rendu peut inclure son propre fichier de langue s'il existe. Les paramètres de langue doivent alors être chargés dans une vue spécifique et non dans une portée globale pour empêcher les paramètres de s'écraser les uns les autres. -
traduction de table de base de données : c'est en fait la chose qui m'inquiète le plus. Je cherche un moyen de créer des traductions de nouvelles / Pages / etc. aussi rapidement que possible. Avoir deux tables pour chaque module (par exemple
News
etNews_translations
) est une option, mais il se sent comme à beaucoup de travail pour obtenir un bon système. Une des choses que j'ai imaginées est basée sur un systèmedata versioning
que j'ai écrit: il y a un nom de table de base de donnéesTranslations
, Cette table a une combinaison unique delanguage
,tablename
etprimarykey
. Par exemple: en_fr / News / 1 (Se référant à la version anglaise de L'article D'actualité avec ID=1). Mais il y a 2 énormes inconvénients à cette méthode: tout d'abord, cette table a tendance à être assez longue avec beaucoup de données dans la base de données et deuxièmement, ce serait un sacré travail d'utiliser cette configuration pour la recherche de la table. Par exemple, la recherche de la limace SEO de l'élément serait une recherche en texte intégral, ce qui est assez stupide. Mais d'un autre côté: c'est un moyen rapide de créer du contenu traduisible dans chaque table très rapidement, mais je ne crois pas que ce pro surpondère les con. - Travail frontal : aussi le frontal aurait besoin d'une réflexion. Bien sûr, nous stockerions les langues disponibles dans une base de données et (dé)active celles dont 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 peuvent décider automatiquement quelles traductions peuvent être faites en utilisant le CMS. La langue choisie (par exemple en_EN) serait alors utilisée lors de l'obtention du fichier de langue pour une vue ou pour obtenir la bonne traduction pour un élément de contenu sur le site web.
Donc, ils sont là. Mes idées jusqu'à présent. Ils n'incluent même pas encore d'options de localisation pour les dates, 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 cela serait utile dans n'importe quel stade de développement ultérieur. Pour l'instant, la question principale est de savoir comment avoir les meilleures pratiques 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ée, ça ressemble à une question simple, mais en fait ça me donne des maux de tête:
Traduction D'URL? Faut-il le faire ou pas? et dans ce de la sorte?
Donc.. si j'ai cette url: http://www.domain.com/about-us
et l'anglais est ma langue par défaut. Cette URL doit-elle être traduite en http://www.domain.com/over-ons
lorsque je choisis le néerlandais comme langue? Ou devrions-nous aller sur la route facile et simplement changer le contenu de la page visible à /about
. La dernière chose ne semble pas une option valide car cela générerait plusieurs versions de la même URL, cette indexation du contenu échouera dans le bon sens.
Une autre option utilise http://www.domain.com/nl/about-us
à la place. Cela génère au moins un URL unique pour chaque contenu. En outre, ce serait plus facile d'aller dans une autre langue, par exemple http://www.domain.com/en/about-us
et L'URL fournie est plus facile à comprendre pour les visiteurs Google et humains. En utilisant cette option, que faisons-nous avec les langues par défaut? La langue par défaut doit-elle supprimer la langue sélectionnée par défaut? Donc rediriger http://www.domain.com/en/about-us
vers http://www.domain.com/about-us
... À mes yeux, c'est la meilleure solution, car lorsque le CMS est configuré pour une seule langue, il n'est pas nécessaire d'avoir cette identification de langue dans le URL.
Et une troisième option est une combinaison des deux options: en utilisant le"language-identification-less" -URL (http://www.domain.com/about-us
) pour la langue principale. Et utilisez une URL avec une limace de référencement traduite pour les sous langues: http://www.domain.com/nl/over-ons
& http://www.domain.com/de/uber-uns
J'espère que ma question vous fera craquer la tête, ils ont craqué la mienne à coup sûr! Cela m'a déjà aidé à résoudre les choses comme une question ici. M'a donné la possibilité de revoir les méthodes que j'ai utilisées auparavant et l'idée que j'ai 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: la fonction _ _ () est un alias pour traduire une chaîne donnée. Dans cette méthode, il devrait évidemment y avoir une sorte de méthode de secours où le texte par défaut est chargé quand il n'y a pas encore de traductions disponibles. Si la traduction est manquante, elle doit être insérée ou le fichier de traduction doit être régénéré.
13 réponses
Prémisse du sujet
Il y a trois aspects distincts dans un site multilingue:
- traduction de l'interface
- contenu
- routage d'url
Bien qu'ils soient tous interconnectés de différentes manières, du point de vue du CMS, ils sont gérés à l'aide de différents éléments D'interface utilisateur et stockés différemment. Vous semblez être confiant dans votre mise en œuvre et votre compréhension des deux premiers. La question portait sur ce dernier aspect - " traduction D'URL? Devrait nous faisons cela ou pas? et de quelle manière?"
De quoi L'URL peut-elle être faite?
Une chose très importante est de ne pas avoir envie de IDN . Au lieu de cela favoriser translittération (aussi: transcription et romanisation). Bien QU'à première vue IDN semble une option viable pour les URL internationales, il ne fonctionne pas comme annoncé pour deux raisons:
- certains navigateurs transforment les caractères non-ASCII comme
'ч'
ou'ž'
en'%D1%87'
et'%C5%BE'
- si l'utilisateur a des thèmes personnalisés, la police du thème est très susceptible de ne pas avoir de symboles pour ces lettres
En fait, j'ai essayé D'aborder L'IDN il y a quelques années dans un projet basé sur Yii (horrible framework, IMHO). J'ai rencontré les deux problèmes mentionnés ci-dessus avant de gratter cette solution. En outre, je soupçonne que ce pourrait être un vecteur d'attaque.
Options disponibles ... comme je les vois.
Fondamentalement, vous avez deux choix, qui pourraient être abstraits comme:
-
http://site.tld/[:query]
: où[:query]
détermine le choix de la langue et du contenu -
http://site.tld/[:language]/[:query]
: où[:language]
une partie de L'URL définit le choix de la langue et[:query]
est utilisée uniquement pour identifier le contenu
La requête est Α Et Ω ..
Disons que vous choisissez http://site.tld/[:query]
.
Dans ce cas, vous avez une source principale de langue: le contenu du segment [:query]
; et deux sources supplémentaires:
- valeur
$_COOKIE['lang']
pour ce navigateur particulier - liste des langues dans HTTP Accepter-langue (1), (2) en-tête
Tout d'abord, vous devez faire correspondre la requête à l'un des modèles de routage définis (si votre choix est Laravel, alors lire ici). Sur correspondance réussie de modèle, vous devez ensuite trouver la langue.
Vous devriez passer par tous les segments du motif. Trouvez les traductions possibles pour tous ces segments et déterminez quelle langue a été utilisée. Les deux sources supplémentaires (cookie et header) serait utilisé pour résoudre les conflits de routage, quand (pas "si") ils surviennent.
Prenez par exemple: http://site.tld/blog/novinka
.
C'est la translittération de "блог, новинка"
, qui en anglais signifie environ "blog", "latest"
.
Comme vous pouvez déjà le remarquer, en russe " блог "sera translittéré comme"blog". Ce qui signifie que pour la première partie de [:query]
vous (dans le meilleur scénario) finira avec ['en', 'ru']
liste des langues possibles. Ensuite, vous prenez le segment suivant - "novinka". Cela pourrait avoir seulement une langue sur la liste des possibilités: ['ru']
.
Lorsque la liste contient un élément, vous avez réussi à trouver la langue.
Mais si vous vous retrouvez avec 2 (Exemple: russe et ukrainien) ou plus de possibilités .. ou 0 possibilités, comme un cas pourrait être. Vous devrez utiliser le cookie et / ou l'en-tête pour trouver l'option correcte.
Et si tout le reste échoue, vous choisissez la langue par défaut du site.
Langue comme paramètre
L'alternative est D'utiliser L'URL, qui peut être défini 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à de l'utiliser.
Il y a aussi une source secondaire de langage: la valeur du cookie. Mais ici, il ne sert à rien de jouer avec L'en-tête Accept-Language, car vous n'avez pas affaire à une quantité inconnue de langues possibles en cas de "démarrage à froid" (lorsque l'utilisateur ouvre le site pour la première fois avec une requête personnalisée).
Au lieu de cela, vous avez 3 simples, priorisés options:
- si
[:language]
segment est défini, utilisez - si
$_COOKIE['lang']
est défini, utilisez - 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 ce segment particulier (en fonction des résultats de routage).
N'est-ce pas là une troisième option?
Oui, techniquement, vous pouvez combiner les deux approches, mais cela compliquerait le processus et n'accommoderait que les gens qui veulent changer manuellement L'URL de http://site.tld/en/news
à http://site.tld/de/news
et s'attendre à ce que la page de nouvelles change en allemand.
Mais même ce cas pourrait probablement être atténué en utilisant la valeur de cookie (qui contiendrait des informations sur le choix précédent de la langue), à mettre en œuvre avec moins de magie et d'espoir.
Quelle approche utiliser?
Comme vous l'avez peut-être déjà deviné, je recommanderais http://site.tld/[:language]/[:query]
comme l'option la plus sensée.
Aussi dans une situation de mot réel, vous auriez une 3ème partie majeure dans L'URL: "intitulé". Comme dans le nom du produit dans la boutique en ligne ou le titre de l'article dans le site de nouvelles.
Exemple: http://site.tld/en/news/article/121415/EU-as-global-reserve-currency
Dans ce cas, '/news/article/121415'
serait la requête, et la 'EU-as-global-reserve-currency'
est dans le titre. Purement à des fins de SEO.
Peut-il être fait à Laravel?
Un Peu, mais pas par défaut.
Je ne suis pas trop familier avec cela, mais D'après ce que J'ai vu, Laravel utilise un mécanisme de routage simple basé sur un motif. Pour implémenter des URL multilingues, vous devrez probablement étendre Classe(S) centrale (S) , car le routage multilingue nécessite l'accès à différentes formes de stockage (base de données, cache et/ou fichiers de configuration).
Il est routé. Que faire maintenant?
À la suite de tout ce que vous finiriez avec deux informations précieuses: la langue actuelle et les segments traduits de la requête. Ces valeurs peuvent ensuite être utilisées pour envoyer à la classe (S) qui produira le résultat.
Fondamentalement, L'URL suivante: http://site.tld/ru/blog/novinka
(ou la version sans '/ru'
) obtient transformé en quelque chose comme
$parameters = [
'language' => 'ru',
'classname' => 'blog',
'method' => 'latest',
];
Que vous utilisez simplement pour l'expédition:
$instance = new {$parameter['classname']};
$instance->{'get'.$parameters['method']}( $parameters );
.. ou une variation de celui-ci, en fonction de la mise en œuvre particulière.
Implémenter i18n sans le Hit de Performance en utilisant un pré-processeur comme suggéré par Thomas Bley
Au travail, nous avons récemment mis en œuvre i18n sur quelques-unes de nos propriétés, et l'une des choses que nous avons continué à lutter avec était le succès de la performance de traiter la traduction à la volée, puis j'ai découvert ce grand billet de blog de Thomas Bley qui a inspiré la façon dont nous question.
Au lieu d'appeler des fonctions pour chaque opération de traduction, ce qui comme nous le savons en PHP est coûteux, nous définissons nos fichiers de base avec des espaces réservés, puis utilisons un pré-processeur pour mettre en cache ces fichiers (nous stockons le temps de modification du fichier pour nous assurer que nous diffusons le contenu le plus récent
Les Balises De Traduction
Thomas utilise les balises {tr}
et {/tr}
pour définir où les traductions commencent et se terminent. En raison du fait que nous utilisons TWIG, nous ne voulons pas utiliser {
pour éviter toute confusion, nous utilisons [%tr%]
et [%/tr%]
à la place. Fondamentalement, cela ressemble à ceci:
`return [%tr%]formatted_value[%/tr%];`
Notez que Thomas suggère d'utiliser l'anglais de base dans le fichier. Nous ne le faisons pas parce que nous ne voulons pas avoir à modifier tous les fichiers de traduction si nous changeons la valeur en anglais.
Les fichiers INI
Ensuite, nous créons un fichier INI pour chaque langue, au 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 les modifier dans le CMS, juste obtenir les paires de touches par un preg_split
sur \n
ou {[14] } et rendant le CMS capable d'écrire dans les fichiers INI.
Le Composant De Pré-Processeur
Essentiellement, Thomas suggère d'utiliser une fonction "compilateur" juste à temps (bien que, en vérité, c'est un préprocesseur) comme celle-ci pour prendre vos fichiers de traduction et créer des fichiers PHP statiques sur le disque. De cette façon, nous mettons essentiellement en cache nos fichiers traduits au lieu d'appeler une fonction de traduction pour chaque chaîne du 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 vérifiez que l'expression rationnelle fonctionne, Je ne l'ai pas copié à partir de notre serveur d'entreprise, mais vous pouvez voir comment fonctionne l'opération.
Comment L'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 une variable de session si nous ne pouvons pas obtenir un cookie), puis la récupérons à chaque requête. Vous pouvez combiner cela avec un paramètre optionnel $_GET
pour remplacer la langue, mais je ne suggère pas de sous-domaine par langue ou de page par langue parce que cela rendra plus difficile de voir quelles pages sont populaires et réduira la valeur des liens entrants car vous les aurez plus peu répandus.
Pourquoi utiliser cette méthode?
, Nous aimons cette méthode de prétraitement pour trois raisons:
- l'énorme gain de performance de ne pas appeler tout un tas de fonctions pour un contenu qui change rarement (avec ce système, 100k visiteurs en français ne finiront toujours par remplacer la traduction qu'une seule fois).
- ce n'est pas le cas ajoutez n'importe quelle charge à notre base de données, car elle utilise des fichiers plats simples et est une solution PHP pure.
- La possibilité d'utiliser des expressions PHP dans nos traductions.
Obtenir Le Contenu De La Base De Données Traduite
Nous ajoutons simplement une colonne pour le contenu de notre base de données appelée language
, puis nous utilisons une méthode d'accesseur pour la constante LANG
que nous avons définie plus tôt, donc nos appels SQL (en utilisant ZF1, malheureusement) ressemblent à ceci:
$query = select()->from($this->_name)
->where('language = ?', User::getLang())
->where('id = ?', $articleId)
->limit(1);
Nos articles ont une clé primaire composée sur id
et language
, donc l'article 54
peut exister dans toutes les langues. Notre LANG
par défaut est en_US
s'il n'est pas spécifié.
URL SLUG traduction
Je combinerais deux choses ici, l'une est une fonction dans votre bootstrap qui accepte un paramètre $_GET
pour la langue et remplace la variable cookie, et une autre est le routage qui accepte plusieurs limaces. Ensuite, vous pouvez faire quelque chose comme ça dans votre routage:
"/wilkommen" => "/welcome/lang/de"
... etc ...
Ceux-ci pourraient être stockés dans un fichier plat qui pourrait être facilement écrit à partir de votre panneau d'administration. JSON ou XML peut fournir une bonne structure pour les supporter.
Notes Concernant Quelques Autres Options
Traduction à la volée basée sur PHP
Je ne vois pas que ceux-ci offrent un avantage par rapport aux traductions prétraitées.
Traduction frontale
J'ai longtemps trouvé cela intéressant, mais il y a quelques mises en garde. Par exemple, vous devez mettre à la disposition de l'utilisateur la liste complète des phrases sur votre site Web que vous envisagez de traduire, cela pourrait être problématique s'il y a des zones du site que vous gardez cachées ou que vous ne leur avez pas autorisé l'accès.
Vous devez également supposer que tous vos utilisateurs sont prêts et capables d'utiliser Javascript sur votre site, mais d'après mes statistiques, environ 2,5% de nos utilisateurs fonctionnent sans Javascript (ou utilisent Noscript pour bloquer nos sites).
Traductions Basées Sur Des Bases De Données
Vitesses de connectivité des bases de données PHP n'y a rien à écrire, et cela ajoute à la surcharge déjà élevée d'appeler une fonction sur chaque phrase à traduire. Les problèmes de performance et d'évolutivité semblent accablants avec cette approche.
Je vous suggère de ne pas inventer une roue et d'utiliser les langages gettext et ISO abbevs list. Avez-vous vu comment i18n / L10N implémenté dans des CMSes ou des frameworks populaires?
En utilisant gettext, vous aurez un outil puissant où de nombreux cas sont déjà implémentés comme des formes plurielles de nombres. En anglais, vous avez seulement 2 options: singulier et pluriel. Mais en russe par exemple, il y a 3 formes et ce n'est pas aussi simple qu'en anglais.
De nombreux traducteurs ont déjà de l'expérience pour travailler avec gettext.
Voir CakePHP ou Drupal . Les deux multilingue Activé. CakePHP comme exemple de localisation d'interface et Drupal comme exemple de traduction de contenu.
Pour l10n, l'utilisation de la base de données n'est pas du tout le cas. Ce sera des tonnes sur les requêtes. L'approche Standard consiste à obtenir toutes les données l10n en mémoire au début (ou lors du premier appel à la fonction i10n si vous préférez un chargement paresseux). Il peut être lu à partir de .fichier po ou de DB toutes les données à la fois. Et que juste lire chaînes demandées à partir du tableau.
Si vous avez besoin d'implémenter l'outil en ligne pour traduire l'interface, vous pouvez avoir toutes ces données dans la base de données, mais encore enregistrer toutes les données dans un fichier pour travailler avec elle. Pour réduire la quantité de données en mémoire, vous pouvez diviser tous vos messages/chaînes traduits en groupes et charger uniquement les groupes dont vous avez besoin si cela est possible.
Donc vous avez totalement raison dans votre # 3. À une exception près: il s'agit généralement d'un gros fichier et non d'un fichier par contrôleur. Parce qu'il est préférable pour performance pour ouvrir un fichier. Vous savez probablement que certaines applications web highloaded compile tout le code PHP dans un fichier pour éviter les opérations de fichier lorsque include/require est appelé.
À Propos Des URL. Google suggère indirectement d'utiliser la traduction:
Pour indiquer clairement le contenu français: http://example.ca/fr/vélo-de-montagne.html
Aussi, je pense que vous devez rediriger l'utilisateur vers le préfixe de langue par défaut, par exemple http://examlpe.com/about-us volonté redirige vers http://examlpe.com/en/about-us Mais si votre site utilise une seule langue, vous n'avez pas besoin de préfixes du tout.
Vérifier: 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
Traduire le contenu est une tâche plus difficile. Je pense que c' il y aura quelques différences avec différents types de contenu, par exemple des articles, des éléments de menu, etc. Mais dans #4, Vous êtes dans le bon sens. Jetez un oeil dans Drupal pour avoir plus d'idées. Il a un schéma de base de données assez clair et une interface assez bonne pour la traduction. Comme vous créez l'article et sélectionnez la langue pour cela. Et que vous pouvez le traduire plus tard dans d'autres langues.
Je pense que ce n'est pas un problème avec les limaces D'URL. Vous pouvez simplement créer une table séparée pour les limaces et ce sera la bonne décision. En utilisant également les index droits, il n'est pas problème d'interroger la table même avec une énorme quantité de données. Et ce n'était pas une recherche en texte intégral mais une correspondance de chaîne si vous utiliserez le type de données varchar pour slug et vous pouvez aussi avoir un index sur ce champ.
PS Désolé, mon anglais est loin d'être parfait cependant.
Cela dépend de la quantité de contenu de votre site web. Au début, j'ai utilisé une base de données comme toutes les autres personnes ici, mais cela peut prendre du temps de scripter 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 utiliser de base de données, cette méthode pourrait fonctionner, cependant, vous ne pouvez pas permettre aux utilisateurs d'entrer des données qui seront utilisées comme Fichiers de traduction. Mais si vous ajoutez les traductions vous même, il vous travail:
Disons que vous avez ce texte:
Welcome!
Vous pouvez entrer ceci dans une base de données avec des traductions, mais vous pouvez aussi 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, transformons-le en un code qui peut facilement être utilisé:
$language=$_COOKIE['language'];
Si votre langue de cookie est le gallois et que vous avez ce morceau de code:
echo $welcome[$language];
Le résultat de ceci sera:
Croeso!
Si vous avez besoin d'ajouter beaucoup de traductions pour votre site web et une base de données est trop consommatrice, en utilisant un tableau peut être une solution idéale.
Je vous suggère de ne pas vraiment dépendre de la base de données pour la traduction, cela pourrait être une tâche vraiment désordonnée et pourrait être un problème extrême en cas d'encodage de données.
J'ai eu un problème similaire il y a quelques temps et j'ai écrit la classe suivante pour 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;
}
}
Utilisation
<?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 la détection fonctionne
- par défaut, si
geoip
est installé, il renverra le code de pays pargeoip_country_code_by_name
et si geoip n'est pas installé, l'en-têteHTTP_ACCEPT_LANGUAGE
Juste une sous-réponse:
Utilisez absolument des URL traduites avec un identifiant de langue devant elles: http://www.domain.com/nl/over-ons
Les solutions hybrides ont tendance à se compliquer, alors je m'en tiendrais bien. Pourquoi? Parce que l'url est essentielle pour le référencement.
A propos de la traduction db: le nombre de langues est-il plus ou moins fixe? Ou plutôt imprévisible et dynamique? Si c'est corrigé, je voudrais simplement ajouter de nouvelles colonnes, sinon aller avec plusieurs tables.
Mais généralement, pourquoi ne pas utiliser Drupal? Je sais que tout le monde veut construire son propre CMS parce que c'est plus rapide, plus maigre, etc. etc. Mais c'est vraiment une mauvaise idée!
J'ai eu le même probem il y a un moment, avant de commencer à utiliser Symfony framework.
Il suffit D'utiliser une fonction _ _ () qui a arameters pageId (ou objectId, objectTable décrit dans #2), la langue cible et un paramètre facultatif de la langue de secours (par défaut). La langue par défaut pourrait être définie dans une configuration globale afin d'avoir un moyen plus facile de la changer plus tard.
-
Pour stocker le contenu dans la base de données, j'ai utilisé la structure suivante: (pageId, language, contenu, variables).
PageId serait un FK à votre page que vous voulez traduire. si vous avez d'autres objets, comme des nouvelles, des galeries ou autre, divisez-le en 2 champs objectId, objectTable.
Langue-évidemment, il stockerait la chaîne de langue ISO EN_en, LT_lt, EN_us etc.
Contenu - le texte que vous souhaitez traduire avec les caractères génériques pour le remplacement de la variable. Exemple "Bonjour monsieur. %%nom%%. Le solde de votre compte est %%équilibre%%."
Variables - les variables codées json. PHP fournit des fonctions pour les analyser rapidement. Exemple "Nom: Laurynas, balance: 15.23".
Vous avez également mentionné slug field. vous pouvez aisément ajouter à ce tableau pour avoir juste un moyen rapide de le rechercher.
Vos appels de base de données doivent être réduits au minimum avec la mise en cache des traductions. Il doit être stocké dans un tableau PHP, car c'est la structure la plus rapide du langage PHP. Comment vous fera cette mise en cache est à vous. De mon expérience, vous devriez avoir un dossier pour chaque langue prise en charge et un tableau pour chaque pageId. Le cache doit être reconstruit après la mise à jour de la traduction. Seul le tableau modifié doit être régénéré.
Je pense que j'ai répondu à cela en #2
Votre idée est parfaitement logique. celui-ci est assez simple et je pense ne vous fera aucun problème.
Les URL doivent être traduites à l'aide des table de traduction.
Les derniers mots de
Il est toujours bon de rechercher les meilleures pratiques, mais ne pas réinventer la roue. il suffit de prendre et utiliser les composants de bien connue de cadres et de les utiliser.
Jetez un oeil à composant de traduction Symfony . Cela pourrait être une bonne base de code pour vous.
Je ne vais pas tenter d'affiner les réponses déjà données. Au lieu de cela, je vais vous parler de la façon dont mon propre framework PHP poo gère les traductions.
En interne, mon framework utilise des codes comme en, fr, es, cn et ainsi de suite. Un tableau contient les langues supportées par le site web: array ('en', 'fr', 'es', 'cn') Le code de langue est passé via $_GET (lang=fr) et s'il n'est pas passé ou non valide, il est défini sur la première langue du tableau. Donc à tout moment pendant l'exécution du programme et à partir du très au début, la langue actuelle est connue.
, Il est utile de comprendre le type de contenu qui doit être traduit dans une application typique:
1) messages d'erreur des classes (ou code de procédure) 2) messages non-erreur de classes (ou code de procédure) 3) Contenu de la page (généralement stocké dans une base de données) 4) chaînes à l'échelle du site (comme le nom du site web) 5) chaînes spécifiques au script
Le premier type est simple à comprendre. Fondamentalement, nous parlons de messages comme " ne pouvait pas se connecter à la base de données ...". Ces messages ne doivent être chargés qu'en cas d'erreur. Ma classe manager reçoit un appel des autres classes et en utilisant les informations transmises en tant que paramètres va simplement dans le dossier de classe pertinent et récupère le fichier d'erreur.
Le deuxième type de message d'erreur ressemble plus aux messages que vous obtenez lorsque la validation d'un formulaire a mal tourné. ("Vous ne pouvez pas laisser ... blanc " ou "veuillez choisir un mot de passe de plus de 5 caractères"). Les chaînes doivent être chargée avant de la classe fonctionne.Je sais ce qui est
Pour le contenu réel de la page, j'utilise une table par langue, chaque table préfixée par le code de la langue. Donc en_content est la table avec le contenu en langue anglaise, es_content est pour l'Espagne, cn_content pour la Chine et fr_content est la substance française.
Le quatrième type de chaîne est pertinent dans tout votre site web. Ceci est chargé via un fichier de configuration nommé en utilisant le code de la langue, c'est-à-dire en_lang.php, es_lang.php et ainsi de sur. Dans le global fichier de langue que vous aurez besoin de charger les langues traduites comme array ("anglais", "Chinois", "espagnol", "français") dans la version anglaise du fichier global et array('Anglais','Chinois', 'Espagnol', 'Francais') dans le dossier français. Donc, lorsque vous remplissez une liste déroulante pour la sélection de la langue, il est dans la bonne langue ;)
Enfin, vous avez les chaînes spécifiques au script. Donc, si vous écrivez une application de cuisson, il pourrait être "votre four n'était pas assez chaud".
Dans ma demande cycle, le fichier de langue globale est chargé en premier. Vous y trouverez non seulement des chaînes globales (comme "Jack's Website") mais aussi des paramètres pour certaines des classes. Fondamentalement, tout ce qui dépend de la langue ou de la culture. Certaines des chaînes comprennent des masques pour les dates (MMDDYYYY ou JJMMYYYY), ou des Codes de langue ISO. Dans le fichier de langue principal, j'inclus des chaînes pour des classes individuelles parce qu'il y en a si peu.
Le deuxième et dernier fichier de langue lu à partir du disque est fichier de langage de script. lang_en_home_welcome.php est le fichier de langue pour le script home/welcome. Un script est défini par un mode (home) et une action (welcome). Chaque script a son propre dossier avec les fichiers config et lang.
Le script extrait le contenu de la base de données en nommant la table de contenu comme expliqué ci-dessus.
Si quelque chose ne va pas, le gestionnaire sait où trouver le fichier d'erreur dépendant de la langue. Ce fichier n'est chargé que dans le cas d'une erreur.
Donc le la conclusion est évidente. Pensez aux problèmes de traduction avant de commencer à développer une application ou un framework. Vous avez également besoin d'un processus de développement qui intègre les traductions. Avec mon framework, je développe l'ensemble du site en anglais puis traduis tous les fichiers pertinents.
Juste un dernier mot rapide sur la façon dont les chaînes de traduction sont implémentées. Mon framework a un seul global, le $ manager, qui exécute des services disponibles pour tout autre service. Ainsi par exemple le formulaire le service met la main sur le service html et l'utilise pour écrire le html. L'un des services sur mon système est le service de traducteur. $ translator - > set($service,$code,$string) définit une chaîne pour la langue actuelle. Le fichier de langue est une liste de ces instructions. $translator->get($service,$code) récupère une chaîne de traduction. Le code $peut être numérique comme 1 ou une chaîne comme 'no_connection'. Il ne peut y avoir de conflit entre les services car chacun a son propre espace de noms dans la zone de données du traducteur.
Je poste ceci ici dans l'espoir qu'il sauvera quelqu'un la tâche de réinventer la roue comme je devais le faire il y a quelques longues années.
Je me suis posé des questions connexes encore et encore, puis je me suis perdu dans les langues formelles... mais juste pour vous aider un peu, je voudrais partager quelques résultats:
Je recommande de jeter un oeil à advanced CMS
Typo3
pour PHP
(je sais il y a beaucoup de choses, mais c'est celui que je pense est le plus mature)
Plone
dans Python
Si vous découvrez que le web en 2013 devrait fonctionner différemment, commencez à partir de zéro. Cela signifierait mettre sur pied une équipe de personnes hautement qualifiées / expérimentées pour construire un nouveau CMS. Peut-être que vous souhaitez jeter un oeil à polymère à cet effet.
S'il s'agit de codage et de sites Web multilingues / prise en charge des langues natives, je pense que chaque programmeur devrait avoir un indice sur unicode. Si vous ne connaissez pas unicode, vous allez certainement gâcher vos données. Ne pas aller avec les milliers de codes ISO. Ils ne vous épargneront que de la mémoire. Mais vous pouvez littéralement tout faire avec UTF-8 même stocker des caractères chinois. Mais pour que vous feriez besoin de stocker 2 ou 4 caractères d'octet qui en fait essentiellement un utf-16 ou utf-32.
S'il s'agit de l'encodage D'URL, encore une fois, vous ne devriez pas mélanger les encodages et être conscient qu'au moins pour le nom de domaine, il existe des règles définies par différents lobbies qui fournissent des applications comme un navigateur. par exemple, un Domaine pourrait être très similaire comme:
Ьankofamerica.com ou bankofamerica.com samesamebutdifferent;)
Bien sûr, vous avez besoin du système de fichiers pour fonctionner avec tous les encodages. Un autre, plus pour unicode utilisant le système de fichiers utf-8.
S'il s'agit de traductions, pensez à la structure des documents. par exemple un livre ou un article. Vous avez les spécifications docbook
à comprendre sur ces structures. Mais en HTML, c'est à peu près des blocs de contenu. Donc, vous souhaitez avoir une traduction à ce niveau, également au niveau de la page Web ou au niveau du domaine.
Donc, si un bloc n'existe pas, ce n'est tout simplement pas là, si une page Web n'existe pas, vous serez redirigé vers le niveau de navigation supérieur. Si un domaine doit être complètement différent dans la structure de navigation, alors.. c'est une structure différente complète à gérer.
Cela peut déjà être fait avec Typo3.
Si c'est à propos des frameworks, les plus matures que je connaisse, pour faire les choses générales comme MVC (mot à la mode je le déteste vraiment! Comme "performance" Si vous voulez vendre quelque chose, utilisez le mot performance et featurerich et vous vendez... que diable) est Zend
. Il s'est avéré être une bonne chose d'apporter des normes aux codeurs PHP chaos. Mais, typo3 a aussi un cadre en plus de la CMS. Récemment, il a été réaménagé et s'appelle flow3 maintenant. Les frameworks couvrent bien sûr l'abstraction de base de données, les modèles et les concepts de mise en cache, mais ont des points forts individuels.
Si c'est à propos de la mise en cache... cela peut être terriblement compliqué / multicouche. En PHP, vous penserez à accélératrice, opcode, mais aussi html, httpd, mysql, xml, css, js ... toutes sortes de caches. Bien sûr, certaines parties devraient être mises en cache et les parties dynamiques comme les réponses de blog ne devraient pas L'être demandé sur AJAX avec les URL générées. JSON, hashbangs etc.
Ensuite, vous souhaitez avoir un petit composant sur votre site web pour être accessible ou géré uniquement par certains utilisateurs , donc conceptuellement cela joue un grand rôle.
Aussi, vous souhaitez faire des statistiques , peut-être avoir un système distribué / un facebook de facebooks, etc. tout logiciel à construire sur le dessus de votre CMS sur le dessus ... vous avez donc besoin de différents types de bases de données inmemory, bigdata, xml, que ce soit.
Eh bien, je pense que c'est assez pour l'instant. Si vous n'avez pas entendu parler de typo3 / plone ou de frameworks mentionnés, vous avez assez à étudier. Sur ce chemin, vous trouverez beaucoup de solutions pour les questions que vous n'avez pas encore posées.
Si alors vous pensez, permet de faire un nouveau CMS parce que son 2013 et php est sur le point de mourir de toute façon, alors vous r bienvenue à rejoindre tout autre groupe de développeurs espérons ne pas se perdre.
Bonne chance!
Et btw. comment les gens vont ne plus avoir de sites web à l'avenir? et nous serons tous sur google+? J'espère que les développeurs deviennent un peu plus créatifs et font quelque chose d'utile (pour ne pas être assimilés par les borgle)
/ / / / modifier /// Juste une petite pensée pour votre application existante:
Si vous avez un CMS PHP mysql et que vous vouliez intégrer le support multilang. vous pouvez utiliser votre table avec une colonne supplémentaire pour n'importe quelle langue ou insérer la traduction avec un ID d'objet et un ID de langue dans la même table ou créer une table identique pour n'importe quelle langue et y insérer des objets, puis faire une union select si vous voulez les avoir tous affichés. Pour la base de données, utilisez utf8 general ci et bien sûr, dans le front / backend, utilisez utf8 text / encoding. J'ai utilisé des segments de chemin d'url pour les URL de la manière que vous avez déjà explanée comme
Domain.org/en/about vous pouvez mapper l'ID lang à votre table de contenu. quoi qu'il en soit vous devez avoir une carte des paramètres pour vos URL afin que vous souhaitiez définir un paramètre à être mappé à partir d'un pathsegment dans votre URL qui serait par exemple
Domain.org/en/about/employees/IT/administrators/
Configuration de recherche
Pageid| url
1 / / sur/employés/../..
1 | /../ sur / employés../../
Mapper les paramètres à l'url pathsegment ""
$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;
}
Par exemple, c'est déjà couvert dans le poste supérieur.
Et pour ne pas oublier, vous devez "réécrire" l'url de votre fichier php générateur qui serait dans la plupart des cas index.php
Travail de Base de données:
Créer une Table de langue 'langues':
Champs:
Language_id (primaire et auto incrémenté)
Language_name
Created_at
Created_by
Updated_at
Updated_by
Créer une table dans la base de données "contenu":
Champs:
Content_id (primaire et auto increamented)
Main_content
Header_content
Footer_content
Leftsidebar_content
Rightsidebar_content
Language_id (clé étrangère: référencée à la table des langues)
Created_at
Created_by
Updated_at
Updated_by
Travail Frontal:
Lorsque l'utilisateur sélectionne une langue dans la liste déroulante ou une zone, enregistrez l'id de langue sélectionné dans la session comme,
$_SESSION['langue']=1;
Récupère maintenant les données de la table de base de données ‘content’ en fonction de l'id de langue stocké dans la session.
Détail peut être trouvé ici http://skillrow.com/multilingual-website-in-php-2/
Comme une personne qui vit au Québec où presque tout le site est français et anglais... j'ai essayé beaucoup sinon le plugin le plus multilingue pour WP... celui une seule solution utile qui fonctionne nive avec tout mon site est mQtranslate... je vis et je meurs avec !
Une option très simple qui fonctionne avec n'importe quel site Web où vous pouvez télécharger Javascript est www.multilingualizer.com
Il vous permet de mettre tout le texte pour toutes les langues sur une page, puis cache les langues que l'utilisateur n'a pas besoin de voir. Fonctionne bien.
Qu'en est-il de WORDPRESS + MULTI-LANGUAGE SITE BASIS
(plugin)?
le site aura une structure:
- example.com/fra/category1/....
- example.com/fra/ma-page....
- example.com/ rus / Category 1/....
- example.com/rus/ma-page....
Le plugin fournit une Interface pour la traduction de toutes les phrases, avec une logique simple:
(ENG) my_title - "Hello user"
(SPA) my_title - "Holla usuario"
Alors il peut être sorti:echo translate('my_title', LNG); // LNG is auto-detected
P. s. Cependant, vérifiez, si le plugin est toujours actif.