User avatar
ai_enabled
Topic Author
Posts: 208
Joined: 18 Jul 2013, 05:28
Contact:

Localization / Translation approaches

25 Jan 2019, 22:46

Hi guys,

I'm starting my work on CryoFall localization soon and researching the localization approaches. I've checked the forums (outdated topic) and NoesisGUI localization sample on Github and here are my thoughts on each approach I've thought about:

1. NoesisGUI approach from the sample which is using {Binding MyLocalizationKey} is not applicable for applications using MVVM pattern as DataContext is already used for ViewModels. Moving localization text data into the ViewModels is not a great idea as in many cases the text entries could be reused in multiple places but ViewModel is usually done for a few specific cases - also I don't like the idea of polluting the ViewModels with localization stuff.


2. XAML Markup extensions (example implementation for localization):
<TextBlock Text="{loc:Traslate MyLocalizationKey}" />
could be a good idea to inject localization but it's not supported in NoesisGUI C# API (#1401).


3. Approach with attached properties (custom localization service) and strong-typed enum usage:
<TextBlock locService.Localize="MyLocalizationEnum_SomeKey" />
I've used it in the previous game (VoidExpanse) and I don't like it for multiple reasons:
  • it's too verbose/lengthy
  • the attached property service should recognize various controls and know how to properly inject the localization (e.g. in case of TextBlock it should use the Text property but in case of ContentControls it should use Content property). It also means that injecting localization into tooltip is hard (all localizatible tooltips should be redone as explicit Tooltip entry with a TextBlock using the localization service).
  • there could be only a single enum type (though it's possible to use x:Static to support different enum types (which is already supported) but then the syntax is even more lengthy!)
  • not compatible with {Binding StringFormat=...} which is a very useful feature
  • introduces a level of indirection as we cannot simply Ctrl+Click to navigate to the original text - the original text is stored in a separate file (could be a simple text file as in VoidExpanse) where each string has a key matching the enum key (which is also very fragile and error-prone - easy to forget to rename the key in text file after renaming the key in enum, etc).

4. x:Static approach to reference C# const string:
<TextBlock Text="{x:Static MyClass.MyTextConstName}" />
alas, it's not yet supported (#1035).


5. Simply define all localizable data in a XAML resource dictionary (one per language, e.g. "UI.en_us.xaml" for US English, "UI.ru_ru.xaml" for Russian, etc) and use StaticResources to reference localizable strings (and probably other resources such as images). Depending on the language the application can (re)load NoesisGUI and provide the localization resource dictionary for the selected language.
I see a potential issue with XAML loading as the localization strings resource dictionary should be loaded as a global resource dictionary (like in WPF App-wide resources) before loading any other resource dictionaries and controls which require these strings but this feature is not yet supported (#1379).
This approach is also requires some extra effort as I don't want to edit localization data by hand - it's expected to be delivered in CSV format to the game publisher for translation so an import-export tool is required.


My current best idea - if I could use x:Static in XAML to reference C# const string entries it will be sufficient for XAML localization and I will not need to spend extra time on implementing tooling for XAML localization import/export. I can just write the required C# localization tooling (which will use Roslyn compiler to rewrite string constants and localizable C# properties - something I can do relatively easily as the C# code is already pre-processed and compiled by the game with Roslyn!). So with this approach, it will work for both C# and XAML localization (as XAML will reference C# string const's) and will save me a lot of time and effort. But it requires support from NoesisGUI (#1035) I'm curious if it could be done before NoesisGUI 2.2 release.

Would love to hear your ideas regarding the better localization approach.

Regards!
AtomicTorch Studio Pte. Ltd. http://atomictorch.com
 
nokola
Posts: 161
Joined: 10 Mar 2015, 05:29

Re: Localization / Translation approaches

27 Jan 2019, 21:51

This is my approach, has been working great and solves most of the issues you mentioned (except string formatting)
 <TextBlock Text="fullscreen" t:Translate.Text="fullscreen" 
     Margin="2,4,2,2" HorizontalAlignment="Center" TextWrapping="Wrap" TextAlignment="Center"/>

<Button x:Name="btnOK" Content="Hello World" t:Translate.Content="helloWorld" 
     HorizontalAlignment="Center" MinWidth="130" MinHeight="60" Margin="0,10,0,0"/>

I use attached properties similar like your solution 3, however I do the following in addition:
1. the .Content vs .Text property are exactly the same except they set .Text and .Content respectively
2. I always put original english string in XAML - easy to reference during development; used as fall back in case translation does not load at runtime for whatever reason
3. Having the static property name as a verb command e.g. "Translate.Content" is intuitive since it translates the Content property - easy for others to pick up and reason about it.
So far works great. 40+ languages and 500+ resource strings that I load dynamically at startup. Hope it helps!

Edit: the t.Translate.Text and .Content contain a key that is used in a Dictionary<string, string> to find the actual translation at runtime. Updated example above (with "Hello World" "helloWorld" to better illustrate.)
Last edited by nokola on 31 Jan 2019, 06:25, edited 2 times in total.
 
User avatar
sfernandez
Site Admin
Posts: 1428
Joined: 22 Dec 2011, 19:20

Re: Localization / Translation approaches

30 Jan 2019, 11:06

Hi, these are my notes on some of the approaches:

1. Even if you are using MVVM is it possible to use bindings for localization, if you have access to a global Localization object from all of your view models. I know this is not very clean, but I just want it to mention so no one discards this option because it cannot be used. In the following snippet I'm assuming that the base class for all my view models exposes a Loc property, which returns an object that implements the this[string key] to return localized strings.
<TextBlock Text="{Binding Loc[AppTitlte]}"/>
4. Alhough there is no support for static members in Noesis yet, there is a non standard workaround I mentioned in other posts. In Noesis you can use the default value of a DependencyProperty with the x:Static extension. So until we implement fully support for static members, you can define a class with all your const strings and DP using those strings as default values. It requires more code but I think it is easy to create a snippet to automatically generate it:
namespace Localization
{
    public class Translate : DependencyObject
    {
        public const string OkButton = "Ok";
        private static readonly DependencyProperty OkButtonProperty = DependencyProperty.Register(
            "OkButton", typeof(string), typeof(Translate), new PropertyMetadata(OkButton));
    }
}
<Grid
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:loc="clr-namespace:Localization">
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
        <TextBlock Text="{x:Static loc:Translate.OkButton}"/>
        <Button Content="{x:Static loc:Translate.OkButton}"/>
    </StackPanel>
</Grid>

Who is online

Users browsing this forum: Bing [Bot] and 3 guests