OpenTTD Development Documentation
External Links

OpenTTD GitHub
Contributing to OpenTTD - guidelines
OpenTTD Doxygen

General Reference

Coding style
Compiling OpenTTD
Add a setting
Add a squirrel function
Understanding the SaveGame handler
Bumping the savegame version
Doing an OpenTTD release

Language and Strings

Manual of style
Format of langfiles
Using OpenTTD strings
List of special strings

Window System

Using the window system
Colour codes that exist in OpenTTD
Adding a text box
Understanding the widget focus system
GUI style guide


The OpenTTD TCP protocol
The OpenTTD UDP protocol
Debugging desyncs
Server Admin Port development

Ingame Console

The console window
Console commands
Console variables
Using console scripting
Adding functions/commands to the console
Adding variables to the console
Console development history

Content APIs (modding frameworks)

Graphics and similar (NewGRF)
AI framework (NoAI)
GameScript framework (NoGO)
Social Integration

Other Reference

Map array (landscape grid)
Train acceleration
Sound IDs

This page deals with i18n strings from a developers point of view. If you are interested in aspects on translations, see Format of langfiles for a more in-depth article. That article also covers language specific concepts like inflection due to gender, causes and plurality.

OpenTTD has built in a multi-language support for strings. These enduser-visible texts must be translated into multiple foreign languages. Language dependent strings are stored in text files under src/lang/ and have the pattern [language name].txt. As any source code file they are under version control via SVN. The master language is considered to be English (British English) and is stored in the file english.txt. For a detailed explanation of these files, refer to Format of langfiles. During the compilation process the tool strgen compiles language files into their corresponding .lng files which in turn are used during gameplay.


Adding Strings

You add a string by giving it an identifier then a number of spaces later a colon ":" and then the string itself. Use the file english.txt for this. By convention identifiers for strings always start with STR_. The line could like something like this:

STR_01A1_IS_GETTING_VERY_OLD   :{WHITE}{STRING} {COMMA} is getting very old

Note that the location to where you add a string is not entirely arbitrary (see Ranges below). The strings enclosed in curly brackets ("{}") are special character strings that are interpreted when rendering it. As you might have guessed, {WHITE} gives the string a white colour. For a list of special strings to use, refer to Special Strings.


Every line which starts with a hash character "#" is considered to be a commentary line. Empty lines (a line with just a carriage return symbol) are ignored and may be used to group strings which belong together semantically.

Special Strings

Strings may include "Special Strings" which allow modifications in how the string will be rendered or make substitutions for placeholders. A list of special strings can be found in a separate article.


Ranges should be visually marked as a block by both starting and ending comments like this:

############ range for months starts
STR_MONTH_ABBREV_JAN                                            :Jan
STR_MONTH_ABBREV_FEB                                            :Feb
STR_MONTH_ABBREV_MAR                                            :Mar
# ...
############ range for months ends
Do not break ranges when adding strings as this might have unwanted side-effects!

Ranges are often used to define a group of texts which are not directly referenced in the source code. Instead, offset calculation in relation to the block's first entry is performed. Thus, texts are referenced only indirectly and during run-time.


Technically, string identifiers are assigned to integer numbers automatically. This is done during the build process. Consistent referencing is ensured via a header file (see lang/table/strings.h) which is auto-generated. The corresponding data type used for strings in the C++ source code is StringID. This approach allows to perform arithmetics on StringID such as offset calculation.

Functions for Processing Variables


As you have seen before, you have to put these special modifiers into the string itself. Let us revisit the example from above:

STR_01A1_IS_GETTING_VERY_OLD   :{WHITE}{STRING} {COMMA} is getting very old

Both "STRING" and "COMMA" are variables. You set them up by calling the appropriate "SetDParam(uint n, uint32 v)" function. So for this one you would do:

SetDParam(0, _vehicle_type_names[v->type - 0x10]);
SetDParam(1, v->unitnumber);

Only after that you call the appropiate draw function, for example:

