Next Previous Contents

3. NLS στην πράξη

3.1 Παράδειγμα χρήσης του NLS

Κοιτάξτε στο κατάλογο

RedHat-6.0: /usr/share/locale (προς εξακρίβωση)
RedHat-5.x: /usr/share/locale
RedHat-4.x: /usr/share/locale (προς εξακρίβωση)
Open Linux 2.2: /usr/share/locale (προς εξακρίβωση)
SuSe 6.χ: /usr/share/locale (προς εξακρίβωση)
Slackware 4.0: /usr/share/locale (προς εξακρίβωση)
Debian 2.x: /usr/share/locale (προς εξακρίβωση)

στο σύστημά σας για διαγράμματα που αφορούν τις υποστηριζόμενες χώρες. Θα βρείτε σίγουρα γαλλικά, ισπανικά, γερμανικά, ολλανδικά κλπ.

Προχωρήστε στο υποκατάλογο es και μετά LC_MESSAGES, δηλαδή /usr/share/es/LC_MESSAGES/ και έστω ότι βρίσκετε το "fileutils.mo". Τότε, έχετε υποστήριξη για ισπανικά στο ls, mkdir κτλ. (Αν έχετε υποκατάλογο el για ελληνικά και το αντίστοιχο αρχείο στο σύστημά σας, μπορείτε να χρησιμοποιήσετε το λεκτικό αυτό αντί του es).

Tρέξτε

Τώρα τρέξτε "ls --help" και θα δείτε ισπανικά μηνύματα!

3.2 Το δοκίμασα άλλα δεν δουλεύει!

Σε αυτή την κατηγορία μπορεί να βρίσκονται οι χρήστες με παλιά έκδοση του Linux.

Στο σύστημά σας μπορεί να υπάρχουν περισσότεροι κατάλογοι με πληροφορίες για μεταφράσεις προγραμμάτων (κατάλογοι locale). Το σύνολο των καταλόγων που ελέγχονται, ορίζεται με τη μεταβλητή περιβάλλοντος NLSPATH. Πάντα υπάρχουν κάποιοι κατάλογοι που το σύστημα ερευνά εξ ορισμού, οι οποίοι έχουν προκαθοριστεί στη βασική βιβλιοθήκη του συστήματός σας, τη libc. Δοκιμάστε "strace -o output.log ls --help" και μετά "grep open output.log" για να δείτε που ψάχνει το σύστημα. Μετά καθορίστε ανάλογα το NLSPATH.

3.3 Με ποιο(ποιους) τρόπους καθορίζεται η γλώσσα με την οποία εμφανίζονται τα μηνύματα ενός συστήματος;

Μέσω μεταβλητών περιβάλλοντος. Για κάποιο λόγο, υπάρχει ένας αριθμός από μεταβλητές που κάνουν σχεδόν την ίδια δουλειά. Η μεταβλητή που φαίνετε να χρησιμοποιούν οι περισσότεροι χρήστες είναι η LANG, με τη μορφή

Διαφορετικά, υπάρχει η μεταβλητή LANGUAGE, η οποία έχει προτεραιότητα έναντι της LANG.

Για τα συστήματα με glibc 2.1 (αυτή τη στιγμή RedHat 6.0), ο χρήστης μπορεί να εκτελέσει (προς εξακρίβωση)

3.4 Μετάπτωση γλωσσών σε περίπτωση απουσίας μεταφράσεων

Tρέξτε

Σε όλες τις περιπτώσεις, υπάρχει η τελική μετάπτωση στην αγγλική.

3.5 Προγραμματιστικές πληροφορίες

Το παρακάτω υλικό προέρχεται από το Νίκο Μαυρογιαννόπουλο.

Μιας και το nls χρησιμοποιείται όλο και περισσότερο στα νέα προγράμματα (ιδιαίτερα αυτά που αποτελούν το GNU σύστημα), είναι αναγκαίο να κάνουμε μια περιγραφή της διαδικασίας που χρειάζεται για να το ενσωματώσουμε και στα δικά μας προγράμματα.

Η άδεια του gettext για τα κομμάτια που θα ενσωματώσουμε στο πρόγραμμά μας είναι η GNU LGPL που δεν μας περιορίζει στον καθορισμό της άδειας του προγράμματός μας. Κοιτάξτε την πάντως για να δείτε αν είστε συμβατοί(!).

Ας προσπεράσουμε όμως τα "τυπικά" και ας περάσουμε στο ψητό. Τα προγράμματα που θα χρειαστούμε είναι τα GNU autoconf και gettext-0.10, και βρίσκονται στο πιο κοντινό gnu mirror.

