How to create resourse file using IFormBuilder.SaveResources in FormFlow (Bot Builder C# SDK)

245 views Asked by At

I need to localize all the strings and Enums in FormFlow for the German language. I have looked at https://learn.microsoft.com/en-us/bot-framework/dotnet/bot-builder-dotnet-formflow-localize And It says there are two methods. One using IFormBuilder.SaveResources and second using RView tool (Which I couldn't figure out). How can I use the first method to generate and save .resx files for both German and English as in AnnotatedSandwich Bot?

  • For example, in following BuildLocalizeForm(), where should I put what piece of code to generate .resx, .de.resx and .en.resx files

Here is that sample: https://github.com/Microsoft/BotBuilder/tree/master/CSharp/Samples/AnnotatedSandwichBot

    public static IForm<SandwichOrder> BuildLocalizedForm()
    {
        var culture = Thread.CurrentThread.CurrentUICulture;

        IForm<SandwichOrder> form;
        if (!_forms.TryGetValue(culture, out form))
        {
            OnCompletionAsyncDelegate<SandwichOrder> processOrder = async (context, state) =>
                            {
                                await context.PostAsync(DynamicSandwich.Processing);
                            };
            // Form builder uses the thread culture to automatically switch framework strings
            // and also your static strings as well.  Dynamically defined fields must do their own localization.
            var builder = new FormBuilder<SandwichOrder>()
                    .Message("Welcome to the sandwich order bot!")
                    .Field(nameof(Sandwich))
                    .Field(nameof(Length))
                    .Field(nameof(Bread))
                    .Field(nameof(Cheese))
                    .Field(nameof(Toppings),
                        validate: async (state, value) =>
                        {
                            var values = ((List<object>)value).OfType<ToppingOptions>();
                            var result = new ValidateResult { IsValid = true, Value = values };
                            if (values != null && values.Contains(ToppingOptions.Everything))
                            {
                                result.Value = (from ToppingOptions topping in Enum.GetValues(typeof(ToppingOptions))
                                                where topping != ToppingOptions.Everything && !values.Contains(topping)
                                                select topping).ToList();
                            }
                            return result;
                        })
                    .Message("For sandwich toppings you have selected {Toppings}.")
                    .Field(nameof(SandwichOrder.Sauces))
                    .Field(new FieldReflector<SandwichOrder>(nameof(Specials))
                        .SetType(null)
                        .SetActive((state) => state.Length == LengthOptions.FootLong)
                        .SetDefine(async (state, field) =>
                            {
                                field
                                    .AddDescription("cookie", DynamicSandwich.FreeCookie)
                                    .AddTerms("cookie", Language.GenerateTerms(DynamicSandwich.FreeCookie, 2))
                                    .AddDescription("drink", DynamicSandwich.FreeDrink)
                                    .AddTerms("drink", Language.GenerateTerms(DynamicSandwich.FreeDrink, 2));
                                return true;
                            }))
                    .Confirm(async (state) =>
                        {
                            var cost = 0.0;
                            switch (state.Length)
                            {
                                case LengthOptions.SixInch: cost = 5.0; break;
                                case LengthOptions.FootLong: cost = 6.50; break;
                            }
                            return new PromptAttribute(string.Format(DynamicSandwich.Cost, cost) + "{||}");
                        })
                    .Field(nameof(SandwichOrder.DeliveryAddress),
                        validate: async (state, response) =>
                        {
                            var result = new ValidateResult { IsValid = true, Value = response };
                            var address = (response as string).Trim();
                            if (address.Length > 0 && address[0] < '0' || address[0] > '9')
                            {
                                result.Feedback = DynamicSandwich.BadAddress;
                                result.IsValid = false;
                            }
                            return result;
                        })
                    .Field(nameof(SandwichOrder.DeliveryTime), "What time do you want your sandwich delivered? {||}")
                    .Confirm("Do you want to order your {Length} {Sandwich} on {Bread} {&Bread} with {[{Cheese} {Toppings} {Sauces}]} to be sent to {DeliveryAddress} {?at {DeliveryTime:t}}?")
                    .AddRemainingFields()
                    .Message("Thanks for ordering a sandwich!")
                    .OnCompletion(processOrder);
            builder.Configuration.DefaultPrompt.ChoiceStyle = ChoiceStyleOptions.Auto;
            form = builder.Build();
            _forms[culture] = form;
        }
        return form;
    }
1

There are 1 answers

0
Ezequiel Jadib On BEST ANSWER

Before the line

 form = builder.Build();

You can call builder.SaveResources passing a IResourceWriter.

For understanding how to create ResourceWriter, read https://msdn.microsoft.com/en-us/library/system.resources.resourcewriter(v=vs.110).aspx but it will basically be something like:

ResourceWriter writer = new ResourceWriter("myResources.resources");

Still, I believe you should go with the RView option. You are not following the steps, it's as easy as having the Rview tool sitting in the same path where the DLLs are or when calling the RView passing the full path to the DLL.