Tuesday, March 12, 2013

Share localization files between Windows Phone and Windows 8

    As I wrote in my previous post I am getting back with a post on how to share the localization files between Windows Phone and Windows 8. This should be a simple task, but in the current version of the Windows Phone SDK and Windows 8 it cannot be easily done. The target is to have only one of the two projects (Windows Phone or Windows 8) that manages the localized (in this sample I am only taking in consideration localized strings but the concept can be easily extended) and use it in the other project. Also I wanted to use the Multilingual App Toolkit to easily manage the translations inside the project.
    First let's see what we have:
  • Windows Phone + Multilingual App Toolkit
    1. The main resource file is called AppResources.resx and is inside the Resources folder 
    2. The AppResources.resx automatically generates the AppResources class
    3. The files generated by the Multilingual App Toolkit for the Windows Phone project are also inside the "\Resources" folder and the name look like AppResources.[culture].xlf
    4. We have a class called LocalizedStrings.cs that helps us with the binding inside xaml files
  • Windows 8 + Multilingual App Toolkit
    •  The main resource file is called Resources.resw and it is inside the \strings\[Default language] folder
    • The Resources.resw doesn't have .cs class in code behind generated (it uses the ResourceLoader class to load the localized values). We can also have values with "." like MainButton.Text to directly use inside xaml with the x:Uid.
    • The files generated by the Multilingual App Toolkit for Windows 8 are inside the "\MultilingualResources" folder and the names are [AppName]_[culture].xlf
    So we cannot directly use the files from one project to another.  There are a lot of differences but the couples (AppResources.resx, Resources.resw) and all the (AppResources.[culture].xlf, [AppName]_[culture].xlf) have different names but share very similar structure (they share most of the structure). 

     My approach and idea is to automatically generate the set of localized files needed by one solution directly from the set of files of the other project at compilation time. As source I have chosen the Windows Phone resource files (the other way around is more difficult as we could have resources not supported by Windows Phone). In order to automatically generate the resource files I have created a console application that would be run as a Post-build event on the Windows Phone project and write the necessary files to the Windows 8 project. the Windows 8 project build will be set to depend on the Windows Phone project this way we will always have updated resources when we run it. The console application needs three parameters/inputs: the folder of the Windows Phone project (source), the folder of the Windows 8 project (destination), and the name of the Windows 8 project as we will need it when we generate the [AppName]_[culture].xlf files for the Windows 8 project. The console application also automatically generates the AppResources.cs class inside the Windows 8 projects which is a class similar to the one used by Windows Phone. The source code for the Console Application is also included in case you need to do some changes and also the full sample.
     So let's see what would be the steps you will have to make if you want to use the solution.
  1. Create a Windows Phone application (in the attached sample the project WindowsPhoneApp). Using the Tools menu enable the Multilingual App Toolkit and using right-click on the project name select "Add translation languages..." to support more languages (in the sample is italian)
  2. Create the Windows 8 projects (in the attached sample the project called WindowsStoreApp). Create the the folder structure \strings\en and add the file Resources.resw to it. Inside the Package.appxmanifest set the default language as en. Now use the Tools menu and enable the Multilingual App Toolkit on the Windows 8 project and add the same languages that you have added to the Windows Phone Project. 
    If later you want to add another language support remember to add it to both Windows 8 and Windows Phone projects.
This is how our solution looks :
two different projects each of one with its localization part. Now we will set that the Windows 8 project build depends on the Windows Phone project and set the post-build action of the Windows Phone project to be:

$(SolutionDir)ConvertAppResources\$(OutDir)ConvertAppResources.exe "$(SolutionDir)WindowsPhoneApp" "$(SolutionDir)WindowsStoreApp" WindowsStoreApp

where ConvertAppResources is the project that converts the resources. We pass 3 parameters to the console application:
  1. the folder of the Windows Phone application which is the source - "$(SolutionDir)WindowsPhoneApp"
  2. the folder of the Windows 8 app which is the destination - "$(SolutionDir)WindowsStoreApp"
  3. the name of the Windows 8 app- WindowsStoreApp
In our case the console application will generate and overwrite Resources.resw, WindowsStoreApp_it.xlf and WindowsStoreApp_qps-ploc.xlf. It will also generate the file AppResources.cs in the Windows 8 application folder which you will have to include in the project after the first compilation of the Windows Phone application.
To keep things even more similar to Windows Phone method I have added the class:

 using Localization;  
 namespace WindowsStoreApp  
   /// <summary>  
   /// Provides access to string resources.  
   /// </summary>  
   public class LocalizedStrings  
     private static AppResources _localizedResources = new AppResources();  
     public AppResources LocalizedResources { get { return _localizedResources; } }  

and inside App.xaml added the resource:

       <local:LocalizedStrings x:Key="LocalizedStrings"/>  
           Styles that define common aspects of the platform look and feel  
           Required by Visual Studio project and item templates  
         <ResourceDictionary Source="Common/StandardStyles.xaml"/>  

This way you could easily bind in Windows Phone style and even reuse xaml and cs that you already have from your Windows Phone project:
Xaml Binding
  <Button Content="{Binding Path=LocalizedResources.ButtonText, Source={StaticResource LocalizedStrings}}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="517,302,0,0" Height="100" Width="283" Click="Button_Click"/>  
and/or code-behind:
 MessageDialog md = new MessageDialog(AppResources.ButtonMessage);  

If you want to know more on how the conversion works look inside Program.cs. The steps I do is save the Resx to Resw and generate the class AppResources.cs. After that for every .xlf file in the Windows Phone Resources folder  I generate and write the corresponding Windows 8 .xlf file (there are some conversions involved). If you want you don't have to add the project ConvertAppResources to your solution but only add the output .exe file to one of the projects and be very careful when you write the post-build action command.

Once everything is setup-ed you will only have to modify the Windows Phone resource strings and the Windows 8 ones will get updated. If you want to use specific Windows 8 resources you can add more resource files and use the ResourceLoader to load them - for example if you've added the file Errors.resw to the \strings\en folder of the Windows 8 project you will access it like this:

                var resourceLoader = new ResourceLoader("Errors");
                this.Scenario5TextBlock.Text = resourceLoader.GetString("InvalidOperation");

Be careful:

  • before adding the post-build action verify that the localization of both Windows Phone and Windows 8 project work (independent from each other) 
  • if the post-build command is not right the Windows 8 application will not get the updated resources when you compile the windows phone project
  • the Window 8 project has to depend on the Windows Phone project this way you will be sure that Windows 8 gets the updated resources
  • modify ONLY the Windows Phone .resx and .xlf files

Some limitations of the Multilingaul App Toolkit:

  • If you modify the Resx file and add a new resource you will have compile the project before you will see the new string in the .xlf files
  • If you are modifying the Resx file for a resource that you have already localized when you compile you will loose the localization and you will have to modify the .xlf files again.  

Let me know if you have problems understanding or making it work (I don't think that I was really good at explaining the process but the hack works).



No comments:

Post a Comment