X-Git-Url: http://git.vanrenterghem.biz/git.ikiwiki.info.git/blobdiff_plain/dcd57dd5c9f3265bb7a78a5696b90976698c43aa..32a70b7c4859dfeb6f9fa91fb4bc0f4d709b8376:/IkiWiki/Plugin/po.pm?ds=sidebyside

diff --git a/IkiWiki/Plugin/po.pm b/IkiWiki/Plugin/po.pm
index 93cf6bbdf..7b62092e1 100644
--- a/IkiWiki/Plugin/po.pm
+++ b/IkiWiki/Plugin/po.pm
@@ -28,7 +28,7 @@ use UNIVERSAL;
 my %translations;
 my @origneedsbuild;
 my %origsubs;
-my @slavelanguages; # orderer as in config po_slave_languages
+my @slavelanguages; # language codes ordered as in config po_slave_languages
 
 memoize("istranslatable");
 memoize("_istranslation");
@@ -106,9 +106,9 @@ sub getsetup () {
 		po_slave_languages => {
 			type => "string",
 			example => [
-				'fr' => 'Français',
-				'es' => 'Español',
-				'de' => 'Deutsch'
+				'fr|Français',
+				'es|Español',
+				'de|Deutsch'
 			],
 			description => "slave languages (PO files)",
 			safe => 1,
@@ -141,24 +141,30 @@ sub checkconfig () {
 
 	if (ref $config{po_slave_languages} eq 'ARRAY') {
 		my %slaves;
-		for (my $i=0; $i<@{$config{po_slave_languages}}; $i = $i + 2) {
-			$slaves{$config{po_slave_languages}->[$i]} = $config{po_slave_languages}->[$i + 1];
-			push @slavelanguages, $config{po_slave_languages}->[$i];
-                }
+		foreach my $pair (@{$config{po_slave_languages}}) {
+			my ($code, $name) = ( $pair =~ /^([a-z]{2})\|(.+)$/ );
+			if (!defined $code || !defined $name) {
+				error(sprintf(gettext("%s has invalid syntax: must use CODE|NAME"),
+				              $pair));
+			}
+			$slaves{$code} = $name;
+			push @slavelanguages, $code;
+
+		}
 		$config{po_slave_languages} = \%slaves;
-        }
+	}
 	elsif (ref $config{po_slave_languages} eq 'HASH') {
 		@slavelanguages = sort {
 			$config{po_slave_languages}->{$a} cmp $config{po_slave_languages}->{$b};
 		} keys %{$config{po_slave_languages}};
-        }
+	}
 
 	delete $config{po_slave_languages}{$config{po_master_language}{code}};;
 
 	map {
 		islanguagecode($_)
 			or error(sprintf(gettext("%s is not a valid language code"), $_));
-	} ($config{po_master_language}{code}, keys %{$config{po_slave_languages}});
+	} ($config{po_master_language}{code}, @slavelanguages);
 
 	if (! exists $config{po_translatable_pages} ||
 	    ! defined $config{po_translatable_pages}) {
@@ -187,7 +193,7 @@ sub checkconfig () {
 		next if $underlay=~/^locale\//;
 
 		# Underlays containing the po files for slave languages.
-		foreach my $ll (keys %{$config{po_slave_languages}}) {
+		foreach my $ll (@slavelanguages) {
 			add_underlay("po/$ll/$underlay")
 				if -d "$config{underlaydirbase}/po/$ll/$underlay";
 		}
@@ -215,40 +221,65 @@ sub needsbuild () {
 	foreach my $master (keys %translations) {
 		map add_depends($_, $master), values %{otherlanguages_pages($master)};
 	}
+
+	return $needsbuild;
 }
 
-# Massage the recorded state of internal links so that:
-# - it matches the actually generated links, rather than the links as written
-#   in the pages' source
-# - backlinks are consistent in all cases
 sub scan (@) {
 	my %params=@_;
 	my $page=$params{page};
 	my $content=$params{content};
-
-	if (istranslation($page)) {
-		foreach my $destpage (@{$links{$page}}) {
-			if (istranslatable($destpage)) {
-				# replace the occurence of $destpage in $links{$page}
-				for (my $i=0; $i<@{$links{$page}}; $i++) {
-					if (@{$links{$page}}[$i] eq $destpage) {
-						@{$links{$page}}[$i] = $destpage . '.' . lang($page);
-						last;
-					}
-				}
+	my $run_by_po=$params{run_by_po};
+
+	# Massage the recorded state of internal links so that:
+	# - it matches the actually generated links, rather than the links as
+	#   written in the pages' source
+	# - backlinks are consistent in all cases
+
+	# A second scan pass is made over translation pages, so as an
+	# optimization, we only do so on the second pass in this case,
+	# i.e. when this hook is called by itself.
+	if ($run_by_po && istranslation($page)) {
+		# replace the occurence of $destpage in $links{$page}
+		my @orig_links = @{$links{$page}};
+		$links{$page} = [];
+		foreach my $destpage (@orig_links) {
+			if (istranslatedto($destpage, lang($page))) {
+				add_link($page, $destpage . '.' . lang($page));
+			}
+			else {
+				add_link($page, $destpage);
 			}
 		}
 	}
-	elsif (! istranslatable($page) && ! istranslation($page)) {
+	# No second scan pass is done for a non-translation page, so
+	# links massaging must happen on first pass in this case.
+	elsif (! $run_by_po && ! istranslatable($page) && ! istranslation($page)) {
 		foreach my $destpage (@{$links{$page}}) {
 			if (istranslatable($destpage)) {
 				# make sure any destpage's translations has
 				# $page in its backlinks
-				push @{$links{$page}},
-					values %{otherlanguages_pages($destpage)};
+				foreach my $link (values %{otherlanguages_pages($destpage)}) {
+					add_link($page, $link);
+				}
 			}
 		}
 	}
+
+	# Re-run the preprocess hooks in scan mode, then the scan hooks,
+	# over the po-to-markup converted content
+	return if $run_by_po; # avoid looping endlessly
+	return unless istranslation($page);
+	$content = po_to_markup($page, $content);
+	require IkiWiki;
+	IkiWiki::preprocess($page, $page, $content, 1);
+	IkiWiki::run_hooks(scan => sub {
+		shift->(
+			page => $page,
+			content => $content,
+			run_by_po => 1,
+		);
+	});
 }
 
 # We use filter to convert PO to the master page's format,
@@ -259,12 +290,6 @@ sub filter (@) {
 	my $page = $params{page};
 	my $destpage = $params{destpage};
 	my $content = $params{content};
-	my $fullpage = $params{fullpage};
-
-	unless ($fullpage) {
-		return $content;
-	}
-
 	if (istranslation($page) && ! alreadyfiltered($page, $destpage)) {
 		$content = po_to_markup($page, $content);
 		setalreadyfiltered($page, $destpage);
@@ -384,41 +409,6 @@ sub mydelete (@) {
 sub change (@) {
 	my @rendered=@_;
 
-	# All meta titles are first extracted at scan time, i.e. before we turn
-	# PO files back into translated markdown; escaping of double-quotes in
-	# PO files breaks the meta plugin's parsing enough to save ugly titles
-	# to %pagestate at this time.
-	#
-	# Then, at render time, every page passes in turn through the Great
-	# Rendering Chain (filter->preprocess->linkify->htmlize), and the meta
-	# plugin's preprocess hook is this time in a position to correctly
-	# extract the titles from slave pages.
-	#
-	# This is, unfortunately, too late: if the page A, linking to the page
-	# B, is rendered before B, it will display the wrongly-extracted meta
-	# title as the link text to B.
-	#
-	# On the one hand, such a corner case only happens on rebuild: on
-	# refresh, every rendered page is fixed to contain correct meta titles.
-	# On the other hand, it can take some time to get every page fixed.
-	# We therefore re-render every rendered page after a rebuild to fix them
-	# at once. As this more or less doubles the time needed to rebuild the
-	# wiki, we do so only when really needed.
-
-	if (@rendered
-	    && exists $config{rebuild} && defined $config{rebuild} && $config{rebuild}
-	    && UNIVERSAL::can("IkiWiki::Plugin::meta", "getsetup")
-	    && exists $config{meta_overrides_page_title}
-	    && defined $config{meta_overrides_page_title}
-	    && $config{meta_overrides_page_title}) {
-		debug(sprintf(gettext("rebuilding all pages to fix meta titles")));
-		resetalreadyfiltered();
-		require IkiWiki::Render;
-		foreach my $file (@rendered) {
-			IkiWiki::render($file, sprintf(gettext("building %s"), $file));
-		}
-	}
-
 	my $updated_po_files=0;
 
 	# Refresh/create POT and PO files as needed.
@@ -558,7 +548,7 @@ sub formbuilder (@) {
 	# This cannot be done in the formbuilder_setup hook as the list of types is
 	# computed later.
 	if ($form->field("do") eq "create") {
-	        foreach my $field ($form->field) {
+		foreach my $field ($form->field) {
 			next unless "$field" eq "type";
 			next unless $field->type eq 'select';
 			my $orig_value = $field->value;
@@ -597,7 +587,7 @@ sub mybestlink ($$) {
 	my $res=$origsubs{'bestlink'}->(masterpage($page), $link);
 	my @caller = caller(1);
 	if (length $res
-	    && istranslatable($res)
+	    && istranslatedto($res, lang($page))
 	    && istranslation($page)
 	    &&  !(exists $caller[3] && defined $caller[3]
 		  && ($caller[3] eq "IkiWiki::PageSpec::match_link"))) {
@@ -615,7 +605,7 @@ sub mybeautify_urlpath ($) {
 		$res =~ s!/\Qindex.$config{htmlext}\E$!/!;
 		map {
 			$res =~ s!/\Qindex.$_.$config{htmlext}\E$!/!;
-		} (keys %{$config{po_slave_languages}});
+		} @slavelanguages;
 	}
 	return $res;
 }
@@ -706,7 +696,7 @@ sub myisselflink ($$) {
 	return 1 if $origsubs{'isselflink'}->($page, $link);
 	if (istranslation($page)) {
 		return $origsubs{'isselflink'}->(masterpage($page), $link);
-        }
+	}
 	return;
 }
 
@@ -778,6 +768,15 @@ sub istranslatable ($) {
 	return;
 }
 
+sub istranslatedto ($$) {
+	my $page=shift;
+	my $destlang = shift;
+
+	$page=~s#^/##;
+	return 0 unless istranslatable($page);
+	exists $pagesources{otherlanguage_page($page, $destlang)};
+}
+
 sub _istranslation ($) {
 	my $page=shift;
 
@@ -854,7 +853,10 @@ sub otherlanguages_codes ($) {
 	foreach my $lang
 		($config{po_master_language}{code}, @slavelanguages) {
 		next if $lang eq $curlang;
-		push @ret, $lang;
+		if ($lang eq $config{po_master_language}{code} ||
+		    istranslatedto(masterpage($page), $lang)) {
+			push @ret, $lang;
+		}
 	}
 	return \@ret;
 }
@@ -862,10 +864,10 @@ sub otherlanguages_codes ($) {
 sub otherlanguages_pages ($) {
 	my $page=shift;
 
-        my %ret;
+	my %ret;
 	map {
 		$ret{$_} = otherlanguage_page($page, $_)
-	} otherlanguages_codes($page);
+	} @{otherlanguages_codes($page)};
 
 	return \%ret;
 }
@@ -890,7 +892,7 @@ sub pofile ($$) {
 sub pofiles ($) {
 	my $masterfile=shift;
 
-	return map pofile($masterfile, $_), (keys %{$config{po_slave_languages}});
+	return map pofile($masterfile, $_), @slavelanguages;
 }
 
 sub refreshpot ($) {
@@ -1048,7 +1050,7 @@ sub ishomepage ($) {
 	my $page = shift;
 
 	return 1 if $page eq 'index';
-	map { return 1 if $page eq 'index.'.$_ } keys %{$config{po_slave_languages}};
+	map { return 1 if $page eq 'index.'.$_ } @slavelanguages;
 	return undef;
 }
 
@@ -1063,7 +1065,7 @@ sub deletetranslations ($) {
 		if (-e $absfile && ! -l $absfile && ! -d $absfile) {
 			push @todelete, $file;
 		}
-	} keys %{$config{po_slave_languages}};
+	} @slavelanguages;
 
 	map {
 		if ($config{rcs}) {
@@ -1194,7 +1196,7 @@ sub isvalidpo ($) {
 	unlink $infile;
 
 	if ($res) {
-	    return IkiWiki::SuccessReason->new("valid gettext data");
+		return IkiWiki::SuccessReason->new("valid gettext data");
 	}
 	return IkiWiki::FailReason->new(gettext("invalid gettext data, go back ".
 					"to previous page to continue edit"));
@@ -1206,7 +1208,7 @@ sub po4a_type ($) {
 	my $pagetype = pagetype($file);
 	if ($pagetype eq 'html') {
 		return 'xhtml';
-        }
+	}
 	return 'text';
 }
 
@@ -1220,13 +1222,13 @@ sub po4a_options($) {
 		# how to disable options is not consistent across po4a modules
 		$options{includessi} = '';
 		$options{includeexternal} = 0;
-        }
+	}
 	elsif ($pagetype eq 'mdwn') {
 		$options{markdown} = 1;
-        }
-        else {
+	}
+	else {
 		$options{markdown} = 0;
-        }
+	}
 
 	return %options;
 }
@@ -1291,4 +1293,32 @@ sub match_currentlang ($$;@) {
 	}
 }
 
+sub match_needstranslation ($$;@) {
+	my $page=shift;
+	my $wanted=shift;
+
+	if (defined $wanted && $wanted ne "") {
+		if ($wanted !~ /^\d+$/) {
+			return IkiWiki::FailReason->new("parameter is not an integer");
+		}
+		elsif ($wanted > 100) {
+			return IkiWiki::FailReason->new("parameter is greater than 100");
+		}
+	}
+	else {
+		$wanted=100;
+	}
+
+	my $percenttranslated=IkiWiki::Plugin::po::percenttranslated($page);
+	if ($percenttranslated eq 'N/A') {
+		return IkiWiki::FailReason->new("file is not a translatable page");
+	}
+	elsif ($percenttranslated < $wanted) {
+		return IkiWiki::SuccessReason->new("file has $percenttranslated translated");
+	}
+	else {
+		return IkiWiki::FailReason->new("file is translated enough");
+	}
+}
+
 1