varios traducir poner para pagina otros multilenguaje idiomas idioma como codigo cambiar boton php mysql localization internationalization multilingual

php - traducir - Mejor práctica sitio web multilingüe



pagina multilenguaje html (13)

Implementando i18n sin el golpe de rendimiento usando un pre-procesador como lo 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 en el desempeño al tratar con la traducción sobre la marcha, luego descubrí esta gran publicación de blog de Thomas Bley que inspiró la forma en que usamos i18n para manejar grandes cargas de tráfico con problemas de rendimiento mínimos.

En lugar de llamar funciones para cada operación de traducción, que como sabemos en PHP es costoso, definimos nuestros archivos base con marcadores de posición, luego usamos un preprocesador para almacenar en caché esos archivos (almacenamos la hora de modificación del archivo para asegurarnos de que estamos sirviendo el último contenido 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 TWIG, no queremos usar { para evitar confusiones, así que usamos [%tr%] y [%/tr%] 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 que un usuario modifique estos dentro del CMS, solo obtenga los preg_split de preg_split mediante una preg_split en /n o = y haga que el CMS pueda escribir en los archivos INI.

El componente preprocesador

Esencialmente, Thomas sugiere utilizar 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: No verifiqué que la expresión regular funciona, no la copié de nuestro servidor de la empresa, pero puede ver cómo funciona la operación.

Cómo llamarlo

De nuevo, este ejemplo es de Thomas Bley, no de mí:

// 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 lo recuperamos en cada solicitud. Puede combinar esto con un parámetro $_GET opcional para anular el idioma, pero no sugiero el subdominio por idioma o la página por idioma porque hará más difícil ver qué páginas son populares y reducirá el valor de enlaces entrantes, ya que los tendrá más dispersos.

¿Por qué usar este método?

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

  1. El gran rendimiento se obtiene al no invocar un montón de funciones para el contenido que rara vez cambia (con este sistema, 100.000 visitantes en francés solo terminarán ejecutando reemplazo de traducción una vez).
  2. No agrega ninguna carga a nuestra base de datos, ya que utiliza archivos planos simples y es una solución pura de PHP.
  3. La capacidad de usar expresiones PHP en nuestras traducciones.

Obtener contenido de base de datos traducido

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, por desgracia) 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 por lo que el artículo 54 puede existir en todos los idiomas. Nuestro LANG en en_US si no se especifica.

Traducción URL Slug

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

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

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

Notas sobre algunas otras opciones

Traducción sobre la marcha basada en PHP

No puedo ver que estos ofrecen ninguna ventaja sobre las traducciones pre-procesadas.

Traducciones basadas en front-end

Durante mucho tiempo he encontrado estos interesantes, pero hay algunas advertencias. Por ejemplo, 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 el acceso.

También debería suponer 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 está ejecutando sin él (o usando Noscript para bloquear el uso de nuestros sitios). .

Traducciones dirigidas por bases de datos

Las velocidades de conectividad de la base de datos de PHP no son nada del otro mundo, 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.

He estado luchando con esta pregunta durante algunos meses, pero no he estado en una situación en la que necesite explorar todas las opciones posibles antes. En este momento, siento que es hora de conocer las posibilidades y crear mis propias preferencias personales para usar en mis próximos proyectos.

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

Estoy a punto de actualizar / volver a desarrollar un sistema de administración de contenido que he estado usando desde hace bastante tiempo. Sin embargo, me siento multi lenguaje es una gran mejora para este sistema. Antes no usaba frameworks 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 plataforma / marco.

Lo que debería traducirse

Como el sistema que busco debe ser tan fácil de usar como sea posible, el método de gestión de la traducción debe estar dentro del CMS. No debería haber necesidad de iniciar una conexión FTP para modificar archivos de traducción o cualquier plantilla analizada html / php.

Además, estoy buscando la manera 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. Hay un par de opciones que tengo. Pero todavía no siento que haya llegado a un método de mejores prácticas para lo que realmente estoy buscando. En este momento, esto es lo que se me ocurrió, pero este método también tiene sus efectos secundarios.

  1. Plantillas PHP Parsed : el sistema de plantillas debe ser analizado por PHP. De esta forma, puedo insertar los parámetros traducidos en el HTML sin tener que abrir las plantillas y modificarlas. Además de eso, las plantillas analizadas de PHP me dan 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 dije, esto debería ser independiente de la solución escrita.
  2. Basado en la base de datos : tal vez no necesito mencionar esto de nuevo. Pero la solución debe ser impulsada por la base de datos. El CMS está 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 estarían estructuradas: templates / Controller / View.php quizás esta estructura tenga más sentido: Controller.View.parameter . La tabla de la base de datos tendría estos campos un largo con un campo de 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 . Por lo tanto, el resultado es Welcome, Joshua . Esto parece una buena manera de hacerlo, porque los parámetros tales como: name son fáciles de entender por el editor.
  3. Baja carga de la base de datos : 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 reproduzca 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. Quizás un .ini incluso se analiza más rápido al final. Este debería contener los datos en el format parameter=value; . Supongo que esta es la mejor manera de hacerlo, ya que cada Vista que se representa puede incluir su propio archivo de idioma, si existe. Los parámetros de idioma entonces 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 tabla de base de datos : esto es lo que más me preocupa. Estoy buscando una forma 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 parece que mucho trabajo para obtener un buen sistema. Una de las cosas que surgió se basa en un sistema de data versioning que escribí: hay un nombre de tabla de base de datos Translations , esta tabla tiene una combinación única de language , nombre de tablename y primarykey . Por ejemplo: en_En / News / 1 (Refiriéndose a la versión en inglés de la noticia con ID = 1). Pero hay dos desventajas enormes para 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 una gran tarea utilizar esta configuración para buscar en la tabla. Por ejemplo, buscar la babosa SEO del artículo sería una búsqueda de texto completo, lo cual es bastante tonto. Pero, por otro lado, es una manera rápida de crear contenido traducible en todas las tablas muy rápido, pero no creo que este profesional tenga más peso que el de las estafas.
  5. Trabajo de front-end : también el front-end necesitaría algo de reflexión. Por supuesto, almacenaremos los idiomas disponibles en una base de datos y (de) activaremos los que necesitemos. De esta forma, la secuencia de comandos puede generar un menú desplegable para seleccionar un idioma y el programa de fondo puede decidir automáticamente qué traducciones se pueden realizar utilizando el CMS. El idioma elegido (por ejemplo, en_EN) se usaría luego al obtener el archivo de idioma para una vista o para obtener la traducción correcta para un elemento de contenido en el sitio web.

Entonces, ahí están. Mis ideas hasta ahora. Ni siquiera incluyen opciones de localización para fechas, etc., pero como mi servidor es compatible con 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 expliqué 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:

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

Entonces ... 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 tomar 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 lugar. Esto genera al menos una URL única para cada contenido. También sería más fácil acceder a otro idioma, por ejemplo, http://www.domain.com/en/about-us y la URL proporcionada es más fácil de entender para los visitantes de Google y los humanos. Al usar esta opción, ¿qué hacemos con los idiomas predeterminados? ¿Debe el idioma predeterminado eliminar el idioma seleccionado por defecto? 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 solo un idioma no es necesario tener esta identificación de idioma en la URL.

Y una tercera opción es una combinación de ambas opciones: usar el "idioma-identificación-menos" -URL ( http://www.domain.com/about-us ) para el idioma principal. Y use una URL con un slug SEO traducido para sublenguajes: http://www.domain.com/nl/over-ons & http://www.domain.com/de/uber-uns

Espero que mi pregunta te rompa la cabeza, ¡seguro que la mía! Me ayudó ya a resolver las cosas como una pregunta aquí. Me dio la posibilidad de revisar los métodos que he usado antes y la idea que tengo para mi próximo CMS.

¡Quiero agradecerles por tomarse el tiempo de 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, debería haber algún tipo de método alternativo donde el texto predeterminado se carga cuando todavía no hay traducciones disponibles. Si falta la traducción, debe insertarse o el archivo de traducción debe regenerarse.


Premisa del tema

Hay tres aspectos distintos en un sitio multilingüe:

  • traducción de interfaz
  • contenido
  • enrutamiento url

Si bien todos se conectaron de diferentes maneras, desde el punto de vista de CMS, se administran utilizando diferentes elementos de la interfaz de usuario y se almacenan de manera diferente. Parece confiar en su implementación y comprensión de los dos primeros. La pregunta era sobre el último aspecto: "¿Traducción de URL? ¿Deberíamos hacer esto o no? ¿De qué manera?"

¿De qué se puede hacer la URL?

Una cosa muy importante es, no seas elegante con IDN . En cambio, favorecer la transliteration (también: transcripción y romanización). Si bien, a primera vista, IDN parece una opción viable para las URL internacionales, en realidad no funciona como se anuncia por dos motivos:

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

De hecho, traté de abordar IDN hace unos años en un proyecto basado en Yii (horrible marco, en mi humilde opinión). Me encontré con los dos problemas mencionados antes de raspar esa solución. Además, sospecho que podría ser un vector de ataque.

Opciones disponibles ... como yo las veo.

Básicamente, tienes dos opciones, que podrían resumirse como:

  • http://site.tld/[:query] : donde [:query] determina el idioma y la elección del contenido

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

La consulta es Α y Ω.

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

En ese caso, tiene 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 el encabezado HTTP Accept-Language (1) , (2)

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

Tendría que pasar por todos los segmentos del patrón. Encuentre las posibles traducciones para todos esos segmentos y determine qué idioma se utilizó. Las dos fuentes adicionales (cookie y encabezado) se usarían para resolver conflictos de enrutamiento cuando surjan (no "si").

Tomemos como ejemplo: http://site.tld/blog/novinka .

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

Como ya puede observar, en ruso "блог" se transcribirá como "blog". Lo que significa que para la primera parte de [:query] usted (en el mejor de los casos ) terminará con la lista [''en'', ''ru''] de idiomas posibles. Luego tomas el siguiente segmento - "novinka". Eso podría tener solo un idioma en la lista de posibilidades: [''ru''] .

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

Pero si termina con 2 (ejemplo: ruso y ucraniano) o más posibilidades ... o 0 posibilidades, según sea el caso. Tendrá que usar la cookie y / o el encabezado para encontrar la opción correcta.

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

Idioma como parámetro

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

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

En cambio, tienes 3 opciones simples y priorizadas:

  1. si el segmento [:language] está configurado, úselo
  2. si $_COOKIE[''lang''] está configurado, $_COOKIE[''lang'']
  3. usar el idioma predeterminado

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

¿No hay aquí una tercera opción?

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

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

¿Qué enfoque usar?

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

También en la situación de palabra real tendrías la tercera parte principal en URL: "título". Como en el nombre del producto en la tienda en línea o en el título 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 ''EU-as-global-reserve-currency'' es el título. Puramente para fines de SEO.

¿Se puede hacer en Laravel?

Un poco, pero no de forma predeterminada.

No estoy muy familiarizado con esto, pero por lo que he visto, Laravel usa un mecanismo de enrutamiento simple basado en patrones. Para implementar URL multilingües, es probable que deba extender las clases principales 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 que?

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

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

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

Que solo usas para despachar:

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

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


Depende de la cantidad de contenido que tenga su sitio web. Al principio, utilicé una base de datos como todas las demás personas aquí, pero puede llevar mucho tiempo programar todo el funcionamiento 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 usará como archivos de traducción. Pero si agrega las traducciones usted mismo, funcionará:

Digamos que tienes este texto:

Welcome!

Puede ingresar 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 usa una cookie, tiene esto por ejemplo:

$_COOKIE[''language''];

Para hacerlo más fácil, transformémoslo en un código que pueda usarse fácilmente:

$language=$_COOKIE[''language''];

Si el idioma de su cookie es galés y tiene este fragmento de código:

echo $welcome[$language];

El resultado de esto será:

Croeso!

Si necesita agregar muchas traducciones para su sitio web y una base de datos consume demasiado, usar una matriz puede ser una solución ideal.


Le sugiero que no dependa realmente de la base de datos para la traducción; podría ser una tarea realmente 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} is replaced by 1st argument passed to method Locale::translate(''key_name'',''arg1'') {a:2} is replaced by 2nd argument passed to method Locale::translate(''key_name'',''arg1'',''arg2'')

How detection works

  • By default if geoip is installed then it will return country code by geoip_country_code_by_name and if geoip is not installed the fallback to HTTP_ACCEPT_LANGUAGE header

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

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 tienes solo 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.

Eche 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á toneladas en consultas. El enfoque estándar es obtener todos los datos l10n en la memoria en una etapa temprana (o durante la primera llamada a la función i10n si prefiere la carga diferida). Puede leer desde el archivo .po o desde DB todos los datos a la vez. Y que solo lean las cadenas solicitadas de la matriz.

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

Así que tienes toda la razón en tu # 3. Con una excepción: por lo general, es un archivo grande, no uno por controlador, más o menos. Porque lo mejor para el rendimiento es abrir un archivo. Probablemente sepa que algunas aplicaciones web cargadas compilan todo el código PHP en un archivo para evitar operaciones de archivos cuando include / require invocó.

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

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

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

Consulte: 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 los diferentes tipos de contenido, por ejemplo, artículos, elementos de menú, etc. Pero en el n. ° 4 estás en el camino correcto. Echa un vistazo en Drupal para tener más ideas. Tiene un esquema DB suficientemente claro y una interfaz lo suficientemente buena para traducir. Como crear un artículo y seleccionar el idioma para él. Y luego puedes traducirlo a otros idiomas.

Creo que no es un problema con las barras URL. Puedes simplemente crear una tabla separada para las babosas y será la decisión correcta. Además, al utilizar índices correctos, no es un problema consultar tablas incluso con una gran cantidad de datos. Y no fue 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.

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


A really simple option that works with any website where you can upload Javascript is www.multilingualizer.com

It lets you put all text for all languages onto one page and then hides the languages the user doesn''t need to see. Funciona bien.


As a person who live in Quebec where almost all site is french and english... i have try many if not most multilanguage plugin for WP... the one an only usefull solution that work nive with all my site is mQtranslate... i live and die with it !

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


Database work:

Create Language Table ''languages'':

Fields:

language_id(primary and auto increamented)

language_name

created_at

created_by

updated_at

updated_by

Create a table in database ''content'':

Fields:

content_id(primary and auto increamented)

main_content

header_content

footer_content

leftsidebar_content

rightsidebar_content

language_id(foreign key: referenced to languages table)

created_at

created_by

updated_at

updated_by

Front End Work:

When user selects any language from dropdown or any area then save selected language id in session like,

$_SESSION[''language'']=1;

Now fetch data from database table ''content'' based on language id stored in session.

Detail may found here http://skillrow.com/multilingual-website-in-php-2/


I am not going to attempt to refine the answers already given. Instead I will tell you about the way my own OOP PHP framework handles translations.

Internally, my framework use codes like en, fr, es, cn and so on. An array holds the languages supported by the website: array(''en'',''fr'',''es'',''cn'') The language code is passed via $_GET (lang=fr) and if not passed or not valid, it is set to the first language in the array. So at any time during program execution and from the very beginning, the current language is known.

It is useful to understand the kind of content that needs to be translated in a typical application:

1) error messages from classes (or procedural code) 2) non-error messages from classes (or procedural code) 3) page content (usually store in a database) 4) site-wide strings (like website name) 5) script-specific strings

