В предыдущей статье я рассказал как разработать судоку для 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. Однако ничего сложного в этом нет. Важно иметь полную инструкцию перед созданием локализации или добавления поддержки нового языка. Надеюсь эта статья поможет при создании мультиязычного приложения. Посмотреть на результат можно здесь:

