--[[
TRANSLATION MODULE USAGE EXAMPLE
This example demonstrates the use of the translation module like the other examples do, but it also shows the
recommended implementation of the module as well. This code accounts for all known edge cases with a player's
online status and multiplayer sessions. It is recommended that you follow this pattern when implementing the module.
]]
local event = require("__flib__.event")
local migration = require("__flib__.migration")
local translation = require("__flib__.translation")
local table = require("__flib__.table")
-- the structure of a player's translations table
-- this is likely more dictionaries than a mod will actually need - there are a lot of them to facilitate the example
-- strive to translate as few strings as possible to save space in `global`
local empty_translation_tables = {
achievement = {},
entity = {},
equipment = {},
fluid = {},
item = {},
recipe = {},
technology = {},
tile = {}
}
-- build the array of strings to translate
local function build_strings()
local strings = {}
local i = 0
-- for demo purposes, we add a LOT of strings by looping through a large amount of game prototypes
for category in pairs(empty_translation_tables) do
for name, prototype in pairs(game[category.."_prototypes"]) do
i = i + 1
-- StringData - a table containing `dictionary`, `internal`, and `localised` keys
-- `dictionary` - the dictionary (subtable) that the string belongs to
-- `internal` - a string that the translation will be keyed by, e.g. "iron-plate"
-- `localised` - the actual localised string that needs to be translated
strings[i] = {dictionary=category, internal=name, localised=prototype.localised_name}
end
end
-- save to global
global.strings = strings
end
-- conditionally registered `on_tick` handler
local function on_tick_handler(e)
-- if any players are translating, call the function, else deregister from on_tick to save performance
if translation.translating_players_count() > 0 then
-- iterate_batch - performs translation operations over multiple ticks
translation.iterate_batch(e)
else
event.on_tick(nil)
end
end
-- register the `on_tick` handler if it needs to be registered
local function register_on_tick()
-- if any players are translating, register the handler
if translation.translating_players_count() > 0 then
event.on_tick(on_tick_handler)
end
end
-- create player data
local function init_player(player_index)
global.players[player_index] = {
flags = {
-- if true, start translations for the player when they join
-- players must be online in order for translations to be processed
translate_on_join = false
},
-- holds the player's translations for use throughout the mod
translations = table.shallow_copy(empty_translation_tables)
}
end
local function start_translations(player_index)
-- add_requests - adds the StringData array to the player's table and starts translation
translation.add_requests(player_index, global.strings)
-- register the `on_tick` handler
register_on_tick()
end
event.on_init(function()
-- set up the module
translation.init()
-- build the array of StringData that the players will translate
build_strings()
-- create player data
global.players = {}
for i, player in pairs(game.players) do
init_player(i)
-- the player must be connected in order for translations to be processed
-- if they're connected, start immediately, else set a flag to begin when they join
if player.connected then
start_translations(i)
else
global.players[i].flags.translate_on_join = true
end
end
end)
-- re-register the `on_tick` handler if it needs to be registered
event.on_load(function()
register_on_tick()
end)
event.on_configuration_changed(function(e)
-- if generic migrations are necessary
if migration.on_config_changed(e, {}) then
-- cancel all translations and do a complete reset
-- this is necessary because the contents of each dictionary may have changed due to game or mod changes
translation.init()
-- re-build the array of StringData that the players will translate
build_strings()
-- iterate players
for i, player in pairs(game.players) do
-- the player is guaranteed to not be translating anymore, since we did a complete module reset
local player_table = global.players[e.player_index]
-- reset the translate_on_join flag
player_table.flags.translate_on_join = false
-- reset the player's translations table
player_table.translations = table.shallow_copy(empty_translation_tables)
-- start translations or set the flag to do so when they join
if player.connected then
start_translations(i)
else
global.players[i].flags.translate_on_join = true
end
end
end
end)
event.on_string_translated(function(e)
-- process_result - retrieves the sort data for the string, and whether or not the player is finished translating
local sort_data, finished = translation.process_result(e)
-- `sort_data` can be `nil` if another mod requested translations as well, so check for it
if sort_data then
local player_table = global.players[e.player_index]
local translations = player_table.translations
-- iterate the ResultSortData
for dictionary_name, internal_names in pairs(sort_data) do
-- insert into the corresponding dictionary in the player's table
local dictionary = translations[dictionary_name]
-- for each internal name
for i = 1, #internal_names do
local internal_name = internal_names[i]
-- if the translation did not succeed, fall back on the internal name
-- another valid option is to not add anything if it failed, in which case this logic would be different
local result = e.translated and e.result or internal_name
-- add the translation or internal name to the dictionary, keyed by the internal name
dictionary[internal_name] = result
end
end
end
-- run some logic when the player is finished translating
-- this is where you would build your GUI if it depends on the translations existing
if finished then
game.print("Player ["..game.get_player(e.player_index).name.."] has finished translations")
end
end)
event.on_player_created(function(e)
init_player(e.player_index)
-- the player is already connected when this event runs, so it's safe to start translations immediately
start_translations(e.player_index)
end)
event.on_player_joined_game(function(e)
local player_flags = global.players[e.player_index].flags
-- if the translate_on_join flag is set
if player_flags.translate_on_join then
-- unset the flag so this only happens once
player_flags.translate_on_join = false
-- start translating
start_translations(e.player_index)
end
end)
event.on_player_left_game(function(e)
-- if the player is currently translating
if translation.is_translating(e.player_index) then
-- cancel the translations
translation.cancel(e.player_index)
-- reset the player's translation tables
local player_table = global.players[e.player_index]
player_table.translations = table.shallow_copy(empty_translation_tables)
-- re-set the flag so the translations will restart when they re-join
player_table.flags.translate_on_join = true
end
end)
event.on_player_removed(function(e)
-- if the player is currently translating
if translation.is_translating(e.player_index) then
-- cancel the translations
translation.cancel(e.player_index)
end
-- destroy the player's data
global.players[e.player_index] = nil
end)