Re: Bug report: $program_name in error message

2016-12-20 Thread Vasco Almeida
Thanks for the report and letting me know.
Yes, these were mistakes and lack of attention mine. It was supposed to
call 'eval_gettext' rather than 'gettext' when \$variable interpolation
is needed. Junio Hamano has the right answer for these errors.

A Seg, 19-12-2016 às 12:50 -0800, Junio C Hamano escreveu:
> Subject: rebase -i: fix mistaken i18n
> 
> f2d17068fd ("i18n: rebase-interactive: mark comments of squash for
> translation", 2016-06-17) attempted to apply sh-i18n and failed to
> use $(eval_gettext "string with \$variable interpolation").
> 
> Signed-off-by: Junio C Hamano 
> ---
>  git-rebase--interactive.sh | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
> index 41fd374c72..96865b2375 100644
> --- a/git-rebase--interactive.sh
> +++ b/git-rebase--interactive.sh
> @@ -437,7 +437,8 @@ update_squash_messages () {
> }' <"$squash_msg".bak
> } >"$squash_msg"
> else
> -   commit_message HEAD > "$fixup_msg" || die "$(gettext "Cannot 
> write \$fixup_msg")"
> +   commit_message HEAD >"$fixup_msg" ||
> +   die "$(eval_gettext "Cannot write \$fixup_msg")"
> count=2
> {
> printf '%s\n' "$comment_char $(gettext "This is a 
> combination of 2 commits.")"

I agree with this fix. Perhaps indent the second line to be easier on
the eyes?:

> +   commit_message HEAD >"$fixup_msg" ||
> +     die "$(eval_gettext "Cannot write \$fixup_msg")"


[PATCH v7 15/16] i18n: send-email: mark composing message for translation

2016-12-14 Thread Vasco Almeida
When composing an e-mail, there is a message for the user whose lines
begin in "GIT:" that can be marked for translation.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-send-email.perl | 16 +---
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/git-send-email.perl b/git-send-email.perl
index 7f3297cdf..068d60b3e 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -672,18 +672,20 @@ if ($compose) {
my $tpl_subject = $initial_subject || '';
my $tpl_reply_to = $initial_reply_to || '';
 
-   print $c <<EOT;
+   print $c <<EOT1, Git::prefix_lines("GIT: ", __ <<EOT2), <<EOT3;
 From $tpl_sender # This line is ignored.
-GIT: Lines beginning in "GIT:" will be removed.
-GIT: Consider including an overall diffstat or table of contents
-GIT: for the patch you are writing.
-GIT:
-GIT: Clear the body content if you don't wish to send a summary.
+EOT1
+Lines beginning in "GIT:" will be removed.
+Consider including an overall diffstat or table of contents
+for the patch you are writing.
+
+Clear the body content if you don't wish to send a summary.
+EOT2
 From: $tpl_sender
 Subject: $tpl_subject
 In-Reply-To: $tpl_reply_to
 
-EOT
+EOT3
for my $f (@files) {
print $c get_patch_subject($f);
}
-- 
2.11.0.44.g7d42c6c



[PATCH v7 02/16] i18n: add--interactive: mark strings for translation

2016-12-14 Thread Vasco Almeida
Mark simple strings (without interpolation) for translation.

Brackets around first parameter of ternary operator is necessary because
otherwise xgettext fails to extract strings marked for translation from
the rest of the file.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 76 ++-
 1 file changed, 42 insertions(+), 34 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index ee3d81269..cf216ecb6 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -4,6 +4,7 @@ use 5.008;
 use strict;
 use warnings;
 use Git;
+use Git::I18N;
 
 binmode(STDOUT, ":raw");
 
@@ -253,8 +254,9 @@ sub list_untracked {
run_cmd_pipe(qw(git ls-files --others --exclude-standard --), @ARGV);
 }
 
-my $status_fmt = '%12s %12s %s';
-my $status_head = sprintf($status_fmt, 'staged', 'unstaged', 'path');
+# TRANSLATORS: you can adjust this to align "git add -i" status menu
+my $status_fmt = __('%12s %12s %s');
+my $status_head = sprintf($status_fmt, __('staged'), __('unstaged'), 
__('path'));
 
 {
my $initial;
@@ -680,7 +682,7 @@ sub update_cmd {
my @mods = list_modified('file-only');
return if (!@mods);
 
-   my @update = list_and_choose({ PROMPT => 'Update',
+   my @update = list_and_choose({ PROMPT => __('Update'),
   HEADER => $status_head, },
 @mods);
if (@update) {
@@ -692,7 +694,7 @@ sub update_cmd {
 }
 
 sub revert_cmd {
-   my @update = list_and_choose({ PROMPT => 'Revert',
+   my @update = list_and_choose({ PROMPT => __('Revert'),
   HEADER => $status_head, },
 list_modified());
if (@update) {
@@ -726,13 +728,13 @@ sub revert_cmd {
 }
 
 sub add_untracked_cmd {
-   my @add = list_and_choose({ PROMPT => 'Add untracked' },
+   my @add = list_and_choose({ PROMPT => __('Add untracked') },
  list_untracked());
if (@add) {
system(qw(git update-index --add --), @add);
say_n_paths('added', @add);
} else {
-   print "No untracked files.\n";
+   print __("No untracked files.\n");
}
print "\n";
 }
@@ -1166,8 +1168,14 @@ sub edit_hunk_loop {
}
else {
prompt_yesno(
-   'Your edited hunk does not apply. Edit again '
-   . '(saying "no" discards!) [y/n]? '
+   # TRANSLATORS: do not translate [y/n]
+   # The program will only accept that input
+   # at this point.
+   # Consider translating (saying "no" discards!) 
as
+   # (saying "n" for "no" discards!) if the 
translation
+   # of the word "no" does not start with n.
+   __('Your edited hunk does not apply. Edit again 
'
+  . '(saying "no" discards!) [y/n]? ')
) or return undef;
}
}
@@ -1213,11 +1221,11 @@ sub apply_patch_for_checkout_commit {
run_git_apply 'apply '.$reverse, @_;
return 1;
} elsif (!$applies_index) {
-   print colored $error_color, "The selected hunks do not apply to 
the index!\n";
-   if (prompt_yesno "Apply them to the worktree anyway? ") {
+   print colored $error_color, __("The selected hunks do not apply 
to the index!\n");
+   if (prompt_yesno __("Apply them to the worktree anyway? ")) {
return run_git_apply 'apply '.$reverse, @_;
} else {
-   print colored $error_color, "Nothing was applied.\n";
+   print colored $error_color, __("Nothing was 
applied.\n");
return 0;
}
} else {
@@ -1237,9 +1245,9 @@ sub patch_update_cmd {
 
if (!@mods) {
if (@all_mods) {
-   print STDERR "Only binary files changed.\n";
+   print STDERR __("Only binary files changed.\n");
} else {
-   print STDERR "No changes.\n";
+   print STDERR __("No changes.\n");
}
return 0;
}
@@ -1247,7 +1255,7 @@ sub patch_update_cmd {
@them = @mods;
}
else {
-   @them = list_and_choose({ PROMPT =>

[PATCH v7 00/16] Mark strings in Perl scripts for translation

2016-12-14 Thread Vasco Almeida
Changes is this re-roll v7:
* Add get_comment_line_char subroutine to perl/Git.pm and use it.
* get_comment_line_char gets the value of core.commentchar configuration
  variable. It handles the 'auto' value taking '#' in this case as the
  comment line character.
* When core.commentchar is not set to one single character, take '#' as the
  comment line character. This follows the system behaviour programmed in
  config.c::git_default_core_config.

Interdiff from v6 included below.

Vasco Almeida (16):
  Git.pm: add subroutines for commenting lines
  i18n: add--interactive: mark strings for translation
  i18n: add--interactive: mark simple here-documents for translation
  i18n: add--interactive: mark strings with interpolation for
translation
  i18n: clean.c: match string with git-add--interactive.perl
  i18n: add--interactive: mark plural strings
  i18n: add--interactive: mark patch prompt for translation
  i18n: add--interactive: i18n of help_patch_cmd
  i18n: add--interactive: mark edit_hunk_manually message for
translation
  i18n: add--interactive: remove %patch_modes entries
  i18n: add--interactive: mark status words for translation
  i18n: send-email: mark strings for translation
  i18n: send-email: mark warnings and errors for translation
  i18n: send-email: mark string with interpolation for translation
  i18n: send-email: mark composing message for translation
  i18n: difftool: mark warnings for translation

 Makefile  |   3 +-
 builtin/clean.c   |  10 +-
 git-add--interactive.perl | 329 ++
 git-difftool.perl |  22 ++--
 git-send-email.perl   | 191 +++
 perl/Git.pm   |  38 ++
 perl/Git/I18N.pm  |  19 ++-
 t/t0202/test.pl   |  14 +-
 8 files changed, 408 insertions(+), 218 deletions(-)

-- >8 --
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 3a6d846..4e0ab5a 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -1072,7 +1072,7 @@ sub edit_hunk_manually {
print $fh @$oldtext;
my $is_reverse = $patch_mode_flavour{IS_REVERSE};
my ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') : ('+', 
'-');
-   my $comment_line_char = Git::config("core.commentchar") || '#';
+   my $comment_line_char = Git::get_comment_line_char;
print $fh Git::comment_lines sprintf(__ <<EOF, $remove_minus, 
$remove_plus, $comment_line_char),
 ---
 To remove '%s' lines, make them ' ' lines (context).
diff --git a/perl/Git.pm b/perl/Git.pm
index 69cd1dd..bfce1f7 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -1451,6 +1451,20 @@ sub prefix_lines {
return $string;
 }
 
+=item get_comment_line_char ( )
+
+Gets the core.commentchar configuration value.
+The value falls-back to '#' if core.commentchar is set to 'auto'.
+
+=cut
+
+sub get_comment_line_char {
+   my $comment_line_char = config("core.commentchar") || '#';
+   $comment_line_char = '#' if ($comment_line_char eq 'auto');
+   $comment_line_char = '#' if (length($comment_line_char) != 1);
+   return $comment_line_char;
+}
+
 =item comment_lines ( STRING [, STRING... ])
 
 Comments lines following core.commentchar configuration.
@@ -1458,7 +1472,7 @@ Comments lines following core.commentchar configuration.
 =cut
 
 sub comment_lines {
-   my $comment_line_char = config("core.commentchar") || '#';
+   my $comment_line_char = get_comment_line_char;
return prefix_lines("$comment_line_char ", @_);
 }
 
-- >8 --

-- 
2.11.0.44.g7d42c6c



[PATCH v7 07/16] i18n: add--interactive: mark patch prompt for translation

2016-12-14 Thread Vasco Almeida
Mark prompt message assembled in place for translation, unfolding each
use case for each entry in the %patch_modes hash table.

Previously, this script relied on whether $patch_mode was set to run the
command patch_update_cmd() or show status and loop the main loop. Now,
it uses $cmd to indicate we must run patch_update_cmd() and $patch_mode
is used to tell which flavor of the %patch_modes are we on.  This is
introduced in order to be able to mark and unfold the message prompt
knowing in which context we are.

The tracking of context was done previously by point %patch_mode_flavour
hash table to the correct entry of %patch_modes, focusing only on value
of %patch_modes. Now, we are also interested in the key ('staged',
'stash', 'checkout_head', ...).

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 Makefile  |  2 +-
 git-add--interactive.perl | 54 ---
 perl/Git/I18N.pm  | 11 +-
 t/t0202/test.pl   |  5 -
 4 files changed, 61 insertions(+), 11 deletions(-)

diff --git a/Makefile b/Makefile
index fdef1dd94..3c889dc63 100644
--- a/Makefile
+++ b/Makefile
@@ -2115,7 +2115,7 @@ XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --language=C \
 XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell \
--keyword=gettextln --keyword=eval_gettextln
 XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --language=Perl \
-   --keyword=__ --keyword="__n:1,2"
+   --keyword=__ --keyword=N__ --keyword="__n:1,2"
 LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
 LOCALIZED_SH = $(SCRIPT_SH)
 LOCALIZED_SH += git-parse-remote.sh
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index cd617837b..b7d382b10 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -93,6 +93,7 @@ sub colored {
 }
 
 # command line options
+my $cmd;
 my $patch_mode;
 my $patch_mode_revision;
 
@@ -173,7 +174,8 @@ my %patch_modes = (
},
 );
 
-my %patch_mode_flavour = %{$patch_modes{stage}};
+$patch_mode = 'stage';
+my %patch_mode_flavour = %{$patch_modes{$patch_mode}};
 
 sub run_cmd_pipe {
if ($^O eq 'MSWin32') {
@@ -1311,6 +1313,44 @@ sub display_hunks {
return $i;
 }
 
+my %patch_update_prompt_modes = (
+   stage => {
+   mode => N__("Stage mode change [y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Stage deletion [y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Stage this hunk [y,n,q,a,d,/%s,?]? "),
+   },
+   stash => {
+   mode => N__("Stash mode change [y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Stash deletion [y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Stash this hunk [y,n,q,a,d,/%s,?]? "),
+   },
+   reset_head => {
+   mode => N__("Unstage mode change [y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Unstage deletion [y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Unstage this hunk [y,n,q,a,d,/%s,?]? "),
+   },
+   reset_nothead => {
+   mode => N__("Apply mode change to index [y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Apply deletion to index [y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Apply this hunk to index [y,n,q,a,d,/%s,?]? "),
+   },
+   checkout_index => {
+   mode => N__("Discard mode change from worktree 
[y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Discard deletion from worktree 
[y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Discard this hunk from worktree [y,n,q,a,d,/%s,?]? 
"),
+   },
+   checkout_head => {
+   mode => N__("Discard mode change from index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Discard deletion from index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Discard this hunk from index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   },
+   checkout_nothead => {
+   mode => N__("Apply mode change to index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Apply deletion to index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Apply this hunk to index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   },
+);
+
 sub patch_update_file {
my $quit = 0;
my ($ix, $num);
@@ -1383,12 +1423,9 @@ sub patch_update_file {
for (@{$hunk[$ix]{DISPLAY}}) {
print;
}
-   print colored $prompt_color, $patch_mode_flavour{VERB},
- ($hunk[$ix]{TYPE} eq 'mode' ? ' mode change' :
-  $hunk[$ix]{TYPE} eq 'deletion' ? ' deletion' :
-  ' this hunk'),
- 

[PATCH v7 14/16] i18n: send-email: mark string with interpolation for translation

2016-12-14 Thread Vasco Almeida
Mark warnings, errors and other messages that are interpolated for
translation.

We call sprintf() before calling die() and in few other circumstances in
order to replace the values on the placeholders.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-send-email.perl | 87 +
 1 file changed, 47 insertions(+), 40 deletions(-)

diff --git a/git-send-email.perl b/git-send-email.perl
index 00d234e11..7f3297cdf 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -279,10 +279,13 @@ sub signal_handler {
# tmp files from --compose
if (defined $compose_filename) {
if (-e $compose_filename) {
-   print "'$compose_filename' contains an intermediate 
version of the email you were composing.\n";
+   printf __("'%s' contains an intermediate version ".
+ "of the email you were composing.\n"),
+ $compose_filename;
}
if (-e ($compose_filename . ".final")) {
-   print "'$compose_filename.final' contains the composed 
email.\n"
+   printf __("'%s.final' contains the composed email.\n"),
+ $compose_filename;
}
}
 
@@ -431,7 +434,7 @@ $smtp_encryption = '' unless (defined $smtp_encryption);
 my(%suppress_cc);
 if (@suppress_cc) {
foreach my $entry (@suppress_cc) {
-   die "Unknown --suppress-cc field: '$entry'\n"
+   die sprintf(__("Unknown --suppress-cc field: '%s'\n"), $entry)
unless $entry =~ 
/^(?:all|cccmd|cc|author|self|sob|body|bodycc)$/;
$suppress_cc{$entry} = 1;
}
@@ -460,7 +463,7 @@ my $confirm_unconfigured = !defined $confirm;
 if ($confirm_unconfigured) {
$confirm = scalar %suppress_cc ? 'compose' : 'auto';
 };
-die "Unknown --confirm setting: '$confirm'\n"
+die sprintf(__("Unknown --confirm setting: '%s'\n"), $confirm)
unless $confirm =~ /^(?:auto|cc|compose|always|never)/;
 
 # Debugging, print out the suppressions.
@@ -492,16 +495,16 @@ my %aliases;
 sub parse_sendmail_alias {
local $_ = shift;
if (/"/) {
-   print STDERR "warning: sendmail alias with quotes is not 
supported: $_\n";
+   printf STDERR __("warning: sendmail alias with quotes is not 
supported: %s\n"), $_;
} elsif (/:include:/) {
-   print STDERR "warning: `:include:` not supported: $_\n";
+   printf STDERR __("warning: `:include:` not supported: %s\n"), 
$_;
} elsif (/[\/|]/) {
-   print STDERR "warning: `/file` or `|pipe` redirection not 
supported: $_\n";
+   printf STDERR __("warning: `/file` or `|pipe` redirection not 
supported: %s\n"), $_;
} elsif (/^(\S+?)\s*:\s*(.+)$/) {
my ($alias, $addr) = ($1, $2);
$aliases{$alias} = [ split_addrs($addr) ];
} else {
-   print STDERR "warning: sendmail line is not recognized: $_\n";
+   printf STDERR __("warning: sendmail line is not recognized: 
%s\n"), $_;
}
 }
 
@@ -582,11 +585,11 @@ sub is_format_patch_arg {
if (defined($format_patch)) {
return $format_patch;
}
-   die(<<EOF);
-File '$f' exists but it could also be the range of commits
+   die sprintf(__ <<EOF, $f, $f);
+File '%s' exists but it could also be the range of commits
 to produce patches for.  Please disambiguate by...
 
-* Saying "./$f" if you mean a file; or
+* Saying "./%s" if you mean a file; or
 * Giving --format-patch option if you mean a range.
 EOF
} catch Git::Error::Command with {
@@ -604,7 +607,7 @@ while (defined(my $f = shift @ARGV)) {
@ARGV = ();
} elsif (-d $f and !is_format_patch_arg($f)) {
opendir my $dh, $f
-   or die "Failed to opendir $f: $!";
+   or die sprintf(__("Failed to opendir %s: %s"), $f, $!);
 
push @files, grep { -f $_ } map { catfile($f, $_) }
sort readdir $dh;
@@ -628,7 +631,8 @@ if ($validate) {
foreach my $f (@files) {
unless (-p $f) {
my $error = validate_patch($f);
-   $error and die "fatal: $f: $error\nwarning: no patches 
were sent\n";
+   $error and die sprintf(__("fatal: %s: %s\nwarning: no 
patches were sent\n"),
+ $f, $error);
}
}

[PATCH v7 12/16] i18n: send-email: mark strings for translation

2016-12-14 Thread Vasco Almeida
Mark strings often displayed to the user for translation.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-send-email.perl | 54 +++--
 1 file changed, 32 insertions(+), 22 deletions(-)

diff --git a/git-send-email.perl b/git-send-email.perl
index da81be40c..06e64699b 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -28,6 +28,7 @@ use File::Temp qw/ tempdir tempfile /;
 use File::Spec::Functions qw(catfile);
 use Error qw(:try);
 use Git;
+use Git::I18N;
 
 Getopt::Long::Configure qw/ pass_through /;
 
@@ -797,12 +798,12 @@ foreach my $f (@files) {
 }
 
 if (!defined $auto_8bit_encoding && scalar %broken_encoding) {
-   print "The following files are 8bit, but do not declare " .
-   "a Content-Transfer-Encoding.\n";
+   print __("The following files are 8bit, but do not declare " .
+"a Content-Transfer-Encoding.\n");
foreach my $f (sort keys %broken_encoding) {
print "$f\n";
}
-   $auto_8bit_encoding = ask("Which 8bit encoding should I declare 
[UTF-8]? ",
+   $auto_8bit_encoding = ask(__("Which 8bit encoding should I declare 
[UTF-8]? "),
  valid_re => qr/.{4}/, confirm_only => 1,
  default => "UTF-8");
 }
@@ -829,7 +830,7 @@ if (defined $sender) {
 # But it's a no-op to run sanitize_address on an already sanitized address.
 $sender = sanitize_address($sender);
 
-my $to_whom = "To whom should the emails be sent (if anyone)?";
+my $to_whom = __("To whom should the emails be sent (if anyone)?");
 my $prompting = 0;
 if (!@initial_to && !defined $to_cmd) {
my $to = ask("$to_whom ",
@@ -859,7 +860,7 @@ sub expand_one_alias {
 
 if ($thread && !defined $initial_reply_to && $prompting) {
$initial_reply_to = ask(
-   "Message-ID to be used as In-Reply-To for the first email (if 
any)? ",
+   __("Message-ID to be used as In-Reply-To for the first email 
(if any)? "),
default => "",
valid_re => qr/\@.*\./, confirm_only => 1);
 }
@@ -918,7 +919,10 @@ sub validate_address {
my $address = shift;
while (!extract_valid_address($address)) {
print STDERR "error: unable to extract a valid address from: 
$address\n";
-   $_ = ask("What to do with this address? ([q]uit|[d]rop|[e]dit): 
",
+   # TRANSLATORS: Make sure to include [q] [d] [e] in your
+   # translation. The program will only accept English input
+   # at this point.
+   $_ = ask(__("What to do with this address? 
([q]uit|[d]rop|[e]dit): "),
valid_re => qr/^(?:quit|q|drop|d|edit|e)/i,
default => 'q');
if (/^d/i) {
@@ -1293,17 +1297,23 @@ Message-Id: $message_id
if ($needs_confirm eq "inform") {
$confirm_unconfigured = 0; # squelch this message for 
the rest of this run
$ask_default = "y"; # assume yes on EOF since user 
hasn't explicitly asked for confirmation
-   print "The Cc list above has been expanded by 
additional\n";
-   print "addresses found in the patch commit message. 
By default\n";
-   print "send-email prompts before sending whenever 
this occurs.\n";
-   print "This behavior is controlled by the 
sendemail.confirm\n";
-   print "configuration setting.\n";
-   print "\n";
-   print "For additional information, run 'git 
send-email --help'.\n";
-   print "To retain the current behavior, but squelch 
this message,\n";
-   print "run 'git config --global sendemail.confirm 
auto'.\n\n";
+   print __ < qr/^(?:yes|y|no|n|quit|q|all|a)/i,
 default => $ask_default);
die "Send this email reply required" unless defined $_;
@@ -1405,7 +1415,7 @@ Message-Id: $message_id
if ($quiet) {
printf (($dry_run ? "Dry-" : "")."Sent %s\n", $subject);
} else {
-   print (($dry_run ? "Dry-" : "")."OK. Log says:\n");
+   print($dry_run ? __("Dry-OK. Log says:\n") : __("OK. Log 
says:\n"));
if (!file_name_is_absolute($smtp_server)) {
print "Server: $smtp_server\n"

[PATCH v7 08/16] i18n: add--interactive: i18n of help_patch_cmd

2016-12-14 Thread Vasco Almeida
Mark help message of help_patch_cmd for translation.  The message must
be unfolded to be free of variables so we can have high quality
translations.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 54 ---
 1 file changed, 46 insertions(+), 8 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index b7d382b10..045b847cf 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -1189,15 +1189,53 @@ sub edit_hunk_loop {
}
 }
 
+my %help_patch_modes = (
+   stage => N__(
+"y - stage this hunk
+n - do not stage this hunk
+q - quit; do not stage this hunk or any of the remaining ones
+a - stage this hunk and all later hunks in the file
+d - do not stage this hunk or any of the later hunks in the file"),
+   stash => N__(
+"y - stash this hunk
+n - do not stash this hunk
+q - quit; do not stash this hunk or any of the remaining ones
+a - stash this hunk and all later hunks in the file
+d - do not stash this hunk or any of the later hunks in the file"),
+   reset_head => N__(
+"y - unstage this hunk
+n - do not unstage this hunk
+q - quit; do not unstage this hunk or any of the remaining ones
+a - unstage this hunk and all later hunks in the file
+d - do not unstage this hunk or any of the later hunks in the file"),
+   reset_nothead => N__(
+"y - apply this hunk to index
+n - do not apply this hunk to index
+q - quit; do not apply this hunk or any of the remaining ones
+a - apply this hunk and all later hunks in the file
+d - do not apply this hunk or any of the later hunks in the file"),
+   checkout_index => N__(
+"y - discard this hunk from worktree
+n - do not discard this hunk from worktree
+q - quit; do not discard this hunk or any of the remaining ones
+a - discard this hunk and all later hunks in the file
+d - do not discard this hunk or any of the later hunks in the file"),
+   checkout_head => N__(
+"y - discard this hunk from index and worktree
+n - do not discard this hunk from index and worktree
+q - quit; do not discard this hunk or any of the remaining ones
+a - discard this hunk and all later hunks in the file
+d - do not discard this hunk or any of the later hunks in the file"),
+   checkout_nothead => N__(
+"y - apply this hunk to index and worktree
+n - do not apply this hunk to index and worktree
+q - quit; do not apply this hunk or any of the remaining ones
+a - apply this hunk and all later hunks in the file
+d - do not apply this hunk or any of the later hunks in the file"),
+);
+
 sub help_patch_cmd {
-   my $verb = lc $patch_mode_flavour{VERB};
-   my $target = $patch_mode_flavour{TARGET};
-   print colored $help_color, <

[PATCH v7 03/16] i18n: add--interactive: mark simple here-documents for translation

2016-12-14 Thread Vasco Almeida
Mark messages in here-documents without interpolation for translation.

The here-document delimiter \EOF, which is the same as 'EOF', indicates
that the text is to be treated literally without interpolation of its
content.  Unfortunately xgettext is not able to extract here-documents
delimited with \EOF but it is with delimiter enclosed in single quotes.
So change \EOF to 'EOF', although in this case does not make
difference what variation of here-document to use since there is nothing
to interpolate.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 8 +---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index cf216ecb6..5800010ed 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -639,7 +639,7 @@ sub list_and_choose {
 }
 
 sub singleton_prompt_help_cmd {
-   print colored $help_color, <<\EOF ;
+   print colored $help_color, __ <<'EOF' ;
 Prompt help:
 1  - select a numbered item
 foo- select item based on unique prefix
@@ -648,7 +648,7 @@ EOF
 }
 
 sub prompt_help_cmd {
-   print colored $help_color, <<\EOF ;
+   print colored $help_color, __ <<'EOF' ;
 Prompt help:
 1  - select a single item
 3-5- select a range of items
@@ -1584,7 +1584,9 @@ sub quit_cmd {
 }
 
 sub help_cmd {
-   print colored $help_color, <<\EOF ;
+# TRANSLATORS: please do not translate the command names
+# 'status', 'update', 'revert', etc.
+   print colored $help_color, __ <<'EOF' ;
 status- show paths with changes
 update- add working tree state to the staged set of changes
 revert- revert staged set of changes back to the HEAD version
-- 
2.11.0.44.g7d42c6c



[PATCH v7 01/16] Git.pm: add subroutines for commenting lines

2016-12-14 Thread Vasco Almeida
Add subroutines prefix_lines and comment_lines.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 perl/Git.pm | 38 ++
 1 file changed, 38 insertions(+)

diff --git a/perl/Git.pm b/perl/Git.pm
index b2732822a..bfce1f795 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -1438,6 +1438,44 @@ sub END {
 
 } # %TEMP_* Lexical Context
 
+=item prefix_lines ( PREFIX, STRING [, STRING... ])
+
+Prefixes lines in C with C.
+
+=cut
+
+sub prefix_lines {
+   my $prefix = shift;
+   my $string = join("\n", @_);
+   $string =~ s/^/$prefix/mg;
+   return $string;
+}
+
+=item get_comment_line_char ( )
+
+Gets the core.commentchar configuration value.
+The value falls-back to '#' if core.commentchar is set to 'auto'.
+
+=cut
+
+sub get_comment_line_char {
+   my $comment_line_char = config("core.commentchar") || '#';
+   $comment_line_char = '#' if ($comment_line_char eq 'auto');
+   $comment_line_char = '#' if (length($comment_line_char) != 1);
+   return $comment_line_char;
+}
+
+=item comment_lines ( STRING [, STRING... ])
+
+Comments lines following core.commentchar configuration.
+
+=cut
+
+sub comment_lines {
+   my $comment_line_char = get_comment_line_char;
+   return prefix_lines("$comment_line_char ", @_);
+}
+
 =back
 
 =head1 ERROR HANDLING
-- 
2.11.0.44.g7d42c6c



[PATCH v7 13/16] i18n: send-email: mark warnings and errors for translation

2016-12-14 Thread Vasco Almeida
Mark warnings, errors and other messages for translation.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-send-email.perl | 34 +-
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/git-send-email.perl b/git-send-email.perl
index 06e64699b..00d234e11 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -118,20 +118,20 @@ sub format_2822_time {
my $localmin = $localtm[1] + $localtm[2] * 60;
my $gmtmin = $gmttm[1] + $gmttm[2] * 60;
if ($localtm[0] != $gmttm[0]) {
-   die "local zone differs from GMT by a non-minute interval\n";
+   die __("local zone differs from GMT by a non-minute 
interval\n");
}
if ((($gmttm[6] + 1) % 7) == $localtm[6]) {
$localmin += 1440;
} elsif ((($gmttm[6] - 1) % 7) == $localtm[6]) {
$localmin -= 1440;
} elsif ($gmttm[6] != $localtm[6]) {
-   die "local time offset greater than or equal to 24 hours\n";
+   die __("local time offset greater than or equal to 24 hours\n");
}
my $offset = $localmin - $gmtmin;
my $offhour = $offset / 60;
my $offmin = abs($offset % 60);
if (abs($offhour) >= 24) {
-   die ("local time offset greater than or equal to 24 hours\n");
+   die __("local time offset greater than or equal to 24 hours\n");
}
 
return sprintf("%s, %2d %s %d %02d:%02d:%02d %s%02d%02d",
@@ -199,13 +199,13 @@ sub do_edit {
map {
system('sh', '-c', $editor.' "$@"', $editor, $_);
if (($? & 127) || ($? >> 8)) {
-   die("the editor exited uncleanly, aborting 
everything");
+   die(__("the editor exited uncleanly, aborting 
everything"));
}
} @_;
} else {
system('sh', '-c', $editor.' "$@"', $editor, @_);
if (($? & 127) || ($? >> 8)) {
-   die("the editor exited uncleanly, aborting everything");
+   die(__("the editor exited uncleanly, aborting 
everything"));
}
}
 }
@@ -299,7 +299,7 @@ my $help;
 my $rc = GetOptions("h" => \$help,
 "dump-aliases" => \$dump_aliases);
 usage() unless $rc;
-die "--dump-aliases incompatible with other options\n"
+die __("--dump-aliases incompatible with other options\n")
 if !$help and $dump_aliases and @ARGV;
 $rc = GetOptions(
"sender|from=s" => \$sender,
@@ -362,7 +362,7 @@ unless ($rc) {
 usage();
 }
 
-die "Cannot run git format-patch from outside a repository\n"
+die __("Cannot run git format-patch from outside a repository\n")
if $format_patch and not $repo;
 
 # Now, let's fill any that aren't set in with defaults:
@@ -617,7 +617,7 @@ while (defined(my $f = shift @ARGV)) {
 }
 
 if (@rev_list_opts) {
-   die "Cannot run git format-patch from outside a repository\n"
+   die __("Cannot run git format-patch from outside a repository\n")
unless $repo;
push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 
1), @rev_list_opts);
 }
@@ -638,7 +638,7 @@ if (@files) {
print $_,"\n" for (@files);
}
 } else {
-   print STDERR "\nNo patch files specified!\n\n";
+   print STDERR __("\nNo patch files specified!\n\n");
usage();
 }
 
@@ -730,7 +730,7 @@ EOT
$sender = $1;
next;
} elsif (/^(?:To|Cc|Bcc):/i) {
-   print "To/Cc/Bcc fields are not interpreted yet, they 
have been ignored\n";
+   print __("To/Cc/Bcc fields are not interpreted yet, 
they have been ignored\n");
next;
}
print $c2 $_;
@@ -739,7 +739,7 @@ EOT
close $c2;
 
if ($summary_empty) {
-   print "Summary email is empty, skipping it\n";
+   print __("Summary email is empty, skipping it\n");
$compose = -1;
}
 } elsif ($annotate) {
@@ -1316,7 +1316,7 @@ EOF
$_ = ask(__("Send this email? ([y]es|[n]o|[q]uit|[a]ll): "),
 valid_re => qr/^(?:yes|y|no|n|quit|q|all|a)/i,
 default => $ask_default);
-   die "Send this email reply required" unless defined $_;
+   die __("Send this email reply required") unless defined $_;
if (/^n/i) {
 

[PATCH v7 09/16] i18n: add--interactive: mark edit_hunk_manually message for translation

2016-12-14 Thread Vasco Almeida
Mark message of edit_hunk_manually displayed in the editing file when
user chooses 'e' option.  The message had to be unfolded to allow
translation of the $participle verb.

Some messages end up being exactly the same for some use cases, but
left it for easier change in the future, e.g., wanting to change wording
of one particular use case.

The comment character is now used according to the git configuration
core.commentchar.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 52 +++
 1 file changed, 39 insertions(+), 13 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 045b847cf..e77f77613 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -1058,6 +1058,30 @@ sub color_diff {
} @_;
 }
 
+my %edit_hunk_manually_modes = (
+   stage => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for staging."),
+   stash => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for stashing."),
+   reset_head => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for unstaging."),
+   reset_nothead => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for applying."),
+   checkout_index => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for discarding"),
+   checkout_head => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for discarding."),
+   checkout_nothead => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for applying."),
+);
+
 sub edit_hunk_manually {
my ($oldtext) = @_;
 
@@ -1065,22 +1089,24 @@ sub edit_hunk_manually {
my $fh;
open $fh, '>', $hunkfile
or die sprintf(__("failed to open hunk edit file for writing: 
%s"), $!);
-   print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n";
+   print $fh Git::comment_lines __("Manual hunk edit mode -- see bottom 
for a quick guide.\n");
print $fh @$oldtext;
-   my $participle = $patch_mode_flavour{PARTICIPLE};
my $is_reverse = $patch_mode_flavour{IS_REVERSE};
my ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') : ('+', 
'-');
-   print $fh <<EOF;
-# ---
-# To remove '$remove_minus' lines, make them ' ' lines (context).
-# To remove '$remove_plus' lines, delete them.
-# Lines starting with # will be removed.
-#
-# If the patch applies cleanly, the edited hunk will immediately be
-# marked for $participle. If it does not apply cleanly, you will be given
-# an opportunity to edit again. If all lines of the hunk are removed,
-# then the edit is aborted and the hunk is left unchanged.
+   my $comment_line_char = Git::get_comment_line_char;
+   print $fh Git::comment_lines sprintf(__ <<EOF, $remove_minus, 
$remove_plus, $comment_line_char),
+---
+To remove '%s' lines, make them ' ' lines (context).
+To remove '%s' lines, delete them.
+Lines starting with %s will be removed.
 EOF
+__($edit_hunk_manually_modes{$patch_mode}),
+# TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
+__ <;
+   my @newtext = grep { !/^$comment_line_char/ } <$fh>;
close $fh;
unlink $hunkfile;
 
-- 
2.11.0.44.g7d42c6c



[PATCH v7 10/16] i18n: add--interactive: remove %patch_modes entries

2016-12-14 Thread Vasco Almeida
Remove unnecessary entries from %patch_modes. After the i18n conversion,
these entries are not used anymore.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 21 -
 1 file changed, 21 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index e77f77613..d3785e8d7 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -106,9 +106,6 @@ my %patch_modes = (
DIFF => 'diff-files -p',
APPLY => sub { apply_patch 'apply --cached', @_; },
APPLY_CHECK => 'apply --cached',
-   VERB => 'Stage',
-   TARGET => '',
-   PARTICIPLE => 'staging',
FILTER => 'file-only',
IS_REVERSE => 0,
},
@@ -116,9 +113,6 @@ my %patch_modes = (
DIFF => 'diff-index -p HEAD',
APPLY => sub { apply_patch 'apply --cached', @_; },
APPLY_CHECK => 'apply --cached',
-   VERB => 'Stash',
-   TARGET => '',
-   PARTICIPLE => 'stashing',
FILTER => undef,
IS_REVERSE => 0,
},
@@ -126,9 +120,6 @@ my %patch_modes = (
DIFF => 'diff-index -p --cached',
APPLY => sub { apply_patch 'apply -R --cached', @_; },
APPLY_CHECK => 'apply -R --cached',
-   VERB => 'Unstage',
-   TARGET => '',
-   PARTICIPLE => 'unstaging',
FILTER => 'index-only',
IS_REVERSE => 1,
},
@@ -136,9 +127,6 @@ my %patch_modes = (
DIFF => 'diff-index -R -p --cached',
APPLY => sub { apply_patch 'apply --cached', @_; },
APPLY_CHECK => 'apply --cached',
-   VERB => 'Apply',
-   TARGET => ' to index',
-   PARTICIPLE => 'applying',
FILTER => 'index-only',
IS_REVERSE => 0,
},
@@ -146,9 +134,6 @@ my %patch_modes = (
DIFF => 'diff-files -p',
APPLY => sub { apply_patch 'apply -R', @_; },
APPLY_CHECK => 'apply -R',
-   VERB => 'Discard',
-   TARGET => ' from worktree',
-   PARTICIPLE => 'discarding',
FILTER => 'file-only',
IS_REVERSE => 1,
},
@@ -156,9 +141,6 @@ my %patch_modes = (
DIFF => 'diff-index -p',
APPLY => sub { apply_patch_for_checkout_commit '-R', @_ },
APPLY_CHECK => 'apply -R',
-   VERB => 'Discard',
-   TARGET => ' from index and worktree',
-   PARTICIPLE => 'discarding',
FILTER => undef,
IS_REVERSE => 1,
},
@@ -166,9 +148,6 @@ my %patch_modes = (
DIFF => 'diff-index -R -p',
APPLY => sub { apply_patch_for_checkout_commit '', @_ },
APPLY_CHECK => 'apply',
-   VERB => 'Apply',
-   TARGET => ' to index and worktree',
-   PARTICIPLE => 'applying',
FILTER => undef,
IS_REVERSE => 0,
},
-- 
2.11.0.44.g7d42c6c



[PATCH v7 06/16] i18n: add--interactive: mark plural strings

2016-12-14 Thread Vasco Almeida
Mark plural strings for translation.  Unfold each action case in one
entire sentence.

Pass new keyword for xgettext to extract.

Update test to include new subroutine __n() for plural strings handling.

Update documentation to include a description of the new __n()
subroutine.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 Makefile  |  3 ++-
 git-add--interactive.perl | 27 ++-
 perl/Git/I18N.pm  | 10 +-
 t/t0202/test.pl   | 11 ++-
 4 files changed, 39 insertions(+), 12 deletions(-)

diff --git a/Makefile b/Makefile
index f53fcc90d..fdef1dd94 100644
--- a/Makefile
+++ b/Makefile
@@ -2114,7 +2114,8 @@ XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --language=C \
--keyword=_ --keyword=N_ --keyword="Q_:1,2"
 XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell \
--keyword=gettextln --keyword=eval_gettextln
-XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --keyword=__ --language=Perl
+XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --language=Perl \
+   --keyword=__ --keyword="__n:1,2"
 LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
 LOCALIZED_SH = $(SCRIPT_SH)
 LOCALIZED_SH += git-parse-remote.sh
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index d05ac608e..cd617837b 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -669,12 +669,18 @@ sub status_cmd {
 sub say_n_paths {
my $did = shift @_;
my $cnt = scalar @_;
-   print "$did ";
-   if (1 < $cnt) {
-   print "$cnt paths\n";
-   }
-   else {
-   print "one path\n";
+   if ($did eq 'added') {
+   printf(__n("added %d path\n", "added %d paths\n",
+  $cnt), $cnt);
+   } elsif ($did eq 'updated') {
+   printf(__n("updated %d path\n", "updated %d paths\n",
+  $cnt), $cnt);
+   } elsif ($did eq 'reverted') {
+   printf(__n("reverted %d path\n", "reverted %d paths\n",
+  $cnt), $cnt);
+   } else {
+   printf(__n("touched %d path\n", "touched %d paths\n",
+  $cnt), $cnt);
}
 }
 
@@ -1423,7 +1429,8 @@ sub patch_update_file {
} elsif (0 < $response && $response <= $num) {
$ix = $response - 1;
} else {
-   error_msg "Sorry, only $num hunks 
available.\n";
+   error_msg sprintf(__n("Sorry, only %d 
hunk available.\n",
+ "Sorry, only %d 
hunks available.\n", $num), $num);
}
next;
}
@@ -1518,8 +1525,10 @@ sub patch_update_file {
elsif ($other =~ /s/ && $line =~ /^s/) {
my @split = split_hunk($hunk[$ix]{TEXT}, 
$hunk[$ix]{DISPLAY});
if (1 < @split) {
-   print colored $header_color, "Split 
into ",
-   scalar(@split), " hunks.\n";
+   print colored $header_color, sprintf(
+   __n("Split into %d hunk.\n",
+   "Split into %d hunks.\n",
+   scalar(@split)), 
scalar(@split));
}
splice (@hunk, $ix, 1, @split);
$num = scalar @hunk;
diff --git a/perl/Git/I18N.pm b/perl/Git/I18N.pm
index f889fd6da..617d8c2a1 100644
--- a/perl/Git/I18N.pm
+++ b/perl/Git/I18N.pm
@@ -13,7 +13,7 @@ BEGIN {
}
 }
 
-our @EXPORT = qw(__);
+our @EXPORT = qw(__ __n);
 our @EXPORT_OK = @EXPORT;
 
 sub __bootstrap_locale_messages {
@@ -44,6 +44,7 @@ BEGIN
eval {
__bootstrap_locale_messages();
*__ = \::Messages::gettext;
+   *__n = \::Messages::ngettext;
1;
} or do {
# Tell test.pl that we couldn't load the gettext library.
@@ -51,6 +52,7 @@ BEGIN
 
# Just a fall-through no-op
*__ = sub ($) { $_[0] };
+   *__n = sub ($$$) { $_[2] == 1 ? $_[0] : $_[1] };
};
 }
 
@@ -70,6 +72,8 @@ Git::I18N - Perl interface to Git's Gettext localizations
 
printf __("The following error occurred: %s\n"), $error;
 
+   printf __n("commited %d file\n", "commited %d files\n", $files), $files;
+
 =head1 DESCRIPTION
 

[PATCH v7 04/16] i18n: add--interactive: mark strings with interpolation for translation

2016-12-14 Thread Vasco Almeida
Since at this point Git::I18N.perl lacks support for Perl i18n
placeholder substitution, use of sprintf following die or error_msg is
necessary for placeholder substitution take place.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 25 +
 1 file changed, 13 insertions(+), 12 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 5800010ed..d05ac608e 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -615,12 +615,12 @@ sub list_and_choose {
else {
$bottom = $top = find_unique($choice, @stuff);
if (!defined $bottom) {
-   error_msg "Huh ($choice)?\n";
+   error_msg sprintf(__("Huh (%s)?\n"), 
$choice);
next TOPLOOP;
}
}
if ($opts->{SINGLETON} && $bottom != $top) {
-   error_msg "Huh ($choice)?\n";
+   error_msg sprintf(__("Huh (%s)?\n"), $choice);
next TOPLOOP;
}
for ($i = $bottom-1; $i <= $top-1; $i++) {
@@ -717,7 +717,7 @@ sub revert_cmd {
$_->{INDEX_ADDDEL} eq 'create') {
system(qw(git update-index 
--force-remove --),
   $_->{VALUE});
-   print "note: $_->{VALUE} is untracked 
now.\n";
+   printf(__("note: %s is untracked 
now.\n"), $_->{VALUE});
}
}
}
@@ -1056,7 +1056,7 @@ sub edit_hunk_manually {
my $hunkfile = $repo->repo_path . "/addp-hunk-edit.diff";
my $fh;
open $fh, '>', $hunkfile
-   or die "failed to open hunk edit file for writing: " . $!;
+   or die sprintf(__("failed to open hunk edit file for writing: 
%s"), $!);
print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n";
print $fh @$oldtext;
my $participle = $patch_mode_flavour{PARTICIPLE};
@@ -1083,7 +1083,7 @@ EOF
}
 
open $fh, '<', $hunkfile
-   or die "failed to open hunk edit file for reading: " . $!;
+   or die sprintf(__("failed to open hunk edit file for reading: 
%s"), $!);
my @newtext = grep { !/^#/ } <$fh>;
close $fh;
unlink $hunkfile;
@@ -1236,7 +1236,7 @@ sub apply_patch_for_checkout_commit {
 
 sub patch_update_cmd {
my @all_mods = list_modified($patch_mode_flavour{FILTER});
-   error_msg "ignoring unmerged: $_->{VALUE}\n"
+   error_msg sprintf(__("ignoring unmerged: %s\n"), $_->{VALUE})
for grep { $_->{UNMERGED} } @all_mods;
@all_mods = grep { !$_->{UNMERGED} } @all_mods;
 
@@ -1418,7 +1418,8 @@ sub patch_update_file {
chomp $response;
}
if ($response !~ /^\s*\d+\s*$/) {
-   error_msg "Invalid number: 
'$response'\n";
+   error_msg sprintf(__("Invalid number: 
'%s'\n"),
+$response);
} elsif (0 < $response && $response <= $num) {
$ix = $response - 1;
} else {
@@ -1460,7 +1461,7 @@ sub patch_update_file {
if ($@) {
my ($err,$exp) = ($@, $1);
$err =~ s/ at .*git-add--interactive 
line \d+,  line \d+.*$//;
-   error_msg "Malformed search regexp 
$exp: $err\n";
+   error_msg sprintf(__("Malformed search 
regexp %s: %s\n"), $exp, $err);
next;
}
my $iy = $ix;
@@ -1625,18 +1626,18 @@ sub process_args {
$patch_mode = $1;
$arg = shift @ARGV or die __("missing --");
} else {
-   die "unknown --patch mode: $1";
+   die sprintf(__("unknown --patch mode: %s"), $1);
}
} else {
   

[PATCH v7 11/16] i18n: add--interactive: mark status words for translation

2016-12-14 Thread Vasco Almeida
Mark words 'nothing', 'unchanged' and 'binary' used to display what has
been staged or not, in "git add -i" status command.

Alternatively one could mark N__('nothing') no-op in order to
xgettext(1) extract the string and then trigger the translation at run
time only with __($print->{FILE}), but that has the side effect of triggering
retrieval of translations for the changes indicator too (e.g. +2/-1)
which may or may not be a problem.

To avoid that potential problem, mark only where there is certain to
trigger translation only of those words but in this case we must also
retrieve the translation for the eq tests, since the value assigned was
of the translation, not the English source.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 12 ++--
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index d3785e8d7..4e0ab5a9b 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -295,7 +295,7 @@ sub list_modified {
my ($change, $bin);
$file = unquote_path($file);
if ($add eq '-' && $del eq '-') {
-   $change = 'binary';
+   $change = __('binary');
$bin = 1;
}
else {
@@ -304,7 +304,7 @@ sub list_modified {
$data{$file} = {
INDEX => $change,
BINARY => $bin,
-   FILE => 'nothing',
+   FILE => __('nothing'),
}
}
elsif (($adddel, $file) =
@@ -320,7 +320,7 @@ sub list_modified {
$file = unquote_path($file);
my ($change, $bin);
if ($add eq '-' && $del eq '-') {
-   $change = 'binary';
+   $change = __('binary');
$bin = 1;
}
else {
@@ -340,7 +340,7 @@ sub list_modified {
$file = unquote_path($2);
if (!exists $data{$file}) {
$data{$file} = +{
-   INDEX => 'unchanged',
+   INDEX => __('unchanged'),
BINARY => 0,
};
}
@@ -355,10 +355,10 @@ sub list_modified {
 
if ($only) {
if ($only eq 'index-only') {
-   next if ($it->{INDEX} eq 'unchanged');
+   next if ($it->{INDEX} eq __('unchanged'));
}
if ($only eq 'file-only') {
-   next if ($it->{FILE} eq 'nothing');
+   next if ($it->{FILE} eq __('nothing'));
}
}
push @return, +{
-- 
2.11.0.44.g7d42c6c



[PATCH v7 05/16] i18n: clean.c: match string with git-add--interactive.perl

2016-12-14 Thread Vasco Almeida
Change strings for help to match the ones in git-add--interactive.perl.
The strings now represent one entry to translate each rather then two
entries each different only by an ending newline character.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 builtin/clean.c | 10 +-
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/builtin/clean.c b/builtin/clean.c
index 0371010af..d6bc3aaae 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -287,11 +287,11 @@ static void pretty_print_menus(struct string_list 
*menu_list)
 static void prompt_help_cmd(int singleton)
 {
clean_print_color(CLEAN_COLOR_HELP);
-   printf_ln(singleton ?
+   printf(singleton ?
  _("Prompt help:\n"
"1  - select a numbered item\n"
"foo- select item based on unique prefix\n"
-   "   - (empty) select nothing") :
+   "   - (empty) select nothing\n") :
  _("Prompt help:\n"
"1  - select a single item\n"
"3-5- select a range of items\n"
@@ -299,7 +299,7 @@ static void prompt_help_cmd(int singleton)
"foo- select item based on unique prefix\n"
"-...   - unselect specified items\n"
"*  - choose all items\n"
-   "   - (empty) finish selecting"));
+   "   - (empty) finish selecting\n"));
clean_print_color(CLEAN_COLOR_RESET);
 }
 
@@ -508,7 +508,7 @@ static int parse_choice(struct menu_stuff *menu_stuff,
if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > 
top ||
(is_single && bottom != top)) {
clean_print_color(CLEAN_COLOR_ERROR);
-   printf_ln(_("Huh (%s)?"), (*ptr)->buf);
+   printf(_("Huh (%s)?\n"), (*ptr)->buf);
clean_print_color(CLEAN_COLOR_RESET);
continue;
}
@@ -774,7 +774,7 @@ static int ask_each_cmd(void)
 static int quit_cmd(void)
 {
string_list_clear(_list, 0);
-   printf_ln(_("Bye."));
+   printf(_("Bye.\n"));
return MENU_RETURN_NO_LOOP;
 }
 
-- 
2.11.0.44.g7d42c6c



[PATCH v7 16/16] i18n: difftool: mark warnings for translation

2016-12-14 Thread Vasco Almeida
Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-difftool.perl | 22 --
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/git-difftool.perl b/git-difftool.perl
index a5790d03a..8d3632e55 100755
--- a/git-difftool.perl
+++ b/git-difftool.perl
@@ -22,6 +22,7 @@ use File::Path qw(mkpath rmtree);
 use File::Temp qw(tempdir);
 use Getopt::Long qw(:config pass_through);
 use Git;
+use Git::I18N;
 
 sub usage
 {
@@ -122,7 +123,7 @@ sub setup_dir_diff
my $i = 0;
while ($i < $#rawdiff) {
if ($rawdiff[$i] =~ /^::/) {
-   warn << 'EOF';
+   warn __ <<'EOF';
 Combined diff formats ('-c' and '--cc') are not supported in
 directory diff mode ('-d' and '--dir-diff').
 EOF
@@ -338,7 +339,7 @@ sub main
if (length($opts{difftool_cmd}) > 0) {
$ENV{GIT_DIFF_TOOL} = $opts{difftool_cmd};
} else {
-   print "No  given for --tool=\n";
+   print __("No  given for --tool=\n");
usage(1);
}
}
@@ -346,7 +347,7 @@ sub main
if (length($opts{extcmd}) > 0) {
$ENV{GIT_DIFFTOOL_EXTCMD} = $opts{extcmd};
} else {
-   print "No  given for --extcmd=\n";
+   print __("No  given for --extcmd=\n");
usage(1);
}
}
@@ -419,11 +420,11 @@ sub dir_diff
}
 
if (exists $wt_modified{$file} and exists $tmp_modified{$file}) 
{
-   my $errmsg = "warning: Both files modified: ";
-   $errmsg .= "'$workdir/$file' and '$b/$file'.\n";
-   $errmsg .= "warning: Working tree file has been 
left.\n";
-   $errmsg .= "warning:\n";
-   warn $errmsg;
+   warn sprintf(__(
+   "warning: Both files modified:\n" .
+   "'%s/%s' and '%s/%s'.\n" .
+   "warning: Working tree file has been left.\n" .
+   "warning:\n"), $workdir, $file, $b, $file);
$error = 1;
} elsif (exists $tmp_modified{$file}) {
my $mode = stat("$b/$file")->mode;
@@ -435,8 +436,9 @@ sub dir_diff
}
}
if ($error) {
-   warn "warning: Temporary files exist in '$tmpdir'.\n";
-   warn "warning: You may want to cleanup or recover these.\n";
+   warn sprintf(__(
+   "warning: Temporary files exist in '%s'.\n" .
+   "warning: You may want to cleanup or recover 
these.\n"), $tmpdir);
exit(1);
} else {
exit_cleanup($tmpdir, $rc);
-- 
2.11.0.44.g7d42c6c



Re: [PATCH v6 01/16] Git.pm: add subroutines for commenting lines

2016-12-13 Thread Vasco Almeida
A Sáb, 10-12-2016 às 14:09 -0800, Junio C Hamano escreveu:
> We only update comment_line_char from the default "#" when the
> configured value is a single-byte character and we ignore incorrect
> values in the configuration file.  So I think the patch you sent is
> correct after all.

I am still not sure what version do we prefer.

Check whether core.commentchar is a single character. If not, use '#'
as the $comment_line_char.

+sub get_comment_line_char {
+   my $comment_line_char = config("core.commentchar") || '#';
+   $comment_line_char = '#' if ($comment_line_char eq 'auto');
+   $comment_line_char = '#' if (length($comment_line_char) != 1);
+   return $comment_line_char;
+}

Check whether core.commentchar is a single character. If not, use the
first character of the core.commentchar value, mirroring the "rebase
-i" behavior introduced recently.

+sub get_comment_line_char {
+   my $comment_line_char = config("core.commentchar") || '#';
+   $comment_line_char = '#' if ($comment_line_char eq 'auto');
+   if (length($comment_line_char) != 1) {
+   # use first character
+   $comment_line_char = substr($comment_line_char, 0, 1);
+   }
+   return $comment_line_char;
+}

Or akin to what I had in the first patch related to handling 'auto'
value of core.commentchar configuration variable:

+sub get_comment_line_char {
+   my $comment_line_char = config("core.commentchar") || '#';
+   $comment_line_char = '#' if ($comment_line_char eq 'auto');
+   return $comment_line_char;
+}

Which assumes that the value of core.commentchar configuration variable
is either 'auto' or one single character, or the variable is not
defined.


Re: [PATCH v6 01/16] Git.pm: add subroutines for commenting lines

2016-12-10 Thread Vasco Almeida
A Sex, 09-12-2016 às 14:23 -0800, Junio C Hamano escreveu:
> > This is exactly the same issue I fixed for rebase -i recently.
> 
> Yes, but the patch we see here punts "core.commentChar is not a
> single-byte single-letter--panic!" case differently.  I think you
> did "just take the first one" in "rebase -i", which I think is more
> in line with the rest of the system, and this addition to Git.pm
> should do the same, I think.

I hope the changes below are in line with the rest of the system. If
so, I will send a new re-roll with them.

I wonder why this is important when Git errors out when
core.commentChar is set to more than 1 characters or 0 characters. Is
it just to be consistent with "rebase -i" changes introduced
by Johannes Schindelin?

I am not sure what does "if (length($comment_line_char) != 1)" check.
Whether it checks single-byte or single-letter or both...

-- >8 --
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 3a6d846..4e0ab5a 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -1072,7 +1072,7 @@ sub edit_hunk_manually {
    print $fh @$oldtext;
    my $is_reverse = $patch_mode_flavour{IS_REVERSE};
    my ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') :
('+', '-');
-   my $comment_line_char = Git::config("core.commentchar") ||
'#';
+   my $comment_line_char = Git::get_comment_line_char;
    print $fh Git::comment_lines sprintf(__ <

Re: [PATCH v6 01/16] Git.pm: add subroutines for commenting lines

2016-12-09 Thread Vasco Almeida
A Ter, 22-11-2016 às 09:42 -0800, Junio C Hamano escreveu:
> The incremental update below looks sensible.  We'd also want to
> protect this codepath from a misconfigured two-or-more byte sequence
> in core.commentchar, I would suspect, to be consistent.

Are the below changes alright for what you propose? It just checks if
the length of core.commentchar's value is 1, otherwise use '#' as the
comment_line_char.
As a note, when I set core.commentchar with "git config
core.commentChar 'batata'", I get the following error message when I
issue "git add -i":

error: core.commentChar should only be one character
fatal: bad config variable 'core.commentchar' in file '.git/config' at line 6

-- >8 --
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 3a6d846..4e0ab5a 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -1072,7 +1072,7 @@ sub edit_hunk_manually {
    print $fh @$oldtext;
    my $is_reverse = $patch_mode_flavour{IS_REVERSE};
    my ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') : ('+', 
'-');
-   my $comment_line_char = Git::config("core.commentchar") || '#';
+   my $comment_line_char = Git::get_comment_line_char;
    print $fh Git::comment_lines sprintf(__ <

Re: [PATCH v6 01/16] Git.pm: add subroutines for commenting lines

2016-11-22 Thread Vasco Almeida
A Sex, 11-11-2016 às 11:45 -0100, Vasco Almeida escreveu:
> +=item comment_lines ( STRING [, STRING... ])
> +
> +Comments lines following core.commentchar configuration.
> +
> +=cut
> +
> +sub comment_lines {
> +   my $comment_line_char = config("core.commentchar") || '#';
> +   return prefix_lines("$comment_line_char ", @_);
> +}
> +

In light of the recent "Fix problems with rebase -i when
core.commentchar is defined" [1], I realized that this patch does not
handle the 'auto' value of core.commentchat configuration variable.

I propose to do the patch below in the next re-roll.

[1] http://www.mail-archive.com/git@vger.kernel.org/msg107818.html

-- >8 --
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 3a6d846..8d33634 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -1073,6 +1073,7 @@ sub edit_hunk_manually {
my $is_reverse = $patch_mode_flavour{IS_REVERSE};
my ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') : ('+', 
'-');
my $comment_line_char = Git::config("core.commentchar") || '#';
+   $comment_line_char = '#' if ($comment_line_char eq 'auto');
print $fh Git::comment_lines sprintf(__ <<EOF, $remove_minus, 
$remove_plus, $comment_line_char),
 ---
 To remove '%s' lines, make them ' ' lines (context).
diff --git a/perl/Git.pm b/perl/Git.pm
index 69cd1dd..47b5899 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -1459,6 +1459,7 @@ Comments lines following core.commentchar configuration.
 
 sub comment_lines {
my $comment_line_char = config("core.commentchar") || '#';
+   $comment_line_char = '#' if ($comment_line_char eq 'auto');
return prefix_lines("$comment_line_char ", @_);
 }


[PATCH v6 15/16] i18n: send-email: mark composing message for translation

2016-11-11 Thread Vasco Almeida
When composing an e-mail, there is a message for the user whose lines
are beginning in "GIT:" that can be marked for translation.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-send-email.perl | 16 +---
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/git-send-email.perl b/git-send-email.perl
index 7f3297cdf..068d60b3e 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -672,18 +672,20 @@ if ($compose) {
my $tpl_subject = $initial_subject || '';
my $tpl_reply_to = $initial_reply_to || '';
 
-   print $c <<EOT;
+   print $c <<EOT1, Git::prefix_lines("GIT: ", __ <<EOT2), <<EOT3;
 From $tpl_sender # This line is ignored.
-GIT: Lines beginning in "GIT:" will be removed.
-GIT: Consider including an overall diffstat or table of contents
-GIT: for the patch you are writing.
-GIT:
-GIT: Clear the body content if you don't wish to send a summary.
+EOT1
+Lines beginning in "GIT:" will be removed.
+Consider including an overall diffstat or table of contents
+for the patch you are writing.
+
+Clear the body content if you don't wish to send a summary.
+EOT2
 From: $tpl_sender
 Subject: $tpl_subject
 In-Reply-To: $tpl_reply_to
 
-EOT
+EOT3
for my $f (@files) {
print $c get_patch_subject($f);
}
-- 
2.11.0.rc0.33.gec17dab



[PATCH v6 11/16] i18n: add--interactive: mark status words for translation

2016-11-11 Thread Vasco Almeida
Mark words 'nothing', 'unchanged' and 'binary' used to display what has
been staged or not, in "git add -i" status command.

Alternatively one could mark N__('nothing') no-op in order to
xgettext(1) extract the string and then trigger the translation at run
time only with __($print->{FILE}), but that has the side effect of triggering
retrieval of translations for the changes indicator too (e.g. +2/-1)
which may or may not be a problem.

To avoid that potential problem, mark only where there is certain to
trigger translation only of those words but in this case we must also
retrieve the translation for the eq tests, since the value assigned was
of the translation, not the English source.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 12 ++--
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 5f6637bde..3a6d846da 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -295,7 +295,7 @@ sub list_modified {
my ($change, $bin);
$file = unquote_path($file);
if ($add eq '-' && $del eq '-') {
-   $change = 'binary';
+   $change = __('binary');
$bin = 1;
}
else {
@@ -304,7 +304,7 @@ sub list_modified {
$data{$file} = {
INDEX => $change,
BINARY => $bin,
-   FILE => 'nothing',
+   FILE => __('nothing'),
}
}
elsif (($adddel, $file) =
@@ -320,7 +320,7 @@ sub list_modified {
$file = unquote_path($file);
my ($change, $bin);
if ($add eq '-' && $del eq '-') {
-   $change = 'binary';
+   $change = __('binary');
$bin = 1;
}
else {
@@ -340,7 +340,7 @@ sub list_modified {
$file = unquote_path($2);
if (!exists $data{$file}) {
$data{$file} = +{
-   INDEX => 'unchanged',
+   INDEX => __('unchanged'),
BINARY => 0,
};
}
@@ -355,10 +355,10 @@ sub list_modified {
 
if ($only) {
if ($only eq 'index-only') {
-   next if ($it->{INDEX} eq 'unchanged');
+   next if ($it->{INDEX} eq __('unchanged'));
}
if ($only eq 'file-only') {
-   next if ($it->{FILE} eq 'nothing');
+   next if ($it->{FILE} eq __('nothing'));
}
}
push @return, +{
-- 
2.11.0.rc0.33.gec17dab



[PATCH v6 01/16] Git.pm: add subroutines for commenting lines

2016-11-11 Thread Vasco Almeida
Add subroutines prefix_lines and comment_lines.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 perl/Git.pm | 24 
 1 file changed, 24 insertions(+)

diff --git a/perl/Git.pm b/perl/Git.pm
index b2732822a..69cd1ddec 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -1438,6 +1438,30 @@ sub END {
 
 } # %TEMP_* Lexical Context
 
+=item prefix_lines ( PREFIX, STRING [, STRING... ])
+
+Prefixes lines in C with C.
+
+=cut
+
+sub prefix_lines {
+   my $prefix = shift;
+   my $string = join("\n", @_);
+   $string =~ s/^/$prefix/mg;
+   return $string;
+}
+
+=item comment_lines ( STRING [, STRING... ])
+
+Comments lines following core.commentchar configuration.
+
+=cut
+
+sub comment_lines {
+   my $comment_line_char = config("core.commentchar") || '#';
+   return prefix_lines("$comment_line_char ", @_);
+}
+
 =back
 
 =head1 ERROR HANDLING
-- 
2.11.0.rc0.33.gec17dab



[PATCH v6 10/16] i18n: add--interactive: remove %patch_modes entries

2016-11-11 Thread Vasco Almeida
Remove unnecessary entries from %patch_modes. After the i18n conversion,
these entries are not used anymore.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 21 -
 1 file changed, 21 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 7d1cc5652..5f6637bde 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -106,9 +106,6 @@ my %patch_modes = (
DIFF => 'diff-files -p',
APPLY => sub { apply_patch 'apply --cached', @_; },
APPLY_CHECK => 'apply --cached',
-   VERB => 'Stage',
-   TARGET => '',
-   PARTICIPLE => 'staging',
FILTER => 'file-only',
IS_REVERSE => 0,
},
@@ -116,9 +113,6 @@ my %patch_modes = (
DIFF => 'diff-index -p HEAD',
APPLY => sub { apply_patch 'apply --cached', @_; },
APPLY_CHECK => 'apply --cached',
-   VERB => 'Stash',
-   TARGET => '',
-   PARTICIPLE => 'stashing',
FILTER => undef,
IS_REVERSE => 0,
},
@@ -126,9 +120,6 @@ my %patch_modes = (
DIFF => 'diff-index -p --cached',
APPLY => sub { apply_patch 'apply -R --cached', @_; },
APPLY_CHECK => 'apply -R --cached',
-   VERB => 'Unstage',
-   TARGET => '',
-   PARTICIPLE => 'unstaging',
FILTER => 'index-only',
IS_REVERSE => 1,
},
@@ -136,9 +127,6 @@ my %patch_modes = (
DIFF => 'diff-index -R -p --cached',
APPLY => sub { apply_patch 'apply --cached', @_; },
APPLY_CHECK => 'apply --cached',
-   VERB => 'Apply',
-   TARGET => ' to index',
-   PARTICIPLE => 'applying',
FILTER => 'index-only',
IS_REVERSE => 0,
},
@@ -146,9 +134,6 @@ my %patch_modes = (
DIFF => 'diff-files -p',
APPLY => sub { apply_patch 'apply -R', @_; },
APPLY_CHECK => 'apply -R',
-   VERB => 'Discard',
-   TARGET => ' from worktree',
-   PARTICIPLE => 'discarding',
FILTER => 'file-only',
IS_REVERSE => 1,
},
@@ -156,9 +141,6 @@ my %patch_modes = (
DIFF => 'diff-index -p',
APPLY => sub { apply_patch_for_checkout_commit '-R', @_ },
APPLY_CHECK => 'apply -R',
-   VERB => 'Discard',
-   TARGET => ' from index and worktree',
-   PARTICIPLE => 'discarding',
FILTER => undef,
IS_REVERSE => 1,
},
@@ -166,9 +148,6 @@ my %patch_modes = (
DIFF => 'diff-index -R -p',
APPLY => sub { apply_patch_for_checkout_commit '', @_ },
APPLY_CHECK => 'apply',
-   VERB => 'Apply',
-   TARGET => ' to index and worktree',
-   PARTICIPLE => 'applying',
FILTER => undef,
IS_REVERSE => 0,
},
-- 
2.11.0.rc0.33.gec17dab



[PATCH v6 04/16] i18n: add--interactive: mark strings with interpolation for translation

2016-11-11 Thread Vasco Almeida
Since at this point Git::I18N.perl lacks support for Perl i18n
placeholder substitution, use of sprintf following die or error_msg is
necessary for placeholder substitution take place.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 25 +
 1 file changed, 13 insertions(+), 12 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 5800010ed..d05ac608e 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -615,12 +615,12 @@ sub list_and_choose {
else {
$bottom = $top = find_unique($choice, @stuff);
if (!defined $bottom) {
-   error_msg "Huh ($choice)?\n";
+   error_msg sprintf(__("Huh (%s)?\n"), 
$choice);
next TOPLOOP;
}
}
if ($opts->{SINGLETON} && $bottom != $top) {
-   error_msg "Huh ($choice)?\n";
+   error_msg sprintf(__("Huh (%s)?\n"), $choice);
next TOPLOOP;
}
for ($i = $bottom-1; $i <= $top-1; $i++) {
@@ -717,7 +717,7 @@ sub revert_cmd {
$_->{INDEX_ADDDEL} eq 'create') {
system(qw(git update-index 
--force-remove --),
   $_->{VALUE});
-   print "note: $_->{VALUE} is untracked 
now.\n";
+   printf(__("note: %s is untracked 
now.\n"), $_->{VALUE});
}
}
}
@@ -1056,7 +1056,7 @@ sub edit_hunk_manually {
my $hunkfile = $repo->repo_path . "/addp-hunk-edit.diff";
my $fh;
open $fh, '>', $hunkfile
-   or die "failed to open hunk edit file for writing: " . $!;
+   or die sprintf(__("failed to open hunk edit file for writing: 
%s"), $!);
print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n";
print $fh @$oldtext;
my $participle = $patch_mode_flavour{PARTICIPLE};
@@ -1083,7 +1083,7 @@ EOF
}
 
open $fh, '<', $hunkfile
-   or die "failed to open hunk edit file for reading: " . $!;
+   or die sprintf(__("failed to open hunk edit file for reading: 
%s"), $!);
my @newtext = grep { !/^#/ } <$fh>;
close $fh;
unlink $hunkfile;
@@ -1236,7 +1236,7 @@ sub apply_patch_for_checkout_commit {
 
 sub patch_update_cmd {
my @all_mods = list_modified($patch_mode_flavour{FILTER});
-   error_msg "ignoring unmerged: $_->{VALUE}\n"
+   error_msg sprintf(__("ignoring unmerged: %s\n"), $_->{VALUE})
for grep { $_->{UNMERGED} } @all_mods;
@all_mods = grep { !$_->{UNMERGED} } @all_mods;
 
@@ -1418,7 +1418,8 @@ sub patch_update_file {
chomp $response;
}
if ($response !~ /^\s*\d+\s*$/) {
-   error_msg "Invalid number: 
'$response'\n";
+   error_msg sprintf(__("Invalid number: 
'%s'\n"),
+$response);
} elsif (0 < $response && $response <= $num) {
$ix = $response - 1;
} else {
@@ -1460,7 +1461,7 @@ sub patch_update_file {
if ($@) {
my ($err,$exp) = ($@, $1);
$err =~ s/ at .*git-add--interactive 
line \d+,  line \d+.*$//;
-   error_msg "Malformed search regexp 
$exp: $err\n";
+   error_msg sprintf(__("Malformed search 
regexp %s: %s\n"), $exp, $err);
next;
}
my $iy = $ix;
@@ -1625,18 +1626,18 @@ sub process_args {
$patch_mode = $1;
$arg = shift @ARGV or die __("missing --");
} else {
-   die "unknown --patch mode: $1";
+   die sprintf(__("unknown --patch mode: %s"), $1);
}
} else {
   

[PATCH v6 09/16] i18n: add--interactive: mark edit_hunk_manually message for translation

2016-11-11 Thread Vasco Almeida
Mark message of edit_hunk_manually displayed in the editing file when
user chooses 'e' option.  The message had to be unfolded to allow
translation of the $participle verb.

Some messages end up being exactly the same for some use cases, but
left it for easier change in the future, e.g., wanting to change wording
of one particular use case.

The comment character is now used according to the git configuration
core.commentchar.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 52 +++
 1 file changed, 39 insertions(+), 13 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 045b847cf..7d1cc5652 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -1058,6 +1058,30 @@ sub color_diff {
} @_;
 }
 
+my %edit_hunk_manually_modes = (
+   stage => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for staging."),
+   stash => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for stashing."),
+   reset_head => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for unstaging."),
+   reset_nothead => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for applying."),
+   checkout_index => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for discarding"),
+   checkout_head => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for discarding."),
+   checkout_nothead => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for applying."),
+);
+
 sub edit_hunk_manually {
my ($oldtext) = @_;
 
@@ -1065,22 +1089,24 @@ sub edit_hunk_manually {
my $fh;
open $fh, '>', $hunkfile
or die sprintf(__("failed to open hunk edit file for writing: 
%s"), $!);
-   print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n";
+   print $fh Git::comment_lines __("Manual hunk edit mode -- see bottom 
for a quick guide.\n");
print $fh @$oldtext;
-   my $participle = $patch_mode_flavour{PARTICIPLE};
my $is_reverse = $patch_mode_flavour{IS_REVERSE};
my ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') : ('+', 
'-');
-   print $fh <<EOF;
-# ---
-# To remove '$remove_minus' lines, make them ' ' lines (context).
-# To remove '$remove_plus' lines, delete them.
-# Lines starting with # will be removed.
-#
-# If the patch applies cleanly, the edited hunk will immediately be
-# marked for $participle. If it does not apply cleanly, you will be given
-# an opportunity to edit again. If all lines of the hunk are removed,
-# then the edit is aborted and the hunk is left unchanged.
+   my $comment_line_char = Git::config("core.commentchar") || '#';
+   print $fh Git::comment_lines sprintf(__ <<EOF, $remove_minus, 
$remove_plus, $comment_line_char),
+---
+To remove '%s' lines, make them ' ' lines (context).
+To remove '%s' lines, delete them.
+Lines starting with %s will be removed.
 EOF
+__($edit_hunk_manually_modes{$patch_mode}),
+# TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
+__ <;
+   my @newtext = grep { !/^$comment_line_char/ } <$fh>;
close $fh;
unlink $hunkfile;
 
-- 
2.11.0.rc0.33.gec17dab



[PATCH v6 07/16] i18n: add--interactive: mark patch prompt for translation

2016-11-11 Thread Vasco Almeida
Mark prompt message assembled in place for translation, unfolding each
use case for each entry in the %patch_modes hash table.

Previously, this script relied on whether $patch_mode was set to run the
command patch_update_cmd() or show status and loop the main loop. Now,
it uses $cmd to indicate we must run patch_update_cmd() and $patch_mode
is used to tell which flavor of the %patch_modes are we on.  This is
introduced in order to be able to mark and unfold the message prompt
knowing in which context we are.

The tracking of context was done previously by point %patch_mode_flavour
hash table to the correct entry of %patch_modes, focusing only on value
of %patch_modes. Now, we are also interested in the key ('staged',
'stash', 'checkout_head', ...).

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 Makefile  |  2 +-
 git-add--interactive.perl | 54 ---
 perl/Git/I18N.pm  | 11 +-
 t/t0202/test.pl   |  5 -
 4 files changed, 61 insertions(+), 11 deletions(-)

diff --git a/Makefile b/Makefile
index 328151dd6..ee89c0624 100644
--- a/Makefile
+++ b/Makefile
@@ -2114,7 +2114,7 @@ XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --language=C \
 XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell \
--keyword=gettextln --keyword=eval_gettextln
 XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --language=Perl \
-   --keyword=__ --keyword="__n:1,2"
+   --keyword=__ --keyword=N__ --keyword="__n:1,2"
 LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
 LOCALIZED_SH = $(SCRIPT_SH)
 LOCALIZED_SH += git-parse-remote.sh
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index cd617837b..b7d382b10 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -93,6 +93,7 @@ sub colored {
 }
 
 # command line options
+my $cmd;
 my $patch_mode;
 my $patch_mode_revision;
 
@@ -173,7 +174,8 @@ my %patch_modes = (
},
 );
 
-my %patch_mode_flavour = %{$patch_modes{stage}};
+$patch_mode = 'stage';
+my %patch_mode_flavour = %{$patch_modes{$patch_mode}};
 
 sub run_cmd_pipe {
if ($^O eq 'MSWin32') {
@@ -1311,6 +1313,44 @@ sub display_hunks {
return $i;
 }
 
+my %patch_update_prompt_modes = (
+   stage => {
+   mode => N__("Stage mode change [y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Stage deletion [y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Stage this hunk [y,n,q,a,d,/%s,?]? "),
+   },
+   stash => {
+   mode => N__("Stash mode change [y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Stash deletion [y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Stash this hunk [y,n,q,a,d,/%s,?]? "),
+   },
+   reset_head => {
+   mode => N__("Unstage mode change [y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Unstage deletion [y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Unstage this hunk [y,n,q,a,d,/%s,?]? "),
+   },
+   reset_nothead => {
+   mode => N__("Apply mode change to index [y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Apply deletion to index [y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Apply this hunk to index [y,n,q,a,d,/%s,?]? "),
+   },
+   checkout_index => {
+   mode => N__("Discard mode change from worktree 
[y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Discard deletion from worktree 
[y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Discard this hunk from worktree [y,n,q,a,d,/%s,?]? 
"),
+   },
+   checkout_head => {
+   mode => N__("Discard mode change from index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Discard deletion from index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Discard this hunk from index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   },
+   checkout_nothead => {
+   mode => N__("Apply mode change to index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Apply deletion to index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Apply this hunk to index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   },
+);
+
 sub patch_update_file {
my $quit = 0;
my ($ix, $num);
@@ -1383,12 +1423,9 @@ sub patch_update_file {
for (@{$hunk[$ix]{DISPLAY}}) {
print;
}
-   print colored $prompt_color, $patch_mode_flavour{VERB},
- ($hunk[$ix]{TYPE} eq 'mode' ? ' mode change' :
-  $hunk[$ix]{TYPE} eq 'deletion' ? ' deletion' :
-  ' this hunk'),
- 

[PATCH v6 13/16] i18n: send-email: mark warnings and errors for translation

2016-11-11 Thread Vasco Almeida
Mark warnings, errors and other messages for translation.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-send-email.perl | 34 +-
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/git-send-email.perl b/git-send-email.perl
index 06e64699b..00d234e11 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -118,20 +118,20 @@ sub format_2822_time {
my $localmin = $localtm[1] + $localtm[2] * 60;
my $gmtmin = $gmttm[1] + $gmttm[2] * 60;
if ($localtm[0] != $gmttm[0]) {
-   die "local zone differs from GMT by a non-minute interval\n";
+   die __("local zone differs from GMT by a non-minute 
interval\n");
}
if ((($gmttm[6] + 1) % 7) == $localtm[6]) {
$localmin += 1440;
} elsif ((($gmttm[6] - 1) % 7) == $localtm[6]) {
$localmin -= 1440;
} elsif ($gmttm[6] != $localtm[6]) {
-   die "local time offset greater than or equal to 24 hours\n";
+   die __("local time offset greater than or equal to 24 hours\n");
}
my $offset = $localmin - $gmtmin;
my $offhour = $offset / 60;
my $offmin = abs($offset % 60);
if (abs($offhour) >= 24) {
-   die ("local time offset greater than or equal to 24 hours\n");
+   die __("local time offset greater than or equal to 24 hours\n");
}
 
return sprintf("%s, %2d %s %d %02d:%02d:%02d %s%02d%02d",
@@ -199,13 +199,13 @@ sub do_edit {
map {
system('sh', '-c', $editor.' "$@"', $editor, $_);
if (($? & 127) || ($? >> 8)) {
-   die("the editor exited uncleanly, aborting 
everything");
+   die(__("the editor exited uncleanly, aborting 
everything"));
}
} @_;
} else {
system('sh', '-c', $editor.' "$@"', $editor, @_);
if (($? & 127) || ($? >> 8)) {
-   die("the editor exited uncleanly, aborting everything");
+   die(__("the editor exited uncleanly, aborting 
everything"));
}
}
 }
@@ -299,7 +299,7 @@ my $help;
 my $rc = GetOptions("h" => \$help,
 "dump-aliases" => \$dump_aliases);
 usage() unless $rc;
-die "--dump-aliases incompatible with other options\n"
+die __("--dump-aliases incompatible with other options\n")
 if !$help and $dump_aliases and @ARGV;
 $rc = GetOptions(
"sender|from=s" => \$sender,
@@ -362,7 +362,7 @@ unless ($rc) {
 usage();
 }
 
-die "Cannot run git format-patch from outside a repository\n"
+die __("Cannot run git format-patch from outside a repository\n")
if $format_patch and not $repo;
 
 # Now, let's fill any that aren't set in with defaults:
@@ -617,7 +617,7 @@ while (defined(my $f = shift @ARGV)) {
 }
 
 if (@rev_list_opts) {
-   die "Cannot run git format-patch from outside a repository\n"
+   die __("Cannot run git format-patch from outside a repository\n")
unless $repo;
push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 
1), @rev_list_opts);
 }
@@ -638,7 +638,7 @@ if (@files) {
print $_,"\n" for (@files);
}
 } else {
-   print STDERR "\nNo patch files specified!\n\n";
+   print STDERR __("\nNo patch files specified!\n\n");
usage();
 }
 
@@ -730,7 +730,7 @@ EOT
$sender = $1;
next;
} elsif (/^(?:To|Cc|Bcc):/i) {
-   print "To/Cc/Bcc fields are not interpreted yet, they 
have been ignored\n";
+   print __("To/Cc/Bcc fields are not interpreted yet, 
they have been ignored\n");
next;
}
print $c2 $_;
@@ -739,7 +739,7 @@ EOT
close $c2;
 
if ($summary_empty) {
-   print "Summary email is empty, skipping it\n";
+   print __("Summary email is empty, skipping it\n");
$compose = -1;
}
 } elsif ($annotate) {
@@ -1316,7 +1316,7 @@ EOF
$_ = ask(__("Send this email? ([y]es|[n]o|[q]uit|[a]ll): "),
 valid_re => qr/^(?:yes|y|no|n|quit|q|all|a)/i,
 default => $ask_default);
-   die "Send this email reply required" unless defined $_;
+   die __("Send this email reply required") unless defined $_;
if (/^n/i) {
 

[PATCH v6 08/16] i18n: add--interactive: i18n of help_patch_cmd

2016-11-11 Thread Vasco Almeida
Mark help message of help_patch_cmd for translation.  The message must
be unfolded to be free of variables so we can have high quality
translations.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 54 ---
 1 file changed, 46 insertions(+), 8 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index b7d382b10..045b847cf 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -1189,15 +1189,53 @@ sub edit_hunk_loop {
}
 }
 
+my %help_patch_modes = (
+   stage => N__(
+"y - stage this hunk
+n - do not stage this hunk
+q - quit; do not stage this hunk or any of the remaining ones
+a - stage this hunk and all later hunks in the file
+d - do not stage this hunk or any of the later hunks in the file"),
+   stash => N__(
+"y - stash this hunk
+n - do not stash this hunk
+q - quit; do not stash this hunk or any of the remaining ones
+a - stash this hunk and all later hunks in the file
+d - do not stash this hunk or any of the later hunks in the file"),
+   reset_head => N__(
+"y - unstage this hunk
+n - do not unstage this hunk
+q - quit; do not unstage this hunk or any of the remaining ones
+a - unstage this hunk and all later hunks in the file
+d - do not unstage this hunk or any of the later hunks in the file"),
+   reset_nothead => N__(
+"y - apply this hunk to index
+n - do not apply this hunk to index
+q - quit; do not apply this hunk or any of the remaining ones
+a - apply this hunk and all later hunks in the file
+d - do not apply this hunk or any of the later hunks in the file"),
+   checkout_index => N__(
+"y - discard this hunk from worktree
+n - do not discard this hunk from worktree
+q - quit; do not discard this hunk or any of the remaining ones
+a - discard this hunk and all later hunks in the file
+d - do not discard this hunk or any of the later hunks in the file"),
+   checkout_head => N__(
+"y - discard this hunk from index and worktree
+n - do not discard this hunk from index and worktree
+q - quit; do not discard this hunk or any of the remaining ones
+a - discard this hunk and all later hunks in the file
+d - do not discard this hunk or any of the later hunks in the file"),
+   checkout_nothead => N__(
+"y - apply this hunk to index and worktree
+n - do not apply this hunk to index and worktree
+q - quit; do not apply this hunk or any of the remaining ones
+a - apply this hunk and all later hunks in the file
+d - do not apply this hunk or any of the later hunks in the file"),
+);
+
 sub help_patch_cmd {
-   my $verb = lc $patch_mode_flavour{VERB};
-   my $target = $patch_mode_flavour{TARGET};
-   print colored $help_color, <

[PATCH v6 14/16] i18n: send-email: mark string with interpolation for translation

2016-11-11 Thread Vasco Almeida
Mark warnings, errors and other messages that are interpolated for
translation.

We call sprintf() before calling die() and in few other circumstances in
order to replace the values on the placeholders.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-send-email.perl | 87 +
 1 file changed, 47 insertions(+), 40 deletions(-)

diff --git a/git-send-email.perl b/git-send-email.perl
index 00d234e11..7f3297cdf 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -279,10 +279,13 @@ sub signal_handler {
# tmp files from --compose
if (defined $compose_filename) {
if (-e $compose_filename) {
-   print "'$compose_filename' contains an intermediate 
version of the email you were composing.\n";
+   printf __("'%s' contains an intermediate version ".
+ "of the email you were composing.\n"),
+ $compose_filename;
}
if (-e ($compose_filename . ".final")) {
-   print "'$compose_filename.final' contains the composed 
email.\n"
+   printf __("'%s.final' contains the composed email.\n"),
+ $compose_filename;
}
}
 
@@ -431,7 +434,7 @@ $smtp_encryption = '' unless (defined $smtp_encryption);
 my(%suppress_cc);
 if (@suppress_cc) {
foreach my $entry (@suppress_cc) {
-   die "Unknown --suppress-cc field: '$entry'\n"
+   die sprintf(__("Unknown --suppress-cc field: '%s'\n"), $entry)
unless $entry =~ 
/^(?:all|cccmd|cc|author|self|sob|body|bodycc)$/;
$suppress_cc{$entry} = 1;
}
@@ -460,7 +463,7 @@ my $confirm_unconfigured = !defined $confirm;
 if ($confirm_unconfigured) {
$confirm = scalar %suppress_cc ? 'compose' : 'auto';
 };
-die "Unknown --confirm setting: '$confirm'\n"
+die sprintf(__("Unknown --confirm setting: '%s'\n"), $confirm)
unless $confirm =~ /^(?:auto|cc|compose|always|never)/;
 
 # Debugging, print out the suppressions.
@@ -492,16 +495,16 @@ my %aliases;
 sub parse_sendmail_alias {
local $_ = shift;
if (/"/) {
-   print STDERR "warning: sendmail alias with quotes is not 
supported: $_\n";
+   printf STDERR __("warning: sendmail alias with quotes is not 
supported: %s\n"), $_;
} elsif (/:include:/) {
-   print STDERR "warning: `:include:` not supported: $_\n";
+   printf STDERR __("warning: `:include:` not supported: %s\n"), 
$_;
} elsif (/[\/|]/) {
-   print STDERR "warning: `/file` or `|pipe` redirection not 
supported: $_\n";
+   printf STDERR __("warning: `/file` or `|pipe` redirection not 
supported: %s\n"), $_;
} elsif (/^(\S+?)\s*:\s*(.+)$/) {
my ($alias, $addr) = ($1, $2);
$aliases{$alias} = [ split_addrs($addr) ];
} else {
-   print STDERR "warning: sendmail line is not recognized: $_\n";
+   printf STDERR __("warning: sendmail line is not recognized: 
%s\n"), $_;
}
 }
 
@@ -582,11 +585,11 @@ sub is_format_patch_arg {
if (defined($format_patch)) {
return $format_patch;
}
-   die(<<EOF);
-File '$f' exists but it could also be the range of commits
+   die sprintf(__ <<EOF, $f, $f);
+File '%s' exists but it could also be the range of commits
 to produce patches for.  Please disambiguate by...
 
-* Saying "./$f" if you mean a file; or
+* Saying "./%s" if you mean a file; or
 * Giving --format-patch option if you mean a range.
 EOF
} catch Git::Error::Command with {
@@ -604,7 +607,7 @@ while (defined(my $f = shift @ARGV)) {
@ARGV = ();
} elsif (-d $f and !is_format_patch_arg($f)) {
opendir my $dh, $f
-   or die "Failed to opendir $f: $!";
+   or die sprintf(__("Failed to opendir %s: %s"), $f, $!);
 
push @files, grep { -f $_ } map { catfile($f, $_) }
sort readdir $dh;
@@ -628,7 +631,8 @@ if ($validate) {
foreach my $f (@files) {
unless (-p $f) {
my $error = validate_patch($f);
-   $error and die "fatal: $f: $error\nwarning: no patches 
were sent\n";
+   $error and die sprintf(__("fatal: %s: %s\nwarning: no 
patches were sent\n"),
+ $f, $error);
}
}

[PATCH v6 05/16] i18n: clean.c: match string with git-add--interactive.perl

2016-11-11 Thread Vasco Almeida
Change strings for help to match the ones in git-add--interactive.perl.
The strings now represent one entry to translate each rather then two
entries each different only by an ending newline character.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 builtin/clean.c | 10 +-
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/builtin/clean.c b/builtin/clean.c
index 0371010af..d6bc3aaae 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -287,11 +287,11 @@ static void pretty_print_menus(struct string_list 
*menu_list)
 static void prompt_help_cmd(int singleton)
 {
clean_print_color(CLEAN_COLOR_HELP);
-   printf_ln(singleton ?
+   printf(singleton ?
  _("Prompt help:\n"
"1  - select a numbered item\n"
"foo- select item based on unique prefix\n"
-   "   - (empty) select nothing") :
+   "   - (empty) select nothing\n") :
  _("Prompt help:\n"
"1  - select a single item\n"
"3-5- select a range of items\n"
@@ -299,7 +299,7 @@ static void prompt_help_cmd(int singleton)
"foo- select item based on unique prefix\n"
"-...   - unselect specified items\n"
"*  - choose all items\n"
-   "   - (empty) finish selecting"));
+   "   - (empty) finish selecting\n"));
clean_print_color(CLEAN_COLOR_RESET);
 }
 
@@ -508,7 +508,7 @@ static int parse_choice(struct menu_stuff *menu_stuff,
if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > 
top ||
(is_single && bottom != top)) {
clean_print_color(CLEAN_COLOR_ERROR);
-   printf_ln(_("Huh (%s)?"), (*ptr)->buf);
+   printf(_("Huh (%s)?\n"), (*ptr)->buf);
clean_print_color(CLEAN_COLOR_RESET);
continue;
}
@@ -774,7 +774,7 @@ static int ask_each_cmd(void)
 static int quit_cmd(void)
 {
string_list_clear(_list, 0);
-   printf_ln(_("Bye."));
+   printf(_("Bye.\n"));
return MENU_RETURN_NO_LOOP;
 }
 
-- 
2.11.0.rc0.33.gec17dab



[PATCH v6 03/16] i18n: add--interactive: mark simple here-documents for translation

2016-11-11 Thread Vasco Almeida
Mark messages in here-documents without interpolation for translation.

The here-document delimiter \EOF, which is the same as 'EOF', indicates
that the text is to be treated literally without interpolation of its
content.  Unfortunately xgettext is not able to extract here-documents
delimited with \EOF but it is with delimiter enclosed in single quotes.
So change \EOF to 'EOF', although in this case does not make
difference what variation of here-document to use since there is nothing
to interpolate.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 8 +---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index cf216ecb6..5800010ed 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -639,7 +639,7 @@ sub list_and_choose {
 }
 
 sub singleton_prompt_help_cmd {
-   print colored $help_color, <<\EOF ;
+   print colored $help_color, __ <<'EOF' ;
 Prompt help:
 1  - select a numbered item
 foo- select item based on unique prefix
@@ -648,7 +648,7 @@ EOF
 }
 
 sub prompt_help_cmd {
-   print colored $help_color, <<\EOF ;
+   print colored $help_color, __ <<'EOF' ;
 Prompt help:
 1  - select a single item
 3-5- select a range of items
@@ -1584,7 +1584,9 @@ sub quit_cmd {
 }
 
 sub help_cmd {
-   print colored $help_color, <<\EOF ;
+# TRANSLATORS: please do not translate the command names
+# 'status', 'update', 'revert', etc.
+   print colored $help_color, __ <<'EOF' ;
 status- show paths with changes
 update- add working tree state to the staged set of changes
 revert- revert staged set of changes back to the HEAD version
-- 
2.11.0.rc0.33.gec17dab



[PATCH v6 16/16] i18n: difftool: mark warnings for translation

2016-11-11 Thread Vasco Almeida
Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-difftool.perl | 22 --
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/git-difftool.perl b/git-difftool.perl
index a5790d03a..8d3632e55 100755
--- a/git-difftool.perl
+++ b/git-difftool.perl
@@ -22,6 +22,7 @@ use File::Path qw(mkpath rmtree);
 use File::Temp qw(tempdir);
 use Getopt::Long qw(:config pass_through);
 use Git;
+use Git::I18N;
 
 sub usage
 {
@@ -122,7 +123,7 @@ sub setup_dir_diff
my $i = 0;
while ($i < $#rawdiff) {
if ($rawdiff[$i] =~ /^::/) {
-   warn << 'EOF';
+   warn __ <<'EOF';
 Combined diff formats ('-c' and '--cc') are not supported in
 directory diff mode ('-d' and '--dir-diff').
 EOF
@@ -338,7 +339,7 @@ sub main
if (length($opts{difftool_cmd}) > 0) {
$ENV{GIT_DIFF_TOOL} = $opts{difftool_cmd};
} else {
-   print "No  given for --tool=\n";
+   print __("No  given for --tool=\n");
usage(1);
}
}
@@ -346,7 +347,7 @@ sub main
if (length($opts{extcmd}) > 0) {
$ENV{GIT_DIFFTOOL_EXTCMD} = $opts{extcmd};
} else {
-   print "No  given for --extcmd=\n";
+   print __("No  given for --extcmd=\n");
usage(1);
}
}
@@ -419,11 +420,11 @@ sub dir_diff
}
 
if (exists $wt_modified{$file} and exists $tmp_modified{$file}) 
{
-   my $errmsg = "warning: Both files modified: ";
-   $errmsg .= "'$workdir/$file' and '$b/$file'.\n";
-   $errmsg .= "warning: Working tree file has been 
left.\n";
-   $errmsg .= "warning:\n";
-   warn $errmsg;
+   warn sprintf(__(
+   "warning: Both files modified:\n" .
+   "'%s/%s' and '%s/%s'.\n" .
+   "warning: Working tree file has been left.\n" .
+   "warning:\n"), $workdir, $file, $b, $file);
$error = 1;
} elsif (exists $tmp_modified{$file}) {
my $mode = stat("$b/$file")->mode;
@@ -435,8 +436,9 @@ sub dir_diff
}
}
if ($error) {
-   warn "warning: Temporary files exist in '$tmpdir'.\n";
-   warn "warning: You may want to cleanup or recover these.\n";
+   warn sprintf(__(
+   "warning: Temporary files exist in '%s'.\n" .
+   "warning: You may want to cleanup or recover 
these.\n"), $tmpdir);
exit(1);
} else {
exit_cleanup($tmpdir, $rc);
-- 
2.11.0.rc0.33.gec17dab



[PATCH v6 06/16] i18n: add--interactive: mark plural strings

2016-11-11 Thread Vasco Almeida
Mark plural strings for translation.  Unfold each action case in one
entire sentence.

Pass new keyword for xgettext to extract.

Update test to include new subroutine __n() for plural strings handling.

Update documentation to include a description of the new __n()
subroutine.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 Makefile  |  3 ++-
 git-add--interactive.perl | 27 ++-
 perl/Git/I18N.pm  | 10 +-
 t/t0202/test.pl   | 11 ++-
 4 files changed, 39 insertions(+), 12 deletions(-)

diff --git a/Makefile b/Makefile
index 9d6c24503..328151dd6 100644
--- a/Makefile
+++ b/Makefile
@@ -2113,7 +2113,8 @@ XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --language=C \
--keyword=_ --keyword=N_ --keyword="Q_:1,2"
 XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell \
--keyword=gettextln --keyword=eval_gettextln
-XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --keyword=__ --language=Perl
+XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --language=Perl \
+   --keyword=__ --keyword="__n:1,2"
 LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
 LOCALIZED_SH = $(SCRIPT_SH)
 LOCALIZED_SH += git-parse-remote.sh
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index d05ac608e..cd617837b 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -669,12 +669,18 @@ sub status_cmd {
 sub say_n_paths {
my $did = shift @_;
my $cnt = scalar @_;
-   print "$did ";
-   if (1 < $cnt) {
-   print "$cnt paths\n";
-   }
-   else {
-   print "one path\n";
+   if ($did eq 'added') {
+   printf(__n("added %d path\n", "added %d paths\n",
+  $cnt), $cnt);
+   } elsif ($did eq 'updated') {
+   printf(__n("updated %d path\n", "updated %d paths\n",
+  $cnt), $cnt);
+   } elsif ($did eq 'reverted') {
+   printf(__n("reverted %d path\n", "reverted %d paths\n",
+  $cnt), $cnt);
+   } else {
+   printf(__n("touched %d path\n", "touched %d paths\n",
+  $cnt), $cnt);
}
 }
 
@@ -1423,7 +1429,8 @@ sub patch_update_file {
} elsif (0 < $response && $response <= $num) {
$ix = $response - 1;
} else {
-   error_msg "Sorry, only $num hunks 
available.\n";
+   error_msg sprintf(__n("Sorry, only %d 
hunk available.\n",
+ "Sorry, only %d 
hunks available.\n", $num), $num);
}
next;
}
@@ -1518,8 +1525,10 @@ sub patch_update_file {
elsif ($other =~ /s/ && $line =~ /^s/) {
my @split = split_hunk($hunk[$ix]{TEXT}, 
$hunk[$ix]{DISPLAY});
if (1 < @split) {
-   print colored $header_color, "Split 
into ",
-   scalar(@split), " hunks.\n";
+   print colored $header_color, sprintf(
+   __n("Split into %d hunk.\n",
+   "Split into %d hunks.\n",
+   scalar(@split)), 
scalar(@split));
}
splice (@hunk, $ix, 1, @split);
$num = scalar @hunk;
diff --git a/perl/Git/I18N.pm b/perl/Git/I18N.pm
index f889fd6da..617d8c2a1 100644
--- a/perl/Git/I18N.pm
+++ b/perl/Git/I18N.pm
@@ -13,7 +13,7 @@ BEGIN {
}
 }
 
-our @EXPORT = qw(__);
+our @EXPORT = qw(__ __n);
 our @EXPORT_OK = @EXPORT;
 
 sub __bootstrap_locale_messages {
@@ -44,6 +44,7 @@ BEGIN
eval {
__bootstrap_locale_messages();
*__ = \::Messages::gettext;
+   *__n = \::Messages::ngettext;
1;
} or do {
# Tell test.pl that we couldn't load the gettext library.
@@ -51,6 +52,7 @@ BEGIN
 
# Just a fall-through no-op
*__ = sub ($) { $_[0] };
+   *__n = sub ($$$) { $_[2] == 1 ? $_[0] : $_[1] };
};
 }
 
@@ -70,6 +72,8 @@ Git::I18N - Perl interface to Git's Gettext localizations
 
printf __("The following error occurred: %s\n"), $error;
 
+   printf __n("commited %d file\n", "commited %d files\n", $files), $files;
+
 =head1 DESCRIPTION
 

[PATCH v6 12/16] i18n: send-email: mark strings for translation

2016-11-11 Thread Vasco Almeida
Mark strings often displayed to the user for translation.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-send-email.perl | 54 +++--
 1 file changed, 32 insertions(+), 22 deletions(-)

diff --git a/git-send-email.perl b/git-send-email.perl
index da81be40c..06e64699b 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -28,6 +28,7 @@ use File::Temp qw/ tempdir tempfile /;
 use File::Spec::Functions qw(catfile);
 use Error qw(:try);
 use Git;
+use Git::I18N;
 
 Getopt::Long::Configure qw/ pass_through /;
 
@@ -797,12 +798,12 @@ foreach my $f (@files) {
 }
 
 if (!defined $auto_8bit_encoding && scalar %broken_encoding) {
-   print "The following files are 8bit, but do not declare " .
-   "a Content-Transfer-Encoding.\n";
+   print __("The following files are 8bit, but do not declare " .
+"a Content-Transfer-Encoding.\n");
foreach my $f (sort keys %broken_encoding) {
print "$f\n";
}
-   $auto_8bit_encoding = ask("Which 8bit encoding should I declare 
[UTF-8]? ",
+   $auto_8bit_encoding = ask(__("Which 8bit encoding should I declare 
[UTF-8]? "),
  valid_re => qr/.{4}/, confirm_only => 1,
  default => "UTF-8");
 }
@@ -829,7 +830,7 @@ if (defined $sender) {
 # But it's a no-op to run sanitize_address on an already sanitized address.
 $sender = sanitize_address($sender);
 
-my $to_whom = "To whom should the emails be sent (if anyone)?";
+my $to_whom = __("To whom should the emails be sent (if anyone)?");
 my $prompting = 0;
 if (!@initial_to && !defined $to_cmd) {
my $to = ask("$to_whom ",
@@ -859,7 +860,7 @@ sub expand_one_alias {
 
 if ($thread && !defined $initial_reply_to && $prompting) {
$initial_reply_to = ask(
-   "Message-ID to be used as In-Reply-To for the first email (if 
any)? ",
+   __("Message-ID to be used as In-Reply-To for the first email 
(if any)? "),
default => "",
valid_re => qr/\@.*\./, confirm_only => 1);
 }
@@ -918,7 +919,10 @@ sub validate_address {
my $address = shift;
while (!extract_valid_address($address)) {
print STDERR "error: unable to extract a valid address from: 
$address\n";
-   $_ = ask("What to do with this address? ([q]uit|[d]rop|[e]dit): 
",
+   # TRANSLATORS: Make sure to include [q] [d] [e] in your
+   # translation. The program will only accept English input
+   # at this point.
+   $_ = ask(__("What to do with this address? 
([q]uit|[d]rop|[e]dit): "),
valid_re => qr/^(?:quit|q|drop|d|edit|e)/i,
default => 'q');
if (/^d/i) {
@@ -1293,17 +1297,23 @@ Message-Id: $message_id
if ($needs_confirm eq "inform") {
$confirm_unconfigured = 0; # squelch this message for 
the rest of this run
$ask_default = "y"; # assume yes on EOF since user 
hasn't explicitly asked for confirmation
-   print "The Cc list above has been expanded by 
additional\n";
-   print "addresses found in the patch commit message. 
By default\n";
-   print "send-email prompts before sending whenever 
this occurs.\n";
-   print "This behavior is controlled by the 
sendemail.confirm\n";
-   print "configuration setting.\n";
-   print "\n";
-   print "For additional information, run 'git 
send-email --help'.\n";
-   print "To retain the current behavior, but squelch 
this message,\n";
-   print "run 'git config --global sendemail.confirm 
auto'.\n\n";
+   print __ < qr/^(?:yes|y|no|n|quit|q|all|a)/i,
 default => $ask_default);
die "Send this email reply required" unless defined $_;
@@ -1405,7 +1415,7 @@ Message-Id: $message_id
if ($quiet) {
printf (($dry_run ? "Dry-" : "")."Sent %s\n", $subject);
} else {
-   print (($dry_run ? "Dry-" : "")."OK. Log says:\n");
+   print($dry_run ? __("Dry-OK. Log says:\n") : __("OK. Log 
says:\n"));
if (!file_name_is_absolute($smtp_server)) {
print "Server: $smtp_server\n"

[PATCH v6 02/16] i18n: add--interactive: mark strings for translation

2016-11-11 Thread Vasco Almeida
Mark simple strings (without interpolation) for translation.

Brackets around first parameter of ternary operator is necessary because
otherwise xgettext fails to extract strings marked for translation from
the rest of the file.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 76 ++-
 1 file changed, 42 insertions(+), 34 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index ee3d81269..cf216ecb6 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -4,6 +4,7 @@ use 5.008;
 use strict;
 use warnings;
 use Git;
+use Git::I18N;
 
 binmode(STDOUT, ":raw");
 
@@ -253,8 +254,9 @@ sub list_untracked {
run_cmd_pipe(qw(git ls-files --others --exclude-standard --), @ARGV);
 }
 
-my $status_fmt = '%12s %12s %s';
-my $status_head = sprintf($status_fmt, 'staged', 'unstaged', 'path');
+# TRANSLATORS: you can adjust this to align "git add -i" status menu
+my $status_fmt = __('%12s %12s %s');
+my $status_head = sprintf($status_fmt, __('staged'), __('unstaged'), 
__('path'));
 
 {
my $initial;
@@ -680,7 +682,7 @@ sub update_cmd {
my @mods = list_modified('file-only');
return if (!@mods);
 
-   my @update = list_and_choose({ PROMPT => 'Update',
+   my @update = list_and_choose({ PROMPT => __('Update'),
   HEADER => $status_head, },
 @mods);
if (@update) {
@@ -692,7 +694,7 @@ sub update_cmd {
 }
 
 sub revert_cmd {
-   my @update = list_and_choose({ PROMPT => 'Revert',
+   my @update = list_and_choose({ PROMPT => __('Revert'),
   HEADER => $status_head, },
 list_modified());
if (@update) {
@@ -726,13 +728,13 @@ sub revert_cmd {
 }
 
 sub add_untracked_cmd {
-   my @add = list_and_choose({ PROMPT => 'Add untracked' },
+   my @add = list_and_choose({ PROMPT => __('Add untracked') },
  list_untracked());
if (@add) {
system(qw(git update-index --add --), @add);
say_n_paths('added', @add);
} else {
-   print "No untracked files.\n";
+   print __("No untracked files.\n");
}
print "\n";
 }
@@ -1166,8 +1168,14 @@ sub edit_hunk_loop {
}
else {
prompt_yesno(
-   'Your edited hunk does not apply. Edit again '
-   . '(saying "no" discards!) [y/n]? '
+   # TRANSLATORS: do not translate [y/n]
+   # The program will only accept that input
+   # at this point.
+   # Consider translating (saying "no" discards!) 
as
+   # (saying "n" for "no" discards!) if the 
translation
+   # of the word "no" does not start with n.
+   __('Your edited hunk does not apply. Edit again 
'
+  . '(saying "no" discards!) [y/n]? ')
) or return undef;
}
}
@@ -1213,11 +1221,11 @@ sub apply_patch_for_checkout_commit {
run_git_apply 'apply '.$reverse, @_;
return 1;
} elsif (!$applies_index) {
-   print colored $error_color, "The selected hunks do not apply to 
the index!\n";
-   if (prompt_yesno "Apply them to the worktree anyway? ") {
+   print colored $error_color, __("The selected hunks do not apply 
to the index!\n");
+   if (prompt_yesno __("Apply them to the worktree anyway? ")) {
return run_git_apply 'apply '.$reverse, @_;
} else {
-   print colored $error_color, "Nothing was applied.\n";
+   print colored $error_color, __("Nothing was 
applied.\n");
return 0;
}
} else {
@@ -1237,9 +1245,9 @@ sub patch_update_cmd {
 
if (!@mods) {
if (@all_mods) {
-   print STDERR "Only binary files changed.\n";
+   print STDERR __("Only binary files changed.\n");
} else {
-   print STDERR "No changes.\n";
+   print STDERR __("No changes.\n");
}
return 0;
}
@@ -1247,7 +1255,7 @@ sub patch_update_cmd {
@them = @mods;
}
else {
-   @them = list_and_choose({ PROMPT =>

[PATCH v6 00/16] Mark strings in Perl scripts for translation

2016-11-11 Thread Vasco Almeida
Mark messages in some perl scripts for translation.

Changes in this re-roll v6:
- Change implementation of prefix_lines subroutine to allow arbitrary
  number of strings as arguments.
- Change a few marks for translation hopefully to be easier on the eyes.

Interdiff included below.


Vasco Almeida (16):
  Git.pm: add subroutines for commenting lines
  i18n: add--interactive: mark strings for translation
  i18n: add--interactive: mark simple here-documents for translation
  i18n: add--interactive: mark strings with interpolation for
translation
  i18n: clean.c: match string with git-add--interactive.perl
  i18n: add--interactive: mark plural strings
  i18n: add--interactive: mark patch prompt for translation
  i18n: add--interactive: i18n of help_patch_cmd
  i18n: add--interactive: mark edit_hunk_manually message for
translation
  i18n: add--interactive: remove %patch_modes entries
  i18n: add--interactive: mark status words for translation
  i18n: send-email: mark strings for translation
  i18n: send-email: mark warnings and errors for translation
  i18n: send-email: mark string with interpolation for translation
  i18n: send-email: mark composing message for translation
  i18n: difftool: mark warnings for translation

 Makefile  |   3 +-
 builtin/clean.c   |  10 +-
 git-add--interactive.perl | 329 ++
 git-difftool.perl |  22 ++--
 git-send-email.perl   | 191 +++
 perl/Git.pm   |  24 
 perl/Git/I18N.pm  |  19 ++-
 t/t0202/test.pl   |  14 +-
 8 files changed, 394 insertions(+), 218 deletions(-)

-- >8 --
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 56e6889..3a6d846 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -1068,22 +1068,24 @@ sub edit_hunk_manually {
my $fh;
open $fh, '>', $hunkfile
or die sprintf(__("failed to open hunk edit file for writing: 
%s"), $!);
-   print $fh Git::comment_lines __("Manual hunk edit mode -- see bottom 
for a quick guide\n");
+   print $fh Git::comment_lines __("Manual hunk edit mode -- see bottom 
for a quick guide.\n");
print $fh @$oldtext;
my $is_reverse = $patch_mode_flavour{IS_REVERSE};
my ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') : ('+', 
'-');
my $comment_line_char = Git::config("core.commentchar") || '#';
-   print $fh Git::comment_lines sprintf(__(
-"---
+   print $fh Git::comment_lines sprintf(__ <<EOF, $remove_minus, 
$remove_plus, $comment_line_char),
+---
 To remove '%s' lines, make them ' ' lines (context).
 To remove '%s' lines, delete them.
 Lines starting with %s will be removed.
-\n"), $remove_minus, $remove_plus, $comment_line_char) .
-__($edit_hunk_manually_modes{$patch_mode}) ."\n". __(
+EOF
+__($edit_hunk_manually_modes{$patch_mode}),
 # TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
-"If it does not apply cleanly, you will be given an opportunity to
+__ <", $compose_filename . ".final"
-   or die sprintf(__("Failed to open %s.final : %s"), 
$compose_filename, $!);
+   or die sprintf(__("Failed to open %s.final: %s"), 
$compose_filename, $!);
 
open $c, "<", $compose_filename
-   or die sprintf(__("Failed to open %s : %s"), $compose_filename, 
$!);
+   or die sprintf(__("Failed to open %s: %s"), $compose_filename, 
$!);
 
my $need_8bit_cte = file_has_nonascii($compose_filename);
my $in_body = 0;
@@ -1304,8 +1305,8 @@ Message-Id: $message_id
if ($needs_confirm eq "inform") {
$confirm_unconfigured = 0; # squelch this message for 
the rest of this run
$ask_default = "y"; # assume yes on EOF since user 
hasn't explicitly asked for confirmation
-   print __(
-"The Cc list above has been expanded by additional
+   print __ < with C.
 
 =cut
 
 sub prefix_lines {
-   my ($prefix, $string) = @_;
+   my $prefix = shift;
+   my $string = join("\n", @_);
$string =~ s/^/$prefix/mg;
return $string;
 }
 
-=item comment_lines ( STRING )
+=item comment_lines ( STRING [, STRING... ])
 
 Comments lines following core.commentchar configuration.
 
-- >8 --

-- 
2.11.0.rc0.33.gec17dab



Re: [PATCH v5 01/16] Git.pm: add subroutines for commenting lines

2016-11-09 Thread Vasco Almeida
A Ter, 08-11-2016 às 17:06 -0800, Junio C Hamano escreveu:
> Vasco Almeida <vascomalme...@sapo.pt> writes:
> 
> > 
> > Add subroutines prefix_lines and comment_lines.
> > 
> > Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
> > ---
> >  perl/Git.pm | 23 +++
> >  1 file changed, 23 insertions(+)
> > 
> > diff --git a/perl/Git.pm b/perl/Git.pm
> > index b2732822a..17be59fb7 100644
> > --- a/perl/Git.pm
> > +++ b/perl/Git.pm
> > @@ -1438,6 +1438,29 @@ sub END {
> >  
> >  } # %TEMP_* Lexical Context
> >  
> > +=item prefix_lines ( PREFIX, STRING )
> > +
> > +Prefixes lines in C with C.
> > +
> > +=cut
> > +
> > +sub prefix_lines {
> > +   my ($prefix, $string) = @_;
> > +   $string =~ s/^/$prefix/mg;
> > +   return $string;
> > +}
> > +
> > +=item comment_lines ( STRING )
> > +
> > +Comments lines following core.commentchar configuration.
> > +
> > +=cut
> > +
> > +sub comment_lines {
> > +   my $comment_line_char = config("core.commentchar") || '#';
> > +   return prefix_lines("$comment_line_char ", @_);
> > +}
> > +
> 
> This makes it appear as if comment_lines can take arbitrary number
> of strings as its arguments (because the outer caller just passes @_
> thru), but in fact because prefix_lines ignores anything other than
> $_[0] and $_[1], only the first parameter given to comment_lineS sub
> is inspected for lines in it and the prefix-char prefixed at the
> beginning of each of them.
> 
> Which is not a great interface, as it is quite misleading.
> 
> Perhaps
> 
>   prefix_lines("#", join("\n", @_));
> 
> or something like that may make it less confusing.

I prefer to have like this instead

sub prefix_lines {
my $prefix = shift;
my $string = join("\n", @_);
$string =~ s/^/$prefix/mg;
return $string;
}

So both subroutines can take several strings as arguments.


[PATCH v5 05/16] i18n: clean.c: match string with git-add--interactive.perl

2016-11-08 Thread Vasco Almeida
Change strings for help to match the ones in git-add--interactive.perl.
The strings now represent one entry to translate each rather then two
entries each different only by an ending newline character.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 builtin/clean.c | 10 +-
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/builtin/clean.c b/builtin/clean.c
index 0371010af..d6bc3aaae 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -287,11 +287,11 @@ static void pretty_print_menus(struct string_list 
*menu_list)
 static void prompt_help_cmd(int singleton)
 {
clean_print_color(CLEAN_COLOR_HELP);
-   printf_ln(singleton ?
+   printf(singleton ?
  _("Prompt help:\n"
"1  - select a numbered item\n"
"foo- select item based on unique prefix\n"
-   "   - (empty) select nothing") :
+   "   - (empty) select nothing\n") :
  _("Prompt help:\n"
"1  - select a single item\n"
"3-5- select a range of items\n"
@@ -299,7 +299,7 @@ static void prompt_help_cmd(int singleton)
"foo- select item based on unique prefix\n"
"-...   - unselect specified items\n"
"*  - choose all items\n"
-   "   - (empty) finish selecting"));
+   "   - (empty) finish selecting\n"));
clean_print_color(CLEAN_COLOR_RESET);
 }
 
@@ -508,7 +508,7 @@ static int parse_choice(struct menu_stuff *menu_stuff,
if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > 
top ||
(is_single && bottom != top)) {
clean_print_color(CLEAN_COLOR_ERROR);
-   printf_ln(_("Huh (%s)?"), (*ptr)->buf);
+   printf(_("Huh (%s)?\n"), (*ptr)->buf);
clean_print_color(CLEAN_COLOR_RESET);
continue;
}
@@ -774,7 +774,7 @@ static int ask_each_cmd(void)
 static int quit_cmd(void)
 {
string_list_clear(_list, 0);
-   printf_ln(_("Bye."));
+   printf(_("Bye.\n"));
return MENU_RETURN_NO_LOOP;
 }
 
-- 
2.11.0.rc0.23.g8236252



[PATCH v5 11/16] i18n: add--interactive: mark status words for translation

2016-11-08 Thread Vasco Almeida
Mark words 'nothing', 'unchanged' and 'binary' used to display what has
been staged or not, in "git add -i" status command.

Alternatively one could mark N__('nothing') no-op in order to
xgettext(1) extract the string and then trigger the translation at run
time only with __($print->{FILE}), but that has the side effect of triggering
retrieval of translations for the changes indicator too (e.g. +2/-1)
which may or may not be a problem.

To avoid that potential problem, mark only where there is certain to
trigger translation only of those words but in this case we must also
retrieve the translation for the eq tests, since the value assigned was
of the translation, not the English source.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 12 ++--
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 93cc6f557..56e688907 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -295,7 +295,7 @@ sub list_modified {
my ($change, $bin);
$file = unquote_path($file);
if ($add eq '-' && $del eq '-') {
-   $change = 'binary';
+   $change = __('binary');
$bin = 1;
}
else {
@@ -304,7 +304,7 @@ sub list_modified {
$data{$file} = {
INDEX => $change,
BINARY => $bin,
-   FILE => 'nothing',
+   FILE => __('nothing'),
}
}
elsif (($adddel, $file) =
@@ -320,7 +320,7 @@ sub list_modified {
$file = unquote_path($file);
my ($change, $bin);
if ($add eq '-' && $del eq '-') {
-   $change = 'binary';
+   $change = __('binary');
$bin = 1;
}
else {
@@ -340,7 +340,7 @@ sub list_modified {
$file = unquote_path($2);
if (!exists $data{$file}) {
$data{$file} = +{
-   INDEX => 'unchanged',
+   INDEX => __('unchanged'),
BINARY => 0,
};
}
@@ -355,10 +355,10 @@ sub list_modified {
 
if ($only) {
if ($only eq 'index-only') {
-   next if ($it->{INDEX} eq 'unchanged');
+   next if ($it->{INDEX} eq __('unchanged'));
}
if ($only eq 'file-only') {
-   next if ($it->{FILE} eq 'nothing');
+   next if ($it->{FILE} eq __('nothing'));
}
}
push @return, +{
-- 
2.11.0.rc0.23.g8236252



[PATCH v5 14/16] i18n: send-email: mark string with interpolation for translation

2016-11-08 Thread Vasco Almeida
Mark warnings, errors and other messages that are interpolated for
translation.

We call sprintf() before calling die() and in few other circumstances in
order to replace the values on the placeholders.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-send-email.perl | 90 -
 1 file changed, 48 insertions(+), 42 deletions(-)

diff --git a/git-send-email.perl b/git-send-email.perl
index 982c6c076..5c014258b 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -279,10 +279,13 @@ sub signal_handler {
# tmp files from --compose
if (defined $compose_filename) {
if (-e $compose_filename) {
-   print "'$compose_filename' contains an intermediate 
version of the email you were composing.\n";
+   printf __("'%s' contains an intermediate version ".
+ "of the email you were composing.\n"),
+ $compose_filename;
}
if (-e ($compose_filename . ".final")) {
-   print "'$compose_filename.final' contains the composed 
email.\n"
+   printf __("'%s.final' contains the composed email.\n"),
+ $compose_filename;
}
}
 
@@ -431,7 +434,7 @@ $smtp_encryption = '' unless (defined $smtp_encryption);
 my(%suppress_cc);
 if (@suppress_cc) {
foreach my $entry (@suppress_cc) {
-   die "Unknown --suppress-cc field: '$entry'\n"
+   die sprintf(__("Unknown --suppress-cc field: '%s'\n"), $entry)
unless $entry =~ 
/^(?:all|cccmd|cc|author|self|sob|body|bodycc)$/;
$suppress_cc{$entry} = 1;
}
@@ -460,7 +463,7 @@ my $confirm_unconfigured = !defined $confirm;
 if ($confirm_unconfigured) {
$confirm = scalar %suppress_cc ? 'compose' : 'auto';
 };
-die "Unknown --confirm setting: '$confirm'\n"
+die sprintf(__("Unknown --confirm setting: '%s'\n"), $confirm)
unless $confirm =~ /^(?:auto|cc|compose|always|never)/;
 
 # Debugging, print out the suppressions.
@@ -492,16 +495,16 @@ my %aliases;
 sub parse_sendmail_alias {
local $_ = shift;
if (/"/) {
-   print STDERR "warning: sendmail alias with quotes is not 
supported: $_\n";
+   printf STDERR __("warning: sendmail alias with quotes is not 
supported: %s\n"), $_;
} elsif (/:include:/) {
-   print STDERR "warning: `:include:` not supported: $_\n";
+   printf STDERR __("warning: `:include:` not supported: %s\n"), 
$_;
} elsif (/[\/|]/) {
-   print STDERR "warning: `/file` or `|pipe` redirection not 
supported: $_\n";
+   printf STDERR __("warning: `/file` or `|pipe` redirection not 
supported: %s\n"), $_;
} elsif (/^(\S+?)\s*:\s*(.+)$/) {
my ($alias, $addr) = ($1, $2);
$aliases{$alias} = [ split_addrs($addr) ];
} else {
-   print STDERR "warning: sendmail line is not recognized: $_\n";
+   printf STDERR __("warning: sendmail line is not recognized: 
%s\n"), $_;
}
 }
 
@@ -582,13 +585,12 @@ sub is_format_patch_arg {
if (defined($format_patch)) {
return $format_patch;
}
-   die(<<EOF);
-File '$f' exists but it could also be the range of commits
+   die sprintf(__(
+"File '%s' exists but it could also be the range of commits
 to produce patches for.  Please disambiguate by...
 
-* Saying "./$f" if you mean a file; or
-* Giving --format-patch option if you mean a range.
-EOF
+* Saying \"./%s\" if you mean a file; or
+* Giving --format-patch option if you mean a range."), $f, $f);
} catch Git::Error::Command with {
# Not a valid revision.  Treat it as a filename.
return 0;
@@ -604,7 +606,7 @@ while (defined(my $f = shift @ARGV)) {
@ARGV = ();
} elsif (-d $f and !is_format_patch_arg($f)) {
opendir my $dh, $f
-   or die "Failed to opendir $f: $!";
+   or die sprintf(__("Failed to opendir %s: %s"), $f, $!);
 
push @files, grep { -f $_ } map { catfile($f, $_) }
sort readdir $dh;
@@ -628,7 +630,8 @@ if ($validate) {
foreach my $f (@files) {
unless (-p $f) {
my $error = validate_patch($f);
-   $error and die "fatal: $f: $error\nwarning: no patches 
were sent\n";
+   $error and die sprintf(__("fat

[PATCH v5 04/16] i18n: add--interactive: mark strings with interpolation for translation

2016-11-08 Thread Vasco Almeida
Since at this point Git::I18N.perl lacks support for Perl i18n
placeholder substitution, use of sprintf following die or error_msg is
necessary for placeholder substitution take place.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 25 +
 1 file changed, 13 insertions(+), 12 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 5800010ed..d05ac608e 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -615,12 +615,12 @@ sub list_and_choose {
else {
$bottom = $top = find_unique($choice, @stuff);
if (!defined $bottom) {
-   error_msg "Huh ($choice)?\n";
+   error_msg sprintf(__("Huh (%s)?\n"), 
$choice);
next TOPLOOP;
}
}
if ($opts->{SINGLETON} && $bottom != $top) {
-   error_msg "Huh ($choice)?\n";
+   error_msg sprintf(__("Huh (%s)?\n"), $choice);
next TOPLOOP;
}
for ($i = $bottom-1; $i <= $top-1; $i++) {
@@ -717,7 +717,7 @@ sub revert_cmd {
$_->{INDEX_ADDDEL} eq 'create') {
system(qw(git update-index 
--force-remove --),
   $_->{VALUE});
-   print "note: $_->{VALUE} is untracked 
now.\n";
+   printf(__("note: %s is untracked 
now.\n"), $_->{VALUE});
}
}
}
@@ -1056,7 +1056,7 @@ sub edit_hunk_manually {
my $hunkfile = $repo->repo_path . "/addp-hunk-edit.diff";
my $fh;
open $fh, '>', $hunkfile
-   or die "failed to open hunk edit file for writing: " . $!;
+   or die sprintf(__("failed to open hunk edit file for writing: 
%s"), $!);
print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n";
print $fh @$oldtext;
my $participle = $patch_mode_flavour{PARTICIPLE};
@@ -1083,7 +1083,7 @@ EOF
}
 
open $fh, '<', $hunkfile
-   or die "failed to open hunk edit file for reading: " . $!;
+   or die sprintf(__("failed to open hunk edit file for reading: 
%s"), $!);
my @newtext = grep { !/^#/ } <$fh>;
close $fh;
unlink $hunkfile;
@@ -1236,7 +1236,7 @@ sub apply_patch_for_checkout_commit {
 
 sub patch_update_cmd {
my @all_mods = list_modified($patch_mode_flavour{FILTER});
-   error_msg "ignoring unmerged: $_->{VALUE}\n"
+   error_msg sprintf(__("ignoring unmerged: %s\n"), $_->{VALUE})
for grep { $_->{UNMERGED} } @all_mods;
@all_mods = grep { !$_->{UNMERGED} } @all_mods;
 
@@ -1418,7 +1418,8 @@ sub patch_update_file {
chomp $response;
}
if ($response !~ /^\s*\d+\s*$/) {
-   error_msg "Invalid number: 
'$response'\n";
+   error_msg sprintf(__("Invalid number: 
'%s'\n"),
+$response);
} elsif (0 < $response && $response <= $num) {
$ix = $response - 1;
} else {
@@ -1460,7 +1461,7 @@ sub patch_update_file {
if ($@) {
my ($err,$exp) = ($@, $1);
$err =~ s/ at .*git-add--interactive 
line \d+,  line \d+.*$//;
-   error_msg "Malformed search regexp 
$exp: $err\n";
+   error_msg sprintf(__("Malformed search 
regexp %s: %s\n"), $exp, $err);
next;
}
my $iy = $ix;
@@ -1625,18 +1626,18 @@ sub process_args {
$patch_mode = $1;
$arg = shift @ARGV or die __("missing --");
} else {
-   die "unknown --patch mode: $1";
+   die sprintf(__("unknown --patch mode: %s"), $1);
}
} else {
   

[PATCH v5 15/16] i18n: send-email: mark composing message for translation

2016-11-08 Thread Vasco Almeida
When composing an e-mail, there is a message for the user whose lines
are beginning in "GIT:" that can be marked for translation.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-send-email.perl | 16 +---
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/git-send-email.perl b/git-send-email.perl
index 5c014258b..bbeb9fbf0 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -671,18 +671,20 @@ if ($compose) {
my $tpl_subject = $initial_subject || '';
my $tpl_reply_to = $initial_reply_to || '';
 
-   print $c <<EOT;
+   print $c <<EOT1, Git::prefix_lines("GIT: ", __ <<EOT2), <<EOT3;
 From $tpl_sender # This line is ignored.
-GIT: Lines beginning in "GIT:" will be removed.
-GIT: Consider including an overall diffstat or table of contents
-GIT: for the patch you are writing.
-GIT:
-GIT: Clear the body content if you don't wish to send a summary.
+EOT1
+Lines beginning in "GIT:" will be removed.
+Consider including an overall diffstat or table of contents
+for the patch you are writing.
+
+Clear the body content if you don't wish to send a summary.
+EOT2
 From: $tpl_sender
 Subject: $tpl_subject
 In-Reply-To: $tpl_reply_to
 
-EOT
+EOT3
for my $f (@files) {
print $c get_patch_subject($f);
}
-- 
2.11.0.rc0.23.g8236252



[PATCH v5 12/16] i18n: send-email: mark strings for translation

2016-11-08 Thread Vasco Almeida
Mark strings often displayed to the user for translation.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-send-email.perl | 52 ++--
 1 file changed, 30 insertions(+), 22 deletions(-)

diff --git a/git-send-email.perl b/git-send-email.perl
index da81be40c..78eb59b21 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -28,6 +28,7 @@ use File::Temp qw/ tempdir tempfile /;
 use File::Spec::Functions qw(catfile);
 use Error qw(:try);
 use Git;
+use Git::I18N;
 
 Getopt::Long::Configure qw/ pass_through /;
 
@@ -797,12 +798,12 @@ foreach my $f (@files) {
 }
 
 if (!defined $auto_8bit_encoding && scalar %broken_encoding) {
-   print "The following files are 8bit, but do not declare " .
-   "a Content-Transfer-Encoding.\n";
+   print __("The following files are 8bit, but do not declare " .
+"a Content-Transfer-Encoding.\n");
foreach my $f (sort keys %broken_encoding) {
print "$f\n";
}
-   $auto_8bit_encoding = ask("Which 8bit encoding should I declare 
[UTF-8]? ",
+   $auto_8bit_encoding = ask(__("Which 8bit encoding should I declare 
[UTF-8]? "),
  valid_re => qr/.{4}/, confirm_only => 1,
  default => "UTF-8");
 }
@@ -829,7 +830,7 @@ if (defined $sender) {
 # But it's a no-op to run sanitize_address on an already sanitized address.
 $sender = sanitize_address($sender);
 
-my $to_whom = "To whom should the emails be sent (if anyone)?";
+my $to_whom = __("To whom should the emails be sent (if anyone)?");
 my $prompting = 0;
 if (!@initial_to && !defined $to_cmd) {
my $to = ask("$to_whom ",
@@ -859,7 +860,7 @@ sub expand_one_alias {
 
 if ($thread && !defined $initial_reply_to && $prompting) {
$initial_reply_to = ask(
-   "Message-ID to be used as In-Reply-To for the first email (if 
any)? ",
+   __("Message-ID to be used as In-Reply-To for the first email 
(if any)? "),
default => "",
valid_re => qr/\@.*\./, confirm_only => 1);
 }
@@ -918,7 +919,10 @@ sub validate_address {
my $address = shift;
while (!extract_valid_address($address)) {
print STDERR "error: unable to extract a valid address from: 
$address\n";
-   $_ = ask("What to do with this address? ([q]uit|[d]rop|[e]dit): 
",
+   # TRANSLATORS: Make sure to include [q] [d] [e] in your
+   # translation. The program will only accept English input
+   # at this point.
+   $_ = ask(__("What to do with this address? 
([q]uit|[d]rop|[e]dit): "),
valid_re => qr/^(?:quit|q|drop|d|edit|e)/i,
default => 'q');
if (/^d/i) {
@@ -1293,17 +1297,21 @@ Message-Id: $message_id
if ($needs_confirm eq "inform") {
$confirm_unconfigured = 0; # squelch this message for 
the rest of this run
$ask_default = "y"; # assume yes on EOF since user 
hasn't explicitly asked for confirmation
-   print "The Cc list above has been expanded by 
additional\n";
-   print "addresses found in the patch commit message. 
By default\n";
-   print "send-email prompts before sending whenever 
this occurs.\n";
-   print "This behavior is controlled by the 
sendemail.confirm\n";
-   print "configuration setting.\n";
-   print "\n";
-   print "For additional information, run 'git 
send-email --help'.\n";
-   print "To retain the current behavior, but squelch 
this message,\n";
-   print "run 'git config --global sendemail.confirm 
auto'.\n\n";
+   print __(
+"The Cc list above has been expanded by additional
+addresses found in the patch commit message. By default
+send-email prompts before sending whenever this occurs.
+This behavior is controlled by the sendemail.confirm
+configuration setting.
+
+For additional information, run 'git send-email --help'.
+To retain the current behavior, but squelch this message,
+run 'git config --global sendemail.confirm auto'."), "\n\n";
}
-   $_ = ask("Send this email? ([y]es|[n]o|[q]uit|[a]ll): ",
+   # TRANSLATORS: Make sure to include [y] [n] [q] [a] in your
+   # translation. The prog

[PATCH v5 06/16] i18n: add--interactive: mark plural strings

2016-11-08 Thread Vasco Almeida
Mark plural strings for translation.  Unfold each action case in one
entire sentence.

Pass new keyword for xgettext to extract.

Update test to include new subroutine __n() for plural strings handling.

Update documentation to include a description of the new __n()
subroutine.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 Makefile  |  3 ++-
 git-add--interactive.perl | 27 ++-
 perl/Git/I18N.pm  | 10 +-
 t/t0202/test.pl   | 11 ++-
 4 files changed, 39 insertions(+), 12 deletions(-)

diff --git a/Makefile b/Makefile
index 9d6c24503..328151dd6 100644
--- a/Makefile
+++ b/Makefile
@@ -2113,7 +2113,8 @@ XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --language=C \
--keyword=_ --keyword=N_ --keyword="Q_:1,2"
 XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell \
--keyword=gettextln --keyword=eval_gettextln
-XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --keyword=__ --language=Perl
+XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --language=Perl \
+   --keyword=__ --keyword="__n:1,2"
 LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
 LOCALIZED_SH = $(SCRIPT_SH)
 LOCALIZED_SH += git-parse-remote.sh
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index d05ac608e..cd617837b 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -669,12 +669,18 @@ sub status_cmd {
 sub say_n_paths {
my $did = shift @_;
my $cnt = scalar @_;
-   print "$did ";
-   if (1 < $cnt) {
-   print "$cnt paths\n";
-   }
-   else {
-   print "one path\n";
+   if ($did eq 'added') {
+   printf(__n("added %d path\n", "added %d paths\n",
+  $cnt), $cnt);
+   } elsif ($did eq 'updated') {
+   printf(__n("updated %d path\n", "updated %d paths\n",
+  $cnt), $cnt);
+   } elsif ($did eq 'reverted') {
+   printf(__n("reverted %d path\n", "reverted %d paths\n",
+  $cnt), $cnt);
+   } else {
+   printf(__n("touched %d path\n", "touched %d paths\n",
+  $cnt), $cnt);
}
 }
 
@@ -1423,7 +1429,8 @@ sub patch_update_file {
} elsif (0 < $response && $response <= $num) {
$ix = $response - 1;
} else {
-   error_msg "Sorry, only $num hunks 
available.\n";
+   error_msg sprintf(__n("Sorry, only %d 
hunk available.\n",
+ "Sorry, only %d 
hunks available.\n", $num), $num);
}
next;
}
@@ -1518,8 +1525,10 @@ sub patch_update_file {
elsif ($other =~ /s/ && $line =~ /^s/) {
my @split = split_hunk($hunk[$ix]{TEXT}, 
$hunk[$ix]{DISPLAY});
if (1 < @split) {
-   print colored $header_color, "Split 
into ",
-   scalar(@split), " hunks.\n";
+   print colored $header_color, sprintf(
+   __n("Split into %d hunk.\n",
+   "Split into %d hunks.\n",
+   scalar(@split)), 
scalar(@split));
}
splice (@hunk, $ix, 1, @split);
$num = scalar @hunk;
diff --git a/perl/Git/I18N.pm b/perl/Git/I18N.pm
index f889fd6da..617d8c2a1 100644
--- a/perl/Git/I18N.pm
+++ b/perl/Git/I18N.pm
@@ -13,7 +13,7 @@ BEGIN {
}
 }
 
-our @EXPORT = qw(__);
+our @EXPORT = qw(__ __n);
 our @EXPORT_OK = @EXPORT;
 
 sub __bootstrap_locale_messages {
@@ -44,6 +44,7 @@ BEGIN
eval {
__bootstrap_locale_messages();
*__ = \::Messages::gettext;
+   *__n = \::Messages::ngettext;
1;
} or do {
# Tell test.pl that we couldn't load the gettext library.
@@ -51,6 +52,7 @@ BEGIN
 
# Just a fall-through no-op
*__ = sub ($) { $_[0] };
+   *__n = sub ($$$) { $_[2] == 1 ? $_[0] : $_[1] };
};
 }
 
@@ -70,6 +72,8 @@ Git::I18N - Perl interface to Git's Gettext localizations
 
printf __("The following error occurred: %s\n"), $error;
 
+   printf __n("commited %d file\n", "commited %d files\n", $files), $files;
+
 =head1 DESCRIPTION
 

[PATCH v5 13/16] i18n: send-email: mark warnings and errors for translation

2016-11-08 Thread Vasco Almeida
Mark warnings, errors and other messages for translation.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-send-email.perl | 34 +-
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/git-send-email.perl b/git-send-email.perl
index 78eb59b21..982c6c076 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -118,20 +118,20 @@ sub format_2822_time {
my $localmin = $localtm[1] + $localtm[2] * 60;
my $gmtmin = $gmttm[1] + $gmttm[2] * 60;
if ($localtm[0] != $gmttm[0]) {
-   die "local zone differs from GMT by a non-minute interval\n";
+   die __("local zone differs from GMT by a non-minute 
interval\n");
}
if ((($gmttm[6] + 1) % 7) == $localtm[6]) {
$localmin += 1440;
} elsif ((($gmttm[6] - 1) % 7) == $localtm[6]) {
$localmin -= 1440;
} elsif ($gmttm[6] != $localtm[6]) {
-   die "local time offset greater than or equal to 24 hours\n";
+   die __("local time offset greater than or equal to 24 hours\n");
}
my $offset = $localmin - $gmtmin;
my $offhour = $offset / 60;
my $offmin = abs($offset % 60);
if (abs($offhour) >= 24) {
-   die ("local time offset greater than or equal to 24 hours\n");
+   die __("local time offset greater than or equal to 24 hours\n");
}
 
return sprintf("%s, %2d %s %d %02d:%02d:%02d %s%02d%02d",
@@ -199,13 +199,13 @@ sub do_edit {
map {
system('sh', '-c', $editor.' "$@"', $editor, $_);
if (($? & 127) || ($? >> 8)) {
-   die("the editor exited uncleanly, aborting 
everything");
+   die(__("the editor exited uncleanly, aborting 
everything"));
}
} @_;
} else {
system('sh', '-c', $editor.' "$@"', $editor, @_);
if (($? & 127) || ($? >> 8)) {
-   die("the editor exited uncleanly, aborting everything");
+   die(__("the editor exited uncleanly, aborting 
everything"));
}
}
 }
@@ -299,7 +299,7 @@ my $help;
 my $rc = GetOptions("h" => \$help,
 "dump-aliases" => \$dump_aliases);
 usage() unless $rc;
-die "--dump-aliases incompatible with other options\n"
+die __("--dump-aliases incompatible with other options\n")
 if !$help and $dump_aliases and @ARGV;
 $rc = GetOptions(
"sender|from=s" => \$sender,
@@ -362,7 +362,7 @@ unless ($rc) {
 usage();
 }
 
-die "Cannot run git format-patch from outside a repository\n"
+die __("Cannot run git format-patch from outside a repository\n")
if $format_patch and not $repo;
 
 # Now, let's fill any that aren't set in with defaults:
@@ -617,7 +617,7 @@ while (defined(my $f = shift @ARGV)) {
 }
 
 if (@rev_list_opts) {
-   die "Cannot run git format-patch from outside a repository\n"
+   die __("Cannot run git format-patch from outside a repository\n")
unless $repo;
push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 
1), @rev_list_opts);
 }
@@ -638,7 +638,7 @@ if (@files) {
print $_,"\n" for (@files);
}
 } else {
-   print STDERR "\nNo patch files specified!\n\n";
+   print STDERR __("\nNo patch files specified!\n\n");
usage();
 }
 
@@ -730,7 +730,7 @@ EOT
$sender = $1;
next;
} elsif (/^(?:To|Cc|Bcc):/i) {
-   print "To/Cc/Bcc fields are not interpreted yet, they 
have been ignored\n";
+   print __("To/Cc/Bcc fields are not interpreted yet, 
they have been ignored\n");
next;
}
print $c2 $_;
@@ -739,7 +739,7 @@ EOT
close $c2;
 
if ($summary_empty) {
-   print "Summary email is empty, skipping it\n";
+   print __("Summary email is empty, skipping it\n");
$compose = -1;
}
 } elsif ($annotate) {
@@ -1314,7 +1314,7 @@ Message-Id: $message_id
$_ = ask(__("Send this email? ([y]es|[n]o|[q]uit|[a]ll): "),
 valid_re => qr/^(?:yes|y|no|n|quit|q|all|a)/i,
 default => $ask_default);
-   die "Send this email reply required" unless defined $_;
+   die __("Send this email reply required") unless defined $_

[PATCH v5 07/16] i18n: add--interactive: mark patch prompt for translation

2016-11-08 Thread Vasco Almeida
Mark prompt message assembled in place for translation, unfolding each
use case for each entry in the %patch_modes hash table.

Previously, this script relied on whether $patch_mode was set to run the
command patch_update_cmd() or show status and loop the main loop. Now,
it uses $cmd to indicate we must run patch_update_cmd() and $patch_mode
is used to tell which flavor of the %patch_modes are we on.  This is
introduced in order to be able to mark and unfold the message prompt
knowing in which context we are.

The tracking of context was done previously by point %patch_mode_flavour
hash table to the correct entry of %patch_modes, focusing only on value
of %patch_modes. Now, we are also interested in the key ('staged',
'stash', 'checkout_head', ...).

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 Makefile  |  2 +-
 git-add--interactive.perl | 54 ---
 perl/Git/I18N.pm  | 11 +-
 t/t0202/test.pl   |  5 -
 4 files changed, 61 insertions(+), 11 deletions(-)

diff --git a/Makefile b/Makefile
index 328151dd6..ee89c0624 100644
--- a/Makefile
+++ b/Makefile
@@ -2114,7 +2114,7 @@ XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --language=C \
 XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell \
--keyword=gettextln --keyword=eval_gettextln
 XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --language=Perl \
-   --keyword=__ --keyword="__n:1,2"
+   --keyword=__ --keyword=N__ --keyword="__n:1,2"
 LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
 LOCALIZED_SH = $(SCRIPT_SH)
 LOCALIZED_SH += git-parse-remote.sh
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index cd617837b..b7d382b10 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -93,6 +93,7 @@ sub colored {
 }
 
 # command line options
+my $cmd;
 my $patch_mode;
 my $patch_mode_revision;
 
@@ -173,7 +174,8 @@ my %patch_modes = (
},
 );
 
-my %patch_mode_flavour = %{$patch_modes{stage}};
+$patch_mode = 'stage';
+my %patch_mode_flavour = %{$patch_modes{$patch_mode}};
 
 sub run_cmd_pipe {
if ($^O eq 'MSWin32') {
@@ -1311,6 +1313,44 @@ sub display_hunks {
return $i;
 }
 
+my %patch_update_prompt_modes = (
+   stage => {
+   mode => N__("Stage mode change [y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Stage deletion [y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Stage this hunk [y,n,q,a,d,/%s,?]? "),
+   },
+   stash => {
+   mode => N__("Stash mode change [y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Stash deletion [y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Stash this hunk [y,n,q,a,d,/%s,?]? "),
+   },
+   reset_head => {
+   mode => N__("Unstage mode change [y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Unstage deletion [y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Unstage this hunk [y,n,q,a,d,/%s,?]? "),
+   },
+   reset_nothead => {
+   mode => N__("Apply mode change to index [y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Apply deletion to index [y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Apply this hunk to index [y,n,q,a,d,/%s,?]? "),
+   },
+   checkout_index => {
+   mode => N__("Discard mode change from worktree 
[y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Discard deletion from worktree 
[y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Discard this hunk from worktree [y,n,q,a,d,/%s,?]? 
"),
+   },
+   checkout_head => {
+   mode => N__("Discard mode change from index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Discard deletion from index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Discard this hunk from index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   },
+   checkout_nothead => {
+   mode => N__("Apply mode change to index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Apply deletion to index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Apply this hunk to index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   },
+);
+
 sub patch_update_file {
my $quit = 0;
my ($ix, $num);
@@ -1383,12 +1423,9 @@ sub patch_update_file {
for (@{$hunk[$ix]{DISPLAY}}) {
print;
}
-   print colored $prompt_color, $patch_mode_flavour{VERB},
- ($hunk[$ix]{TYPE} eq 'mode' ? ' mode change' :
-  $hunk[$ix]{TYPE} eq 'deletion' ? ' deletion' :
-  ' this hunk'),
- 

[PATCH v5 03/16] i18n: add--interactive: mark simple here-documents for translation

2016-11-08 Thread Vasco Almeida
Mark messages in here-documents without interpolation for translation.

The here-document delimiter \EOF, which is the same as 'EOF', indicates
that the text is to be treated literally without interpolation of its
content.  Unfortunately xgettext is not able to extract here-documents
delimited with \EOF but it is with delimiter enclosed in single quotes.
So change \EOF to 'EOF', although in this case does not make
difference what variation of here-document to use since there is nothing
to interpolate.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 8 +---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index cf216ecb6..5800010ed 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -639,7 +639,7 @@ sub list_and_choose {
 }
 
 sub singleton_prompt_help_cmd {
-   print colored $help_color, <<\EOF ;
+   print colored $help_color, __ <<'EOF' ;
 Prompt help:
 1  - select a numbered item
 foo- select item based on unique prefix
@@ -648,7 +648,7 @@ EOF
 }
 
 sub prompt_help_cmd {
-   print colored $help_color, <<\EOF ;
+   print colored $help_color, __ <<'EOF' ;
 Prompt help:
 1  - select a single item
 3-5- select a range of items
@@ -1584,7 +1584,9 @@ sub quit_cmd {
 }
 
 sub help_cmd {
-   print colored $help_color, <<\EOF ;
+# TRANSLATORS: please do not translate the command names
+# 'status', 'update', 'revert', etc.
+   print colored $help_color, __ <<'EOF' ;
 status- show paths with changes
 update- add working tree state to the staged set of changes
 revert- revert staged set of changes back to the HEAD version
-- 
2.11.0.rc0.23.g8236252



[PATCH v5 10/16] i18n: add--interactive: remove %patch_modes entries

2016-11-08 Thread Vasco Almeida
Remove unnecessary entries from %patch_modes. After the i18n conversion,
these entries are not used anymore.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 21 -
 1 file changed, 21 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 45653e094..93cc6f557 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -106,9 +106,6 @@ my %patch_modes = (
DIFF => 'diff-files -p',
APPLY => sub { apply_patch 'apply --cached', @_; },
APPLY_CHECK => 'apply --cached',
-   VERB => 'Stage',
-   TARGET => '',
-   PARTICIPLE => 'staging',
FILTER => 'file-only',
IS_REVERSE => 0,
},
@@ -116,9 +113,6 @@ my %patch_modes = (
DIFF => 'diff-index -p HEAD',
APPLY => sub { apply_patch 'apply --cached', @_; },
APPLY_CHECK => 'apply --cached',
-   VERB => 'Stash',
-   TARGET => '',
-   PARTICIPLE => 'stashing',
FILTER => undef,
IS_REVERSE => 0,
},
@@ -126,9 +120,6 @@ my %patch_modes = (
DIFF => 'diff-index -p --cached',
APPLY => sub { apply_patch 'apply -R --cached', @_; },
APPLY_CHECK => 'apply -R --cached',
-   VERB => 'Unstage',
-   TARGET => '',
-   PARTICIPLE => 'unstaging',
FILTER => 'index-only',
IS_REVERSE => 1,
},
@@ -136,9 +127,6 @@ my %patch_modes = (
DIFF => 'diff-index -R -p --cached',
APPLY => sub { apply_patch 'apply --cached', @_; },
APPLY_CHECK => 'apply --cached',
-   VERB => 'Apply',
-   TARGET => ' to index',
-   PARTICIPLE => 'applying',
FILTER => 'index-only',
IS_REVERSE => 0,
},
@@ -146,9 +134,6 @@ my %patch_modes = (
DIFF => 'diff-files -p',
APPLY => sub { apply_patch 'apply -R', @_; },
APPLY_CHECK => 'apply -R',
-   VERB => 'Discard',
-   TARGET => ' from worktree',
-   PARTICIPLE => 'discarding',
FILTER => 'file-only',
IS_REVERSE => 1,
},
@@ -156,9 +141,6 @@ my %patch_modes = (
DIFF => 'diff-index -p',
APPLY => sub { apply_patch_for_checkout_commit '-R', @_ },
APPLY_CHECK => 'apply -R',
-   VERB => 'Discard',
-   TARGET => ' from index and worktree',
-   PARTICIPLE => 'discarding',
FILTER => undef,
IS_REVERSE => 1,
},
@@ -166,9 +148,6 @@ my %patch_modes = (
DIFF => 'diff-index -R -p',
APPLY => sub { apply_patch_for_checkout_commit '', @_ },
APPLY_CHECK => 'apply',
-   VERB => 'Apply',
-   TARGET => ' to index and worktree',
-   PARTICIPLE => 'applying',
FILTER => undef,
IS_REVERSE => 0,
},
-- 
2.11.0.rc0.23.g8236252



[PATCH v5 16/16] i18n: difftool: mark warnings for translation

2016-11-08 Thread Vasco Almeida
Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-difftool.perl | 22 --
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/git-difftool.perl b/git-difftool.perl
index a5790d03a..8d3632e55 100755
--- a/git-difftool.perl
+++ b/git-difftool.perl
@@ -22,6 +22,7 @@ use File::Path qw(mkpath rmtree);
 use File::Temp qw(tempdir);
 use Getopt::Long qw(:config pass_through);
 use Git;
+use Git::I18N;
 
 sub usage
 {
@@ -122,7 +123,7 @@ sub setup_dir_diff
my $i = 0;
while ($i < $#rawdiff) {
if ($rawdiff[$i] =~ /^::/) {
-   warn << 'EOF';
+   warn __ <<'EOF';
 Combined diff formats ('-c' and '--cc') are not supported in
 directory diff mode ('-d' and '--dir-diff').
 EOF
@@ -338,7 +339,7 @@ sub main
if (length($opts{difftool_cmd}) > 0) {
$ENV{GIT_DIFF_TOOL} = $opts{difftool_cmd};
} else {
-   print "No  given for --tool=\n";
+   print __("No  given for --tool=\n");
usage(1);
}
}
@@ -346,7 +347,7 @@ sub main
if (length($opts{extcmd}) > 0) {
$ENV{GIT_DIFFTOOL_EXTCMD} = $opts{extcmd};
} else {
-   print "No  given for --extcmd=\n";
+   print __("No  given for --extcmd=\n");
usage(1);
}
}
@@ -419,11 +420,11 @@ sub dir_diff
}
 
if (exists $wt_modified{$file} and exists $tmp_modified{$file}) 
{
-   my $errmsg = "warning: Both files modified: ";
-   $errmsg .= "'$workdir/$file' and '$b/$file'.\n";
-   $errmsg .= "warning: Working tree file has been 
left.\n";
-   $errmsg .= "warning:\n";
-   warn $errmsg;
+   warn sprintf(__(
+   "warning: Both files modified:\n" .
+   "'%s/%s' and '%s/%s'.\n" .
+   "warning: Working tree file has been left.\n" .
+   "warning:\n"), $workdir, $file, $b, $file);
$error = 1;
} elsif (exists $tmp_modified{$file}) {
my $mode = stat("$b/$file")->mode;
@@ -435,8 +436,9 @@ sub dir_diff
}
}
if ($error) {
-   warn "warning: Temporary files exist in '$tmpdir'.\n";
-   warn "warning: You may want to cleanup or recover these.\n";
+   warn sprintf(__(
+   "warning: Temporary files exist in '%s'.\n" .
+   "warning: You may want to cleanup or recover 
these.\n"), $tmpdir);
exit(1);
} else {
exit_cleanup($tmpdir, $rc);
-- 
2.11.0.rc0.23.g8236252



[PATCH v5 08/16] i18n: add--interactive: i18n of help_patch_cmd

2016-11-08 Thread Vasco Almeida
Mark help message of help_patch_cmd for translation.  The message must
be unfolded to be free of variables so we can have high quality
translations.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 54 ---
 1 file changed, 46 insertions(+), 8 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index b7d382b10..045b847cf 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -1189,15 +1189,53 @@ sub edit_hunk_loop {
}
 }
 
+my %help_patch_modes = (
+   stage => N__(
+"y - stage this hunk
+n - do not stage this hunk
+q - quit; do not stage this hunk or any of the remaining ones
+a - stage this hunk and all later hunks in the file
+d - do not stage this hunk or any of the later hunks in the file"),
+   stash => N__(
+"y - stash this hunk
+n - do not stash this hunk
+q - quit; do not stash this hunk or any of the remaining ones
+a - stash this hunk and all later hunks in the file
+d - do not stash this hunk or any of the later hunks in the file"),
+   reset_head => N__(
+"y - unstage this hunk
+n - do not unstage this hunk
+q - quit; do not unstage this hunk or any of the remaining ones
+a - unstage this hunk and all later hunks in the file
+d - do not unstage this hunk or any of the later hunks in the file"),
+   reset_nothead => N__(
+"y - apply this hunk to index
+n - do not apply this hunk to index
+q - quit; do not apply this hunk or any of the remaining ones
+a - apply this hunk and all later hunks in the file
+d - do not apply this hunk or any of the later hunks in the file"),
+   checkout_index => N__(
+"y - discard this hunk from worktree
+n - do not discard this hunk from worktree
+q - quit; do not discard this hunk or any of the remaining ones
+a - discard this hunk and all later hunks in the file
+d - do not discard this hunk or any of the later hunks in the file"),
+   checkout_head => N__(
+"y - discard this hunk from index and worktree
+n - do not discard this hunk from index and worktree
+q - quit; do not discard this hunk or any of the remaining ones
+a - discard this hunk and all later hunks in the file
+d - do not discard this hunk or any of the later hunks in the file"),
+   checkout_nothead => N__(
+"y - apply this hunk to index and worktree
+n - do not apply this hunk to index and worktree
+q - quit; do not apply this hunk or any of the remaining ones
+a - apply this hunk and all later hunks in the file
+d - do not apply this hunk or any of the later hunks in the file"),
+);
+
 sub help_patch_cmd {
-   my $verb = lc $patch_mode_flavour{VERB};
-   my $target = $patch_mode_flavour{TARGET};
-   print colored $help_color, <

[PATCH v5 09/16] i18n: add--interactive: mark edit_hunk_manually message for translation

2016-11-08 Thread Vasco Almeida
Mark message of edit_hunk_manually displayed in the editing file when
user chooses 'e' option.  The message had to be unfolded to allow
translation of the $participle verb.

Some messages end up being exactly the same for some uses cases, but
left it for easier change in the future, e.g., wanting to change wording
of one particular use case.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 52 ++-
 1 file changed, 38 insertions(+), 14 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 045b847cf..45653e094 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -1058,6 +1058,30 @@ sub color_diff {
} @_;
 }
 
+my %edit_hunk_manually_modes = (
+   stage => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for staging."),
+   stash => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for stashing."),
+   reset_head => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for unstaging."),
+   reset_nothead => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for applying."),
+   checkout_index => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for discarding"),
+   checkout_head => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for discarding."),
+   checkout_nothead => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for applying."),
+);
+
 sub edit_hunk_manually {
my ($oldtext) = @_;
 
@@ -1065,22 +1089,22 @@ sub edit_hunk_manually {
my $fh;
open $fh, '>', $hunkfile
or die sprintf(__("failed to open hunk edit file for writing: 
%s"), $!);
-   print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n";
+   print $fh Git::comment_lines __("Manual hunk edit mode -- see bottom 
for a quick guide\n");
print $fh @$oldtext;
-   my $participle = $patch_mode_flavour{PARTICIPLE};
my $is_reverse = $patch_mode_flavour{IS_REVERSE};
my ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') : ('+', 
'-');
-   print $fh <<EOF;
-# ---
-# To remove '$remove_minus' lines, make them ' ' lines (context).
-# To remove '$remove_plus' lines, delete them.
-# Lines starting with # will be removed.
-#
-# If the patch applies cleanly, the edited hunk will immediately be
-# marked for $participle. If it does not apply cleanly, you will be given
-# an opportunity to edit again. If all lines of the hunk are removed,
-# then the edit is aborted and the hunk is left unchanged.
-EOF
+   my $comment_line_char = Git::config("core.commentchar") || '#';
+   print $fh Git::comment_lines sprintf(__(
+"---
+To remove '%s' lines, make them ' ' lines (context).
+To remove '%s' lines, delete them.
+Lines starting with %s will be removed.
+\n"), $remove_minus, $remove_plus, $comment_line_char) .
+__($edit_hunk_manually_modes{$patch_mode}) ."\n". __(
+# TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
+"If it does not apply cleanly, you will be given an opportunity to
+edit again.  If all lines of the hunk are removed, then the edit is
+aborted and the hunk is left unchanged.\n");
close $fh;
 
chomp(my $editor = run_cmd_pipe(qw(git var GIT_EDITOR)));
@@ -1092,7 +1116,7 @@ EOF
 
open $fh, '<', $hunkfile
or die sprintf(__("failed to open hunk edit file for reading: 
%s"), $!);
-   my @newtext = grep { !/^#/ } <$fh>;
+   my @newtext = grep { !/^$comment_line_char/ } <$fh>;
close $fh;
unlink $hunkfile;
 
-- 
2.11.0.rc0.23.g8236252



[PATCH v5 01/16] Git.pm: add subroutines for commenting lines

2016-11-08 Thread Vasco Almeida
Add subroutines prefix_lines and comment_lines.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 perl/Git.pm | 23 +++
 1 file changed, 23 insertions(+)

diff --git a/perl/Git.pm b/perl/Git.pm
index b2732822a..17be59fb7 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -1438,6 +1438,29 @@ sub END {
 
 } # %TEMP_* Lexical Context
 
+=item prefix_lines ( PREFIX, STRING )
+
+Prefixes lines in C with C.
+
+=cut
+
+sub prefix_lines {
+   my ($prefix, $string) = @_;
+   $string =~ s/^/$prefix/mg;
+   return $string;
+}
+
+=item comment_lines ( STRING )
+
+Comments lines following core.commentchar configuration.
+
+=cut
+
+sub comment_lines {
+   my $comment_line_char = config("core.commentchar") || '#';
+   return prefix_lines("$comment_line_char ", @_);
+}
+
 =back
 
 =head1 ERROR HANDLING
-- 
2.11.0.rc0.23.g8236252



[PATCH v5 00/16] Mark strings in Perl scripts for translation

2016-11-08 Thread Vasco Almeida
Mark messages in some perl scripts for translation.

In these series v5:
- Add and use a subroutine to comment lines. This way we can mark strings
  for translation without including the comment char within them.
- Mark for translation a message for the user when she is composing an
  e-mail in git-send-email.perl.

Interdiff included below.


Vasco Almeida (16):
  Git.pm: add subroutines for commenting lines
  i18n: add--interactive: mark strings for translation
  i18n: add--interactive: mark simple here-documents for translation
  i18n: add--interactive: mark strings with interpolation for
translation
  i18n: clean.c: match string with git-add--interactive.perl
  i18n: add--interactive: mark plural strings
  i18n: add--interactive: mark patch prompt for translation
  i18n: add--interactive: i18n of help_patch_cmd
  i18n: add--interactive: mark edit_hunk_manually message for
translation
  i18n: add--interactive: remove %patch_modes entries
  i18n: add--interactive: mark status words for translation
  i18n: send-email: mark strings for translation
  i18n: send-email: mark warnings and errors for translation
  i18n: send-email: mark string with interpolation for translation
  i18n: send-email: mark composing message for translation
  i18n: difftool: mark warnings for translation

 Makefile  |   3 +-
 builtin/clean.c   |  10 +-
 git-add--interactive.perl | 329 ++
 git-difftool.perl |  22 ++--
 git-send-email.perl   | 192 ++-
 perl/Git.pm   |  23 
 perl/Git/I18N.pm  |  19 ++-
 t/t0202/test.pl   |  14 +-
 8 files changed, 391 insertions(+), 221 deletions(-)

-- >8 --
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 4754104..56e6889 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -1039,26 +1039,26 @@ sub color_diff {
 
 my %edit_hunk_manually_modes = (
stage => N__(
-"# If the patch applies cleanly, the edited hunk will immediately be
-# marked for staging."),
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for staging."),
stash => N__(
-"# If the patch applies cleanly, the edited hunk will immediately be
-# marked for stashing."),
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for stashing."),
reset_head => N__(
-"# If the patch applies cleanly, the edited hunk will immediately be
-# marked for unstaging."),
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for unstaging."),
reset_nothead => N__(
-"# If the patch applies cleanly, the edited hunk will immediately be
-# marked for applying."),
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for applying."),
checkout_index => N__(
-"# If the patch applies cleanly, the edited hunk will immediately be
-# marked for discarding"),
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for discarding"),
checkout_head => N__(
-"# If the patch applies cleanly, the edited hunk will immediately be
-# marked for discarding."),
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for discarding."),
checkout_nothead => N__(
-"# If the patch applies cleanly, the edited hunk will immediately be
-# marked for applying."),
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for applying."),
 );
 
 sub edit_hunk_manually {
@@ -1068,21 +1068,22 @@ sub edit_hunk_manually {
my $fh;
open $fh, '>', $hunkfile
or die sprintf(__("failed to open hunk edit file for writing: 
%s"), $!);
-   print $fh __("# Manual hunk edit mode -- see bottom for a quick 
guide\n");
+   print $fh Git::comment_lines __("Manual hunk edit mode -- see bottom 
for a quick guide\n");
print $fh @$oldtext;
my $is_reverse = $patch_mode_flavour{IS_REVERSE};
my ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') : ('+', 
'-');
-   print $fh sprintf(__(
-"# ---
-# To remove '%s' lines, make them ' ' lines (context).
-# To remove '%s' lines, delete them.
-# Lines starting with # will be removed.
-#\n"), $remove_minus, $remove_plus),
-__($edit_hunk_manually_modes{$patch_mode}), __(
+   my $comment_line_char = Git::config("core.commentchar") || '#';
+   print $fh Git::comment_lines sprintf(__(
+"---
+To remove '%s' lines, make them ' ' lines (context).
+To remove '%s' lines, delete them.
+Lines starting with %s will be removed.
+\n"), $remove_minus, $remove_plus, $comment_line_char) .
+__($edit_hunk_manually_modes{$patch_mode}) ."\n". __(
 # TRANSLATORS: 'it' refers to the patch mentione

[PATCH v5 02/16] i18n: add--interactive: mark strings for translation

2016-11-08 Thread Vasco Almeida
Mark simple strings (without interpolation) for translation.

Brackets around first parameter of ternary operator is necessary because
otherwise xgettext fails to extract strings marked for translation from
the rest of the file.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 76 ++-
 1 file changed, 42 insertions(+), 34 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index ee3d81269..cf216ecb6 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -4,6 +4,7 @@ use 5.008;
 use strict;
 use warnings;
 use Git;
+use Git::I18N;
 
 binmode(STDOUT, ":raw");
 
@@ -253,8 +254,9 @@ sub list_untracked {
run_cmd_pipe(qw(git ls-files --others --exclude-standard --), @ARGV);
 }
 
-my $status_fmt = '%12s %12s %s';
-my $status_head = sprintf($status_fmt, 'staged', 'unstaged', 'path');
+# TRANSLATORS: you can adjust this to align "git add -i" status menu
+my $status_fmt = __('%12s %12s %s');
+my $status_head = sprintf($status_fmt, __('staged'), __('unstaged'), 
__('path'));
 
 {
my $initial;
@@ -680,7 +682,7 @@ sub update_cmd {
my @mods = list_modified('file-only');
return if (!@mods);
 
-   my @update = list_and_choose({ PROMPT => 'Update',
+   my @update = list_and_choose({ PROMPT => __('Update'),
   HEADER => $status_head, },
 @mods);
if (@update) {
@@ -692,7 +694,7 @@ sub update_cmd {
 }
 
 sub revert_cmd {
-   my @update = list_and_choose({ PROMPT => 'Revert',
+   my @update = list_and_choose({ PROMPT => __('Revert'),
   HEADER => $status_head, },
 list_modified());
if (@update) {
@@ -726,13 +728,13 @@ sub revert_cmd {
 }
 
 sub add_untracked_cmd {
-   my @add = list_and_choose({ PROMPT => 'Add untracked' },
+   my @add = list_and_choose({ PROMPT => __('Add untracked') },
  list_untracked());
if (@add) {
system(qw(git update-index --add --), @add);
say_n_paths('added', @add);
} else {
-   print "No untracked files.\n";
+   print __("No untracked files.\n");
}
print "\n";
 }
@@ -1166,8 +1168,14 @@ sub edit_hunk_loop {
}
else {
prompt_yesno(
-   'Your edited hunk does not apply. Edit again '
-   . '(saying "no" discards!) [y/n]? '
+   # TRANSLATORS: do not translate [y/n]
+   # The program will only accept that input
+   # at this point.
+   # Consider translating (saying "no" discards!) 
as
+   # (saying "n" for "no" discards!) if the 
translation
+   # of the word "no" does not start with n.
+   __('Your edited hunk does not apply. Edit again 
'
+  . '(saying "no" discards!) [y/n]? ')
) or return undef;
}
}
@@ -1213,11 +1221,11 @@ sub apply_patch_for_checkout_commit {
run_git_apply 'apply '.$reverse, @_;
return 1;
} elsif (!$applies_index) {
-   print colored $error_color, "The selected hunks do not apply to 
the index!\n";
-   if (prompt_yesno "Apply them to the worktree anyway? ") {
+   print colored $error_color, __("The selected hunks do not apply 
to the index!\n");
+   if (prompt_yesno __("Apply them to the worktree anyway? ")) {
return run_git_apply 'apply '.$reverse, @_;
} else {
-   print colored $error_color, "Nothing was applied.\n";
+   print colored $error_color, __("Nothing was 
applied.\n");
return 0;
}
} else {
@@ -1237,9 +1245,9 @@ sub patch_update_cmd {
 
if (!@mods) {
if (@all_mods) {
-   print STDERR "Only binary files changed.\n";
+   print STDERR __("Only binary files changed.\n");
} else {
-   print STDERR "No changes.\n";
+   print STDERR __("No changes.\n");
}
return 0;
}
@@ -1247,7 +1255,7 @@ sub patch_update_cmd {
@them = @mods;
}
else {
-   @them = list_and_choose({ PROMPT =>

Re: [PATCH v4 08/14] i18n: add--interactive: mark edit_hunk_manually message for translation

2016-11-07 Thread Vasco Almeida
A Seg, 10-10-2016 às 12:54 +, Vasco Almeida escreveu:
> diff --git a/git-add--interactive.perl b/git-add--interactive.perl
> index 045b847..861f7b0 100755
> --- a/git-add--interactive.perl
> +++ b/git-add--interactive.perl
> @@ -1058,6 +1058,30 @@ sub color_diff {
> } @_;
>  }
>  
> +my %edit_hunk_manually_modes = (
> +   stage => N__(
> +"# If the patch applies cleanly, the edited hunk will immediately be
> +# marked for staging."),
> +   stash => N__(
> +"# If the patch applies cleanly, the edited hunk will immediately be
> +# marked for stashing."),
> +   reset_head => N__(
> +"# If the patch applies cleanly, the edited hunk will immediately be
> +# marked for unstaging."),
> +   reset_nothead => N__(
> +"# If the patch applies cleanly, the edited hunk will immediately be
> +# marked for applying."),
> +   checkout_index => N__(
> +"# If the patch applies cleanly, the edited hunk will immediately be
> +# marked for discarding"),
> +   checkout_head => N__(
> +"# If the patch applies cleanly, the edited hunk will immediately be
> +# marked for discarding."),
> +   checkout_nothead => N__(
> +"# If the patch applies cleanly, the edited hunk will immediately be
> +# marked for applying."),
> +);
> +

I think marking strings comment with '#' for translation is not the
best thing because there is a change that a translator will miss to
comment a line. I will change this to mark only the text and then, at
run time, prefix comment chars to that text.

>  sub edit_hunk_manually {
> my ($oldtext) = @_;
>  
> @@ -1065,22 +1089,21 @@ sub edit_hunk_manually {
> my $fh;
> open $fh, '>', $hunkfile
> or die sprintf(__("failed to open hunk edit file for
> writing: %s"), $!);
> -   print $fh "# Manual hunk edit mode -- see bottom for a quick
> guide\n";
> +   print $fh __("# Manual hunk edit mode -- see bottom for a
> quick guide\n");

Here too.

> print $fh @$oldtext;
> -   my $participle = $patch_mode_flavour{PARTICIPLE};
> my $is_reverse = $patch_mode_flavour{IS_REVERSE};
> my ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') :
> ('+', '-');
> -   print $fh <<EOF;
> -# ---
> -# To remove '$remove_minus' lines, make them ' ' lines (context).
> -# To remove '$remove_plus' lines, delete them.
> +   print $fh sprintf(__(
> +"# ---
> +# To remove '%s' lines, make them ' ' lines (context).
> +# To remove '%s' lines, delete them.
>  # Lines starting with # will be removed.
> -#
> -# If the patch applies cleanly, the edited hunk will immediately be
> -# marked for $participle. If it does not apply cleanly, you will be
> given
> +#\n"), $remove_minus, $remove_plus),
> +__($edit_hunk_manually_modes{$patch_mode}), __(
> +# TRANSLATORS: 'it' refers to the patch mentioned in the previous
> messages.
> +" If it does not apply cleanly, you will be given
>  # an opportunity to edit again. If all lines of the hunk are
> removed,
> -# then the edit is aborted and the hunk is left unchanged.
> -EOF
> +# then the edit is aborted and the hunk is left unchanged.\n");

And here too.
Currently this joins the two sentences/parts in the same line like so

# If the patch applies cleanly, the edited hunk will immediately be
# marked for staging. If it does not apply cleanly, you will be given
# an opportunity to edit again. If all lines of the hunk are removed,
# then the edit is aborted and the hunk is left unchanged.

But since the translator translates each sentence separately, it is
hard to align them properly to not make lines too long. Hence I am
considering to break each sentence to start on its own line.


Re: [PATCH v4 05/14] i18n: add--interactive: mark plural strings

2016-10-20 Thread Vasco Almeida
A Seg, 10-10-2016 às 12:54 +, Vasco Almeida escreveu:
> @@ -70,6 +72,8 @@ Git::I18N - Perl interface to Git's Gettext localizations
>  
> printf __("The following error occurred: %s\n"), $error;
>  
> +   printf __n("commited %d file", "commited %d files", $files), $files;
> +

I forgot to add \n to this example as suggested in
<xmqqoa2ymnb1@gitster.mtv.corp.google.com>

What should I do? Should I wait for more reviews and then send a new
re-roll fixing this?


Re: [PATCH v4 05/14] i18n: add--interactive: mark plural strings

2016-10-20 Thread Vasco Almeida
A Qua, 19-10-2016 às 11:40 -0700, Junio C Hamano escreveu:
> Vasco Almeida <vascomalme...@sapo.pt> writes:
> 
> > 
> > @@ -669,12 +669,18 @@ sub status_cmd {
> >  sub say_n_paths {
> >     my $did = shift @_;
> >     my $cnt = scalar @_;
> > -   print "$did ";
> > -   if (1 < $cnt) {
> > -   print "$cnt paths\n";
> > -   }
> > -   else {
> > -   print "one path\n";
> > +   if ($did eq 'added') {
> > +   printf(__n("added %d path\n", "added %d paths\n",
> > +      $cnt), $cnt);
> > +   } elsif ($did eq 'updated') {
> > +   printf(__n("updated %d path\n", "updated %d
> > paths\n",
> > +      $cnt), $cnt);
> > +   } elsif ($did eq 'reverted') {
> > +   printf(__n("reverted %d path\n", "reverted %d
> > paths\n",
> > +      $cnt), $cnt);
> > +   } else {
> > +   printf(__n("touched %d path\n", "touched %d
> > paths\n",
> > +      $cnt), $cnt);
> >     }
> >  }
> 
> Nice to see you covered all verbs currently in use and then
> future-proofed by adding a fallback "touched" here.
> 
> Thanks.
> 

Thanks. Here I added %d to the singular sentences "added %d path\n" to
avoid a Perl warning about a redundant argument in printf.


Re: [PATCH v4 01/14] i18n: add--interactive: mark strings for translation

2016-10-20 Thread Vasco Almeida
A Qua, 19-10-2016 às 11:14 -0700, Junio C Hamano escreveu:
> Vasco Almeida <vascomalme...@sapo.pt> writes:
> 
> > 
> >     } else {
> > -   print "No untracked files.\n";
> > +   print __("No untracked files.\n");
> >     }
> 
> Not a big deal, but this makes me wonder if we want to do this
> instead
> 
>   print __("No untracked files.") . "\n";
> 
> so that translators do not have to remember to keep the final LF.

This can be a good idea. On the other hand, I think translators are
cautious to not forget the final LF since there is a lot of them from C
source. Also I think msgfmt checks if English source and translation
both end with newline or not. So if a translator forgets to put a \n
then msgfmt would return an error. If it is not the translator to find
the error herself, someone else will, like the Translation coordinator.

I will leave this patch as is.

https://www.gnu.org/software/gettext/FAQ.html#newline


[PATCH 1/4] i18n: apply: mark error message for translation

2016-10-17 Thread Vasco Almeida
Update test to reflect changes.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 apply.c   | 4 ++--
 t/t4254-am-corrupt.sh | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/apply.c b/apply.c
index 8215874..705cf56 100644
--- a/apply.c
+++ b/apply.c
@@ -1586,8 +1586,8 @@ static int find_header(struct apply_state *state,
patch->new_name = xstrdup(patch->def_name);
}
if (!patch->is_delete && !patch->new_name) {
-   error("git diff header lacks filename 
information "
-"(line %d)", state->linenr);
+   error(_("git diff header lacks filename 
information "
+"(line %d)"), state->linenr);
return -128;
}
patch->is_toplevel_relative = 1;
diff --git a/t/t4254-am-corrupt.sh b/t/t4254-am-corrupt.sh
index 9bd7dd2..168739c 100755
--- a/t/t4254-am-corrupt.sh
+++ b/t/t4254-am-corrupt.sh
@@ -31,7 +31,7 @@ test_expect_success 'try to apply corrupted patch' '
 test_expect_success 'compare diagnostic; ensure file is still here' '
echo "error: git diff header lacks filename information (line 4)" 
>expected &&
test_path_is_file f &&
-   test_cmp expected actual
+   test_i18ncmp expected actual
 '
 
 test_done
-- 
2.10.1.459.g5fd885d



[PATCH 2/4] i18n: convert mark error messages for translation

2016-10-17 Thread Vasco Almeida
Mark error messages about CRLF for translation.

Update test to reflect changes.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 convert.c   | 12 
 t/t0020-crlf.sh |  6 +-
 2 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/convert.c b/convert.c
index 077f5e6..0ad39b1 100644
--- a/convert.c
+++ b/convert.c
@@ -197,17 +197,21 @@ static void check_safe_crlf(const char *path, enum 
crlf_action crlf_action,
 * CRLFs would not be restored by checkout
 */
if (checksafe == SAFE_CRLF_WARN)
-   warning("CRLF will be replaced by LF in %s.\nThe file 
will have its original line endings in your working directory.", path);
+   warning(_("CRLF will be replaced by LF in %s.\n"
+ "The file will have its original line"
+ " endings in your working directory."), path);
else /* i.e. SAFE_CRLF_FAIL */
-   die("CRLF would be replaced by LF in %s.", path);
+   die(_("CRLF would be replaced by LF in %s."), path);
} else if (old_stats->lonelf && !new_stats->lonelf ) {
/*
 * CRLFs would be added by checkout
 */
if (checksafe == SAFE_CRLF_WARN)
-   warning("LF will be replaced by CRLF in %s.\nThe file 
will have its original line endings in your working directory.", path);
+   warning(_("LF will be replaced by CRLF in %s.\n"
+ "The file will have its original line"
+ " endings in your working directory."), path);
else /* i.e. SAFE_CRLF_FAIL */
-   die("LF would be replaced by CRLF in %s", path);
+   die(_("LF would be replaced by CRLF in %s"), path);
}
 }
 
diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh
index f94120a..71350e0 100755
--- a/t/t0020-crlf.sh
+++ b/t/t0020-crlf.sh
@@ -83,7 +83,11 @@ test_expect_success 'safecrlf: print warning only once' '
git add doublewarn &&
git commit -m "nowarn" &&
for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr 
>doublewarn &&
-   test $(git add doublewarn 2>&1 | grep "CRLF will be replaced by LF" | 
wc -l) = 1
+   git add doublewarn 2>err &&
+   if test_have_prereq C_LOCALE_OUTPUT
+   then
+   test $(grep "CRLF will be replaced by LF" err | wc -l) = 1
+   fi
 '
 
 
-- 
2.10.1.459.g5fd885d



[PATCH 4/4] i18n: diff: mark warnings for translation

2016-10-17 Thread Vasco Almeida
Mark rename_limit_warning and degrade_cc_to_c_warning and
rename_limit_warning for translation.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 diff.c | 14 +++---
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/diff.c b/diff.c
index 1d304e0..1687317 100644
--- a/diff.c
+++ b/diff.c
@@ -4638,25 +4638,25 @@ static int is_summary_empty(const struct 
diff_queue_struct *q)
 }
 
 static const char rename_limit_warning[] =
-"inexact rename detection was skipped due to too many files.";
+N_("inexact rename detection was skipped due to too many files.");
 
 static const char degrade_cc_to_c_warning[] =
-"only found copies from modified paths due to too many files.";
+N_("only found copies from modified paths due to too many files.");
 
 static const char rename_limit_advice[] =
-"you may want to set your %s variable to at least "
-"%d and retry the command.";
+N_("you may want to set your %s variable to at least "
+   "%d and retry the command.");
 
 void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc)
 {
if (degraded_cc)
-   warning(degrade_cc_to_c_warning);
+   warning(_(degrade_cc_to_c_warning));
else if (needed)
-   warning(rename_limit_warning);
+   warning(_(rename_limit_warning));
else
return;
if (0 < needed && needed < 32767)
-   warning(rename_limit_advice, varname, needed);
+   warning(_(rename_limit_advice), varname, needed);
 }
 
 void diff_flush(struct diff_options *options)
-- 
2.10.1.459.g5fd885d



[PATCH 3/4] i18n: credential-cache--daemon: mark advice for translation

2016-10-17 Thread Vasco Almeida
Mark permissions_advice for translation.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 credential-cache--daemon.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c
index 1e5f16a..46c5937 100644
--- a/credential-cache--daemon.c
+++ b/credential-cache--daemon.c
@@ -219,11 +219,11 @@ static void serve_cache(const char *socket_path, int 
debug)
close(fd);
 }
 
-static const char permissions_advice[] =
+static const char permissions_advice[] = N_(
 "The permissions on your socket directory are too loose; other\n"
 "users may be able to read your cached credentials. Consider running:\n"
 "\n"
-"  chmod 0700 %s";
+"  chmod 0700 %s");
 static void init_socket_directory(const char *path)
 {
struct stat st;
@@ -232,7 +232,7 @@ static void init_socket_directory(const char *path)
 
if (!stat(dir, )) {
if (st.st_mode & 077)
-   die(permissions_advice, dir);
+   die(_(permissions_advice), dir);
} else {
/*
 * We must be sure to create the directory with the correct 
mode,
-- 
2.10.1.459.g5fd885d



[PATCH 2/3] i18n: apply: mark info messages for translation

2016-10-14 Thread Vasco Almeida
Mark messages for translation printed to stderr.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 apply.c | 8 
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/apply.c b/apply.c
index 201d3a7..13b2064 100644
--- a/apply.c
+++ b/apply.c
@@ -3554,7 +3554,7 @@ static int try_threeway(struct apply_state *state,
return error("repository lacks the necessary blob to fall back 
on 3-way merge.");
 
if (state->apply_verbosity > verbosity_silent)
-   fprintf(stderr, "Falling back to three-way merge...\n");
+   fprintf(stderr, _("Falling back to three-way merge...\n"));
 
img = strbuf_detach(, );
prepare_image(_image, img, len, 1);
@@ -3586,7 +3586,7 @@ static int try_threeway(struct apply_state *state,
if (status < 0) {
if (state->apply_verbosity > verbosity_silent)
fprintf(stderr,
-   "Failed to fall back on three-way merge...\n");
+   _("Failed to fall back on three-way 
merge...\n"));
return status;
}
 
@@ -3600,12 +3600,12 @@ static int try_threeway(struct apply_state *state,
oidcpy(>threeway_stage[2], _oid);
if (state->apply_verbosity > verbosity_silent)
fprintf(stderr,
-   "Applied patch to '%s' with conflicts.\n",
+   _("Applied patch to '%s' with conflicts.\n"),
patch->new_name);
} else {
if (state->apply_verbosity > verbosity_silent)
fprintf(stderr,
-   "Applied patch to '%s' cleanly.\n",
+   _("Applied patch to '%s' cleanly.\n"),
patch->new_name);
}
return 0;
-- 
2.10.1.459.g5fd885d



[PATCH 1/3] i18n: apply: mark plural string for translation

2016-10-14 Thread Vasco Almeida
Mark plural string for translation using Q_().

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 apply.c | 10 ++
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/apply.c b/apply.c
index b03d274..201d3a7 100644
--- a/apply.c
+++ b/apply.c
@@ -4869,10 +4869,12 @@ int apply_all_patches(struct apply_state *state,
goto end;
}
if (state->applied_after_fixing_ws && state->apply)
-   warning("%d line%s applied after"
-   " fixing whitespace errors.",
-   state->applied_after_fixing_ws,
-   state->applied_after_fixing_ws == 1 ? "" : "s");
+   warning(Q_("%d line applied after"
+  " fixing whitespace errors.",
+  "%d lines applied after"
+  " fixing whitespace errors.",
+  state->applied_after_fixing_ws),
+   state->applied_after_fixing_ws);
else if (state->whitespace_error)
warning(Q_("%d line adds whitespace errors.",
   "%d lines add whitespace errors.",
-- 
2.10.1.459.g5fd885d



[PATCH 3/3] i18n: apply: mark error messages for translation

2016-10-14 Thread Vasco Almeida
Mark error messages for translation passed to error() and die()
functions.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 apply.c | 48 
 1 file changed, 24 insertions(+), 24 deletions(-)

diff --git a/apply.c b/apply.c
index 13b2064..8215874 100644
--- a/apply.c
+++ b/apply.c
@@ -122,9 +122,9 @@ int check_apply_state(struct apply_state *state, int 
force_apply)
int is_not_gitdir = !startup_info->have_repository;
 
if (state->apply_with_reject && state->threeway)
-   return error("--reject and --3way cannot be used together.");
+   return error(_("--reject and --3way cannot be used together."));
if (state->cached && state->threeway)
-   return error("--cached and --3way cannot be used together.");
+   return error(_("--cached and --3way cannot be used together."));
if (state->threeway) {
if (is_not_gitdir)
return error(_("--3way outside a repository"));
@@ -3095,8 +3095,8 @@ static int apply_binary_fragment(struct apply_state 
*state,
/* Binary patch is irreversible without the optional second hunk */
if (state->apply_in_reverse) {
if (!fragment->next)
-   return error("cannot reverse-apply a binary patch "
-"without the reverse hunk to '%s'",
+   return error(_("cannot reverse-apply a binary patch "
+  "without the reverse hunk to '%s'"),
 patch->new_name
 ? patch->new_name : patch->old_name);
fragment = fragment->next;
@@ -3141,8 +3141,8 @@ static int apply_binary(struct apply_state *state,
strlen(patch->new_sha1_prefix) != 40 ||
get_oid_hex(patch->old_sha1_prefix, ) ||
get_oid_hex(patch->new_sha1_prefix, ))
-   return error("cannot apply binary patch to '%s' "
-"without full index line", name);
+   return error(_("cannot apply binary patch to '%s' "
+  "without full index line"), name);
 
if (patch->old_name) {
/*
@@ -3151,16 +3151,16 @@ static int apply_binary(struct apply_state *state,
 */
hash_sha1_file(img->buf, img->len, blob_type, oid.hash);
if (strcmp(oid_to_hex(), patch->old_sha1_prefix))
-   return error("the patch applies to '%s' (%s), "
-"which does not match the "
-"current contents.",
+   return error(_("the patch applies to '%s' (%s), "
+  "which does not match the "
+  "current contents."),
 name, oid_to_hex());
}
else {
/* Otherwise, the old one must be empty. */
if (img->len)
-   return error("the patch applies to an empty "
-"'%s' but it is not empty", name);
+   return error(_("the patch applies to an empty "
+  "'%s' but it is not empty"), name);
}
 
get_oid_hex(patch->new_sha1_prefix, );
@@ -3177,8 +3177,8 @@ static int apply_binary(struct apply_state *state,
 
result = read_sha1_file(oid.hash, , );
if (!result)
-   return error("the necessary postimage %s for "
-"'%s' cannot be read",
+   return error(_("the necessary postimage %s for "
+  "'%s' cannot be read"),
 patch->new_sha1_prefix, name);
clear_image(img);
img->buf = result;
@@ -3551,7 +3551,7 @@ static int try_threeway(struct apply_state *state,
write_sha1_file("", 0, blob_type, pre_oid.hash);
else if (get_sha1(patch->old_sha1_prefix, pre_oid.hash) ||
 read_blob_object(, _oid, patch->old_mode))
-   return error("repository lacks the necessary blob to fall back 
on 3-way merge.");
+   return error(_("repository lacks the necessary blob to fall 
back on 3-way merge."));
 
if (state->apply_verbosity > verbosity_silent)
fprintf(

[PATCH] t1512: become resilient to GETTEXT_POISON build

2016-10-12 Thread Vasco Almeida
The concerned message was marked for translation by 0c99171
("get_short_sha1: mark ambiguity error for translation", 2016-09-26).

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 t/t1512-rev-parse-disambiguation.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/t1512-rev-parse-disambiguation.sh 
b/t/t1512-rev-parse-disambiguation.sh
index 7c659eb..711704b 100755
--- a/t/t1512-rev-parse-disambiguation.sh
+++ b/t/t1512-rev-parse-disambiguation.sh
@@ -42,7 +42,7 @@ test_expect_success 'blob and tree' '
 
 test_expect_success 'warn ambiguity when no candidate matches type hint' '
test_must_fail git rev-parse --verify 0^{commit} 2>actual &&
-   grep "short SHA1 0 is ambiguous" actual
+   test_i18ngrep "short SHA1 0 is ambiguous" actual
 '
 
 test_expect_success 'disambiguate tree-ish' '
-- 
2.7.4



[PATCH v4 12/14] i18n: send-email: mark warnings and errors for translation

2016-10-10 Thread Vasco Almeida
Mark warnings, errors and other messages for translation.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-send-email.perl | 34 +-
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/git-send-email.perl b/git-send-email.perl
index 78eb59b..982c6c0 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -118,20 +118,20 @@ sub format_2822_time {
my $localmin = $localtm[1] + $localtm[2] * 60;
my $gmtmin = $gmttm[1] + $gmttm[2] * 60;
if ($localtm[0] != $gmttm[0]) {
-   die "local zone differs from GMT by a non-minute interval\n";
+   die __("local zone differs from GMT by a non-minute 
interval\n");
}
if ((($gmttm[6] + 1) % 7) == $localtm[6]) {
$localmin += 1440;
} elsif ((($gmttm[6] - 1) % 7) == $localtm[6]) {
$localmin -= 1440;
} elsif ($gmttm[6] != $localtm[6]) {
-   die "local time offset greater than or equal to 24 hours\n";
+   die __("local time offset greater than or equal to 24 hours\n");
}
my $offset = $localmin - $gmtmin;
my $offhour = $offset / 60;
my $offmin = abs($offset % 60);
if (abs($offhour) >= 24) {
-   die ("local time offset greater than or equal to 24 hours\n");
+   die __("local time offset greater than or equal to 24 hours\n");
}
 
return sprintf("%s, %2d %s %d %02d:%02d:%02d %s%02d%02d",
@@ -199,13 +199,13 @@ sub do_edit {
map {
system('sh', '-c', $editor.' "$@"', $editor, $_);
if (($? & 127) || ($? >> 8)) {
-   die("the editor exited uncleanly, aborting 
everything");
+   die(__("the editor exited uncleanly, aborting 
everything"));
}
} @_;
} else {
system('sh', '-c', $editor.' "$@"', $editor, @_);
if (($? & 127) || ($? >> 8)) {
-   die("the editor exited uncleanly, aborting everything");
+   die(__("the editor exited uncleanly, aborting 
everything"));
}
}
 }
@@ -299,7 +299,7 @@ my $help;
 my $rc = GetOptions("h" => \$help,
 "dump-aliases" => \$dump_aliases);
 usage() unless $rc;
-die "--dump-aliases incompatible with other options\n"
+die __("--dump-aliases incompatible with other options\n")
 if !$help and $dump_aliases and @ARGV;
 $rc = GetOptions(
"sender|from=s" => \$sender,
@@ -362,7 +362,7 @@ unless ($rc) {
 usage();
 }
 
-die "Cannot run git format-patch from outside a repository\n"
+die __("Cannot run git format-patch from outside a repository\n")
if $format_patch and not $repo;
 
 # Now, let's fill any that aren't set in with defaults:
@@ -617,7 +617,7 @@ while (defined(my $f = shift @ARGV)) {
 }
 
 if (@rev_list_opts) {
-   die "Cannot run git format-patch from outside a repository\n"
+   die __("Cannot run git format-patch from outside a repository\n")
unless $repo;
push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 
1), @rev_list_opts);
 }
@@ -638,7 +638,7 @@ if (@files) {
print $_,"\n" for (@files);
}
 } else {
-   print STDERR "\nNo patch files specified!\n\n";
+   print STDERR __("\nNo patch files specified!\n\n");
usage();
 }
 
@@ -730,7 +730,7 @@ EOT
$sender = $1;
next;
} elsif (/^(?:To|Cc|Bcc):/i) {
-   print "To/Cc/Bcc fields are not interpreted yet, they 
have been ignored\n";
+   print __("To/Cc/Bcc fields are not interpreted yet, 
they have been ignored\n");
next;
}
print $c2 $_;
@@ -739,7 +739,7 @@ EOT
close $c2;
 
if ($summary_empty) {
-   print "Summary email is empty, skipping it\n";
+   print __("Summary email is empty, skipping it\n");
$compose = -1;
}
 } elsif ($annotate) {
@@ -1314,7 +1314,7 @@ Message-Id: $message_id
$_ = ask(__("Send this email? ([y]es|[n]o|[q]uit|[a]ll): "),
 valid_re => qr/^(?:yes|y|no|n|quit|q|all|a)/i,
 default => $ask_default);
-   die "Send this email reply required" unless defined $_;
+   die __("Send this email reply required") unless defined $_

[PATCH v4 10/14] i18n: add--interactive: mark status words for translation

2016-10-10 Thread Vasco Almeida
Mark words 'nothing', 'unchanged' and 'binary' used to display what has
been staged or not, in "git add -i" status command.

Alternatively one could mark N__('nothing') no-op in order to
xgettext(1) extract the string and then trigger the translation at run
time only with __($print->{FILE}), but that has the side effect of triggering
retrieval of translations for the changes indicator too (e.g. +2/-1)
which may or may not be a problem.

To avoid that potential problem, mark only where there is certain to
trigger translation only of those words but in this case we must also
retrieve the translation for the eq tests, since the value assigned was
of the translation, not the English source.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 12 ++--
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index d7a8e0d..4754104 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -295,7 +295,7 @@ sub list_modified {
my ($change, $bin);
$file = unquote_path($file);
if ($add eq '-' && $del eq '-') {
-   $change = 'binary';
+   $change = __('binary');
$bin = 1;
}
else {
@@ -304,7 +304,7 @@ sub list_modified {
$data{$file} = {
INDEX => $change,
BINARY => $bin,
-   FILE => 'nothing',
+   FILE => __('nothing'),
}
}
elsif (($adddel, $file) =
@@ -320,7 +320,7 @@ sub list_modified {
$file = unquote_path($file);
my ($change, $bin);
if ($add eq '-' && $del eq '-') {
-   $change = 'binary';
+   $change = __('binary');
$bin = 1;
}
else {
@@ -340,7 +340,7 @@ sub list_modified {
$file = unquote_path($2);
if (!exists $data{$file}) {
$data{$file} = +{
-   INDEX => 'unchanged',
+   INDEX => __('unchanged'),
BINARY => 0,
};
}
@@ -355,10 +355,10 @@ sub list_modified {
 
if ($only) {
if ($only eq 'index-only') {
-   next if ($it->{INDEX} eq 'unchanged');
+   next if ($it->{INDEX} eq __('unchanged'));
}
if ($only eq 'file-only') {
-   next if ($it->{FILE} eq 'nothing');
+   next if ($it->{FILE} eq __('nothing'));
}
}
push @return, +{
-- 
2.7.4



[PATCH v4 11/14] i18n: send-email: mark strings for translation

2016-10-10 Thread Vasco Almeida
Mark strings often displayed to the user for translation.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-send-email.perl | 52 ++--
 1 file changed, 30 insertions(+), 22 deletions(-)

diff --git a/git-send-email.perl b/git-send-email.perl
index da81be4..78eb59b 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -28,6 +28,7 @@ use File::Temp qw/ tempdir tempfile /;
 use File::Spec::Functions qw(catfile);
 use Error qw(:try);
 use Git;
+use Git::I18N;
 
 Getopt::Long::Configure qw/ pass_through /;
 
@@ -797,12 +798,12 @@ foreach my $f (@files) {
 }
 
 if (!defined $auto_8bit_encoding && scalar %broken_encoding) {
-   print "The following files are 8bit, but do not declare " .
-   "a Content-Transfer-Encoding.\n";
+   print __("The following files are 8bit, but do not declare " .
+"a Content-Transfer-Encoding.\n");
foreach my $f (sort keys %broken_encoding) {
print "$f\n";
}
-   $auto_8bit_encoding = ask("Which 8bit encoding should I declare 
[UTF-8]? ",
+   $auto_8bit_encoding = ask(__("Which 8bit encoding should I declare 
[UTF-8]? "),
  valid_re => qr/.{4}/, confirm_only => 1,
  default => "UTF-8");
 }
@@ -829,7 +830,7 @@ if (defined $sender) {
 # But it's a no-op to run sanitize_address on an already sanitized address.
 $sender = sanitize_address($sender);
 
-my $to_whom = "To whom should the emails be sent (if anyone)?";
+my $to_whom = __("To whom should the emails be sent (if anyone)?");
 my $prompting = 0;
 if (!@initial_to && !defined $to_cmd) {
my $to = ask("$to_whom ",
@@ -859,7 +860,7 @@ sub expand_one_alias {
 
 if ($thread && !defined $initial_reply_to && $prompting) {
$initial_reply_to = ask(
-   "Message-ID to be used as In-Reply-To for the first email (if 
any)? ",
+   __("Message-ID to be used as In-Reply-To for the first email 
(if any)? "),
default => "",
valid_re => qr/\@.*\./, confirm_only => 1);
 }
@@ -918,7 +919,10 @@ sub validate_address {
my $address = shift;
while (!extract_valid_address($address)) {
print STDERR "error: unable to extract a valid address from: 
$address\n";
-   $_ = ask("What to do with this address? ([q]uit|[d]rop|[e]dit): 
",
+   # TRANSLATORS: Make sure to include [q] [d] [e] in your
+   # translation. The program will only accept English input
+   # at this point.
+   $_ = ask(__("What to do with this address? 
([q]uit|[d]rop|[e]dit): "),
valid_re => qr/^(?:quit|q|drop|d|edit|e)/i,
default => 'q');
if (/^d/i) {
@@ -1293,17 +1297,21 @@ Message-Id: $message_id
if ($needs_confirm eq "inform") {
$confirm_unconfigured = 0; # squelch this message for 
the rest of this run
$ask_default = "y"; # assume yes on EOF since user 
hasn't explicitly asked for confirmation
-   print "The Cc list above has been expanded by 
additional\n";
-   print "addresses found in the patch commit message. 
By default\n";
-   print "send-email prompts before sending whenever 
this occurs.\n";
-   print "This behavior is controlled by the 
sendemail.confirm\n";
-   print "configuration setting.\n";
-   print "\n";
-   print "For additional information, run 'git 
send-email --help'.\n";
-   print "To retain the current behavior, but squelch 
this message,\n";
-   print "run 'git config --global sendemail.confirm 
auto'.\n\n";
+   print __(
+"The Cc list above has been expanded by additional
+addresses found in the patch commit message. By default
+send-email prompts before sending whenever this occurs.
+This behavior is controlled by the sendemail.confirm
+configuration setting.
+
+For additional information, run 'git send-email --help'.
+To retain the current behavior, but squelch this message,
+run 'git config --global sendemail.confirm auto'."), "\n\n";
}
-   $_ = ask("Send this email? ([y]es|[n]o|[q]uit|[a]ll): ",
+   # TRANSLATORS: Make sure to include [y] [n] [q] [a] in your
+   # translation. The prog

[PATCH v4 13/14] i18n: send-email: mark string with interpolation for translation

2016-10-10 Thread Vasco Almeida
Mark warnings, errors and other messages that are interpolated for
translation.

We call sprintf() before calling die() and in few other circumstances in
order to replace the values on the placeholders.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-send-email.perl | 90 -
 1 file changed, 48 insertions(+), 42 deletions(-)

diff --git a/git-send-email.perl b/git-send-email.perl
index 982c6c0..5c01425 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -279,10 +279,13 @@ sub signal_handler {
# tmp files from --compose
if (defined $compose_filename) {
if (-e $compose_filename) {
-   print "'$compose_filename' contains an intermediate 
version of the email you were composing.\n";
+   printf __("'%s' contains an intermediate version ".
+ "of the email you were composing.\n"),
+ $compose_filename;
}
if (-e ($compose_filename . ".final")) {
-   print "'$compose_filename.final' contains the composed 
email.\n"
+   printf __("'%s.final' contains the composed email.\n"),
+ $compose_filename;
}
}
 
@@ -431,7 +434,7 @@ $smtp_encryption = '' unless (defined $smtp_encryption);
 my(%suppress_cc);
 if (@suppress_cc) {
foreach my $entry (@suppress_cc) {
-   die "Unknown --suppress-cc field: '$entry'\n"
+   die sprintf(__("Unknown --suppress-cc field: '%s'\n"), $entry)
unless $entry =~ 
/^(?:all|cccmd|cc|author|self|sob|body|bodycc)$/;
$suppress_cc{$entry} = 1;
}
@@ -460,7 +463,7 @@ my $confirm_unconfigured = !defined $confirm;
 if ($confirm_unconfigured) {
$confirm = scalar %suppress_cc ? 'compose' : 'auto';
 };
-die "Unknown --confirm setting: '$confirm'\n"
+die sprintf(__("Unknown --confirm setting: '%s'\n"), $confirm)
unless $confirm =~ /^(?:auto|cc|compose|always|never)/;
 
 # Debugging, print out the suppressions.
@@ -492,16 +495,16 @@ my %aliases;
 sub parse_sendmail_alias {
local $_ = shift;
if (/"/) {
-   print STDERR "warning: sendmail alias with quotes is not 
supported: $_\n";
+   printf STDERR __("warning: sendmail alias with quotes is not 
supported: %s\n"), $_;
} elsif (/:include:/) {
-   print STDERR "warning: `:include:` not supported: $_\n";
+   printf STDERR __("warning: `:include:` not supported: %s\n"), 
$_;
} elsif (/[\/|]/) {
-   print STDERR "warning: `/file` or `|pipe` redirection not 
supported: $_\n";
+   printf STDERR __("warning: `/file` or `|pipe` redirection not 
supported: %s\n"), $_;
} elsif (/^(\S+?)\s*:\s*(.+)$/) {
my ($alias, $addr) = ($1, $2);
$aliases{$alias} = [ split_addrs($addr) ];
} else {
-   print STDERR "warning: sendmail line is not recognized: $_\n";
+   printf STDERR __("warning: sendmail line is not recognized: 
%s\n"), $_;
}
 }
 
@@ -582,13 +585,12 @@ sub is_format_patch_arg {
if (defined($format_patch)) {
return $format_patch;
}
-   die(<<EOF);
-File '$f' exists but it could also be the range of commits
+   die sprintf(__(
+"File '%s' exists but it could also be the range of commits
 to produce patches for.  Please disambiguate by...
 
-* Saying "./$f" if you mean a file; or
-* Giving --format-patch option if you mean a range.
-EOF
+* Saying \"./%s\" if you mean a file; or
+* Giving --format-patch option if you mean a range."), $f, $f);
} catch Git::Error::Command with {
# Not a valid revision.  Treat it as a filename.
return 0;
@@ -604,7 +606,7 @@ while (defined(my $f = shift @ARGV)) {
@ARGV = ();
} elsif (-d $f and !is_format_patch_arg($f)) {
opendir my $dh, $f
-   or die "Failed to opendir $f: $!";
+   or die sprintf(__("Failed to opendir %s: %s"), $f, $!);
 
push @files, grep { -f $_ } map { catfile($f, $_) }
sort readdir $dh;
@@ -628,7 +630,8 @@ if ($validate) {
foreach my $f (@files) {
unless (-p $f) {
my $error = validate_patch($f);
-   $error and die "fatal: $f: $error\nwarning: no patches 
were sent\n";
+   $error and die sprintf(__("fatal: %s: %s\n

[PATCH v4 14/14] i18n: difftool: mark warnings for translation

2016-10-10 Thread Vasco Almeida
Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-difftool.perl | 22 --
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/git-difftool.perl b/git-difftool.perl
index a5790d0..8d3632e 100755
--- a/git-difftool.perl
+++ b/git-difftool.perl
@@ -22,6 +22,7 @@ use File::Path qw(mkpath rmtree);
 use File::Temp qw(tempdir);
 use Getopt::Long qw(:config pass_through);
 use Git;
+use Git::I18N;
 
 sub usage
 {
@@ -122,7 +123,7 @@ sub setup_dir_diff
my $i = 0;
while ($i < $#rawdiff) {
if ($rawdiff[$i] =~ /^::/) {
-   warn << 'EOF';
+   warn __ <<'EOF';
 Combined diff formats ('-c' and '--cc') are not supported in
 directory diff mode ('-d' and '--dir-diff').
 EOF
@@ -338,7 +339,7 @@ sub main
if (length($opts{difftool_cmd}) > 0) {
$ENV{GIT_DIFF_TOOL} = $opts{difftool_cmd};
} else {
-   print "No  given for --tool=\n";
+   print __("No  given for --tool=\n");
usage(1);
}
}
@@ -346,7 +347,7 @@ sub main
if (length($opts{extcmd}) > 0) {
$ENV{GIT_DIFFTOOL_EXTCMD} = $opts{extcmd};
} else {
-   print "No  given for --extcmd=\n";
+   print __("No  given for --extcmd=\n");
usage(1);
}
}
@@ -419,11 +420,11 @@ sub dir_diff
}
 
if (exists $wt_modified{$file} and exists $tmp_modified{$file}) 
{
-   my $errmsg = "warning: Both files modified: ";
-   $errmsg .= "'$workdir/$file' and '$b/$file'.\n";
-   $errmsg .= "warning: Working tree file has been 
left.\n";
-   $errmsg .= "warning:\n";
-   warn $errmsg;
+   warn sprintf(__(
+   "warning: Both files modified:\n" .
+   "'%s/%s' and '%s/%s'.\n" .
+   "warning: Working tree file has been left.\n" .
+   "warning:\n"), $workdir, $file, $b, $file);
$error = 1;
} elsif (exists $tmp_modified{$file}) {
my $mode = stat("$b/$file")->mode;
@@ -435,8 +436,9 @@ sub dir_diff
}
}
if ($error) {
-   warn "warning: Temporary files exist in '$tmpdir'.\n";
-   warn "warning: You may want to cleanup or recover these.\n";
+   warn sprintf(__(
+   "warning: Temporary files exist in '%s'.\n" .
+   "warning: You may want to cleanup or recover 
these.\n"), $tmpdir);
exit(1);
} else {
exit_cleanup($tmpdir, $rc);
-- 
2.7.4



[PATCH v4 09/14] i18n: add--interactive: remove %patch_modes entries

2016-10-10 Thread Vasco Almeida
Remove unnecessary entries from %patch_modes. After the i18n conversion,
these entries are not used anymore.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 21 -
 1 file changed, 21 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 861f7b0..d7a8e0d 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -106,9 +106,6 @@ my %patch_modes = (
DIFF => 'diff-files -p',
APPLY => sub { apply_patch 'apply --cached', @_; },
APPLY_CHECK => 'apply --cached',
-   VERB => 'Stage',
-   TARGET => '',
-   PARTICIPLE => 'staging',
FILTER => 'file-only',
IS_REVERSE => 0,
},
@@ -116,9 +113,6 @@ my %patch_modes = (
DIFF => 'diff-index -p HEAD',
APPLY => sub { apply_patch 'apply --cached', @_; },
APPLY_CHECK => 'apply --cached',
-   VERB => 'Stash',
-   TARGET => '',
-   PARTICIPLE => 'stashing',
FILTER => undef,
IS_REVERSE => 0,
},
@@ -126,9 +120,6 @@ my %patch_modes = (
DIFF => 'diff-index -p --cached',
APPLY => sub { apply_patch 'apply -R --cached', @_; },
APPLY_CHECK => 'apply -R --cached',
-   VERB => 'Unstage',
-   TARGET => '',
-   PARTICIPLE => 'unstaging',
FILTER => 'index-only',
IS_REVERSE => 1,
},
@@ -136,9 +127,6 @@ my %patch_modes = (
DIFF => 'diff-index -R -p --cached',
APPLY => sub { apply_patch 'apply --cached', @_; },
APPLY_CHECK => 'apply --cached',
-   VERB => 'Apply',
-   TARGET => ' to index',
-   PARTICIPLE => 'applying',
FILTER => 'index-only',
IS_REVERSE => 0,
},
@@ -146,9 +134,6 @@ my %patch_modes = (
DIFF => 'diff-files -p',
APPLY => sub { apply_patch 'apply -R', @_; },
APPLY_CHECK => 'apply -R',
-   VERB => 'Discard',
-   TARGET => ' from worktree',
-   PARTICIPLE => 'discarding',
FILTER => 'file-only',
IS_REVERSE => 1,
},
@@ -156,9 +141,6 @@ my %patch_modes = (
DIFF => 'diff-index -p',
APPLY => sub { apply_patch_for_checkout_commit '-R', @_ },
APPLY_CHECK => 'apply -R',
-   VERB => 'Discard',
-   TARGET => ' from index and worktree',
-   PARTICIPLE => 'discarding',
FILTER => undef,
IS_REVERSE => 1,
},
@@ -166,9 +148,6 @@ my %patch_modes = (
DIFF => 'diff-index -R -p',
APPLY => sub { apply_patch_for_checkout_commit '', @_ },
APPLY_CHECK => 'apply',
-   VERB => 'Apply',
-   TARGET => ' to index and worktree',
-   PARTICIPLE => 'applying',
FILTER => undef,
IS_REVERSE => 0,
},
-- 
2.7.4



[PATCH v4 08/14] i18n: add--interactive: mark edit_hunk_manually message for translation

2016-10-10 Thread Vasco Almeida
Mark message of edit_hunk_manually displayed in the editing file when
user chooses 'e' option.  The message had to be unfolded to allow
translation of the $participle verb.

Some messages end up being exactly the same for some uses cases, but
left it for easier change in the future, e.g., wanting to change wording
of one particular use case.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 45 ++---
 1 file changed, 34 insertions(+), 11 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 045b847..861f7b0 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -1058,6 +1058,30 @@ sub color_diff {
} @_;
 }
 
+my %edit_hunk_manually_modes = (
+   stage => N__(
+"# If the patch applies cleanly, the edited hunk will immediately be
+# marked for staging."),
+   stash => N__(
+"# If the patch applies cleanly, the edited hunk will immediately be
+# marked for stashing."),
+   reset_head => N__(
+"# If the patch applies cleanly, the edited hunk will immediately be
+# marked for unstaging."),
+   reset_nothead => N__(
+"# If the patch applies cleanly, the edited hunk will immediately be
+# marked for applying."),
+   checkout_index => N__(
+"# If the patch applies cleanly, the edited hunk will immediately be
+# marked for discarding"),
+   checkout_head => N__(
+"# If the patch applies cleanly, the edited hunk will immediately be
+# marked for discarding."),
+   checkout_nothead => N__(
+"# If the patch applies cleanly, the edited hunk will immediately be
+# marked for applying."),
+);
+
 sub edit_hunk_manually {
my ($oldtext) = @_;
 
@@ -1065,22 +1089,21 @@ sub edit_hunk_manually {
my $fh;
open $fh, '>', $hunkfile
or die sprintf(__("failed to open hunk edit file for writing: 
%s"), $!);
-   print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n";
+   print $fh __("# Manual hunk edit mode -- see bottom for a quick 
guide\n");
print $fh @$oldtext;
-   my $participle = $patch_mode_flavour{PARTICIPLE};
my $is_reverse = $patch_mode_flavour{IS_REVERSE};
my ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') : ('+', 
'-');
-   print $fh <<EOF;
-# ---
-# To remove '$remove_minus' lines, make them ' ' lines (context).
-# To remove '$remove_plus' lines, delete them.
+   print $fh sprintf(__(
+"# ---
+# To remove '%s' lines, make them ' ' lines (context).
+# To remove '%s' lines, delete them.
 # Lines starting with # will be removed.
-#
-# If the patch applies cleanly, the edited hunk will immediately be
-# marked for $participle. If it does not apply cleanly, you will be given
+#\n"), $remove_minus, $remove_plus),
+__($edit_hunk_manually_modes{$patch_mode}), __(
+# TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
+" If it does not apply cleanly, you will be given
 # an opportunity to edit again. If all lines of the hunk are removed,
-# then the edit is aborted and the hunk is left unchanged.
-EOF
+# then the edit is aborted and the hunk is left unchanged.\n");
close $fh;
 
chomp(my $editor = run_cmd_pipe(qw(git var GIT_EDITOR)));
-- 
2.7.4



[PATCH v4 04/14] i18n: clean.c: match string with git-add--interactive.perl

2016-10-10 Thread Vasco Almeida
Change strings for help to match the ones in git-add--interactive.perl.
The strings now represent one entry to translate each rather then two
entries each different only by an ending newline character.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 builtin/clean.c | 10 +-
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/builtin/clean.c b/builtin/clean.c
index 0371010..d6bc3aa 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -287,11 +287,11 @@ static void pretty_print_menus(struct string_list 
*menu_list)
 static void prompt_help_cmd(int singleton)
 {
clean_print_color(CLEAN_COLOR_HELP);
-   printf_ln(singleton ?
+   printf(singleton ?
  _("Prompt help:\n"
"1  - select a numbered item\n"
"foo- select item based on unique prefix\n"
-   "   - (empty) select nothing") :
+   "   - (empty) select nothing\n") :
  _("Prompt help:\n"
"1  - select a single item\n"
"3-5- select a range of items\n"
@@ -299,7 +299,7 @@ static void prompt_help_cmd(int singleton)
"foo- select item based on unique prefix\n"
"-...   - unselect specified items\n"
"*  - choose all items\n"
-   "   - (empty) finish selecting"));
+   "   - (empty) finish selecting\n"));
clean_print_color(CLEAN_COLOR_RESET);
 }
 
@@ -508,7 +508,7 @@ static int parse_choice(struct menu_stuff *menu_stuff,
if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > 
top ||
(is_single && bottom != top)) {
clean_print_color(CLEAN_COLOR_ERROR);
-   printf_ln(_("Huh (%s)?"), (*ptr)->buf);
+   printf(_("Huh (%s)?\n"), (*ptr)->buf);
clean_print_color(CLEAN_COLOR_RESET);
continue;
}
@@ -774,7 +774,7 @@ static int ask_each_cmd(void)
 static int quit_cmd(void)
 {
string_list_clear(_list, 0);
-   printf_ln(_("Bye."));
+   printf(_("Bye.\n"));
return MENU_RETURN_NO_LOOP;
 }
 
-- 
2.7.4



[PATCH v4 05/14] i18n: add--interactive: mark plural strings

2016-10-10 Thread Vasco Almeida
Mark plural strings for translation.  Unfold each action case in one
entire sentence.

Pass new keyword for xgettext to extract.

Update test to include new subroutine __n() for plural strings handling.

Update documentation to include a description of the new __n() subroutine.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 Makefile  |  3 ++-
 git-add--interactive.perl | 27 ++-
 perl/Git/I18N.pm  |  9 -
 t/t0202/test.pl   | 11 ++-
 4 files changed, 38 insertions(+), 12 deletions(-)

diff --git a/Makefile b/Makefile
index 1aad150..4ef0344 100644
--- a/Makefile
+++ b/Makefile
@@ -2111,7 +2111,8 @@ XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --language=C \
--keyword=_ --keyword=N_ --keyword="Q_:1,2"
 XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell \
--keyword=gettextln --keyword=eval_gettextln
-XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --keyword=__ --language=Perl
+XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --language=Perl \
+   --keyword=__ --keyword="__n:1,2"
 LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
 LOCALIZED_SH = $(SCRIPT_SH)
 LOCALIZED_SH += git-parse-remote.sh
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index d05ac60..cd61783 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -669,12 +669,18 @@ sub status_cmd {
 sub say_n_paths {
my $did = shift @_;
my $cnt = scalar @_;
-   print "$did ";
-   if (1 < $cnt) {
-   print "$cnt paths\n";
-   }
-   else {
-   print "one path\n";
+   if ($did eq 'added') {
+   printf(__n("added %d path\n", "added %d paths\n",
+  $cnt), $cnt);
+   } elsif ($did eq 'updated') {
+   printf(__n("updated %d path\n", "updated %d paths\n",
+  $cnt), $cnt);
+   } elsif ($did eq 'reverted') {
+   printf(__n("reverted %d path\n", "reverted %d paths\n",
+  $cnt), $cnt);
+   } else {
+   printf(__n("touched %d path\n", "touched %d paths\n",
+  $cnt), $cnt);
}
 }
 
@@ -1423,7 +1429,8 @@ sub patch_update_file {
} elsif (0 < $response && $response <= $num) {
$ix = $response - 1;
} else {
-   error_msg "Sorry, only $num hunks 
available.\n";
+   error_msg sprintf(__n("Sorry, only %d 
hunk available.\n",
+ "Sorry, only %d 
hunks available.\n", $num), $num);
}
next;
}
@@ -1518,8 +1525,10 @@ sub patch_update_file {
elsif ($other =~ /s/ && $line =~ /^s/) {
my @split = split_hunk($hunk[$ix]{TEXT}, 
$hunk[$ix]{DISPLAY});
if (1 < @split) {
-   print colored $header_color, "Split 
into ",
-   scalar(@split), " hunks.\n";
+   print colored $header_color, sprintf(
+   __n("Split into %d hunk.\n",
+   "Split into %d hunks.\n",
+   scalar(@split)), 
scalar(@split));
}
splice (@hunk, $ix, 1, @split);
$num = scalar @hunk;
diff --git a/perl/Git/I18N.pm b/perl/Git/I18N.pm
index f889fd6..3f7ac25 100644
--- a/perl/Git/I18N.pm
+++ b/perl/Git/I18N.pm
@@ -13,7 +13,7 @@ BEGIN {
}
 }
 
-our @EXPORT = qw(__);
+our @EXPORT = qw(__ __n);
 our @EXPORT_OK = @EXPORT;
 
 sub __bootstrap_locale_messages {
@@ -44,6 +44,7 @@ BEGIN
eval {
__bootstrap_locale_messages();
*__ = \::Messages::gettext;
+   *__n = \::Messages::ngettext;
1;
} or do {
# Tell test.pl that we couldn't load the gettext library.
@@ -51,6 +52,7 @@ BEGIN
 
# Just a fall-through no-op
*__ = sub ($) { $_[0] };
+   *__n = sub ($$$) { $_[2] == 1 ? $_[0] : $_[1] };
};
 }
 
@@ -70,6 +72,8 @@ Git::I18N - Perl interface to Git's Gettext localizations
 
printf __("The following error occurred: %s\n"), $error;
 
+   printf __n("commited %d file", "commited %d files", $files), $files;
+
 =head1 DESCRIPTION
 
 Git's internal Perl

[PATCH v4 06/14] i18n: add--interactive: mark patch prompt for translation

2016-10-10 Thread Vasco Almeida
Mark prompt message assembled in place for translation, unfolding each
use case for each entry in the %patch_modes hash table.

Previously, this script relied on whether $patch_mode was set to run the
command patch_update_cmd() or show status and loop the main loop. Now,
it uses $cmd to indicate we must run patch_update_cmd() and $patch_mode
is used to tell which flavor of the %patch_modes are we on.  This is
introduced in order to be able to mark and unfold the message prompt
knowing in which context we are.

The tracking of context was done previously by point %patch_mode_flavour
hash table to the correct entry of %patch_modes, focusing only on value
of %patch_modes. Now, we are also interested in the key ('staged',
'stash', 'checkout_head', ...).

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 Makefile  |  2 +-
 git-add--interactive.perl | 54 ---
 perl/Git/I18N.pm  | 10 -
 t/t0202/test.pl   |  5 -
 4 files changed, 60 insertions(+), 11 deletions(-)

diff --git a/Makefile b/Makefile
index 4ef0344..9dc95cb 100644
--- a/Makefile
+++ b/Makefile
@@ -2112,7 +2112,7 @@ XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --language=C \
 XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell \
--keyword=gettextln --keyword=eval_gettextln
 XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --language=Perl \
-   --keyword=__ --keyword="__n:1,2"
+   --keyword=__ --keyword=N__ --keyword="__n:1,2"
 LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
 LOCALIZED_SH = $(SCRIPT_SH)
 LOCALIZED_SH += git-parse-remote.sh
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index cd61783..b7d382b 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -93,6 +93,7 @@ sub colored {
 }
 
 # command line options
+my $cmd;
 my $patch_mode;
 my $patch_mode_revision;
 
@@ -173,7 +174,8 @@ my %patch_modes = (
},
 );
 
-my %patch_mode_flavour = %{$patch_modes{stage}};
+$patch_mode = 'stage';
+my %patch_mode_flavour = %{$patch_modes{$patch_mode}};
 
 sub run_cmd_pipe {
if ($^O eq 'MSWin32') {
@@ -1311,6 +1313,44 @@ sub display_hunks {
return $i;
 }
 
+my %patch_update_prompt_modes = (
+   stage => {
+   mode => N__("Stage mode change [y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Stage deletion [y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Stage this hunk [y,n,q,a,d,/%s,?]? "),
+   },
+   stash => {
+   mode => N__("Stash mode change [y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Stash deletion [y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Stash this hunk [y,n,q,a,d,/%s,?]? "),
+   },
+   reset_head => {
+   mode => N__("Unstage mode change [y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Unstage deletion [y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Unstage this hunk [y,n,q,a,d,/%s,?]? "),
+   },
+   reset_nothead => {
+   mode => N__("Apply mode change to index [y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Apply deletion to index [y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Apply this hunk to index [y,n,q,a,d,/%s,?]? "),
+   },
+   checkout_index => {
+   mode => N__("Discard mode change from worktree 
[y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Discard deletion from worktree 
[y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Discard this hunk from worktree [y,n,q,a,d,/%s,?]? 
"),
+   },
+   checkout_head => {
+   mode => N__("Discard mode change from index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Discard deletion from index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Discard this hunk from index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   },
+   checkout_nothead => {
+   mode => N__("Apply mode change to index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   deletion => N__("Apply deletion to index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   hunk => N__("Apply this hunk to index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   },
+);
+
 sub patch_update_file {
my $quit = 0;
my ($ix, $num);
@@ -1383,12 +1423,9 @@ sub patch_update_file {
for (@{$hunk[$ix]{DISPLAY}}) {
print;
}
-   print colored $prompt_color, $patch_mode_flavour{VERB},
- ($hunk[$ix]{TYPE} eq 'mode' ? ' mode change' :
-  $hunk[$ix]{TYPE} eq 'deletion' ? ' deletion' :
-  ' this hunk'),
- 

[PATCH v4 07/14] i18n: add--interactive: i18n of help_patch_cmd

2016-10-10 Thread Vasco Almeida
Mark help message of help_patch_cmd for translation.  The message must
be unfolded to be free of variables so we can have high quality
translations.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 54 ---
 1 file changed, 46 insertions(+), 8 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index b7d382b..045b847 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -1189,15 +1189,53 @@ sub edit_hunk_loop {
}
 }
 
+my %help_patch_modes = (
+   stage => N__(
+"y - stage this hunk
+n - do not stage this hunk
+q - quit; do not stage this hunk or any of the remaining ones
+a - stage this hunk and all later hunks in the file
+d - do not stage this hunk or any of the later hunks in the file"),
+   stash => N__(
+"y - stash this hunk
+n - do not stash this hunk
+q - quit; do not stash this hunk or any of the remaining ones
+a - stash this hunk and all later hunks in the file
+d - do not stash this hunk or any of the later hunks in the file"),
+   reset_head => N__(
+"y - unstage this hunk
+n - do not unstage this hunk
+q - quit; do not unstage this hunk or any of the remaining ones
+a - unstage this hunk and all later hunks in the file
+d - do not unstage this hunk or any of the later hunks in the file"),
+   reset_nothead => N__(
+"y - apply this hunk to index
+n - do not apply this hunk to index
+q - quit; do not apply this hunk or any of the remaining ones
+a - apply this hunk and all later hunks in the file
+d - do not apply this hunk or any of the later hunks in the file"),
+   checkout_index => N__(
+"y - discard this hunk from worktree
+n - do not discard this hunk from worktree
+q - quit; do not discard this hunk or any of the remaining ones
+a - discard this hunk and all later hunks in the file
+d - do not discard this hunk or any of the later hunks in the file"),
+   checkout_head => N__(
+"y - discard this hunk from index and worktree
+n - do not discard this hunk from index and worktree
+q - quit; do not discard this hunk or any of the remaining ones
+a - discard this hunk and all later hunks in the file
+d - do not discard this hunk or any of the later hunks in the file"),
+   checkout_nothead => N__(
+"y - apply this hunk to index and worktree
+n - do not apply this hunk to index and worktree
+q - quit; do not apply this hunk or any of the remaining ones
+a - apply this hunk and all later hunks in the file
+d - do not apply this hunk or any of the later hunks in the file"),
+);
+
 sub help_patch_cmd {
-   my $verb = lc $patch_mode_flavour{VERB};
-   my $target = $patch_mode_flavour{TARGET};
-   print colored $help_color, <

[PATCH v4 02/14] i18n: add--interactive: mark simple here-documents for translation

2016-10-10 Thread Vasco Almeida
Mark messages in here-document without interpolation for translation.

The here-document delimiter \EOF, which is the same as 'EOF', indicate
that the text is to be treated literally without interpolation of its
content.  Unfortunately xgettext is not able to extract here documents
with delimiter \EOF but it is with delimiter enclosed in single quotes.
Then change \EOF to 'EOF', although in this case does not make
difference what variation of here-document to use since there is nothing
to interpolate.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 8 +---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index cf216ec..5800010 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -639,7 +639,7 @@ sub list_and_choose {
 }
 
 sub singleton_prompt_help_cmd {
-   print colored $help_color, <<\EOF ;
+   print colored $help_color, __ <<'EOF' ;
 Prompt help:
 1  - select a numbered item
 foo- select item based on unique prefix
@@ -648,7 +648,7 @@ EOF
 }
 
 sub prompt_help_cmd {
-   print colored $help_color, <<\EOF ;
+   print colored $help_color, __ <<'EOF' ;
 Prompt help:
 1  - select a single item
 3-5- select a range of items
@@ -1584,7 +1584,9 @@ sub quit_cmd {
 }
 
 sub help_cmd {
-   print colored $help_color, <<\EOF ;
+# TRANSLATORS: please do not translate the command names
+# 'status', 'update', 'revert', etc.
+   print colored $help_color, __ <<'EOF' ;
 status- show paths with changes
 update- add working tree state to the staged set of changes
 revert- revert staged set of changes back to the HEAD version
-- 
2.7.4



[PATCH v4 03/14] i18n: add--interactive: mark strings with interpolation for translation

2016-10-10 Thread Vasco Almeida
Since at this point Git::I18N.perl lacks support for Perl i18n
placeholder substitution, use of sprintf following die or error_msg is
necessary for placeholder substitution take place.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 25 +
 1 file changed, 13 insertions(+), 12 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 5800010..d05ac60 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -615,12 +615,12 @@ sub list_and_choose {
else {
$bottom = $top = find_unique($choice, @stuff);
if (!defined $bottom) {
-   error_msg "Huh ($choice)?\n";
+   error_msg sprintf(__("Huh (%s)?\n"), 
$choice);
next TOPLOOP;
}
}
if ($opts->{SINGLETON} && $bottom != $top) {
-   error_msg "Huh ($choice)?\n";
+   error_msg sprintf(__("Huh (%s)?\n"), $choice);
next TOPLOOP;
}
for ($i = $bottom-1; $i <= $top-1; $i++) {
@@ -717,7 +717,7 @@ sub revert_cmd {
$_->{INDEX_ADDDEL} eq 'create') {
system(qw(git update-index 
--force-remove --),
   $_->{VALUE});
-   print "note: $_->{VALUE} is untracked 
now.\n";
+   printf(__("note: %s is untracked 
now.\n"), $_->{VALUE});
}
}
}
@@ -1056,7 +1056,7 @@ sub edit_hunk_manually {
my $hunkfile = $repo->repo_path . "/addp-hunk-edit.diff";
my $fh;
open $fh, '>', $hunkfile
-   or die "failed to open hunk edit file for writing: " . $!;
+   or die sprintf(__("failed to open hunk edit file for writing: 
%s"), $!);
print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n";
print $fh @$oldtext;
my $participle = $patch_mode_flavour{PARTICIPLE};
@@ -1083,7 +1083,7 @@ EOF
}
 
open $fh, '<', $hunkfile
-   or die "failed to open hunk edit file for reading: " . $!;
+   or die sprintf(__("failed to open hunk edit file for reading: 
%s"), $!);
my @newtext = grep { !/^#/ } <$fh>;
close $fh;
unlink $hunkfile;
@@ -1236,7 +1236,7 @@ sub apply_patch_for_checkout_commit {
 
 sub patch_update_cmd {
my @all_mods = list_modified($patch_mode_flavour{FILTER});
-   error_msg "ignoring unmerged: $_->{VALUE}\n"
+   error_msg sprintf(__("ignoring unmerged: %s\n"), $_->{VALUE})
for grep { $_->{UNMERGED} } @all_mods;
@all_mods = grep { !$_->{UNMERGED} } @all_mods;
 
@@ -1418,7 +1418,8 @@ sub patch_update_file {
chomp $response;
}
if ($response !~ /^\s*\d+\s*$/) {
-   error_msg "Invalid number: 
'$response'\n";
+   error_msg sprintf(__("Invalid number: 
'%s'\n"),
+$response);
} elsif (0 < $response && $response <= $num) {
$ix = $response - 1;
} else {
@@ -1460,7 +1461,7 @@ sub patch_update_file {
if ($@) {
my ($err,$exp) = ($@, $1);
$err =~ s/ at .*git-add--interactive 
line \d+,  line \d+.*$//;
-   error_msg "Malformed search regexp 
$exp: $err\n";
+   error_msg sprintf(__("Malformed search 
regexp %s: %s\n"), $exp, $err);
next;
}
my $iy = $ix;
@@ -1625,18 +1626,18 @@ sub process_args {
$patch_mode = $1;
$arg = shift @ARGV or die __("missing --");
} else {
-   die "unknown --patch mode: $1";
+   die sprintf(__("unknown --patch mode: %s"), $1);
}
} else {
   

[PATCH v4 01/14] i18n: add--interactive: mark strings for translation

2016-10-10 Thread Vasco Almeida
Mark simple strings (without interpolation) for translation.

Brackets around first parameter of ternary operator is necessary because
otherwise xgettext fails to extract strings marked for translation from
the rest of the file.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 76 ++-
 1 file changed, 42 insertions(+), 34 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index ee3d812..cf216ec 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -4,6 +4,7 @@ use 5.008;
 use strict;
 use warnings;
 use Git;
+use Git::I18N;
 
 binmode(STDOUT, ":raw");
 
@@ -253,8 +254,9 @@ sub list_untracked {
run_cmd_pipe(qw(git ls-files --others --exclude-standard --), @ARGV);
 }
 
-my $status_fmt = '%12s %12s %s';
-my $status_head = sprintf($status_fmt, 'staged', 'unstaged', 'path');
+# TRANSLATORS: you can adjust this to align "git add -i" status menu
+my $status_fmt = __('%12s %12s %s');
+my $status_head = sprintf($status_fmt, __('staged'), __('unstaged'), 
__('path'));
 
 {
my $initial;
@@ -680,7 +682,7 @@ sub update_cmd {
my @mods = list_modified('file-only');
return if (!@mods);
 
-   my @update = list_and_choose({ PROMPT => 'Update',
+   my @update = list_and_choose({ PROMPT => __('Update'),
   HEADER => $status_head, },
 @mods);
if (@update) {
@@ -692,7 +694,7 @@ sub update_cmd {
 }
 
 sub revert_cmd {
-   my @update = list_and_choose({ PROMPT => 'Revert',
+   my @update = list_and_choose({ PROMPT => __('Revert'),
   HEADER => $status_head, },
 list_modified());
if (@update) {
@@ -726,13 +728,13 @@ sub revert_cmd {
 }
 
 sub add_untracked_cmd {
-   my @add = list_and_choose({ PROMPT => 'Add untracked' },
+   my @add = list_and_choose({ PROMPT => __('Add untracked') },
  list_untracked());
if (@add) {
system(qw(git update-index --add --), @add);
say_n_paths('added', @add);
} else {
-   print "No untracked files.\n";
+   print __("No untracked files.\n");
}
print "\n";
 }
@@ -1166,8 +1168,14 @@ sub edit_hunk_loop {
}
else {
prompt_yesno(
-   'Your edited hunk does not apply. Edit again '
-   . '(saying "no" discards!) [y/n]? '
+   # TRANSLATORS: do not translate [y/n]
+   # The program will only accept that input
+   # at this point.
+   # Consider translating (saying "no" discards!) 
as
+   # (saying "n" for "no" discards!) if the 
translation
+   # of the word "no" does not start with n.
+   __('Your edited hunk does not apply. Edit again 
'
+  . '(saying "no" discards!) [y/n]? ')
) or return undef;
}
}
@@ -1213,11 +1221,11 @@ sub apply_patch_for_checkout_commit {
run_git_apply 'apply '.$reverse, @_;
return 1;
} elsif (!$applies_index) {
-   print colored $error_color, "The selected hunks do not apply to 
the index!\n";
-   if (prompt_yesno "Apply them to the worktree anyway? ") {
+   print colored $error_color, __("The selected hunks do not apply 
to the index!\n");
+   if (prompt_yesno __("Apply them to the worktree anyway? ")) {
return run_git_apply 'apply '.$reverse, @_;
} else {
-   print colored $error_color, "Nothing was applied.\n";
+   print colored $error_color, __("Nothing was 
applied.\n");
return 0;
}
} else {
@@ -1237,9 +1245,9 @@ sub patch_update_cmd {
 
if (!@mods) {
if (@all_mods) {
-   print STDERR "Only binary files changed.\n";
+   print STDERR __("Only binary files changed.\n");
} else {
-   print STDERR "No changes.\n";
+   print STDERR __("No changes.\n");
}
return 0;
}
@@ -1247,7 +1255,7 @@ sub patch_update_cmd {
@them = @mods;
}
else {
-   @them = list_and_choose({ PROMPT =>

[PATCH v4 00/14] Mark strings in Perl scripts for translation

2016-10-10 Thread Vasco Almeida
Mark messages in some perl scripts for translation.

Fix minor stuff and follow Jakub Narębski's suggestion to use N__() instead of
__() in the hash tables.

Interdiff included below.

Vasco Almeida (14):
  i18n: add--interactive: mark strings for translation
  i18n: add--interactive: mark simple here-documents for translation
  i18n: add--interactive: mark strings with interpolation for
translation
  i18n: clean.c: match string with git-add--interactive.perl
  i18n: add--interactive: mark plural strings
  i18n: add--interactive: mark patch prompt for translation
  i18n: add--interactive: i18n of help_patch_cmd
  i18n: add--interactive: mark edit_hunk_manually message for
translation
  i18n: add--interactive: remove %patch_modes entries
  i18n: add--interactive: mark status words for translation
  i18n: send-email: mark strings for translation
  i18n: send-email: mark warnings and errors for translation
  i18n: send-email: mark string with interpolation for translation
  i18n: difftool: mark warnings for translation

 Makefile  |   3 +-
 builtin/clean.c   |  10 +-
 git-add--interactive.perl | 322 ++
 git-difftool.perl |  22 ++--
 git-send-email.perl   | 176 +
 perl/Git/I18N.pm  |  17 ++-
 t/t0202/test.pl   |  14 +-
 7 files changed, 353 insertions(+), 211 deletions(-)


diff --git a/Makefile b/Makefile
index 4ef0344..9dc95cb 100644
--- a/Makefile
+++ b/Makefile
@@ -2112,7 +2112,7 @@ XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --language=C \
 XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell \
--keyword=gettextln --keyword=eval_gettextln
 XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --language=Perl \
-   --keyword=__ --keyword="__n:1,2"
+   --keyword=__ --keyword=N__ --keyword="__n:1,2"
 LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
 LOCALIZED_SH = $(SCRIPT_SH)
 LOCALIZED_SH += git-parse-remote.sh
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 0b4a27c..4754104 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -601,7 +601,7 @@ sub list_and_choose {
}
}
if ($opts->{SINGLETON} && $bottom != $top) {
-   error_msg sprintf(__("Huh (%s)?"), $choice);
+   error_msg sprintf(__("Huh (%s)?\n"), $choice);
next TOPLOOP;
}
for ($i = $bottom-1; $i <= $top-1; $i++) {
@@ -704,7 +704,7 @@ sub revert_cmd {
$_->{INDEX_ADDDEL} eq 'create') {
system(qw(git update-index 
--force-remove --),
   $_->{VALUE});
-   print "note: $_->{VALUE} is untracked 
now.\n";
+   printf(__("note: %s is untracked 
now.\n"), $_->{VALUE});
}
}
}
@@ -1038,25 +1038,25 @@ sub color_diff {
 }
 
 my %edit_hunk_manually_modes = (
-   stage => __(
+   stage => N__(
 "# If the patch applies cleanly, the edited hunk will immediately be
 # marked for staging."),
-   stash => __(
+   stash => N__(
 "# If the patch applies cleanly, the edited hunk will immediately be
 # marked for stashing."),
-   reset_head => __(
+   reset_head => N__(
 "# If the patch applies cleanly, the edited hunk will immediately be
 # marked for unstaging."),
-   reset_nothead => __(
+   reset_nothead => N__(
 "# If the patch applies cleanly, the edited hunk will immediately be
 # marked for applying."),
-   checkout_index => __(
+   checkout_index => N__(
 "# If the patch applies cleanly, the edited hunk will immediately be
 # marked for discarding"),
-   checkout_head => __(
+   checkout_head => N__(
 "# If the patch applies cleanly, the edited hunk will immediately be
 # marked for discarding."),
-   checkout_nothead => __(
+   checkout_nothead => N__(
 "# If the patch applies cleanly, the edited hunk will immediately be
 # marked for applying."),
 );
@@ -1078,7 +1078,7 @@ sub edit_hunk_manually {
 # To remove '%s' lines, delete them.
 # Lines starting with # will be removed.
 #\n"), $remove_minus, $remove_plus),
-$edit_hunk_manually_modes{$patch_mode}, __(
+__($edit_hunk_manually_modes{$patch_mode}), __(
 # TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
 " If it does not apply cleanly, you will be given
 # an opportunity to edit again. If all lines of the hunk are removed,
@@ -1192,43 +1192,43 @@ sub edit_hunk_loop {
 }
 
 my %help_patc

[PATCH v3 13/14] i18n: send-email: mark string with interpolation for translation

2016-10-05 Thread Vasco Almeida
Mark warnings, errors and other messages that are interpolated for
translation.

We call sprintf() before calling die() and in few other circumstances in
order to replace the values on the placeholders.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---

I changed (y|N) to [y|N] around line 1750 to match with the others
questions style.

 git-send-email.perl | 90 -
 1 file changed, 48 insertions(+), 42 deletions(-)

diff --git a/git-send-email.perl b/git-send-email.perl
index 982c6c0..5c01425 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -279,10 +279,13 @@ sub signal_handler {
# tmp files from --compose
if (defined $compose_filename) {
if (-e $compose_filename) {
-   print "'$compose_filename' contains an intermediate 
version of the email you were composing.\n";
+   printf __("'%s' contains an intermediate version ".
+ "of the email you were composing.\n"),
+ $compose_filename;
}
if (-e ($compose_filename . ".final")) {
-   print "'$compose_filename.final' contains the composed 
email.\n"
+   printf __("'%s.final' contains the composed email.\n"),
+ $compose_filename;
}
}
 
@@ -431,7 +434,7 @@ $smtp_encryption = '' unless (defined $smtp_encryption);
 my(%suppress_cc);
 if (@suppress_cc) {
foreach my $entry (@suppress_cc) {
-   die "Unknown --suppress-cc field: '$entry'\n"
+   die sprintf(__("Unknown --suppress-cc field: '%s'\n"), $entry)
unless $entry =~ 
/^(?:all|cccmd|cc|author|self|sob|body|bodycc)$/;
$suppress_cc{$entry} = 1;
}
@@ -460,7 +463,7 @@ my $confirm_unconfigured = !defined $confirm;
 if ($confirm_unconfigured) {
$confirm = scalar %suppress_cc ? 'compose' : 'auto';
 };
-die "Unknown --confirm setting: '$confirm'\n"
+die sprintf(__("Unknown --confirm setting: '%s'\n"), $confirm)
unless $confirm =~ /^(?:auto|cc|compose|always|never)/;
 
 # Debugging, print out the suppressions.
@@ -492,16 +495,16 @@ my %aliases;
 sub parse_sendmail_alias {
local $_ = shift;
if (/"/) {
-   print STDERR "warning: sendmail alias with quotes is not 
supported: $_\n";
+   printf STDERR __("warning: sendmail alias with quotes is not 
supported: %s\n"), $_;
} elsif (/:include:/) {
-   print STDERR "warning: `:include:` not supported: $_\n";
+   printf STDERR __("warning: `:include:` not supported: %s\n"), 
$_;
} elsif (/[\/|]/) {
-   print STDERR "warning: `/file` or `|pipe` redirection not 
supported: $_\n";
+   printf STDERR __("warning: `/file` or `|pipe` redirection not 
supported: %s\n"), $_;
} elsif (/^(\S+?)\s*:\s*(.+)$/) {
my ($alias, $addr) = ($1, $2);
$aliases{$alias} = [ split_addrs($addr) ];
} else {
-   print STDERR "warning: sendmail line is not recognized: $_\n";
+   printf STDERR __("warning: sendmail line is not recognized: 
%s\n"), $_;
}
 }
 
@@ -582,13 +585,12 @@ sub is_format_patch_arg {
if (defined($format_patch)) {
return $format_patch;
}
-   die(<<EOF);
-File '$f' exists but it could also be the range of commits
+   die sprintf(__(
+"File '%s' exists but it could also be the range of commits
 to produce patches for.  Please disambiguate by...
 
-* Saying "./$f" if you mean a file; or
-* Giving --format-patch option if you mean a range.
-EOF
+* Saying \"./%s\" if you mean a file; or
+* Giving --format-patch option if you mean a range."), $f, $f);
} catch Git::Error::Command with {
# Not a valid revision.  Treat it as a filename.
return 0;
@@ -604,7 +606,7 @@ while (defined(my $f = shift @ARGV)) {
@ARGV = ();
} elsif (-d $f and !is_format_patch_arg($f)) {
opendir my $dh, $f
-   or die "Failed to opendir $f: $!";
+   or die sprintf(__("Failed to opendir %s: %s"), $f, $!);
 
push @files, grep { -f $_ } map { catfile($f, $_) }
sort readdir $dh;
@@ -628,7 +630,8 @@ if ($validate) {
foreach my $f (@files) {
unless (-p $f) {
my $error = validate_patch($f);
-   $error and die "fatal: $f: $error\nwarning: no patches 

[PATCH v3 09/14] i18n: add--interactive: remove %patch_modes entries

2016-10-05 Thread Vasco Almeida
Remove unnecessary entries from %patch_modes. After the i18n conversion,
these entries are not used anymore.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 21 -
 1 file changed, 21 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 5356d5a..e81939f 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -106,9 +106,6 @@ my %patch_modes = (
DIFF => 'diff-files -p',
APPLY => sub { apply_patch 'apply --cached', @_; },
APPLY_CHECK => 'apply --cached',
-   VERB => 'Stage',
-   TARGET => '',
-   PARTICIPLE => 'staging',
FILTER => 'file-only',
IS_REVERSE => 0,
},
@@ -116,9 +113,6 @@ my %patch_modes = (
DIFF => 'diff-index -p HEAD',
APPLY => sub { apply_patch 'apply --cached', @_; },
APPLY_CHECK => 'apply --cached',
-   VERB => 'Stash',
-   TARGET => '',
-   PARTICIPLE => 'stashing',
FILTER => undef,
IS_REVERSE => 0,
},
@@ -126,9 +120,6 @@ my %patch_modes = (
DIFF => 'diff-index -p --cached',
APPLY => sub { apply_patch 'apply -R --cached', @_; },
APPLY_CHECK => 'apply -R --cached',
-   VERB => 'Unstage',
-   TARGET => '',
-   PARTICIPLE => 'unstaging',
FILTER => 'index-only',
IS_REVERSE => 1,
},
@@ -136,9 +127,6 @@ my %patch_modes = (
DIFF => 'diff-index -R -p --cached',
APPLY => sub { apply_patch 'apply --cached', @_; },
APPLY_CHECK => 'apply --cached',
-   VERB => 'Apply',
-   TARGET => ' to index',
-   PARTICIPLE => 'applying',
FILTER => 'index-only',
IS_REVERSE => 0,
},
@@ -146,9 +134,6 @@ my %patch_modes = (
DIFF => 'diff-files -p',
APPLY => sub { apply_patch 'apply -R', @_; },
APPLY_CHECK => 'apply -R',
-   VERB => 'Discard',
-   TARGET => ' from worktree',
-   PARTICIPLE => 'discarding',
FILTER => 'file-only',
IS_REVERSE => 1,
},
@@ -156,9 +141,6 @@ my %patch_modes = (
DIFF => 'diff-index -p',
APPLY => sub { apply_patch_for_checkout_commit '-R', @_ },
APPLY_CHECK => 'apply -R',
-   VERB => 'Discard',
-   TARGET => ' from index and worktree',
-   PARTICIPLE => 'discarding',
FILTER => undef,
IS_REVERSE => 1,
},
@@ -166,9 +148,6 @@ my %patch_modes = (
DIFF => 'diff-index -R -p',
APPLY => sub { apply_patch_for_checkout_commit '', @_ },
APPLY_CHECK => 'apply',
-   VERB => 'Apply',
-   TARGET => ' to index and worktree',
-   PARTICIPLE => 'applying',
FILTER => undef,
IS_REVERSE => 0,
},
-- 
2.7.4



[PATCH v3 12/14] i18n: send-email: mark warnings and errors for translation

2016-10-05 Thread Vasco Almeida
Mark warnings, errors and other messages for translation.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-send-email.perl | 34 +-
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/git-send-email.perl b/git-send-email.perl
index 78eb59b..982c6c0 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -118,20 +118,20 @@ sub format_2822_time {
my $localmin = $localtm[1] + $localtm[2] * 60;
my $gmtmin = $gmttm[1] + $gmttm[2] * 60;
if ($localtm[0] != $gmttm[0]) {
-   die "local zone differs from GMT by a non-minute interval\n";
+   die __("local zone differs from GMT by a non-minute 
interval\n");
}
if ((($gmttm[6] + 1) % 7) == $localtm[6]) {
$localmin += 1440;
} elsif ((($gmttm[6] - 1) % 7) == $localtm[6]) {
$localmin -= 1440;
} elsif ($gmttm[6] != $localtm[6]) {
-   die "local time offset greater than or equal to 24 hours\n";
+   die __("local time offset greater than or equal to 24 hours\n");
}
my $offset = $localmin - $gmtmin;
my $offhour = $offset / 60;
my $offmin = abs($offset % 60);
if (abs($offhour) >= 24) {
-   die ("local time offset greater than or equal to 24 hours\n");
+   die __("local time offset greater than or equal to 24 hours\n");
}
 
return sprintf("%s, %2d %s %d %02d:%02d:%02d %s%02d%02d",
@@ -199,13 +199,13 @@ sub do_edit {
map {
system('sh', '-c', $editor.' "$@"', $editor, $_);
if (($? & 127) || ($? >> 8)) {
-   die("the editor exited uncleanly, aborting 
everything");
+   die(__("the editor exited uncleanly, aborting 
everything"));
}
} @_;
} else {
system('sh', '-c', $editor.' "$@"', $editor, @_);
if (($? & 127) || ($? >> 8)) {
-   die("the editor exited uncleanly, aborting everything");
+   die(__("the editor exited uncleanly, aborting 
everything"));
}
}
 }
@@ -299,7 +299,7 @@ my $help;
 my $rc = GetOptions("h" => \$help,
 "dump-aliases" => \$dump_aliases);
 usage() unless $rc;
-die "--dump-aliases incompatible with other options\n"
+die __("--dump-aliases incompatible with other options\n")
 if !$help and $dump_aliases and @ARGV;
 $rc = GetOptions(
"sender|from=s" => \$sender,
@@ -362,7 +362,7 @@ unless ($rc) {
 usage();
 }
 
-die "Cannot run git format-patch from outside a repository\n"
+die __("Cannot run git format-patch from outside a repository\n")
if $format_patch and not $repo;
 
 # Now, let's fill any that aren't set in with defaults:
@@ -617,7 +617,7 @@ while (defined(my $f = shift @ARGV)) {
 }
 
 if (@rev_list_opts) {
-   die "Cannot run git format-patch from outside a repository\n"
+   die __("Cannot run git format-patch from outside a repository\n")
unless $repo;
push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 
1), @rev_list_opts);
 }
@@ -638,7 +638,7 @@ if (@files) {
print $_,"\n" for (@files);
}
 } else {
-   print STDERR "\nNo patch files specified!\n\n";
+   print STDERR __("\nNo patch files specified!\n\n");
usage();
 }
 
@@ -730,7 +730,7 @@ EOT
$sender = $1;
next;
} elsif (/^(?:To|Cc|Bcc):/i) {
-   print "To/Cc/Bcc fields are not interpreted yet, they 
have been ignored\n";
+   print __("To/Cc/Bcc fields are not interpreted yet, 
they have been ignored\n");
next;
}
print $c2 $_;
@@ -739,7 +739,7 @@ EOT
close $c2;
 
if ($summary_empty) {
-   print "Summary email is empty, skipping it\n";
+   print __("Summary email is empty, skipping it\n");
$compose = -1;
}
 } elsif ($annotate) {
@@ -1314,7 +1314,7 @@ Message-Id: $message_id
$_ = ask(__("Send this email? ([y]es|[n]o|[q]uit|[a]ll): "),
 valid_re => qr/^(?:yes|y|no|n|quit|q|all|a)/i,
 default => $ask_default);
-   die "Send this email reply required" unless defined $_;
+   die __("Send this email reply required") unless defined $_

[PATCH v3 10/14] i18n: add--interactive: mark status words for translation

2016-10-05 Thread Vasco Almeida
Mark words 'nothing', 'unchanged' and 'binary' used to display what has
been staged or not, in "git add -i" status command.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 12 ++--
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index e81939f..0b4a27c 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -295,7 +295,7 @@ sub list_modified {
my ($change, $bin);
$file = unquote_path($file);
if ($add eq '-' && $del eq '-') {
-   $change = 'binary';
+   $change = __('binary');
$bin = 1;
}
else {
@@ -304,7 +304,7 @@ sub list_modified {
$data{$file} = {
INDEX => $change,
BINARY => $bin,
-   FILE => 'nothing',
+   FILE => __('nothing'),
}
}
elsif (($adddel, $file) =
@@ -320,7 +320,7 @@ sub list_modified {
$file = unquote_path($file);
my ($change, $bin);
if ($add eq '-' && $del eq '-') {
-   $change = 'binary';
+   $change = __('binary');
$bin = 1;
}
else {
@@ -340,7 +340,7 @@ sub list_modified {
$file = unquote_path($2);
if (!exists $data{$file}) {
$data{$file} = +{
-   INDEX => 'unchanged',
+   INDEX => __('unchanged'),
BINARY => 0,
};
}
@@ -355,10 +355,10 @@ sub list_modified {
 
if ($only) {
if ($only eq 'index-only') {
-   next if ($it->{INDEX} eq 'unchanged');
+   next if ($it->{INDEX} eq __('unchanged'));
}
if ($only eq 'file-only') {
-   next if ($it->{FILE} eq 'nothing');
+   next if ($it->{FILE} eq __('nothing'));
}
}
push @return, +{
-- 
2.7.4



[PATCH v3 11/14] i18n: send-email: mark strings for translation

2016-10-05 Thread Vasco Almeida
Mark strings often displayed to the user for translation.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-send-email.perl | 52 ++--
 1 file changed, 30 insertions(+), 22 deletions(-)

diff --git a/git-send-email.perl b/git-send-email.perl
index da81be4..78eb59b 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -28,6 +28,7 @@ use File::Temp qw/ tempdir tempfile /;
 use File::Spec::Functions qw(catfile);
 use Error qw(:try);
 use Git;
+use Git::I18N;
 
 Getopt::Long::Configure qw/ pass_through /;
 
@@ -797,12 +798,12 @@ foreach my $f (@files) {
 }
 
 if (!defined $auto_8bit_encoding && scalar %broken_encoding) {
-   print "The following files are 8bit, but do not declare " .
-   "a Content-Transfer-Encoding.\n";
+   print __("The following files are 8bit, but do not declare " .
+"a Content-Transfer-Encoding.\n");
foreach my $f (sort keys %broken_encoding) {
print "$f\n";
}
-   $auto_8bit_encoding = ask("Which 8bit encoding should I declare 
[UTF-8]? ",
+   $auto_8bit_encoding = ask(__("Which 8bit encoding should I declare 
[UTF-8]? "),
  valid_re => qr/.{4}/, confirm_only => 1,
  default => "UTF-8");
 }
@@ -829,7 +830,7 @@ if (defined $sender) {
 # But it's a no-op to run sanitize_address on an already sanitized address.
 $sender = sanitize_address($sender);
 
-my $to_whom = "To whom should the emails be sent (if anyone)?";
+my $to_whom = __("To whom should the emails be sent (if anyone)?");
 my $prompting = 0;
 if (!@initial_to && !defined $to_cmd) {
my $to = ask("$to_whom ",
@@ -859,7 +860,7 @@ sub expand_one_alias {
 
 if ($thread && !defined $initial_reply_to && $prompting) {
$initial_reply_to = ask(
-   "Message-ID to be used as In-Reply-To for the first email (if 
any)? ",
+   __("Message-ID to be used as In-Reply-To for the first email 
(if any)? "),
default => "",
valid_re => qr/\@.*\./, confirm_only => 1);
 }
@@ -918,7 +919,10 @@ sub validate_address {
my $address = shift;
while (!extract_valid_address($address)) {
print STDERR "error: unable to extract a valid address from: 
$address\n";
-   $_ = ask("What to do with this address? ([q]uit|[d]rop|[e]dit): 
",
+   # TRANSLATORS: Make sure to include [q] [d] [e] in your
+   # translation. The program will only accept English input
+   # at this point.
+   $_ = ask(__("What to do with this address? 
([q]uit|[d]rop|[e]dit): "),
valid_re => qr/^(?:quit|q|drop|d|edit|e)/i,
default => 'q');
if (/^d/i) {
@@ -1293,17 +1297,21 @@ Message-Id: $message_id
if ($needs_confirm eq "inform") {
$confirm_unconfigured = 0; # squelch this message for 
the rest of this run
$ask_default = "y"; # assume yes on EOF since user 
hasn't explicitly asked for confirmation
-   print "The Cc list above has been expanded by 
additional\n";
-   print "addresses found in the patch commit message. 
By default\n";
-   print "send-email prompts before sending whenever 
this occurs.\n";
-   print "This behavior is controlled by the 
sendemail.confirm\n";
-   print "configuration setting.\n";
-   print "\n";
-   print "For additional information, run 'git 
send-email --help'.\n";
-   print "To retain the current behavior, but squelch 
this message,\n";
-   print "run 'git config --global sendemail.confirm 
auto'.\n\n";
+   print __(
+"The Cc list above has been expanded by additional
+addresses found in the patch commit message. By default
+send-email prompts before sending whenever this occurs.
+This behavior is controlled by the sendemail.confirm
+configuration setting.
+
+For additional information, run 'git send-email --help'.
+To retain the current behavior, but squelch this message,
+run 'git config --global sendemail.confirm auto'."), "\n\n";
}
-   $_ = ask("Send this email? ([y]es|[n]o|[q]uit|[a]ll): ",
+   # TRANSLATORS: Make sure to include [y] [n] [q] [a] in your
+   # translation. The prog

[PATCH v3 14/14] i18n: difftool: mark warnings for translation

2016-10-05 Thread Vasco Almeida
Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-difftool.perl | 22 --
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/git-difftool.perl b/git-difftool.perl
index a5790d0..8d3632e 100755
--- a/git-difftool.perl
+++ b/git-difftool.perl
@@ -22,6 +22,7 @@ use File::Path qw(mkpath rmtree);
 use File::Temp qw(tempdir);
 use Getopt::Long qw(:config pass_through);
 use Git;
+use Git::I18N;
 
 sub usage
 {
@@ -122,7 +123,7 @@ sub setup_dir_diff
my $i = 0;
while ($i < $#rawdiff) {
if ($rawdiff[$i] =~ /^::/) {
-   warn << 'EOF';
+   warn __ <<'EOF';
 Combined diff formats ('-c' and '--cc') are not supported in
 directory diff mode ('-d' and '--dir-diff').
 EOF
@@ -338,7 +339,7 @@ sub main
if (length($opts{difftool_cmd}) > 0) {
$ENV{GIT_DIFF_TOOL} = $opts{difftool_cmd};
} else {
-   print "No  given for --tool=\n";
+   print __("No  given for --tool=\n");
usage(1);
}
}
@@ -346,7 +347,7 @@ sub main
if (length($opts{extcmd}) > 0) {
$ENV{GIT_DIFFTOOL_EXTCMD} = $opts{extcmd};
} else {
-   print "No  given for --extcmd=\n";
+   print __("No  given for --extcmd=\n");
usage(1);
}
}
@@ -419,11 +420,11 @@ sub dir_diff
}
 
if (exists $wt_modified{$file} and exists $tmp_modified{$file}) 
{
-   my $errmsg = "warning: Both files modified: ";
-   $errmsg .= "'$workdir/$file' and '$b/$file'.\n";
-   $errmsg .= "warning: Working tree file has been 
left.\n";
-   $errmsg .= "warning:\n";
-   warn $errmsg;
+   warn sprintf(__(
+   "warning: Both files modified:\n" .
+   "'%s/%s' and '%s/%s'.\n" .
+   "warning: Working tree file has been left.\n" .
+   "warning:\n"), $workdir, $file, $b, $file);
$error = 1;
} elsif (exists $tmp_modified{$file}) {
my $mode = stat("$b/$file")->mode;
@@ -435,8 +436,9 @@ sub dir_diff
}
}
if ($error) {
-   warn "warning: Temporary files exist in '$tmpdir'.\n";
-   warn "warning: You may want to cleanup or recover these.\n";
+   warn sprintf(__(
+   "warning: Temporary files exist in '%s'.\n" .
+   "warning: You may want to cleanup or recover 
these.\n"), $tmpdir);
exit(1);
} else {
exit_cleanup($tmpdir, $rc);
-- 
2.7.4



[PATCH v3 03/14] i18n: add--interactive: mark strings with interpolation for translation

2016-10-05 Thread Vasco Almeida
Since at this point Git::I18N.perl lacks support for Perl i18n
placeholder substitution, use of sprintf following die or error_msg is
necessary for placeholder substitution take place.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 23 ---
 1 file changed, 12 insertions(+), 11 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 3c10ced..50bcf94 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -615,12 +615,12 @@ sub list_and_choose {
else {
$bottom = $top = find_unique($choice, @stuff);
if (!defined $bottom) {
-   error_msg "Huh ($choice)?\n";
+   error_msg sprintf(__("Huh (%s)?\n"), 
$choice);
next TOPLOOP;
}
}
if ($opts->{SINGLETON} && $bottom != $top) {
-   error_msg "Huh ($choice)?\n";
+   error_msg sprintf(__("Huh (%s)?"), $choice);
next TOPLOOP;
}
for ($i = $bottom-1; $i <= $top-1; $i++) {
@@ -1056,7 +1056,7 @@ sub edit_hunk_manually {
my $hunkfile = $repo->repo_path . "/addp-hunk-edit.diff";
my $fh;
open $fh, '>', $hunkfile
-   or die "failed to open hunk edit file for writing: " . $!;
+   or die sprintf(__("failed to open hunk edit file for writing: 
%s"), $!);
print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n";
print $fh @$oldtext;
my $participle = $patch_mode_flavour{PARTICIPLE};
@@ -1083,7 +1083,7 @@ EOF
}
 
open $fh, '<', $hunkfile
-   or die "failed to open hunk edit file for reading: " . $!;
+   or die sprintf(__("failed to open hunk edit file for reading: 
%s"), $!);
my @newtext = grep { !/^#/ } <$fh>;
close $fh;
unlink $hunkfile;
@@ -1236,7 +1236,7 @@ sub apply_patch_for_checkout_commit {
 
 sub patch_update_cmd {
my @all_mods = list_modified($patch_mode_flavour{FILTER});
-   error_msg "ignoring unmerged: $_->{VALUE}\n"
+   error_msg sprintf(__("ignoring unmerged: %s\n"), $_->{VALUE})
for grep { $_->{UNMERGED} } @all_mods;
@all_mods = grep { !$_->{UNMERGED} } @all_mods;
 
@@ -1418,7 +1418,8 @@ sub patch_update_file {
chomp $response;
}
if ($response !~ /^\s*\d+\s*$/) {
-   error_msg "Invalid number: 
'$response'\n";
+   error_msg sprintf(__("Invalid number: 
'%s'\n"),
+$response);
} elsif (0 < $response && $response <= $num) {
$ix = $response - 1;
} else {
@@ -1460,7 +1461,7 @@ sub patch_update_file {
if ($@) {
my ($err,$exp) = ($@, $1);
$err =~ s/ at .*git-add--interactive 
line \d+,  line \d+.*$//;
-   error_msg "Malformed search regexp 
$exp: $err\n";
+   error_msg sprintf(__("Malformed search 
regexp %s: %s\n"), $exp, $err);
next;
}
my $iy = $ix;
@@ -1625,18 +1626,18 @@ sub process_args {
$patch_mode = $1;
$arg = shift @ARGV or die __("missing --");
} else {
-   die "unknown --patch mode: $1";
+   die sprintf(__("unknown --patch mode: %s"), $1);
}
} else {
$patch_mode = 'stage';
$arg = shift @ARGV or die __("missing --");
}
-   die "invalid argument $arg, expecting --"
-   unless $arg eq "--";
+   die sprintf(__("invalid argument %s, expecting --"),
+  $arg) unless $arg eq "--";
%patch_mode_flavour = %{$patch_modes{$patch_mode}};
}
elsif ($arg ne "--") {
-   die "invalid argument $arg, expecting --";
+   die sprintf(__("invalid argument %s, expecting --"), $arg);
}
 }
 
-- 
2.7.4



[PATCH v3 06/14] i18n: add--interactive: mark patch prompt for translation

2016-10-05 Thread Vasco Almeida
Mark prompt message assembled in place for translation, unfolding each
use case for each entry in the %patch_modes hash table.

Previously, this script relied on whether $patch_mode was set to run the
command patch_update_cmd() or show status and loop the main loop. Now,
it uses $cmd to indicate we must run patch_update_cmd() and $patch_mode
is used to tell which flavor of the %patch_modes are we on.  This is
introduced in order to be able to mark and unfold the message prompt
knowing in which context we are.

The tracking of context was done previously by point %patch_mode_flavour
hash table to the correct entry of %patch_modes, focusing only on value
of %patch_modes. Now, we are also interested in the key ('staged',
'stash', 'checkout_head', ...).

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 54 ---
 1 file changed, 46 insertions(+), 8 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 6bbde2d..c8d5093 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -93,6 +93,7 @@ sub colored {
 }
 
 # command line options
+my $cmd;
 my $patch_mode;
 my $patch_mode_revision;
 
@@ -173,7 +174,8 @@ my %patch_modes = (
},
 );
 
-my %patch_mode_flavour = %{$patch_modes{stage}};
+$patch_mode = 'stage';
+my %patch_mode_flavour = %{$patch_modes{$patch_mode}};
 
 sub run_cmd_pipe {
if ($^O eq 'MSWin32') {
@@ -1311,6 +1313,44 @@ sub display_hunks {
return $i;
 }
 
+my %patch_update_prompt_modes = (
+   stage => {
+   mode => __("Stage mode change [y,n,q,a,d,/%s,?]? "),
+   deletion => __("Stage deletion [y,n,q,a,d,/%s,?]? "),
+   hunk => __("Stage this hunk [y,n,q,a,d,/%s,?]? "),
+   },
+   stash => {
+   mode => __("Stash mode change [y,n,q,a,d,/%s,?]? "),
+   deletion => __("Stash deletion [y,n,q,a,d,/%s,?]? "),
+   hunk => __("Stash this hunk [y,n,q,a,d,/%s,?]? "),
+   },
+   reset_head => {
+   mode => __("Unstage mode change [y,n,q,a,d,/%s,?]? "),
+   deletion => __("Unstage deletion [y,n,q,a,d,/%s,?]? "),
+   hunk => __("Unstage this hunk [y,n,q,a,d,/%s,?]? "),
+   },
+   reset_nothead => {
+   mode => __("Apply mode change to index [y,n,q,a,d,/%s,?]? "),
+   deletion => __("Apply deletion to index [y,n,q,a,d,/%s,?]? "),
+   hunk => __("Apply this hunk to index [y,n,q,a,d,/%s,?]? "),
+   },
+   checkout_index => {
+   mode => __("Discard mode change from worktree 
[y,n,q,a,d,/%s,?]? "),
+   deletion => __("Discard deletion from worktree 
[y,n,q,a,d,/%s,?]? "),
+   hunk => __("Discard this hunk from worktree [y,n,q,a,d,/%s,?]? 
"),
+   },
+   checkout_head => {
+   mode => __("Discard mode change from index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   deletion => __("Discard deletion from index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   hunk => __("Discard this hunk from index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   },
+   checkout_nothead => {
+   mode => __("Apply mode change to index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   deletion => __("Apply deletion to index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   hunk => __("Apply this hunk to index and worktree 
[y,n,q,a,d,/%s,?]? "),
+   },
+);
+
 sub patch_update_file {
my $quit = 0;
my ($ix, $num);
@@ -1383,12 +1423,9 @@ sub patch_update_file {
for (@{$hunk[$ix]{DISPLAY}}) {
print;
}
-   print colored $prompt_color, $patch_mode_flavour{VERB},
- ($hunk[$ix]{TYPE} eq 'mode' ? ' mode change' :
-  $hunk[$ix]{TYPE} eq 'deletion' ? ' deletion' :
-  ' this hunk'),
- $patch_mode_flavour{TARGET},
- " [y,n,q,a,d,/$other,?]? ";
+   print colored $prompt_color,
+   
sprintf($patch_update_prompt_modes{$patch_mode}{$hunk[$ix]{TYPE}}, $other);
+
my $line = prompt_single_character;
last unless defined $line;
if ($line) {
@@ -1644,6 +1681,7 @@ sub process_args {
die sprintf(__("invalid argument %s, expecting --"),
   $arg) unless $arg eq "--";
%patch_mode_flavour = %{$patch_modes{$patch_mode}};
+   $cmd = 1;
}
elsif ($arg ne "--") {
die sprintf(__("invalid argument %s, expecting --"), $arg);
@@ -1680,7 +1718,7 @@ sub main_loop {
 
 process_args();
 refresh();
-if ($patch_mode) {
+if ($cmd) {
patch_update_cmd();
 }
 else {
-- 
2.7.4



[PATCH v3 01/14] i18n: add--interactive: mark strings for translation

2016-10-05 Thread Vasco Almeida
Mark simple strings (without interpolation) for translation.

Brackets around first parameter of ternary operator is necessary because
otherwise xgettext fails to extract strings marked for translation from
the rest of the file.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 74 ++-
 1 file changed, 41 insertions(+), 33 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index ee3d812..da0255b 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -4,6 +4,7 @@ use 5.008;
 use strict;
 use warnings;
 use Git;
+use Git::I18N;
 
 binmode(STDOUT, ":raw");
 
@@ -253,8 +254,9 @@ sub list_untracked {
run_cmd_pipe(qw(git ls-files --others --exclude-standard --), @ARGV);
 }
 
-my $status_fmt = '%12s %12s %s';
-my $status_head = sprintf($status_fmt, 'staged', 'unstaged', 'path');
+# TRANSLATORS: you can adjust this to align "git add -i" status menu
+my $status_fmt = __('%12s %12s %s');
+my $status_head = sprintf($status_fmt, __('staged'), __('unstaged'), 
__('path'));
 
 {
my $initial;
@@ -680,7 +682,7 @@ sub update_cmd {
my @mods = list_modified('file-only');
return if (!@mods);
 
-   my @update = list_and_choose({ PROMPT => 'Update',
+   my @update = list_and_choose({ PROMPT => __('Update'),
   HEADER => $status_head, },
 @mods);
if (@update) {
@@ -692,7 +694,7 @@ sub update_cmd {
 }
 
 sub revert_cmd {
-   my @update = list_and_choose({ PROMPT => 'Revert',
+   my @update = list_and_choose({ PROMPT => __('Revert'),
   HEADER => $status_head, },
 list_modified());
if (@update) {
@@ -726,13 +728,13 @@ sub revert_cmd {
 }
 
 sub add_untracked_cmd {
-   my @add = list_and_choose({ PROMPT => 'Add untracked' },
+   my @add = list_and_choose({ PROMPT => __('Add untracked') },
  list_untracked());
if (@add) {
system(qw(git update-index --add --), @add);
say_n_paths('added', @add);
} else {
-   print "No untracked files.\n";
+   print __("No untracked files.\n");
}
print "\n";
 }
@@ -1166,8 +1168,14 @@ sub edit_hunk_loop {
}
else {
prompt_yesno(
-   'Your edited hunk does not apply. Edit again '
-   . '(saying "no" discards!) [y/n]? '
+   # TRANSLATORS: do not translate [y/n]
+   # The program will only accept that input
+   # at this point.
+   # Consider translating (saying "no" discards!) 
as
+   # (saying "n" for "no" discards!) if the 
translation
+   # of the word "no" does not start with n.
+   __('Your edited hunk does not apply. Edit again 
'
+  . '(saying "no" discards!) [y/n]? ')
) or return undef;
}
}
@@ -1213,11 +1221,11 @@ sub apply_patch_for_checkout_commit {
run_git_apply 'apply '.$reverse, @_;
return 1;
} elsif (!$applies_index) {
-   print colored $error_color, "The selected hunks do not apply to 
the index!\n";
-   if (prompt_yesno "Apply them to the worktree anyway? ") {
+   print colored $error_color, __("The selected hunks do not apply 
to the index!\n");
+   if (prompt_yesno __("Apply them to the worktree anyway? ")) {
return run_git_apply 'apply '.$reverse, @_;
} else {
-   print colored $error_color, "Nothing was applied.\n";
+   print colored $error_color, __("Nothing was 
applied.\n");
return 0;
}
} else {
@@ -1237,9 +1245,9 @@ sub patch_update_cmd {
 
if (!@mods) {
if (@all_mods) {
-   print STDERR "Only binary files changed.\n";
+   print STDERR __("Only binary files changed.\n");
} else {
-   print STDERR "No changes.\n";
+   print STDERR __("No changes.\n");
}
return 0;
}
@@ -1397,12 +1405,12 @@ sub patch_update_file {
my $response = $1;
my $no = $ix > 10 ? $ix - 10 : 0;

[PATCH v3 04/14] i18n: clean.c: match string with git-add--interactive.perl

2016-10-05 Thread Vasco Almeida
Change strings for help to match the ones in git-add--interactive.perl.
The strings now represent one entry to translate each rather then two
entries each different only by an ending newline character.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 builtin/clean.c | 10 +-
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/builtin/clean.c b/builtin/clean.c
index 0371010..d6bc3aa 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -287,11 +287,11 @@ static void pretty_print_menus(struct string_list 
*menu_list)
 static void prompt_help_cmd(int singleton)
 {
clean_print_color(CLEAN_COLOR_HELP);
-   printf_ln(singleton ?
+   printf(singleton ?
  _("Prompt help:\n"
"1  - select a numbered item\n"
"foo- select item based on unique prefix\n"
-   "   - (empty) select nothing") :
+   "   - (empty) select nothing\n") :
  _("Prompt help:\n"
"1  - select a single item\n"
"3-5- select a range of items\n"
@@ -299,7 +299,7 @@ static void prompt_help_cmd(int singleton)
"foo- select item based on unique prefix\n"
"-...   - unselect specified items\n"
"*  - choose all items\n"
-   "   - (empty) finish selecting"));
+   "   - (empty) finish selecting\n"));
clean_print_color(CLEAN_COLOR_RESET);
 }
 
@@ -508,7 +508,7 @@ static int parse_choice(struct menu_stuff *menu_stuff,
if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > 
top ||
(is_single && bottom != top)) {
clean_print_color(CLEAN_COLOR_ERROR);
-   printf_ln(_("Huh (%s)?"), (*ptr)->buf);
+   printf(_("Huh (%s)?\n"), (*ptr)->buf);
clean_print_color(CLEAN_COLOR_RESET);
continue;
}
@@ -774,7 +774,7 @@ static int ask_each_cmd(void)
 static int quit_cmd(void)
 {
string_list_clear(_list, 0);
-   printf_ln(_("Bye."));
+   printf(_("Bye.\n"));
return MENU_RETURN_NO_LOOP;
 }
 
-- 
2.7.4



[PATCH v3 07/14] i18n: add--interactive: i18n of help_patch_cmd

2016-10-05 Thread Vasco Almeida
Mark help message of help_patch_cmd for translation.  The message must
be unfolded to be free of variables so we can have high quality
translations.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 54 ---
 1 file changed, 46 insertions(+), 8 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index c8d5093..35967fe 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -1189,15 +1189,53 @@ sub edit_hunk_loop {
}
 }
 
+my %help_patch_modes = (
+   stage => __(
+"y - stage this hunk
+n - do not stage this hunk
+q - quit; do not stage this hunk or any of the remaining ones
+a - stage this hunk and all later hunks in the file
+d - do not stage this hunk or any of the later hunks in the file"),
+   stash => __(
+"y - stash this hunk
+n - do not stash this hunk
+q - quit; do not stash this hunk or any of the remaining ones
+a - stash this hunk and all later hunks in the file
+d - do not stash this hunk or any of the later hunks in the file"),
+   reset_head => __(
+"y - unstage this hunk
+n - do not unstage this hunk
+q - quit; do not unstage this hunk or any of the remaining ones
+a - unstage this hunk and all later hunks in the file
+d - do not unstage this hunk or any of the later hunks in the file"),
+   reset_nothead => __(
+"y - apply this hunk to index
+n - do not apply this hunk to index
+q - quit; do not apply this hunk or any of the remaining ones
+a - apply this hunk and all later hunks in the file
+d - do not apply this hunk or any of the later hunks in the file"),
+   checkout_index => __(
+"y - discard this hunk from worktree
+n - do not discard this hunk from worktree
+q - quit; do not discard this hunk or any of the remaining ones
+a - discard this hunk and all later hunks in the file
+d - do not discard this hunk or any of the later hunks in the file"),
+   checkout_head => __(
+"y - discard this hunk from index and worktree
+n - do not discard this hunk from index and worktree
+q - quit; do not discard this hunk or any of the remaining ones
+a - discard this hunk and all later hunks in the file
+d - do not discard this hunk or any of the later hunks in the file"),
+   checkout_nothead => __(
+"y - apply this hunk to index and worktree
+n - do not apply this hunk to index and worktree
+q - quit; do not apply this hunk or any of the remaining ones
+a - apply this hunk and all later hunks in the file
+d - do not apply this hunk or any of the later hunks in the file"),
+);
+
 sub help_patch_cmd {
-   my $verb = lc $patch_mode_flavour{VERB};
-   my $target = $patch_mode_flavour{TARGET};
-   print colored $help_color, <

[PATCH v3 02/14] i18n: add--interactive: mark simple here-documents for translation

2016-10-05 Thread Vasco Almeida
Mark messages in here-document without interpolation for translation.

The here-document delimiter \EOF, which is the same as 'EOF', indicate
that the text is to be treated literally without interpolation of its
content.  Unfortunately xgettext is not able to extract here documents
with delimiter \EOF but it is with delimiter enclosed in single quotes.
Then change \EOF to 'EOF', although in this case does not make
difference what variation of here-document to use since there is nothing
to interpolate.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 8 +---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index da0255b..3c10ced 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -639,7 +639,7 @@ sub list_and_choose {
 }
 
 sub singleton_prompt_help_cmd {
-   print colored $help_color, <<\EOF ;
+   print colored $help_color, __ <<'EOF' ;
 Prompt help:
 1  - select a numbered item
 foo- select item based on unique prefix
@@ -648,7 +648,7 @@ EOF
 }
 
 sub prompt_help_cmd {
-   print colored $help_color, <<\EOF ;
+   print colored $help_color, __ <<'EOF' ;
 Prompt help:
 1  - select a single item
 3-5- select a range of items
@@ -1584,7 +1584,9 @@ sub quit_cmd {
 }
 
 sub help_cmd {
-   print colored $help_color, <<\EOF ;
+# TRANSLATORS: please do not translate the command names
+# 'status', 'update', 'revert', etc.
+   print colored $help_color, __ <<'EOF' ;
 status- show paths with changes
 update- add working tree state to the staged set of changes
 revert- revert staged set of changes back to the HEAD version
-- 
2.7.4



[PATCH v3 05/14] i18n: add--interactive: mark plural strings

2016-10-05 Thread Vasco Almeida
Mark plural strings for translation.  Unfold each action case in one
entire sentence.

Pass new keyword for xgettext to extract.

Update test to include new subroutine __n() for plural strings handling.

Update documentation to include a description of the new __n() subroutine.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---

On the present re-roll, change name __Q to __n following the
Locale::Message convention.
Add documentation about __n().

 Makefile  |  3 ++-
 git-add--interactive.perl | 27 ++-
 perl/Git/I18N.pm  |  9 -
 t/t0202/test.pl   | 11 ++-
 4 files changed, 38 insertions(+), 12 deletions(-)

diff --git a/Makefile b/Makefile
index 1aad150..4ef0344 100644
--- a/Makefile
+++ b/Makefile
@@ -2111,7 +2111,8 @@ XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --language=C \
--keyword=_ --keyword=N_ --keyword="Q_:1,2"
 XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell \
--keyword=gettextln --keyword=eval_gettextln
-XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --keyword=__ --language=Perl
+XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --language=Perl \
+   --keyword=__ --keyword="__n:1,2"
 LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
 LOCALIZED_SH = $(SCRIPT_SH)
 LOCALIZED_SH += git-parse-remote.sh
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 50bcf94..6bbde2d 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -669,12 +669,18 @@ sub status_cmd {
 sub say_n_paths {
my $did = shift @_;
my $cnt = scalar @_;
-   print "$did ";
-   if (1 < $cnt) {
-   print "$cnt paths\n";
-   }
-   else {
-   print "one path\n";
+   if ($did eq 'added') {
+   printf(__n("added %d path\n", "added %d paths\n",
+  $cnt), $cnt);
+   } elsif ($did eq 'updated') {
+   printf(__n("updated %d path\n", "updated %d paths\n",
+  $cnt), $cnt);
+   } elsif ($did eq 'reverted') {
+   printf(__n("reverted %d path\n", "reverted %d paths\n",
+  $cnt), $cnt);
+   } else {
+   printf(__n("touched %d path\n", "touched %d paths\n",
+  $cnt), $cnt);
}
 }
 
@@ -1423,7 +1429,8 @@ sub patch_update_file {
} elsif (0 < $response && $response <= $num) {
$ix = $response - 1;
} else {
-   error_msg "Sorry, only $num hunks 
available.\n";
+   error_msg sprintf(__n("Sorry, only %d 
hunk available.\n",
+ "Sorry, only %d 
hunks available.\n", $num), $num);
}
next;
}
@@ -1518,8 +1525,10 @@ sub patch_update_file {
elsif ($other =~ /s/ && $line =~ /^s/) {
my @split = split_hunk($hunk[$ix]{TEXT}, 
$hunk[$ix]{DISPLAY});
if (1 < @split) {
-   print colored $header_color, "Split 
into ",
-   scalar(@split), " hunks.\n";
+   print colored $header_color, sprintf(
+   __n("Split into %d hunk.\n",
+   "Split into %d hunks.\n",
+   scalar(@split)), 
scalar(@split));
}
splice (@hunk, $ix, 1, @split);
$num = scalar @hunk;
diff --git a/perl/Git/I18N.pm b/perl/Git/I18N.pm
index f889fd6..3f7ac25 100644
--- a/perl/Git/I18N.pm
+++ b/perl/Git/I18N.pm
@@ -13,7 +13,7 @@ BEGIN {
}
 }
 
-our @EXPORT = qw(__);
+our @EXPORT = qw(__ __n);
 our @EXPORT_OK = @EXPORT;
 
 sub __bootstrap_locale_messages {
@@ -44,6 +44,7 @@ BEGIN
eval {
__bootstrap_locale_messages();
*__ = \::Messages::gettext;
+   *__n = \::Messages::ngettext;
1;
} or do {
# Tell test.pl that we couldn't load the gettext library.
@@ -51,6 +52,7 @@ BEGIN
 
# Just a fall-through no-op
*__ = sub ($) { $_[0] };
+   *__n = sub ($$$) { $_[2] == 1 ? $_[0] : $_[1] };
};
 }
 
@@ -70,6 +72,8 @@ Git::I18N - Perl interface to Git's Gettext localizations
 
printf __("The following error occurred: %s\n"), $error;
 
+   printf 

[PATCH v3 08/14] i18n: add--interactive: mark edit_hunk_manually message for translation

2016-10-05 Thread Vasco Almeida
Mark message of edit_hunk_manually displayed in the editing file when
user chooses 'e' option.  The message had to be unfolded to allow
translation of the $participle verb.

Some messages end up being exactly the same for some uses cases, but
left it for easier change in the future, e.g., wanting to change wording
of one particular use case.

Signed-off-by: Vasco Almeida <vascomalme...@sapo.pt>
---
 git-add--interactive.perl | 45 ++---
 1 file changed, 34 insertions(+), 11 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 35967fe..5356d5a 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -1058,6 +1058,30 @@ sub color_diff {
} @_;
 }
 
+my %edit_hunk_manually_modes = (
+   stage => __(
+"# If the patch applies cleanly, the edited hunk will immediately be
+# marked for staging."),
+   stash => __(
+"# If the patch applies cleanly, the edited hunk will immediately be
+# marked for stashing."),
+   reset_head => __(
+"# If the patch applies cleanly, the edited hunk will immediately be
+# marked for unstaging."),
+   reset_nothead => __(
+"# If the patch applies cleanly, the edited hunk will immediately be
+# marked for applying."),
+   checkout_index => __(
+"# If the patch applies cleanly, the edited hunk will immediately be
+# marked for discarding"),
+   checkout_head => __(
+"# If the patch applies cleanly, the edited hunk will immediately be
+# marked for discarding."),
+   checkout_nothead => __(
+"# If the patch applies cleanly, the edited hunk will immediately be
+# marked for applying."),
+);
+
 sub edit_hunk_manually {
my ($oldtext) = @_;
 
@@ -1065,22 +1089,21 @@ sub edit_hunk_manually {
my $fh;
open $fh, '>', $hunkfile
or die sprintf(__("failed to open hunk edit file for writing: 
%s"), $!);
-   print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n";
+   print $fh __("# Manual hunk edit mode -- see bottom for a quick 
guide\n");
print $fh @$oldtext;
-   my $participle = $patch_mode_flavour{PARTICIPLE};
my $is_reverse = $patch_mode_flavour{IS_REVERSE};
my ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') : ('+', 
'-');
-   print $fh <<EOF;
-# ---
-# To remove '$remove_minus' lines, make them ' ' lines (context).
-# To remove '$remove_plus' lines, delete them.
+   print $fh sprintf(__(
+"# ---
+# To remove '%s' lines, make them ' ' lines (context).
+# To remove '%s' lines, delete them.
 # Lines starting with # will be removed.
-#
-# If the patch applies cleanly, the edited hunk will immediately be
-# marked for $participle. If it does not apply cleanly, you will be given
+#\n"), $remove_minus, $remove_plus),
+$edit_hunk_manually_modes{$patch_mode}, __(
+# TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
+" If it does not apply cleanly, you will be given
 # an opportunity to edit again. If all lines of the hunk are removed,
-# then the edit is aborted and the hunk is left unchanged.
-EOF
+# then the edit is aborted and the hunk is left unchanged.\n");
close $fh;
 
chomp(my $editor = run_cmd_pipe(qw(git var GIT_EDITOR)));
-- 
2.7.4



[PATCH v3 00/14] Mark strings in Perl scripts for translation

2016-10-05 Thread Vasco Almeida
Mark messages in some perl scripts for translation.

Thanks for the reviews of Junio Hamano and Jakub Narębski.  Although I think
Jakub Narębski's suggestions are overall good, I am not willing to go that path
because I cannot see huge benefits from them given what we already have and
also I lack Perl skills.

Interdiff bellow.

Vasco Almeida (14):
  i18n: add--interactive: mark strings for translation
  i18n: add--interactive: mark simple here-documents for translation
  i18n: add--interactive: mark strings with interpolation for
translation
  i18n: clean.c: match string with git-add--interactive.perl
  i18n: add--interactive: mark plural strings
  i18n: add--interactive: mark patch prompt for translation
  i18n: add--interactive: i18n of help_patch_cmd
  i18n: add--interactive: mark edit_hunk_manually message for
translation
  i18n: add--interactive: remove %patch_modes entries
  i18n: add--interactive: mark status words for translation
  i18n: send-email: mark strings for translation
  i18n: send-email: mark warnings and errors for translation
  i18n: send-email: mark string with interpolation for translation
  i18n: difftool: mark warnings for translation

 Makefile  |   3 +-
 builtin/clean.c   |  10 +-
 git-add--interactive.perl | 318 ++
 git-difftool.perl |  22 ++--
 git-send-email.perl   | 176 +
 perl/Git/I18N.pm  |   9 +-
 t/t0202/test.pl   |  11 +-
 7 files changed, 340 insertions(+), 209 deletions(-)


diff --git a/Makefile b/Makefile
index fc29d85..4ef0344 100644
--- a/Makefile
+++ b/Makefile
@@ -2112,7 +2112,7 @@ XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --language=C \
 XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell \
--keyword=gettextln --keyword=eval_gettextln
 XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --language=Perl \
-   --keyword=__ --keyword="Q__:1,2"
+   --keyword=__ --keyword="__n:1,2"
 LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
 LOCALIZED_SH = $(SCRIPT_SH)
 LOCALIZED_SH += git-parse-remote.sh
diff --git a/builtin/clean.c b/builtin/clean.c
index 0371010..d6bc3aa 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -287,11 +287,11 @@ static void pretty_print_menus(struct string_list 
*menu_list)
 static void prompt_help_cmd(int singleton)
 {
clean_print_color(CLEAN_COLOR_HELP);
-   printf_ln(singleton ?
+   printf(singleton ?
  _("Prompt help:\n"
"1  - select a numbered item\n"
"foo- select item based on unique prefix\n"
-   "   - (empty) select nothing") :
+   "   - (empty) select nothing\n") :
  _("Prompt help:\n"
"1  - select a single item\n"
"3-5- select a range of items\n"
@@ -299,7 +299,7 @@ static void prompt_help_cmd(int singleton)
"foo- select item based on unique prefix\n"
"-...   - unselect specified items\n"
"*  - choose all items\n"
-   "   - (empty) finish selecting"));
+   "   - (empty) finish selecting\n"));
clean_print_color(CLEAN_COLOR_RESET);
 }
 
@@ -508,7 +508,7 @@ static int parse_choice(struct menu_stuff *menu_stuff,
if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > 
top ||
(is_single && bottom != top)) {
clean_print_color(CLEAN_COLOR_ERROR);
-   printf_ln(_("Huh (%s)?"), (*ptr)->buf);
+   printf(_("Huh (%s)?\n"), (*ptr)->buf);
clean_print_color(CLEAN_COLOR_RESET);
continue;
}
@@ -774,7 +774,7 @@ static int ask_each_cmd(void)
 static int quit_cmd(void)
 {
string_list_clear(_list, 0);
-   printf_ln(_("Bye."));
+   printf(_("Bye.\n"));
return MENU_RETURN_NO_LOOP;
 }
 
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index b72cc97..0b4a27c 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -106,9 +106,6 @@ my %patch_modes = (
DIFF => 'diff-files -p',
APPLY => sub { apply_patch 'apply --cached', @_; },
APPLY_CHECK => 'apply --cached',
-   VERB => 'Stage',
-   TARGET => '',
-   PARTICIPLE => 'staging',
FILTER => 'file-only',
IS_REVERSE => 0,
},
@@ -116,9 +113,6 @@ my %patch_modes = (
DIFF => 'diff-index -p HEAD',
APPLY => sub { apply_

Re: [PATCH v2 05/11] i18n: add--interactive: mark message for translation

2016-10-03 Thread Vasco Almeida
A Sáb, 01-10-2016 às 19:09 +0200, Jakub Narębski escreveu:
> W dniu 26.09.2016 o 01:09, Junio C Hamano pisze:
> > Vasco Almeida <vascomalme...@sapo.pt> writes:
> > 
> >> -print colored $prompt_color,
> $patch_mode_flavour{VERB},
> >> -  ($hunk[$ix]{TYPE} eq 'mode' ? ' mode change' :
> >> -   $hunk[$ix]{TYPE} eq 'deletion' ? ' deletion' :
> >> -   ' this hunk'),
> >> -  $patch_mode_flavour{TARGET},
> >> -  " [y,n,q,a,d,/$other,?]? ";
> > 
> > I hate to say this but expanding this single-liner into if/elsif/
> > cascade of uncountable number of arms is simply a disaster.
> 
> Even if we turn this "single"-liner composition of sentence into
> interpolation (allowing for reordering of parts in translation),
> like
> 
>    print colored $prompt_color, __x("{verb} {noun}{maybe_target}
> [y,n,q,a,d,/{other},?]? ",
> verb => $patch_mode_flavour{VERB}, noun =>
> $patch_mode_noun{$hunk[$ix]{TYPE}},
> maybe_target => $patch_mode_flavour{TARGET} || "", other =>
> $other);
> 
> This would of course require N__() on values of hash, somewhere.
> 
> the problem is that the ordering may need to change depending on
> verb: "Stage", "Stash", "Unstage", "Apply", "Discard", and/or noun:
> "mode change", "deletion", "this hunk", and/or presence and value
> of maybe_target: " to index", " from worktree", " from index and
> worktree",
> " to index and worktree".

So it does not work, unfortunately. The plus side is it would be very
concise compared to laying every combination as entire sentences.
However, if it worked, I think it would be a bit difficult to translate
and translators would be prone to commit some mistake somewhere. It may
be harder to translate a sentence by its bits than translate it as a
whole.


  1   2   3   4   5   >