How to get the translation?

Place to get help with not working mods / modding interface.
Post Reply
User avatar
yaim904
Long Handed Inserter
Long Handed Inserter
Posts: 86
Joined: Wed Nov 17, 2021 11:26 pm
Contact:

How to get the translation?

Post by yaim904 »

What am I doing wrong?
I am trying to get the translation of the texts I want.

Code: Select all

-- Control.lue
local localised_name = { "recipe-name.iron-plate" }
Player.request_translation( localised_name )
The event is not receiving anything

Code: Select all

script.on_event( defines.events.on_string_translated, function( event )
    log( "LoadTranslation" )
end)
What am I doing wrong?
Solo entiendo español, pero si tu también lo entiendes, escríbeme
:D
Everything i write in English is translated by google.
:D

Pi-C
Smart Inserter
Smart Inserter
Posts: 1639
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: How to get the translation?

Post by Pi-C »

The event will be triggered only if the string could be translated, i.e. if the return value of LuaPlayer::request_translation(localised_string) is true.
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

User avatar
yaim904
Long Handed Inserter
Long Handed Inserter
Posts: 86
Joined: Wed Nov 17, 2021 11:26 pm
Contact:

Re: How to get the translation?

Post by yaim904 »

Pi-C wrote:
Mon May 09, 2022 3:15 am
The event will be triggered only if the string could be translated, i.e. if the return value of LuaPlayer::request_translation(localised_string) is true.
I don't understand you, because the function always returns true

But just in case, I tried other variants and it still doesn't work.

Code: Select all

-- Control.lue
local localised_name = { "item-name.iron-plate" }
Player.request_translation( localised_name )

local localised_name = { "recipe-name.iron-plate" }
Player.request_translation( localised_name )

local localised_name = { "entity-name.iron-plate" }
Player.request_translation( localised_name )
I never see this message in "factorio-current.log"

Code: Select all

script.on_event( defines.events.on_string_translated, function( event )
    log( "LoadTranslation" )
end)
What am I doing wrong?
Solo entiendo español, pero si tu también lo entiendes, escríbeme
:D
Everything i write in English is translated by google.
:D

User avatar
DaveMcW
Smart Inserter
Smart Inserter
Posts: 3699
Joined: Tue May 13, 2014 11:06 am
Contact:

Re: How to get the translation?

Post by DaveMcW »

Paste this in the console.

Code: Select all

/c
script.on_event(defines.events.on_string_translated, function(event)
  game.print(event.result)
end)
game.player.request_translation({"item-name.iron-plate"})

User avatar
yaim904
Long Handed Inserter
Long Handed Inserter
Posts: 86
Joined: Wed Nov 17, 2021 11:26 pm
Contact:

Re: How to get the translation?

Post by yaim904 »

Reading your comments it occurred to me to try some things
Pi-C wrote:
Mon May 09, 2022 3:15 am
DaveMcW wrote:
Tue May 10, 2022 12:14 am
This code worked!!

Code: Select all

-- Control.lua
local localised_name = Player.forge.recipes[ "iron-plate" ]
localised_name = localised_name.localised_name
Player.request_translation( localised_name )

script.on_event(
    defines.events.on_string_translated,
    function( event )
        log( "LoadTranslation" )
    end
)
Although, it only works when creating a map and not when loading a save game.

The events I am using are:

Code: Select all

on_init( )
defines.events.on_player_created
defines.events.on_player_changed_force
Check the Data lifecycle and it is following the sequence [ 1 ] Control.lua, [ 2 ] on_init( ) and [ 3 ] Migrations
Image

For some reason the event is not executed when the game loads.
Do you know of any way to activate the event when a game loads?
Last edited by yaim904 on Tue May 10, 2022 1:53 pm, edited 1 time in total.
Solo entiendo español, pero si tu también lo entiendes, escríbeme
:D
Everything i write in English is translated by google.
:D

Pi-C
Smart Inserter
Smart Inserter
Posts: 1639
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: How to get the translation?

Post by Pi-C »

yaim904 wrote:
Mon May 09, 2022 11:09 am
Pi-C wrote:
Mon May 09, 2022 3:15 am
The event will be triggered only if the string could be translated, i.e. if the return value of LuaPlayer::request_translation(localised_string) is true.
I don't understand you, because the function always returns true
Here it says:
request_translation(localised_string) → boolean

Requests a translation for the given localised string. If the request is successful the on_string_translated event will be fired at a later time with the results.
So there must be cases where the function will return false. That would have explained why the event wasn't triggered.
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

robot256
Filter Inserter
Filter Inserter
Posts: 594
Joined: Sun Mar 17, 2019 1:52 am
Contact:

Re: How to get the translation?

