]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/plugin/_WikiTranslation.php
Add translation for TextFormattingRules
[SourceForge/phpwiki.git] / lib / plugin / _WikiTranslation.php
1 <?php
2
3 /*
4  * Copyright 2004,2005 $ThePhpWikiProgrammingTeam
5  *
6  * This file is part of PhpWiki.
7  *
8  * PhpWiki is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * PhpWiki is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with PhpWiki; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22
23 /**
24  * _WikiTranslation:  Display pagenames and other strings in various languages.
25  * Can also be used to let a favorite translation service translate a whole page.
26  * Current favorite: translate.google.com if from_lang = en or fr
27  *
28  * Examples:
29  *  <<_WikiTranslation page=HomePage languages=fr >>
30  *     Translation service for HomePage into french (redirect to translate.google.com)
31  *  <<_WikiTranslation what=pages >>
32  *     Translation matrix of all pages with proper translations (all in pgsrc)
33  *  <<_WikiTranslation what=wikiwords match="W*" limit=20 >>
34  *     Translation matrix of the first 20 wikiwords matching "W*"
35  *  <<_WikiTranslation string=HomePage languages=fr,de,sv >>
36  *     Translation matrix for all given languages
37  *  <<_WikiTranslation string=HomePage >>
38  *     Translation matrix for all supported languages
39  *  <<_WikiTranslation string=HomePage languages=fr >>
40  *     Just return the translated string for this language.
41  *
42  * @author:  Reini Urban
43  */
44
45 /* Container for untranslated pagenames. Needed to show up in locale/po/phpwiki.pot */
46 $pgsrc_container =
47     _("AddComment") . ',' .
48     _("AllPages") . ',' .
49     _("AllPagesCreatedByMe") . ',' .
50     _("AllPagesLastEditedByMe") . ',' .
51     _("AllPagesOwnedByMe") . ',' .
52     _("AllUsers") . ',' .
53     _("AnalyseAccessLogSql") . ',' .
54     _("AppendText") . ',' .
55     _("AsciiMath") . ',' .
56     _("AsciiSVG") . ',' .
57     _("AtomFeed") . ',' .
58     _("_AuthInfo") . ',' .
59     _("AuthorHistory") . ',' .
60     _("_BackendInfo") . ',' .
61     _("BackLinks") . ',' .
62     _("BlogArchives") . ',' .
63     _("BlogJournal") . ',' .
64     _("BoxRight") . ',' .
65     _("CacheTest") . ',' .
66     _("Calendar") . ',' .
67     _("CalendarList") . ',' .
68     _("CategoryCategory") . ',' .
69     _("CategoryHomePages") . ',' .
70     _("CategoryPage") . ',' .
71     _("Chart") . ',' .
72     _("Comment") . ',' .
73     _("CreateBib") . ',' .
74     _("CreatePage") . ',' .
75     _("CreateToc") . ',' .
76     _("CurrentTime") . ',' .
77     _("DeadEndPages") . ',' .
78     _("DebugInfo") . ',' .
79     _("Diff") . ',' .
80     _("DynamicIncludePage") . ',' .
81     _("EditMetaData") . ',' .
82     _("ExternalSearch") . ',' .
83     _("FacebookLike") . ',' .
84     _("FileInfo") . ',' .
85     _("FindPage") . ',' .
86     _("FoafViewer") . ',' .
87     _("FrameInclude") . ',' .
88     _("FullRecentChanges") . ',' .
89     _("FullTextSearch") . ',' .
90     _("FuzzyPages") . ',' .
91     _("GoogleMaps") . ',' .
92     _("GooglePlugin") . ',' .
93     _("GoTo") . ',' .
94     _("GraphViz") . ',' .
95     _("_GroupInfo") . ',' .
96     _("HelloWorld") . ',' .
97     _("Help/AddCommentPlugin") . ',' .
98     _("Help/AddingPages") . ',' .
99     _("Help/AuthorHistoryPlugin") . ',' .
100     _("Help/CalendarListPlugin") . ',' .
101     _("Help/CalendarPlugin") . ',' .
102     _("Help/CommentPlugin") . ',' .
103     _("Help/CreateTocPlugin") . ',' .
104     _("Help/EditMetaDataPlugin") . ',' .
105     _("Help/ExternalSearchPlugin") . ',' .
106     _("Help/FoafViewerPlugin") . ',' .
107     _("Help/FrameIncludePlugin") . ',' .
108     _("Help/HelloWorldPlugin") . ',' .
109     _("Help/IncludePagePlugin") . ',' .
110     _("Help/LinkIcons") . ',' .
111     _("Help/MagicPhpWikiURLs") . ',' .
112     _("Help/MoreAboutMechanics") . ',' .
113     _("Help/OldStyleTablePlugin") . ',' .
114     _("Help/PhotoAlbumPlugin") . ',' .
115     _("Help/PhpHighlightPlugin") . ',' .
116     _("Help/PhpWeatherPlugin") . ',' .
117     _("Help/PhpWiki") . ',' .
118     _("Help/PloticusPlugin") . ',' .
119     _("Help/RawHtmlPlugin") . ',' .
120     _("Help/RedirectToPlugin") . ',' .
121     _("Help/RichTablePlugin") . ',' .
122     _("Help/SystemInfoPlugin") . ',' .
123     _("Help/TranscludePlugin") . ',' .
124     _("Help/UnfoldSubpagesPlugin") . ',' .
125     _("Help/UpLoadPlugin") . ',' .
126     _("Help/WabiSabi") . ',' .
127     _("Help/WikiBlogPlugin") . ',' .
128     _("Help/WikiPlugin") . ',' .
129     _("Help/WikiWikiWeb") . ',' .
130     _("HomePageAlias") . ',' .
131     _("HtmlConverter") . ',' .
132     _("Imdb") . ',' .
133     _("IncludePage") . ',' .
134     _("IncludePages") . ',' .
135     _("IncludeSiteMap") . ',' .
136     _("IncludeTree") . ',' .
137     _("InterWiki") . ',' .
138     _("InterWikiSearch") . ',' .
139     _("JabberPresence") . ',' .
140     _("LdapSearch") . ',' .
141     _("LikePages") . ',' .
142     _("LinkDatabase") . ',' .
143     _("LinkSearch") . ',' .
144     _("ListPages") . ',' .
145     _("ListRelations") . ',' .
146     _("ListSubpages") . ',' .
147     _("MediawikiTable") . ',' .
148     _("ModeratedPage") . ',' .
149     _("MostPopular") . ',' .
150     _("NewPagesPerUser") . ',' .
151     _("NoCache") . ',' .
152     _("OldStyleTable") . ',' .
153     _("OrphanedPages") . ',' .
154     _("PageDump") . ',' .
155     _("PageGroup") . ',' .
156     _("PageHistory") . ',' .
157     _("PageInfo") . ',' .
158     _("PageTrail") . ',' .
159     _("PasswordReset") . ',' .
160     _("PhotoAlbum") . ',' .
161     _("PhpHighlight") . ',' .
162     _("PhpWeather") . ',' .
163     _("PhpWikiAdministration/Chmod") . ',' .
164     _("PhpWikiAdministration/Chown") . ',' .
165     _("PhpWikiAdministration/Remove") . ',' .
166     _("PhpWikiAdministration/Rename") . ',' .
167     _("PhpWikiAdministration/Replace") . ',' .
168     _("PhpWikiAdministration/SetAcl") . ',' .
169     _("PhpWikiDocumentation") . ',' .
170     _("PhpWikiPoll") . ',' .
171     _("Ploticus") . ',' .
172     _("PluginManager") . ',' .
173     _("PopularNearby") . ',' .
174     _("PopularTags") . ',' .
175     _("PopUp") . ',' .
176     _("PreferenceApp") . ',' .
177     _("_PreferencesInfo") . ',' .
178     _("PrevNext") . ',' .
179     _("Processing") . ',' .
180     _("RandomPage") . ',' .
181     _("RateIt") . ',' .
182     _("RawHtml") . ',' .
183     _("RecentChanges") . ',' .
184     _("RecentChangesCached") . ',' .
185     _("RecentComments") . ',' .
186     _("RecentEdits") . ',' .
187     _("RecentReferrers") . ',' .
188     _("RecentVisitors") . ',' .
189     _("RedirectTo") . ',' .
190     _("RelatedChanges") . ',' .
191     _("ReleaseNotes") . ',' .
192     _("_Retransform") . ',' .
193     _("RichTable") . ',' .
194     _("RssFeed") . ',' .
195     _("SearchHighlight") . ',' .
196     _("SemanticRelations") . ',' .
197     _("SemanticSearch") . ',' .
198     _("SemanticSearchAdvanced") . ',' .
199     _("SiteMap") . ',' .
200     _("SpellCheck") . ',' .
201     _("SqlResult") . ',' .
202     _("SyncWiki") . ',' .
203     _("SyntaxHighlighter") . ',' .
204     _("SystemInfo") . ',' .
205     _("Template") . ',' .
206     _("TeX2png") . ',' .
207     _("text2png") . ',' .
208     _("TextFormattingRules") . ',' .
209     _("TexToPng") . ',' .
210     _("TitleSearch") . ',' .
211     _("Transclude") . ',' .
212     _("TranslateText") . ',' .
213     _("UnfoldSubpages") . ',' .
214     _("UpLoad") . ',' .
215     _("UriResolver") . ',' .
216     _("UserPreferences") . ',' .
217     _("UserRatings") . ',' .
218     _("Video") . ',' .
219     _("VisualWiki") . ',' .
220     _("WantedPages") . ',' .
221     _("WantedPagesOld") . ',' .
222     _("WatchPage") . ',' .
223     _("WhoIsOnline") . ',' .
224     _("WikiAdminChmod") . ',' .
225     _("WikiAdminChown") . ',' .
226     _("WikiAdminDeleteAcl") . ',' .
227     _("WikiAdminMarkup") . ',' .
228     _("WikiAdminPurge") . ',' .
229     _("WikiAdminRemove") . ',' .
230     _("WikiAdminRename") . ',' .
231     _("WikiAdminSearchReplace") . ',' .
232     _("WikiAdminSelect") . ',' .
233     _("WikiAdminSetAcl") . ',' .
234     _("WikiAdminSetAclSimple") . ',' .
235     _("WikiAdminSetExternal") . ',' .
236     _("WikiAdminUtils") . ',' .
237     _("WikiBlog") . ',' .
238     _("WikicreoleTable") . ',' .
239     _("WikiForm") . ',' .
240     _("WikiFormRich") . ',' .
241     _("WikiForum") . ',' .
242     _("WikiPoll") . ',' .
243     _("_WikiTranslation") . ',' .
244     _("YouTube");
245
246 require_once 'lib/PageList.php';
247
248 class WikiPlugin__WikiTranslation
249     extends WikiPlugin
250 {
251
252     function getDescription()
253     {
254         return _("Show translations of various words or pages.");
255     }
256
257     function getDefaultArguments()
258     {
259         return array_merge
260         (
261             PageList::supportedArgs(),
262             array('languages' => '', // comma delimited string of de,en,sv,...
263                 'string' => '',
264                 'page' => '', // use a translation service
265                 'what' => 'pages', // or 'buttons', 'plugins' or 'wikiwords'
266
267                 'match' => '*',
268                 'from_lang' => false,
269                 'include_empty' => false,
270                 //'exclude'       => '',
271                 //'sortby'        => '',
272                 //'limit'         => 0,
273                 'nolinks' => false, // don't display any links
274                 // (for development only)
275                 'noT' => false // don't display the T link
276                 // (for development only)
277             ));
278     }
279
280     function init_locale($lang)
281     {
282         if ($lang != $this->lang)
283             update_locale($lang);
284         if ($lang == 'en') {
285             // Hack alert! we need hash for stepping through it, even if it's
286             // in the wrong language
287             include (FindFile("locale/de/LC_MESSAGES/phpwiki.php", 0, 'reinit'));
288             foreach ($locale as $en => $de) {
289                 $locale[$en] = $en;
290             }
291             // gettext module loaded: must load the LC_MESSAGES php hash
292         } elseif (function_exists('bindtextdomain')) {
293             include (FindFile("locale/$lang/LC_MESSAGES/phpwiki.php", 0, 'reinit'));
294             //include (FindLocalizedFile("LC_MESSAGES/phpwiki.php", 0,'reinit'));
295             // we already have a $locale, but maybe it's in the wrong language
296         } elseif ($lang != $this->lang or empty($GLOBALS['locale'])) {
297             include (FindFile("locale/$lang/LC_MESSAGES/phpwiki.php", 0, 'reinit'));
298         } else {
299             $locale = & $GLOBALS['locale'];
300         }
301         $this->_locales[$lang] = $locale;
302     }
303
304     // reverse translation:
305     function translate_to_en($text, $lang = false)
306     {
307         if (!$lang) $lang = $this->lang; // current locale
308         if ($lang == 'en') return $text;
309
310         $this->_locales = array();
311         $this->_reverse_locales = array();
312
313         if (!isset($this->_locales[$lang])) {
314             $this->init_locale($lang);
315         }
316         assert(!empty($this->_locales[$lang]));
317         if (!isset($this->_reverse_locales[$lang])) {
318             // and now do a reverse lookup in the $locale hash
319             $this->_reverse_locales[$lang] = array_flip($this->_locales[$lang]);
320         }
321         if (!empty($this->_reverse_locales[$lang][$text])) {
322             return $this->_reverse_locales[$lang][$text];
323         } else {
324             return $text;
325         }
326     }
327
328     /**
329      * setlocale() switching with the gettext extension is by far too slow.
330      * So use the hash regardless if gettext is loaded or not.
331      */
332     function fast_translate($text, $to_lang, $from_lang = false)
333     {
334         if (!$from_lang) $from_lang = $this->lang; // current locale
335         if ($from_lang == $to_lang) return $text;
336         // setup hash from en => to_lang
337         if (!isset($this->_locales[$to_lang]))
338             $this->init_locale($to_lang);
339         if ($from_lang != 'en') {
340             // get reverse gettext: translate to english
341             $text = $this->translate_to_en($text, $from_lang);
342         }
343         return !empty($this->_locales[$to_lang][$text])
344             ? $this->_locales[$to_lang][$text]
345             : $text;
346     }
347
348     //FIXME! There's something wrong.
349     function translate($text, $to_lang, $from_lang = false)
350     {
351         if (!$from_lang) $from_lang = $this->lang; // current locale
352         if ($from_lang == $to_lang) return $text;
353         // Speed up hash lookup. Not needed for gettext module
354         if (!isset($this->_locales[$from_lang]) and !function_exists('bindtextdomain')) {
355             $this->init_locale($from_lang);
356         }
357         if ($from_lang != 'en') {
358             // get reverse gettext: translate to english
359             $en = $this->translate_to_en($text, $from_lang);
360             // and then to target
361             update_locale($to_lang);
362             $result = gettext($en);
363             update_locale($from_lang);
364         } else {
365             // locale switching is very slow with the gettext extension.
366             // better use fast_translate
367             if ($from_lang != $to_lang) {
368                 update_locale($to_lang);
369             }
370             $result = gettext($text);
371             if ($from_lang != $to_lang) {
372                 update_locale($from_lang);
373             }
374         }
375         return $result;
376     }
377
378     function run($dbi, $argstr, &$request, $basepage)
379     {
380         $this->args = $this->getArgs($argstr, $request);
381         extract($this->args);
382         $this->request = &$request;
383         if (!$from_lang) $from_lang = $request->getPref('lang');
384         if (!$from_lang) $from_lang = $GLOBALS['LANG'];
385         $this->lang = $from_lang;
386
387         if (empty($languages)) {
388             $available_languages = listAvailableLanguages();
389             if ($from_lang == 'en') {
390                 // "en" is always the first.
391                 array_shift($available_languages);
392             }
393             // put from_lang to the very end.
394             if (in_array($from_lang, $available_languages))
395                 $languages = $available_languages;
396             else
397                 $languages = array_merge($available_languages, array($from_lang));
398         } elseif (strstr($languages, ',')) {
399             $languages = explode(',', $languages);
400         } else {
401             $languages = array($languages);
402         }
403         $to_lang = $languages[0];
404         if (!empty($string) and count($languages) == 1) {
405             return $this->translate($string, $to_lang, $from_lang);
406         }
407         if (!empty($page)) {
408             $pagename = $page;
409             if ($dbi->isWikiPage($pagename)) {
410                 $url = '';
411                 // google can only translate from english and french
412                 if (in_array($from_lang, array('en', 'fr'))) {
413                     $url = "http://translate.google.com/translate";
414                     $url .= "?langpair=" . urlencode($from_lang . "|" . $to_lang);
415                     $url .= "&u=" . urlencode(WikiURL($pagename, false, true));
416                 }
417                 // redirect or transclude?
418                 if ($url) {
419                     return $request->redirect($url);
420                 }
421                 return HTML(fmt("TODO: Google can only translate from english and french. Find a translation service for %s to language %s",
422                     WikiURL($pagename, false, true),
423                     $to_lang));
424             } else {
425                 return $this->error(fmt("%s is empty.", $pagename));
426             }
427         }
428
429         $pagelist = new PageList('', $exclude, $this->args);
430         $pagelist->_columns[0]->_heading = "$from_lang";
431         foreach ($languages as $lang) {
432             if ($lang == $from_lang) continue;
433             $field = "custom:$lang";
434             $pagelist->addColumnObject(
435                 new _PageList_Column_customlang($field, $from_lang, $this));
436         }
437         if (!empty($string)) {
438             $pagelist->addPage($string);
439             return $pagelist;
440         }
441         switch ($what) {
442             case 'allpages':
443                 $pagelist->addPages($dbi->getAllPages($include_empty, $sortby,
444                     $limit, $exclude));
445                 break;
446             case 'pages':
447                 // not all pages, only the pgsrc pages
448                 if (!is_array($exclude))
449                     $exclude = $pagelist->explodePageList($exclude, false, $sortby,
450                         $limit, $exclude);
451                 $path = FindLocalizedFile(WIKI_PGSRC);
452                 $pgsrc = new fileSet($path);
453                 foreach ($pgsrc->getFiles($exclude, $sortby, $limit) as $pagename) {
454                     $pagename = urldecode($pagename);
455                     if (substr($pagename, -1, 1) == '~') continue;
456                     if (in_array($pagename, $exclude))
457                         continue; // exclude page.
458                     if ($match != '*' and !glob_match($match, $pagename))
459                         continue;
460                     $page_handle = $dbi->getPage($pagename);
461                     $pagelist->addPage($page_handle);
462                 }
463                 break;
464             case 'wikiwords':
465                 if (!isset($this->_locales[$from_lang])) {
466                     $this->init_locale($from_lang);
467                 }
468                 $locale = & $this->_locales[$from_lang];
469                 if (is_array($locale)) {
470                     $count = 0;
471                     foreach ($locale as $from => $to) {
472                         if ($match != '*' and !glob_match($match, $from))
473                             continue;
474                         if (isWikiWord($from)) {
475                             $count++;
476                             $pagelist->addPage($from);
477                             if ($limit and $count > $limit) break;
478                         }
479                     }
480                 }
481                 break;
482             // all Button texts, which need a localized .png
483             // where to get them from? templates/*.tmpl: Button()
484             // and WikiLink(?,'button')
485             // navbar links, actionpages, and admin requests
486             case 'buttons':
487                 $buttons = $GLOBALS['AllActionPages'];
488                 $fileset = new FileSet(FindFile("themes/MacOSX/buttons/en"),
489                     "*.png");
490                 foreach ($fileset->getFiles() as $file) {
491                     $b = urldecode(substr($file, 0, -4));
492                     if (!in_array($b, $buttons))
493                         $buttons[] = $b;
494                 }
495                 $count = 0;
496                 foreach ($buttons as $button) {
497                     $pagelist->addPage($button);
498                     if ($limit and ++$count > $limit) break;
499                 }
500                 break;
501         }
502         return $pagelist;
503     }
504 }
505
506 class _PageList_Column_customlang extends _PageList_Column
507 {
508     function _PageList_Column_customlang($field, $from_lang, $plugin)
509     {
510         $this->_field = $field;
511         $this->_from_lang = $from_lang;
512         $this->_plugin =& $plugin;
513         $this->_what = $plugin->args['what'];
514         $this->_noT = $plugin->args['noT'];
515         $this->_nolinks = $plugin->args['nolinks'];
516         $this->_iscustom = substr($field, 0, 7) == 'custom:';
517         if ($this->_iscustom)
518             $this->_field = substr($field, 7);
519         //$heading = $field;
520         $this->dbi = &$GLOBALS['request']->getDbh();
521         $this->_PageList_Column_base($this->_field);
522     }
523
524     function _getValue($page, &$revision_handle)
525     {
526         if (is_object($page)) $text = $page->getName();
527         else $text = $page;
528         $trans = $this->_plugin->fast_translate($text, $this->_field,
529             $this->_from_lang);
530         // how to markup untranslated words and not existing pages?
531         // untranslated: (TODO) link to translation editor
532         if ($trans == $text or // untranslated
533             (($this->_from_lang != 'en') and
534                 ($this->_field != 'en') and
535                     ($trans == $this->_plugin->fast_translate($text, 'en',
536                         $this->_from_lang))
537             )
538         ) {
539             global $WikiTheme;
540             $link = $WikiTheme->linkUnknownWikiWord($trans);
541             if (!($this->_noT or $this->_nolinks)
542                 and $this->dbi->isWikiPage($trans)
543             ) {
544                 $url = WikiURL($trans, array('action' => 'TranslateText',
545                     'lang' => $this->_field));
546                 $button = $WikiTheme->makeButton('T', $url);
547                 $button->addTooltip(sprintf(_("Define the translation for %s in %s"),
548                     $trans, $this->_field));
549                 $link = HTML::span($button);
550                 $link->setAttr('class', 'wikiunknown');
551                 $text = HTML::span($WikiTheme->maybeSplitWikiWord($trans));
552                 $text->setAttr('style', 'text-decoration:line-through');
553                 $link->pushContent($text);
554                 return $link;
555             } elseif (is_object($page))
556                 return ''; else // not existing: empty
557                 return '';
558         } elseif (is_object($page)) {
559             if (!$this->_nolinks)
560                 return WikiLink($trans, 'auto');
561             else
562                 return $trans;
563         } else {
564             return $trans;
565         }
566     }
567 }
568
569 // Local Variables:
570 // mode: php
571 // tab-width: 8
572 // c-basic-offset: 4
573 // c-hanging-comment-ender-p: nil
574 // indent-tabs-mode: nil
575 // End: