Universal Windows Platform Localization and Internationalization |
Universal Windows Platform, UWP, (Wikipedia) is a cross platform application platform created by Microsoft. Platform is new deprecatd and replaced with Windows App SDK/WinUI. UWP uses only Unicode, and localized resources a included in a locale depend sub directory. This make UWP one of the best environment for localization. Soluling localization tool and service support UWP. The following pages describe UWP localization step by step.
There are basically two ways to create Universal Windows Platform application. The first one uses XAML and Resw files with either C#, VB or C++ programming languages. The second one use HTML and ResJson files with JavaScript programming language. Both methods can use various other resource files such as images, sound and video. Following table contains all resource types that Universal Windows Platform applications use.
Resource | Description | Used |
---|---|---|
Resw | String resources | C#, VB, C++ |
ResJson | String resources | JavaScript |
XAML | User interface resources | C#, VB, C++ |
HTML | User interface resources | JavaScript |
Images | Image resources | All |
Other | Audio, video, XML data, JSON data, text data, etc | All |
No matter what the resource file is the localized resource files are placed on a language specific sub directory of the original files. If the original string resource file is
Resources.resw
then the German file is
de\Resources.resw
and the French file is
fr\Resources.resw
You can also use a country specific sub directory. The following file is for German (Switzerland)
de-CH\Resources.resw
You can localize Universal Windows Platform applications using the following methods.
Visual Studio project file (.csproj, .vbproj or .jsproj) contains all the information about your application. Add the project file into Soluling project. When scanning it Soluling automatically scans also all the resource files it contains. You can turn on/off certain resource types. The advantage of using project files is that if you add or remove any resource file from the project you don't have to modify the sources in your Soluling project file. When building the project Soluling add the localized resources into language specific sub directories. Finally you add those files into your Visual Studio project and rebuild it to get a multilingual application.
Read Universal Windows Platform tutorial to get familiar into this method.
When you start localizing your UWP project you have to select what files you add into a Soluling project. In most cases this is easy and you just add your Visual Studio project file or files. However in some cases you might have several project files, or you might have some other files types that are hard to locate. This is why Soluling has made it easy for you to localize your complete Visual Studio solution. Instead of manually adding all necessary project files you just add one file: your Visual Studio solution file (.sln). Soluling reads the solution file and locates all the necessary project files that need to be localized. If you add new projects into your solution you don't have to add anything into your Soluling project because Soluling will automatically find the new items from the solution file and adds them into the project.
The result you will get by using Visual Studio solution file instead of individual project files is exactly the same. You Soluling project will contain the same project sources, you can configure them in the same way, and the output files will be the same. Support for Visual Studio solution file is just to make things easier for you.
The last localization method is to individually localize each XAML file (.xaml), resource file (.resw), HTML file (.html) and/or JavaScript resource file (.resjson). Soluling reads the original resource file and when building creates localized resource files. There is one output file for each language. You have to add all the resource files into your Soluling project. Even using wildcards it may be complicated. Also every time you add a new resource file you may have to add it to the project. It is recommended that you use either Visual Studio project file localization or Visual Studio solution file localization method.
This tutorial shows you how to localize a C# Universal Windows Platform application. The process is the same if you use VB or C++. The process is very similar if you use JavaScript. <data-dir>\Samples\UWP\Tutorial\Original\Original.csproj is your sample tutorial application. Open it into Visual Studio. The project contains one page, MainMage.xaml, that has some text controls. The first text control has Text property defined in XAML on design time.
<TextBlock Text="Hello World" Style="{StaticResource SubheaderTextStyle}" />
The control does not have any name or id property set. It is something that is not absolutely required for localization but if a control does not have a name Soluling can not assign a permanent context for it. Instead it uses a context value that depends on the index of the control. If you add or remove controls before the control the context value may change. This is why it is recommended to give a name property for every control. So lets add it.
<TextBlock x:Name="text1" Text="Hello World" Style="{StaticResource SubheaderTextStyle}" />
Other text controls are updated on run time by assigning a value to Text control. This is why XAML contain only dummy text.
<TextBlock x:Name="text2" Text="dummy" Style="{StaticResource SubheaderTextStyle}" />
Because controls are accessed on run time they already have name properties set. These controls could be without Text="dummy" properties but the dummy text is used to make it easier to see the controls on the page on design time. The actual Text properties will be set on run time in Page_Loaded event. For example:
text2.Text = "This is a sample";
The above code sets the Text property. Unfortunately the text it uses is hard coded into program code. It can not be localized. In order to localize such text we need to replace hard coded strings with resource strings.
The first step is to add a string resource file. Right click the project file in Solution Explorer and choose Add | New Item. Select Resource File (.resw), accept the default name "Resources.resw" and click Add. Visual Studio adds an empty resource file.
Copy "This is a sample" text from source code and paste it to the value field. Change the value in the name field to Sample. The comment field can contain any text. It is meant to give some extra information for translator. Enter "This message is used on the main form to describe the application". The resource file should look like this now:
Now we have successfully added a hard coded string into a resource file. The next step is to remove the hard coded string and to use the resource string instead. First we have to create a ResourceLoaded object that is used to access resource strings. Add the following code
public sealed partial class MainPage : Page { private ResourceLoader rl = new ResourceLoader();
This creates the object and makes it available to all method of MainPage. Next replace the hard coded string with call to rl.GetString.
text2.Text = rl.GetString("Sample");
The parameter specified the name of the resource string. It is the value in the name field of the resource file.
The next hard code string is little bit more complicated.
text3.Text = "My name is " + name;
The value of Text property is a combination of a constant string and a string variable. Depending on the value of the variable the actual string gets different values. If name is "John" then the string gets to "My name is John". If name is "Mary" then the strings becomes to "My name is Mary". We could use the same approach as with text2 control
text3.Text = rl.GetString("Name") + name;
However this assumes that the name in the sentence is always the last word. This works on many languages such English, German and Finnish. However in Japanese you would say "マリーです" where マリー is the name and です is "My name is". This means that to create correct Japanese the above code should be
text3.Text = name + rl.GetString("Name");
Because our code has to work both on English and Japanese we need to find a more general solution. Such as solution is to use message patterns. Instead of using "..." + name we using String.Format function that combines a message pattern with the value(s). The message pattern is a string that contains placeholder(s) for the variables. Placeholders are marked with {x} where x specifies the index of the placeholder. 0 is the first, 1 is the second, etc. The line using message pattern and Format function is
text3.Text = String.Format("My name is {0}", name);
Add "My name is {0}" into the resource file with "Name" in the name field. The final line would be:
text3.Text = String.Format(rl.GetString("Name"), name);
Now the translator can freely move the placeholder where ever the grammar of the target language requires. The following table contains translated patterns in few languages:
Language | Pattern |
---|---|
English | My name is {0} |
Finnish | Nimeni on {0} |
Japanese | {0}です |
Most languages use plural forms. It means that the noun in the sentence is either in singular or plural form depending on the amount of object. For example we way "I have 1 orange" or "I have an orange" but "I have 2 oranges". In the last sentence the orange is in plural form. How can we make out code to generate grammatically correct dynamic messages. One approach it to use if clause. For example
if (count == 1) text5.Text = String.Format("I have {0} orange", count); else text5.Text = String.Format("I have {0} oranges", count);
Unfortunately this would only work on such languages where one uses singular and zero, two or more uses plurals. Such languages are English, German and Finnish. However French uses singular with 0 and 1, and plural in other cases. Japanese does not have plural at all. Polish has three forms: singular (1), paucal (2-4) and plural (0, 5 or more). So in order to handle all languages our code would be very complex. Standard Universal Windows Platform does not offer any solution for this. Fortunately Soluling does. Soluling.Plural.Format function is a plural enabled format function similar to String.Format. To use Soluling.Plural.Format add <data-dir>\Library\NET\Standard library project into your Visual Studio project file. Add the file as link if you want that Visual Studio does not copy the file into your project directory. Now we can use the following line.
text5.Text = Soluling.MultiPattern.Format("{plural, one {I have {0} orange} other {I have {0} oranges}}", count);
Our original language is English and because it uses two forms the message pattern contains two parts. The first part is the singular pattern and the second part is the plural pattern. The second parameter in MultiPattern.Format function specifies the count that determines what plural form (singural, plural, ...) is used. The rest of the parameters work just like in String.Format. They are the variables to be inserted into the placeholders. Now the translator can enter patterns for each plural forms the language uses. The following table contains the translated multipattern strings is few languages:
Language | Pattern |
---|---|
English | {plural, one {I have {0} orange} other {I have {0} oranges}} |
Finnish | {plural, one {Minulla on {0} appelsiini} other {Minulla on {0} appelsiinia}} |
Japanese | {plural, other {{0}オレンジを持っています}} |
Soluling contains a sophisticated plural editor that lets you to easily and safely edit each patterns. The plural engine must know what is the current language. There is not 100% accurate method to acquire the current language. This is why the best way is to set the language yourself.
Plural.Language.Id = rl.GetString("Language");
Language resource string contains initially en
that is a ISO code for English. Each translated resource contains the language code of that language. For example German resource file would contain de. After initializing the engine we can use it. Out final line would be
text5.Text = Soluling.MultiPattern.Format(rl.GetString("OrangesPlural"), count);
We have one more thing to do. Look at the following line:
text4.Text = "Today is " + today.Month + "/" + today.Day + "/" + today.Year % 100;
It converts date into a string. In US this would be for example "Today is 1/14/2016". We could use message patterns like this
text4.Text = String.Format("Today is {0}/{1}/{2}", today.Month, today.Day, today.Year % 100);
but it would only work in US and other countries that use month/day/two-digit-year. For example in Finland a correct format would be 14.1.2016. The order is day, month and year. Separator is . and a full four digit year is used. Universal Windows Platform contains API that help to create correct date strings. Use Windows.Globalization.DateTimeFormatting.DateTimeFormatter class. First you create a date formatter using short date and current language of the user.
var formatter = new DateTimeFormatter("shortdate", GlobalizationPreferences.Languages);
Once you have the formatter you use used Format method to return a date as string.
text4.Text = String.Format(rl.GetString("Today"), formatter.Format(today));
The Today resource string is "Today is {0}". formatter.Format(today) returns date formatted into short string using the date format of the current language. Now the internationalization part is finished. We can start localizing our project.
Start Soluling and click New from File button. Project Wizard appears. Browse into <data-dir>\Samples\UWP\Tutorial\Complete directory and select Complete.csproj.
Click Next. Resources sheet appears. It shows the available resource type. We don't need to localize images so uncheck Image resources.
Click Next. Select Languages sheet appears. This lets you to choose the target languages. Drag the language you want from right to left. In our sample we use Finnish.
Click Finish to complete the wizard. Soluling creates a new project, reads Complete.csproj, scans the resource files it contains, and finally shows the created project in the editor.
On the right side you see the project tree. The top level items are called sources. Our sample contains one source, Complete.csproj. Each source contains rows and sub node. Each sub node represents one resource file. For example Resources.resw contains the string resources. If you select a specific node in the project tree the right side grid shows only rows belonging into the that node or one of the sub node of the selected node. Our project contains some resource file that we do not want to localize. Select App.xaml, right click and choose Exclude this Node.
Now App.xaml is excluded from the project. Exclude also Common\StandardStyles.xaml. MainPage.xaml contains four rows that all have dummy text. Those texts belong to dummy value of Text property. We do not localize them so we can exclude them. Easiest way is to exclude string"dummy". Select a "dummy" row, in the grid right click on the left side of the row and choose Exclude | All Rows with "dummy" original.
Soluling prompts to rescan. Accept that. Once scanned out project is ready for translation.
The rightmost column of the grid contains Finnish strings. Currently there are no translations so all values are empty. Start typing translations. You can also use machine translations by right click Finnish column header and choose Machine Translate. The fourth row contains a string that has plural forms. You can see a double bubble in the info column for all rows that have plural forms. To enter translations Soluling shows a plural editor.
The editor shows the original forms. In our case the original language is English so the original forms are singular and plural. Out target language also contains two forms: singural and plural. Enter translations and click OK. Once you have translated all rows the project looks like this.
Now we can create the localized resource files. Click Build button on ribbon. Soluling creates the localized resource files: fi\Resources.resw and fi\MainPage.xaml. During the build process Soluling also check if the Visual Studio project has the files included. If they are not then the compiled deploy package won't contains the resources and your application won't have localized data. This is why Soluling warns if a localized resource file is not include into the project.
Let's add the localized resource file into the project.
Solution Explorer contains Show All Files button. Click it and Solution Explorer shows also those files and directories that are not current included into the project.
Select fi, right click and choose Include In Project.
Now Finnish resources are included. By default Visual Studio sets the Build Action of localized XAML files to Page. This is what it should be. If it is not Page change the value to Page.
Now localized resource files are correctly included into the project and we can build a multilingual application.
When you build your application Visual Studio will warn that you have "fi" resources but no "en" resource that is the default language. "en" resources are not needed because the project contains original neutral resources. When running a multilingual application Universal Windows Platform uses resource cascading. If a specific language resource is not found then default resource is looked. If that is not found either then a neutral resource is used.
Unfortunately there is no way to turn off this warning. You just have to ignore it.
Now we have a multilingual application that supports English and Finnish. Lets run it. If we run it on English Windows or any Windows that has not Finnish language added into preferred languages the application will start in English.
Start Control Panel. Select Language sheet and you will see your language of preferences. Add Finnish as the topmost language.
Run the application again and now it will appear in Finnish.
<data-dir>\Samples\UWP\Tutorial\Complete\Complete.csproj is the same turorial project that has been fully internationalized and localized into Finnish.
In our sample we localized XAML and RESW files. Soluling can localize all kind of resource files. If you have image or sound files in your project Soluling lets you easily specify a language depend version of the file. <data-dir>\Samples\UWP\Resources sample to see how images, audio, XML, structural text, and segmented text data is localized.
In addition to the above resource data, Soluling can localize plain text, JSON data, image lists, video or any binary data.
ResJson is the basic resource format of JavaScript projects of Universal Windows Platform. It is based on JSON but files are converted to a binary format when added into deployment package.
Appx file is a deployment package used in Universal Windows Platform. The file extension of Appx file is .appx. The .appx file is a compressed file that includes AppxManifest.xml, compiled output assemblies of the application (.exe and .dll) and resource files referred by the application. Appx files use the standard zip compression algorithm to minimize client download size.
GitHub and <data-dir>\Samples\UWP contains following Universal Windows Platform samples:
Directory | Description | Notes | ||||||
---|---|---|---|---|---|---|---|---|
Driving | A C# sample that shows how to localize a Windows Runtime application. | |||||||
Font | A C# sample that shows how to localize fonts. | |||||||
HelloWorld | A C# sample that compares ResW only localization method to XAML/ResW localization method.
Contains following projects:
|
|||||||
JavaScript | A JavaScript + HTML sample. | |||||||
Plural | A C# sample that shows how to use plural messages. | |||||||
Resources | A C# sample that shows how to localize various kinds of resources such as images, sound and XML. | |||||||
Simple | A C# sample that shows how to localize user interface, strings and images. | |||||||
Tutorial | A tutorial project that shows you how to internationalize a UWP application.
Contains following projects:
|
Try this first! |
Samples.sln is a Visual Studio 2017 solution file that contains all the above samples.
You can configure how to localize your UWP project file (.csproj, .vbproj or .jsproj) by selecting the item in the project tree, right-clicking, and choosing the Options menu. A source dialog appears that lets you edit the options. This source uses the following option sheets.
You can configure how to localize your UWP resource file (.resw or .resjson) by selecting the item in the project tree, right-clicking, and choosing the Options menu. A source dialog appears that lets you edit the options. This source uses the following option sheets.