NML:Language files

From GRFSpecs
Jump to navigationJump to search

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:

Tag Meaning Example
empty tag, written as {} is used to create a newline
NBSP non-breaking space
{ Display a left curly bracket. You write this as {{}. If you'd include it without surrounding it by { and } it'd be seen as the start of a tag and the rest of the string would be read as name of the tag.
COPYRIGHT copyright symbol ©
TRAIN display train symbol
LORRY display lorry symbol
BUS display bus symbol
PLANE display plane symbol
SHIP display ship symbol
TINYFONT switch to small font
BIGFONT switch to big font
BLUE switch to blue text
 
SILVER switch to silver text
 
GOLD switch to golden text
 
RED switch to red text
 
PURPLE switch to purple text
 
LTBROWN switch to light brown text
 
ORANGE switch to orange text
 
GREEN switch to green text
 
YELLOW switch to yellow text
 
DKGREEN switch to dark green text
 
CREAM switch to cream-coloured text
 
BROWN switch to brown text
 
WHITE switch to white text
 
LTBLUE switch to light blue text
 
GRAY switch to gray text
 
DKBLUE switch to dark blue text
 
BLACK switch to black text
 
PUSH_COLOUR Supported by OpenTTD 1.101.10 Not supported by TTDPatch Push current colour onto colour stack. Use this if you need to switch colour and restore later
POP_COLOUR Supported by OpenTTD 1.101.10 Not supported by TTDPatch Pop last colour from colour stack. Use this to restore previous colour

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 Supported by OpenTTD 1.4 (r26244)1.4 Not supported by TTDPatch cargo amount in proper units 4 (2 + 2)[1] 10 bags of mail
CARGO_SHORT Supported by OpenTTD 1.4 (r26244)1.4 Not supported by TTDPatch cargo amount in proper units in short form 4 (2 + 2)[1] 10 bags
CARGO_TINY Supported by OpenTTD 1.4 (r26244)1.4 Not supported by TTDPatch cargo amount in proper units in tiny form (only unit conversion) 4 (2 + 2)[1] 10
CARGO_NAME Supported by OpenTTD 1.7 (r27706)1.7 Not supported by TTDPatch 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 Supported by OpenTTD 14.014.0 Not supported by TTDPatch force in proper units 4 1,000 kN
  1. 1.0 1.1 1.2 First (lower) 2 bytes for cargo type, second (higher) 2 bytes for cargo amount.

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