Tag Archives: php

GNU gettext con php5

gnu-head-sm

GNU gettext es la biblioteca GNU de internacionalización (i18n) y es usada para escribir programas con interfaz en múltiples idiomas.

wikipedia

La internacionalización es el proceso de diseñar software de manera tal que pueda adaptarse a diferentes idiomas y regiones sin necesidad de cambios de ingeniería ni de código. La localización es el proceso de adaptar el software para una región específica mediante la adición de componentes específicos de un locale y la traducción de los textos, por lo que también se le puede denominar regionalización.

Es una práctica común en el idioma inglés (sobre todo en el ámbito de la computación), abreviar internationalization como “i18n”. Ello se debe a que entre la primera i y la última n de dicha palabra hay 18 letras. Lo mismo sucede con localization, que se abrevia “L10n”.

gettext en PHP

Existen dos formas de usar gettext en PHP: mediante la extensión gettext nativa de PHP o utilizando la librería PHP-gettext, escrita en PHP, que no necesita ninguna extensión.

La forma de usar gettext es

1
2
3
4
# @file: index.php
<?php
print _("hello world");
?>

La función_(), alias degettext(), devolverá la cadena “hello world” en el lenguaje correspondiente, de acuerdo al locale que hayamos seleccionado.

Si ejecutamos el script obtendremos “hello world” como salida, puesto que no hemos indicado ningún locale, ni creado ningún catálogo de traducción. Necesitamos crear el archivocomments.pocon la traducción de la cadena “hello world”. Esto puede hacerse mediante el comandoxgettext, o mediante un editor gráfico como poEdit.

apt-get install poedit

Creación del catálogo con poEdit

Ante todo debemos saber que la estructura de directorios para guardar los distintos catálogos tiene forma determinada. Por cada traducción se utiliza un directorio. Este generalmente se nombra usando dos letras minúsculas para el idioma, un guión bajo y dos mayúsculas para el país (es_AR, para español de Argentina). Dentro de este directorio debe existir otro con el nombre LC_MESSAGES, el cual será finalmente el directorio que contendrá el catálogo.

La estructura de nuestro ejemplo será

.
|-- locale
|   `-- es_AR
|       `-- LC_MESSAGES
|           |-- messages.mo
|           `-- messages.po
`-- index.php

El archivomessages.moes la versión compilada demessages.poy el que usará PHP para obtener la traducción.

Iniciemos poEdit. La primera vez nos preguntará por nuestro nombre y correo electrónico. Estos datos servirán para saber quién fue el último traductor que modificó el catálogo. Una vez en la ventana principal vamos a File -> New catalog. Seleccionamos el idioma (Español en este caso), el país y el código de caracteres tanto para el catálogo como para la fuente de datos desde la que obtendremos el listado de cadenas que requieren traducción. En la solapa Paths debemos colocar en ‘Base path’ la ruta completa al directorio base de archivos php (conteniendo las cadenas mencionadas anteriormente) y, debajo, en el cuadro ‘Paths’ agregar una entrada con ‘.’ indicando que se debe utilizar el directorio ingresado anteriormente. Al hacer click en OK aparecerá un cuadro de diálogo para guardar el nuevo catálogo. Navegamos hasta el directorio LC_MESSAGES y guardamos el archivo como messages.po. poEdit escaneará el directorio ingresado en Paths y extraerá todos las cadenas gettext. Ahora queda simplemente realizar la traducción y guardar el archivo. Al guardarlo, poEdit lo compilará y generará el .mo que finalmente usará PHP.

Ahora, modificaremos index.php para setear el locale

1
2
3
4
5
6
7
8
9
10
11
12
13
# @file: index.php
<?php
$language = 'es_AR.UTF-8';
putenv("LANG=$language");
setlocale(LC_ALL, "");

$domain = "messages";
bindtextdomain($domain, "./locale");
bind_textdomain_codeset($domain, 'UTF-8');
textdomain($domain);

print _("hello world");
?>

Aun nos queda un paso y es comprobar que tengamos dicho locale habilitado en nuestro sistema. Esto lo hacemos mirando la salida de

locale -a

Si no tenemos habilitado el charset es_AR.UTF8, ejecutamos

