我正在使用具有多语言的 botframework 作曲家,并希望每个用户都能够选择首选语言/区域设置。在使用选择对话框解决他的选择的本地代码后,我如何在对话中设置它,以便他在设备中的区域设置将被否决以进行其余的对话?
在模拟器中更改语言环境可以正常工作,希望在用户选择后具有相同的行为。
设置 turn.locale 工作一回合,但在下一回合重置。
我正在使用具有多语言的 botframework 作曲家,并希望每个用户都能够选择首选语言/区域设置。在使用选择对话框解决他的选择的本地代码后,我如何在对话中设置它,以便他在设备中的区域设置将被否决以进行其余的对话?
在模拟器中更改语言环境可以正常工作,希望在用户选择后具有相同的行为。
设置 turn.locale 工作一回合,但在下一回合重置。
假设您无法控制客户端,那将是最好的。
您可以在不断增长的未标记为已弃用的 bot 适配器层次结构上诉诸旧的重载。
您必须在以下控制器中使用PostAsync
方法(api/post-messages
端点)(显示由当前一组机器人框架模板创建的只是为了比较):
[Route("api")]
[ApiController]
public class BotController : ControllerBase
{
private readonly IBotFrameworkHttpAdapter StreamingAdapter;
private readonly BotFrameworkAdapter PostAdapter;
private readonly ConversationLocales ConversationLocales;
private readonly IBot Bot;
public BotController(
IBotFrameworkHttpAdapter streamingAdapter,
BotFrameworkAdapter postAdapter,
ConversationLocales conversationLocales,
IBot bot)
{
StreamingAdapter = streamingAdapter;
PostAdapter = postAdapter;
Bot = bot;
}
[HttpPost("messages"), HttpGet("messages")]
public async Task PostOrStreamingAsync()
{
// Delegate the processing of the HTTP POST to the adapter.
// The adapter will invoke the bot.
await StreamingAdapter.ProcessAsync(Request, Response, Bot);
}
[HttpPost("post-messages")]
public async Task<InvokeResponse> PostAsync([FromBody] Activity activity)
{
var savedLocale = ConversationLocales.GetLocaleForConversation(activity.Conversation.Id);
activity.Locale = savedLocale ?? activity.Locale;
return await PostAdapter.ProcessActivityAsync(string.Empty, activity, Bot.OnTurnAsync, default);
}
}
假设您实现了一项ConversationLocales
服务,该服务允许您为每个对话 id 保留选定的语言环境。
在上面的代码中,我们使用BotFrameworkAdapter
适配器而不是IBotFrameworkHttpAdapter
,但是AdapterWithErrorHandler
模板中使用的 间接继承自BotFrameworkAdapter
,因此您可以执行以下操作ConfigureServices
来注册“两个”适配器:
services.AddSingleton<AdapterWithErrorHandler>();
services.AddSingleton<IBotFrameworkHttpAdapter>(sp => sp.GetRequiredService<AdapterWithErrorHandler>());
services.AddSingleton<BotFrameworkAdapter>(sp => sp.GetRequiredService<AdapterWithErrorHandler>());
拥有一个适配器实例。
使用此方法,适配器将无法使用机器人通道流式传输端点,但这应该不是什么大问题,只要您不使用语音客户端即可。
您还可以在我的博客文章Bot Builder v4 机器人如何工作?,它有点过时但仍然有效。
更新- 找到了更好的解决方案
这个适用于当前的适配器浪潮并使用消息管道,因此它是“现代的”。
它还要求您使用自定义运行时,您将按如下方式进行自定义。
1 - 创建以下中间件
public class LocaleSelectionMiddleware : IMiddleware
{
private readonly IStatePropertyAccessor<string> _userLocale;
public LocaleSelectionMiddleware(UserState userState)
{
_userLocale = userState.CreateProperty<string>("locale");
}
public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default)
{
if (turnContext is null)
{
throw new ArgumentNullException(nameof(turnContext));
}
var userLocale = await _userLocale.GetAsync(turnContext, () => turnContext.Activity.Locale);
turnContext.Activity.Locale = userLocale;
(turnContext as TurnContext).Locale = userLocale;
await next(cancellationToken).ConfigureAwait(false);
}
}
2 - 在适配器中配置GetBotAdapter()
中间件Startup.cs
public class Startup
{
public Startup(IWebHostEnvironment env, IConfiguration configuration)
{
this.HostingEnvironment = env;
this.Configuration = configuration;
}
//...
public BotFrameworkHttpAdapter GetBotAdapter(IStorage storage, BotSettings settings, UserState userState, ConversationState conversationState, IServiceProvider s)
{
var adapter = IsSkill(settings)
? new BotFrameworkHttpAdapter(new ConfigurationCredentialProvider(this.Configuration), s.GetService<AuthenticationConfiguration>())
: new BotFrameworkHttpAdapter(new ConfigurationCredentialProvider(this.Configuration));
adapter
.UseStorage(storage)
.UseBotState(userState, conversationState)
.Use(new RegisterClassMiddleware<IConfiguration>(Configuration))
.Use(new LocaleSelectionMiddleware(userState)) // <-- Add the middleware here
.Use(s.GetService<TelemetryInitializerMiddleware>());
//...
return adapter;
}
//...
}
3 - 在任何对话框中设置 user.locale 属性
从任何对话框设置user.locale
属性,下一轮将具有所需的语言环境,并将保持在用户状态,直到他们再次更改它。