Sitio web en varios idiomas de las mejores prácticas


He estado luchando con esta pregunta durante varios meses, pero no he estado en una situación en la que tuviera que explorar todas las opciones posibles antes. En este momento, siento que es hora de conocer las posibilidades y crear mi propia preferencia personal para usar en mis próximos proyectos.

Permítanme primero bosquejar la situación que estoy buscando

Estoy a punto de actualizar/volver a desarrollar un sistema de gestión de contenido que he estado usando desde hace bastante tiempo. Sin embargo, me siento multi idioma es una gran mejora para este sistema. Antes no usaba ningún framework pero voy a usar Laraval4 para el próximo proyecto. Laravel parece la mejor opción de una forma más limpia de codificar PHP. Sidenote: Laraval4 should be no factor in your answer. Estoy buscando formas generales de traducción que sean independientes de la plataforma/marco.

Lo que se debe traducir

Como el sistema que estoy buscando debe ser lo más fácil de usar posible, el método de gestión de la traducción debería estar dentro del CMS. No debería ser necesario iniciar una conexión FTP para modificar archivos de traducción o cualquier plantilla de html/php.

Además, estoy buscando la forma más fácil de traducir varias tablas de bases de datos, tal vez sin la necesidad de hacer tablas adicionales.

¿Qué se me ocurrió a mí mismo

Como he estado buscando, leyendo y probando cosas yo mismo ya. Hay un par de opciones que tengo. Pero todavía no siento que haya alcanzado un método de mejor práctica para lo que realmente estoy buscando. En este momento, esto es lo que he llegado con, pero este método también tiene efectos secundarios.

  1. PHP Parsed Templates: el sistema de plantillas debe ser analizado por PHP. De esta manera puedo insertar los parámetros traducidos en el HTML sin tener que abrir las plantillas y modificarlas. Además de eso, PHP parsed templates me da la capacidad de tener 1 plantilla para el sitio web completo en lugar de tener una subcarpeta para cada idioma (que he tenido antes). El método para alcanzar este objetivo puede ser Smarty, TemplatePower, Laravel's Blade o cualquier otro analizador de plantillas. Como he dicho, esto debe ser independiente de la solución escrita.
  2. Database Driven: quizás no necesite mencionar esto de nuevo. Pero la solución debe estar basada en bases de datos. El CMS está dirigido a ser orientado a objetos y MVC, por lo que tendría que pensar en una estructura de datos lógica para las cadenas. Como mis plantillas serían estructurado: plantillas/Controlador/Vista.php quizás esta estructura tendría más sentido: Controller.View.parameter. La tabla de la base de datos tendría estos campos un largo con un campo value. Dentro de las plantillas podríamos usar algún método de ordenación como echo __('Controller.View.welcome', array('name', 'Joshua')) y el parámetro contiene Welcome, :name. Así el resultado es Welcome, Joshua. Esto parece una buena manera de hacer esto, porque los parámetros tales como :nombre son fáciles de entender por el editor.
  3. Carga de base de datos baja: Por supuesto, el sistema anterior causaría cargas de carga de la base de datos si estas cadenas se cargan sobre la marcha. Por lo tanto, necesitaría un sistema de almacenamiento en caché que vuelva a renderizar los archivos de idioma tan pronto como se editen/guarden en el entorno de administración. Debido a que se generan archivos, también se necesita un buen diseño del sistema de archivos. Supongo que podemos ir con languages/en_EN/Controller/View.php o .ini, lo que más te convenga. Tal vez una.ini se analiza incluso más rápido al final. Este fould debe contener los datos en el format parameter=value; . Supongo que esta es la mejor manera de hacer esto, ya que cada vista que se representa puede incluir su propio archivo de idioma si existe. Los parámetros de idioma deben cargarse en una vista específica y no en un ámbito global para evitar que los parámetros se sobrescriban entre sí.
  4. Traducción de tablas de base de datos: de hecho, esto es lo que más me preocupa. Estoy buscando una manera de crear traducciones de Noticias / Páginas / etc. lo más rápido posible. Tener dos tablas para cada módulo (por ejemplo News y News_translations) es una opción, pero se siente mucho trabaja para conseguir un buen sistema. Una de las cosas que se me ocurrió se basa en un sistema data versioning que escribí: hay un nombre de tabla de base de datos Translations, esta tabla tiene una combinación única de language, tablename y primarykey. Por ejemplo: en_Es / News / 1 (Refiriéndose a la versión en inglés de la noticia con ID=1). Pero hay 2 enormes desventajas de este método: en primer lugar, esta tabla tiende a ser bastante larga con una gran cantidad de datos en la base de datos y, en segundo lugar, sería un trabajo infernal usar esta configuración para busca en la mesa. Por ejemplo, buscar la babosa SEO del elemento sería una búsqueda de texto completo, lo cual es bastante tonto. Pero por otro lado: es una forma rápida de crear contenido traducible en cada tabla muy rápido, pero no creo que este pro supere a los con.
  5. Trabajo Front-end: También el front-end necesitaría un poco de reflexión. Por supuesto, almacenaríamos los idiomas disponibles en una base de datos y (de)activa los que necesitamos. De esta manera, el script puede generar un menú desplegable para seleccionar un idioma y el back-end pueden decidir automáticamente qué traducciones se pueden hacer usando el CMS. El idioma elegido (por ejemplo, en_EN) se usaría cuando se obtiene el archivo de idioma para una vista o para obtener la traducción correcta para un elemento de contenido en el sitio web.

