my @origneedsbuild;
my %origsubs;
+memoize("istranslatable");
memoize("_istranslation");
memoize("percenttranslated");
hook(type => "filter", id => "po", call => \&filter);
hook(type => "htmlize", id => "po", call => \&htmlize);
hook(type => "pagetemplate", id => "po", call => \&pagetemplate, last => 1);
+ hook(type => "renamepage", id => "po", call => \&renamepage);
+ hook(type => "delete", id => "po", call => \&mydelete);
hook(type => "change", id => "po", call => \&change);
hook(type => "editcontent", id => "po", call => \&editcontent);
# a given master page was rendered because its source file was changed
@origneedsbuild=(@$needsbuild);
+ flushmemoizecache();
buildtranslationscache();
# make existing translations depend on the corresponding master page
my $page = $params{page};
my $content = $params{content};
- my $masterfile = srcfile($pagesources{masterpage($page)});
+
+ # ignore PO files this plugin did not create
+ return $content unless istranslation($page);
# force content to be htmlize'd as if it was the same type as the master page
- return IkiWiki::htmlize($page, $page, pagetype($masterfile), $content);
+ return IkiWiki::htmlize($page, $page,
+ pagetype(srcfile($pagesources{masterpage($page)})),
+ $content);
} #}}}
sub pagetemplate (@) { #{{{
if ($template->query(name => "istranslatable")) {
$template->param(istranslatable => istranslatable($page));
}
+ if ($template->query(name => "HOMEPAGEURL")) {
+ $template->param(homepageurl => homepageurl($page));
+ }
if ($template->query(name => "otherlanguages")) {
$template->param(otherlanguages => [otherlanguagesloop($page)]);
map add_depends($page, $_), (values %{otherlanguages($page)});
}
} # }}}
+# Save information about master page rename, so that:
+# - our delete hook can ignore the translations not renamed already
+# - our change hook can rename the translations accordingly.
+#
+# FIXME:
+# This hook is called once per page linking to the old page, which
+# means our delete hook won't know it should not delete a renamed orphan
+# page's translation.
+#
+# Moreover, we can't recognize such pages at delete stage:
+# existing links are fixed in the renaming process, so every
+# renamed page's old location will be an orphan anyway at this time.
+sub renamepage(@) { #{{{
+ my %params=@_;
+ my $oldpage=$params{oldpage};
+ my $newpage=$params{newpage};
+
+ setrenamed($oldpage, $newpage) if istranslatable($oldpage);
+ return $params{content};
+} #}}}
+
+sub mydelete(@) { #{{{
+ my @deleted=@_;
+
+ map {
+ deletetranslations($_);
+ } grep { istranslatablefile($_) && ! renamed(pagename($_))} @deleted;
+} #}}}
+
sub change(@) { #{{{
my @rendered=@_;
+ my $eachrenamed=eachrenamed();
+ while (my ($oldpage, $newpage) = $eachrenamed->()) {
+ renametranslations($oldpage, $newpage);
+ }
+ resetrenamed();
+
my $updated_po_files=0;
# Refresh/create POT and PO files as needed.
- foreach my $page (map pagename($_), @rendered) {
- next unless istranslatable($page);
- my $file=srcfile($pagesources{$page});
+ foreach my $file (@rendered) {
+ next unless istranslatablefile($file);
+ my $page=pagename($file);
+ my $masterfile=srcfile($file);
my $updated_pot_file=0;
# Only refresh Pot file if it does not exist, or if
# $pagesources{$page} was changed: don't if only the HTML was
# refreshed, e.g. because of a dependency.
if ((grep { $_ eq $pagesources{$page} } @origneedsbuild)
- || ! -e potfile($file)) {
- refreshpot($file);
+ || ! -e potfile($masterfile)) {
+ refreshpot($masterfile);
$updated_pot_file=1;
}
my @pofiles;
map {
push @pofiles, $_ if ($updated_pot_file || ! -e $_);
- } (pofiles($file));
+ } (pofiles($masterfile));
if (@pofiles) {
- refreshpofiles($file, @pofiles);
+ refreshpofiles($masterfile, @pofiles);
map { IkiWiki::rcs_add($_); } @pofiles if ($config{rcs});
$updated_po_files=1;
}
# Reinitialize module's private variables.
resetalreadyfiltered();
resettranslationscache();
+ flushmemoizecache();
# Trigger a wiki refresh.
require IkiWiki::Render;
+ # without preliminary saveindex/loadindex, refresh()
+ # complains about a lot of uninitialized variables
+ IkiWiki::saveindex();
+ IkiWiki::loadindex();
IkiWiki::refresh();
IkiWiki::saveindex();
}
# | Injected functions
# `----
-# Implement po_link_to=current
+# Implement po_link_to 'current' and 'negotiated' settings.
sub mybestlink ($$) { #{{{
my $page=shift;
my $link=shift;
- my $res=$origsubs{'bestlink'}->($page, $link);
+ my $res=$origsubs{'bestlink'}->(masterpage($page), $link);
if (length $res
- && $config{po_link_to} eq "current"
+ && ($config{po_link_to} eq "current" || $config{po_link_to} eq "negotiated")
&& istranslatable($res)
&& istranslation($page)) {
return $res . "." . lang($page);
my $res=$origsubs{'beautify_urlpath'}->($url);
if ($config{po_link_to} eq "negotiated") {
$res =~ s!/\Qindex.$config{po_master_language}{code}.$config{htmlext}\E$!/!;
+ $res =~ s!/\Qindex.$config{htmlext}\E$!/!;
+ map {
+ $res =~ s!/\Qindex.$_.$config{htmlext}\E$!/!;
+ } (keys %{$config{po_slave_languages}});
}
return $res;
} #}}}
# workaround hard-coded /index.$config{htmlext} in IkiWiki::urlto()
if (! length $to
&& $config{po_link_to} eq "current"
- && istranslation($from)
&& istranslatable('index')) {
return IkiWiki::beautify_urlpath(IkiWiki::baseurl($from) . "index." . lang($from) . ".$config{htmlext}");
}
} #}}}
}
+{
+ my %renamed;
+
+ sub renamed ($) { #{{{
+ my $page=shift;
+
+ if (exists $renamed{$page} &&
+ defined $renamed{$page}) {
+ return $renamed{$page};
+ }
+ return;
+ } #}}}
+
+ sub setrenamed ($$) { #{{{
+ my $oldpage=shift;
+ my $newpage=shift;
+
+ $renamed{$oldpage}=$newpage;
+ } #}}}
+
+ sub resetrenamed () { #{{{
+ undef %renamed;
+ } #}}}
+
+ sub eachrenamed () { #{{{
+ return sub { each %renamed };
+ } #}}}
+}
# ,----
# | Helper functions
# `----
-sub istranslatable ($) { #{{{
- my $page=shift;
+sub maybe_add_leading_slash ($;$) { #{{{
+ my $str=shift;
+ my $add=shift;
+ $add=1 unless defined $add;
+ return '/' . $str if $add;
+ return $str;
+} #}}}
- my $file=$pagesources{$page};
+sub istranslatablefile ($) { #{{{
+ my $file=shift;
return 0 unless defined $file;
return 0 if (defined pagetype($file) && pagetype($file) eq 'po');
return 0 if $file =~ /\.pot$/;
- return pagespec_match($page, $config{po_translatable_pages});
+ return 1 if pagespec_match(pagename($file), $config{po_translatable_pages});
+ return;
+} #}}}
+
+sub istranslatable ($) { #{{{
+ my $page=shift;
+
+ $page=~s#^/##;
+ return 1 if istranslatablefile($pagesources{$page});
+ return;
} #}}}
sub _istranslation ($) { #{{{
my $page=shift;
+ my $hasleadingslash = ($page=~s#^/##);
my $file=$pagesources{$page};
-
return 0 unless (defined $file
&& defined pagetype($file)
&& pagetype($file) eq 'po');
&& defined $pagesources{$masterpage}
&& defined $config{po_slave_languages}{$lang});
- return ($masterpage, $lang) if istranslatable($masterpage);
+ return (maybe_add_leading_slash($masterpage, $hasleadingslash), $lang)
+ if istranslatable($masterpage);
} #}}}
sub istranslation ($) { #{{{
my $page=shift;
if (1 < (my ($masterpage, $lang) = _istranslation($page))) {
+ my $hasleadingslash = ($masterpage=~s#^/##);
$translations{$masterpage}{$lang}=$page unless exists $translations{$masterpage}{$lang};
- return ($masterpage, $lang);
+ return (maybe_add_leading_slash($masterpage, $hasleadingslash), $lang);
}
return;
} #}}}
return ($code =~ /^[a-z]{2}$/);
} #}}}
-sub otherlanguages($) { #{{{
+sub otherlanguage ($$) { #{{{
+ my $page=shift;
+ my $code=shift;
+
+ return masterpage($page) if $code eq $config{po_master_language}{code};
+ return masterpage($page) . '.' . $code;
+} #}}}
+
+sub otherlanguages ($) { #{{{
my $page=shift;
my %ret;
- if (istranslatable($page)) {
- %ret = %{$translations{$page}};
- }
- elsif (istranslation($page)) {
- my $masterpage = masterpage($page);
- $ret{$config{po_master_language}{code}} = $masterpage;
- foreach my $lang (sort keys %{$translations{$masterpage}}) {
- next if $lang eq lang($page);
- $ret{$lang} = $translations{$masterpage}{$lang};
- }
+ return \%ret unless (istranslation($page) || istranslatable($page));
+ my $curlang=lang($page);
+ foreach my $lang
+ ($config{po_master_language}{code}, keys %{$config{po_slave_languages}}) {
+ next if $lang eq $curlang;
+ $ret{$lang}=otherlanguage($page, $lang);
}
return \%ret;
} #}}}
undef %translations;
} #}}}
+sub flushmemoizecache() { #{{{
+ Memoize::flush_cache("istranslatable");
+ Memoize::flush_cache("_istranslation");
+ Memoize::flush_cache("percenttranslated");
+} #}}}
+
sub urlto_with_orig_beautiful_urlpath($$) { #{{{
my $to=shift;
my $from=shift;
}
else {
push @ret, {
- url => urlto($otherpage, $page),
+ url => urlto_with_orig_beautiful_urlpath($otherpage, $page),
code => $lang,
language => languagename($lang),
percent => percenttranslated($otherpage),
} @ret;
} #}}}
+sub homepageurl (;$) { #{{{
+ my $page=shift;
+
+ return urlto('', $page);
+} #}}}
+
+# - do *not* implement this until the renamepage hook works
+# - do *not* delete translations of pages that were orphans
+# before being renamed (see renamepage hook comments above)
+sub deletetranslations ($) { #{{{
+ my $deletedmasterfile=shift;
+
+ debug "po(deletetranslations): TODO: delete translations of $deletedmasterfile";
+} #}}}
+
+sub renametranslations (@) { #{{{
+ my ($oldpage, $newpage)=shift;
+
+ debug "po(renametranslations): TODO: rename translations of $oldpage to $newpage";
+} #}}}
+
# ,----
# | PageSpec's