Localidades en Linux COMO

Peeter Joot, peeter_joot@vnet.ibm.com Traducción por Juan José López Mellado,laveneno@hotmail.com

v1.5, 21 Julio 1997, traducido el 27 julio 1998
Este documento describe cómo configurar su máquina Linux para usar localidades (N. del T. locales se refiere a modificaciones para hacer compatible Linux con varias lenguas y culturas, por lo que creo que la mejor traducción es localidades).

1. Introducción.

Esto es realmente una descripción de cómo hice para tener localedef instalado, compilar algunas localidades, y cómo probarlas. Lo hago solo por entretenimiento, y pensando que alguien podría estar interesado en probarlo por si mismo. Una vez esté configurado usted podrá usar aplicaciones con NLS activado y con la localidad que desee. De aquí a poco tiempo, el soporte a las localidades será una parte de las distribuciones estándar, por lo que gran parte de este mini-HOWTO será redundante.

2. ¿ Qué es "locales" ?

Locales encapsula varias de las características específicas de la lengua y la cultura que no puede ser codificada directamente en sus programas. Si tiene varias localidades instaladas en su ordenador entonces puede seleccionar a través de las correspondientes variables de entorno como se de sensitivo a su localidad debe ser un programa. Las localidades por defecto son las de C, o las POSIX locale las cuales se encuentran codificadas en libc.

LANG

Selecciona la localidad, pero puede ser superpuesta por cualquier otra variable de entorno de las LC_xxxx.

LC_COLLATE

Método de ordenamiento.

LC_CTYPE

Definiciones de caracteres, mayúsculas, minúsculas, ... Son usadas por funciones como toupper, tolower, islower, isdigit, ...

LC_MONETARY

Contiene la información necesaria para el formato monetario. Contiene definiciones de cosas como el separador de millares, el separador decimal, y el símbolo monetario junto con la posición que ocupa.

LC_NUMERIC

Separadores de millares y decimal, y la agrupación numérica esperada.

LC_TIME

Como especificar la hora y la fecha. Contiene cosas como los días de la semana, y los meses del año en forma abreviada y completa.

LC_MESSAGES

Expresiones Sí, y No.

LC_ALL

Activa la localidad, y se superpone a cualquier otra variable de entorno LC_xxxx.

Algunas localidades son las siguientes, y existen muchas más.

en_CA

Inglés Canadiense.

en_US

Inglés Estadounidense.

de_DE

Alemán de Alemania.

fr_FR

Francés de Francia.

es_ES

Español de España.

Si está escribiendo un programa, y espera que sea usado de manera internacional debe usar las localidades. La razón más evidente para ello es porque no todo el mundo va a usar el mismo código y páginas de caracteres que usted. Vaya con cuidado de no incorporar en su programa código como el siguiente:

/* aceptar caracteres alfabéticos */ 
if ( (( c >= 'a') && ( c <= 'z' )) || 
     (( c >= 'A') && ( c <= 'Z' )) ) { ... } 

Si escribe este tipo de código está asumiendo en su programa que el usuario/fichero/... usa ASCII y nada más que ASCII, y no estará respetando la página de códigos de la localidad del usuario. Por ejemplo está rechazando caracteres como la a acentuada del entorno Español (N. del T. el ejemplo ha sido convenientemente modificado para la localidad Española). Lo que debe hacer en vez de aquello es usar funciones que tengan en cuenta la localidad como isalpha(). Si su programa requiere necesariamente del uso del alfabeto US-ASCII, utilice de todas maneras la función isalpha(), pero antes debería hacer setlocale(LC_CTYPE,"C") o definir las variables de entorno LANG, LC_CTYPE, o LC_ALL al valor "C".

Locales permite un gran grado de flexibilidad y hace que ciertas presunciones que un programador hacía en programas C basados en ASCII sean incorrectas. Por ejemplo, no puede asumir la posición de los caracteres dentro del código. Nada le prohibe el hacer un fichero de mapa de caracteres que defina la posición del carácter 'A' como 0xC1 en vez de 0x41. Este es en realidad el código de mapeo para la 'A' en la página de códigos 37 de IBM, usada en los mainframes, mientras que el primero es el usado por US-ASCII, iso8859-x, y otros. La idea básica es que la gente que habla en lenguas diferentes, esperan ordenaciones de caracteres diferentes, usan páginas de códigos diferentes, y viven en diferentes paises. Las localidades y los programas sensitivos a la localidad dan a uno la capacidad de respetar estas cosas, y manejarlas de forma correcta. No es mucho más el trabajo que debe realizarse, solo se necesita una manera diferente de pensar al escribir los programas.

3. Notas.

4. Qué necesita.

