Category Archives: LINQ

Picking the Right Voice for Windows Phone

Recently, I had an interesting problem come up when testing localization (in French and Spanish in my case) in conjunction with letting the user choose a female or male voice in one of my Windows Phone apps (http://www.windowsphone.com/en-us/store/app/fn8/dd72d13d-1a7b-42c4-81d3-adbc98d4b002). The main part of the problem is basically financial, i.e. given the economics of app development it isn’t feasible to localize for many languages, let alone variants (cultures), e.g. whatever dialect of French is spoken in lower, mid-western Upper Volta. Additionally, given the inefficient workings of the Windows Phone store, it takes forever to submit (or even update) an app for each supported language. In my case, I have asked my translators to use a international a flavor as possible to cover as many markets as I can. Localizing into a language will still allow you to reach all the cultures that support the base language, e.g. localizing into Spanish will work for users in Spain (es-es) as well as Mexico (es-mx).

Part of the problem is technical. If you go this route, then when the user selects a gender for an installed voice, the app has to be able to match the appropriate voices from among those installed. With my first approach what I found when testing was that no voices were being selected at all in a device set to Mexican Spanish. The reason became immediately apparent when debugging:

The app was working fine in English because I was looking for a culture name and because en-us was the default culture for the app, it was finding that. But since the Spanish-speaking user is only getting a generic Spanish translation, the app was looking for a culture name of es-mx and blithely skipping over plain ol’ es.

So then I switched to grabbing the two-letter ISO language name instead. That made the app feliz in Español, but now on my device set to American English I was getting a male voice with a British accent when the radio button was set to female.

Another quick debug session and I could see that American voices Mike and Zira had some friends in the house I didn’t know about, including George and Susan from Great Britain and their friends Raul from Mexico and Stefan from Germany. All obviously there to enjoy Cortana’s predictions on the World Cup.

So here’s what can only be described as an amateurish hack to deal with this issue. Well, as my co-workers and I used to tell our boss in a previous job, all our code is “QDC – quick, dirty, or cheap. Pick any two.”

var twoLetterIsoLanguageName = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;

var currentCultureName = CultureInfo.CurrentCulture.Name;

IEnumerable<VoiceInformation> voiceInfos = from voice in InstalledVoices.All select voice;

var localVoices = new List<VoiceInformation>();

// See if any voices match the current culture specifically
foreach (var voiceInfo in voiceInfos)
{
 if (voiceInfo.Language == currentCultureName)
 {
 localVoices.Add(voiceInfo);
 }

 localVoices =
 localVoices.OrderByDescending(v => v.Language).ThenByDescending(v => v.Gender).ToList();
}

// No matches? Go for the generic language instead
if (localVoices.Count < 1)
{
 foreach (var voiceInfo in voiceInfos)
 {
 var primaryLangStr = voiceInfo.Language.Split('-')[0];

 if (primaryLangStr == twoLetterIsoLanguageName)
 {
 localVoices.Add(voiceInfo);
 }

 localVoices =
 localVoices.OrderByDescending(v => v.Language).ThenByDescending(v => v.Gender).ToList();
 }
}

App.StorageSettings.TryGetValue("VoiceGender", out _voiceGender);

_speechSynthesizer.SetVoice(localVoices.ElementAt(_voiceGender));

IKnow IEquatable

Well, now anyway. Sometimes I think the statement “Necessity is the mother of invention.” should – when applied to coders – be restated as “Necessity is the mother of fruitless hacks, StackOverflow searches and more code.” Catchy, eh?

Prior to my somewhat painful re-education (not going to use a diaeresis this time), I was operating under the ridiculous assumption that comparing to collections would be straightforward…if not downright simple. But my code kept telling me two identical collections were not. Of course a better coder would immediately recognize a reference problem underlying this. First I had blithely tried:

if (savedExercisesCollection != ExercisesCollection)
{
 for (int i = 0; i < ExercisesCollection.Count; i++)
 {
 ExercisesCollection[i].Order = i;
 }

App.StorageSettings["SettingsChanged"] = true;
}

But that was always returning true. Next, I turned to LINQ (’cause it is the solver of all problems, isn’t it?). Well, at any rate, my thinking was that it would actually compare the values. And it is elegantly concise:

if (savedExercisesCollection.Except(ExercisesCollection).Any())
{
 for (int i = 0; i < ExercisesCollection.Count; i++)
 {
 ExercisesCollection[i].Order = i;
 }</pre>
App.StorageSettings["SettingsChanged"] = true;
 }

I had also tried SequenceEqual but with the same sad results…until I stumbled across the IEqualityComparer<T> IEquatable @ http://msdn.microsoft.com/en-us/library/bb348567.aspx.

if (!savedExercisesCollection.SequenceEqual(ExercisesCollection))
{
 for (int i = 0; i < ExercisesCollection.Count; i++)
 {
 ExercisesCollection[i].Order = i;
 }

App.StorageSettings["SettingsChanged"] = true;
}

Now I was on the path to righteousness. All it would take is adding a comparer to my original class. I approached this with some trepidation since I’m relying on being able to serialize portions of that class. But the only way to know if something will blow up is to light the fuse. Turns out no worries.

I modified the class as follows:

[DataContract]
 public class Exercise : IEquatable<Exercise>
 {
 public List<Asset> AssetsList;

public Exercise(int id, int order, string description)
 {
 Id = id;

Order = order;

Description = description;

AssetsList = new List<Asset>();
 }

[DataMember]
 public int Id { get; set; }

[DataMember]
 public int Order { get; set; }

[DataMember]
 public string Description { get; set; }

 public bool Equals(Exercise other)
 {
 //Check whether the compared object is null.
 if (ReferenceEquals(other, null)) return false;

//Check whether the compared object references the same data.
 if (ReferenceEquals(this, other)) return true;

//Check whether the products' properties are equal.
 return Id.Equals(other.Id) && Order.Equals(other.Order);
 }

The take-home lesson from this is that unless an ObservableCollection is instantiated from the same source as another they are not automatically comparable. I can see this knowledge coming in handy when rehydrating a chunk of JSON, for instance to compare a remotely stored to-do list with one on the client.