Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Advanced WPF Localization

0.00/5 (No votes)
16 Jun 2016 1  
This is an alternative for Advanced WPF Localization that adds functionality for capitalizing and pluralizing the text to be display.

Introduction

This article presents a project that has a couple of enhancements to the original code presented in Advanced WPF Localization article: It adds the ability to specify capitalization and pluralization.

Background

I was on a project that use this Localization project to do localization. The problem was that the same text was used in different places, but in some cases the text was capitalized, and not in others. In general the English name was used for the names for the resources. Unfortunately, it is not possible to use the same name for two different resources even it the case is different. The funny thing is that the case has to match the name associated with the resource. To get around this problem, I enhanced the original value to include the ability to specify the case as Property on the ExtensionMethod. I used and enumeration to specify the different cases allowed (none, upper, lower), which meant that intellesence works. This did a great job at solving our problem.

Later I thought that I could also include pluralization. In our application we did have a method to do pluralization, and I thought about including that code, but later found out about PluralizationServices, which is in System.Data.Entity.Design namespace. There are some limitation with the service, so may actually want to use something different.

While at it I did some cleanup of the code.

New Code

There was only a little bit of code that was needed to add these two capabilities to the existing project. In the derived MarkupExtension class LocExtension needed to add the two properties and have an enumeration for each:

public CaseEnum Case { get; set; }

public enum CaseEnum
{
 None = 0, Upper, Lower
}

public PluralizationEnum Pluralization { get; set; }

public enum PluralizationEnum
{
 None = 0, Plural, Singular
}

Also needed to add these two properties to the abstract LocalizedProperty class that is used as the base class to for the different TargetProperty types. Then in the ProvideValue method of the LocExtension just needed to set the properties in the LocalizedProperty class to match the values in the LocExtension class.

Then all that was needed was to modify the string generated in the GetValue method of the LocalizedProperty class as appropriate before returning the value if the type is actually a string:

internal object GetValue()
{
 var localizedValue = GetLocalizedValue();
 var converter = Property.Converter;

 if (converter != null)
 {
  localizedValue = converter.Convert(
    localizedValue,
    Property.GetValueType(),
    Property.ConverterParameter,
    Property.GetCulture()
    );
 }

 return AdjustForCase(AdjustForPluralization(localizedValue));
}

private object AdjustForCase(object localizedValue)
{
 var strValue = localizedValue as string;
 if (strValue == null) return localizedValue;
 switch (Property.Case)
 {
  case LocExtension.CaseEnum.None:
   return strValue;
  case LocExtension.CaseEnum.Upper:
   return strValue.ToUpper();
  case LocExtension.CaseEnum.Lower:
   return strValue.ToLower();
  default:
   throw new ArgumentOutOfRangeException();
 }
}

private object AdjustForPluralization(object localizedValue)
{
 var strValue = localizedValue as string;
 if (strValue == null) return localizedValue;
 switch (Property.Pluralization)
 {
  case LocExtension.PluralizationEnum.None:
   return strValue;
  case LocExtension.PluralizationEnum.Singular:
   return strValue.ToSingular();
  case LocExtension.PluralizationEnum.Plural:
   return strValue.ToPlural();
  default:
   throw new ArgumentOutOfRangeException();
 }
}

It should be noted that use an object for the localizedValue since this value may not actually be a string. Each of the two methods used to capitalize and pluralize the value first checks if the localizedValue is a string before processing.

The only other thing added to this project was a static class to contain the extension methods for the pluralization:

public static class PluralizationExtensions
{
 private static PluralizationService _pluralizationService;
 private static PluralizationService PluralizationService => _pluralizationService
   ?? (_pluralizationService = PluralizationService.CreateService(CultureInfo.CurrentCulture));

 public static string ToPlural(this string value)
 {
  if (String.IsNullOrEmpty(value)) throw new ArgumentException("Value is required", nameof(value));
  return PluralizationService.IsPlural(value) ? value : PluralizationService.Pluralize(value);
 }

 public static string ToSingular(this string value)
 {
  if (String.IsNullOrEmpty(value)) throw new ArgumentException("Value is required", nameof(value));
  return PluralizationService.IsSingular(value) ? value : PluralizationService.Singularize(value);
 }
}

This class could be used on its own.

One More Addition

I actually added one feature to this class. It may not be needed, but I did use it. This was a static Loc class that gave access to the same values using the same key string:

public static class Loc
{
 public static object GetValue(string value)
 {
  var resourceManager = LocalizationManager.DefaultResourceManager;
  if (resourceManager == null) return $"[{value}]";
  var uiCulture = Thread.CurrentThread.CurrentCulture;
  var returnValue = resourceManager.GetObject(value, uiCulture);
  return returnValue ?? $"[{value}]";

 }
}

I also updated the solution to framework version 4.6.1, and replaced a lot of the code with the conditional operator, and some of the newer C# 6.0 features..

Using the code

The original article can be refered to for informatin about the original features of the code. This article only covers the additions, which are the added elements to specify the captitalization and pluralization of the code.

This snipit shows how to specify that the text should be upper case and pluralized:

<TextBlock Grid.Row="0"
           Grid.Column="0"
           Style="{StaticResource Label}"
           Text="{Loc Label_CodeBehind_Callback, Case=Upper, Pluralization=Plural}" />

Both the Case and Pluralization arguments are optional, so can be left out if this feature is not desired. Both are specified as enumerations in the code:

public enum CaseEnum
{
 None = 0, Upper, Lower
}

public enum PluralizationEnum
{
 None = 0, Plural, Singular
}

The Sample

This is the same sample as in the original code, but with a couple of changes. One is that control that contains "Callback:" has the Case and Pluralization specified in the XAML, as can be seen in the code for the TextBlock above. The second is that the colon (":") at the end of "Callback:" in the Resources.resx file for Label_CodeBehind_Callback has been removed. This is because the colon (":") interferes with pluralization as done by the PluralizationService. The PluralizationService does not deal with the colon, so to demonstrate the capability, had to remove the color.

History

  • 16/06/16: Initial Version
  • 16/06/17: Cleanup

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here