Post by robot256 »

Do you know of any way to activate the event when a game loads?
Normally that would be the purpose of on_load(), in the lower branch of the data lifestyle diagram. It runs when you load the save, and is only allowed to do things to restore the game state to exactly what it was before you saved and quit. I think it is not allowed to issue new event requests (either it will cause an immediate error, or it will cause a desync in multiplayer).

The correct option in your case would be to store the results you get from the first event in the global table, so that you can access it whenever you want. This is stored in the save file, so it will already be present when you load a game. When later player events fire, you can uodate the values in the global table.

User avatar
yaim904
Long Handed Inserter
Long Handed Inserter
Posts: 86
Joined: Wed Nov 17, 2021 11:26 pm
Contact:

Re: How to get the translation?

Post by yaim904 »

robot256 wrote:
Tue May 10, 2022 3:41 pm
I wanted to avoid that option because of the weight (in MB) that would mean.
But if I have no other choice...

If I do it as you say, when should I upload the translations?
That is, in such a way that it affects the players who open a previously saved game.
I want to load all the information before giving control of the character to the player.
Solo entiendo español, pero si tu también lo entiendes, escríbeme
:D
Everything i write in English is translated by google.
:D

robot256
Filter Inserter
Filter Inserter
Posts: 594
Joined: Sun Mar 17, 2019 1:52 am
Contact:

Re: How to get the translation?

Post by robot256 »

I'm not very familiar with how the translation API works. Does it do things that you can't with simple localised printing? Where do you need to display the translated strings?

Normally I just do, for example, "game.print({"item-name.iron-plate"})" and it selects the correct translation for each player that seea it.

Xorimuth
Filter Inserter
Filter Inserter
Posts: 623
Joined: Sat Mar 02, 2019 9:39 pm
Contact:

Re: How to get the translation?

Post by Xorimuth »

robot256 wrote:
Tue May 10, 2022 5:12 pm
I'm not very familiar with how the translation API works. Does it do things that you can't with simple localised printing? Where do you need to display the translated strings?

Normally I just do, for example, "game.print({"item-name.iron-plate"})" and it selects the correct translation for each player that seea it.
The only use I've seen is it allows you to do your own searching for items by name, as used in Recipe Book: https://mods.factorio.com/mod/RecipeBook. Pretty much any other use should just be printed/displayed as a localised string, yes.
My mods
Content: Freight Forwarding | Spidertron Patrols | Spidertron Enhancements | Power Overload
QoL: Factory Search | Remote Configuration | Module Inserter Simplified | Wire Shortcuts X | Ghost Warnings

User avatar
yaim904
Long Handed Inserter
Long Handed Inserter
Posts: 86
Joined: Wed Nov 17, 2021 11:26 pm
Contact:

Re: How to get the translation?

Post by yaim904 »

robot256 wrote:
Tue May 10, 2022 5:12 pm
You're right
I haven't said why I'm doing all this.

I want to create a dictionary from which to filter a word (or fragment of a word) in the translations.
Xorimuth wrote:
Tue May 10, 2022 6:01 pm
Yes, that's what I'm looking for, but that MOD uses a library that I don't understand, and I have tried to understand it.
Solo entiendo español, pero si tu también lo entiendes, escríbeme
:D
Everything i write in English is translated by google.
:D

Xorimuth
Filter Inserter
Filter Inserter
Posts: 623
Joined: Sat Mar 02, 2019 9:39 pm
Contact:

Re: How to get the translation?

Post by Xorimuth »

yaim904 wrote:
Tue May 10, 2022 6:14 pm
robot256 wrote:
Tue May 10, 2022 5:12 pm
You're right
I haven't said why I'm doing all this.

I want to create a dictionary from which to filter a word (or fragment of a word) in the translations.
Xorimuth wrote:
Tue May 10, 2022 6:01 pm
Yes, that's what I'm looking for, but that MOD uses a library that I don't understand, and I have tried to understand it.
Fair enough - though I'd say that a lot of work and thought has been put into that library (https://github.com/factoriolib/flib/blo ... ionary.lua, https://factoriolib.github.io/flib/modu ... onary.html) so you'd save yourself a lot of time and effort by using it. Have you seen the example: https://factoriolib.github.io/flib/exam ... y.lua.html# ?
My mods
Content: Freight Forwarding | Spidertron Patrols | Spidertron Enhancements | Power Overload
QoL: Factory Search | Remote Configuration | Module Inserter Simplified | Wire Shortcuts X | Ghost Warnings

robot256
Filter Inserter
Filter Inserter
Posts: 594
Joined: Sun Mar 17, 2019 1:52 am
Contact:

Re: How to get the translation?

Post by robot256 »

