[12/12] fsck: mark strings for translation
diff mbox series

Message ID 20181028065157.26727-13-pclouds@gmail.com
State New
Headers show
Series
  • Mark more strings for translation
Related show

Commit Message

Duy Nguyen Oct. 28, 2018, 6:51 a.m. UTC
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/fsck.c             | 113 ++++++++++++++++++++-----------------
 t/t1410-reflog.sh          |   6 +-
 t/t1450-fsck.sh            |  50 ++++++++--------
 t/t6050-replace.sh         |   4 +-
 t/t7415-submodule-names.sh |   6 +-
 5 files changed, 94 insertions(+), 85 deletions(-)

Comments

SZEDER Gábor Oct. 29, 2018, 10:53 a.m. UTC | #1
On Sun, Oct 28, 2018 at 07:51:57AM +0100, Nguyễn Thái Ngọc Duy wrote:
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  builtin/fsck.c             | 113 ++++++++++++++++++++-----------------
>  t/t1410-reflog.sh          |   6 +-
>  t/t1450-fsck.sh            |  50 ++++++++--------
>  t/t6050-replace.sh         |   4 +-
>  t/t7415-submodule-names.sh |   6 +-
>  5 files changed, 94 insertions(+), 85 deletions(-)
> 
> diff --git a/builtin/fsck.c b/builtin/fsck.c
> index 06eb421720..13f8fe35c5 100644
> --- a/builtin/fsck.c
> +++ b/builtin/fsck.c
> @@ -108,8 +108,9 @@ static int fsck_config(const char *var, const char *value, void *cb)
>  static void objreport(struct object *obj, const char *msg_type,
>  			const char *err)
>  {
> -	fprintf(stderr, "%s in %s %s: %s\n",
> -		msg_type, printable_type(obj), describe_object(obj), err);
> +	fprintf_ln(stderr, _("%s in %s %s: %s"),

Are the (f)printf() -> (f)printf_ln() changes all over
'builtin/fsck.c' really necessary to mark strings for translation?

> diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
> index 90c765df3a..500c21366e 100755
> --- a/t/t1450-fsck.sh
> +++ b/t/t1450-fsck.sh

> @@ -594,7 +594,7 @@ test_expect_success 'fsck --name-objects' '
>  		remove_object $(git rev-parse julius:caesar.t) &&
>  		test_must_fail git fsck --name-objects >out &&
>  		tree=$(git rev-parse --verify julius:) &&
> -		egrep "$tree \((refs/heads/master|HEAD)@\{[0-9]*\}:" out
> +		test -n "$GETTEXT_POISON" || egrep "$tree \((refs/heads/master|HEAD)@\{[0-9]*\}:" out

'test_i18ngrep' accepts all 'grep' options, so this could be written
as:

  test_i18ngrep -E "..." out


There might be something else wrong with the patch, because now this
test tends to fail on current 'pu' on Travis CI:

  [... snipped output from 'test_commit' ...]
  +git rev-parse julius:caesar.t
  +remove_object be45bbd3809e0829297cefa576e699c134abacfd
  +sha1_file be45bbd3809e0829297cefa576e699c134abacfd
  +remainder=45bbd3809e0829297cefa576e699c134abacfd
  +firsttwo=be
  +echo .git/objects/be/45bbd3809e0829297cefa576e699c134abacfd
  +rm .git/objects/be/45bbd3809e0829297cefa576e699c134abacfd
  +test_must_fail git fsck --name-objects
  +_test_ok=
  +git fsck --name-objects
  +exit_code=2
  +test 2 -eq 0
  +test_match_signal 13 2
  +test 2 = 141
  +test 2 = 269
  +return 1
  +test 2 -gt 129
  +test 2 -eq 127
  +test 2 -eq 126
  +return 0
  +git rev-parse --verify julius:
  +tree=c2fab98f409a47394d992eca10a20e0b22377c0c
  +test -n 
  +egrep c2fab98f409a47394d992eca10a20e0b22377c0c \((refs/heads/master|HEAD)@\{[0-9]*\}: out
  error: last command exited with $?=1
  not ok 65 - fsck --name-objects

The contents of 'out':

  broken link from    tree be45bbd3809e0829297cefa576e699c134abacfd (refs/heads/master@{1112912113}:caesar.t)
                to    blob be45bbd3809e0829297cefa576e699c134abacfd (refs/heads/master@{1112912113}:caesar.t)
  missing blob be45bbd3809e0829297cefa576e699c134abacfd (refs/heads/master@{1112912113}:caesar.t)

On a related (side)note, I think it would be better if this 'grep'
pattern were more explicit about which of the three lines it is
supposed to match.


Anyway, I couldn't reproduce the failure locally yet, but, admittedly,
I didn't try that hard...  And my builds on Travis CI haven't yet come
that far to try this topic on its own.
Junio C Hamano Oct. 29, 2018, 2:09 p.m. UTC | #2
SZEDER Gábor <szeder.dev@gmail.com> writes:

>> -	fprintf(stderr, "%s in %s %s: %s\n",
>> -		msg_type, printable_type(obj), describe_object(obj), err);
>> +	fprintf_ln(stderr, _("%s in %s %s: %s"),
>
> Are the (f)printf() -> (f)printf_ln() changes all over
> 'builtin/fsck.c' really necessary to mark strings for translation?

It is beyond absolute minimum but I saw it argued here that this
makes it easier to manage the .po and .pot files if your message
strings do not end with LF, a you are much less likely to _add_
unneeded LF to the translated string than _lose_ LF at the end of
translated string.
Duy Nguyen Oct. 29, 2018, 4:14 p.m. UTC | #3
On Mon, Oct 29, 2018 at 3:09 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> SZEDER Gábor <szeder.dev@gmail.com> writes:
>
> >> -    fprintf(stderr, "%s in %s %s: %s\n",
> >> -            msg_type, printable_type(obj), describe_object(obj), err);
> >> +    fprintf_ln(stderr, _("%s in %s %s: %s"),
> >
> > Are the (f)printf() -> (f)printf_ln() changes all over
> > 'builtin/fsck.c' really necessary to mark strings for translation?
>
> It is beyond absolute minimum but I saw it argued here that this
> makes it easier to manage the .po and .pot files if your message
> strings do not end with LF, a you are much less likely to _add_
> unneeded LF to the translated string than _lose_ LF at the end of
> translated string.

Especially when \n plays an important role and we don't trust
translators to keep it [1] [2]. It's probably a too defensive stance
and often does not apply, so nowadays I do it just to keep a
consistent pattern in the code.

[1] https://public-inbox.org/git/20120308220131.GA10122@burratino/#t
[2] but then translators can crash programs anyway (e.g. changing %d
to %s...) we just trust gettext tools to catch problems early.
Ævar Arnfjörð Bjarmason Oct. 29, 2018, 5:38 p.m. UTC | #4
On Mon, Oct 29 2018, Duy Nguyen wrote:

> On Mon, Oct 29, 2018 at 3:09 PM Junio C Hamano <gitster@pobox.com> wrote:
>>
>> SZEDER Gábor <szeder.dev@gmail.com> writes:
>>
>> >> -    fprintf(stderr, "%s in %s %s: %s\n",
>> >> -            msg_type, printable_type(obj), describe_object(obj), err);
>> >> +    fprintf_ln(stderr, _("%s in %s %s: %s"),
>> >
>> > Are the (f)printf() -> (f)printf_ln() changes all over
>> > 'builtin/fsck.c' really necessary to mark strings for translation?
>>
>> It is beyond absolute minimum but I saw it argued here that this
>> makes it easier to manage the .po and .pot files if your message
>> strings do not end with LF, a you are much less likely to _add_
>> unneeded LF to the translated string than _lose_ LF at the end of
>> translated string.
>
> Especially when \n plays an important role and we don't trust
> translators to keep it [1] [2]. It's probably a too defensive stance
> and often does not apply, so nowadays I do it just to keep a
> consistent pattern in the code.
>
> [1] https://public-inbox.org/git/20120308220131.GA10122@burratino/#t
> [2] but then translators can crash programs anyway (e.g. changing %d
> to %s...) we just trust gettext tools to catch problems early.

As Jonathan pointed out in the follow-up message[1] this sort of thing
is checked for in msgfmt, so sure, let's strip the \n, but it's really
not something we need to worry about. Likewise with translators turning
"%s" into "%d" (or "% s") or whatever.

    $ git diff -U0
    diff --git a/po/de.po b/po/de.po
    index 47986814c9..62de46c63d 100644
    --- a/po/de.po
    +++ b/po/de.po
    @@ -23 +23 @@ msgid "%shint: %.*s%s\n"
    -msgstr "%sHinweis: %.*s%s\n"
    +msgstr "%sHinweis: %.*s%s"
    $ make [...]
    [...]
    po/de.po:23: 'msgid' and 'msgstr' entries do not both end with '\n'
    msgfmt: found 1 fatal error
    3470 translated messages.
    make: *** [Makefile:2488: po/build/locale/de/LC_MESSAGES/git.mo] Error 1

1. https://public-inbox.org/git/CACsJy8AcUy9FZiiehGc7mEL4i+XP6u0pmH1rGoR-WZnhYT1UMQ@mail.gmail.com/
Ævar Arnfjörð Bjarmason Oct. 29, 2018, 5:43 p.m. UTC | #5
On Mon, Oct 29 2018, Ævar Arnfjörð Bjarmason wrote:

> On Mon, Oct 29 2018, Duy Nguyen wrote:
>
>> On Mon, Oct 29, 2018 at 3:09 PM Junio C Hamano <gitster@pobox.com> wrote:
>>>
>>> SZEDER Gábor <szeder.dev@gmail.com> writes:
>>>
>>> >> -    fprintf(stderr, "%s in %s %s: %s\n",
>>> >> -            msg_type, printable_type(obj), describe_object(obj), err);
>>> >> +    fprintf_ln(stderr, _("%s in %s %s: %s"),
>>> >
>>> > Are the (f)printf() -> (f)printf_ln() changes all over
>>> > 'builtin/fsck.c' really necessary to mark strings for translation?
>>>
>>> It is beyond absolute minimum but I saw it argued here that this
>>> makes it easier to manage the .po and .pot files if your message
>>> strings do not end with LF, a you are much less likely to _add_
>>> unneeded LF to the translated string than _lose_ LF at the end of
>>> translated string.
>>
>> Especially when \n plays an important role and we don't trust
>> translators to keep it [1] [2]. It's probably a too defensive stance
>> and often does not apply, so nowadays I do it just to keep a
>> consistent pattern in the code.
>>
>> [1] https://public-inbox.org/git/20120308220131.GA10122@burratino/#t
>> [2] but then translators can crash programs anyway (e.g. changing %d
>> to %s...) we just trust gettext tools to catch problems early.
>
> As Jonathan pointed out in the follow-up message[1] this sort of thing
> is checked for in msgfmt, so sure, let's strip the \n, but it's really
> not something we need to worry about. Likewise with translators turning
> "%s" into "%d" (or "% s") or whatever.
>
>     $ git diff -U0
>     diff --git a/po/de.po b/po/de.po
>     index 47986814c9..62de46c63d 100644
>     --- a/po/de.po
>     +++ b/po/de.po
>     @@ -23 +23 @@ msgid "%shint: %.*s%s\n"
>     -msgstr "%sHinweis: %.*s%s\n"
>     +msgstr "%sHinweis: %.*s%s"
>     $ make [...]
>     [...]
>     po/de.po:23: 'msgid' and 'msgstr' entries do not both end with '\n'
>     msgfmt: found 1 fatal error
>     3470 translated messages.
>     make: *** [Makefile:2488: po/build/locale/de/LC_MESSAGES/git.mo] Error 1
>
> 1. https://public-inbox.org/git/CACsJy8AcUy9FZiiehGc7mEL4i+XP6u0pmH1rGoR-WZnhYT1UMQ@mail.gmail.com/

...to add to that, if there *are* formatting errors that --check doesn't
spot let's hear about it so we can just patch gettext upstream:
https://github.com/autotools-mirror/gettext/blob/master/gettext-tools/src/msgl-check.c#L572-L756

Unlike the rest of our stack where we need to support however many years
old tools we can freely rely on bleeding-edge gettext features, since
the only person we need to convince to upgrade is Jiang. I.e. he accepts
the PRs from translators, (presumably) runs msgfmt --check and then
submits the result to Junio.

See the "Usually..." paragraph in my 66f5f6dca9 ("C style: use standard
style for "TRANSLATORS" comments", 2017-05-11) for an example. We
already rely on a fairly recent gettext toolchain.
Jonathan Nieder Oct. 30, 2018, 11:27 p.m. UTC | #6
Hi,

Ævar Arnfjörð Bjarmason wrote:
>> On Mon, Oct 29, 2018 at 3:09 PM Junio C Hamano <gitster@pobox.com> wrote:
>>> SZEDER Gábor <szeder.dev@gmail.com> writes:
>>>> Nguyễn Thái Ngọc Duy wrote:

>>>>> -    fprintf(stderr, "%s in %s %s: %s\n",
>>>>> -            msg_type, printable_type(obj), describe_object(obj), err);
>>>>> +    fprintf_ln(stderr, _("%s in %s %s: %s"),
>>>>
>>>> Are the (f)printf() -> (f)printf_ln() changes all over
>>>> 'builtin/fsck.c' really necessary to mark strings for translation?
>>>
>>> It is beyond absolute minimum but I saw it argued here that this
>>> makes it easier to manage the .po and .pot files if your message
>>> strings do not end with LF, a you are much less likely to _add_
>>> unneeded LF to the translated string than _lose_ LF at the end of
>>> translated string.
[...]
> As Jonathan pointed out in the follow-up message[1] this sort of thing
> is checked for in msgfmt, so sure, let's strip the \n, but it's really
> not something we need to worry about. Likewise with translators turning
> "%s" into "%d" (or "% s") or whatever.

IMHO the advantage of leaving the \n out in the message is not so much
that we don't trust translators but more that where we can make their
lives easier, we should.

In other words, I'm glad the patch does that, and Ævar, I agree.

Thanks, both.

Jonathan

> 1. https://public-inbox.org/git/CACsJy8AcUy9FZiiehGc7mEL4i+XP6u0pmH1rGoR-WZnhYT1UMQ@mail.gmail.com/
Jiang Xin Nov. 1, 2018, 1:36 a.m. UTC | #7
Ævar Arnfjörð Bjarmason <avarab@gmail.com> 于2018年10月30日周二 上午1:43写道:
> On Mon, Oct 29 2018, Ævar Arnfjörð Bjarmason wrote:
> Unlike the rest of our stack where we need to support however many years
> old tools we can freely rely on bleeding-edge gettext features, since
> the only person we need to convince to upgrade is Jiang. I.e. he accepts
> the PRs from translators, (presumably) runs msgfmt --check and then
> submits the result to Junio.

I used a shell script to check commits before I send a pull request to Junio.
This script is in the po-helper branch, see:
https://github.com/git-l10n/git-po/tree/po-helper

It can catch unmatched '\n' errors (missing or unnecessary '\n').

    $ git diff
   diff --git a/po/zh_CN.po b/po/zh_CN.po
   index eabd4d1f8e..6b0d9ebc60 100644
   --- a/po/zh_CN.po
   +++ b/po/zh_CN.po
   @@ -5157,7 +5157,7 @@ msgstr  "略过补丁 '%s'。"
    #
    #, perl-format
    msgid   "Skipping %s with backup suffix '%s'.\n"
   -msgstr  "略过 %s 含备份后缀 '%s'。\n"
   +msgstr  "略过 %s 含备份后缀 '%s'。"
    #
    #, c-format
    msgid   "Skipping repository %s\n"

   $ LC_ALL=C po-helper.sh check
   ------------------------------------------------------------
   bg.po     : 3958 translated messages.
   ca.po     : 3328 translated messages, 18 fuzzy translations, 30
untranslated messages.
   de.po     : 3958 translated messages.
   es.po     : 3958 translated messages.
   fr.po     : 3957 translated messages, 1 fuzzy translation.
   is.po     : 14 translated messages.
   it.po     : 716 translated messages, 350 untranslated messages.
   ko.po     : 3608 translated messages.
   pt_PT.po  : 3198 translated messages.
   ru.po     : 3366 translated messages, 594 untranslated messages.
   sv.po     : 3958 translated messages.
   vi.po     : 3958 translated messages.
   zh_CN.po  : po/zh_CN.po:19717: 'msgid' and 'msgstr' entries do not
both end with '\n'
   msgfmt: found 1 fatal error
   3958 translated messages.
   ------------------------------------------------------------
   Show updates of git.pot...

   # Nothing changed. (run 'make pot' first)
   ------------------------------------------------------------
   Check commits...

   0 commits checked complete.
   ------------------------------------------------------------
   Note: If you want to check for upstream l10n update, run:
   Note:
   Note:     po-helper.sh check update <remote>
   ------------------------------------------------------------

So, no warry about it. BTW, I agree with Jonathan.

> Jonathan said:
> IMHO the advantage of leaving the \n out in the message is not so much
> that we don't trust translators but more that where we can make their
> lives easier, we should.

--
Jiang Xin
Duy Nguyen Nov. 5, 2018, 5:21 p.m. UTC | #8
On Mon, Oct 29, 2018 at 12:53 PM SZEDER Gábor <szeder.dev@gmail.com> wrote:
> The contents of 'out':
>
>   broken link from    tree be45bbd3809e0829297cefa576e699c134abacfd (refs/heads/master@{1112912113}:caesar.t)
>                 to    blob be45bbd3809e0829297cefa576e699c134abacfd (refs/heads/master@{1112912113}:caesar.t)
>   missing blob be45bbd3809e0829297cefa576e699c134abacfd (refs/heads/master@{1112912113}:caesar.t)
>
> On a related (side)note, I think it would be better if this 'grep'

I found the problem. Some function returns static string which works
fine when we do two printf()'s, one call of that function per
printf(). But when combining two printf() in one, we have a problem.
Thanks for catching.

Patch
diff mbox series

diff --git a/builtin/fsck.c b/builtin/fsck.c
index 06eb421720..13f8fe35c5 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -108,8 +108,9 @@  static int fsck_config(const char *var, const char *value, void *cb)
 static void objreport(struct object *obj, const char *msg_type,
 			const char *err)
 {
-	fprintf(stderr, "%s in %s %s: %s\n",
-		msg_type, printable_type(obj), describe_object(obj), err);
+	fprintf_ln(stderr, _("%s in %s %s: %s"),
+		   msg_type, printable_type(obj),
+		   describe_object(obj), err);
 }
 
 static int objerror(struct object *obj, const char *err)
@@ -139,17 +140,18 @@  static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
 	 */
 	if (!obj) {
 		/* ... these references to parent->fld are safe here */
-		printf("broken link from %7s %s\n",
-			   printable_type(parent), describe_object(parent));
-		printf("broken link from %7s %s\n",
-			   (type == OBJ_ANY ? "unknown" : type_name(type)), "unknown");
+		printf_ln(_("broken link from %7s %s"),
+			  printable_type(parent), describe_object(parent));
+		printf_ln(_("broken link from %7s %s"),
+			  (type == OBJ_ANY ? _("unknown") : type_name(type)),
+			  _("unknown"));
 		errors_found |= ERROR_REACHABLE;
 		return 1;
 	}
 
 	if (type != OBJ_ANY && obj->type != type)
 		/* ... and the reference to parent is safe here */
-		objerror(parent, "wrong object type in link");
+		objerror(parent, _("wrong object type in link"));
 
 	if (obj->flags & REACHABLE)
 		return 0;
@@ -165,10 +167,12 @@  static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
 
 	if (!(obj->flags & HAS_OBJ)) {
 		if (parent && !has_object_file(&obj->oid)) {
-			printf("broken link from %7s %s\n",
-				 printable_type(parent), describe_object(parent));
-			printf("              to %7s %s\n",
-				 printable_type(obj), describe_object(obj));
+			printf_ln(_("broken link from %7s %s\n"
+				    "              to %7s %s"),
+				  printable_type(parent),
+				  describe_object(parent),
+				  printable_type(obj),
+				  describe_object(obj));
 			errors_found |= ERROR_REACHABLE;
 		}
 		return 1;
@@ -232,8 +236,8 @@  static void check_reachable_object(struct object *obj)
 			return;
 		if (has_object_pack(&obj->oid))
 			return; /* it is in pack - forget about it */
-		printf("missing %s %s\n", printable_type(obj),
-			describe_object(obj));
+		printf_ln(_("missing %s %s"), printable_type(obj),
+			  describe_object(obj));
 		errors_found |= ERROR_REACHABLE;
 		return;
 	}
@@ -258,8 +262,8 @@  static void check_unreachable_object(struct object *obj)
 	 * since this is something that is prunable.
 	 */
 	if (show_unreachable) {
-		printf("unreachable %s %s\n", printable_type(obj),
-			describe_object(obj));
+		printf_ln(_("unreachable %s %s"), printable_type(obj),
+			  describe_object(obj));
 		return;
 	}
 
@@ -277,8 +281,8 @@  static void check_unreachable_object(struct object *obj)
 	 */
 	if (!(obj->flags & USED)) {
 		if (show_dangling)
-			printf("dangling %s %s\n", printable_type(obj),
-			       describe_object(obj));
+			printf_ln(_("dangling %s %s"), printable_type(obj),
+				  describe_object(obj));
 		if (write_lost_and_found) {
 			char *filename = git_pathdup("lost-found/%s/%s",
 				obj->type == OBJ_COMMIT ? "commit" : "other",
@@ -286,18 +290,18 @@  static void check_unreachable_object(struct object *obj)
 			FILE *f;
 
 			if (safe_create_leading_directories_const(filename)) {
-				error("Could not create lost-found");
+				error(_("could not create lost-found"));
 				free(filename);
 				return;
 			}
 			f = xfopen(filename, "w");
 			if (obj->type == OBJ_BLOB) {
 				if (stream_blob_to_fd(fileno(f), &obj->oid, NULL, 1))
-					die_errno("Could not write '%s'", filename);
+					die_errno(_("could not write '%s'"), filename);
 			} else
 				fprintf(f, "%s\n", describe_object(obj));
 			if (fclose(f))
-				die_errno("Could not finish '%s'",
+				die_errno(_("could not finish '%s'"),
 					  filename);
 			free(filename);
 		}
@@ -314,7 +318,7 @@  static void check_unreachable_object(struct object *obj)
 static void check_object(struct object *obj)
 {
 	if (verbose)
-		fprintf(stderr, "Checking %s\n", describe_object(obj));
+		fprintf_ln(stderr, _("Checking %s"), describe_object(obj));
 
 	if (obj->flags & REACHABLE)
 		check_reachable_object(obj);
@@ -332,7 +336,7 @@  static void check_connectivity(void)
 	/* Look up all the requirements, warn about missing objects.. */
 	max = get_max_object_index();
 	if (verbose)
-		fprintf(stderr, "Checking connectivity (%d objects)\n", max);
+		fprintf_ln(stderr, _("Checking connectivity (%d objects)"), max);
 
 	for (i = 0; i < max; i++) {
 		struct object *obj = get_indexed_object(i);
@@ -351,11 +355,11 @@  static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
 	obj->flags |= SEEN;
 
 	if (verbose)
-		fprintf(stderr, "Checking %s %s\n",
-			printable_type(obj), describe_object(obj));
+		fprintf_ln(stderr, _("Checking %s %s"),
+			   printable_type(obj), describe_object(obj));
 
 	if (fsck_walk(obj, NULL, &fsck_obj_options))
-		objerror(obj, "broken links");
+		objerror(obj, _("broken links"));
 	err = fsck_object(obj, buffer, size, &fsck_obj_options);
 	if (err)
 		goto out;
@@ -364,17 +368,19 @@  static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
 		struct commit *commit = (struct commit *) obj;
 
 		if (!commit->parents && show_root)
-			printf("root %s\n", describe_object(&commit->object));
+			printf_ln(_("root %s"),
+				  describe_object(&commit->object));
 	}
 
 	if (obj->type == OBJ_TAG) {
 		struct tag *tag = (struct tag *) obj;
 
 		if (show_tags && tag->tagged) {
-			printf("tagged %s %s", printable_type(tag->tagged),
-				describe_object(tag->tagged));
-			printf(" (%s) in %s\n", tag->tag,
-				describe_object(&tag->object));
+			printf(_("tagged %s %s (%s) in %s"),
+			       printable_type(tag->tagged),
+			       describe_object(tag->tagged),
+			       tag->tag,
+			       describe_object(&tag->object));
 		}
 	}
 
@@ -398,7 +404,8 @@  static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
 				  eaten);
 	if (!obj) {
 		errors_found |= ERROR_OBJECT;
-		return error("%s: object corrupt or missing", oid_to_hex(oid));
+		return error(_("%s: object corrupt or missing"),
+			     oid_to_hex(oid));
 	}
 	obj->flags &= ~(REACHABLE | SEEN);
 	obj->flags |= HAS_OBJ;
@@ -422,7 +429,8 @@  static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
 			obj->flags |= USED;
 			mark_object_reachable(obj);
 		} else if (!is_promisor_object(oid)) {
-			error("%s: invalid reflog entry %s", refname, oid_to_hex(oid));
+			error(_("%s: invalid reflog entry %s"),
+			      refname, oid_to_hex(oid));
 			errors_found |= ERROR_REACHABLE;
 		}
 	}
@@ -435,8 +443,8 @@  static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid
 	const char *refname = cb_data;
 
 	if (verbose)
-		fprintf(stderr, "Checking reflog %s->%s\n",
-			oid_to_hex(ooid), oid_to_hex(noid));
+		fprintf_ln(stderr, _("Checking reflog %s->%s"),
+			   oid_to_hex(ooid), oid_to_hex(noid));
 
 	fsck_handle_reflog_oid(refname, ooid, 0);
 	fsck_handle_reflog_oid(refname, noid, timestamp);
@@ -465,13 +473,14 @@  static int fsck_handle_ref(const char *refname, const struct object_id *oid,
 			 default_refs++;
 			 return 0;
 		}
-		error("%s: invalid sha1 pointer %s", refname, oid_to_hex(oid));
+		error(_("%s: invalid sha1 pointer %s"),
+		      refname, oid_to_hex(oid));
 		errors_found |= ERROR_REACHABLE;
 		/* We'll continue with the rest despite the error.. */
 		return 0;
 	}
 	if (obj->type != OBJ_COMMIT && is_branch(refname)) {
-		error("%s: not a commit", refname);
+		error(_("%s: not a commit"), refname);
 		errors_found |= ERROR_REFS;
 	}
 	default_refs++;
@@ -505,7 +514,7 @@  static void get_default_heads(void)
 	 * "show_unreachable" flag.
 	 */
 	if (!default_refs) {
-		fprintf(stderr, "notice: No default references\n");
+		fprintf_ln(stderr, _("notice: No default references"));
 		show_unreachable = 0;
 	}
 }
@@ -520,7 +529,7 @@  static int fsck_loose(const struct object_id *oid, const char *path, void *data)
 
 	if (read_loose_object(path, oid, &type, &size, &contents) < 0) {
 		errors_found |= ERROR_OBJECT;
-		error("%s: object corrupt or missing: %s",
+		error(_("%s: object corrupt or missing: %s"),
 		      oid_to_hex(oid), path);
 		return 0; /* keep checking other objects */
 	}
@@ -533,7 +542,7 @@  static int fsck_loose(const struct object_id *oid, const char *path, void *data)
 
 	if (!obj) {
 		errors_found |= ERROR_OBJECT;
-		error("%s: object could not be parsed: %s",
+		error(_("%s: object could not be parsed: %s"),
 		      oid_to_hex(oid), path);
 		if (!eaten)
 			free(contents);
@@ -553,7 +562,7 @@  static int fsck_loose(const struct object_id *oid, const char *path, void *data)
 static int fsck_cruft(const char *basename, const char *path, void *data)
 {
 	if (!starts_with(basename, "tmp_obj_"))
-		fprintf(stderr, "bad sha1 file: %s\n", path);
+		fprintf_ln(stderr, _("bad sha1 file: %s"), path);
 	return 0;
 }
 
@@ -568,7 +577,7 @@  static void fsck_object_dir(const char *path)
 	struct progress *progress = NULL;
 
 	if (verbose)
-		fprintf(stderr, "Checking object directory\n");
+		fprintf_ln(stderr, _("Checking object directory"));
 
 	if (show_progress)
 		progress = start_progress(_("Checking object directories"), 256);
@@ -584,28 +593,28 @@  static int fsck_head_link(void)
 	int null_is_error = 0;
 
 	if (verbose)
-		fprintf(stderr, "Checking HEAD link\n");
+		fprintf_ln(stderr, _("Checking HEAD link"));
 
 	head_points_at = resolve_ref_unsafe("HEAD", 0, &head_oid, NULL);
 	if (!head_points_at) {
 		errors_found |= ERROR_REFS;
-		return error("Invalid HEAD");
+		return error(_("invalid HEAD"));
 	}
 	if (!strcmp(head_points_at, "HEAD"))
 		/* detached HEAD */
 		null_is_error = 1;
 	else if (!starts_with(head_points_at, "refs/heads/")) {
 		errors_found |= ERROR_REFS;
-		return error("HEAD points to something strange (%s)",
+		return error(_("HEAD points to something strange (%s)"),
 			     head_points_at);
 	}
 	if (is_null_oid(&head_oid)) {
 		if (null_is_error) {
 			errors_found |= ERROR_REFS;
-			return error("HEAD: detached HEAD points at nothing");
+			return error(_("HEAD: detached HEAD points at nothing"));
 		}
-		fprintf(stderr, "notice: HEAD points to an unborn branch (%s)\n",
-			head_points_at + 11);
+		fprintf_ln(stderr, _("notice: HEAD points to an unborn branch (%s)"),
+			   head_points_at + 11);
 	}
 	return 0;
 }
@@ -616,12 +625,12 @@  static int fsck_cache_tree(struct cache_tree *it)
 	int err = 0;
 
 	if (verbose)
-		fprintf(stderr, "Checking cache tree\n");
+		fprintf_ln(stderr, _("Checking cache tree"));
 
 	if (0 <= it->entry_count) {
 		struct object *obj = parse_object(the_repository, &it->oid);
 		if (!obj) {
-			error("%s: invalid sha1 pointer in cache-tree",
+			error(_("%s: invalid sha1 pointer in cache-tree"),
 			      oid_to_hex(&it->oid));
 			errors_found |= ERROR_REFS;
 			return 1;
@@ -632,7 +641,7 @@  static int fsck_cache_tree(struct cache_tree *it)
 				obj, xstrdup(":"));
 		mark_object_reachable(obj);
 		if (obj->type != OBJ_TREE)
-			err |= objerror(obj, "non-tree in cache-tree");
+			err |= objerror(obj, _("non-tree in cache-tree"));
 	}
 	for (i = 0; i < it->subtree_nr; i++)
 		err |= fsck_cache_tree(it->down[i]->cache_tree);
@@ -774,7 +783,7 @@  int cmd_fsck(int argc, const char **argv, const char *prefix)
 			if (!obj || !(obj->flags & HAS_OBJ)) {
 				if (is_promisor_object(&oid))
 					continue;
-				error("%s: object missing", oid_to_hex(&oid));
+				error(_("%s: object missing"), oid_to_hex(&oid));
 				errors_found |= ERROR_OBJECT;
 				continue;
 			}
@@ -786,7 +795,7 @@  int cmd_fsck(int argc, const char **argv, const char *prefix)
 			mark_object_reachable(obj);
 			continue;
 		}
-		error("invalid parameter: expected sha1, got '%s'", arg);
+		error(_("invalid parameter: expected sha1, got '%s'"), arg);
 		errors_found |= ERROR_OBJECT;
 	}
 
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index 388b0611d8..353bdfd415 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -20,12 +20,12 @@  check_have () {
 }
 
 check_fsck () {
-	output=$(git fsck --full)
+	git fsck --full >fsck.output
 	case "$1" in
 	'')
-		test -z "$output" ;;
+		test_must_be_empty fsck.output ;;
 	*)
-		echo "$output" | grep "$1" ;;
+		test_i18ngrep "$1" fsck.output ;;
 	esac
 }
 
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index 90c765df3a..500c21366e 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -70,7 +70,7 @@  test_expect_success 'object with bad sha1' '
 
 	test_must_fail git fsck 2>out &&
 	cat out &&
-	grep "$sha.*corrupt" out
+	test_i18ngrep "$sha.*corrupt" out
 '
 
 test_expect_success 'branch pointing to non-commit' '
@@ -78,7 +78,7 @@  test_expect_success 'branch pointing to non-commit' '
 	test_when_finished "git update-ref -d refs/heads/invalid" &&
 	test_must_fail git fsck 2>out &&
 	cat out &&
-	grep "not a commit" out
+	test_i18ngrep "not a commit" out
 '
 
 test_expect_success 'HEAD link pointing at a funny object' '
@@ -88,7 +88,7 @@  test_expect_success 'HEAD link pointing at a funny object' '
 	# avoid corrupt/broken HEAD from interfering with repo discovery
 	test_must_fail env GIT_DIR=.git git fsck 2>out &&
 	cat out &&
-	grep "detached HEAD points" out
+	test_i18ngrep "detached HEAD points" out
 '
 
 test_expect_success 'HEAD link pointing at a funny place' '
@@ -98,7 +98,7 @@  test_expect_success 'HEAD link pointing at a funny place' '
 	# avoid corrupt/broken HEAD from interfering with repo discovery
 	test_must_fail env GIT_DIR=.git git fsck 2>out &&
 	cat out &&
-	grep "HEAD points to something strange" out
+	test_i18ngrep "HEAD points to something strange" out
 '
 
 test_expect_success 'email without @ is okay' '
@@ -122,7 +122,7 @@  test_expect_success 'email with embedded > is not okay' '
 	test_when_finished "git update-ref -d refs/heads/bogus" &&
 	test_must_fail git fsck 2>out &&
 	cat out &&
-	grep "error in commit $new" out
+	test_i18ngrep "error in commit $new" out
 '
 
 test_expect_success 'missing < email delimiter is reported nicely' '
@@ -134,7 +134,7 @@  test_expect_success 'missing < email delimiter is reported nicely' '
 	test_when_finished "git update-ref -d refs/heads/bogus" &&
 	test_must_fail git fsck 2>out &&
 	cat out &&
-	grep "error in commit $new.* - bad name" out
+	test_i18ngrep "error in commit $new.* - bad name" out
 '
 
 test_expect_success 'missing email is reported nicely' '
@@ -146,7 +146,7 @@  test_expect_success 'missing email is reported nicely' '
 	test_when_finished "git update-ref -d refs/heads/bogus" &&
 	test_must_fail git fsck 2>out &&
 	cat out &&
-	grep "error in commit $new.* - missing email" out
+	test_i18ngrep "error in commit $new.* - missing email" out
 '
 
 test_expect_success '> in name is reported' '
@@ -158,7 +158,7 @@  test_expect_success '> in name is reported' '
 	test_when_finished "git update-ref -d refs/heads/bogus" &&
 	test_must_fail git fsck 2>out &&
 	cat out &&
-	grep "error in commit $new" out
+	test_i18ngrep "error in commit $new" out
 '
 
 # date is 2^64 + 1
@@ -172,7 +172,7 @@  test_expect_success 'integer overflow in timestamps is reported' '
 	test_when_finished "git update-ref -d refs/heads/bogus" &&
 	test_must_fail git fsck 2>out &&
 	cat out &&
-	grep "error in commit $new.*integer overflow" out
+	test_i18ngrep "error in commit $new.*integer overflow" out
 '
 
 test_expect_success 'commit with NUL in header' '
@@ -184,7 +184,7 @@  test_expect_success 'commit with NUL in header' '
 	test_when_finished "git update-ref -d refs/heads/bogus" &&
 	test_must_fail git fsck 2>out &&
 	cat out &&
-	grep "error in commit $new.*unterminated header: NUL at offset" out
+	test_i18ngrep "error in commit $new.*unterminated header: NUL at offset" out
 '
 
 test_expect_success 'tree object with duplicate entries' '
@@ -205,7 +205,7 @@  test_expect_success 'tree object with duplicate entries' '
 		git hash-object -w -t tree --stdin
 	) &&
 	test_must_fail git fsck 2>out &&
-	grep "error in tree .*contains duplicate file entries" out
+	test_i18ngrep "error in tree .*contains duplicate file entries" out
 '
 
 test_expect_success 'unparseable tree object' '
@@ -259,7 +259,7 @@  test_expect_success 'tag pointing to nonexistent' '
 	test_when_finished "git update-ref -d refs/tags/invalid" &&
 	test_must_fail git fsck --tags >out &&
 	cat out &&
-	grep "broken link" out
+	test_i18ngrep "broken link" out
 '
 
 test_expect_success 'tag pointing to something else than its type' '
@@ -301,7 +301,7 @@  test_expect_success 'tag with incorrect tag name & missing tagger' '
 	warning in tag $tag: badTagName: invalid '\''tag'\'' name: wrong name format
 	warning in tag $tag: missingTaggerEntry: invalid format - expected '\''tagger'\'' line
 	EOF
-	test_cmp expect out
+	test_i18ncmp expect out
 '
 
 test_expect_success 'tag with bad tagger' '
@@ -320,7 +320,7 @@  test_expect_success 'tag with bad tagger' '
 	echo $tag >.git/refs/tags/wrong &&
 	test_when_finished "git update-ref -d refs/tags/wrong" &&
 	test_must_fail git fsck --tags 2>out &&
-	grep "error in tag .*: invalid author/committer" out
+	test_i18ngrep "error in tag .*: invalid author/committer" out
 '
 
 test_expect_success 'tag with NUL in header' '
@@ -340,7 +340,7 @@  test_expect_success 'tag with NUL in header' '
 	test_when_finished "git update-ref -d refs/tags/wrong" &&
 	test_must_fail git fsck --tags 2>out &&
 	cat out &&
-	grep "error in tag $tag.*unterminated header: NUL at offset" out
+	test_i18ngrep "error in tag $tag.*unterminated header: NUL at offset" out
 '
 
 test_expect_success 'cleaned up' '
@@ -396,7 +396,7 @@  test_expect_success 'fsck notices blob entry pointing to null sha1' '
 	       git hash-object -w --stdin -t tree) &&
 	  git fsck 2>out &&
 	  cat out &&
-	  grep "warning.*null sha1" out
+	  test_i18ngrep "warning.*null sha1" out
 	)
 '
 
@@ -407,7 +407,7 @@  test_expect_success 'fsck notices submodule entry pointing to null sha1' '
 	       git hash-object -w --stdin -t tree) &&
 	  git fsck 2>out &&
 	  cat out &&
-	  grep "warning.*null sha1" out
+	  test_i18ngrep "warning.*null sha1" out
 	)
 '
 
@@ -428,7 +428,7 @@  while read name path pretty; do
 			bad_tree=$(git mktree <bad) &&
 			git fsck 2>out &&
 			cat out &&
-			grep "warning.*tree $bad_tree" out
+			test_i18ngrep "warning.*tree $bad_tree" out
 		)'
 	done <<-\EOF
 	100644 blob
@@ -474,9 +474,9 @@  test_expect_success 'NUL in commit' '
 		git branch bad $(cat name) &&
 
 		test_must_fail git -c fsck.nulInCommit=error fsck 2>warn.1 &&
-		grep nulInCommit warn.1 &&
+		test_i18ngrep nulInCommit warn.1 &&
 		git fsck 2>warn.2 &&
-		grep nulInCommit warn.2
+		test_i18ngrep nulInCommit warn.2
 	)
 '
 
@@ -594,7 +594,7 @@  test_expect_success 'fsck --name-objects' '
 		remove_object $(git rev-parse julius:caesar.t) &&
 		test_must_fail git fsck --name-objects >out &&
 		tree=$(git rev-parse --verify julius:) &&
-		egrep "$tree \((refs/heads/master|HEAD)@\{[0-9]*\}:" out
+		test -n "$GETTEXT_POISON" || egrep "$tree \((refs/heads/master|HEAD)@\{[0-9]*\}:" out
 	)
 '
 