locale-gen es_AR.UTF-8

En este punto la salida del script debería ser la traducción que hicimos al español.

Tip

Para casos en que la cadena a traducir contenga variables, podemos utilizar esta función:

1
2
3
4
5
6
7
8
9
10
function __($string)
{
    $arg = array();
    for($i = 1 ; $i < func_num_args(); $i++)
        $arg[] = func_get_arg($i);
    return vsprintf(gettext($string), $arg);
}

$total = 400;
print __("results %d - %d of about %d", 1, 20, $total);

La traducción podría quedar

resultados %d - %d de aproximadamente %d

Tags: , , ,

Code Golf – Saving Time

Code Golf es un sitio donde se juega a resolver problemas informáticos en la menor cantidad posible de pulsaciones de tecla. Para ello pueden utilizarse lenguajes como Perl, PHP, Python y Ruby. Quien resuelva el desafío en menor cantidad de keystrokes es quien tendrá mayor puntaje.

Me anoté en uno llamado Saving Time y llegué al puesto 20 de PHP, con 245 keystrokes. (2009-03-27: Aunque ahora me encuentro en el 22, malditos nerds!)

El desafío consiste en crear la cara de un reloj analógico partiendo de una hora dada en formatohh:mm. Algunas de las reglas son

  • La hora dada en hh:mm se representa con una ‘h’ minúscula.
  • La hora que indica los minutos dados en hh:mm se representa con una ‘m’ minúscula.
  • Cuando hora y minutos coinciden en la misma posición se representa con una ‘x’ minúscula.
  • El resto de las horas se representan con una ‘o’ minúscula.
  • Los minutos deben redondearse al múltiplo de 5 más cercano (23 se convierte en 20, 39 en 35, etc.)

Salida del programa

Código

<?$f="874D1F0D1748110202020201";
list($h,$m)=split(':',fgets(STDIN));
if($h>=12)$h-=12;
$m=floor($m/5);$a=$i=12;
while($i){$a+=$i%2?$i:-$i;
echo str_pad("",hexdec($f{--$i})).
(($a==$h)?($h==$m?'x':'h'):($a==$m?'m':'o')).
str_repeat("\n",$f{$i+12});}?>

El string hexadecimal $f contiene el formato necesario para “dibujar” el reloj. Para interpretarlo es necesario dividirlo en dos mitades. La primera mitad contiene, en cada caracter hexadecimal, la distancia a la que se encuentra respecto de su elemento anterior, que puede ser el número de la izquierda o un salto de línea. De aquí surge el por qué de haber usado el sistema hexadecimal: el espacio mayor a representar es 15. En la segunda mitad cada caracter representa la cantidad de saltos de línea que preceden al caracter.

Tags: ,

Validate an E-Mail Address with PHP

From ILoveJackDaniel’s

function check_email_address($email) {
  // First, we check that there's one @ symbol,
  // and that the lengths are right.
  if (!ereg("^[^@]{1,64}@[^@]{1,255}$", $email)) {
    // Email invalid because wrong number of characters
    // in one section or wrong number of @ symbols.
    return false;
  }
  // Split it into sections to make life easier
  $email_array = explode("@", $email);
  $local_array = explode(".", $email_array[0]);
  for ($i = 0; $i < sizeof($local_array); $i++) {
    if (!ereg("^(([A-Za-z0-9!#$%&'*+/=?^_`{|}~-][A-Za-z0-9!#$%&'*+/=?^_`{|}~\.-]{0,63})|(\"[^(\\|\")]{0,62}\"))$", $local_array[$i])) {
      return false;
    }
  }
  // Check if domain is IP. If not,
  // it should be valid domain name
  if (!ereg("^\[?[0-9\.]+\]?$", $email_array[1])) {
    $domain_array = explode(".", $email_array[1]);
    if (sizeof($domain_array) < 2) {
        return false; // Not enough parts to domain
    }
    for ($i = 0; $i < sizeof($domain_array); $i++) {
      if (!ereg("^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|([A-Za-z0-9]+))$", $domain_array[$i])) {
        return false;
      }
    }
  }
  return true;
}

Tags: ,

