All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH/RFC 0/5] Add internationalization support to Git
@ 2010-05-29 22:45 Ævar Arnfjörð Bjarmason
  2010-05-29 22:45 ` [PATCH 1/5] Add infrastructure for translating Git with gettext Ævar Arnfjörð Bjarmason
                   ` (20 more replies)
  0 siblings, 21 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-05-29 22:45 UTC (permalink / raw)
  To: git; +Cc: Jeff Epler, Ævar Arnfjörð Bjarmason

Here's an updated version of the patch series begun by Jeff Epler to
make Git optionally translatable with GNU gettext. I'm submitting it
now because I'd like to get some comments on the approach, and
specifically whether something like this is likely to be accepted into
Git.

Changes since the initial submission:

  * Merged in changes to wt-status.c from the master branch

  * Made Gettext play nice with the build system (.gitignore & make
    clean)

  * Added support for making Git's shell scripts translatable by
    importing the gettext functions in git-sh-setup.sh, or supplying
    replacements if they don't work. Comments from shellscript-savy
    list members about whether the fallbacks work on odd non-GNU
    shells are especially welcome.

    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

    but not this:

        die gettext "hello world"; die

    or this:

        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. I don't know what to do about
    'die gettext' other than define a 'die_gettext' wrapper function
    and use `xgettext --keyword=die_gettext'.

  * There's now a skeleton is.po file. It just has one string
    translated. I can't figure out what to call any of the Git core
    concepts so I'm stuck :)

Next up:

  * Perl support. I haven't yet found what the best way of using
    libintl from Perl would be. GNU recommends Locale::TextDomain, but
    Debian only seems to have Locale::gettext which is a more
    lightweight wrapper.

    In any case I can write a wrapper for those/the shell command and
    fall back on the hardcoded strings like the C/Shell code does.

Most importantly. If there are any interested list members that want
to translate Git, it's now ready for translators. You just have to add
a po/$lang.po file with your favorite editor and Git's build system
will install it.

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. Due to
their nature it's a big headache to maintain changes like that if
anything moves upstream.

Of course that's subject to change if other people are interested in
contributing.

For reference, here's the branch on GitHub I'm hacking this in,
patches welcome:

    http://github.com/avar/git/compare/master...topic/git-gettext

Jeff Epler (1):
  Add infrastructure for translating Git with gettext

Ævar Arnfjörð Bjarmason (4):
  gitignore: Ignore files generated by gettext
  Makefile: Remove Gettext files on make clean
  gettext: Add a skeleton po/is.po
  Add infrastructure to make shellscripts translatable

 .gitignore      |    1 +
 Makefile        |   35 +++++++-
 gettext.c       |   17 ++++
 gettext.h       |   15 +++
 git-pull.sh     |   15 ++--
 git-sh-setup.sh |   33 +++++++
 git.c           |    3 +
 po/.gitignore   |    1 +
 po/is.po        |  288 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 wt-status.c     |  129 +++++++++++++------------
 10 files changed, 465 insertions(+), 72 deletions(-)
 create mode 100644 gettext.c
 create mode 100644 gettext.h
 create mode 100644 po/.gitignore
 create mode 100644 po/is.po

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH 1/5] Add infrastructure for translating Git with gettext
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
@ 2010-05-29 22:45 ` Ævar Arnfjörð Bjarmason
  2010-05-29 22:45 ` [PATCH 2/5] gitignore: Ignore files generated by gettext Ævar Arnfjörð Bjarmason
                   ` (19 subsequent siblings)
  20 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-05-29 22:45 UTC (permalink / raw)
  To: git; +Cc: Jeff Epler, Ævar Arnfjörð Bjarmason

From: Jeff Epler <jepler@unpythonic.net>

Change the build process to use GNU's libintl if it's available. If
not we define our own skeleton replacement functions which degrade
gracefully to English.

Signed-off-by: Jeff Epler <jepler@unpythonic.net>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Makefile    |   26 ++++++++++++
 gettext.c   |   17 ++++++++
 gettext.h   |   15 +++++++
 git.c       |    3 +
 wt-status.c |  129 ++++++++++++++++++++++++++++++-----------------------------
 5 files changed, 126 insertions(+), 64 deletions(-)
 create mode 100644 gettext.c
 create mode 100644 gettext.h

diff --git a/Makefile b/Makefile
index d5d6565..7989121 100644
--- a/Makefile
+++ b/Makefile
@@ -297,6 +297,8 @@ RPMBUILD = rpmbuild
 TCL_PATH = tclsh
 TCLTK_PATH = wish
 PTHREAD_LIBS = -lpthread
+XGETTEXT = xgettext
+MSGFMT = msgfmt
 
 export TCL_PATH TCLTK_PATH
 
@@ -523,6 +525,7 @@ LIB_H += userdiff.h
 LIB_H += utf8.h
 LIB_H += xdiff-interface.h
 LIB_H += xdiff/xdiff.h
+LIB_H += gettext.h
 
 LIB_OBJS += abspath.o
 LIB_OBJS += advice.o
@@ -564,6 +567,7 @@ LIB_OBJS += entry.o
 LIB_OBJS += environment.o
 LIB_OBJS += exec_cmd.o
 LIB_OBJS += fsck.o
+LIB_OBJS += gettext.o
 LIB_OBJS += graph.o
 LIB_OBJS += grep.o
 LIB_OBJS += hash.o
@@ -1386,6 +1390,12 @@ ifdef USE_NED_ALLOCATOR
        COMPAT_OBJS += compat/nedmalloc/nedmalloc.o
 endif
 
+ifdef NO_GETTEXT
+	COMPAT_CFLAGS += -DNO_GETTEXT
+else
+	LIBINTL = -lintl
+endif
+
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK=NoThanks
 endif
@@ -1415,6 +1425,7 @@ ifndef V
 	QUIET_BUILT_IN = @echo '   ' BUILTIN $@;
 	QUIET_GEN      = @echo '   ' GEN $@;
 	QUIET_LNCP     = @echo '   ' LN/CP $@;
+	QUIET_MSGFMT   = @echo '   ' MSGFMT $@;
 	QUIET_SUBDIR0  = +@subdir=
 	QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
 			 $(MAKE) $(PRINT_DIR) -C $$subdir
@@ -1442,6 +1453,7 @@ gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
 template_dir_SQ = $(subst ','\'',$(template_dir))
 htmldir_SQ = $(subst ','\'',$(htmldir))
 prefix_SQ = $(subst ','\'',$(prefix))
+sharedir_SQ = $(subst ','\'',$(sharedir))
 
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
@@ -1868,6 +1880,17 @@ cscope:
 	$(RM) cscope*
 	$(FIND) . -name '*.[hcS]' -print | xargs cscope -b
 
+pot:
+	$(XGETTEXT) -k_ -o po/git.pot $(C_OBJ:o=c)
+
+POFILES := $(wildcard po/*.po)
+MOFILES := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/git.mo,$(POFILES))
+MODIRS := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/,$(POFILES))
+all:: $(MOFILES)
+share/locale/%/LC_MESSAGES/git.mo: po/%.po
+	@mkdir -p $(dir $@)
+	$(QUIET_MSGFMT)$(MSGFMT) -o $@ $<
+
 ### Detect prefix changes
 TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
              $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
@@ -1980,6 +2003,9 @@ install: all
 	$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 	$(INSTALL) -m 644 $(SCRIPT_LIB) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 	$(INSTALL) $(install_bindir_programs) '$(DESTDIR_SQ)$(bindir_SQ)'
+	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sharedir_SQ)/locale'
+	(cd share && tar cf - locale) | \
+		(cd '$(DESTDIR_SQ)$(sharedir_SQ)' && umask 022 && tar xof -)
 	$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
 ifndef NO_PERL
 	$(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
diff --git a/gettext.c b/gettext.c
new file mode 100644
index 0000000..aadce19
--- /dev/null
+++ b/gettext.c
@@ -0,0 +1,17 @@
+#ifdef NO_GETTEXT
+void git_setup_gettext() {}
+#else
+#include "exec_cmd.h"
+#include <libintl.h>
+#include <stdlib.h>
+
+void git_setup_gettext() {
+    const char *podir = system_path("share/locale");
+    if(!podir) return;
+    char *ret = bindtextdomain("git", podir);
+    free((void*)podir);
+    ret = setlocale(LC_MESSAGES, "");
+    ret = setlocale(LC_CTYPE, "");
+    ret = textdomain("git");
+}
+#endif
diff --git a/gettext.h b/gettext.h
new file mode 100644
index 0000000..8b221b4
--- /dev/null
+++ b/gettext.h
@@ -0,0 +1,15 @@
+#ifndef GETTEXT_H
+#define GETTEXT_H
+
+void git_setup_gettext();
+
+#ifdef NO_GETTEXT
+#define _(s) (s)
+#define N_(s) (s)
+#else
+#include <libintl.h>
+#define _(s) gettext(s)
+#define N_(s) (s)
+#endif
+
+#endif
diff --git a/git.c b/git.c
index 99f0363..d749eab 100644
--- a/git.c
+++ b/git.c
@@ -3,6 +3,7 @@
 #include "cache.h"
 #include "quote.h"
 #include "run-command.h"
+#include "gettext.h"
 
 const char git_usage_string[] =
 	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n"
@@ -490,6 +491,8 @@ int main(int argc, const char **argv)
 	if (!cmd)
 		cmd = "git-help";
 
+	git_setup_gettext();
+
 	/*
 	 * "git-xxxx" is the same as "git xxxx", but we obviously:
 	 *
diff --git a/wt-status.c b/wt-status.c
index 14e0acc..70b4293 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -9,6 +9,7 @@
 #include "quote.h"
 #include "run-command.h"
 #include "remote.h"
+#include "gettext.h"
 
 static char default_wt_status_colors[][COLOR_MAXLEN] = {
 	GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
@@ -49,33 +50,33 @@ static void wt_status_print_unmerged_header(struct wt_status *s)
 {
 	const char *c = color(WT_STATUS_HEADER, s);
 
-	color_fprintf_ln(s->fp, c, "# Unmerged paths:");
+	color_fprintf_ln(s->fp, c, _("# Unmerged paths:"));
 	if (!advice_status_hints)
 		return;
 	if (s->in_merge)
 		;
 	else if (!s->is_initial)
-		color_fprintf_ln(s->fp, c, "#   (use \"git reset %s <file>...\" to unstage)", s->reference);
+		color_fprintf_ln(s->fp, c, _("#   (use \"git reset %s <file>...\" to unstage)"), s->reference);
 	else
-		color_fprintf_ln(s->fp, c, "#   (use \"git rm --cached <file>...\" to unstage)");
-	color_fprintf_ln(s->fp, c, "#   (use \"git add/rm <file>...\" as appropriate to mark resolution)");
-	color_fprintf_ln(s->fp, c, "#");
+		color_fprintf_ln(s->fp, c, _("#   (use \"git rm --cached <file>...\" to unstage)"));
+	color_fprintf_ln(s->fp, c, _("#   (use \"git add/rm <file>...\" as appropriate to mark resolution)"));
+	color_fprintf_ln(s->fp, c, _("#"));
 }
 
 static void wt_status_print_cached_header(struct wt_status *s)
 {
 	const char *c = color(WT_STATUS_HEADER, s);
 
-	color_fprintf_ln(s->fp, c, "# Changes to be committed:");
+	color_fprintf_ln(s->fp, c, _("# Changes to be committed:"));
 	if (!advice_status_hints)
 		return;
 	if (s->in_merge)
 		; /* NEEDSWORK: use "git reset --unresolve"??? */
 	else if (!s->is_initial)
-		color_fprintf_ln(s->fp, c, "#   (use \"git reset %s <file>...\" to unstage)", s->reference);
+		color_fprintf_ln(s->fp, c, _("#   (use \"git reset %s <file>...\" to unstage)"), s->reference);
 	else
-		color_fprintf_ln(s->fp, c, "#   (use \"git rm --cached <file>...\" to unstage)");
-	color_fprintf_ln(s->fp, c, "#");
+		color_fprintf_ln(s->fp, c, _("#   (use \"git rm --cached <file>...\" to unstage)"));
+	color_fprintf_ln(s->fp, c, _("#"));
 }
 
 static void wt_status_print_dirty_header(struct wt_status *s,
@@ -84,17 +85,17 @@ static void wt_status_print_dirty_header(struct wt_status *s,
 {
 	const char *c = color(WT_STATUS_HEADER, s);
 
-	color_fprintf_ln(s->fp, c, "# Changed but not updated:");
+	color_fprintf_ln(s->fp, c, _("# Changed but not updated:"));
 	if (!advice_status_hints)
 		return;
 	if (!has_deleted)
-		color_fprintf_ln(s->fp, c, "#   (use \"git add <file>...\" to update what will be committed)");
+		color_fprintf_ln(s->fp, c, _("#   (use \"git add <file>...\" to update what will be committed)"));
 	else
-		color_fprintf_ln(s->fp, c, "#   (use \"git add/rm <file>...\" to update what will be committed)");
-	color_fprintf_ln(s->fp, c, "#   (use \"git checkout -- <file>...\" to discard changes in working directory)");
+		color_fprintf_ln(s->fp, c, _("#   (use \"git add/rm <file>...\" to update what will be committed)"));
+	color_fprintf_ln(s->fp, c, _("#   (use \"git checkout -- <file>...\" to discard changes in working directory)"));
 	if (has_dirty_submodules)
-		color_fprintf_ln(s->fp, c, "#   (commit or discard the untracked or modified content in submodules)");
-	color_fprintf_ln(s->fp, c, "#");
+		color_fprintf_ln(s->fp, c, _("#   (commit or discard the untracked or modified content in submodules)"));
+	color_fprintf_ln(s->fp, c, _("#"));
 }
 
 static void wt_status_print_other_header(struct wt_status *s,
@@ -102,16 +103,16 @@ static void wt_status_print_other_header(struct wt_status *s,
 					 const char *how)
 {
 	const char *c = color(WT_STATUS_HEADER, s);
-	color_fprintf_ln(s->fp, c, "# %s files:", what);
+	color_fprintf_ln(s->fp, c, _("# %s files:"), what);
 	if (!advice_status_hints)
 		return;
-	color_fprintf_ln(s->fp, c, "#   (use \"git %s <file>...\" to include in what will be committed)", how);
-	color_fprintf_ln(s->fp, c, "#");
+	color_fprintf_ln(s->fp, c, _("#   (use \"git %s <file>...\" to include in what will be committed)"), how);
+	color_fprintf_ln(s->fp, c, _("#"));
 }
 
 static void wt_status_print_trailer(struct wt_status *s)
 {
-	color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
+	color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("#"));
 }
 
 #define quote_path quote_path_relative
@@ -122,20 +123,20 @@ static void wt_status_print_unmerged_data(struct wt_status *s,
 	const char *c = color(WT_STATUS_UNMERGED, s);
 	struct wt_status_change_data *d = it->util;
 	struct strbuf onebuf = STRBUF_INIT;
-	const char *one, *how = "bug";
+	const char *one, *how = _("bug");
 
 	one = quote_path(it->string, -1, &onebuf, s->prefix);
-	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
+	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), _("#\t"));
 	switch (d->stagemask) {
-	case 1: how = "both deleted:"; break;
-	case 2: how = "added by us:"; break;
-	case 3: how = "deleted by them:"; break;
-	case 4: how = "added by them:"; break;
-	case 5: how = "deleted by us:"; break;
-	case 6: how = "both added:"; break;
-	case 7: how = "both modified:"; break;
+	case 1: how = _("both deleted:"); break;
+	case 2: how = _("added by us:"); break;
+	case 3: how = _("deleted by them:"); break;
+	case 4: how = _("added by them:"); break;
+	case 5: how = _("deleted by us:"); break;
+	case 6: how = _("both added:"); break;
+	case 7: how = _("both modified:"); break;
 	}
-	color_fprintf(s->fp, c, "%-20s%s\n", how, one);
+	color_fprintf(s->fp, c, _("%-20s%s\n"), how, one);
 	strbuf_release(&onebuf);
 }
 
@@ -161,13 +162,13 @@ static void wt_status_print_change_data(struct wt_status *s,
 		break;
 	case WT_STATUS_CHANGED:
 		if (d->new_submodule_commits || d->dirty_submodule) {
-			strbuf_addstr(&extra, " (");
+			strbuf_addstr(&extra, _(" ("));
 			if (d->new_submodule_commits)
-				strbuf_addf(&extra, "new commits, ");
+				strbuf_addf(&extra, _("new commits, "));
 			if (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
-				strbuf_addf(&extra, "modified content, ");
+				strbuf_addf(&extra, _("modified content, "));
 			if (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
-				strbuf_addf(&extra, "untracked content, ");
+				strbuf_addf(&extra, _("untracked content, "));
 			strbuf_setlen(&extra, extra.len - 2);
 			strbuf_addch(&extra, ')');
 		}
@@ -178,40 +179,40 @@ static void wt_status_print_change_data(struct wt_status *s,
 	one = quote_path(one_name, -1, &onebuf, s->prefix);
 	two = quote_path(two_name, -1, &twobuf, s->prefix);
 
-	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
+	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), _("#\t"));
 	switch (status) {
 	case DIFF_STATUS_ADDED:
-		color_fprintf(s->fp, c, "new file:   %s", one);
+		color_fprintf(s->fp, c, _("new file:   %s"), one);
 		break;
 	case DIFF_STATUS_COPIED:
-		color_fprintf(s->fp, c, "copied:     %s -> %s", one, two);
+		color_fprintf(s->fp, c, _("copied:     %s -> %s"), one, two);
 		break;
 	case DIFF_STATUS_DELETED:
-		color_fprintf(s->fp, c, "deleted:    %s", one);
+		color_fprintf(s->fp, c, _("deleted:    %s"), one);
 		break;
 	case DIFF_STATUS_MODIFIED:
-		color_fprintf(s->fp, c, "modified:   %s", one);
+		color_fprintf(s->fp, c, _("modified:   %s"), one);
 		break;
 	case DIFF_STATUS_RENAMED:
-		color_fprintf(s->fp, c, "renamed:    %s -> %s", one, two);
+		color_fprintf(s->fp, c, _("renamed:    %s -> %s"), one, two);
 		break;
 	case DIFF_STATUS_TYPE_CHANGED:
-		color_fprintf(s->fp, c, "typechange: %s", one);
+		color_fprintf(s->fp, c, _("typechange: %s"), one);
 		break;
 	case DIFF_STATUS_UNKNOWN:
-		color_fprintf(s->fp, c, "unknown:    %s", one);
+		color_fprintf(s->fp, c, _("unknown:    %s"), one);
 		break;
 	case DIFF_STATUS_UNMERGED:
-		color_fprintf(s->fp, c, "unmerged:   %s", one);
+		color_fprintf(s->fp, c, _("unmerged:   %s"), one);
 		break;
 	default:
-		die("bug: unhandled diff status %c", status);
+		die(_("bug: unhandled diff status %c"), status);
 	}
 	if (extra.len) {
-		color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "%s", extra.buf);
+		color_fprintf(s->fp, color(WT_STATUS_HEADER, s), _("%s"), extra.buf);
 		strbuf_release(&extra);
 	}
-	fprintf(s->fp, "\n");
+	fprintf(s->fp, _("\n"));
 	strbuf_release(&onebuf);
 	strbuf_release(&twobuf);
 }
@@ -618,14 +619,14 @@ void wt_status_print(struct wt_status *s)
 	const char *branch_color = color(WT_STATUS_HEADER, s);
 
 	if (s->branch) {
-		const char *on_what = "On branch ";
+		const char *on_what = _("On branch ");
 		const char *branch_name = s->branch;
 		if (!prefixcmp(branch_name, "refs/heads/"))
 			branch_name += 11;
 		else if (!strcmp(branch_name, "HEAD")) {
 			branch_name = "";
 			branch_color = color(WT_STATUS_NOBRANCH, s);
-			on_what = "Not currently on any branch.";
+			on_what = _("Not currently on any branch.");
 		}
 		color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "# ");
 		color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name);
@@ -634,9 +635,9 @@ void wt_status_print(struct wt_status *s)
 	}
 
 	if (s->is_initial) {
-		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
-		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "# Initial commit");
-		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
+		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("#"));
+		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("# Initial commit"));
+		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("#"));
 	}
 
 	wt_status_print_updated(s);
@@ -647,38 +648,38 @@ void wt_status_print(struct wt_status *s)
 		wt_status_print_submodule_summary(s, 1);  /* unstaged */
 	}
 	if (s->show_untracked_files) {
-		wt_status_print_other(s, &s->untracked, "Untracked", "add");
+		wt_status_print_other(s, &s->untracked, _("Untracked"), _("add"));
 		if (s->show_ignored_files)
-			wt_status_print_other(s, &s->ignored, "Ignored", "add -f");
+			wt_status_print_other(s, &s->ignored, _("Ignored"), _("add -f"));
 	} else if (s->commitable)
-		fprintf(s->fp, "# Untracked files not listed%s\n",
+		fprintf(s->fp, _("# Untracked files not listed%s\n"),
 			advice_status_hints
-			? " (use -u option to show untracked files)" : "");
+			? _(" (use -u option to show untracked files)") : "");
 
 	if (s->verbose)
 		wt_status_print_verbose(s);
 	if (!s->commitable) {
 		if (s->amend)
-			fprintf(s->fp, "# No changes\n");
+			fprintf(s->fp, _("# No changes\n"));
 		else if (s->nowarn)
 			; /* nothing */
 		else if (s->workdir_dirty)
-			printf("no changes added to commit%s\n",
+			printf(_("no changes added to commit%s\n"),
 				advice_status_hints
-				? " (use \"git add\" and/or \"git commit -a\")" : "");
+				? _(" (use \"git add\" and/or \"git commit -a\")") : "");
 		else if (s->untracked.nr)
-			printf("nothing added to commit but untracked files present%s\n",
+			printf(_("nothing added to commit but untracked files present%s\n"),
 				advice_status_hints
-				? " (use \"git add\" to track)" : "");
+				? _(" (use \"git add\" to track)") : "");
 		else if (s->is_initial)
 			printf("nothing to commit%s\n", advice_status_hints
-				? " (create/copy files and use \"git add\" to track)" : "");
+				? _(" (create/copy files and use \"git add\" to track)") : "");
 		else if (!s->show_untracked_files)
-			printf("nothing to commit%s\n", advice_status_hints
-				? " (use -u to show untracked files)" : "");
+			printf(_("nothing to commit%s\n"), advice_status_hints
+				? _(" (use -u to show untracked files)") : "");
 		else
-			printf("nothing to commit%s\n", advice_status_hints
-				? " (working directory clean)" : "");
+			printf(_("nothing to commit%s\n"), advice_status_hints
+				? _(" (working directory clean)") : "");
 	}
 }
 
-- 
1.7.1.242.ge2b63.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH 2/5] gitignore: Ignore files generated by gettext
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
  2010-05-29 22:45 ` [PATCH 1/5] Add infrastructure for translating Git with gettext Ævar Arnfjörð Bjarmason
@ 2010-05-29 22:45 ` Ævar Arnfjörð Bjarmason
  2010-05-29 22:45 ` [PATCH 3/5] Makefile: Remove Gettext files on make clean Ævar Arnfjörð Bjarmason
                   ` (18 subsequent siblings)
  20 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-05-29 22:45 UTC (permalink / raw)
  To: git; +Cc: Jeff Epler, Ævar Arnfjörð Bjarmason

  - Ignore po/git.pot which is autogenerated by a manual run of the
    Makefile. We're only interested in tracking the po/*.po files.

  - Ignore the /share/ directory. It's created during installation and
    contains gettext-related files to be installed.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 .gitignore    |    1 +
 po/.gitignore |    1 +
 2 files changed, 2 insertions(+), 0 deletions(-)
 create mode 100644 po/.gitignore

diff --git a/.gitignore b/.gitignore
index 14e2b6b..d22c0e5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -204,3 +204,4 @@
 *.pdb
 /Debug/
 /Release/
+/share/
diff --git a/po/.gitignore b/po/.gitignore
new file mode 100644
index 0000000..221000e
--- /dev/null
+++ b/po/.gitignore
@@ -0,0 +1 @@
+/*.pot
-- 
1.7.1.242.ge2b63.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH 3/5] Makefile: Remove Gettext files on make clean
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
  2010-05-29 22:45 ` [PATCH 1/5] Add infrastructure for translating Git with gettext Ævar Arnfjörð Bjarmason
  2010-05-29 22:45 ` [PATCH 2/5] gitignore: Ignore files generated by gettext Ævar Arnfjörð Bjarmason
@ 2010-05-29 22:45 ` Ævar Arnfjörð Bjarmason
  2010-05-29 22:45 ` [PATCH 4/5] gettext: Add a skeleton po/is.po Ævar Arnfjörð Bjarmason
                   ` (17 subsequent siblings)
  20 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-05-29 22:45 UTC (permalink / raw)
  To: git; +Cc: Jeff Epler, Ævar Arnfjörð Bjarmason

Remove the automatically generated po/git.pot and share/
files. They're created by the gettext installation process.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Makefile |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile
index 7989121..4de0627 100644
--- a/Makefile
+++ b/Makefile
@@ -2153,6 +2153,10 @@ ifndef NO_TCLTK
 	$(MAKE) -C git-gui clean
 endif
 	$(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS
+ifndef NO_GETTEXT
+	$(RM) po/git.pot
+	$(RM) -r share/
+endif
 
 .PHONY: all install clean strip
 .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
-- 
1.7.1.242.ge2b63.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH 4/5] gettext: Add a skeleton po/is.po
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
                   ` (2 preceding siblings ...)
  2010-05-29 22:45 ` [PATCH 3/5] Makefile: Remove Gettext files on make clean Ævar Arnfjörð Bjarmason
@ 2010-05-29 22:45 ` Ævar Arnfjörð Bjarmason
  2010-05-29 22:45 ` [PATCH 5/5] Add infrastructure to make shellscripts translatable Ævar Arnfjörð Bjarmason
                   ` (16 subsequent siblings)
  20 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-05-29 22:45 UTC (permalink / raw)
  To: git; +Cc: Jeff Epler, Ævar Arnfjörð Bjarmason

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 po/is.po |  288 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 288 insertions(+), 0 deletions(-)
 create mode 100644 po/is.po

diff --git a/po/is.po b/po/is.po
new file mode 100644
index 0000000..4aac4e9
--- /dev/null
+++ b/po/is.po
@@ -0,0 +1,288 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR Free Software Foundation, Inc.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\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: 8bit\n"
+
+# SOME DESCRIPTIVE TITLE.#~ 
+# Copyright (C) YEAR Free Software Foundation, Inc.#~ 
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.#~ 
+##~ 
+#, fuzzy#~ 
+msgid ""#~ 
+msgstr ""#~ 
+"Project-Id-Version: Git\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-29 21:17+0000\n"
+"PO-Revision-Date: \n"
+"Last-Translator: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n"
+"Language-Team: Git Mailing List <git@vger.kernel.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: English\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+#: wt-status.c:53
+msgid "# Unmerged paths:"
+msgstr ""
+
+#: wt-status.c:59 wt-status.c:76
+#, c-format
+msgid "#   (use \"git reset %s <file>...\" to unstage)"
+msgstr ""
+
+#: wt-status.c:61 wt-status.c:78
+msgid "#   (use \"git rm --cached <file>...\" to unstage)"
+msgstr ""
+
+#: wt-status.c:62
+msgid "#   (use \"git add/rm <file>...\" as appropriate to mark resolution)"
+msgstr ""
+
+#: wt-status.c:63 wt-status.c:79 wt-status.c:98 wt-status.c:110
+#: wt-status.c:115 wt-status.c:638 wt-status.c:640
+msgid "#"
+msgstr ""
+
+#: wt-status.c:70
+msgid "# Changes to be committed:"
+msgstr ""
+
+#: wt-status.c:88
+msgid "# Changed but not updated:"
+msgstr ""
+
+#: wt-status.c:92
+msgid "#   (use \"git add <file>...\" to update what will be committed)"
+msgstr ""
+
+#: wt-status.c:94
+msgid "#   (use \"git add/rm <file>...\" to update what will be committed)"
+msgstr ""
+
+#: wt-status.c:95
+msgid ""
+"#   (use \"git checkout -- <file>...\" to discard changes in working "
+"directory)"
+msgstr ""
+
+#: wt-status.c:97
+msgid "#   (commit or discard the untracked or modified content in submodules)"
+msgstr ""
+
+#: wt-status.c:106
+#, c-format
+msgid "# %s files:"
+msgstr ""
+
+#: wt-status.c:109
+#, c-format
+msgid "#   (use \"git %s <file>...\" to include in what will be committed)"
+msgstr ""
+
+#: wt-status.c:126
+msgid "bug"
+msgstr ""
+
+#: wt-status.c:129 wt-status.c:182
+msgid "#\t"
+msgstr ""
+
+#: wt-status.c:131
+msgid "both deleted:"
+msgstr ""
+
+#: wt-status.c:132
+msgid "added by us:"
+msgstr ""
+
+#: wt-status.c:133
+msgid "deleted by them:"
+msgstr ""
+
+#: wt-status.c:134
+msgid "added by them:"
+msgstr ""
+
+#: wt-status.c:135
+msgid "deleted by us:"
+msgstr ""
+
+#: wt-status.c:136
+msgid "both added:"
+msgstr ""
+
+#: wt-status.c:137
+msgid "both modified:"
+msgstr ""
+
+#: wt-status.c:139
+#, c-format
+msgid "%-20s%s\n"
+msgstr ""
+
+#: wt-status.c:165
+msgid " ("
+msgstr ""
+
+#: wt-status.c:167
+msgid "new commits, "
+msgstr ""
+
+#: wt-status.c:169
+msgid "modified content, "
+msgstr ""
+
+#: wt-status.c:171
+msgid "untracked content, "
+msgstr ""
+
+#: wt-status.c:185
+#, c-format
+msgid "new file:   %s"
+msgstr ""
+
+#: wt-status.c:188
+#, c-format
+msgid "copied:     %s -> %s"
+msgstr ""
+
+#: wt-status.c:191
+#, c-format
+msgid "deleted:    %s"
+msgstr ""
+
+#: wt-status.c:194
+#, c-format
+msgid "modified:   %s"
+msgstr ""
+
+#: wt-status.c:197
+#, c-format
+msgid "renamed:    %s -> %s"
+msgstr ""
+
+#: wt-status.c:200
+#, c-format
+msgid "typechange: %s"
+msgstr ""
+
+#: wt-status.c:203
+#, c-format
+msgid "unknown:    %s"
+msgstr ""
+
+#: wt-status.c:206
+#, c-format
+msgid "unmerged:   %s"
+msgstr ""
+
+#: wt-status.c:209
+#, c-format
+msgid "bug: unhandled diff status %c"
+msgstr ""
+
+#: wt-status.c:212
+#, c-format
+msgid "%s"
+msgstr ""
+
+#: wt-status.c:215
+#, c-format
+msgid "\n"
+msgstr ""
+
+#: wt-status.c:622
+msgid "On branch "
+msgstr "Á greininni "
+
+#: wt-status.c:629
+msgid "Not currently on any branch."
+msgstr "Ekki á neinni grein."
+
+#: wt-status.c:639
+msgid "# Initial commit"
+msgstr ""
+
+#: wt-status.c:651
+msgid "Untracked"
+msgstr ""
+
+#: wt-status.c:651
+msgid "add"
+msgstr ""
+
+#: wt-status.c:653
+msgid "Ignored"
+msgstr ""
+
+#: wt-status.c:653
+msgid "add -f"
+msgstr ""
+
+#: wt-status.c:655
+#, c-format
+msgid "# Untracked files not listed%s\n"
+msgstr ""
+
+#: wt-status.c:657
+msgid " (use -u option to show untracked files)"
+msgstr ""
+
+#: wt-status.c:663
+#, c-format
+msgid "# No changes\n"
+msgstr "# Engar breytingar\n"
+
+#: wt-status.c:667
+#, c-format
+msgid "no changes added to commit%s\n"
+msgstr ""
+
+#: wt-status.c:669
+msgid " (use \"git add\" and/or \"git commit -a\")"
+msgstr ""
+
+#: wt-status.c:671
+#, c-format
+msgid "nothing added to commit but untracked files present%s\n"
+msgstr ""
+
+#: wt-status.c:673
+msgid " (use \"git add\" to track)"
+msgstr ""
+
+#: wt-status.c:676
+msgid " (create/copy files and use \"git add\" to track)"
+msgstr ""
+
+#: wt-status.c:678 wt-status.c:681
+#, c-format
+msgid "nothing to commit%s\n"
+msgstr ""
+
+#: wt-status.c:679
+msgid " (use -u to show untracked files)"
+msgstr ""
+
+#: wt-status.c:682
+msgid " (working directory clean)"
+msgstr ""
+
+#: git-pull.sh:124
+msgid "Fetching tags only, you probably meant:"
+msgstr ""
+
+#: git-pull.sh:125
+msgid "  git fetch --tags"
+msgstr ""
-- 
1.7.1.242.ge2b63.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH 5/5] Add infrastructure to make shellscripts translatable
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
                   ` (3 preceding siblings ...)
  2010-05-29 22:45 ` [PATCH 4/5] gettext: Add a skeleton po/is.po Ævar Arnfjörð Bjarmason
@ 2010-05-29 22:45 ` Ævar Arnfjörð Bjarmason
  2010-05-30  1:46 ` [PATCH/RFC 0/5] Add internationalization support to Git Jonathan Nieder
                   ` (15 subsequent siblings)
  20 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-05-29 22:45 UTC (permalink / raw)
  To: git; +Cc: Jeff Epler, Ævar Arnfjörð Bjarmason

Use GNU's gettext.sh in git-sh-setup if it's available, otherwise
fallback on our own custom functions.

A couple of strings in git-pull.sh are now translatable as a proof of
concept, including a multiline string.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Makefile        |    7 +++++--
 git-pull.sh     |   15 ++++++++-------
 git-sh-setup.sh |   33 +++++++++++++++++++++++++++++++++
 3 files changed, 46 insertions(+), 9 deletions(-)

diff --git a/Makefile b/Makefile
index 4de0627..dce2faa 100644
--- a/Makefile
+++ b/Makefile
@@ -272,6 +272,7 @@ mandir = share/man
 infodir = share/info
 gitexecdir = libexec/git-core
 sharedir = $(prefix)/share
+localedir = $(sharedir)/locale
 template_dir = share/git-core/templates
 htmldir = share/doc/git-doc
 ifeq ($(prefix),/usr)
@@ -285,7 +286,7 @@ lib = lib
 # DESTDIR=
 pathsep = :
 
-export prefix bindir sharedir sysconfdir
+export prefix bindir sharedir localedir sysconfdir
 
 CC = gcc
 AR = ar
@@ -1455,6 +1456,7 @@ htmldir_SQ = $(subst ','\'',$(htmldir))
 prefix_SQ = $(subst ','\'',$(prefix))
 sharedir_SQ = $(subst ','\'',$(sharedir))
 
+LOCALEDIR_SQ = $(subst ','\'',$(localedir))
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
@@ -1548,6 +1550,7 @@ $(RM) $@ $@+ && \
 sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
     -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
     -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+    -e 's|@@LOCALEDIR@@|$(LOCALEDIR_SQ)|g' \
     -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
     -e $(BROKEN_PATH_FIX) \
     $@.sh >$@+
@@ -1881,7 +1884,7 @@ cscope:
 	$(FIND) . -name '*.[hcS]' -print | xargs cscope -b
 
 pot:
-	$(XGETTEXT) -k_ -o po/git.pot $(C_OBJ:o=c)
+	$(XGETTEXT) -k_ -o po/git.pot $(C_OBJ:o=c) $(SCRIPT_SH)
 
 POFILES := $(wildcard po/*.po)
 MOFILES := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/git.mo,$(POFILES))
diff --git a/git-pull.sh b/git-pull.sh
index 1a4729f..22a6da2 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -121,8 +121,8 @@ error_on_no_merge_candidates () {
 	do
 		case "$opt" in
 		-t|--t|--ta|--tag|--tags)
-			echo "Fetching tags only, you probably meant:"
-			echo "  git fetch --tags"
+			gettext "Fetching tags only, you probably meant:"; echo
+			gettext "  git fetch --tags"; echo
 			exit 1
 		esac
 	done
@@ -154,11 +154,12 @@ error_on_no_merge_candidates () {
 		echo "a branch. Because this is not the default configured remote"
 		echo "for your current branch, you must specify a branch on the command line."
 	elif [ -z "$curr_branch" ]; then
-		echo "You are not currently on a branch, so I cannot use any"
-		echo "'branch.<branchname>.merge' in your configuration file."
-		echo "Please specify which remote branch you want to use on the command"
-		echo "line and try again (e.g. 'git pull <repository> <refspec>')."
-		echo "See git-pull(1) for details."
+        gettext "You are not currently on a branch, so I cannot use any
+'branch.<branchname>.merge' in your configuration file.
+Please specify which remote branch you want to use on the command
+line and try again (e.g. 'git pull <repository> <refspec>').
+See git-pull(1) for details.";
+        echo
 	elif [ -z "$upstream" ]; then
 		echo "You asked me to pull without telling me which branch you"
 		echo "want to $op_type $op_prep, and 'branch.${curr_branch}.merge' in"
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index 6131670..c8010f2 100644
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -211,3 +211,36 @@ case $(uname -s) in
 	}
 	;;
 esac
+
+# Try to use libintl's gettext.sh, or fall back to English if we
+# can't.
+. gettext.sh
+if test $? -ne 0
+then
+    TEXTDOMAIN=git
+    export TEXDTOMAIN
+    TEXTDOMAINDIR="@@LOCALEDIR@@"
+    export TEXTDOMAINDIR
+else
+    # Since GNU gettext.sh isn't available we'll have to define our
+    # own dummy functions.
+
+    # This code adapted from NessusClient-1.0.2's nessusclient-mkcert
+    # by Michel Arboi <arboi@alussinan.org>. The original code is
+    # under the GPLv2.
+
+    # Not everyone has echo -n
+    case $(echo -n) in
+        \-n)    Xn=   ; Xc='\c' ;;
+        *)      Xn=-n ; Xc=
+    esac
+
+    gettext () {
+        echo $Xn "$1" $Xc
+    }
+
+    eval_gettext () {
+        eval_gettext_var="echo $1"
+        echo $Xn `eval $eval_gettext_var` $Xc
+    }
+fi
-- 
1.7.1.242.ge2b63.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC 0/5] Add internationalization support to Git
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
                   ` (4 preceding siblings ...)
  2010-05-29 22:45 ` [PATCH 5/5] Add infrastructure to make shellscripts translatable Ævar Arnfjörð Bjarmason
@ 2010-05-30  1:46 ` Jonathan Nieder
  2010-05-30 16:04   ` Ævar Arnfjörð Bjarmason
  2010-05-30 20:54 ` [PATCH/RFC v2 0/6] " Ævar Arnfjörð Bjarmason
                   ` (14 subsequent siblings)
  20 siblings, 1 reply; 56+ messages in thread
From: Jonathan Nieder @ 2010-05-30  1:46 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason; +Cc: git, Jeff Epler

Hi Ævar,

Ævar Arnfjörð Bjarmason wrote:

>     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.)

>     but not this:
[...]
> 
>         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).

>     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.

> 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?

Apologies if this was already answered in the earlier discussion.

Thanks,
Jonathan

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC 0/5] Add internationalization support to Git
  2010-05-30  1:46 ` [PATCH/RFC 0/5] Add internationalization support to Git Jonathan Nieder
@ 2010-05-30 16:04   ` Ævar Arnfjörð Bjarmason
  2010-05-30 22:23     ` Jonathan Nieder
  0 siblings, 1 reply; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-05-30 16:04 UTC (permalink / raw)
  To: Jonathan Nieder; +Cc: git, Jeff Epler

On Sun, May 30, 2010 at 01:46, Jonathan Nieder <jrnieder@gmail.com> wrote:
> Hi Ævar,

Hi, and thanks for taking the time to review this.

> Ævar Arnfjörð Bjarmason wrote:
>
>>     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.

>>     but not this:
> [...]
>>
>>         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.

>>     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.
>
>> 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.

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH/RFC v2 0/6] Add internationalization support to Git
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
                   ` (5 preceding siblings ...)
  2010-05-30  1:46 ` [PATCH/RFC 0/5] Add internationalization support to Git Jonathan Nieder
@ 2010-05-30 20:54 ` Ævar Arnfjörð Bjarmason
  2010-05-30 20:54 ` [PATCH/RFC v2 1/6] Add infrastructure for translating Git with gettext Ævar Arnfjörð Bjarmason
                   ` (13 subsequent siblings)
  20 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-05-30 20:54 UTC (permalink / raw)
  To: git; +Cc: Jeff Epler, Ævar Arnfjörð Bjarmason

Here's version 2 of the patch series begun by Jeff Epler to make Git
optionally translatable with GNU gettext.

Due to an error of mine only the cover letter of the previous series
had "PATCH/RFC" in the subject line. That's rectified in this series.

Aside from making sure the test suite always runs under LC_ALL=C,
sanity tests for the translations, "how to translate" documentation,
and the Perl Makefile issue noted below, this series is pretty much
ready for inclusion in my opinion. Perl support was the last missing
piece for translating all of the core C+Shell+Perl code.

New stuff:

  * Perl scripts are now translatable through the Gettext
    facility. Like the shellscript facility I added this has a
    graceful fallback if the prerequisite libraries aren't installed.

    I'm using the libintl-perl library which provides
    Locale::Messages. I could also have used liblocale-gettext-perl
    which provides Locale::gettext. Debian seems to prefer the latter,
    but the former is recomended by GNU. The new Git::Gettext library
    could probably be modified to try both.

    It works 100% for me. Aside from this bit:
    
    	# TODO: How do I make the sed replacements in the top level
    	# Makefile reach me here?
    	#our $TEXTDOMAINDIR = q|@@LOCALEDIR@@|;
    	our $TEXTDOMAINDIR = q</usr/local/share/locale>;

    I couldn't figure out how to make the sed substitution the core
    /Makefile does for e.g. @@GIT_VERSION@@ work for the hybrid
    Makefile / perl.mak setup in perl/.

    Suggestions from contributors more familiar with Git's build
    system welcome.

  * Another thing to note regarding Perl. We have some Perl code
    that's obviously intended to work with perls as old as 5.6. It
    would be preferable to translation support if the minimum were
    bumped to 5.8. Here's why:

        $ perl -e 'printf "%2\$s the %1\$s has\n", ("Clone War", "Begun")'
        Begun the Clone War has

    Perl 5.6's printf doesn't support that printf syntax, which means
    that translators would have to make do with the word order of the
    English messages.

    I haven't seen any documentation about Perl coding guidelines or
    Perl versions that we aim to support in Documentation/, so I don't
    know if 5.6 is supposed to be supported or whether the support is
    just incidental.

    Note that this 5.8 could only be a requirement if the user was
    using a translation with positional printf formats. Users
    interested in running the English-only version could still do so
    on 5.6.

  * Updated is.po. It now contains example translations for C, Shell
    and Perl programs as a proof of concept.

  * Rewrote the commit message of the shell script support commit to
    use use the gettext: prefix.
    
Jeff Epler (1):
  Add infrastructure for translating Git with gettext

Ævar Arnfjörð Bjarmason (5):
  gitignore: Ignore files generated by gettext
  Makefile: Remove Gettext files on make clean
  gettext: Add a Gettext interface for shell scripts
  gettext: Add a Gettext interface for Perl
  gettext: Add a skeleton po/is.po

 .gitignore          |    1 +
 Makefile            |   37 +++++++-
 gettext.c           |   17 +++
 gettext.h           |   15 +++
 git-pull.sh         |   15 ++--
 git-send-email.perl |    3 +-
 git-sh-setup.sh     |   33 ++++++
 git.c               |    3 +
 perl/Git/Gettext.pm |   83 +++++++++++++++
 perl/Makefile.PL    |    5 +-
 po/.gitignore       |    1 +
 po/is.po            |  282 +++++++++++++++++++++++++++++++++++++++++++++++++++
 wt-status.c         |  129 ++++++++++++------------
 13 files changed, 550 insertions(+), 74 deletions(-)
 create mode 100644 gettext.c
 create mode 100644 gettext.h
 create mode 100644 perl/Git/Gettext.pm
 create mode 100644 po/.gitignore
 create mode 100644 po/is.po

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH/RFC v2 1/6] Add infrastructure for translating Git with gettext
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
                   ` (6 preceding siblings ...)
  2010-05-30 20:54 ` [PATCH/RFC v2 0/6] " Ævar Arnfjörð Bjarmason
@ 2010-05-30 20:54 ` Ævar Arnfjörð Bjarmason
  2010-06-01 17:01   ` Jakub Narebski
  2010-05-30 20:54 ` [PATCH/RFC v2 2/6] gitignore: Ignore files generated by gettext Ævar Arnfjörð Bjarmason
                   ` (12 subsequent siblings)
  20 siblings, 1 reply; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-05-30 20:54 UTC (permalink / raw)
  To: git; +Cc: Jeff Epler, Ævar Arnfjörð Bjarmason

From: Jeff Epler <jepler@unpythonic.net>

Change the build process to use GNU's libintl if it's available. If
not we define our own skeleton replacement functions which degrade
gracefully to English.

Signed-off-by: Jeff Epler <jepler@unpythonic.net>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Makefile    |   26 ++++++++++++
 gettext.c   |   17 ++++++++
 gettext.h   |   15 +++++++
 git.c       |    3 +
 wt-status.c |  129 ++++++++++++++++++++++++++++++-----------------------------
 5 files changed, 126 insertions(+), 64 deletions(-)
 create mode 100644 gettext.c
 create mode 100644 gettext.h

diff --git a/Makefile b/Makefile
index d5d6565..7989121 100644
--- a/Makefile
+++ b/Makefile
@@ -297,6 +297,8 @@ RPMBUILD = rpmbuild
 TCL_PATH = tclsh
 TCLTK_PATH = wish
 PTHREAD_LIBS = -lpthread
+XGETTEXT = xgettext
+MSGFMT = msgfmt
 
 export TCL_PATH TCLTK_PATH
 
@@ -523,6 +525,7 @@ LIB_H += userdiff.h
 LIB_H += utf8.h
 LIB_H += xdiff-interface.h
 LIB_H += xdiff/xdiff.h
+LIB_H += gettext.h
 
 LIB_OBJS += abspath.o
 LIB_OBJS += advice.o
@@ -564,6 +567,7 @@ LIB_OBJS += entry.o
 LIB_OBJS += environment.o
 LIB_OBJS += exec_cmd.o
 LIB_OBJS += fsck.o
+LIB_OBJS += gettext.o
 LIB_OBJS += graph.o
 LIB_OBJS += grep.o
 LIB_OBJS += hash.o
@@ -1386,6 +1390,12 @@ ifdef USE_NED_ALLOCATOR
        COMPAT_OBJS += compat/nedmalloc/nedmalloc.o
 endif
 
+ifdef NO_GETTEXT
+	COMPAT_CFLAGS += -DNO_GETTEXT
+else
+	LIBINTL = -lintl
+endif
+
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK=NoThanks
 endif
@@ -1415,6 +1425,7 @@ ifndef V
 	QUIET_BUILT_IN = @echo '   ' BUILTIN $@;
 	QUIET_GEN      = @echo '   ' GEN $@;
 	QUIET_LNCP     = @echo '   ' LN/CP $@;
+	QUIET_MSGFMT   = @echo '   ' MSGFMT $@;
 	QUIET_SUBDIR0  = +@subdir=
 	QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
 			 $(MAKE) $(PRINT_DIR) -C $$subdir
@@ -1442,6 +1453,7 @@ gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
 template_dir_SQ = $(subst ','\'',$(template_dir))
 htmldir_SQ = $(subst ','\'',$(htmldir))
 prefix_SQ = $(subst ','\'',$(prefix))
+sharedir_SQ = $(subst ','\'',$(sharedir))
 
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
@@ -1868,6 +1880,17 @@ cscope:
 	$(RM) cscope*
 	$(FIND) . -name '*.[hcS]' -print | xargs cscope -b
 
+pot:
+	$(XGETTEXT) -k_ -o po/git.pot $(C_OBJ:o=c)
+
+POFILES := $(wildcard po/*.po)
+MOFILES := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/git.mo,$(POFILES))
+MODIRS := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/,$(POFILES))
+all:: $(MOFILES)
+share/locale/%/LC_MESSAGES/git.mo: po/%.po
+	@mkdir -p $(dir $@)
+	$(QUIET_MSGFMT)$(MSGFMT) -o $@ $<
+
 ### Detect prefix changes
 TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
              $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
@@ -1980,6 +2003,9 @@ install: all
 	$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 	$(INSTALL) -m 644 $(SCRIPT_LIB) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 	$(INSTALL) $(install_bindir_programs) '$(DESTDIR_SQ)$(bindir_SQ)'
+	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sharedir_SQ)/locale'
+	(cd share && tar cf - locale) | \
+		(cd '$(DESTDIR_SQ)$(sharedir_SQ)' && umask 022 && tar xof -)
 	$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
 ifndef NO_PERL
 	$(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
diff --git a/gettext.c b/gettext.c
new file mode 100644
index 0000000..aadce19
--- /dev/null
+++ b/gettext.c
@@ -0,0 +1,17 @@
+#ifdef NO_GETTEXT
+void git_setup_gettext() {}
+#else
+#include "exec_cmd.h"
+#include <libintl.h>
+#include <stdlib.h>
+
+void git_setup_gettext() {
+    const char *podir = system_path("share/locale");
+    if(!podir) return;
+    char *ret = bindtextdomain("git", podir);
+    free((void*)podir);
+    ret = setlocale(LC_MESSAGES, "");
+    ret = setlocale(LC_CTYPE, "");
+    ret = textdomain("git");
+}
+#endif
diff --git a/gettext.h b/gettext.h
new file mode 100644
index 0000000..8b221b4
--- /dev/null
+++ b/gettext.h
@@ -0,0 +1,15 @@
+#ifndef GETTEXT_H
+#define GETTEXT_H
+
+void git_setup_gettext();
+
+#ifdef NO_GETTEXT
+#define _(s) (s)
+#define N_(s) (s)
+#else
+#include <libintl.h>
+#define _(s) gettext(s)
+#define N_(s) (s)
+#endif
+
+#endif
diff --git a/git.c b/git.c
index 99f0363..d749eab 100644
--- a/git.c
+++ b/git.c
@@ -3,6 +3,7 @@
 #include "cache.h"
 #include "quote.h"
 #include "run-command.h"
+#include "gettext.h"
 
 const char git_usage_string[] =
 	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n"
@@ -490,6 +491,8 @@ int main(int argc, const char **argv)
 	if (!cmd)
 		cmd = "git-help";
 
+	git_setup_gettext();
+
 	/*
 	 * "git-xxxx" is the same as "git xxxx", but we obviously:
 	 *
diff --git a/wt-status.c b/wt-status.c
index 14e0acc..70b4293 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -9,6 +9,7 @@
 #include "quote.h"
 #include "run-command.h"
 #include "remote.h"
+#include "gettext.h"
 
 static char default_wt_status_colors[][COLOR_MAXLEN] = {
 	GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
@@ -49,33 +50,33 @@ static void wt_status_print_unmerged_header(struct wt_status *s)
 {
 	const char *c = color(WT_STATUS_HEADER, s);
 
-	color_fprintf_ln(s->fp, c, "# Unmerged paths:");
+	color_fprintf_ln(s->fp, c, _("# Unmerged paths:"));
 	if (!advice_status_hints)
 		return;
 	if (s->in_merge)
 		;
 	else if (!s->is_initial)
-		color_fprintf_ln(s->fp, c, "#   (use \"git reset %s <file>...\" to unstage)", s->reference);
+		color_fprintf_ln(s->fp, c, _("#   (use \"git reset %s <file>...\" to unstage)"), s->reference);
 	else
-		color_fprintf_ln(s->fp, c, "#   (use \"git rm --cached <file>...\" to unstage)");
-	color_fprintf_ln(s->fp, c, "#   (use \"git add/rm <file>...\" as appropriate to mark resolution)");
-	color_fprintf_ln(s->fp, c, "#");
+		color_fprintf_ln(s->fp, c, _("#   (use \"git rm --cached <file>...\" to unstage)"));
+	color_fprintf_ln(s->fp, c, _("#   (use \"git add/rm <file>...\" as appropriate to mark resolution)"));
+	color_fprintf_ln(s->fp, c, _("#"));
 }
 
 static void wt_status_print_cached_header(struct wt_status *s)
 {
 	const char *c = color(WT_STATUS_HEADER, s);
 
-	color_fprintf_ln(s->fp, c, "# Changes to be committed:");
+	color_fprintf_ln(s->fp, c, _("# Changes to be committed:"));
 	if (!advice_status_hints)
 		return;
 	if (s->in_merge)
 		; /* NEEDSWORK: use "git reset --unresolve"??? */
 	else if (!s->is_initial)
-		color_fprintf_ln(s->fp, c, "#   (use \"git reset %s <file>...\" to unstage)", s->reference);
+		color_fprintf_ln(s->fp, c, _("#   (use \"git reset %s <file>...\" to unstage)"), s->reference);
 	else
-		color_fprintf_ln(s->fp, c, "#   (use \"git rm --cached <file>...\" to unstage)");
-	color_fprintf_ln(s->fp, c, "#");
+		color_fprintf_ln(s->fp, c, _("#   (use \"git rm --cached <file>...\" to unstage)"));
+	color_fprintf_ln(s->fp, c, _("#"));
 }
 
 static void wt_status_print_dirty_header(struct wt_status *s,
@@ -84,17 +85,17 @@ static void wt_status_print_dirty_header(struct wt_status *s,
 {
 	const char *c = color(WT_STATUS_HEADER, s);
 
-	color_fprintf_ln(s->fp, c, "# Changed but not updated:");
+	color_fprintf_ln(s->fp, c, _("# Changed but not updated:"));
 	if (!advice_status_hints)
 		return;
 	if (!has_deleted)
-		color_fprintf_ln(s->fp, c, "#   (use \"git add <file>...\" to update what will be committed)");
+		color_fprintf_ln(s->fp, c, _("#   (use \"git add <file>...\" to update what will be committed)"));
 	else
-		color_fprintf_ln(s->fp, c, "#   (use \"git add/rm <file>...\" to update what will be committed)");
-	color_fprintf_ln(s->fp, c, "#   (use \"git checkout -- <file>...\" to discard changes in working directory)");
+		color_fprintf_ln(s->fp, c, _("#   (use \"git add/rm <file>...\" to update what will be committed)"));
+	color_fprintf_ln(s->fp, c, _("#   (use \"git checkout -- <file>...\" to discard changes in working directory)"));
 	if (has_dirty_submodules)
-		color_fprintf_ln(s->fp, c, "#   (commit or discard the untracked or modified content in submodules)");
-	color_fprintf_ln(s->fp, c, "#");
+		color_fprintf_ln(s->fp, c, _("#   (commit or discard the untracked or modified content in submodules)"));
+	color_fprintf_ln(s->fp, c, _("#"));
 }
 
 static void wt_status_print_other_header(struct wt_status *s,
@@ -102,16 +103,16 @@ static void wt_status_print_other_header(struct wt_status *s,
 					 const char *how)
 {
 	const char *c = color(WT_STATUS_HEADER, s);
-	color_fprintf_ln(s->fp, c, "# %s files:", what);
+	color_fprintf_ln(s->fp, c, _("# %s files:"), what);
 	if (!advice_status_hints)
 		return;
-	color_fprintf_ln(s->fp, c, "#   (use \"git %s <file>...\" to include in what will be committed)", how);
-	color_fprintf_ln(s->fp, c, "#");
+	color_fprintf_ln(s->fp, c, _("#   (use \"git %s <file>...\" to include in what will be committed)"), how);
+	color_fprintf_ln(s->fp, c, _("#"));
 }
 
 static void wt_status_print_trailer(struct wt_status *s)
 {
-	color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
+	color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("#"));
 }
 
 #define quote_path quote_path_relative
@@ -122,20 +123,20 @@ static void wt_status_print_unmerged_data(struct wt_status *s,
 	const char *c = color(WT_STATUS_UNMERGED, s);
 	struct wt_status_change_data *d = it->util;
 	struct strbuf onebuf = STRBUF_INIT;
-	const char *one, *how = "bug";
+	const char *one, *how = _("bug");
 
 	one = quote_path(it->string, -1, &onebuf, s->prefix);
-	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
+	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), _("#\t"));
 	switch (d->stagemask) {
-	case 1: how = "both deleted:"; break;
-	case 2: how = "added by us:"; break;
-	case 3: how = "deleted by them:"; break;
-	case 4: how = "added by them:"; break;
-	case 5: how = "deleted by us:"; break;
-	case 6: how = "both added:"; break;
-	case 7: how = "both modified:"; break;
+	case 1: how = _("both deleted:"); break;
+	case 2: how = _("added by us:"); break;
+	case 3: how = _("deleted by them:"); break;
+	case 4: how = _("added by them:"); break;
+	case 5: how = _("deleted by us:"); break;
+	case 6: how = _("both added:"); break;
+	case 7: how = _("both modified:"); break;
 	}
-	color_fprintf(s->fp, c, "%-20s%s\n", how, one);
+	color_fprintf(s->fp, c, _("%-20s%s\n"), how, one);
 	strbuf_release(&onebuf);
 }
 
@@ -161,13 +162,13 @@ static void wt_status_print_change_data(struct wt_status *s,
 		break;
 	case WT_STATUS_CHANGED:
 		if (d->new_submodule_commits || d->dirty_submodule) {
-			strbuf_addstr(&extra, " (");
+			strbuf_addstr(&extra, _(" ("));
 			if (d->new_submodule_commits)
-				strbuf_addf(&extra, "new commits, ");
+				strbuf_addf(&extra, _("new commits, "));
 			if (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
-				strbuf_addf(&extra, "modified content, ");
+				strbuf_addf(&extra, _("modified content, "));
 			if (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
-				strbuf_addf(&extra, "untracked content, ");
+				strbuf_addf(&extra, _("untracked content, "));
 			strbuf_setlen(&extra, extra.len - 2);
 			strbuf_addch(&extra, ')');
 		}
@@ -178,40 +179,40 @@ static void wt_status_print_change_data(struct wt_status *s,
 	one = quote_path(one_name, -1, &onebuf, s->prefix);
 	two = quote_path(two_name, -1, &twobuf, s->prefix);
 
-	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
+	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), _("#\t"));
 	switch (status) {
 	case DIFF_STATUS_ADDED:
-		color_fprintf(s->fp, c, "new file:   %s", one);
+		color_fprintf(s->fp, c, _("new file:   %s"), one);
 		break;
 	case DIFF_STATUS_COPIED:
-		color_fprintf(s->fp, c, "copied:     %s -> %s", one, two);
+		color_fprintf(s->fp, c, _("copied:     %s -> %s"), one, two);
 		break;
 	case DIFF_STATUS_DELETED:
-		color_fprintf(s->fp, c, "deleted:    %s", one);
+		color_fprintf(s->fp, c, _("deleted:    %s"), one);
 		break;
 	case DIFF_STATUS_MODIFIED:
-		color_fprintf(s->fp, c, "modified:   %s", one);
+		color_fprintf(s->fp, c, _("modified:   %s"), one);
 		break;
 	case DIFF_STATUS_RENAMED:
-		color_fprintf(s->fp, c, "renamed:    %s -> %s", one, two);
+		color_fprintf(s->fp, c, _("renamed:    %s -> %s"), one, two);
 		break;
 	case DIFF_STATUS_TYPE_CHANGED:
-		color_fprintf(s->fp, c, "typechange: %s", one);
+		color_fprintf(s->fp, c, _("typechange: %s"), one);
 		break;
 	case DIFF_STATUS_UNKNOWN:
-		color_fprintf(s->fp, c, "unknown:    %s", one);
+		color_fprintf(s->fp, c, _("unknown:    %s"), one);
 		break;
 	case DIFF_STATUS_UNMERGED:
-		color_fprintf(s->fp, c, "unmerged:   %s", one);
+		color_fprintf(s->fp, c, _("unmerged:   %s"), one);
 		break;
 	default:
-		die("bug: unhandled diff status %c", status);
+		die(_("bug: unhandled diff status %c"), status);
 	}
 	if (extra.len) {
-		color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "%s", extra.buf);
+		color_fprintf(s->fp, color(WT_STATUS_HEADER, s), _("%s"), extra.buf);
 		strbuf_release(&extra);
 	}
-	fprintf(s->fp, "\n");
+	fprintf(s->fp, _("\n"));
 	strbuf_release(&onebuf);
 	strbuf_release(&twobuf);
 }
@@ -618,14 +619,14 @@ void wt_status_print(struct wt_status *s)
 	const char *branch_color = color(WT_STATUS_HEADER, s);
 
 	if (s->branch) {
-		const char *on_what = "On branch ";
+		const char *on_what = _("On branch ");
 		const char *branch_name = s->branch;
 		if (!prefixcmp(branch_name, "refs/heads/"))
 			branch_name += 11;
 		else if (!strcmp(branch_name, "HEAD")) {
 			branch_name = "";
 			branch_color = color(WT_STATUS_NOBRANCH, s);
-			on_what = "Not currently on any branch.";
+			on_what = _("Not currently on any branch.");
 		}
 		color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "# ");
 		color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name);
@@ -634,9 +635,9 @@ void wt_status_print(struct wt_status *s)
 	}
 
 	if (s->is_initial) {
-		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
-		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "# Initial commit");
-		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
+		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("#"));
+		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("# Initial commit"));
+		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("#"));
 	}
 
 	wt_status_print_updated(s);
@@ -647,38 +648,38 @@ void wt_status_print(struct wt_status *s)
 		wt_status_print_submodule_summary(s, 1);  /* unstaged */
 	}
 	if (s->show_untracked_files) {
-		wt_status_print_other(s, &s->untracked, "Untracked", "add");
+		wt_status_print_other(s, &s->untracked, _("Untracked"), _("add"));
 		if (s->show_ignored_files)
-			wt_status_print_other(s, &s->ignored, "Ignored", "add -f");
+			wt_status_print_other(s, &s->ignored, _("Ignored"), _("add -f"));
 	} else if (s->commitable)
-		fprintf(s->fp, "# Untracked files not listed%s\n",
+		fprintf(s->fp, _("# Untracked files not listed%s\n"),
 			advice_status_hints
-			? " (use -u option to show untracked files)" : "");
+			? _(" (use -u option to show untracked files)") : "");
 
 	if (s->verbose)
 		wt_status_print_verbose(s);
 	if (!s->commitable) {
 		if (s->amend)
-			fprintf(s->fp, "# No changes\n");
+			fprintf(s->fp, _("# No changes\n"));
 		else if (s->nowarn)
 			; /* nothing */
 		else if (s->workdir_dirty)
-			printf("no changes added to commit%s\n",
+			printf(_("no changes added to commit%s\n"),
 				advice_status_hints
-				? " (use \"git add\" and/or \"git commit -a\")" : "");
+				? _(" (use \"git add\" and/or \"git commit -a\")") : "");
 		else if (s->untracked.nr)
-			printf("nothing added to commit but untracked files present%s\n",
+			printf(_("nothing added to commit but untracked files present%s\n"),
 				advice_status_hints
-				? " (use \"git add\" to track)" : "");
+				? _(" (use \"git add\" to track)") : "");
 		else if (s->is_initial)
 			printf("nothing to commit%s\n", advice_status_hints
-				? " (create/copy files and use \"git add\" to track)" : "");
+				? _(" (create/copy files and use \"git add\" to track)") : "");
 		else if (!s->show_untracked_files)
-			printf("nothing to commit%s\n", advice_status_hints
-				? " (use -u to show untracked files)" : "");
+			printf(_("nothing to commit%s\n"), advice_status_hints
+				? _(" (use -u to show untracked files)") : "");
 		else
-			printf("nothing to commit%s\n", advice_status_hints
-				? " (working directory clean)" : "");
+			printf(_("nothing to commit%s\n"), advice_status_hints
+				? _(" (working directory clean)") : "");
 	}
 }
 
-- 
1.7.1.248.gcd6d1

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH/RFC v2 2/6] gitignore: Ignore files generated by gettext
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
                   ` (7 preceding siblings ...)
  2010-05-30 20:54 ` [PATCH/RFC v2 1/6] Add infrastructure for translating Git with gettext Ævar Arnfjörð Bjarmason
@ 2010-05-30 20:54 ` Ævar Arnfjörð Bjarmason
  2010-05-30 20:54 ` [PATCH/RFC v2 3/6] Makefile: Remove Gettext files on make clean Ævar Arnfjörð Bjarmason
                   ` (11 subsequent siblings)
  20 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-05-30 20:54 UTC (permalink / raw)
  To: git; +Cc: Jeff Epler, Ævar Arnfjörð Bjarmason

  - Ignore po/git.pot which is autogenerated by a manual run of the
    Makefile. We're only interested in tracking the po/*.po files.

  - Ignore the /share/ directory. It's created during installation and
    contains gettext-related files to be installed.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 .gitignore    |    1 +
 po/.gitignore |    1 +
 2 files changed, 2 insertions(+), 0 deletions(-)
 create mode 100644 po/.gitignore

diff --git a/.gitignore b/.gitignore
index 14e2b6b..d22c0e5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -204,3 +204,4 @@
 *.pdb
 /Debug/
 /Release/
+/share/
diff --git a/po/.gitignore b/po/.gitignore
new file mode 100644
index 0000000..221000e
--- /dev/null
+++ b/po/.gitignore
@@ -0,0 +1 @@
+/*.pot
-- 
1.7.1.248.gcd6d1

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH/RFC v2 3/6] Makefile: Remove Gettext files on make clean
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
                   ` (8 preceding siblings ...)
  2010-05-30 20:54 ` [PATCH/RFC v2 2/6] gitignore: Ignore files generated by gettext Ævar Arnfjörð Bjarmason
@ 2010-05-30 20:54 ` Ævar Arnfjörð Bjarmason
  2010-05-30 20:54 ` [PATCH/RFC v2 4/6] gettext: Add a Gettext interface for shell scripts Ævar Arnfjörð Bjarmason
                   ` (10 subsequent siblings)
  20 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-05-30 20:54 UTC (permalink / raw)
  To: git; +Cc: Jeff Epler, Ævar Arnfjörð Bjarmason

Remove the automatically generated po/git.pot and share/
files. They're created by the gettext installation process.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Makefile |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile
index 7989121..4de0627 100644
--- a/Makefile
+++ b/Makefile
@@ -2153,6 +2153,10 @@ ifndef NO_TCLTK
 	$(MAKE) -C git-gui clean
 endif
 	$(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS
+ifndef NO_GETTEXT
+	$(RM) po/git.pot
+	$(RM) -r share/
+endif
 
 .PHONY: all install clean strip
 .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
-- 
1.7.1.248.gcd6d1

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH/RFC v2 4/6] gettext: Add a Gettext interface for shell scripts
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
                   ` (9 preceding siblings ...)
  2010-05-30 20:54 ` [PATCH/RFC v2 3/6] Makefile: Remove Gettext files on make clean Ævar Arnfjörð Bjarmason
@ 2010-05-30 20:54 ` Ævar Arnfjörð Bjarmason
  2010-05-30 20:54 ` [PATCH/RFC v2 5/6] gettext: Add a Gettext interface for Perl Ævar Arnfjörð Bjarmason
                   ` (9 subsequent siblings)
  20 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-05-30 20:54 UTC (permalink / raw)
  To: git; +Cc: Jeff Epler, Ævar Arnfjörð Bjarmason

Use GNU's gettext.sh in git-sh-setup if it's available, otherwise
fallback on our own custom functions.

A couple of strings in git-pull.sh are now translatable as a proof of
concept, including a multiline string.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Makefile        |    7 +++++--
 git-pull.sh     |   15 ++++++++-------
 git-sh-setup.sh |   33 +++++++++++++++++++++++++++++++++
 3 files changed, 46 insertions(+), 9 deletions(-)

diff --git a/Makefile b/Makefile
index 4de0627..dce2faa 100644
--- a/Makefile
+++ b/Makefile
@@ -272,6 +272,7 @@ mandir = share/man
 infodir = share/info
 gitexecdir = libexec/git-core
 sharedir = $(prefix)/share
+localedir = $(sharedir)/locale
 template_dir = share/git-core/templates
 htmldir = share/doc/git-doc
 ifeq ($(prefix),/usr)
@@ -285,7 +286,7 @@ lib = lib
 # DESTDIR=
 pathsep = :
 
-export prefix bindir sharedir sysconfdir
+export prefix bindir sharedir localedir sysconfdir
 
 CC = gcc
 AR = ar
@@ -1455,6 +1456,7 @@ htmldir_SQ = $(subst ','\'',$(htmldir))
 prefix_SQ = $(subst ','\'',$(prefix))
 sharedir_SQ = $(subst ','\'',$(sharedir))
 
+LOCALEDIR_SQ = $(subst ','\'',$(localedir))
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
@@ -1548,6 +1550,7 @@ $(RM) $@ $@+ && \
 sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
     -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
     -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+    -e 's|@@LOCALEDIR@@|$(LOCALEDIR_SQ)|g' \
     -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
     -e $(BROKEN_PATH_FIX) \
     $@.sh >$@+
@@ -1881,7 +1884,7 @@ cscope:
 	$(FIND) . -name '*.[hcS]' -print | xargs cscope -b
 
 pot:
-	$(XGETTEXT) -k_ -o po/git.pot $(C_OBJ:o=c)
+	$(XGETTEXT) -k_ -o po/git.pot $(C_OBJ:o=c) $(SCRIPT_SH)
 
 POFILES := $(wildcard po/*.po)
 MOFILES := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/git.mo,$(POFILES))
diff --git a/git-pull.sh b/git-pull.sh
index 1a4729f..22a6da2 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -121,8 +121,8 @@ error_on_no_merge_candidates () {
 	do
 		case "$opt" in
 		-t|--t|--ta|--tag|--tags)
-			echo "Fetching tags only, you probably meant:"
-			echo "  git fetch --tags"
+			gettext "Fetching tags only, you probably meant:"; echo
+			gettext "  git fetch --tags"; echo
 			exit 1
 		esac
 	done
@@ -154,11 +154,12 @@ error_on_no_merge_candidates () {
 		echo "a branch. Because this is not the default configured remote"
 		echo "for your current branch, you must specify a branch on the command line."
 	elif [ -z "$curr_branch" ]; then
-		echo "You are not currently on a branch, so I cannot use any"
-		echo "'branch.<branchname>.merge' in your configuration file."
-		echo "Please specify which remote branch you want to use on the command"
-		echo "line and try again (e.g. 'git pull <repository> <refspec>')."
-		echo "See git-pull(1) for details."
+        gettext "You are not currently on a branch, so I cannot use any
+'branch.<branchname>.merge' in your configuration file.
+Please specify which remote branch you want to use on the command
+line and try again (e.g. 'git pull <repository> <refspec>').
+See git-pull(1) for details.";
+        echo
 	elif [ -z "$upstream" ]; then
 		echo "You asked me to pull without telling me which branch you"
 		echo "want to $op_type $op_prep, and 'branch.${curr_branch}.merge' in"
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index 6131670..c8010f2 100644
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -211,3 +211,36 @@ case $(uname -s) in
 	}
 	;;
 esac
+
+# Try to use libintl's gettext.sh, or fall back to English if we
+# can't.
+. gettext.sh
+if test $? -ne 0
+then
+    TEXTDOMAIN=git
+    export TEXDTOMAIN
+    TEXTDOMAINDIR="@@LOCALEDIR@@"
+    export TEXTDOMAINDIR
+else
+    # Since GNU gettext.sh isn't available we'll have to define our
+    # own dummy functions.
+
+    # This code adapted from NessusClient-1.0.2's nessusclient-mkcert
+    # by Michel Arboi <arboi@alussinan.org>. The original code is
+    # under the GPLv2.
+
+    # Not everyone has echo -n
+    case $(echo -n) in
+        \-n)    Xn=   ; Xc='\c' ;;
+        *)      Xn=-n ; Xc=
+    esac
+
+    gettext () {
+        echo $Xn "$1" $Xc
+    }
+
+    eval_gettext () {
+        eval_gettext_var="echo $1"
+        echo $Xn `eval $eval_gettext_var` $Xc
+    }
+fi
-- 
1.7.1.248.gcd6d1

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH/RFC v2 5/6] gettext: Add a Gettext interface for Perl
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
                   ` (10 preceding siblings ...)
  2010-05-30 20:54 ` [PATCH/RFC v2 4/6] gettext: Add a Gettext interface for shell scripts Ævar Arnfjörð Bjarmason
@ 2010-05-30 20:54 ` Ævar Arnfjörð Bjarmason
  2010-06-01 17:00   ` Jakub Narebski
  2010-05-30 20:54 ` [PATCH/RFC v2 6/6] gettext: Add a skeleton po/is.po Ævar Arnfjörð Bjarmason
                   ` (8 subsequent siblings)
  20 siblings, 1 reply; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-05-30 20:54 UTC (permalink / raw)
  To: git; +Cc: Jeff Epler, Ævar Arnfjörð Bjarmason

Make Git's gettext messages available to Perl programs through
Locale::Messages. Gracefully fall back to English on systems that
don't contain the module.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Makefile            |    4 ++-
 git-send-email.perl |    3 +-
 perl/Git/Gettext.pm |   83 +++++++++++++++++++++++++++++++++++++++++++++++++++
 perl/Makefile.PL    |    5 ++-
 4 files changed, 92 insertions(+), 3 deletions(-)
 create mode 100644 perl/Git/Gettext.pm

diff --git a/Makefile b/Makefile
index dce2faa..2101713 100644
--- a/Makefile
+++ b/Makefile
@@ -1884,7 +1884,9 @@ cscope:
 	$(FIND) . -name '*.[hcS]' -print | xargs cscope -b
 
 pot:
-	$(XGETTEXT) -k_ -o po/git.pot $(C_OBJ:o=c) $(SCRIPT_SH)
+	$(XGETTEXT) --keyword=_ --output=po/git.pot --language=C $(C_OBJ:o=c)
+	$(XGETTEXT) --join-existing --output=po/git.pot --language=Shell $(SCRIPT_SH)
+	$(XGETTEXT) --join-existing --output=po/git.pot --language=Perl $(SCRIPT_PERL)
 
 POFILES := $(wildcard po/*.po)
 MOFILES := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/git.mo,$(POFILES))
diff --git a/git-send-email.perl b/git-send-email.perl
index 111c981..a36718e 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -26,6 +26,7 @@ use Term::ANSIColor;
 use File::Temp qw/ tempdir tempfile /;
 use Error qw(:try);
 use Git;
+use Git::Gettext qw< :all >;
 
 Getopt::Long::Configure qw/ pass_through /;
 
@@ -674,7 +675,7 @@ if (!defined $sender) {
 	$sender = $repoauthor || $repocommitter || '';
 	$sender = ask("Who should the emails appear to be from? [$sender] ",
 	              default => $sender);
-	print "Emails will be sent from: ", $sender, "\n";
+	printf gettext("Emails will be sent from: %s\n"), $sender;
 	$prompting++;
 }
 
diff --git a/perl/Git/Gettext.pm b/perl/Git/Gettext.pm
new file mode 100644
index 0000000..f434783
--- /dev/null
+++ b/perl/Git/Gettext.pm
@@ -0,0 +1,83 @@
+package Git::Gettext;
+use strict;
+use warnings;
+use Exporter;
+use base 'Exporter';
+
+our $VERSION = '0.01';
+
+our @EXPORT;
+our @EXPORT_OK = qw< gettext >;
+our %EXPORT_TAGS;
+@{ $EXPORT_TAGS{'all'} } = @EXPORT_OK;
+
+sub __bootstrap_locale_messages {
+	our $TEXTDOMAIN = 'git';
+
+	# TODO: How do I make the sed replacements in the top level
+	# Makefile reach me here?
+	#our $TEXTDOMAINDIR = q|@@LOCALEDIR@@|;
+	our $TEXTDOMAINDIR = q</usr/local/share/locale>;
+
+	require POSIX;
+	POSIX->import(qw< setlocale >);
+	# Non-core prerequisite module
+	require Locale::Messages;
+	Locale::Messages->import(qw< :locale_h :libintl_h >);
+
+	setlocale(LC_MESSAGES(), '');
+	setlocale(LC_CTYPE(), '');
+	textdomain($TEXTDOMAIN);
+	bindtextdomain($TEXTDOMAIN => $TEXTDOMAINDIR);
+
+	return;
+}
+
+BEGIN
+{
+	local ($@, $!);
+	eval { __bootstrap_locale_messages() };
+	if ($@) {
+		# Oh noes, no Locale::Messages here
+		*gettext = sub ($) { $_[0] };
+	}
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Git::Gettext - Perl interface to Git's Gettext localizations
+
+=head1 DESCRIPTION
+
+Git's internal interface to Gettext via L<Locale::Messages>. If
+L<Locale::Messages> can't be loaded (it's not a core module) we
+provide stub passthrough fallbacks.
+
+=head1 FUNCTIONS
+
+=head2 gettext($)
+
+L<Locale::Messages>'s gettext function if all goes well, otherwise our
+passthrough fallback function.
+
+=head1 EXPORTS
+
+Exports are done via L<Exporter>. Invididual functions can be
+exporter, or all of them via the C<:all> export tag.
+
+=head1 AUTHOR
+
+E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avarab@gmail.com>
+
+=head1 LICENSE AND COPYRIGHT
+
+Copyright 2010 E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avarab@gmail.com>
+
+This program is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
diff --git a/perl/Makefile.PL b/perl/Makefile.PL
index 0b9deca..702ec7c 100644
--- a/perl/Makefile.PL
+++ b/perl/Makefile.PL
@@ -16,7 +16,10 @@ endif
 MAKE_FRAG
 }
 
-my %pm = ('Git.pm' => '$(INST_LIBDIR)/Git.pm');
+my %pm = (
+	'Git.pm' => '$(INST_LIBDIR)/Git.pm',
+	'Git/Gettext.pm' => '$(INST_LIBDIR)/Git/Gettext.pm',
+);
 
 # We come with our own bundled Error.pm. It's not in the set of default
 # Perl modules so install it if it's not available on the system yet.
-- 
1.7.1.248.gcd6d1

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH/RFC v2 6/6] gettext: Add a skeleton po/is.po
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
                   ` (11 preceding siblings ...)
  2010-05-30 20:54 ` [PATCH/RFC v2 5/6] gettext: Add a Gettext interface for Perl Ævar Arnfjörð Bjarmason
@ 2010-05-30 20:54 ` Ævar Arnfjörð Bjarmason
  2010-05-30 21:29   ` Jonathan Nieder
  2010-06-01 23:39 ` [PATCH/RFC v3 0/7] Add internationalization support to Git Ævar Arnfjörð Bjarmason
                   ` (7 subsequent siblings)
  20 siblings, 1 reply; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-05-30 20:54 UTC (permalink / raw)
  To: git; +Cc: Jeff Epler, Ævar Arnfjörð Bjarmason

This example is.po file translates a little bit of C, Shell and Perl
as an example.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 po/is.po |  282 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 282 insertions(+), 0 deletions(-)
 create mode 100644 po/is.po

diff --git a/po/is.po b/po/is.po
new file mode 100644
index 0000000..b716e23
--- /dev/null
+++ b/po/is.po
@@ -0,0 +1,282 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Git\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-30 19:24+0000\n"
+"PO-Revision-Date: 2010-05-30 19:31+0000\n"
+"Last-Translator: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n"
+"Language-Team: Git Mailing List <git@vger.kernel.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: English\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+#: wt-status.c:53
+msgid "# Unmerged paths:"
+msgstr ""
+
+#: wt-status.c:59 wt-status.c:76
+#, c-format
+msgid "#   (use \"git reset %s <file>...\" to unstage)"
+msgstr ""
+
+#: wt-status.c:61 wt-status.c:78
+msgid "#   (use \"git rm --cached <file>...\" to unstage)"
+msgstr ""
+
+#: wt-status.c:62
+msgid "#   (use \"git add/rm <file>...\" as appropriate to mark resolution)"
+msgstr ""
+
+#: wt-status.c:63 wt-status.c:79 wt-status.c:98 wt-status.c:110
+#: wt-status.c:115 wt-status.c:638 wt-status.c:640
+msgid "#"
+msgstr ""
+
+#: wt-status.c:70
+msgid "# Changes to be committed:"
+msgstr ""
+
+#: wt-status.c:88
+msgid "# Changed but not updated:"
+msgstr ""
+
+#: wt-status.c:92
+msgid "#   (use \"git add <file>...\" to update what will be committed)"
+msgstr ""
+
+#: wt-status.c:94
+msgid "#   (use \"git add/rm <file>...\" to update what will be committed)"
+msgstr ""
+
+#: wt-status.c:95
+msgid ""
+"#   (use \"git checkout -- <file>...\" to discard changes in working "
+"directory)"
+msgstr ""
+
+#: wt-status.c:97
+msgid "#   (commit or discard the untracked or modified content in submodules)"
+msgstr ""
+
+#: wt-status.c:106
+#, c-format
+msgid "# %s files:"
+msgstr ""
+
+#: wt-status.c:109
+#, c-format
+msgid "#   (use \"git %s <file>...\" to include in what will be committed)"
+msgstr ""
+
+#: wt-status.c:126
+msgid "bug"
+msgstr ""
+
+#: wt-status.c:129 wt-status.c:182
+msgid "#\t"
+msgstr ""
+
+#: wt-status.c:131
+msgid "both deleted:"
+msgstr ""
+
+#: wt-status.c:132
+msgid "added by us:"
+msgstr ""
+
+#: wt-status.c:133
+msgid "deleted by them:"
+msgstr ""
+
+#: wt-status.c:134
+msgid "added by them:"
+msgstr ""
+
+#: wt-status.c:135
+msgid "deleted by us:"
+msgstr ""
+
+#: wt-status.c:136
+msgid "both added:"
+msgstr ""
+
+#: wt-status.c:137
+msgid "both modified:"
+msgstr ""
+
+#: wt-status.c:139
+#, c-format
+msgid "%-20s%s\n"
+msgstr ""
+
+#: wt-status.c:165
+msgid " ("
+msgstr ""
+
+#: wt-status.c:167
+msgid "new commits, "
+msgstr ""
+
+#: wt-status.c:169
+msgid "modified content, "
+msgstr ""
+
+#: wt-status.c:171
+msgid "untracked content, "
+msgstr ""
+
+#: wt-status.c:185
+#, c-format
+msgid "new file:   %s"
+msgstr ""
+
+#: wt-status.c:188
+#, c-format
+msgid "copied:     %s -> %s"
+msgstr ""
+
+#: wt-status.c:191
+#, c-format
+msgid "deleted:    %s"
+msgstr ""
+
+#: wt-status.c:194
+#, c-format
+msgid "modified:   %s"
+msgstr ""
+
+#: wt-status.c:197
+#, c-format
+msgid "renamed:    %s -> %s"
+msgstr ""
+
+#: wt-status.c:200
+#, c-format
+msgid "typechange: %s"
+msgstr ""
+
+#: wt-status.c:203
+#, c-format
+msgid "unknown:    %s"
+msgstr ""
+
+#: wt-status.c:206
+#, c-format
+msgid "unmerged:   %s"
+msgstr ""
+
+#: wt-status.c:209
+#, c-format
+msgid "bug: unhandled diff status %c"
+msgstr ""
+
+#: wt-status.c:212
+#, c-format
+msgid "%s"
+msgstr ""
+
+#: wt-status.c:215
+#, c-format
+msgid "\n"
+msgstr ""
+
+#: wt-status.c:622
+msgid "On branch "
+msgstr "Á greininni "
+
+#: wt-status.c:629
+msgid "Not currently on any branch."
+msgstr "Ekki á neinni grein."
+
+#: wt-status.c:639
+msgid "# Initial commit"
+msgstr ""
+
+#: wt-status.c:651
+msgid "Untracked"
+msgstr ""
+
+#: wt-status.c:651
+msgid "add"
+msgstr ""
+
+#: wt-status.c:653
+msgid "Ignored"
+msgstr ""
+
+#: wt-status.c:653
+msgid "add -f"
+msgstr ""
+
+#: wt-status.c:655
+#, c-format
+msgid "# Untracked files not listed%s\n"
+msgstr ""
+
+#: wt-status.c:657
+msgid " (use -u option to show untracked files)"
+msgstr ""
+
+#: wt-status.c:663
+#, c-format
+msgid "# No changes\n"
+msgstr "# Engar breytingar\n"
+
+#: wt-status.c:667
+#, c-format
+msgid "no changes added to commit%s\n"
+msgstr ""
+
+#: wt-status.c:669
+msgid " (use \"git add\" and/or \"git commit -a\")"
+msgstr ""
+
+#: wt-status.c:671
+#, c-format
+msgid "nothing added to commit but untracked files present%s\n"
+msgstr ""
+
+#: wt-status.c:673
+msgid " (use \"git add\" to track)"
+msgstr ""
+
+#: wt-status.c:676
+msgid " (create/copy files and use \"git add\" to track)"
+msgstr ""
+
+#: wt-status.c:678 wt-status.c:681
+#, c-format
+msgid "nothing to commit%s\n"
+msgstr ""
+
+#: wt-status.c:679
+msgid " (use -u to show untracked files)"
+msgstr ""
+
+#: wt-status.c:682
+msgid " (working directory clean)"
+msgstr ""
+
+#: git-pull.sh:124
+msgid "Fetching tags only, you probably meant:"
+msgstr ""
+
+#: git-pull.sh:125
+msgid "  git fetch --tags"
+msgstr ""
+
+#: git-pull.sh:157
+msgid ""
+"You are not currently on a branch, so I cannot use any\n"
+"'branch.<branchname>.merge' in your configuration file.\n"
+"Please specify which remote branch you want to use on the command\n"
+"line and try again (e.g. 'git pull <repository> <refspec>').\n"
+"See git-pull(1) for details."
+msgstr ""
+
+#: git-send-email.perl:678
+#, perl-format
+msgid "Emails will be sent from: %s\n"
+msgstr "Póstarnir verða sendir frá: %s\n"
-- 
1.7.1.248.gcd6d1

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC v2 6/6] gettext: Add a skeleton po/is.po
  2010-05-30 20:54 ` [PATCH/RFC v2 6/6] gettext: Add a skeleton po/is.po Ævar Arnfjörð Bjarmason
@ 2010-05-30 21:29   ` Jonathan Nieder
  2010-05-30 21:39     ` Jonathan Nieder
  2010-05-31 14:17     ` Ævar Arnfjörð Bjarmason
  0 siblings, 2 replies; 56+ messages in thread
From: Jonathan Nieder @ 2010-05-30 21:29 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason; +Cc: git, Jeff Epler

Hi Ævar,

Ævar Arnfjörð Bjarmason wrote:

> +#: wt-status.c:53
> +msgid "# Unmerged paths:"
> +msgstr ""

On projects that use gettext, merging these line number changes can
be a royal pain.  In effect, the .po files are mixing semantically
meaningful text and automatically generated cruft.

So my ignorant questions:

 . does gettext use the #: comments for anything important?
 . can they be suppressed when generating the .po file?
 . can they be easily re-added if some translation front-end needs
   them?

> +#: git-pull.sh:124
> +msgid "Fetching tags only, you probably meant:"
> +msgstr ""

I noticed there are no printf formats among the shell scripting
examples.  Would we have to roll our own, like this?

  gettext 'You asked me to pull without telling me which branch you
want to OP_TYPE OP_PREP, and 'branch.CURR_BRANCH.merge' in
your configuration file does not tell me, either. Please
try again (e.g., 'git pull <repository> <refspec>').
See git-pull(1) for details.
' |
  sed "
	s/OP_TYPE/$op_type/g
	s/OP_PREP/$op_prep/g
	s/CURR_BRANCH/$curr_branch/g
  "

Jonathan

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC v2 6/6] gettext: Add a skeleton po/is.po
  2010-05-30 21:29   ` Jonathan Nieder
@ 2010-05-30 21:39     ` Jonathan Nieder
  2010-05-31 14:17     ` Ævar Arnfjörð Bjarmason
  1 sibling, 0 replies; 56+ messages in thread
From: Jonathan Nieder @ 2010-05-30 21:39 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason; +Cc: git, Jeff Epler

Jonathan Nieder wrote:

>   gettext 'You asked me to pull without telling me which branch you
> want to OP_TYPE OP_PREP, and 'branch.CURR_BRANCH.merge' in
> your configuration file does not tell me, either. Please
> try again (e.g., 'git pull <repository> <refspec>').
> See git-pull(1) for details.
> ' |
>   sed "
> 	s/OP_TYPE/$op_type/g
> 	s/OP_PREP/$op_prep/g
> 	s/CURR_BRANCH/$curr_branch/g
>   "

Apparently this is what "gettext -e"/eval_gettext is for.  Sorry for the
noise.

Jonathan

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC 0/5] Add internationalization support to Git
  2010-05-30 16:04   ` Ævar Arnfjörð Bjarmason
@ 2010-05-30 22:23     ` Jonathan Nieder
  2010-05-31 12:17       ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 56+ messages in thread
From: Jonathan Nieder @ 2010-05-30 22:23 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason; +Cc: git, Jeff Epler

Ævar Arnfjörð Bjarmason wrote:

> 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%      --

Given:

-- 8< --
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <libintl.h>
#include "gettext.h"

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

int main(void) {
	const char *podir = "/usr/local/share/locale";
	long int i;

	bindtextdomain("git", podir);
	setlocale(LC_MESSAGES, "");
	setlocale(LC_CTYPE, "");
	textdomain("git");

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

	return 0;
}
-- >8 --

No message catalog is installed here, and I compile with gcc-4.5 -Wall -W -O2.
The results are similar.

A: the standard way.  gettext.h contains "#define _(s) gettext(s)" or
| static inline char *_(const char *s) __attribute__((__format_arg(1)__))
| {
|	return gettext(s);
| }

 6.74user 0.02system 0:06.78elapsed 99%CPU (0avgtext+0avgdata 2304maxresident)k
 0inputs+0outputs (0major+182minor)pagefaults 0swaps

 (about 7 seconds.)

B: noop.  gettext.h contains "#define _(s) s"

 1.35user 0.01system 0:01.37elapsed 99%CPU (0avgtext+0avgdata 2192maxresident)k
 0inputs+0outputs (0major+172minor)pagefaults 0swaps

 (about 1.5 seconds.)

It would seem that __attribute__((__pure__)) should let the compiler give
us the best of both worlds, but no luck.  Even __attribute__((__const__))
is ignored; the compiler inlines the body of _() before it has a chance
to notice.

We can fool the compiler into paying attention by making it not
inlinable: if gettext.h contains

| extern char *_(const char *s) __attribute__((__format_arg__(1), __const__));

and a separate gettext.c contains

| #include <libintl.h>
| #include "gettext.h"
| char *_(const char *s) { return gettext(s); }

we get the performance of B again:

 1.36user 0.01system 0:01.38elapsed 98%CPU (0avgtext+0avgdata 2304maxresident)k
 0inputs+0outputs (0major+180minor)pagefaults 0swaps

This amounts to lying to the compiler, since it is possible for the string
pointed to by a single address s to differ between calls to _.  The __pure__
attribute would be more honest, but for reasons I don’t understand it
suppresses the optimization.

Moral of the story: at least in simple cases, we can keep the performance
and the typechecking.  Phew.

HTH,
Jonathan

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC 0/5] Add internationalization support to Git
  2010-05-30 22:23     ` Jonathan Nieder
@ 2010-05-31 12:17       ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-05-31 12:17 UTC (permalink / raw)
  To: Jonathan Nieder; +Cc: git, Jeff Epler

On Sun, May 30, 2010 at 22:23, Jonathan Nieder <jrnieder@gmail.com> wrote:
> [...]
> It would seem that __attribute__((__pure__)) should let the compiler give
> us the best of both worlds, but no luck.  Even __attribute__((__const__))
> is ignored; the compiler inlines the body of _() before it has a chance
> to notice.
>
> We can fool the compiler into paying attention by making it not
> inlinable: if gettext.h contains
>
> | extern char *_(const char *s) __attribute__((__format_arg__(1), __const__));
>
> and a separate gettext.c contains
>
> | #include <libintl.h>
> | #include "gettext.h"
> | char *_(const char *s) { return gettext(s); }
>
> we get the performance of B again:
>
>  1.36user 0.01system 0:01.38elapsed 98%CPU (0avgtext+0avgdata 2304maxresident)k
>  0inputs+0outputs (0major+180minor)pagefaults 0swaps
>
> This amounts to lying to the compiler, since it is possible for the string
> pointed to by a single address s to differ between calls to _.  The __pure__
> attribute would be more honest, but for reasons I don’t understand it
> suppresses the optimization.
>
> Moral of the story: at least in simple cases, we can keep the performance
> and the typechecking.  Phew.

Awesome, how portable is this? If it's just GCC I guess it can just be
wrapped in #ifdef __GNUC__.

Thanks for investingating this.

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC v2 6/6] gettext: Add a skeleton po/is.po
  2010-05-30 21:29   ` Jonathan Nieder
  2010-05-30 21:39     ` Jonathan Nieder
@ 2010-05-31 14:17     ` Ævar Arnfjörð Bjarmason
  1 sibling, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-05-31 14:17 UTC (permalink / raw)
  To: Jonathan Nieder; +Cc: git, Jeff Epler

On Sun, May 30, 2010 at 21:29, Jonathan Nieder <jrnieder@gmail.com> wrote:
> Hi Ęvar,
>
> Ęvar Arnfjörš Bjarmason wrote:
>
>> +#: wt-status.c:53
>> +msgid "# Unmerged paths:"
>> +msgstr ""
>
> On projects that use gettext, merging these line number changes can
> be a royal pain.  In effect, the .po files are mixing semantically
> meaningful text and automatically generated cruft.
>
> So my ignorant questions:
>
>  . does gettext use the #: comments for anything important?
>  . can they be suppressed when generating the .po file?
>  . can they be easily re-added if some translation front-end needs
>   them?

Let me answer that more generally.

Usually when people first see the Gettext format they go "ew!". The
thing is that it's really meant as a format for mixed human and
programmatic editing. Almost everyone editing PO files doesn't do so
purely with a simple non-PO aware text editor.

PO editors use these comments to allow you to jump to the source
definition to see the message in context. E.g. Emacs's po-mode allows
you to do this, and Launchpad displays the file & line number (but
last I checked they didn't have an embedded viewer).

A merge of two PO files that doesn't use msgmerge(1) is going to be
painful. There are some things that it'll throw out and regenerate
(e.g. automated comments, those matching /^#\S/), and it might move
messages around to match their new position in the respective source
files. Thus a merge that might be hard with a simple git merge might
be very easy with a msgmerge-based merge.

Now, we could filter out all these comments (and e.g. context
comments) before checking these files in. But that would cause a lot
of tools like Launchpad and PO editors to behave in unexpected
ways. The Gettext utilities themselves also seem to have no support
for initializing/emitting these sort of slim files.

I don't know what the answer is, but this is all something to
consider. I'm new to the Gettext format myself, but I've come to like
the automatic editing the tools do, especially the automaticy fuzzy
tagging.

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC v2 5/6] gettext: Add a Gettext interface for Perl
  2010-05-30 20:54 ` [PATCH/RFC v2 5/6] gettext: Add a Gettext interface for Perl Ævar Arnfjörð Bjarmason
@ 2010-06-01 17:00   ` Jakub Narebski
  2010-06-01 19:06     ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 56+ messages in thread
From: Jakub Narebski @ 2010-06-01 17:00 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Jeff Epler, Jakub Narębski

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> Make Git's gettext messages available to Perl programs through
> Locale::Messages. Gracefully fall back to English on systems that
> don't contain the module.

This is I think a very good idea, both providing wrapper with fallback
to untranslated messages (and encapsulating translation), and using the
Locale::Messages module from libintl-perl library.  The "On the state of
i18n in Perl" (http://rassie.org/archives/247) blog post from 26 April
2009 provides nice counterpoint to Locale::Maketext::TPJ13 / The Perl
Journal #13 article[1] from 1999... especially because using gettext is
natural for translating git command output and GUI, because git uses it
already for Tcl/Tk (gitk and git-gui), and it is natural solution for
code in C, which slightly less than half of git code.

Well, we could use Locale::Maketext::Gettext, but it is not in Perl
core either, and as http://rassie.org/archives/247 says its '*.po'
files are less natural.  The gettext documentation (gettext.info) also
recommends libintl-perl, or to be more exact Locale::TextDomain from
it.

[1] http://search.cpan.org/perldoc?Locale::Maketext::TPJ13
    http://interglacial.com/~sburke/tpj/as_html/tpj13.html


The question is why not use Locale::TextDomain, the high-level Perl-y
framework, wrapper around Locale::Messages from the same libintl-perl
library?  The gettext documentation (in gettext.info, chapter "13.5.18
Perl") says:

  Prerequisite
     `use POSIX;'
     `use Locale::TextDomain;' (included in the package libintl-perl
     which is available on the Comprehensive Perl Archive Network CPAN,
     http://www.cpan.org/).


This would change

 -	print "Emails will be sent from: ", $sender, "\n";
 +	printf gettext("Emails will be sent from: %s\n"), $sender;

to either

 +	print __"Emails will be sent from: ", $sender, "\n";

or

 +	printf __("Emails will be sent from: %s\n"), $sender;

or

 +	print __x("Emails will be sent from: {sender}\n",
 +	          sender => $sender);


> 
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  Makefile            |    4 ++-
>  git-send-email.perl |    3 +-
>  perl/Git/Gettext.pm |   83 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  perl/Makefile.PL    |    5 ++-
>  4 files changed, 92 insertions(+), 3 deletions(-)
>  create mode 100644 perl/Git/Gettext.pm
> 
> diff --git a/Makefile b/Makefile
> index dce2faa..2101713 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -1884,7 +1884,9 @@ cscope:
>  	$(FIND) . -name '*.[hcS]' -print | xargs cscope -b
>  
>  pot:
> -	$(XGETTEXT) -k_ -o po/git.pot $(C_OBJ:o=c) $(SCRIPT_SH)
> +	$(XGETTEXT) --keyword=_ --output=po/git.pot --language=C $(C_OBJ:o=c)
> +	$(XGETTEXT) --join-existing --output=po/git.pot --language=Shell $(SCRIPT_SH)

Shouldn't this line be in earlier patch, i.e. in "gettext: Add a
Gettext interface for shell scripts"?

> +	$(XGETTEXT) --join-existing --output=po/git.pot --language=Perl $(SCRIPT_PERL)

From gettext documentation (in gettext.info, chapter "13.5.18 Perl"):

  Extractor
     `xgettext -k__ -k\$__ -k%__ -k__x -k__n:1,2 -k__nx:1,2 -k__xn:1,2
     -kN__ -k'

Is it equivalent to specifying 'xgettext --language=Perl'?

Of course the above assumes that you are using Locale::TextDomain, or
at least use the same conventions.

>  
>  POFILES := $(wildcard po/*.po)
>  MOFILES := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/git.mo,$(POFILES))
> diff --git a/git-send-email.perl b/git-send-email.perl
> index 111c981..a36718e 100755
> --- a/git-send-email.perl
> +++ b/git-send-email.perl
> @@ -26,6 +26,7 @@ use Term::ANSIColor;
>  use File::Temp qw/ tempdir tempfile /;
>  use Error qw(:try);
>  use Git;
> +use Git::Gettext qw< :all >;

Please follow the existing convention, i.e.

  +use Git::Gettext qw(:all);

like you see in context line for 'use Error'.


Well, not that git-send-email.perl is very consistent about this
issue, see 'use File::Temp qw/ tempdir tempfile /;' versus 
'use Error qw(:try);' but I'd reather you didn't introduce yet
another form.

>  
>  Getopt::Long::Configure qw/ pass_through /;
>  
> @@ -674,7 +675,7 @@ if (!defined $sender) {
>  	$sender = $repoauthor || $repocommitter || '';
>  	$sender = ask("Who should the emails appear to be from? [$sender] ",
>  	              default => $sender);
> -	print "Emails will be sent from: ", $sender, "\n";
> +	printf gettext("Emails will be sent from: %s\n"), $sender;

As I wrote, this IMHO should be either

  +	printf __("Emails will be sent from: %s\n"), $sender;

or

  +	print __x("Emails will be sent from: {sender}\n", sender => $sender);

(note that parantheses differ in those two examples).

>  	$prompting++;
>  }
>  
> diff --git a/perl/Git/Gettext.pm b/perl/Git/Gettext.pm
> new file mode 100644
> index 0000000..f434783
> --- /dev/null
> +++ b/perl/Git/Gettext.pm
> @@ -0,0 +1,83 @@
> +package Git::Gettext;

Should this package be named Git::Gettext, or other name would be
better, perhaps Git::I18N (like e.g. Games::Risk have
Games::Risk::I18N), or Git::Locale, or even Git::Translator?

Not very important.

> +use strict;
> +use warnings;
> +use Exporter;
> +use base 'Exporter';

O.K.

The alternative would be to use

  +use Exporter qw(import);

> +
> +our $VERSION = '0.01';
> +
> +our @EXPORT;
> +our @EXPORT_OK = qw< gettext >;

Same comment as above: more common way is to use qw(gettext) not 
qw< gettext >; it is also the notation used in Exporter documentation.

> +our %EXPORT_TAGS;
> +@{ $EXPORT_TAGS{'all'} } = @EXPORT_OK;

Why not simply

  +our %EXPORT_TAGS = ('all' => [ @EXPORT_OK ]);

or the reverse

  +our %EXPORT_TAGS = ('all' => [qw(gettext)]);
  +our @EXPORT_OK   = @{$EXPORT_TAGS{'all'}};

or the reverse using tag utility functions

  +our %EXPORT_TAGS = ('all' => [qw(gettext)]);
  +Exporter::export_ok_tags('all');

> +
> +sub __bootstrap_locale_messages {
> +	our $TEXTDOMAIN = 'git';
> +
> +	# TODO: How do I make the sed replacements in the top level
> +	# Makefile reach me here?
> +	#our $TEXTDOMAINDIR = q|@@LOCALEDIR@@|;

In Perl (well, in gitweb/gitweb.perl) we use '++VAR++' and not
'@@VAR@@' for placeholders, because '@' is sigil in Perl.  This is not
important in above example, because it is not interpolated string.


Make invoked on perl/Makefile, when invoked from main Makefile by
'$(MAKE) -C perl' (via QUIET_SUBDIR0) passes 'localedir' to submake;
perl/Makefile should probably have something like

  localedir ?= $(sharedir)/locale

That is assuming that 'localedir' is added to list of exported
variables.


But I am not sure how such substitution should be performed.

> +	our $TEXTDOMAINDIR = q</usr/local/share/locale>;

Why q<...> and not simply '...'?

> +
> +	require POSIX;
> +	POSIX->import(qw< setlocale >);
> +	# Non-core prerequisite module
> +	require Locale::Messages;
> +	Locale::Messages->import(qw< :locale_h :libintl_h >);
> +
> +	setlocale(LC_MESSAGES(), '');
> +	setlocale(LC_CTYPE(), '');
> +	textdomain($TEXTDOMAIN);
> +	bindtextdomain($TEXTDOMAIN => $TEXTDOMAINDIR);
> +
> +	return;
> +}

This would probably be a bit simpler with Locale::TextDomain.

> +
> +BEGIN
> +{
> +	local ($@, $!);
> +	eval { __bootstrap_locale_messages() };
> +	if ($@) {
> +		# Oh noes, no Locale::Messages here
> +		*gettext = sub ($) { $_[0] };

Do you intended to use subroutine protytype here?  Ah, I see that
Locale::Messages::gettext has the same prototype...

> +	}
> +}

Does it need to be in BEGIN block?  Probably yes.

> +
> +1;
> +
> +__END__
> +

It's nice that you have provided documentation.

> +=head1 NAME
> +
> +Git::Gettext - Perl interface to Git's Gettext localizations
> +
> +=head1 DESCRIPTION
> +
> +Git's internal interface to Gettext via L<Locale::Messages>. If
> +L<Locale::Messages> can't be loaded (it's not a core module) we
> +provide stub passthrough fallbacks.

Very good.

It would probably be better though to use L<Locale::TextDomain>
instead of low(er)-level L<Locale::Messages>.

> +
> +=head1 FUNCTIONS
> +
> +=head2 gettext($)
> +
> +L<Locale::Messages>'s gettext function if all goes well, otherwise our
> +passthrough fallback function.

Other packages use _T() function for that, or like Locale::TextDomain
__() function.

> +
> +=head1 EXPORTS
> +
> +Exports are done via L<Exporter>. Invididual functions can be
> +exporter, or all of them via the C<:all> export tag.

Shouldn't this be described in less technical way in SYNOPSIS and
DESCRIPTION sections instead?

> +
> +=head1 AUTHOR
> +
> +E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avarab@gmail.com>
> +
> +=head1 LICENSE AND COPYRIGHT
> +
> +Copyright 2010 E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avarab@gmail.com>
> +
> +This program is free software, you can redistribute it and/or modify
> +it under the same terms as Perl itself.

Which is dual licensed: GPL + Perl artistic.

Was it intended to use 'same terms as Perl itself' rather than GPLv2
or GPLv2+?

> +
> +=cut
> diff --git a/perl/Makefile.PL b/perl/Makefile.PL
> index 0b9deca..702ec7c 100644
> --- a/perl/Makefile.PL
> +++ b/perl/Makefile.PL
> @@ -16,7 +16,10 @@ endif
>  MAKE_FRAG
>  }
>  
> -my %pm = ('Git.pm' => '$(INST_LIBDIR)/Git.pm');
> +my %pm = (
> +	'Git.pm' => '$(INST_LIBDIR)/Git.pm',
> +	'Git/Gettext.pm' => '$(INST_LIBDIR)/Git/Gettext.pm',
> +);
>  
>  # We come with our own bundled Error.pm. It's not in the set of default
>  # Perl modules so install it if it's not available on the system yet.
> -- 
> 1.7.1.248.gcd6d1
> 

-- 
Jakub Narebski
Poland
ShadeHawk on #git

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC v2 1/6] Add infrastructure for translating Git with gettext
  2010-05-30 20:54 ` [PATCH/RFC v2 1/6] Add infrastructure for translating Git with gettext Ævar Arnfjörð Bjarmason
@ 2010-06-01 17:01   ` Jakub Narebski
  2010-06-01 18:11     ` [PATCH/RCF] autoconf: Check if <libintl.h> exists and set NO_GETTEXT Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 56+ messages in thread
From: Jakub Narebski @ 2010-06-01 17:01 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason; +Cc: git, Jeff Epler

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> From: Jeff Epler <jepler@unpythonic.net>
> 
> Change the build process to use GNU's libintl if it's available. If
> not we define our own skeleton replacement functions which degrade
> gracefully to English.

Very nice.

> 
> Signed-off-by: Jeff Epler <jepler@unpythonic.net>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  Makefile    |   26 ++++++++++++
>  gettext.c   |   17 ++++++++
>  gettext.h   |   15 +++++++
>  git.c       |    3 +
>  wt-status.c |  129 ++++++++++++++++++++++++++++++-----------------------------
>  5 files changed, 126 insertions(+), 64 deletions(-)
>  create mode 100644 gettext.c
>  create mode 100644 gettext.h

Could you also provide change to configure.ac, so that ./configure
would detect if we have gettext installed or not?  Thanks in advance.

-- 
Jakub Narebski
Poland
ShadeHawk on #git

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH/RCF] autoconf: Check if <libintl.h> exists and set NO_GETTEXT
  2010-06-01 17:01   ` Jakub Narebski
@ 2010-06-01 18:11     ` Ævar Arnfjörð Bjarmason
  2010-06-01 21:22       ` Jakub Narebski
  0 siblings, 1 reply; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-01 18:11 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Ævar Arnfjörð Bjarmason

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---

On Tue, Jun 1, 2010 at 17:01, Jakub Narebski <jnareb@gmail.com> wrote:
> [...]
> Could you also provide change to configure.ac, so that ./configure
> would detect if we have gettext installed or not?  Thanks in advance.

Here's a check that just checks if we have libintl.h similar to the
existing checks for libgen.h and other headers.

Do you think this be adequate?

 config.mak.in |    1 +
 configure.ac  |    6 ++++++
 2 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/config.mak.in b/config.mak.in
index 0d4b64d..a15f3c1 100644
--- a/config.mak.in
+++ b/config.mak.in
@@ -32,6 +32,7 @@ NO_CURL=@NO_CURL@
 NO_EXPAT=@NO_EXPAT@
 NO_LIBGEN_H=@NO_LIBGEN_H@
 HAVE_PATHS_H=@HAVE_PATHS_H@
+NO_GETTEXT=@NO_GETTEXT@
 NEEDS_LIBICONV=@NEEDS_LIBICONV@
 NEEDS_SOCKET=@NEEDS_SOCKET@
 NEEDS_RESOLV=@NEEDS_RESOLV@
diff --git a/configure.ac b/configure.ac
index 71038fc..7bebfd8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -730,6 +730,12 @@ AC_CHECK_HEADER([paths.h],
 [HAVE_PATHS_H=])
 AC_SUBST(HAVE_PATHS_H)
 #
+# Define NO_GETTEXT if you don't have libintl.h
+AC_CHECK_HEADER([libintl.h],
+[NO_GETTEXT=],
+[NO_GETTEXT=YesPlease])
+AC_SUBST(NO_GETTEXT)
+#
 # Define NO_STRCASESTR if you don't have strcasestr.
 GIT_CHECK_FUNC(strcasestr,
 [NO_STRCASESTR=],
-- 
1.7.1.243.g01eae

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC v2 5/6] gettext: Add a Gettext interface for Perl
  2010-06-01 17:00   ` Jakub Narebski
@ 2010-06-01 19:06     ` Ævar Arnfjörð Bjarmason
  2010-06-02 11:47       ` Jakub Narebski
  0 siblings, 1 reply; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-01 19:06 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git, Jeff Epler

On Tue, Jun 1, 2010 at 17:00, Jakub Narebski <jnareb@gmail.com> wrote:
>
> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
>
>> Make Git's gettext messages available to Perl programs through
>> Locale::Messages. Gracefully fall back to English on systems that
>> don't contain the module.
>
> This is I think a very good idea, both providing wrapper with fallback
> to untranslated messages (and encapsulating translation), and using the
> Locale::Messages module from libintl-perl library.  The "On the state of
> i18n in Perl" (http://rassie.org/archives/247) blog post from 26 April
> 2009 provides nice counterpoint to Locale::Maketext::TPJ13 / The Perl
> Journal #13 article[1] from 1999... especially because using gettext is
> natural for translating git command output and GUI, because git uses it
> already for Tcl/Tk (gitk and git-gui), and it is natural solution for
> code in C, which slightly less than half of git code.
>
> Well, we could use Locale::Maketext::Gettext, but it is not in Perl
> core either, and as http://rassie.org/archives/247 says its '*.po'
> files are less natural.  The gettext documentation (gettext.info) also
> recommends libintl-perl, or to be more exact Locale::TextDomain from
> it.
>
> [1] http://search.cpan.org/perldoc?Locale::Maketext::TPJ13
>    http://interglacial.com/~sburke/tpj/as_html/tpj13.html

Locale::Maketext is a less complete and non-standard alternative as
the above blogs note. I think the main reason it's used in favor of
Gettext in many Perl projects is that it can be compiled stand-alone
from the CPAN, i.e. it doesn't depend on libintl.

That's comfortable when CPAN is your main distribution channel, but
Git's main distribution channel is via OS packages, which can
trivially introduce a gettext dependency (for which they doubtless
already have a package).

Locale::Maketext has a Gettext emulation layer, but using it would be
a potential source of bugs (no emulation is perfect).

That being said the way I wrote Git::Gexttext means any alterante
implementation or emulation layer can be seemlessly added later on. We
could use (or write) something for Perl, C or Shell that completely
bypasses libintl for systems that don't have a gettext C library.

> The question is why not use Locale::TextDomain, the high-level Perl-y
> framework, wrapper around Locale::Messages from the same libintl-perl
> library?  The gettext documentation (in gettext.info, chapter "13.5.18
> Perl") says:
>
>  Prerequisite
>     `use POSIX;'
>     `use Locale::TextDomain;' (included in the package libintl-perl
>     which is available on the Comprehensive Perl Archive Network CPAN,
>     http://www.cpan.org/).
>
>
> This would change
>
>  -      print "Emails will be sent from: ", $sender, "\n";
>  +      printf gettext("Emails will be sent from: %s\n"), $sender;
>
> to either
>
>  +      print __"Emails will be sent from: ", $sender, "\n";
>
> or
>
>  +      printf __("Emails will be sent from: %s\n"), $sender;
>
> or
>
>  +      print __x("Emails will be sent from: {sender}\n",
>  +                sender => $sender);

I didn't use Locale::TextDomain because it exports a fatter interface
by default, and I'm not sure at this point what subset of it it would
make sense to support.

This is the default Locale::TexdDomain interface:

   @EXPORT = qw (__ __x __n __nx __xn __p __px __np __npx $__ %__
                 N__ N__n N__p N__np);

My Git::Gettext only exports a single gettext() function now. I think
it's better at this point to have a really small interface and decide
later if we'd like to expand it, preferably in a way that'll work
consistently across C, Perl and Shell programs.

We might want to improve plural support, add msgctxt etc. later. But
for an initial implementation I'd rather have something simple &
stupid.

>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
>> ---
>>  Makefile            |    4 ++-
>>  git-send-email.perl |    3 +-
>>  perl/Git/Gettext.pm |   83 +++++++++++++++++++++++++++++++++++++++++++++++++++
>>  perl/Makefile.PL    |    5 ++-
>>  4 files changed, 92 insertions(+), 3 deletions(-)
>>  create mode 100644 perl/Git/Gettext.pm
>>
>> diff --git a/Makefile b/Makefile
>> index dce2faa..2101713 100644
>> --- a/Makefile
>> +++ b/Makefile
>> @@ -1884,7 +1884,9 @@ cscope:
>>       $(FIND) . -name '*.[hcS]' -print | xargs cscope -b
>>
>>  pot:
>> -     $(XGETTEXT) -k_ -o po/git.pot $(C_OBJ:o=c) $(SCRIPT_SH)
>> +     $(XGETTEXT) --keyword=_ --output=po/git.pot --language=C $(C_OBJ:o=c)
>> +     $(XGETTEXT) --join-existing --output=po/git.pot --language=Shell $(SCRIPT_SH)
>
> Shouldn't this line be in earlier patch, i.e. in "gettext: Add a
> Gettext interface for shell scripts"?

It needed to be split up into seperate line in the Perl commit because ...

>> +     $(XGETTEXT) --join-existing --output=po/git.pot --language=Perl $(SCRIPT_PERL)
>
> From gettext documentation (in gettext.info, chapter "13.5.18 Perl"):
>
>  Extractor
>     `xgettext -k__ -k\$__ -k%__ -k__x -k__n:1,2 -k__nx:1,2 -k__xn:1,2
>     -kN__ -k'
>
> Is it equivalent to specifying 'xgettext --language=Perl'?

I'm doing --language=Perl because gettext doesn't recognize .perl as a
valid Perl extension. It only knows about `pl', `PL', `pm', `cgi'. (As
an aside, I haven't found why Git chose to use the quaint .perl
extension).

But yeah, using --language=Perl is overshooting it with regards to
keyword extraction. From "5.1 Invoking the `xgettext' Program":

     To disable the default keyword specifications, the option `-k' or
     `--keyword' or `--keyword=', without a KEYWORDSPEC, can be used.

I'll check if I can supply --keyword to all the XGETTEXT invocations.

> Of course the above assumes that you are using Locale::TextDomain, or
> at least use the same conventions.
>
>>
>>  POFILES := $(wildcard po/*.po)
>>  MOFILES := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/git.mo,$(POFILES))
>> diff --git a/git-send-email.perl b/git-send-email.perl
>> index 111c981..a36718e 100755
>> --- a/git-send-email.perl
>> +++ b/git-send-email.perl
>> @@ -26,6 +26,7 @@ use Term::ANSIColor;
>>  use File::Temp qw/ tempdir tempfile /;
>>  use Error qw(:try);
>>  use Git;
>> +use Git::Gettext qw< :all >;
>
> Please follow the existing convention, i.e.
>
>  +use Git::Gettext qw(:all);
>
> like you see in context line for 'use Error'.
>
>
> Well, not that git-send-email.perl is very consistent about this
> issue, see 'use File::Temp qw/ tempdir tempfile /;' versus
> 'use Error qw(:try);' but I'd reather you didn't introduce yet
> another form.

OK. I'll change that. I usually just pick whatever Perl quote
delimiters that happen to, literally, be close at hand at the time :)

>>  Getopt::Long::Configure qw/ pass_through /;
>>
>> @@ -674,7 +675,7 @@ if (!defined $sender) {
>>       $sender = $repoauthor || $repocommitter || '';
>>       $sender = ask("Who should the emails appear to be from? [$sender] ",
>>                     default => $sender);
>> -     print "Emails will be sent from: ", $sender, "\n";
>> +     printf gettext("Emails will be sent from: %s\n"), $sender;
>
> As I wrote, this IMHO should be either
>
>  +     printf __("Emails will be sent from: %s\n"), $sender;
>
> or
>
>  +     print __x("Emails will be sent from: {sender}\n", sender => $sender);
>
> (note that parantheses differ in those two examples).

I'd prefer the former argument expansion form (i.e. %s, not
{$sender}), just so translators don't have to deal with two different
argument forms.

As for __ or gettext I don't mind. I just picked the former on a whim
to match the Shell version. Maybe I should just change C|Perl|Shell to
use _?

And we can use _, __ is just used to disambiguate it from the _
filehandle, and Perl's pretty good at disambiguating that already:

    $ perl -E 'sub _ ($) { scalar reverse $_[0] }; say _("moo"); stat
"/etc/hosts"; say ((stat(_))[7])'
    oom
    168

>>       $prompting++;
>>  }
>>
>> diff --git a/perl/Git/Gettext.pm b/perl/Git/Gettext.pm
>> new file mode 100644
>> index 0000000..f434783
>> --- /dev/null
>> +++ b/perl/Git/Gettext.pm
>> @@ -0,0 +1,83 @@
>> +package Git::Gettext;
>
> Should this package be named Git::Gettext, or other name would be
> better, perhaps Git::I18N (like e.g. Games::Risk have
> Games::Risk::I18N), or Git::Locale, or even Git::Translator?
>
> Not very important.

I really have no opinion on that, but it does seem like a lot of Perl
packages on CPAN use the ::I18N suffix.

>> +use strict;
>> +use warnings;
>> +use Exporter;
>> +use base 'Exporter';
>
> O.K.
>
> The alternative would be to use
>
>  +use Exporter qw(import);

Not if we want to maintain 5.6 support. The `use Exporter "import"'
form was only introduced in 5.8. I use it everywhere where I don't
have to care about 5.6 support (which is everywhere but Git).

>> +
>> +our $VERSION = '0.01';
>> +
>> +our @EXPORT;
>> +our @EXPORT_OK = qw< gettext >;
>
> Same comment as above: more common way is to use qw(gettext) not
> qw< gettext >; it is also the notation used in Exporter documentation.

Will do.

>> +our %EXPORT_TAGS;
>> +@{ $EXPORT_TAGS{'all'} } = @EXPORT_OK;
>
> Why not simply
>
>  +our %EXPORT_TAGS = ('all' => [ @EXPORT_OK ]);
>
> or the reverse
>
>  +our %EXPORT_TAGS = ('all' => [qw(gettext)]);
>  +our @EXPORT_OK   = @{$EXPORT_TAGS{'all'}};
>
> or the reverse using tag utility functions
>
>  +our %EXPORT_TAGS = ('all' => [qw(gettext)]);
>  +Exporter::export_ok_tags('all');

Just because it some old code of mine I had around that I copied
from. I could rewrite it to a nicer form.

Actually I was considering changing it to just have @EXPORT and
nothing else. You're going to use gettext if you use the medule
anyway, so we might as well just import them all everywhere we use
Gettext in Perl.

>> +
>> +sub __bootstrap_locale_messages {
>> +     our $TEXTDOMAIN = 'git';
>> +
>> +     # TODO: How do I make the sed replacements in the top level
>> +     # Makefile reach me here?
>> +     #our $TEXTDOMAINDIR = q|@@LOCALEDIR@@|;
>
> In Perl (well, in gitweb/gitweb.perl) we use '++VAR++' and not
> '@@VAR@@' for placeholders, because '@' is sigil in Perl.  This is not
> important in above example, because it is not interpolated string.

I'll change it to follow that convention.

> Make invoked on perl/Makefile, when invoked from main Makefile by
> '$(MAKE) -C perl' (via QUIET_SUBDIR0) passes 'localedir' to submake;
> perl/Makefile should probably have something like
>
>  localedir ?= $(sharedir)/locale
>
> That is assuming that 'localedir' is added to list of exported
> variables.
>
> But I am not sure how such substitution should be performed.

I'll try to get something like that to work. Actually the main problem
seemed to be that it had a hybrid handwritten Makefile and one made by
EU::MM.

>> +     our $TEXTDOMAINDIR = q</usr/local/share/locale>;
>
> Why q<...> and not simply '...'?

Same reason I use qw<>, that is no particular reason other than it
being easier to type on my keyboard. I'll just change it to ''.

>> +     require POSIX;
>> +     POSIX->import(qw< setlocale >);
>> +     # Non-core prerequisite module
>> +     require Locale::Messages;
>> +     Locale::Messages->import(qw< :locale_h :libintl_h >);
>> +
>> +     setlocale(LC_MESSAGES(), '');
>> +     setlocale(LC_CTYPE(), '');
>> +     textdomain($TEXTDOMAIN);
>> +     bindtextdomain($TEXTDOMAIN => $TEXTDOMAINDIR);
>> +
>> +     return;
>> +}
>
> This would probably be a bit simpler with Locale::TextDomain.
>
>> +
>> +BEGIN
>> +{
>> +     local ($@, $!);
>> +     eval { __bootstrap_locale_messages() };
>> +     if ($@) {
>> +             # Oh noes, no Locale::Messages here
>> +             *gettext = sub ($) { $_[0] };
>
> Do you intended to use subroutine protytype here?  Ah, I see that
> Locale::Messages::gettext has the same prototype...

Yeah, otherwise the fallback would have different semantics.

>> +     }
>> +}
>
> Does it need to be in BEGIN block?  Probably yes.

I'm pretty sure it needs to all be at BEGIN time. I recall that
declaring prototypes at runtime was an error in some older versions,
but it works now:

    $ perl -E '*meh = sub ($) { "foo" }; say prototype "meh"'
    $

Anyway since it's code that's being use'd it's going to always be at
BEGIN time anyway. I just wanted to be explicit.

>> +
>> +1;
>> +
>> +__END__
>> +
>
> It's nice that you have provided documentation.
>
>> +=head1 NAME
>> +
>> +Git::Gettext - Perl interface to Git's Gettext localizations
>> +
>> +=head1 DESCRIPTION
>> +
>> +Git's internal interface to Gettext via L<Locale::Messages>. If
>> +L<Locale::Messages> can't be loaded (it's not a core module) we
>> +provide stub passthrough fallbacks.
>
> Very good.
>
> It would probably be better though to use L<Locale::TextDomain>
> instead of low(er)-level L<Locale::Messages>.
>
>> +
>> +=head1 FUNCTIONS
>> +
>> +=head2 gettext($)
>> +
>> +L<Locale::Messages>'s gettext function if all goes well, otherwise our
>> +passthrough fallback function.
>
> Other packages use _T() function for that, or like Locale::TextDomain
> __() function.
>
>> +
>> +=head1 EXPORTS
>> +
>> +Exports are done via L<Exporter>. Invididual functions can be
>> +exporter, or all of them via the C<:all> export tag.
>
> Shouldn't this be described in less technical way in SYNOPSIS and
> DESCRIPTION sections instead?

Yeah, maybe. But there's a convention for `=head1 EXPORTS' in perl
core / CPAN. So that's what I added at a whim.

>> +
>> +=head1 AUTHOR
>> +
>> +E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avarab@gmail.com>
>> +
>> +=head1 LICENSE AND COPYRIGHT
>> +
>> +Copyright 2010 E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avarab@gmail.com>
>> +
>> +This program is free software, you can redistribute it and/or modify
>> +it under the same terms as Perl itself.
>
> Which is dual licensed: GPL + Perl artistic.
>
> Was it intended to use 'same terms as Perl itself' rather than GPLv2
> or GPLv2+?

It's the permissive boilerplate I auto-insert for Perl code. I'll just
remove it. The COPYING file at the top level will suffice.

>> +
>> +=cut
>> diff --git a/perl/Makefile.PL b/perl/Makefile.PL
>> index 0b9deca..702ec7c 100644
>> --- a/perl/Makefile.PL
>> +++ b/perl/Makefile.PL
>> @@ -16,7 +16,10 @@ endif
>>  MAKE_FRAG
>>  }
>>
>> -my %pm = ('Git.pm' => '$(INST_LIBDIR)/Git.pm');
>> +my %pm = (
>> +     'Git.pm' => '$(INST_LIBDIR)/Git.pm',
>> +     'Git/Gettext.pm' => '$(INST_LIBDIR)/Git/Gettext.pm',
>> +);
>>
>>  # We come with our own bundled Error.pm. It's not in the set of default
>>  # Perl modules so install it if it's not available on the system yet.
>> --
>> 1.7.1.248.gcd6d1
>>

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH/RCF] autoconf: Check if <libintl.h> exists and set NO_GETTEXT
  2010-06-01 18:11     ` [PATCH/RCF] autoconf: Check if <libintl.h> exists and set NO_GETTEXT Ævar Arnfjörð Bjarmason
@ 2010-06-01 21:22       ` Jakub Narebski
  0 siblings, 0 replies; 56+ messages in thread
From: Jakub Narebski @ 2010-06-01 21:22 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason; +Cc: git, Ben Walton, David M. Syzdek

On Tue, Jun 1, 2010, Ævar Arnfjörð Bjarmason wrote:
> On Tue, Jun 1, 2010 at 17:01, Jakub Narebski <jnareb@gmail.com> wrote:
> > [...]
> > Could you also provide change to configure.ac, so that ./configure
> > would detect if we have gettext installed or not?  Thanks in advance.
> 
> Here's a check that just checks if we have libintl.h similar to the
> existing checks for libgen.h and other headers.
> 
> Do you think this be adequate?

I'm not sure.  If NO_GETTEXT is used only to protect '#include <libintl.h>'
it might be enough.

> 
>  config.mak.in |    1 +
>  configure.ac  |    6 ++++++
>  2 files changed, 7 insertions(+), 0 deletions(-)
> 
> diff --git a/config.mak.in b/config.mak.in
> index 0d4b64d..a15f3c1 100644
> --- a/config.mak.in
> +++ b/config.mak.in
> @@ -32,6 +32,7 @@ NO_CURL=@NO_CURL@
>  NO_EXPAT=@NO_EXPAT@
>  NO_LIBGEN_H=@NO_LIBGEN_H@
>  HAVE_PATHS_H=@HAVE_PATHS_H@
> +NO_GETTEXT=@NO_GETTEXT@
>  NEEDS_LIBICONV=@NEEDS_LIBICONV@
>  NEEDS_SOCKET=@NEEDS_SOCKET@
>  NEEDS_RESOLV=@NEEDS_RESOLV@

O.K.

> diff --git a/configure.ac b/configure.ac
> index 71038fc..7bebfd8 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -730,6 +730,12 @@ AC_CHECK_HEADER([paths.h],
>  [HAVE_PATHS_H=])
>  AC_SUBST(HAVE_PATHS_H)
>  #
> +# Define NO_GETTEXT if you don't have libintl.h
> +AC_CHECK_HEADER([libintl.h],
> +[NO_GETTEXT=],
> +[NO_GETTEXT=YesPlease])
> +AC_SUBST(NO_GETTEXT)
> +#
>  # Define NO_STRCASESTR if you don't have strcasestr.
>  GIT_CHECK_FUNC(strcasestr,
>  [NO_STRCASESTR=],

The getext documentation says that `gettext.m4' defines AM_GNU_GETTEXT
macro that tests for the presence of GNU gettext function family.

The complication is that `gettext.m4' might not exist, and then
AM_GNU_GETTEXT would be not defined.  So if we are to use
AM_GNU_GETTEXT, we should I guess protect it with m4_ifdef():

  m4_ifdef([AM_GNU_GETTEXT], [AM_GNU_GETTEXT([external])])

or something like that.  And of course we need to work around the fact
that AM_GNU_GETTEXT sets USE_NLS to either 'yes' or 'no' instead of
setting setting NO_GETTEXT to either non-empty or empty value.

Or we can do something similar to what we do for NEEDS_LIBICINV /
NO_ICONV, namely make ./configure compile simple test program using
gettext.

Cc-ed Ben Walton and David M. Syzdek: perhaps they can help with this
issue.
-- 
Jakub Narebski
Poland

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH/RFC v3 0/7] Add internationalization support to Git
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
                   ` (12 preceding siblings ...)
  2010-05-30 20:54 ` [PATCH/RFC v2 6/6] gettext: Add a skeleton po/is.po Ævar Arnfjörð Bjarmason
@ 2010-06-01 23:39 ` Ævar Arnfjörð Bjarmason
  2010-06-02  0:11   ` Ævar Arnfjörð Bjarmason
  2010-06-01 23:39 ` [PATCH/RFC v3 1/7] Add infrastructure for translating Git with gettext Ævar Arnfjörð Bjarmason
                   ` (6 subsequent siblings)
  20 siblings, 1 reply; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-01 23:39 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Jeff Epler, Ævar Arnfjörð Bjarmason

Here's version 3 of the patch series begun by Jeff Epler to make Git
optionally translatable with GNU gettext.

This version contains a lot of fixes to issues pointe out by Jakub
Narebski, and other misc.

Since the last RFC:

  * Squashed some infrastructure patches into the initial commit.

  * Added basic sanity testing for the Gettext support

  * .mo files aren't installed on `make install` time if NO_GETTEXT
    was specified during the build.

    In C gettext support isn't compiled in. But this happens
    implicitly in Shell/Perl because the locale files won't be there
    on the system.

  * NO_GETTEXT is now set correctly depending on whether libintl.h
    exists when autoconf is used to build Git. Whether this is the
    best approach to gettext detection with autoconf is being
    discussed in the "[PATCH/RCF] autoconf: Check if <libintl.h>
    exists and set NO_GETTEX" thread.

  * The localedir is now passed to the Perl code via Makefile
    trickery. It's no longer hardcoded to /usr/local/share/locale

  * Add `--keyword=` to all xgettext invocations. This way we make
    sure that only our gettext functions are extracted, not the
    defaults xgettext.

  * Git::Gettext is now called Git::I18N

  * Remove LICENSE from I18N.pm. It was a boilerplate that shouldn't
    have been there.

  * Use qw() not qw< > in Perl code.

  * Changed the interface to Git::I18N. Now it exports all its
    functions by default. If something's using the module it's going
    to want to import everything anyway. This makes the issue of '@{
    $EXPORT_TAGS{'all'} }' being ugly syntax moot since it isn't used
    anymore.

    Users can still import selectively by specifying arguments to
    import().

  * The GIT_TEXTDOMAINDIR environment variable can be set to override
    where the C/Perl/Shell locale files are found. This is intended to
    be set by the test suite.

  * The boolean test for whether gettext.sh was successfully loaded
    was inverted (oops!)

Things that haven't changed:

  Jakub pointed out some things that I might want to change that I
  didn't yet. Mostly because I didn't have time yet.

  * Git::I18N still uses Locale::Messages, not Locale::TextDomain.

    I'm still not convinced that the low-level Locale::Messages
    interface isn't a better fit for us. But I didn't get
    Locale::TextDomain working at all today so I'm still using
    Locale::Messages.

  * Perl still uses gettext($), not __($) (see above).

  * use Exporter 'import': Subclassing Exporter instead since we
    apparently need Perl 5.6 compatibility.

Jeff Epler (1):
  Add infrastructure for translating Git with gettext

Ævar Arnfjörð Bjarmason (6):
  gettext: Add a Gettext interface for shell scripts
  gettext: Add a Gettext interface for Perl
  Makefile: Don't install Gettext .mo files if NO_GETTEXT
  Makefile: Override --keyword= for all languages
  gettext: Basic sanity tests for Git's Gettext support
  gettext: Add a skeleton po/is.po

 .gitignore          |    1 +
 Makefile            |   43 ++++++++-
 config.mak.in       |    1 +
 configure.ac        |    6 +
 gettext.c           |   25 +++++
 gettext.h           |   13 +++
 git-pull.sh         |   15 ++--
 git-send-email.perl |    3 +-
 git-sh-setup.sh     |   33 ++++++
 git.c               |    3 +
 perl/Git/I18N.pm    |   73 +++++++++++++
 perl/Makefile       |    3 +-
 perl/Makefile.PL    |   14 +++-
 po/.gitignore       |    1 +
 po/is.po            |  282 +++++++++++++++++++++++++++++++++++++++++++++++++++
 t/t0200-gettext.sh  |   28 +++++
 wt-status.c         |  129 ++++++++++++------------
 17 files changed, 597 insertions(+), 76 deletions(-)
 create mode 100644 gettext.c
 create mode 100644 gettext.h
 create mode 100644 perl/Git/I18N.pm
 create mode 100644 po/.gitignore
 create mode 100644 po/is.po
 create mode 100755 t/t0200-gettext.sh

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH/RFC v3 1/7] Add infrastructure for translating Git with gettext
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
                   ` (13 preceding siblings ...)
  2010-06-01 23:39 ` [PATCH/RFC v3 0/7] Add internationalization support to Git Ævar Arnfjörð Bjarmason
@ 2010-06-01 23:39 ` Ævar Arnfjörð Bjarmason
  2010-06-01 23:39 ` [PATCH/RFC v3 2/7] gettext: Add a Gettext interface for shell scripts Ævar Arnfjörð Bjarmason
                   ` (5 subsequent siblings)
  20 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-01 23:39 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Jeff Epler, Ævar Arnfjörð Bjarmason

From: Jeff Epler <jepler@unpythonic.net>

Change the build process to use GNU's libintl if it's available. If
not we define our own skeleton replacement functions which degrade
gracefully to English.

Gettext .mo files will be installed and looked for in
$(prefix)/share/locale. GIT_LOCALEDIR can also be set to override
that, this variable is only intended for testing Git itself.

Signed-off-by: Jeff Epler <jepler@unpythonic.net>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 .gitignore    |    1 +
 Makefile      |   30 +++++++++++++
 config.mak.in |    1 +
 configure.ac  |    6 +++
 gettext.c     |   25 +++++++++++
 gettext.h     |   13 ++++++
 git.c         |    3 +
 po/.gitignore |    1 +
 wt-status.c   |  129 +++++++++++++++++++++++++++++----------------------------
 9 files changed, 145 insertions(+), 64 deletions(-)
 create mode 100644 gettext.c
 create mode 100644 gettext.h
 create mode 100644 po/.gitignore

diff --git a/.gitignore b/.gitignore
index 14e2b6b..d22c0e5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -204,3 +204,4 @@
 *.pdb
 /Debug/
 /Release/
+/share/
diff --git a/Makefile b/Makefile
index d5d6565..4de0627 100644
--- a/Makefile
+++ b/Makefile
@@ -297,6 +297,8 @@ RPMBUILD = rpmbuild
 TCL_PATH = tclsh
 TCLTK_PATH = wish
 PTHREAD_LIBS = -lpthread
+XGETTEXT = xgettext
+MSGFMT = msgfmt
 
 export TCL_PATH TCLTK_PATH
 
@@ -523,6 +525,7 @@ LIB_H += userdiff.h
 LIB_H += utf8.h
 LIB_H += xdiff-interface.h
 LIB_H += xdiff/xdiff.h
+LIB_H += gettext.h
 
 LIB_OBJS += abspath.o
 LIB_OBJS += advice.o
@@ -564,6 +567,7 @@ LIB_OBJS += entry.o
 LIB_OBJS += environment.o
 LIB_OBJS += exec_cmd.o
 LIB_OBJS += fsck.o
+LIB_OBJS += gettext.o
 LIB_OBJS += graph.o
 LIB_OBJS += grep.o
 LIB_OBJS += hash.o
@@ -1386,6 +1390,12 @@ ifdef USE_NED_ALLOCATOR
        COMPAT_OBJS += compat/nedmalloc/nedmalloc.o
 endif
 
+ifdef NO_GETTEXT
+	COMPAT_CFLAGS += -DNO_GETTEXT
+else
+	LIBINTL = -lintl
+endif
+
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK=NoThanks
 endif
@@ -1415,6 +1425,7 @@ ifndef V
 	QUIET_BUILT_IN = @echo '   ' BUILTIN $@;
 	QUIET_GEN      = @echo '   ' GEN $@;
 	QUIET_LNCP     = @echo '   ' LN/CP $@;
+	QUIET_MSGFMT   = @echo '   ' MSGFMT $@;
 	QUIET_SUBDIR0  = +@subdir=
 	QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
 			 $(MAKE) $(PRINT_DIR) -C $$subdir
@@ -1442,6 +1453,7 @@ gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
 template_dir_SQ = $(subst ','\'',$(template_dir))
 htmldir_SQ = $(subst ','\'',$(htmldir))
 prefix_SQ = $(subst ','\'',$(prefix))
+sharedir_SQ = $(subst ','\'',$(sharedir))
 
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
@@ -1868,6 +1880,17 @@ cscope:
 	$(RM) cscope*
 	$(FIND) . -name '*.[hcS]' -print | xargs cscope -b
 
+pot:
+	$(XGETTEXT) -k_ -o po/git.pot $(C_OBJ:o=c)
+
+POFILES := $(wildcard po/*.po)
+MOFILES := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/git.mo,$(POFILES))
+MODIRS := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/,$(POFILES))
+all:: $(MOFILES)
+share/locale/%/LC_MESSAGES/git.mo: po/%.po
+	@mkdir -p $(dir $@)
+	$(QUIET_MSGFMT)$(MSGFMT) -o $@ $<
+
 ### Detect prefix changes
 TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
              $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
@@ -1980,6 +2003,9 @@ install: all
 	$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 	$(INSTALL) -m 644 $(SCRIPT_LIB) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 	$(INSTALL) $(install_bindir_programs) '$(DESTDIR_SQ)$(bindir_SQ)'
+	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sharedir_SQ)/locale'
+	(cd share && tar cf - locale) | \
+		(cd '$(DESTDIR_SQ)$(sharedir_SQ)' && umask 022 && tar xof -)
 	$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
 ifndef NO_PERL
 	$(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
@@ -2127,6 +2153,10 @@ ifndef NO_TCLTK
 	$(MAKE) -C git-gui clean
 endif
 	$(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS
+ifndef NO_GETTEXT
+	$(RM) po/git.pot
+	$(RM) -r share/
+endif
 
 .PHONY: all install clean strip
 .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
diff --git a/config.mak.in b/config.mak.in
index 0d4b64d..a15f3c1 100644
--- a/config.mak.in
+++ b/config.mak.in
@@ -32,6 +32,7 @@ NO_CURL=@NO_CURL@
 NO_EXPAT=@NO_EXPAT@
 NO_LIBGEN_H=@NO_LIBGEN_H@
 HAVE_PATHS_H=@HAVE_PATHS_H@
+NO_GETTEXT=@NO_GETTEXT@
 NEEDS_LIBICONV=@NEEDS_LIBICONV@
 NEEDS_SOCKET=@NEEDS_SOCKET@
 NEEDS_RESOLV=@NEEDS_RESOLV@
diff --git a/configure.ac b/configure.ac
index 71038fc..7bebfd8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -730,6 +730,12 @@ AC_CHECK_HEADER([paths.h],
 [HAVE_PATHS_H=])
 AC_SUBST(HAVE_PATHS_H)
 #
+# Define NO_GETTEXT if you don't have libintl.h
+AC_CHECK_HEADER([libintl.h],
+[NO_GETTEXT=],
+[NO_GETTEXT=YesPlease])
+AC_SUBST(NO_GETTEXT)
+#
 # Define NO_STRCASESTR if you don't have strcasestr.
 GIT_CHECK_FUNC(strcasestr,
 [NO_STRCASESTR=],
diff --git a/gettext.c b/gettext.c
new file mode 100644
index 0000000..c67e5ca
--- /dev/null
+++ b/gettext.c
@@ -0,0 +1,25 @@
+#ifdef NO_GETTEXT
+void git_setup_gettext(void) {}
+#else
+#include "exec_cmd.h"
+#include <libintl.h>
+#include <stdlib.h>
+
+void git_setup_gettext(void) {
+	char *podir;
+    char *envdir = getenv("GIT_TEXTDOMAINDIR");
+
+	if (envdir) {
+		(void)bindtextdomain("git", envdir);
+	} else {
+		podir = (char *)system_path("share/locale");
+		if (!podir) return;
+		(void)bindtextdomain("git", podir);
+		free(podir);
+	}
+
+	(void)setlocale(LC_MESSAGES, "");
+	(void)setlocale(LC_CTYPE, "");
+	(void)textdomain("git");
+}
+#endif
diff --git a/gettext.h b/gettext.h
new file mode 100644
index 0000000..a99da6a
--- /dev/null
+++ b/gettext.h
@@ -0,0 +1,13 @@
+#ifndef GETTEXT_H
+#define GETTEXT_H
+
+void git_setup_gettext(void);
+
+#ifdef NO_GETTEXT
+#define _(s) (s)
+#else
+#include <libintl.h>
+#define _(s) gettext(s)
+#endif
+
+#endif
diff --git a/git.c b/git.c
index 99f0363..d749eab 100644
--- a/git.c
+++ b/git.c
@@ -3,6 +3,7 @@
 #include "cache.h"
 #include "quote.h"
 #include "run-command.h"
+#include "gettext.h"
 
 const char git_usage_string[] =
 	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n"
@@ -490,6 +491,8 @@ int main(int argc, const char **argv)
 	if (!cmd)
 		cmd = "git-help";
 
+	git_setup_gettext();
+
 	/*
 	 * "git-xxxx" is the same as "git xxxx", but we obviously:
 	 *
diff --git a/po/.gitignore b/po/.gitignore
new file mode 100644
index 0000000..221000e
--- /dev/null
+++ b/po/.gitignore
@@ -0,0 +1 @@
+/*.pot
diff --git a/wt-status.c b/wt-status.c
index 14e0acc..70b4293 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -9,6 +9,7 @@
 #include "quote.h"
 #include "run-command.h"
 #include "remote.h"
+#include "gettext.h"
 
 static char default_wt_status_colors[][COLOR_MAXLEN] = {
 	GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
@@ -49,33 +50,33 @@ static void wt_status_print_unmerged_header(struct wt_status *s)
 {
 	const char *c = color(WT_STATUS_HEADER, s);
 
-	color_fprintf_ln(s->fp, c, "# Unmerged paths:");
+	color_fprintf_ln(s->fp, c, _("# Unmerged paths:"));
 	if (!advice_status_hints)
 		return;
 	if (s->in_merge)
 		;
 	else if (!s->is_initial)
-		color_fprintf_ln(s->fp, c, "#   (use \"git reset %s <file>...\" to unstage)", s->reference);
+		color_fprintf_ln(s->fp, c, _("#   (use \"git reset %s <file>...\" to unstage)"), s->reference);
 	else
-		color_fprintf_ln(s->fp, c, "#   (use \"git rm --cached <file>...\" to unstage)");
-	color_fprintf_ln(s->fp, c, "#   (use \"git add/rm <file>...\" as appropriate to mark resolution)");
-	color_fprintf_ln(s->fp, c, "#");
+		color_fprintf_ln(s->fp, c, _("#   (use \"git rm --cached <file>...\" to unstage)"));
+	color_fprintf_ln(s->fp, c, _("#   (use \"git add/rm <file>...\" as appropriate to mark resolution)"));
+	color_fprintf_ln(s->fp, c, _("#"));
 }
 
 static void wt_status_print_cached_header(struct wt_status *s)
 {
 	const char *c = color(WT_STATUS_HEADER, s);
 
-	color_fprintf_ln(s->fp, c, "# Changes to be committed:");
+	color_fprintf_ln(s->fp, c, _("# Changes to be committed:"));
 	if (!advice_status_hints)
 		return;
 	if (s->in_merge)
 		; /* NEEDSWORK: use "git reset --unresolve"??? */
 	else if (!s->is_initial)
-		color_fprintf_ln(s->fp, c, "#   (use \"git reset %s <file>...\" to unstage)", s->reference);
+		color_fprintf_ln(s->fp, c, _("#   (use \"git reset %s <file>...\" to unstage)"), s->reference);
 	else
-		color_fprintf_ln(s->fp, c, "#   (use \"git rm --cached <file>...\" to unstage)");
-	color_fprintf_ln(s->fp, c, "#");
+		color_fprintf_ln(s->fp, c, _("#   (use \"git rm --cached <file>...\" to unstage)"));
+	color_fprintf_ln(s->fp, c, _("#"));
 }
 
 static void wt_status_print_dirty_header(struct wt_status *s,
@@ -84,17 +85,17 @@ static void wt_status_print_dirty_header(struct wt_status *s,
 {
 	const char *c = color(WT_STATUS_HEADER, s);
 
-	color_fprintf_ln(s->fp, c, "# Changed but not updated:");
+	color_fprintf_ln(s->fp, c, _("# Changed but not updated:"));
 	if (!advice_status_hints)
 		return;
 	if (!has_deleted)
-		color_fprintf_ln(s->fp, c, "#   (use \"git add <file>...\" to update what will be committed)");
+		color_fprintf_ln(s->fp, c, _("#   (use \"git add <file>...\" to update what will be committed)"));
 	else
-		color_fprintf_ln(s->fp, c, "#   (use \"git add/rm <file>...\" to update what will be committed)");
-	color_fprintf_ln(s->fp, c, "#   (use \"git checkout -- <file>...\" to discard changes in working directory)");
+		color_fprintf_ln(s->fp, c, _("#   (use \"git add/rm <file>...\" to update what will be committed)"));
+	color_fprintf_ln(s->fp, c, _("#   (use \"git checkout -- <file>...\" to discard changes in working directory)"));
 	if (has_dirty_submodules)
-		color_fprintf_ln(s->fp, c, "#   (commit or discard the untracked or modified content in submodules)");
-	color_fprintf_ln(s->fp, c, "#");
+		color_fprintf_ln(s->fp, c, _("#   (commit or discard the untracked or modified content in submodules)"));
+	color_fprintf_ln(s->fp, c, _("#"));
 }
 
 static void wt_status_print_other_header(struct wt_status *s,
@@ -102,16 +103,16 @@ static void wt_status_print_other_header(struct wt_status *s,
 					 const char *how)
 {
 	const char *c = color(WT_STATUS_HEADER, s);
-	color_fprintf_ln(s->fp, c, "# %s files:", what);
+	color_fprintf_ln(s->fp, c, _("# %s files:"), what);
 	if (!advice_status_hints)
 		return;
-	color_fprintf_ln(s->fp, c, "#   (use \"git %s <file>...\" to include in what will be committed)", how);
-	color_fprintf_ln(s->fp, c, "#");
+	color_fprintf_ln(s->fp, c, _("#   (use \"git %s <file>...\" to include in what will be committed)"), how);
+	color_fprintf_ln(s->fp, c, _("#"));
 }
 
 static void wt_status_print_trailer(struct wt_status *s)
 {
-	color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
+	color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("#"));
 }
 
 #define quote_path quote_path_relative
@@ -122,20 +123,20 @@ static void wt_status_print_unmerged_data(struct wt_status *s,
 	const char *c = color(WT_STATUS_UNMERGED, s);
 	struct wt_status_change_data *d = it->util;
 	struct strbuf onebuf = STRBUF_INIT;
-	const char *one, *how = "bug";
+	const char *one, *how = _("bug");
 
 	one = quote_path(it->string, -1, &onebuf, s->prefix);
-	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
+	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), _("#\t"));
 	switch (d->stagemask) {
-	case 1: how = "both deleted:"; break;
-	case 2: how = "added by us:"; break;
-	case 3: how = "deleted by them:"; break;
-	case 4: how = "added by them:"; break;
-	case 5: how = "deleted by us:"; break;
-	case 6: how = "both added:"; break;
-	case 7: how = "both modified:"; break;
+	case 1: how = _("both deleted:"); break;
+	case 2: how = _("added by us:"); break;
+	case 3: how = _("deleted by them:"); break;
+	case 4: how = _("added by them:"); break;
+	case 5: how = _("deleted by us:"); break;
+	case 6: how = _("both added:"); break;
+	case 7: how = _("both modified:"); break;
 	}
-	color_fprintf(s->fp, c, "%-20s%s\n", how, one);
+	color_fprintf(s->fp, c, _("%-20s%s\n"), how, one);
 	strbuf_release(&onebuf);
 }
 
@@ -161,13 +162,13 @@ static void wt_status_print_change_data(struct wt_status *s,
 		break;
 	case WT_STATUS_CHANGED:
 		if (d->new_submodule_commits || d->dirty_submodule) {
-			strbuf_addstr(&extra, " (");
+			strbuf_addstr(&extra, _(" ("));
 			if (d->new_submodule_commits)
-				strbuf_addf(&extra, "new commits, ");
+				strbuf_addf(&extra, _("new commits, "));
 			if (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
-				strbuf_addf(&extra, "modified content, ");
+				strbuf_addf(&extra, _("modified content, "));
 			if (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
-				strbuf_addf(&extra, "untracked content, ");
+				strbuf_addf(&extra, _("untracked content, "));
 			strbuf_setlen(&extra, extra.len - 2);
 			strbuf_addch(&extra, ')');
 		}
@@ -178,40 +179,40 @@ static void wt_status_print_change_data(struct wt_status *s,
 	one = quote_path(one_name, -1, &onebuf, s->prefix);
 	two = quote_path(two_name, -1, &twobuf, s->prefix);
 
-	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
+	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), _("#\t"));
 	switch (status) {
 	case DIFF_STATUS_ADDED:
-		color_fprintf(s->fp, c, "new file:   %s", one);
+		color_fprintf(s->fp, c, _("new file:   %s"), one);
 		break;
 	case DIFF_STATUS_COPIED:
-		color_fprintf(s->fp, c, "copied:     %s -> %s", one, two);
+		color_fprintf(s->fp, c, _("copied:     %s -> %s"), one, two);
 		break;
 	case DIFF_STATUS_DELETED:
-		color_fprintf(s->fp, c, "deleted:    %s", one);
+		color_fprintf(s->fp, c, _("deleted:    %s"), one);
 		break;
 	case DIFF_STATUS_MODIFIED:
-		color_fprintf(s->fp, c, "modified:   %s", one);
+		color_fprintf(s->fp, c, _("modified:   %s"), one);
 		break;
 	case DIFF_STATUS_RENAMED:
-		color_fprintf(s->fp, c, "renamed:    %s -> %s", one, two);
+		color_fprintf(s->fp, c, _("renamed:    %s -> %s"), one, two);
 		break;
 	case DIFF_STATUS_TYPE_CHANGED:
-		color_fprintf(s->fp, c, "typechange: %s", one);
+		color_fprintf(s->fp, c, _("typechange: %s"), one);
 		break;
 	case DIFF_STATUS_UNKNOWN:
-		color_fprintf(s->fp, c, "unknown:    %s", one);
+		color_fprintf(s->fp, c, _("unknown:    %s"), one);
 		break;
 	case DIFF_STATUS_UNMERGED:
-		color_fprintf(s->fp, c, "unmerged:   %s", one);
+		color_fprintf(s->fp, c, _("unmerged:   %s"), one);
 		break;
 	default:
-		die("bug: unhandled diff status %c", status);
+		die(_("bug: unhandled diff status %c"), status);
 	}
 	if (extra.len) {
-		color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "%s", extra.buf);
+		color_fprintf(s->fp, color(WT_STATUS_HEADER, s), _("%s"), extra.buf);
 		strbuf_release(&extra);
 	}
-	fprintf(s->fp, "\n");
+	fprintf(s->fp, _("\n"));
 	strbuf_release(&onebuf);
 	strbuf_release(&twobuf);
 }
@@ -618,14 +619,14 @@ void wt_status_print(struct wt_status *s)
 	const char *branch_color = color(WT_STATUS_HEADER, s);
 
 	if (s->branch) {
-		const char *on_what = "On branch ";
+		const char *on_what = _("On branch ");
 		const char *branch_name = s->branch;
 		if (!prefixcmp(branch_name, "refs/heads/"))
 			branch_name += 11;
 		else if (!strcmp(branch_name, "HEAD")) {
 			branch_name = "";
 			branch_color = color(WT_STATUS_NOBRANCH, s);
-			on_what = "Not currently on any branch.";
+			on_what = _("Not currently on any branch.");
 		}
 		color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "# ");
 		color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name);
@@ -634,9 +635,9 @@ void wt_status_print(struct wt_status *s)
 	}
 
 	if (s->is_initial) {
-		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
-		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "# Initial commit");
-		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
+		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("#"));
+		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("# Initial commit"));
+		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("#"));
 	}
 
 	wt_status_print_updated(s);
@@ -647,38 +648,38 @@ void wt_status_print(struct wt_status *s)
 		wt_status_print_submodule_summary(s, 1);  /* unstaged */
 	}
 	if (s->show_untracked_files) {
-		wt_status_print_other(s, &s->untracked, "Untracked", "add");
+		wt_status_print_other(s, &s->untracked, _("Untracked"), _("add"));
 		if (s->show_ignored_files)
-			wt_status_print_other(s, &s->ignored, "Ignored", "add -f");
+			wt_status_print_other(s, &s->ignored, _("Ignored"), _("add -f"));
 	} else if (s->commitable)
-		fprintf(s->fp, "# Untracked files not listed%s\n",
+		fprintf(s->fp, _("# Untracked files not listed%s\n"),
 			advice_status_hints
-			? " (use -u option to show untracked files)" : "");
+			? _(" (use -u option to show untracked files)") : "");
 
 	if (s->verbose)
 		wt_status_print_verbose(s);
 	if (!s->commitable) {
 		if (s->amend)
-			fprintf(s->fp, "# No changes\n");
+			fprintf(s->fp, _("# No changes\n"));
 		else if (s->nowarn)
 			; /* nothing */
 		else if (s->workdir_dirty)
-			printf("no changes added to commit%s\n",
+			printf(_("no changes added to commit%s\n"),
 				advice_status_hints
-				? " (use \"git add\" and/or \"git commit -a\")" : "");
+				? _(" (use \"git add\" and/or \"git commit -a\")") : "");
 		else if (s->untracked.nr)
-			printf("nothing added to commit but untracked files present%s\n",
+			printf(_("nothing added to commit but untracked files present%s\n"),
 				advice_status_hints
-				? " (use \"git add\" to track)" : "");
+				? _(" (use \"git add\" to track)") : "");
 		else if (s->is_initial)
 			printf("nothing to commit%s\n", advice_status_hints
-				? " (create/copy files and use \"git add\" to track)" : "");
+				? _(" (create/copy files and use \"git add\" to track)") : "");
 		else if (!s->show_untracked_files)
-			printf("nothing to commit%s\n", advice_status_hints
-				? " (use -u to show untracked files)" : "");
+			printf(_("nothing to commit%s\n"), advice_status_hints
+				? _(" (use -u to show untracked files)") : "");
 		else
-			printf("nothing to commit%s\n", advice_status_hints
-				? " (working directory clean)" : "");
+			printf(_("nothing to commit%s\n"), advice_status_hints
+				? _(" (working directory clean)") : "");
 	}
 }
 
-- 
1.7.1.248.gb7713.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH/RFC v3 2/7] gettext: Add a Gettext interface for shell scripts
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
                   ` (14 preceding siblings ...)
  2010-06-01 23:39 ` [PATCH/RFC v3 1/7] Add infrastructure for translating Git with gettext Ævar Arnfjörð Bjarmason
@ 2010-06-01 23:39 ` Ævar Arnfjörð Bjarmason
  2010-06-02  6:32   ` Johannes Sixt
  2010-06-01 23:39 ` [PATCH/RFC v3 3/7] gettext: Add a Gettext interface for Perl Ævar Arnfjörð Bjarmason
                   ` (4 subsequent siblings)
  20 siblings, 1 reply; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-01 23:39 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Jeff Epler, Ævar Arnfjörð Bjarmason

Use GNU's gettext.sh in git-sh-setup if it's available, otherwise
fallback on our own custom functions.

A couple of strings in git-pull.sh are now translatable as a proof of
concept, including a multiline string.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Makefile        |    7 +++++--
 git-pull.sh     |   15 ++++++++-------
 git-sh-setup.sh |   33 +++++++++++++++++++++++++++++++++
 3 files changed, 46 insertions(+), 9 deletions(-)

diff --git a/Makefile b/Makefile
index 4de0627..dce2faa 100644
--- a/Makefile
+++ b/Makefile
@@ -272,6 +272,7 @@ mandir = share/man
 infodir = share/info
 gitexecdir = libexec/git-core
 sharedir = $(prefix)/share
+localedir = $(sharedir)/locale
 template_dir = share/git-core/templates
 htmldir = share/doc/git-doc
 ifeq ($(prefix),/usr)
@@ -285,7 +286,7 @@ lib = lib
 # DESTDIR=
 pathsep = :
 
-export prefix bindir sharedir sysconfdir
+export prefix bindir sharedir localedir sysconfdir
 
 CC = gcc
 AR = ar
@@ -1455,6 +1456,7 @@ htmldir_SQ = $(subst ','\'',$(htmldir))
 prefix_SQ = $(subst ','\'',$(prefix))
 sharedir_SQ = $(subst ','\'',$(sharedir))
 
+LOCALEDIR_SQ = $(subst ','\'',$(localedir))
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
@@ -1548,6 +1550,7 @@ $(RM) $@ $@+ && \
 sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
     -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
     -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+    -e 's|@@LOCALEDIR@@|$(LOCALEDIR_SQ)|g' \
     -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
     -e $(BROKEN_PATH_FIX) \
     $@.sh >$@+
@@ -1881,7 +1884,7 @@ cscope:
 	$(FIND) . -name '*.[hcS]' -print | xargs cscope -b
 
 pot:
-	$(XGETTEXT) -k_ -o po/git.pot $(C_OBJ:o=c)
+	$(XGETTEXT) -k_ -o po/git.pot $(C_OBJ:o=c) $(SCRIPT_SH)
 
 POFILES := $(wildcard po/*.po)
 MOFILES := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/git.mo,$(POFILES))
diff --git a/git-pull.sh b/git-pull.sh
index 1a4729f..22a6da2 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -121,8 +121,8 @@ error_on_no_merge_candidates () {
 	do
 		case "$opt" in
 		-t|--t|--ta|--tag|--tags)
-			echo "Fetching tags only, you probably meant:"
-			echo "  git fetch --tags"
+			gettext "Fetching tags only, you probably meant:"; echo
+			gettext "  git fetch --tags"; echo
 			exit 1
 		esac
 	done
@@ -154,11 +154,12 @@ error_on_no_merge_candidates () {
 		echo "a branch. Because this is not the default configured remote"
 		echo "for your current branch, you must specify a branch on the command line."
 	elif [ -z "$curr_branch" ]; then
-		echo "You are not currently on a branch, so I cannot use any"
-		echo "'branch.<branchname>.merge' in your configuration file."
-		echo "Please specify which remote branch you want to use on the command"
-		echo "line and try again (e.g. 'git pull <repository> <refspec>')."
-		echo "See git-pull(1) for details."
+        gettext "You are not currently on a branch, so I cannot use any
+'branch.<branchname>.merge' in your configuration file.
+Please specify which remote branch you want to use on the command
+line and try again (e.g. 'git pull <repository> <refspec>').
+See git-pull(1) for details.";
+        echo
 	elif [ -z "$upstream" ]; then
 		echo "You asked me to pull without telling me which branch you"
 		echo "want to $op_type $op_prep, and 'branch.${curr_branch}.merge' in"
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index 6131670..d67901c 100644
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -211,3 +211,36 @@ case $(uname -s) in
 	}
 	;;
 esac
+
+# Try to use libintl's gettext.sh, or fall back to English if we
+# can't.
+. gettext.sh
+if test $? -eq 0
+then
+    TEXTDOMAIN=git
+    export TEXDTOMAIN
+    TEXTDOMAINDIR="@@LOCALEDIR@@"
+    export TEXTDOMAINDIR
+else
+    # Since GNU gettext.sh isn't available we'll have to define our
+    # own dummy functions.
+
+    # This code adapted from NessusClient-1.0.2's nessusclient-mkcert
+    # by Michel Arboi <arboi@alussinan.org>. The original code is
+    # under the GPLv2.
+
+    # Not everyone has echo -n
+    case $(echo -n) in
+        \-n)    Xn=   ; Xc='\c' ;;
+        *)      Xn=-n ; Xc=
+    esac
+
+    gettext () {
+        echo $Xn "$1" $Xc
+    }
+
+    eval_gettext () {
+        eval_gettext_var="echo $1"
+        echo $Xn `eval $eval_gettext_var` $Xc
+    }
+fi
-- 
1.7.1.248.gb7713.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH/RFC v3 3/7] gettext: Add a Gettext interface for Perl
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
                   ` (15 preceding siblings ...)
  2010-06-01 23:39 ` [PATCH/RFC v3 2/7] gettext: Add a Gettext interface for shell scripts Ævar Arnfjörð Bjarmason
@ 2010-06-01 23:39 ` Ævar Arnfjörð Bjarmason
  2010-06-01 23:39 ` [PATCH/RFC v3 4/7] Makefile: Don't install Gettext .mo files if NO_GETTEXT Ævar Arnfjörð Bjarmason
                   ` (3 subsequent siblings)
  20 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-01 23:39 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Jeff Epler, Ævar Arnfjörð Bjarmason

Make Git's gettext messages available to Perl programs through
Locale::Messages. Gracefully fall back to English on systems that
don't contain the module.

This contains some makefile hacks to pass localedir to
perl/Makefile. Which in turn passes it to perl.mak, which'll use it to
search/replace occurances of ++LOCALEDIR++ in .pm files.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Makefile            |    6 +++-
 git-send-email.perl |    3 +-
 perl/Git/I18N.pm    |   73 +++++++++++++++++++++++++++++++++++++++++++++++++++
 perl/Makefile       |    3 +-
 perl/Makefile.PL    |   14 +++++++++-
 5 files changed, 94 insertions(+), 5 deletions(-)
 create mode 100644 perl/Git/I18N.pm

diff --git a/Makefile b/Makefile
index dce2faa..3f0e305 100644
--- a/Makefile
+++ b/Makefile
@@ -1505,7 +1505,7 @@ ifndef NO_TCLTK
 	$(QUIET_SUBDIR0)gitk-git $(QUIET_SUBDIR1) all
 endif
 ifndef NO_PERL
-	$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
+	$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' localedir='$(localedir_SQ)' all
 endif
 ifndef NO_PYTHON
 	$(QUIET_SUBDIR0)git_remote_helpers $(QUIET_SUBDIR1) PYTHON_PATH='$(PYTHON_PATH_SQ)' prefix='$(prefix_SQ)' all
@@ -1884,7 +1884,9 @@ cscope:
 	$(FIND) . -name '*.[hcS]' -print | xargs cscope -b
 
 pot:
-	$(XGETTEXT) -k_ -o po/git.pot $(C_OBJ:o=c) $(SCRIPT_SH)
+	$(XGETTEXT) --keyword=_ --output=po/git.pot --language=C $(C_OBJ:o=c)
+	$(XGETTEXT) --join-existing --output=po/git.pot --language=Shell $(SCRIPT_SH)
+	$(XGETTEXT) --join-existing --output=po/git.pot --language=Perl $(SCRIPT_PERL)
 
 POFILES := $(wildcard po/*.po)
 MOFILES := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/git.mo,$(POFILES))
diff --git a/git-send-email.perl b/git-send-email.perl
index 111c981..b9288af 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -26,6 +26,7 @@ use Term::ANSIColor;
 use File::Temp qw/ tempdir tempfile /;
 use Error qw(:try);
 use Git;
+use Git::I18N;
 
 Getopt::Long::Configure qw/ pass_through /;
 
@@ -674,7 +675,7 @@ if (!defined $sender) {
 	$sender = $repoauthor || $repocommitter || '';
 	$sender = ask("Who should the emails appear to be from? [$sender] ",
 	              default => $sender);
-	print "Emails will be sent from: ", $sender, "\n";
+	printf gettext("Emails will be sent from: %s\n"), $sender;
 	$prompting++;
 }
 
diff --git a/perl/Git/I18N.pm b/perl/Git/I18N.pm
new file mode 100644
index 0000000..568fd3d
--- /dev/null
+++ b/perl/Git/I18N.pm
@@ -0,0 +1,73 @@
+package Git::I18N;
+use strict;
+use warnings;
+use Exporter;
+use base 'Exporter';
+
+our $VERSION = '0.01';
+
+our @EXPORT = qw(gettext);
+our @EXPORT_OK = @EXPORT;
+
+sub __bootstrap_locale_messages {
+	our $TEXTDOMAIN = 'git';
+	our $TEXTDOMAINDIR = '++LOCALEDIR++';
+
+	require POSIX;
+	POSIX->import(qw(setlocale));
+	# Non-core prerequisite module
+	require Locale::Messages;
+	Locale::Messages->import(qw(:locale_h :libintl_h));
+
+	setlocale(LC_MESSAGES(), '');
+	setlocale(LC_CTYPE(), '');
+	textdomain($TEXTDOMAIN);
+	bindtextdomain($TEXTDOMAIN => $TEXTDOMAINDIR);
+
+	return;
+}
+
+BEGIN
+{
+	local ($@, $!);
+	eval { __bootstrap_locale_messages() };
+	if ($@) {
+		# Oh noes, no Locale::Messages here
+		*gettext = sub ($) { $_[0] };
+	}
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Git::I18N - Perl interface to Git's Gettext localizations
+
+=head1 SYNOPSIS
+
+	use Git::I18N;
+
+	print gettext("Welcome to Git!\n");
+
+	printf gettext("The following error occured: %s\n"), $error;
+
+=head1 DESCRIPTION
+
+Git's internal interface to Gettext via L<Locale::Messages>. If
+L<Locale::Messages> can't be loaded (it's not a core module) we
+provide stub passthrough fallbacks.
+
+=head1 FUNCTIONS
+
+=head2 gettext($)
+
+L<Locale::Messages>'s gettext function if all goes well, otherwise our
+passthrough fallback function.
+
+=head1 AUTHOR
+
+E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avarab@gmail.com>
+
+=cut
diff --git a/perl/Makefile b/perl/Makefile
index 4ab21d6..4e624ff 100644
--- a/perl/Makefile
+++ b/perl/Makefile
@@ -5,6 +5,7 @@ makfile:=perl.mak
 
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 prefix_SQ = $(subst ','\'',$(prefix))
+localedir_SQ = $(subst ','\'',$(localedir))
 
 ifndef V
 	QUIET = @
@@ -38,7 +39,7 @@ $(makfile): ../GIT-CFLAGS Makefile
 	echo '	echo $(instdir_SQ)' >> $@
 else
 $(makfile): Makefile.PL ../GIT-CFLAGS
-	$(PERL_PATH) $< PREFIX='$(prefix_SQ)'
+	$(PERL_PATH) $< PREFIX='$(prefix_SQ)' --localedir='$(localedir_SQ)'
 endif
 
 # this is just added comfort for calling make directly in perl dir
diff --git a/perl/Makefile.PL b/perl/Makefile.PL
index 0b9deca..456d45b 100644
--- a/perl/Makefile.PL
+++ b/perl/Makefile.PL
@@ -1,4 +1,12 @@
+use strict;
+use warnings;
 use ExtUtils::MakeMaker;
+use Getopt::Long;
+
+# Sanity: die at first unknown option
+Getopt::Long::Configure qw/ pass_through /;
+
+GetOptions("localedir=s" => \my $localedir);
 
 sub MY::postamble {
 	return <<'MAKE_FRAG';
@@ -16,7 +24,10 @@ endif
 MAKE_FRAG
 }
 
-my %pm = ('Git.pm' => '$(INST_LIBDIR)/Git.pm');
+my %pm = (
+	'Git.pm' => '$(INST_LIBDIR)/Git.pm',
+	'Git/I18N.pm' => '$(INST_LIBDIR)/Git/I18N.pm',
+);
 
 # We come with our own bundled Error.pm. It's not in the set of default
 # Perl modules so install it if it's not available on the system yet.
@@ -33,6 +44,7 @@ WriteMakefile(
 	NAME            => 'Git',
 	VERSION_FROM    => 'Git.pm',
 	PM		=> \%pm,
+	PM_FILTER	=> qq[\$(PERL) -pe "s<\\Q++LOCALEDIR++\\E><$localedir>"],
 	MAKEFILE	=> 'perl.mak',
 	INSTALLSITEMAN3DIR => '$(SITEPREFIX)/share/man/man3'
 );
-- 
1.7.1.248.gb7713.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH/RFC v3 4/7] Makefile: Don't install Gettext .mo files if NO_GETTEXT
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
                   ` (16 preceding siblings ...)
  2010-06-01 23:39 ` [PATCH/RFC v3 3/7] gettext: Add a Gettext interface for Perl Ævar Arnfjörð Bjarmason
@ 2010-06-01 23:39 ` Ævar Arnfjörð Bjarmason
  2010-06-01 23:39 ` [PATCH/RFC v3 5/7] Makefile: Override --keyword= for all languages Ævar Arnfjörð Bjarmason
                   ` (2 subsequent siblings)
  20 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-01 23:39 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Jeff Epler, Ævar Arnfjörð Bjarmason

Change the Makefile not to build/install .mo files as part of the
default target if NO_GETTEXT=1 is given.

This was both redundant, and meant that the Perl and Shell programs
would use the Gettext strings, since they don't use the
git_setup_gettext() function in gettext.c, which is compiled to a stub
if NO_GETTEXT=1 is set.

Reported-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Makefile |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile
index 3f0e305..40eb1a2 100644
--- a/Makefile
+++ b/Makefile
@@ -1891,7 +1891,9 @@ pot:
 POFILES := $(wildcard po/*.po)
 MOFILES := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/git.mo,$(POFILES))
 MODIRS := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/,$(POFILES))
+ifndef NO_GETTEXT
 all:: $(MOFILES)
+endif
 share/locale/%/LC_MESSAGES/git.mo: po/%.po
 	@mkdir -p $(dir $@)
 	$(QUIET_MSGFMT)$(MSGFMT) -o $@ $<
@@ -2008,9 +2010,11 @@ install: all
 	$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 	$(INSTALL) -m 644 $(SCRIPT_LIB) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 	$(INSTALL) $(install_bindir_programs) '$(DESTDIR_SQ)$(bindir_SQ)'
+ifndef NO_GETTEXT
 	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sharedir_SQ)/locale'
 	(cd share && tar cf - locale) | \
 		(cd '$(DESTDIR_SQ)$(sharedir_SQ)' && umask 022 && tar xof -)
+endif
 	$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
 ifndef NO_PERL
 	$(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
-- 
1.7.1.248.gb7713.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH/RFC v3 5/7] Makefile: Override --keyword= for all languages
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
                   ` (17 preceding siblings ...)
  2010-06-01 23:39 ` [PATCH/RFC v3 4/7] Makefile: Don't install Gettext .mo files if NO_GETTEXT Ævar Arnfjörð Bjarmason
@ 2010-06-01 23:39 ` Ævar Arnfjörð Bjarmason
  2010-06-01 23:39 ` [PATCH/RFC v3 6/7] gettext: Basic sanity tests for Git's Gettext support Ævar Arnfjörð Bjarmason
  2010-06-01 23:39 ` [PATCH/RFC v3 7/7] gettext: Add a skeleton po/is.po Ævar Arnfjörð Bjarmason
  20 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-01 23:39 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Jeff Epler, Ævar Arnfjörð Bjarmason

By default xgettext will supply a default list of keywords depending
on the --language parameter. Disable this in favor of supplying a list
of keywords manually.

This reduced the surface area for potential bugs.
---
 Makefile |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Makefile b/Makefile
index 40eb1a2..62b8279 100644
--- a/Makefile
+++ b/Makefile
@@ -1884,9 +1884,9 @@ cscope:
 	$(FIND) . -name '*.[hcS]' -print | xargs cscope -b
 
 pot:
-	$(XGETTEXT) --keyword=_ --output=po/git.pot --language=C $(C_OBJ:o=c)
-	$(XGETTEXT) --join-existing --output=po/git.pot --language=Shell $(SCRIPT_SH)
-	$(XGETTEXT) --join-existing --output=po/git.pot --language=Perl $(SCRIPT_PERL)
+	$(XGETTEXT) --keyword= --keyword=_ --output=po/git.pot --language=C $(C_OBJ:o=c)
+	$(XGETTEXT) --join-existing --keyword= --keyword=gettext --output=po/git.pot --language=Shell $(SCRIPT_SH)
+	$(XGETTEXT) --join-existing --keyword= --keyword=gettext --output=po/git.pot --language=Perl $(SCRIPT_PERL)
 
 POFILES := $(wildcard po/*.po)
 MOFILES := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/git.mo,$(POFILES))
-- 
1.7.1.248.gb7713.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH/RFC v3 6/7] gettext: Basic sanity tests for Git's Gettext support
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
                   ` (18 preceding siblings ...)
  2010-06-01 23:39 ` [PATCH/RFC v3 5/7] Makefile: Override --keyword= for all languages Ævar Arnfjörð Bjarmason
@ 2010-06-01 23:39 ` Ævar Arnfjörð Bjarmason
  2010-06-02  6:32   ` Johannes Sixt
  2010-06-01 23:39 ` [PATCH/RFC v3 7/7] gettext: Add a skeleton po/is.po Ævar Arnfjörð Bjarmason
  20 siblings, 1 reply; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-01 23:39 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Jeff Epler, Ævar Arnfjörð Bjarmason

---
 t/t0200-gettext.sh |   28 ++++++++++++++++++++++++++++
 1 files changed, 28 insertions(+), 0 deletions(-)
 create mode 100755 t/t0200-gettext.sh

diff --git a/t/t0200-gettext.sh b/t/t0200-gettext.sh
new file mode 100755
index 0000000..a8f9edd
--- /dev/null
+++ b/t/t0200-gettext.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+test_description='Gettext support for Git'
+. ./test-lib.sh
+. ../../git-sh-setup
+
+test_expect_success 'sanity: $TEXTDOMAIN is git' '
+	test $TEXTDOMAIN = "git"
+'
+
+test_expect_success 'sanity: $TEXTDOMAINDIR exists' '
+	test -d $TEXTDOMAINDIR 
+'
+
+test_expect_success 'sanity: Icelandic locale was compiled' '
+    test -f $TEXTDOMAINDIR/is/LC_MESSAGES/git.mo
+'
+
+test_expect_success 'sanity: gettext(unknown) is passed through' '
+    gettext "" > expect &&
+    > actual &&
+    test_cmp expect actual &&
+    printf "This is not a translation string"  > expect
+	gettext "This is not a translation string" > actual &&
+    test_cmp expect actual
+'
+
+test_done
-- 
1.7.1.248.gb7713.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH/RFC v3 7/7] gettext: Add a skeleton po/is.po
  2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
                   ` (19 preceding siblings ...)
  2010-06-01 23:39 ` [PATCH/RFC v3 6/7] gettext: Basic sanity tests for Git's Gettext support Ævar Arnfjörð Bjarmason
@ 2010-06-01 23:39 ` Ævar Arnfjörð Bjarmason
  20 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-01 23:39 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Jeff Epler, Ævar Arnfjörð Bjarmason

This example is.po file translates a little bit of C, Shell and Perl
as an example.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 po/is.po |  282 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 282 insertions(+), 0 deletions(-)
 create mode 100644 po/is.po

diff --git a/po/is.po b/po/is.po
new file mode 100644
index 0000000..aa1923e
--- /dev/null
+++ b/po/is.po
@@ -0,0 +1,282 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Git\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-30 19:24+0000\n"
+"PO-Revision-Date: 2010-06-01 22:15+0000\n"
+"Last-Translator: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n"
+"Language-Team: Git Mailing List <git@vger.kernel.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: English\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+#: wt-status.c:53
+msgid "# Unmerged paths:"
+msgstr ""
+
+#: wt-status.c:59 wt-status.c:76
+#, c-format
+msgid "#   (use \"git reset %s <file>...\" to unstage)"
+msgstr ""
+
+#: wt-status.c:61 wt-status.c:78
+msgid "#   (use \"git rm --cached <file>...\" to unstage)"
+msgstr ""
+
+#: wt-status.c:62
+msgid "#   (use \"git add/rm <file>...\" as appropriate to mark resolution)"
+msgstr ""
+
+#: wt-status.c:63 wt-status.c:79 wt-status.c:98 wt-status.c:110
+#: wt-status.c:115 wt-status.c:638 wt-status.c:640
+msgid "#"
+msgstr ""
+
+#: wt-status.c:70
+msgid "# Changes to be committed:"
+msgstr ""
+
+#: wt-status.c:88
+msgid "# Changed but not updated:"
+msgstr ""
+
+#: wt-status.c:92
+msgid "#   (use \"git add <file>...\" to update what will be committed)"
+msgstr ""
+
+#: wt-status.c:94
+msgid "#   (use \"git add/rm <file>...\" to update what will be committed)"
+msgstr ""
+
+#: wt-status.c:95
+msgid ""
+"#   (use \"git checkout -- <file>...\" to discard changes in working "
+"directory)"
+msgstr ""
+
+#: wt-status.c:97
+msgid "#   (commit or discard the untracked or modified content in submodules)"
+msgstr ""
+
+#: wt-status.c:106
+#, c-format
+msgid "# %s files:"
+msgstr ""
+
+#: wt-status.c:109
+#, c-format
+msgid "#   (use \"git %s <file>...\" to include in what will be committed)"
+msgstr ""
+
+#: wt-status.c:126
+msgid "bug"
+msgstr ""
+
+#: wt-status.c:129 wt-status.c:182
+msgid "#\t"
+msgstr ""
+
+#: wt-status.c:131
+msgid "both deleted:"
+msgstr ""
+
+#: wt-status.c:132
+msgid "added by us:"
+msgstr ""
+
+#: wt-status.c:133
+msgid "deleted by them:"
+msgstr ""
+
+#: wt-status.c:134
+msgid "added by them:"
+msgstr ""
+
+#: wt-status.c:135
+msgid "deleted by us:"
+msgstr ""
+
+#: wt-status.c:136
+msgid "both added:"
+msgstr ""
+
+#: wt-status.c:137
+msgid "both modified:"
+msgstr ""
+
+#: wt-status.c:139
+#, c-format
+msgid "%-20s%s\n"
+msgstr ""
+
+#: wt-status.c:165
+msgid " ("
+msgstr ""
+
+#: wt-status.c:167
+msgid "new commits, "
+msgstr ""
+
+#: wt-status.c:169
+msgid "modified content, "
+msgstr ""
+
+#: wt-status.c:171
+msgid "untracked content, "
+msgstr ""
+
+#: wt-status.c:185
+#, c-format
+msgid "new file:   %s"
+msgstr ""
+
+#: wt-status.c:188
+#, c-format
+msgid "copied:     %s -> %s"
+msgstr ""
+
+#: wt-status.c:191
+#, c-format
+msgid "deleted:    %s"
+msgstr ""
+
+#: wt-status.c:194
+#, c-format
+msgid "modified:   %s"
+msgstr ""
+
+#: wt-status.c:197
+#, c-format
+msgid "renamed:    %s -> %s"
+msgstr ""
+
+#: wt-status.c:200
+#, c-format
+msgid "typechange: %s"
+msgstr ""
+
+#: wt-status.c:203
+#, c-format
+msgid "unknown:    %s"
+msgstr ""
+
+#: wt-status.c:206
+#, c-format
+msgid "unmerged:   %s"
+msgstr ""
+
+#: wt-status.c:209
+#, c-format
+msgid "bug: unhandled diff status %c"
+msgstr ""
+
+#: wt-status.c:212
+#, c-format
+msgid "%s"
+msgstr ""
+
+#: wt-status.c:215
+#, c-format
+msgid "\n"
+msgstr ""
+
+#: wt-status.c:622
+msgid "On branch "
+msgstr "Á greininni "
+
+#: wt-status.c:629
+msgid "Not currently on any branch."
+msgstr "Ekki á neinni grein."
+
+#: wt-status.c:639
+msgid "# Initial commit"
+msgstr ""
+
+#: wt-status.c:651
+msgid "Untracked"
+msgstr ""
+
+#: wt-status.c:651
+msgid "add"
+msgstr ""
+
+#: wt-status.c:653
+msgid "Ignored"
+msgstr ""
+
+#: wt-status.c:653
+msgid "add -f"
+msgstr ""
+
+#: wt-status.c:655
+#, c-format
+msgid "# Untracked files not listed%s\n"
+msgstr ""
+
+#: wt-status.c:657
+msgid " (use -u option to show untracked files)"
+msgstr ""
+
+#: wt-status.c:663
+#, c-format
+msgid "# No changes\n"
+msgstr "# Engar breytingar\n"
+
+#: wt-status.c:667
+#, c-format
+msgid "no changes added to commit%s\n"
+msgstr ""
+
+#: wt-status.c:669
+msgid " (use \"git add\" and/or \"git commit -a\")"
+msgstr ""
+
+#: wt-status.c:671
+#, c-format
+msgid "nothing added to commit but untracked files present%s\n"
+msgstr ""
+
+#: wt-status.c:673
+msgid " (use \"git add\" to track)"
+msgstr ""
+
+#: wt-status.c:676
+msgid " (create/copy files and use \"git add\" to track)"
+msgstr ""
+
+#: wt-status.c:678 wt-status.c:681
+#, c-format
+msgid "nothing to commit%s\n"
+msgstr ""
+
+#: wt-status.c:679
+msgid " (use -u to show untracked files)"
+msgstr ""
+
+#: wt-status.c:682
+msgid " (working directory clean)"
+msgstr ""
+
+#: git-pull.sh:124
+msgid "Fetching tags only, you probably meant:"
+msgstr "Næ aðeins í tögg, þú áttir líkast til við:"
+
+#: git-pull.sh:125
+msgid "  git fetch --tags"
+msgstr ""
+
+#: git-pull.sh:157
+msgid ""
+"You are not currently on a branch, so I cannot use any\n"
+"'branch.<branchname>.merge' in your configuration file.\n"
+"Please specify which remote branch you want to use on the command\n"
+"line and try again (e.g. 'git pull <repository> <refspec>').\n"
+"See git-pull(1) for details."
+msgstr ""
+
+#: git-send-email.perl:678
+#, perl-format
+msgid "Emails will be sent from: %s\n"
+msgstr "Póstarnir verða sendir frá: %s\n"
-- 
1.7.1.248.gb7713.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC v3 0/7] Add internationalization support to Git
  2010-06-01 23:39 ` [PATCH/RFC v3 0/7] Add internationalization support to Git Ævar Arnfjörð Bjarmason
@ 2010-06-02  0:11   ` Ævar Arnfjörð Bjarmason
  2010-06-02  1:05     ` [PATCH/RFC v4 " Ævar Arnfjörð Bjarmason
                       ` (11 more replies)
  0 siblings, 12 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-02  0:11 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Jeff Epler, Ævar Arnfjörð Bjarmason

On Tue, Jun 1, 2010 at 23:39, Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:
> Here's version 3 of the patch series begun by Jeff Epler to make Git
> optionally translatable with GNU gettext.
>
> This version contains a lot of fixes to issues pointe out by Jakub
> Narebski, and other misc.
> [...]
>  * The GIT_TEXTDOMAINDIR environment variable can be set to override
>    where the C/Perl/Shell locale files are found. This is intended to
>    be set by the test suite.

I made a silly error in my rebases and rewrote this commit out of my
tree. Instead of sending new commits here's the `git diff -w` below:

    diff --git a/git-sh-setup.sh b/git-sh-setup.sh
    index d67901c..379c9a1 100644
    --- a/git-sh-setup.sh
    +++ b/git-sh-setup.sh
    @@ -219,7 +219,12 @@ if test $? -eq 0
     then
         TEXTDOMAIN=git
         export TEXDTOMAIN
    +	if [ -z "$GIT_TEXTDOMAINDIR" ]
    +	then
         TEXTDOMAINDIR="@@LOCALEDIR@@"
    +	else
    +		TEXTDOMAINDIR=$GIT_TEXTDOMAINDIR
    +	fi
         export TEXTDOMAINDIR
     else
         # Since GNU gettext.sh isn't available we'll have to define our
    diff --git a/perl/Git/I18N.pm b/perl/Git/I18N.pm
    index 568fd3d..e19d99b 100644
    --- a/perl/Git/I18N.pm
    +++ b/perl/Git/I18N.pm
    @@ -11,7 +11,7 @@ our @EXPORT_OK = @EXPORT;

     sub __bootstrap_locale_messages {
     	our $TEXTDOMAIN = 'git';
    -	our $TEXTDOMAINDIR = '++LOCALEDIR++';
    +	our $TEXTDOMAINDIR = $ENV{GIT_TEXTDOMAINDIR} || '++LOCALEDIR++';

     	require POSIX;
     	POSIX->import(qw(setlocale));
    diff --git a/t/t0200-gettext.sh b/t/t0200-gettext.sh
    index a8f9edd..7c2a4cb 100755
    --- a/t/t0200-gettext.sh
    +++ b/t/t0200-gettext.sh
    @@ -1,5 +1,7 @@
     #!/bin/sh

    +export GIT_TEXTDOMAINDIR=`pwd`/../share/locale
    +
     test_description='Gettext support for Git'
     . ./test-lib.sh
     . ../../git-sh-setup
    @@ -9,7 +11,8 @@ test_expect_success 'sanity: $TEXTDOMAIN is git' '
     '

     test_expect_success 'sanity: $TEXTDOMAINDIR exists' '
    -	test -d $TEXTDOMAINDIR
    +	test -d $TEXTDOMAINDIR &&
    +	test $TEXTDOMAINDIR = $GIT_TEXTDOMAINDIR
     '

     test_expect_success 'sanity: Icelandic locale was compiled' '

Along with another silly error:

    diff --git a/git-sh-setup.sh b/git-sh-setup.sh
    index 379c9a1..da8c47f 100644
    --- a/git-sh-setup.sh
    +++ b/git-sh-setup.sh
    @@ -218,7 +218,7 @@ esac
     if test $? -eq 0
     then
         TEXTDOMAIN=git
    -       export TEXDTOMAIN
    +       export TEXTDOMAIN
            if [ -z "$GIT_TEXTDOMAINDIR" ]
            then
                    TEXTDOMAINDIR="@@LOCALEDIR@@"


These fixes will be included (along with any other fixes) in my next
series. Which'll be submitted after soliciting more RFC
comments. Sorry everyone.

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH/RFC v4 0/7] Add internationalization support to Git
  2010-06-02  0:11   ` Ævar Arnfjörð Bjarmason
@ 2010-06-02  1:05     ` Ævar Arnfjörð Bjarmason
  2010-06-02  1:05     ` [PATCH/RFC v4 1/7] Add infrastructure for translating Git with gettext Ævar Arnfjörð Bjarmason
                       ` (10 subsequent siblings)
  11 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-02  1:05 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Jeff Epler, Ævar Arnfjörð Bjarmason

Here's version 4 of the patch series begun by Jeff Epler to make Git
optionally translatable with GNU gettext.

It's like the version 4 I submitted an ~hour ago, sans the annoying
errors I made and lots of tests:

Changes from v3 to v4:

  * Fix typos and removal of GIT_TEXTDOMAINDIR that I rewrote out with
    some rebase mistakes.

  * Add meaningful testing. The C, Shell and Perl wrappers are now
    tested as part of a new t/t0200-gettext.sh test file. Put under
    the t/t0* namespace because it's a core feature, and using a new
    t02 prefix because there'll probably be a lot of Gettext tests
    down the line.

Changes from v2 to v3:

  * Squashed some infrastructure patches into the initial commit.

  * Added basic sanity testing for the Gettext support

  * .mo files aren't installed on `make install` time if NO_GETTEXT
    was specified during the build.

    In C gettext support isn't compiled in. But this happens
    implicitly in Shell/Perl because the locale files won't be there
    on the system.

  * NO_GETTEXT is now set correctly depending on whether libintl.h
    exists when autoconf is used to build Git. Whether this is the
    best approach to gettext detection with autoconf is being
    discussed in the "[PATCH/RCF] autoconf: Check if <libintl.h>
    exists and set NO_GETTEX" thread.

  * The localedir is now passed to the Perl code via Makefile
    trickery. It's no longer hardcoded to /usr/local/share/locale

  * Add `--keyword=` to all xgettext invocations. This way we make
    sure that only our gettext functions are extracted, not the
    defaults xgettext.

  * Git::Gettext is now called Git::I18N

  * Remove LICENSE from I18N.pm. It was a boilerplate that shouldn't
    have been there.

  * Use qw() not qw< > in Perl code.

  * Changed the interface to Git::I18N. Now it exports all its
    functions by default. If something's using the module it's going
    to want to import everything anyway. This makes the issue of '@{
    $EXPORT_TAGS{'all'} }' being ugly syntax moot since it isn't used
    anymore.

    Users can still import selectively by specifying arguments to
    import().

  * The GIT_TEXTDOMAINDIR environment variable can be set to override
    where the C/Perl/Shell locale files are found. This is intended to
    be set by the test suite.

  * The boolean test for whether gettext.sh was successfully loaded
    was inverted (oops!)

Things that haven't changed:

  Jakub pointed out some things that I might want to change that I
  didn't yet. Mostly because I didn't have time yet.

  * Git::I18N still uses Locale::Messages, not Locale::TextDomain.

    I'm still not convinced that the low-level Locale::Messages
    interface isn't a better fit for us. But I didn't get
    Locale::TextDomain working at all today so I'm still using
    Locale::Messages.

  * Perl still uses gettext($), not __($) (see above).

  * use Exporter 'import': Subclassing Exporter instead since we
    apparently need Perl 5.6 compatibility.

Jeff Epler (1):
  Add infrastructure for translating Git with gettext

Ævar Arnfjörð Bjarmason (6):
  gettext: Add a Gettext interface for shell scripts
  gettext: Add a Gettext interface for Perl
  Makefile: Don't install Gettext .mo files if NO_GETTEXT
  Makefile: Override --keyword= for all languages
  gettext: Sanity tests for Git's Gettext support
  gettext: Add a skeleton po/is.po

 .gitignore          |    1 +
 Makefile            |   43 ++++++++-
 config.mak.in       |    1 +
 configure.ac        |    6 +
 gettext.c           |   25 +++++
 gettext.h           |   13 +++
 git-pull.sh         |   15 ++--
 git-send-email.perl |    3 +-
 git-sh-setup.sh     |   38 +++++++
 git.c               |    3 +
 perl/Git/I18N.pm    |   73 +++++++++++++
 perl/Makefile       |    3 +-
 perl/Makefile.PL    |   14 +++-
 po/.gitignore       |    1 +
 po/is.po            |  282 +++++++++++++++++++++++++++++++++++++++++++++++++++
 t/t0200-gettext.sh  |   73 +++++++++++++
 wt-status.c         |  129 ++++++++++++------------
 17 files changed, 647 insertions(+), 76 deletions(-)
 create mode 100644 gettext.c
 create mode 100644 gettext.h
 create mode 100644 perl/Git/I18N.pm
 create mode 100644 po/.gitignore
 create mode 100644 po/is.po
 create mode 100755 t/t0200-gettext.sh

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH/RFC v4 1/7] Add infrastructure for translating Git with gettext
  2010-06-02  0:11   ` Ævar Arnfjörð Bjarmason
  2010-06-02  1:05     ` [PATCH/RFC v4 " Ævar Arnfjörð Bjarmason
@ 2010-06-02  1:05     ` Ævar Arnfjörð Bjarmason
  2010-06-02  9:12       ` Peter Krefting
  2010-06-02  1:05     ` [PATCH/RFC v4 2/7] gettext: Add a Gettext interface for shell scripts Ævar Arnfjörð Bjarmason
                       ` (9 subsequent siblings)
  11 siblings, 1 reply; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-02  1:05 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Jeff Epler, Ævar Arnfjörð Bjarmason

From: Jeff Epler <jepler@unpythonic.net>

Change the build process to use GNU's libintl if it's available. If
not we define our own skeleton replacement functions which degrade
gracefully to English.

Gettext .mo files will be installed and looked for in
$(prefix)/share/locale. GIT_LOCALEDIR can also be set to override
that, this variable is only intended for testing Git itself.

Signed-off-by: Jeff Epler <jepler@unpythonic.net>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 .gitignore    |    1 +
 Makefile      |   30 +++++++++++++
 config.mak.in |    1 +
 configure.ac  |    6 +++
 gettext.c     |   25 +++++++++++
 gettext.h     |   13 ++++++
 git.c         |    3 +
 po/.gitignore |    1 +
 wt-status.c   |  129 +++++++++++++++++++++++++++++----------------------------
 9 files changed, 145 insertions(+), 64 deletions(-)
 create mode 100644 gettext.c
 create mode 100644 gettext.h
 create mode 100644 po/.gitignore

diff --git a/.gitignore b/.gitignore
index 14e2b6b..d22c0e5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -204,3 +204,4 @@
 *.pdb
 /Debug/
 /Release/
+/share/
diff --git a/Makefile b/Makefile
index d5d6565..4de0627 100644
--- a/Makefile
+++ b/Makefile
@@ -297,6 +297,8 @@ RPMBUILD = rpmbuild
 TCL_PATH = tclsh
 TCLTK_PATH = wish
 PTHREAD_LIBS = -lpthread
+XGETTEXT = xgettext
+MSGFMT = msgfmt
 
 export TCL_PATH TCLTK_PATH
 
@@ -523,6 +525,7 @@ LIB_H += userdiff.h
 LIB_H += utf8.h
 LIB_H += xdiff-interface.h
 LIB_H += xdiff/xdiff.h
+LIB_H += gettext.h
 
 LIB_OBJS += abspath.o
 LIB_OBJS += advice.o
@@ -564,6 +567,7 @@ LIB_OBJS += entry.o
 LIB_OBJS += environment.o
 LIB_OBJS += exec_cmd.o
 LIB_OBJS += fsck.o
+LIB_OBJS += gettext.o
 LIB_OBJS += graph.o
 LIB_OBJS += grep.o
 LIB_OBJS += hash.o
@@ -1386,6 +1390,12 @@ ifdef USE_NED_ALLOCATOR
        COMPAT_OBJS += compat/nedmalloc/nedmalloc.o
 endif
 
+ifdef NO_GETTEXT
+	COMPAT_CFLAGS += -DNO_GETTEXT
+else
+	LIBINTL = -lintl
+endif
+
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK=NoThanks
 endif
@@ -1415,6 +1425,7 @@ ifndef V
 	QUIET_BUILT_IN = @echo '   ' BUILTIN $@;
 	QUIET_GEN      = @echo '   ' GEN $@;
 	QUIET_LNCP     = @echo '   ' LN/CP $@;
+	QUIET_MSGFMT   = @echo '   ' MSGFMT $@;
 	QUIET_SUBDIR0  = +@subdir=
 	QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
 			 $(MAKE) $(PRINT_DIR) -C $$subdir
@@ -1442,6 +1453,7 @@ gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
 template_dir_SQ = $(subst ','\'',$(template_dir))
 htmldir_SQ = $(subst ','\'',$(htmldir))
 prefix_SQ = $(subst ','\'',$(prefix))
+sharedir_SQ = $(subst ','\'',$(sharedir))
 
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
@@ -1868,6 +1880,17 @@ cscope:
 	$(RM) cscope*
 	$(FIND) . -name '*.[hcS]' -print | xargs cscope -b
 
+pot:
+	$(XGETTEXT) -k_ -o po/git.pot $(C_OBJ:o=c)
+
+POFILES := $(wildcard po/*.po)
+MOFILES := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/git.mo,$(POFILES))
+MODIRS := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/,$(POFILES))
+all:: $(MOFILES)
+share/locale/%/LC_MESSAGES/git.mo: po/%.po
+	@mkdir -p $(dir $@)
+	$(QUIET_MSGFMT)$(MSGFMT) -o $@ $<
+
 ### Detect prefix changes
 TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
              $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
@@ -1980,6 +2003,9 @@ install: all
 	$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 	$(INSTALL) -m 644 $(SCRIPT_LIB) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 	$(INSTALL) $(install_bindir_programs) '$(DESTDIR_SQ)$(bindir_SQ)'
+	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sharedir_SQ)/locale'
+	(cd share && tar cf - locale) | \
+		(cd '$(DESTDIR_SQ)$(sharedir_SQ)' && umask 022 && tar xof -)
 	$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
 ifndef NO_PERL
 	$(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
@@ -2127,6 +2153,10 @@ ifndef NO_TCLTK
 	$(MAKE) -C git-gui clean
 endif
 	$(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS
+ifndef NO_GETTEXT
+	$(RM) po/git.pot
+	$(RM) -r share/
+endif
 
 .PHONY: all install clean strip
 .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
diff --git a/config.mak.in b/config.mak.in
index 0d4b64d..a15f3c1 100644
--- a/config.mak.in
+++ b/config.mak.in
@@ -32,6 +32,7 @@ NO_CURL=@NO_CURL@
 NO_EXPAT=@NO_EXPAT@
 NO_LIBGEN_H=@NO_LIBGEN_H@
 HAVE_PATHS_H=@HAVE_PATHS_H@
+NO_GETTEXT=@NO_GETTEXT@
 NEEDS_LIBICONV=@NEEDS_LIBICONV@
 NEEDS_SOCKET=@NEEDS_SOCKET@
 NEEDS_RESOLV=@NEEDS_RESOLV@
diff --git a/configure.ac b/configure.ac
index 71038fc..7bebfd8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -730,6 +730,12 @@ AC_CHECK_HEADER([paths.h],
 [HAVE_PATHS_H=])
 AC_SUBST(HAVE_PATHS_H)
 #
+# Define NO_GETTEXT if you don't have libintl.h
+AC_CHECK_HEADER([libintl.h],
+[NO_GETTEXT=],
+[NO_GETTEXT=YesPlease])
+AC_SUBST(NO_GETTEXT)
+#
 # Define NO_STRCASESTR if you don't have strcasestr.
 GIT_CHECK_FUNC(strcasestr,
 [NO_STRCASESTR=],
diff --git a/gettext.c b/gettext.c
new file mode 100644
index 0000000..22cdcc1
--- /dev/null
+++ b/gettext.c
@@ -0,0 +1,25 @@
+#ifdef NO_GETTEXT
+void git_setup_gettext(void) {}
+#else
+#include "exec_cmd.h"
+#include <libintl.h>
+#include <stdlib.h>
+
+void git_setup_gettext(void) {
+	char *podir;
+	char *envdir = getenv("GIT_TEXTDOMAINDIR");
+
+	if (envdir) {
+		(void)bindtextdomain("git", envdir);
+	} else {
+		podir = (char *)system_path("share/locale");
+		if (!podir) return;
+		(void)bindtextdomain("git", podir);
+		free(podir);
+	}
+
+	(void)setlocale(LC_MESSAGES, "");
+	(void)setlocale(LC_CTYPE, "");
+	(void)textdomain("git");
+}
+#endif
diff --git a/gettext.h b/gettext.h
new file mode 100644
index 0000000..a99da6a
--- /dev/null
+++ b/gettext.h
@@ -0,0 +1,13 @@
+#ifndef GETTEXT_H
+#define GETTEXT_H
+
+void git_setup_gettext(void);
+
+#ifdef NO_GETTEXT
+#define _(s) (s)
+#else
+#include <libintl.h>
+#define _(s) gettext(s)
+#endif
+
+#endif
diff --git a/git.c b/git.c
index 99f0363..d749eab 100644
--- a/git.c
+++ b/git.c
@@ -3,6 +3,7 @@
 #include "cache.h"
 #include "quote.h"
 #include "run-command.h"
+#include "gettext.h"
 
 const char git_usage_string[] =
 	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n"
@@ -490,6 +491,8 @@ int main(int argc, const char **argv)
 	if (!cmd)
 		cmd = "git-help";
 
+	git_setup_gettext();
+
 	/*
 	 * "git-xxxx" is the same as "git xxxx", but we obviously:
 	 *
diff --git a/po/.gitignore b/po/.gitignore
new file mode 100644
index 0000000..221000e
--- /dev/null
+++ b/po/.gitignore
@@ -0,0 +1 @@
+/*.pot
diff --git a/wt-status.c b/wt-status.c
index 14e0acc..70b4293 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -9,6 +9,7 @@
 #include "quote.h"
 #include "run-command.h"
 #include "remote.h"
+#include "gettext.h"
 
 static char default_wt_status_colors[][COLOR_MAXLEN] = {
 	GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
@@ -49,33 +50,33 @@ static void wt_status_print_unmerged_header(struct wt_status *s)
 {
 	const char *c = color(WT_STATUS_HEADER, s);
 
-	color_fprintf_ln(s->fp, c, "# Unmerged paths:");
+	color_fprintf_ln(s->fp, c, _("# Unmerged paths:"));
 	if (!advice_status_hints)
 		return;
 	if (s->in_merge)
 		;
 	else if (!s->is_initial)
-		color_fprintf_ln(s->fp, c, "#   (use \"git reset %s <file>...\" to unstage)", s->reference);
+		color_fprintf_ln(s->fp, c, _("#   (use \"git reset %s <file>...\" to unstage)"), s->reference);
 	else
-		color_fprintf_ln(s->fp, c, "#   (use \"git rm --cached <file>...\" to unstage)");
-	color_fprintf_ln(s->fp, c, "#   (use \"git add/rm <file>...\" as appropriate to mark resolution)");
-	color_fprintf_ln(s->fp, c, "#");
+		color_fprintf_ln(s->fp, c, _("#   (use \"git rm --cached <file>...\" to unstage)"));
+	color_fprintf_ln(s->fp, c, _("#   (use \"git add/rm <file>...\" as appropriate to mark resolution)"));
+	color_fprintf_ln(s->fp, c, _("#"));
 }
 
 static void wt_status_print_cached_header(struct wt_status *s)
 {
 	const char *c = color(WT_STATUS_HEADER, s);
 
-	color_fprintf_ln(s->fp, c, "# Changes to be committed:");
+	color_fprintf_ln(s->fp, c, _("# Changes to be committed:"));
 	if (!advice_status_hints)
 		return;
 	if (s->in_merge)
 		; /* NEEDSWORK: use "git reset --unresolve"??? */
 	else if (!s->is_initial)
-		color_fprintf_ln(s->fp, c, "#   (use \"git reset %s <file>...\" to unstage)", s->reference);
+		color_fprintf_ln(s->fp, c, _("#   (use \"git reset %s <file>...\" to unstage)"), s->reference);
 	else
-		color_fprintf_ln(s->fp, c, "#   (use \"git rm --cached <file>...\" to unstage)");
-	color_fprintf_ln(s->fp, c, "#");
+		color_fprintf_ln(s->fp, c, _("#   (use \"git rm --cached <file>...\" to unstage)"));
+	color_fprintf_ln(s->fp, c, _("#"));
 }
 
 static void wt_status_print_dirty_header(struct wt_status *s,
@@ -84,17 +85,17 @@ static void wt_status_print_dirty_header(struct wt_status *s,
 {
 	const char *c = color(WT_STATUS_HEADER, s);
 
-	color_fprintf_ln(s->fp, c, "# Changed but not updated:");
+	color_fprintf_ln(s->fp, c, _("# Changed but not updated:"));
 	if (!advice_status_hints)
 		return;
 	if (!has_deleted)
-		color_fprintf_ln(s->fp, c, "#   (use \"git add <file>...\" to update what will be committed)");
+		color_fprintf_ln(s->fp, c, _("#   (use \"git add <file>...\" to update what will be committed)"));
 	else
-		color_fprintf_ln(s->fp, c, "#   (use \"git add/rm <file>...\" to update what will be committed)");
-	color_fprintf_ln(s->fp, c, "#   (use \"git checkout -- <file>...\" to discard changes in working directory)");
+		color_fprintf_ln(s->fp, c, _("#   (use \"git add/rm <file>...\" to update what will be committed)"));
+	color_fprintf_ln(s->fp, c, _("#   (use \"git checkout -- <file>...\" to discard changes in working directory)"));
 	if (has_dirty_submodules)
-		color_fprintf_ln(s->fp, c, "#   (commit or discard the untracked or modified content in submodules)");
-	color_fprintf_ln(s->fp, c, "#");
+		color_fprintf_ln(s->fp, c, _("#   (commit or discard the untracked or modified content in submodules)"));
+	color_fprintf_ln(s->fp, c, _("#"));
 }
 
 static void wt_status_print_other_header(struct wt_status *s,
@@ -102,16 +103,16 @@ static void wt_status_print_other_header(struct wt_status *s,
 					 const char *how)
 {
 	const char *c = color(WT_STATUS_HEADER, s);
-	color_fprintf_ln(s->fp, c, "# %s files:", what);
+	color_fprintf_ln(s->fp, c, _("# %s files:"), what);
 	if (!advice_status_hints)
 		return;
-	color_fprintf_ln(s->fp, c, "#   (use \"git %s <file>...\" to include in what will be committed)", how);
-	color_fprintf_ln(s->fp, c, "#");
+	color_fprintf_ln(s->fp, c, _("#   (use \"git %s <file>...\" to include in what will be committed)"), how);
+	color_fprintf_ln(s->fp, c, _("#"));
 }
 
 static void wt_status_print_trailer(struct wt_status *s)
 {
-	color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
+	color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("#"));
 }
 
 #define quote_path quote_path_relative
@@ -122,20 +123,20 @@ static void wt_status_print_unmerged_data(struct wt_status *s,
 	const char *c = color(WT_STATUS_UNMERGED, s);
 	struct wt_status_change_data *d = it->util;
 	struct strbuf onebuf = STRBUF_INIT;
-	const char *one, *how = "bug";
+	const char *one, *how = _("bug");
 
 	one = quote_path(it->string, -1, &onebuf, s->prefix);
-	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
+	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), _("#\t"));
 	switch (d->stagemask) {
-	case 1: how = "both deleted:"; break;
-	case 2: how = "added by us:"; break;
-	case 3: how = "deleted by them:"; break;
-	case 4: how = "added by them:"; break;
-	case 5: how = "deleted by us:"; break;
-	case 6: how = "both added:"; break;
-	case 7: how = "both modified:"; break;
+	case 1: how = _("both deleted:"); break;
+	case 2: how = _("added by us:"); break;
+	case 3: how = _("deleted by them:"); break;
+	case 4: how = _("added by them:"); break;
+	case 5: how = _("deleted by us:"); break;
+	case 6: how = _("both added:"); break;
+	case 7: how = _("both modified:"); break;
 	}
-	color_fprintf(s->fp, c, "%-20s%s\n", how, one);
+	color_fprintf(s->fp, c, _("%-20s%s\n"), how, one);
 	strbuf_release(&onebuf);
 }
 
@@ -161,13 +162,13 @@ static void wt_status_print_change_data(struct wt_status *s,
 		break;
 	case WT_STATUS_CHANGED:
 		if (d->new_submodule_commits || d->dirty_submodule) {
-			strbuf_addstr(&extra, " (");
+			strbuf_addstr(&extra, _(" ("));
 			if (d->new_submodule_commits)
-				strbuf_addf(&extra, "new commits, ");
+				strbuf_addf(&extra, _("new commits, "));
 			if (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
-				strbuf_addf(&extra, "modified content, ");
+				strbuf_addf(&extra, _("modified content, "));
 			if (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
-				strbuf_addf(&extra, "untracked content, ");
+				strbuf_addf(&extra, _("untracked content, "));
 			strbuf_setlen(&extra, extra.len - 2);
 			strbuf_addch(&extra, ')');
 		}
@@ -178,40 +179,40 @@ static void wt_status_print_change_data(struct wt_status *s,
 	one = quote_path(one_name, -1, &onebuf, s->prefix);
 	two = quote_path(two_name, -1, &twobuf, s->prefix);
 
-	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
+	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), _("#\t"));
 	switch (status) {
 	case DIFF_STATUS_ADDED:
-		color_fprintf(s->fp, c, "new file:   %s", one);
+		color_fprintf(s->fp, c, _("new file:   %s"), one);
 		break;
 	case DIFF_STATUS_COPIED:
-		color_fprintf(s->fp, c, "copied:     %s -> %s", one, two);
+		color_fprintf(s->fp, c, _("copied:     %s -> %s"), one, two);
 		break;
 	case DIFF_STATUS_DELETED:
-		color_fprintf(s->fp, c, "deleted:    %s", one);
+		color_fprintf(s->fp, c, _("deleted:    %s"), one);
 		break;
 	case DIFF_STATUS_MODIFIED:
-		color_fprintf(s->fp, c, "modified:   %s", one);
+		color_fprintf(s->fp, c, _("modified:   %s"), one);
 		break;
 	case DIFF_STATUS_RENAMED:
-		color_fprintf(s->fp, c, "renamed:    %s -> %s", one, two);
+		color_fprintf(s->fp, c, _("renamed:    %s -> %s"), one, two);
 		break;
 	case DIFF_STATUS_TYPE_CHANGED:
-		color_fprintf(s->fp, c, "typechange: %s", one);
+		color_fprintf(s->fp, c, _("typechange: %s"), one);
 		break;
 	case DIFF_STATUS_UNKNOWN:
-		color_fprintf(s->fp, c, "unknown:    %s", one);
+		color_fprintf(s->fp, c, _("unknown:    %s"), one);
 		break;
 	case DIFF_STATUS_UNMERGED:
-		color_fprintf(s->fp, c, "unmerged:   %s", one);
+		color_fprintf(s->fp, c, _("unmerged:   %s"), one);
 		break;
 	default:
-		die("bug: unhandled diff status %c", status);
+		die(_("bug: unhandled diff status %c"), status);
 	}
 	if (extra.len) {
-		color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "%s", extra.buf);
+		color_fprintf(s->fp, color(WT_STATUS_HEADER, s), _("%s"), extra.buf);
 		strbuf_release(&extra);
 	}
-	fprintf(s->fp, "\n");
+	fprintf(s->fp, _("\n"));
 	strbuf_release(&onebuf);
 	strbuf_release(&twobuf);
 }
@@ -618,14 +619,14 @@ void wt_status_print(struct wt_status *s)
 	const char *branch_color = color(WT_STATUS_HEADER, s);
 
 	if (s->branch) {
-		const char *on_what = "On branch ";
+		const char *on_what = _("On branch ");
 		const char *branch_name = s->branch;
 		if (!prefixcmp(branch_name, "refs/heads/"))
 			branch_name += 11;
 		else if (!strcmp(branch_name, "HEAD")) {
 			branch_name = "";
 			branch_color = color(WT_STATUS_NOBRANCH, s);
-			on_what = "Not currently on any branch.";
+			on_what = _("Not currently on any branch.");
 		}
 		color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "# ");
 		color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name);
@@ -634,9 +635,9 @@ void wt_status_print(struct wt_status *s)
 	}
 
 	if (s->is_initial) {
-		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
-		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "# Initial commit");
-		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
+		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("#"));
+		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("# Initial commit"));
+		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("#"));
 	}
 
 	wt_status_print_updated(s);
@@ -647,38 +648,38 @@ void wt_status_print(struct wt_status *s)
 		wt_status_print_submodule_summary(s, 1);  /* unstaged */
 	}
 	if (s->show_untracked_files) {
-		wt_status_print_other(s, &s->untracked, "Untracked", "add");
+		wt_status_print_other(s, &s->untracked, _("Untracked"), _("add"));
 		if (s->show_ignored_files)
-			wt_status_print_other(s, &s->ignored, "Ignored", "add -f");
+			wt_status_print_other(s, &s->ignored, _("Ignored"), _("add -f"));
 	} else if (s->commitable)
-		fprintf(s->fp, "# Untracked files not listed%s\n",
+		fprintf(s->fp, _("# Untracked files not listed%s\n"),
 			advice_status_hints
-			? " (use -u option to show untracked files)" : "");
+			? _(" (use -u option to show untracked files)") : "");
 
 	if (s->verbose)
 		wt_status_print_verbose(s);
 	if (!s->commitable) {
 		if (s->amend)
-			fprintf(s->fp, "# No changes\n");
+			fprintf(s->fp, _("# No changes\n"));
 		else if (s->nowarn)
 			; /* nothing */
 		else if (s->workdir_dirty)
-			printf("no changes added to commit%s\n",
+			printf(_("no changes added to commit%s\n"),
 				advice_status_hints
-				? " (use \"git add\" and/or \"git commit -a\")" : "");
+				? _(" (use \"git add\" and/or \"git commit -a\")") : "");
 		else if (s->untracked.nr)
-			printf("nothing added to commit but untracked files present%s\n",
+			printf(_("nothing added to commit but untracked files present%s\n"),
 				advice_status_hints
-				? " (use \"git add\" to track)" : "");
+				? _(" (use \"git add\" to track)") : "");
 		else if (s->is_initial)
 			printf("nothing to commit%s\n", advice_status_hints
-				? " (create/copy files and use \"git add\" to track)" : "");
+				? _(" (create/copy files and use \"git add\" to track)") : "");
 		else if (!s->show_untracked_files)
-			printf("nothing to commit%s\n", advice_status_hints
-				? " (use -u to show untracked files)" : "");
+			printf(_("nothing to commit%s\n"), advice_status_hints
+				? _(" (use -u to show untracked files)") : "");
 		else
-			printf("nothing to commit%s\n", advice_status_hints
-				? " (working directory clean)" : "");
+			printf(_("nothing to commit%s\n"), advice_status_hints
+				? _(" (working directory clean)") : "");
 	}
 }
 
-- 
1.7.1.251.gec7f5.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH/RFC v4 2/7] gettext: Add a Gettext interface for shell scripts
  2010-06-02  0:11   ` Ævar Arnfjörð Bjarmason
  2010-06-02  1:05     ` [PATCH/RFC v4 " Ævar Arnfjörð Bjarmason
  2010-06-02  1:05     ` [PATCH/RFC v4 1/7] Add infrastructure for translating Git with gettext Ævar Arnfjörð Bjarmason
@ 2010-06-02  1:05     ` Ævar Arnfjörð Bjarmason
  2010-06-02  1:06     ` [PATCH/RFC v4 3/7] gettext: Add a Gettext interface for Perl Ævar Arnfjörð Bjarmason
                       ` (8 subsequent siblings)
  11 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-02  1:05 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Jeff Epler, Ævar Arnfjörð Bjarmason

Use GNU's gettext.sh in git-sh-setup if it's available, otherwise
fallback on our own custom functions.

A couple of strings in git-pull.sh are now translatable as a proof of
concept, including a multiline string.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Makefile        |    7 +++++--
 git-pull.sh     |   15 ++++++++-------
 git-sh-setup.sh |   38 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 51 insertions(+), 9 deletions(-)

diff --git a/Makefile b/Makefile
index 4de0627..dce2faa 100644
--- a/Makefile
+++ b/Makefile
@@ -272,6 +272,7 @@ mandir = share/man
 infodir = share/info
 gitexecdir = libexec/git-core
 sharedir = $(prefix)/share
+localedir = $(sharedir)/locale
 template_dir = share/git-core/templates
 htmldir = share/doc/git-doc
 ifeq ($(prefix),/usr)
@@ -285,7 +286,7 @@ lib = lib
 # DESTDIR=
 pathsep = :
 
-export prefix bindir sharedir sysconfdir
+export prefix bindir sharedir localedir sysconfdir
 
 CC = gcc
 AR = ar
@@ -1455,6 +1456,7 @@ htmldir_SQ = $(subst ','\'',$(htmldir))
 prefix_SQ = $(subst ','\'',$(prefix))
 sharedir_SQ = $(subst ','\'',$(sharedir))
 
+LOCALEDIR_SQ = $(subst ','\'',$(localedir))
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
@@ -1548,6 +1550,7 @@ $(RM) $@ $@+ && \
 sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
     -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
     -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+    -e 's|@@LOCALEDIR@@|$(LOCALEDIR_SQ)|g' \
     -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
     -e $(BROKEN_PATH_FIX) \
     $@.sh >$@+
@@ -1881,7 +1884,7 @@ cscope:
 	$(FIND) . -name '*.[hcS]' -print | xargs cscope -b
 
 pot:
-	$(XGETTEXT) -k_ -o po/git.pot $(C_OBJ:o=c)
+	$(XGETTEXT) -k_ -o po/git.pot $(C_OBJ:o=c) $(SCRIPT_SH)
 
 POFILES := $(wildcard po/*.po)
 MOFILES := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/git.mo,$(POFILES))
diff --git a/git-pull.sh b/git-pull.sh
index 1a4729f..22a6da2 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -121,8 +121,8 @@ error_on_no_merge_candidates () {
 	do
 		case "$opt" in
 		-t|--t|--ta|--tag|--tags)
-			echo "Fetching tags only, you probably meant:"
-			echo "  git fetch --tags"
+			gettext "Fetching tags only, you probably meant:"; echo
+			gettext "  git fetch --tags"; echo
 			exit 1
 		esac
 	done
@@ -154,11 +154,12 @@ error_on_no_merge_candidates () {
 		echo "a branch. Because this is not the default configured remote"
 		echo "for your current branch, you must specify a branch on the command line."
 	elif [ -z "$curr_branch" ]; then
-		echo "You are not currently on a branch, so I cannot use any"
-		echo "'branch.<branchname>.merge' in your configuration file."
-		echo "Please specify which remote branch you want to use on the command"
-		echo "line and try again (e.g. 'git pull <repository> <refspec>')."
-		echo "See git-pull(1) for details."
+        gettext "You are not currently on a branch, so I cannot use any
+'branch.<branchname>.merge' in your configuration file.
+Please specify which remote branch you want to use on the command
+line and try again (e.g. 'git pull <repository> <refspec>').
+See git-pull(1) for details.";
+        echo
 	elif [ -z "$upstream" ]; then
 		echo "You asked me to pull without telling me which branch you"
 		echo "want to $op_type $op_prep, and 'branch.${curr_branch}.merge' in"
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index 6131670..53cfc42 100644
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -211,3 +211,41 @@ case $(uname -s) in
 	}
 	;;
 esac
+
+# Try to use libintl's gettext.sh, or fall back to English if we
+# can't.
+. gettext.sh
+if test $? -eq 0
+then
+	TEXTDOMAIN=git
+	export TEXTDOMAIN
+	if [ -z "$GIT_TEXTDOMAINDIR" ]
+	then
+		TEXTDOMAINDIR="@@LOCALEDIR@@"
+	else
+		TEXTDOMAINDIR=$GIT_TEXTDOMAINDIR
+	fi
+	export TEXTDOMAINDIR
+else
+	# Since GNU gettext.sh isn't available we'll have to define our
+	# own dummy functions.
+
+	# This code adapted from NessusClient-1.0.2's nessusclient-mkcert
+	# by Michel Arboi <arboi@alussinan.org>. The original code is
+	# under the GPLv2.
+
+	# Not everyone has echo -n
+	case $(echo -n) in
+		\-n)	Xn=	  ; Xc='\c' ;;
+		*)		Xn=-n ; Xc=
+	esac
+
+	gettext () {
+		echo $Xn "$1" $Xc
+	}
+
+	eval_gettext () {
+		eval_gettext_var="echo $1"
+		echo $Xn `eval $eval_gettext_var` $Xc
+	}
+fi
-- 
1.7.1.251.gec7f5.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH/RFC v4 3/7] gettext: Add a Gettext interface for Perl
  2010-06-02  0:11   ` Ævar Arnfjörð Bjarmason
                       ` (2 preceding siblings ...)
  2010-06-02  1:05     ` [PATCH/RFC v4 2/7] gettext: Add a Gettext interface for shell scripts Ævar Arnfjörð Bjarmason
@ 2010-06-02  1:06     ` Ævar Arnfjörð Bjarmason
  2010-06-02  1:06     ` [PATCH/RFC v4 4/7] Makefile: Don't install Gettext .mo files if NO_GETTEXT Ævar Arnfjörð Bjarmason
                       ` (7 subsequent siblings)
  11 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-02  1:06 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Jeff Epler, Ævar Arnfjörð Bjarmason

Make Git's gettext messages available to Perl programs through
Locale::Messages. Gracefully fall back to English on systems that
don't contain the module.

This contains some makefile hacks to pass localedir to
perl/Makefile. Which in turn passes it to perl.mak, which'll use it to
search/replace occurances of ++LOCALEDIR++ in .pm files.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Makefile            |    6 +++-
 git-send-email.perl |    3 +-
 perl/Git/I18N.pm    |   73 +++++++++++++++++++++++++++++++++++++++++++++++++++
 perl/Makefile       |    3 +-
 perl/Makefile.PL    |   14 +++++++++-
 5 files changed, 94 insertions(+), 5 deletions(-)
 create mode 100644 perl/Git/I18N.pm

diff --git a/Makefile b/Makefile
index dce2faa..3f0e305 100644
--- a/Makefile
+++ b/Makefile
@@ -1505,7 +1505,7 @@ ifndef NO_TCLTK
 	$(QUIET_SUBDIR0)gitk-git $(QUIET_SUBDIR1) all
 endif
 ifndef NO_PERL
-	$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
+	$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' localedir='$(localedir_SQ)' all
 endif
 ifndef NO_PYTHON
 	$(QUIET_SUBDIR0)git_remote_helpers $(QUIET_SUBDIR1) PYTHON_PATH='$(PYTHON_PATH_SQ)' prefix='$(prefix_SQ)' all
@@ -1884,7 +1884,9 @@ cscope:
 	$(FIND) . -name '*.[hcS]' -print | xargs cscope -b
 
 pot:
-	$(XGETTEXT) -k_ -o po/git.pot $(C_OBJ:o=c) $(SCRIPT_SH)
+	$(XGETTEXT) --keyword=_ --output=po/git.pot --language=C $(C_OBJ:o=c)
+	$(XGETTEXT) --join-existing --output=po/git.pot --language=Shell $(SCRIPT_SH)
+	$(XGETTEXT) --join-existing --output=po/git.pot --language=Perl $(SCRIPT_PERL)
 
 POFILES := $(wildcard po/*.po)
 MOFILES := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/git.mo,$(POFILES))
diff --git a/git-send-email.perl b/git-send-email.perl
index 111c981..b9288af 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -26,6 +26,7 @@ use Term::ANSIColor;
 use File::Temp qw/ tempdir tempfile /;
 use Error qw(:try);
 use Git;
+use Git::I18N;
 
 Getopt::Long::Configure qw/ pass_through /;
 
@@ -674,7 +675,7 @@ if (!defined $sender) {
 	$sender = $repoauthor || $repocommitter || '';
 	$sender = ask("Who should the emails appear to be from? [$sender] ",
 	              default => $sender);
-	print "Emails will be sent from: ", $sender, "\n";
+	printf gettext("Emails will be sent from: %s\n"), $sender;
 	$prompting++;
 }
 
diff --git a/perl/Git/I18N.pm b/perl/Git/I18N.pm
new file mode 100644
index 0000000..e19d99b
--- /dev/null
+++ b/perl/Git/I18N.pm
@@ -0,0 +1,73 @@
+package Git::I18N;
+use strict;
+use warnings;
+use Exporter;
+use base 'Exporter';
+
+our $VERSION = '0.01';
+
+our @EXPORT = qw(gettext);
+our @EXPORT_OK = @EXPORT;
+
+sub __bootstrap_locale_messages {
+	our $TEXTDOMAIN = 'git';
+	our $TEXTDOMAINDIR = $ENV{GIT_TEXTDOMAINDIR} || '++LOCALEDIR++';
+
+	require POSIX;
+	POSIX->import(qw(setlocale));
+	# Non-core prerequisite module
+	require Locale::Messages;
+	Locale::Messages->import(qw(:locale_h :libintl_h));
+
+	setlocale(LC_MESSAGES(), '');
+	setlocale(LC_CTYPE(), '');
+	textdomain($TEXTDOMAIN);
+	bindtextdomain($TEXTDOMAIN => $TEXTDOMAINDIR);
+
+	return;
+}
+
+BEGIN
+{
+	local ($@, $!);
+	eval { __bootstrap_locale_messages() };
+	if ($@) {
+		# Oh noes, no Locale::Messages here
+		*gettext = sub ($) { $_[0] };
+	}
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Git::I18N - Perl interface to Git's Gettext localizations
+
+=head1 SYNOPSIS
+
+	use Git::I18N;
+
+	print gettext("Welcome to Git!\n");
+
+	printf gettext("The following error occured: %s\n"), $error;
+
+=head1 DESCRIPTION
+
+Git's internal interface to Gettext via L<Locale::Messages>. If
+L<Locale::Messages> can't be loaded (it's not a core module) we
+provide stub passthrough fallbacks.
+
+=head1 FUNCTIONS
+
+=head2 gettext($)
+
+L<Locale::Messages>'s gettext function if all goes well, otherwise our
+passthrough fallback function.
+
+=head1 AUTHOR
+
+E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avarab@gmail.com>
+
+=cut
diff --git a/perl/Makefile b/perl/Makefile
index 4ab21d6..4e624ff 100644
--- a/perl/Makefile
+++ b/perl/Makefile
@@ -5,6 +5,7 @@ makfile:=perl.mak
 
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 prefix_SQ = $(subst ','\'',$(prefix))
+localedir_SQ = $(subst ','\'',$(localedir))
 
 ifndef V
 	QUIET = @
@@ -38,7 +39,7 @@ $(makfile): ../GIT-CFLAGS Makefile
 	echo '	echo $(instdir_SQ)' >> $@
 else
 $(makfile): Makefile.PL ../GIT-CFLAGS
-	$(PERL_PATH) $< PREFIX='$(prefix_SQ)'
+	$(PERL_PATH) $< PREFIX='$(prefix_SQ)' --localedir='$(localedir_SQ)'
 endif
 
 # this is just added comfort for calling make directly in perl dir
diff --git a/perl/Makefile.PL b/perl/Makefile.PL
index 0b9deca..456d45b 100644
--- a/perl/Makefile.PL
+++ b/perl/Makefile.PL
@@ -1,4 +1,12 @@
+use strict;
+use warnings;
 use ExtUtils::MakeMaker;
+use Getopt::Long;
+
+# Sanity: die at first unknown option
+Getopt::Long::Configure qw/ pass_through /;
+
+GetOptions("localedir=s" => \my $localedir);
 
 sub MY::postamble {
 	return <<'MAKE_FRAG';
@@ -16,7 +24,10 @@ endif
 MAKE_FRAG
 }
 
-my %pm = ('Git.pm' => '$(INST_LIBDIR)/Git.pm');
+my %pm = (
+	'Git.pm' => '$(INST_LIBDIR)/Git.pm',
+	'Git/I18N.pm' => '$(INST_LIBDIR)/Git/I18N.pm',
+);
 
 # We come with our own bundled Error.pm. It's not in the set of default
 # Perl modules so install it if it's not available on the system yet.
@@ -33,6 +44,7 @@ WriteMakefile(
 	NAME            => 'Git',
 	VERSION_FROM    => 'Git.pm',
 	PM		=> \%pm,
+	PM_FILTER	=> qq[\$(PERL) -pe "s<\\Q++LOCALEDIR++\\E><$localedir>"],
 	MAKEFILE	=> 'perl.mak',
 	INSTALLSITEMAN3DIR => '$(SITEPREFIX)/share/man/man3'
 );
-- 
1.7.1.251.gec7f5.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH/RFC v4 4/7] Makefile: Don't install Gettext .mo files if NO_GETTEXT
  2010-06-02  0:11   ` Ævar Arnfjörð Bjarmason
                       ` (3 preceding siblings ...)
  2010-06-02  1:06     ` [PATCH/RFC v4 3/7] gettext: Add a Gettext interface for Perl Ævar Arnfjörð Bjarmason
@ 2010-06-02  1:06     ` Ævar Arnfjörð Bjarmason
  2010-06-02  1:06     ` [PATCH/RFC v4 5/7] Makefile: Override --keyword= for all languages Ævar Arnfjörð Bjarmason
                       ` (6 subsequent siblings)
  11 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-02  1:06 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Jeff Epler, Ævar Arnfjörð Bjarmason

Change the Makefile not to build/install .mo files as part of the
default target if NO_GETTEXT=1 is given.

This was both redundant, and meant that the Perl and Shell programs
would use the Gettext strings, since they don't use the
git_setup_gettext() function in gettext.c, which is compiled to a stub
if NO_GETTEXT=1 is set.

Reported-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Makefile |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile
index 3f0e305..40eb1a2 100644
--- a/Makefile
+++ b/Makefile
@@ -1891,7 +1891,9 @@ pot:
 POFILES := $(wildcard po/*.po)
 MOFILES := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/git.mo,$(POFILES))
 MODIRS := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/,$(POFILES))
+ifndef NO_GETTEXT
 all:: $(MOFILES)
+endif
 share/locale/%/LC_MESSAGES/git.mo: po/%.po
 	@mkdir -p $(dir $@)
 	$(QUIET_MSGFMT)$(MSGFMT) -o $@ $<
@@ -2008,9 +2010,11 @@ install: all
 	$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 	$(INSTALL) -m 644 $(SCRIPT_LIB) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 	$(INSTALL) $(install_bindir_programs) '$(DESTDIR_SQ)$(bindir_SQ)'
+ifndef NO_GETTEXT
 	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sharedir_SQ)/locale'
 	(cd share && tar cf - locale) | \
 		(cd '$(DESTDIR_SQ)$(sharedir_SQ)' && umask 022 && tar xof -)
+endif
 	$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
 ifndef NO_PERL
 	$(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
-- 
1.7.1.251.gec7f5.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH/RFC v4 5/7] Makefile: Override --keyword= for all languages
  2010-06-02  0:11   ` Ævar Arnfjörð Bjarmason
                       ` (4 preceding siblings ...)
  2010-06-02  1:06     ` [PATCH/RFC v4 4/7] Makefile: Don't install Gettext .mo files if NO_GETTEXT Ævar Arnfjörð Bjarmason
@ 2010-06-02  1:06     ` Ævar Arnfjörð Bjarmason
  2010-06-02  1:06     ` [PATCH/RFC v4 6/7] gettext: Sanity tests for Git's Gettext support Ævar Arnfjörð Bjarmason
                       ` (5 subsequent siblings)
  11 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-02  1:06 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Jeff Epler, Ævar Arnfjörð Bjarmason

By default xgettext will supply a default list of keywords depending
on the --language parameter. Disable this in favor of supplying a list
of keywords manually.

This reduced the surface area for potential bugs.
---
 Makefile |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Makefile b/Makefile
index 40eb1a2..62b8279 100644
--- a/Makefile
+++ b/Makefile
@@ -1884,9 +1884,9 @@ cscope:
 	$(FIND) . -name '*.[hcS]' -print | xargs cscope -b
 
 pot:
-	$(XGETTEXT) --keyword=_ --output=po/git.pot --language=C $(C_OBJ:o=c)
-	$(XGETTEXT) --join-existing --output=po/git.pot --language=Shell $(SCRIPT_SH)
-	$(XGETTEXT) --join-existing --output=po/git.pot --language=Perl $(SCRIPT_PERL)
+	$(XGETTEXT) --keyword= --keyword=_ --output=po/git.pot --language=C $(C_OBJ:o=c)
+	$(XGETTEXT) --join-existing --keyword= --keyword=gettext --output=po/git.pot --language=Shell $(SCRIPT_SH)
+	$(XGETTEXT) --join-existing --keyword= --keyword=gettext --output=po/git.pot --language=Perl $(SCRIPT_PERL)
 
 POFILES := $(wildcard po/*.po)
 MOFILES := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/git.mo,$(POFILES))
-- 
1.7.1.251.gec7f5.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH/RFC v4 6/7] gettext: Sanity tests for Git's Gettext support
  2010-06-02  0:11   ` Ævar Arnfjörð Bjarmason
                       ` (5 preceding siblings ...)
  2010-06-02  1:06     ` [PATCH/RFC v4 5/7] Makefile: Override --keyword= for all languages Ævar Arnfjörð Bjarmason
@ 2010-06-02  1:06     ` Ævar Arnfjörð Bjarmason
  2010-06-02  1:06     ` [PATCH/RFC v4 7/7] gettext: Add a skeleton po/is.po Ævar Arnfjörð Bjarmason
                       ` (4 subsequent siblings)
  11 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-02  1:06 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Jeff Epler, Ævar Arnfjörð Bjarmason

These tests check if Git's C, Shell and Perl wrappers for Gettext are
working.
---
 t/t0200-gettext.sh |   73 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 73 insertions(+), 0 deletions(-)
 create mode 100755 t/t0200-gettext.sh

diff --git a/t/t0200-gettext.sh b/t/t0200-gettext.sh
new file mode 100755
index 0000000..ff2f9f9
--- /dev/null
+++ b/t/t0200-gettext.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+D=`pwd`
+export GIT_TEXTDOMAINDIR=$D/../share/locale
+
+test_description='Gettext support for Git'
+. ./test-lib.sh
+. ../../git-sh-setup
+
+test_expect_success 'sanity: $TEXTDOMAIN is git' '
+    test $TEXTDOMAIN = "git"
+'
+
+test_expect_success 'sanity: $TEXTDOMAINDIR exists' '
+    test -d $TEXTDOMAINDIR &&
+    test $TEXTDOMAINDIR = $GIT_TEXTDOMAINDIR
+'
+
+test_expect_success 'sanity: Icelandic locale was compiled' '
+    test -f $TEXTDOMAINDIR/is/LC_MESSAGES/git.mo
+'
+
+test_expect_success 'sanity: gettext("") metadata is OK' '
+    LC_ALL=is_IS.UTF-8 gettext "" > expect &&
+    grep "Project-Id-Version: Git" expect &&
+    grep "Git Mailing List <git@vger.kernel.org>" expect &&
+    grep "Content-Type: text/plain; charset=UTF-8" expect &&
+    grep "Content-Transfer-Encoding: 8bit" expect
+'
+
+test_expect_success 'sanity: gettext(unknown) is passed through' '
+    printf "This is not a translation string"  > expect
+    gettext "This is not a translation string" > actual &&
+    test_cmp expect actual
+'
+
+test_expect_success 'sanity: C program git-status reads our message catalog ' '
+    > foo &&
+    test_commit foo "a message"
+    git checkout -b topic/gettext-testing &&
+    git status | grep topic/gettext-testing > expect
+    echo "# On branch topic/gettext-testing" > actual
+    test_cmp expect actual &&
+    LC_ALL=is_IS.UTF-8 git status | grep topic/gettext-testing > expect
+    echo "# Á greininni topic/gettext-testing" > actual
+    test_cmp expect actual
+'
+
+test_expect_success 'sanity: Perl program Git::I18N reads our message catalog ' '
+    echo "On branch " > expect &&
+    perl -I"$D/../perl" -MGit::I18N -le "print gettext(q[On branch ])" > actual &&
+    test_cmp expect actual &&
+
+    echo "Á greininni " > expect &&
+    LC_ALL=is_IS.UTF-8 perl -I"$D/../perl" -MGit::I18N -le "print gettext(q[On branch ])" > actual &&
+    test_cmp expect actual
+'
+
+test_expect_success 'Setup another Git repository for testing' '
+    mkdir parent &&
+    (cd parent && git init &&
+     echo one >file && git add file &&
+     git commit -m one)
+'
+
+test_expect_success 'sanity: Shell program git-pull reads our message catalog' '
+    cd parent &&
+    (git pull --tags "../" >out 2>err || :) &&
+    grep "Fetching tags only" err &&
+    (LC_ALL=is_IS.UTF-8 git pull --tags "../" >out 2>err || :) &&
+    grep "Næ aðeins í" err 
+'
+
+test_done
-- 
1.7.1.251.gec7f5.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH/RFC v4 7/7] gettext: Add a skeleton po/is.po
  2010-06-02  0:11   ` Ævar Arnfjörð Bjarmason
                       ` (6 preceding siblings ...)
  2010-06-02  1:06     ` [PATCH/RFC v4 6/7] gettext: Sanity tests for Git's Gettext support Ævar Arnfjörð Bjarmason
@ 2010-06-02  1:06     ` Ævar Arnfjörð Bjarmason
  2010-06-02  6:32     ` [PATCH/RFC v3 0/7] Add internationalization support to Git Johannes Sixt
                       ` (3 subsequent siblings)
  11 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-02  1:06 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Jeff Epler, Ævar Arnfjörð Bjarmason

This example is.po file translates a little bit of C, Shell and Perl
as an example.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 po/is.po |  282 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 282 insertions(+), 0 deletions(-)
 create mode 100644 po/is.po

diff --git a/po/is.po b/po/is.po
new file mode 100644
index 0000000..aa1923e
--- /dev/null
+++ b/po/is.po
@@ -0,0 +1,282 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Git\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-30 19:24+0000\n"
+"PO-Revision-Date: 2010-06-01 22:15+0000\n"
+"Last-Translator: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n"
+"Language-Team: Git Mailing List <git@vger.kernel.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: English\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+#: wt-status.c:53
+msgid "# Unmerged paths:"
+msgstr ""
+
+#: wt-status.c:59 wt-status.c:76
+#, c-format
+msgid "#   (use \"git reset %s <file>...\" to unstage)"
+msgstr ""
+
+#: wt-status.c:61 wt-status.c:78
+msgid "#   (use \"git rm --cached <file>...\" to unstage)"
+msgstr ""
+
+#: wt-status.c:62
+msgid "#   (use \"git add/rm <file>...\" as appropriate to mark resolution)"
+msgstr ""
+
+#: wt-status.c:63 wt-status.c:79 wt-status.c:98 wt-status.c:110
+#: wt-status.c:115 wt-status.c:638 wt-status.c:640
+msgid "#"
+msgstr ""
+
+#: wt-status.c:70
+msgid "# Changes to be committed:"
+msgstr ""
+
+#: wt-status.c:88
+msgid "# Changed but not updated:"
+msgstr ""
+
+#: wt-status.c:92
+msgid "#   (use \"git add <file>...\" to update what will be committed)"
+msgstr ""
+
+#: wt-status.c:94
+msgid "#   (use \"git add/rm <file>...\" to update what will be committed)"
+msgstr ""
+
+#: wt-status.c:95
+msgid ""
+"#   (use \"git checkout -- <file>...\" to discard changes in working "
+"directory)"
+msgstr ""
+
+#: wt-status.c:97
+msgid "#   (commit or discard the untracked or modified content in submodules)"
+msgstr ""
+
+#: wt-status.c:106
+#, c-format
+msgid "# %s files:"
+msgstr ""
+
+#: wt-status.c:109
+#, c-format
+msgid "#   (use \"git %s <file>...\" to include in what will be committed)"
+msgstr ""
+
+#: wt-status.c:126
+msgid "bug"
+msgstr ""
+
+#: wt-status.c:129 wt-status.c:182
+msgid "#\t"
+msgstr ""
+
+#: wt-status.c:131
+msgid "both deleted:"
+msgstr ""
+
+#: wt-status.c:132
+msgid "added by us:"
+msgstr ""
+
+#: wt-status.c:133
+msgid "deleted by them:"
+msgstr ""
+
+#: wt-status.c:134
+msgid "added by them:"
+msgstr ""
+
+#: wt-status.c:135
+msgid "deleted by us:"
+msgstr ""
+
+#: wt-status.c:136
+msgid "both added:"
+msgstr ""
+
+#: wt-status.c:137
+msgid "both modified:"
+msgstr ""
+
+#: wt-status.c:139
+#, c-format
+msgid "%-20s%s\n"
+msgstr ""
+
+#: wt-status.c:165
+msgid " ("
+msgstr ""
+
+#: wt-status.c:167
+msgid "new commits, "
+msgstr ""
+
+#: wt-status.c:169
+msgid "modified content, "
+msgstr ""
+
+#: wt-status.c:171
+msgid "untracked content, "
+msgstr ""
+
+#: wt-status.c:185
+#, c-format
+msgid "new file:   %s"
+msgstr ""
+
+#: wt-status.c:188
+#, c-format
+msgid "copied:     %s -> %s"
+msgstr ""
+
+#: wt-status.c:191
+#, c-format
+msgid "deleted:    %s"
+msgstr ""
+
+#: wt-status.c:194
+#, c-format
+msgid "modified:   %s"
+msgstr ""
+
+#: wt-status.c:197
+#, c-format
+msgid "renamed:    %s -> %s"
+msgstr ""
+
+#: wt-status.c:200
+#, c-format
+msgid "typechange: %s"
+msgstr ""
+
+#: wt-status.c:203
+#, c-format
+msgid "unknown:    %s"
+msgstr ""
+
+#: wt-status.c:206
+#, c-format
+msgid "unmerged:   %s"
+msgstr ""
+
+#: wt-status.c:209
+#, c-format
+msgid "bug: unhandled diff status %c"
+msgstr ""
+
+#: wt-status.c:212
+#, c-format
+msgid "%s"
+msgstr ""
+
+#: wt-status.c:215
+#, c-format
+msgid "\n"
+msgstr ""
+
+#: wt-status.c:622
+msgid "On branch "
+msgstr "Á greininni "
+
+#: wt-status.c:629
+msgid "Not currently on any branch."
+msgstr "Ekki á neinni grein."
+
+#: wt-status.c:639
+msgid "# Initial commit"
+msgstr ""
+
+#: wt-status.c:651
+msgid "Untracked"
+msgstr ""
+
+#: wt-status.c:651
+msgid "add"
+msgstr ""
+
+#: wt-status.c:653
+msgid "Ignored"
+msgstr ""
+
+#: wt-status.c:653
+msgid "add -f"
+msgstr ""
+
+#: wt-status.c:655
+#, c-format
+msgid "# Untracked files not listed%s\n"
+msgstr ""
+
+#: wt-status.c:657
+msgid " (use -u option to show untracked files)"
+msgstr ""
+
+#: wt-status.c:663
+#, c-format
+msgid "# No changes\n"
+msgstr "# Engar breytingar\n"
+
+#: wt-status.c:667
+#, c-format
+msgid "no changes added to commit%s\n"
+msgstr ""
+
+#: wt-status.c:669
+msgid " (use \"git add\" and/or \"git commit -a\")"
+msgstr ""
+
+#: wt-status.c:671
+#, c-format
+msgid "nothing added to commit but untracked files present%s\n"
+msgstr ""
+
+#: wt-status.c:673
+msgid " (use \"git add\" to track)"
+msgstr ""
+
+#: wt-status.c:676
+msgid " (create/copy files and use \"git add\" to track)"
+msgstr ""
+
+#: wt-status.c:678 wt-status.c:681
+#, c-format
+msgid "nothing to commit%s\n"
+msgstr ""
+
+#: wt-status.c:679
+msgid " (use -u to show untracked files)"
+msgstr ""
+
+#: wt-status.c:682
+msgid " (working directory clean)"
+msgstr ""
+
+#: git-pull.sh:124
+msgid "Fetching tags only, you probably meant:"
+msgstr "Næ aðeins í tögg, þú áttir líkast til við:"
+
+#: git-pull.sh:125
+msgid "  git fetch --tags"
+msgstr ""
+
+#: git-pull.sh:157
+msgid ""
+"You are not currently on a branch, so I cannot use any\n"
+"'branch.<branchname>.merge' in your configuration file.\n"
+"Please specify which remote branch you want to use on the command\n"
+"line and try again (e.g. 'git pull <repository> <refspec>').\n"
+"See git-pull(1) for details."
+msgstr ""
+
+#: git-send-email.perl:678
+#, perl-format
+msgid "Emails will be sent from: %s\n"
+msgstr "Póstarnir verða sendir frá: %s\n"
-- 
1.7.1.251.gec7f5.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC v3 2/7] gettext: Add a Gettext interface for shell scripts
  2010-06-01 23:39 ` [PATCH/RFC v3 2/7] gettext: Add a Gettext interface for shell scripts Ævar Arnfjörð Bjarmason
@ 2010-06-02  6:32   ` Johannes Sixt
  2010-06-02  8:53     ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 56+ messages in thread
From: Johannes Sixt @ 2010-06-02  6:32 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason; +Cc: git, Jakub Narebski, Jeff Epler

[When you resend the series in such a manner that you collect a Cc list
manually, please do *not* include me. I'm not interested in this series.]

Am 6/2/2010 1:39, schrieb Ævar Arnfjörð Bjarmason:
> +    # Not everyone has echo -n
> +    case $(echo -n) in
> +        \-n)    Xn=   ; Xc='\c' ;;
> +        *)      Xn=-n ; Xc=
> +    esac

Don't use 'echo'; use 'printf %s'.

> +
> +    gettext () {
> +        echo $Xn "$1" $Xc
> +    }
> +
> +    eval_gettext () {
> +        eval_gettext_var="echo $1"
> +        echo $Xn `eval $eval_gettext_var` $Xc
> +    }

-- Hannes

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC v3 6/7] gettext: Basic sanity tests for Git's Gettext support
  2010-06-01 23:39 ` [PATCH/RFC v3 6/7] gettext: Basic sanity tests for Git's Gettext support Ævar Arnfjörð Bjarmason
@ 2010-06-02  6:32   ` Johannes Sixt
  0 siblings, 0 replies; 56+ messages in thread
From: Johannes Sixt @ 2010-06-02  6:32 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason; +Cc: git, Jakub Narebski, Jeff Epler

[When you resend the series in such a manner that you collect a Cc list
manually, please do *not* include me. I'm not interested in this series.]

Am 6/2/2010 1:39, schrieb Ævar Arnfjörð Bjarmason:
> +test_expect_success 'sanity: $TEXTDOMAIN is git' '
> +	test $TEXTDOMAIN = "git"
> +'
> +
> +test_expect_success 'sanity: $TEXTDOMAINDIR exists' '
> +	test -d $TEXTDOMAINDIR 
> +'
> +
> +test_expect_success 'sanity: Icelandic locale was compiled' '
> +    test -f $TEXTDOMAINDIR/is/LC_MESSAGES/git.mo
> +'

3 times: put "$foo" in double-quotes.

> +test_expect_success 'sanity: gettext(unknown) is passed through' '
> +    gettext "" > expect &&
> +    > actual &&
> +    test_cmp expect actual &&
> +    printf "This is not a translation string"  > expect
> +	gettext "This is not a translation string" > actual &&

Strange indentation and you break the && chain.

-- Hannes

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC v3 0/7] Add internationalization support to Git
  2010-06-02  0:11   ` Ævar Arnfjörð Bjarmason
                       ` (7 preceding siblings ...)
  2010-06-02  1:06     ` [PATCH/RFC v4 7/7] gettext: Add a skeleton po/is.po Ævar Arnfjörð Bjarmason
@ 2010-06-02  6:32     ` Johannes Sixt
  2010-06-02 22:33     ` [PATCH/RFC v5 0/2] Add infrastructure for translating Git with gettext Ævar Arnfjörð Bjarmason
                       ` (2 subsequent siblings)
  11 siblings, 0 replies; 56+ messages in thread
From: Johannes Sixt @ 2010-06-02  6:32 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason; +Cc: git, Jakub Narebski, Jeff Epler

Am 6/2/2010 2:11, schrieb Ævar Arnfjörð Bjarmason:
>     +export GIT_TEXTDOMAINDIR=`pwd`/../share/locale

This works only in some shells for two reasons:

- 'export foo=bar' syntax is not understood everywhere;

- if it is understood, then some shells still split the expansion in
'export foo=`pwd`' at white-space.

Use 'foo=bar; export foo', like you did elsewhere.

>      test_expect_success 'sanity: $TEXTDOMAINDIR exists' '
>     -	test -d $TEXTDOMAINDIR
>     +	test -d $TEXTDOMAINDIR &&
>     +	test $TEXTDOMAINDIR = $GIT_TEXTDOMAINDIR

Double-quotes, double-quotes!

-- Hannes

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC v3 2/7] gettext: Add a Gettext interface for shell  scripts
  2010-06-02  6:32   ` Johannes Sixt
@ 2010-06-02  8:53     ` Ævar Arnfjörð Bjarmason
  2010-06-02  9:38       ` Johannes Sixt
  0 siblings, 1 reply; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-02  8:53 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: git, Jakub Narebski, Jeff Epler

On Wed, Jun 2, 2010 at 06:32, Johannes Sixt <j.sixt@viscovery.net> wrote:
> [When you resend the series in such a manner that you collect a Cc list
> manually, please do *not* include me. I'm not interested in this series.]
>
> Am 6/2/2010 1:39, schrieb Ævar Arnfjörð Bjarmason:
>> +    # Not everyone has echo -n
>> +    case $(echo -n) in
>> +        \-n)    Xn=   ; Xc='\c' ;;
>> +        *)      Xn=-n ; Xc=
>> +    esac
>
> Don't use 'echo'; use 'printf %s'.

I didn't write this code (as the comment says) but I /thought/ that
the whole point of it was to autodetect if echo -n worked, and if so
use it because it'd be faster than printf "%s", otherwise use some
evil foo\n\c hack.

That hack doesn't work on bash ('\c' produces a space), maybe it
doesn't work anywhere. Do you have a shell that doesn't support echo
-n? What does it output there?

I wouldn't be surprised if it did need to be changed to printf
"%s". I'm just curious where it broke and how.

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC v4 1/7] Add infrastructure for translating Git with gettext
  2010-06-02  1:05     ` [PATCH/RFC v4 1/7] Add infrastructure for translating Git with gettext Ævar Arnfjörð Bjarmason
@ 2010-06-02  9:12       ` Peter Krefting
  2010-06-02  9:29         ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 56+ messages in thread
From: Peter Krefting @ 2010-06-02  9:12 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Ævar Arnfjörð Bjarmason, Jakub Narebski, Jeff Epler

Ævar Arnfjörð Bjarmason:

> +		color_fprintf_ln(s->fp, c, _("#   (use \"git rm --cached <file>...\" to unstage)"));
> +	color_fprintf_ln(s->fp, c, _("#   (use \"git add/rm <file>...\" as appropriate to mark resolution)"));
> +	color_fprintf_ln(s->fp, c, _("#"));

This is a multi-line string, and should be translated as such.

The trailing "#" should either be part of the translated multi-line string, 
or be added explicitely, and untranslated, at the end.

> +		color_fprintf_ln(s->fp, c, _("#   (use \"git rm --cached <file>...\" to unstage)"));
> +	color_fprintf_ln(s->fp, c, _("#"));

Determine how to handle the trailing "#" as above.

> +		color_fprintf_ln(s->fp, c, _("#   (use \"git add/rm <file>...\" to update what will be committed)"));
> +	color_fprintf_ln(s->fp, c, _("#   (use \"git checkout -- <file>...\" to discard changes in working directory)"));

Multi-line.

> +		color_fprintf_ln(s->fp, c, _("#   (commit or discard the untracked or modified content in submodules)"));
> +	color_fprintf_ln(s->fp, c, _("#"));

Trailing "#".

> +	color_fprintf_ln(s->fp, c, _("#   (use \"git %s <file>...\" to include in what will be committed)"), how);
> +	color_fprintf_ln(s->fp, c, _("#"));

Ditto.

> +	case 1: how = _("both deleted:"); break;
> +	case 2: how = _("added by us:"); break;
> +	case 3: how = _("deleted by them:"); break;
> +	case 4: how = _("added by them:"); break;
> +	case 5: how = _("deleted by us:"); break;
> +	case 6: how = _("both added:"); break;
> +	case 7: how = _("both modified:"); break;
> 	}
> -	color_fprintf(s->fp, c, "%-20s%s\n", how, one);
> +	color_fprintf(s->fp, c, _("%-20s%s\n"), how, one);

does color_fprintf() gracefully handle cutting text for multi-byte 
encodings, and text width? Otherwise I can see this bite us for all 
non-ASCII locales in general (if long translated are needed), and 
for CJK locales in particular (for variable character widths).

> -		color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "%s", extra.buf);
> +		color_fprintf(s->fp, color(WT_STATUS_HEADER, s), _("%s"), extra.buf);

Should not be in _()

> -	fprintf(s->fp, "\n");
> +	fprintf(s->fp, _("\n"));

Ditto.

> +		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("#"));
> +		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("# Initial commit"));
> +		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("#"));

Either a multi-line string, or untranslate the "#"s.

-- 
\\// Peter - http://www.softwolves.pp.se/

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC v4 1/7] Add infrastructure for translating Git with  gettext
  2010-06-02  9:12       ` Peter Krefting
@ 2010-06-02  9:29         ` Ævar Arnfjörð Bjarmason
  2010-06-02 10:11           ` Peter Krefting
  0 siblings, 1 reply; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-02  9:29 UTC (permalink / raw)
  To: Peter Krefting; +Cc: Git Mailing List, Jakub Narebski, Jeff Epler

On Wed, Jun 2, 2010 at 09:12, Peter Krefting <peter@softwolves.pp.se> wrote:
> Ævar Arnfjörð Bjarmason:
>
>> +               color_fprintf_ln(s->fp, c, _("#   (use \"git rm --cached
>> <file>...\" to unstage)"));
>> +       color_fprintf_ln(s->fp, c, _("#   (use \"git add/rm <file>...\" as
>> appropriate to mark resolution)"));
>> +       color_fprintf_ln(s->fp, c, _("#"));
>
> This is a multi-line string, and should be translated as such.
>
> The trailing "#" should either be part of the translated multi-line string,
> or be added explicitely, and untranslated, at the end.
>
>> +               color_fprintf_ln(s->fp, c, _("#   (use \"git rm --cached
>> <file>...\" to unstage)"));
>> +       color_fprintf_ln(s->fp, c, _("#"));
>
> Determine how to handle the trailing "#" as above.
>
>> +               color_fprintf_ln(s->fp, c, _("#   (use \"git add/rm
>> <file>...\" to update what will be committed)"));
>> +       color_fprintf_ln(s->fp, c, _("#   (use \"git checkout --
>> <file>...\" to discard changes in working directory)"));
>
> Multi-line.
>
>> +               color_fprintf_ln(s->fp, c, _("#   (commit or discard the
>> untracked or modified content in submodules)"));
>> +       color_fprintf_ln(s->fp, c, _("#"));
>
> Trailing "#".
>
>> +       color_fprintf_ln(s->fp, c, _("#   (use \"git %s <file>...\" to
>> include in what will be committed)"), how);
>> +       color_fprintf_ln(s->fp, c, _("#"));
>
> Ditto.
>
>> +       case 1: how = _("both deleted:"); break;
>> +       case 2: how = _("added by us:"); break;
>> +       case 3: how = _("deleted by them:"); break;
>> +       case 4: how = _("added by them:"); break;
>> +       case 5: how = _("deleted by us:"); break;
>> +       case 6: how = _("both added:"); break;
>> +       case 7: how = _("both modified:"); break;
>>        }
>> -       color_fprintf(s->fp, c, "%-20s%s\n", how, one);
>> +       color_fprintf(s->fp, c, _("%-20s%s\n"), how, one);
>
> does color_fprintf() gracefully handle cutting text for multi-byte
> encodings, and text width? Otherwise I can see this bite us for all
> non-ASCII locales in general (if long translated are needed), and for CJK
> locales in particular (for variable character widths).
>
>> -               color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "%s",
>> extra.buf);
>> +               color_fprintf(s->fp, color(WT_STATUS_HEADER, s), _("%s"),
>> extra.buf);
>
> Should not be in _()
>
>> -       fprintf(s->fp, "\n");
>> +       fprintf(s->fp, _("\n"));
>
> Ditto.
>
>> +               color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s),
>> _("#"));
>> +               color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("#
>> Initial commit"));
>> +               color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s),
>> _("#"));
>
> Either a multi-line string, or untranslate the "#"s.

All of the points you raised are correct. This is a bad way to do
things.

It was added in Jeff in his original WIP patch as an example of
translation being possible, but I should just remove it. It's not
ready to go in for the reasons you cited.

I'll just remove the wt-status.c chunk out of the patch completely in
the next submission, and instead translate a few select strings in
some C program well.

Making things like wt-status.c translatable is going to be hard to do
properly. It uses a lot of lego (sticking strings incrementally
together), and due to the coloring of output this'll be particularly
hard to convert.

(Also, for reference, here's a general guide on how to translate C
programs well):

    http://gnu.april.org/software/automake/manual/gettext/Preparing-Strings.html

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC v3 2/7] gettext: Add a Gettext interface for shell scripts
  2010-06-02  8:53     ` Ævar Arnfjörð Bjarmason
@ 2010-06-02  9:38       ` Johannes Sixt
  0 siblings, 0 replies; 56+ messages in thread
From: Johannes Sixt @ 2010-06-02  9:38 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason; +Cc: git, Jakub Narebski, Jeff Epler

Am 6/2/2010 10:53, schrieb Ævar Arnfjörð Bjarmason:
> That hack doesn't work on bash ('\c' produces a space), maybe it
> doesn't work anywhere. Do you have a shell that doesn't support echo
> -n? What does it output there?

It's not just echo -n. You are dealing with text that is outside your
control. There are implementations of echo that interpret backslash in the
text (like \t, \n etc.). You are risking that they are treated as escape
characters rather than printed as-are.

> I wouldn't be surprised if it did need to be changed to printf
> "%s". I'm just curious where it broke and how.

Actually, I don't know where -n does not work.

-- Hannes

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC v4 1/7] Add infrastructure for translating Git with gettext
  2010-06-02  9:29         ` Ævar Arnfjörð Bjarmason
@ 2010-06-02 10:11           ` Peter Krefting
  2010-06-02 10:56             ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 56+ messages in thread
From: Peter Krefting @ 2010-06-02 10:11 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason; +Cc: Git Mailing List, Jakub Narebski

Ævar Arnfjörð Bjarmason:

> Making things like wt-status.c translatable is going to be hard to do 
> properly. It uses a lot of lego (sticking strings incrementally together),

However, preparing them for translation is probably going to make them 
better anyway, as this kind of string building is incredibly brittle, even 
for English strings.

You also have to look out for pluralisation issues, fortunately Gettext does 
have support for the various types of plural forms that are used, so it 
should be possible. The problem is just identifying where they are needed.

> and due to the coloring of output this'll be particularly hard to convert.

Is the colouring based on knowing the contents of the actual text, or is it 
enough to know the markers? I admit not having looked at the colouring code 
at all...

-- 
\\// Peter - http://www.softwolves.pp.se/

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC v4 1/7] Add infrastructure for translating Git with  gettext
  2010-06-02 10:11           ` Peter Krefting
@ 2010-06-02 10:56             ` Ævar Arnfjörð Bjarmason
  2010-06-02 11:31               ` Peter Krefting
  0 siblings, 1 reply; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-02 10:56 UTC (permalink / raw)
  To: Peter Krefting; +Cc: Git Mailing List, Jakub Narebski

On Wed, Jun 2, 2010 at 10:11, Peter Krefting <peter@softwolves.pp.se> wrote:
> Ævar Arnfjörð Bjarmason:
>
>> Making things like wt-status.c translatable is going to be hard to do
>> properly. It uses a lot of lego (sticking strings incrementally together),
>
> However, preparing them for translation is probably going to make them
> better anyway, as this kind of string building is incredibly brittle, even
> for English strings.

Yeah, but they aren't better as they are now. Personally I'd rather
incrementally add translation support correctly than do an incomplete
job of it and wait for the complaints (and subsequent fixes) to come
in.

But maybe that's counter-productive. Just making the English strings
translatable as-is is still going to make most of Git translatable,
and it isn't going to be that bad for languages like German and
French.

Maybe worse is better :)

> You also have to look out for pluralisation issues, fortunately Gettext does
> have support for the various types of plural forms that are used, so it
> should be possible. The problem is just identifying where they are needed.

Yeah. I'm going to support pluralisation (and maybe msgctxt) across
the C-Perl-Shell stack when there's a need for it. I haven't found any
message in Git yet that requires it, but then again I haven't looked
hard either.

It would be useful to have an example of a message that could use
pluralization, even better examples in all of C, Perl and Shell.

>> and due to the coloring of output this'll be particularly hard to convert.
>
> Is the colouring based on knowing the contents of the actual text, or is it
> enough to know the markers? I admit not having looked at the colouring code
> at all...

There's nothing wrong with coloring in particular, it's just that due
to the nature of the current implementation code that does coloring
ends up being a bad offender in the "assemble the strings a few bytes
at a time with strcat()" department.

That can be fixed, it just takes a bit of time and tedium.

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC v4 1/7] Add infrastructure for translating Git with gettext
  2010-06-02 10:56             ` Ævar Arnfjörð Bjarmason
@ 2010-06-02 11:31               ` Peter Krefting
  0 siblings, 0 replies; 56+ messages in thread
From: Peter Krefting @ 2010-06-02 11:31 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Ævar Arnfjörð Bjarmason, Jakub Narebski

Ævar Arnfjörð Bjarmason:

> But maybe that's counter-productive. Just making the English strings 
> translatable as-is is still going to make most of Git translatable, and it 
> isn't going to be that bad for languages like German and French.

I think this is the best approach. Without actually making *everything* 
available for translation it is impossible to weed out the problems. Some of 
the easier "hack" strings (plural word%s, sentence %s%s building, etc) can 
be fixed during conversion, the rest can be done later as complai^Wcomments 
come in.

> It would be useful to have an example of a message that could use 
> pluralization, even better examples in all of C, Perl and Shell.

There is the "Your branch is ahead of..." messages, they were introduced in 
commit b0030db331141bedfaf02f34a83f18712c0ae011, and are currently using a 
"commit%s" form, where the %s either maps to an empty string or "s". Should 
be fairly trivial to fix. I think there are other examples as well, but 
that's the first one I came to think of. :-)

-- 
\\// Peter - http://www.softwolves.pp.se/

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH/RFC v2 5/6] gettext: Add a Gettext interface for Perl
  2010-06-01 19:06     ` Ævar Arnfjörð Bjarmason
@ 2010-06-02 11:47       ` Jakub Narebski
  0 siblings, 0 replies; 56+ messages in thread
From: Jakub Narebski @ 2010-06-02 11:47 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason; +Cc: git, Jeff Epler

On Tue, 1 June 2010, Ævar Arnfjörð Bjarmason wrote:
> On Tue, Jun 1, 2010 at 17:00, Jakub Narebski <jnareb@gmail.com> wrote:
>> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
>>
>>> Make Git's gettext messages available to Perl programs through
>>> Locale::Messages. [...]
>>
>> This is I think a very good idea [...] using the
>> Locale::Messages module from libintl-perl library.  The "On the state of
>> i18n in Perl" (http://rassie.org/archives/247) blog post from 26 April
>> 2009 provides nice counterpoint to Locale::Maketext::TPJ13 / The Perl
>> Journal #13 article[1] from 1999... especially because using gettext is
>> natural for translating git command output and GUI, because git uses it
>> already for Tcl/Tk (gitk and git-gui), and it is natural solution for
>> code in C, which slightly less than half of git code.
>>
>> Well, we could use Locale::Maketext::Gettext, but it is not in Perl
>> core either, and as http://rassie.org/archives/247 says its '*.po'
>> files are less natural.  The gettext documentation (gettext.info) also
>> recommends libintl-perl, or to be more exact Locale::TextDomain from
>> it.
>>
>> [1] http://search.cpan.org/perldoc?Locale::Maketext::TPJ13
>>     http://interglacial.com/~sburke/tpj/as_html/tpj13.html
> 
> Locale::Maketext is a less complete and non-standard alternative as
> the above blogs note. I think the main reason it's used in favor of
> Gettext in many Perl projects is that it can be compiled stand-alone
> from the CPAN, i.e. it doesn't depend on libintl.

That, and the fact that some Perl projects / modules on CPAN started
using Locale::Maketext before gettext acquired functionality for
handling plural forms like ngettext.  The mentioned article from
The Perl Journal #13 might have also mislead module authors...

On the other hand libintl-perl distribution contains pure-Perl gettext
emulation module: Locale::gettext_pp.

> 
> That's comfortable when CPAN is your main distribution channel, but
> Git's main distribution channel is via OS packages, which can
> trivially introduce a gettext dependency (for which they doubtless
> already have a package).

We need gettext for C, for shell, and for Tcl/Tk anyway.

BTW. some projects keep copy of libintl / gettext library in 'intl/'
subdirectory, to be used if it is not present in the target system.
Perl has 'inc/' and 'use inc::latest' (unfortunately in core only
since 5.011002, i.e. 5.12 in practice) to keep private copy of modules
to be used / installed if they are not present in system.

> 
> Locale::Maketext has a Gettext emulation layer, but using it would be
> a potential source of bugs (no emulation is perfect).

OTOH see Locale::gettext_pp from libintl-perl (the distribution
containing Locale::Messages and Locale::TextDomain modules).

> 
> That being said the way I wrote Git::Gexttext means any alterante
> implementation or emulation layer can be seemlessly added later on. We
> could use (or write) something for Perl, C or Shell that completely
> bypasses libintl for systems that don't have a gettext C library.

Right.

Besides I think that the base gettext (no plural forms etc.) is the
same (would produce the same *.po files) for Locale::Maketext::Gettext
and for Locale::Messages / Locale::TextDomain.

> 
>> The question is why not use Locale::TextDomain, the high-level Perl-y
>> framework, wrapper around Locale::Messages from the same libintl-perl
>> library?  The gettext documentation (in gettext.info, chapter "13.5.18
>> Perl") says:
>>
>>  Prerequisite
>>     `use POSIX;'
>>     `use Locale::TextDomain;' (included in the package libintl-perl
>>     which is available on the Comprehensive Perl Archive Network CPAN,
>>     http://www.cpan.org/).
>>
>>
>> This would change
>>
>>  -      print "Emails will be sent from: ", $sender, "\n";
>>  +      printf gettext("Emails will be sent from: %s\n"), $sender;
>>
>> to either
>>
>>  +      print __"Emails will be sent from: ", $sender, "\n";
>>
>> or
>>
>>  +      printf __("Emails will be sent from: %s\n"), $sender;
>>
>> or
>>
>>  +      print __x("Emails will be sent from: {sender}\n",
>>  +                sender => $sender);
> 
> I didn't use Locale::TextDomain because it exports a fatter interface
> by default, and I'm not sure at this point what subset of it it would
> make sense to support.
> 
> This is the default Locale::TextDomain interface:
> 
>    @EXPORT = qw (__ __x __n __nx __xn __p __px __np __npx $__ %__
>                  N__ N__n N__p N__np);
> 
> My Git::Gettext only exports a single gettext() function now. I think
> it's better at this point to have a really small interface and decide
> later if we'd like to expand it, preferably in a way that'll work
> consistently across C, Perl and Shell programs.

That is not, I think, the problem.  You don't need to re-export _all_
of Locale::TextDomain interface (or create and export stubs for all
above functions and variables).

> 
> We might want to improve plural support, add msgctxt etc. later. But
> for an initial implementation I'd rather have something simple &
> stupid.

That's understandable.

BTW. I wonder if git currently has anywhere mesage that depends on
plural form, or does it always use plural / multiform, like for
example '1 files changed' from "git diff --summary"...

> 
>>> Signed-off-by: ?var Arnfjör? Bjarmason <avarab@gmail.com>
>>> ---
>>>  Makefile            |    4 ++-
>>>  git-send-email.perl |    3 +-
>>>  perl/Git/Gettext.pm |   83 +++++++++++++++++++++++++++++++++++++++++++++++++++
>>>  perl/Makefile.PL    |    5 ++-
>>>  4 files changed, 92 insertions(+), 3 deletions(-)
>>>  create mode 100644 perl/Git/Gettext.pm
>>>
>>> diff --git a/Makefile b/Makefile
>>> index dce2faa..2101713 100644
>>> --- a/Makefile
>>> +++ b/Makefile
>>> @@ -1884,7 +1884,9 @@ cscope:
>>>       $(FIND) . -name '*.[hcS]' -print | xargs cscope -b
>>>
>>>  pot:
>>> -     $(XGETTEXT) -k_ -o po/git.pot $(C_OBJ:o=c) $(SCRIPT_SH)
>>> +     $(XGETTEXT) --keyword=_ --output=po/git.pot --language=C $(C_OBJ:o=c)
>>> +     $(XGETTEXT) --join-existing --output=po/git.pot --language=Shell $(SCRIPT_SH)
>>
>> Shouldn't this line be in earlier patch, i.e. in "gettext: Add a
>> Gettext interface for shell scripts"?
> 
> It needed to be split up into seperate line in the Perl commit because ...
> 
>>> +     $(XGETTEXT) --join-existing --output=po/git.pot --language=Perl $(SCRIPT_PERL)
>>
>> From gettext documentation (in gettext.info, chapter "13.5.18 Perl"):
>>
>>  Extractor
>>     `xgettext -k__ -k\$__ -k%__ -k__x -k__n:1,2 -k__nx:1,2 -k__xn:1,2
>>     -kN__ -k'
>>
>> Is it equivalent to specifying 'xgettext --language=Perl'?
> 
> I'm doing --language=Perl because gettext doesn't recognize .perl as a
> valid Perl extension. It only knows about `pl', `PL', `pm', `cgi'.

All right.

> (As an aside, I haven't found why Git chose to use the quaint .perl
> extension).
> 
> But yeah, using --language=Perl is overshooting it with regards to
> keyword extraction. From "5.1 Invoking the `xgettext' Program":
> 
>      To disable the default keyword specifications, the option `-k' or
>      `--keyword' or `--keyword=', without a KEYWORDSPEC, can be used.
> 
> I'll check if I can supply --keyword to all the XGETTEXT invocations.
> 
>> Of course the above assumes that you are using Locale::TextDomain, or
>> at least use the same conventions.

In my opinion we should use the common convention for the programming
language, like described in chapter 15 "Other Programming Languages"
in gettext manual: 
  http://www.gnu.org/software/gettext/manual/gettext.html#Programming-Languages

 * C:      _("msg")
 * Perl:   __"msg"
 * shell:  $(gettext "abc")
 * Tcl/Tk: [_ "msg"]
 * Python: _('msg')

On the other hand the only currently traslated part of git, namely
gitk and git-gui, both of which are written in Tcl/Tk, do not use
recommended gettext shorthand, i.e. [_ "msg"], but the gettext
function directly, i.e. [mc "msg"] (in the imported form, and not
[::msgcat::mc "msg"]).  I guess this is caused by the fact that
shorthand is not that shorter here...

>>>  Getopt::Long::Configure qw/ pass_through /;
>>>
>>> @@ -674,7 +675,7 @@ if (!defined $sender) {
>>>       $sender = $repoauthor || $repocommitter || '';
>>>       $sender = ask("Who should the emails appear to be from? [$sender] ",
>>>                     default => $sender);
>>> -     print "Emails will be sent from: ", $sender, "\n";
>>> +     printf gettext("Emails will be sent from: %s\n"), $sender;
>>
>> As I wrote, this IMHO should be either
>>
>>  +     printf __("Emails will be sent from: %s\n"), $sender;
>>
>> or
>>
>>  +     print __x("Emails will be sent from: {sender}\n", sender => $sender);
>>
>> (note that parantheses differ in those two examples).
> 
> I'd prefer the former argument expansion form (i.e. %s, not
> {$sender}), just so translators don't have to deal with two different
> argument forms.

Well, in this case this does not matter (much), because we have only
single placeholder.

The Locale::TextDomain manpage says that the reason behind __x() is
twofold.  First, you would need context to know what placeholder means
in

  printf __"This is the %s %s.\n", $color, $thing;

as you would have only

  msgid "This is the %s %s.\n"

in the PO file.  Second, formatting with position (to change order of
parameters) is possible with 'printf' only for Perl 5.8.0 or never:

  printf __("This is the %1\$s %2\$s.\n"), $color, $thing;

So that's the reason for introducing

  print __x("This is the {color} {thing}.\n",
            color => $color, thing => $thing);

> As for __ or gettext I don't mind. I just picked the former on a whim
> to match the Shell version. Maybe I should just change C|Perl|Shell to
> use _?
> 
> And we can use _, __ is just used to disambiguate it from the _
> filehandle, and Perl's pretty good at disambiguating that already:
> 
>     $ perl -E 'sub _ ($) { scalar reverse $_[0] }; say _("moo"); stat
> "/etc/hosts"; say ((stat(_))[7])'
>     oom
>     168

Locale::TextDomain::FAQ says:

 Q. Why does Locale::TextDomain use a double underscore? I am used to
    a single underscore from C or other languages.

 A. Function names that consist of exactly one non-alphanumerical
    character make the function automatically global in Perl. Besides,
    in Perl 6 the concatenation operator will be the underscore
    instead of the dot.

    [jn: actually the string concatenation operator in Perl 6 is '~',
     according to http://perlgeek.de/en/article/5-to-6#post_11: 
     "Perl 5 to 6" Lesson 11 - Changes to Perl 5 Operators]


Although 'gettext-tools/examples/hello-perl/hello-1.pl.in' from
gettext sources
  http://git.savannah.gnu.org/cgit/gettext.git/tree/gettext-tools/examples/hello-perl/hello-1.pl.in
(hello-1 uses Locale::Messages, hello-2 uses Locale::TextDomain)
uses _("msg") shortcut:

  sub _ ($) { &gettext; }

Hmmm...

>>> diff --git a/perl/Git/Gettext.pm b/perl/Git/Gettext.pm
>>> new file mode 100644
>>> index 0000000..f434783
>>> --- /dev/null
>>> +++ b/perl/Git/Gettext.pm
>>> @@ -0,0 +1,83 @@
>>> +package Git::Gettext;
>>
>> Should this package be named Git::Gettext, or other name would be
>> better, perhaps Git::I18N (like e.g. Games::Risk have
>> Games::Risk::I18N), or Git::Locale, or even Git::Translator?
>>
>> Not very important.
> 
> I really have no opinion on that, but it does seem like a lot of Perl
> packages on CPAN use the ::I18N suffix.

Also using Git::I18N allows us to change the backend from gettext to
something other (like Locale::Maketext lexicon, or something yet
another).

>>> +use strict;
>>> +use warnings;
>>> +use Exporter;
>>> +use base 'Exporter';
>>
>> O.K.
>>
>> The alternative would be to use
>>
>>  +use Exporter qw(import);
> 
> Not if we want to maintain 5.6 support. The `use Exporter "import"'
> form was only introduced in 5.8. I use it everywhere where I don't
> have to care about 5.6 support (which is everywhere but Git).

Oh, all right.  Thanks for explanation.

>>> +our %EXPORT_TAGS;
>>> +@{ $EXPORT_TAGS{'all'} } = @EXPORT_OK;
>>
>> Why not simply
>>
>>  +our %EXPORT_TAGS = ('all' => [ @EXPORT_OK ]);
[...]
> 
> Just because it some old code of mine I had around that I copied
> from. I could rewrite it to a nicer form.
> 
> Actually I was considering changing it to just have @EXPORT and
> nothing else. You're going to use gettext if you use the module
> anyway, so we might as well just import them all everywhere we use
> Gettext in Perl.

Right, if we use Git::Gettext / Git::I18N it means that we want to do
message traslation, so we would need to import subroutines anyway.

>>> +
>>> +sub __bootstrap_locale_messages {
>>> +     our $TEXTDOMAIN = 'git';
>>> +
>>> +     # TODO: How do I make the sed replacements in the top level
>>> +     # Makefile reach me here?
>>> +     #our $TEXTDOMAINDIR = q|@@LOCALEDIR@@|;
[...]
>> Make invoked on perl/Makefile, when invoked from main Makefile by
>> '$(MAKE) -C perl' (via QUIET_SUBDIR0) passes 'localedir' to submake;
>> perl/Makefile should probably have something like
>>
>>  localedir ?= $(sharedir)/locale
>>
>> That is assuming that 'localedir' is added to list of exported
>> variables.
>>
>> But I am not sure how such substitution should be performed.
> 
> I'll try to get something like that to work. Actually the main problem
> seemed to be that it had a hybrid handwritten Makefile and one made by
> EU::MM.

The problem with EU::MM is that while ExtUtils::MakeMaker was first
released as a core module with Perl 5, git requires for generated
makefile (well, perl.mak included by perl/Makefile) to be compatibile
with DESTDIR mechanism.  This requires ExtUtils::MakeMaker version 6.11+,
which was released with perl 5.006002 (Perl v5.6.2).

Hmmm... perhaps we can bump requirement from Perl 5.6 to Perl 5.6.2
and get rid of hybrid handwritten Makefile?

Otherwise you would have to either duplicate EU::MM work in
handwritten Makefile, or somehow make perl/Makefile to do
transformation before perl/perl.mak generated by EU::MM starts
working...

>>> +
>>> +BEGIN
>>> +{
>>> +     local ($@, $!);
>>> +     eval { __bootstrap_locale_messages() };
>>> +     if ($@) {
>>> +             # Oh noes, no Locale::Messages here
>>> +             *gettext = sub ($) { $_[0] };
>>> +     }
>>> +}
>>
>> Does it need to be in BEGIN block?  Probably yes.
> 
> I'm pretty sure it needs to all be at BEGIN time. I recall that
> declaring prototypes at runtime was an error in some older versions,
> but it works now:
> 
>     $ perl -E '*meh = sub ($) { "foo" }; say prototype "meh"'
>     $
> 
> Anyway since it's code that's being use'd it's going to always be at
> BEGIN time anyway. I just wanted to be explicit.

All right, probably better to be safe.

>>> +=head1 EXPORTS
>>> +
>>> +Exports are done via L<Exporter>. Invididual functions can be
>>> +exporter, or all of them via the C<:all> export tag.
>>
>> Shouldn't this be described in less technical way in SYNOPSIS and
>> DESCRIPTION sections instead?
> 
> Yeah, maybe. But there's a convention for `=head1 EXPORTS' in perl
> core / CPAN. So that's what I added at a whim.

Hmmm... I don't recall seeing EXPORTS section often in manpages for
Perl modules, at least not for the ones I use...


BTW. I tried to find Perl module / Perl program that uses
Locale::TextDomain for l10n, but provides passthrough fallback if it
is not installed... but haven't find any.

-- 
Jakub Narebski
Poland

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH/RFC v5 0/2] Add infrastructure for translating Git with gettext
  2010-06-02  0:11   ` Ævar Arnfjörð Bjarmason
                       ` (8 preceding siblings ...)
  2010-06-02  6:32     ` [PATCH/RFC v3 0/7] Add internationalization support to Git Johannes Sixt
@ 2010-06-02 22:33     ` Ævar Arnfjörð Bjarmason
  2010-06-02 22:33     ` [PATCH/RFC v5 1/2] " Ævar Arnfjörð Bjarmason
  2010-06-02 22:33     ` [PATCH/RFC v5 2/2] Add initial C, Shell and Perl gettext translations Ævar Arnfjörð Bjarmason
  11 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-02 22:33 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Jeff Epler, Ævar Arnfjörð Bjarmason

This is version 5 of the patch series to make Git optionally
translatable with GNU gettext.

I split and squashed the whole thing down into just two patches. The
first commit adds all the needed core infrastructure, and the second
commit contains an initial stab at starting translation for the core
tools, along with some tests that could be added as a result.

The changes this time around are:

  * Testing testing & more testing. Including testing for xgettext
    message extraction, that .po files are generated correctly etc.

  * There's now a git-sh-i18n library for the shell, so gettext.sh
    isn't loaded as part of git-sh-setup.

  * Shell portability fixes suggested by Johannes Sixt

  * Other misc suggestions and fixes Jakub Narebski, Peter Krefting,
    and Jonathan Nieder pointed out were incorporated.

  * I did not change over to Locale::TextDomain for Perl. I wrote a
    version of Git::I18N in it and found that it was more trouble than
    it was worth

I think (this time around, really) that this series is ready for
inclusion unless some issue is raised that I've missed. But to play
devil's advocate this is what might prevent it from being included.

  * gettext is on by default. I think that's a feature, but Git won't
    build by default on e.g. Solaris unless the packager adds
    NO_GETTEXT=YesPlease to the make arguments. Of course if they use
    ./configure it'll detect if libintl.h exists and act
    appropriately.

    I think gettext on by default is a feature, it's equivalent to
    what we do for our other /*/po/ stuff. But pending discussion of
    this I haven't updated e.g. INSTALL. Comments welcome.

  * There isn't documentation on how to use this from a developer and
    maintainer standpoint. And there's no docs for translators.

    Where would the developer/maintainer docs belong? But for most of
    the things such a documentation would include I think other
    existing resources would do fine, e.g. the GNU gettext manual.

Ævar Arnfjörð Bjarmason (2):
  Add infrastructure for translating Git with gettext
  Add initial C, Shell and Perl gettext translations

 .gitignore                         |    2 +
 Makefile                           |   44 ++++++++++-
 config.mak.in                      |    1 +
 configure.ac                       |    6 ++
 gettext.c                          |   25 ++++++
 gettext.h                          |   13 +++
 git-pull.sh                        |   16 ++--
 git-send-email.perl                |    3 +-
 git-sh-i18n.sh                     |   33 ++++++++
 git.c                              |    3 +
 perl/Git/I18N.pm                   |   77 +++++++++++++++++++
 perl/Makefile                      |    3 +-
 perl/Makefile.PL                   |   14 +++-
 po/.gitignore                      |    1 +
 po/is.po                           |   71 +++++++++++++++++
 t/t0200-gettext.sh                 |  146 ++++++++++++++++++++++++++++++++++++
 t/t0200-gettext/test.c             |   10 +++
 t/t0200-gettext/test.perl          |   11 +++
 t/t0200-gettext/test.sh            |   14 ++++
 t/t0201-gettext-shell-fallbacks.sh |   40 ++++++++++
 t/test-lib.sh                      |    1 +
 wt-status.c                        |  107 +++++++++++++-------------
 22 files changed, 576 insertions(+), 65 deletions(-)
 create mode 100644 gettext.c
 create mode 100644 gettext.h
 create mode 100644 git-sh-i18n.sh
 create mode 100644 perl/Git/I18N.pm
 create mode 100644 po/.gitignore
 create mode 100644 po/is.po
 create mode 100755 t/t0200-gettext.sh
 create mode 100644 t/t0200-gettext/test.c
 create mode 100644 t/t0200-gettext/test.perl
 create mode 100644 t/t0200-gettext/test.sh
 create mode 100755 t/t0201-gettext-shell-fallbacks.sh

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH/RFC v5 1/2] Add infrastructure for translating Git with gettext
  2010-06-02  0:11   ` Ævar Arnfjörð Bjarmason
                       ` (9 preceding siblings ...)
  2010-06-02 22:33     ` [PATCH/RFC v5 0/2] Add infrastructure for translating Git with gettext Ævar Arnfjörð Bjarmason
@ 2010-06-02 22:33     ` Ævar Arnfjörð Bjarmason
  2010-06-02 22:33     ` [PATCH/RFC v5 2/2] Add initial C, Shell and Perl gettext translations Ævar Arnfjörð Bjarmason
  11 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-02 22:33 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Jeff Epler, Ævar Arnfjörð Bjarmason

All of the interface messages in Git core are currently hardcoded in
English. Change that by optionally enabling translation of the core C,
Shell and Perl programs via GNU gettext. If you set the appropriate
LC_* variables Git will speak your language, provided that someone has
submitted a translation.

If gettext isn't available, or if Git is compiled with
NO_GETTEXT=YesPlease, then Git fall back on its previous behavior of
only speaking English.

With NO_GETTEXT=YesPlease gettext support will be #defined away for C
programs. For Shell and Perl programs we rely on the git message
catalog not being avalalable. That's a reasonable assumption since the
*.po files won't be installed on the system during make install.

The gettext wrappers that are provided in the patch are only the bare
minimum required to begin translation work. In particular I haven't
added wrappers for the gettext functions that enable plural support,
or those that provide message context (msgctxt). Those can be added
later. The intent is to start with a small subset and see what we need
later, not to start with something that's unnecessarily large right
away.

Implementation and usage notes:

 * General:

   Gettext .mo files will be installed and looked for in the standard
   $(prefix)/share/locale path. GIT_TEXTDOMAINDIR can also be set to
   override that. But this is only intended to be used to test Git
   itself.

 * Perl:

   Perl code that wants to be localized should use the new Git::I18n
   module. It imports a __ function into the caller's package by
   default.

   Instead of using the high level Locale::TextDomain interface I've
   opted to use the low-level (equivalent to the C interface)
   Locale::Messages module, which Locale::TextDomain itself uses.

   Locale::TextDomain does a lot of redundant work we don't need, and
   some of it would potentially introduce bugs. It tries to set the
   $TEXTDOMAIN based on package of the caller, and has its own
   hardcoded paths where it'll search for messages.

   I found it easier just to completely avoid it rather than try to
   circumvent its behavior. In any case, this is an issue wholly
   internal Git::I18N. Its guts can be changed later if that's deemed
   necessary.

 * Shell:

   Shell code that wants to be localized should use the new
   git-sh-i18n library. It's just a wrapper for the system's
   gettext.sh.

   If gettext.sh isn't available we'll fall back on a dumb
   printf(1)-powered fall-through wrapper.

   I originally tried to detect if the system supported `echo -n' but
   I found this to be a waste of time. My benchmarks on Linux, Solaris
   and FreeBSD reveal that printf(1) is fast enough, especially since
   we aren't calling gettext() from within any tight loops.

This patch is based on work by Jeff Epler <jepler@unpythonic.net> who
did the initial Makefile / C work, and a lot of comments from the Git
mailing list.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 .gitignore                         |    2 +
 Makefile                           |   44 ++++++++++++++-
 config.mak.in                      |    1 +
 configure.ac                       |    6 ++
 gettext.c                          |   25 +++++++++
 gettext.h                          |   13 +++++
 git-sh-i18n.sh                     |   33 +++++++++++
 git.c                              |    3 +
 perl/Git/I18N.pm                   |   77 ++++++++++++++++++++++++++
 perl/Makefile                      |    3 +-
 perl/Makefile.PL                   |   14 +++++-
 po/.gitignore                      |    1 +
 po/is.po                           |   48 ++++++++++++++++
 t/t0200-gettext.sh                 |  104 ++++++++++++++++++++++++++++++++++++
 t/t0200-gettext/test.c             |   11 ++++
 t/t0200-gettext/test.perl          |   11 ++++
 t/t0200-gettext/test.sh            |   14 +++++
 t/t0201-gettext-shell-fallbacks.sh |   40 ++++++++++++++
 t/test-lib.sh                      |    1 +
 19 files changed, 447 insertions(+), 4 deletions(-)
 create mode 100644 gettext.c
 create mode 100644 gettext.h
 create mode 100644 git-sh-i18n.sh
 create mode 100644 perl/Git/I18N.pm
 create mode 100644 po/.gitignore
 create mode 100644 po/is.po
 create mode 100755 t/t0200-gettext.sh
 create mode 100644 t/t0200-gettext/test.c
 create mode 100644 t/t0200-gettext/test.perl
 create mode 100644 t/t0200-gettext/test.sh
 create mode 100755 t/t0201-gettext-shell-fallbacks.sh

diff --git a/.gitignore b/.gitignore
index 14e2b6b..6c2b193 100644
--- a/.gitignore
+++ b/.gitignore
@@ -125,6 +125,7 @@
 /git-rm
 /git-send-email
 /git-send-pack
+/git-sh-i18n
 /git-sh-setup
 /git-shell
 /git-shortlog
@@ -204,3 +205,4 @@
 *.pdb
 /Debug/
 /Release/
+/share/
diff --git a/Makefile b/Makefile
index d5d6565..32f5e52 100644
--- a/Makefile
+++ b/Makefile
@@ -272,6 +272,7 @@ mandir = share/man
 infodir = share/info
 gitexecdir = libexec/git-core
 sharedir = $(prefix)/share
+localedir = $(sharedir)/locale
 template_dir = share/git-core/templates
 htmldir = share/doc/git-doc
 ifeq ($(prefix),/usr)
@@ -285,7 +286,7 @@ lib = lib
 # DESTDIR=
 pathsep = :
 
-export prefix bindir sharedir sysconfdir
+export prefix bindir sharedir sysconfdir localedir
 
 CC = gcc
 AR = ar
@@ -297,6 +298,8 @@ RPMBUILD = rpmbuild
 TCL_PATH = tclsh
 TCLTK_PATH = wish
 PTHREAD_LIBS = -lpthread
+XGETTEXT = xgettext
+MSGFMT = msgfmt
 
 export TCL_PATH TCLTK_PATH
 
@@ -358,6 +361,7 @@ SCRIPT_SH += git-web--browse.sh
 SCRIPT_LIB += git-mergetool--lib
 SCRIPT_LIB += git-parse-remote
 SCRIPT_LIB += git-sh-setup
+SCRIPT_LIB += git-sh-i18n
 
 SCRIPT_PERL += git-add--interactive.perl
 SCRIPT_PERL += git-difftool.perl
@@ -523,6 +527,7 @@ LIB_H += userdiff.h
 LIB_H += utf8.h
 LIB_H += xdiff-interface.h
 LIB_H += xdiff/xdiff.h
+LIB_H += gettext.h
 
 LIB_OBJS += abspath.o
 LIB_OBJS += advice.o
@@ -564,6 +569,7 @@ LIB_OBJS += entry.o
 LIB_OBJS += environment.o
 LIB_OBJS += exec_cmd.o
 LIB_OBJS += fsck.o
+LIB_OBJS += gettext.o
 LIB_OBJS += graph.o
 LIB_OBJS += grep.o
 LIB_OBJS += hash.o
@@ -1386,6 +1392,12 @@ ifdef USE_NED_ALLOCATOR
        COMPAT_OBJS += compat/nedmalloc/nedmalloc.o
 endif
 
+ifdef NO_GETTEXT
+	COMPAT_CFLAGS += -DNO_GETTEXT
+else
+	LIBINTL = -lintl
+endif
+
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK=NoThanks
 endif
@@ -1415,6 +1427,7 @@ ifndef V
 	QUIET_BUILT_IN = @echo '   ' BUILTIN $@;
 	QUIET_GEN      = @echo '   ' GEN $@;
 	QUIET_LNCP     = @echo '   ' LN/CP $@;
+	QUIET_MSGFMT   = @echo '   ' MSGFMT $@;
 	QUIET_SUBDIR0  = +@subdir=
 	QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
 			 $(MAKE) $(PRINT_DIR) -C $$subdir
@@ -1442,7 +1455,9 @@ gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
 template_dir_SQ = $(subst ','\'',$(template_dir))
 htmldir_SQ = $(subst ','\'',$(htmldir))
 prefix_SQ = $(subst ','\'',$(prefix))
+sharedir_SQ = $(subst ','\'',$(sharedir))
 
+LOCALEDIR_SQ = $(subst ','\'',$(localedir))
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
@@ -1491,7 +1506,7 @@ ifndef NO_TCLTK
 	$(QUIET_SUBDIR0)gitk-git $(QUIET_SUBDIR1) all
 endif
 ifndef NO_PERL
-	$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
+	$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' localedir='$(localedir_SQ)' all
 endif
 ifndef NO_PYTHON
 	$(QUIET_SUBDIR0)git_remote_helpers $(QUIET_SUBDIR1) PYTHON_PATH='$(PYTHON_PATH_SQ)' prefix='$(prefix_SQ)' all
@@ -1536,6 +1551,7 @@ $(RM) $@ $@+ && \
 sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
     -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
     -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+    -e 's|@@LOCALEDIR@@|$(LOCALEDIR_SQ)|g' \
     -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
     -e $(BROKEN_PATH_FIX) \
     $@.sh >$@+
@@ -1868,6 +1884,21 @@ cscope:
 	$(RM) cscope*
 	$(FIND) . -name '*.[hcS]' -print | xargs cscope -b
 
+pot:
+	$(XGETTEXT) --add-comments --keyword=_ --output=po/git.pot --language=C $(C_OBJ:o=c) t/t0200-gettext/test.c
+	$(XGETTEXT) --add-comments --join-existing --output=po/git.pot --language=Shell $(SCRIPT_SH) t/t0200-gettext/test.sh
+	$(XGETTEXT) --add-comments --join-existing --keyword=__ --output=po/git.pot --language=Perl $(SCRIPT_PERL) t/t0200-gettext/test.perl
+
+POFILES := $(wildcard po/*.po)
+MOFILES := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/git.mo,$(POFILES))
+MODIRS := $(patsubst po/%.po,share/locale/%/LC_MESSAGES/,$(POFILES))
+ifndef NO_GETTEXT
+all:: $(MOFILES)
+endif
+share/locale/%/LC_MESSAGES/git.mo: po/%.po
+	@mkdir -p $(dir $@)
+	$(QUIET_MSGFMT)$(MSGFMT) -o $@ $<
+
 ### Detect prefix changes
 TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
              $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
@@ -1980,6 +2011,11 @@ install: all
 	$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 	$(INSTALL) -m 644 $(SCRIPT_LIB) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 	$(INSTALL) $(install_bindir_programs) '$(DESTDIR_SQ)$(bindir_SQ)'
+ifndef NO_GETTEXT
+	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sharedir_SQ)/locale'
+	(cd share && tar cf - locale) | \
+		(cd '$(DESTDIR_SQ)$(sharedir_SQ)' && umask 022 && tar xof -)
+endif
 	$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
 ifndef NO_PERL
 	$(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
@@ -2127,6 +2163,10 @@ ifndef NO_TCLTK
 	$(MAKE) -C git-gui clean
 endif
 	$(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS
+ifndef NO_GETTEXT
+	$(RM) po/git.pot
+	$(RM) -r share/
+endif
 
 .PHONY: all install clean strip
 .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
diff --git a/config.mak.in b/config.mak.in
index 0d4b64d..a15f3c1 100644
--- a/config.mak.in
+++ b/config.mak.in
@@ -32,6 +32,7 @@ NO_CURL=@NO_CURL@
 NO_EXPAT=@NO_EXPAT@
 NO_LIBGEN_H=@NO_LIBGEN_H@
 HAVE_PATHS_H=@HAVE_PATHS_H@
+NO_GETTEXT=@NO_GETTEXT@
 NEEDS_LIBICONV=@NEEDS_LIBICONV@
 NEEDS_SOCKET=@NEEDS_SOCKET@
 NEEDS_RESOLV=@NEEDS_RESOLV@
diff --git a/configure.ac b/configure.ac
index 71038fc..7bebfd8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -730,6 +730,12 @@ AC_CHECK_HEADER([paths.h],
 [HAVE_PATHS_H=])
 AC_SUBST(HAVE_PATHS_H)
 #
+# Define NO_GETTEXT if you don't have libintl.h
+AC_CHECK_HEADER([libintl.h],
+[NO_GETTEXT=],
+[NO_GETTEXT=YesPlease])
+AC_SUBST(NO_GETTEXT)
+#
 # Define NO_STRCASESTR if you don't have strcasestr.
 GIT_CHECK_FUNC(strcasestr,
 [NO_STRCASESTR=],
diff --git a/gettext.c b/gettext.c
new file mode 100644
index 0000000..22cdcc1
--- /dev/null
+++ b/gettext.c
@@ -0,0 +1,25 @@
+#ifdef NO_GETTEXT
+void git_setup_gettext(void) {}
+#else
+#include "exec_cmd.h"
+#include <libintl.h>
+#include <stdlib.h>
+
+void git_setup_gettext(void) {
+	char *podir;
+	char *envdir = getenv("GIT_TEXTDOMAINDIR");
+
+	if (envdir) {
+		(void)bindtextdomain("git", envdir);
+	} else {
+		podir = (char *)system_path("share/locale");
+		if (!podir) return;
+		(void)bindtextdomain("git", podir);
+		free(podir);
+	}
+
+	(void)setlocale(LC_MESSAGES, "");
+	(void)setlocale(LC_CTYPE, "");
+	(void)textdomain("git");
+}
+#endif
diff --git a/gettext.h b/gettext.h
new file mode 100644
index 0000000..a99da6a
--- /dev/null
+++ b/gettext.h
@@ -0,0 +1,13 @@
+#ifndef GETTEXT_H
+#define GETTEXT_H
+
+void git_setup_gettext(void);
+
+#ifdef NO_GETTEXT
+#define _(s) (s)
+#else
+#include <libintl.h>
+#define _(s) gettext(s)
+#endif
+
+#endif
diff --git a/git-sh-i18n.sh b/git-sh-i18n.sh
new file mode 100644
index 0000000..ae1d5cd
--- /dev/null
+++ b/git-sh-i18n.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# This is what Git uses internally to bootstrap its gettext
+# shellscript interface.
+
+# Try to use libintl's gettext.sh, or fall back to English if we
+# can't.
+. gettext.sh
+
+if test $? -eq 0 && test -z "$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS"
+then
+	TEXTDOMAIN=git
+	export TEXTDOMAIN
+	if [ -z "$GIT_TEXTDOMAINDIR" ]
+	then
+		TEXTDOMAINDIR="@@LOCALEDIR@@"
+	else
+		TEXTDOMAINDIR="$GIT_TEXTDOMAINDIR"
+	fi
+	export TEXTDOMAINDIR
+else
+	# Since gettext.sh isn't available we'll have to define our own
+	# dummy pass-through functions.
+
+	gettext () {
+		printf "%s" "$1"
+	}
+
+	eval_gettext () {
+		gettext_eval="printf '%s' \"$1\""
+		printf "%s" "`eval \"$gettext_eval\"`"
+	}
+fi
diff --git a/git.c b/git.c
index 99f0363..d749eab 100644
--- a/git.c
+++ b/git.c
@@ -3,6 +3,7 @@
 #include "cache.h"
 #include "quote.h"
 #include "run-command.h"
+#include "gettext.h"
 
 const char git_usage_string[] =
 	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n"
@@ -490,6 +491,8 @@ int main(int argc, const char **argv)
 	if (!cmd)
 		cmd = "git-help";
 
+	git_setup_gettext();
+
 	/*
 	 * "git-xxxx" is the same as "git xxxx", but we obviously:
 	 *
diff --git a/perl/Git/I18N.pm b/perl/Git/I18N.pm
new file mode 100644
index 0000000..cabdcec
--- /dev/null
+++ b/perl/Git/I18N.pm
@@ -0,0 +1,77 @@
+package Git::I18N;
+use strict;
+use warnings;
+use Exporter;
+use base 'Exporter';
+
+our $VERSION = '0.01';
+
+our @EXPORT = qw(__);
+our @EXPORT_OK = @EXPORT;
+
+sub __ ($);
+
+sub __bootstrap_locale_messages {
+	our $TEXTDOMAIN = 'git';
+	our $TEXTDOMAINDIR = $ENV{GIT_TEXTDOMAINDIR} || '++LOCALEDIR++';
+
+	require POSIX;
+	POSIX->import(qw(setlocale));
+	# Non-core prerequisite module
+	require Locale::Messages;
+	Locale::Messages->import(qw(:locale_h :libintl_h));
+
+	setlocale(LC_MESSAGES(), '');
+	setlocale(LC_CTYPE(), '');
+	textdomain($TEXTDOMAIN);
+	bindtextdomain($TEXTDOMAIN => $TEXTDOMAINDIR);
+
+	return;
+}
+
+BEGIN
+{
+	local ($@, $!);
+	eval { __bootstrap_locale_messages() };
+	if ($@) {
+		# Oh noes, no Locale::Messages here
+		*__	 = sub ($) { $_[0] };
+	} else {
+		*__	 = sub ($) { goto &Locale::Messages::gettext };
+	}
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Git::I18N - Perl interface to Git's Gettext localizations
+
+=head1 SYNOPSIS
+
+	use Git::I18N;
+
+	print __("Welcome to Git!\n");
+
+	printf __("The following error occured: %s\n"), $error;
+
+=head1 DESCRIPTION
+
+Git's internal interface to Gettext via L<Locale::Messages>. If
+L<Locale::Messages> can't be loaded (it's not a core module) we
+provide stub passthrough fallbacks.
+
+=head1 FUNCTIONS
+
+=head2 __($)
+
+L<Locale::Messages>'s gettext function if all goes well, otherwise our
+passthrough fallback function.
+
+=head1 AUTHOR
+
+E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avarab@gmail.com>
+
+=cut
diff --git a/perl/Makefile b/perl/Makefile
index 4ab21d6..4e624ff 100644
--- a/perl/Makefile
+++ b/perl/Makefile
@@ -5,6 +5,7 @@ makfile:=perl.mak
 
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 prefix_SQ = $(subst ','\'',$(prefix))
+localedir_SQ = $(subst ','\'',$(localedir))
 
 ifndef V
 	QUIET = @
@@ -38,7 +39,7 @@ $(makfile): ../GIT-CFLAGS Makefile
 	echo '	echo $(instdir_SQ)' >> $@
 else
 $(makfile): Makefile.PL ../GIT-CFLAGS
-	$(PERL_PATH) $< PREFIX='$(prefix_SQ)'
+	$(PERL_PATH) $< PREFIX='$(prefix_SQ)' --localedir='$(localedir_SQ)'
 endif
 
 # this is just added comfort for calling make directly in perl dir
diff --git a/perl/Makefile.PL b/perl/Makefile.PL
index 0b9deca..456d45b 100644
--- a/perl/Makefile.PL
+++ b/perl/Makefile.PL
@@ -1,4 +1,12 @@
+use strict;
+use warnings;
 use ExtUtils::MakeMaker;
+use Getopt::Long;
+
+# Sanity: die at first unknown option
+Getopt::Long::Configure qw/ pass_through /;
+
+GetOptions("localedir=s" => \my $localedir);
 
 sub MY::postamble {
 	return <<'MAKE_FRAG';
@@ -16,7 +24,10 @@ endif
 MAKE_FRAG
 }
 
-my %pm = ('Git.pm' => '$(INST_LIBDIR)/Git.pm');
+my %pm = (
+	'Git.pm' => '$(INST_LIBDIR)/Git.pm',
+	'Git/I18N.pm' => '$(INST_LIBDIR)/Git/I18N.pm',
+);
 
 # We come with our own bundled Error.pm. It's not in the set of default
 # Perl modules so install it if it's not available on the system yet.
@@ -33,6 +44,7 @@ WriteMakefile(
 	NAME            => 'Git',
 	VERSION_FROM    => 'Git.pm',
 	PM		=> \%pm,
+	PM_FILTER	=> qq[\$(PERL) -pe "s<\\Q++LOCALEDIR++\\E><$localedir>"],
 	MAKEFILE	=> 'perl.mak',
 	INSTALLSITEMAN3DIR => '$(SITEPREFIX)/share/man/man3'
 );
diff --git a/po/.gitignore b/po/.gitignore
new file mode 100644
index 0000000..221000e
--- /dev/null
+++ b/po/.gitignore
@@ -0,0 +1 @@
+/*.pot
diff --git a/po/is.po b/po/is.po
new file mode 100644
index 0000000..ac4317b
--- /dev/null
+++ b/po/is.po
@@ -0,0 +1,48 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Git\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-06-02 17:35+0000\n"
+"PO-Revision-Date: 2010-06-02 16:01+0000\n"
+"Last-Translator: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n"
+"Language-Team: Git Mailing List <git@vger.kernel.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: English\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+#. TRANSLATORS: This is a test. You don't need to translate it.
+#: t/t0200-gettext/test.c:6
+msgid "TEST: A C test string"
+msgstr "TILRAUN: C tilraunastrengur"
+
+#. TRANSLATORS: This is a test. You don't need to translate it.
+#: t/t0200-gettext/test.c:9
+#, c-format
+msgid "TEST: A C test string %s"
+msgstr "TILRAUN: C tilraunastrengur %s"
+
+#. TRANSLATORS: This is a test. You don't need to translate it.
+#: t/t0200-gettext/test.sh:8
+msgid "TEST: A Shell test string"
+msgstr "TILRAUN: Skeljartilraunastrengur"
+
+#. TRANSLATORS: This is a test. You don't need to translate it.
+#: t/t0200-gettext/test.sh:11
+#, sh-format
+msgid "TEST: A Shell test $variable"
+msgstr "TILRAUN: Skeljartilraunastrengur með breytunni $variable"
+
+#. This is a phony Perl program that's only here to test xgettext
+#. message extraction
+#. TRANSLATORS: This is a test. You don't need to translate it.
+#: t/t0200-gettext/test.perl:5
+msgid "TEST: A Perl test string"
+msgstr "TILRAUN: Perl tilraunastrengur"
+
+#. TRANSLATORS: This is a test. You don't need to translate it.
+#: t/t0200-gettext/test.perl:8
+#, perl-format
+msgid "TEST: A Perl test variable %s"
+msgstr "TILRAUN: Perl tilraunastrengur með breytunni %s"
diff --git a/t/t0200-gettext.sh b/t/t0200-gettext.sh
new file mode 100755
index 0000000..7b01ce4
--- /dev/null
+++ b/t/t0200-gettext.sh
@@ -0,0 +1,104 @@
+#!/bin/sh
+D=`pwd`
+GIT_TEXTDOMAINDIR="$D/../share/locale"
+GIT_POROOT="$D/../po"
+export GIT_TEXTDOMAINDIR GIT_POROOT
+
+test_description='Gettext support for Git'
+. ./test-lib.sh
+. ../../git-sh-i18n
+
+test_expect_success 'sanity: $TEXTDOMAIN is git' '
+    test $TEXTDOMAIN = "git"
+'
+
+test_expect_success 'sanity: $TEXTDOMAINDIR exists' '
+    test -d "$TEXTDOMAINDIR" &&
+    test "$TEXTDOMAINDIR" = "$GIT_TEXTDOMAINDIR"
+'
+
+test_expect_success 'sanity: Icelandic locale was compiled' '
+    test -f "$TEXTDOMAINDIR/is/LC_MESSAGES/git.mo"
+'
+
+test_expect_success 'sanity: No gettext("") data for fantasy locale' '
+    LANGUAGE=is LC_ALL=tlh_TLH.UTF-8 gettext "" > real-locale &&
+    test_expect_failure test -s real-locale
+'
+
+test_expect_success 'sanity: Some gettext("") data for real locale' '
+    LANGUAGE=is LC_ALL=is_IS.UTF-8 gettext "" > fantasy-locale &&
+    test -s fantasy-locale
+'
+
+# TODO: When we have more locales, generalize this to test them
+# all. Maybe we'll need a dir->locale map for that.
+test_expect_success 'sanity: gettext("") metadata is OK' '
+    # Return value may be non-zero
+    LANGUAGE=is LC_ALL=is_IS.UTF-8 gettext "" > zero-expect &&
+    grep "Project-Id-Version: Git" zero-expect &&
+    grep "Git Mailing List <git@vger.kernel.org>" zero-expect &&
+    grep "Content-Type: text/plain; charset=UTF-8" zero-expect &&
+    grep "Content-Transfer-Encoding: 8bit" zero-expect
+'
+
+test_expect_success 'sanity: gettext(unknown) is passed through' '
+    printf "This is not a translation string"  > expect &&
+    gettext "This is not a translation string" > actual &&
+    eval_gettext "This is not a translation string" > actual &&
+    test_cmp expect actual
+'
+
+# xgettext from C
+test_expect_success 'xgettext: C extraction' '
+    printf "TILRAUN: C tilraunastrengur" > expect &&
+    LANGUAGE=is LC_ALL=is_IS.UTF-8 gettext "TEST: A C test string" > actual &&
+    test_cmp expect actual
+'
+
+test_expect_success 'xgettext: C extraction with %s' '
+    printf "TILRAUN: C tilraunastrengur %%s" > expect &&
+    LANGUAGE=is LC_ALL=is_IS.UTF-8 gettext "TEST: A C test string %s" > actual &&
+    test_cmp expect actual
+'
+
+# xgettext from Shell
+test_expect_success 'xgettext: Shell extraction' '
+    printf "TILRAUN: Skeljartilraunastrengur" > expect &&
+    LANGUAGE=is LC_ALL=is_IS.UTF-8 gettext "TEST: A Shell test string" > actual &&
+    test_cmp expect actual
+'
+
+test_expect_success 'xgettext: Shell extraction with $variable' '
+    printf "TILRAUN: Skeljartilraunastrengur með breytunni a var i able" > x-expect &&
+    LANGUAGE=is LC_ALL=is_IS.UTF-8 variable="a var i able" eval_gettext "TEST: A Shell test \$variable" > x-actual &&
+    test_cmp x-expect x-actual
+'
+
+# xgettext from Perl
+test_expect_success 'xgettext: Perl extraction' '
+    printf "TILRAUN: Perl tilraunastrengur" > expect &&
+    LANGUAGE=is LC_ALL=is_IS.UTF-8 gettext "TEST: A Perl test string" > actual &&
+    test_cmp expect actual
+'
+
+test_expect_success 'xgettext: Perl extraction with %s' '
+    printf "TILRAUN: Perl tilraunastrengur með breytunni %%s" > expect &&
+    LANGUAGE=is LC_ALL=is_IS.UTF-8 gettext "TEST: A Perl test variable %s" > actual &&
+    test_cmp expect actual
+'
+
+test_expect_success 'xgettext: Perl _() strings are not extracted' '
+    > expect &&
+    grep "A Perl string xgettext will not get" $GIT_POROOT/is.po > actual;
+    test_cmp expect actual
+'
+
+# xgettext extracts comments
+test_expect_success 'xgettext: Comment extraction with --add-comments' '
+    grep "TRANSLATORS: This is a test" ../t0200-gettext/* | wc -l > expect &&
+    grep "TRANSLATORS: This is a test" $GIT_POROOT/is.po  | wc -l > actual &&
+    test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0200-gettext/test.c b/t/t0200-gettext/test.c
new file mode 100644
index 0000000..9fa4c23
--- /dev/null
+++ b/t/t0200-gettext/test.c
@@ -0,0 +1,11 @@
+/* This is a phony C program that's only here to test xgettext message extraction */
+
+int main(void)
+{
+	/* TRANSLATORS: This is a test. You don't need to translate it. */
+	puts(_("TEST: A C test string"));
+
+	/* TRANSLATORS: This is a test. You don't need to translate it. */
+	printf(_("TEST: A C test string %s"), "variable");
+}
+	
diff --git a/t/t0200-gettext/test.perl b/t/t0200-gettext/test.perl
new file mode 100644
index 0000000..8c50ef0
--- /dev/null
+++ b/t/t0200-gettext/test.perl
@@ -0,0 +1,11 @@
+# This is a phony Perl program that's only here to test xgettext
+# message extraction
+
+# TRANSLATORS: This is a test. You don't need to translate it.
+print __("TEST: A Perl test string");
+
+# TRANSLATORS: This is a test. You don't need to translate it.
+printf __("TEST: A Perl test variable %s"), "moo";
+
+# TRANSLATORS: If you see this, Git has a bug
+print _"TEST: A Perl string xgettext will not get";
diff --git a/t/t0200-gettext/test.sh b/t/t0200-gettext/test.sh
new file mode 100644
index 0000000..022d607
--- /dev/null
+++ b/t/t0200-gettext/test.sh
@@ -0,0 +1,14 @@
+# This is a phony Shell program that's only here to test xgettext
+# message extraction
+
+# so the above comment won't be folded into the next one by xgettext
+echo
+
+# TRANSLATORS: This is a test. You don't need to translate it.
+gettext "TEST: A Shell test string"
+
+# TRANSLATORS: This is a test. You don't need to translate it.
+eval_gettext "TEST: A Shell test \$variable"
+
+# TRANSLATORS: If you see this, Git has a bug
+_("TEST: A Shell string xgettext won't get")
diff --git a/t/t0201-gettext-shell-fallbacks.sh b/t/t0201-gettext-shell-fallbacks.sh
new file mode 100755
index 0000000..827adb2
--- /dev/null
+++ b/t/t0201-gettext-shell-fallbacks.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+D=`pwd`
+GIT_TEXTDOMAINDIR="$D/../share/locale"
+GIT_INTERNAL_GETTEXT_TEST_FALLBACKS=YesPlease
+
+export GIT_TEXTDOMAINDIR GIT_INTERNAL_GETTEXT_TEST_FALLBACKS
+
+test_description='Gettext Shell fallbacks'
+. ./test-lib.sh
+. ../../git-sh-i18n
+
+test_expect_success 'sanity: $GIT_INTERNAL_GETTEXT_TEST_FALLBACKS is set' '
+	test_expect_failure test -z "$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS"
+'
+
+test_expect_success 'gettext: our gettext() fallback has pass-through semantics' '
+    printf "test" > expect &&
+    gettext "test" > actual &&
+    test_cmp expect actual &&
+    printf "test more words" > expect &&
+    gettext "test more words" > actual &&
+    test_cmp expect actual
+'
+
+test_expect_success 'eval_gettext: our eval_gettext() fallback has pass-through semantics' '
+    printf "test" > expect &&
+    eval_gettext "test" > actual &&
+    test_cmp expect actual &&
+    printf "test more words" > expect &&
+    eval_gettext "test more words" > actual &&
+    test_cmp expect actual
+'
+
+test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables' '
+    printf "test YesPlease" > expect &&
+    eval_gettext "test \$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS" > actual &&
+    test_cmp expect actual
+'
+
+test_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 454880a..cb3c9c5 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -37,6 +37,7 @@ ORIGINAL_TERM=$TERM
 # For repeatability, reset the environment to known value.
 LANG=C
 LC_ALL=C
+LANGUAGE=C
 PAGER=cat
 TZ=UTC
 TERM=dumb
-- 
1.7.1.242.g290e3.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH/RFC v5 2/2] Add initial C, Shell and Perl gettext translations
  2010-06-02  0:11   ` Ævar Arnfjörð Bjarmason
                       ` (10 preceding siblings ...)
  2010-06-02 22:33     ` [PATCH/RFC v5 1/2] " Ævar Arnfjörð Bjarmason
@ 2010-06-02 22:33     ` Ævar Arnfjörð Bjarmason
  11 siblings, 0 replies; 56+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2010-06-02 22:33 UTC (permalink / raw)
  To: git; +Cc: Jakub Narebski, Jeff Epler, Ævar Arnfjörð Bjarmason

Change the git status, git pull, and git send-email commands to have
at least one translatable string. Each command uses a different core
language, so this makes a good example of how C, Shell and Perl
programs can be translated using gettext.

Since this introduces translation into the real Git tools more tests
can be added to check if they translations actually work for real core
tools.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 git-pull.sh            |   16 ++++---
 git-send-email.perl    |    3 +-
 po/is.po               |   27 +++++++++++-
 t/t0200-gettext.sh     |   42 +++++++++++++++++++
 t/t0200-gettext/test.c |    1 -
 wt-status.c            |  107 ++++++++++++++++++++++++------------------------
 6 files changed, 132 insertions(+), 64 deletions(-)

diff --git a/git-pull.sh b/git-pull.sh
index 1a4729f..0d95722 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -9,6 +9,7 @@ LONG_USAGE='Fetch one or more remote refs and merge it/them into the current HEA
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
 . git-sh-setup
+. git-sh-i18n
 set_reflog_action "pull $*"
 require_work_tree
 cd_to_toplevel
@@ -121,8 +122,8 @@ error_on_no_merge_candidates () {
 	do
 		case "$opt" in
 		-t|--t|--ta|--tag|--tags)
-			echo "Fetching tags only, you probably meant:"
-			echo "  git fetch --tags"
+			gettext "Fetching tags only, you probably meant:"; echo
+			gettext "  git fetch --tags"; echo
 			exit 1
 		esac
 	done
@@ -154,11 +155,12 @@ error_on_no_merge_candidates () {
 		echo "a branch. Because this is not the default configured remote"
 		echo "for your current branch, you must specify a branch on the command line."
 	elif [ -z "$curr_branch" ]; then
-		echo "You are not currently on a branch, so I cannot use any"
-		echo "'branch.<branchname>.merge' in your configuration file."
-		echo "Please specify which remote branch you want to use on the command"
-		echo "line and try again (e.g. 'git pull <repository> <refspec>')."
-		echo "See git-pull(1) for details."
+		gettext "You are not currently on a branch, so I cannot use any
+'branch.<branchname>.merge' in your configuration file.
+Please specify which remote branch you want to use on the command
+line and try again (e.g. 'git pull <repository> <refspec>').
+See git-pull(1) for details.";
+		echo
 	elif [ -z "$upstream" ]; then
 		echo "You asked me to pull without telling me which branch you"
 		echo "want to $op_type $op_prep, and 'branch.${curr_branch}.merge' in"
diff --git a/git-send-email.perl b/git-send-email.perl
index 111c981..4977fdf 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -26,6 +26,7 @@ use Term::ANSIColor;
 use File::Temp qw/ tempdir tempfile /;
 use Error qw(:try);
 use Git;
+use Git::I18N;
 
 Getopt::Long::Configure qw/ pass_through /;
 
@@ -674,7 +675,7 @@ if (!defined $sender) {
 	$sender = $repoauthor || $repocommitter || '';
 	$sender = ask("Who should the emails appear to be from? [$sender] ",
 	              default => $sender);
-	print "Emails will be sent from: ", $sender, "\n";
+	printf __("Emails will be sent from: %s\n"), $sender;
 	$prompting++;
 }
 
diff --git a/po/is.po b/po/is.po
index ac4317b..72437c3 100644
--- a/po/is.po
+++ b/po/is.po
@@ -2,8 +2,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-06-02 17:35+0000\n"
-"PO-Revision-Date: 2010-06-02 16:01+0000\n"
+"POT-Creation-Date: 2010-06-02 19:43+0000\n"
+"PO-Revision-Date: 2010-06-02 19:35+0000\n"
 "Last-Translator: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n"
 "Language-Team: Git Mailing List <git@vger.kernel.org>\n"
 "MIME-Version: 1.0\n"
@@ -12,6 +12,20 @@ msgstr ""
 "X-Poedit-Language: English\n"
 "X-Poedit-SourceCharset: utf-8\n"
 
+#: wt-status.c:63 wt-status.c:79 wt-status.c:98 wt-status.c:110
+#: wt-status.c:622
+msgid "On branch "
+msgstr "Á greininni "
+
+#: wt-status.c:629
+msgid "Not currently on any branch."
+msgstr "Ekki á neinni grein."
+
+#: wt-status.c:663
+#, c-format
+msgid "# No changes\n"
+msgstr "# Engar breytingar\n"
+
 #. TRANSLATORS: This is a test. You don't need to translate it.
 #: t/t0200-gettext/test.c:6
 msgid "TEST: A C test string"
@@ -23,6 +37,10 @@ msgstr "TILRAUN: C tilraunastrengur"
 msgid "TEST: A C test string %s"
 msgstr "TILRAUN: C tilraunastrengur %s"
 
+#: git-pull.sh:124
+msgid "Fetching tags only, you probably meant:"
+msgstr "Næ aðeins í tögg, þú áttir líkast til við:"
+
 #. TRANSLATORS: This is a test. You don't need to translate it.
 #: t/t0200-gettext/test.sh:8
 msgid "TEST: A Shell test string"
@@ -34,6 +52,11 @@ msgstr "TILRAUN: Skeljartilraunastrengur"
 msgid "TEST: A Shell test $variable"
 msgstr "TILRAUN: Skeljartilraunastrengur með breytunni $variable"
 
+#: git-send-email.perl:678
+#, perl-format
+msgid "Emails will be sent from: %s\n"
+msgstr "Póstarnir verða sendir frá: %s\n"
+
 #. This is a phony Perl program that's only here to test xgettext
 #. message extraction
 #. TRANSLATORS: This is a test. You don't need to translate it.
diff --git a/t/t0200-gettext.sh b/t/t0200-gettext.sh
index 7b01ce4..95f05ec 100755
--- a/t/t0200-gettext.sh
+++ b/t/t0200-gettext.sh
@@ -101,4 +101,46 @@ test_expect_success 'xgettext: Comment extraction with --add-comments' '
     test_cmp expect actual
 '
 
+# Actually execute some C, Perl and Shell code that uses Gettext
+test_expect_success 'C: git-status reads our message catalog ' '
+    > foo &&
+    test_commit "some-file" &&
+    git checkout -b topic/gettext-testing &&
+    LANGUAGE=C LC_ALL=C git status | grep topic/gettext-testing > expect &&
+    echo "# On branch topic/gettext-testing" > actual &&
+    test_cmp expect actual &&
+
+    LANGUAGE=is LC_ALL=is_IS.UTF-8 git status | grep topic/gettext-testing > expect &&
+    echo "# Á greininni topic/gettext-testing" > actual &&
+    test_cmp expect actual
+'
+
+test_expect_success 'Perl: Git::I18N reads our message catalog ' '
+    # Bail out if the system doesn not have Locale::Messages
+    perl -MLocale::Messages -e1 2>/dev/null || return 0
+
+    echo "On branch " > expect &&
+    LANGUAGE=C LC_ALL=C perl -I"$D/../perl" -MGit::I18N -le "print __(q[On branch ])" > actual &&
+    test_cmp expect actual &&
+
+    echo "Á greininni " > expect &&
+    LANGUAGE=is LC_ALL=is_IS.UTF-8 perl -I"$D/../perl" -MGit::I18N -le "print __(q[On branch ])" > actual &&
+    test_cmp expect actual
+'
+
+test_expect_success 'Shell: git-pull reads our message catalog' '
+    # Repository for testing
+    mkdir parent &&
+    (cd parent && git init &&
+     echo one >file && git add file &&
+     git commit -m one) &&
+
+    # Actual test
+    (cd parent &&
+    (LANGUAGE=C LC_ALL=C git pull --tags "../" >out 2>err);
+    grep "Fetching tags only" err &&
+    (LANGUAGE=is LC_ALL=is_IS.UTF-8 git pull --tags ../ >out 2>err || :) &&
+    grep "Næ aðeins í" err)
+'
+
 test_done
diff --git a/t/t0200-gettext/test.c b/t/t0200-gettext/test.c
index 9fa4c23..171bcc6 100644
--- a/t/t0200-gettext/test.c
+++ b/t/t0200-gettext/test.c
@@ -8,4 +8,3 @@ int main(void)
 	/* TRANSLATORS: This is a test. You don't need to translate it. */
 	printf(_("TEST: A C test string %s"), "variable");
 }
-	
diff --git a/wt-status.c b/wt-status.c
index 14e0acc..484a866 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -9,6 +9,7 @@
 #include "quote.h"
 #include "run-command.h"
 #include "remote.h"
+#include "gettext.h"
 
 static char default_wt_status_colors[][COLOR_MAXLEN] = {
 	GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
@@ -49,16 +50,16 @@ static void wt_status_print_unmerged_header(struct wt_status *s)
 {
 	const char *c = color(WT_STATUS_HEADER, s);
 
-	color_fprintf_ln(s->fp, c, "# Unmerged paths:");
+	color_fprintf_ln(s->fp, c, _("# Unmerged paths:"));
 	if (!advice_status_hints)
 		return;
 	if (s->in_merge)
 		;
 	else if (!s->is_initial)
-		color_fprintf_ln(s->fp, c, "#   (use \"git reset %s <file>...\" to unstage)", s->reference);
+		color_fprintf_ln(s->fp, c, _("#   (use \"git reset %s <file>...\" to unstage)"), s->reference);
 	else
-		color_fprintf_ln(s->fp, c, "#   (use \"git rm --cached <file>...\" to unstage)");
-	color_fprintf_ln(s->fp, c, "#   (use \"git add/rm <file>...\" as appropriate to mark resolution)");
+		color_fprintf_ln(s->fp, c, _("#   (use \"git rm --cached <file>...\" to unstage)"));
+	color_fprintf_ln(s->fp, c, _("#   (use \"git add/rm <file>...\" as appropriate to mark resolution)"));
 	color_fprintf_ln(s->fp, c, "#");
 }
 
@@ -66,15 +67,15 @@ static void wt_status_print_cached_header(struct wt_status *s)
 {
 	const char *c = color(WT_STATUS_HEADER, s);
 
-	color_fprintf_ln(s->fp, c, "# Changes to be committed:");
+	color_fprintf_ln(s->fp, c, _("# Changes to be committed:"));
 	if (!advice_status_hints)
 		return;
 	if (s->in_merge)
 		; /* NEEDSWORK: use "git reset --unresolve"??? */
 	else if (!s->is_initial)
-		color_fprintf_ln(s->fp, c, "#   (use \"git reset %s <file>...\" to unstage)", s->reference);
+		color_fprintf_ln(s->fp, c, _("#   (use \"git reset %s <file>...\" to unstage)"), s->reference);
 	else
-		color_fprintf_ln(s->fp, c, "#   (use \"git rm --cached <file>...\" to unstage)");
+		color_fprintf_ln(s->fp, c, _("#   (use \"git rm --cached <file>...\" to unstage)"));
 	color_fprintf_ln(s->fp, c, "#");
 }
 
@@ -84,16 +85,16 @@ static void wt_status_print_dirty_header(struct wt_status *s,
 {
 	const char *c = color(WT_STATUS_HEADER, s);
 
-	color_fprintf_ln(s->fp, c, "# Changed but not updated:");
+	color_fprintf_ln(s->fp, c, _("# Changed but not updated:"));
 	if (!advice_status_hints)
 		return;
 	if (!has_deleted)
-		color_fprintf_ln(s->fp, c, "#   (use \"git add <file>...\" to update what will be committed)");
+		color_fprintf_ln(s->fp, c, _("#   (use \"git add <file>...\" to update what will be committed)"));
 	else
-		color_fprintf_ln(s->fp, c, "#   (use \"git add/rm <file>...\" to update what will be committed)");
-	color_fprintf_ln(s->fp, c, "#   (use \"git checkout -- <file>...\" to discard changes in working directory)");
+		color_fprintf_ln(s->fp, c, _("#   (use \"git add/rm <file>...\" to update what will be committed)"));
+	color_fprintf_ln(s->fp, c, _("#   (use \"git checkout -- <file>...\" to discard changes in working directory)"));
 	if (has_dirty_submodules)
-		color_fprintf_ln(s->fp, c, "#   (commit or discard the untracked or modified content in submodules)");
+		color_fprintf_ln(s->fp, c, _("#   (commit or discard the untracked or modified content in submodules)"));
 	color_fprintf_ln(s->fp, c, "#");
 }
 
@@ -102,10 +103,10 @@ static void wt_status_print_other_header(struct wt_status *s,
 					 const char *how)
 {
 	const char *c = color(WT_STATUS_HEADER, s);
-	color_fprintf_ln(s->fp, c, "# %s files:", what);
+	color_fprintf_ln(s->fp, c, _("# %s files:"), what);
 	if (!advice_status_hints)
 		return;
-	color_fprintf_ln(s->fp, c, "#   (use \"git %s <file>...\" to include in what will be committed)", how);
+	color_fprintf_ln(s->fp, c, _("#   (use \"git %s <file>...\" to include in what will be committed)"), how);
 	color_fprintf_ln(s->fp, c, "#");
 }
 
@@ -122,20 +123,20 @@ static void wt_status_print_unmerged_data(struct wt_status *s,
 	const char *c = color(WT_STATUS_UNMERGED, s);
 	struct wt_status_change_data *d = it->util;
 	struct strbuf onebuf = STRBUF_INIT;
-	const char *one, *how = "bug";
+	const char *one, *how = _("bug");
 
 	one = quote_path(it->string, -1, &onebuf, s->prefix);
 	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
 	switch (d->stagemask) {
-	case 1: how = "both deleted:"; break;
-	case 2: how = "added by us:"; break;
-	case 3: how = "deleted by them:"; break;
-	case 4: how = "added by them:"; break;
-	case 5: how = "deleted by us:"; break;
-	case 6: how = "both added:"; break;
-	case 7: how = "both modified:"; break;
+	case 1: how = _("both deleted:"); break;
+	case 2: how = _("added by us:"); break;
+	case 3: how = _("deleted by them:"); break;
+	case 4: how = _("added by them:"); break;
+	case 5: how = _("deleted by us:"); break;
+	case 6: how = _("both added:"); break;
+	case 7: how = _("both modified:"); break;
 	}
-	color_fprintf(s->fp, c, "%-20s%s\n", how, one);
+	color_fprintf(s->fp, c, _("%-20s%s\n"), how, one);
 	strbuf_release(&onebuf);
 }
 
@@ -163,11 +164,11 @@ static void wt_status_print_change_data(struct wt_status *s,
 		if (d->new_submodule_commits || d->dirty_submodule) {
 			strbuf_addstr(&extra, " (");
 			if (d->new_submodule_commits)
-				strbuf_addf(&extra, "new commits, ");
+				strbuf_addf(&extra, _("new commits, "));
 			if (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
-				strbuf_addf(&extra, "modified content, ");
+				strbuf_addf(&extra, _("modified content, "));
 			if (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
-				strbuf_addf(&extra, "untracked content, ");
+				strbuf_addf(&extra, _("untracked content, "));
 			strbuf_setlen(&extra, extra.len - 2);
 			strbuf_addch(&extra, ')');
 		}
@@ -178,34 +179,34 @@ static void wt_status_print_change_data(struct wt_status *s,
 	one = quote_path(one_name, -1, &onebuf, s->prefix);
 	two = quote_path(two_name, -1, &twobuf, s->prefix);
 
-	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
+	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), _("#\t"));
 	switch (status) {
 	case DIFF_STATUS_ADDED:
-		color_fprintf(s->fp, c, "new file:   %s", one);
+		color_fprintf(s->fp, c, _("new file:   %s"), one);
 		break;
 	case DIFF_STATUS_COPIED:
-		color_fprintf(s->fp, c, "copied:     %s -> %s", one, two);
+		color_fprintf(s->fp, c, _("copied:     %s -> %s"), one, two);
 		break;
 	case DIFF_STATUS_DELETED:
-		color_fprintf(s->fp, c, "deleted:    %s", one);
+		color_fprintf(s->fp, c, _("deleted:    %s"), one);
 		break;
 	case DIFF_STATUS_MODIFIED:
-		color_fprintf(s->fp, c, "modified:   %s", one);
+		color_fprintf(s->fp, c, _("modified:   %s"), one);
 		break;
 	case DIFF_STATUS_RENAMED:
-		color_fprintf(s->fp, c, "renamed:    %s -> %s", one, two);
+		color_fprintf(s->fp, c, _("renamed:    %s -> %s"), one, two);
 		break;
 	case DIFF_STATUS_TYPE_CHANGED:
-		color_fprintf(s->fp, c, "typechange: %s", one);
+		color_fprintf(s->fp, c, _("typechange: %s"), one);
 		break;
 	case DIFF_STATUS_UNKNOWN:
-		color_fprintf(s->fp, c, "unknown:    %s", one);
+		color_fprintf(s->fp, c, _("unknown:    %s"), one);
 		break;
 	case DIFF_STATUS_UNMERGED:
-		color_fprintf(s->fp, c, "unmerged:   %s", one);
+		color_fprintf(s->fp, c, _("unmerged:   %s"), one);
 		break;
 	default:
-		die("bug: unhandled diff status %c", status);
+		die(_("bug: unhandled diff status %c"), status);
 	}
 	if (extra.len) {
 		color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "%s", extra.buf);
@@ -618,14 +619,14 @@ void wt_status_print(struct wt_status *s)
 	const char *branch_color = color(WT_STATUS_HEADER, s);
 
 	if (s->branch) {
-		const char *on_what = "On branch ";
+		const char *on_what = _("On branch ");
 		const char *branch_name = s->branch;
 		if (!prefixcmp(branch_name, "refs/heads/"))
 			branch_name += 11;
 		else if (!strcmp(branch_name, "HEAD")) {
 			branch_name = "";
 			branch_color = color(WT_STATUS_NOBRANCH, s);
-			on_what = "Not currently on any branch.";
+			on_what = _("Not currently on any branch.");
 		}
 		color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "# ");
 		color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name);
@@ -635,7 +636,7 @@ void wt_status_print(struct wt_status *s)
 
 	if (s->is_initial) {
 		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
-		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "# Initial commit");
+		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), _("# Initial commit"));
 		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
 	}
 
@@ -647,38 +648,38 @@ void wt_status_print(struct wt_status *s)
 		wt_status_print_submodule_summary(s, 1);  /* unstaged */
 	}
 	if (s->show_untracked_files) {
-		wt_status_print_other(s, &s->untracked, "Untracked", "add");
+		wt_status_print_other(s, &s->untracked, _("Untracked"), _("add"));
 		if (s->show_ignored_files)
-			wt_status_print_other(s, &s->ignored, "Ignored", "add -f");
+			wt_status_print_other(s, &s->ignored, _("Ignored"), _("add -f"));
 	} else if (s->commitable)
-		fprintf(s->fp, "# Untracked files not listed%s\n",
+		fprintf(s->fp, _("# Untracked files not listed%s\n"),
 			advice_status_hints
-			? " (use -u option to show untracked files)" : "");
+			? _(" (use -u option to show untracked files)") : "");
 
 	if (s->verbose)
 		wt_status_print_verbose(s);
 	if (!s->commitable) {
 		if (s->amend)
-			fprintf(s->fp, "# No changes\n");
+			fprintf(s->fp, _("# No changes\n"));
 		else if (s->nowarn)
 			; /* nothing */
 		else if (s->workdir_dirty)
-			printf("no changes added to commit%s\n",
+			printf(_("no changes added to commit%s\n"),
 				advice_status_hints
-				? " (use \"git add\" and/or \"git commit -a\")" : "");
+				? _(" (use \"git add\" and/or \"git commit -a\")") : "");
 		else if (s->untracked.nr)
-			printf("nothing added to commit but untracked files present%s\n",
+			printf(_("nothing added to commit but untracked files present%s\n"),
 				advice_status_hints
-				? " (use \"git add\" to track)" : "");
+				? _(" (use \"git add\" to track)") : "");
 		else if (s->is_initial)
 			printf("nothing to commit%s\n", advice_status_hints
-				? " (create/copy files and use \"git add\" to track)" : "");
+				? _(" (create/copy files and use \"git add\" to track)") : "");
 		else if (!s->show_untracked_files)
-			printf("nothing to commit%s\n", advice_status_hints
-				? " (use -u to show untracked files)" : "");
+			printf(_("nothing to commit%s\n"), advice_status_hints
+				? _(" (use -u to show untracked files)") : "");
 		else
-			printf("nothing to commit%s\n", advice_status_hints
-				? " (working directory clean)" : "");
+			printf(_("nothing to commit%s\n"), advice_status_hints
+				? _(" (working directory clean)") : "");
 	}
 }
 
-- 
1.7.1.242.g290e3.dirty

^ permalink raw reply related	[flat|nested] 56+ messages in thread

end of thread, other threads:[~2010-06-02 22:34 UTC | newest]

Thread overview: 56+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-05-29 22:45 [PATCH/RFC 0/5] Add internationalization support to Git Ævar Arnfjörð Bjarmason
2010-05-29 22:45 ` [PATCH 1/5] Add infrastructure for translating Git with gettext Ævar Arnfjörð Bjarmason
2010-05-29 22:45 ` [PATCH 2/5] gitignore: Ignore files generated by gettext Ævar Arnfjörð Bjarmason
2010-05-29 22:45 ` [PATCH 3/5] Makefile: Remove Gettext files on make clean Ævar Arnfjörð Bjarmason
2010-05-29 22:45 ` [PATCH 4/5] gettext: Add a skeleton po/is.po Ævar Arnfjörð Bjarmason
2010-05-29 22:45 ` [PATCH 5/5] Add infrastructure to make shellscripts translatable Ævar Arnfjörð Bjarmason
2010-05-30  1:46 ` [PATCH/RFC 0/5] Add internationalization support to Git Jonathan Nieder
2010-05-30 16:04   ` Ævar Arnfjörð Bjarmason
2010-05-30 22:23     ` Jonathan Nieder
2010-05-31 12:17       ` Ævar Arnfjörð Bjarmason
2010-05-30 20:54 ` [PATCH/RFC v2 0/6] " Ævar Arnfjörð Bjarmason
2010-05-30 20:54 ` [PATCH/RFC v2 1/6] Add infrastructure for translating Git with gettext Ævar Arnfjörð Bjarmason
2010-06-01 17:01   ` Jakub Narebski
2010-06-01 18:11     ` [PATCH/RCF] autoconf: Check if <libintl.h> exists and set NO_GETTEXT Ævar Arnfjörð Bjarmason
2010-06-01 21:22       ` Jakub Narebski
2010-05-30 20:54 ` [PATCH/RFC v2 2/6] gitignore: Ignore files generated by gettext Ævar Arnfjörð Bjarmason
2010-05-30 20:54 ` [PATCH/RFC v2 3/6] Makefile: Remove Gettext files on make clean Ævar Arnfjörð Bjarmason
2010-05-30 20:54 ` [PATCH/RFC v2 4/6] gettext: Add a Gettext interface for shell scripts Ævar Arnfjörð Bjarmason
2010-05-30 20:54 ` [PATCH/RFC v2 5/6] gettext: Add a Gettext interface for Perl Ævar Arnfjörð Bjarmason
2010-06-01 17:00   ` Jakub Narebski
2010-06-01 19:06     ` Ævar Arnfjörð Bjarmason
2010-06-02 11:47       ` Jakub Narebski
2010-05-30 20:54 ` [PATCH/RFC v2 6/6] gettext: Add a skeleton po/is.po Ævar Arnfjörð Bjarmason
2010-05-30 21:29   ` Jonathan Nieder
2010-05-30 21:39     ` Jonathan Nieder
2010-05-31 14:17     ` Ævar Arnfjörð Bjarmason
2010-06-01 23:39 ` [PATCH/RFC v3 0/7] Add internationalization support to Git Ævar Arnfjörð Bjarmason
2010-06-02  0:11   ` Ævar Arnfjörð Bjarmason
2010-06-02  1:05     ` [PATCH/RFC v4 " Ævar Arnfjörð Bjarmason
2010-06-02  1:05     ` [PATCH/RFC v4 1/7] Add infrastructure for translating Git with gettext Ævar Arnfjörð Bjarmason
2010-06-02  9:12       ` Peter Krefting
2010-06-02  9:29         ` Ævar Arnfjörð Bjarmason
2010-06-02 10:11           ` Peter Krefting
2010-06-02 10:56             ` Ævar Arnfjörð Bjarmason
2010-06-02 11:31               ` Peter Krefting
2010-06-02  1:05     ` [PATCH/RFC v4 2/7] gettext: Add a Gettext interface for shell scripts Ævar Arnfjörð Bjarmason
2010-06-02  1:06     ` [PATCH/RFC v4 3/7] gettext: Add a Gettext interface for Perl Ævar Arnfjörð Bjarmason
2010-06-02  1:06     ` [PATCH/RFC v4 4/7] Makefile: Don't install Gettext .mo files if NO_GETTEXT Ævar Arnfjörð Bjarmason
2010-06-02  1:06     ` [PATCH/RFC v4 5/7] Makefile: Override --keyword= for all languages Ævar Arnfjörð Bjarmason
2010-06-02  1:06     ` [PATCH/RFC v4 6/7] gettext: Sanity tests for Git's Gettext support Ævar Arnfjörð Bjarmason
2010-06-02  1:06     ` [PATCH/RFC v4 7/7] gettext: Add a skeleton po/is.po Ævar Arnfjörð Bjarmason
2010-06-02  6:32     ` [PATCH/RFC v3 0/7] Add internationalization support to Git Johannes Sixt
2010-06-02 22:33     ` [PATCH/RFC v5 0/2] Add infrastructure for translating Git with gettext Ævar Arnfjörð Bjarmason
2010-06-02 22:33     ` [PATCH/RFC v5 1/2] " Ævar Arnfjörð Bjarmason
2010-06-02 22:33     ` [PATCH/RFC v5 2/2] Add initial C, Shell and Perl gettext translations Ævar Arnfjörð Bjarmason
2010-06-01 23:39 ` [PATCH/RFC v3 1/7] Add infrastructure for translating Git with gettext Ævar Arnfjörð Bjarmason
2010-06-01 23:39 ` [PATCH/RFC v3 2/7] gettext: Add a Gettext interface for shell scripts Ævar Arnfjörð Bjarmason
2010-06-02  6:32   ` Johannes Sixt
2010-06-02  8:53     ` Ævar Arnfjörð Bjarmason
2010-06-02  9:38       ` Johannes Sixt
2010-06-01 23:39 ` [PATCH/RFC v3 3/7] gettext: Add a Gettext interface for Perl Ævar Arnfjörð Bjarmason
2010-06-01 23:39 ` [PATCH/RFC v3 4/7] Makefile: Don't install Gettext .mo files if NO_GETTEXT Ævar Arnfjörð Bjarmason
2010-06-01 23:39 ` [PATCH/RFC v3 5/7] Makefile: Override --keyword= for all languages Ævar Arnfjörð Bjarmason
2010-06-01 23:39 ` [PATCH/RFC v3 6/7] gettext: Basic sanity tests for Git's Gettext support Ævar Arnfjörð Bjarmason
2010-06-02  6:32   ` Johannes Sixt
2010-06-01 23:39 ` [PATCH/RFC v3 7/7] gettext: Add a skeleton po/is.po Ævar Arnfjörð Bjarmason

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.