Así que, ahí están. Mis ideas hasta ahora. Aún no incluyen opciones de localización para fechas, etc., pero como mi servidor admite PHP5.3. 2+, la mejor opción es usar la extensión intl como se explica aquí: http://devzone.zend.com/1500/internationalization-in-php-53 / - pero esto sería útil en cualquier estadio posterior de desarrollo. Por ahora, el problema principal es cómo tener las mejores prácticas de traducción del contenido en un sitio web.

Además de todo lo que he explicado aquí, todavía tengo otra cosa que aún no he decidido, parece una pregunta simple, pero de hecho me ha estado dando dolores de cabeza:{[27]]}

¿Traducción de URL? ¿Deberíamos hacer esto o no? y en qué ¿camino?

So.. si tengo esta url: http://www.domain.com/about-us y el inglés es mi idioma predeterminado. ¿Debería traducirse esta URL a http://www.domain.com/over-ons cuando elijo holandés como mi idioma? O deberíamos ir por el camino fácil y simplemente cambiar el contenido de la página visible en /about. Lo último no parece una opción válida porque eso generaría múltiples versiones de la misma URL, esta indexación del contenido fallará de la manera correcta.

Otra opción es usar http://www.domain.com/nl/about-us en su lugar. Esto genera al menos un URL única para cada contenido. También esto sería más fácil ir a otro idioma, por ejemplo http://www.domain.com/en/about-us y la URL proporcionada es más fácil de entender tanto para Google como para los visitantes humanos. Usando esta opción, ¿qué hacemos con los idiomas por defecto? ¿Debería el idioma predeterminado eliminar el idioma seleccionado por defecto? Así que redirigiendo http://www.domain.com/en/about-us a http://www.domain.com/about-us... En mi opinión, esta es la mejor solución, porque cuando el CMS está configurado para un solo idioma, no es necesario tener esta identificación de idioma en el URL.

Y una tercera opción es una combinación de ambas opciones: usando la URL "language-identification-less"(http://www.domain.com/about-us) para el idioma principal. Y utilizar una URL con una slug SEO traducida para sublenguajes: http://www.domain.com/nl/over-ons & http://www.domain.com/de/uber-uns

Espero que mi pregunta haga que sus cabezas se rompan, ¡ellos rompieron la mía con seguridad! Ya me ayudó a resolver las cosas como una pregunta aquí. Me dio la posibilidad de revisar los métodos que he utilizado antes y la idea que estoy teniendo para mi próximo CMS.

I quisiera agradecer a usted ya por tomarse el tiempo para leer este montón de texto!

// Edit #1:

Olvidé mencionar: la función __() es un alias para traducir una cadena dada. Dentro de este método, obviamente, debe haber algún tipo de método alternativo donde se carga el texto predeterminado cuando aún no hay traducciones disponibles. Si falta la traducción, debe insertarse o el archivo de traducción debe regenerarse.

Author: Anthony, 2013-10-08

13 answers

Premisa del tema

Hay tres aspectos distintos en un sitio multilingüe:{[34]]}

  • traducción de interfaz
  • contenido
  • enrutamiento de url

Si bien todos se interconectan de diferentes maneras, desde el punto de vista de CMS se administran utilizando diferentes elementos de interfaz de usuario y se almacenan de manera diferente. Parece estar seguro de su implementación y comprensión de los dos primeros. La pregunta era sobre el último aspecto - " URL Translation? Deber hacemos esto o no? y ¿de qué manera?"

¿De qué se puede hacer la URL?

Una cosa muy importante es, no te pongas elegante con IDN. En su lugar favor transliteración (también: transcripción y romanización). Si bien a primera vista IDN parece una opción viable para URLs internacionales, en realidad no funciona como se anuncia por dos razones:

  • algunos navegadores convertirán los caracteres no ASCII como 'ч' o 'ž' en '%D1%87' y '%C5%BE'
  • si el usuario tiene temas personalizados, la fuente del tema es muy probable que no tenga símbolos para esas letras

En realidad traté de IDN enfoque hace unos años en un proyecto basado en Yii (horrible framework, IMHO). Me encontré con los dos problemas antes mencionados antes de raspar esa solución. Además, sospecho que podría ser un vector de ataque.

Opciones Disponibles ... como yo los veo.

Básicamente tienes dos opciones, que podrían resumirse como:{[34]]}

  • http://site.tld/[:query]: donde [:query] determina tanto el idioma como la elección de contenido

  • http://site.tld/[:language]/[:query]: donde [:language] parte de la URL define la elección del idioma y [:query] se utiliza solo para identificar el contenido

La consulta es Α y Ω ..

Digamos que eliges http://site.tld/[:query].

En ese caso tienes una fuente primaria de lenguaje: el contenido del segmento [:query]; y dos fuentes adicionales:

  • valor $_COOKIE['lang'] para ese navegador en particular
  • lista de idiomas en HTTP Accept-Language (1), (2) header