Pattern Singleton en PHP4

A pesar de sus limitaciones en cuanto a orientación a objetos, PHP4 permite implementar un pattern del tipo Singleton.

Por defecto, en PHP4 (y a diferencia de PHP5) los objetos se devuelven/asignan por valor (una copia), por ello es necesario indicar expresamente que deseamos devolver/asignar una referencia del objeto en cuestión.

Una de las formas más elegantes de implemetar este pattern es:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// Clase de la cual deseamos que exista
// una única instancia en todo el programa
class Config
{
   var $message = null;

   function set($message)
   {
      $this->message = $message;
   }

   function get()
   {
      return $this->message;
   }
}

// Pattern que se ocupará de devolver
// la única instancia de la clase Config
class Singleton
{
   // Método que devuelve una referencia al objeto
   function &get()
   {      
      static $instance;
     
      if ( ! isset($instance) )
      {
         $instance = new Config();
         print "instancia creada.\n";
      }
     
      return $instance;      
   }
}

// Forma de uso
$config =& Singleton::get();
$config->set('hello world.');
printf("%s\n", $config->get());

$config =& Singleton::get();
printf("%s\n", $config->get());

Salida del programa

instancia creada.
hello world.
hello world.

Tags: , ,

Comfortable editing with VIM

Tomado del blog de Tobias Schlitt

Next occurrence of a word

If you hit*in command mode and your cursor resides on a word, you are taken to the next occurrence of the word. This is quite nice, if you like to know, where a function is called again.

Find matching brace

VIM 7.0 luckily highlights matching parenthesis, if your cursor resides on a brace, but sometimes you need to quickly jump to that matching brace. You can achieve this by hitting the%sign in command mode.

Repeat the last change

It often occurs, that you need to perform 1 change several times, but not often enough to write a short script or to address the changes with a complex regex. In those cases you can perform the change once, move the cursor to the next place and hit the.(dot) char, in command mode.


Read more »

Tags: , ,

Generando Captchas con PHP

Captcha es el acrónimo de Completely Automated Public Turing test to tell Computers and Humans Apart (Prueba de Turing pública y automática para diferenciar a máquinas y humanos).

Se trata de una prueba desafío-respuesta utilizada en computación para determinar cuándo el usuario es o no humano. Como el test es controlado por una máquina en lugar de un humano como en la prueba de turing, también se denomina Prueba de Turing inversa.

Clase Captcha

Esta es una clase que hice para www.lamarchaperonista.com.ar que genera captchas usando fuentes True Type (.ttf). La clase tiene varios métodos para personalizar el captcha, generar el código y seteralo en una variable de sesión de PHP.

La forma de usarla es realmente simple. Sólo hay que crear un script que contenga la clase y devuelva el captcha. Este mismo script será el encargado de generar el código y guardarlo en una variable de sesión:

// File: getCaptcha.php
requiere_once('Capcha.php');
session_start();

// Creo un captcha de 150 x 50 px
$captcha = new Captcha(150, 50);

// Creo y seteo en $_SESSION el código de 4 caracteres
$captcha->setSessionVar(4);

// Seteo la fuente TTF a usar
if ($captcha->setFontFile('ArsleGothic.ttf')) {

   // Seteo el tamaño de la tipografía
   $captcha->setFontSize(24);

   // El color
   $captcha->setFontColorWeb('716052');

   // Envío la imagen
   $captcha->sendCaptcha();

}

Al ser llamado desde otra página (como esta), mediante

&lt;img scr="getCapcha.php"/&gt;

, el script generará un captcha como el siguiente:

Captcha generado con la clase Captcha.php
generar nuevo

Luego, en el script que controla los datos que vienen del formulario (en el que el captcha fue mostrado), hacemos algo como esto:

// File: checkForm.php
requiere_once('Capcha.php');
session_start();

// Creo una instancia de Captcha, pero sin pasarle parámetros,
// ya que no voy a generar ninguna imagen.
$captcha = new Captcha();

// Traigo el código del captcha (seteado en $_SESSION)
// y lo comparo con el que me vino del formulario
if ( $_POST['codeCaptcha'] == $captcha->getCode() ) {

   // lo que haré en caso de coincidir los códigos.

}
else {

   // lo que haré en caso de NO coincidir los códigos.

}

