Menubar

viernes, 16 de junio de 2017

Como leer un archivo PO desde PHP para traducir nuestra web

Banderas  A partir de este momento y por algunas ediciones estaré presentando algunos trucos para PHP y Wordpress esperando que sean de su agrado. Wordpress es un sistema de gestión de contenidos o CMS (por sus siglas en inglés) que se encarga de crearnos un sitio web completo en minutos,1 dentro de sus ventajas es que puede ser traducido de un idioma a otro gracias a los archivos PO que acompañan a muchos temas y plugins diseñados para Wordpress.
  Este recurso te va a mostrar como leer un archivo PO directamente desde PHP (sin pasar por Wordpress que tiene soporte nativo), te invito a que sigas leyendo por favor.


  Veamos antes que nada que son los archivos PO. Los archivos PO (Portable Object) son simples archivos de texto que nos proporcionan información de una interfaz en diferentes idiomas, dentro de ellos encontramos una llave y su respectivo valor a mostrar como traducido, ejemplo:

#: includes/api/class-wc-rest-system-status-controller.php:491
msgid "Terms in the product visibility taxonomy."
msgstr "Los términos en la taxonomía de visibilidad del producto."

  Donde la primer línea (comienza con #) es un comentario sobre la funcionalidad de la sección en cuestión, la segunda línea es la llave y la tercera la traducción respectiva de esa llave.

  Así de un modo simple podemos traducir temas, plugins y demás material para nuestra plantilla de Wordpress, una vez editado nuestro archivo hay que generar el archivo MO (Machine Object, podemos usar Poedit para agilizar el trabajo), luego dentro de Wordpress hay que copiar esos dos archivos a la carpeta wp-content/languages/pluginswp-content/languages/themes respectivamente (dependiendo del tipo de componente), de lo contrario Wordpress no puede encontrarlos y no se puede efectuar la traducción.

  Pero, el problema es que si queremos usar esos archivos (concretamente el archivo PO) desde un archivo PHP que no tenga ninguna relación, no podremos, ¿porqué no?, porque para leer los archivos PO necesitamos de las funciones _e o translate de Wordpress, esas funciones no existen de forma nativa en PHP. Así que podemos hacer dos cosas para remediar el problema:

  • Integrar a nuestro sistema una librería que nos permita usar estas funciones de forma "nativa"
  • Usar una función desarrollada por un servidor que hace lo mismo que la función "translate"

  Con respecto a la segunda opción, presento a ustedes la función "getTranslatedStringFromPOFile", esta función retorna una cadena de texto que es el valor traducido (desde un archivo PO usando como referencia una llave) y acepta dos parámetros para funcionar: la ruta del archivo PO a leer y el ID o llave a leer.

El código fuente es el siguiente:

Actualización:

  Gracias a Javier Gutierrez Chamorro (Guti) se ha hecho una mejora a esta función para reducir el tiempo de lectura de las cadenas traducidas, pueden ver el nuevo código fuente en este post.

//thanks to http://www.desarrolloweb.com/articulos/893.php
function getTranslatedStringFromPOFile($pofile, $msgid)
{

  $found = false;


  if(trim($pofile) == "")
    return $msgid;  
  else
  {
 
if(!file_exists(trim($pofile)))
     return $msgid;
else
 {
   
     if(trim($msgid) == "")
      return "";
     else
   {
  
       $line = "";
       $fp = fopen(trim($pofile), "r");
    
       while(!feof($fp) || $found == false)
       {
 
          $line = trim(fgets($fp, 1024));
  
          if(trim($line) != "")
         {
 
           if(startWith(strtolower(trim($line)), "msgid", false) == true)
          {
    
            $start = strpos(strtolower($line), "msgid");
            $msgidvalue = substr($line, $start, strlen($line));
            $start = strpos(strtolower($msgidvalue), " ");
            $msgidvalue = substr($msgidvalue, $start + 1, strlen($line));
      
            if(startWith(strtolower(trim($msgidvalue)), chr(34), false) == true)
              $msgidvalue = substr($msgidvalue, 1, strlen($msgidvalue) - 1);
        
            if(endWith(strtolower(trim($msgidvalue)), chr(34), false) == true)
              $msgidvalue = substr($msgidvalue, 0, strlen($msgidvalue) - 1);
        
            if(trim(strtolower($msgidvalue)) == trim(strtolower($msgid)))
           {

                   $found = true;

               $line = trim(fgets($fp, 1024));
  
               if(trim($line) != "")
              {

                       $start = strpos(strtolower($line), "msgstr");
                       $msgstrvalue = substr($line, $start, strlen($line));
                       $start = strpos(strtolower($msgstrvalue), " ");
                       $msgstrvalue = substr($msgstrvalue, $start + 1, strlen($line));

                       if(startWith(strtolower(trim($msgstrvalue)), chr(34), false) == true)
                          $msgstrvalue = substr($msgstrvalue, 1, strlen($msgstrvalue) - 1);

                       if(endWith(strtolower(trim($msgstrvalue)), chr(34), false) == true)
   $msgstrvalue = substr($msgstrvalue, 0, strlen($msgstrvalue) - 1);

                       $msgstr = $msgstrvalue;
    
               }
                else
$msgstr = "";
  
}

           }


        }


    }


    fclose($fp);

 
if($found == false)
     $msgstr = $msgid;
  
return $msgstr;
 
}

    }

    }  
 
}

//thanks to https://stackoverflow.com/questions/834303/startswith-and-endswith-functions-in-php

function startWith($str, $c, $sensitivity)
{

  if(trim($str) == "")

    return false;
  elseif(strlen(trim($str)) <= 0)
    return false;
  else
  {
  
    if($sensitivity == true)
   {

if(strlen(trim($str)) == strlen(trim($c)))
{
 
        if(trim($str) == trim($c))
             return true;
          else
            return false;

      }

else
{
 
    if(substr(trim($str), 0, strlen(trim($c))) == trim($c))
      return true;
   else
     return false;

}
 
}
else
{

       if(strlen(trim($str)) == strlen(trim($c)))

      {
 
           if(trim(strtolower($str)) == trim(strtolower($c)))
             return true;
          else
            return false;

      }

      else
     {

         if(substr(trim(strtolower($str)), 0, strlen(trim($c))) == trim(strtolower($c)))

           return true;
       else
          return false;

     }
 
    }

  }
  
}

function endWith($str, $c, $sensitivity)

{

    if(trim($str) == "")

       return false;
    elseif(strlen(trim($str)) <= 0)
       return false;
    else
    {
  
        if($sensitivity == true)
       {

          if(strlen(trim($str)) == strlen(trim($c)))
         {
 
            if(trim($str) == trim($c))
              return true;
           else
             return false;

        }

        else
       {

          if(substr(trim($str), (strlen(trim($str)) - strlen(trim($c))), strlen(trim($c))) == trim($c))

            return true;
else
            return false;

     }
 
  }
  else
  {

      if(strlen(trim($str)) == strlen(trim($c)))
      {
 
        if(trim(strtolower($str)) == trim(strtolower($c)))
            return true;
         else
          return false;

      }

      else
      {

   if(substr(trim(strtolower($str)), (strlen(trim($str)) - strlen(trim($c))), strlen(trim($c))) == trim(strtolower($c)))

        return true;
     else
         return false;

      }
 
  }
  
}

}

Como pueden ver la funcionalidad es muy simple:
  1. Si el archivo existe, es abierto para lectura
  2. Se localiza la llave (leyendo línea por línea el archivo)
  3. Una vez localizada la llave, se lee la siguiente línea que es el valor a traducir
  4. Se cierra el archivo
  5. Se devuelve el valor traducido
Espero les sea de mucha utilidad.

Procedencia de las imágenes:
File: Banderas de América 
URL: https://www.vecteezy.com/vector-art/117771-americas-flags
Licencia: Creative Commons

Referencias:
1.- Wordpress

¿Te gustó este post?, entonces si lo deseas puedes apoyarnos para continuar con nuestra labor, gracias.


publicidad:
Compartir:
Temas relacionados:
Caritas para las respuestas:
Escriba el código en la respuesta para insertar divertidas caritas en sus respuestas.
:) :( ;) :D :-/ :x :P :-* =(( :-O X( :7 B-) #:-S :(( :)) =)) :-B :-c :)] ~X( :-h I-) =D7 @-) :-w 7:P 2):) :!! \m/ :-q :-bd ^#(^

2 comentarios:

  1. Para editar archivos PO, yo uso poedit, así te ahorras editarlos a mano. En cuanto a la función, la verdad que no me gusta que tenga que abrir el archivo, buscar el string, y cerrar el archivo cada vez que se necesita una traducción. Es poco eficiente.

    Lo que yo haría sería leer el archivo completo en memoria, y entonces buscar las traducciones allí cada vez que se pidiera una, sin necesidad de volverlo a cargar.

    ResponderEliminar
    Respuestas
    1. También uso poedit y es muy bueno, tienes razón en lo de abrir y cerrar el archivo, la verdad no había pensado en eso y haré la corrección que me sugieres, gracias. :-bd

      Eliminar

Votar por nosotros en Hispatop
Votar por nosotros en La Blogoteca
Comunidad Kynamio
Directorio de blogs, ¡agrega el tuyo!
Programas para el mantenimiento de Windows
Blog de seguridad informatica