
Being a developer of applications for Windows Phone, I realized long ago that the market for mobile applications is global and for successful promotion of an application, it must be properly localized for the maximum number of available languages. Localization of applications for Windows Phone is described in detail in the
application development documentation . However, the
process of localization of headers and headers of application tiles differs from the localization of the application itself and often causes difficulties, since it requires the preparation of a separate resource file for each language.
To solve this problem, I created several resource projects that, after a successful build, copied the result into the application directory. This solution completely suited me, while my applications supported no more than three languages. After the need to localize applications into more languages appeared, this approach became burdensome.
')
Search has shown that many are looking for ways to conveniently solve this problem. But I did not find a convenient way for myself; this led me to the decision to write a utility that will allow you to create and manage resource files. For me, the most convenient way was to describe the headers in the XML file and automatically generate the necessary files based on it.
For the application header description file, the following format would be selected:
<Localization> <Application> <Name /> <Description /> </Application> <Languages> <Default inactive="false"> <Title /> <TileTitle /> </Default> <Albanian inactive="true"> <Title /> <TileTitle /> </Albanian> … </Languages> </Localization>
Utility decided to create as a Custom Tool for Visual Studio

To implement an object that can be used as a Custom Tool, you must implement the
IVsSingleFileGenerator interface.
interface IVsSingleFileGenerator { int DefaultExtension(out string); int Generate(string, string, string, IntPtr[], out uint, IVsGeneratorProgress); }
As the name and description of the interface shows, with its help you can create only one file, which is not enough to solve the problem. However, the study showed that during the generation, the utility has access to
the Visual Studio context , through which projects can be changed in any way.
To manage a project, we need to get a reference to the
ProjectItem object with which an XML file is associated that describes the source data.
var dte = (EnvDTE.DTE)Package.GetGlobalService(typeof(EnvDTE.DTE)); var sourceControl = dte.SourceControl; var projectItem = dte.Solution.FindProjectItem(sourceFilePath); if (projectItem == null) { throw new ApplicationException("Unable to locate a ProjectItem associated to the file"); }
After that, we can add and remove child files from the ProjectItems collection of this item.
private static void UpdateNestedProjectItems(ProjectItem projectItem, HashSet<string> generatedFiles) { foreach (ProjectItem item in projectItem.ProjectItems) { if (item.Properties != null) { var property = item.Properties.Item("FullPath"); if (property != null) { var value = property.Value as string; if (value != null) { if (generatedFiles.Contains(value)) { item.Properties.Item("BuildAction").Value = 2; generatedFiles.Remove(value); } else { if(value.EndsWith("txt", StringComparison.CurrentCultureIgnoreCase)) { item.Properties.Item("BuildAction").Value = 0; } else { item.Remove(); } } } } } } foreach (var file in generatedFiles) { var item = projectItem.ProjectItems.AddFromFile(file); item.Properties.Item("BuildAction").Value = 2; } }
The second important part of the utility being created is the creation of resource files, to solve it, an empty dll file was created and implemented in the utility resources. During operation, this file is copied and renamed in accordance with the requirements for naming resource files. After that, resources are added to this file using the WIN API functions:
BeginUpdateResource ,
UpdateResource ,
EndUpdateResource .
internal static class APIHelper { public const int RT_STRING = 6; [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr BeginUpdateResource(string pFileName, bool bDeleteExistingResources); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool UpdateResource(IntPtr hUpdate, int lpType, int lpName, ushort wLanguage, IntPtr lpData, uint cbData); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool EndUpdateResource(IntPtr hUpdate, bool fDiscard); }
When creating string resources there is a little trick, the fact is that strings in resources are stored in blocks of 16 lines each, which is done for the purpose of optimization. To change a line, it is necessary to rewrite the entire block of lines in which it is included. For our task there is no need to change the data therefore, we can simply write down the data we need, and do not care about the safety of other lines of the block.
The row block number can be calculated using the following algorithm:
N = STRID / 16 + 1
where N is the number of the block of lines, STRID is the ID of the resource line (in the localization documentation it is assumed for the application header 100, for the tile header 200)
The finished utility must be registered with the GAC, as well as add an entry to the registry to register the class as a Custom Tool, after which it can be used in projects.
The result of the utility looks like this

For more convenient use of the utility, an element template for Visual Studio was also created and an installer was created for automatic registration of the utility.
Source codes are available:
http://wptitlelocalizationtool.codeplex.com/The installation package is available:
http://visualstudiogallery.msdn.microsoft.com/527c7d79-55f7-4ff2-9ab8-d6122bf6e75d