Skip to content

Latest commit

 

History

History
66 lines (47 loc) · 13.9 KB

GlobalizationNotes.md

File metadata and controls

66 lines (47 loc) · 13.9 KB

Globalization notes

These are explanatory notes and tips for how the SnapGold sample was made global-ready and how you can make your own apps global-ready.

MVVM and global readiness

The MVVM pattern divides your app's implementation into three parts: The Model is the data your app manipulates. The View is how your app displays data to the user and allows the user to manipulate that data. The View Model is an intermediary layer that translates Model data into data suitable for the View, and that translates user actions in the View into changes in the Model's data. Each layer works differently with regard to global-readiness and localization.

  • Model data is not displayed directly to the user, so localization is rarely a concern in the Model. Global readiness can come into play when you are designing your model data, depending on your app's requirements. For example, if you want to store dates and times in the model and display them again later, you will want to record not just the date and time, but also the time zone in which the date and time were originally recorded, so that you can display the date and time properly later on. (You can either update the date/time to the local time zone for display, or you can display the stored date and time along with a time zone indicator. The concern is that you not give the inaccurate impression that all dates and times are in the current user's time zone.)
  • The View Model translates Model data into app user terms, and that includes possibly using the user's language to display model data. (In the date/time example above, this is where you'd format the date/time appropriately for your app's requirements, using the stored date/time and time zone.) View Model data is often displayed according to XAML templates defined in the View. Note that not everything exposed by the View Model needs to be localized. For example, in making SnapGold global-ready, we decided that data entered by the app's users, such as category names or picture comments, would be displayed as they were originally input by a user. The user interface is localized, but user-generated content is not. That makes sense for this app, but your app might choose to work differently.
  • The View specifies text and controls that are displayed to the user, usually in XAML. Global-ready apps don't specify text directly in their XAML files; rather, they load text from string resource files, so that the proper resource file can be chosen at runtime, based on the user's language settings.

There is other code in this app whose place in the MVVM model is not immediately obvious, but which manipulates or displays data and needs to be global-ready. Some examples:

  • The navigation bar button labels were specified as text in each button's implementation of INavigationBarMenuItem. Those implementations were changed to load the label text from a resource file.
  • Several custom controls had text in their XAML files, and can be considered part of the Views that use them. These were changed to load resources instead.
  • Some converters used hard-coded strings in their output. These were changed to load resources instead.

Finally, there were some issues that weren't easily solved by simple changes to XAML or code, but needed to be addressed at the design stage. One example of this is the "My" control. See the detailed discussion below for why some elements of this design choice don't work well in a global-ready app. In localizing this version of the app, we've chosen a simple change that doesn't meet all of the same design goals, but is quick and easy to implement.

Issues of implementation

Issues of implementation are places in the code where it was not written in a way that makes localization possible. The example of this that you always hear about is the use of hard-coded strings in code, but there are other more subtle issues of implementation, as well.

Using localized strings in XAML: the basics

  1. Using x:Uid, give any control with localizable properties a UID.
  2. The convention used in this project is to use the .XAML file name, then an underscore, then text that describes the use of the control.
  3. A second convention is that when assigning a UID to a dialog, as in CategoriesChooserDialog, it gets a UID which is just the name of the dialog with "_Dialog" at the end.
  4. If you just use resources for control text, then there's no text visible at design time, which is confusing. It turns out that you can assign values to control properties in XAML so they show up at design time, and then also put resources in your .resw file which will override the control properties at runtime. This is done in most views, including AboutPage. So go ahead and assign Text or Content properties to your controls for use in the Designer when necessary. Once all of your code is global-ready, the resources in your .resw file will be used at runtime.

Localizing strings that are formatted by an item template

This app has several Views which display a lists of items, and they use the standard XAML pattern of defining an item template to format the data for each item. At runtime, the ViewModel for the View constructs an observable collection and binds it to the list, and then each collection element is formatted using the item template. When this pattern is being used, we modify the ViewModel to load the appropriate resources when it is constructing the observable list of items, and then XAML ensures that each item is formatted using the item template.

We do a similar thing with the tool tip text on the menu buttons. They are provided to the View by the View Model, so we modified the code called by the View Model to load the tool tips as resources rather than just returning static text from the code. The button bar labels are in the code in the NavigationBar folder. The button bar label template is in AppShell.xaml, and the template there binds to a property named Label. So we searched for code that implemented a class with a property by that name to find the code in the NavigationBar folder that implemented the objects that describe each menu bar item.

Using the localized resources

We had to manually add a <Resource> element to the app manifest for each added language. According to some documentation, Visual Studio is supposed to do this automatically, but apparently that's not true in every situation. To do the manual addition, right-click the .appxmanifest file, then Open With the XML editor. The syntax is plain from the existing entry for "en".

The Categories view model gets a new property

