Mar 7 at 1:20 PM
Edited Mar 7 at 4:12 PM
|
In a somewhat divergent path from my previous endeavors, I've set up a service which downloads .resx files, converts them to a binary .resources file, then using al.exe to create an assembly file out of it then loading the assembly at runtime with Assembly.Load(). All of that seems to work nicely, but I have not been successful in actually getting this extension to see it.
I found this method: WPFLocalizeExtension.Providers.ResxLocalizationProvider.Instance.UpdateCultureList()
I believe this method may not do what I expect it to do because within this line:
var possiblePrefixes = new List<string>(assembly.GetTypes().Select((t) => t.Namespace).Distinct());
assembly.GetTypes() returns 0 types. I expected the VS-generated assemblies to pass this but it does not so that leads me to believe that I am approaching this from the wrong direction.
*edited for additional info
|
|
Mar 8 at 4:44 PM
Edited Mar 8 at 4:52 PM
|
So, I really just needed this to work so I made some modifications to ResxLocalizationProviderBase.cs to support loading loading generated satellite assemblies at runtime which have no connection to any assemblies already loaded. GetResourceManager() now makes a huge assumption that we know what we're doing and creates a ResourceManager when we are asking to update the culture list with a satellite assembly that doesn't have any Type, static Class, or Namespace. This is most likely highly egregious and I hope the developers can chime in with a proper way to do this, but here it is for anybody who may be desperate.
tl;dr inverted the IF where we check if foundResource == null so we skip over much of the logic that is not needed if we are just creating a new ResourceManager.
ResxLocalizationProviderBase.GetResourceManager
protected ResourceManager GetResourceManager(string resourceAssembly, string resourceDictionary) {
PropertyInfo propInfo;
MethodInfo methodInfo;
Assembly assembly = null;
ResourceManager resManager;
string foundResource = null;
string resManagerNameToSearch = "." + resourceDictionary + ResourceFileExtension;
string[] availableResources;
var resManKey = resourceAssembly + resManagerNameToSearch;
if (!TryGetValue(resManKey, out resManager)) {
// if the assembly cannot be loaded, throw an exception
try {
// go through every assembly loaded in the app domain
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assemblyInAppDomain in loadedAssemblies) {
// check if the name pf the assembly is not null
if (assemblyInAppDomain.FullName != null) {
// get the assembly name object
AssemblyName assemblyName = new AssemblyName(assemblyInAppDomain.FullName);
// check if the name of the assembly is the seached one
if (assemblyName.Name == resourceAssembly) {
// assigne the assembly
assembly = assemblyInAppDomain;
// stop the search here
break;
}
}
}
// check if the assembly is still null
if (assembly == null) {
// assign the loaded assembly
#if SILVERLIGHT
var name = new AssemblyName(resourceAssembly);
assembly = Assembly.Load(name.FullName);
#else
assembly = Assembly.Load(new AssemblyName(resourceAssembly));
#endif
}
} catch (Exception ex) {
throw new Exception(string.Format("The Assembly '{0}' cannot be loaded.", resourceAssembly), ex);
}
// get all available resourcenames
availableResources = assembly.GetManifestResourceNames();
// The proposed approach of Andras (http://wpflocalizeextension.codeplex.com/discussions/66098?ProjectName=wpflocalizeextension)
var possiblePrefixes = new List<string>(assembly.GetTypes().Select((t) => t.Namespace).Distinct());
for (int i = 0;
i < availableResources.Length;
i++) {
if (availableResources[i].EndsWith(resManagerNameToSearch)) {
var matches = possiblePrefixes.Where((p) => availableResources[i].StartsWith(p + "."));
if (matches.Count() != 0) {
// take the first occurrence and break
foundResource = availableResources[i];
break;
}
}
}
// NOTE: Inverted this IF (nesting is bad, I know) so we just create a new ResourceManager. -gen3ric
if (foundResource != null) {
// remove ".resources" from the end
foundResource = foundResource.Substring(0, foundResource.Length - ResourceFileExtension.Length);
//// Resources.{foundResource}.ResourceManager.GetObject()
//// ^^ prop-info ^^ method get
try {
// get the propertyinfo from resManager over the type from foundResource
var resourceManagerType = assembly.GetType(foundResource);
// check if the resource manager was found.
// if not, assume that the assembly was build with VisualBasic.
// in this case try to manipulate the resource identifier.
if (resourceManagerType == null) {
#if SILVERLIGHT
var assemblyName = resourceAssembly;
#else
var assemblyName = assembly.GetName().Name;
#endif
resourceManagerType = assembly.GetType(foundResource.Replace(assemblyName, assemblyName + ".My.Resources"));
}
propInfo = resourceManagerType.GetProperty(ResourceManagerName, ResourceBindingFlags);
// get the GET-method from the methodinfo
methodInfo = propInfo.GetGetMethod(true);
// get the static ResourceManager property
object resManObject = methodInfo.Invoke(null, null);
// cast it to a ResourceManager for better working with
resManager = (ResourceManager)resManObject;
} catch
(Exception
ex) {
// this error has to get thrown because this has to work
throw new InvalidOperationException("Cannot resolve the ResourceManager!", ex);
}
} else {
resManager = new ResourceManager(resManagerNameToSearch, assembly);
}
// if no one was found, exception
if (resManager == null) {
throw new ArgumentException(string.Format("No resource manager for dictionary '{0}' in assembly '{1}' found! ({1}.{0})", resourceDictionary, resourceAssembly));
}
// Add the ResourceManager to the cachelist
Add(resManKey, resManager);
try {
#if SILVERLIGHT
var cultures = CultureInfoHelper.GetCultures();
foreach (var c in cultures)
{
var dir = c.Name + "/";
foreach (var p in Deployment.Current.Parts)
if (p.Source.StartsWith(dir))
{
AddCulture(c);
break;
}
}
#else
var assemblyLocation = Path.GetDirectoryName(assembly.Location);
// Get all directories named like a specific culture.
var dirs = Directory.GetDirectories(assemblyLocation, "??-??").ToList();
// Get all directories named like a culture.
dirs.AddRange(Directory.GetDirectories(assemblyLocation, "??"));
// Get the list of all cultures.
var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
foreach (var c in cultures) {
var dir = Path.Combine(assemblyLocation, c.Name);
if (Directory.Exists(dir) &&
Directory.GetFiles(dir, "*.resources.dll").Length > 0) {
AddCulture(c);
}
}
#endif
} catch {
// This may lead to problems with Silverlight
}
}
// return the found ResourceManager
return resManager;
}
How I am generating an assembly and updating Localization:
var psi = new ProcessStartInfo(Path.Combine(workingDir, "al.exe")) {
CreateNoWindow = true,
RedirectStandardOutput = true,
UseShellExecute = false,
Arguments = args
};
var process = new Process {
StartInfo = psi
};
ThreadPool.QueueUserWorkItem(delegate {
process.Start();
while (!process.StandardOutput.EndOfStream) {
Debug.WriteLine(process.StandardOutput.ReadLine());
}
if (process.WaitForExit(15000)) {
// completed
Debug.WriteLine("Completed");
var asm = Assembly.LoadFrom({{path to resources assembly}}));
// NOTE: These ResourceAssembly & ResourceDictionary strings might be a little confusing for the ignorant. -gen3ric
WPFLocalizeExtension.Providers.ResxLocalizationProvider.Instance.UpdateCultureList("Namespace.resources", "Localization.UI.{0}".FormatWith(file.LanguageTag));
} else {
// timeout
Debug.WriteLine("Timeout");
}
});
|
|
Coordinator
Mar 15 at 8:20 AM
|
|
|
Coordinator
Apr 5 at 10:13 AM
|
Hi GEN3RIC,
I've built in your addition. Thank you.
Uwe
|
|
|
|
Ergh, sorry for not following up. Not being fully versed in the project, I am curious to know if this passes any unit tests you have for this. I have only utilized it in my own capacity so far.
|
|
Coordinator
Apr 5 at 1:14 PM
|
As far as it is an addition to the existing code it does not hit the usual test cases.
I would appreciate if you could break down your specific test case to a simple demo app that we can include in our tests.
|
|