Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

TextManager: Localization Script

Discussion in 'Scripting' started by kenlem, Nov 27, 2009.

  1. kenlem

    kenlem

    Joined:
    Oct 16, 2008
    Posts:
    1,630
    I started this awhile back in order to localize a project. It reads a language file on start up so the application can reference strings by tag. It only supports the default character set.


    Code (csharp):
    1.  
    2.  
    3. using UnityEngine;
    4. using System.Collections;
    5. using System.IO;
    6.  
    7.  
    8. //===============================================================================================
    9. // TextManager
    10. // KLL 09/04/2009
    11. //
    12. // Reads text files in the Assets\Resources\Languages directory into a Hashtable. The text file
    13. // consists of one line that has the key name while the next line has the actual
    14. // text to display.  This is used to localize a game.
    15. //
    16. //  Example:
    17. //      Assume we have a text file called English.txt in Assets\Resources\Languages
    18. //      The file looks like this:
    19. // 
    20. //      HELLO
    21. //      Hello and welcome!
    22. //      GOODBYE
    23. //      Goodbye and thanks for playing!
    24. //
    25. //      in code we file load the language file:
    26. //          TextManager.LoadLanguage("English");
    27. //
    28. //      then we can retrieve text by calling
    29. //          TextManager.GetText("HELLO");
    30. //
    31. //      will return a string containing "Hello and welcome!"
    32. //
    33. //===============================================================================================
    34.  
    35. public class TextManager : MonoBehaviour {
    36.    
    37.     // PRIVATE DECLARATIONS
    38.     private static TextManager instance;
    39.     private static Hashtable textTable;
    40.     private TextManager () {}
    41.  
    42.     //-----------------------------------------------------------------------------------------------
    43.     // Returns an instance of the TextManager
    44.     //-----------------------------------------------------------------------------------------------
    45.     private static TextManager Instance
    46.     {
    47.         get
    48.             {
    49.             if (instance == null)
    50.                 {
    51.                 // Because the TextManager is a component, we have to create a GameObject to attach it to.
    52.                 GameObject notificationObject = new GameObject("Default TextManager");
    53.                
    54.                 // Add the DynamicObjectManager component, and set it as the defaultCenter
    55.                 instance = (TextManager) notificationObject.AddComponent(typeof(TextManager));
    56.                 }
    57.             return instance;
    58.             }
    59.     }
    60.    
    61.     //-----------------------------------------------------------------------------------------------
    62.     // Returns an instance of the TextManager
    63.     //-----------------------------------------------------------------------------------------------
    64.     public static TextManager GetInstance()
    65.     {
    66.         return Instance;
    67.     }  
    68.    
    69.     public static bool LoadLanguage(string filename)
    70.     {
    71.         GetInstance();
    72.        
    73.         string fullpath = "Languages/" +  filename;
    74.  
    75.         TextAsset textAsset = (TextAsset) Resources.Load(fullpath, typeof(TextAsset));
    76.         if (textAsset == null)
    77.             {
    78.             Debug.Log(fullpath + " file not found.");
    79.             return false;
    80.             }
    81.        
    82.         // create the text hash table if one doesn't exist
    83.         if (textTable == null)
    84.             {
    85.             textTable = new Hashtable();
    86.             }
    87.            
    88.         // clear the hashtable
    89.         textTable.Clear();
    90.        
    91.         StringReader reader = new StringReader(textAsset.text);
    92.         string key;
    93.         string val;
    94.         while(true)
    95.             {
    96.             key = reader.ReadLine();
    97.             val = reader.ReadLine();
    98.             if (key != null  val != null)
    99.                 {
    100.                 // TODO: add error handling here in case of duplicate keys
    101.                 textTable.Add(key, val);
    102.                 }
    103.             else
    104.                 {
    105.                 break;
    106.                 }
    107.             }
    108.  
    109.        
    110.         reader.Close();
    111.                
    112.         return true;
    113.     }
    114.  
    115.    
    116.     public static string GetText(string key)
    117.     {
    118.         // TODO: add error handling here in case the key doesn't exist
    119.         return  (string)textTable[key];
    120.     }
    121.  
    }
     
  2. kirsis

    kirsis

    Joined:
    Nov 19, 2009
    Posts:
    31
    Cheers, mate, this is pretty cool. I used to work as a translator for a bit once and at work we i18n everything, so I had this pretty high on my to-look-into list. Glad to see I've got something to start with now :)
     
  3. kenlem

    kenlem

    Joined:
    Oct 16, 2008
    Posts:
    1,630
    One of the reasons I posted this is that I have no knowledge of what it takes to actually localize something. I was hoping someone like yourself could point out what this needs to be useful. For example, I could have German and French files now but I don't think they are useful unless they support the proper accent characters.

    From what I understand, localization is one of the things Apple looks for when deciding what apps to feature.
     
  4. MichaelMartin_526

    MichaelMartin_526

    Joined:
    Oct 8, 2009
    Posts:
    24
    Hi Kenlem,

    I've just been looking at your TextManager script and am struggling to understand how to get it to work (I'm quite new to scripting and have not done any C sharp code yet).

    I have put your script into an empty project and added a text document in the correct file structure. It does not produce errors! but I am struggling with the correct way to call the TextManager, is it ok to call from a JavaScript? I created a GUIText object with a script on it to call the functions but I'm kind of getting nowhere, could you give me some pointers please?
     
  5. CgShady

    CgShady

    Joined:
    Mar 31, 2011
    Posts:
    10
    Hi,

    I'm new to Unity, but managed to get this to work with Unity 3.3.
    Placed in Assets/Plugins, and with the txt files properly setup it works.

    Though, it creates a new gameObject everytime I run the project.
    It seems that
    if (instance == null)
    always returns true

    help would be greatly appreciated. I know there are some localization prefabs available for a less than $100, but getting to understand things is always better, even if I end up buying it off the shelf.

    Thanks!
     
  6. javatlacati

    javatlacati

    Joined:
    Apr 5, 2011
    Posts:
    6
    I've translated it into a Java Script version


    import System.IO;//using System.IO;

    //===============================================================================================
    // TextManager
    // 08/06/2011
    //
    // Reads text files in the Assets\Resources\Languages directory into a Hashtable. The text file
    // consists of one line that has the key name while the next line has the actual
    // text to display. This is used to localize a game.
    //
    // Example:
    // Assume we have a text file called English.txt in Assets\Resources\Languages
    // The file looks like this:
    //
    // HELLO
    // Hello and welcome!
    // GOODBYE
    // Goodbye and thanks for playing!
    //
    // in code we load the language file automatically at start
    // LoadLanguage("English");
    //
    // then we can retrieve text by calling
    // var adm_texto = TextManager(Application.systemLanguage.ToString()); //Here we load the file with our
    // // Language
    // textGUI.text = adm_texto.GetText("HELP"); //Load the localized string
    //
    //
    // will return a String containing "Hello and welcome!"
    //
    //===============================================================================================

    class TextManager{

    //DECLARATIONS
    private var textTable : Hashtable;
    var filename : String; // name of the text asset

    function TextManager(mypath:String){
    filename=mypath;
    LoadLanguage(filename);
    }

    public function LoadLanguage( filename : String ) : boolean
    {

    var fullpath : String = "Languages/" + filename;

    var textAsset : TextAsset = Resources.Load(fullpath, typeof(TextAsset));
    if (textAsset == null)
    {
    Debug.Log(fullpath + " file not found.");
    return false;
    }

    // create the text hash table if one doesn't exist
    if (textTable == null)
    {
    textTable = new Hashtable();
    }

    // clear the hashtable
    textTable.Clear();

    var reader : StringReader = new StringReader(textAsset.text);
    var key : String;
    var val : String;
    while(true)
    {
    key = reader.ReadLine();
    val = reader.ReadLine();
    if (key != null val != null)
    {
    // TODO: add error handling here in case of duplicate keys
    textTable.Add(key, val);
    }
    else
    {
    break;
    }
    }


    reader.Close();

    return true;
    }


    public function GetText( key : String ): String
    {
    // TODO: add error handling here in case the key doesn't exist
    return textTable[key];
    }
    }
     
  7. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
  8. reveriejake

    reveriejake

    Joined:
    Jul 8, 2007
    Posts:
    819
    I hope this really is your script... but it looks VERY similar to a script I wrote about 4 months ago and posted to the asset store. Even the comments and variable names and text file locations are identical.

    Not a big deal to me but I just hope that peopledont steal without giving some credit.

    Jake
     
  9. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    "Posted: 10:37 AM 11-27-2009"

    --Eric
     
  10. reveriejake

    reveriejake

    Joined:
    Jul 8, 2007
    Posts:
    819
    Yeah I noticed that shortly after posting... There are a ton of localization scripts out there now anyway so its no wonder that there are duplicates... I was just reading the code and thinking at how amazingly similar they were.. And as I was saying at the start of that post, I am not assuming there was any copying and I really wouldn't care anyway seeing how many scripts I have handed out for free on the forums ;)...

    Thanks, Eric!

    Jake
     
  11. CoCoNutti

    CoCoNutti

    Joined:
    Nov 30, 2009
    Posts:
    513

    Is there a way to load the file based on the user's iphone settings? And if not, I imagine it would mean an additional screen prior to the main menu, where the user selects the language?

    OR!! Do we submit separate apps to Apple, one for each language??!!!!

    Thanks :) :) :p
     
  12. javatlacati

    javatlacati

    Joined:
    Apr 5, 2011
    Posts:
    6

    In the JScript Version you can use var adm_texto = TextManager( SOME_IPHONE_SETTINGS_IE_PREFERRED_LANGUAGE ) ;
    However as Unity doesn't support other charsets but ASCII, you must write them in ASCII.

    Regards
     
  13. javatlacati

    javatlacati

    Joined:
    Apr 5, 2011
    Posts:
    6
    Note: In the iPhone the locales are named different ;)
     
    Last edited: Jan 22, 2012
  14. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    Just in case UnifyCommunity goes down, here's the code from my previous post, copied from Unify itself:

    Usage
    Code (csharp):
    1.  
    2. // This will reset to default POT Language
    3. TextManager.LoadLanguage(null);
    4.  
    5. // This will load filename-en.po
    6. TextManager.LoadLanguage("filename-en");
    7.  
    8. // Considering the PO file has a msgid "Ola Mundo" and msgstr "Hello World" this will print "Hello World"
    9. print(TextManager.GetText("Ola Mundo"));
    10.  
    TextManager.cs
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.IO;
    5.  
    6. // originally found in: http://forum.unity3d.com/threads/35617-TextManager-Localization-Script
    7.  
    8. /// <summary>
    9. /// TextManager
    10. ///
    11. /// Reads PO files in the Assets\Resources\Languages directory into a Hashtable.
    12. /// Look for "PO editors" as that's a standard for translating software.
    13. ///
    14. /// Example:
    15. ///
    16. /// load the language file:
    17. ///   TextManager.LoadLanguage("helptext-pt-br");
    18. ///
    19. /// which has to contain at least two lines as such:
    20. ///   msgid "HELLO WORLD"
    21. ///   msgstr "OLA MUNDO"
    22. ///
    23. /// then we can retrieve the text by calling:
    24. ///   TextManager.GetText("HELLO WORLD");
    25. /// </summary>
    26. public class TextManager : MonoBehaviour {
    27.  
    28.     private static TextManager instance;
    29.     private static Hashtable textTable;
    30.     private TextManager () {}
    31.  
    32.     private static TextManager Instance
    33.     {
    34.         get
    35.         {
    36.             if (instance == null)
    37.             {
    38.                 // Because the TextManager is a component, we have to create a GameObject to attach it to.
    39.                 GameObject notificationObject = new GameObject("Default TextManager");
    40.  
    41.                 // Add the DynamicObjectManager component, and set it as the defaultCenter
    42.                 instance = (TextManager) notificationObject.AddComponent(typeof(TextManager));
    43.             }
    44.             return instance;
    45.         }
    46.     }
    47.  
    48.     public static TextManager GetInstance ()
    49.     {
    50.         return Instance;
    51.     }  
    52.  
    53.     public static bool LoadLanguage (string filename)
    54.     {
    55.         GetInstance();
    56.  
    57.         if (filename == null)
    58.         {
    59.             DebugConsole.Log("[TextManager] loading default language.");
    60.             textTable = null; // reset to default
    61.             return false; // this means: call LoadLanguage with null to reset to default
    62.         }
    63.  
    64.         string fullpath = "Languages/" +  filename + ".po"; // the file is actually ".txt" in the end
    65.  
    66.         TextAsset textAsset = (TextAsset) Resources.Load(fullpath, typeof(TextAsset));
    67.         if (textAsset == null)
    68.         {
    69.             DebugConsole.LogError("[TextManager] "+ fullpath +" file not found.");
    70.             return false;
    71.         }
    72.  
    73.         DebugConsole.Log("[TextManager] loading: "+ fullpath);
    74.  
    75.         if (textTable == null)
    76.         {
    77.             textTable = new Hashtable();
    78.         }
    79.  
    80.         textTable.Clear();
    81.  
    82.         StringReader reader = new StringReader(textAsset.text);
    83.         string key = null;
    84.         string val = null;
    85.         string line;
    86.         while ( (line = reader.ReadLine()) != null)
    87.         {
    88.             if (line.StartsWith("msgid \""))
    89.             {
    90.                 key = line.Substring(7, line.Length - 8);
    91.             }
    92.             else if (line.StartsWith("msgstr \""))
    93.             {
    94.                 val = line.Substring(8, line.Length - 9);
    95.             }
    96.             else
    97.             {
    98.                 if (key != null  val != null)
    99.                 {
    100.                     // TODO: add error handling here in case of duplicate keys
    101.                     textTable.Add(key, val);
    102.                     key = val = null;
    103.                 }
    104.             }
    105.         }
    106.  
    107.         reader.Close();
    108.  
    109.         return true;
    110.     }
    111.  
    112.  
    113.     public static string GetText (string key)
    114.     {
    115.         if (key != null  textTable != null)
    116.         {
    117.             if (textTable.ContainsKey(key))
    118.             {
    119.                 string result = (string)textTable[key];
    120.                 if (result.Length > 0)
    121.                 {
    122.                     key = result;
    123.                 }
    124.             }
    125.         }
    126.         return key;
    127.     }
    128. }
    129.  
     
  15. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    I'm curious as to why you chose to have this inherit from MonoBehaviour.
     
  16. deepthinker

    deepthinker

    Joined:
    Mar 1, 2012
    Posts:
    4
    kelso I think every class which is to be applied to an object has to be inherited from monobehavior. Javascript code without classes is inherited automatically. This is, unless I am missing something from your question?
     
  17. Javy

    Javy

    Joined:
    Mar 18, 2012
    Posts:
    20
  18. ahmetDP_1

    ahmetDP_1

    Joined:
    Sep 23, 2010
    Posts:
    113
    @Cawas
    is DebugConsole a runtime console works in web player? care to share that, too
     
  19. hvirtual

    hvirtual

    Joined:
    Mar 4, 2013
    Posts:
    7
    Hi,

    I was wondering if you guys managed to sort out issues with translated text that overflows?

    Some words in other languages have more characters than English words, so did you manage to scale the TextMesh/GuiText text to fit to the area it should be displayed on?

    Thanks
     
  20. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    Yes, it is! I completely forgot that. It's also in UnifyCommunity. But it's too big to post here and it can be safely replaced by regular `Debug`: http://wiki.unity3d.com/index.php/DebugConsole
     
  21. Foram

    Foram

    Joined:
    Apr 5, 2013
    Posts:
    12
    Hello ,

    Do I have to load my .po file in every script?
    Is there any other solution for this?

    Thanks
     
  22. Madrayken

    Madrayken

    Joined:
    Mar 14, 2009
    Posts:
    24
    Just so people know, your files have to be named like this:

    ... (Where English is whatever language you choose) or else Unity won't recognise them as a TextAsset. You won't ever use the .txt filename extension, of course, as Unity will 'strip' it, internally.
     
  23. Deleted User

    Deleted User

    Guest

    How do I make .pot file? I'm trying to use Poedit and it's telling me I need a .pot file for my .po file.
     
  24. ipatimo

    ipatimo

    Joined:
    Jun 16, 2018
    Posts:
    5
    For me it doesn`t work because of else (line 96) . Without it works.
     
    webzapeu likes this.
  25. webzapeu

    webzapeu

    Joined:
    Apr 13, 2020
    Posts:
    1
    Yes @ipatimo you are right.
    This is my LoadLanguage Function now:

    Code (CSharp):
    1.     public static bool LoadLanguage(string filename)
    2.     {
    3.         GetInstance();
    4.  
    5.         if (filename == null)
    6.         {
    7.             Debug.Log("[TextManager] loading default language.");
    8.             textTable = null; // reset to default
    9.             return false; // this means: call LoadLanguage with null to reset to default
    10.         }
    11.  
    12.         string fullpath = "Languages/" + filename + ".po"; // the file is actually ".txt" in the end
    13.  
    14.         TextAsset textAsset = Resources.Load<TextAsset>(fullpath);
    15.         if (textAsset == null)
    16.         {
    17.             Debug.LogError("[TextManager] " + fullpath + " file not found.");
    18.             return false;
    19.         }
    20.  
    21.         Debug.Log("[TextManager] loading: " + fullpath);
    22.  
    23.         if (textTable == null)
    24.         {
    25.             textTable = new Hashtable();
    26.         }
    27.  
    28.         textTable.Clear();
    29.  
    30.         StringReader reader = new StringReader(textAsset.text);
    31.         string key = null;
    32.         string val = null;
    33.         string line;
    34.         while ((line = reader.ReadLine()) != null)
    35.         {
    36.             if (line.StartsWith("msgid \""))
    37.             {
    38.                 key = line.Substring(7, line.Length - 8);
    39.             }
    40.             else if (line.StartsWith("msgstr \""))
    41.             {
    42.                 val = line.Substring(8, line.Length - 9);
    43.             }
    44.  
    45.             if (key != null && val != null)
    46.             {
    47.                 // TODO: add error handling here in case of duplicate keys
    48.                 textTable.Add(key, val);
    49.                 key = val = null;
    50.             }
    51.         }
    52.  
    53.         reader.Close();
    54.  
    55.         return true;
    56.     }