📜 ⬆️ ⬇️

Silverlight Localization

Localization once comes to your international home. Whatever you build - a big skyscraper or Uncle Tom's hut - you must be able to speak the language of the inhabitants of this house.

If your Silverlight house needs localization, you are welcome, I will try to give paints and a brush, and draw the posters yourself.

Problem.


There are no problems in the use of resources for developing a desktop application, it has long been possible to create ResX files and use secondary (satellite) assemblies for a specific language. Silverlight inherited these functions, but unlike desktop applications, you must remember that increasing the number of localized resources increases the size of the assembly. So it turns out that the user is waiting for the download, and some of the downloaded information is not needed at all. On the other hand, I’m talking about the server, the larger the file, the greater the load on the server. Imagine, you need to fix the resources, which means that you need to once again collect and upload the full assembly and users will download it again, because it will not be possible to pull out the cache. And the resources would lie separately went, changed and that's it.
To summarize: any extra assembly resource requires extra time for the user, server, developer.

Overview


There are common approaches that I use in my decision and with which I would like to acquaint you.
')
Creating a resource is simple, but still briefly about it:
- add a new item from the context menu on the desired folder in Solution Explorer (Add -> New Item);
- select the tab General;
- select the item Resources Files (or look for the item with the same name in the full list);
- enter the name - ProjectResources;
- click add (or just press Enter).
A resource has been created in the required folder. Open the resource file designer by double clicking on the file in Solution Explorer. In the appeared grid, in the first column we write the name of our resource - “Message”, in the next column we enter the value - “Hello World!”.
Sure to! do not forget to make our resource Public, for which, in the Access Modifier, change the state Internal to Public.

With this, the creation of the main (neutral) resource ends and we can proceed to the next part of the “Merlezonsky ballet” - the creation of a provider for resources. This approach has already been mentioned in the topic Programming Reversi in Silverlight and it consists in creating a class with a set of properties that are resources.

