The .NET Framework uses a hub and spoke model to package and deploy resources. The hub is the main assembly that contains the nonlocalizable executable code and the resources for a single culture, called the neutral or default culture. The default culture is the fallback culture for the application. Each spoke connects to a satellite assembly that contains the resources for a single culture, but does not contain any code.
There are several advantages to this model:
You can incrementally add resources for new cultures after you have deployed an application. Because subsequent development of culture-specific resources can require a significant amount of time, this allows you to release your main application first, and deliver culture-specific resources at a later date.
You can update and change an application's satellite assemblies without recompiling the application.
An application needs to load only those satellite assemblies that contain the resources needed for a particular culture. This can significantly reduce the use of system resources.
However, there are also disadvantages to this model:
You must manage multiple sets of resources.
The initial cost of testing an application increases, because you must test several configurations. Note that in the long term it will be easier and less expensive to test one core application with several satellites, than to test and maintain several parallel international versions.
When you package your application's resources, you must name them using the resource naming conventions that the common language runtime expects. The runtime identifies a resource by its culture signature, or name. Each culture is given a unique name, which is a combination of a two-digit lowercase culture name associated with a language and, if required, a two-digit uppercase subculture name associated with a country or region. The subculture name follows the culture name, separated by a dash (-). Examples include ja-JP for Japanese in Japan, en-US for US English, or de-DE for German in Germany (as opposed to an alternate such as de-AT for German in Austria). See the CultureInfo class for a complete list of culture names.
The hub and spoke model for packaging and deploying resources uses a fallback process to locate appropriate resources. If an application user requests a ResourceSet that is unavailable, the common language runtime searches the hierarchy of cultures looking for an appropriate fallback resource that most closely matches the user's request, and raises an exception only as a last resort. At each level of the hierarchy, if an appropriate resource is found, the runtime uses it. If the resource is not found, the search continues at the next level. The resource fallback process is described in the following steps:
The runtime first checks the global assembly cache for an assembly matching the requested culture for your application.
The global assembly cache can store resource assemblies that are shared by many applications. This frees you from having to include specific sets of resources in the directory structure of every application you create. If the runtime finds a reference to the assembly, it searches the assembly for the requested resource. If it finds the entry in the assembly, it uses the requested resource. If it does not find the entry, it continues the search.
The runtime next checks the directory of the currently executing assembly for a directory matching the requested culture. If it finds the directory, it searches that directory for a valid satellite assembly for the requested culture. The runtime then searches the satellite assembly for the requested resource. If it finds the resource in the assembly, it uses it. If it does not find the resource, it continues the search.
The runtime next searches the global assembly cache again, this time for the parent assembly of the requested resource. If the parent assembly exists in the global assembly cache, the runtime searches the assembly for the requested resource.
The parent is defined as the appropriate fallback culture. Consider parents as best-fit candidates; providing any resource is preferable to throwing an exception. This process also allows you to reuse resources. You need to include a particular resource at the parent level only if the child culture does not need to localize the requested resource. For example, if you supply satellite assemblies for en (neutral English), en-GB (English as spoken in the UK), and en-US (English as spoken in the US), the en satellite would contain the common terminology, and the en-GB and en-US satellites could provide overrides for only those terms that differ.
The runtime next checks the directory of the currently executing assembly to see if it contains a parent directory. If a parent directory exists, the runtime searches the directory for a valid satellite assembly for the parent culture. If it finds the assembly, the runtime searches the assembly for the requested resource. If it finds the resource, it uses it. If it does not find the resource, it continues the search.
The runtime next searches parent assemblies, as in the previous step, through many potential levels. Each culture has only one parent, but a parent might have its own parent.
If the culture that was originally specified and all parents have been searched and the resource is still not found, the resource for the default (fallback) culture is used.
Note The default resource is the only resource that is compiled with the main assembly. It is the absolute fallback (final parent). Therefore, it is strongly suggested that you always include a default set of resources in your main assembly. This helps to ensure that exceptions are not thrown. By including a default resource file you provide a fallback for all resources, and ensure that at least one resource is always present for the user, even if it is not culturally specific.
Finally, if the runtime does not find a resource for a default (fallback) culture, an exception is thrown indicating that the resource could not be found.
As an example of how the search for a requested resource is conducted, suppose the user requests a resource localized for Mexican Spanish. In accordance with the resource naming conventions described above, the runtime first searches the global assembly cache for the assembly matching the requested culture, "es-MX". Not finding it, the runtime then searches the directory of the currently executing assembly for an "es-MX" directory. Failing that, the runtime searches the global assembly cache again for a parent assembly reflecting the appropriate fallback culture — in this case, "es" (Spanish). If the parent assembly is not found, the runtime searches all potential levels of parent assemblies for the "es-MX" culture until a corresponding resource is found. If a resource is not found, the runtime uses the resource for the default culture.
Due to time or budget constraints, it might not be feasible for you to create a set of resources for every subculture that your application supports. In this situation, you can create a single satellite assembly for a parent culture that can be used by all related subcultures. For example, you could provide a single English satellite assembly (en) that would be retrieved by users requesting region-specific English resources, and a single German satellite assembly (de) for users requesting region-specific German resources. For example, requests for German in Germany (de-DE), German in Austria (de-AT), and German in Switzerland (de-CH) would fall back to the German satellite assembly (de). Carefully select the default resources to compile with the main assembly. The default resources are the final fallback and therefore should be the resources that will be requested by the majority of your application's users. Although this solution deploys resources that are less culturally specific, it can significantly reduce your application's localization costs.