В предыдущей статье я рассказал как разработать судоку для windows 8.1, в этой расскажу как портировать приложение на Windows Phone 8 и локализовать его на несколько языков.
Для начала создаем проект шаблона 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
. Но нет ничего сложного написать несколько строк для построения меню приложения.
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
.
// Language display initialization
InitializeLanguage();</code></pre>
<p>Сам метод (может быть уже создан студией).</p>
<pre><code class="cs"> 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. Однако ничего сложного в этом нет. Важно иметь полную инструкцию перед созданием локализации или добавления поддержки нового языка. Надеюсь эта статья поможет при создании мультиязычного приложения. Посмотреть на результат можно здесь: