A while ago I decided to make a plugin that could show a menu to the player containing all of the available weapons for each slot. Since TF2 Items Info was dead I decided to use TF2 Items Database instead. That plguin is currently running in my server, though it is working and is very functional when I finished it I felt like it was incomplete since my native language is spanish and I run the tf2 client in that language so, it felt a little out of place.
I started to analyze how I could translate the names of those items to spsnish, the first thing that came into my head was to use the included translation system in sourcemod, but if you think about it, it is a little silly since all of those translations are already in the tf2 translation files. The way that valve stores translations in tf2 is pretty easy to understand, you have a lot of files in the tf2/resource folder that match this filename "tf_(Only allow characters from a-Z).txt", those are translation files and they follow this basic structure
Code:
"lang"
{
"Language" "spanish"
"Tokens"
{
"TF_ClassSkill_Heavy" "HEAVY: Girar el cañón de la ametralladora"
"[english]TF_ClassSkill_Heavy" "HEAVY: Spin Minigun Barrel"
"TF_zoomin_broom" "Burla: La Nimbus Mercenaria"
"[english]TF_zoomin_broom" "Taunt: Zoomin' Broom"
}
}
Knowing that, it was just a matter of parsing all of the files and storing the result somewhere to retrive a tarnslation to any language using a token (what I call KeyPhrase, TF_zoomin_broom). The first thing that came to my head was to solve that problem using the integrated sourcemod KV system, That was absolutely not a good idea beacuse the translation files take up 65~mb of space in the hardrive, so using that was not an option becuase it could screw up any server as tf2 items info did.
I finally settled down on creating an sqlite databse and store all of the translations there. This is a much cleaner and a faster way to do it, the only problem is that it'd be more complicated to setup than any plugin if I planned to parse the files and buid the database outside of sourcemod, which I did, the database builder was programmed in Python and it takes about 15-25 seconds to build it. It's not as fast as I would like to but it works and you only have to run it when you update the server (assuming that you have python installed in your server, it comes included with some distos).
Yesterday I kinda finished setting up the natives and testing for memory leaks. It works, and it's pretty fast (using threaded queries) considering that the final database has 12072 rows (phrases) and 26 columns (languages). I made a test plugin and this is the output.
Code:
] sm_debug_Tgetphraserow TF_zoomin_broom
[TFtDB Test] Searching KeyPhrase in the DB...
[TFtDB Test] Results for TF_zoomin_broom:
[TFtDB Test] brazilian. Provocação: Vassoura Veloz
[TFtDB Test] bulgarian. Присмех: Профучаващата метла
[TFtDB Test] czech. Taunt: Zoomin' Broom
[TFtDB Test] danish. --not translated to this language--
[TFtDB Test] dutch. Bespotting: Bliksemse Bezem
[TFtDB Test] English. Taunt: Zoomin' Broom
[TFtDB Test] finnish. Pilkka: Lähtölakaisu
[TFtDB Test] french. Raillerie : Le balais filant
[TFtDB Test] german. Verspottung: Brausender Besen
[TFtDB Test] greek. Χλευασμός: Zoomin' Broom
[TFtDB Test] hungarian. Beszólás: Seprűn söprés
[TFtDB Test] italian. --not translated to this language--
[TFtDB Test] japanese. --not translated to this language--
[TFtDB Test] korean. --not translated to this language--
[TFtDB Test] norwegian. --not translated to this language--
[TFtDB Test] polish. Drwina: Lot na Miotle
[TFtDB Test] portuguese. Provocação: Vassoura Veloz
[TFtDB Test] romanian. Taunt: Zoomin' Broom
[TFtDB Test] russian. Насмешка: Улётные мётлы
[TFtDB Test] schinese. 嘲讽:扫帚飞行术
[TFtDB Test] spanish. Burla: La Nimbus Mercenaria
[TFtDB Test] swedish. Hån: Hast på Kvast
[TFtDB Test] tchinese. 嘲諷:飛翔掃帚
[TFtDB Test] turkish. Alay: Yakınlaşan Süpürge
[TFtDB Test] ukrainian. Кепкування: політ на мітлі
[TFtDB Test] Finished.
The natives can be implemented in any plugin that you might be building to make it more dynamic and to fit a wider audience. Some natives are:
Spoiler
PHP Code:
/** * Returns an ADT array containing the languages in the order of the db. * Close it when you are done with it or put it into a global variable and * don't call us again. * * @return Language Database Array. */ native Handle TFtDB_GetLangList()
/** * Returns number of languages available in the database. * * @return Number of languages available in the database. */ native int TFtDB_GetNumbOfLangs()
/** * Returns the size of the largest string of all of the key phrases. * * @return The size of the largest string in the KeyPhrase column. */ native int TFtDB_GetKeyPhrase_MaxLength()
/** * Checks if the language exist in the TFtranslations database. * * @param langname Name of the language. * @return True if the language exist in the database false otherwise. */ native bool TFtDB_IsLanguageInDB_byName(char[] langname);
/** * Checks if the language exist in the TFtranslations database * using a language index returned by the GetClientLanguage() * fucntion. * * @param index The language index. * @param bufferLangname Buffer to store the language name. * @param buffersize Size of the buffer. * @return True if the language exist in the database false otherwise. * @error Invalid language index. */ native bool TFtDB_IsLanguageInDB_byindex(int index, char[] bufferLangname = "", int buffersize = 0);
/** * Returns an ADT array handle containing the translated phrase sized properyl given a * language index and a KeyPhrase. Close the handle when you are done with it. * * @param index The language index. * @param KeyPhrase The KeyPhrase. * @param phrasesize Buffer to store the size of the string stored in the database. * @param result Optional var to store the results of the returned value. * @param langindb Set to true if the language was found. * @param isphraseindb Set to true if the phrase was found even if langindb is false. * @return An ADT array handle containing the string sized properly at index 0. * @error Invalid language index. */ native Handle TFtDB_RequestKeyPhraseTranslation_byIndex(int index, const char[] KeyPhrase, int &phrasesize = 0, DBResult &result = DBVal_Error, bool &langindb = false, bool &isphraseindb = false);
/** * Executes a threaded query that calls a function in your plugin when finished, * returning a string that contains the translated phrase, moving the query * to a separed thread to avoid interrupting gameplay, you can pass any kind of data * to the callback, you can pass datapacks if you want. Close the DataPack when you are done with it. * * @param index The language index. * @param KeyPhrase The KeyPhrase. * @param callback The callback function. * @param TestPhraseIfLangNotFound Continue the query to find out if the phrase exists in the databse * even if the language is not avilable. * @param prio The Threaded query priority. * @param data data to pass to the callback. * @return true if the language is avilable in the database, false otherwise. * @error Invalid language index. */ native bool TFtDB_TRequestKeyPhraseTranslation_byIndex(int index, const char[] KeyPhrase, TFtDBQT Callback, bool TestPhraseIfLangNotFound = false, DBPriority prio = DBPrio_Normal, any data = 0);
/** * Executes a threaded query that calls a function in your plugin when finished, * returning an array handle that contains the translated phrases, moving the query * to a separed thread to avoid interrupting gameplay, you can pass any kind of data * to the callback, you can pass datapacks ifyou want. * * @param KeyPhrase The KeyPhrase. * @param callback The callback function. * @param prio The Threaded query priority. * @param data data to pass to the callback. * @noreturn */ native void TFtDB_TRequestRow_byKeyphrase(const char[] KeyPhrase, TFtDBQTRow Callback, DBPriority prio = DBPrio_Normal, any data = 0);
Please keep in mind that this a work in progress and I WILL add (custom query) and remove natives becuse some of those seem to redundant. The main focus of this was on the functionality of the plugin itself so that's why I tried not to get into a lot of technical details.
I just wanted ask if you consider this plugin to be useful or just a huge waste of time D: . I hugely appreciate your feedback so don't be shy! Tell me what you think and thanks a lot for reading this, have a great day.