October 2014

Sun Mon Tue Wed Thu Fri Sat
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  










« AutoCAD Customization Facecast | Main | Live AutoCAD Customization Facecast, today! »

July 18, 2011

Caching translations of AutoCAD tooltips using .NET

In this previous post we introduced a technique for automatically translating AutoCAD’s tooltips into one of 35 different languages via an online translation service.

To improve the process at various levels – rendering it more efficient and enabling the possibility of local editing and crowdsourced localization – this post introduces caching of the translation results to a set of local XML files.

A few comments on the implementation changes:

  • There’s now a TRANSTIPSSRC command, which allows you to set the source language (using a similar UI to the target language). This is useful if you’re working on non-English AutoCAD (our Localization team is already looking at it, to see whether it helps them validate the correctness of product translations into other languages, which is interesting).
    • I don’t ultimately see either the source or target language as being options the user should ultimately set: the source language should be based on the product’s language, while the target should – in most cases – be based on the OS language (thanks to Cyrille Fauvel for that suggestion :-).
  • The approach we use for unresolved tooltips has been adjusted: we now set the cursor to the top-center of the primary screen (getting it well clear of the item currently hovered over, but also away from the top-left of the screen, which has a tendency to make the big red A flash when AutoCAD is maximised).
  • A few adjustments have been made to the creation of a unique ID for caching purposes, as well as the places we mark these IDs as having been translated.
  • The main change, of course, is to save the translations returned from the server into XML files. A single XML file will contain the various translations of the source content for a particular item. This change enables some very important capabilities:
    • The implementation is now more efficient: we don’t have to call out to the web service when we have pre-translated content for a target language.
    • Approved translations can be deployed with the app.
    • An editing/approval mechanism could easily be implemented to enable crowdsourced localization of items.
    • Caching the source content allows us to switch back to the source language but also to translate into other target languages (before this you had to restart AutoCAD).

Here’s the updated C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using Autodesk.Windows;

using System.Runtime.Serialization;

using System.Collections.Generic;

using System.Windows.Documents;

using System.Windows.Controls;

using System.Windows;

using System.Text.RegularExpressions;

using System.Text;

using System.Linq;

using System.Net;

using System.Xml;

using System.IO;

using System;

 

[assembly: ExtensionApplication(typeof(TranslateTooltips.Commands))]

 

namespace TranslateTooltips

{

  public class Commands : IExtensionApplication

  {

    // Keep track of currently translated items

 

    static List<string> _handled = null;

 

    // Our source and target languages

 

    static string _srcLang = "en";

    static string _trgLang = "";

 

    // Location off the main application to which to move

    // the cursor, for tooltip redisplay

 

    static System.Drawing.Point _offTarget;

 

    public void Initialize()

    {

      HijackTooltips();

    }

 

    public void Terminate()

    {

    }

 

    [CommandMethod("ADNPLUGINS", "TRANSTIPSSRC", CommandFlags.Modal)]

    public static void ChooseSourceLanguage()

    {

      Document doc =

        Autodesk.AutoCAD.ApplicationServices.Application.

          DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

 

      // Get the list of language codes and their corresponding

      // names

 

      List<string> codes = GetLanguageCodes();

      string[] names = GetLanguageNames(codes);

 

      // Make sure we have as many names as languages supported

 

      if (codes.Count == names.Length)

      {

        // Ask the user to select a source language

 

        string lang =

          ChooseLanguage(ed, codes, names, _srcLang, true);

 

        // If the language code returned is neither empty

        // nor the same as the target, print the code's

        // name and set it as the new target

 

        if (lang == _trgLang)

        {

          ed.WriteMessage(

            "\nSource language cannot be the same as the " +

            "target language."

          );

        }

        else if (!String.IsNullOrEmpty(lang))

        {

          // Get the name corresponding to a language code

 

          string name =

            names[

              codes.FindIndex(0, x => x == lang)

            ];

 

          // Print it to the user

 

          ed.WriteMessage(

            "\nSource language set to {0}.\n", name

          );

 

          // Set the new source language

 

          _srcLang = lang;

        }

      }

    }

 