public class ResProvider : INotifyPropertyChanged { static ProjectResources resources = new ProjectResources(); public ProjectResources ProjectResources { get { return resources; } } #region INotifyPropertyChanged Members // Implemention of interface. #endregion } * This source code was highlighted with Source Code Highlighter .
  1. public class ResProvider : INotifyPropertyChanged { static ProjectResources resources = new ProjectResources(); public ProjectResources ProjectResources { get { return resources; } } #region INotifyPropertyChanged Members // Implemention of interface. #endregion } * This source code was highlighted with Source Code Highlighter .
  2. public class ResProvider : INotifyPropertyChanged { static ProjectResources resources = new ProjectResources(); public ProjectResources ProjectResources { get { return resources; } } #region INotifyPropertyChanged Members // Implemention of interface. #endregion } * This source code was highlighted with Source Code Highlighter .
  3. public class ResProvider : INotifyPropertyChanged { static ProjectResources resources = new ProjectResources(); public ProjectResources ProjectResources { get { return resources; } } #region INotifyPropertyChanged Members // Implemention of interface. #endregion } * This source code was highlighted with Source Code Highlighter .
  4. public class ResProvider : INotifyPropertyChanged { static ProjectResources resources = new ProjectResources(); public ProjectResources ProjectResources { get { return resources; } } #region INotifyPropertyChanged Members // Implemention of interface. #endregion } * This source code was highlighted with Source Code Highlighter .
  5. public class ResProvider : INotifyPropertyChanged { static ProjectResources resources = new ProjectResources(); public ProjectResources ProjectResources { get { return resources; } } #region INotifyPropertyChanged Members // Implemention of interface. #endregion } * This source code was highlighted with Source Code Highlighter .
  6. public class ResProvider : INotifyPropertyChanged { static ProjectResources resources = new ProjectResources(); public ProjectResources ProjectResources { get { return resources; } } #region INotifyPropertyChanged Members // Implemention of interface. #endregion } * This source code was highlighted with Source Code Highlighter .
  7. public class ResProvider : INotifyPropertyChanged { static ProjectResources resources = new ProjectResources(); public ProjectResources ProjectResources { get { return resources; } } #region INotifyPropertyChanged Members // Implemention of interface. #endregion } * This source code was highlighted with Source Code Highlighter .
  8. public class ResProvider : INotifyPropertyChanged { static ProjectResources resources = new ProjectResources(); public ProjectResources ProjectResources { get { return resources; } } #region INotifyPropertyChanged Members // Implemention of interface. #endregion } * This source code was highlighted with Source Code Highlighter .
  9. public class ResProvider : INotifyPropertyChanged { static ProjectResources resources = new ProjectResources(); public ProjectResources ProjectResources { get { return resources; } } #region INotifyPropertyChanged Members // Implemention of interface. #endregion } * This source code was highlighted with Source Code Highlighter .
  10. public class ResProvider : INotifyPropertyChanged { static ProjectResources resources = new ProjectResources(); public ProjectResources ProjectResources { get { return resources; } } #region INotifyPropertyChanged Members // Implemention of interface. #endregion } * This source code was highlighted with Source Code Highlighter .
  11. public class ResProvider : INotifyPropertyChanged { static ProjectResources resources = new ProjectResources(); public ProjectResources ProjectResources { get { return resources; } } #region INotifyPropertyChanged Members // Implemention of interface. #endregion } * This source code was highlighted with Source Code Highlighter .
  12. public class ResProvider : INotifyPropertyChanged { static ProjectResources resources = new ProjectResources(); public ProjectResources ProjectResources { get { return resources; } } #region INotifyPropertyChanged Members // Implemention of interface. #endregion } * This source code was highlighted with Source Code Highlighter .
  13. public class ResProvider : INotifyPropertyChanged { static ProjectResources resources = new ProjectResources(); public ProjectResources ProjectResources { get { return resources; } } #region INotifyPropertyChanged Members // Implemention of interface. #endregion } * This source code was highlighted with Source Code Highlighter .
  14. public class ResProvider : INotifyPropertyChanged { static ProjectResources resources = new ProjectResources(); public ProjectResources ProjectResources { get { return resources; } } #region INotifyPropertyChanged Members // Implemention of interface. #endregion } * This source code was highlighted with Source Code Highlighter .
public class ResProvider : INotifyPropertyChanged { static ProjectResources resources = new ProjectResources(); public ProjectResources ProjectResources { get { return resources; } } #region INotifyPropertyChanged Members // Implemention of interface. #endregion } * This source code was highlighted with Source Code Highlighter .

Used in markup this way:

  1. < UserControl >
  2. < UserControl.Resources >
  3. < Resources: ResProvider x: Key = "ResProvider" />
  4. </ UserControl.Resources >
  5. < Grid >
  6. < TextBlock Text = "{Binding Source = {StaticResource ResProvider}, Path = ProjectResources.Message}" />
  7. </ Grid >
  8. </ UserControl >
* This source code was highlighted with Source Code Highlighter .

Moreover, if you need to use this provider for the entire application, you simply define it at the Application level, that is, in App.xaml.

The main resource is ready, the provider is there, now we need to expand the resource with additional languages.

For those who asked "How?":
- call the context menu on the desired folder in Solution Explorer (Add -> New Item);
- select the General tab;
- create Resources Files with the name of the main resource (in our case ProjectResources);
- at the end add the name of the desired culture - ProjectResources.ru-Ru.resx;
- click Add.
This will create a file with the desired name. We open the resource in the designer, in the first column we write the name coinciding with the base resource, in our case it is “Message”, the second column is filled with the translated value - “Hello World!”.

I did not invent anything new; in fact, with my post I described two documents:
  1. How to: Add Resources to a Silverlight-based Application
  2. Silverlight and localized string data
For those who carefully read the first (or have already encountered it), from the above documents, I will say that I specifically kept silent about adding a special section <SupportedCultures /> to the xml project that defines the list of language assemblies necessary for inclusion in the compiled xap file. If you add this section, then you will not get away from the problem with which I started the story - from using extra resources.

Decision


The method that I developed, unfortunately, is not suitable for those who want to enable support of lazy (lazy) resource loading by one state of the checkbox. Oh how spun. In short - you have to work hard to drain the pond and get to the desired fish.

First, download the latest version of the ResourceExtension library from the codeplex and add it to the references to the Silverlight project.

Creating resources does not differ from the above, so I will omit it. The only thing required for resources is to add a pre-build event. Call the project's Silverlight properties. Open the Build Events tab. In Pre-build we bring:
@ECHO off
DEL "$(TargetPath)"
FOR /F "delims=" %%i IN ( 'findstr /M /L /S /C:"global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager" "$(ProjectDir)*.Designer.cs"' ) DO (
DEL resource.gen
FOR /F "usebackq delims=" %%j IN ( "%%i" ) DO (
ECHO. %%j > resourcecurrentrow.gen
FOR /F "tokens=1,2* delims=(=" %%k IN (resourcecurrentrow.gen) DO (
IF "%%l" == " new global::System.Resources.ResourceManager" (ECHO %%k = new ResourceExtension.ResourceXManager(%%m)>>resource.gen ELSE (ECHO. %%j) >>resource.gen
))
XCOPY resource.gen "%%i" /Q /Y /R /K
)

* This source code was highlighted with Source Code Highlighter .

This action, automatically, in all project resources, replaces the ResourceManager class with the ResourceXManager implemented in the ResourceExtension library.

Unfortunately, this code does not work without the additional section <UseHostCompilerIfAvailable> FALSE </ UseHostCompilerIfAvailable> in the project file. You need to add this code to the <PropertyGroup> section.

Markup has not undergone any changes, everything remained in its place.

But the provider has changed, now he is the heir to the CultureResourceProvider class and has acquired metadata on the properties:
  1. using ResourceExtension;
  2. using ResourceExtension.Implementation;
  3. namespace ResourceExtensionExample.Resources
  4. {
  5. public class ResProvider: CultureResourcesProvider
  6. {
  7. private static ProjectResources projectResources = new ProjectResources ();
  8. [ResourceProperty]
  9. public ProjectResources ProjectResources
  10. {
  11. get { return projectResources; }
  12. }
  13. }
  14. }
* This source code was highlighted with Source Code Highlighter .

The final chord. We collect. We enable the checkbox to view all files and find the bin \ Debug \ ru-RU folder, which we need to move to the ClientBin folder and put next to the xap file.
So it should turn out
And, "easy transformation into elegant shorts":
  1. public partial class MainPage: UserControl
  2. {
  3. public MainPage ()
  4. {
  5. Initializecomponent ();
  6. }
  7. private void Switch_Click ( object sender, RoutedEventArgs e)
  8. {
  9. ((ResProvider) Resources [ "ResProvider" ]). Options.CultureInfo = new CultureInfo ( "en-Ru" );
  10. }
  11. }
* This source code was highlighted with Source Code Highlighter .

allows you to conveniently download resources from the Russian locale.

If you have, something did not work out. You can download an example and see how everything is implemented there.

The component is in alpha stage, so no complaints, you use it at your own risk.
In this implementation, only string resources work.

Why was this approach chosen? What's inside? How does it work? I will try to tell about it in the second article “Silverlight Resource Extension. Part 2. Descriptive ", which I plan to finish in the foreseeable future.

Source: https://habr.com/ru/post/87450/


All Articles