Search Unity

Feedback Localize Font

Discussion in 'Localization Tools' started by wardetta, May 26, 2020.

  1. wardetta

    wardetta

    Joined:
    Oct 27, 2018
    Posts:
    6
    Hello, i was trying out Localization Package and i do like it, however we did run into a problem where we want to have different fonts for different languages and i believe we do need Localize Font along side string,audio and textures, it would be really appreciated and useful!
    For now i edited the script to support it for our game but would be simpler to have it by default , right.

    Thanks, and keep the amazing work :)
     
  2. DhiaSendi

    DhiaSendi

    Joined:
    May 16, 2018
    Posts:
    42
    could you please share your solution ?
     
    karl_jones likes this.
  3. wardetta

    wardetta

    Joined:
    Oct 27, 2018
    Posts:
    6
    Yes sure its quite simple nothing special, i created a script that registers to locale changes, then check if locale equals to certain language code, change font asset of this gameobejct. so basically whenever i add localize string component i also add a localize font script to the same component. and just reference your font assests however you like, im having them in a permanent script so i dont duplicate data. here is my code:

    Code (CSharp):
    1. using System.Collections;
    2. using TMPro;
    3. using UnityEngine;
    4. using UnityEngine.Localization;
    5. using UnityEngine.Localization.Settings;
    6.  
    7. public class LocalizeFont : MonoBehaviour
    8. {
    9.     private TextMeshProUGUI text;
    10.  
    11.     protected virtual void OnEnable()
    12.     {
    13.         text = GetComponent<TextMeshProUGUI>();
    14.         LocalizationSettings.SelectedLocaleChanged += ChangeHandler;
    15.     }
    16.     void OnDisable() => LocalizationSettings.SelectedLocaleChanged -= ChangeHandler;
    17.     IEnumerator Start()
    18.     {
    19.         // Wait for the localization system to initialize, loading Locales, preloading etc.
    20.         yield return LocalizationSettings.InitializationOperation;
    21.         ChangeHandler(LocalizationSettings.SelectedLocale);
    22.     }
    23.  
    24.     protected virtual void ChangeHandler(Locale value)
    25.     {
    26.         if(value.Identifier.Code.Equals("ar"))
    27.             text.font = LocalizationScript.Instance.ArabicFont;
    28.         else
    29.             text.font = LocalizationScript.Instance.EnglishFont;
    30.     }
    31. }
     
    tsukimi, mikeohc, AsemBahra and 3 others like this.
  4. DhiaSendi

    DhiaSendi

    Joined:
    May 16, 2018
    Posts:
    42
  5. krasner

    krasner

    Joined:
    Sep 9, 2014
    Posts:
    7
    There's an even easier solution to this. I made an Asset Table with my font assets per language(I'm using Text Mesh Pro font assets) and then made a Localized Font Class and Localized Font Event to add as a component to objects.
     

    Attached Files:

  6. wardetta

    wardetta

    Joined:
    Oct 27, 2018
    Posts:
    6
    I did this at first but the Asset Table object reverts back to null, i thought it did not support Text Mesh Pro Font as an asset and moved on, could have been a bug, mmmm.
    Well either way we do add a component that we created so still same steps :D but thanks for letting me know that it worked for you! maybe i will try that again later if i added more languages
     
  7. krasner

    krasner

    Joined:
    Sep 9, 2014
    Posts:
    7
    If it's any help I'm on Unity 2019.4.1 (the LTS version) and on 0.7.1 for the localization package.
     
  8. wardetta

    wardetta

    Joined:
    Oct 27, 2018
    Posts:
    6
    Oh so thats why, im still on the previous version 0.6, Thx :)
     
    karl_jones and krasner like this.
  9. crochelmeyer

    crochelmeyer

    Joined:
    Mar 27, 2016
    Posts:
    4
    Necro-ing an old thread to say that this helped me. The script can also be condensed down into one file as such:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Events;
    3. using System;
    4. using Sirenix.OdinInspector;
    5. using TMPro;
    6. using UnityEngine.Localization;
    7. using UnityEngine.Localization.Components;
    8.  
    9. [Serializable]
    10. [DrawWithUnity]
    11. public class LocalizedTMPFont : LocalizedAsset<TMP_FontAsset> { }
    12.  
    13. [Serializable]
    14. public class UnityEventTMPFont : UnityEvent<TMP_FontAsset> { }
    15.  
    16. [AddComponentMenu("Localization/Asset/Localize TMP Font Event")]
    17. public class LocalizeTMPFontEvent : LocalizedAssetEvent<TMP_FontAsset, LocalizedTMPFont, UnityEventTMPFont>
    18. { }
    Localized Asset Event

    Also if you are using Odin Inspector, the DrawWithUnity attribute fixes the issue of Odin overriding the LocalizedAsset drawer, allowing you to select your asset table key with the original dropdown as intended. Unfortunately there's no way of adding the attribute to the existing Audio, Prefab, Sprite and Texture events without creating a similar custom component as we did here for the font :/
     
    xuwilliam01 likes this.
  10. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    Also would like to add few more things that the above solution need file name LocalizeTMPFontEvent.cs as per usual rule of MonoBehaviour, that one is the MonoBehaviour that serializes the above 2 mini classes.

    And with TMP, switching font likely also need a new set of material as well to get those same outline style matching between different fonts. (In the case that you have many materials of the same font) So you make the same set of loc event for material except that it seems `LocalizedMaterial` is already provided in the package, so down to 2 classes.

    Code (CSharp):
    1.     [Serializable]
    2.     internal class UnityEventMaterial : UnityEvent<Material>
    3.     {
    4.     }
    5.  
    6.     internal class LocalizeFontMaterialEvent
    7.         : LocalizedAssetEvent<Material, LocalizedMaterial, UnityEventMaterial>
    8.     {
    9.     }
    Then target the event to dynamic `fontSharedMaterial`.

    upload_2021-9-22_6-21-7.png
     
    Last edited: Sep 22, 2021
    tsukimi, xuwilliam01 and BeorGames like this.
  11. BeorGames

    BeorGames

    Joined:
    Jul 28, 2018
    Posts:
    65
    Hello!
    I'm trying to add it to my TMP object, but it simply doesn't appear on the inspector:
    upload_2021-10-21_7-3-0.png

    I really just copied and pasted the code on a new cs script, what could be happening??
     
  12. BeorGames

    BeorGames

    Joined:
    Jul 28, 2018
    Posts:
    65
    Thanks! After some time without any response to my previous message I came back here and found the answer just above my question :rolleyes:

    Just for someone in the future that my find the same problem I did, as 5argon said before, you need to name the file LocalizeTMPFontEvent.cs, otherwise it won't work... ;)
     
    5argon and karl_jones like this.
  13. BeorGames

    BeorGames

    Joined:
    Jul 28, 2018
    Posts:
    65
    Hello everybody,

    Again, just to help future people that like me, took a long time to find a solution to change fonts.

    My problem was that I'm including languages like Chinese and Japanese in my game, and these don't use the regular characters, so I needed a font specifically for them, not that I wanted to change the font because of the style, and well, there's a much easier way of doing so, it's the font Fallback, it is much easier and does the job just fine, here are the tutorial links https://forum.unity.com/threads/ver...ic-sdf-for-unity-2018-3-now-available.622420/ and
    this is much easier than adding this script on every TMP object inside the game...

    And this one can also be useful:
    https://forum.unity.com/threads/table-of-general-standard-chinese-characters.559882/
     
  14. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    I had gone to this direction before and there are some problems when using fallback fonts as localization you may want to keep in mind.
    https://gametorrahod.com/textmeshpro-anatomy#fallback-font-is-not-for-localization

    In short,

    - Better switch TMP Font asset than having a master one that could fallback to all languages in the game because :
    - You are less in control of the baseline, padding, etc. because it tries to use the "best" one in your fallback chain. But some languages may or may not need higher line height, and may looks weird with line height that is too much (that the fallback languages forces on it)
    - Material that has effects dependent on amount of pixels in the texture like applying border will have thicker or thinner border for example when you use English (small 512x512 sheet?) then falling back to Japanese (huge 2048x2048 sheet), the Japanese font will appears to have thin border as the material is tuned for English texture size. Japanese game can sometimes need occasional English character, it is better the other way around and true to the meaning of "fallback". (On English loc : Use English with no fallback. On Japanese loc : Use Japanese with fallback to English, not using English with fallback to Japanese and also attempt to use that with English also.) You can also fix this by coordinating fixed character size of all languages instead of fixed sheet size, but I think at this point using separate one is generally better. (Worse in space requirement, the "true" English and English for Japanese fallback is a different sheet.)
    - The most painful thing is to add loc component to ALL text in the game. Which can be fixed by using Prefab Workflow and never just create a text no matter how small it is. Always instantiate from a prefab or a variant of that.
     
    Dennooo, Potatas, jiraphatK and 2 others like this.
  15. sarebots2

    sarebots2

    Joined:
    Jul 4, 2016
    Posts:
    34
    Updated the above script to automatically add local TextMeshProUGUI.font dynamic event to OnUpdateAsset UnityEvent. File name should be LocalizeTMPFontEvent.cs

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Events;
    3. using System;
    4. using Sirenix.OdinInspector;
    5. using TMPro;
    6. using UnityEngine.Localization;
    7. using UnityEngine.Localization.Components;
    8. [Serializable]
    9. [DrawWithUnity]
    10. public class LocalizedTMPFont : LocalizedAsset<TMP_FontAsset> { }
    11. [Serializable]
    12. public class UnityEventTMPFont : UnityEvent<TMP_FontAsset> { }
    13.  
    14. [AddComponentMenu("Localization/Asset/Localize TMP Font Event")]
    15. public class LocalizeTMPFontEvent : LocalizedAssetEvent<TMP_FontAsset, LocalizedTMPFont, UnityEventTMPFont>
    16. {
    17.     #if UNITY_EDITOR
    18.     void OnValidate()
    19.     {
    20.         if(OnUpdateAsset.GetPersistentEventCount()>0) return;
    21.         TextMeshProUGUI target = gameObject.GetComponent<TextMeshProUGUI>();
    22.         var setStringMethod = target.GetType().GetProperty("font").GetSetMethod();
    23.         var methodDelegate = Delegate.CreateDelegate(typeof(UnityAction<TMP_FontAsset>), target, setStringMethod) as UnityAction<TMP_FontAsset>;
    24.         UnityEditor.Events.UnityEventTools.AddPersistentListener(OnUpdateAsset, methodDelegate);
    25.     }
    26.     #endif
    27. }
    28.  
    29. [Serializable]
    30. internal class UnityEventMaterial : UnityEvent<Material>
    31. {
    32. }
    33. internal class LocalizeFontMaterialEvent
    34.     : LocalizedAssetEvent<Material, LocalizedMaterial, UnityEventMaterial>
    35. {
    36. }
     
  16. Leslie-Young

    Leslie-Young

    Joined:
    Dec 24, 2008
    Posts:
    1,148
    Everything seems to work fine in editor but my stand-alone builds act strange. It is like the asset or font is not applied initially and only after changing to a different language (with different font asset) and switching back does the text get updated properly. See attached video to explain better...

     
  17. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    What version of the package are you using? Can you try 1.3.2? If you are using 1.3.2 and its still happening, do you have any errors in the log file?

    It could also be due to the execution order. If the text updates before the font then this is likely to happen. If you change the script execution order so that the LocalizedTMPFont class comes earlier than default then this should fix it.https://docs.unity3d.com/Manual/class-MonoManager.html
     
  18. Leslie-Young

    Leslie-Young

    Joined:
    Dec 24, 2008
    Posts:
    1,148
    I was still on 1.0.5 since that is what the package manager shows available so I updated it to 1.3.2 via manifest change. Works now with either script order applied, or not.

    Thanks. I really need to watch the forum closer. Did not realize the package manager could let me fall behind in version number so far.
     
  19. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Yeah, the package manager often doesn't show the latest version. It's by design but I cant say I particularly agree with the decision. Its supposed to be showing the versions that have been verified with that version of Unity when it was released, but the way to verify a new version is convoluted and slow to update so you often miss new releases if you don't update Unity. It's supposed to improve in the future....
     
  20. Metosian

    Metosian

    Joined:
    May 21, 2018
    Posts:
    14
    Heya, I'm using this script and its works wonderfully, so thank you very much!

    I was wondering if there was a way I could have it auto-populate which entry in the asset table it defaults to when the script is added to a component?

    I have many different localizedstring components that I need to place this script on and then select the font entry in the asset table, which is the same in 99% of cases (eg I always select "fonts/regular") so that would save me a heap of time by just being able to add the component and then move on instead of selecting it from the dropdown.

    Thanks!

     
  21. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Metosian likes this.
  22. magister_yoda_

    magister_yoda_

    Joined:
    Mar 27, 2016
    Posts:
    32
  23. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    What do you mean it doesn't run? Do you get any errors? Have you built the addressables?
     
  24. magister_yoda_

    magister_yoda_

    Joined:
    Mar 27, 2016
    Posts:
    32
    Yeap, I've built addresables, then built my game for Android and after SplashScreen it just stopped on black screen and nothing happened. AndroidStudio showed me an error something like this "...Expected to have a current operation to wait on. Can not complete...". I downgraded Localization to 1.3.2 and now finally I can run my game on device. Now the problem seems to be solved with this version.
     
  25. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Could you please file a bug report for this?
     
  26. magister_yoda_

    magister_yoda_

    Joined:
    Mar 27, 2016
    Posts:
    32
    Look, now I'm totally stucked. Turned out, that the problem wasn't in Localization version. Now I'm not able run my game even in Editor in UseExistingBuild mode. Unity starts Not Responding and I have to Force Kill it every time I try to run game. And if I run on Android it also starts not responding, sends no error to AndroidStudio and my phone starts to heat up.
     
  27. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Sounds like you are stuck in a loop somewhere. Does it also happen on the latest localization package 1.4.3?

    Do you get any errors?
    Try debugging and breaking to see where it could be stuck.
     
  28. magister_yoda_

    magister_yoda_

    Joined:
    Mar 27, 2016
    Posts:
    32
    This is really very similar to an infinite loop, but it isn't. The game just hangs. It's because of localization.
    I've found solution. I added a StartScene and in a script on this scene do the code below.
    Code (CSharp):
    1. async void Start() {
    2.         await LocalizationSettings.InitializationOperation;
    3.  
    4.         loadingScreen.load(new SceneLoader("MainScene"));
    5.     }
    After initialization it started to work fine, but before this it didn't show me anything, no error, no log.
     
  29. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Could you please file a bug report?
     
  30. CoderPro

    CoderPro

    Joined:
    Feb 21, 2014
    Posts:
    327
    I think we need to support for font size too.
     
  31. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Localized Property Events are a good way to handle the font size.
     
    CoderPro likes this.
  32. CoderPro

    CoderPro

    Joined:
    Feb 21, 2014
    Posts:
    327
    How can I set the default language in UnityEditor to English instead of ?
    I can not set it to English, Its always showing the system language of my PC. I can not change it.
     
  33. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    In the localization settings, under locale selector drag the specific locale selector above the system local selector so it uses that one instead.
     
    CoderPro likes this.
  34. CoderPro

    CoderPro

    Joined:
    Feb 21, 2014
    Posts:
    327
    Thank you so much @karl_jones . Finally, I can change it.
     
    karl_jones likes this.
  35. Dennooo

    Dennooo

    Joined:
    May 12, 2015
    Posts:
    88
    Just thinking about the best practices in terms of localizing font and font size to keep a bigger project somewhat flexible.

    From my understanding would these be the 'best' practices:
    Font size: Localized Property Events
    Font material: Solution that is developed in this thread using the LocalizedTMPFont.cs.

    Or: Should the font material also be changed using the localized property events?
     
  36. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    You should be able to change the material with the localized property events so it would be simpler to do it all using that.
     
    Dennooo likes this.
  37. Dennooo

    Dennooo

    Joined:
    May 12, 2015
    Posts:
    88
    Out of curiosity: Is there a practical way to basically change the font and font material with a single mono-behavior similiar to that one described here as LocalizedTMPFont.cs?

    I think that this would work faster for my usecase and would allow quicker modifcations later down the line but I can't think of a solution without changing the TMPro base class :/
     
  38. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Have you tried this? https://forum.unity.com/threads/localize-the-default-textmeshpro-font.1366863/
     
    Dennooo likes this.
  39. Dennooo

    Dennooo

    Joined:
    May 12, 2015
    Posts:
    88
    Thanks for sharing. I'm not sure whether this will do for my case as I use quite a lot of different font materials. Maybe the solution is quite obvious but I currently can't wrap my head around how to change font and font size for each tmpro text in a quite text heavy project efficiently.
     
  40. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Are you planning to set them all to the same value or will it need to be different for each text? If it's different then the localized property variants is the way to go otherwise you could try writing a script like the one I posted for fonts.
     
  41. Dennooo

    Dennooo

    Joined:
    May 12, 2015
    Posts:
    88
    They need to be different for each texts (practically 80% feature the same font/material but a 'catch all' solution wouldn't work).

    How about this approach that works with a wrapper to simultaneously change font and font material (and other stuff that comes up later down the line). Not really pretty but should allow quite some flexibility while being easy and quick to implement:

    File 1: Localized mono-behavior that takes the wrapper data (TMPFont_Data) and changes the TMPro components font / font material.

    Code (CSharp):
    1. using TMPro;
    2. using UnityEngine;
    3. using UnityEngine.Localization.Components;
    4.  
    5. [AddComponentMenu("Localization/Asset/Localize TMP Font")]
    6. public class LocalizeTMPFont : LocalizedAssetBehaviour<TMPFont_Data, LocalizedTMPFontData>
    7. {
    8.     [SerializeField, HideInInspector]
    9.     protected TMPro.TMP_Text targetText;
    10.  
    11. #if UNITY_EDITOR
    12.     void OnValidate()
    13.     {
    14.         if(targetText == null) targetText = gameObject.GetComponent<TMP_Text>();
    15.     }
    16. #endif
    17.  
    18.     protected override void UpdateAsset(TMPFont_Data _fontData)
    19.     {
    20.         if (targetText == null) return;
    21.  
    22.         targetText.font = _fontData.Font;
    23.         targetText.fontSharedMaterial = _fontData.Material;
    24.     }
    25. }
    File 2: Definitions for how we want the text component to be affected and saved in separate scriptable objects.
    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using UnityEngine.Localization;
    4. using Sirenix.OdinInspector;
    5.  
    6. // convenience wrapper to wrap a font and a material and group
    7. [Serializable]
    8. public class TMPFont_Data : ScriptableObject
    9. {
    10.     [SerializeField]
    11.     protected TMPro.TMP_FontAsset font;
    12.     public TMPro.TMP_FontAsset Font => font;
    13.  
    14.     [SerializeField]
    15.     protected Material material;
    16.     public Material Material => material;
    17.  
    18.     // room for other adjustments like font size etc.
    19. }
    20.  
    21.  
    22. [Serializable]
    23. [DrawWithUnity]
    24. public class LocalizedTMPFontData : LocalizedAsset<TMPFont_Data> { }
    25.  
    That way one can easily add the 'LocalizeTMPFont' component to any tmp text object of interest and set them up quickly by pointing to the entry in the asset table.

    Does that make sense or are there any obvious shortcomings to this?
     
    karl_jones likes this.