Strings

From OpenTTD
Jump to: navigation, search
HAL (Hardware Abstraction Layer)

Audio
Music
Graphic

Window System

Using the Window System
Colour codes that exist in OpenTTD
Adding a text box
Understanding the widget focus system

Settings

Add a setting
Add a squirrel function
Do a savegame BUMP

The Map / Scenario

Understanding the Dynamic Landscape Array
Understanding the SaveGame Handler
HOWTO - Create good Scenarios
HOWTO - Add New Town Name Generators

The actual simulation

Vehicles
Using Orders
Pathfinding
Ratings
Train Acceleration

Language and Strings

Format of langfiles
Using OpenTTD Strings
List of Special Strings

Multiplayer

The Core Interface

Starting a Server
Connecting to a Server
Using the list of LAN/Internet Games

The OpenTTD TCP Protocol
The OpenTTD UDP Protocol
HOWTO - Debug desyncs

Ingame Console

The Console Window
Using Console Scripting
HOWTO - Add Functions/Commands to the Console
HOWTO - Add Variables to the Console
HOWTO - Direct Variable Access using ICONSOLE_VAR_POINTER
OpenTTD Console Commands
OpenTTD Console Variables
Development History

Notice.png

Note
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.


Contents

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.

Comments

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 to modify the way how the string is being rendered or make substitutions for placeholders. A list of special strings can be found in a separate article.

Ranges

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
Content.png

Warning
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.

Internals

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

Example

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}
STR_1ST_LEVEL      : {STRING1} / {STRING2}
STR_2ND_LEVEL_A    : {STRING}
STR_2ND_LEVEL_X    : {STRING} - {STRING} 

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).

Lifecycle

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:

  • If you are sure that your changes will affect all languages (also those which you do not speak) in the same way (for example, you want to change {RED} to {GREEN}), then go ahead and change it mechanically in all languages yourself. Don't wait for translators to adjust all other languages.
  • If you cannot assess if your change will alter things in other languages (for example, you changed a single word in the phrase) or if you know that it will change things for sure (for example, you changed the entire phrase), then do the modification in English and delete all other translations of this string in the other text tables. For translators, it is much easier to find a missing translation than searching for a changed one! As SVN keeps a history, the original translation is not lost either and can be restored, if really necessary.
    Also for the current case, if you are sure for a certain language (for example, it is your mother tongue) how the translation needs to be adjusted, adjust it yourself. Still get rid of the other strings in the other languages which you do not know well enough. If in doubt, whether it is right what you are doing, do not change the entry, but remove it.
Personal tools