@@ -605,7 +605,7 @@  test_expect_success 'alternate objects are correctly blamed' '
 	mkdir alt.git/objects/12 &&
 	>alt.git/objects/12/34567890123456789012345678901234567890 &&
 	test_must_fail git fsck >out 2>&1 &&
-	grep alt.git out
+	test_i18ngrep alt.git out
 '
 
 test_expect_success 'fsck errors in packed objects' '
@@ -624,8 +624,8 @@  test_expect_success 'fsck errors in packed objects' '
 	remove_object $one &&
 	remove_object $two &&
 	test_must_fail git fsck 2>out &&
-	grep "error in commit $one.* - bad name" out &&
-	grep "error in commit $two.* - bad name" out &&
+	test_i18ngrep "error in commit $one.* - bad name" out &&
+	test_i18ngrep "error in commit $two.* - bad name" out &&
 	! grep corrupt out
 '
 
@@ -706,7 +706,7 @@  test_expect_success 'fsck notices dangling objects' '
 		git fsck >actual &&
 		# the output order is non-deterministic, as it comes from a hash
 		sort <actual >actual.sorted &&
-		test_cmp expect actual.sorted
+		test_i18ncmp expect actual.sorted
 	)
 '
 
diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh
index 86374a9c52..7700a8e793 100755
--- a/t/t6050-replace.sh
+++ b/t/t6050-replace.sh
@@ -133,8 +133,8 @@  test_expect_success 'tag replaced commit' '
 
 test_expect_success '"git fsck" works' '
      git fsck master >fsck_master.out &&
