如何在运行时更改对话的语言环境

How to change locale for a conversation in runtme

我正在使用多语言 botframework composer,希望每个用户都能够 select 首选 language/locale。在通过选择对话框解析了他的 selection 的本地代码后,我如何在对话中设置它,以便他的设备中的语言环境设置在接下来的对话中被否决?

在模拟器中更改语言环境工作正常,在用户 selection 之后需要相同的行为。

设置 turn.locale 有效一回合,但在下一回合重置。

假设您无法控制客户端,那将是最好的。

您可以在不断增长的尚未标记为已弃用的机器人适配器层次结构上求助于旧的过载。

您必须在以下控制器中使用 PostAsync 方法(api/post-messages 端点)(显示由当前 bot 框架模板集创建的控制器只是为了比较):

    [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>());

要有一个适配器实例。

使用此方法适配器将无法使用机器人通道流式传输端点,但只要您不使用语音客户端,那应该不会有太大问题。

您还可以在我的博客 post How does a Bot Builder v4 bot work? 中阅读一些可能与您相关的其他详细信息,它有点过时但仍然有效。

更新 - 找到更好的解决方案

这个与当前的适配器浪潮一起工作并使用消息管道,所以它是“现代的”。

它还要求您使用自定义运行时,您将按如下方式进行自定义。

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 属性,下一轮将具有所需的语言环境,并将在用户状态下持续存在,直到他们再次更改它。