Otro ejemplo

La clase tiene varios métodos más para setear las distintas propiedades del captcha, como el color de fondo, la densidad del ruido (las lineas que aparecen detras del texto) y el color del mismo.

Captcha generado con la clase Captcha.php
generar nuevo

$captcha = new Captcha(280, 70);
$captcha->setSessionVar(6);
if ($captcha->setFontFile('BagadBold.ttf')) {
   $captcha->setFontSize(32);
   $captcha->setFontColorWeb('00497B');
   $captcha->setBgColorWeb('5C9FCC');
   $captcha->setNoiseColorWeb('0069B0');
   $captcha->setNoiseFactor(70);
   $captcha->sendCaptcha();
}

Existen métodos para setear los colores pasando el código hexadecimal, ej.

setFontColorWeb("FF0033")

, y para setearlos pasando los valores por cada canal, ej.

setFontColor(255,0,51)

.

Archivos y documentación

El código junto con la documentación pueden bajarse desde aquí: Captcha.tar.gz
La documentación también puede verse en línea en http://blog.calcifer.com.ar/devel/captcha/doc
Un buen sitio para bajar fuentes TrueType es http://www.creamundo.com/

Tags: ,

Función para generar passwords aleatorios

Vía Intenta

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function generarClave($longitud)
{
   $caracteres=array();
   $clave='';

   # números
   for($i=48; $i&lt;=57; $i++)
      array_push($caracteres, chr($i));

   # mayúsculas
   for($i=65; $i&lt;=90; $i++)
      array_push($caracteres, chr($i));

   # minúsculas
   for($i=97; $i&lt;=122; $i++)
      array_push($caracteres, chr($i));

   for($i=0; $i&lt;$longitud; $i++)
   {
      mt_srand((double)microtime()*1000000);
      $clave .= $caracteres[mt_rand(0,count($caracteres))];
   }

   return $clave;
}

Otra opción es usar el identificador de sesión (session id) para obtener la contraseña de él:

session_start();
$password = substr (session_id(), 0, 10);

Tags: , ,

Client does not support authentication protocol

Luego de agregar un nuevo usuario MySQL he tenido el siguiente problema al intentar conectarme a la DB usando PHP4:

Client does not support authentication protocol requested by server;
consider upgrading MySQL client

Buscando llego a: http://dev.mysql.com/doc/refman/5.0/en/password-hashing.html

El problema

The password hashing mechanism was updated in MySQL 4.1 to provide better security and to reduce the risk of passwords being intercepted. However, this new mechanism is understood only by MySQL 4.1 (and newer) servers and clients, which can result in some compatibility problems.

Hace un tiempo pasé de la versión 4 a la 4.1. Resulta que ahora el hash correspondiente a las passwords se realiza utilizando un nuevo algoritmo que genera un hash de 41 bytes frente al viejo de 16 bytes.

Antes de la versión 4.1 la función

PASSWORD()

devolvía un hash de 16 bytes:

mysql> SELECT PASSWORD('mypass');
+--------------------+
| PASSWORD('mypass') |
+--------------------+
| 6f8c114b58f2ce9e   |
+--------------------+

A partir de la versión 4.1 la función

PASSWORD()

devuelve un hash de 41 bytes:

mysql> SELECT PASSWORD('mypass');
+-------------------------------------------+
| PASSWORD('mypass')                        |
+-------------------------------------------+
| *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4 |
+-------------------------------------------+

Solución

Según: Common Problems with MySQL and PHP.

Error: Client does not support authentication protocol: This is most often encountered when trying to use the older mysql extension with MySQL 4.1.1 and later. Possible solutions are: downgrade to MySQL 4.0; switch to PHP 5 and the newer mysqli extension; or configure the MySQL server with –old-passwords. (See Section A.2.3, “Client does not support authentication protocol”, for more information.)

Otra solución

La función

OLD_PASSWORD

devuelve el viejo hash de 16 bytes:

mysql> SET PASSWORD FOR 'some_user'@'some_host' = OLD_PASSWORD('newpwd');

Tags: ,