summaryrefslogtreecommitdiff
path: root/IkiWiki/Plugin/po.pm
blob: fea4ca6ebc6578c5b6ba3979ab2ecdea7de1f12e (plain)
  1. #!/usr/bin/perl
  2. # .po as a wiki page type
  3. # inspired by the GPL'd po4a-translate,
  4. # which is Copyright 2002, 2003, 2004 by Martin Quinson (mquinson#debian.org)
  5. package IkiWiki::Plugin::po;
  6. use warnings;
  7. use strict;
  8. use IkiWiki 2.00;
  9. use Encode;
  10. use Locale::Po4a::Chooser;
  11. use File::Temp;
  12. sub import {
  13. hook(type => "getsetup", id => "po", call => \&getsetup);
  14. hook(type => "checkconfig", id => "po", call => \&checkconfig);
  15. hook(type => "targetpage", id => "po", call => \&targetpage);
  16. hook(type => "filter", id => "po", call => \&filter);
  17. hook(type => "preprocess", id => "translatable", call => \&preprocess_translatable);
  18. hook(type => "htmlize", id => "po", call => \&htmlize);
  19. }
  20. sub getsetup () { #{{{
  21. return
  22. plugin => {
  23. safe => 0,
  24. rebuild => 1, # format plugin
  25. },
  26. po_master_language => {
  27. type => "string",
  28. example => {
  29. 'code' => 'en',
  30. 'name' => 'English'
  31. },
  32. description => "master language (non-PO files)",
  33. safe => 1,
  34. rebuild => 1,
  35. },
  36. po_slave_languages => {
  37. type => "string",
  38. example => {'fr' => { 'name' => 'Français' },
  39. 'es' => { 'name' => 'Castellano' },
  40. 'de' => { 'name' => 'Deutsch' },
  41. },
  42. description => "slave languages (PO files)",
  43. safe => 1,
  44. rebuild => 1,
  45. },
  46. po_link_to_current_language => {
  47. type => "boolean",
  48. example => 1,
  49. description => "internal links point to pages in the current language (useful if Content Negotiation is not supported)",
  50. safe => 1,
  51. rebuild => 1,
  52. },
  53. } #}}}
  54. sub checkconfig () { #{{{
  55. foreach my $field (qw{po_master_language po_slave_languages}) {
  56. if (! exists $config{$field} || ! defined $config{$field}) {
  57. error(sprintf(gettext("Must specify %s"), $field));
  58. }
  59. }
  60. if (! exists $config{po_link_to_current_language} ||
  61. ! defined $config{po_link_to_current_language}) {
  62. $config{po_link_to_current_language}=0;
  63. }
  64. } #}}}
  65. sub targetpage (@) { #{{{
  66. my %params = @_;
  67. my $page=$params{page};
  68. my $ext=$params{ext};
  69. if (pagespec_match($page,"istranslation()")) {
  70. my ($masterpage, $lang) = ($page =~ /(.*)[.]([a-z]{2})$/);
  71. if (! $config{usedirs} || $page eq 'index') {
  72. return $masterpage . "." . $lang . "." . $ext;
  73. }
  74. else {
  75. return $masterpage . "/index." . $lang . "." . $ext;
  76. }
  77. }
  78. else {
  79. if (! $config{usedirs} || $page eq 'index') {
  80. return $page . "." . $config{po_master_language}{code} . "." . $ext;
  81. }
  82. else {
  83. return $page . "/index." . $config{po_master_language}{code} . "." . $ext;
  84. }
  85. }
  86. } #}}}
  87. # We use filter to convert PO to the master page's type,
  88. # since other plugins should not work on PO files
  89. sub filter (@) { #{{{
  90. my %params = @_;
  91. my $page = $params{page};
  92. my $content = decode_utf8(encode_utf8($params{content}));
  93. # decide if this is a PO file that should be converted into a translated document,
  94. # and perform various sanity checks
  95. if (! pagespec_match($page, "istranslation()")) {
  96. return $content;
  97. }
  98. my ($masterpage, $lang) = ($page =~ /(.*)[.]([a-z]{2})$/);
  99. my $file=srcfile(exists $params{file} ? $params{file} : $IkiWiki::pagesources{$page});
  100. my $masterfile = srcfile($pagesources{$masterpage});
  101. my (@pos,@masters);
  102. push @pos,$file;
  103. push @masters,$masterfile;
  104. my %options = (
  105. "markdown" => (pagetype($masterfile) eq 'mdwn') ? 1 : 0,
  106. );
  107. my $doc=Locale::Po4a::Chooser::new('text',%options);
  108. $doc->process(
  109. 'po_in_name' => \@pos,
  110. 'file_in_name' => \@masters,
  111. 'file_in_charset' => 'utf-8',
  112. 'file_out_charset' => 'utf-8',
  113. ) or error("[po/filter:$file]: failed to translate");
  114. my ($percent,$hit,$queries) = $doc->stats();
  115. my $tmpfh = File::Temp->new(TEMPLATE => "/tmp/ikiwiki-po-filter-out.XXXXXXXXXX");
  116. my $tmpout = $tmpfh->filename;
  117. $doc->write($tmpout) or error("[po/filter:$file] could not write $tmpout");
  118. $content = readfile($tmpout) or error("[po/filter:$file] could not read $tmpout");
  119. return $content;
  120. } #}}}
  121. sub preprocess_translatable (@) { #{{{
  122. my %params = @_;
  123. my $match = exists $params{match} ? $params{match} : $params{page};
  124. $pagestate{$params{page}}{po_translatable}{$match}=1;
  125. return "" if ($params{silent} && IkiWiki::yesno($params{silent}));
  126. return sprintf(gettext("pages %s set as translatable"), $params{match});
  127. } #}}}
  128. sub htmlize (@) { #{{{
  129. my %params=@_;
  130. my $page = $params{page};
  131. my $content = $params{content};
  132. my ($masterpage, $lang) = ($page =~ /(.*)[.]([a-z]{2})$/);
  133. my $masterfile = srcfile($pagesources{$masterpage});
  134. # force content to be htmlize'd as if it was the same type as the master page
  135. return IkiWiki::htmlize($page, $page, pagetype($masterfile), $content);
  136. } #}}}
  137. package IkiWiki::PageSpec;
  138. sub match_istranslation ($;@) { #{{{
  139. my $page=shift;
  140. my $wanted=shift;
  141. my %params=@_;
  142. my $file=exists $params{file} ? $params{file} : $IkiWiki::pagesources{$page};
  143. if (! defined $file) {
  144. return IkiWiki::FailReason->new("no file specified");
  145. }
  146. if (! IkiWiki::pagetype($page) eq 'po') {
  147. return IkiWiki::FailReason->new("is not a PO file");
  148. }
  149. my ($masterpage, $lang) = ($page =~ /(.*)[.]([a-z]{2})$/);
  150. if (! defined $masterpage || ! defined $lang
  151. || ! (length($masterpage) > 0) || ! (length($lang) > 0)) {
  152. return IkiWiki::FailReason->new("is not named like a translation file");
  153. }
  154. if (! defined $IkiWiki::pagesources{$masterpage}) {
  155. return IkiWiki::FailReason->new("the master page does not exist");
  156. }
  157. if (! defined $IkiWiki::config{po_slave_languages}{$lang}) {
  158. return IkiWiki::FailReason->new("language $lang is not supported");
  159. }
  160. return IkiWiki::SuccessReason->new("page $page is a translation");
  161. } #}}}
  162. 1