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

image

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

Windows Phone Application Template

В этом решении я скопировал необходимые элементы управления из проекта 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 Phone 8

Локализация названия приложения


В отличии от 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.

Локализация названия приложения Windows Phone 8

В файле 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.

WP localization

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

файл локализации

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

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

Судоку+

Источники