]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/plugin/_WikiTranslation.php
rcs_id no longer makes sense with Subversion global version number
[SourceForge/phpwiki.git] / lib / plugin / _WikiTranslation.php
1 <?php // -*-php-*-
2 // rcs_id('$Id$');
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     _("PgsrcTranslation") .','.
99     _("PgsrcTranslation/de") .','.
100     _("PgsrcTranslation/fr") .','.
101     _("PgsrcTranslation/it") .','.
102     _("PgsrcTranslation/es") .','.
103     _("PgsrcTranslation/nl") .','.
104     _("PgsrcTranslation/sv") .','.
105     _("PgsrcTranslation/ja") .','.
106     _("PgsrcTranslation/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 getDefaultArguments() {
140         return array_merge
141             (
142              PageList::supportedArgs(),
143              array( 'languages'  => '',  // comma delimited string of de,en,sv,...
144                     'string'     => '',
145                     'page'       => '',  // use a translation service
146                     'what'       => 'pages', // or 'buttons', 'plugins' or 'wikiwords'
147
148                     'match'         => '*',
149                     'from_lang'     => false,
150                     'include_empty' => false,
151                     //'exclude'       => '',
152                     //'sortby'        => '',
153                     //'limit'         => 0,
154                     'nolinks'       => false,  // don't display any links
155                                                    // (for development only)
156                     'noT'           => false   // don't display the T link
157                                                   // (for development only)
158                     ));
159     }
160
161     function init_locale($lang) {
162         if ($lang != $this->lang)
163             update_locale($lang);
164         if ($lang == 'en') {
165             // Hack alert! we need hash for stepping through it, even if it's
166             // in the wrong language
167             include (FindFile("locale/de/LC_MESSAGES/phpwiki.php", 0,'reinit'));
168             foreach ($locale as $en => $de) {
169                     $locale[$en] = $en;
170             }
171         // gettext module loaded: must load the LC_MESSAGES php hash
172         } elseif (function_exists ('bindtextdomain')) {
173             include (FindFile("locale/$lang/LC_MESSAGES/phpwiki.php", 0,'reinit'));
174             //include (FindLocalizedFile("LC_MESSAGES/phpwiki.php", 0,'reinit'));
175         // we already have a $locale, but maybe it's in the wrong language
176         } elseif ($lang != $this->lang or empty($GLOBALS['locale'])) {
177             include (FindFile("locale/$lang/LC_MESSAGES/phpwiki.php", 0,'reinit'));
178         } else {
179            $locale = & $GLOBALS['locale'];
180         }
181         $this->_locales[$lang] = $locale;
182     }
183
184     // reverse translation:
185     function translate_to_en($text, $lang=false) {
186         if (!$lang) $lang = $this->lang; // current locale
187         if ($lang == 'en') return $text;
188
189         $this->_locales = array();
190         $this->_reverse_locales = array();
191
192         if (!isset($this->_locales[$lang])) {
193             $this->init_locale($lang);
194         }
195         assert(!empty($this->_locales[$lang]));
196         if (!isset($this->_reverse_locales[$lang])) {
197             // and now do a reverse lookup in the $locale hash
198             $this->_reverse_locales[$lang] = array_flip($this->_locales[$lang]);
199         }
200         if (!empty($this->_reverse_locales[$lang][$text])) {
201             return $this->_reverse_locales[$lang][$text];
202         } else {
203             return $text;
204         }
205     }
206
207     /**
208      * setlocale() switching with the gettext extension is by far too slow.
209      * So use the hash regardless if gettext is loaded or not.
210      */
211     function fast_translate($text, $to_lang, $from_lang=false) {
212         if (!$from_lang) $from_lang = $this->lang; // current locale
213         if ($from_lang == $to_lang) return $text;
214         // setup hash from en => to_lang
215         if (!isset($this->_locales[$to_lang]))
216             $this->init_locale($to_lang);
217         if ($from_lang != 'en') {
218             // get reverse gettext: translate to english
219             $text = $this->translate_to_en($text, $from_lang);
220         }
221         return !empty($this->_locales[$to_lang][$text])
222                  ? $this->_locales[$to_lang][$text]
223                  : $text;
224     }
225
226     //FIXME! There's something wrong.
227     function translate($text, $to_lang, $from_lang=false) {
228         if (!$from_lang) $from_lang = $this->lang; // current locale
229         if ($from_lang == $to_lang) return $text;
230         // Speed up hash lookup. Not needed for gettext module
231         if (!isset($this->_locales[$from_lang]) and !function_exists('bindtextdomain')) {
232             $this->init_locale($from_lang);
233         }
234         if ($from_lang != 'en') {
235             // get reverse gettext: translate to english
236             $en = $this->translate_to_en($text, $from_lang);
237             // and then to target
238             update_locale($to_lang);
239             $result = gettext($en);
240             update_locale($from_lang);
241         } else {
242             // locale switching is very slow with the gettext extension.
243             // better use fast_translate
244             if ($from_lang != $to_lang) {
245                 update_locale($to_lang);
246             }
247             $result = gettext($text);
248             if ($from_lang != $to_lang) {
249                 update_locale($from_lang);
250             }
251         }
252         return $result;
253     }
254
255     function run($dbi, $argstr, &$request, $basepage) {
256         $this->args = $this->getArgs($argstr, $request);
257         extract($this->args);
258         $this->request = &$request;
259         if (!$from_lang) $from_lang = $request->getPref('lang');
260         if (!$from_lang) $from_lang = $GLOBALS['LANG'];
261         $this->lang = $from_lang;
262
263         if (empty($languages)) {
264             $available_languages = listAvailableLanguages();
265             if ($from_lang == 'en') {
266                 // "en" is always the first.
267                 array_shift($available_languages);
268             }
269             // put from_lang to the very end.
270             if (in_array($from_lang, $available_languages))
271                 $languages = $available_languages;
272             else
273                     $languages = array_merge($available_languages, array($from_lang));
274         } elseif (strstr($languages,',')) {
275             $languages = explode(',', $languages);
276         } else {
277             $languages = array($languages);
278         }
279         if (in_array('zh', $languages) or in_array('ja', $languages)) {
280
281             // If the current charset != utf-8 the text will not be displayed correctly.
282             // But here we cannot change the header anymore. So we can decide to ignore them,
283             // or display them with all the errors.
284             //FIXME: do iconv the ob
285             if ($GLOBALS['charset'] != 'utf-8' and !defined('NEED_ICONV_TO')) {
286                     define('NEED_ICONV_TO', 'utf-8');
287                     //either the extension or external
288                 //$GLOBALS['charset'] = 'utf-8';
289             }
290         }
291         $to_lang = $languages[0];
292         if (!empty($string) and count($languages)==1) {
293             return $this->translate($string, $to_lang, $from_lang);
294         }
295         if (!empty($page)) {
296             $pagename = $page;
297             if ($dbi->isWikiPage($pagename)) {
298                     $url = '';
299                     // google can only translate from english and french
300                     if (in_array($from_lang, array('en', 'fr'))) {
301                     $url = "http://translate.google.com/translate";
302                     $url .= "?langpair=" . urlencode($from_lang."|".$to_lang);
303                     $url .= "&u=" . urlencode(WikiURL($pagename, false, true));
304                     }
305                     // redirect or transclude?
306                 if ($url) {
307                     return $request->redirect($url);
308                 }
309                     return HTML(fmt("TODO: Google can only translate from english and french. Find a translation service for %s to language %s",
310                                     WikiURL($pagename, false, true),
311                                     $to_lang));
312             } else {
313                 return $this->error(fmt("%s is empty",$pagename));
314             }
315         }
316
317         $pagelist = new PageList('', $exclude, $this->args);
318         $pagelist->_columns[0]->_heading = "$from_lang";
319         foreach ($languages as $lang) {
320             if ($lang == $from_lang) continue;
321             $field = "custom:$lang";
322             $pagelist->addColumnObject (
323               new _PageList_Column_customlang($field, $from_lang, $this));
324         }
325         if (!empty($string)) {
326             $pagelist->addPage( $string );
327             return $pagelist;
328         }
329         switch ($what) {
330         case 'allpages':
331             $pagelist->addPages( $dbi->getAllPages($include_empty, $sortby,
332                                                    $limit, $exclude) );
333             break;
334         case 'pages':
335             // not all pages, only the pgsrc pages
336             if (!is_array($exclude))
337                 $exclude = $pagelist->explodePageList($exclude, false, $sortby,
338                                                       $limit, $exclude);
339             $path = FindLocalizedFile(WIKI_PGSRC);
340             $pgsrc = new fileSet($path);
341             foreach ($pgsrc->getFiles($exclude, $sortby, $limit) as $pagename) {
342                 $pagename = urldecode($pagename);
343                 if (substr($pagename,-1,1) == '~') continue;
344                 if (in_array($pagename, $exclude))
345                     continue;             // exclude page.
346                 if ($match != '*' and !glob_match($match, $pagename))
347                     continue;
348                 $page_handle = $dbi->getPage($pagename);
349                 $pagelist->addPage( $page_handle );
350             }
351             break;
352         case 'wikiwords':
353             if (!isset($this->_locales[$from_lang])) {
354                 $this->init_locale($from_lang);
355             }
356             $locale = & $this->_locales[$from_lang];
357             if (is_array($locale)) {
358                 $count = 0;
359                 foreach ($locale as $from => $to) {
360                     if ($match != '*' and !glob_match($match, $from))
361                         continue;
362                     if (isWikiWord($from)) {
363                         $count++;
364                         $pagelist->addPage( $from );
365                         if ($limit and $count > $limit) break;
366                     }
367                 }
368             }
369             break;
370         // all Button texts, which need a localized .png
371         // where to get them from? templates/*.tmpl: Button()
372         // and WikiLink(?,'button')
373         // navbar links, actionpages, and admin requests
374         case 'buttons':
375             $buttons = $GLOBALS['AllActionPages'];
376             $fileset = new FileSet(FindFile("themes/MacOSX/buttons/en"),
377                                    "*.png");
378             foreach ($fileset->getFiles() as $file) {
379                 $b = urldecode(substr($file, 0, -4));
380                 if (!in_array($b,$buttons))
381                     $buttons[] = $b;
382             }
383             $count = 0;
384             foreach ($buttons as $button) {
385                 $pagelist->addPage( $button );
386                 if ($limit and ++$count > $limit) break;
387             }
388             break;
389         }
390         return $pagelist;
391     }
392 };
393
394 class _PageList_Column_customlang extends _PageList_Column {
395     function _PageList_Column_customlang($field, $from_lang, $plugin) {
396         $this->_field = $field;
397         $this->_from_lang = $from_lang;
398         $this->_plugin =& $plugin;
399         $this->_what = $plugin->args['what'];
400         $this->_noT = $plugin->args['noT'];
401         $this->_nolinks = $plugin->args['nolinks'];
402         $this->_iscustom = substr($field, 0, 7) == 'custom:';
403         if ($this->_iscustom)
404             $this->_field = substr($field, 7);
405         //$heading = $field;
406         $this->dbi = &$GLOBALS['request']->getDbh();
407         $this->_PageList_Column_base($this->_field);
408     }
409
410     function _getValue($page, &$revision_handle) {
411             if (is_object($page)) $text = $page->getName();
412         else $text = $page;
413             $trans = $this->_plugin->fast_translate($text, $this->_field,
414                                                 $this->_from_lang);
415         // how to markup untranslated words and not existing pages?
416         // untranslated: (TODO) link to translation editor
417         if ($trans == $text or // untranslated
418             (($this->_from_lang != 'en') and
419              ($this->_field != 'en') and
420              ($trans == $this->_plugin->fast_translate($text, 'en',
421                                                        $this->_from_lang))
422              ))
423         {
424             global $WikiTheme;
425             $link = $WikiTheme->linkUnknownWikiWord($trans);
426             if (!($this->_noT or $this->_nolinks)
427                 and $this->dbi->isWikiPage($trans))
428             {
429                 $url = WikiURL($trans, array('action' => 'TranslateText',
430                                              'lang' => $this->_field));
431                 $button = $WikiTheme->makeButton('T', $url);
432                 $button->addTooltip(sprintf(_("Define the translation for %s in %s"),
433                                             $trans, $this->_field));
434                 $link = HTML::span($button);
435                 $link->setAttr('class', 'wikiunknown');
436                 $text = HTML::span($WikiTheme->maybeSplitWikiWord($trans));
437                 $text->setAttr('style', 'text-decoration:line-through');
438                 $link->pushContent($text);
439                 return $link;
440             } elseif (is_object($page))
441                 return '';
442             else                        // not existing: empty
443                 return '';
444         } elseif (is_object($page)) {
445             if (!$this->_nolinks)
446                 return WikiLink($trans, 'auto');
447             else
448                 return $trans;
449         } else {
450             return $trans;
451         }
452     }
453 }
454
455 // For emacs users
456 // Local Variables:
457 // mode: php
458 // tab-width: 8
459 // c-basic-offset: 4
460 // c-hanging-comment-ender-p: nil
461 // indent-tabs-mode: nil
462 // End:
463 ?>