    [CommandMethod("ADNPLUGINS", "TRANSTIPS", CommandFlags.Modal)]

    public static void ChooseTranslationLanguage()

    {

      Document doc =

        Autodesk.AutoCAD.ApplicationServices.Application.

          DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

 

      // Get the list of language codes and their corresponding

      // names

 

      List<string> codes = GetLanguageCodes();

      string[] names = GetLanguageNames(codes);

 

      // Make sure we have as many names as languages supported

 

      if (codes.Count == names.Length)

      {

        // Ask the user to select a target language

 

        string lang =

          ChooseLanguage(ed, codes, names, _trgLang, false);

 

        // If the language code returned is empty or the same

        // as the source, turn off translations

 

        if (lang == "" || lang == _srcLang)

        {

          ed.WriteMessage(

            "\nTooltip translation is turned off."

          );

          _trgLang = "";

        }

        else if (lang != null)

        {

          // Otherwise get the name corresponding to a language

          // code

 

          string name =

            names[

              codes.FindIndex(0, x => x == lang)

            ];

 

          // Print it to the user

 

          ed.WriteMessage(

            "\nTooltips will be translated into {0}.\n", name

          );

 

          // Set the new target language

 

          _trgLang = lang;

        }

      }

    }

 

    private static string ChooseLanguage(

      Editor ed, List<string> codes, string[] names,

      string lang, bool source

    )

    {

      // First option (0) is to unselect

 

      ed.WriteMessage("\n0 None");

 

      // The others (1..n) are the languages

      // available on the server

 

      for (int i = 0; i < names.Length; i++)

      {

        ed.WriteMessage("\n{0} {1}", i + 1, names[i]);

      }

 

      // Adjust the prompt based on whether selecting

      // a source or target language

 

      PromptIntegerOptions pio =

        new PromptIntegerOptions(

          String.Format(

            "\nEnter number of {0} language to select: ",

            source ? "source" : "target"

          )

        );

 

      // Add each of the codes as hidden keywords, which

      // allows the user to also select the language using

      // the 2-digit code (good for scripting on startup,

      // to avoid having to hard code a number)

 

      foreach (string code in codes)

      {

        pio.Keywords.Add(code, code, code, false, true);

      }

 

      // Set the bounds and the default value

 

      pio.LowerLimit = 0;

      pio.UpperLimit = names.Length;

      if (codes.Contains(lang))

      {

        pio.DefaultValue =

          codes.FindIndex(0, x => lang == x) + 1;

      }

      else

      {

        pio.DefaultValue = 0;

      }

      pio.UseDefaultValue = true;

 

      // Get the selection

 

      PromptIntegerResult pir = ed.GetInteger(pio);

 

      string resLang = null;

 

      if (pir.Status == PromptStatus.Keyword)

      {

        // The code was entered as a string

 

        if (!codes.Contains(pir.StringResult))

        {

          ed.WriteMessage(

            "\nNot a valid language code."

          );

          resLang = null;

        }

        else

        {

          resLang = pir.StringResult;

        }

      }

      else if (pir.Status == PromptStatus.OK)

      {

        // A number was selected

 

        if (pir.Value == 0)

        {

          // A blank string indicates none

 

          resLang = "";

        }

        else

        {

          // Otherwise we return the corresponding

          // code

 

          resLang = codes[pir.Value - 1];

        }

      }

 

      return resLang;

    }

 

    public static void HijackTooltips()

    {

      // For unresolved tooltips we move the cursor off

      // the current item and bring it back to force

      // redisplay. Here we calculate the target location

      // for the cursor as the top center of the primary

      // screen (will hopefully avoid flashing for most

      // scenarios)

 

      System.Drawing.Rectangle bounds =

        System.Windows.Forms.Screen.PrimaryScreen.Bounds;

 

      _offTarget =

        new System.Drawing.Point(

          bounds.X + (bounds.Width / 2), bounds.Y

        );

 

      // Instantiate our list of translated items

 

      if (_handled == null)

      {

        _handled = new List<string>();

      }

 

      // Respond to an event fired when any tooltip is

      // displayed inside AutoCAD

 

      Autodesk.Windows.ComponentManager.ToolTipOpened +=

        (s, e) =>

        {

          try

          {

            if (!String.IsNullOrEmpty(_trgLang))

            {

              // The outer object is of an Autodesk.Internal

              // class, hence subject to change

 

              Autodesk.Internal.Windows.ToolTip tt =

                s as Autodesk.Internal.Windows.ToolTip;

              if (tt != null)

              {

                if (tt.Content is RibbonToolTip)

                {

                  // Enhanced tooltips

 

                  RibbonToolTip rtt = (RibbonToolTip)tt.Content;

 

                  if (rtt.Content == null &&

                      rtt.ExpandedContent == null)

                  {

                    // To stop from closing the browser control

                    // tooltips in Revit

 

                    if (!rtt.Title.Contains(" : "))

                    {

                      CloseAndReshowTooltip(tt);

                    }

                  }

                  else

                  {

                    rtt.Content =

                      TranslateIfString(

                        rtt.Content, GetId(rtt)

                      );

                    TranslateObjectContent(

                      rtt.Content, GetId(rtt)

                    );

 

                    // Translate any expanded content

                    // (adding a suffix to the ID to

                    // distinguish from the basic content)

 

                    rtt.ExpandedContent =

                      TranslateIfString(

                        rtt.ExpandedContent,

                        GetExpandedId(GetId(rtt))

                      );

                    TranslateObjectContent(

                      rtt.ExpandedContent,

                      GetExpandedId(GetId(rtt))

                    );

 

                    // Force display of the tooltip, which avoids it

                    // not being shown for certain controls

                    // (currently hard-coding the offset, which may

                    // be based on the height of the cursor's glyph)

 

                    tt.Show(

                      System.Windows.Forms.Cursor.Position.X,

                      System.Windows.Forms.Cursor.Position.Y + 16

                    );

                  }

                }

                else if (tt.Content is UriKey)

                {

                  // This is called once for tooltips that

                  // need to be resolved by the system

 

                  // Here we close the tooltip and defer the

                  // redisplay to the system

 

                  CloseAndReshowTooltip(tt);

                }

                else

                {

                  // A basic, string-only tooltip

 

                  tt.Content = TranslateIfString(tt.Content, null);

                }

              }

            }

          }

 

          // Indiscriminate catch, ultimately to avoid crashing host

 

          catch { }

        };

    }

 

    private static void CloseAndReshowTooltip(

      Autodesk.Internal.Windows.ToolTip tt

    )

    {

      // Close the current (as yet unresolved) tooltip

 

      tt.Close();

 

      // Store the current cursor location

 

      System.Drawing.Point pt =

        System.Windows.Forms.Cursor.Position;

 

      // Set the new cursor location to that previously

      // calculated (the top-center of the primary display,

      // which should avoid strange activity inside the host

      // product)

 

      System.Windows.Forms.Cursor.Position = _offTarget;

 

      // Process events (several times, for luck)

 

      for (int i = 0; i < 5; i++)

      {

        System.Windows.Forms.Application.DoEvents();

      }

 

      // Set the cursor back to display the resolved

      // (and now translated) tooltip

 

      System.Windows.Forms.Cursor.Position = pt;

    }

 

    private static object TranslateIfString(

      object obj, string id

    )

    {

      // If the object passed in is a string,

      // return its translation to the caller

 

      object ret = obj;

      if (obj is string)

      {

        string trans =

          TranslateContent((string)obj, id);

        if (!String.IsNullOrEmpty(trans))

        {

          ret = trans;

        }

      }

      return ret;

    }

 

    private static void TranslateObjectContent(

      object obj, string id

    )

