Localidades en Linux COMO <author>Peeter Joot, <tt>peeter_joot@vnet.ibm.com</tt> Traducción por Juan José López Mellado,<tt>laveneno@hotmail.com</tt> <date>v1.5, 21 Julio 1997, traducido el 27 julio 1998 <abstract>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). </abstract> <toc> <sect>Introducción. <p> 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. <sect>¿ Qué es "locales" ? <p> 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. <descrip> <tag> LANG</tag>Selecciona la localidad, pero puede ser superpuesta por cualquier otra variable de entorno de las LC_xxxx. <tag> LC_COLLATE</tag>Método de ordenamiento. <tag> LC_CTYPE</tag>Definiciones de caracteres, mayúsculas, minúsculas, ... Son usadas por funciones como toupper, tolower, islower, isdigit, ... <tag> LC_MONETARY</tag>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. <tag> LC_NUMERIC</tag>Separadores de millares y decimal, y la agrupación numérica esperada. <tag> LC_TIME</tag>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. <tag> LC_MESSAGES</tag>Expresiones Sí, y No. <tag> LC_ALL</tag>Activa la localidad, y se superpone a cualquier otra variable de entorno LC_xxxx. </descrip> Algunas localidades son las siguientes, y existen muchas más. <descrip> <tag>en_CA</tag>Inglés Canadiense. <tag>en_US</tag>Inglés Estadounidense. <tag>de_DE</tag>Alemán de Alemania. <tag>fr_FR</tag>Francés de Francia. <tag>es_ES</tag>Español de España. </descrip> 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: <tscreen> <verb>/* aceptar caracteres alfabéticos */ if ( (( c >= 'a') && ( c <= 'z' )) || (( c >= 'A') && ( c <= 'Z' )) ) { ... } </verb> </tscreen> 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 (<em>N. del T. el ejemplo ha sido convenientemente modificado para la localidad Española)</em>. 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 <tt>setlocale(LC_CTYPE,"C")</tt> o definir las variables de entorno <tt>LANG</tt>, <tt>LC_CTYPE</tt>, o <tt>LC_ALL</tt> 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. <sect>Notas. <p> <itemize> <item>Para poder configurar las localidades en mi máquina he tenido que hacer algunas modificaciones. Aparentemente<tt> ftp.tu-clausthal.de:/pub/linux/SLT/nls</tt> contiene una versión a.out de locale o localedef (en el fichero nlsutils-0.5.tar.gz), por si no dispone de un sistema ELF, o no quiere una copia que lo use. Probablemente haya una copia del paquete nlsutils en otros sitios, pero no los he buscado. No se si era una versión completa e independiente de locale y localedef, ni que tipo de librería libc necesitaba tener instalada. Por este motivo un buen trozo de este HOWTO es solo un recordatorio de lo que necesité hacer para actualizar libc y familia. Si lo hace, como hice yo, necesitará un sistema ELF corriendo, o actualizarse a uno al activar las localidades. <item>Todas las actualizaciones que hice son las mismas actualizaciones que deben hacerse para actualizarse de a.out a ELF. Si no lo ha hecho, o si se ha actualizado a ELF reinstalando Linux entonces debería obtener el HOWTO de ELF a partir de un sitio espejo de sunsite. Es una excelente guía, y da ayuda adicional para instalar libc, ld.so, y otras cosas actualizaciones de un sistema ELF <item>Por cada cosa que instale, lea las notas que acompañen a esa versión, o ficheros de tipo README. Si deja inestable su sistema por alguna mal interpretación de algo que diga aquí, o (espero que no) haciendo algo de lo que diga aquí, por favor no me lo reproche. <item>Instalar inadecuadamente una nueva libc, y ld.so, puede dejarle con un sistema que no arranque. Probablemente necesitará tener un disco de arranque a mano, y estar seguro de que tiene copia de seguridad de cualquier dato crítico y no reemplazable. </itemize> <sect>Qué necesita. <p> Es necesario bajarse algunas cosas de varios sitios. Todo lo que aparece aquí exceptuando los ficheros fuentes de locale puede obtenerse de <tt>sunsite.unc.edu</tt>, <tt>tsx-11.mit.edu</tt>, 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 (<em>N. del T. threads</em>), 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. <itemize> <item> <tt>locale, y los fuentes de charmap</tt> --- Esto es lo que usted compila usando localedef. <item> <tt>libc-5.4.17.bin.tar.gz</tt> --- las librerías compartidas ELF para el C y librerías matemáticas. Nótese que el programa localefef precompilado para libc 5.4.17 es aparentemente incorrecto y crea un LC_CTYPE con un número mágico incorrecto. Esto probablemente signifique que un localedef más antiguo se utilizó para hacerla distribución binaria. <item> <tt>libc-5.4.17.tar.gz</tt> --- el código fuente para las librerías compartidas ELF. Lo necesitará para compilar localedef. <item> <tt>make-3.74.tar.gz</tt> --- necesitará compilar make para incorporar un parche para arreglar el fallo de <tt>dirent</tt>. <item> <tt>release.libc-5.2.18</tt> --- Estas notas tienen el parche para hacer el make. Hace bastante que este fallo del make apareció, y probablemente no necesite preocuparse por el. <item><tt>ld.so-1.7.12+</tt> --- el enlazador dinámico. <item><tt>ELF gcc-2.7.2+</tt> --- para compilar cosas :-) <item> <tt>a el kernel ELF ( ej. 2.0.xx ) </tt> --- para compilar cosas. <item> <tt>binutils 2.6.0.2+</tt> --- para compilar cosas. </itemize> 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 <url url="ftp://dkuug.dk/i18n/WG15-collection/locales" name="dkuug.dk:/i18n/WG15-collection/locales">y <url url="ftp://dkuug.dk/i18n/WG15-collection/charmaps" name="dkuug.dk:/i18n/WG15-collection/charmaps"> respectivamente. <sect>Instalando todo. <p> Esto es lo que hice para instalar todo. Disponía de un sistema ELF (compilador, kernel, ...) instalado antes de hacer todo esto. <enum> Primero instalé el paquete binutils. <tt> tar xzf binutils-2.6.0.2.bin.tar.gz -C / </tt> <item>Después instalé el enlazador dinámico: <tscreen> <verb>tar zxf ld.so-1.7.12.tar.gz -C /usr/src cd /usr/src/ld.so-1.7.12 sh instldso.sh </verb> </tscreen> <item>A continuación instalé los binarios de libc. Véase <tt>release.libc-5.4.17</tt> para más instrucciones al respecto. <tscreen> <verb>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 / </verb> </tscreen> <item>Ahora debe ejecutar <tt>ldconfig</tt> para que se encuentren las nuevas librerías compartidas. <tt>ldconfig -v</tt>. <item>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. <tscreen> <verb>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 </verb> </tscreen> <item>Ahora puede compilarse e instalarse localedef. <tscreen> <verb>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 </verb></tscreen> <item>Ponga los mapas de caracteres donde <tt>localedef</tt> los pueda encontrar. En este punto uso los mapas de caracteres y fuentes de locale que bajé desde <tt>dkuug.dk</tt> ftp como<tt> charmaps.tar</tt>, y<tt> locales.tar</tt> respectivamente. La versión antigua de localedef (5.2.18) buscaba los fuentes de los mapas de caracteres en <tt>/usr/share/nls/charmap</tt> , pero ahora localedef busca en <tt>/usr/share/i18n/charmaps</tt> y <tt>/usr/share/i18n/locales</tt> por defecto en busca de los fuentes de los mapas de caracteres y los fuentes de locale: <tscreen> <verb>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 </verb> </tscreen> <p> 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. <tscreen> <verb> 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 </verb> </tscreen> </enum> <sect>Y ahora qué. <p> 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. <tscreen> <verb>/* test.c : una simple prueba para ver si las localidades pueden ser </verb> </tscreen> <verb>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); } </verb> Puede usar el programa <tt>locale</tt> para ver cual es su actual entorno local de variables de entorno relacionadas. <tscreen> <verb>$ # compile el programa de prueba anterior, y ejecútelo con </verb> </tscreen> <verb>$ # 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 </verb> <verb>$ # 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 </verb> <sect>Arreglando el fallo de catopen. <p> 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 <tt>/home/peeter/catalogs/de_DE</tt>. Ahora haciendo lo que sigue, sin la localidad de_DE instalada: <tscreen> <verb>export LC_MESSAGES=de_DE export NLSPATH=/home/peeter/catalogs/%L/%N.cat:$NLSPATH </verb> </tscreen> 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 <tscreen> <verb>export NLSPATH=/home/peeter/catalogs/de_DE/%N.cat:$NLSPATH </verb> </tscreen> , pero esto va en contra del propósito general de las variables de entorno de categorías de localidad. <sect>Preguntas y respuestas. <p> Esta sección podría crecer hasta un FAQ, pero todavía no lo es. <sect1>pregunta sobre msgcat <p> Soy un usuario de LINUX, y he escrito el siguiente programa de prueba: <tscreen> <verb>-------------------------------------------------------------------- #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 -------------------------------------------------------------------- </verb> </tscreen> Si uso un camino absoluto como <tt>catopen("/etc/locale/msg.cat",MCLoadBySet); </tt> , entonces obtengo el resultado correcto. Pero si uso el ejemplo anterior, catopen devuelve un -1 (error). <sect1>Respuesta sobre msgcat <p> 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: <tscreen> <verb>$ 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 </verb> </tscreen> asíarriba que si ha hecho algo de esto : <tscreen> <verb>$ export LC_MESSAGES=en_CA $ export LC_ALL=en_CA $ export LANG=en_CA </verb> </tscreen> Con la NLSPATH de arriba y el entorno especificado, la llamada <tt>catopen("msg", MCLoadBySet);</tt> debería funcionar si su catálogo de mensajes se ha copiado en alguno de estos lugares: <tscreen> <verb>/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 </verb> </tscreen> 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"). <sect>Más información. <p> 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 <url url="http://i44www.info.uni-karlsruhe.de/~drepper" name="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. </article>