Your use case makes sense to me. No, there is no way to avoid keeping the dictionary in global if you want to avoid recreating it from scratch every time the user enters a query. It is really not that much memory, only a few kilobytes of text per player, and it will compress well in the save file zip. I second Xorimuth's encouragement to use the Flib module, because it does exactly what you need already.

Xorimuth
Filter Inserter
Filter Inserter
Posts: 623
Joined: Sat Mar 02, 2019 9:39 pm
Contact:

Re: How to get the translation?

Post by Xorimuth »

Note that you can use flib's set_use_local_storage to avoid storing anything in global (I think) but at the expense of needing to regenerate the dictionary every load (generating the dictionary causes quite large network traffic in multiplayer).
My mods
Content: Freight Forwarding | Spidertron Patrols | Spidertron Enhancements | Power Overload
QoL: Factory Search | Remote Configuration | Module Inserter Simplified | Wire Shortcuts X | Ghost Warnings

User avatar
yaim904
Long Handed Inserter
Long Handed Inserter
Posts: 86
Joined: Wed Nov 17, 2021 11:26 pm
Contact:

Re: How to get the translation?

Post by yaim904 »

Xorimuth wrote:
Tue May 10, 2022 6:36 pm
Fair enough - though I'd say that a lot of work and thought has been put into that library (https://github.com/factoriolib/flib/blo ... ionary.lua, https://factoriolib.github.io/flib/modu ... onary.html) so you'd save yourself a lot of time and effort by using it. Have you seen the example: https://factoriolib.github.io/flib/exam ... y.lua.html# ?
I did a quick test with the dictionary example, but they have the same problem (doesn't work on pre-saves without the mod).

Xorimuth wrote:
Tue May 10, 2022 6:01 pm
The only use I've seen is it allows you to do your own searching for items by name, as used in Recipe Book: https://mods.factorio.com/mod/RecipeBook. Pretty much any other use should just be printed/displayed as a localised string, yes.
I checked the code RecipeBook and i found that this mod does work, but it does it with the on_tick.

Taking into account the above and taking advantage of the fact that my MOD does not need to start with the game or the player, I am thinking of making the translations load when the player connects to the game for the first time (Player.online_time >= X) and save all the information in a local variable.

I welcome comments, suggestions and advice.
Solo entiendo español, pero si tu también lo entiendes, escríbeme
:D
Everything i write in English is translated by google.
:D

robot256
Filter Inserter
Filter Inserter
Posts: 594
Joined: Sun Mar 17, 2019 1:52 am
Contact:

Re: How to get the translation?

Post by robot256 »

Is on_player_joined_game fired when loading a single player map? I think there is a small bug in the example. This code from on_configuration_changed should also be at the end of on_init to handle when the mod is added to an existing single player map.

Code: Select all

-- Request translations for all connected players
    for _, player in pairs(game.players) do
      if player.connected then
        dictionary.translate(player)
      end
    end
Player.online_time only tells you how long a player has been connected total since the game started. It won't tell you if they are connected right now or how long it has been since they joined most recently.

Pi-C
Smart Inserter
Smart Inserter
Posts: 1639
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: How to get the translation?

Post by Pi-C »

robot256 wrote:
Wed May 11, 2022 4:42 pm
Is on_player_joined_game fired when loading a single player map?
IIRC, the event fires only once in single player, after on_player_created. So the safe way to handle both single- and multiplayer games is to store player data in the global table when on_player_joined_game fires, and to reinitialize the data when on_configuration_changed triggers.
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

robot256
Filter Inserter
Filter Inserter
Posts: 594
Joined: Sun Mar 17, 2019 1:52 am
Contact:

Re: How to get the translation?

Post by robot256 »

So if the mod is installed before the player joins, the mod can add the player's translation in on_player_joined_game. If the mod is installed after the player joined, it will add the player's translation in on_configuration_changed (which will run after on_init when adding the mod to an existing save).

What did you mean by
yaim904 wrote:doesn't work on pre-saves without the mod
?

User avatar
yaim904
Long Handed Inserter
Long Handed Inserter
Posts: 86
Joined: Wed Nov 17, 2021 11:26 pm
Contact:

Re: How to get the translation?

Post by yaim904 »

I will start by saying: I have not tried what you are saying.
robot256 wrote:
Wed May 11, 2022 5:23 pm
So if the mod is installed before the player joins, the mod can add the player's translation in on_player_joined_game. If the mod is installed after the player joined, it will add the player's translation in on_configuration_changed (which will run after on_init when adding the mod to an existing save).

What did you mean by
yaim904 wrote:doesn't work on pre-saves without the mod
?
The translation is loaded in the on_string_translated event, so I need the event to fire, which is not happening when loading a previously saved game (I'm testing in a single game).

I'll use on_tick to activate on_string_translated and load the translation no matter if the game is loading or being created (I'll code it over the weekend), I still have to perfect this idea, but it seems for the best.

I know, all this is done by Factorio Library, but I want to learn how to do it, so I can fix it when something goes wrong (or a game update makes the mod obsolete). Maybe later in a next version I will integrate it.
Solo entiendo español, pero si tu también lo entiendes, escríbeme
:D
Everything i write in English is translated by google.
:D

User avatar
yaim904
Long Handed Inserter
Long Handed Inserter
Posts: 86
Joined: Wed Nov 17, 2021 11:26 pm
Contact:

Re: How to get the translation?

Post by yaim904 »

I haven't tried this code yet, but it's an idea of what I want to do.

Code: Select all

-- Variable initialization
_G.OnTick = _G.OnTick or { }

-- Requests per tick
local TickRequest = 25

local function Add( Table, Data )
    if not Table[ 1 ] then table.insert( Table, { } ) end
    if #Table[ 1 ] > TickRequest - 1 then table.insert( Table, 1, { } ) end
    table.insert( Table[ 1 ], Data )
end

local function AddLocalised( Data )
    local Received = game.table_to_json( Data.Localised )
    local Expected = game.table_to_json( { "locale-identifier" } )

    OnTick.Queue = OnTick.Queue or { }
    local Queue = OnTick.Queue

    if Received == Expected then
        Queue.Language = Queue.Language or { }
        Add( Queue.Language, Data )
    else Add( Queue, Data ) end
end

script.on_event(
    defines.events.defines.events.on_tick,
    function ( Event )
       if not OnTick.Queue then return end
       local Queue = OnTick.Queue

       OnTick.Translated = OnTick.Translated or { }
       local Translated = OnTick.Translated

        if not Queue.Language then goto JumpLanguage end
        local Language = table.remove( Queue.Language )
        if #Queue.Language < 1 then Queue.Language = nil end
        for _, Data in pairs( Language ) do
            Data.Player.request_translation( Data.Localised )
        end
        Translated.Language = Translated.Language or { }
        table.insert( Translated.Language, Language )
        :: JumpLanguage ::

        if not Queue then goto JumpLocalised end
        local ArrayData = table.remove( Queue )
        if #Queue < 1 then OnTick.Queue = nil end
        for _, Data in pairs( ArrayData ) do
            Data.Player.request_translation( Data.Localised )
        end
        table.insert( Translated, ArrayData )
        :: JumpLocalised ::
    end
)

local function Remove( Data )
    for i = 1, #Data.Array, 1 do
        for j = 1, #Data.Array[ i ], 1 do
            local Element = Data.Array[ i ][ j ]
            Expected = game.table_to_json( Element.Localised )
            if Element.Player == Data.Player and Expected == Data.Received then
                -- Save data
                table.remove( Data.Array[ i ], j )
                if #Data.Array[ i ] < 1 then table.remove( Data.Array, i ) end
                if #Data.Array < 1 then Translated = nil end
                return true
            end
        end
    end
    return false
end

script.on_event(
    defines.events.defines.events.on_string_translated,
    function ( Event )
        if not OnTick.Translated then return end
        local Expected = game.table_to_json( { "locale-identifier" } )
        local Translated = OnTick.Translated

        local Data = { }
        Data.Player = game.get_player( Event.player_index )
        Data.Received = game.table_to_json( Event.localised_string )

        if Data.Received ~= Expected then goto JumpLanguage end
        if not Translated.Language then goto JumpLanguage end
        Data.Array = Translated.Language
        if Remove( Data ) then return end
        :: JumpLanguage ::

        if not Translated then goto JumpLocalised end
        Data.Array = Translated
        if Remove( Data ) then return end
        :: JumpLocalised ::
    end
)

script.on_init( function ( )
    AddLocalised( {
        [ 'Player' ] = { },
        [ 'MOD' ] = "MOD Name",
        [ 'Localised' ] = { '', { 'item-name.iron-plate' } }
    } )
    AddLocalised( {
        [ 'Player' ] = { },
        [ 'MOD' ] = "MOD Name",
        [ 'Localised' ] = { { 'locale-identifier' } }
    } )
end )
I've already checked out the Factorio Library code and they do it more efficiently using advanced string handling.

Which left me with several questions:
  • How much does it cost to make as many requests as I am doing?
  • When functions or variables are created globally, is this shared with another MOD??
As always, I will accept advice, recommendations, suggestions and comments.
Solo entiendo español, pero si tu también lo entiendes, escríbeme
:D
Everything i write in English is translated by google.
:D

Post Reply

Return to “Modding help”