XAML won't load a resource based on x:UID for the PageHeader control's DependencyProperty in the way that it does for other controls' properties. Custom controls work more like item templates in this regard. We can't use x:UID to replace the static text "Categories" in the page header with the value of a string resource. Instead, we added a new "HeaderText" property to the view model which loads the resource string on a property get. The property value can then be bound to the DependencyProperty. This uses the header control in an analogous way to the way that StreamPage uses it to bind to a single category's name in XAML. We're just loading the resource directly instead of getting it from a ViewModel data structure. The same technique was applied for the LeaderboardsPage header, and for each hub section header.

We don't localize the model data

The hard-coded string "my" occurs in Category.cs, as part of the model for categories. (The model puts those two letters at the front of stored category names in its data store, and the code in Category.cs is where those letters are removed when the view model reads the name from the model.) We don't try to localize that model text, because it's part of the model that's never exposed to the user, and might be used in the model implementation for purposes we can't see or completely understand. (In this case, we can see the code of the server-side components of the model, but in general we might not be able to see those components, or we might be able to see them but not change them because the back-end is shared with other apps.) In MVVM, it's the ViewModel and the View that determine what users experience. If you think you need to change the Model to support localization of the client UI, then you should either be adding data to the Model (like adding time zone information to date/time data as discussed earlier in this document), or you should be changing the ViewModel instead.

Don't just stringify an enum value to make a UI string

PhotoStatusToStringConverter did this, but it's not generally localizable. Instead, we now use the enum value to choose which string resource to load, and then we can localize the string resources appropriately. (See the discussion of the Sign In page below for an example where we seem to be able get away with doing this, in a very specific situation, and note the discussion about it being a maintenance issue as new sign in providers are added later.)

Issues of design

The hardest issues in making an existing app global-ready are issues of design - the app's user experience was not designed in a way that makes localization possible, or the design makes assumptions about language or user experience that are not valid for all languages. These issues tend to be complicated and more expensive to fix, because changing the design requires writing and testing new code for an updated UX.

The MY control

This was a literal "My" in an orange box, and it appears in various places throughout the app. It's visually distinctive, and helps give visual cues for the different parts of the page. However, there are some drawbacks from both a user experience standpoint and a localization standpoint. From a user experience standpoint, the control is used even when no one is logged in to the app, when "My" anything isn't really appropriate. And it's used around content that was shared by other users, where again "My" isn't accurate. A design that retains the visual cues but makes clear what belongs to the user and what is shared from others would be more accurate about content ownership. Another minor UI inconsistency: Why are all the categories "My CategoryName", while the signed-in user's Gold balance is "Your Gold"? Isn't it all either "mine" or "yours"? Combining the text content of this control with other text by putting them next to each other, as with "My Categories" in the page header on the home page works in English, but it is not appropriate design for many other languages. Not all languages combine words in that way. Some languages use different analogs to "My" depending on grammatical details of the other words in a phrase. Some languages might put the "My" word after the category name, rather than before. Others might use different glyphs or diacritics to spell out "My" in "My X", depending on exactly what X is.

The MY control makes for a distinctive piece of visual design in this app, but might not work as well for a global market as it does in English.

Images are a potential localization pitfall, and need to be planned

The images on a couple of the pages of the welcome experience are pictures of the current UI, in English. This means that what the user sees in the image won't match the UI they see, if they aren't using English. There are multiple approaches to solving this. One is to use pictures that show the UI but don't show UI text, through clever cropping or by collapsing the labels on the navigation bar. If the text isn't there, it doesn't need localization. Another way to deal with the problem is to take screenshots that show the UI in the localized language, and to load the appropriate localized picture at runtime. This requires extra effort to produce the pictures, but it does give the user pictures and UI that match. Both approaches are legitimate; the proper approach depends on many factors, including localization budget and timeline, ease of creating the localized image, the size of the market for various languages, and design goals and sensibilities.

RankConverter is not global-ready

RankConverter took a rank number and returned "st", "nd", "rd", or "th", which were placed as superscripts next to the rank number in a layout (leaderboard ranks, for example). This approach is a nice extra in English, but doesn't work reliably for languages that aren't English. (It doesn't even work the same way in English for 11-20 as it does for 1-10.) Not all languages use the same counting algorithm, let alone structure it the same way. We've removed the converter and the controls that used it.

Sign In page's display of service names...

This is done by the AuthProviderToStringConverter class, which did it by simply taking the enum values from the Azure API, and putting a space in front of any interior capital letters. That seems to work for the services used, because they are trademarks that don't change in different locales. If any new authentication services are added later, this will need to be checked to make sure that it's still working properly for other locales. A more robust converter would load a (localized) resource for each enum value instead of doing this quick and dirty conversion. (See PhotoStatusToStringConverter.cs for an example of this approach.)

Other notes

  1. We didn't set up DebugPage for localization, as it is only available in debug builds, and developers who want it localized can just change the content to a language they're comfortable with.
  2. PhotoDetailsPage does a couple of Yes/No flyouts on the page, when they could just use the Yes/No dialog. This is an inconsistency in design that should probably be fixed, but it's not inherently a a localization issue, so it's not addressed in this change. In the best case, you'd standardize your interaction model around one method of asking yes/no questions, either a message box everywhere or flyouts everywhere or something else everywhere, but consistently just one of those. Making that standard interaction model localizable would use the techniques discussed earlier in these notes.