[Include] i_loc - Reference-based fast lightweight script localization system
#1

i_loc
Download
About
This include provides a way to localize your script, i.e. present messages in different languages to different players, based on their language setting. This include can be used for gamemodes and filterscripts of any size, and doesn't require additional files. All translations will be stored in the AMX file.

Introduction
I tried to come up with the easiest way to add localization to an already established script, and found a possible solution. In Pawn, all global variables have a unique address representing them, and if I get this address, I can use it to assign an identity to any variable, or any global string allocated in Pawn. Thanks to this, you don't have to specify any "ID" to represent each text in your script, and no string comparison is done when looking for translation.

Configuration
Before you include this in your script, you have to define LOC_MAX_TEXTS and LOC_NUM_LOCALIZATIONS. LOC_MAX_TEXTS is the maximum number of localized texts in your script; if you set it too low, the include will produce an error if you try to register more translations. LOC_NUM_LOCALIZATIONS is the number of localizations you wish to use in your script (not counting the default one).

In addition, you can specify a number of optional constants. LOC_MAX_TEXT_LENGTH (default 144) must be set if you plan to use larger strings. As the player's selected language is stored in a PVar, you can set LOC_LANG_PVAR to change the PVar name, or LOC_LANG_FUNC to specify a different method of obtaining player's language. LOC_FILTER_FUNC can be defined for a case when a replacement is not available, and you want to process the message in some other way. You can also define LOC_PACKED to specify the default parameter for other functions.

Usage
All localizable strings must be defined globally, and only passed via their variables. Moving all your texts to one place will make your script more consistent and well-arranged, and if you reuse some strings, they will not require more memory than they need.

Code:
//Mandatory constants
#define LOC_MAX_TEXTS 4 //if you try to use more than 4 texts, an error is produced and the script is halted
#define LOC_NUM_LOCALIZATIONS 2 //use 2 additional languages

#include "i_loc.inc"

//Do not use "stock". This will inform you if your text is not in use. With "const", you will not accidentally modify the text.
new const TEXT_WELCOME[] = "Welcome!";
new const TEXT_YES[] = "Yes.";
new const TEXT_NO[] = "No.";
new const TEXT_GOODBYE[] = "Goodbye!";

//Optionally use constants for language identifiers.
#define LANG_ENGLISH 0
#define LANG_GERMAN 1
#define LANG_FRENCH 2

//In this function, we register all translations for the texts. This should be called exactly once in your script.
stock RegisterTranslations()
{
    //These in-line defines help maintain the code. You can call "RegisterTranslation" directly, but this will make your code smaller and easier to understand.

    //German
    #define LOC[%0,%1] RegisterTranslation(LANG_GERMAN,TEXT_%0,%1)
    LOC[WELCOME, "Wilkommen!"];
    LOC[YES, "Ja."];
    LOC[NO, "Nein."];
    LOC[GOODBYE, "Auf Wiedersehen!"];
    #undef LOC
    
    //French
    #define LOC[%0,%1] RegisterTranslation(LANG_FRENCH,TEXT_%0,%1)
    LOC[WELCOME, "Salut!"];
    LOC[YES, "Oui."];
    LOC[NO, "Non."];
    LOC[GOODBYE, "Au revoir!"];
    #undef LOC
}

public OnFilterScriptInit()
{
    RegisterTranslations();
    
    SetPlayerLanguage(0, LANG_ENGLISH); //player 0 will get English messages
    SetPlayerLanguage(1, LANG_GERMAN); //player 1 will get German messages
    SetPlayerLanguage(2, LANG_FRENCH); //player 2 will get French messages
    SendClientMessageToAllLoc(-1, TEXT_WELCOME);
}
"Welcome!" is displayed to player 0, player 1 sees "Wilkommen!", and player 2 sees "Salut!"

Functions
Code:
RegisterTranslation(language, const text[], const replacement[LOC_MAX_TEXT_LENGTH])
Registers a new replacement text for a specific language. language must be between 1 and LOC_NUM_LOCALIZATIONS, and you cannot use 0 (the default language). If you attempt to register over an already existing translation, it will be modified.

Code:
SetPlayerLanguage(playerid, language)
Sets a player's language. Only available when LOC_LANG_FUNC is not specified.

Code:
GetPlayerLanguage(playerid)
Returns a player's language. Always available.

Code:
GetLocText(playerid, const text[], rettext[], bool:packed=LOC_PACKED, maxlength=sizeof(rettext))
Gets the replacement of a text, for a specific player, if possible. On success, returns true and fills rettext. If packed is specified, the result string is packed (packed strings save ~75 % of memory).

Code:
LocText(playerid, const text[], bool:packed=LOC_PACKED)
Returns the localized text, for a specific player, or the one passed in text. The default buffer is used.

Code:
GetTextReplacement(language, const text[], rettext[], bool:packed=LOC_PACKED, maxlength=sizeof(rettext))
Gets the replacement of a text, for a specific language, if possible. On success, returns true and fills rettext.

Code:
TextReplacement(language, const text[], bool:packed=LOC_PACKED)
Returns the localized text, for a specific language, or the one passed.

Code:
SendClientMessageLoc(playerid, color, const message[])
Sends a localized message to a player.

Code:
SendClientMessageToAllLoc(color, const message[])
Sends a localized message to all players.

Code:
SendClientMessageFormatLoc(playerid, color, const format[], {Float,_}:...)
Sends a localized formatted message to a player. SendClientMessageFormat must be available to use this function.

Code:
GameTextForPlayerLoc(playerid, const string[], time, style)
Displays a localized text for a player.

Code:
LOC_LANG_FUNC(playerid)
If you define LOC_LANG_FUNC, it must have this signature, and return the player's language.

Code:
LOC_FILTER_FUNC(language, const text[], rettext[LOC_MAX_TEXT_LENGTH])
LOC_FILTER_FUNC must be defined with this signature. It should return true if the text was modified, or false to use the original text in caller functions.

If you do not specify any of the optional constants, they will be defined after you include the script.

Download
At the top of this topic.
Reply
#2

Awesome one!
It could be handy for multi-language servers.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)