.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

How to get the localize type names?

7 REPLIES 7
SOLVED
Reply
Message 1 of 8
moon_pelican_cat
600 Views, 7 Replies

How to get the localize type names?

I found the post:

https://forums.autodesk.com/t5/net/get-localized-type-names-or-all-geometry-types/m-p/4325104#M35866

 

this is helpfull. but it's not perfect on my purpose.

 

I need the localized type names for showing filterling types in the command.

 

 

internal class adskUtil { ...

public static ObjectId? GetEntity<T>() where T : DBObject
{
  var objType = typeof(T);
  var RXType = RXClass.GetClass(objType);
  // I need the code to get the localized type name.
  var localized = ...GetLocalizedTypeName(...)
  //var id = AdskUtil.GetEntity(msg : $"select {RXType.Name}", 
         //filtered_types: new Type[] { objType });
  var id = AdskUtil.GetEntity(msg : $"select {localized}",
         filtered_types: new Type[] { objType });
  return id;
}

public static ObjectId? GetEntity(string msg = "", Type[] filtered_types = null)
{
    // Get the current document
    Editor acDocEd = Application.DocumentManager.MdiActiveDocument.Editor;

    // Get Typenames
    string msg_ = (msg == "") ? "Pick Entity" : msg;
    string proper_msg = "(" + GetFilteredTypes(filtered_types) + "):";

    // Get the Entity
    PromptEntityResult acEPrompt;
    acEPrompt = acDocEd.GetEntity(new PromptEntityOptions(msg_));

    var result = null as ObjectId?;

    // If the prompt status is OK, objects were selected before
    // the command was started
    if (acEPrompt.Status == PromptStatus.OK)
    {
        result = acEPrompt.ObjectId;

        // if there are filtered types, check types.
        bool type_exists = true;
        var acCurDb = Application.DocumentManager.MdiActiveDocument.Database;
        using (var acTrans = acCurDb.TransactionManager.StartTransaction())
        {
            Object obj = acTrans.GetObject((ObjectId)result, OpenMode.ForRead);

            type_exists = false;
            foreach (Type t in filtered_types)
            {
                // if it is assignable, it is allowed.
                if (t.IsAssignableFrom(obj.GetType()))
                {
                    type_exists = true;
                    break;
                }
            }
        }

        // if type miss-matched, remove selection object
        if (type_exists == false)
        {
            result = null;
        }
    }
    return result;
}

... }

 

 

Implementation is generic. so that I need to get the localized type names from the type infomations.

not the Object Ids. (I want to know the names before the instatiations.)

 

Why did I think it can be likely possible?

I saw the names of the property set definition filter types in the UI.

So there are the names in the API??? I might think. 

moon_pelican_cat_0-1713755225227.png

This Tab is shown in the property set definition window, and the names might be gotten from the Types, not the instances likely, I think.

 

Any help is appreciated. 

Labels (1)
7 REPLIES 7
Message 2 of 8

Out of curiosity, why are you apparently reinventing PromptEntityOptions.AddAllowedClass() ?

 

About the localized type names, see if this helps:

 

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Resources;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;

namespace Autodesk.AutoCAD.Runtime
{
   public static class LocalizedNames
   {
      static Dictionary<Assembly, ResourceManager> map 
         = new Dictionary<Assembly, ResourceManager>();

      public static ResourceManager GetResourceManager(Type type)
      {
         if(type == null)
            throw new ArgumentNullException(nameof(type));
         if(!map.TryGetValue(type.Assembly, out ResourceManager value))
            map[type.Assembly] = value = new LenientResourceManager(type);
         return value;
      }

      public static string GetLocalizedName(string name, Type type)
      {
         if(type == null)
            throw new ArgumentNullException(nameof(type));
         ResourceManager resourceManager = GetResourceManager(type);
         return GetLocalizedName(name, resourceManager);
      }

      public static bool TryGetLocalizedName(string key, Type type, out string result)
      {
         if(string.IsNullOrWhiteSpace(key))
            throw new ArgumentException(nameof(key));
         if(type == null)
            throw new ArgumentNullException(nameof(type));
         ResourceManager resourceManager = GetResourceManager(type);
         return TryGetLocalizedName(key, resourceManager, out result);
      }

      public static string GetLocalizedName(string key, ResourceManager manager)
      {
         if(string.IsNullOrWhiteSpace(key))
            throw new ArgumentException(nameof(key));
         if(manager == null)
            throw new ArgumentNullException(nameof(manager));
         string str = manager.GetString(key);
         return string.IsNullOrWhiteSpace(str) ? key : str;
      }

      public static bool TryGetLocalizedName(string key, ResourceManager manager, out string result)
      {
         if(string.IsNullOrWhiteSpace(key))
            throw new ArgumentException(nameof(key));
         if(manager == null)
            throw new ArgumentNullException(nameof(manager));
         string str = manager.GetString(key);
         bool flag = !string.IsNullOrWhiteSpace(str);
         result = flag ? str : string.Empty;
         return flag;
      }

      public static string GetLocalizedTypeName(Type type)
      {
         if(type == null)
            throw new ArgumentNullException(nameof(type));
         string localName = type.Name;
         if(TryGetLocalizedName("Class." + type.FullName, type, out localName))
            return localName;
         else
            return type.Name;
      }

      /// <summary>
      /// Extension method targeting System.Type, searches for
      /// and returns a localized name for the type, as defined
      /// in the assembly's resources module.
      /// 
      /// If no localized name is found, the Name of the type
      /// argument is returned.
      /// </summary>

      public static string GetLocalName(this Type type)
      {
         if(type == null)
            throw new ArgumentNullException(nameof(type));
         return LocalizedNames.GetLocalizedTypeName(type);
      }
   }
}

namespace Test
{ 
   public static class TestLocalNames
   {
      [CommandMethod("TESTLOCALNAMES")]
      public static void Test()
      {
         string localPolyLineName = typeof(Polyline).GetLocalName();
         Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(
            $"Localized name of Polyline: {localPolyLineName}");
      }
   }
}

 

 

 

 

 

Message 3 of 8

Thanks for your insightful answer.

 

PromptEntityOptions.AddAllowedClass()

I just didn't know the functionality. Thanks for pointing that out. 
 

I tried to use the code and it works partially (can get the names of types in natural language, English).

 

but I need the localized names in Japanese (or other languages if there is any resoruces).

 

The testing code is shown below:: 

 

 

[CommandMethod("TESTLOCALNAMES")]
public static void Test()
{
    List<Type> types = new List<Type>() { 
        typeof(Polyline), typeof(Polyline2d), typeof(Circle), typeof(Spline), typeof(Ellipse),
        typeof(Polyline3d), typeof(Polygon), typeof(PolygonMesh), typeof(Surface), typeof(TinSurface), typeof(Profile), typeof(ProfileView) };
    
    TestLocalNameInfo();
    types.ForEach(t => TestLocalName(t));
}
public static void TestLocalName(Type t)
{
    string localName = t.GetLocalName();
    string RXName = RXClass.GetClass(t).Name;

    Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(
        $"Localized name of {RXName,20}: {localName,20}\n");
}

public static void TestLocalNameInfo()
{
    string cultureExpr(CultureInfo info) => $"{info.DisplayName}({info.EnglishName}, {info.Name})";

    Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(
            $"Culture    : {cultureExpr(CultureInfo.CurrentCulture)}\n"
            );
    Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(
            $"UICulture  : {cultureExpr(CultureInfo.CurrentUICulture)}\n"
            );
}

 

 

 

Execution results:

 

 