The first type is simple to understand. Basically, we are talking about messages like "could not connect to the database ...". These messages only need to be loaded when an error occurs. My manager class receives a call from the other classes and using the information passed as parameters simply goes to relevant the class folder and retrieves the error file.

The second type of error message is more like the messages you get when the validation of a form went wrong. ("You cannot leave ... blank" or "please choose a password with more than 5 characters"). The strings need to be loaded before the class runs.I know what is

For the actual page content, I use one table per language, each table prefixed by the code for the language. So en_content is the table with English language content, es_content is for spain, cn_content for China and fr_content is the French stuff.

The fourth kind of string is relevant throughout your website. This is loaded via a configuration file named using the code for the language, that is en_lang.php, es_lang.php and so on. In the global language file you will need to load the translated languages such as array(''English'',''Chinese'', ''Spanish'',''French'') in the English global file and array(''Anglais'',''Chinois'', ''Espagnol'', ''Francais'') in the French file. So when you populate a dropdown for language selection, it is in the correct language ;)

Finally you have the script-specific strings. So if you write a cooking application, it might be "Your oven was not hot enough".

In my application cycle, the global language file is loaded first. In there you will find not just global strings (like "Jack''s Website") but also settings for some of the classes. Basically anything that is language or culture-dependent. Some of the strings in there include masks for dates (MMDDYYYY or DDMMYYYY), or ISO Language Codes. In the main language file, I include strings for individual classes becaue there are so few of them.

