Localization
Learn how to add localization to your game to support multiple regions.
Localization is an important part of any game. While it can be possible to design a game that is region independent, it is quite hard. At some point you will need to produce localized text and graphics.
MonoGame has a simple localization system built in. If you want to develop your own system you are still able to do so. But the default system should be good enough for most use cases.
Creating resx files
MonoGame runs on .net/Mono on most platforms. Localization is handled by those platforms via the use of resx files. There are walkthroughs on MSDN which walk you through the process. A simplified version is presented here.
Create a .resx file in the IDE e.g Foo.resx and add it to your game project. Note this needs to be added to the main app projects. The Foo.resx file should have an Action of EmbeddedResouce and a Generator value of ResXFileCodeGenerator.
Here is a snippet from the .csproj:
<EmbeddedResource Include="Foo.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Foo.Designer.cs</LastGenOutput>
</EmbeddedResource>
Add any string resources to that file. These are in the form of a Key/Value pair. You can use the built-in editor or manually edit the .resx file by hand. It is an xml file so you can view the contents easily.
<data name="Wall_Style" xml:space="preserve">
<value>Wall Style : {0}</value>
</data>
When the resx is processed by the generator, it produces a Foo.Designer.cs file which is then included in your project. You can then access the "string" value by using code as follows:
var s = MyProject.Foo.Wall_Style;
Note in the example we have a place holder ({0}) for additional text. You can still use te property of Foo.Wall_Style with things like string.Format.
int i = 1;
var s = string.Format (MyProject.Foo.Wall_Style, i);
All this means you do not need to hardcode the string directly. When accessing the MyProject.Foo.Wall_Style property, the code will look up the value from the embedded resx file automatically.
You can add support for a new language by adding a new resx file which uses the language/region code as a suffix, e.g Foo.de-DE.resx. This new file will contain the translations for that language/region. In the above example we are targetting German.
Universal Windows Platform (UWP) considerations
Unfortunately UWP does not support resx files any more. They have a new file called a resw. The format is similar but incompatible to resx. As a result, you will need to duplicate the data into a set of additional resw files to get the translation to work on UWP. The process is like the standard resx process.
Upgrading your SpriteFont files
By default, the SpriteFont processor uses a limited set of characters to generate the font. While this is fine for English languages it will probably not include special characters needed for other languages (French, Arabic, Korean etc).
As a result MonoGame has a LocalizedFontProcessor which does something slightly different. The process looks at the resx files you provide it with and generates an optimized spritefont which only contains the characters your game uses.
To make use of this functionality you ned to tell the spritefont which resx files to use. Open the .spritefont with a xml/text editor and add lines like this inside the Asset node:
<ResourceFiles>
<Resx>..\Foo.resx</Resx>
<Resx>..\Foo.de-DE.resx</Resx>
</ResourceFiles>
Note the paths are relative to the .spritefont directory. In the example above the resx files are in the directory above the .spritefont.
You should end up with a .spritefont file like this
<?xml version="1.0" encoding="utf-8"?>
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
<Asset Type="Graphics:LocalizedFontDescription">
<FontName>Verdana</FontName>
<Size>14</Size>
<Spacing>1</Spacing>
<Style>Regular</Style>
<CharacterRegions>
<CharacterRegion>
<Start> </Start>
<End> </End>
</CharacterRegion>
</CharacterRegions>
<ResourceFiles>
<Resx>..\Foo.resx</Resx>
<Resx>..\Foo.de-DE.resx</Resx>
</ResourceFiles>
</Asset>
</XnaContent>
Once that is done you then need to change the .mgcb file so that the SpriteFontProcessor is replaced with the LocalizedFontProcessor. This can be done by editing the .mgcb file or using the MGCB Editor. After that you can just compile your content as normal. If the processor has any trouble resolving or reading the resx files you will get an error.
Loading the Font
Loading the font can be done in the normal way. The end result of the process is an .xnb file containing a normal SpriteFont.
var font = Content.Load<SpriteFont>("Foo");
Other Localized assets
Not all localized assets will be fonts. In certain situations you might need to swap out an entire textures or spritesheets. For these cases a new method has been added to the ContentManager, LoadLocalized. The idea behind this method is that it will look for localized files BEFORE loading the default one.
So for example say you have an asset, MyCharacter. You have a MyCharacter.xnb file which contains the data for that item. You can also has a MyCharacter.de-DE.xnb file which contains the German version of that asset. This asset could be a Texture, Audio or any other game asset. You can then use LoadLocalized to load the localized version of the asset.
var myCharacter = Content.LoadLocalized<Texture2D>("MyCharacter");
The decision on which localized asset to load is made by looking for a file with the following patterns
<AssetName>.<CurrentCulture.Name>
<AssetName>.<CurrentCulture.TwoLetterISOLanguageName>
These values are retrieved from
CultureInfo.CurrentCulture.Name // eg. "en-US"
CultureInfo.CurrentCulture.TwoLetterISOLanguageName // eg. "en"
which are part of the System.Globalization namespace.
On a side note you can also use the
LoadLocalized
to load language specific SpriteFonts. They just need to be named in the same way as we have described above.