surva network logo

surva network: development blog

Articles and news about Minecraft: Bedrock Edition / PocketMine server development

Using automatic language detection for translated messages in PocketMine-MP plugins

Many plugins provide a translation feature where the language of the plugin messages can be set server-wide in the config file. To enhance the experience for players, a better way is to automatically detect which language the player has set on its device and send translated messages for each player's own language.

Getting the language short code

This is possible using the getLocale() method of the player or command sender object, like in this example:

echo $player->getLocale();

This will simply print the player's locale (language and country) to the server console.

Example output:

en_US
de_DE

First one is English (US), second one is German (Germany).

For a simple translation, it's enough to know the language without the country, so we just cut this off:

preg_match("/^[a-z][a-z]/", $player->getLocale(), $language);

echo $language;

Will now output: en and de.

Using language files

Let's look at a full example on how to handle translations in a plugin.

We'll store our translations as .yml files in the resources/languages directory.

First, some default values should be defined, and we'll use an array to store the language config files:

public const AVAILABLE_LANGUAGES = ["en", "de"];
public const DEFAULT_LANGUAGE = "en";

/**
 * @var \pocketmine\utils\Config[] translation files
 */
private array $translations;

We'll also need a method to load the language files into the array:

/**
 * Load all language translation files
 *
 * @return void
 */
public function loadTranslationFiles(): void
{
    foreach (self::AVAILABLE_LANGUAGES as $availableLanguage) {
        $this->translations[$availableLanguage] = new Config(
            $this->getFile() . "resources/languages/" . $availableLanguage . ".yml"
        );
    }
}

Now, we can write the most important function which will detect the players language and load the translation value from the correct language file:

/**
 * Get a translated message
 *
 * @param  \pocketmine\player\Player  $player
 * @param  string  $key
 *
 * @return string
 */
public function getMessage(Player $player, string $key): string
{
    preg_match("/^[a-z][a-z]/", $player->getLocale(), $language);

    if (count($language) > 0 && isset($this->translations[$language[0]])) {
        return $this->translations[$language[0]]->getNested($key);
    } else {
        return $this->translations[self::DEFAULT_LANGUAGE]->getNested($key);
    }
}

The first line is already known, it is extracting the language short code. The following if-statement checks if there is a language file for the player's preferred language. If not, the default language (defined above) will be used.

Here is an example on how you could use the translation method:

/**
 * Send a welcome message when a player joins the game
 *
 * @param  \pocketmine\event\player\PlayerJoinEvent  $event
 *
 * @return void
 */
public function onPlayerJoin(PlayerJoinEvent $event): void
{
    $player = $event->getPlayer();

    $player->sendMessage($this->getMessage($player, "join.welcome"));
}

This code in the event listener class sends a translated welcome message to every player at game join.

Translation files in the resources/languages directory could look like this:

en.yml:

join:
    welcome: "Welcome on our server!"

de.yml:

join:
    welcome: "Willkommen auf unserem Server!"