Culture    : 日本語 (日本)(Japanese (Japan), ja-JP)
UICulture  : 日本語 (日本)(Japanese (Japan), ja-JP)
Localized name of         AcDbPolyline:             Polyline
Localized name of       AcDb2dPolyline:           Polyline2d
Localized name of           AcDbCircle:               Circle
Localized name of           AcDbSpline:               Spline
Localized name of          AcDbEllipse:              Ellipse
Localized name of       AcDb3dPolyline:           Polyline3d
Localized name of         AecDbPolygon:              Polygon
Localized name of      AcDbPolygonMesh:          PolygonMesh
Localized name of          AcDbSurface:              Surface
Localized name of     AeccDbSurfaceTin:           TinSurface
Localized name of     AeccDbVAlignment:              Profile
Localized name of   AeccDbGraphProfile:          ProfileView

 

 

moon_pelican_cat_0-1713857767043.png

 

If you have a solution, it would be appreciated. 

I've been still strugling the solution myself ... it's key to solve this to specify the culture of a resource directly, anyhow I think.

 

 

And I would like to have your answer if you don't mind,
 
What the difference of LenientResourceManager (Autodesk.AutoCAD.Runtime) and ResourceManager (System.Resources) ?

I tried for several hours to understand but could only dimly make sense of it:

  • LenientResourceManager
    • inherits ResourceManager
    • overload override InternalGetResourceSet
      • load automatically an assembly of the indicated type(key) if there is no resoruces found.
      • just return null value if there is no loadable resources. ??? (I think, probably)
      • 'Lenient(kind, forgiving)' is derives from this fetures ?

 

  • ResourceManager
    • throw Exception if there is no loadable resources.
 
Sincerely.
Message 4 of 8

I made a mistake in the reply source. Please check below above.

Message 5 of 8

The code I posted most-likely has a bug, because it is not returning the localized display names stored in the resources .DLL, and it was coded to fallback to the name of the type, which is what you're getting. I have been playing around with merging that code with another class I have that looks for DisplayName attributes on Types and Properties, and returns their values instead of the type/property names.  You can find that ongoing work-in-progress here  Feel free to give it a try, as it is much more robust and comprehensive than what I had posted above, and also provides access to localized display names of properties and Enum types in addition to Types.

 

To see where the data is (or should be) coming from, you should have this file under your AutoCAD root folder:

 

English AutoCAD:
   
   Program Files\AutoCAD 20XX\en-US\acdbmgd.resources.dll

Japanaese AutoCAD

   Program Files\AutoCAD 20XX\ja-JP\acdbmgd.resources.dll

 

You can open this file with ILSpy, and examine the resource strings.

 

 

 

    

Message 6 of 8

