Thread (56 messages) flat view 56 messages, 5 authors, 2016-06-15

Re: [PATCH/RFC 0/5] Add internationalization support to Git

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2016-06-15 22:48:52

On Sun, May 30, 2010 at 01:46, Jonathan Nieder [off-list ref] wrote:
Hi Ævar,
Hi, and thanks for taking the time to review this.
Ævar Arnfjörð Bjarmason wrote:
quoted
    I made three strings in git-pull.sh translatable as a proof of
    concept. One problem that I ran into is that xgettext(1) seems
    very particular when picking up translation strings. It accepts
    this:

        gettext "hello world"; echo
Does ‘gettext -s "hello world"’ work, too?  (Just curious.)
No, that just makes "-s" translatable. Even options that gettext
accepts don't work either, you have to use eval_gettext "\$foo"
instead of gettext -e "\$foo". The xgettext program is quite naïve
like that.
quoted
    but not this:
[...]
quoted
        gettext <<"END";
hello world
END

    Maybe there's a way to make it play nice. But I just used a large
    multiline string as a workaround.
Not so nice, but it seems that gettext expects a message id as
an argument (i.e., it will only replace echo and not cat).
Yes. I mailed the maintainer about this. gettext would need to accept
text on STDIN and xgettext would need to find the messages for it to
work.

In the meantime we could just use multiline strings. It works for the
test suite.
quoted
    I don't know what to do about
    'die gettext' other than define a 'die_gettext' wrapper function
    and use `xgettext --keyword=die_gettext'.
Sounds sensible.
quoted
One thing I haven't done is to try to go ahead and make massive
changes to the Git source code to make everything translatable.
I am vaguely worried about performance.  Suppose a function does

       for (i = 0; i < 1000000; i++)
               printf(_("Some interesting label: %s\n"), foo(i));

Will this compile to the equivalent of

       const char *s = _("Some interesting label: %s\n");
       for (i = 0; i < 1000000; i++)
               printf(s, foo(i));

Suppose someone decides to make that change by hand (maybe the
loop is too large for the compiler to notice the potential
winnings).  Then presumably gcc cannot be able to type-check the
format any more.  Is there some way around this that avoids
both speed regressions and loss of type-safety?
Any level of indirection is of course going to be slower, there's no
way around that. I made two test programs to test this out:

test-in-loop.c:

    #include <stdio.h>
    #include <stdlib.h>
    #include <locale.h>
    #include <libintl.h>

    #define _(s) gettext(s)

    int foo(long int x) {
        return x * x;
    }

    int main(void) {
        const char *podir = "/usr/local/share/locale";
        if(!podir) puts("zomg error");
        char *ret = bindtextdomain("git", podir);
        ret = setlocale(LC_MESSAGES, "");
        ret = setlocale(LC_CTYPE, "");
        ret = textdomain("git");

        for (long int i = 0; i < 10000000; i++) {
            printf(_("Some interesting label: %ld\n"), foo(i));
        }

        return 0;
    }

test-outside-loop.c:

    #include <stdio.h>
    #include <stdlib.h>
    #include <locale.h>
    #include <libintl.h>

    #define _(s) gettext(s)

    int foo(long int x) {
        return x * x;
    }

    int main(void) {
        const char *podir = "/usr/local/share/locale";
        if(!podir) puts("zomg error");
        char *ret = bindtextdomain("git", podir);
        ret = setlocale(LC_MESSAGES, "");
        ret = setlocale(LC_CTYPE, "");
        ret = textdomain("git");

        const char *s = _("Some interesting label: %ld\n");
        for (long int i = 0; i < 10000000; i++)
            printf(s, foo(i));

        return 0;
    }

Note that I use 10 million iterations, not 1 million like in your
example.

Here's how they compile:

    $ gcc -std=c99 -o test-in-loop test-in-loop.c ; gcc -std=c99 -o
test-outside-loop test-outside-loop.c
    test-in-loop.c: In function ‘main’:
    test-in-loop.c:21: warning: format ‘%ld’ expects type ‘long int’,
but argument 2 has type ‘int’

I.e. your concerns are valid. GCC won't catch an invalid format
specifier in this case.

And even though gettext tries to make cases like these fast
(http://www.gnu.org/software/hello/manual/gettext/Optimized-gettext.html)
it's still a lot slower than hardcoded English:

    perl -MBenchmark=:all -MData::Dump=dump -E 'cmpthese(10, {
         outside => sub { system "./test-outside-loop >/dev/null" },
         inside =>  sub { system "./test-in-loop >/dev/null" },
    });'

            s/iter  inside outside
    inside    13.4      --    -83%
    outside   2.26    495%      --
Apologies if this was already answered in the earlier discussion.
What you can do (and this was covered) is to use msgfmt to check that
no translations use different format specifiers. But hopefully cases
where you have messages like these in tight loops and the message
lookup itself is a significant contributor to the program time will be
so rare as to not be an issue.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help