Index: trunk/extensions/Polyglot/README |
— | — | @@ -0,0 +1,84 @@ |
| 2 | +-------------------------------------------------------------------------- |
| 3 | +README for the Polyglot extension |
| 4 | +Copyright © 2007 Daniel Kinzler |
| 5 | +Licenses: GNU General Public Licence (GPL) |
| 6 | + GNU Free Documentation License (GFDL) |
| 7 | +-------------------------------------------------------------------------- |
| 8 | + |
| 9 | +Polyglot is EXPERIMENTAL |
| 10 | + |
| 11 | +The Polyglot extension provides automatic redirects based on user language. |
| 12 | +This allows for multilingual content to be handeled more easily on a |
| 13 | +single wiki. Polyglot also changes the interlanguage links in the sidebar |
| 14 | +to automatically show available localized version of each page. |
| 15 | + |
| 16 | +For more comprehensive support for multilingual content, Polyglot can be |
| 17 | +combined with the MultiLang and LanguageSelector extensions. |
| 18 | + |
| 19 | +Automatic redirects are performed based on a naming scheme: when visiting |
| 20 | +the page Foo with the user language set to de (German), Polyglot would |
| 21 | +redirect the user to Foo/de, if it exists. This automatic redirection can |
| 22 | +be bypassed by visiting Foo/, which will always show Foo proper. The base |
| 23 | +page (Foo in this example) is considered to be associated with the wiki's |
| 24 | +content language ($wgLanguageCode in LocalSettings.php). |
| 25 | + |
| 26 | +Note that Polyglot can not readily be used to localized templates. Instead, |
| 27 | +use the Multilang extension for template messages. |
| 28 | + |
| 29 | +The Polyglot extension was originally written by Daniel Kinzler in 2007 |
| 30 | +and is released under the GNU General Public Licence (GPL). |
| 31 | + |
| 32 | +<http://mediawiki.org/wiki/Extension:Polyglot> |
| 33 | + |
| 34 | +== Installing == |
| 35 | + |
| 36 | +Copy the Polyglot directory into the extensions folder of your |
| 37 | +MediaWiki installation. Then add the following line to your |
| 38 | +LocalSettings.php file (near the end): |
| 39 | + |
| 40 | + require_once( "$IP/extensions/Polyglot/Polyglot.php" ); |
| 41 | + |
| 42 | +== Configuration == |
| 43 | + |
| 44 | +You can specify the following settings in your LocalSettings.php (after |
| 45 | +including the extension file): |
| 46 | + |
| 47 | +* $wgPolyglotLanguages: Languages to be considered when looking for |
| 48 | + matching subpages (localized versions). If set th NULL (the default), |
| 49 | + all languages known to MediaWiki (by $wgLanguageNames) are considered. |
| 50 | + If the LanguageSelector extension is installed and |
| 51 | + $wgLanguageSelectorLanguages is set, that value will be used as a fallback. |
| 52 | + Setting $wgPolyglotLanguages to a shorter list may speed things up, |
| 53 | + especially on large wikis. |
| 54 | + |
| 55 | +* $wfPolyglotExcemptNamespaces: namespaces that should not have magic |
| 56 | + redirection applied by Polyglot. Per default, this includes the |
| 57 | + Category, Image, Template, MediaWiki, Media, and Special namespaces. |
| 58 | + There should be no reason to remove any of those, but you can add more |
| 59 | + namespaces to be left alone. |
| 60 | + |
| 61 | +* $wfPolyglotExcemptTalkPages: wether talk pages should be imune to magic |
| 62 | + redirection by Polyglot. true per default. |
| 63 | + |
| 64 | +* $wfPolyglotFollowRedirects: wether Polyglot should follow redirects it find |
| 65 | + as the target of a magic redirect. This is false per default; setting it to |
| 66 | + true causes an additional database lookup on every magic redirect, but it |
| 67 | + allows for locaized page titles. For example, Main_Page/de could redirect to |
| 68 | + Hauptseite, so people with the user language set to "de" (German) would end |
| 69 | + up on a page called "Hauptseite" instead of the clunky "Main_Page/de". |
| 70 | + NOTE: currently, the magic interlanguage links don't take into consideration |
| 71 | + this type of redirect. |
| 72 | + |
| 73 | +== Todo == |
| 74 | +* allowing users to define page localization links manually, using the standard |
| 75 | + interlanguage link syntax and mechanism. Which links are treated as |
| 76 | + interlanguage links is determiend by the interwiki table. An additional hook |
| 77 | + in Title.php would be needed to solved that. Or dummy entries in the |
| 78 | + interwiki table. |
| 79 | + |
| 80 | +* When following redirects ( base -> local -> target ), make the "redirected |
| 81 | + from" bit show both, the base and the refering page. Would require a skin |
| 82 | + hack. |
| 83 | + |
| 84 | +* When following redirects, look at the redirecting pages subpages and |
| 85 | + siblings, and merge the result with the target page's language links. |
Index: trunk/extensions/Polyglot/Polyglot.php |
— | — | @@ -0,0 +1,269 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Polyglot extension - automatic redirects based on user language |
| 5 | + * |
| 6 | + * Features: |
| 7 | + * * Magic redirects to localized page version |
| 8 | + * * Interlanguage links in the sidebar point to localized local pages |
| 9 | + * |
| 10 | + * This can be combined with LanguageSelector and MultiLang to provide more internationalization support. |
| 11 | + * |
| 12 | + * See the README file for more information |
| 13 | + * |
| 14 | + * @package MediaWiki |
| 15 | + * @subpackage Extensions |
| 16 | + * @author Daniel Kinzler, brightbyte.de |
| 17 | + * @copyright © 2007 Daniel Kinzler |
| 18 | + * @licence GNU General Public Licence 2.0 or later |
| 19 | + */ |
| 20 | + |
| 21 | +if( !defined( 'MEDIAWIKI' ) ) { |
| 22 | + echo( "This file is an extension to the MediaWiki software and cannot be used standalone.\n" ); |
| 23 | + die( 1 ); |
| 24 | +} |
| 25 | + |
| 26 | +$wgExtensionCredits['other'][] = array( |
| 27 | + 'name' => 'Polyglot', |
| 28 | + 'author' => 'Daniel Kinzler', |
| 29 | + 'url' => 'http://mediawiki.org/wiki/Extension:Polyglot', |
| 30 | + 'description' => 'support for content in multiple languages in a single mediawiki.', |
| 31 | +); |
| 32 | + |
| 33 | +/** |
| 34 | +* Set languages with polyglot support; applies to negotiation of interface language, |
| 35 | +* and to lookups for loclaized pages. |
| 36 | +* Set this to a small set of languages that are likely to be used on your site to |
| 37 | +* improve performance. Leave NULL to allow all languages known to MediaWiki via |
| 38 | +* $wgLanguageNames. |
| 39 | +* If the LanguageSelector extension is installed, $wgLanguageSelectorLanguages is used |
| 40 | +* as a fallback. |
| 41 | +*/ |
| 42 | +$wgPolyglotLanguages = NULL; |
| 43 | + |
| 44 | +/** |
| 45 | +* Namespaces to excempt from polyglot support, with respect to automatic redirects. |
| 46 | +* All "magic" namespaces are excempt per default. There should be no reason to change this. |
| 47 | +* Note: internationalizing templates is best done on-page, using the MultiLang extension. |
| 48 | +*/ |
| 49 | +$wfPolyglotExcemptNamespaces = array(NS_CATEGORY, NS_TEMPLATE, NS_IMAGE, NS_MEDIA, NS_SPECIAL, NS_MEDIAWIKI); |
| 50 | + |
| 51 | +/** |
| 52 | +* Wether talk pages should be excempt from automatic polyglot support, with respect to |
| 53 | +* automatic redirects. True per default. |
| 54 | +*/ |
| 55 | +$wfPolyglotExcemptTalkPages = true; |
| 56 | + |
| 57 | +/** |
| 58 | +* Set to true if polyglot should resolve redirects that are encountered when applying an |
| 59 | +* automatic redirect to a localized page. This requires additional database access every |
| 60 | +* time a locaized page is accessed. |
| 61 | +*/ |
| 62 | +$wfPolyglotFollowRedirects = false; |
| 63 | + |
| 64 | +///// hook it up ///////////////////////////////////////////////////// |
| 65 | +$wgHooks['ArticleFromTitle'][] = 'wfPolyglotArticleFromTitle'; |
| 66 | +$wgHooks['ParserAfterTidy'][] = 'wfPolyglotParserAfterTidy'; |
| 67 | +$wgHooks['SkinTemplateOutputPageBeforeExec'][] = 'wfPolyglotSkinTemplateOutputPageBeforeExec'; |
| 68 | + |
| 69 | +$wgExtensionFunctions[] = "wfPolyglotExtension"; |
| 70 | + |
| 71 | +function wfPolyglotExtension() { |
| 72 | + global $wgPolyglotLanguages; |
| 73 | + |
| 74 | + if ( $wgPolyglotLanguages === NULL ) { |
| 75 | + $wgPolyglotLanguages = @$GLOBALS['wgLanguageSelectorLanguages']; |
| 76 | + } |
| 77 | + |
| 78 | + if ( $wgPolyglotLanguages === NULL ) { |
| 79 | + $wgPolyglotLanguages = array_keys( $GLOBALS['wgLanguageNames'] ); |
| 80 | + } |
| 81 | +} |
| 82 | + |
| 83 | +function wfPolyglotArticleFromTitle( &$title, &$article ) { |
| 84 | + global $wfPolyglotExcemptNamespaces, $wfPolyglotExcemptTalkPages, $wfPolyglotFollowRedirects; |
| 85 | + global $wgLang, $wgTitle, $wgRequest; |
| 86 | + |
| 87 | + if ($wgRequest->getVal( 'redirect' ) == 'no') { |
| 88 | + return true; |
| 89 | + } |
| 90 | + |
| 91 | + $ns = $title->getNamespace(); |
| 92 | + |
| 93 | + if ( $ns < 0 |
| 94 | + || in_array($ns, $wfPolyglotExcemptNamespaces) |
| 95 | + || ($wfPolyglotExcemptTalkPages && Namespace::isTalk($ns)) ) { |
| 96 | + return true; |
| 97 | + } |
| 98 | + |
| 99 | + $n = $title->getDBKey(); |
| 100 | + $nofollow = false; |
| 101 | + |
| 102 | + //TODO: when user-defined language links start working (see below), |
| 103 | + // we need to look at the langlinks table here. |
| 104 | + |
| 105 | + if (!$title->exists() && strlen($n)>1 && preg_match('!/$!', $n)) { |
| 106 | + $t = Title::makeTitle($ns, substr($n, 0, strlen($n)-1)); |
| 107 | + $nofollow = true; |
| 108 | + } |
| 109 | + else { |
| 110 | + $lang = $wgLang->getCode(); |
| 111 | + $t = Title::makeTitle($ns, $n . '/' . $lang); |
| 112 | + } |
| 113 | + |
| 114 | + if (!$t->exists()) { |
| 115 | + return true; |
| 116 | + } |
| 117 | + |
| 118 | + if ($wfPolyglotFollowRedirects && !$nofollow) { |
| 119 | + $a = new Article($t); |
| 120 | + $a->loadPageData(); |
| 121 | + |
| 122 | + if ($a->mIsRedirect) { |
| 123 | + $rt = $a->followRedirect(); |
| 124 | + if ($rt && $rt->exists()) { |
| 125 | + //TODO: make "redirected from" show $source, not $title, if we followed a redirect internally. |
| 126 | + // there seems to be no clean way to do that, though. |
| 127 | + //$source = $t; |
| 128 | + $t = $rt; |
| 129 | + } |
| 130 | + } |
| 131 | + } |
| 132 | + |
| 133 | + if (!class_exists('PolyglotRedirect')) { |
| 134 | + class PolyglotRedirect extends Article { |
| 135 | + var $mTarget; |
| 136 | + |
| 137 | + function __construct( $source, $target ) { |
| 138 | + Article::__construct($source); |
| 139 | + $this->mTarget = $target; |
| 140 | + $this->mIsRedirect = true; |
| 141 | + } |
| 142 | + |
| 143 | + function followRedirect() { |
| 144 | + return $this->mTarget; |
| 145 | + } |
| 146 | + |
| 147 | + function loadPageData( $data = 'fromdb' ) { |
| 148 | + Article::loadPageData( $data ); |
| 149 | + $this->mIsRedirect = true; |
| 150 | + } |
| 151 | + } |
| 152 | + } |
| 153 | + |
| 154 | + //print $t->getFullText(); |
| 155 | + |
| 156 | + $article = new PolyglotRedirect( $title, $t ); //trigger redirect to lovcalized page |
| 157 | + |
| 158 | + return true; |
| 159 | +} |
| 160 | + |
| 161 | +function wfPolyglotGetLanguages( $title ) { |
| 162 | + global $wgPolyglotLanguages; |
| 163 | + if (!$wgPolyglotLanguages) return NULL; |
| 164 | + |
| 165 | + $n = $title->getDBKey(); |
| 166 | + $ns = $title->getNamespace(); |
| 167 | + |
| 168 | + $links = array(); |
| 169 | + |
| 170 | + foreach ($wgPolyglotLanguages as $lang) { |
| 171 | + $t = Title::makeTitle($ns, $n . '/' . $lang); |
| 172 | + if ($t->exists()) $links[$lang] = $t->getFullText(); |
| 173 | + } |
| 174 | + |
| 175 | + return $links; |
| 176 | +} |
| 177 | + |
| 178 | +function wfPolyglotParserAfterTidy( &$parser, &$text ) { |
| 179 | + global $wgPolyglotLanguages, $wfPolyglotExcemptNamespaces, $wfPolyglotExcemptTalkPages; |
| 180 | + global $wgContLang; |
| 181 | + |
| 182 | + if ( !$wgPolyglotLanguages ) return true; |
| 183 | + if ( !$parser->mOptions->getInterwikiMagic() ) return true; |
| 184 | + |
| 185 | + $n = $parser->mTitle->getDBKey(); |
| 186 | + $ns = $parser->mTitle->getNamespace(); |
| 187 | + $contln = $wgContLang->getCode(); |
| 188 | + |
| 189 | + $userlinks = $parser->mOutput->getLanguageLinks(); |
| 190 | + |
| 191 | + $links = array(); |
| 192 | + $pagelang = NULL; |
| 193 | + |
| 194 | + //TODO: if we followed a redirect, analyze the redirect's title too. |
| 195 | + // at least if wgPolyglotFollowRedirects is true |
| 196 | + |
| 197 | + if ( $ns >= 0 && !in_array($ns, $wfPolyglotExcemptNamespaces) |
| 198 | + && (!$wfPolyglotExcemptTalkPages || !Namespace::isTalk($ns)) ) { |
| 199 | + $ll = wfPolyglotGetLanguages($parser->mTitle); |
| 200 | + if ($ll) $links = array_merge($links, $ll); |
| 201 | + |
| 202 | + if (preg_match('!(.+)/(\w[-\w]*\w)$!', $n, $m)) { |
| 203 | + $pagelang = $m[2]; |
| 204 | + $t = Title::makeTitle($ns, $m[1]); |
| 205 | + if (!isset($links[$contln]) && $t->exists()) $links[$contln] = $t->getFullText() . '/'; |
| 206 | + |
| 207 | + $ll = wfPolyglotGetLanguages($t); |
| 208 | + if ($ll) { |
| 209 | + unset($ll[$pagelang]); |
| 210 | + $links = array_merge($links, $ll); |
| 211 | + } |
| 212 | + } |
| 213 | + } |
| 214 | + |
| 215 | + //TODO: would be nice to handle "normal" interwiki-links here. |
| 216 | + // but we would have to hack into Title::getInterwikiLink, otherwise |
| 217 | + // the links are not recognized. |
| 218 | + /* |
| 219 | + foreach ($userlinks as $link) { |
| 220 | + $m = explode(':', $link, 2); |
| 221 | + if (sizeof($m)<2) continue; |
| 222 | + |
| 223 | + $links[$m[0]] = $m[1]; |
| 224 | + } |
| 225 | + */ |
| 226 | + |
| 227 | + if ($pagelang) unset($links[$pagelang]); |
| 228 | + |
| 229 | + //print_r($links); |
| 230 | + |
| 231 | + $fakelinks = array(); |
| 232 | + foreach ($links as $lang => $t) { |
| 233 | + $fakelinks[] = $lang . ':' . $t; |
| 234 | + } |
| 235 | + |
| 236 | + $parser->mOutput->setLanguageLinks($fakelinks); |
| 237 | + return true; |
| 238 | +} |
| 239 | + |
| 240 | +function wfPolyglotSkinTemplateOutputPageBeforeExec($skin, $tpl) { |
| 241 | + global $wgOut, $wgContLang; |
| 242 | + |
| 243 | + $language_urls = array(); |
| 244 | + foreach( $wgOut->getLanguageLinks() as $l ) { |
| 245 | + if (preg_match('!^(\w[-\w]*\w):(.+)$!', $l, $m)) { |
| 246 | + $lang = $m[1]; |
| 247 | + $l = $m[2]; |
| 248 | + } |
| 249 | + else { |
| 250 | + continue; //NOTE: shouldn't happen |
| 251 | + } |
| 252 | + |
| 253 | + $nt = Title::newFromText( $l ); |
| 254 | + $language_urls[] = array( |
| 255 | + 'href' => $nt->getFullURL(), |
| 256 | + 'text' => $wgContLang->getLanguageName( $lang ), |
| 257 | + 'class' => 'interwiki-' . $lang, |
| 258 | + ); |
| 259 | + } |
| 260 | + |
| 261 | + if(count($language_urls)) { |
| 262 | + $tpl->setRef( 'language_urls', $language_urls); |
| 263 | + } else { |
| 264 | + $tpl->set('language_urls', false); |
| 265 | + } |
| 266 | + |
| 267 | + return true; |
| 268 | +} |
| 269 | + |
| 270 | +?> |
\ No newline at end of file |
Index: trunk/extensions/Polyglot/install.settings |
— | — | @@ -0,0 +1,2 @@ |
| 2 | +require_once( "{{path}}/Polyglot.php" ); |
| 3 | + |