Es necesario bajarse algunas cosas de varios sitios. Todo lo que aparece aquí exceptuando los ficheros fuentes de locale puede obtenerse de sunsite.unc.edu, tsx-11.mit.edu, o, preferiblemente, un sitio espejo de estos. Cuando hice esto originalmente use libc-5.2.18, la cual está bastante actualizada. La actual versión de libc es la 5.4.17, y más abajo se han hecho las correspondientes sustituciones. De todas maneras, libc 5.4.17, estará probablemente anticuada cuando realice estos cambios, así que use la última versión de que disponga. Debería considerar el usar glibc (gnu libc) en vez de libc 5 de Linux para cualquier trabajo de internacionalización.Actualmente está disponible glibc 2.0.4 (gnu libc) pero ninguna distribución la ha comenzado a utilizar como su librería estándar libc (al menos para las distribuciones de Linux para Intel). Además de ser completamente reentrante y tener soporte para hilos (N. del T. threads), glibc está completamente internacionalizada y tiene un excelente soporte para la internacionalización en la programación. La mayoría de la internacionalización hecha en libc 5 se ha obtenido de glibc. Si opta por usar glibc entonces puede ignorar este mini-howto. Incluir el añadido de locale en la compilación e instalación de glibc es trivial, y está cubierto por la documentación de instalación de glibc. ¡ Debe advertirse que una actualización completa no es un trabajo trivial ! Estoy esperando a que redhat (la cual uso) tenga pronto una distribución basada en glibc, ya que no deseo tener que recompilar completamente mi sistema.

Hay probablemente un montón se sitios con los fuentes de locale. He encontrado fuentes de locale y de mapas de caracteres de dominio público en dkuug.dk:/i18n/WG15-collection/localesy dkuug.dk:/i18n/WG15-collection/charmaps respectivamente.

5. Instalando todo.

