В предыдущей статье я рассказал как разработать судоку для windows 8.1, в этой расскажу как портировать приложение на Windows Phone 8 и локализовать его на несколько языков.

Для начала создаем проект шаблона Windows Phone

В этом решении я скопировал необходимые элементы управления из проекта Windows 8.1 в проект Windows Phone. Это решение не является лучшим, но оно самое быстрое. В следующих статьях я покажу как делать кроссплатформенные решения.
Сейчас же вернемся к портированию приложения. В игре на телефоне решено было сделать постраничную навигацию. Создано 4 страницы:

Игра начинается со страницы GamePage. Если нет сохраненной игры — необходимо отправить пользователя на экран создания новой игры:
В шаблоне Windows 8.1 автоматически будет создан класс
На телефоне работа с файлами немного отличается. Есть такое понятия, как IsolatedStorageSettings. Понятие это пришло из Silverlight версии 2. Как понятно из названия этот тип предназначен для хранения файлов и данных изолированно на локальной файловой системе. Это значит, что другое приложение, помимо вашего не получит доступ к этим данным.
Windows Phone 8 не позволяет сохранять шрифты в ресурсах xaml. Такой код на платформе WP8 не валиден:
Поэтому пришлось разделить файлы ресурсов и очистить лишнее из WP8.
Очень порадовала работа с разметкой. Во-первых нет необходимости делать верстку приложения для 3-х видов как в Win 8.1 (Full, Filled, Snapped). Важно правильно сделать верстку для экрана в целом. В судоку поддерживается только вертикальное положение экрана (горизонтальное поддерживаться на 99% не будет).
После адаптации верстки, стилей и размеров объектов необходимо протестировать на различных устройствах и экранах.

В отличии от Windows 8.1, описание приложений производится на сайте https://dev.windowsphone.com при настройки публикации. А название хранится в приложении. Но не все так просто :)
Есть такое понятие как Display Name и Tile Title. Первое будет использоваться в маркете и списке приложений на телефоне. Второе используется как надпись на тайле, если такая опция выбрана.
Как описано в MSDN How to localize an app title for Windows Phone (ссылки в конце) — нам нужно создать C++ DLL, в которой будет файл ресурсов, содержащий 2 поля: название и название для тайла. Для каждого языка нужно создать свою dll. Да, это грустно. Но на помощь приходит проект WP8 Localize. Скачиваем инструмент, заполняем поля и автоматически создаем dll для всех необходимых языков. Пару часов этим мы сэкономили точно.
После создания всех dll, их необходимо добавить в проект. Мне удобнее, чтобы они все были в отдельной папке. Я назвал ее Langs и поместил их все туда. Не забываем изменить BuildAction=Content.

В файле WPAppManifest.xml изменяем название и тайл на @Langs/AppResLib.dll,-100 и @Langs/AppResLib.dll,-200 соответственно.

На этом этап локализации названия приложения и тайла закончен. Приступаем к изменению языка для контента приложения.
В Windows Phone 8 инструменты для локализации стали гораздо лучше чем в 7-й версии. Создаем или находим класс LocalizedStrings.
Создаем папку Resources в проекте. Для каждого поддерживаемого языка создаем файл ресурсов AppResources.LOCALE.resx (например AppResources.resx и AppResources.ru.resx). Второй шаг — создание ресурсов в файле App.xaml:
Тем самым даем возможность биндинга в xaml.

Сам файл выглядит следующим образом:

Важное отличие, что здесь это конкретно файл текстовых ресурсов, а не объектов. Т.к. происходит связывание (биндинг) и генерация кода ресурсов — разделитель точка использоваться не может. Далее необходимо настроить связку для текстовых элементов к ресурсам.
Для страницы Новая игра:

Локализация поддерживается и в режиме верстки (в Expression Blend все правильно подхватывается)
ApplicationBar не под��ерживает локализацию на биндингах. Для этого при создании проекта создается закомментированный код метода
В отличии от Windows 8.1 локализацию нужно включить вручную. Для этого в app.xaml.cs в метод App добавляем вызов метода
В AssemblyInfo указываем культуру по умолчанию:
В файле WPAppManifest.xml на вкладке Packaging указываем настройки приложения: список поддерживаемых языков, язык по умолчанию.

Локализация приложения Windows Phone 8 в некоторых местах кардинально отличается от той же локализации в Windows 8.1. Однако ничего сложного в этом нет. Важно иметь полную инструкцию перед созданием локализации или добавления поддержки нового языка. Надеюсь эта статья поможет при создании мультиязычного приложения. Посмотреть на результат можно здесь:


Для начала создаем проект шаблона Windows Phone