    {

      // Translate more complex objects and their

      // contents

 

      if (obj != null)

      {

        if (obj is TextBlock)

        {

          // Translate TextBlocks

 

          TextBlock tb = (TextBlock)obj;

          TranslateTextBlock(tb, id);

        }

        else if (obj is StackPanel)

        {

          // And also handle StackPanels of content

 

          StackPanel sp = (StackPanel)obj;

          TranslateStackPanel(sp, id);

        }

      }

    }

 

    private static void TranslateTextBlock(

      TextBlock tb, string id

    )

    {

      // Translate a TextBlock

 

      string trans =

        TranslateContent(tb.Text, id);

      if (!String.IsNullOrEmpty(trans))

      {

        tb.Text = trans;

      }

    }

 

    private static void TranslateStackPanel(

      StackPanel sp, string id

    )

    {

      // Translate a StackPanel of content

 

      TextBlock tb;

      for (int i=0; i < sp.Children.Count; i++)

      {

        UIElement elem = sp.Children[i];

 

        tb = elem as TextBlock;

        if (tb != null)

        {

          TranslateTextBlock(tb, GetExpandedItemId(id, i));

        }

        else

        {

          FlowDocumentScrollViewer sv =

            elem as FlowDocumentScrollViewer;

          if (sv != null)

          {

            TranslateFlowDocumentScrollViewer(

              sv, GetExpandedItemId(id, i)

            );

          }

        }

      }

    }

 

    private static void TranslateFlowDocumentScrollViewer(

      FlowDocumentScrollViewer sv, string id

    )

    {

      // Translate a FlowDocumentScrollViewer, which

      // hosts content such as bullet-lists in

      // certain tooltips (e.g. for HATCH)

 

      int n = 0;

      Block b = sv.Document.Blocks.FirstBlock;

      while (b != null)

      {

        List l = b as List;

        if (l != null)

        {

          ListItem li = l.ListItems.FirstListItem;

          while (li != null)

          {

            Block b2 = li.Blocks.FirstBlock;

            while (b2 != null)

            {

              Paragraph p = b2 as Paragraph;

              if (p != null)

              {

                Inline i = p.Inlines.FirstInline;

                while (i != null)

                {

                  string contents =

                    i.ContentStart.GetTextInRun(

                      LogicalDirection.Forward

                    );

 

                  // We need to suffix the IDs to

                  // keep them distinct

 

                  string trans =

                    TranslateContent(

                      contents, GetExpandedItemId(id, n)

                    );

                  if (!String.IsNullOrEmpty(trans))

                  {

                    i.ContentStart.DeleteTextInRun(

                      contents.Length

                    );

                    i.ContentStart.InsertTextInRun(trans);

                  }

                  n++;

                  i = i.NextInline;

                }

              }

              b2 = b2.NextBlock;

            }

            li = li.NextListItem;

          }

        }

        b = b.NextBlock;

      }

    }

 

    private static string GetId(RibbonToolTip rtt)

    {

      // The ID should be the command string, where

      // it exists (e.g. AutoCAD) or the item title,

      // otherwise (e.g. Revit)

 

      return

        String.IsNullOrEmpty(rtt.Command) ?

          rtt.Title :

          rtt.Command;

    }

 

    private static string GetExpandedId(string id)

    {

      // Suffix any non-null ID with -x for expanded content

 

      return String.IsNullOrEmpty(id) ? id : id + "-x";

    }

 

    private static string GetExpandedItemId(string id, int n)

    {

      // Get an ID for a sub-item of expanded content

 

      return

        String.IsNullOrEmpty(id) ? id : id + "_" + n.ToString();

    }

 

    private static void MarkAsTranslated(string id)

    {

      // Mark an item as having been translated

 

      if (!String.IsNullOrEmpty(id) && !_handled.Contains(id))

        _handled.Add(id);

    }

 

    private static void UnmarkAsTranslated(string id)

    {

      // Remove an item from the list of marked items

 

      if (!String.IsNullOrEmpty(id) && _handled.Contains(id))

        _handled.Remove(id);

    }

 

    private static bool AlreadyTranslated(string id)

    {

      // Check the list, to see whether an item has been

      // translated

 

      return _handled.Contains(id);

    }

 

    private static string TranslateContent(

      string contents, string id

    )

    {

      // Our translation to return, and the string to pass

      // as source (which may be the string passed in,

      // or may come from disk)

 

      string trans = null,

             source = contents;

 

      // If the target language is empty, we'll take the source

      // language

 

      string trgLang =

        String.IsNullOrEmpty(_trgLang) ? _srcLang : _trgLang;

 

      // Get the name of the XML file for this data

 

      string fn = GetXmlFileName(id);

      if (File.Exists(fn))

      {

        // If the item has already been translated in the UI,

        // we need to load the source language version to

        // retranslate that

 

        if (AlreadyTranslated(id))

        {

          source = LoadXmlTranslation(fn, _srcLang);

          source = (source == null ? contents : source);

 

          // If the source and target are the same,

          // reset to the source language

 

          if (_srcLang == trgLang)

          {

            UnmarkAsTranslated(id);

            return source;

          }

        }

 

        // Attempt to get a prior translation from XML

 

        trans = LoadXmlTranslation(fn, trgLang);

      }

 

      if (trans == null)

      {

        // If there was no translation on disk, translate

        // via the online service

 

        trans =

          GetTranslatedText(_srcLang, trgLang, source);

 

        // If the filename is valid, save the data to XML

 

        if (!String.IsNullOrEmpty(fn))

        {

          SaveXmlTranslation(

            fn, _srcLang, source, trgLang, trans

          );

        }

      }

      if (!String.IsNullOrEmpty(trans))

      {

        MarkAsTranslated(id);

      }

 

      return trans;

    }

 

    private static string GetXmlFileName(string id)

    {

      // The XML file will be beneath My Documents, under

      // a sub-folder named "TransTip Cache"

 

      string path =

        System.Environment.GetFolderPath(

          Environment.SpecialFolder.MyDocuments

        ) + "\\TransTips Cache";

 

      if (!Directory.Exists(path))

      {

        Directory.CreateDirectory(path);

      }

 

      // The filename is the id with the .xml extension

 

      return

        id == null ?

        "" :

        path + "\\" + MakeValidFileName(id) + ".xml";

    }

 

    private static string MakeValidFileName(string name)

    {

      string invChars =

        Regex.Escape(new string(Path.GetInvalidFileNameChars()));

      string invRegEx = string.Format(@"[{0}]", invChars + ".");

      return Regex.Replace(name, invRegEx, "-");

    }

 

    private static void SaveXmlTranslation(

      string fn,

      string srcLang, string contents,

      string trgLang, string trans

    )

    {

      if (File.Exists(fn))

      {

        // Add our content to an existing XML file

 

        XmlDocument xd = new XmlDocument();

        xd.Load(fn);

        XmlNode tg =

          xd.SelectSingleNode(

            "/Translation/Content[@Language='" + trgLang + "']"

          );

        if (tg == null)

        {

          XmlNode xn =

            xd.CreateNode(XmlNodeType.Element, "Content", "");

          XmlAttribute xlang = xd.CreateAttribute("Language");

          xlang.Value = trgLang;

          xn.InnerText = trans;

          xn.Attributes.Append(xlang);

          xd.GetElementsByTagName("Translation")[0].InsertAfter(

            xn,

            xd.GetElementsByTagName("Translation")[0].LastChild

          );

          xd.Save(fn);

        }

      }

      else

      {

        // Create a new Unicode XML file

 

        XmlTextWriter xw =

          new XmlTextWriter(fn, Encoding.Unicode);

        using (xw)

        {

          xw.WriteStartDocument();

          xw.WriteStartElement("Translation");

          xw.WriteStartElement("Content");

          xw.WriteAttributeString("Language", srcLang);

          xw.WriteAttributeString("Source", "True");

          xw.WriteString(contents);

          xw.WriteEndElement();

          xw.WriteStartElement("Content");

          xw.WriteAttributeString("Language", trgLang);

          xw.WriteString(trans);

          xw.WriteEndElement();

          xw.WriteEndElement();

          xw.Close();

        }

      }

    }

 

    private static string LoadXmlTranslation(

      string fn, string lang

    )

    {

      // Load the XML document

 

      XmlDocument xd = new XmlDocument();

      xd.Load(fn);

 

      // Look for a Content node for our language

 

      XmlNode tg =

        xd.SelectSingleNode(

          "/Translation/Content[@Language='" + lang + "']"

        );

      return tg == null ? null : tg.InnerXml;

    }

 

    // Replace the following string with the AppId you receive

    // from the Bing Developer Center

 

    const string AppId =

      "Kean's Application Id – please get your own :-)";

 

    const string baseTransUrl =

      "http://api.microsofttranslator.com/v2/Http.svc/";

 

    private static string GetTranslatedText(

      string from, string to, string content

    )

    {

      // Translate a string from one language to another

 

      string uri =

        baseTransUrl + "Translate?appId=" + AppId +

        "&text=" + content + "&from=" + from + "&to=" + to;

 

      // Create the request

 

      HttpWebRequest request =

        (HttpWebRequest)WebRequest.Create(uri);

 

      string output = null;

      WebResponse response = null;

 

      try

      {

        // Get the response

 

        response = request.GetResponse();

        Stream strm = response.GetResponseStream();

 

        // Extract the results string

 

        DataContractSerializer dcs =

          new DataContractSerializer(

            Type.GetType("System.String")

          );

        output = (string)dcs.ReadObject(strm);

      }

      catch (WebException e)

      {

        ProcessWebException(

          e, "\nFailed to translate text."

        );

      }

      finally

      {

        if (response != null)

        {

          response.Close();

          response = null;

        }

      }

      return output;

    }

 

    private static List<string> GetLanguageCodes()

    {

      // Get the list of language codes supported

 

      string uri =

        baseTransUrl + "GetLanguagesForTranslate?appId=" + AppId;

 

      // Create the request

 

      HttpWebRequest request =

        (HttpWebRequest)WebRequest.Create(uri);

 

      WebResponse response = null;

      List<String> codes = null;

 

      try

      {

        // Get the response

 

        response = request.GetResponse();

        using (Stream stream = response.GetResponseStream())

        {

          // Extract the list of language codes

 

          DataContractSerializer dcs =

            new DataContractSerializer(typeof(List<String>));

 

          codes = (List<String>)dcs.ReadObject(stream);

        }

      }

      catch (WebException e)

      {

        ProcessWebException(

          e, "\nFailed to get target translation languages."

        );

      }

      finally

      {

        if (response != null)

        {

          response.Close();

          response = null;

        }

      }

      return codes;

    }

 

    public static string[] GetLanguageNames(List<string> codes)

    {

      string uri =

        baseTransUrl + "GetLanguageNames?appId=" + AppId +

        "&locale=en";

 

      // Create the request

 

      HttpWebRequest req =

        (HttpWebRequest)WebRequest.Create(uri);

 

      req.ContentType = "text/xml";

      req.Method = "POST";

 

      // Encode the list of language codes

 

      DataContractSerializer dcs =

        new DataContractSerializer(

          Type.GetType("System.String[]")

        );

      using (Stream stream = req.GetRequestStream())

      {

        dcs.WriteObject(stream, codes.ToArray());

      }

 

      WebResponse response = null;

      try

      {

        // Get the response

 

        response = req.GetResponse();

 

        using (Stream stream = response.GetResponseStream())

        {

          // Extract the list of language names

 

          string[] results = (string[])dcs.ReadObject(stream);

          string[] names =

            results.Select(x => x.ToString()).ToArray();

          return names;

        }

      }

      catch (WebException e)

      {

        ProcessWebException(

          e, "\nFailed to get target language."

        );

      }

      finally

      {

        if (response != null)

        {

          response.Close();

          response = null;

        }

      }

      return null;

    }

 

    private static void ProcessWebException(

      WebException e, string message

    )

    {

      // Provide information regarding an exception

 

      Document doc =

        Autodesk.AutoCAD.ApplicationServices.Application.

          DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

 

      ed.WriteMessage("{0}: {1}", message, e.ToString());

 

      // Obtain detailed error information

 

      string strResponse = string.Empty;

      using (

        HttpWebResponse response =

          (HttpWebResponse)e.Response

      )

      {

        using (

          Stream responseStream =

            response.GetResponseStream()

        )

        {

          using (

            StreamReader sr =

              new StreamReader(

                responseStream, System.Text.Encoding.ASCII

              )

            )

          {

            strResponse = sr.ReadToEnd();

          }

        }

      }

 

      // Print it to the user

 

      ed.WriteMessage(

        "\nHttp status code={0}, error message={1}",

        e.Status, strResponse

      );

    }                              

  }

}

The behaviour is more-or-less the same, although the TRANSTIP command will now more effectively switch between languages. Here’s the same tooltip in a number of languages, all in the same editing session (after subsequent uses of the TRANSTIP command).

Scale tooltip in Arabic

Scale tooltip in Estonian

Scale tooltip in Korean

The crowdsourced editing mechanism – while enabled by saving to XML – has yet to be implemented via a direct, in-product UI. For now, though, the XML files could very easily be edited and deployed separately by someone choosing to deploy a specific translation of AutoCAD’s tooltips.

For instance, here's the content of the SCALE.xml file from the cache, containing the results of that items text having been translated into all 35 languages:

<?xml version="1.0" encoding="utf-16"?>

<Translation>

  <Content Source="True" Language="en">

    Enlarges or reduces selected objects, keeping the proportions

    of the object the same after scaling

  </Content>

  <Content Language="fr">

    Agrandit ou réduit les objets sélectionnés, en conservant les

    proportions de l'objet de la même après la mise à l'échelle

  </Content>

  <Content Language="ar">

    تكبير أو يقلل من

    الكائنات المحددة، الحفاظ على أبعاد الكائن نفسه بعد رفع

  </Content>

  <Content Language="bg">

    Увеличава или намалява избраните обекти, като пропорциите на

    обекта, същото след мащабиране

  </Content>

  <Content Language="ca">

    Augmenta o redueix els objectes seleccionats, mantenir les

    proporcions de l'objecte el mateix després de l'ampliació

  </Content>

  <Content Language="zh-CHS">

    放大或缩小所选的对象,同样保持对象的比例后缩放

  </Content>

  <Content Language="zh-CHT">

    放大或縮小所選的物件,同樣保持物件的比例後縮放

  </Content>

  <Content Language="cs">

    Zvětší nebo zmenší vybraných objektů, udržuje proporce

    objektu stejná po změně velikosti

  </Content>

  <Content Language="da">

    Forstørrer eller formindsker markerede objekter, holde

    objektet proportioner det samme efter skalering

  </Content>

  <Content Language="nl">

    Vergroot of verkleint u geselecteerde objecten, houden

    de verhoudingen van het object hetzelfde na schalen

  </Content>

  <Content Language="et">

    Suurendab või vähendab valitud objektid, hoides objekti

    proportsioonide sama pärast mastaapimine

  </Content>

  <Content Language="fi">

    Suurentaa tai pienentää valittuja objekteja, objektin

    mittasuhteita sama pitäminen jälkeen skaalaus

  </Content>

  <Content Language="de">

    Vergrößert oder verkleinert die ausgewählten Objekte,

    halten den Proportionen des Objekts gleich nach Skalierung

  </Content>

  <Content Language="el">

    Μεγεθύνει ή μειώνει τα επιλεγμένα αντικείμενα, διατηρώντας

    τις αναλογίες του αντικειμένου το ίδιο μετά την κλιμάκωση

  </Content>

  <Content Language="ht">

    Agrandir ou réduit sélectionné objets, kenbe proportions de

    objet a menm bagay la tou apre dekale

  </Content>

  <Content Language="he">

    הגדלה או הקטנה של אובייקטים שנבחרו, שמירה

    על הפרופורציות של האובייקט אותו לאחר שינוי קנה מידה

  </Content>

  <Content Language="hu">

    Nagyítása vagy kicsinyítése a kijelölt objektumok, tartás

    az objektum arányait azonos után méretezés

  </Content>

  <Content Language="id">

    Membesar atau mengurangi objek terpilih, menjaga proporsi

    objek yang sama setelah scaling

  </Content>

  <Content Language="it">

    Ingrandisce o riduce gli oggetti selezionati, mantenendo le

    proporzioni dell'oggetto la stessa dopo aver scalatura

  </Content>

  <Content Language="ja">

    拡大または縮小し、オブジェクトの縦横比、同じスケーリング後維持、

    選択したオブジェクト

  </Content>

  <Content Language="ko">

    확대 또는 축소 배율 조정 후 동일 개체의 비율 유지 선택한 개체

  </Content>

  <Content Language="lv">

    Paplašina vai samazina atlasītos objektus, saglabājot objekta

    proporcijas pats pēc mērogošanas

  </Content>

  <Content Language="lt">

    Padidina arba sumažina pažymėtus objektus, išlaikyti objekto

    proporcijas tą patį po mastelio nustatymas

  </Content>

  <Content Language="no">

    Forstørrer eller reduserer markerte objekter, beholde

    proporsjonene for objektet det samme etter skalering

  </Content>

  <Content Language="pl">

    Powiększa lub zmniejsza zaznaczonych obiektów, utrzymując

    proporcje obiektu to samo po skalowanie

  </Content>

  <Content Language="pt">

    Amplia ou reduz a objetos selecionados, mantendo as

    proporções do objeto o mesmo depois de dimensionamento

  </Content>

  <Content Language="ro">

    Măreşte sau reduce obiectele selectate, păstrând proporţiile

    obiectului la fel după scalare

  </Content>

  <Content Language="ru">

    Увеличивает или уменьшает выбранных объектов, изменяя

    пропорций объекта после масштабирования

  </Content>

  <Content Language="sk">

    Zväčší alebo zmenší vybraté objekty, vedenie proporcie

    objektu rovnaké po mierka

  </Content>

  <Content Language="sl">

    Poveča oziroma zmanjša izbrane predmete, vodenje razmerja

    predmeta, enaki po škaje

  </Content>

  <Content Language="es">

    Aumenta o reduce los objetos seleccionados, mantener las

    proporciones del objeto de la misma después de escalar

  </Content>

  <Content Language="sv">

    Förstorar eller förminskar markerade objekt, hålla objektet

    proportioner samma efter skalning

  </Content>

  <Content Language="th">

    ขยาย หรือวัตถุที่เลือก การรักษาสัดส่วนของวัตถุเดียวกันหลังจากปรับมาตราส่วน

  </Content>

  <Content Language="tr">

    Büyütür veya küçültür seçili nesneler, nesnenin oranlarını

    aynı tutmak sonra ölçekleme

  </Content>

  <Content Language="uk">

    Збільшує або зменшує виділених об'єктів, зберігаючи

    пропорції об'єкта ж після масштабування

  </Content>

  <Content Language="vi">

    Enlarges hoặc làm giảm đối tượng được chọn, giữ tỷ lệ của

    các đối tượng như vậy sau khi rộng

  </Content>

</Translation>

It may prove better to keep separate files for each language, and that’s certainly something that can be implemented with little effort, should the need arise.

I think that’s enough, for today. My next steps with this tool will be to create a common implementation that can be used in other Autodesk products making use of AdWindows.dll (Jeremy Tammik very kindly ported the code to Revit, so we at least know it’s possible). I’ll probably stick with a “shared source” approach, rather than adding the overhead of a standalone DLL, but – either way – some work will be needed to refactor the appropriate code into a separate, shared implementation.

Ah yes, and I should probably make sure the application behaves predictably – or at least fails gracefully – when an Internet connection is not available.

blog comments powered by Disqus

Feed/Share

10 Random Posts