Primero, necesita hacer coincidir la consulta con uno de los patrones de enrutamiento definidos (si su elección es Laravel, entonces lea aquí). En la coincidencia exitosa del patrón, entonces debe encontrar el idioma.

Tendrías que ir a través de todos los segmentos del patrón. Encuentre las traducciones potenciales para todos esos segmentos y determine qué idioma se utilizó. Las dos fuentes adicionales (cookie y header) se usaría para resolver conflictos de enrutamiento, cuando (no "si") surgen.

Tomemos, por ejemplo: http://site.tld/blog/novinka.

Es la transliteración de "блог, новинка", que en inglés significa aproximadamente "blog", "latest".

Como ya puedes notar, en ruso "блог" se transliterará como "blog". Lo que significa que para la primera parte de [:query] usted (en el mejor escenario) terminará con ['en', 'ru'] lista de posibles idiomas. Luego tomas el siguiente segmento - "novinka". Que podría tener sólo un idioma en la lista de posibilidades: ['ru'].

Cuando la lista tiene un elemento, ha encontrado correctamente el idioma.

Pero si terminas con 2 (ejemplo: ruso y ucraniano) o más posibilidades .. o 0 posibilidades, como un caso podría ser. Usted tendrá que utilizar cookie y / o encabezado para encontrar la opción correcta.

Y si todo lo demás falla, elige el idioma predeterminado del sitio.

Lenguaje como parámetro

La alternativa es usar URL, que se puede definir como http://site.tld/[:language]/[:query]. En este caso, al traducir query, no necesita adivinar el idioma, porque en ese momento ya sabe cuál usar.

También hay una fuente secundaria de lenguaje: el valor de la cookie. Pero aquí no tiene sentido jugar con el encabezado Accept-Language, porque no está tratando con una cantidad desconocida de idiomas posibles en caso de "arranque en frío" (cuando el usuario abre el sitio por primera vez con una consulta personalizada).

En su lugar tienes 3 simple, priorizado opciones:

  1. si [:language] segmento está establecido, utilícelo
  2. si $_COOKIE['lang'] está configurado, utilícelo
  3. use el lenguaje predeterminado

Cuando tenga el idioma, simplemente intente traducir la consulta, y si la traducción falla, use el "valor predeterminado" para ese segmento en particular (basado en los resultados de enrutamiento).

¿No hay aquí una tercera opción?

Sí, técnicamente se pueden combinar ambos enfoques, pero eso complicaría el proceso y solo acomodaría a las personas que quieren cambiar manualmente la URL de http://site.tld/en/news a http://site.tld/de/news y esperan que la página de noticias cambie a alemán.

Pero incluso este caso podría probablemente mitigarse usando el valor de la cookie (que contendría información sobre la elección previa del lenguaje), para implementarlo con menos magia y esperanza.

¿Qué enfoque utilizar?

Como ya adivinado, yo recomendaría http://site.tld/[:language]/[:query] como la opción más sensata.

También en la situación real de la palabra que tendría 3a parte importante en la URL: "titular". Como en el nombre del producto en la tienda en línea o el titular del artículo en el sitio de noticias.

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

En este caso '/news/article/121415' sería la consulta, y el 'EU-as-global-reserve-currency' es el título. Puramente para fines de SEO.

Se Puede hacer en Laravel?

Un poco, pero no por defecto.

No estoy muy familiarizado con él, pero por lo que he visto, Laravel utiliza un mecanismo de enrutamiento simple basado en patrones. Para implementar URLs multilingües probablemente tendrá que extender core class(es), porque el enrutamiento multilingüe necesita acceso a diferentes formas de almacenamiento (base de datos, caché y/o archivos de configuración).

Está enrutado. ¿Ahora qué?

Como resultado de todo, terminaría con dos piezas valiosas de información: el idioma actual y los segmentos de consulta traducidos. Estos valores se pueden usar para enviar a la(s) clase (es) que producirá el resultado.

Básicamente, la siguiente URL: http://site.tld/ru/blog/novinka (o la versión sin '/ru') obtiene se convirtió en algo como

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

Que solo se utiliza para el envío:

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

.. o alguna variación de la misma, dependiendo de la implementación particular.

 99
Author: tereško,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-17 16:53:11

Implementando i18n Sin El Golpe de Rendimiento Usando un Pre-Procesador como sugiere Thomas Bley

En el trabajo, recientemente pasamos por la implementación de i18n en un par de nuestras propiedades, y una de las cosas con las que seguimos luchando fue el éxito de rendimiento de lidiar con la traducción sobre la marcha, entonces descubrí esta gran entrada de blog de Thomas Bley que inspiró la forma en que estamos usando i18n para manejar grandes cargas de tráfico con un rendimiento mínimo cuestión.

En lugar de llamar a funciones para cada operación de traducción, que como sabemos en PHP es costosa, definimos nuestros archivos base con marcadores de posición, luego usamos un preprocesador para almacenar en caché esos archivos (almacenamos el tiempo de modificación del archivo para asegurarnos de que estamos sirviendo el contenido más reciente en todo momento).

Las Etiquetas de traducción