Thanks for your kind reply and new suggestions. I have heard the name, ILSpy.
(but I have never used and today tried to use, ok, it's great.)
and looking into the dll:

 

Program Files\autodesk\autocad 2022\ja-JP\acdbmgd.resources.dll
-> Resources
-> Autodesk.AutoCAD.DatabaseServices.Entity.ja-JP.resources

 

Ok. This is exactly what I wanted ever.

 

So then I searching equivalent ones in AddIn directories like:

 

Architechture/Map
  Program Files\autodesk\autocad 2022\ACA\ja-JP\

Civil
  Program Files\autodesk\autocad 2022\C3D\ja-JP\

 

I was chechking almost all dlls in these, but no resources like vanilla one.(*)
(dragging all the dlls in these, and removing the ones unable to load, chechking the ones left...)

 

Otherwise perhaps not in there, or something different method pass applied for AddIn object types,
or might change the inmemory resourceset gradually each loading AddIns internally(???)
 
...

 

By the way, I realized why 'LenientResourceManager' cannot load the vanilla dll('acdbmgd.resources.dll') :

 

// [in the VS decompiled code(from line 45 to 89)]
Assembly assembly = null;
if (culture == CultureInfo.InvariantCulture)
{
    assembly = MainAssembly;
}
else if (!culture.IsNeutralCulture)
{
    AssemblyName assemblyName = new AssemblyName();
    string text = ".resources";
    string name = MainAssembly.GetName().Name;
    assemblyName.Name = name + text;
    assemblyName.CultureInfo = culture;
    assemblyName.Version = ResourceManager.GetSatelliteContractVersion(MainAssembly);
    try
    {
        assembly = Assembly.Load(assemblyName); // it could load the resoruce dll, ok.
    }
    catch (ThreadAbortException)
    {
        throw;
    }
    catch (System.Exception)
    {
    }
}

if (assembly != null)
{
    string resourceFileName = GetResourceFileName(culture);
    Type type = m_type;
    Stream manifestResourceStream = assembly.GetManifestResourceStream(type, resourceFileName); // it could not find steams.
    object[] args = new object[1] { manifestResourceStream };
    if (manifestResourceStream != null)
    {
        try
        {
            CultureInfo invariantCulture = CultureInfo.InvariantCulture;
            resourceSet = (ResourceSet)Activator.CreateInstance(ResourceSetType, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance, null, args, invariantCulture, null);
        }
        catch (System.Exception)
        {
            resourceSet = new ResourceSet(manifestResourceStream);
        }
    }
}

 

So I tried to load the resourceset which be alone in the dll, changing the code ...

 

    Stream manifestResourceStream = assembly.GetManifestResourceStream(type, resourceFileName); // it could not find steams.

 

like the following (experiment code, not for product ones):

 

    Stream manifestResourceStream = null;
    try
    {
        // get all resoruced names.
        var names = assembly.GetManifestResourceNames();
        // get the first one.
        manifestResourceStream = assembly.GetManifestResourceStream(names.FirstOrDefault());
    }
    catch {}​

 

then it could load the localized names for vanilla ones(yay!):

 

Culture    : 日本語 (日本)(Japanese (Japan), ja-JP)
UICulture  : 日本語 (日本)(Japanese (Japan), ja-JP)
Localized name of         AcDbPolyline:                ポリライン
Localized name of       AcDb2dPolyline:             2D ポリライン
Localized name of           AcDbCircle:                    円
Localized name of           AcDbSpline:                スプライン
Localized name of          AcDbEllipse:                   楕円
Localized name of       AcDb3dPolyline:             3D ポリライン
Localized name of         AecDbPolygon:              Polygon
Localized name of      AcDbPolygonMesh:            ポリゴン メッシュ
Localized name of          AcDbSurface:                サーフェス
Localized name of     AeccDbSurfaceTin:           TinSurface
Localized name of     AeccDbVAlignment:              Profile
Localized name of   AeccDbGraphProfile:          ProfileView​

 

Still be needed to do some research with Aec, Aecc typed ones.

 

The above was just a quick report.



Of course, I'm looking and using your code too.
The results is same above.

It's great and more safety, natural and smarter way.
Extensioning Type objects, and load the resource more meaningfull way:

 

// [Localization.cs (line 78-79)]
static LenientResourceManager manager =
    new LenientResourceManager(typeof(Entity));

 

 
I perhaps understood the topic a little better, now. (resourceset, something like specifications, reflectioning, etc.)
Thanks alot. It was very informative.

 

(*) vailla : means no modification, original base one.(here indicating to acdbmgd.resources.dll)
Message 7 of 8

Thanks for taking to tuckling this complications.

 

I determined that this answer can resolve about the vanilla type, if there are localizations in the resources.

 

Here is my new Question: 
How to get the localize type names in Civil3D/Map Entities?

https://forums.autodesk.com/t5/civil-3d-customization/how-to-get-the-localize-type-names-in-civil3d-...

 

And I've also introduced your project too.

Message 8 of 8

Glad it helped.

 

I think if you take a look at some of the other *.resources.dll files in the same folder, you might find the ones used by AutoCAD verticals, but I strongly doubt they are using the same conventions used by the vanilla resources (they use the prefixes "Class.", "Property.", "Category.", and "Enum." for each of those four types of localized elements.

 

I'll enable discussions on the Localization repo in case anyone wants to share their experiences with the code or with trying to adapt it for use with industry solutions.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Technology Administrators


Autodesk Design & Make Report