{"id":1047,"date":"2020-07-16T17:52:26","date_gmt":"2020-07-16T16:52:26","guid":{"rendered":"https:\/\/hudecekpetr.cz\/?p=1047"},"modified":"2020-07-16T17:52:26","modified_gmt":"2020-07-16T16:52:26","slug":"multilanguage-game-using-gnu-gettext-in-c","status":"publish","type":"post","link":"https:\/\/hudecekpetr.cz\/cs\/multilanguage-game-using-gnu-gettext-in-c\/","title":{"rendered":"Multilanguage game using GNU gettext in C#"},"content":{"rendered":"<p class=\"qtranxs-available-languages-message qtranxs-available-languages-message-cs\"> <\/p><p>I\u2019m currently creating <a href=\"https:\/\/hudecekpetr.cz\/najdi-cestu-ven\/\">a video game in C#\/Monogame<\/a>. It started off in Czech, but I wanted it to be available in Czech and English, so I set out to add multilanguage support to the game.<\/p>\n<p>In this article, I\u2019ll explain why I chose <a href=\"https:\/\/www.gnu.org\/software\/gettext\/\">gettext<\/a> and how I added it to my C# game.<!--more--><\/p>\n<h2>Motivation<\/h2>\n<p>I knew I wanted this:<\/p>\n<ul>\n<li>Translate UI elements such as buttons<\/li>\n<li>Translate names and descriptions of in-game items<\/li>\n<li>Translate dialogue lines<\/li>\n<\/ul>\n<p>There are few translation systems that handle all three points well.<\/p>\n<p>The first two lend themselves to systems where the source code contains an identifier and you have configuration files (Windows .ini, XML, JSON, whatever) for each language that assign translations to these identifiers. For example, the source code could have \u201cDrawString(warnInsufficientGold)\u201d and the configuration file would have a line with \u2018warnInsufficientGold : \u201cYou don\u2019t have enough enough gold.\u201d\u2019.<\/p>\n<p>But this is a problem for dialogue lines. A game can have thousands of lines of dialogue and it\u2019s a lot of useless effort to create identifiers for each line. They\u2019d be confusing, too. This doesn\u2019t read well:<\/p>\n<p><em>AddDialogue(namePaladin, paladinHello);<\/em><br \/>\n<em>AddDialogue(nameCivilian, civilianAsksForHelp);<\/em><br \/>\n<em>AddDialogue(namePaladin, paladinOffersHelp);<\/em><\/p>\n<p>But this is better:<\/p>\n<p><em>AddDialogue(\u201cArthur\u201d, \u201cGreetings, citizen. What troubles you?\u201d);<\/em><br \/>\n<em>AddDialogue(\u201cCivilian\u201d, \u201cThere\u2019s\u2026 ogres! They\u2019ve entered the village!\u201d);<\/em><br \/>\n<em>AddDialogue(\u201cArthur\u201d, \u201cSay no more, and point the way! My steed and I will come to your aid.\u201d);<\/em><\/p>\n<p>The other approach would be to use some domain-specific language for the dialogue, some kind of script files. The script file would contain dialogue, or potentially even dialogue trees, as pure text, with no identifiers. Reading the script file would allow you to understand the dialogue, and would be quick to write.<\/p>\n<p>But then either each language would have to have a separate script file, in which case synchronization might be difficult, or all languages would be in the same script file, and it wouldn\u2019t be easy to read and edit (without specialized tools).<\/p>\n<p>So I chose the <em>gettext<\/em> approach which contains one translation (the canonical translation) in the source code and for each other language, you maintain a translation file that matches the canonical translation to that language. In the source code, all translatable strings are passed to some function, traditionally named <em>gettext<\/em> or <em>_<\/em> (underscore), but any name can be used. So then the gold warning example would be:<\/p>\n<p><em>DrawString(_(\u201cYou don\u2019t have enough gold.\u201d));<\/em><\/p>\n<p>and the dialogue example would be:<\/p>\n<p><em>AddDialogue(_(\u201cArthur\u201d), _(\u201cGreetings, citizen. What troubles you?\u201d));<\/em><br \/>\n<em>AddDialogue(_(\u201cCivilian\u201d), _(\u201cThere\u2019s\u2026 ogres! They\u2019ve entered the village!\u201d));<\/em><br \/>\n<em>AddDialogue(_(\u201cArthur\u201d), _(\u201cSay no more, and point the way! My steed and I will come to your aid.\u201d));<\/em><\/p>\n<h2>How to use <em>gettext<\/em> with C#<\/h2>\n<p>Here\u2019s how <em>gettext<\/em> works:<\/p>\n<p><a href=\"https:\/\/hudecekpetr.cz\/wp-content\/uploads\/2020\/07\/Translation-with-gettext.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-1049 size-full\" src=\"https:\/\/hudecekpetr.cz\/wp-content\/uploads\/2020\/07\/Translation-with-gettext.png\" alt=\"\" width=\"860\" height=\"640\" srcset=\"https:\/\/hudecekpetr.cz\/wp-content\/uploads\/2020\/07\/Translation-with-gettext.png 860w, https:\/\/hudecekpetr.cz\/wp-content\/uploads\/2020\/07\/Translation-with-gettext-300x223.png 300w, https:\/\/hudecekpetr.cz\/wp-content\/uploads\/2020\/07\/Translation-with-gettext-768x572.png 768w\" sizes=\"auto, (max-width: 860px) 100vw, 860px\" \/><\/a><\/p>\n<p><strong>1. Translatable strings.<\/strong> First, you choose the method names that count as <em>gettext<\/em> or <em>_<\/em>. For my game, I chose the name \u201cT\u201d because it\u2019s quick to type and doesn\u2019t require the use of the Shift key as an underscore does. You can choose several method names.<\/p>\n<p>In C#, methods need to be in classes, so I\u2019ll create a class named \u201cG\u201d (so that it would be as short as possible and reasonably close to T on a keyboard). Then wherever I need a translatable string, I use <em>G.T(\u201cEnglish here\u2026\u201d)<\/em>. It stands for GetText :).<\/p>\n<p><strong>2. Create the G.T class.<\/strong> In the <em>G<\/em> class, you must load the translation file based on some logic (perhaps based on what the user chose in the Options menu) and in the <em>G.T<\/em> method, you must return the string from that translation. <em>Gettext<\/em> keeps machine-readable translations in <em>.mo<\/em> files which you can load into C# with some NuGet packages. I used <a href=\"https:\/\/github.com\/perpetualKid\/GetText.NET\">GetText.NET<\/a> and you can find information on how to create your <em>G.T<\/em> class with <em>GetText.NET<\/em> <a href=\"https:\/\/github.com\/perpetualKid\/GetText.NET\/blob\/master\/README.md\">in their readme<\/a>.<\/p>\n<p>Now, you can change the strings in your code into <em>G.T<\/em> translatable strings.<\/p>\n<p><strong>3. Extract strings from source code.<\/strong> Then, each time you want to update the translations because you changed or added new translatable strings in your source code, you run the <em>xgettext<\/em> utility. I got it by downloading the NuGet package <a href=\"https:\/\/www.nuget.org\/packages\/Gettext.Tools\/\">GetText.Tools<\/a>, going into my local nuget cache and copying them from there to my <em>%PATH%<\/em>, but you can also download the binaries online.<\/p>\n<p>As arguments to <em>xgettext.exe<\/em>, you pass the method names, the list of source files that you want translatable strings to be extracted from, and the name of the resulting <em>.pot<\/em> file which will contain the canonical translation.<\/p>\n<p>Here\u2019s my command line:<\/p>\n<p><em>xgettext.exe -kT &#8211;from-code=utf-8 -o SeekAWayOut.pot &#8211;files-from=~allCSharpFiles.txt<\/em><\/p>\n<p>where <em>~allCSharpFiles.txt<\/em> is a generated list of all C# files in my repository and <em>T<\/em> is the method name I chose. <em>xgettext<\/em> then scans all of those files (using, I assume, regexes) and finds all methods and constructors with the name <em>T<\/em> (regardless of what class they are in) and considers the first argument of those methods to be the translatable string.<\/p>\n<p>The source code is thus your authoritative canonical translation and the fallback if there are no translations.<\/p>\n<p><strong>4. Get the language file to work on.<\/strong> The first time you add a new language, you copy the template .pot file into a new file named, say, <em>slovak.po<\/em> for the Slovak translation, and you can then edit that file in an editor such as <a href=\"https:\/\/poedit.net\/\">Poedit<\/a>.<\/p>\n<p>If you already have a <em>slovak.po<\/em> file, perhaps because you already translated the game but have now added\/changed translatable strings, you will want to merge the new changes into your existing <em>slovak.po<\/em> file. You can do that from poedit as well (<em>Catalogue &#8211; Update from POT file<\/em>) or with the GNU command line utility <em>msgmerge<\/em>.<\/p>\n<p><strong>5. Translate the text<\/strong>. I\u2019m using <a href=\"https:\/\/poedit.net\/\">Poedit<\/a> as my translation tool. I find it pretty, fast to work with, stable and intuitive.<\/p>\n<p><a href=\"https:\/\/hudecekpetr.cz\/wp-content\/uploads\/2020\/07\/PoEdit.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-1050\" src=\"https:\/\/hudecekpetr.cz\/wp-content\/uploads\/2020\/07\/PoEdit-1024x584.png\" alt=\"\" width=\"660\" height=\"376\" srcset=\"https:\/\/hudecekpetr.cz\/wp-content\/uploads\/2020\/07\/PoEdit-1024x584.png 1024w, https:\/\/hudecekpetr.cz\/wp-content\/uploads\/2020\/07\/PoEdit-300x171.png 300w, https:\/\/hudecekpetr.cz\/wp-content\/uploads\/2020\/07\/PoEdit-768x438.png 768w, https:\/\/hudecekpetr.cz\/wp-content\/uploads\/2020\/07\/PoEdit.png 1321w\" sizes=\"auto, (max-width: 660px) 100vw, 660px\" \/><\/a><\/p>\n<p>Poedit\u2019s free edition is very reasonable. It\u2019s not to be confused with Poeditor, which comes from a different author and has a very limited free edition.<\/p>\n<p><strong>6. Create the <em>.mo<\/em> file.<\/strong> From your translated <em>slovak.po<\/em> file, you can create a sorta-binary \u201cmachine file\u201d called <em>slovak.mo<\/em>, again using Poedit, or with the command-line utility <em>msgfmt<\/em>. Poedit creates the file automatically each time you save. This file contains the same data as the <em>.po<\/em> file, except, I assume, it lacks comments and is somewhat faster to read for a computer.<\/p>\n<p><strong>6. Have the .mo file read by your game.<\/strong> Coming back to the beginning, this .mo file can now be read from disk or assembly resources or anywhere you want by your runtime .mo file reader, such as <a href=\"https:\/\/github.com\/perpetualKid\/GetText.NET\">GetText.NET<\/a>.<\/p>\n<p>And you\u2019re done!<\/p>\n<h2>Conclusion<\/h2>\n<p>I think that <em>gettext<\/em> is a good way to write translatable multi-language programs for many use cases, especially in games or where there is a lot of strings (as opposed to, for example, software libraries where you only really need to translate error messages).<\/p>\n<p>I found it difficult at first to wrap my head around some concepts, especially where does the <em>_<\/em> method come from, and how does gettext extract the translatable strings from C# files (using text scanning and the <em>xgettext<\/em> utility), so I wrote this blog post so that others find this easier.<\/p>\n<p>My experience with <em>gettext<\/em> is positive and I can recommend it to you as well.<\/p>","protected":false},"excerpt":{"rendered":"<p>I\u2019m currently creating a video game in C#\/Monogame. It started off in Czech, but I wanted it to be available in Czech and English, so I set out to add multilanguage support to the game. In this article, I\u2019ll explain why I chose gettext and how I added it to my C# game.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[],"class_list":["post-1047","post","type-post","status-publish","format-standard","hentry","category-blog"],"_links":{"self":[{"href":"https:\/\/hudecekpetr.cz\/cs\/wp-json\/wp\/v2\/posts\/1047","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hudecekpetr.cz\/cs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hudecekpetr.cz\/cs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hudecekpetr.cz\/cs\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/hudecekpetr.cz\/cs\/wp-json\/wp\/v2\/comments?post=1047"}],"version-history":[{"count":1,"href":"https:\/\/hudecekpetr.cz\/cs\/wp-json\/wp\/v2\/posts\/1047\/revisions"}],"predecessor-version":[{"id":1051,"href":"https:\/\/hudecekpetr.cz\/cs\/wp-json\/wp\/v2\/posts\/1047\/revisions\/1051"}],"wp:attachment":[{"href":"https:\/\/hudecekpetr.cz\/cs\/wp-json\/wp\/v2\/media?parent=1047"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hudecekpetr.cz\/cs\/wp-json\/wp\/v2\/categories?post=1047"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hudecekpetr.cz\/cs\/wp-json\/wp\/v2\/tags?post=1047"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}