Simple Localization for Corona SDK Apps and Games


mod_localize is a localization module built to add language support to your Corona SDK app or game in a simple way.


Docs: HTML module documentation is located in the doc folder in the download.

Localizing Strings

To use this module you must create language files for any language you want to support.

The easiest way is to create the main translation file that you will give to the translator. Usually you start this in English (US). All translation files are stored in the lang directory, and use a standard locale naming convention.

In the lang directory, create a text file called en_US.txt, which is the locale code (en_US) for English (US) with a “.txt” appended to it. You can find a list of common locales here.

Your folder structure should look like the following:


Now to add the translations use one translation entry per line, in a stringKey = translatedValue format.

stringKey = translatedValue
stringKey2 = translatedValue2
stringKeyN = translatedValueN

For example, open the en_US.txt and enter the following:

Hello = Hello

Save the file and open up the main.lua file. Add the following to the top:

local localization = require( "mod_localize" )
local _s = localization.str

localization:setLocale( 'en_US' )

print( _s( "Hello" ) )
> Hello

Not too exciting. Let’s use some string tokens to customize the translation. Open up the en_US.txt file and add the following:

Hello = Hello
%s wants %d muffins. = %s wants %d muffins.

Save the file and let’s go back to the main.lua file. Update it as follows:

local localization = require( "mod_localize" )
local _s = localization.str

localization:setLocale( 'en_US' )

print( _s( "Hello" ) )
print( _s( "%s wants %d muffins.", "Chris", 12 ) )
> Hello
> Chris wants 12 muffins.

Still not too exciting… but important, as some languages place words in different parts of a sentence. We can handle that. A string value token is represented by %s. While an integer can be represented by %d. If the proper data is not passed to the token, an error will occur. See the string.format function for more options.

So far we have been translating in English so let’s add an additional language file.

Again, try and finalize the English translation text file before starting other translations. This will save you a lot of time and effort with your translator or translation service.

Open up the en_US.txt file and save it as es_ES.txt which will be the translation file for Spain.

You can add comments to the file by using the double-dash. Multi-line comments are not supported.

--This is a comment
--and this too...

You can now update the es_ES.txt like so:

--Spanish translation (es_ES)
Hello = ¡Hola
%s wants %d muffins. = %s quiere %d magdalenas.

As you can see we only update the translated values. In this case I just used Google Translate (probably not a good idea for production apps). Save the file and go back to the main.lua. Update the setLocale method call as shown:

local localization = require( "mod_localize" )
local _s = localization.str

localization:setLocale( 'es_ES' ) --change to Spanish

print( _s( "Hello" ) )
print( _s( "%s wants %d muffins.", "Chris", 12 ) )
> ¡Hola
> Chris quiere 12 magdalenas.

Now that’s much more useful. To add new languages, just create the translation file and add it to the lang folder. For example, a French translation, named fr_FR.txt might look like this:

--French translation (fr_FR)
Hello = Bonjour
%s wants %d muffins. = %s veut %d muffins.

String Constants

Some people prefer to use a string constant to represent the translated key. The module can handle this as well. For example, a constant based system, in en_US.txt:

MUFFIN_REQUEST = %s wants %d muffins.

…and in fr_FR.txt:

GREETING = Bonjour
MUFFIN_REQUEST = %s veut %d muffins.

We can then use the string contants in our code and the translations will take place as well:

local localization = require( "mod_localize" )
local _s = localization.str

localization:setLocale( 'fr_FR' ) --change to French

print( _s( "GREETING" ) )
print( _s( "MUFFIN_REQUEST", "Chris", 12 ) )
> Bonjour
> Chris veut 12 muffins.


I generally prefer, and recommend, the regular string literal style versus string constants. You get a better idea of what the actual string formatting will be. It also makes it easier for the translator to see the string in context.

Translating Images

You will probably need to swap out images based on the selected locale as well. This is easily handled just as other text translations. For example, in en_US.txt:

app_header.png = app_header.png

…and in es_ES.txt:

app_header.png = app_header_es.png

…and in main.lua:

local localization = require( "mod_localize" )
local _s = localization.str

localization:setLocale( 'es_ES' ) --change to Spanish

local image = display.newImage( _s( "app_header.png" ) )
> displays app_header_es.png

Now You’re Speaking My Language

Having your app or game translated into other languages can increase your download count. It takes a little extra time, and is best done towards the end of the project, or when all text has been finalized. Simply start with a en_US file and when it comes time to translate, give a copy of that file to whoever will be translating it. It will make for a much easier production experience.

Thanks for taking the time to read, and please share this post.


Docs: HTML module documentation is located in the doc folder in the download.

Stay up to date on Twitter:


9 thoughts on “Simple Localization for Corona SDK Apps and Games

  1. I changed line 31 to:
    local langFilePath = system.pathForFile( “lang/” .. self.locale .. “.txt”, system.ResourceDirectory )
    and the lang folder gets included by default and no need to put the files in the sandbox. I tested it in Windows, Mac, iOS and Android.
    Cheers! 🙂


  2. A lovely addition to the mod_Awesome series of modules! I think using the stringKey method for things like buttons, and the string constant method for entire phrases, as some languages are not SVO (subject-verb-object) in syntax and don’t follow the English patterns. A great tool!


Comments are closed.