NML:Language files
Vehicles, Stations, Canals, Bridges, Towns, Houses, Industries (Tiles), Cargos, Airports+Tiles, Objects, Railtypes, Roadtypes, Tramtypes, Terrain
No subpages in this chapter.
Introduction
Language files are usually found in the lang
sub-folder of the project, but the place NML looks for the language files can be changed via command line parameter to any directory desired:
nmlc -l path/to/lang-dir path/to/nml-source-file.nml nmlc --lang-dir=path/to/lang-dir path/to/nml-source-file.nml
Language files MUST have the extension .lng
The language files themselves follow a certain structure:
##grflangid <number> <string-name> :<text> <string-name> :<text> ...
where the first line must give the language code for the language this file describes (see below). The following lines each describe a string. The translated string follows immediately the colon. An NML project has exactly one fallback language, by default this is english.lng. You can change this with the command line parameter --default-lang. Example for a valid language file:
##grflangid 0x01 STR_GRF_NAME :{TITLE} 0.1.0 - {VERSION} STR_GRF_DESCRIPTION :{TITLE} contains pimped ground tiles. STR_NAME_MYVEHICLE :General Robotics Anti-Grav UFO Mark X
String codes
Strings may contain a number of special string codes which control how the string is being printed or which values to insert into the string. Custom replacements can be stored in the plain text file custom_tags.txt
(for example a newgrf version which is written by your build script). In the case above the custom_tags.txt
could, for example read
VERSION :alpha-r88 TITLE :Example NewGRF
Additionally to custom-defined tags, NML comes with a number of default tags:
String parameters
The above tags can be used in almost all strings. There are also a number of tags that need an argument they'll read from the textstack. The textstack is only available for certain callbacks an properties.
Tag | Meaning | Size (number of bytes read from stack) | Example |
---|---|---|---|
COMMA | signed number with decimal separator between 1000's. | 4 | |
SIGNED_WORD | signed number with decimal separator between 1000's. | 2 | |
UNSIGNED_WORD | unsigned number with decimal separator between 1000's. | 2 | |
CURRENCY | money in proper currency units | 4 | 24,492 € |
VELOCITY | velocity in proper units | 2 | 35 km/h |
VOLUME | volume in proper units | 2 | 29,000 litres |
VOLUME_SHORT | volume in proper units in short form | 2 | 29,000l |
POWER | power in proper units | 2 | 2,030 kW |
WEIGHT | weight in proper units | 2 | 5 tonnes |
WEIGHT_SHORT | weight in proper units in short form | 2 | 5t |
CARGO_LONG | 1.4 cargo amount in proper units | 4 (2 + 2)[1] | 10 bags of mail |
CARGO_SHORT | 1.4 cargo amount in proper units in short form | 4 (2 + 2)[1] | 10 bags |
CARGO_TINY | 1.4 cargo amount in proper units in tiny form (only unit conversion) | 4 (2 + 2)[1] | 10 |
CARGO_NAME | 1.7 cargo name | 2 | Coal |
HEX | hexadecimal presentation | 4 | F0 3D D4 33 |
STRING | include other string | 2 | |
DATE1920_LONG | days since 1920 as long date | 2 | 12 August 1935 |
DATE1920_SHORT | days since 1920 as short date | 2 | 12 Aug 1935 |
DATE_LONG | days since 0 as long date | 4 | 12 August 1610 |
DATE_SHORT | days since 0 as short date | 4 | 12 Aug 3049 |
POP_WORD | Remove 2 bytes from the top of the stack | 2 | |
STATION | Station name of station with given ID | 2 | |
FORCE | 14.0 force in proper units | 4 | 1,000 kN |
In case you don't want to use the first 2 (or 4) bytes from the stack but rather another variable, you can prefix the tag name with the number, like {1:COMMA} to use the second (indexing starts at 0) argument from the stack. You can also use this in translations that require a different order, for example (in english.lng):
STR_SOME_STRING :{COMMA} items cost {CURRENCY}
And the same string in dutch.lng:
STR_SOME_STRING :{1:CURRENCY} is de prijs van {0:COMMA} items.
The string parameters itself are assigned in units of dwords (4 bytes), starting with temporary storage 256, up to 259 or 261. Thus if you use parameters which are not of dword size (e.g. strings or word-sized numbers), you have to construct the text stack via bit arithmetic:
switch(FEAT_INDUSTRIES, SELF, extra_text_switch, [ STORE_TEMP( ( (LOAD_PERM(var_supply_efficiency_factor) == 1) * string(STR_EXTRA_PRIMARY_EFFICIENT1) ) + ( (LOAD_PERM(var_supply_efficiency_factor) == 2) * string(STR_EXTRA_PRIMARY_EFFICIENT2) ) + ( (LOAD_PERM(var_supply_efficiency_factor) == 3) * string(STR_EXTRA_PRIMARY_EFFICIENT3) ) + ( (LOAD_PERM(var_supply_efficiency_factor) == 4) * string(STR_EXTRA_PRIMARY_EFFICIENT4) ) | LOAD_PERM(var_supply_requirement) << 16, 256 ), return string(STR_EXTRA_PRIMARY); }
for a language file which reads
STR_EXTRA_PRIMARY_EFFICIENT1 :{YELLOW}very efficient{BLACK}ly STR_EXTRA_PRIMARY_EFFICIENT2 :{YELLOW}efficient{BLACK}ly STR_EXTRA_PRIMARY_EFFICIENT3 :in a {YELLOW}normal{BLACK} way STR_EXTRA_PRIMARY_EFFICIENT4 :{YELLOW}wasteful{BLACK}ly STR_EXTRA_PRIMARY :{BLACK}This industry needs {YELLOW}supplies{BLACK}.{}They use them {STRING}{}{}Monthly demand: {YELLOW}{UNSIGNED_WORD} crate{P "" s} of engineering supplies{BLACK}.
Defining cases
Similar to OpenTTD's language files, NML can also make use of cases, genders and plural forms. In order to utilize them, they have to be defined in the language file's header and have to match the definition as used in OpenTTD's language files:
##case pragma STR_NAME :Utas STR_NAME.pragma :utast
e.g. for Hungarian you'd have
##case t ba STR_CARGO_PLURAL_PASSENGERS :Utas STR_CARGO_PLURAL_PASSENGERS.t :utast
In case the name of a case changes in the OpenTTD language file, your language file will have to follow that change. To keep compatible with older OpenTTD versions you can use the ##map_case pragma, like this:
##map_case t t_old
Defining genders
Genders just like cases are defined in the header of the language file and have to match the definition as found within OpenTTD's language files. The gender of a word or expression is declared within the string's text itself by a tag of the form {G=gendername}
:
##gender pragma STR_NAME :{G=gendername}String which requires gender 'gendername'
where the string with the defined gender then will trigger the correct usage in a string which includes it and which has to take provisions to show the proper gender:
STR_OTHER :Some text {G gender1 gender2 ...} {STRING}
for example in German
##gender m w n p STR_CARGO_NAME_BAUXITE :{G=n}Bauxit
which then will be used with the correct gender by a string which includes it:
STR_NEWS_STATION_NO_LONGER_ACCEPTS_CARGO :{WHITE}{STATION} akzeptiert kein{G "en" "e" "" "e"} {STRING} mehr.
In case the name of a gender changes in the OpenTTD language file, your language file will have to follow that change. To keep compatible with older OpenTTD versions you can use the ##map_gender pragma, like this:
##gender m f ##map_gender m male ##map_gender f female
LanguageIDs
Currently, the scheme is to use international phone codes as language IDs, unless they're out of range. When creating a new translation for OpenTTD pick a number vaguely related in some way. Or something else.
Valid language IDs are listed in the table below. The cases, genders and the type of plural form is only given as reference. Authorative are the definitions in OpenTTD's language file for each language. A complete list of these exact definitions is available here (generated page).
In case a language is not in this list, edit the shared master language list in the NewGRF Specs. This master list is always leading. NFO language ID 7F has no meaning in NML (set --default-lang instead) and setting bit 8 will break your language file.ID | language | cases | genders | plural form |
---|---|---|---|---|
00 | English (US) | |||
01 | English (GB) | |||
02 | German | m w n p | ||
03 | French | m m2 f | 2 | |
04 | Spanish | m f | ||
05 | Esperanto | n | ||
06 | Ido | |||
07 | Russian | m f n p nom gen dat acc abl pre | m f n p | 6 |
08 | Irish | 4 | ||
09 | Maltese | 12 | ||
0A | Tamil | |||
0B | Chuvash | |||
0C | Chinese (Traditional) | 1 | ||
0D | Serbian | nom big gen dat aku vok lok ins | muški ženski srednji | 6 |
0E | Norwegian (Nynorsk) | small | masculine feminine neuter | |
0F | Welsh | |||
10 | Belarusian | m f n p nom gen dat acc abl pre | m f n p | 6 |
11 | Marathi | |||
12 | Faroese | m f n | ||
13 | Scottish Gaelic | dat gen nom voc | m f | 13 |
14 | Arabic (Egypt) | 1 | ||
15 | Czech | nom gen dat acc voc loc ins big small | m f n map mnp fp np | 10 |
16 | Slovak | g | m z s | 10 |
17 | Hindi | |||
18 | Bulgarian | m f n p | m f n p | |
1B | Afrikaans | male | ||
1E | Greek | subs date geniki | m f n | 2 |
1F | Dutch | |||
21 | Basque | |||
22 | Catalan | Masculin Femenin | ||
23 | Luxembourgish | |||
24 | Hungarian | t ba | 2 | |
26 | Macedonian | |||
27 | Italian | ms mp fs fp | m ma f | |
28 | Romanian | |||
29 | Icelandic | karlkyn kvenkyn hvorugkyn | ||
2A | Latvian | kas | m f | 3 |
2B | Lithuanian | kas ko kam ka kuo kur kreip | vyr mot | 5 |
2C | Slovenian | r d t | 8 | |
2D | Danish | |||
2E | Swedish | |||
2F | Norwegian (Bokmal) | small | masculine feminine neuter | |
30 | Polish | d c b n m w | m f n | 7 |
31 | Galician | m f n | ||
32 | Frisian | |||
33 | Ukrainian | r d z | m f s mn | 6 |
34 | Estonian | g in sü | ||
35 | Finnish | |||
36 | Portuguese | n m f mp fp | ||
37 | Brazilian Portuguese | m f | 2 | |
38 | Croatian | nom gen dat aku vok lok ins | male female middle | 6 |
39 | Japanese | 1 | ||
3A | Korean | m f | 11 | |
3C | Malay | |||
3D | English (AU) | |||
3E | Turkish | tamlanan | 1 | |
42 | Thai | 1 | ||
54 | Vietnamese | 1 | ||
55 | Mexican Spanish | m f | 0 | |
56 | Chinese (Simplified) | 1 | ||
5A | Indonesian | 1 | ||
5C | Urdu | m f | ||
61 | Hebrew | singular plural gen | m f | |
62 | Persian | |||
66 | Latin | gen acc abl dat | m f n mp fp np |