Esto es lo que hice para instalar todo. Disponía de un sistema ELF (compilador, kernel, ...) instalado antes de hacer todo esto.

  1. Primero instalé el paquete binutils. tar xzf binutils-2.6.0.2.bin.tar.gz -C /
  2. Después instalé el enlazador dinámico:
    tar zxf ld.so-1.7.12.tar.gz -C /usr/src 
    cd /usr/src/ld.so-1.7.12 
    sh instldso.sh 
    
  3. A continuación instalé los binarios de libc. Véase release.libc-5.4.17 para más instrucciones al respecto.
    rm -f /usr/lib/libc.so /usr/lib/libm.so 
    rm -f /usr/include/iolibio.h /usr/include/iostdio.h 
    rm -f /usr/include/ld_so_config.h /usr/include/localeinfo.h 
    rm -rf /usr/include/netinet /usr/include/net /usr/include/pthread 
    tar -xzf libc-5.4.17.bin.tar.gz -C / 
    
  4. Ahora debe ejecutar ldconfig para que se encuentren las nuevas librerías compartidas. ldconfig -v.
  5. Hay un fallo que fue arreglado en libc que hace parar a make, y otros programas. Esto es lo que hice para reconstruir e instalar make.
    tar zxf make-3.74.tar.gz -C /usr/src 
    cd /usr/src/make-3.74 
    patch < /donde_sea_que_lo_puso/release.libc-5.4.17 
    configure --prefix=/usr 
    sh build.sh 
     ./make install 
    cd .. 
    rm -rf make-2.74 
    
  6. Ahora puede compilarse e instalarse localedef.
    mkdir /usr/src/libc 
    tar zxf libc-5.4.17.tar.gz -C /usr/src/libc 
    cd /usr/src/libc 
    cd include 
    ln -s /usr/src/linux/include/asm . 
    ln -s /usr/src/linux/include/linux . 
    cd ../libc 
     ./configure 
    # No estoy seguro de que estos dos makes sean necesarios, pero mejor
    asegurarse: make clean ; make depend 
    cd locale 
    make programs 
    mv localedef /usr/local/bin 
    mv locale /usr/local/bin 
    
  7. Ponga los mapas de caracteres donde localedef los pueda encontrar. En este punto uso los mapas de caracteres y fuentes de locale que bajé desde dkuug.dk ftp como charmaps.tar, ylocales.tar respectivamente. La versión antigua de localedef (5.2.18) buscaba los fuentes de los mapas de caracteres en /usr/share/nls/charmap , pero ahora localedef busca en /usr/share/i18n/charmaps y /usr/share/i18n/locales por defecto en busca de los fuentes de los mapas de caracteres y los fuentes de locale:
    mkdir /usr/share/i18n 
    mkdir /usr/share/i18n/charmaps 
    mkdir /usr/share/i18n/locales 
    tar xf charmaps.tar -C /usr/share/i18n/charmaps 
    tar xf locales.tar -C /usr/share/i18n/locales 
    

    El nuevo localedef (5.4.17) se ha hecho más inteligente y buscará otros ficheros fuente de locale cuando esté trabajando con el comando 'copy', mientras que el antiguo localedef necesitaba tener los objetos locale creados de antemano para poder hacer el comando copy. Esta lista de comandos tiene las dependencias ordenadas y pueden usarse para generar todos los objetos locales indiferentemente de la versión de libc que esté usando, pero puede crear solo aquellos que desee.

    localedef -ci en_DK -f ISO_8859-1:1987 en_DK  
    localedef -ci sv_SE -f ISO_8859-1:1987 sv_SE  
    localedef -ci fi_FI -f ISO_8859-1:1987 fi_FI  
    localedef -ci sv_FI -f ISO_8859-1:1987 sv_FI  
    localedef -ci ro_RO -f ISO_8859-1:1987 ro_RO  
    localedef -ci pt_PT -f ISO_8859-1:1987 pt_PT  
    localedef -ci no_NO -f ISO_8859-1:1987 no_NO  
    localedef -ci nl_NL -f ISO_8859-1:1987 nl_NL  
    localedef -ci fr_BE -f ISO_8859-1:1987 fr_BE  
    localedef -ci nl_BE -f ISO_8859-1:1987 nl_BE  
    localedef -ci da_DK -f ISO_8859-1:1987 da_DK 
    localedef -ci kl_GL -f ISO_8859-1:1987 kl_GL 
    localedef -ci it_IT -f ISO_8859-1:1987 it_IT 
    localedef -ci is_IS -f ISO_8859-1:1987 is_IS 
    localedef -ci fr_LU -f ISO_8859-1:1987 fr_LU 
    localedef -ci fr_FR -f ISO_8859-1:1987 fr_FR 
    localedef -ci de_DE -f ISO_8859-1:1987 de_DE 
    localedef -ci de_CH -f ISO_8859-1:1987 de_CH 
    localedef -ci fr_CH -f ISO_8859-1:1987 fr_CH 
    localedef -ci en_CA -f ISO_8859-1:1987 en_CA 
    localedef -ci fr_CA -f ISO_8859-1:1987 fr_CA 
    localedef -ci fo_FO -f ISO_8859-1:1987 fo_FO 
    localedef -ci et_EE -f ISO_8859-1:1987 et_EE 
    localedef -ci es_ES -f ISO_8859-1:1987 es_ES 
    localedef -ci en_US -f ISO_8859-1:1987 en_US 
    localedef -ci en_GB -f ISO_8859-1:1987 en_GB 
    localedef -ci en_IE -f ISO_8859-1:1987 en_IE 
    localedef -ci de_LU -f ISO_8859-1:1987 de_LU 
    localedef -ci de_BE -f ISO_8859-1:1987 de_BE 
    localedef -ci de_AT -f ISO_8859-1:1987 de_AT 
    localedef -ci sl_SI -f ISO_8859-2:1987 sl_SI 
    localedef -ci ru_RU -f ISO_8859-5:1988 ru_RU 
    localedef -ci pl_PL -f ISO_8859-2:1987 pl_PL 
    localedef -ci lv_LV -f BALTIC lv_LV 
    localedef -ci lt_LT -f BALTIC lt_LT 
    localedef -ci iw_IL -f ISO_8859-8:1988 iw_IL 
    localedef -ci hu_HU -f ISO_8859-2:1987 hu_HU 
    localedef -ci hr_HR -f ISO_8859-4:1988 hr_HR 
    localedef -ci gr_GR -f ISO_8859-7:1987 gr_GR 
    

6. Y ahora qué.

Después de hacer todo este trabajo usted debe poder utilizar las localidades que han sido creadas. Aquí puede ver un simple programa de ejemplo.

/* test.c : una simple prueba para ver si las localidades pueden ser 
cargadas y usadas */ 
#include <locale.h> 
#include <stdio.h> 
#include <time.h> 
 
main(){ 
 time_t t; 
 struct tm * _t; 
 char buf[256]; 
 
 time(&t); 
 _t = gmtime(&t); 
 
 setlocale(LC_TIME,""); 
 strftime(buf,256,"%c",_t); 
 
 printf("%s\n",buf);
} 

Puede usar el programa locale para ver cual es su actual entorno local de variables de entorno relacionadas.

