Skip to content

Localisation

Bartłomiej Dach edited this page Aug 7, 2023 · 7 revisions

LocalisableString

Localisation in osu-framework revolves around the LocalisableString class, which UI controls and text destinations accept in place of string. This page explains the provided methods of interacting with LocalisableString, but further customisation is also possible by implementing ILocalisableStringData directly.

LocalisableFormattableString

Represents a string which can be formatted based on the currently selected locale, exposed in two methods and one extension method:

  • LocalisableString.Format, accepting a format string and a list of arguments designed in a similar fashion to string.Format:

    // Text = string.Format("{0} + {1} = {2}", localisable1, localisable2, localisable3);
    Text = LocalisableString.Format("{0} + {1} = {2}", localisable1, localisable2, localisable3);
  • LocalisableString.Interpolate, accepting an interpolated string ($"..."):

    // Text = $"{localisable1} + {localisable2} = {localisable3}";
    Text = LocalisableString.Interpolate($"{localisable1} + {localisable2} = {localisable3}");
  • LocalisableStringExtensions.ToLocalisableString, accepting an IFormattable object (e.g. number types, DateTimes, TimeSpans, etc.) and a format string, in a similar fashion to IFomrattable.ToString:

    // Text = formattable.ToString("N0");
    Text = formattable.ToLocalisableString("N0");

TranslatableString

Represents a string which can be translated across different languages via a specific "key" that is used to look up on the ILocalisationStore associated with the locale.

Text = new TranslatableString("music_string_key", "music");

string Get(string lookup) // ILocalisationStore.Get(string)
{
    if (lookup == "music_string_key")
    {
        switch (EffectiveCulture.Name)
        {
            case "en": return "music";
            case "ja": return "音楽";
            ...
        }
    }
}

In addition, TranslatableStrings support formatting, allowing for the translated strings to specify placeholder items which are replaced with the given arguments list, in a similar fashion to string.Format/LocalisableString.Format.

Text = new TranslatableString("music_string_key", "{0} music", number.ToLocalisableString("0.00%"));

string Get(string lookup) // ILocalisationStore.Get(string)
{
    if (lookup == "music_string_key")
    {
        switch (EffectiveCulture.Name)
        {
            case "en": return "{0} music";
            case "ja": return "音楽 {0}";
            ...
        }
    }
}

RomanisableString

Represents a unicode string that has a romanised variant, in which can be toggled on/off by the FrameworkSetting.ShowUnicode setting available in the config manager.

Text = new RomanisableString("音楽", "ongaku");
// FrameworkSetting.ShowUnicode == true: "音楽" is shown
// FrameworkSetting.ShowUnicode == false: "ongaku" is shown

CaseTransformableString

Represents a string which accepts a LocalisableString and transforms it to the specified casing, exposed in a set of extension methods which accept either LocalisableString or ILocalisableStringData (string implementations):

  • LocalisableStringExtensions.ToUpper, which converts all characters of the input string to uppercase, using TextInfo.ToUpper.
  • LocalisableStringExtensions.ToLower, which converts all characters of the input string to lowercase, using TextInfo.ToLower.
  • LocalisableStringExtensions.ToTitle, which converts the first character of every word to uppercase while the rest to lowercase, using TextInfo.ToTitleCase.

It's implemented in a way that allows it to build itself over a LocalisableString or any string implementation:

Text = LocalisableString.Format($"{localisable1} -> {localisable2}").ToUpper();
Text = new RomanisableString("音楽", "ongaku").ToTitle();
...

Custom string implementations

LocalisableString accepts any implementation that confronts to the ILocalisableStringData interface, in which it can retrieve the final localised string with.

    public class CustomLocalisableString : IEquatable<CustomLocalisableString>, ILocalisableStringData
    {
        public string GetLocalised(LocalisationParameters parameters)
        {
            return /* localisation logic */;
        }

        public bool Equals(CustomLocalisableString other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;

            return /* equality */;
        }

        public bool Equals(ILocalisableStringData other) => other is CustomLocalisableString custom && Equals(custom);
        public override bool Equals(object? obj) => obj is CustomLocalisableString custom && Equals(custom);

        public override int GetHashCode() => /* hash code */;
    }

LocalisationParameters

String implementations require context to be able to localise the string. LocalisationParameters provide enough context to fit the need for the framework's own string implementations, but it can be built upon for custom string implementations.

This can be achieved by creating a LocalisationManager subclass that overrides the LocalisationParameters factory method and calls UpdateLocalisationParameters to update the parameters:

    public class CustomLocalisationManager : LocalisationManager
    {
        private readonly IBindable<int> custom = new BindableInt();

        public CustomLocalisationManager(FrameworkConfigManager frameworkConfig, CustomConfigManager config)
            : base(frameworkConfig)
        {
            config.BindWith(CustomSetting.Custom, custom);
            custom.BindValueChanged(_ => UpdateLocalisationParameters(), true);
        }

        protected override LocalisationParameters CreateLocalisationParameters() => new CustomLocalisationParameters(base.CreateLocalisationParameters(), custom.Value);

        protected class CustomLocalisationParameters : LocalisationParameters
        {
            public readonly int Custom;

            protected CustomLocalisationParameters(CustomLocalisationParameters parameters)
                : base(parameters, parameters.Custom)
            {
            }

            public CustomLocalisationParameters(LocalisationParameters parameters, int custom)
                : base(parameters)
            {
                Custom = custom;
            }
        }
    }
        public string GetLocalised(LocalisationParameters parameters)
        {
            var customParameters = (CustomLocalisationParameters)parameters;
            return /* localisation logic */;
        }

LocalisableDescriptionAttribute

Represents an attribute for assigning localised description strings to classes/enums, similar to DescriptionAttribute.

Since attributes in general only allow constant input, the LocalisableStrings to use for the attribute must be stored statically in a public field or a property:

public static class Strings
{
    public static LocalisableString Class => ...;
    public static LocalisableString EnumOne => ...;
    public static LocalisableString EnumTwo => ...;
    public static LocalisableString EnumThree => ...;
}

The way LocalisableDescriptionAttribute works is by specifying the Type of the class containing the LocalisableString member, followed by the name of said member for lookup:

// [Description("Class")]
[LocalisableDescription(typeof(Strings), nameof(Strings.Class))]
public class ClassWithLocalisableDescription
{
}

public enum EnumWithLocalisableDescription
{
    // [Description("One")]
    [LocalisableDescription(typeof(Strings), nameof(Strings.EnumOne))]
    One,

    // [Description("Two")]
    [LocalisableDescription(typeof(Strings), nameof(Strings.EnumTwo))]
    Two,

    // [Description("Three")]
    [LocalisableDescription(typeof(Strings), nameof(Strings.EnumThree))]
    Three,
}

And in order to retrieve the description, use the extension method ExtensionMethods.GetLocalisableDescription:

// Text = classInstance.GetDescription();
Text = classInstance.GetLocalisableDescription();
// Text = enumValue.GetDescription();
Text = enumValue.GetLocalisableDescription();
Clone this wiki locally