• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.
  • Create a faction for Warcraft 3 and enter Hive's 19th Techtree Contest: Co-Op Commanders! Click here to enter!
  • Get your art tools and paintbrushes ready and enter Hive's 34th Texturing Contest: Void! Click here to enter!

Does war3mapSkin.txt no longer work for custom localized strings?

Level 24
Joined
Jun 26, 2020
Messages
1,937
Hello, a year and a half ago I made this thread: Can war3mapSkin.txt be used to create custom localized strings?
Where I said how can I used the war3mapSkin.txt file to create custom localized strings, and recently I tried the same thing with this flie:
Code:
[FrameDef]

YOU_HAVE_YOUR_DIGIMON=You already got your digimon!
And then imported it to my map with the exact root "war3mapSkin.txt", but and tried to print it:
  • Game - Display to (All players) the text: (Localize(YOU_HAVE_YOUR_DIGIMON))
But it printed me "YOU_HAVE_YOUR_DIGIMON" instead of "You already got your digimon!", I don't know why, probably is my map because I tried the same thing in a blank map and it worked fine as it should.

Do you know what could be the reason?

Edit: I found the reason, if I modify the Game Interface it overwrites my imported war3mapSkin.txt file, so I need to be more tricky then.
 
Last edited:
Level 24
Joined
Jun 26, 2020
Messages
1,937
Out of curiosity, any reason you don't want to use war3map.wts for handling your localization? (e.g. you can open up the Blizzard campaign maps in an MPQ editor to see their _Locales folder structure)
I use both, because the war3map.wts file doesn't let me add more custom strings, I tried once and my map file got bugged, and the methods to trick the game to get more that I saw are more annoying than using the war3mapSkin.txt file.
 
hmm interesting. If the only problem is adding more strings, then why not create a GUI trigger (with no event) and add strings there? e.g.:
  • Strings
    • Events
    • Conditions
    • Actions
      • Game - Display to (All players) the text: New String 1
      • Game - Display to (All players) the text: New String 2
Then when you save the map, those strings will get added to the war3map.wts:
Code:
STRING 7
{
New String 1
}

STRING 8
{
New String 2
}

Or do you mean that you eventually run into a limit?
 
Level 24
Joined
Jun 26, 2020
Messages
1,937
hmm interesting. If the only problem is adding more strings, then why not create a GUI trigger (with no event) and add strings there? e.g.:
  • Strings
    • Events
    • Conditions
    • Actions
      • Game - Display to (All players) the text: New String 1
      • Game - Display to (All players) the text: New String 2
Then when you save the map, those strings will get added to the war3map.wts:
Code:
STRING 7
{
New String 1
}

STRING 8
{
New String 2
}

Or do you mean that you eventually run into a limit?
I think this is more that I wanna avoid using the war3map.wts as much as I can, because by itself is not very flexible and very volatile and also the strings are very all out of place.
 
good to know! I don't think a ton of people have actually localized their maps, so kudos to you for doing so and documenting your findings. :thumbs_up:

And yeah I don't think warcraft 3's processes around localization are very intuitive--and the editor's way of saving things is definitely volatile. Because of that, I'd personally treat it as a task to do whenever you're finalizing your map. Or if you plan on doing this process regularly and you're experienced with programming/scripting, I'd personally store the translations in a separate file completely as key/values:
Code:
--
-- Example 1: base language as key (English) - translated language as value (French)
--

"Twenty minutes later, at Uther's encampment near the Blackrock clan village..." = "Vingt minutes plus tard, au camp d’Uther non loin du village du clan Rochenoire…"

