Resources embedded in several Satellite Assembly's - c#

I know there are several questions about satellite Assembly's out there, but I still have some troubles when trying to implement them
My Goal is to separate each culture in a single assembly, giving me the flexibility to only re-compile one assembly at a time when required, I wouldn't like to compile all languages if I only want to make some minor changes to just one language
I want to fully understand how satellite Assembly's work. This is my current understanding:
Satellite Assembly's can be dropped in the bin directory without the need to recompile the whole application, which means the app does not require these assemblies in order to work correctly (as long as the fallback resource is specified)
S.A. has to be linked to a single culture
Now I can generate a S.A. using the al tool or Visual Studio can do it for me if I add the .resx files, the S.A. are created (in the bin folder I can see the folder structure for the specified cultures)
Question 1. Are these dll's equivalent, the one's generated by Visual Studio and the one's generated with the al tool?
Question 2. Do all these dll's must share a common name in order to work?? (I know for consistency they should, but if they do not share the name let's say creating them with the al tool specifying different names) can they still be recognized by the .Net framework to be loaded?
Question 3. If I want to use the ResourceManager class, do I have to instantiate one instance for each assembly-culture? (and since they contain the culture in their name, they have different assembly names, do I have to manually format the embedded resource file to load to match the current culture and load that assembly manually? Since they would probably not be loaded the first time, do I have to load it manually by specifying the file path inside the culture folder?)
Question 4. Are these S.A. loaded automatically by the .Net framework or do I have to explicitly load them?
Question 5. In case I have to load them, this means if I want to specify the resources declaratively in my control tags, do I have to create and register a custom resources factory in order to load them?
Question 6. If I add several resources for several cultures in a class project in visual studio with no code, they are automatically embedded, when I compile, the satellite assemblies for each culture are created, are these dll's related? I wonder if they are related by name, namespace or something
All these questions are based on this: I thought I would be able to just add the assemblies to the bin folder and specify something like a global assembly in the ResourceManager, and it would load automatically the resources even when they'd be in a different assembly (that's why my concern about the assembly names or how several satellite assemblies are related) just like when you define resources in a single assembly, you just call the Resources.MyResourceKey and that's it
I appreciate your hellp, this topic is driving me crazy =(

I know the resgen and al tools look a bit more complicated than they ought to be, but I am pretty sure that your goal is achievable, i.e. you can build your satellite assemblies outside of a Visual Studio solution (to save you having to maintain all your translated resources in your solution) but get exactly the same results.
Question 1: Yes
Question 2: Yes, the ResourceManager depends on the naming convention to load localized resources
Question 3: No. You can use the standard ResourceManager. In fact you don't have to use ResourceManager directly at all. Include your base resources in your solution and configure it with Build Action = Embedded Resource and Custom Tool = PublicResXFileCodeGenerator, then Visual Studio will automatically generate and maintain a class (with the same name as your resx file) that lets you access resources through static properties. Depending on Thread.CurrentThread.CurrentUICulture and on what satellite assemblies are deployed, these properties will either give you localized resources from a satellite assembly or from your base assembly.
Question 4: No. The ResourceManager does that automatically.
Question 5: See Q4. Nothing to do.
Question 6: See Q2. It's based on a naming convention (folder named as per the culture, satellite assembly named as per the base dll) and metadata (e.g. culture name used when compiling using al)

Related

Programatically add ressource file to ANOTHER project

I need to create resource files (.resx) programatically. Those ressource files are meant for
other projects, not for the one where I programatically create those resx files.
Is there a clean way to tell the other solutions/projects that it has to add the externally created ressource file.
In the resoure files strings are stored which shall be used in that project later.
Example:
I create a Resource.resx file with a project called ResourceCreator.
Now I have to tell a Project called MyProject to bind it into the solution/project WITHOUT to have to manually open the solution and add it then.
I believe you really want to create Satellite Assemblies.
A satellite assembly is a compiled library (DLL) that contains
(“localizable”) resources such as strings, bitmaps, etc. You are
likely to use them when creating a multilingual (UI) application.
Satellite assemblies provide you with the capability of designing and
deploying your solution to multiple cultures, rather than hard coding
strings, bitmaps, etc., into your main application. Satellite
assemblies are used to deploy applications in multiple cultures (not
languages), with 1 satellite assembly per culture - this is the
default behavior, but you can obviously have more granular control if
you handle the build process manually.
MSDN Ref - Introduction to Satellite Assemblies
Try looking into the Microsoft.Build namespace, it has all sorts of fancy ways of loading/modifying project and solution files.
Microsoft.Build namespaces

ASP.NET, reference a local resource files from a class library

I'm having a hard time wrapping my mind around how the resources work in an ASP.NET project. In my solution I have 2 projects:
A Website project that contains an App_GlobalResources folder where most of my resources reside (build action: "content").
A utility project, compiled as class library (that the website references), where I need another set of resources (called "ExceptionMessages" hereafter).
Nota: each project uses its own resources, there is no cross linking involved here.
I added a new resource directly in my second project (both as "content" or "embeded resource") and tried to access it via ExceptionMessages.Tag. Intellisense is happy about it and correctly suggests the Tag but I get the following error at runtime:
"Could not find any resources appropriate for the specified culture or
the neutral culture. Make sure
"Resources.ExceptionMessages.resources" was correctly embedded or
linked into assembly "App_GlobalResources.lqgd5zqy" at compile time,
or that all the satellite assemblies required are loadable and fully
signed."
Somehow, I don't understand why the webserver is looking for the resource in the App_GlobalResources instead of directly in the assembly where it is used... or how I could go around this in a clean way. Any hint or idea?
Thanks!

How do I create a neutral fallback satellite resource for localization in C#.?

I am trying to modify an existing WPF application for localization. One of my restrictions is that all resources (including the default en-US fallback resource) have to be satellite resources. None of them can be embedded in the main assembly.
Through some reading on codeplex, I found that all I need to do is make the following change in my AssemblyInfo.cs file.
[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
When I do this, I get an exception on application startup saying:
Could not find any resources
appropriate for the specified culture
or the neutral culture. Make sure
"XXX.YYY.ZZZ.g.en-US.resources"
was correctly embedded or linked into
assembly "XXX.YYY.ZZZ"
at compile time, or that all the
satellite assemblies required are
loadable and fully signed.
If I switch back to putting the fallback resource (en-US) in the main assembly, everything works fine again. From what I've read, if you want to have the fallback resource as a satellite resource, you must have a specific resource for that locale (i.e. you must have a Resources.en-US.resx). Am I correct?
What seems fishy to me in the exception is the g (.g.en-US). Does that signify something? The g doesn't exist in any of my resource files?
Am I creating my resources correctly. I first create a generic Resources.resx file, put all my strings in it. Then, I simply copy/paste within Visual Studio and rename for each specific locale (ar, es-MX, etc.). Then, make the appropriate translations.
For now, I am just focusing on one projects resource files. Maybe it's getting hung up on a different project that doesn't have an en-US version of it's resource file?? If so, is there any way to get Visual Studio to tell me more about what it's really getting hung up on?
In short, your understanding of the resource files and what needs built seems correct.
The .g is for the compiled XAML files and built for you automatically. Nothing special or magical going on here.
Normally the fallback resource is embedded in the main assembly. If you are not doing that, you must create a satellite resource assembly for every culture you are going to do, plus an en-US one.

Embedding assemblies inside another assembly

If you create a class library that uses things from other assemblies, is it possible to embed those other assemblies inside the class library as some kind of resource?
I.e. instead of having MyAssembly.dll, SomeAssembly1.dll and SomeAssembly2.dll sitting on the file system, those other two files get bundled in to MyAssembly.dll and are usable in its code.
I'm also a little confused about why .NET assemblies are .dll files. Didn't this format exist before .NET? Are all .NET assemblies DLLs, but not all DLLs are .NET assemblies? Why do they use the same file format and/or file extension?
ILMerge does merge assemblies, which is nice, but sometimes not quite what you want. For example, when the assembly in question is a strongly-named assembly, and you don't have the key for it, then you cannot do ILMerge without breaking that signature. Which means you have to deploy multiple assemblies.
As an alternative to ilmerge, you can embed one or more assemblies as resources into your exe or DLL. Then, at runtime, when the assemblies are being loaded, you can extract the embedded assembly programmatically, and load and run it. It sounds tricky but there's just a little bit of boilerplate code.
To do it, embed an assembly, just as you would embed any other resource (image, translation file, data, etc). Then, set up an AssemblyResolver that gets called at runtime. It should be set up in the static constructor of the startup class. The code is very simple.
static NameOfStartupClassHere()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(Resolver);
}
static System.Reflection.Assembly Resolver(object sender, ResolveEventArgs args)
{
Assembly a1 = Assembly.GetExecutingAssembly();
Stream s = a1.GetManifestResourceStream(args.Name);
byte[] block = new byte[s.Length];
s.Read(block, 0, block.Length);
Assembly a2 = Assembly.Load(block);
return a2;
}
The Name property on the ResolveEventArgs parameter is the name of the assembly to be resolved. This name refers to the resource, not to the filename. If you embed the file named "MyAssembly.dll", and call the embedded resource "Foo", then the name you want here is "Foo". But that would be confusing, so I suggest using the filename of the assembly for the name of the resource. If you have embedded and named your assembly properly, you can just call GetManifestResourceStream() with the assembly name and load the assembly that way. Very simple.
This works with multiple assemblies, just as nicely as with a single embedded assembly.
In a real app you're gonna want better error handling in that routine - like what if there is no stream by the given name? What happens if the Read fails? etc. But that's left for you to do.
In the rest of the application code, you use types from the assembly as normal.
When you build the app, you need to add a reference to the assembly in question, as you would normally. If you use the command-line tools, use the /r option in csc.exe; if you use Visual Studio, you'll need to "Add Reference..." in the popup menu on the project.
At runtime, assembly version-checking and verification works as usual.
The only difference is in distribution. When you deploy or distribute your app, you need not distribute the DLL for the embedded (and referenced) assembly. Just deploy the main assembly; there's no need to distribute the other assemblies because they're embedded into the main DLL or EXE.
Take a look at ILMerge for merging assemblies.
I'm also a little confused about why .NET assemblies are .dll files. Didn't this format exist before .NET?
Yes.
Are all .NET assemblies DLLs,
Either DLLs or EXE normally - but can also be netmodule.
but not all DLLs are .NET assemblies?
Correct.
Why do they use the same file format and/or file extension?
Why should it be any different - it serves the same purpose!
You can embed an assembly (or any file, actually) as a resource (and then use the ResourceManager class to access them), but if you just want to combine assemblies, you're better off using a tool like ILMerge.
EXE and DLL files are Windows portable executables, which are generic enough to accomodate future types of code, including any .NET code (they can also run in DOS but only display a message saying that they're not supposed to run in DOS). They include instructions to fire up the .NET runtime if it isn't already running. It's also possible for a single assembly to span across multiple files, though this is hardly ever the case.
Note ILMerge doesn't work with embedded resources like XAML, so WPF apps etc will need to use Cheeso's method.
There's also the mkbundle utility offered by the Mono project
Why do they use the same file format and/or file extension?
Why should it be any different - it serves the same purpose!
My 2¢ bit of clarification here: DLL is Dynamic Link Library. Both the old style .dll (C-code) and .net style .dll are by definition "dynamic link" libraries. So .dll is a proper description for both.
With respect to Cheeso's answer of embedding the assemblies as resources and loading them dynamically using the Load(byte[]) overload using an AssemblyResolve event handler, you need to modify the resolver to check the AppDomain for an existing instance of the Assembly to load and return the existing assembly instance if it's already loaded.
Assemblies loaded using that overload do not have a context, which can cause the framework to try and reload the assembly multiple times. Without returning an already loaded instance, you can end up with multiple instances of the same assembly code and types that should be equal but won't be, because the framework considers them to be from two different assemblies.
At least one way that multiple AssemblyResolve events will be made for the same assembly loaded into the "No context" is when you have references to types it exposes from multiple assemblies loaded into your AppDomain, as code executes that needs those types resolved.
https://msdn.microsoft.com/en-us/library/dd153782%28v=vs.110%29.aspx
A couple of salient points from the link:
"Other assemblies cannot bind to assemblies that are loaded without context, unless you handle the AppDomain.AssemblyResolve event"
"Loading multiple assemblies with the same identity without context can cause type identity problems similar to those caused by loading assemblies with the same identity into multiple contexts. See Avoid Loading an Assembly into Multiple Contexts."
I would suggest you to try Costura.Fody. Just don't forget to Install-Package Fody before Costura.Fody (in order to get the newest Fody!)

Globalizing runtime generated assemblies

Background
A project installs some files that contain all the elements to define a UserControl - some user source, a CodeCompileUnit for designer code, and a resx file. At runtime, these files are compiled into an assembly and the classes are consumed by our main application (the assembly is only updated when necessary).
Question
The project has to be globalized and as part of that process, there is a need to provide localizations of these files. Two options are either to allow the inclusion of additional resx files for different locales (either within the same files or as additional side-by-side files) that can be compiled into a satellite assembly for the main assembly, or to provide a copy of each full file for each supported language, compiling the appropriate set for the language being supported.
Does anyone have any other options that might be worth considering?
What problems might be inherent in either of the solutions I've proposed?
Constraints/Disclaimer
I am aware that the scenario is less than ideal and that better choices could've been made in some areas (like globalizing from the start), but they cannot be changed at this point in the project. I appreciate any advice, solutions, or leads you can provide. Thanks.
Create a separate satellite assembly for each culture. This has two benefits:
You can build all of the assemblies in one go, and have a definitive file for each version number and filename combination, rather than it also depending on the culture.
You can have multiple assemblies in the same installation, and base the language to use on the system language, or a user preference etc. This will make development and testing significantly easier, as you won't need to keep rebuilding and copying files around just for the sake of changing languages.
It's how .NET i18n is designed to work. While I'm not an expert on .NET i18n ("read Guy Smith-Ferrier's book" is my best advice!) I generally find that frameworks work best when you follow their expected model.
Even if the final part of "building the satellite assembly" is done at runtime (can you do it at install time instead?) you still get the second and third bullet advantages at least. It also means that if you ever do go the more normal route of supplying the satellite assemblies to start with (instead of building them on the user's box) you'll have less to change.
Apologies if I've misunderstood the question though...
If you're not planning on adding additional languages after deployment (at least not without a software update), then I'd favor compiling all the additional RESX files into a satellite assembly that you include. That way, they're not user editable once they're deployed.

Categories

Resources