Difference between revisions of "Strings and translation"

From PioneerWiki
Jump to: navigation, search
(Tutorial: Strings and translation, initial commit)
 
(Lua Standard Library - String manipulation)
Line 76: Line 76:
  
 
A more complex example from '''[https://github.com/pioneerspacesim/pioneer/blob/452e9468bc32b844b379307f982c5869733bd85b/data/pigui/modules/hyperjump-planner.lua#L140 data/pigui/modules/hyperjump-planner.lua]'''<br>
 
A more complex example from '''[https://github.com/pioneerspacesim/pioneer/blob/452e9468bc32b844b379307f982c5869733bd85b/data/pigui/modules/hyperjump-planner.lua#L140 data/pigui/modules/hyperjump-planner.lua]'''<br>
Here <code>string.format("%.2f", distance)</code> is used to set the number of decimals in the jump to two. The rest is string variables that are not translated (names) and concatenation
+
Here <code>string.format("%.2f", distance)</code> is used to set the number of decimals in the jump to two. The rest is string variables that are not translated (names) and string concatenation. The translator is named ' '''lc''' '.<br>
The translator is named ' '''lc''' '<br>
 
 
textLine = jumpIndex ..": ".. jump_sys.name .. " (" .. string.format("%.2f", distance) .. lc.UNIT_LY .. " - " .. fuel .. lc.UNIT_TONNES..")"
 
textLine = jumpIndex ..": ".. jump_sys.name .. " (" .. string.format("%.2f", distance) .. lc.UNIT_LY .. " - " .. fuel .. lc.UNIT_TONNES..")"
  
 
Here is what the final formatted text looks like (right side, two selected jump routes):
 
Here is what the final formatted text looks like (right side, two selected jump routes):
 
[[File:hyperjumpplanner.png]]
 
[[File:hyperjumpplanner.png]]
 
  
 
==String variables==
 
==String variables==

Revision as of 17:18, 3 October 2021

Internationalization

It is important that scripters are familiar with the translation system. In the previous example hello_world.lua used strings directly like print("Hello, World!"). In order for Pioneer to be translated we instead use tokens and calls on the 'Lang' module to substitute it for the string in the chosen language. The translated strings are stored together with their 'key' in json format, one language per file that are kept in the data/lang/ directory. If they are from the data/modules directory they have names starting with module- and ending with the name of the module written in one word, in small caps, and with the '.lua' ending omitted. The final translation from English to other languages is done on Pioneers project page at Transifex, a dedicated online translation tool. The translations are committed to the pioneer/master branch on GitHub by a bot and are not to edited manually apart from the json.en files.

Translating strings

Here's a minimal example with one string translated into one language. It takes two files, one in data/modules and one in data/lang/module-hello. The print statement isn't wrapped in a function so it will be executed when the hello.lua is being parsed on startup. You will have to scroll through the command-line history to find it interleaved with the other output.

data/modules/hello.lua.

local Lang = require 'Lang'
local l = Lang.GetResource("module-hello")

print(l.HELLO)

And a corresponding file containing the translated string.

data/lang/hello/en.json

{
  "HELLO": {
    "description": "A classic message.",
    "message": "Hello World!"
  }
}

In short, any script containing strings that needs translation needs to load the 'Lang' module:

local Lang = require 'Lang'

Then declare a translator function, often just named 'l' as in:

local l = Lang.GetResource("module-hello")

Working with strings

Recommended chapters from Programming in Lua (first edition)
2.4 – Strings
3.4 – Concatenation
20 – The String Library
And from the Lua 5.0 Reference Manual
5.3 – String Manipulation

Concatenation

Instead of serving ready made sentences we can stitch them together with the Lua string concatenation operator ... The following codelines result in the same output.

print("Hello Clarice!")
print("Hello" .. " " .. "Clarice!")

This could be created by the more generic:

local Comms = require 'Comms'
local Event = require 'Event'
local Game = require 'Game'
local NameGen = require 'NameGen'

local GREETING = 'Hello'
local FBIAGENT = NameGen.Surname()

local onShipDocked = function (ship)
  if ship == Game.player then
    Comms.Message(GREETING .. ' ' .. FBIAGENT .. '!')
  end
end

Event.Register("onShipDocked", onShipDocked)

The .. operator is used all over the codebase. Try a search for its occurrence. On Linux I would do grep -R " \.\. " data/modules/ data/libs/ data/pigui/ and that will render a lot of output.

Lua Standard Library - String manipulation

I would start to search the data/ register for 'string.' to get a feeling for how it's used. Again on Linux i would use the 'grep' command. grep -R "string\." data\. You will see that the two most use cases is string.format( which is a part of the lua standard library, and string.interp( which is pulled into the script via the 'Lang' module and is described in the next paragraph.

Some examples:

Comms.Message(string.lower('wHaT cAsE sHoUlLd I usE?'))

Maybe not the most easy function to fit into Pioneer. But it's there:

Comms.Message('Today is opposite day!')
Comms.Message(string.reverse('No, today is not opposite day!')

A more complex example from data/pigui/modules/hyperjump-planner.lua
Here string.format("%.2f", distance) is used to set the number of decimals in the jump to two. The rest is string variables that are not translated (names) and string concatenation. The translator is named ' lc '.
textLine = jumpIndex ..": ".. jump_sys.name .. " (" .. string.format("%.2f", distance) .. lc.UNIT_LY .. " - " .. fuel .. lc.UNIT_TONNES..")"

Here is what the final formatted text looks like (right side, two selected jump routes): Hyperjumpplanner.png

String variables

When you want a string to present variable data such as numbers and names you use the interp() function. interp() accepts a table '{}' and the corresponding keys are then embedded in the translated strings enclosed by curly brackets '{}'.

Using 'module-stationrefuelling' we can do:

print(l.WELCOME_TO_STATION_FEE_DEDUCTED:interp({station = 'JFK', fee = 50}))

Example from the actual StationRefuelling module:

data/modules/StationRefuelling/

Comms.Message(l.WELCOME_TO_STATION_FEE_DEDUCTED:interp({station = station.label,fee = Format.Money(fee)}))

data/lang/module-stationrefuelling/en.json

 "WELCOME_ABOARD_STATION_FEE_DEDUCTED": {
   "description": "Station welcome message, docking in orbit",
   "message": "Welcome aboard {station}. Your docking fee of {fee} has been deducted."
 },

data/lang/module-stationrefuelling/es.json

 "WELCOME_ABOARD_STATION_FEE_DEDUCTED": {
   "description": "Station welcome message, docking in orbit",
   "message": "Bienvenido a bordo de {station}. Le ha sido deducida su cuota de atraque de {fee}."
 },