The second and last language file that is read from disk is the script language file. lang_en_home_welcome.php is the language file for the home/welcome script. A script is defined by a mode (home) and an action (welcome). Each script has its own folder with config and lang files.

The script pulls the content from the database naming the content table as explained above.

If something goes wrong, the manager knows where to get the language-dependent error file. That file is only loaded in case of an error.

So the conclusion is obvious. Think about the translation issues before you start developing an application or framework. You also need a development workflow that incorporates translations. With my framework, I develop the whole site in English and then translate all the relevant files.

Just a quick final word on the way the translation strings are implemented. My framework has a single global, the $manager, which runs services available to any other service. So for example the form service gets hold of the html service and uses it to write the html. One of the services on my system is the translator service. $translator->set($service,$code,$string) sets a string for the current language. The language file is a list of such statements. $translator->get($service,$code) retrieves a translation string. The $code can be numeric like 1 or a string like ''no_connection''. There can be no clash between services because each has its own namespace in the translator''s data area.

I post this here in the hope it will save somebody the task of reinventing the wheel like I had to do a few long years ago.


I had the same probem a while ago, before starting using Symfony framework.

  1. Just use a function __() which has arameters pageId (or objectId, objectTable described in #2), target language and an optional parameter of fallback (default) language. The default language could be set in some global config in order to have an easier way to change it later.

  2. For storing the content in database i used following structure: (pageId, language, content, variables).

    • pageId would be a FK to your page you want to translate. if you have other objects, like news, galleries or whatever, just split it into 2 fields objectId, objectTable.

    • language - obviously it would store the ISO language string EN_en, LT_lt, EN_us etc.

    • content - the text you want to translate together with the wildcards for variable replacing. Example "Hello mr. %%name%%. Your account balance is %%balance%%."

    • variables - the json encoded variables. PHP provides functions to quickly parse these. Example "name: Laurynas, balance: 15.23".

    • you mentioned also slug field. you could freely add it to this table just to have a quick way to search for it.

  3. Your database calls must be reduced to minimum with caching the translations. It must be stored in PHP array, because it is the fastest structure in PHP language. How you will make this caching is up to you. From my experience you should have a folder for each language supported and an array for each pageId. The cache should be rebuilt after you update the translation. ONLY the changed array should be regenerated.

  4. i think i answered that in #2

  5. your idea is perfectly logical. this one is pretty simple and i think will not make you any problems.

URLs should be translated using the stored slugs in the translation table.

Final words

it is always good to research the best practices, but do not reinvent the wheel. just take and use the components from well known frameworks and use them.

take a look at Symfony translation component . It could be a good code base for you.


I''ve been asking myself related questions over and over again, then got lost in formal languages... but just to help you out a little I''d like to share some findings:

I recommend to give a look at advanced CMS

Typo3 for PHP (I know there is a lot of stuff but thats the one I think is most mature)

Plone in Python

If you find out that the web in 2013 should work different then, start from scratch. That would mean to put together a team of highly skilled/experienced people to build a new CMS. May be you''d like to give a look at polymer for that purpose.

If it comes to coding and multilingual websites / native language support, I think every programmer should have a clue about unicode. If you don''t know unicode you''ll most certainly mess up your data. Do not go with the thousands of ISO codes. They''ll only save you some memory. But you can do literally everything with UTF-8 even store chinese chars. But for that you''d need to store either 2 or 4 byte chars that makes it basically a utf-16 or utf-32.

If it''s about URL encoding, again there you shouldn''t mix encodings and be aware that at least for the domainname there are rules defined by different lobbies that provide applications like a browser. eg a Domain could be very similar like:

ьankofamerica.com or bankofamerica.com samesamebutdifferent ;)