Εδώ θα προσπαθήσουμε να προσθέσουμε υποστήριξη εθνικής/τοπικής γλώσσας (nls) σε ένα πρόγραμμα γραμμένο σε C. Ας δούμε ένα απλό πρόγραμμα, που τυπώνει την ώρα στην οθόνη:


#include <stdio.h>
#include <time.h>

main ()
{
        time_t tv;

        printf( "The time is: " );
        time(&tv);
        printf ( "%s",ctime(&tv) );

}

Η ώρα όπως και το μήνυμα τυπώνεται στα αγγλικά. Επειδή η μετάφραση πρέπει να γίνει γενικά - δηλαδή το ίδιο πρόγραμμα να τρέχει και στα γερμανικά και στα ελληνικά και σε οποιαδήποτε άλλη γλώσσα - δεν μεταφράζουμε απ'ευθείας το κείμενο. Χρησιμοποιούμε τα localedata και τη βιβλιοθήκη intl της C βιβλιοθήκης.

Ας χρησιμοποιήσουμε τα localedata (τοπικά δεδομένα) για την ώρα.

Το ίδιο πρόγραμμα τώρα γίνεται:


#include <stdio.h>
#include <time.h>
#include <locale.h>

main ()
{
        struct tm *tp;
        char buf[80];
        time_t date;
        
/* Θέτει το locale σύμφωνα με την μεταβλητή περιβάλλοντος LANG ή LANGUAGE
 * Για τα ελληνικά πρέπει να είναι el (ISO639) */
        setlocale (LC_TIME, ""); /* Για την ώρα και μόνο */
                         
        printf( "The time is: " );

        time(&date);

/* Μετατρέπει την ώρα έκφραση που συνδέεται με την ζώνη ώρας */
        tp = localtime(&date);
        strftime(buf, sizeof buf, "%a %b %e %H:%M:%S %z %Y", tp);

/* εκτυπώνει την ώρα */
        printf ("%s\n",buf);

}

Η ώρα τώρα θα τυπωθεί στην μορφή: Τετ Νοέ 11 22:44:29 +0200 1998 με ελληνικούς χαρακτήρες όπως βλέπετε(αν LANG=el ή gr για τις παλιές glibc2). Γι'αυτό ξεχάστε το ctime και ασχοληθείτε με το strftime().

Σε περίπτωση που δεν είδατε ελληνικούς χαρακτήρες ελέγξτε αν στο /usr/share/locale/el υπάρχουν τα απαραίτητα αρχεία. Αν δεν υπάρχουν προμηθευτείτε ένα νέο localedata - συμπεριλαμβάνεται συνήθως στην libc ή εγκαταστήστε το tarball που βρίσκεται στο ftp://argeas.cs-net.gr/pub/unix/linux/GREEK/locale.glibc2.el.tar.gz

Για την μετάφραση των μηνυμάτων, που είναι και η κυριότερη ιδιότητα του NLS χρησιμοποιείται ο εξής τρόπος:


#include <stdio.h>
#include <time.h>
#include <locale.h>
#include <libintl.h> /* αρχείο της GNU libc */

main ()
{
        struct tm *tp;
        char buf[80];
        time_t date;
        
/* πέρα απο το LC_ALL υπάρχουν τα LC_TIME, LC_MESSAGES κλπ, τα οποία
 * προσδιορίζουν επ'ακριβώς τι μεταφράσεις θα χρησιμοποιήσουμε.
 */
        setlocale (LC_ALL, ""); 

        bindtextdomain ("my_time", "/usr/share/locale");
        textdomain ("my_time");
                          
        printf( gettext("The time is: ") );

        time(&date);
        tp = localtime(&date);
        strftime(buf, sizeof buf, "%a %b %e %H:%M:%S %z %Y", tp);

        printf ("%s\n",buf);

}

Οι εντολές bindtextdomain() και textdomain() αφορούν την nls βιβλιοθήκη (libintl) και την πληροφορούν ότι η μετάφραση του προγράμματος βρίσκεται στο my_time.mo στον κατάλογο /usr/share/locale/XX, όπου ΧΧ η γλώσσα του χρήστη (καθορίζεται απο την μεταβλητή LANG ή LANGUAGE).

Επειδή πολλές φορές το να γράφουμε gettext(...) είναι επίπονο για πολλά μηνύματα, χρησιμοποιούμε το:


        #define _(Text) gettext(Text) /* στις επικεφαλίδες */

        printf( _("The time is: ") );