DrawStringRightAligned(x, y, STR_01A1_IS_GETTING_VERY_OLD, colour);

Both parameters of "STR_01.." have been set up, and you will get the info nicely on your screen.

List of Functions

Besides SetDParam(uint n, uint32 v) there are other SetDParamxxx functions available:

Signature Description Example Remarks
SetDParam(uint n, uint32 v) Set the variable value at index n to value v. see above Depending on the variable used, the value is interpreted differently.
SetDParamStr(uint n, const char *str) Use the verbatim string str to apply it to the variable at index n. Assume that it is STR_XYZ: Hello World - {RAW_STRING} in english.txt and STR_XYZ: Hallo Welt - {RAW_STRING} in german.txt. The coding
char *hello = 'Hello World!';
SetDParamStr(0, hello);
DrawStringRightAligned(x, y, STR_XYZ, colour);
would produce Hello World - Hello World! in English and Hallo Welt - Hello World! in German.
Note that str is being used in a verbatim manner, i.e. the variable value is printed "as is" without any translation lookup in between (typically used for providing non-translated names to variables). Only use {RAW_STRING} as variable!
SetDParamMaxDigits(uint n, uint count) available since r24801: Emulate the variable value at index n, which is of an integer type, by creating a number which has count maximal-width digits. Assume that for the current font, the digit 8 is the digit with the largest font width. If you call SetDParamMaxDigits(1, 4), this would be equivalent to calling SetDParam(1, 8888). These functions are typically used when there is a need to guess the maximal width of a certain string, such as in UpdateWidgetSize. Make use of this function, if you know an upper boundary of your variable and calculating the exact value may be expensive, but you just need a "good guess" for the total width of your final string.
SetDParamMaxValue(uint n, uint64 max_value, uint min_count = 0) available since r24801: Emulate the variable value at index n, which is of an integer type, by creating a number with as many maximal-width digits as max_value provides. If min_count is provided, ensure that at least min_count digits are created. Assume that for the current font, the digit 8 is the digit with the largest font width. If you call SetDParamMaxValue(1, 1234), this would be equivalent to calling SetDParam(1, 8888), because 1234 consists of four digits. If you call SetDParamMaxValue(2, 123, 5), this would be equivalent to calling SetDParam(2, 88888), because 123 has only three digits, but you requested to have five digits at minimum.

Nested Resolution Using {STRINGx} Commands

Using the {STRINGx} commands, it is possible to trigger a nested resolution of strings. When using functions for processing variables, the indices are referencing in a top-to-bottom order whilst the leaf nodes are required to be in a left-to-right order. Here is an example: Let the strings be defined as

STR_A              : A
STR_X              : X
STR_Y              : Y
STR_TOP            : {STRING5}

To produce the output A / X - Y on drawing the string STR_TOP you have to set the variables like this:

SetDParam(0, STR_1ST_LEVEL);
SetDParam(1, STR_2ND_LEVEL_A);
SetDParam(2, STR_A);
SetDParam(3, STR_2ND_LEVEL_X);
SetDParam(4, STR_X);
SetDParam(5, STR_Y);
DrawString(x, y, STR_TOP, colour);

Moreover, note how STR_TOP is defined as {STRING5}: The first variable of STR_1ST_LEVEL requires space for two variables (the StringID of the string to show plus its single variable value). The second variable of STR_1ST_LEVEL requires space for three variables (the StringID of the string to show plus two additional variable values).


The "source of truth" for the i18n strings is located in the text tables of the language files (that is for example "english.txt") only. This means that the WebTranslator is updated automatically based on the latest state available in trunk of the SVN repository. Therefore, it is safe to remove an entire entry from "english.txt" in case that you know that it is not used elsewhere anymore (NB: If you do not find the name of the string in any source code file, you cannot conclude that it is not used anymore due to ranges). Make sure that you clean up the entry in all the other languages as well, as this is not done in trunk for you automatically.

Moreover, speaking more generally, if you want to change an English original text, you need to think about the impact on the other languages as well. Here are some rules of thumb: