Développement d’un robot – Comment changer la langue utilisée pour le dialogue

L’objet de cet article est d’indiquer comment j’ai intégré un changement de la langue utilisée durant le dialogue avec un robot.

Tout d’abord, voici un exemple de dialogue dans Skype avec changement de langue:

Pour implémenter des langues différentes, j’ai tout d’abord indiqué la langue neutre du robot dans les informations de l’assembly.

J’ai choisi l’Anglais car j’ai constaté une grande différence dans la qualité de traduction suivant la langue source, en bref, le service de traduction est plus performant en partant de l’Anglais. De plus, l’API que j’utilise attend des requêtes en Anglais.

Ensuite, j’ai intégré 2 fichiers de ressources, l’un pour la langue neutre, l’autre pour le Français.

Pour gérer les traductions dans le code, j’ai utilisé le composant accessible par nuget  TranslatorService

Pour utiliser les services de traduction, il faut s’inscrire sur Azure et obtenir une clé qu’il faudra passer au composant.

public static Task<string> DoTranslation(string inputText, string inputLocale, string outputLocale)
{
   var translator = new TranslatorServiceClient(WebConfigurationManager.AppSettings["TextTranslatorId"]);
   return translator.TranslateAsync(inputText, inputLocale, outputLocale);
}

La modification de la langue passe par 2 grands aspects :

  • L’un est le changement de la culture d’affichage du thread en cours.
  • L’autre est la possibilité de sauvegarder pour l’utilisateur en cours sa langue de préférence.

Le changement de langue est déclenché quand l’utilisateur indique “parle …” avec le nom de la langue.

L’implémentation est effectué quand l’Intent LUIS “speak” est détecté

  • Je lis d’abord la langue détectée par LUIS dans l’entité “languageEntityRecommendation”.
  • La méthode “GetCulture” me permet de savoir si la culture fait partie des ressources de l’assembly.
  • L’ensemble des cultures a été chargée précédemment dans la méthode “LoadCultures”.
  • Si la culture est trouvée, la langue de l’utilisateur est conservée grâce à la méthode “SetUserUiLanguage” et la culture du thread en cours est modifiée.
  • Si elle n’est pas trouvée, un message dans la langue de l’utilisateur est retourné.
  • Le code de ces méthodes est présenté ci-dessous.
[LuisIntent("speak")]
public async Task Speak(IDialogContext context, IAwaitable<IMessageActivity> activity, LuisResult result)
{
        var message = await activity;
        if (result.TryFindEntity(LanguageEntityName, out EntityRecommendation languageEntityRecommendation))
        {
            var culture = CultureHelper.GetCulture(languageEntityRecommendation.Entity);
            if (culture == null)
            {                    
                string languageName = await TranslationHelper.DoTranslation(languageEntityRecommendation.Entity,
                   QueryLanguage,
                   LanguageHelper.GetUserUiLanguage(context));
                await context.PostAsync(string.Format(Resources.Resource.LanguageUnaivalableString, languageName));
            }
            else
            {                    
                LanguageHelper.SetUserUiLanguage(context, culture.TwoLetterISOLanguageName);
                Thread.CurrentThread.CurrentUICulture = culture;
                await context.PostAsync(string.Concat(Resources.Resource.NewLanguageString, " ", culture.NativeName));
            }                  
        }
        context.Wait(this.MessageReceived);
}

Gestion des cultures

public static List<CultureInfo> CulturesAvailable { get => _culturesAvailable; set => _culturesAvailable = value; }
        
public static CultureInfo GetCulture(string EnglishName)
{
      foreach (CultureInfo info in _culturesAvailable)
      {
            if (info.EnglishName.ToLower().Contains(EnglishName.ToLower()))
                    return info;
      }
      return null;
}
 
public static void LoadCultures()
{
       if (_culturesAvailable.Count > 0)
            _culturesAvailable.Clear();
 
       ResourceManager rm = new ResourceManager(typeof(Resources.Resource));
            CultureInfo[] cultures = CultureInfo.GetCultures(CultureTypes.InstalledWin32Cultures);
       foreach (CultureInfo culture in cultures)
       {
            try
            {
                ResourceSet rs = rm.GetResourceSet(culture, true, false);
                if (rs != null && culture.ThreeLetterWindowsLanguageName != "IVL")
                    _culturesAvailable.Add(culture);
            }
            catch (CultureNotFoundException)
            {
            }
        }
}

Gestion de la langue de l’utilisateur

public static void SetUserUiLanguage(IDialogContext context, string UiLanguage)
{
    try
    {
        context.UserData.SetValue("UiLanguage", UiLanguage);
    }
    catch (Exception ex)
    {
        throw ex;
    }
}
 
public static string GetUserUiLanguage(IDialogContext context)
{
    try
    {
        context.UserData.TryGetValue("UiLanguage", out string result);
        if (result == null)
        {
            _neutralLanguage =
                Assembly.GetExecutingAssembly().GetCustomAttribute<NeutralResourcesLanguageAttribute>().CultureName.Substring(0, 2);
            result = _neutralLanguage;
        }
 
        return result;
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

Ainsi, à chaque fois qu’un message est retourné à l’utilisateur, sa langue préférée est détectée et le thread de la culture d’affichage modifié.

Ici, le code du message d’aide

public async static Task HelpMessageAsync(IDialogContext context)
{
    var UiCulture = new CultureInfo(LanguageHelper.GetUserUiLanguage(context));
    if (UiCulture != null)
        Thread.CurrentThread.CurrentUICulture = UiCulture;
 
    var text = string.Concat(Resources.Resource.UsageFirstLine, Environment.NewLine,
                Resources.Resource.UsageSecondLine, Environment.NewLine,
                Resources.Resource.UsageThirdLine, Environment.NewLine,
                Resources.Resource.UsageFourthLine, Environment.NewLine,
                Resources.Resource.UsageFifthLine);
    await context.PostAsync(text);
}

Comme je l’ai indiqué, j’ai choisi l’Anglais comme langue de l’assembly, il s’agit donc de traduire tous les messages reçus par le robot, quand ils ne sont pas dans la langue de l’assembly, avant de traiter les actions dans la classe gérant les dialogues.

C’est ce qui est fait dans le contrôleur de messages du robot.

[BotAuthentication]
public class MessagesController : ApiController
{        
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
///
 
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{          
    if (activity.Type == ActivityTypes.Message)
    {              
        var UiLanguage =  LanguageHelper.GetUserUiLanguage(activity);
        var neutralLanguage = LanguageHelper.GetNeutralLanguage();
        if (UiLanguage != neutralLanguage)
            activity.Text = await TranslationHelper.DoTranslation(activity.Text, UiLanguage, neutralLanguage);
        await Conversation.SendAsync(activity, () => new RootDialog());              
    }
    else
    {
        await HandleSystemMessageAsync(activity);
    }
    return new HttpResponseMessage(System.Net.HttpStatusCode.OK);
}

En conclusion, il convient de signaler que le code ici présenté permet de gérer les ajouts de langues, par la création de fichiers de ressources, sans autre modification.

March 11, 2018

Tags: ,

Leave a Reply

Your email address will not be published. Required fields are marked *