Forum rules - please read before posting.

Translating custom script with multiple variables and arrays

First of all, Happy New Year! Hope you all had a great time during Christmas and got some well deserved rest!

I'm exploring some translation opportunities for our game and I'm stuck with some custom translation from scripts.

I'm trying to get some of my custom scripts to be picked up by Gathering Text. I've managed to do it based on the CustomTranslatableExample with a simple line of text written in script. This works well for a single line of text in script and once I do Gather Text, it get's picked up and appears later in my CSV, which is great.

Now, I'm trying to extend this to some other parts of the script where I use string arrays and multiple variables in a single script. For example, a quest log, which has:

        public string[] questTitle = new string[MAX_QUESTS];
        public string[] questDescription = new string[MAX_QUESTS];

Ideally I would like to get both of these to be picked up and later appear in CSV, but I can't figure out how to deal with it.

I've found two other examples that tackle the two issues I'm having (array and multiple variables in a single script). First, I realised that MenuJournal.cs is using lists, and has something like this for the translations:

        public string GetTranslatableString (int index)
        {
                return pages[index].text;
        }

For example, my questTitle array normally gets initiated at the start of the game. I'm guessing the problem with array is that when AC is gathering text, the array is empty and so it doesn't get picked up for translation?

I also found out that Document.cs is using more than one variable as translatable in it's script. It uses something like this:

        public string GetTranslatableString (int index)
        {
                if (index == 0)
                {
                        return Title;
                }
                else
                {
                        return pages[index-1].text;
                }
        }

In this case, I can't figure out how index is working. At what stage it would be 0, for it to know that you're talking about Title or a Page.

I guess what I need is something like:

        public string GetTranslatableString (int index)
        {
                if (index == 0)
                {
                        return questTitle[index];
                }
                else
                {
                        return questDescription[index-1];
                }
        }

But of course, that didn't work.

So the two problems I'm struggling with, is to get AC to pick up two (or more) variables from a single custom script. It appears to me that ITranslatable is designed to work best with one translatable variable per script, but the comment in GetNumTranslatables() -Return 1 unless you want to store multiple translatable texts in a single script. - sounds like it is possible to grab more variables from a script.

Other problem is, how to pick up text from a variable? MyJournal.cs is picking up lists, but can I do the same thing with an array? I hope this makes sense, any pointers in the right direction are greatly appreciated! Thank you!

Unity Version 2018.2.18f1
AC Version 1.68.4

Comments

  • edited January 2020

    FWIW, AC will be getting a new "Objectives" system as part of the next update. This won't affect or involve logic/variables, but will provide an easier way of displaying active and completed quests, each with textures, descriptions and multiple states.

    GetNumTranslatables() -Return 1 unless you want to store multiple translatable texts in a single script. - sounds like it is possible to grab more variables from a script.

    This is it, exactly. The "Gather text" operation needs to know, in advance, how many translatable lines a given class has. It'll then iterate through GetTranslatableString this many times - each with a different index value.

    As a super-clear example, if GetNumTranslatables returns 2, then the following will be called (in sequence)

    GetTranslatableString (0);
    GetTranslatableString (1);
    

    (The same goes for the other functions that feature an "index" parameter).

    It's also important to note that while GetNumTranslatables needs to return the number of lines in advance, this itself needn't be fixed - and can be based on conditions that you set within, i.e.:

    public int GetNumTranslatables ()
    {
        if (some_condition)
        {
            return 1;
        }
        return 2;
    }
    

    Other problem is, how to pick up text from a variable?

    So long as you're returning a string, it can come from an array or a List, or even a class/struct. From your description, I'd say you're looking to have something more along the lines of:

    public int GetNumTranslatables ()
    {
        return MAX_QUESTS * 2;
    }
    
    public string GetTranslatableString (int index)
    {
        bool isForDescription = (index >= MAX_QUESTS);
    
        if (isForDescription)
        {
            return questDescription[index - MAX_QUESTS];
        }
        else
        {
            return questTitle[index];
        }
    }
    
  • Thank you! I got it to work... Well, sort of.

    This is what I've managed to conjure. It produces best results, but I have some problems with it.

    /** ITranslatable implementation */
        public string GetTranslatableString(int index)
        {
            InitQuests(); // Normally runs at the starts of the game, initialises all the questlog titles and descriptions
            bool isForDescription = (index >= MAX_QUESTS);
            if (isForDescription)
            {
                return questDescription[index - MAX_QUESTS];
            }
            else
            {
                return questTitle[index];
            }
        }
        public int GetTranslationID(int index)
        {
            bool isForDescription = (index >= MAX_QUESTS);
            if (isForDescription)
            {
                return questDescriptionID[index - MAX_QUESTS];
            }
            else
            {
                return questTitleID[index];
            }
        }
    
        #if UNITY_EDITOR
        /** Note: These functions are placed in UNITY_EDITOR as they only need accessing from within the Speech Manager, outside of runtime */
        public int GetNumTranslatables()
        {
            return MAX_QUESTS * 2; // *2 Because we have two arrays worth of text
        }
        public bool CanTranslate(int index)
        {
            InitQuests(); // Again, without this, the arrays would be empty (?)
            bool isForDescription = (index >= MAX_QUESTS);
            if (isForDescription)
            {
                return !string.IsNullOrEmpty(questDescription[index - MAX_QUESTS]);
            }
            else
            {
                return !string.IsNullOrEmpty(questTitle[index]);
            }
        }
        public bool HasExistingTranslation(int index)
        {
            bool isForDescription = (index >= MAX_QUESTS);
            if (isForDescription)
            {
                return (questDescriptionID[index - MAX_QUESTS] >= -1); ;
            }
            else
            {
                return (questTitleID[index] >= -1); ;
            }
        }
        public void SetTranslationID(int index, int lineID)
        {
            bool isForDescription = (index >= MAX_QUESTS);
            if (isForDescription)
            {
                questDescriptionID[index - MAX_QUESTS] = lineID; ;
            }
            else
            {
                questTitleID[index] = lineID; ;
            }
        }
        public string GetOwner()
        {
            return string.Empty;
        }
        public bool OwnerIsPlayer()
        {
            return false;
        }
        public AC_TextType GetTranslationType(int index)
        {
            return AC_TextType.Custom;
        }
        #endif
    

    If I understand correctly, if I already have Gathered text before, and I want to Gather some more, I first have to click on Reset Text. I can't seem to find any info about this in the Manual, but if I don't do so, Unity pops up a message that the text has been gathered, I click OK and it just freezes. I have to kill Unity through Task Manager, and when I re-open it, it does not have the gathered text anymore.

    Resetting Text before Gather, seems to work fine though.

    It appears to me that without InitQuests(), the Text Gathering doesn't pick up the lines from my arrays (questTitle, questDescription). QuestInit() is a function I run at the start of the game and I basically set up all text in arrays in there - otherwise all arrays are empty, so I figured GetTranslaableString() and CanTranslate() need InitQuests() in order to be able to read the arrays.

    By doing the above, I finally managed to get the text across to CSVs but there's two problems.

    First is that the ID for first of the Arrays starts with -1, but I'm not sure if that's an issue.

    Second thing is that all the lines appear duplicated in CSV, so I get something like this:

    Title 1
    Title 2
    Description 1
    Description 2
    Title 1
    Title 2
    Description 1
    Description 2
    

    I'm suspecting it's something to do with
    public int GetNumTranslatables() { return MAX_QUESTS * 2; }
    But to my understanding it should work just fine. If I remove *2 then CSV only exports the titles, as expected, but also duplicated.

    I'm just really puzzled with all this, but it does seem like it's getting there. Any thoughts on how to get around the problems? Thank you.

  • edited January 2020

    If I understand correctly, if I already have Gathered text before, and I want to Gather some more, I first have to click on Reset Text.

    No, Resetting Text will clear all previously-set ID numbers - meaning they'll likely be assigned new ones and invalidate old translation / speech audio files. You should not normally have to Reset Text - re-gathering should be enough.

    If Unity crashes without this, I'd recommend temporarily commenting out your script and re-gathering so that we can be sure your script is the issue.

    Are your ID arrays public/serializable? Do duplicated entries appear in the Speech Manager, or just the CSV - and are their ID values also duplicated?

  • No, Resetting Text will clear all previously-set ID numbers - meaning they'll likely be assigned new ones and invalidate old translation / speech audio files.

    Ahh, I thought as much.

    I'd recommend temporarily commenting out your script and re-gathering

    After commenting out the new Translatable code, I managed to Gather Text without first resetting it. I also tried putting the code back in and... it also worked.

    I am sure that I tried this before and it didn't work. At one stage I also discarded all changes in my project, and restored a backed up version (without new code), but even that didn't help if I first didn't reset the text. It appears like everything works just fine now though!

    Are your ID arrays public/serializable?

    If you are referring to questTitleID and questDescriptionID then yes, they are both public.

    Do duplicated entries appear in the Speech Manager, or just the CSV

    They appear duplicated in both.

    and are their ID values also duplicated?

    Nope. The ID's for duplicated text are incremental. So something like this:

    Title 1              = ID 1
    Title 2              = ID 2
    Description 1        = ID 3
    Description 2        = ID 4
    Title 1              = ID 5
    Title 2              = ID 6
    Description 1        = ID 7
    Description 2        = ID 8
    
  • edited January 2020

    From the code that you've shared, all looks as it should be. If the duplicated texts have unique IDs, my first thought would be that the process is somehow uncovering two separate instances of the script. Would that be possible? Where is the script located exactly?

    Try placing the following at the top of your GetNumTranslatables function:

    Debug.Log ("Num translatables: " + (MAX_QUESTS * 2) + " on " + this.name + " in scene " + this.gameObject.scene.name, this);
    

    What then appears in the Console upon gathering text?

  • Oh wow, I feel very silly right now.

    I added the debug code, like advised. Turns out the script was in two scenes and it's meant to be only in one!

    Removed it from the other scene and I get this line twice in the debug:

    Num translatables: 200 on QuestManager in scene MainMenu
    

    I had a quick look over on SpeechManager.cs and it seems like this would be correct though. If I'm not mistaken it first checks for lines that already have ID and then for those that don't, hence two outputs in debug for this line.

    Anyway, the translation appears once in the CSV as well as the Speech Manager.

    Thank you so much for the help!

  • If I'm not mistaken it first checks for lines that already have ID and then for those that don't, hence two outputs in debug for this line.

    That's correct - existing lines are checked first, before new lines.

  • edited January 2020

    Ahh, I have one last problem: the custom translations are starting with ID at -1.

    Even though it's picked up by ITranslatable interface and shows up in CSV and Speech Manager, it doesn't work when retrieving the line with GetTranslatableText. It says:

    Cannot find translation for X because the text has not been added to the Speech Manager.
    

    But it has, it just has an ID of -1.

    Any clue how I can change the value so it starts with 0/1?
    Thank you.

  • This is in the Speech Manager? What about in the script itself? The two should match up.

    All IDs should have a value of 0 or greater. A value of -1 signifies the line hasn't been translated.

    Your "ID arrays" in your script should start off as an array of -1 values - is this the case?

  • This is in the Speech Manager and the CSV. The script also matches with the ID numbers.

    Your "ID arrays" in your script should start off as an array of -1 values - is this the case?

    Not originally, so I've tried initialising the arrays in GetNumTranslatables(), so before it returns number of lines to translate, it sets all ID arrays to -1.

    After doing this, the script doesn't match. The arrays in script are set to -1,-1,1,2,3 and so on, while the ID's in Speech Manager are set to -1,0,1,2,3 and so on.

  • Not originally, so I've tried initialising the arrays in GetNumTranslatables(), so before it returns number of lines to translate, it sets all ID arrays to -1.

    This should only be done if the arrays have not yet been initialised. Even if it's in a backup/duplicate project, I'd recommend resetting all game text and modifying your script so that GetNumTranslatables only resets the ID array if it is not yet initialised.

  • I'd recommend resetting all game text and modifying your script so that GetNumTranslatables only resets the ID array if it is not yet initialised.

    Hmm, sadly I can't find a way to affect that -1 at all. I tried the suggestion, but still the same result. No matter what I do it seems that first variable in the array is assigned that value.

    As a workaround, I simply added a "dummy" string in front of the array that won't be used in actual game (so it doesn't matter if it's translated or not) and now that gets assigned -1 and the rest works fine.

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Welcome to the official forum for Adventure Creator.