// This enum goes at the top of your script, it defines the numbers in the arrays without millions of defines. Adding new ones is dead easy as you just add them before the last entry, all numbers auto update (enums are awesome!)// These are my texts (I cba to edit it) - you need to change the names to the names you use in your language files:enum{ TEXT_MAIN_DISALLOW, TEXT_MAIN_BLEVEL, TEXT_MAIN_LOADED, TEXT_MAIN_UNLOAD, TEXT_MAIN_COM_HELP, TEXT_MAIN_COM_NONE, TEXT_MAIN_COM_INFO, TEXT_MAIN_COM_MORE, TEXT_MAIN_COM_COMM, TEXT_MAIN_HELP_HELP, TEXT_MAIN_HELP_UNKNOWN, TEXT_MAIN_HELP_1, TEXT_MAIN_HELP_2, TEXT_MAIN_HELP_3, TEXT_MAIN_HELP_4, TEXT_MAIN_HELP_5, TEXT_MAIN_HELP_6, TEXT_MAIN_HELP_7, TEXT_MAIN_LOAD_HELP, TEXT_MAIN_LOAD_ERR, TEXT_MAIN_UN_HELP_1, TEXT_MAIN_UN_HELP_2, TEXT_MAIN_UN_NONE, TEXT_MAIN_UN_INV, TEXT_MAIN_LIST_HELP, TEXT_MAIN_LIST_NONE, TEXT_MAIN_LIST_LIST, TEXT_MAIN_LIST_MORE, TEXT_MAIN_SET_HELP_1, TEXT_MAIN_SET_HELP_2, TEXT_MAIN_SET_HELP_3, TEXT_MAIN_SET_HELP_4, TEXT_MAIN_SET_ENTER, TEXT_MAIN_SET_SET, TEXT_MAIN_SET_NONE, TEXT_MAIN_REL_HELP, TEXT_MAIN_REL_DONE, TEXT_MAIN_LANG_HELP_1, TEXT_MAIN_LANG_HELP_2, TEXT_MAIN_LANG_SET, TEXT_MAIN_LANG_ERROR, TEXT_MAIN_LANG_LIST, YS_TEXT_STRING_COUNT // MUST be last in the enum!}// This code just checks the passed strings against all the existing ones and puts it in the correct position in the array (it's fully dynamic to add more to the enum and here, order doesn't matter at all:ReadLanguages(str[], i){ GET_LANG(TEXT_MAIN_DISALLOW, 18) //printf("str = %s", str); // This is basically the code GET_LANG() is: /*if (str[0] && strcmp(str, "TEXT_MAIN_DISALLOW", true, 18) == 0 && (str[18] == ' ' || str[18] == '=' || str[18] == '\t')) { printf("value: %s", ReturnINIValue(str[18])); strmid(gLangStrings[i][TEXT_MAIN_DISALLOW], StarCheck(ReturnINIValue(str[18])), 0, MAX_STRING); StripNL(gLangStrings[i][TEXT_MAIN_DISALLOW]); }*/ else GET_LANG(TEXT_MAIN_BLEVEL, 16) else GET_LANG(TEXT_MAIN_LOADED, 16) else GET_LANG(TEXT_MAIN_UNLOAD, 16) else GET_LANG(TEXT_MAIN_COM_HELP, 18) else GET_LANG(TEXT_MAIN_COM_NONE, 18) else GET_LANG(TEXT_MAIN_COM_INFO, 18) else GET_LANG(TEXT_MAIN_COM_MORE, 18) else GET_LANG(TEXT_MAIN_COM_COMM, 18) else GET_LANG(TEXT_MAIN_HELP_HELP, 19) else GET_LANG(TEXT_MAIN_HELP_UNKNOWN, 22) else GET_LANG(TEXT_MAIN_HELP_1, 16) else GET_LANG(TEXT_MAIN_HELP_2, 16) else GET_LANG(TEXT_MAIN_HELP_3, 16) else GET_LANG(TEXT_MAIN_HELP_4, 16) else GET_LANG(TEXT_MAIN_HELP_5, 16) else GET_LANG(TEXT_MAIN_HELP_6, 16) else GET_LANG(TEXT_MAIN_HELP_7, 16) else GET_LANG(TEXT_MAIN_LOAD_HELP, 19) else GET_LANG(TEXT_MAIN_LOAD_ERR, 18) else GET_LANG(TEXT_MAIN_UN_HELP_1, 19) else GET_LANG(TEXT_MAIN_UN_HELP_2, 19) else GET_LANG(TEXT_MAIN_UN_NONE, 17) else GET_LANG(TEXT_MAIN_UN_INV, 16) else GET_LANG(TEXT_MAIN_LIST_HELP, 19) else GET_LANG(TEXT_MAIN_LIST_NONE, 19) else GET_LANG(TEXT_MAIN_LIST_LIST, 19) else GET_LANG(TEXT_MAIN_LIST_MORE, 19) else GET_LANG(TEXT_MAIN_SET_HELP_1, 20) else GET_LANG(TEXT_MAIN_SET_HELP_2, 20) else GET_LANG(TEXT_MAIN_SET_HELP_3, 20) else GET_LANG(TEXT_MAIN_SET_HELP_4, 20) else GET_LANG(TEXT_MAIN_SET_ENTER, 19) else GET_LANG(TEXT_MAIN_SET_SET, 17) else GET_LANG(TEXT_MAIN_SET_NONE, 18) else GET_LANG(TEXT_MAIN_REL_HELP, 18) else GET_LANG(TEXT_MAIN_REL_DONE, 18) else GET_LANG(TEXT_MAIN_LANG_HELP_1, 21) else GET_LANG(TEXT_MAIN_LANG_HELP_2, 21) else GET_LANG(TEXT_MAIN_LANG_SET, 18) else GET_LANG(TEXT_MAIN_LANG_ERROR, 20) else GET_LANG(TEXT_MAIN_LANG_LIST, 19)}// This is the main processing code - because of the way my system works there are two sets of text arrays - one for module text the other for common text but you can easily change this. It also uses some 0.2 functions but you can swap these for either array reads (e.g. for the GetLenguage call - just read an array instead new gLangNames[MAX_LANGUAGES][4]; new gLangStrings[MAX_LANGUAGES][YS_TEXT_STRING_COUNT][MAX_STRING]; new gMsgPrefix[16] = "*** "; new gOldPrefix[sizeof (gMsgPrefix)]; enum { TEXT_COMO_NO_USER, TEXT_COMO_INVALID, TEXT_COMO_MULTI, TEXT_COMO_MATCH, TEXT_COMO_NO_SPEC, TEXT_COMO_NO_LANG, TEXT_COMO_IDENT, TEXT_COMO_CANT_1, TEXT_COMO_CANT_2, YS_INTERNAL_TEXT_STRING_COUNT } static gYSSInternalText[MAX_LANGUAGES][YS_INTERNAL_TEXT_STRING_COUNT][MAX_STRING]; #define GET_LANG_I(%1,%2) if (strcmp(str, "%1", true, (%2)) == 0 && (str[(%2)] == ' ' || str[(%2)] == '=' || str[(%2)] == '\t')) { strmid(gYSSInternalText[i][%1], StarCheck(ReturnINIValue(str[(%2)])), 0, MAX_STRING); /*StripNL(gYSSInternalText[i][%1]);*/ } StarCheck(string[]) { new str[MAX_STRING]; if (strcmp(string, gMsgPrefix, true, strlen(gMsgPrefix)) == 0) { strmid(str, string, 0, strlen(string)); } else if (gOldPrefix[0] && strcmp(string, gOldPrefix, true, strlen(gOldPrefix)) == 0) { format(str, sizeof (str), "%s%s", gMsgPrefix, string[strlen(gOldPrefix)]); } else { format(str, sizeof (str), "%s%s", gMsgPrefix, string); } return str; } MainGetText() { new fname[13], i = 0; while (gLangNames[i][0]) { format(fname, sizeof (fname), "YSS_lang.%s", gLangNames[i]); new File:bla = fopen(fname, io_read); if (bla) { new str[256], area = 0; while (fread(bla, str)) { StripNL(str); if (str[0]) { if (str[0] == '[') area = 0; if (!area) { if (strcmp(str, "[common]", true) == 0) { area = 1; } else { format(fname, sizeof (fname), "[%s]", MODULE_NAME); if (strcmp(str, fname, true) == 0) { area = 2; } else { area = 0; } } } else if (area == 1) { GET_LANG_I(TEXT_COMO_NO_USER, 17) else GET_LANG_I(TEXT_COMO_INVALID, 17) else GET_LANG_I(TEXT_COMO_MULTI, 15) else GET_LANG_I(TEXT_COMO_MATCH, 15) else GET_LANG_I(TEXT_COMO_NO_SPEC, 17) else GET_LANG_I(TEXT_COMO_NO_LANG, 17) else GET_LANG_I(TEXT_COMO_IDENT, 15) else GET_LANG_I(TEXT_COMO_CANT_1, 16) else GET_LANG_I(TEXT_COMO_CANT_2, 16) //else GET_LANG_I(TEXT_COMO_DEF_RET, 15) } else if (area == 2) { ReadLanguages(str, i); } } } } i++; } } GetPlayerLangText(playerid, text, internal = 0) { new lang; // I've sacrificed efficiency for nice code, the remote function is only called once lang = CallRemoteFunction("ReturnPlayerLanguageID", "i", playerid); // Default language handled there static LastError[MAX_PLAYERS]; new str[MAX_STRING] = " "; new time = GetTickCount(); if (internal) { if (gYSSInternalText[lang][text][0]) { return gYSSInternalText[lang][text]; } else if (gYSSInternalText[0][text][0]) { // Default to (should be) english. if (time - LastError[playerid] >= 10000) { // Only warn every 10s LastError[playerid] = time; YS_ERROR_1(TEXT_COMO_NO_SPEC); } return gYSSInternalText[0][text]; } // Default to (should be) english. if (time - LastError[playerid] >= 10000) { // Only warn every 10s LastError[playerid] = time; YS_ERROR_1(TEXT_COMO_NO_LANG); } return str; } if (gLangStrings[lang][text][0]) { return gLangStrings[lang][text]; } else if (gLangStrings[0][text][0]) { // Default to (should be) english. if (time - LastError[playerid] >= 10000) { // Only warn every 10s LastError[playerid] = time; YS_ERROR_1(TEXT_COMO_NO_SPEC); } return gLangStrings[0][text]; } // Default to (should be) english. if (time - LastError[playerid] >= 10000) { // Only warn every 10s LastError[playerid] = time; YS_ERROR_1(TEXT_COMO_NO_LANG); } return str; //new str[MAX_STRING], lang; }// You will have noticed alot of defines in there, these are basically the wrappers which call the functions and are here (obviously they should be first):#define GET_LANG(%1,%2) if (strcmp(str, "%1", true, (%2)) == 0 && (str[(%2)] == ' ' || str[(%2)] == '=' || str[(%2)] == '\t')) { strmid(gLangStrings[i][%1], StarCheck(ReturnINIValue(str[(%2)])), 0, MAX_STRING); }#define YS_ERROR(%1) SEND_FORMATTED_MESSAGE(playerid, YSS_ERROR_COLOR, GetPlayerLangText(playerid, %1))#define YS_MESSAGE(%1) SEND_FORMATTED_MESSAGE(playerid, YSS_MESSAGE_COLOR, GetPlayerLangText(playerid, %1))#define YS_ERROR_1(%1) SEND_FORMATTED_MESSAGE(playerid, YSS_ERROR_COLOR, GetPlayerLangText(playerid, %1, 1))#define YS_MESSAGE_1(%1) SEND_FORMATTED_MESSAGE(playerid, YSS_MESSAGE_COLOR, GetPlayerLangText(playerid, %1, 1))#define YS_ERROR_2(%1,%2) SEND_FORMATTED_MESSAGE(%1, YSS_ERROR_COLOR, GetPlayerLangText(%1, %2))#define YS_MESSAGE_2(%1,%2) SEND_FORMATTED_MESSAGE(%1, YSS_MESSAGE_COLOR, GetPlayerLangText(%1, %2))#define TEXT(%1) YS_MESSAGE(TEXT_%1)#define ERROR(%1) YS_ERROR(TEXT_%1)#define RAW_ERROR(%1,%2) SEND_FORMATTED_MESSAGE(%1, YSS_ERROR_COLOR, %2)#define RAW_TEXT(%1,%2) SEND_FORMATTED_MESSAGE(%1, YSS_MESSAGE_COLOR, %2)#define YS_HELP(%1) YS_MESSAGE(%1)#define GET_TEXT(%1,%2) GetPlayerLangText(%1, %2)#define GTEXT(%1) GET_TEXT(playerid, TEXT_%1)#define SEND_FORMATTED_MESSAGE(%1,%2,%3) CallRemoteFunction("SendFormattedMessage", "iis", %1, %2, %3)#define SEND_FORMATTED_MESSAGE_TO_ALL(%1,%2) CallRemoteFunction("SendFormattedMessageToAll", "is", %1, %2)#define SEND_FORMATTED_GAME_MESSAGE(%1,%2,%3,%4) CallRemoteFunction("GameFormattedMessage", "isii", %1, %2, %3, %4)#define SEND_FORMATTED_GAME_MESSAGE_TO_ALL(%1,%2,%3) CallRemoteFunction("GameFormattedMessageToAll", "sii", %1, %2, %3)#define YSS_ERROR_COLOR 0xFF0000AA#define YSS_MESSAGE_COLOR 0x00FFFFAA#define LG_MSG_COLOR YSS_ERROR_COLOR// There's a whole load there and a lot won't be needed - the FormattedMessage calls can be replaced with standard text calls, that's just my processor for my %n system// ReturnINIValue is part of my INI processing code nicked for this as it's a similar format (item = value), it also supports a multitude of things between the name and value (must have at least a space but can also have more spaces, tabs and equals signs (not required though). ReturnINIValue(string[], &start = 0) { new retstr[MAX_INI_LINE] = ""; new i = 0, j; j = strlen(string); while ((string[i] == ' ' || string[i] == '=' || string[i] == '\t') && i < j) { i++; } if (i < j) { strmid(retstr, string, i, j); start += i; } return retstr; }// I'm not sure if this uses GetININame (quite possibly) so: GetININame(name[], str[], len = MAX_FUNC_NAME) { new i; while (str[i] != ' ' && str[i] != '=' && str[i] != '\t' && str[i] != 0 && i < len) // Added overflow error checking to the checks { name[i] = str[i]; i++; } return i; }