Thomas usa etiquetas {tr} y {/tr} para definir dónde comienzan y terminan las traducciones. Debido al hecho de que estamos usando RAMITA, no queremos usar { para evitar confusiones usamos [%tr%] y [%/tr%] en su lugar. Básicamente, esto se ve así:

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

Tenga en cuenta que Thomas sugiere usar el inglés base en el archivo. No hacemos esto porque no queremos tener que modificar todos los archivos de traducción si cambiamos el valor en inglés.

Los archivos INI

Luego, creamos un archivo INI para cada idioma, en el formato placeholder = translated:

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

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

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

Sería trivial permitir a un usuario modificarlos dentro del CMS, simplemente obtener los pares de teclas por un preg_split en \n o = y haciendo que el CMS sea capaz de escribir en los archivos INI.

El Componente Preprocesador

Esencialmente, Thomas sugiere usar una función de 'compilador' justo a tiempo (aunque, en realidad, es un preprocesador) como esta para tomar sus archivos de traducción y crear archivos PHP estáticos en el disco. De esta manera, esencialmente almacenamos en caché nuestros archivos traducidos en lugar de llamar a una función de traducción para cada cadena en el archivo:

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

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

Nota: Yo no verifique que la expresión regular funciona, no la copié del servidor de nuestra compañía, pero puede ver cómo funciona la operación.

Cómo llamarlo

Nuevamente, este ejemplo es de Thomas Bley, no de mí:{[29]]}

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

Almacenamos el idioma en una cookie (o variable de sesión si no podemos obtener una cookie) y luego la recuperamos en cada solicitud. Podría combinar esto con un parámetro opcional $_GET para anular el idioma, pero no sugiero subdominio por idioma o página por idioma porque hará que sea más difícil ver qué páginas son populares y reducirá el valor de los enlaces entrantes, ya que los tendrás menos difundidos.

¿Por qué usar este método?

Nos gusta este método de preprocesamiento por tres razones:

  1. La enorme ganancia de rendimiento de no llamar a un montón de funciones para el contenido que rara vez cambia (con este sistema, los visitantes de 100k en francés solo terminarán ejecutando el reemplazo de traducción una vez).
  2. No agregue cualquier carga a nuestra base de datos, ya que utiliza archivos planos simples y es una solución de PHP puro.
  3. La capacidad de usar expresiones PHP dentro de nuestras traducciones.

Cómo traducir el Contenido de la Base de Datos

Simplemente agregamos una columna para el contenido en nuestra base de datos llamada language, luego usamos un método de acceso para la constante LANG que definimos anteriormente, por lo que nuestras llamadas SQL (usando ZF1, lamentablemente) se ven así:

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

Nuestros artículos tienen una clave primaria compuesta sobre id y language así que el artículo 54 puede existir en todos los idiomas. Nuestro LANG por defecto es en_US si no se especifica.

URL Slug Translation

Combinaría dos cosas aquí, una es una función en su bootstrap que acepta un parámetro $_GET para el lenguaje y anula la variable cookie, y otra es enrutamiento que acepta múltiples slugs. Entonces puedes hacer algo como esto en tu enrutamiento:

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

Estos podrían almacenarse en un archivo plano que podría escribirse fácilmente a desde su panel de administración. JSON o XML pueden proporcionar una buena estructura para soportarlos.

Notas Sobre Algunas Otras Opciones

Traducción al vuelo basada en PHP

No veo que estos ofrezcan ninguna ventaja sobre las traducciones preprocesadas.

Traducciones basadas en Front-end

Durante mucho tiempo he encontrado estos interesantes, pero hay algunas advertencias. Por ejemplo, usted tiene que poner a disposición del usuario toda la lista de frases en su sitio web que planea traducir, esto podría ser problemático si hay áreas del sitio que mantiene ocultas o no les ha permitido acceder.

También tendría que asumir que todos sus usuarios están dispuestos y pueden usar Javascript en su sitio, pero según mis estadísticas, alrededor del 2.5% de nuestros usuarios se ejecutan sin él (o usan Noscript para bloquear nuestros sitios de usarlo).

Traducciones basadas en Bases de datos

Velocidades de conectividad de bases de datos de PHP no hay nada que escribir en casa, y esto se suma a la ya alta sobrecarga de llamar a una función en cada frase para traducir. Los problemas de rendimiento y escalabilidad parecen abrumadores con este enfoque.

 47
Author: Glitch Desire,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-17 12:44:04

Le sugiero que no invente una rueda y use la lista de abreviaturas de idiomas gettext e ISO. ¿Has visto cómo se implementó i18n / l10n en CMSes o frameworks populares?

Usando gettext tendrás una poderosa herramienta donde muchos de los casos ya están implementados como formas plurales de números. En inglés solo tienes 2 opciones: singular y plural. Pero en ruso, por ejemplo, hay 3 formas y no es tan simple como en inglés.

También muchos traductores ya tienen experiencia para trabajar con gettext.

Echa un vistazo a CakePHP o Drupal . Ambos multilingües habilitados. CakePHP como ejemplo de localización de interfaz y Drupal como ejemplo de traducción de contenido.

