User:Nike/iged

From translatewiki.net

Draft!!! Unfinished, please improve

Internationalisation guide for extension developers

Introduction

This is a guide directed for people writing extensions for MediaWiki and may be useful for MediaWiki developers too. Internationalisation (later i18n because the writer is lazy) is an integral part of developing high quality extensions. Using the so called best practises benefits everyone. Translations are happy and efficient when strings are easy to translate and stable. I18n coordinators are happy when the extensions follows common structure and integrating it to the translation system is almost instant. The developers itself gets a good feeling when translation begin to flow to his extensions in great amounts.

Starting up

Nowadays every extensions needs the basic i18n support, because every extensions ought to have translatable description message for Special:Version (Code listing 1).

Code listing 1: example extension first line: GoodExtension.php

<?php

$wgExtensionCredits['specialpage'][] = array(
	'name'           => 'GoodExtension',
	'version'        => '1.0',
	'author'         => 'Debby Developer',
	'descriptionmsg' => 'goodextension-desc', // New style description
	'url'            => 'http://www.mediawiki.org/wiki/Extension:GoodExtension',
);

Here we can already see first bits of i18n best practises.

BP1: Extension should have a translatable description message, usually prefix-desc.
BP2: All messages should use common prefix derived from the extension name, all in lower case. (few exceptions later)

BP1 is quite self-explanatory, as is BP2. Using a prefix is an important thing for two reasons: it avoids message name collisions, and it allows implementing efficient message autoloader in the future.

So now we have two messages already. We are developing a special page, and that page needs a name of course. By default it is the name of the special page, all in lower case. It is usually also same as the prefix, in case of extension having only single special page.

I18n file

MediaWiki relies a lot on autoloading and lazy initialization. Now let's go ahead and create a i18n file and register it.

Code listing 2: example i18n file: GoodExtension.i18n.php

<?php
/**
 * Translations of the GoodExtension extension.
 *
 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 */

$messages = array();

/** English
 * @author Debby Developer
 */
$messages['en'] = array(
	'goodextension'      => 'Good Extension',
	'goodextension-desc' => '[[Special:GoodExtension|Special page]] for demonstrating i18n',
);

Code listing 3: registering the i18n file and the special page

$dir = dirname(__FILE__) . '/';
$wgExtensionMessagesFiles['GoodExtension'] = $dir . 'GoodExtension.i18n.php';
$wgAutoloadClasses['SpecialGoodExtension'] = $dir . 'SpecialGoodExtension.php';
$wgSpecialPages['GoodExtension'] = 'SpecialGoodExtension';

So now we have created a i18n file named GoodExension.i18n.php and added a Special page class SpecialGoodExtension for autoloading.

BP3: Message file should be named ExtensionName.i18n.php
BP4: Message file should contain a header, line $messages = array() and English definitions as shown in code listing 2.

Simple Special Page

Now that we have basic things set up, let's create a simple special page.


Code listing 4: start of simple special page: SpecialGoodExtension.php
<?php
if (!defined('MEDIAWIKI')) die();
class SpecialGoodExtension extends SpecialPage {
	function __construct() {
		SpecialPage::SpecialPage( 'GoodExtension' );
	}

	public function execute( $parameters ) {
		wfLoadExtensionMessages( 'GoodExtension' );

		global $wgOut;
		$this->setHeaders();

		$this->addWikiMsg( 'goodextension-introduction' );
	}

}


Code listing 5: new message to GoodExtension.i18n.php
$messages['en'] = array(
	'goodextension'      => 'GoodExtension',
	'goodextension-desc' => '[[Special:GoodExtension|Special page]] for demonstrating i18n',

	'goodextension-introduction' => "Welcome. This is a simple ''wikitext'' string.",
);


Now we should have a working special page. Include it in MediaWiki by putting following code to LocalSettings.php:

require_once( "$IP/extensions/GoodExtension/GoodExtension.php" );

The relevant thing in this section is outputting text. We used an instance of OutputPage, which is a global variable $wgOut. If you just want to add simple wikitext message to output, OutputPage::addWikiMsg. It will handle parsing the message and all the i18n features happen behind the scenes. It is the preferred choice for multi sentence messages.

BP5: Use OutputPage::addWikiMsg for outputting explanatory messages that use wikitext.

Message variables

It is usually necessary to use variables in messages. MediaWiki allows using up to nine independent variables from $1 to $9 in a message. The most common thing to use a variable for is a number. In most languages numbers inflect in number, for example one car, but 2 cars. In MediaWiki you can (and should!) use one message for these kind of messages.

Let's go ahead and modify our introductory message to use a number as a variable.

//# GoodExtension.i18n.php

$messages['en'] = array(
	'goodextension'      => 'Good extension',
	'goodextension-desc' => '[[Special:GoodExtension|Special page]] for demonstrating i18n',

	'goodextension-introduction' => "Welcome. We have had $1 {{PLURAL:$1|visitor|visitors}} today.",
);

//# SpecialGoodExtension.php
	public function execute( $parameters ) {
		wfLoadExtensionMessages( 'GoodExtension' );

		global $wgOut, $wgLang;
		$this->setHeaders();

		$visitorCount = $wgLang->formatNum( rand( 0, 5 ) );
		$wgOut->addWikiMsg( 'goodextension-introduction', $visitorCount );
	}


Try it out few times and make sure it says 1 visitor but 2 visitors. The important thing is to always use {{PLURAL:count|singular|plural}}. Even if the number cannot be smaller than two! Don't let English make you think you don't need plural in some special cases. It is safe and thus suggested to use plural handling in all messages having whole numbers, even if in English doesn't use more than one form. In some languages there can be as many as 6 different forms, where for example numbers 2 and 8 can get a different form. For floating point numbers on the other hand, plural handling should not be used.

BP6: Always use plural handling if there is changing whole number.

Number formatting

 BP7: Always format numbers that user sees in normal use with Language::formatNum.

You probably noticed that we used a new global variable in last example: $wgLang. It represents a language object for the users interface language. There is also another language object $wgContLang, which represents the wikis content language. You need to use it in some special cases.

Take a minute to look at the some functions provided by Language class. There is many formatting methods from dates to file sizes. Using these functions when applicable ensures you are producing output in correct, i18ned format.

Whenever you output a number that user sees in normal use, you should format it with Language::formatNum. It can take whole numbers as well as integers. If you change the rand function above to produce value in thousand, you should see how the numbers are appropriately formatted for your language. It is important to format small numbers also, because some languages don't use arabic numerals (1…9), but a different kind of numbers. You would not like guessing which number is ১০২,২৭২, would you?(you are missing a font if you don't see number-like symbols here).

If you are critical reader, you might have noticed that we are passing a formatted number to {{PLURAL}}. That is totally fine, because it can normalise the number we just formatted for logic calculations. If you want to look what kind of plural codes there is around, browse the language files in languages/classes.