gettext.con commit Merge branch 'jc/merge-tag-object' into maint (499231d)
   1/*
   2 * Copyright (c) 2010 Ævar Arnfjörð Bjarmason
   3 */
   4
   5#include "git-compat-util.h"
   6#include "gettext.h"
   7#include "strbuf.h"
   8#include "utf8.h"
   9
  10#ifndef NO_GETTEXT
  11#       include <locale.h>
  12#       include <libintl.h>
  13#       ifdef HAVE_LIBCHARSET_H
  14#               include <libcharset.h>
  15#       else
  16#               include <langinfo.h>
  17#               define locale_charset() nl_langinfo(CODESET)
  18#       endif
  19#endif
  20
  21#ifdef GETTEXT_POISON
  22int use_gettext_poison(void)
  23{
  24        static int poison_requested = -1;
  25        if (poison_requested == -1)
  26                poison_requested = getenv("GIT_GETTEXT_POISON") ? 1 : 0;
  27        return poison_requested;
  28}
  29#endif
  30
  31#ifndef NO_GETTEXT
  32static const char *charset;
  33static void init_gettext_charset(const char *domain)
  34{
  35        /*
  36           This trick arranges for messages to be emitted in the user's
  37           requested encoding, but avoids setting LC_CTYPE from the
  38           environment for the whole program.
  39
  40           This primarily done to avoid a bug in vsnprintf in the GNU C
  41           Library [1]. which triggered a "your vsnprintf is broken" error
  42           on Git's own repository when inspecting v0.99.6~1 under a UTF-8
  43           locale.
  44
  45           That commit contains a ISO-8859-1 encoded author name, which
  46           the locale aware vsnprintf(3) won't interpolate in the format
  47           argument, due to mismatch between the data encoding and the
  48           locale.
  49
  50           Even if it wasn't for that bug we wouldn't want to use LC_CTYPE at
  51           this point, because it'd require auditing all the code that uses C
  52           functions whose semantics are modified by LC_CTYPE.
  53
  54           But only setting LC_MESSAGES as we do creates a problem, since
  55           we declare the encoding of our PO files[2] the gettext
  56           implementation will try to recode it to the user's locale, but
  57           without LC_CTYPE it'll emit something like this on 'git init'
  58           under the Icelandic locale:
  59
  60               Bj? til t?ma Git lind ? /hlagh/.git/
  61
  62           Gettext knows about the encoding of our PO file, but we haven't
  63           told it about the user's encoding, so all the non-US-ASCII
  64           characters get encoded to question marks.
  65
  66           But we're in luck! We can set LC_CTYPE from the environment
  67           only while we call nl_langinfo and
  68           bind_textdomain_codeset. That suffices to tell gettext what
  69           encoding it should emit in, so it'll now say:
  70
  71               Bjó til tóma Git lind í /hlagh/.git/
  72
  73           And the equivalent ISO-8859-1 string will be emitted under a
  74           ISO-8859-1 locale.
  75
  76           With this change way we get the advantages of setting LC_CTYPE
  77           (talk to the user in his language/encoding), without the major
  78           drawbacks (changed semantics for C functions we rely on).
  79
  80           However foreign functions using other message catalogs that
  81           aren't using our neat trick will still have a problem, e.g. if
  82           we have to call perror(3):
  83
  84           #include <stdio.h>
  85           #include <locale.h>
  86           #include <errno.h>
  87
  88           int main(void)
  89           {
  90                   setlocale(LC_MESSAGES, "");
  91                   setlocale(LC_CTYPE, "C");
  92                   errno = ENODEV;
  93                   perror("test");
  94                   return 0;
  95           }
  96
  97           Running that will give you a message with question marks:
  98
  99           $ LANGUAGE= LANG=de_DE.utf8 ./test
 100           test: Kein passendes Ger?t gefunden
 101
 102           In the long term we should probably see about getting that
 103           vsnprintf bug in glibc fixed, and audit our code so it won't
 104           fall apart under a non-C locale.
 105
 106           Then we could simply set LC_CTYPE from the environment, which would
 107           make things like the external perror(3) messages work.
 108
 109           See t/t0203-gettext-setlocale-sanity.sh's "gettext.c" tests for
 110           regression tests.
 111
 112           1. http://sourceware.org/bugzilla/show_bug.cgi?id=6530
 113           2. E.g. "Content-Type: text/plain; charset=UTF-8\n" in po/is.po
 114        */
 115        setlocale(LC_CTYPE, "");
 116        charset = locale_charset();
 117        bind_textdomain_codeset(domain, charset);
 118        setlocale(LC_CTYPE, "C");
 119}
 120
 121void git_setup_gettext(void)
 122{
 123        const char *podir = getenv("GIT_TEXTDOMAINDIR");
 124
 125        if (!podir)
 126                podir = GIT_LOCALE_PATH;
 127        bindtextdomain("git", podir);
 128        setlocale(LC_MESSAGES, "");
 129        init_gettext_charset("git");
 130        textdomain("git");
 131}
 132
 133/* return the number of columns of string 's' in current locale */
 134int gettext_width(const char *s)
 135{
 136        static int is_utf8 = -1;
 137        if (is_utf8 == -1)
 138                is_utf8 = !strcmp(charset, "UTF-8");
 139
 140        return is_utf8 ? utf8_strwidth(s) : strlen(s);
 141}
 142#endif