Click here to Skip to main content
15,867,453 members
Articles / Desktop Programming / WPF
Alternative
Article

Advanced WPF Localization

Rate me:
Please Sign up or sign in to vote.
4.93/5 (10 votes)
16 Jun 2016CPOL3 min read 18.4K   191   13   6
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:

C#
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:

C#
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:

C#
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:

C#
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:

C#
<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:

C#
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Clifford Nelson Consulting
United States United States
Has been working as a C# developer on contract for the last several years, including 3 years at Microsoft. Previously worked with Visual Basic and Microsoft Access VBA, and have developed code for Word, Excel and Outlook. Started working with WPF in 2007 when part of the Microsoft WPF team. For the last eight years has been working primarily as a senior WPF/C# and Silverlight/C# developer. Currently working as WPF developer with BioNano Genomics in San Diego, CA redesigning their UI for their camera system. he can be reached at qck1@hotmail.com.

Comments and Discussions

 
QuestionHow to use static Loc class along with adjusting case and pluralization Pin
Nanatiris21-Nov-16 10:35
Nanatiris21-Nov-16 10:35 
AnswerRe: How to use static Loc class along with adjusting case and pluralization Pin
Clifford Nelson22-Nov-16 7:24
Clifford Nelson22-Nov-16 7:24 
QuestionHow do I localize the value of a binding? Pin
Nanatiris18-Nov-16 14:46
Nanatiris18-Nov-16 14:46 
AnswerRe: How do I localize the value of a binding? Pin
Clifford Nelson29-Nov-16 9:58
Clifford Nelson29-Nov-16 9:58 
GeneralMy vote of 5 Pin
Arkitec20-Jun-16 17:32
professionalArkitec20-Jun-16 17:32 
QuestionHmm...I wonder how my WpfSharp.Globalizer handles case sensitivity? Pin
rhyous17-Jun-16 10:05
rhyous17-Jun-16 10:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.