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.
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.
Solved! Go to Solution.
Solved by ActivistInvestor. Go to Solution.
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}");
}
}
}
Thanks for your insightful answer.
> PromptEntityOptions.AddAllowedClass()
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
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.
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.
Program Files\autodesk\autocad 2022\ja-JP\acdbmgd.resources.dll
-> Resources
-> Autodesk.AutoCAD.DatabaseServices.Entity.ja-JP.resources
Architechture/Map
Program Files\autodesk\autocad 2022\ACA\ja-JP\
Civil
Program Files\autodesk\autocad 2022\C3D\ja-JP\
// [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.
Stream manifestResourceStream = null;
try
{
// get all resoruced names.
var names = assembly.GetManifestResourceNames();
// get the first one.
manifestResourceStream = assembly.GetManifestResourceStream(names.FirstOrDefault());
}
catch {}
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
// [Localization.cs (line 78-79)]
static LenientResourceManager manager =
new LenientResourceManager(typeof(Entity));
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?
And I've also introduced your project too.
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.