Το πρόγραμμα δεν θα λειτουργήσει ακόμη με ελληνικά. Χρειάζεται ακόμη τη δημιουργία του .po αρχείου. Αυτό είναι σχετικά απλό αν γίνει με το πρόγραμμα xgettext (από το πακέτο GNU gettext).

Για το συγκεκριμένο πρόγραμμα η έξοδος του xgettext είναι: (σύνταξη "xgettext my_time.c")


# messages.po
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR Free Software Foundation, Inc.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 1998-11-11 22:52+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: ENCODING\n"

#: c.c:16
msgid "The time is: "
msgstr ""

#end of messages.po

και εδώ μετονομάζουμε το messages.po σε el.po, προσθέτοντας ταυτόχρονα στο msgstr την μετάφραση, πχ:
msgstr "Η ώρα είναι: "

Τελευταίο μέλημα του προγραμματιστή είναι να μετατρέψει αυτό το .po αρχείο σε .mo ή .gmo μορφή. Αυτό επιτυγχάνεται με το πρόγραμμα msgfmt (από το GNU gettext πάλι).


πχ msgfmt el.po -o el.gmo

και μετά να αντιγραφεί στο /usr/share/locale/el/LC_MESSAGES/ το el.gmo σαν my_time.mo (αυτά τα τελευταία καλό είναι να γίνουν μέσω κάποιου makefile).

Το σημαντικότερο τώρα είναι να αυτοματοποιήσουμε αυτή τη διαδικασία. Αυτό επιτυγχάνεται με το GNU autoconf. Ας φτιάξουμε ένα configure.in που θα ελέγχει κατά πόσο υπάρχει υποστήριξη nls από την libc και καθώς και του strftime. (για καλύτερες διαμορφώσεις κοιτάξτε στο: http://teamball.sdsu.edu/doc/texi/gettext_toc.html)

Για να απλοποιήσουμε τα πράγματα θεωρούμε την εξής διαμόρφωση των αρχείων του προγράμματος: (έστω ότι το πρόγραμμα είναι στο /xxx) στον κατάλογο /xxx/

configure.in: Χρειάζεται για το autoconf (δείτε παρακάτω)
config.h.in : (δείτε παρακάτω)
στον κατάλογο /xxx/src/
my_time.c   : Το πρόγραμμα

στον κατάλογο /xxx/po/

my_time.pot :  Αυτό είναι το messages.po που δημιουργείται απο το
               xgettext απλώς μετονομασμένο
el.po       :  Το my_time.pot μεταφρασμένο στα ελληνικά
POTFILES.in :  Εδώ προσθέτετε όλα τα .c αρχεία στο src που χρησιμοποιούν
               το gettext. πχ:
               /xxx/src/my_time.c

Αρκεί τώρα να αντιγράψετε απο το πακέτο gettext-0.10 τα po/Makefile.in στον κατάλογο του προγράμματός /xxx/po/ , όλο τον κατάλογο intl/ στον /xxx/intl/ και τα ABOUT-NLS, aclocal.m4 στον /xxx .

Πάμε τώρα στον πρωταρχικό κατάλογο του προγράμματός μας (/xxx) και ας φτιάξουμε το configure.in απο το οποίο θα προκύψει το γνωστό(;) script configure.


# configure.in for my_time.c
AC_INIT()
AC_CONFIG_HEADER(config.h)

AC_PROG_CC
AC_PATH_PROG(MAKE,make)
AC_PROG_INSTALL

VERSION=1.0
PROGRAMS="my_time"

AC_PREFIX_DEFAULT(/usr/local)
AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE")
AC_DEFINE_UNQUOTED(VERSION, "$VERSION")
AC_SUBST(PACKAGE)
AC_SUBST(VERSION)
AC_SUBST(PROGRAMS)

dnl υποστηρίζουμε ελληνικά (το el είναι απο το iso639 που πρέπει να
dnl χρησιμοποιείται για την ένδειξη γλώσσας στις μεταφράσεις σύμφωνα με το
dnl εγχειρίδιο το gettext.)
ALL_LINGUAS="el"

dnl Για το gettext 0.10.
ud_GNU_GETTEXT

AC_LINK_FILES($nls_cv_header_libgt, $nls_cv_header_intl)

AC_FUNC_STRFTIME

AC_OUTPUT([Makefile src/Makefile intl/Makefile po/Makefile.in
[sed -e "/POTFILES =/r po/POTFILES" po/Makefile.in > po/Makefile])

#end of configure.in

και αμέσως μετά δημιουργούμε ένα config.h.in (είναι βάση του αρχείου config.h που θα συμπεριλαμβάνεται στο πρόγραμμά μας. Η δημιουργία του config.h γίνεται απο το script configure.) Το config.h.in θα περιέχει:


# config.h.in for my_time.c
/* Το όνομα του πακέτου (θα χρησιμοποιηθεί για το textdomain) */
#undef PACKAGE
#undef VERSION

/* strftime */
#undef HAVE_STRFTIME
#undef ENABLE_NLS

#end of config.h.in

Το Makefile.in για το πρόγραμμα (το Makefile δημιουργείται επίσης αυτόματα απο το configure), πρέπει να έχει σε γενικές γραμμές τα:


# Makefile.in
CC      = @CC@
LIBS    = @LIBS@
CCOPTS  = @CFLAGS@ -I. -I..
LN      = @LN_S@
INSTALL = @INSTALL@
prefix  = @prefix@
exec_prefix  = @prefix@
datadir = $(prefix)/lib
bin     = $(prefix)/bin
localedir = $(datadir)/locale
DEFS    = -DLOCALEDIR=\"$(localedir)\"
SUBDIRS = @INTLSUB@ src @POSUB@
MAKE    = @MAKE@
INSTALL = @INSTALL@

#Εάν ο κώδικας σας δεν είναι στον src/ κατάλογο χρειάζεται ορισμένες αλλαγές
all:
        @for subdir in $(SUBDIRS); do \
                echo making all in $$subdir; \
                (cd $$subdir && $(MAKE) all) \
                || case "$(MFLAGS)" in *k*) fail=yes;; *) exit 1;; esac; \
        done && test -z "$$fail"