Para l10n usar la base de datos no es el caso en absoluto. Será un montón de preguntas. El enfoque estándar es obtener todos los datos l10n en memoria en una etapa temprana (o durante la primera llamada a la función i10n si prefiere carga lenta). Puede ser de lectura .archivo po o desde la base de datos todos los datos a la vez. Y que solo leer cadenas solicitadas de array.

Si necesita implementar la herramienta en línea para traducir la interfaz, puede tener todos los datos en la base de datos, pero aún así guardar todos los datos en un archivo para trabajar con ella. Para reducir la cantidad de datos en la memoria, puede dividir todos sus mensajes/cadenas traducidos en grupos y luego cargar solo los grupos que necesite si es posible.

Así que tienes toda la razón en tu #3. Con una excepción: normalmente es un archivo grande, no un archivo por controlador o algo así. Porque es mejor para rendimiento para abrir un archivo. Probablemente sepa que algunas aplicaciones web de alta carga compilan todo el código PHP en un archivo para evitar operaciones de archivo cuando se llama include/require.

Acerca de las URLs. Google sugiere indirectamente usar la traducción:

Para indicar claramente el contenido francés: http://example.ca/fr/vélo-de-montagne.html

También creo que necesita redirigir al usuario al prefijo de idioma predeterminado, por ejemplo, http://examlpe.com/about-us will redirecciones a http://examlpe.com/en/about-us Pero si su sitio utiliza solo un idioma, por lo que no necesita prefijos en absoluto.

Echa un vistazo: 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

Traducir contenido es una tarea más difícil. Creo que habrá algunas diferencias con diferentes tipos de contenido, por ejemplo, artículos, elementos de menú, etc. Pero en el #4 estás en el camino correcto. Echa un vistazo en Drupal para tener más ideas. Tiene un esquema de base de datos lo suficientemente claro y una interfaz lo suficientemente buena para traducir. Al igual que la creación de artículo y seleccione el idioma para él. Y luego puedes traducirlo a otros idiomas.

Interfaz de traducción Drupal

Creo que no es un problema con los slugs de URL. Puede crear una tabla separada para las babosas y será la decisión correcta. También usando índices correctos no es problema consultar la tabla incluso con una gran cantidad de datos. Y no era una búsqueda de texto completo, sino una coincidencia de cadena si usará el tipo de datos varchar para slug y también puede tener un índice en ese campo.

PS Lo siento, mi inglés está lejos de ser perfecto.

 12
Author: Yaroslav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-11 03:05:14

Depende de la cantidad de contenido que tenga su sitio web. Al principio usé una base de datos como todas las demás personas aquí, pero puede llevar mucho tiempo escribir todos los trabajos de una base de datos. No digo que este sea un método ideal y especialmente si tienes mucho texto, pero si quieres hacerlo rápido sin usar una base de datos, este método podría funcionar, sin embargo, no puedes permitir que los usuarios ingresen datos que se utilizarán como archivos de traducción. Pero si agrega las traducciones usted mismo, lo hará trabajo:

Digamos que tienes este texto:

Welcome!

Puede introducir esto en una base de datos con traducciones, pero también puede hacer esto:

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

Ahora, si su sitio web utiliza una cookie, tiene esto por ejemplo:

$_COOKIE['language'];

Para que sea fácil vamos a transformarlo en un código que se puede utilizar fácilmente:

$language=$_COOKIE['language'];

Si su lenguaje de cookies es galés y tiene este fragmento de código:

echo $welcome[$language];

El resultado de esto será:

Croeso!

Si necesita agregar una gran cantidad de las traducciones para su sitio web y una base de datos son demasiado consumidoras, el uso de una matriz puede ser una solución ideal.

 8
Author: user3749746,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-29 23:35:47

Le sugeriré que no dependa realmente de la base de datos para la traducción, podría ser realmente una tarea complicada y podría ser un problema extremo en el caso de la codificación de datos.

Me enfrenté a un problema similar hace tiempo y escribí la siguiente clase para resolver mi problema

Objeto: Locale \ Locale

<?php

  namespace Locale;

  class Locale{

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

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

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

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

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

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

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

    return $this;
}

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

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

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

    return $this;
}

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

    return false;
}

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

    return array();
}

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

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

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

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

    $this->init_locale();
}

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

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

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

        if ($country) {

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

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

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

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

            foreach ($languages as $lang) {

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

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

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

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

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

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

                    return;
                }
            }
        }
    }

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

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

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

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

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

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

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

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

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

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

    return $translation;
  }
}

Uso

 <?php
    ## /locale/en.php

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

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

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

Cómo funciona

{a:1} se sustituye por el 1er argumento pasado al método Locale::translate('key_name','arg1') {a:2} se sustituye por el segundo argumento pasado al método Locale::translate('key_name','arg1','arg2')

Cómo la detección funciona

  • Por defecto si geoip está instalado entonces devolverá el código de país por geoip_country_code_by_name y si geoip no está instalado el respaldo a HTTP_ACCEPT_LANGUAGE header
 5
Author: Shushant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-16 07:15:42

Solo una respuesta secundaria: Utilice absolutamente urls traducidas con un identificador de idioma delante de ellas: http://www.domain.com/nl/over-ons
Las soluciones Hybride tienden a complicarse, así que me quedaría con ellas. ¿Por qué? Porque la url es esencial para el SEO.