Of course you need the filesystem to work with all encodings. Another plus for unicode using utf-8 filesystem.

If its about translations, think about the structure of documents. eg a book or an article. You have the docbook specifications to understand about those structures. But in HTML its just about content blocks. So you''d like to have a translation on that level, also on webpage level or domain level. So if a block doesn''t exist its just not there, if a webpage doesn''t exist you''ll get redirected to the upper navigation level. If a domain should be completely different in navigation structure, then.. its a complete different structure to manage. This can already be done with Typo3.

If its about frameworks, the most mature ones I know, to do the general stuff like MVC(buzzword I really hate it! Like "performance" If you want to sell something, use the word performance and featurerich and you sell... what the hell) is Zend . It has proven to be a good thing to bring standards to php chaos coders. But, typo3 also has a Framework besides the CMS. Recently it has been redeveloped and is called flow3 now. The frameworks of course cover database abstraction, templating and concepts for caching, but have individual strengths.

If its about caching... that can be awefully complicated / multilayered. In PHP you''ll think about accellerator, opcode, but also html, httpd, mysql, xml, css, js ... any kinds of caches. Of course some parts should be cached and dynamic parts like blog answers shouldn''t. Some should be requested over AJAX with generated urls. JSON, hashbangs etc.

Then, you''d like to have any little component on your website to be accessed or managed only by certain users , so conceptually that plays a big role.