-     grep "dangling commit $R" fsck_master.out &&
-     grep "dangling tag $(cat .git/refs/tags/mytag)" fsck_master.out &&
+     test_i18ngrep "dangling commit $R" fsck_master.out &&
+     test_i18ngrep "dangling tag $(cat .git/refs/tags/mytag)" fsck_master.out &&
      test -z "$(git fsck)"
 '
 
diff --git a/t/t7415-submodule-names.sh b/t/t7415-submodule-names.sh
index 293e2e1963..49a37efe9c 100755
--- a/t/t7415-submodule-names.sh
+++ b/t/t7415-submodule-names.sh
@@ -154,7 +154,7 @@  test_expect_success 'fsck detects symlinked .gitmodules file' '
 		# symlink detector; this grep string comes from the config
 		# variable name and will not be translated.
 		test_must_fail git fsck 2>output &&
-		grep gitmodulesSymlink output
+		test_i18ngrep gitmodulesSymlink output
 	)
 '
 
@@ -172,7 +172,7 @@  test_expect_success 'fsck detects non-blob .gitmodules' '
 		git ls-tree HEAD | sed s/subdir/.gitmodules/ | git mktree &&
 
 		test_must_fail git fsck 2>output &&
-		grep gitmodulesBlob output
+		test_i18ngrep gitmodulesBlob output
 	)
 '
 
@@ -186,7 +186,7 @@  test_expect_success 'fsck detects corrupt .gitmodules' '
 		git commit -m "broken gitmodules" &&
 
 		git fsck 2>output &&
-		grep gitmodulesParse output &&
+		test_i18ngrep gitmodulesParse output &&
 		test_i18ngrep ! "bad config" output
 	)
 '