Acerca de la traducción de la base de datos: ¿Es el número de idiomas más o menos fijo? O más bien impredecible y dinámico? Si es fijo, simplemente agregaría nuevas columnas, de lo contrario iría con varias tablas.

Pero en general, ¿por qué no usar Drupal? Sé que todo el mundo quiere construir su propio CMS porque es más rápido, más delgado, etc. sucesivamente. Pero eso es realmente una mala idea!

 4
Author: Remy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-09 07:08:56

Tuve el mismo probem hace un tiempo, antes de comenzar a usar Symfony framework.

  1. Simplemente use una función _ _ () que tenga arámeters pageId (o ObjectId, objectTable descrito en #2), idioma de destino y un parámetro opcional de idioma de reserva (predeterminado). El lenguaje por defecto se podría establecer en alguna configuración global para tener una forma más fácil de cambiarlo más tarde.

  2. Para almacenar el contenido en la base de datos utilicé la siguiente estructura: (pageId, language, contenido, variables).

    • PageId sería un FK a su página que desea traducir. si tiene otros objetos, como noticias, galerías o lo que sea, simplemente divídalo en 2 campos ObjectId, objectTable.

    • Idioma-obviamente almacenaría la cadena de idioma ISO en_EN, LT_lt, en_US, etc.

    • Contenido: el texto que desea traducir junto con los comodines para la sustitución de variables. Ejemplo "Hola mr. %%nombre%%. El saldo de su cuenta es %%equilibrio%%."

    • Variables-las variables codificadas json. PHP proporciona funciones para analizarlas rápidamente. Ejemplo "nombre: Laurynas, balance: 15.23".

    • También mencionaste campo de babosas. puede agregarlo libremente a esta tabla solo para tener una forma rápida de buscarlo.

  3. Las llamadas a la base de datos deben reducirse al mínimo con el almacenamiento en caché de las traducciones. Debe ser almacenado en una matriz PHP, porque es la estructura más rápida en el lenguaje PHP. Cómo hará que este almacenamiento en caché depende de usted. Según mi experiencia, debería tener una carpeta para cada idioma soportado y una matriz para cada pageId. La caché debe reconstruirse después de actualizar la traducción. SOLO la matriz cambiada debe ser regenerada.

  4. Creo que respondí que en #2

  5. Tu idea es perfectamente lógica. este es bastante simple y creo que no te causará ningún problema.

Las URL deben traducirse utilizando las slugs almacenadas en el tabla de traducción.

Palabras Finales

Siempre es bueno investigar las mejores prácticas, pero no reinventar la rueda. simplemente toma y usa los componentes de frameworks conocidos y úsalos.

Echa un vistazo a Symfony translation component. Podría ser una buena base de código para ti.

 4
Author: Laurynas Mališauskas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-12 10:54:46

No voy a intentar refinar las respuestas ya dadas. En su lugar, le hablaré sobre la forma en que mi propio marco PHP OOP maneja las traducciones.

Internamente, mi framework usa códigos como en, fr, es, cn y así sucesivamente. Un array contiene los idiomas soportados por el sitio web: array ('en', 'fr', 'es','cn') El código de idioma se pasa a través de $_GET (lang = fr) y si no se pasa o no es válido, se establece en el primer idioma de la matriz. Así que en cualquier momento durante la ejecución del programa y desde el al principio, se conoce el idioma actual.

Es útil entender el tipo de contenido que necesita ser traducido en una aplicación típica:

1) mensajes de error de clases (o código de procedimiento) 2) mensajes sin errores de clases (o código de procedimiento) 3) contenido de la página (generalmente se almacena en una base de datos) 4) cadenas de todo el sitio (como el nombre del sitio web) 5) cadenas específicas del script

El primer tipo es fácil de entender. Básicamente, estamos hablando de mensajes como " no podría conéctese a la base de datos ...". Estos mensajes solo necesitan cargarse cuando se produce un error. Mi clase manager recibe una llamada de las otras clases y usando la información pasada como parámetros simplemente va a la carpeta de clase relevante y recupera el archivo de error.

El segundo tipo de mensaje de error se parece más a los mensajes que recibe cuando la validación de un formulario salió mal. ("No puedes irte ... en blanco " o "por favor, elija una contraseña con más de 5 caracteres"). Las cuerdas necesitan ser cargado antes de que se ejecute la clase.Sé lo que es

Para el contenido real de la página, utilizo una tabla por idioma, cada tabla precedida por el código para el idioma. Así que en_content es la tabla con contenido en inglés, es_content es para españa, cn_content para China y fr_content es el material francés.

El cuarto tipo de cadena es relevante en todo su sitio web. Esto se carga a través de un archivo de configuración llamado usando el código para el lenguaje, que es en_lang.php, es_lang.php y así en. En el archivo de idioma global tendrá que cargar los idiomas traducidos como array ('Inglés', 'Chino','Español','Francés') en el archivo global inglés y array ('Anglais', 'Chinois', 'Español', 'Francais') en el archivo francés. Así que cuando rellenas un menú desplegable para la selección de idioma, está en el idioma correcto;)

