]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/plugin/_WikiTranslation.php
add fast_translate: setlocale() switching with the gettext extension is by far too...
[SourceForge/phpwiki.git] / lib / plugin / _WikiTranslation.php
1 <?php // -*-php-*-
2 rcs_id('$Id: _WikiTranslation.php,v 1.15 2005-01-25 08:06:47 rurban Exp $');
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
19  along with PhpWiki; if not, write to the Free Software
20  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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  *  <?plugin _WikiTranslation page=HomePage languages=fr ?>
30  *     Translation service for HomePage into french (redirect to translate.google.com)
31  *  <?plugin _WikiTranslation what=pages ?>
32  *     Translation matrix of all pages with proper translations (all in pgsrc)
33  *  <?plugin _WikiTranslation what=wikiwords match="W*" limit=20 ?>
34  *     Translation matrix of the first 20 wikiwords matching "W*"
35  *  <?plugin _WikiTranslation string=HomePage languages=fr,de,sv ?>
36  *     Translation matrix for all given languages
37  *  <?plugin _WikiTranslation string=HomePage ?>
38  *     Translation matrix for all supported languages
39  *  <?plugin _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     _("AddCommentPlugin")  .','.
48     _("AddingPages")  .','.
49     _("AllPagesCreatedByMe")  .','.
50     _("AllPagesLastEditedByMe")  .','.
51     _("AllPagesOwnedByMe")  .','.
52     _("AuthorHistoryPlugin") .','.
53     _("BackLinks") .','.
54     _("CalendarListPlugin") .','.
55     _("CalendarPlugin") .','.
56     _("CategoryCategory")  .','.
57     _("CategoryHomePages")  .','.
58     _("CommentPlugin")  .','.
59     _("CreateTocPlugin")  .','.
60     _("DebugInfo") .','.
61     _("EditMetaData") .','.
62     _("EditMetaDataPlugin") .','.
63     _("ExternalSearchPlugin") .','.
64     _("FindPage") .','.
65     _("FoafViewerPlugin") .','.
66     _("FrameIncludePlugin") .','.
67     _("FullRecentChanges") .','.
68     _("HelloWorldPlugin") .','.
69     _("HomePageAlias") .','.
70     _("IncludePagePlugin") .','.
71     _("InterWiki") .','.
72     _("LinkIcons") .','.
73     _("MagicPhpWikiURLs") .','.
74     _("MoreAboutMechanics") .','.
75     _("NewMarkupTestPage") .','.
76     _("OldMarkupTestPage") .','.
77     _("OldStyleTablePlugin") .','.
78 //  _("PageDump") .','.
79     _("PageGroupTest") .','.
80     _("PageGroupTest/Four") .','.
81     _("PageGroupTest/One") .','.
82     _("PageGroupTest/Three") .','.
83     _("PageGroupTest/Two") .','.
84     _("PgsrcTranslation") .','.
85     _("PhotoAlbumPlugin") .','.
86     _("PhpHighlightPlugin") .','.
87     _("PhpWeatherPlugin") .','.
88     _("PhpWiki") .','.
89     _("PhpWikiAdministration/Chmod") .','.
90     _("PhpWikiAdministration/Chown") .','.
91     _("PhpWikiAdministration/Remove") .','.
92     _("PhpWikiAdministration/Rename") .','.
93     _("PhpWikiAdministration/Replace") .','.
94     _("PhpWikiAdministration/SetAcl") .','.
95     _("PhpWikiDocumentation") .','.
96     _("PhpWikiPoll") .','.
97     _("PloticusPlugin") .','.
98     _("PgrsrcTranslation") .','.
99     _("PgrsrcTranslation/de") .','.
100     _("PgrsrcTranslation/fr") .','.
101     _("PgrsrcTranslation/it") .','.
102     _("PgrsrcTranslation/es") .','.
103     _("PgrsrcTranslation/nl") .','.
104     _("PgrsrcTranslation/sv") .','.
105     _("PgrsrcTranslation/ja") .','.
106     _("PgrsrcTranslation/zh") .','.
107     _("RawHtmlPlugin") .','.
108     _("RecentVisitors") .','.
109     _("RedirectToPlugin") .','.
110     _("ReleaseNotes") .','.
111     _("RichTablePlugin") .','.
112 //    _("SpellCheck") .','.
113     _("SteveWainstead") .','.
114     _("SystemInfoPlugin") .','.
115     _("TranscludePlugin") .','.
116     _("TranslateText") .','.
117     _("UnfoldSubpagesPlugin") .','.
118     _("UpLoad") .','.
119     _("UpLoadPlugin") .','.
120     _("WabiSabi") .','.
121     _("WikiBlogPlugin") .','.
122     _("WikiPlugin") .','.
123     _("WikiWikiWeb");
124  
125 require_once('lib/PageList.php');
126
127 class WikiPlugin__WikiTranslation
128 extends WikiPlugin
129 {
130
131     function getName() {
132         return _("_WikiTranslation");
133     }
134
135     function getDescription() {
136         return _("Show translations of various words or pages");
137     }
138
139     function getVersion() {
140         return preg_replace("/[Revision: $]/", '',
141                             "\$Revision: 1.15 $");
142     }
143
144     function getDefaultArguments() {
145         return array_merge
146             (
147              PageList::supportedArgs(),
148              array( 'languages'  => '',  // comma delimited string of de,en,sv,...
149                     'string'     => '',  
150                     'page'       => '',  // use a translation service
151                     'what'       => 'pages', // or 'buttons', 'plugins' or 'wikiwords'
152
153                     'match'         => '*',
154                     'from_lang'     => false,
155                     'include_empty' => false,
156                     //'exclude'       => '',
157                     //'sortby'        => '',
158                     //'limit'         => 0,
159                     'nolinks'       => false,  // don't display any links 
160                                                // (for development only)
161                     'noT'           => false,  // don't display the T link 
162                                               // (for development only)
163                     'debug'         => false
164                     ));
165     }
166
167     function init_locale($lang) {
168         if ($lang != $this->lang)
169             update_locale($lang);
170         if ($lang == 'en') {
171             // Hack alert! we need hash for stepping through it, even if it's 
172             // in the wrong language
173             include (FindFile("locale/de/LC_MESSAGES/phpwiki.php", 0,'reinit'));
174             foreach ($locale as $en => $de) {
175                 $locale[$en] = $en;
176             }
177         // gettext module loaded: must load the LC_MESSAGES php hash
178         } elseif (function_exists ('bindtextdomain')) {
179             include (FindFile("locale/$lang/LC_MESSAGES/phpwiki.php", 0,'reinit'));
180             //include (FindLocalizedFile("LC_MESSAGES/phpwiki.php", 0,'reinit'));
181         // we already have a $locale, but maybe it's in the wrong language
182         } elseif ($lang != $this->lang or empty($GLOBALS['locale'])) {
183             include (FindFile("locale/$lang/LC_MESSAGES/phpwiki.php", 0,'reinit'));
184         } else {
185            $locale = & $GLOBALS['locale'];
186         }
187         $this->_locales[$lang] = $locale;
188     }
189
190     // reverse translation: 
191     function translate_to_en($text, $lang=false) {
192         if (!$lang) $lang = $this->lang; // current locale
193         if ($lang == 'en') return $text;
194
195         $this->_locales = array();
196         $this->_reverse_locales = array();
197
198         if (!isset($this->_locales[$lang])) {
199             $this->init_locale($lang);
200         }
201         assert(!empty($this->_locales[$lang]));
202         if (!isset($this->_reverse_locales[$lang])) {
203             // and now do a reverse lookup in the $locale hash
204             $this->_reverse_locales[$lang] = array_flip($this->_locales[$lang]);
205         }
206         if (!empty($this->_reverse_locales[$lang][$text])) {
207             return $this->_reverse_locales[$lang][$text];
208         } else {
209             return $text;
210         }
211     }
212     
213     /** 
214      * setlocale() switching with the gettext extension is by far too slow.
215      * So use the hash regardless if gettext is loaded or not.
216      */
217     function fast_translate($text, $to_lang, $from_lang=false) {
218         if (!$from_lang) $from_lang = $this->lang; // current locale
219         if ($from_lang == $to_lang) return $text;
220         // setup hash from en => to_lang
221         if (!isset($this->_locales[$to_lang]))
222             $this->init_locale($to_lang);
223         if ($from_lang != 'en') {
224             // get reverse gettext: translate to english
225             $text = $this->translate_to_en($text, $from_lang);
226         }
227         return !empty($this->_locales[$to_lang][$text]) 
228                  ? $this->_locales[$to_lang][$text] 
229                  : $text;
230     }
231
232     //FIXME! There's something wrong.
233     function translate($text, $to_lang, $from_lang=false) {
234         if (!$from_lang) $from_lang = $this->lang; // current locale
235         if ($from_lang == $to_lang) return $text;
236         // Speed up hash lookup. Not needed for gettext module
237         if (!isset($this->_locales[$from_lang]) and !function_exists('bindtextdomain')) {
238             $this->init_locale($from_lang);
239         }
240         if ($from_lang != 'en') {
241             // get reverse gettext: translate to english
242             $en = $this->translate_to_en($text, $from_lang);
243             // and then to target
244             update_locale($to_lang);
245             $result = gettext($en);
246             update_locale($from_lang);
247         } else {
248             // locale switching is very slow with the gettext extension.
249             // better use fast_translate
250             if ($from_lang != $to_lang) {
251                 update_locale($to_lang);
252             }
253             $result = gettext($text);
254             if ($from_lang != $to_lang) {
255                 update_locale($from_lang);
256             }
257         }
258         return $result;
259     }
260                 
261     function run($dbi, $argstr, &$request, $basepage) {
262         $this->args = $this->getArgs($argstr, $request);
263         extract($this->args);
264         $this->request = &$request;
265         if (!$from_lang) $from_lang = $request->getPref('lang');
266         if (!$from_lang) $from_lang = $GLOBALS['LANG'];
267         $this->lang = $from_lang;
268
269         if (empty($languages)) {
270             $available_languages = listAvailableLanguages();
271             if ($from_lang == 'en') {
272                 // "en" is always the first.
273                 array_shift($available_languages);
274             }
275             // put from_lang to the very end.
276             if (in_array($from_lang, $available_languages))
277                 $languages = $available_languages;
278             else
279                 $languages = array_merge($available_languages, array($from_lang));
280         } elseif (strstr($languages,',')) {
281             $languages = explode(',', $languages);
282         } else {
283             $languages = array($languages);
284         }
285         if (in_array('zh', $languages) or in_array('ja', $languages)) {
286                 
287             // If the current charset != utf-8 the text will not be displayed correctly.
288             // But here we cannot change the header anymore. So we can decide to ignore them, 
289             // or display them with all the errors.
290             //FIXME: do iconv the ob
291             if ($GLOBALS['charset'] != 'utf-8') {
292                 define('NEED_ICONV_TO', 'utf-8');
293                 //either the extension or external 
294                 //$GLOBALS['charset'] = 'utf-8';
295             }
296         }
297         $to_lang = $languages[0];
298         if (!empty($string) and count($languages)==1) {
299             return $this->translate($string, $to_lang, $from_lang);
300         }
301         if (!empty($page)) {
302             $pagename = $page;
303             if ($dbi->isWikiPage($pagename)) {
304                 $url = '';
305                 // google can only translate from english and french
306                 if (in_array($from_lang, array('en', 'fr'))) {
307                     $url = "http://translate.google.com/translate";
308                     $url .= "?langpair=" . urlencode($from_lang."|".$to_lang);
309                     $url .= "&u=" . urlencode(WikiURL($pagename, false, true));
310                 }
311                 // redirect or transclude?
312                 if ($url) {
313                     return $request->redirect($url);
314                 }
315                 return HTML(fmt("TODO: Google can only translate from english and french. Find a translation service for %s to language %s",
316                                 WikiURL($pagename, false, true),
317                                 $to_lang));
318             } else {
319                 return $this->error(fmt("%s is empty",$pagename));
320             }
321         }
322         
323         $pagelist = new PageList('', $exclude, $this->args);
324         $pagelist->_columns[0]->_heading = "$from_lang";
325         foreach ($languages as $lang) {
326             if ($lang == $from_lang) continue;
327             $field = "custom:$lang";
328             $pagelist->addColumnObject (
329               new _PageList_Column_customlang($field, $from_lang, $this));
330         }
331         if (!empty($string)) {
332             $pagelist->addPage( $string );
333             return $pagelist;
334         }
335         switch ($what) {
336         case 'allpages':
337             $pagelist->addPages( $dbi->getAllPages($include_empty, $sortby, 
338                                                    $limit, $exclude) );
339             break;
340         case 'pages':
341             // not all pages, only the pgsrc pages
342             if (!is_array($exclude))
343                 $exclude = $pagelist->explodePageList($exclude, false, $sortby, 
344                                                       $limit, $exclude);
345             $path = FindLocalizedFile(WIKI_PGSRC);
346             $pgsrc = new fileSet($path);
347             foreach ($pgsrc->getFiles($exclude, $sortby, $limit) as $pagename) {
348                 $pagename = urldecode($pagename);
349                 if (substr($pagename,-1,1) == '~') continue;
350                 if (in_array($pagename, $exclude))
351                     continue;             // exclude page.
352                 if ($match != '*' and !glob_match($match, $pagename))
353                     continue;
354                 $page_handle = $dbi->getPage($pagename);
355                 $pagelist->addPage( $page_handle );
356             }
357             break;
358         case 'wikiwords':
359             if (!isset($this->_locales[$from_lang])) {
360                 $this->init_locale($from_lang);
361             }
362             $locale = & $this->_locales[$from_lang];
363             if (is_array($locale)) {
364                 $count = 0;
365                 foreach ($locale as $from => $to) {
366                     if ($match != '*' and !glob_match($match, $from))
367                         continue;
368                     if (isWikiWord($from)) {
369                         $count++;
370                         $pagelist->addPage( $from );
371                         if ($limit and $count > $limit) break;
372                     }
373                 }
374             }
375             break;
376         // all Button texts, which need a localized .png
377         // where to get them from? templates/*.tmpl: Button() 
378         // and WikiLink(?,'button')
379         // navbar links, actionpages, and admin requests
380         case 'buttons':
381             $buttons = $GLOBALS['AllActionPages'];
382             $fileset = new FileSet(FindFile("themes/MacOSX/buttons/en"), 
383                                    "*.png");
384             foreach ($fileset->getFiles() as $file) {
385                 $b = urldecode(substr($file, 0, -4));
386                 if (!in_array($b,$buttons))
387                     $buttons[] = $b;
388             }
389             $count = 0;
390             foreach ($buttons as $button) {
391                 $pagelist->addPage( $button );
392                 if ($limit and ++$count > $limit) break;
393             }
394             break;
395         }
396         return $pagelist;
397     }
398 };
399
400 class _PageList_Column_customlang extends _PageList_Column {
401     function _PageList_Column_customlang($field, $from_lang, $plugin) {
402         $this->_field = $field;
403         $this->_from_lang = $from_lang;
404         $this->_plugin =& $plugin;
405         $this->_what = $plugin->args['what'];
406         $this->_noT = $plugin->args['noT'];
407         $this->_nolinks = $plugin->args['nolinks'];
408         $this->_iscustom = substr($field, 0, 7) == 'custom:';
409         if ($this->_iscustom)
410             $this->_field = substr($field, 7);
411         //$heading = $field;
412         $this->dbi = &$GLOBALS['request']->getDbh();
413         $this->_PageList_Column_base($this->_field);
414     }
415     
416     function _getValue($page, &$revision_handle) {
417         if (is_object($page)) $text = $page->getName();
418         else $text = $page;
419         $trans = $this->_plugin->fast_translate($text, $this->_field, 
420                                                 $this->_from_lang);
421         // how to markup untranslated words and not existing pages?
422         // untranslated: (TODO) link to translation editor
423         if ($trans == $text or // untranslated
424             (($this->_from_lang != 'en') and 
425              ($this->_field != 'en') and
426              ($trans == $this->_plugin->fast_translate($text, 'en', 
427                                                        $this->_from_lang))
428              ))
429         {    
430             global $WikiTheme;
431             $link = $WikiTheme->linkUnknownWikiWord($trans);
432             if (!($this->_noT or $this->_nolinks) 
433                 and $this->dbi->isWikiPage($trans)) 
434             {
435                 $url = WikiURL($trans, array('action' => 'TranslateText',
436                                              'lang' => $this->_field));
437                 $button = $WikiTheme->makeButton('T', $url);
438                 $button->addTooltip(sprintf(_("Define the translation for %s in %s"), 
439                                             $trans, $this->_field));
440                 $link = HTML::span($button);
441                 $link->setAttr('class', 'wikiunknown');
442                 $text = HTML::span($WikiTheme->maybeSplitWikiWord($trans));
443                 $text->setAttr('style', 'text-decoration:line-through');
444                 $link->pushContent($text);
445                 return $link;
446             } elseif (is_object($page))
447                 return '';
448             else                        // not existing: empty
449                 return '';
450         } elseif (is_object($page)) {
451             if (!$this->_nolinks)
452                 return WikiLink($trans, 'auto');
453             else
454                 return $trans;
455         } else {
456             return $trans;
457         }
458     }
459 }
460
461 // $Log: not supported by cvs2svn $
462 // Revision 1.14  2004/07/08 20:30:07  rurban
463 // plugin->run consistency: request as reference, added basepage.
464 // encountered strange bug in AllPages (and the test) which destroys ->_dbi
465 //
466 // Revision 1.13  2004/06/18 14:38:22  rurban
467 // adopt new PageList style
468 //
469 // Revision 1.12  2004/06/17 10:39:18  rurban
470 // fix reverse translation of possible actionpage
471 //
472 // Revision 1.11  2004/06/14 11:31:39  rurban
473 // renamed global $Theme to $WikiTheme (gforge nameclash)
474 // inherit PageList default options from PageList
475 //   default sortby=pagename
476 // use options in PageList_Selectable (limit, sortby, ...)
477 // added action revert, with button at action=diff
478 // added option regex to WikiAdminSearchReplace
479 //
480 // Revision 1.10  2004/05/03 21:57:47  rurban
481 // locale updates: we previously lost some words because of wrong strings in
482 //   PhotoAlbum, german rewording.
483 // fixed $_SESSION registering (lost session vars, esp. prefs)
484 // fixed ending slash in listAvailableLanguages/Themes
485 //
486 // Revision 1.9  2004/05/03 20:44:58  rurban
487 // fixed gettext strings
488 // new SqlResult plugin
489 // _WikiTranslation: fixed init_locale
490 //
491 // Revision 1.8  2004/05/02 21:26:38  rurban
492 // limit user session data (HomePageHandle and auth_dbi have to invalidated anyway)
493 //   because they will not survive db sessions, if too large.
494 // extended action=upgrade
495 // some WikiTranslation button work
496 // revert WIKIAUTH_UNOBTAINABLE (need it for main.php)
497 // some temp. session debug statements
498 //
499 // Revision 1.7  2004/05/02 15:10:08  rurban
500 // new finally reliable way to detect if /index.php is called directly
501 //   and if to include lib/main.php
502 // new global AllActionPages
503 // SetupWiki now loads all mandatory pages: HOME_PAGE, action pages, and warns if not.
504 // WikiTranslation what=buttons for Carsten to create the missing MacOSX buttons
505 // PageGroupTestOne => subpages
506 // renamed PhpWikiRss to PhpWikiRecentChanges
507 // more docs, default configs, ...
508 //
509 // Revision 1.6  2004/04/21 04:29:50  rurban
510 // write WikiURL consistently (not WikiUrl)
511 //
512 // Revision 1.5  2004/03/17 15:38:03  rurban
513 // more translations
514 //
515 // Revision 1.4  2004/03/17 13:20:31  rurban
516 // Placeholder for all yet untranslated pgsrc pagenames. Add german translations of these.
517 //
518 // Revision 1.3  2004/03/16 20:22:32  rurban
519 // added link to TranslateText action
520 //
521 // Revision 1.2  2004/03/16 15:47:27  rurban
522 // added match, fixed reverse translation, added page=, what=allpages, what=wikiwords, fixed what=pages, simplified _PageList_Column_custom
523 //
524 // Revision 1.1  2004/03/14 16:45:10  rurban
525 // Just the page matrix for now.
526 // doesn't work yet, if the default langauge != en
527 //
528 //
529
530 // For emacs users
531 // Local Variables:
532 // mode: php
533 // tab-width: 8
534 // c-basic-offset: 4
535 // c-hanging-comment-ender-p: nil
536 // indent-tabs-mode: nil
537 // End:
538 ?>