4 * Copyright 2004,2005 $ThePhpWikiProgrammingTeam
6 * This file is part of PhpWiki.
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.
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.
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.
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
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.
42 * @author: Reini Urban
45 /* Container for untranslated pagenames. Needed to show up in locale/po/phpwiki.pot */
47 _("AddComment") . ',' .
48 _("AddCommentPlugin") . ',' .
49 _("AddingPages") . ',' .
51 _("AllPagesCreatedByMe") . ',' .
52 _("AllPagesLastEditedByMe") . ',' .
53 _("AllPagesOwnedByMe") . ',' .
55 _("AnalyseAccessLogSql") . ',' .
56 _("AppendText") . ',' .
57 _("AsciiMath") . ',' .
60 _("DebugAuthInfo") . ',' .
61 _("AuthorHistory") . ',' .
62 _("AuthorHistoryPlugin") . ',' .
63 _("DebugBackendInfo") . ',' .
64 _("BackLinks") . ',' .
65 _("BlogArchives") . ',' .
66 _("BlogJournal") . ',' .
68 _("CacheTest") . ',' .
70 _("CalendarList") . ',' .
71 _("CalendarListPlugin") . ',' .
72 _("CalendarPlugin") . ',' .
73 _("CategoryCategory") . ',' .
74 _("CategoryHomePages") . ',' .
75 _("CategoryPage") . ',' .
79 _("CommentPlugin") . ',' .
80 _("CreateBib") . ',' .
81 _("CreatePage") . ',' .
82 _("CreateToc") . ',' .
83 _("CreateTocPlugin") . ',' .
84 _("CurrentTime") . ',' .
85 _("DeadEndPages") . ',' .
86 _("DebugInfo") . ',' .
88 _("DynamicIncludePage") . ',' .
89 _("EditMetaData") . ',' .
90 _("EditMetaDataPlugin") . ',' .
91 _("ExternalSearch") . ',' .
92 _("ExternalSearchPlugin") . ',' .
93 _("FacebookLike") . ',' .
96 _("FoafViewer") . ',' .
97 _("FoafViewerPlugin") . ',' .
98 _("FrameInclude") . ',' .
99 _("FrameIncludePlugin") . ',' .
100 _("FullRecentChanges") . ',' .
101 _("FullTextSearch") . ',' .
102 _("FuzzyPages") . ',' .
103 _("GoogleMaps") . ',' .
104 _("GooglePlugin") . ',' .
106 _("GraphViz") . ',' .
107 _("DebugGroupInfo") . ',' .
108 _("HelloWorld") . ',' .
109 _("HelloWorldPlugin") . ',' .
110 _("HomePageAlias") . ',' .
111 _("HtmlConverter") . ',' .
113 _("IncludePage") . ',' .
114 _("IncludePagePlugin") . ',' .
115 _("IncludePages") . ',' .
116 _("IncludeSiteMap") . ',' .
117 _("IncludeTree") . ',' .
118 _("InterWiki") . ',' .
119 _("InterWikiSearch") . ',' .
120 _("JabberPresence") . ',' .
121 _("LdapSearch") . ',' .
122 _("LikePages") . ',' .
123 _("LinkDatabase") . ',' .
124 _("LinkIcons") . ',' .
125 _("LinkSearch") . ',' .
126 _("ListPages") . ',' .
127 _("ListRelations") . ',' .
128 _("ListSubpages") . ',' .
129 _("MagicPhpWikiURLs") . ',' .
130 _("MediawikiTable") . ',' .
131 _("ModeratedPage") . ',' .
132 _("MoreAboutMechanics") . ',' .
133 _("MostPopular") . ',' .
134 _("NewPagesPerUser") . ',' .
136 _("OldStyleTable") . ',' .
137 _("OldStyleTablePlugin") . ',' .
138 _("OrphanedPages") . ',' .
139 _("PageDump") . ',' .
140 _("PageGroup") . ',' .
141 _("PageHistory") . ',' .
142 _("PageInfo") . ',' .
143 _("PageTrail") . ',' .
144 _("PasswordReset") . ',' .
145 _("PhotoAlbum") . ',' .
146 _("PhotoAlbumPlugin") . ',' .
147 _("PhpHighlight") . ',' .
148 _("PhpHighlightPlugin") . ',' .
149 _("PhpWeather") . ',' .
150 _("PhpWeatherPlugin") . ',' .
152 _("PhpWikiAdministration") . ',' .
153 _("PhpWikiDocumentation") . ',' .
154 _("PhpWikiPoll") . ',' .
155 _("Ploticus") . ',' .
156 _("PloticusPlugin") . ',' .
157 _("PluginManager") . ',' .
158 _("PopularNearby") . ',' .
159 _("PopularTags") . ',' .
161 _("PreferenceApp") . ',' .
162 _("PreferencesInfo") . ',' .
163 _("PrevNext") . ',' .
164 _("Processing") . ',' .
165 _("RandomPage") . ',' .
168 _("RawHtmlPlugin") . ',' .
169 _("RecentChanges") . ',' .
170 _("RecentChangesCached") . ',' .
171 _("RecentComments") . ',' .
172 _("RecentEdits") . ',' .
173 _("RecentReferrers") . ',' .
174 _("RecentVisitors") . ',' .
175 _("RedirectTo") . ',' .
176 _("RedirectToPlugin") . ',' .
177 _("RelatedChanges") . ',' .
178 _("ReleaseNotes") . ',' .
182 _("DebugRetransform") . ',' .
183 _("RichTable") . ',' .
184 _("RichTablePlugin") . ',' .
186 _("SearchHighlight") . ',' .
187 _("SemanticRelations") . ',' .
188 _("SemanticSearch") . ',' .
189 _("SemanticSearchAdvanced") . ',' .
192 _("SpellCheck") . ',' .
193 _("SpecialPages") . ',' .
194 _("SqlResult") . ',' .
195 _("SyncWiki") . ',' .
196 _("SyntaxHighlighter") . ',' .
197 _("SystemInfo") . ',' .
198 _("SystemInfoPlugin") . ',' .
199 _("Template") . ',' .
201 _("text2png") . ',' .
202 _("TextFormattingRules") . ',' .
203 _("TexToPng") . ',' .
204 _("TitleSearch") . ',' .
205 _("Transclude") . ',' .
206 _("TranscludePlugin") . ',' .
207 _("TranslateText") . ',' .
208 _("UnfoldSubpages") . ',' .
209 _("UnfoldSubpagesPlugin") . ',' .
211 _("UpLoadPlugin") . ',' .
212 _("UriResolver") . ',' .
213 _("UserPreferences") . ',' .
214 _("UserRatings") . ',' .
216 _("VisualWiki") . ',' .
217 _("WabiSabi") . ',' .
218 _("WantedPages") . ',' .
219 _("WantedPagesOld") . ',' .
220 _("WatchPage") . ',' .
221 _("WhoIsOnline") . ',' .
222 _("WikiAdminChown") . ',' .
223 _("WikiAdminDeleteAcl") . ',' .
224 _("WikiAdminPurge") . ',' .
225 _("WikiAdminRemove") . ',' .
226 _("WikiAdminRename") . ',' .
227 _("WikiAdminSearchReplace") . ',' .
228 _("WikiAdminSelect") . ',' .
229 _("WikiAdminSetAcl") . ',' .
230 _("WikiAdminSetAclSimple") . ',' .
231 _("WikiAdminSetExternal") . ',' .
232 _("WikiAdminUtils") . ',' .
233 _("WikiBlog") . ',' .
234 _("WikiBlogPlugin") . ',' .
235 _("WikicreoleTable") . ',' .
236 _("WikiForm") . ',' .
237 _("WikiFormRich") . ',' .
238 _("WikiForum") . ',' .
239 _("WikiPlugin") . ',' .
240 _("WikiPoll") . ',' .
241 _("WikiTranslation") . ',' .
242 _("WikiWikiWeb") . ',' .
245 require_once 'lib/PageList.php';
247 class WikiPlugin_WikiTranslation
251 function getDescription()
253 return _("Show translations of various words or pages.");
256 function getDefaultArguments()
260 PageList::supportedArgs(),
261 array('languages' => '', // comma delimited string of de,en,sv,...
263 'page' => '', // use a translation service
264 'what' => 'pages', // or 'buttons', 'plugins' or 'wikiwords'
267 'from_lang' => false,
268 'include_empty' => false,
272 'nolinks' => false, // don't display any links
273 // (for development only)
274 'noT' => false // don't display the T link
275 // (for development only)
279 function init_locale($lang)
281 if ($lang != $this->lang)
282 update_locale($lang);
283 $this->_locales[$lang] = $locale;
286 // reverse translation:
287 function translate_to_en($text, $lang = false)
289 if (!$lang) $lang = $this->lang; // current locale
290 if ($lang == 'en') return $text;
292 $this->_locales = array();
293 $this->_reverse_locales = array();
295 if (!isset($this->_locales[$lang])) {
296 $this->init_locale($lang);
298 assert(!empty($this->_locales[$lang]));
299 if (!isset($this->_reverse_locales[$lang])) {
300 // and now do a reverse lookup in the $locale hash
301 $this->_reverse_locales[$lang] = array_flip($this->_locales[$lang]);
303 if (!empty($this->_reverse_locales[$lang][$text])) {
304 return $this->_reverse_locales[$lang][$text];
311 * setlocale() switching with the gettext extension is by far too slow.
312 * So use the hash regardless if gettext is loaded or not.
314 function fast_translate($text, $to_lang, $from_lang = false)
316 if (!$from_lang) $from_lang = $this->lang; // current locale
317 if ($from_lang == $to_lang) return $text;
318 // setup hash from en => to_lang
319 if (!isset($this->_locales[$to_lang]))
320 $this->init_locale($to_lang);
321 if ($from_lang != 'en') {
322 // get reverse gettext: translate to english
323 $text = $this->translate_to_en($text, $from_lang);
325 return !empty($this->_locales[$to_lang][$text])
326 ? $this->_locales[$to_lang][$text]
330 //FIXME! There's something wrong.
331 function translate($text, $to_lang, $from_lang = false)
333 if (!$from_lang) $from_lang = $this->lang; // current locale
334 if ($from_lang == $to_lang) return $text;
335 // Speed up hash lookup. Not needed for gettext module
336 if (!isset($this->_locales[$from_lang]) and !function_exists('bindtextdomain')) {
337 $this->init_locale($from_lang);
339 if ($from_lang != 'en') {
340 // get reverse gettext: translate to english
341 $en = $this->translate_to_en($text, $from_lang);
342 // and then to target
343 update_locale($to_lang);
344 $result = gettext($en);
345 update_locale($from_lang);
347 // locale switching is very slow with the gettext extension.
348 // better use fast_translate
349 if ($from_lang != $to_lang) {
350 update_locale($to_lang);
352 $result = gettext($text);
353 if ($from_lang != $to_lang) {
354 update_locale($from_lang);
360 function run($dbi, $argstr, &$request, $basepage)
362 $this->args = $this->getArgs($argstr, $request);
363 extract($this->args);
364 $this->request = &$request;
365 if (!$from_lang) $from_lang = $request->getPref('lang');
366 if (!$from_lang) $from_lang = $GLOBALS['LANG'];
367 $this->lang = $from_lang;
369 if (empty($languages)) {
370 $available_languages = listAvailableLanguages();
371 if ($from_lang == 'en') {
372 // "en" is always the first.
373 array_shift($available_languages);
375 // put from_lang to the very end.
376 if (in_array($from_lang, $available_languages))
377 $languages = $available_languages;
379 $languages = array_merge($available_languages, array($from_lang));
380 } elseif (strstr($languages, ',')) {
381 $languages = explode(',', $languages);
383 $languages = array($languages);
385 $to_lang = $languages[0];
386 if (!empty($string) and count($languages) == 1) {
387 return $this->translate($string, $to_lang, $from_lang);
391 if ($dbi->isWikiPage($pagename)) {
393 // google can only translate from english and french
394 if (in_array($from_lang, array('en', 'fr'))) {
395 $url = "http://translate.google.com/translate";
396 $url .= "?langpair=" . urlencode($from_lang . "|" . $to_lang);
397 $url .= "&u=" . urlencode(WikiURL($pagename, false, true));
399 // redirect or transclude?
401 return $request->redirect($url);
403 return HTML(fmt("TODO: Google can only translate from english and french. Find a translation service for %s to language %s",
404 WikiURL($pagename, false, true),
407 return $this->error(fmt("%s is empty.", $pagename));
411 $pagelist = new PageList('', $exclude, $this->args);
412 $pagelist->_columns[0]->_heading = "$from_lang";
413 foreach ($languages as $lang) {
414 if ($lang == $from_lang) continue;
415 $field = "custom:$lang";
416 $pagelist->addColumnObject(
417 new _PageList_Column_customlang($field, $from_lang, $this));
419 if (!empty($string)) {
420 $pagelist->addPage($string);
425 $pagelist->addPages($dbi->getAllPages($include_empty, $sortby,
429 // not all pages, only the pgsrc pages
430 if (!is_array($exclude))
431 $exclude = $pagelist->explodePageList($exclude, false, $sortby,
433 $path = FindLocalizedFile(WIKI_PGSRC);
434 $pgsrc = new fileSet($path);
435 foreach ($pgsrc->getFiles($exclude, $sortby, $limit) as $pagename) {
436 $pagename = urldecode($pagename);
437 if (substr($pagename, -1, 1) == '~') continue;
438 if (in_array($pagename, $exclude))
439 continue; // exclude page.
440 if ($match != '*' and !glob_match($match, $pagename))
442 $page_handle = $dbi->getPage($pagename);
443 $pagelist->addPage($page_handle);
447 if (!isset($this->_locales[$from_lang])) {
448 $this->init_locale($from_lang);
450 $locale = & $this->_locales[$from_lang];
451 if (is_array($locale)) {
453 foreach ($locale as $from => $to) {
454 if ($match != '*' and !glob_match($match, $from))
456 if (isWikiWord($from)) {
458 $pagelist->addPage($from);
459 if ($limit and $count > $limit) break;
464 // all Button texts, which need a localized .png
465 // where to get them from? templates/*.tmpl: Button()
466 // and WikiLink(?,'button')
467 // navbar links, actionpages, and admin requests
469 $buttons = $GLOBALS['AllActionPages'];
470 $fileset = new FileSet(FindFile("themes/MacOSX/buttons/en"),
472 foreach ($fileset->getFiles() as $file) {
473 $b = urldecode(substr($file, 0, -4));
474 if (!in_array($b, $buttons))
478 foreach ($buttons as $button) {
479 $pagelist->addPage($button);
480 if ($limit and ++$count > $limit) break;
488 class _PageList_Column_customlang extends _PageList_Column
490 function _PageList_Column_customlang($field, $from_lang, $plugin)
492 $this->_field = $field;
493 $this->_from_lang = $from_lang;
494 $this->_plugin =& $plugin;
495 $this->_what = $plugin->args['what'];
496 $this->_noT = $plugin->args['noT'];
497 $this->_nolinks = $plugin->args['nolinks'];
498 $this->_iscustom = substr($field, 0, 7) == 'custom:';
499 if ($this->_iscustom)
500 $this->_field = substr($field, 7);
502 $this->dbi = &$GLOBALS['request']->getDbh();
503 $this->_PageList_Column_base($this->_field);
506 function _getValue($page, &$revision_handle)
508 if (is_object($page)) $text = $page->getName();
510 $trans = $this->_plugin->fast_translate($text, $this->_field,
512 // how to markup untranslated words and not existing pages?
513 // untranslated: (TODO) link to translation editor
514 if ($trans == $text or // untranslated
515 (($this->_from_lang != 'en') and
516 ($this->_field != 'en') and
517 ($trans == $this->_plugin->fast_translate($text, 'en',
522 $link = $WikiTheme->linkUnknownWikiWord($trans);
523 if (!($this->_noT or $this->_nolinks)
524 and $this->dbi->isWikiPage($trans)
526 $url = WikiURL($trans, array('action' => 'TranslateText',
527 'lang' => $this->_field));
528 $button = $WikiTheme->makeButton('T', $url);
529 $button->addTooltip(sprintf(_("Define the translation for %s in %s"),
530 $trans, $this->_field));
531 $link = HTML::span($button);
532 $link->setAttr('class', 'wikiunknown');
533 $text = HTML::span($WikiTheme->maybeSplitWikiWord($trans));
534 $text->setAttr('style', 'text-decoration:line-through');
535 $link->pushContent($text);
537 } elseif (is_object($page))
538 return ''; else // not existing: empty
540 } elseif (is_object($page)) {
541 if (!$this->_nolinks)
542 return WikiLink($trans, 'auto');
555 // c-hanging-comment-ender-p: nil
556 // indent-tabs-mode: nil