Or as YAML with specific keys (this is a bit more standard, but wc3 doesn't let you really name your strings beyond assigning them a number):
YAML:
--
-- Example 2: YAML with specific keys
--

map_description: "Vingt minutes plus tard, au camp d’Uther non loin du village du clan Rochenoire…"

mission_failed: |
    |cffffcc00ÉCHEC DE LA MISSION|r
    Tous vos bâtiments ont été détruits

And then I'd write a script that can take in your English war3map.wts and spit out the translated war3map.wts. That way the order isn't really any issue and you can translate your strings in whatever format you desire. You could even make your script emit warnings for untranslated strings (or unused translations!). Just throwing out ideas, chatGPT could probably help whip up something that suits your needs. :thumbs_up:
 
Level 24
Joined
Jun 26, 2020
Messages
1,937
hmm interesting. If the only problem is adding more strings, then why not create a GUI trigger (with no event) and add strings there? e.g.:
  • Strings
    • Events
    • Conditions
    • Actions
      • Game - Display to (All players) the text: New String 1
      • Game - Display to (All players) the text: New String 2
Then when you save the map, those strings will get added to the war3map.wts:
Code:
STRING 7
{
New String 1
}

STRING 8
{
New String 2
}

Or do you mean that you eventually run into a limit?
Oh, I didn't consider the flaw with this method, I CAN'T directly access to the values on the string table, and doing "TRIGSTR_XXXX" doesn't do sh*t, the only way to do that requires hack the game and that requires using Jass and I'm using Lua, this is the only existing method, and its precisly what I wanted to avoid: How to: Warcraft 3 Reforged Localized Dialogues
 
Oh, I didn't consider the flaw with this method, I CAN'T directly access to the values on the string table, and doing "TRIGSTR_XXXX" doesn't do sh*t, the only way to do that requires hack the game and that requires using Jass and I'm using Lua, this is the only existing method, and its precisly what I wanted to avoid: How to: Warcraft 3 Reforged Localized Dialogues
Ah okay. I noticed that tutorial talks about using items and physically creating them on the map (which can be tedious). Another option is to use abilities, since you can just use BlzGetAbilityTooltip and that'll return a localized string (without you having to "create" anything). When you customize the tooltip of the ability, it'll automatically get added to the war3map.wts since reforged. You could potentially use many levels to store related strings into a single ability.

But if that still doesn't fit your purposes, or you'd rather use those keys (e.g. YOU_HAVE_YOUR_DIGIMON), I'd recommend this approach:
  1. Similar to before, create a trigger to store all your localizable strings. Instead, this time put the keys right before the line containing their value:
    • Localizable Strings
      • Events
      • Conditions
      • Actions
        • -------- Format: --------
        • -------- First line: Key (e.g. YOU_HAVE_YOUR_DIGIMON) --------
        • -------- Second line: String value in your base language (e.g. "You already got your digimon!") --------
        • -------- --------
        • -------- Important: --------
        • -------- Be sure to always list the key and value in one go before saving the map to ensure the order is correct. --------
        • -------- Editing keys/strings is fine, as that will update the existing entry --------
        • -------- In case you make any mistakes, you can copy this trigger, delete it, save the map, and paste it again to "refresh" this in the string table with the correct order --------
        • -------- --------
        • Game - Display to (All players) the text: KEY_OPENING_GREETING
        • Game - Display to (All players) the text: Hello world!
        • Game - Display to (All players) the text: KEY_YOU_HAVE_YOUR_DIGIMON
        • Game - Display to (All players) the text: You already got your digimon!
    We'll rely on this order when reading the string table to know which "value" belongs to which localization "key". And you'll want to use a reasonably unique prefix, e.g. KEY_, so we can search for these keys manually later. Don't translate these keys--they should remain fixed across all locales.

    But how do we guarantee that they are ordered one after the other the way we expect? The general rule with these is that any new strings will be appended to the war3map.wts when you save your map. So whenever you need to add a new string, just make sure you always put a line for the KEY_ and then a line for the value before saving the map--that way they'll be arranged one after the other. If you need to modify the key/value afterwards--that's fine, the editor is smart enough to modify the existing entry. But if you're ever worried that you made a mistake, you can always copy the trigger, delete it, save the map (which will remove those strings), and then paste the trigger again to append them all to the string table in the correct order.
  2. Next, you can use a helper script like the one below to read the table on initialization and set up a dictionary, e.g.
    KEY_OPENING_GREETING => "Hello world!" (localized)
    KEY_YOU_HAVE_YOUR_DIGIMON => "You already got your digimon!" (localized)

    Lua:
    -- Requires DebugUtils and TotalInitialization
    Debug.beginFile("StringTable")
    do
        LocalizedStringsByKey = {}
    
        --- Looks up a string with the given key and returns the localized string value.
        --- @param key string
        --- @return string
        function Localize(key)
            return LocalizedStringsByKey[key]
        end
    
        function LookupStringByTableIndex(index)
            if type(index) ~= "number" or math.type(index) ~= "integer" or index < 1 then
                return ""
            end
    
            if index < 10 then
                return "TRIGSTR_00" .. tostring(index)
            elseif index < 100 then
                return "TRIGSTR_0" .. tostring(index)
            end
            
            return "TRIGSTR_" .. tostring(index)
        end
    
        local function scanStringTable()
            for i = 1, 10000 do
                -- GetLocalizedString will turn the TRIGSTR_ representation into the actual string table value in memory
                local potentialKey = GetLocalizedString(LookupStringByTableIndex(i))
                if potentialKey:sub(1, 4) == "KEY_" then
                    local value = GetLocalizedString(LookupStringByTableIndex(i + 1))
                    LocalizedStringsByKey[potentialKey] = value
                end
            end
        end
    
        OnInit.global(scanStringTable)
    end
    Debug.endFile()

    On initialization, this will read the string table (by reading TRIGSTR_001 through TRIGSTR_10000), and then any time it encounters a string starting with KEY_, it'll store the next item in the string table as the value for that key. So in our example, the scanner would encounter "KEY_YOU_HAVE_YOUR_DIGIMON", and then it'd look up the next string listed and store that as the value ("You already got your digimon!").
  3. Finally, when you want to use it, you can just call Localize("KEY_YOU_HAVE_YOUR_DIGIMON"). If you don't want to use magic strings, you could make global Lua variables in a separate file and use those instead.
    Lua:
    Debug.beginFile("Display Strings")
    do
        local function registerEvents()
            local timer = CreateTimer()
            TimerStart(timer, 1, false, function ()
                DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 10, "Printing localized strings via key look-up:")
                DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 10, Localize("KEY_OPENING_GREETING"))
                DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 10, Localize("KEY_YOU_HAVE_YOUR_DIGIMON"))
            end)
        end
    
        OnInit.map(registerEvents)
    end
    Debug.endFile()
If done correctly, this will output what we expect:
1750915813833.png


You'll just want to make sure your war3map.wts is correctly synced and translated across any locales you want to use. I'll attach the sample map below if you want to test it (you'll need to use the French locale to test the translation).
 

Attachments

  • LocalizationSampleMap.w3x
    110.7 KB · Views: 0
Top