Finalmente tienes las cadenas específicas del script. Así que si escribes una aplicación de cocina, podría ser "Tu horno no estaba lo suficientemente caliente".

En mi solicitud ciclo, el archivo de idioma global se carga primero. Allí encontrará no solo cadenas globales (como "Sitio web de Jack"), sino también configuraciones para algunas de las clases. Básicamente cualquier cosa que dependa del idioma o la cultura. Algunas de las cadenas incluyen máscaras para fechas (MMDDYYYY o DDMMYYYY), o códigos de lenguaje ISO. En el archivo de idioma principal, incluyo cadenas para clases individuales porque hay muy pocas de ellas.

El segundo y último archivo de idioma que se lee desde el disco es el archivo de idioma del script. lang_en_home_welcome.php es el archivo de idioma para el script home/welcome. Un script está definido por un modo (inicio) y una acción (bienvenida). Cada script tiene su propia carpeta con archivos de configuración y lang.

El script extrae el contenido de la base de datos nombrando la tabla de contenido como se explicó anteriormente.

Si algo sale mal, el administrador sabe dónde obtener el archivo de error dependiente del idioma. Ese archivo solo se carga en caso de error.

Así que el la conclusión es obvia. Piense en los problemas de traducción antes de comenzar a desarrollar una aplicación o marco. También necesita un flujo de trabajo de desarrollo que incorpore traducciones. Con mi framework, desarrollo todo el sitio en inglés y luego traduzco todos los archivos relevantes.

Solo una palabra final rápida sobre la forma en que se implementan las cadenas de traducción. Mi framework tiene un único global, el manager manager, que ejecuta servicios disponibles para cualquier otro servicio. Así, por ejemplo, la forma service se apodera del servicio html y lo usa para escribir el html. Uno de los servicios de mi sistema es el servicio de traductor. translator translator- > set (service service, code code, string string) establece una cadena para el idioma actual. El archivo de idioma es una lista de tales declaraciones. translator translator - > get (service service, code code) recupera una cadena de traducción. El $código puede ser numérico, como 1 o una cadena como 'no_connection'. No puede haber conflicto entre servicios porque cada uno tiene su propio espacio de nombres en el área de datos del traductor.

Publico esto aquí con la esperanza de que le ahorre a alguien la tarea de reinventar la rueda como tuve que hacer hace unos años.

 3
Author: JG Estiot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-16 05:03:18

Me he estado haciendo preguntas relacionadas una y otra vez, luego me perdí en los idiomas formales... pero solo para ayudarte un poco, me gustaría compartir algunos hallazgos:

Recomiendo echar un vistazo a advanced CMS

Typo3 para PHP (Sé que hay un montón de cosas, pero eso es lo que creo que es más maduro)

Plone en Python

Si descubres que la web en 2013 debería funcionar diferente, entonces, comienza desde cero. Eso significaría armar un equipo de personas altamente capacitadas / experimentadas para construir un nuevo CMS. Puede ser que le gustaría dar un vistazo a polímero para ese propósito.

Si se trata de codificación y soporte de sitios web multilingües / idioma nativo, creo que cada programador debería tener una pista sobre unicode. Si no conoces unicode seguramente estropearás tus datos. No vaya con los miles de códigos ISO. Sólo te ahorrarán algo de memoria. Pero puedes hacer literalmente todo con UTF-8 incluso almacenar caracteres chinos. Pero para eso tú necesita almacenar caracteres de 2 o 4 bytes que lo convierten básicamente en utf-16 o utf-32.

Si se trata de codificación de URL, de nuevo no debe mezclar codificaciones y tenga en cuenta que al menos para el nombre de dominio hay reglas definidas por diferentes lobbies que proporcionan aplicaciones como un navegador. por ejemplo, un dominio podría ser muy similar como:

Ьankofamerica.com o bankofamerica.com samesamebutdifferent ;)

Por supuesto que necesita el sistema de archivos para trabajar con todas las codificaciones. Otra ventaja para unicode usando el sistema de archivos utf-8.

Si se trata de traducciones, piense en la estructura de los documentos. por ejemplo, un libro o un artículo. Usted tiene las especificaciones docbook para entender acerca de esas estructuras. Pero en HTML se trata de bloques de contenido. Así que te gustaría tener una traducción en ese nivel, también a nivel de página web o nivel de dominio. Entonces, si un bloque no existe, simplemente no existe, si una página web no existe, se le redirigirá al nivel de navegación superior. Si un dominio debe ser completamente diferente en la estructura de navegación, entonces.. es una estructura completamente diferente para manejar. Esto ya se puede hacer con Typo3.

Si se trata de frameworks, los más maduros que conozco, para hacer las cosas generales como MVC(palabra de moda que realmente odio! Como "rendimiento" Si quieres vender algo, usa la palabra rendimiento y featurerich y vendes... qué demonios) es Zend. Ha demostrado ser una buena cosa para traer estándares a los codificadores de caos php. Pero, typo3 también tiene un marco además del CMS. Recientemente se ha rediseñado y se llama flow3 now. Los frameworks, por supuesto, cubren la abstracción de bases de datos, plantillas y conceptos para el almacenamiento en caché, pero tienen fortalezas individuales.

