Skip to content

Commit

Permalink
parity ChoicePrompt fix with dotnet
Browse files Browse the repository at this point in the history
  • Loading branch information
mdrichardson committed Nov 8, 2019
1 parent 779f270 commit 3985a06
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 6 deletions.
18 changes: 13 additions & 5 deletions libraries/botbuilder-dialogs/src/prompts/choicePrompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,7 @@ export class ChoicePrompt extends Prompt<FoundChoice> {

protected async onPrompt(context: TurnContext, state: any, options: PromptOptions, isRetry: boolean): Promise<void> {
// Determine locale
let locale: string = PromptCultureModels.mapToNearestLanguage(context.activity.locale || this.defaultLocale);
if (!locale || !this.choiceDefaults.hasOwnProperty(locale)) {
locale = 'en-us';
}
const locale = this.determineCulture(context.activity);

// Format prompt to send
let prompt: Partial<Activity>;
Expand All @@ -111,7 +108,7 @@ export class ChoicePrompt extends Prompt<FoundChoice> {
const utterance: string = activity.text;
const choices: any[] = (this.style === ListStyle.suggestedAction ? ChoiceFactory.toChoices(options.choices) : options.choices)|| [];
const opt: FindChoicesOptions = this.recognizerOptions || {};
opt.locale = activity.locale || opt.locale || this.defaultLocale || 'en-us';
opt.locale = this.determineCulture(activity, opt);
const results: any[] = recognizeChoices(utterance, choices, opt);
if (Array.isArray(results) && results.length > 0) {
result.succeeded = true;
Expand All @@ -120,4 +117,15 @@ export class ChoicePrompt extends Prompt<FoundChoice> {

return result;
}

private determineCulture(activity: Activity, opt: FindChoicesOptions = null): string {
const optLocale = opt && opt.locale ? opt.locale : null;
let culture = PromptCultureModels.mapToNearestLanguage(activity.locale || optLocale || this.defaultLocale || PromptCultureModels.English.locale);
if (!culture || !this.choiceDefaults[culture])
{
culture = PromptCultureModels.English.locale;
}

return culture;
}
}
25 changes: 24 additions & 1 deletion libraries/botbuilder-dialogs/src/prompts/promptCultureModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,35 @@ export class PromptCultureModels {
noInLanguage: 'No',
}

private static getSupportedCultureCodes(): string[] {
return this.getSupportedCultures().map((c): string => c.locale);
}

/**
* Use Recognizers-Text to normalize various potential Locale strings to a standard.
* @remarks This is mostly a copy/paste from https://github.com/microsoft/Recognizers-Text/blob/master/JavaScript/packages/recognizers-text/src/culture.ts#L39
* This doesn't directly use Recognizers-Text's MapToNearestLanguage because if they add language support before we do, it will break our prompts.
* @param cultureCode Represents locale. Examples: "en-US, en-us, EN".
* @returns Normalized locale.
*/
public static mapToNearestLanguage = (cultureCode: string): string => Culture.mapToNearestLanguage(cultureCode);
public static mapToNearestLanguage(cultureCode: string): string {
if (cultureCode !== undefined) {
cultureCode = cultureCode.toLowerCase();
let supportedCultureCodes = this.getSupportedCultureCodes();

if (supportedCultureCodes.indexOf(cultureCode) < 0) {
let culturePrefix = cultureCode.split('-')[0].trim();

supportedCultureCodes.forEach(function(supportedCultureCode): void {
if (supportedCultureCode.startsWith(culturePrefix)) {
cultureCode = supportedCultureCode;
}
});
}
}

return cultureCode;
}

public static getSupportedCultures = (): PromptCultureModel[] =>
[
Expand Down
45 changes: 45 additions & 0 deletions libraries/botbuilder-dialogs/tests/choicePrompt.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const { ActivityTypes, CardFactory, ConversationState, MemoryStorage, TestAdapter } = require('botbuilder-core');
const { ChoicePrompt, ChoiceFactory, DialogSet, ListStyle, DialogTurnStatus } = require('../');
const { PromptCultureModels } = require('../');
const assert = require('assert');

const answerMessage = { text: `red`, type: 'message' };
Expand Down Expand Up @@ -385,6 +386,50 @@ describe('ChoicePrompt', function () {
}));
});

it('should default to english locale', async function () {
const locales = [
null,
'',
'not-supported'
];
await Promise.all(locales.map(async (testLocale) => {
const adapter = new TestAdapter(async (turnContext) => {
const dc = await dialogs.createContext(turnContext);

const results = await dc.continueDialog();
if (results.status === DialogTurnStatus.empty) {
await dc.prompt('prompt', { prompt: 'Please choose a color.', choices: stringChoices });
} else if (results.status === DialogTurnStatus.complete) {
const selectedChoice = results.result;
await turnContext.sendActivity(selectedChoice.value);
}
await convoState.saveChanges(turnContext);
});
const convoState = new ConversationState(new MemoryStorage());

const dialogState = convoState.createProperty('dialogState');
const dialogs = new DialogSet(dialogState);
const choicePrompt = new ChoicePrompt('prompt', async (prompt) => {
assert(prompt);
if (!prompt.recognized.succeeded) {
await prompt.context.sendActivity('bad input.');
}
return prompt.recognized.succeeded;
}, null);
dialogs.add(choicePrompt);

await adapter.send({ text: 'Hello', type: ActivityTypes.Message, locale: testLocale })
.assertReply((activity) => {
const expectedChoices = ChoiceFactory.inline(stringChoices, null, null, {
inlineOr: PromptCultureModels.English.inlineOr,
inlineOrMore: PromptCultureModels.English.inlineOrMore,
inlineSeparator: PromptCultureModels.English.separator,
}).text;
assert.strictEqual(activity.text, `Please choose a color.${ expectedChoices }`);
});
}));
});

it('should accept and recognize custom locale dict', async function() {
const adapter = new TestAdapter(async (turnContext) => {
const dc = await dialogs.createContext(turnContext);
Expand Down

0 comments on commit 3985a06

Please sign in to comment.