Also you''d like to make statistics , maybe have distributed system / a facebook of facebooks etc. any software to be built on top of your over the top cms ... so you need different type of databases inmemory, bigdata, xml, whatsoever.

well, I think thats enough for now. If you haven''t heard of either typo3 / plone or mentioned frameworks, you have enough to study. On that path you''ll find a lot of solutions for questions you haven''t asked yet.

If then you think, lets make a new CMS because its 2013 and php is about to die anyway, then you r welcome to join any other group of developers hopefully not getting lost.

¡Buena suerte!

And btw. how about people will not having any websites anymore in the future? and we''ll all be on google+? I hope developers become a little more creative and do something usefull(to not be assimilated by the borgle)

//// Edit /// Just a little thought for your existing application:

If you have a php mysql CMS and you wanted to embed multilang support. you could either use your table with an aditional column for any language or insert the translation with an object id and a language id in the same table or create an identical table for any language and insert objects there, then make a select union if you want to have them all displayed. For the database use utf8 general ci and of course in the front/backend use utf8 text/encoding. I have used url path segments for urls in the way you already explaned like

domain.org/en/about you can map the lang ID to your content table. anyway you need to have a map of parameters for your urls so you''d like to define a parameter to be mapped from a pathsegment in your URL that would be eg

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

lookup configuration

pageid| url

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

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

map parameters to 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; }

per say, thats been covered already in upper post.

And to not forget, you''d need to "rewrite" the url to your generating php file that would in most cases be index.php


Just a sub answer: Absolutely use translated urls with a language identifier in front of them: http://www.domain.com/nl/over-ons
Hybride solutions tend to get complicated, so I would just stick with it. ¿Por qué? Cause the url is essential for SEO.

About the db translation: Is the number of languages more or less fixed? Or rather unpredictable and dynamic? If it is fixed, I would just add new columns, otherwise go with multiple tables.

But generally, why not use Drupal? I know everybody wants to build their own CMS cause it''s faster, leaner, etc. etc. But that is just really a bad idea!


What about WORDPRESS + MULTI-LANGUAGE SITE BASIS (plugin) ? the site will have structure:

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

The plugin provides Interface for Translation all phrases, with simple logic:

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

then it can be outputed:
echo translate(''my_title'', LNG); // LNG is auto-detected

ps however, check, if the plugin is still active.