Si se trata de almacenamiento en caché... eso puede ser terriblemente complicado / multicapa. En PHP pensarás en accellerator, opcode, pero también html, httpd, mysql, xml, css, js ... cualquier tipo de cachés. Por supuesto, algunas partes deben almacenarse en caché y las partes dinámicas, como las respuestas del blog, no. solicitado a través de AJAX con urls generadas. JSON, hashbangs etc.

Entonces, te gustaría tener cualquier pequeño componente en tu sitio web para ser accedido o administrado solo por ciertos usuarios , por lo que conceptualmente eso juega un papel importante.

También te gustaría hacer estadísticas , tal vez tener un sistema distribuido / un facebook de facebooks, etc. cualquier software para ser construido en la parte superior de su sobre el cms superior ... así que necesita diferentes tipos de bases de datos inmemory, bigdata, xml, lo que sea.

Bueno, creo que eso es suficiente por ahora. Si usted no ha oído hablar de cualquiera de typo3 / plone o marcos mencionados, usted tiene suficiente para estudiar. En ese camino encontrarás muchas soluciones para preguntas que aún no has hecho.

Si entonces piensas, vamos a hacer un nuevo CMS porque su 2013 y php está a punto de morir de todos modos, entonces usted r bienvenido a unirse a cualquier otro grupo de desarrolladores con suerte no perderse.

¡Buena suerte!

Y por cierto. ¿qué tal si la gente ¿no tendrá más sitios web en el futuro? ¿y todos estaremos en Google+? Espero que los desarrolladores se vuelvan un poco más creativos y hagan algo útil(para no ser asimilados por el borgle)

//// Editar /// Solo un pequeño pensamiento para su aplicación existente:

Si tiene un CMS php mysql y desea incrustar soporte multilang. puede usar su tabla con una columna adicional para cualquier idioma o insertar la traducción con un id de objeto y un id de idioma en el mismo tabla o cree una tabla idéntica para cualquier idioma e inserte objetos allí, luego haga una unión de selección si desea que se muestren todos. Para la base de datos use utf8 general ci y por supuesto en el front/backend use utf8 text / encoding. He utilizado segmentos de ruta de url para las URL de la manera que ya expliqué como

Domain.org/en/about puede asignar el ID de lang a su tabla de contenido. de todos modos, necesita tener un mapa de parámetros para sus URL, por lo que le gustaría definir un parámetro para ser mapeado desde un pathsegment en tu URL que sería, por ejemplo,

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

Configuración de búsqueda

Pageid / url

1 / / acerca de / employees/../..

1 | /../ acerca de / empleados../../

Asignar parámetros a 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;
}

Por decir, eso ya se ha cubierto en upper post.

Y para no olvidarlo, necesitará "reescribir" la url de su archivo php generador que en la mayoría de los casos sería Indice.php

 1
Author: Dr. Dama,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-18 03:26:15

Trabajo de base de datos:

Crear la Tabla de idiomas 'languages':

Campos:

Language_id (primario y auto increamented)

Language_name

Created_at

Created_by

Updated_at

Updated_by

Crear una tabla en la base de datos 'content':

Campos:

Content_id (primario y automático increamented)

Main_content

Header_content

Footer_content

Leftsidebar_content

Rightsidebar_content

Language_id (clave foránea: referenciada a la tabla de idiomas)

Created_at

Created_by

Updated_at

Updated_by

Trabajo frontal:

Cuando el usuario selecciona cualquier idioma del menú desplegable o cualquier área, guarde el id de idioma seleccionado en la sesión como,

$_SESSION ['language'] = 1;

Ahora obtiene datos de la tabla de base de datos 'content' basado en el id de idioma almacenado en la sesión.

Los detalles pueden encontrarse aquí http://skillrow.com/multilingual-website-in-php-2 /

 -1
Author: user3445130,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-04 05:52:50

Como una persona que vive en Quebec, donde casi todo el sitio es francés e inglés... he intentado muchos si no la mayoría del plugin multilenguaje para WP... la única solución útil que funciona nive con todo mi sitio es mQtranslate... ¡vivo y muero con él !

Https://wordpress.org/plugins/mqtranslate /

 -1
Author: menardmam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-11 17:12:40

Una opción muy simple que funciona con cualquier sitio web donde se puede cargar Javascript es www.multilingualizer.com

Le permite poner todo el texto para todos los idiomas en una página y luego oculta los idiomas que el usuario no necesita ver. Funciona bien.

 -4
Author: Paul Martin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-08 16:40:24

¿Qué acerca de WORDPRESS + MULTI-LANGUAGE SITE BASIS(plugin) ? el sitio tendrá la estructura:

  • example.com/eng / category1/....
  • example.com/ eng /my-page....
  • example.com/rus / category1/....
  • example.com / rus /my-page....

El plugin proporciona una interfaz para la Traducción de todas las frases, con una lógica simple:

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

Entonces puede ser outputed:
echo translate('my_title', LNG); // LNG is auto-detected

P. S. sin embargo, compruebe, si el plugin sigue activo.

 -4
Author: T.Todua,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-23 23:27:54