install:
        @$(INSTALL) my_time $(bin)
        @$(MAKE) -C po/ install

# end of makefile.in


# src/Makefile.in
# Εδώ οι επικεφαλίδες είναι περίπου ίδιες με πριν:
CC      = @CC@
LIBS    = @LIBS@
CCOPTS  = @CFLAGS@ -I../intl -I. -I..
LN      = @LN_S@
INSTALL = @INSTALL@
prefix  = @prefix@
exec_prefix  = @prefix@
datadir = $(prefix)/lib
localedir = $(datadir)/locale
DEFS    = -DLOCALEDIR=\"$(localedir)\"

all: my_time

my_time: my_time.o
        $(CC) $(OBJECTS) -o ../my_time $(LIBDIRS) $(LIBS)

my_time.o: my_time.c
        $(CC) -c my_time.c $(CCOPTS) $(DEFS)

#end of src/Makefile.in

Το πρόγραμμα τώρα θα γίνει:


# src/my_time.c

#include <stdio.h>
#include <time.h>
#include <config.h> /* ή "config.h" αν δεν βάλετε το -I. στο Makefile */

#ifdef ENABLE_NLS
#include <libintl.h>
#endif

main ()
{
#ifdef HAVE_STRFTIME
        struct tm *tp;
        char buf[80];
        time_t date;
#else
        time_t tv;
#endif


#ifdef ENABLE_NLS
        setlocale (LC_ALL, "");
        bindtextdomain (PACKAGE, LOCALEDIR);
        textdomain (PACKAGE);
# define _(Text) gettext(Text)
#else
# define _(Text) (Text)
#endif
                          

        printf( _("The time is: ") );

#ifdef HAVE_STRFTIME_H
        time(&date);
        tp = localtime(&date);
        strftime(buf, sizeof buf, "%a %b %e %H:%M:%S %z %Y", tp);
        printf ("%s\n",buf);
#else
        time(&tv);
        printf ( "%s",ctime(&tv) );
#endif

}

#end of src/my_time.c

Τώρα τρέχουμε το autoconf που δημιουργεί το configure script. Aν όλα πήγαν καλά έχουμε ένα πρόγραμμα που μιλάει ξένες γλώσσες!

Το παραπάνω κείμενο είναι μια εισαγωγή μόνο στο θέμα. Δεν είναι σε καμιά περίπτωση πλήρες, αν θέλετε παραπάνω πληροφορίες συμβουλευτείτε τα εγχειρίδια των προγραμμάτων autoconf, gettext. Περισσότερα για το gettext (και την χρήση του με το autoconf) στο: http://teamball.sdsu.edu/doc/texi/gettext_toc.html


Next Previous Contents