В этом решении я скопировал необходимые элементы управления из проекта Windows 8.1 в проект Windows Phone. Это решение не является лучшим, но оно самое быстрое. В следующих статьях я покажу как делать кроссплатформенные решения.
Сейчас же вернемся к портированию приложения. В игре на телефоне решено было сделать постраничную навигацию. Создано 4 страницы:

Игра начинается со страницы GamePage. Если нет сохраненной игры — необходимо отправить пользователя на экран создания новой игры:
Исходный код
protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); GameState game = SettingsProvider.LoadSavedGame(); if (game == null) { NavigationService.Navigate(new Uri("/Pages/NewGame.xaml", UriKind.Relative)); } else { //... } }
Работа с файлами и сессиям в Windows 8.1
В шаблоне Windows 8.1 автоматически будет создан класс
SuspensionManager, который реализует механизм сохранения сессий. Тогда в классе страницы необходимо определить метод NavigationHelperLoadState и NavigationHelperSaveState. Код NavigationHelperLoadState показан ниже, сохранения состояния происходит похожим образом.Исходный код
private async void NavigationHelperLoadState(object sender, LoadStateEventArgs e) { try { if (SuspensionManager.SessionState.ContainsKey("game-data")) { string previousGame = SuspensionManager.SessionState["game-data"] as string; if (!string.IsNullOrEmpty(previousGame)) { GameStateModel game = GameStateModel.FromJson(previousGame); if (game != null) { LoadGameToBoard(game.ToGameState()); } } } } catch (FileNotFoundException fileNotFound) { } catch (Exception) { } }
Работа с файлами и сессиям в Windows Phone 8
На телефоне работа с файлами немного отличается. Есть такое понятия, как IsolatedStorageSettings. Понятие это пришло из Silverlight версии 2. Как понятно из названия этот тип предназначен для хранения файлов и данных изолированно на локальной файловой системе. Это значит, что другое приложение, помимо вашего не получит доступ к этим данным.
Код загрузки данных об игре
public static GameState LoadSavedGame() { try { IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings; if (settings.Contains(SavedGame)) { string previousGame = settings[SavedGame] as string; if (!string.IsNullOrEmpty(previousGame)) { GameStateModel game = GameStateModel.FromJson(previousGame); if (game != null) { return game.ToGameState(); } } } } catch (Exception exception) { } return null; }
Найденные особенности
Windows Phone 8 не позволяет сохранять шрифты в ресурсах xaml. Такой код на платформе WP8 не валиден:
<!-- Fonts --> <FontFamily x:Key="ThemeFontFamily">Segoe UI Light</FontFamily> <FontWeight x:Key="ThemeFontWeight">SemiLight</FontWeight>
Поэтому пришлось разделить файлы ресурсов и очистить лишнее из WP8.
Работа с разметкой
Очень порадовала работа с разметкой. Во-первых нет необходимости делать верстку приложения для 3-х видов как в Win 8.1 (Full, Filled, Snapped). Важно правильно сделать верстку для экрана в целом. В судоку поддерживается только вертикальное положение экрана (горизонтальное поддерживаться на 99% не будет).
После адаптации верстки, стилей и размеров объектов необходимо протестировать на различных устройствах и экранах.

Локализация названия приложения
В отличии от Windows 8.1, описание приложений производится на сайте https://dev.windowsphone.com при настройки публикации. А название хранится в приложении. Но не все так просто :)
Есть такое понятие как Display Name и Tile Title. Первое будет использоваться в маркете и списке приложений на телефоне. Второе используется как надпись на тайле, если такая опция выбрана.
Как описано в MSDN How to localize an app title for Windows Phone (ссылки в конце) — нам нужно создать C++ DLL, в которой будет файл ресурсов, содержащий 2 поля: название и название для тайла. Для каждого языка нужно создать свою dll. Да, это грустно. Но на помощь приходит проект WP8 Localize. Скачиваем инструмент, заполняем поля и автоматически создаем dll для всех необходимых языков. Пару часов этим мы сэкономили точно.
После создания всех dll, их необходимо добавить в проект. Мне удобнее, чтобы они все были в отдельной папке. Я назвал ее Langs и поместил их все туда. Не забываем изменить BuildAction=Content.

В файле WPAppManifest.xml изменяем название и тайл на @Langs/AppResLib.dll,-100 и @Langs/AppResLib.dll,-200 соответственно.

На этом этап локализации названия приложения и тайла закончен. Приступаем к изменению языка для контента приложения.
Локализация Xaml
В Windows Phone 8 инструменты для локализации стали гораздо лучше чем в 7-й версии. Создаем или находим класс LocalizedStrings.
Исходный код
/// <summary> /// Provides access to string resources. /// </summary> public class LocalizedStrings { private static AppResources _localizedResources = new AppResources(); public AppResources LocalizedResources { get { return _localizedResources; } } }
Создаем папку Resources в проекте. Для каждого поддерживаемого языка создаем файл ресурсов AppResources.LOCALE.resx (например AppResources.resx и AppResources.ru.resx). Второй шаг — создание ресурсов в файле App.xaml:
<Application.Resources> <winPhone8:LocalizedStrings xmlns:local="clr-namespace:Oxozle.Sudoku.WinPhone8" x:Key="LocalizedStrings"/> </Application.Resources>
Тем самым даем возможность биндинга в xaml.

Сам файл выглядит следующим образом:

Важное отличие, что здесь это конкретно файл текстовых ресурсов, а не объектов. Т.к. происходит связывание (биндинг) и генерация кода ресурсов — разделитель точка использоваться не может. Далее необходимо настроить связку для текстовых элементов к ресурсам.
<TextBlock Text="{Binding LocalizedResources.ApplicationTitle, Source={StaticResource LocalizedStrings}}" Style="{StaticResource PhoneTextNormalStyle}"/>
Для страницы Новая игра:

Локализация поддерживается и в режиме верстки (в Expression Blend все правильно подхватывается)
Локализация из кода
ApplicationBar не под��ерживает локализацию на биндингах. Для этого при создании проекта создается закомментированный код метода
BuildLocalizedApplicationBar. Но нет ничего сложного написать несколько строк для построения меню приложения.BuildLocalizedApplicationBar
private void BuildLocalizedApplicationBar() { // Set the page's ApplicationBar to a new instance of ApplicationBar. ApplicationBar = new ApplicationBar(); //// Create a new button and set the text value to the localized string from AppResources. //ApplicationBarIconButton appBarButton = new ApplicationBarIconButton(new Uri("/Assets/AppBar/edit.png", UriKind.Relative)); //appBarButton.Text = AppResources.GamePage_Pencil; //ApplicationBar.Buttons.Add(appBarButton); // Create a new menu item with the localized string from AppResources. ApplicationBarMenuItem appBarNewGame = new ApplicationBarMenuItem(AppResources.NewGameTitle); appBarNewGame.Click += delegate { NavigationService.Navigate(new Uri("/Pages/NewGame.xaml", UriKind.Relative)); }; ApplicationBar.MenuItems.Add(appBarNewGame); ApplicationBarMenuItem appBarMenuItem = new ApplicationBarMenuItem(AppResources.GamePage_ButtonAbout); appBarMenuItem.Click += delegate { NavigationService.Navigate(new Uri("/Pages/AboutPage.xaml", UriKind.Relative)); }; ApplicationBar.MenuItems.Add(appBarMenuItem); ApplicationBarMenuItem appBarRate = new ApplicationBarMenuItem(AppResources.WinGame_Rate); appBarRate.Click += delegate { MarketplaceReviewTask marketplaceReviewTask = new MarketplaceReviewTask(); marketplaceReviewTask.Show(); }; ApplicationBar.MenuItems.Add(appBarRate); }
Включение локализации
В отличии от Windows 8.1 локализацию нужно включить вручную. Для этого в app.xaml.cs в метод App добавляем вызов метода
InitializeLanguage.InitializeLanguage
// Language display initialization InitializeLanguage(); //Сам метод (может быть уже создан студией). private void InitializeLanguage() { try { // Set the font to match the display language defined by the // ResourceLanguage resource string for each supported language. // // Fall back to the font of the neutral language if the Display // language of the phone is not supported. // // If a compiler error is hit then ResourceLanguage is missing from // the resource file. RootFrame.Language = XmlLanguage.GetLanguage(AppResources.ResourceLanguage); // Set the FlowDirection of all elements under the root frame based // on the ResourceFlowDirection resource string for each // supported language. // // If a compiler error is hit then ResourceFlowDirection is missing from // the resource file. FlowDirection flow = (FlowDirection)Enum.Parse(typeof(FlowDirection), AppResources.ResourceFlowDirection); RootFrame.FlowDirection = flow; } catch { // If an exception is caught here it is most likely due to either // ResourceLangauge not being correctly set to a supported language // code or ResourceFlowDirection is set to a value other than LeftToRight // or RightToLeft. if (Debugger.IsAttached) { Debugger.Break(); } throw; } }
В AssemblyInfo указываем культуру по умолчанию:
[assembly: NeutralResourcesLanguageAttribute("en-US")]
В файле WPAppManifest.xml на вкладке Packaging указываем настройки приложения: список поддерживаемых языков, язык по умолчанию.

Выводы
Локализация приложения Windows Phone 8 в некоторых местах кардинально отличается от той же локализации в Windows 8.1. Однако ничего сложного в этом нет. Важно иметь полную инструкцию перед созданием локализации или добавления поддержки нового языка. Надеюсь эта статья поможет при создании мультиязычного приложения. Посмотреть на результат можно здесь:

