Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question How to reference localized string in another localized string?

Discussion in 'Localization Tools' started by Peter77, Sep 24, 2021.

  1. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,454
    I have a localized string entry named
    game_title
    that represents the name of the game. Now I have another localized string entry where I want to insert the
    game_title
    , without having to write code for it.

    How can I do this?

    Example:
    Code (CSharp):
    1. game_title = Super Mario
    2. quit_game_message = Do you really want to quit '<game_title>'?
    The localized string of the
    quit_game_message
    should return:
    Code (CSharp):
    1. Do you really want to quit 'Super Mario'?
    I guess it's somehow possible with SmartFormat, but I didn't figure out how.
     
  2. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,876
    Try this https://docs.unity3d.com/Packages/c...ent-Variables-Source.html#nested-translations

    You can also setup from script. see withNestedTranslation
    https://docs.unity3d.com/Packages/c...gine_Localization_Tables_TableEntryReference_
     
  3. Dredika

    Dredika

    Joined:
    May 16, 2020
    Posts:
    4
    Is it possible to perform this kind of replacement without explicitly providing arguments? In other words, having the system automatically parse the placeholder to find and inject the second localized string.

    For example, a string entry:
    "Your {Stats.strength_name} is too low to move this" , would search the string table 'Stats' for the key 'strength_name'.

    The resulting string would be:
    "Your Strength is too low to move this"

    I feel like writing a custom Source might be the way to do it, based on https://docs.unity3d.com/Packages/c....0/manual/Smart/Creating-a-Custom-Source.html
    I'm still a relative coding newbie and I'm not exactly sure how to go about it, as my attempts have been unsuccessful. Maybe adding some kind of unique character (ex. {@Stats.strength_name}) to flag the placeholder as a Localized String ref.

    This is a simple example; I know I could simply write out the word Strength, but the goal here is convenience when renaming terms/mechanics, as well as enforcing uniformity across game terms. I've worked on games in the past where translations came back with 3-4 different terms for the same concept in the same language, because multiple translators each had their own ideas :)
     
    freeze3548 likes this.
  4. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,876
    Nested strings must be provided as arguments. We did explore adding them the other way but it would be more complex to handle and would likely regress performance as every named placeholder would need to be checked against all the string tables which would require waiting for addressables to complete. By using the persistent variables system we can use a callback later on to refresh the string when the nested operation is completed.
    There was also some other issues which I have forgotten at the moment o_O

    A custom source would be the way to do it if you want to try and create your own. I would be interested to see what you end up with :)
     
  5. Dredika

    Dredika

    Joined:
    May 16, 2020
    Posts:
    4
    I see, good points. I hadn't considered the potential async issues.
    I think ultimately the best solution for me is to set up a Global variables list with all my common terms linked to their table entries.

    After toying around a bit, I was able to get the behaviour I'm looking for with a very crude custom Source. It doesn't do any async loading (all my string tables are pre-loaded for the moment, still early in the project) and there's probably many other things that I overlooked, so I'm not sure how much mileage I can get out of it.

    I placed it at the end of the Sources list to reduce the amount of cases in which it's called. Is that assumption correct?

    Code (CSharp):
    1. public class NestedLocStringSource : ISource
    2.     {
    3.         public bool TryEvaluateSelector(ISelectorInfo selectorInfo)
    4.         {
    5.             var selectors = selectorInfo.Placeholder.Selectors;
    6.  
    7.             if (selectors.Count != 2)
    8.                 return false;
    9.  
    10.             TableReference tableRef = selectors[0].RawText;
    11.             TableEntryReference keyRef = selectors[1].RawText;
    12.  
    13.             // check if String Table exists
    14.             StringTable stringTable = LocalizationSettings.StringDatabase.GetTable(tableRef);      
    15.             if (stringTable == null)
    16.             {
    17.                 Debug.LogWarning($"No String Table found '{tableRef}'");
    18.                 return false;
    19.             }
    20.  
    21.             // check if Key exists
    22.             StringTableEntry stringEntry = stringTable.GetEntryFromReference(keyRef);      
    23.             if (stringEntry == null)
    24.             {
    25.                 Debug.LogWarning($"No Loc Key found '{keyRef}' in String Table {tableRef}");
    26.                 return false;
    27.             }
    28.  
    29.             LocalizedString locString = new LocalizedString(tableRef, keyRef);
    30.  
    31.             selectorInfo.Result = locString.GetLocalizedString();
    32.  
    33.             return true;
    34.         }
    35.     }
     
    Last edited: Sep 29, 2021
    Seromu, Keitaro_Ura and karl_jones like this.