$ # compile el programa de prueba anterior, y ejecútelo con
$ # diferentes localidades
$ gcc -s -o Test test.c 
$ # vea que localidad está activada en este momento: 
$ locale 
LANG=POSIX 
LC_COLLATE="POSIX" 
LC_CTYPE="POSIX" 
LC_MONETARY="POSIX" 
LC_NUMERIC="POSIX" 
LC_TIME="POSIX" 
LC_MESSAGES="POSIX" 
LC_ALL= 
$ # Esto... estamos usando la localidad de C (bastante
$ # aburrida) cambiemoslo al Inglés Canadiense: 
$ export LC_TIME=en_CA 
$ Test 
Sat 23 Mar 1996 07:51:49 PM 
$ # Probemos con el Francés Canadiense: 
$ export LC_TIME=fr_CA 
$ Test 
sam 23 mar 1996 19:55:27 
 

7. Arreglando el fallo de catopen.

Instalando las localidades se arregla un fallo (¿característica?) que hay en el comando catopen de libc de Linux. Digamos que usted crea un programa que usa catálogos de mensajes, y crea un catálogo en Alemán y lo pone en /home/peeter/catalogs/de_DE. Ahora haciendo lo que sigue, sin la localidad de_DE instalada:

export LC_MESSAGES=de_DE 
export NLSPATH=/home/peeter/catalogs/%L/%N.cat:$NLSPATH 

el catálogo de mensajes en Alemán no se abre, y los mensajes por defecto son los que se usan en las llamadas a catgets. Esto es así porque catopen hace una llamada a setlocale para obtener la categoría correcta de mensajes, setlocale falla aunque las variables de entorno estén activadas. Entonces catopen intenta cargar el catálogo de mensajes sustituyendo "C" por todas las "%L" en NLSPATH. Puede seguir usando su catálogo de mensajes sin instalar la localidad, pero debe añadir explícitamente la parte "%L" en NLSPATH como

export NLSPATH=/home/peeter/catalogs/de_DE/%N.cat:$NLSPATH 

, pero esto va en contra del propósito general de las variables de entorno de categorías de localidad.

8. Preguntas y respuestas.

Esta sección podría crecer hasta un FAQ, pero todavía no lo es.

8.1 pregunta sobre msgcat

Soy un usuario de LINUX, y he escrito el siguiente programa de prueba:

-------------------------------------------------------------------- 
#include <stdio.h> 
#include <locale.h> 
#include <features.h> 
#include <nl_types.h> 
 
main(int argc, char ** argv) 
{ 
 nl_catd catd; 
 
 setlocale(LC_MESSAGES, ""); 
 catd = catopen("msg", MCLoadBySet); 
 fprintf(stderr,catgets(catd, 1, 1, "locale message fail\n"));
 catclose(catd); 
} 
-------------------------------------------------------------------- 
$ msg.m 
$set 1 
 
1 locale message pass
-------------------------------------------------------------------- 

Si uso un camino absoluto como catopen("/etc/locale/msg.cat",MCLoadBySet); , entonces obtengo el resultado correcto. Pero si uso el ejemplo anterior, catopen devuelve un -1 (error).

8.2 Respuesta sobre msgcat

Esta pregunta ha sido casi contestada en la sección anterior, pero aquí se añade información adicional. Hay un número de lugares válidos donde puede poner sus catálogos de mensajes. Aun así, no debe tener explícitamente definida NLSPATH en su entorno porque está definido en libc como sigue:

$ strings /lib/libc.so.5.4.17 | grep locale | grep %L 
/etc/locale/%L/%N.cat:/usr/lib/locale/%L/%N.cat:/usr 
/lib/locale/%N/%L:/usr/share/locale/%L/%N.cat:/usr/ 
local/share/locale/%L/%N.cat 

asíarriba que si ha hecho algo de esto :

$ export LC_MESSAGES=en_CA 
$ export LC_ALL=en_CA 
$ export LANG=en_CA 

Con la NLSPATH de arriba y el entorno especificado, la llamada catopen("msg", MCLoadBySet); debería funcionar si su catálogo de mensajes se ha copiado en alguno de estos lugares:

/etc/locale/en_CA/msg.cat 
/usr/lib/locale/en_CA/msg.cat 
/usr/lib/locale/msg/en_CA 
/usr/share/locale/en_CA/msg.cat 
/usr/local/share/locale/en_CA/msg.cat 

Esto, de todas maneras, no funcionará si no tiene la localidad en_CA instalada porque el setlocale fallará, y "C" será sustituido por "%L" en la rutina catopen (en vez de "en_CA").

9. Más información.

Esto es todo. Espero que esta guía le haya sido útil. Debe haber probablemente un montón de sitios donde pueda buscar información adicional sobre la escritura de programas sensibles a la localidad, y documentos sobre internacionalización, y localización en general. Apuesto que si busca por la Web un poco será capaz de encontrar un montón de información. Ulrich Drepper que fue quien implementó la mayor parte del código de internacionalización y localización de GNU tiene alguna información en su página personal y puede hecharle un vistazo para comenzar. También hay alguna información en las páginas de información de libc, y por supuesto, también hay páginas de manual.