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
19 * along with PhpWiki; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 * <?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.
42 * @author: Reini Urban
45 /* Container for untranslated pagenames. Needed to show up in locale/po/phpwiki.pot */
47 _("AddCommentPlugin") .','.
48 _("AddingPages") .','.
49 _("AllPagesCreatedByMe") .','.
50 _("AllPagesLastEditedByMe") .','.
51 _("AllPagesOwnedByMe") .','.
52 _("AuthorHistoryPlugin") .','.
54 _("CalendarListPlugin") .','.
55 _("CalendarPlugin") .','.
56 _("CategoryCategory") .','.
57 _("CategoryHomePages") .','.
58 _("CommentPlugin") .','.
59 _("CreateTocPlugin") .','.
61 _("EditMetaData") .','.
62 _("EditMetaDataPlugin") .','.
63 _("ExternalSearchPlugin") .','.
65 _("FoafViewerPlugin") .','.
66 _("FrameIncludePlugin") .','.
67 _("FullRecentChanges") .','.
68 _("HelloWorldPlugin") .','.
69 _("HomePageAlias") .','.
70 _("IncludePagePlugin") .','.
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") .','.
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") .','.
119 _("UpLoadPlugin") .','.
121 _("WikiBlogPlugin") .','.
122 _("WikiPlugin") .','.
125 require_once('lib/PageList.php');
127 class WikiPlugin__WikiTranslation
132 return _("_WikiTranslation");
135 function getDescription() {
136 return _("Show translations of various words or pages");
139 function getVersion() {
140 return preg_replace("/[Revision: $]/", '',
144 function getDefaultArguments() {
147 PageList::supportedArgs(),
148 array( 'languages' => '', // comma delimited string of de,en,sv,...
150 'page' => '', // use a translation service
151 'what' => 'pages', // or 'buttons', 'plugins' or 'wikiwords'
154 'from_lang' => false,
155 'include_empty' => false,
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)
166 function init_locale($lang) {
167 if ($lang != $this->lang)
168 update_locale($lang);
170 // Hack alert! we need hash for stepping through it, even if it's
171 // in the wrong language
172 include (FindFile("locale/de/LC_MESSAGES/phpwiki.php", 0,'reinit'));
173 foreach ($locale as $en => $de) {
176 // gettext module loaded: must load the LC_MESSAGES php hash
177 } elseif (function_exists ('bindtextdomain')) {
178 include (FindFile("locale/$lang/LC_MESSAGES/phpwiki.php", 0,'reinit'));
179 //include (FindLocalizedFile("LC_MESSAGES/phpwiki.php", 0,'reinit'));
180 // we already have a $locale, but maybe it's in the wrong language
181 } elseif ($lang != $this->lang or empty($GLOBALS['locale'])) {
182 include (FindFile("locale/$lang/LC_MESSAGES/phpwiki.php", 0,'reinit'));
184 $locale = & $GLOBALS['locale'];
186 $this->_locales[$lang] = $locale;
189 // reverse translation:
190 function translate_to_en($text, $lang=false) {
191 if (!$lang) $lang = $this->lang; // current locale
192 if ($lang == 'en') return $text;
194 $this->_locales = array();
195 $this->_reverse_locales = array();
197 if (!isset($this->_locales[$lang])) {
198 $this->init_locale($lang);
200 assert(!empty($this->_locales[$lang]));
201 if (!isset($this->_reverse_locales[$lang])) {
202 // and now do a reverse lookup in the $locale hash
203 $this->_reverse_locales[$lang] = array_flip($this->_locales[$lang]);
205 if (!empty($this->_reverse_locales[$lang][$text])) {
206 return $this->_reverse_locales[$lang][$text];
213 * setlocale() switching with the gettext extension is by far too slow.
214 * So use the hash regardless if gettext is loaded or not.
216 function fast_translate($text, $to_lang, $from_lang=false) {
217 if (!$from_lang) $from_lang = $this->lang; // current locale
218 if ($from_lang == $to_lang) return $text;
219 // setup hash from en => to_lang
220 if (!isset($this->_locales[$to_lang]))
221 $this->init_locale($to_lang);
222 if ($from_lang != 'en') {
223 // get reverse gettext: translate to english
224 $text = $this->translate_to_en($text, $from_lang);
226 return !empty($this->_locales[$to_lang][$text])
227 ? $this->_locales[$to_lang][$text]
231 //FIXME! There's something wrong.
232 function translate($text, $to_lang, $from_lang=false) {
233 if (!$from_lang) $from_lang = $this->lang; // current locale
234 if ($from_lang == $to_lang) return $text;
235 // Speed up hash lookup. Not needed for gettext module
236 if (!isset($this->_locales[$from_lang]) and !function_exists('bindtextdomain')) {
237 $this->init_locale($from_lang);
239 if ($from_lang != 'en') {
240 // get reverse gettext: translate to english
241 $en = $this->translate_to_en($text, $from_lang);
242 // and then to target
243 update_locale($to_lang);
244 $result = gettext($en);
245 update_locale($from_lang);
247 // locale switching is very slow with the gettext extension.
248 // better use fast_translate
249 if ($from_lang != $to_lang) {
250 update_locale($to_lang);
252 $result = gettext($text);
253 if ($from_lang != $to_lang) {
254 update_locale($from_lang);
260 function run($dbi, $argstr, &$request, $basepage) {
261 $this->args = $this->getArgs($argstr, $request);
262 extract($this->args);
263 $this->request = &$request;
264 if (!$from_lang) $from_lang = $request->getPref('lang');
265 if (!$from_lang) $from_lang = $GLOBALS['LANG'];
266 $this->lang = $from_lang;
268 if (empty($languages)) {
269 $available_languages = listAvailableLanguages();
270 if ($from_lang == 'en') {
271 // "en" is always the first.
272 array_shift($available_languages);
274 // put from_lang to the very end.
275 if (in_array($from_lang, $available_languages))
276 $languages = $available_languages;
278 $languages = array_merge($available_languages, array($from_lang));
279 } elseif (strstr($languages,',')) {
280 $languages = explode(',', $languages);
282 $languages = array($languages);
284 if (in_array('zh', $languages) or in_array('ja', $languages)) {
286 // If the current charset != utf-8 the text will not be displayed correctly.
287 // But here we cannot change the header anymore. So we can decide to ignore them,
288 // or display them with all the errors.
289 //FIXME: do iconv the ob
290 if ($GLOBALS['charset'] != 'utf-8' and !defined('NEED_ICONV_TO')) {
291 define('NEED_ICONV_TO', 'utf-8');
292 //either the extension or external
293 //$GLOBALS['charset'] = 'utf-8';
296 $to_lang = $languages[0];
297 if (!empty($string) and count($languages)==1) {
298 return $this->translate($string, $to_lang, $from_lang);
302 if ($dbi->isWikiPage($pagename)) {
304 // google can only translate from english and french
305 if (in_array($from_lang, array('en', 'fr'))) {
306 $url = "http://translate.google.com/translate";
307 $url .= "?langpair=" . urlencode($from_lang."|".$to_lang);
308 $url .= "&u=" . urlencode(WikiURL($pagename, false, true));
310 // redirect or transclude?
312 return $request->redirect($url);
314 return HTML(fmt("TODO: Google can only translate from english and french. Find a translation service for %s to language %s",
315 WikiURL($pagename, false, true),
318 return $this->error(fmt("%s is empty",$pagename));
322 $pagelist = new PageList('', $exclude, $this->args);
323 $pagelist->_columns[0]->_heading = "$from_lang";
324 foreach ($languages as $lang) {
325 if ($lang == $from_lang) continue;
326 $field = "custom:$lang";
327 $pagelist->addColumnObject (
328 new _PageList_Column_customlang($field, $from_lang, $this));
330 if (!empty($string)) {
331 $pagelist->addPage( $string );
336 $pagelist->addPages( $dbi->getAllPages($include_empty, $sortby,
340 // not all pages, only the pgsrc pages
341 if (!is_array($exclude))
342 $exclude = $pagelist->explodePageList($exclude, false, $sortby,
344 $path = FindLocalizedFile(WIKI_PGSRC);
345 $pgsrc = new fileSet($path);
346 foreach ($pgsrc->getFiles($exclude, $sortby, $limit) as $pagename) {
347 $pagename = urldecode($pagename);
348 if (substr($pagename,-1,1) == '~') continue;
349 if (in_array($pagename, $exclude))
350 continue; // exclude page.
351 if ($match != '*' and !glob_match($match, $pagename))
353 $page_handle = $dbi->getPage($pagename);
354 $pagelist->addPage( $page_handle );
358 if (!isset($this->_locales[$from_lang])) {
359 $this->init_locale($from_lang);
361 $locale = & $this->_locales[$from_lang];
362 if (is_array($locale)) {
364 foreach ($locale as $from => $to) {
365 if ($match != '*' and !glob_match($match, $from))
367 if (isWikiWord($from)) {
369 $pagelist->addPage( $from );
370 if ($limit and $count > $limit) break;
375 // all Button texts, which need a localized .png
376 // where to get them from? templates/*.tmpl: Button()
377 // and WikiLink(?,'button')
378 // navbar links, actionpages, and admin requests
380 $buttons = $GLOBALS['AllActionPages'];
381 $fileset = new FileSet(FindFile("themes/MacOSX/buttons/en"),
383 foreach ($fileset->getFiles() as $file) {
384 $b = urldecode(substr($file, 0, -4));
385 if (!in_array($b,$buttons))
389 foreach ($buttons as $button) {
390 $pagelist->addPage( $button );
391 if ($limit and ++$count > $limit) break;
399 class _PageList_Column_customlang extends _PageList_Column {
400 function _PageList_Column_customlang($field, $from_lang, $plugin) {
401 $this->_field = $field;
402 $this->_from_lang = $from_lang;
403 $this->_plugin =& $plugin;
404 $this->_what = $plugin->args['what'];
405 $this->_noT = $plugin->args['noT'];
406 $this->_nolinks = $plugin->args['nolinks'];
407 $this->_iscustom = substr($field, 0, 7) == 'custom:';
408 if ($this->_iscustom)
409 $this->_field = substr($field, 7);
411 $this->dbi = &$GLOBALS['request']->getDbh();
412 $this->_PageList_Column_base($this->_field);
415 function _getValue($page, &$revision_handle) {
416 if (is_object($page)) $text = $page->getName();
418 $trans = $this->_plugin->fast_translate($text, $this->_field,
420 // how to markup untranslated words and not existing pages?
421 // untranslated: (TODO) link to translation editor
422 if ($trans == $text or // untranslated
423 (($this->_from_lang != 'en') and
424 ($this->_field != 'en') and
425 ($trans == $this->_plugin->fast_translate($text, 'en',
430 $link = $WikiTheme->linkUnknownWikiWord($trans);
431 if (!($this->_noT or $this->_nolinks)
432 and $this->dbi->isWikiPage($trans))
434 $url = WikiURL($trans, array('action' => 'TranslateText',
435 'lang' => $this->_field));
436 $button = $WikiTheme->makeButton('T', $url);
437 $button->addTooltip(sprintf(_("Define the translation for %s in %s"),
438 $trans, $this->_field));
439 $link = HTML::span($button);
440 $link->setAttr('class', 'wikiunknown');
441 $text = HTML::span($WikiTheme->maybeSplitWikiWord($trans));
442 $text->setAttr('style', 'text-decoration:line-through');
443 $link->pushContent($text);
445 } elseif (is_object($page))
447 else // not existing: empty
449 } elseif (is_object($page)) {
450 if (!$this->_nolinks)
451 return WikiLink($trans, 'auto');
465 // c-hanging-comment-ender-p: nil
466 // indent-tabs-mode: nil