From 3559ebbebeb3c45321288bab13c3f1a80cb537d1 Mon Sep 17 00:00:00 2001 From: mdrichardson Date: Fri, 8 Nov 2019 09:39:30 -0800 Subject: [PATCH] ChoicePrompt handles null locales --- .../Prompts/ChoicePrompt.cs | 6 +-- .../Prompts/PromptCultureModels.cs | 34 ++++++++++++- .../ChoicePromptTests.cs | 48 +++++++++++++++++++ 3 files changed, 84 insertions(+), 4 deletions(-) diff --git a/libraries/Microsoft.Bot.Builder.Dialogs/Prompts/ChoicePrompt.cs b/libraries/Microsoft.Bot.Builder.Dialogs/Prompts/ChoicePrompt.cs index 9e1ca1be5..03994fa48 100644 --- a/libraries/Microsoft.Bot.Builder.Dialogs/Prompts/ChoicePrompt.cs +++ b/libraries/Microsoft.Bot.Builder.Dialogs/Prompts/ChoicePrompt.cs @@ -123,10 +123,10 @@ namespace Microsoft.Bot.Builder.Dialogs } // Determine culture - var culture = MapToNearestLanguage(turnContext.Activity.Locale ?? DefaultLocale); - if (string.IsNullOrEmpty(culture) || !_choiceDefaults.ContainsKey(culture)) + var culture = turnContext.Activity.Locale ?? DefaultLocale ?? English.Locale; + if (!_choiceDefaults.ContainsKey(culture)) { - culture = English.Locale; + culture = MapToNearestLanguage(culture); } // Format prompt to send diff --git a/libraries/Microsoft.Bot.Builder.Dialogs/Prompts/PromptCultureModels.cs b/libraries/Microsoft.Bot.Builder.Dialogs/Prompts/PromptCultureModels.cs index 63046232e..78e3df75d 100644 --- a/libraries/Microsoft.Bot.Builder.Dialogs/Prompts/PromptCultureModels.cs +++ b/libraries/Microsoft.Bot.Builder.Dialogs/Prompts/PromptCultureModels.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; +using System.Linq; using Microsoft.Recognizers.Text; namespace Microsoft.Bot.Builder.Dialogs.Prompts @@ -10,6 +12,8 @@ namespace Microsoft.Bot.Builder.Dialogs.Prompts /// public static class PromptCultureModels { + private static readonly string[] SupportedLocales = GetSupportedCultures().Select(c => c.Locale).ToArray(); + public static PromptCultureModel Chinese => new PromptCultureModel { @@ -103,7 +107,35 @@ namespace Microsoft.Bot.Builder.Dialogs.Prompts /// /// Represents locale. Examples: "en-US, en-us, EN". /// Normalized locale. - public static string MapToNearestLanguage(string cultureCode) => Culture.MapToNearestLanguage(cultureCode); + public static string MapToNearestLanguage(string cultureCode) + { + cultureCode = cultureCode.ToLowerInvariant(); + + if (SupportedLocales.All(o => o != cultureCode)) + { + // Handle cases like EnglishOthers with cultureCode "en-*" + var fallbackCultureCodes = SupportedLocales + .Where(o => o.EndsWith("*", StringComparison.Ordinal) && + cultureCode.StartsWith(o.Split('-').First(), StringComparison.Ordinal)).ToList(); + + if (fallbackCultureCodes.Count == 1) + { + return fallbackCultureCodes.First(); + } + + // If there is no cultureCode like "-*", map only the prefix + // For example, "es-mx" will be mapped to "es-es" + fallbackCultureCodes = SupportedLocales + .Where(o => cultureCode.StartsWith(o.Split('-').First(), StringComparison.Ordinal)).ToList(); + + if (fallbackCultureCodes.Any()) + { + return fallbackCultureCodes.First(); + } + } + + return cultureCode; + } public static PromptCultureModel[] GetSupportedCultures() => new PromptCultureModel[] { diff --git a/tests/Microsoft.Bot.Builder.Dialogs.Tests/ChoicePromptTests.cs b/tests/Microsoft.Bot.Builder.Dialogs.Tests/ChoicePromptTests.cs index baa227eb4..00de2237d 100644 --- a/tests/Microsoft.Bot.Builder.Dialogs.Tests/ChoicePromptTests.cs +++ b/tests/Microsoft.Bot.Builder.Dialogs.Tests/ChoicePromptTests.cs @@ -654,6 +654,54 @@ namespace Microsoft.Bot.Builder.Dialogs.Tests .StartTestAsync(); } + [TestMethod] + public async Task ShouldDefaultToEnglishLocale() + { + var convoState = new ConversationState(new MemoryStorage()); + var dialogState = convoState.CreateProperty("dialogState"); + + var adapter = new TestAdapter() + .Use(new AutoSaveStateMiddleware(convoState)); + + // Create new DialogSet. + var dialogs = new DialogSet(dialogState); + dialogs.Add(new ChoicePrompt("ChoicePrompt", defaultLocale: null)); + + var helloLocale = MessageFactory.Text("hello"); + helloLocale.Locale = null; + + await new TestFlow(adapter, async (turnContext, cancellationToken) => + { + var dc = await dialogs.CreateContextAsync(turnContext, cancellationToken); + + var results = await dc.ContinueDialogAsync(cancellationToken); + if (results.Status == DialogTurnStatus.Empty) + { + await dc.PromptAsync( + "ChoicePrompt", + new PromptOptions + { + Prompt = new Activity { Type = ActivityTypes.Message, Text = "favorite color?", Locale = null }, + Choices = _colorChoices, + }, + cancellationToken); + } + }) + .Send(helloLocale) + .AssertReply((activity) => + { + // Use ChoiceFactory to build the expected answer, manually + var expectedChoices = ChoiceFactory.Inline(_colorChoices, null, null, new ChoiceFactoryOptions() + { + InlineOr = English.InlineOr, + InlineOrMore = English.InlineOrMore, + InlineSeparator = English.Separator, + }).Text; + Assert.AreEqual($"favorite color?{expectedChoices}", activity.AsMessageActivity().Text); + }) + .StartTestAsync(); + } + [TestMethod] public async Task ShouldAcceptAndRecognizeCustomLocaleDict() {