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 _("AllPagesCreatedByMe") . ',' .
48 _("AllPagesLastEditedByMe") . ',' .
49 _("AllPagesOwnedByMe") . ',' .
50 _("BackLinks") . ',' .
51 _("CategoryCategory") . ',' .
52 _("CategoryHomePages") . ',' .
53 _("DebugInfo") . ',' .
54 _("EditMetaData") . ',' .
56 _("FullRecentChanges") . ',' .
57 _("Help/AddingPages") . ',' .
58 _("Help/AddCommentPlugin") . ',' .
59 _("Help/AuthorHistoryPlugin") . ',' .
60 _("Help/CalendarListPlugin") . ',' .
61 _("Help/CalendarPlugin") . ',' .
62 _("Help/CommentPlugin") . ',' .
63 _("Help/CreateTocPlugin") . ',' .
64 _("Help/EditMetaDataPlugin") . ',' .
65 _("Help/ExternalSearchPlugin") . ',' .
66 _("Help/FoafViewerPlugin") . ',' .
67 _("Help/FrameIncludePlugin") . ',' .
68 _("Help/HelloWorldPlugin") . ',' .
69 _("Help/IncludePagePlugin") . ',' .
70 _("Help/LinkIcons") . ',' .
71 _("Help/MagicPhpWikiURLs") . ',' .
72 _("Help/MoreAboutMechanics") . ',' .
73 _("Help/OldStyleTablePlugin") . ',' .
74 _("Help/PhotoAlbumPlugin") . ',' .
75 _("Help/PhpHighlightPlugin") . ',' .
76 _("Help/PhpWeatherPlugin") . ',' .
77 _("Help/PhpWiki") . ',' .
78 _("Help/PloticusPlugin") . ',' .
79 _("Help/RawHtmlPlugin") . ',' .
80 _("Help/RedirectToPlugin") . ',' .
81 _("Help/RichTablePlugin") . ',' .
82 _("Help/SystemInfoPlugin") . ',' .
83 _("Help/TranscludePlugin") . ',' .
84 _("Help/UnfoldSubpagesPlugin") . ',' .
85 _("Help/UpLoadPlugin") . ',' .
86 _("Help/WabiSabi") . ',' .
87 _("Help/WikiBlogPlugin") . ',' .
88 _("Help/WikiPlugin") . ',' .
89 _("Help/WikiWikiWeb");
90 _("HomePageAlias") . ',' .
91 _("InterWiki") . ',' .
92 // _("PageDump") .','.
93 _("PhpWikiAdministration/Chmod") . ',' .
94 _("PhpWikiAdministration/Chown") . ',' .
95 _("PhpWikiAdministration/Remove") . ',' .
96 _("PhpWikiAdministration/Rename") . ',' .
97 _("PhpWikiAdministration/Replace") . ',' .
98 _("PhpWikiAdministration/SetAcl") . ',' .
99 _("PhpWikiDocumentation") . ',' .
100 _("PhpWikiPoll") . ',' .
101 _("RecentVisitors") . ',' .
102 _("ReleaseNotes") . ',' .
103 // _("SpellCheck") .','.
104 _("SteveWainstead") . ',' .
105 _("TranslateText") . ',' .
108 require_once 'lib/PageList.php';
110 class WikiPlugin__WikiTranslation
116 return _("_WikiTranslation");
119 function getDescription()
121 return _("Show translations of various words or pages.");
124 function getDefaultArguments()
128 PageList::supportedArgs(),
129 array('languages' => '', // comma delimited string of de,en,sv,...
131 'page' => '', // use a translation service
132 'what' => 'pages', // or 'buttons', 'plugins' or 'wikiwords'
135 'from_lang' => false,
136 'include_empty' => false,
140 'nolinks' => false, // don't display any links
141 // (for development only)
142 'noT' => false // don't display the T link
143 // (for development only)
147 function init_locale($lang)
149 if ($lang != $this->lang)
150 update_locale($lang);
152 // Hack alert! we need hash for stepping through it, even if it's
153 // in the wrong language
154 include (FindFile("locale/de/LC_MESSAGES/phpwiki.php", 0, 'reinit'));
155 foreach ($locale as $en => $de) {
158 // gettext module loaded: must load the LC_MESSAGES php hash
159 } elseif (function_exists('bindtextdomain')) {
160 include (FindFile("locale/$lang/LC_MESSAGES/phpwiki.php", 0, 'reinit'));
161 //include (FindLocalizedFile("LC_MESSAGES/phpwiki.php", 0,'reinit'));
162 // we already have a $locale, but maybe it's in the wrong language
163 } elseif ($lang != $this->lang or empty($GLOBALS['locale'])) {
164 include (FindFile("locale/$lang/LC_MESSAGES/phpwiki.php", 0, 'reinit'));
166 $locale = & $GLOBALS['locale'];
168 $this->_locales[$lang] = $locale;
171 // reverse translation:
172 function translate_to_en($text, $lang = false)
174 if (!$lang) $lang = $this->lang; // current locale
175 if ($lang == 'en') return $text;
177 $this->_locales = array();
178 $this->_reverse_locales = array();
180 if (!isset($this->_locales[$lang])) {
181 $this->init_locale($lang);
183 assert(!empty($this->_locales[$lang]));
184 if (!isset($this->_reverse_locales[$lang])) {
185 // and now do a reverse lookup in the $locale hash
186 $this->_reverse_locales[$lang] = array_flip($this->_locales[$lang]);
188 if (!empty($this->_reverse_locales[$lang][$text])) {
189 return $this->_reverse_locales[$lang][$text];
196 * setlocale() switching with the gettext extension is by far too slow.
197 * So use the hash regardless if gettext is loaded or not.
199 function fast_translate($text, $to_lang, $from_lang = false)
201 if (!$from_lang) $from_lang = $this->lang; // current locale
202 if ($from_lang == $to_lang) return $text;
203 // setup hash from en => to_lang
204 if (!isset($this->_locales[$to_lang]))
205 $this->init_locale($to_lang);
206 if ($from_lang != 'en') {
207 // get reverse gettext: translate to english
208 $text = $this->translate_to_en($text, $from_lang);
210 return !empty($this->_locales[$to_lang][$text])
211 ? $this->_locales[$to_lang][$text]
215 //FIXME! There's something wrong.
216 function 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 // Speed up hash lookup. Not needed for gettext module
221 if (!isset($this->_locales[$from_lang]) and !function_exists('bindtextdomain')) {
222 $this->init_locale($from_lang);
224 if ($from_lang != 'en') {
225 // get reverse gettext: translate to english
226 $en = $this->translate_to_en($text, $from_lang);
227 // and then to target
228 update_locale($to_lang);
229 $result = gettext($en);
230 update_locale($from_lang);
232 // locale switching is very slow with the gettext extension.
233 // better use fast_translate
234 if ($from_lang != $to_lang) {
235 update_locale($to_lang);
237 $result = gettext($text);
238 if ($from_lang != $to_lang) {
239 update_locale($from_lang);
245 function run($dbi, $argstr, &$request, $basepage)
247 $this->args = $this->getArgs($argstr, $request);
248 extract($this->args);
249 $this->request = &$request;
250 if (!$from_lang) $from_lang = $request->getPref('lang');
251 if (!$from_lang) $from_lang = $GLOBALS['LANG'];
252 $this->lang = $from_lang;
254 if (empty($languages)) {
255 $available_languages = listAvailableLanguages();
256 if ($from_lang == 'en') {
257 // "en" is always the first.
258 array_shift($available_languages);
260 // put from_lang to the very end.
261 if (in_array($from_lang, $available_languages))
262 $languages = $available_languages;
264 $languages = array_merge($available_languages, array($from_lang));
265 } elseif (strstr($languages, ',')) {
266 $languages = explode(',', $languages);
268 $languages = array($languages);
270 if (in_array('zh', $languages) or in_array('ja', $languages)) {
272 // If the current charset != utf-8 the text will not be displayed correctly.
273 // But here we cannot change the header anymore. So we can decide to ignore them,
274 // or display them with all the errors.
275 //FIXME: do iconv the ob
276 if ($GLOBALS['charset'] != 'utf-8' and !defined('NEED_ICONV_TO')) {
277 define('NEED_ICONV_TO', 'utf-8');
278 //either the extension or external
279 //$GLOBALS['charset'] = 'utf-8';
282 $to_lang = $languages[0];
283 if (!empty($string) and count($languages) == 1) {
284 return $this->translate($string, $to_lang, $from_lang);
288 if ($dbi->isWikiPage($pagename)) {
290 // google can only translate from english and french
291 if (in_array($from_lang, array('en', 'fr'))) {
292 $url = "http://translate.google.com/translate";
293 $url .= "?langpair=" . urlencode($from_lang . "|" . $to_lang);
294 $url .= "&u=" . urlencode(WikiURL($pagename, false, true));
296 // redirect or transclude?
298 return $request->redirect($url);
300 return HTML(fmt("TODO: Google can only translate from english and french. Find a translation service for %s to language %s",
301 WikiURL($pagename, false, true),
304 return $this->error(fmt("%s is empty.", $pagename));
308 $pagelist = new PageList('', $exclude, $this->args);
309 $pagelist->_columns[0]->_heading = "$from_lang";
310 foreach ($languages as $lang) {
311 if ($lang == $from_lang) continue;
312 $field = "custom:$lang";
313 $pagelist->addColumnObject(
314 new _PageList_Column_customlang($field, $from_lang, $this));
316 if (!empty($string)) {
317 $pagelist->addPage($string);
322 $pagelist->addPages($dbi->getAllPages($include_empty, $sortby,
326 // not all pages, only the pgsrc pages
327 if (!is_array($exclude))
328 $exclude = $pagelist->explodePageList($exclude, false, $sortby,
330 $path = FindLocalizedFile(WIKI_PGSRC);
331 $pgsrc = new fileSet($path);
332 foreach ($pgsrc->getFiles($exclude, $sortby, $limit) as $pagename) {
333 $pagename = urldecode($pagename);
334 if (substr($pagename, -1, 1) == '~') continue;
335 if (in_array($pagename, $exclude))
336 continue; // exclude page.
337 if ($match != '*' and !glob_match($match, $pagename))
339 $page_handle = $dbi->getPage($pagename);
340 $pagelist->addPage($page_handle);
344 if (!isset($this->_locales[$from_lang])) {
345 $this->init_locale($from_lang);
347 $locale = & $this->_locales[$from_lang];
348 if (is_array($locale)) {
350 foreach ($locale as $from => $to) {
351 if ($match != '*' and !glob_match($match, $from))
353 if (isWikiWord($from)) {
355 $pagelist->addPage($from);
356 if ($limit and $count > $limit) break;
361 // all Button texts, which need a localized .png
362 // where to get them from? templates/*.tmpl: Button()
363 // and WikiLink(?,'button')
364 // navbar links, actionpages, and admin requests
366 $buttons = $GLOBALS['AllActionPages'];
367 $fileset = new FileSet(FindFile("themes/MacOSX/buttons/en"),
369 foreach ($fileset->getFiles() as $file) {
370 $b = urldecode(substr($file, 0, -4));
371 if (!in_array($b, $buttons))
375 foreach ($buttons as $button) {
376 $pagelist->addPage($button);
377 if ($limit and ++$count > $limit) break;
385 class _PageList_Column_customlang extends _PageList_Column
387 function _PageList_Column_customlang($field, $from_lang, $plugin)
389 $this->_field = $field;
390 $this->_from_lang = $from_lang;
391 $this->_plugin =& $plugin;
392 $this->_what = $plugin->args['what'];
393 $this->_noT = $plugin->args['noT'];
394 $this->_nolinks = $plugin->args['nolinks'];
395 $this->_iscustom = substr($field, 0, 7) == 'custom:';
396 if ($this->_iscustom)
397 $this->_field = substr($field, 7);
399 $this->dbi = &$GLOBALS['request']->getDbh();
400 $this->_PageList_Column_base($this->_field);
403 function _getValue($page, &$revision_handle)
405 if (is_object($page)) $text = $page->getName();
407 $trans = $this->_plugin->fast_translate($text, $this->_field,
409 // how to markup untranslated words and not existing pages?
410 // untranslated: (TODO) link to translation editor
411 if ($trans == $text or // untranslated
412 (($this->_from_lang != 'en') and
413 ($this->_field != 'en') and
414 ($trans == $this->_plugin->fast_translate($text, 'en',
419 $link = $WikiTheme->linkUnknownWikiWord($trans);
420 if (!($this->_noT or $this->_nolinks)
421 and $this->dbi->isWikiPage($trans)
423 $url = WikiURL($trans, array('action' => 'TranslateText',
424 'lang' => $this->_field));
425 $button = $WikiTheme->makeButton('T', $url);
426 $button->addTooltip(sprintf(_("Define the translation for %s in %s"),
427 $trans, $this->_field));
428 $link = HTML::span($button);
429 $link->setAttr('class', 'wikiunknown');
430 $text = HTML::span($WikiTheme->maybeSplitWikiWord($trans));
431 $text->setAttr('style', 'text-decoration:line-through');
432 $link->pushContent($text);
434 } elseif (is_object($page))
435 return ''; else // not existing: empty
437 } elseif (is_object($page)) {
438 if (!$this->_nolinks)
439 return WikiLink($trans, 'auto');
452 // c-hanging-comment-ender-p: nil
453 // indent-tabs-mode: nil