"Платформа J2Me" - читать интересную книгу автора (неизвестен Автор)Глава 9. ИнтернационализацияРазработчику приложения MIDP необходимо подумать и о международных пользователях. Люди по всему миру используют мобильные устройства каждый день, а экспансивный рост мобильных телефонов все еще продолжается. Проблема заключается в создании приложений MIDP, которые могут отвечать нуждам пользователей по всему миру с учетом лингвистических, географических и культурных ограничений. Практика разработки программного обеспечения, которое относится к разрешению этих глобальных задач, называется интернационализацией. Разработка интернационализированных приложений MIDP является темой данной главы. В этой главе не обсуждается общее решение интернационализации для произвольных компьютерных платформ — даже общее решение платформы Java. А также не обсуждается его понятийное представление во всей широте и глубине темы интернационализации, являющейся областью, которой можно посвятить целую профессию. Тема интернационализации состоит из обширного и всестороннего набора компьютерных действий, и даже скромное рассмотрение этой темы лежит далеко за пределами возможностей данной главы или книги. Скорее, эта глава покрывает только области интернационализации, поддерживаемые MIDP. Вы узнаете, как использовать программные интерфейсы приложений MIDP, которые поддерживают интернационализацию. Вы также узнаете об областях, для которых не существует явной поддержки, и как вы можете проектировать в обход ограничений доступных вам инструментов. Региональная настройка представляет собой определенный географический, лингвистический и культурный контекст. Она описывает контекст, в котором работают интернационализированные приложения. Термин локализация относится к практике предоставления приложению возможности работать в контексте определенной региональной настройки. Слово локализация иногда сокращается как «11Оn», поскольку 10 букв опускаются между буквами «l» и «n» в этом слове. Разработчик локализует приложение для одной или нескольких региональных настроек после его интернационализации. Язык обычно является наиважнейшим фактором различия региональных настроек. Различия в использовании языка включают такие характеристики, как орфография, капитализация, пунктуация, идиоматические выражения и даже написание. В действительности географический контекст обычно отделяет регионы, которые по-разному используют язык, использование языка, как правило, связано с определенным географическим контекстом. По этой причине языковой и географический контекст являются основной информацией, описываемой в региональной настройке. Например, Франция, Швейцария и Канада представляют три географические зоны, в которых французский язык используется по-разному. Китай и Тайвань представляют собой различные регионы, в которых на китайском языке говорят и пишут по-разному, и где существуют различия в идиоматических выражениях. Географический контекст региональной настройки может представлять собой область меньше, чем страна, как, например, провинция, регион или даже такая маленькая область, как город. Например, Гонконг и Гуанчжоу, оба они расположены в Китае, в них обоих говорят на кантонском диалекте китайского языка, но совершенно по-разному, а также различным образом пишут китайские идеограммы. Подобные различия существуют во многих языках, включая использование английского и испанского языков по всему миру. Региональная настройка предоставляет контекст более, чем просто для языковой информации. Региональная настройка также предполагает использование определенной временной зоны, конкретных способов задания форматов дат, времени и числовых значений, а также использование определенного календаря (грегорианского, лунного и так далее). Локализация приложения под определенную региональную настройку включает предоставление данных, требуемых программе для правильного функционирования в данной местной специфике. Локализованные данные включают перевод текста, который просматривают пользователи, определенные политики сортировки, выбор определенного формата даты и времени, использование правильных числовых и денежных форматов и так далее. В действительности существует множество других деталей, необходимых для создания полностью интернационализированного и локализированного пользовательского интерфейса. Комплексные усилия включают обращение к культурным соглашениям — например, избегать использования агрессивных значков, использование цветов, представляющих символику, которую понимает местный пользователь и так далее. Многие из этих вопросов, однако, связаны с проектированием. Разработка решений 118п лежит за пределами возможностей данной главы. В программных интерфейсах интернационализации не существует определенных механизмов, которые были бы связаны со многими из этих задач. Создание высококвалифицированных интернационализированных разработок является искусством, как и многие другие области в разработке программного обеспечения. ASCII является примером символьной кодировки. Поскольку символьная кодировка ASCII соответствует только английскому алфавиту, нам необходимы другие символьные кодировки — иногда называемые наборами символов — для работы с другими языками. Как вы знаете, Java внутренне использует символьную кодировку уникода для представления всех символьных данных. Данные, считываемые программой, могут быть представлены в уникоде, а могут и не быть. Если они не представлены, данные могут быть преобразованы перед импортом в приложение. Кроме должного внутреннего представления данных с помощью символьных кодировок, приложения должны представлять данные правильно пользователю. Это требует использования шрифта, который представляет элементы, используемые в языке. Компьютерная платформа отвечает за предоставление приложениям поддержки шрифтов. Платформа создает соответствия между символьными кодировками и шрифтами, чтобы приложениям не приходилось этого делать. Эти соответствия определяют связи между точками кода и глифами. Глиф — это объект, визуализирующий то, что представляет собой элемент языка, как, например, буква или идеограмма. Интернационализация включает множество аспектов разработки приложений. Фактически наиважнейшая цель всех этих аспектов разработки заключается в создании пользовательского интерфейса, — и поддерживающей его инфраструктуры — который представляет всю информацию пользовательского интерфейса в понятном для местных пользователей виде. Как минимум, эти работы включаю поддержку следующих аспектов выполнения приложения: Для поддержки вышеупомянутых свойств интернационализированное приложение должно выполнять некоторое динамическое конфигурационное и информационное извлечение. Обычно приложение определяет свой контекст региональной настройки динамически при запуске. Затем оно конфигурирует все необходимые компоненты выполнения — такие, как календарные объекты, сортировщики строк, объекты задания формата и компоненты работы с сообщениями — которые должны отвечать требованиям контекста региональной настройки. Работа с сообщениями. определение среды региональной настройки устройства; загрузка локализованных ресурсов приложения; динамический поиск и извлечение локализованных ресурсов для отображения пользовательского интерфейса; отображение локализованных ресурсов. Работа с сообщениями — это область, которая лучше всего подчеркивает близкое родство между интернационализацией и локализацией. Чтобы сделать интернационализированную реализацию используемой, приложение должно быть локализовано. Для каждой поддерживаемой региональной настройки процесс локализации должен создавать набор переведенных строк сообщений, к которому приложение может получить доступ во время работы. Сортировка строк. Различные языки определяют различные правила сортировки. Элемент строковой сортировки должен использовать механизм, который понимает правила сортировки по языковому контексту строк. На практике, это включает понимание подробностей базовой символьной кодировки. Приложения выполняют строковую сортировку, не зная источника текста строки. То есть функция сортировки не извлекает отсортированных строк из некоторого хранилища локализованного текста. Вместо этого программа сортирует строки, которые уже были извлечены из локализованного хранилища. Функциям сортировки не нужно знать источник происхождения строки. Им нужен лишь языковой контекст и должным образом кодированная строка. Форматирование даты, времени, числовых и денежных значений. В различных ре гионах используют различные форматы написания дат, времени и чисел. Например, в Европе люди пишут даты и числа не так, как жители Соединенных Штатов. Француз ский пользователь пишет дату, время и числовые величины с помощью следующих форм: 25 decembre 2002 2002/12/25 25/12/2002 08.30 14.45 20.000,45 (двадцать тысяч и сорок пять сотых) В Соединенных Штатах, однако, те же самые значения обычно пишутся следующим образом: December 25, 2002 12/25/2002 8:30 am 2:45 pm 20,000.45 (двадцать тысяч и сорок пять сотых) Интернационализированная программа должна отображать формат дат, времени и чисел в соответствии с требованиями местной специфики среды исполнения. Программы не выбирают эти отформатированные значения из некоторой базы данных, они высчитывают их динамически тем же образом, как и динамически сортируют строки. Поддержка календаря и временных зон. Календари, хотя связаны с датами и временем, определяют другие свойства и функциональные возможности. Различие заключается в том, что календари выполняют вычисления, которые включают даты и время, в то время как объекты даты и времени поддерживают форматирование и отображают эти значения. В реальности решение интернационализации на любой платформе ограничивается ресурсами и механизмами программного интерфейса приложения, доступными приложениям. Легкость, с которой программист может реализовать поддержку интернационализации — и полнота этой поддержки — зависят в значительной степени от уровня явной поддержки основных областей разработки интернационализации. Платформа MIDP предоставляет следующие элементы для поддержки разработки интернационализации: — Классы — Системные свойства: microedition.encoding, microedition.locale; — Изменение кодировки: пакет java.io; — Определяемые пользователем атрибуты набора MID-летов: файл дескриптора приложения (файл JAD); — Извлечение ресурсов (файлов) из файла JAR набора MID-летов: Class.getResourceAsStream(String resourceName). Пакет java.util MIDP содержит три класса, которые связаны с интернационализацией, а именно: Классы Большинство реализаций MIDP поддерживает только одну региональную настройку. В действительности спецификация MIDP требует от реализации поддержки лишь одной региональной настройки и одной временной зоны — время по Гринвичу (GMT). Спецификация MIDP требует от реализаций поддержки только одной региональной настройки. Реализации MIDP в настоящее время не определяют достаточной поддержки для интернационализации и локализации, которые требуют слишком много ресурсов. В реальности разработчики приложений, надеющиеся поставить приложения для нескольких платформ, должны встроить поддержку региональных настроек вместо той, что поддерживается реализацией производителя. Чтобы внедрить поддержку настоящих интернационализации и локализации, разработчики должны создавать программные интерфейсы уровня приложений, которые поддерживают многочисленные временные зоны, календари и так далее. Производители могут предоставлять некоторую часть этой поддержки в форме родных программных интерфейсов приложений и библиотек Java, которые находятся поверх их реализаций MIDP. Разработчики должны знать о том, какой уровень интернационализации и локализации предоставляется, если они вообще предоставляются, и соответственно планировать создание своих приложений. Поддержка реализации одной временной зоны и одного календаря может быть достаточной в большинстве случаев. Производители могут, однако, предоставить реализации дополнительные ресурсы календаря и временных зон. Мотив такого рода дополнительной поддержки заключается в поддержке многих языков. Производители устройств обычно поддерживают региональную настройку, связанную с целевым рынком устройства. Реализация, которая поддерживает только одну региональную настройку, не будет интернациональной. Хотя этот подход, вероятно, работает на большинстве рынков сегодня, все может измениться в скором будущем. Поддержка одной региональной настройки не поддерживает многоязыковой работы. Приложениям может понадобиться выполнять работу с сообщениями в одной региональной настройке, задавать денежный формат в другой и так далее. Читатели, знакомые с поддержкой интернационализации платформы J2SE, заметят, что в MIDP довольно заметно не хватает всесторонней поддержки интернационализации (смотри пакет Java.text J2SE). Причина опять же заключается в ограниченности среды мобильных устройств. MIDP не имеет пакета Java.text. В действительности MIDP не поддерживает API для таких свойств интернационализации, как пакеты ресурсов, форматирование сообщений, числовое форматирование, чувствительные к региональным настройкам форматы дат и времени и так далее. В MIDP также отсутствуют новые свойства интернационализации пакета Java.text JDK версии 1.4, такие, как сортировка, двунаправленная текстовая поддержка, аннотации, атрибуты и так далее. В основном проблема всех разработок интернационализации заключается в поиске механизма, который дает возможность приложениям извлекать правильную версию локализованных ресурсов при работе. В отличие от J2SE, MIDP не имеет реального API или классов, которые поддерживают общее извлечение локализованных ресурсов. Здесь нет класса ResourceBundle или каких-либо его подклассов. Приложения MIDP должны создавать свои собственные механизмы для определения и извлечения локализованного ресурса. В реальности, наиболее жизнеспособными подходами являются: — извлечение локализованных ресурсов из файла JAD; — извлечение локализованных ресурсов из текстового файла, который является частью файла JAR приложения; — извлечение локализованных ресурсов из классификационного файла Java, такого, как определяемый приложением механизм упаковки пакетов ресурсов стиля J2SE. Каждый из трех примеров разработок, показанных в разделе Разработка решения интернационализации приложения MIDP, использует один из трех механизмов как основу своей разработки. К сожалению, в MIDP также отсутствует явная поддержка организации сообщений. В отличие от J2SE, MIDP не предлагает API для работы с сообщениями и здесь нет класса MessageFormat. При разработке решения организации работы с сообщениями в приложении MIDP, разработчики должны ра матривать следующие вопросы: — местонахождение локализованных данных; — механизм получения доступа к локализованным данным; — используемый формат локализованных данных и символьная кодировка; — следы локализованных ресурсов; производительность выполнения; — проблемы процесса локализации, такие, как управление ресурсами разработки, восстановление и пр.; — беспроводные сетевые среды, среды инициализации приложений и проблемы установки. MIDP не поддерживает строковую сортировку. Если ваши приложения нуждаются в выполнении лексикографической сортировки какого-либо вида, вам придется спроектировать и реализовать ваш собственный механизм для ее выполнения. Хотя подобная поддержка существует в J2SE, классы затрачивают слишком много ресурсов для сред устройств MIDP в настоящее время. MIDP не предоставляет поддержки форматирования дат, времени, числовых или денежных значений. В MIDP нет классов платформы J2SE, которые поддерживают это форматирование: здесь нет классов DateFormat, NumberFormat и DecimalFormat. Однако производители могут предоставлять определяемые реализацией классы для поддержки этих возможностей форматирования. MIDP определяет классы Класс Date просто представляет определенный экземпляр формата времени в Универсальном синхронизированном времени (UTC). В MIDP нет поддержки изменения значения Date на представление временного значения в любой другой временной зоне или для форматирования временных значений при отображении пользователям. Платформа J2SE, однако, имеет связанные классы (такие, как DateFormat), которые могут форматировать значения даты в манере, принятой в данном регионе. В MIDP нет таких классов. MIDP поддерживает временные зоны с помощью своего класса Метод Метод Time Опять же в отличие от платформы J2SE в MIDP есть только ограниченная поддержка календаря. Класс java.util.Calendar абстрактен. Поэтому каждая платформа MIDP будет предоставлять, по крайней мере, одну конкретную реализацию. Скорее всего, она не будет интернационализирована. Конкретный подкласс платформы Calendar, вероятнее всего, реализует один определенный календарь, такой, как грегорианский или лунный. Он может соответствовать контекстам региональных настроек, в которых вы размещаете ваши приложения, а может и не соответствовать. Метод Calendar.getlnstance(TimeZone zone) выдает объект Calendar, который использует указанную временную зону и региональную настройку платформы, установленную по умолчанию. Обратите внимание, что этот фабричный метод не делает класс Calendar полностью интернационализированным классом. Он все еще не выдает соответствующий календарь, основываясь на региональном контексте. Например, если вы укажете китайское стандартное время (Chinese Standard Time), вы не получите объект, который представляет лунный календарь, используемый в Китае, во всех реализациях MIDP. Это означает, что вам нужно знать, какой календарь поддерживается вашей платформой, и согласуется ли он с региональной настройкой, поддерживаемой реализацией. В этом разделе представлены три подхода к разработке поддержки интернационализации и локализации приложений MIDP. Эти подходы были отобраны на основе доступных в MIDP API, которые могут предоставить некоторый уровень поддержки интернационализации, или тех, что включены в MIDP для работы с интернационализацией. Использование атрибутов МID-лета для определения локализованных ресурсов Как вы знаете, вы можете размещать определяемые пользователем атрибуты в файле JAD вашего приложения. Это означает, что вы можете использовать файл JAD для определения атрибутов MID-лета, которые представляют локализованные ресурсы, используемые вашим приложением. В данном подходе программы больше не вставляют ресурсы (например, текстовую строку) в приложение. Вместо этого программы размещают ресурсы в файле JAD. Программа ищет ресурс, извлекая значение некоторого атрибута. Программист определяет имена атрибутов так, чтобы они содержали компонент, который представляет контекст региональной настройки. Таким образом программы могут извлекать версию ресурса, который совместим с их контекстом региональной настройки среды исполнения. Для демонстрации данного подхода я вновь использовал демонстрационную программу HelloWorld из главы 3. Приложение переименовано на IISNDemo для отличия его от оригинальной версии. В листинге 9.1 показан файл дескриптора приложения, используемый программой IISNDemo. Несколько новых атрибутов были добавлены в файл JAD. Они представляют собой текстовые строки, которые пользователь видит во время исполнения приложения. Обратите внимание, что существует две версии каждой из данных строк: одна английская и одна французская. Этот файл поддерживает выполнение приложения в английской и французской языковых средах. I18NDerao-alert-en_US: Alert I18NDemo-alert-fr_FR: Alerce H8NDemo-alert_text-en_US: The button was pressed I18NDemo-alert_text-f#163;_FR: Le bouton a ete presse I18NDemo-alert_title-en_US: Button Pressed I18NDemo-alert_title-fr_FR: Eouton a ete Presse I18NDemo-cancel-en_US: Cancel!18NDemo-cancel-fr_FR: Quitter I18NDemo-exit-en_US: Exit IlSNDemo-exit-fr_FR: Sortie I18NDemo-greeting-en_US: Another MIDlet! I18NDerao-greeting-fr_FR: Un autre MIDlet! I18NDemo-help-en_US: Help I18NDemo-help-fr_FR: Aider I18NDemo-item-en_US: Item I18NDemo-item-fr_FR: Item, I18NDemo-menu-en US: Menu I18NDemo-menu-fr_Fr: Menu I18NDemo-ok-en_US: OK I18NDemo-ok-fr_FR: OK I18NDe: r.o-sayhi-en_US: Say hi I18NDemo-sayhi-fr_FR: Dis bonjour I18NDemo-screen-en_US: Screen I18NDemc-screen-fr_FR: Ecran I18NDemo-stop-en_US: Stop I18NDemo-stop-fr_FR: Arreter I18NDemo-title-en_US: Hello, World I18NDemo-title-fr_FR: A116, tout le Monde MIDlet-1: I18N Demo 1, I18n.png, I18NDemo MIDlet-Info-URL: MIDlet-Jar-Size: 19101 MIDlet-Jar-URL: ilSn.jar MIDlet-Name: I18n MIDlet-Vendor: Vartan Piroumian MIDlet-Version: 1.0 Имена атрибутов в файле JAD, показанные в листинге 9.1, приобретают следующую форму: lt;название МID-летаgt;-lt;ключgt;-lt;обозначение региональной настройкиgt; Например, следующие два атрибута определяют заголовок MID-лета на английском и французском языках: I18NDemo-title-en_US: Hello, World. I18NDemo-title-fr_FR: A116, tout le Monde В листингах 9.2 и 9.3 показаны два файла, которые составляют исходный код приложения. Они определяют и реализуют схему поиска атрибутов, отражаемую именами атрибутов в файле JAD. Программа извлекает версию атрибута, связанного с контекстом региональной настройки, в котором приложение работает. Проектирование интернационализации обуславливает использование указанной схемы для того, чтобы приложение было способно найти локализованные ресурсы в файле JAD. Данный пример демонстрирует, как разработка решения интернационализации вовлекает конфигурирование локализованных ресурсов и присваивание имен атрибутам. 1 import javax.microedition.midlet.MIDlet; 2 3 import javax.microedition.Icdui.Display; 4 import javax.microedition.Icdui.Displayable; 5 import javax.microedition.Icdui.Form; 6 7 /** 8 Первая версия приложения IlSNDemo. 9 10 lt;рgt;Данная версия демонстрирует простейший подход к 11 загрузке локализованных ресурсов из файла JAD MID-лета. 12 этот подход быстро становится непригодным при большом 13 количестве ресурсов. И он полезен только для текстовых 14 сообщений, но не для других видов локализованных 15 ресурсов. 16 */ 17 public class IlSNDemo extends MIDlet 18 { 19 // Региональная настройка, указанная для выполнения в 20 // данном MID-лете. 21 private String locale; 22 23 // Displayable. Этот компонент отображается 24 // на экране. 25 private HelloForm form; 26 27 // Экземпляр Display. Данный объект управляет всеми 28 // компонентами Displayable данного MID-лета. 29 private Display display; 30 31 // Экземпляр MID-лета. 32 private static IlSNDemo instance; 33 34 // Префикс имен атрибутов локализуемых 35 // ресурсов. 36 String attrPrefix = new String("I18NDemo-"); 37 38 /** 39 Конструктор No-arg. 40 */ 41 public I18NDemo() 42 { 43 super(); 44 instance = this; 45 } 46 47 /* 48 Получает экземпляр данного класса, который существует в 49 работающем приложении. 50 51 Звоззращает экземпляр, созданный при запуске 52 приложения. 53 */ 54 public static IlSNDemo getlnstance() 55 { 56 if (instance == null) 57 { 58 instance = new IlSNDemo (); 59 } 60 return instance; 61 } 62 63 /** 64 Запускает. MID-лет. Получает текущую региональную 65 настройку для реализации. Использует ее для 66 создания префикса ключей атрибутов всех 67 локализованных ресурсов. Названия локализованных 68 ресурсов в файле JAD соответствуют 69 совместимой схеме имен. 70 */ 71 public void startApp() 72 { 73 // Извлекает региональную настройку из программного 74 // обеспечения AMS. Региональная настройка должна быть 75 // установлена прежде, чем данный MID-лет начнет выполняться. 76 locale = 77 System.get Property("microedition.locale"); 78 79 // Создает элемент Displayable. Получает локализованную 80 // String, которая представляет заголовок 81 // Form, из определенных пользователем атрибутов файла 82 // JAD. Получает все локализованные строки таким 83 // образом. 84 String formTitle = getResource("title"); 85 form = new HelloForm(formTitle); 86 87 // Это приложение просто отображает единственную форму, 88 // созданную ранее. 89 display = Display.getDisplay(this); 90 display.setCurrent(form); 91 } 92 93 /** 94 Выдает значение, связанное с указанным 95 ключом из списка определяемых пользователем 96 ресурсов MID-лета в файле JAD приложения. 97 98 @param key — ключ пары «ключ-значение». 99 100 @выдает значение, связанное с указанным 101 ключом. 102 */ 103 public String getResource(String key) 104 { 105 StringBuffer index = new 106 StringBuffer(«ttrPrefix); 107 String value; 108 109 index.append(key); 110 index.append(-); 111 index.append(locale); 112 113 value = getAppProperty(index.toString ()); 114 return value; 115 } 116 117 /** 118 Закрываем приложение. Уведомляем 119 реализацию о выходе. 120 */ 121 public void quit(), 122 { 123 notifyDestroyed (); 124 } 125 126 public void destroyApp(boolean destroy) 127 { 128 129 } 130 131 public void pauseApp() 132 ( 133 134 } 135 } 1 import javax.raicroedition.midlet.MIDlet; 2 3 import javax.microedition.Icdui.Alert; 4 import javax.microedition.Icdui.AlertType; 5 import javax.microedition.Icdui.Command; 6 import javax.microedition.Icdui.CommandListener; 7 import javax.microedition.Icdui.Display; 8 import javax.microedition.Icdui.Displayable; 9 import javax.microedition.Icdui.Form; 10 11 /* 12 Данный класс определяет Form, которая отображает 13 простой текст и меню команд. Цель данного класса 14 заключается в демонстрации i18n и 110n 15 видимых пользователю атрибутов. Класс извлекает 16 локализованные ресурсы из программного обеспечения 17 управления приложениями. 18 */ 19 открытый HelloForm дополняет Form 20 { 21 // Заголовок данной Form, устанавливаемый по умолчанию. 22 private static final String DEFAULT_TITLE = 23 "Hello, World"; 24 25 // Блок прослушивания команд, который обрабатывает 26 // командные события в данной Form. 27 private MyCommandListener cl = new 28 MyCommandListener (); 29 30 //. Экземпляр дисплея, связанный с 31 // данным MID-летом. 32 Display display; 33 34 // Ссылка на связанный с данным объектом 35 // объект MID-лета. 36 I18NDemo midlet; 37 38 // Уведомление, отображаемое в ответ на 39 // активацию некоторых команд данной Form. 40 Alert alert; 41 42 // Команды, размещаемые в данной форме. 43 private Command showAlert; 44 private Command sayHi; 45 private Command cancel; 46 private Command exit; 47 private Command help; 48 private Command item; 49 private Command ok; 50 private Command screen; 51 private Command stop; 52 53 /** 54 Конструктор No-arg. Устанавливает заголовок по умолчанию 55 для данной формы. 56 */ 57 HelloForm() 58 { 59 this(DEFAULT_TITLE); 60 } 61 62 /** 63 Конструктор. 64 65 @param title — заголовок Form. 66 */ 67 HelloForm(String title) 68 { 69 super(title); 70 71 midlet = IISNDemo.get Instance() 72 73 // Добавляет строковый элемент в форму. 74 String msg = midlet.getResource("greeting"); 75 append(msg); 76 77 display = Display.getDisplay(midlet); 78 79 // Добавляет MyCommandListener в Form для прослушивания 80 // события нажатия клавиши «Back», которое должно 81 // создавать всплывающее диалоговое уведомление Alert. 82 setCommandListener(cl); 83 84 showAlert = new 85 Command(midlet.getRe source("alert"), 86 Command.SCREEN, 1); 87 addCommand(showAlert); 88 89 sayHi = new. 9 °Command(midiet.getResource("sayhi"), 91 Command.SCREEN, 1); 92 addCommand(sayHi); 93 94 cancel = new 95 Command{midlet.getResource("cancel"), 96 Command. SCREEN, 1); 97 addCommand(cancel); 98 99 exit = new 10 °Command(midlet.getResource("exit"), 101 Command.SCREEN, 1); 102 addCommand(exit); 103 104 help = new 105 Command(midlet.getResource("help"), 106 Command.SCREEN, 1); 107 addCommand(help); 108 109 item = new 11 °Command(midiet.getResource("item"), 111 Command.SCREEN, 1); 112 addCommand(item); 113 114 ok = new 115 Command(midlet.getResource("ok"), 116 Command.SCREEN, 1); 117 addCommand(ok); 118 119 screen = new 12 °Command(midlet.getResource("screen"), 121 Command.SCREEN, 1); 122 addCommand(screen); 123 124 stop = new 125 Command(midlet.getResource("stop"), 126 Command.SCREEN, 1); 127 addCommand(stop); 128 } 129 130 // Данный класс просто прослушивает активацию 131 // какой-либо команды. Экземпляр HelloForm 132 // устанавливает экземпляр данного класса как 133 // свой блок прослушивания команд. Экземпляр 134 // объекта не проверяет информацию команды, а 135 // просто отображает модальное Ale показывающее, 136 // что экранная клавиша была активирована пользователем. 137 private class MyCoramandListener 138 implements CommandLister.er 139 { 140 public void commandAction(Command c, 141 Displayable d) 142 { 143 String title = 144 midlet.getResource("alert_title"); 145 String msg = null; 146 147 if (c == showAlert) 148 { 149 msg = midlet.getResource("alert_text"); 150 alert = new Alert(title, 151 msg, 152 null, AlertType.INFO); 153 alert.setTimeout(Alert.FOREVER); 154 display.setCurrer.t (alert, HelloForm.this); 155 } 156 else if (c == sayHi) 157 { 158 alert = new Alert("Button pressed", 159 msg, 160 r.ull, AlertType.INFO); 161 alert.setTimeout(Alert.FOREVER); 162 display.setCurrent(alert, HelloForm.this); 163 } 164 165 if (c == exit) 166 { 167 IISNDemo.get Instance(). destroyApp (true); 168 } 169 } 170 } 171 } Проблема разработки интернационализации заключается в схеме поиска, используемой для нахождения локализованных строк в файле JAD. Программно определяемый метод getResource (String key), заданный в строках с 103 по 115, на самом деле определяет и реализует схему поиска. Чтобы обнаружить ресурс, метод getResource (String key) создает имя атрибута, а затем ищет сам атрибут. Например, следующие два оператора, показанные в строках 84 и 85, выбирают строку заголовка Form, использующейся в приложении. String formTitle = getResource("title"); form = new HelloForm(formTitle); Метод создает полное имя атрибута, объединяя три строки: 118NDemo — префикс атрибута для данного приложения, идентификатор атрибута ресурса без какой-либо меняющейся в зависимости от региональной настройки информации и обозначение региональной настройки. Параметр строки title является идентификатором атрибута ресурса, а не заголовком формы. В строке 36 MID-лет определяет префикс атрибута I18NDemo-. Метод startApp() извлекает информацию о контексте региональной настройки, в котором исполняется приложение, из системного свойства microedition.locale и сохраняет его как экземпляр атрибута. Объект HelloForm использует значение, выданное вызовом getResource(), как его заголовок. Класс HelloForm в листинге 9.3 повторяет этот сценарий. Он просто вызывает getResource() для поиска локализованных значений всех текстов, которые пользователь видит во время исполнения приложения. Поскольку реализации MIDP будут, скорее всего, поддерживать только одну региональную настройку, возможно, будет лучше для приложений сделать центральной ссылку региональной настройки, которую, как вы знаете, поддерживает ваше устройство, вместо того чтобы извлекать региональную информацию из свойств системы. Альтернативный подход заключается в создании нескольких версий файла JAD приложения так, чтобы каждая версия содержала атрибуты для каждой региональной настройки. Добавьте соответствующую версию JAD для требуемого контекста региональной настройки. Конечно, вам понадобится определить контекст локальной настройки, в которой будет использоваться телефон, или просто местные настройки пользователя. В листинге 9.2 используется системное свойство microedition.locale для извлечения региональной настройки для того, чтобы акцентировать внимание на понятии динамически определяемого контекста региональной настройки и ресурсов, связанных с контекстами региональных настроек. Разграничение ресурсов для различных региональных настроек может помочь пониманию вашей разработки и сделать ваше программное обеспечение более восстанавливаемым. Не забывайте, что в будущем, когда устройства станут более мощными, реализации MIDP смогут очень хорошо поддерживать множество региональных настроек. Когда это произойдет, подход, показанный в листинге 9.2, станет более предпочтительным. Взглянув на метод getResource(), показанный в строчках с 103 по 115, вы увидите, что он использует метод MIDlet.getAppProperty() для извлечения ресурсов из файла дескриптора приложения и файла манифеста. Если атрибут существует в обоих файлах с абсолютно одинаковыми именами ключа, тогда значение извлекается из файла дескриптора приложения и значение в файле манифеста игнорируется. Если не найдено ни одного атрибута или если для ключа не найдено ни одного значения, выдается значение ноль. Если вводимый ключ не найден, сбрасывается NullPointerException. Значения атрибутов в файле JAD (или манифеста) должны быть кодированы с помощью символьной кодировки, которая поддерживает нужный язык. Существует два способа выполнения этого: Листинг 9.2 включает поддержку английской и французской региональных настроек. Символьная кодировка ISO8859-1 может представлять английский и французский алфавиты. Если вы желаете локализовать данное приложение на языки, не поддерживаемые семейством ISO8859 (китайский, например), вам придется кодировать значения атрибутов с помощью соответствующей многобайтовой символьной кодировки. Если вы выбрали первый из только что описанных подходов (кодирование с помощью символьной кодировки, предназначенной для языка региональной настройки), вам понадобится найти текстовой редактор, который поддерживает методы ввода китайского языка и записывает символы в соответствующую кодировку. Либо вы можете использовать второй подход и вводить последовательности переключения уникода Java для каждого символа. Просто найдите точку кодирования уникода для каждого символа в вашей строке. Этот подход работает, поскольку класс Java.lang.String знает, как создавать строковые объекты из последовательностей переключения уникода. Ваше приложение может затем считывать значения атрибутов и создавать из них объекты String. Вы можете определить имена атрибутов с помощью панели Settings J2MEWTK. Поскольку WTK не поддерживает ввод не-ASCII текста, однако, вы не можете определить не английский локализованный текст значений атрибутов. Чтобы ввести не английские символы, вам придется использовать текстовой редактор для ввода символов непосредственно в файл JAD. Вы можете использовать тот, что поддерживает редакторы методов ввода (IME) для назначенного языка, или вводить последовательности переключения уникода. Хотя эта разработка интернационализации и локализации, как кажется, работает прекрасно, у нее есть несколько проблем. Прежде всего, пример, который вы только что видели, обращается только к строковым ресурсам. Вам необходимо будет выполнить немного дополнительной работы для осуществления поддержки локализации других типов ресурсов, таких, как календари, средства задания формата даты и времени или даже изображения и цвета. Чтобы поддерживать нестроковые локализованные ресурсы — например, чувствительный к региональной настройке числовой форматер, — вы можете установить значение атрибута на имя класса, который реализует ваш форматер. Например, вы можете определить атрибут следующим образом: I18NDemo-number_forraat-fr_FR: NumberFormat_FR Вы извлекаете значение атрибута, а затем создаете экземпляр данного класса. Следующий фрагмент кода показывает способ, с помощью которого ваш MID-лет может это сделать. … try { String name = getAppProperty("I18NDemo-number_format-fr_FR"); // «name» теперь эквивалентно «NumberFormat_FR» Class с = Class.forName(name); NumberFormat_FR nf = (NumberFormat_FR) с. new Instance(); } catch (InstantiationException ie) { … } catch (IllegalAccessException iae) { … catch (ClassNotFoundException cnfe) { … } … Конечно, вы должны предоставить MIDP-совместимый классификационный файл Java с файлом JAR вашего приложения для того, чтобы эта схема работала. Другой недостаток использования дескрипторов приложения заключается в том, что они неправильно обращаются с файлами JAD и манифеста для программно определяемых свойств. Вы, возможно, думаете, что это просто философское размышление, но это отражается на производительности. Чтобы прочитать файлы JAD или манифеста, вы должны привлечь помощь AMS. Единственный вызов вовлекает несколько компонентов реализации. Файл JAD на самом деле не предназначен для подобного частого доступа. Вы могли заметить, что происходит снижение производительности при попытке прочесть большой файл локализованных ресурсов — или любого другого типа программно определяемых ресурсов. Кроме того, этот единственный файл JAD должен соответствовать всем МЮ-летам в наборе MID-летов, что делает его еще больше. При использовании файла JAD не в простой демонстрационной программе, а где-либо еще он станет слишком громоздким, так как число атрибутов вырастет. Другой проблемой является место для указателя ошибок. Разработчик, вручную локализующий файл JAD, легко может сделать незаметные опечатки в указанных атрибутах. А файл JAD не поддерживает вставку комментариев, которые могут помочь людям понять использование атрибута в приложении. Использование текстовых файлов приложения для определения локализованных ресурсов Второй подход к локализации использует программно определяемые текстовые файлы, которые содержат локализуемые атрибуты. Приложение с помощью данной схемы может, например, определить один файл локализованных атрибутов для каждой региональной настройки. Схема имен может соответствовать обозначению региональной настройки, например, en_US.txt для английской, fr_FR.txt для французской, ja_JP.txt для японской и так далее. В листинге 9.4 показан один пример такого файла, содержащего пары «ключ-значение» локализованных строк. Листинг 9.4. Имя данного файла — fr_FR.txt. Он состоит из франкоязычных версий строк приложения alert: Alerte alert_title: Bouton Presse alert_text: Le bouton a ete presse cancel: Quitter exit: Sortie greeting: Mon troisieme MIDlet! help: Aider item: Item menu: Menu ok: OK sayhi: Dis bonjour screen: Ecran stop: Arreter Mtle: A116, tout le Monde Этот подход по существу тот же самый, что и предыдущий, за исключением того, что теперь вы должны создавать и поддерживать файл ресурса. Любые файлы ресурсов, которые вы создаете, должны быть частью JAR приложения. Вспомните, что спецификация MIDP запрещает прямой доступ к файлам на родной платформе. Прежде чем идти дальше, важно повторить, что эта схема, как и первая, представляет собственный подход к созданию всестороннего решения интернационализации. Тем не менее, эти схемы представлены так, что вы поймете, в чем заключаются преимущества и альтернативы использования различных схем, а также то, как использовать средства различных платформ и API, доступные вам. В листингах 9.5 и 9.6 содержится код, который реализует одну возможную разработку, которая использует файлы текстовых ресурсов. Этот код считывает файлы, отформатированные подобно тому, что показан в листинге 9.4. 1 import javax.microedition.midlet.MIDlet; 2 3 import javax.microedition.Icdui.Display; 4 import javax.microedition.Icdui.Displayable; 5 import javax.microedition.Icdui.Form; 6 7 import java.io.DatalnputStream; 8 import Java.io.InputStream; 9 import Java.io.InputStreamReader; 10 import Java. io. EOFException; 11 import Java.io.lOException; 12 import Java.io.Reader; 13 import Java.io.UTFDataFormatException; 14 import Java.io.UnsupportedEncodingException; 15 16 import Java.util.Hashtable; 17 18 /** 19 Вторая версия приложения HSNDemo. 20 21 lt;рgt;Эта версия'также дехонстрирует простой способ определения 22 локализованных ресурсов. Она считывает файл, который является 23 частью файла JAR приложения (не файла JAD) 24 для загрузки локализованных ресурсов. Файл 25 состоит из набора пар «ключ-значение», одной на строку, 26 которые представляют локализованные строки. 27 MID-лет должен затем проанализировать содержимое файла 28 и создать внутреннюю хэшированную таблицу для поиска. 29 30 lt;рgt;Этот подход требует слишком много усилий по обработке 31 потока, который содержит файл 32 локализованных ресурсов. Более того, этот подход 33 не отвечает за локализацию ресурсов, которые 34 не являются строками. 35 */ 36 public class I18NDemo2 extends MIDlet 37 { 38 // Файл, который содержит ресурсы для 39 // активной локальной настройки. 40 private String resourceFile; 41 42 // Региональная настройка, указанная для выполнения данного 43 // MID-лета. 44 private String locale; 45 46 // Символьная кодировка, установленная по умолчанию, 47 // используемая данной платформой. 48 private String encoding; 49 50 // HashTable, которая содержит локализованные 51 // ресурсы. 52 private Hashtable resources = new Hashtable (); 53 54 // Displayable. Этот компонент отображается 55 // на экране. 56 private HelloForm2 form; 57 58 // Экземпляр Display. Данный объект управляет всеми 59 // компонентами Displayable данного MID-лета. 60 private Display display; 61 62 // Экземпляр данного MID-лета. 63 private static I18NDemo2 instance; 64 65 /** 66 Конструктор No-arg. 67 */ 68 public I18NDemo2() 69 { 70 super(); 71 instance = this; 72 } 73 74 /*" 75 Получает экземпляр данного класса, который существует 76 в действующем приложении. 77 78 @выдает экземпляр, созданный при запуске 79 приложения… 80 */ 81 public static I18NDemo2 getlnstance () 82 { 83 return instance; 84 { 85 86 /** 87 Запускает MID-лет. Получает текущее название 88 региональной настройки. Использует его для создания 89 имени файла, который содержит локализованные 90 ресурсы региональной настройки. Файл ресурса 91 находится в файле JAR приложения. 92 */ 93 public void startApp() 94 { 95 // Извлекает региональную настройку из программного 96 // обеспечения AMS. Региональная настройка должна быть 97 // установлена до выполнения данного MID-лета. 98 locale = 99 System.getProperty("microedition.locale"); 100 101 // Названия файлов локализованных ресурсов, соответствующих 102 // форме: lt;языкgt;_lt;странаgt;.txt. 103 // Создает строку имени файла и передает ее в 104 // метод, который открывает файл и извлекает 105 // содержимое. 106 resourceFile = locale +".txt"; 107 int status = loadResources(resourceFile); 108 109 if (status lt; 0) 110 { 111 quit(); 112 return; 113 } 114 115 // Создаем элемент Displayable. Получаем 116 // локализованную String, которая представляет 117 // заголовок Form. 118 String formTitle = getResource ("title"); 119 form = new HelloForm2(formTitle); 120 121 // Это приложение просто отображает одну. 122 // форму, созданную выше. 123 display = Display.getDisplay (this); 124 display.setCurrent(form); 125 } 126 127 /** 128 Загружает определенные пользователем ресурсы приложения 129 из указанного файла. Файл является частью файла JAR 130 приложения, расположенного на реальном устройстве. 131 J2MEWTK хранит файл в файле JAR приложения, расположенном 132 в директории приложения bin/. 133 134 @param file — имя определенного пользователем файла 135 ресурса приложения. 136 */ 137 private int loadResources(String file) 138 { 139. Class с = getClass (); 140 141 if (file == null) 142 { 143 return -1; 144 } 145 InputStream is = null; 146 is = с. getResourceAsStream(file); 147 if (is == null) 148 { 149 return -1; 150 } 151 Reader reader = new InputStreamReader(is); 152 processStream(reader); 153 return 0; 154 } 155 156 /** 157 158 */ 159 private void processStream(Reader stream) 160 { 161 if (stream == null) 162 { 163 return; 164 } 165 StringBuffer key = new StringBuffer();; 166 StringBuffer value = new StringBuffer ();; 167 while (true) 168 { 169 // Считываем строку. Предполагается, что каждая строка 170 // содержит ключ и значение, 171 // отделенные двоеточием. Если -1, значит 172 // мы достигли конца файла. 173 key.deletef(), key.length()); 174 value.delete(0, value.length()); 175 int status = readLine(key, value, stream); 176 if (status == -1) 177 { 178 break; 179 } 180 181 // Вставляем этот ресурс в хэшированную таблицу 182 // ресурсов приложения. 183 resources.put(key, value); 184 } 185 } 186 187 /** 188 Считывает и обрабатывает следующую не пустую строку 189 из потока. Формат строки ожидается следующий 190 lt;ключgt;[\t]*:[и]*lt;значениеgt;, где 191 lt;ключgt; and lt;значениеgt; являются метками, состоящими 192 из буквенных символов или знаков пунктуации, но не 193 из пустых знаков пробела. 194 */ 195 private int readLine(StringBuffer key, 196 StringBuffer value, 197 Reader stream) 198 { 199 if (key == null || value == null || 200 stream == null) 201 { 202 return -1; 203 } 204 205 try 206 { 207 char c; 208 while (true) 209 { 210 // Пропускаем символы новой строки. 211 while (true) 212 { 213 с = (char) stream.read (); 214 if (c == r\n') 215 { 216 continue; 217 } 218 break; 219 } 220 221 if (lisWhiteSpace(c) Si!isDelimeter(c)) 222 { 223 key.append(c); 224 } 225 226 // Пропускаем впередиидущий пробел. 227 while (true) 228 { 229 с = (char) stream.read(); 230 if (isWhiteSpace (c)) 231 { 232 continue; 233 } 234 break; 235 } 236 237 if (lisWhiteSpace(c) Samp;!isDelimeter(c)) 238 { 239 key.append (с); 240 } 241 242 // Считываем ключ. 243 while (true) 244 { 245 с = (char) stream.read(); 246 if (isWhiteSpace(c) II isDeliraeter(c)) 247 { 248 break; 249 } 250 else 251 { 252 key.append(c); 253 } 254 } 255 256 // Пропускаем пробел, идущий перед или 257 // после символа. 258 while (true) 259 { 260 с = (char) stream.read(); 261 if (isWhiteSpace(c) II isDelimeter(c)) 262 { 263 continue; 264 } 265 value.append(c); 266 break; 267 } 268 269 // Считываем остальную часть значения метки. 270 while (true) 271 { 272 с = (char) stream.read(); 273 if (c == \n') 274 { 275 break; 276 } 277 else 278 { 279 value.append(c); 280 } 281 } 282 break; 283 } 284 } 285 catch (lOException ioe) 286 { 287 ioe.printStackTrace(); 288 return -1; 289 } 290 return 0; 291 } 292 293 /** 294 295 */ 296 private boolean isWhiteSpace(char c) 297 { 298 if (c == И с == \t') 299 { 300 return true; 301 } 302 else 303 { 304 return false; 305 } 306 } 307 308 /** 309 310 */ 311 private boolean isDelimeter(char c) 312 { 313 if (c ==:) 314 { 315 return true; 316 } 317 return false; 318 } 319 320 /** 321 Выдает значение, связанное с указанным 322 ключом из пакета ресурсов приложения. 323 324 @param key — ключ пары «ключ-значение». 325 326 @выдает значение, связанное с 327 указанным ключом. 328 */ 329 private String getResource(String key) 330 { 331 if (resources == null) 332 { 333 return null; 334 } 335 return (String) resources.get (-key); 336 } 337 338 /** 339 Прекращает выполнение. Запрашивает реализацию 340 на завершение данного MID-лета. 341 */ 342 public void quit() 343 { 344 notifyDestroyed (); 345 } 346 347 public void destroyApp(boolean destroy) 348 { 349 350 } 351 352 public void pauseApp() 353 { 354 355 } 356 } 1 import javax.microedition.midlet.MIDlet; 2 3 import javax.microedition.Icdui.Alert; 4 import javax.microedition.Icdui.AlertType; 5 import javax.microedition.Icdui.Command; 6 import javax.microedition.Icdui.CommandListener; 7 import javax.mi'croedition. Icdui.Display; 8 import javax.microedition.Icdui.Displayable; 9 import javax.microedition.Icdui.Form; 10 11 /** 12 Данный класс определяет Form, которая отображает некоторый 13 простой текст и меню команд. Цель данного класса 14 продемонстрировать интернационализацию и локализацию 15 видимых пользователю атрибутов. Он работает с классом 16 I18NDemo2. 17 */ 18 public class HelloForm2 extends Form 19 { 20 // Заголовок даннвй Form, устанавливаемый по умолчанию. 21 private static final String DEFAULTJTITLE = 22 "Hello, World"; 23 24 // Блок прослушивания команд, который обрабатывает 25 // командные события в данной Form. 26 private MyCommandListener cl = new 27 MyCommandListener (1; 28 29 // Экземпляр дисплея, связанный с данным 30 // MID-летом. 31 Display display; 32 33 // Ссылка на связанный с данным объектом 34 // объект MID-лета. 35 IlSNDemo midlet; 36 37 // Уведомление, отображаемое в ответ на активацию 38 // некоторой из команд данной Form. 39 Alert alert; 40 41 private Command showAlert; 42 private Command sayHi; 43 private Command cancel; 44 private Command exit; 45 private Command help; 46 private Command item; 47 private Command ok; 48 private Command screen; 49 private Command stop; 50 51 /** 52 Конструктор No-arg. Устанавливает заголовок 53 по умолчанию данной формы. 54 */ 55 HelloForm2() 56 { 57 this(DEFAULT_TITLE); 58 } 59 60 /** 61 Конструктор. 62 63 @param title — Заголовок данной Form. 64 */ 65 KelloForm2(String title) 66 { 67 super (title); 68 69 midlet = IlSNDemo.getlnstance(); 70 71 // Добавляет строковый элемент в форму. 72 String msg = midlet.getResource("greeting"); 73 append (msg); 74 75 display = Display.getDisplay(midlet); 76 77 // Добавляет MyCommandListener в Form для прослушивания 78 // события нажатия клавиши «Back», которое должно 79 // создавать всплывающее уведомление Alert. 80 setCommandLiscener (cl); 81 82 showAiert = new 83 Command(midlet.getResource("alert"), 84 Command.SCREEN, 1); 85 addCommand(showAlert); 86 87 sayHi = new 88 Command(midlet.getResource("sayhi"), 89 Command.SCREEN, 1); 90 addCommand(sayHi); 91 92 cancel = new 93 Command(midlet.getResource("cancel"), 94 Command.SCREEN, 1); 95 addCommand(cancel); 96 97 exit = new 98 Command(midlet.getResource("exit"), 99 Command.SCREEN, 1); 100 addCommand(exit); 101 102 help = new 103 Command(midlet.getResource("help"), 104 Command.SCREEN, 1); 105 addCommand(help); 106 107 item = new 108 Command(midlet.getResource ("item"), 109 Command.SCREEN, 1); 110 addCommand(item); 111 112 ok = new 113 Command(midlet.getResource("ok"), 114 Command.SCREEN, 1); 115 addCommand(ok); 116 117 screen = new 118 Command(midlet.getResource("screen"), 119 Command.SCREEN, 1); 120 addCommand(screen); 121 122 stop = new 123 Command(midlet.getResource("stop"), 124 Command.SCREEN, 1); 125 addCommand(stop); 126 } 127 128 // Данный класс просто прослушивает активацию 129 // какой-либо команды. Экземпляр HelloForm 130 // устанавливает экземпляр данного класса как 131 // свой блок прослушивания команд. Экземпляр 132 // объекта не проверяет информацию команды, 133 // а просто отображает модальное Alert, показывающее, 134 // что экранная клавиша была активирована пользователем. 135 public class MyCommandListener 136 реализует CommandListener 137 { 138 public void commandAction(Command c, 139 Displayable d) 140 { 141 String title = 142 midlet.getResource("alert_title"); 143 String msg = midlet.getResource("alert_text"); 144 145 if (с == showAlert) 146 { 147 alert = new Alert(title, 148 msg, 149 null, AlertType.INFO); 150 alert.setTimeout(Alert.FOREVER); 151 display.setCurrent(alert, HelloForm2.this); 152 } 153 else if (c == sayHi) 154 { 155 alert = new Alert(title, 156 msg, 157 null, AlertType.INFO); 158 alert.setTimeout(Alert.FOREVER); 159 display.setCurrent(alert, HelloForm2.this); 160 } 161 162 if (c == exit) 163 { 164 I18NDemo.getInstance-(). destroyApp (true); 165 } 166 } 167 } 168 } Наиболее проблематичным аспектом данного подхода является то, что вы, разработчик, должны создать инфраструктуру, которая позволит вашим приложениям считывать и анализировать файлы ресурсов. Также приложение должно создавать структуры внутренних данных, которые содержат локализованные ресурсы, считанные из файлов. Самым проблематичным аспектом создания этой инфраструктуры является предоставление адекватной обработки потоков, особенно обработки потоков для поддержки считывания значений строковых атрибутов. Метод MIDlet.getAppProperty(), использовавшийся в предыдущей схеме, основанной на файле JAD, извлекает информацию об обработке потоков. Но в данной схеме вы должны проделать всю эту работу самостоятельно. Метод Class.getResourceAsStream(String name) является единственным способом, с помощью которого MID-лет может считывать файл из JAR приложения. Параметр имени представляет собой имя файла без информации о пути. Этот метод выдает объект java.io.InputStream, который является байтовым потоком. Вы должны преобразовать этот байтовый поток в символьный для того, чтобы считывать значения строковых атрибутов в вашей программе. Единственный практичный способ преобразовать байтовые потоки в символьные — это использовать класс java.io.InputStreamReader. Вы создаете экземпляр данного класса, пересылая ваш объект InputStream в конструктор InputStreamReader. В строках с 137 до 154 листинга 9.5 символьный поток срздает определяемый приложением метод Чтобы преобразовывать из байтов в символы, вы должны знать символьную кодировку файла ресурса, который вы считываете. В листинге 9.5 происходит преобразование из кодировки ISO8859-1 (используемой файлом en_US.txt) в уникод. При считывании символьных данных в программу конечной кодировкой всегда является уникод. Java всегда представляет символы и строки внутренне с помощью уникода. Первая форма конструктора InputStreamReader, показанная в таблице 9.1 и использованная в листинге 9.5, преобразует из символьной кодировки, устанавливаемой платформой по умолчанию, в уникод. Если ваш файл ресурсов использует кодировку, отличную от кодировки, используемой платформой по умолчанию, вы должны использовать второй конструктор InputStreamReader, который принимает аргумент, указывающий- кодировку потока, считываемого вами. Важным решением проектировки интернационализации и локализации является выбор символьной кодировки ваших файлов ресурсов. Значения строковых атрибутов локализованы и должны быть кодированы с помощью символьной кодировки, которая поддерживает язык локализации. Ключи атрибутов, однако, не локализуются, и они могут быть написаны с помощью ASCII. Варианты выбора кодировки должны учитывать следующее: — ключи и значения атрибутов должны быть кодированы с помощью одной и той же символьной кодировки; — все файлы ресурсов для всех региональных настроек должны использовать одну символьную кодировку. Лучше всего использовать одну символьную кодировку для всего файла. В противном случае вам понадобится создать два символьных потока: один для анализа ключей атрибутов и второй для анализа значений. Подобная схема добавляет вашей обработке потоков ненужный уровень сложности. Сходным образом, если вы используете другую символьную кодировку для ресурсов каждой региональной настройки, ваше приложение должно создавать свою символьную кодировку отдельно для каждой региональной настройки. Ему придется иметь некоторый способ определения кодировки файла ресурса для того, чтобы создать соответствующий символьный поток. Намного легче использовать одну символьную кодировку для всех региональных настроек. Двумя практичными вариантами символьных кодировок являются последовательности переключения кодов UTF-8 и Unicode Java. UTF-8 — это код изменяющейся ширины, который поддерживает определения символьной кодировки ASCII. Он вмещает все символы всех языков. К несчастью, потоковые классы MIDP не имеют удобных методов, таких, как DatalnputStream.readUTFO J2SE, для считывания строк UTF. Итак, вам все равно придется выполнять анализ потока собственноручно. Другая сложность заключается в том, что теперь вам придется записывать ваши файлы ресурса в формате UTF-8. Поэтому вам нужны текстовые редакторы и другие инструменты, которые поддерживают создание файлов, кодированных в UTF-8. Простейшее решение — использовать последовательности переключения кода Unicode Java для кодирования значений строковых атрибутов. Каждая последовательность переключения кода представляет собой уникальный символ уникода. У этого подхода есть два преимущества. Во-первых, вы можете записывать эти последовательности в файл как символы ASCII. Во-вторых, уникод поддерживает все языки. В листинге 9.4 используется ISO8859-1. Он адекватен для франкоязычных ресурсов, но в то же время он может не поддерживать корейский язык, например, тогда как уникод поддерживает. Вы можете использовать некоторые другие многобайтные кодировки, но затем вам придется положиться на редакторы методов ввода и другие инструменты для считывания и записи файла ресурса в этой кодировке. Если вы используете другие многобайтные кодировки, вам придется учитывать проблемы совместимости и возможность отладки. Есть ли у вас инструменты — текстовые редакторы, редакторы методов ввода и так далее — для поддержки всех ваших региональных настроек? Есть ли у вас те же инструменты, что и у вашей команды по локализации или, по крайней мере, совместимые с ними? Кто будет следить за правильностью работы вашего приложения? Есть ли у них те же инструменты, что и у остальных? Все эти аспекты надо рассматривать при выборе метода кодировки. Когда вы создали ваш объект Название Конструктора и метода java.io. Reader — Элемент — Описание InputStreamReader (InputStream is) — Конструктор — Создает поток, который преобразует из кодировки, устанавливаемой платформой по умолчанию, в уникод InputStreamReader (InputStream is, String enc) — Конструктор — Преобразует из указанной кодировки в уникод void closed — Метод — Закрывает поток void mark(int readAheadLimit) — Метод — Устанавливает лимит опережающего считывания для метки boolean markSupported() — Метод — Показывает, поддерживает ли данный поток разметку int read() — Метод — Считывает один символ int read (char [] cbuf, int off, int len) — Метод — Считывает количество «len» символов в части символьного массива, начиная от указанной сдвига boolean ready() — Метод — Показывает, должно ли здесь считываться что-либо void reset() — Метод — Переустанавливает поток в последнюю позицию метки long skip (long n) — Метод — Пропускает указанное число символов Единственной пропущенной задачей является анализ символов, которые вы считываете. К сожалению, MIDP не имеет удобных классов, таких, как класс StringTokenizer J2SE, который облегчает расстановку меток. Вы должны поэтому самостоятельно проанализировать локализованные ресурсы, один символ за раз, с помощью любой из двух форм перегрузки Reader.read(). Если вы использовали такой формат файла, как в листинге 9.4, самое меньшее, что вы должны сделать затем, это отделить поле ключа от поля значения для каждого атрибута, убрать пробелы и так далее. В листинге 9.5 весь код в строках 127–318 посвящен обработке потока. Одним из существенных недостатков данного проектирования интернационализации является дополнительное кодирование, необходимое для создания. анализаторов потоков. Кроме того, что это включает дополнительную работу по разработке данного кода, ваше приложение также ограничивается средой исполнения. Файл ввода/вывода может отнять много рабочих ресурсов и выдать при этом только минимально приемлемую производительность. Это важно рассматривать при разработке приложений MIDP. Кроме того, вам необходимо учитывать создание портативной библиотеки классов обработки ввода-вывода, которую вы можете использовать и для других приложений. Будет лишней тратой времени на проектирование повторно реализовать данную инфраструктуру вновь и вновь. За исключением этих недостатков этот второй подход в значительной степени сходен с первым, который использует файл JAD для хранения локализованных ресурсов. Как и подход с файлом JAD, этот подход может обрабатывать нестроковые ресурсы, определяя атрибуты, чьи значения являются именами чувствительных к региональным настройкам классов. Использование классификационных файлов Java для определения интернационализированных ресурсов В данном третьем подходе приложения определяют классы Java, которые содержат локализованные ресурсы. Каждый класс содержит ресурсы для одной региональной настройки. Файлы откомпилированы и упакованы как часть JAR приложения. При работе локализованные ресурсы затем достаются с помощью создания экземпляра соответствующего класса. Эта разработка аналогична разработке иерархии пакетов ресурсов J2SE. Классы Этот подход к разработке интернационализации определяет свою собственную версию классов ResourceBundle и ListResourceEundle J2SE. В листингах 9.7 и 9.8 показаны их реализации, которые определяют, соответственно, подмножества классов ResourceBundle и ListResourceBundle платформы J2SE. Хотя эти реализации являются собственными, сигнатуры методов являются теми же, что и у их аналогов в J2SE. import Java.util.Hashtable; /** Данный класс определяет базовый класс для определения локализованных ресурсов приложения. Он реализует подмножество класса java.util.ResourceBundle J2SE, но придерживается интерфейса, определенного данным классом. public abstract class ResourceBundle «Родительские» ресурсы. Если ресурс не найден в данном пакете, производятся поиски родительского пакета. */ protected ResourceBundle parent; /** Конструктор No-arg. public ResourceBundle () super(); /** Получает пакет ресурсов с указанным именем класса. Имя класса уже содержит язык и код страны назначения в стандартном формате. Например, имя класса пакета ресурсов может быть «I18NDeraoResources_fr_FR». @param className Полное имя класса, такое, как «I18NDemoResources_fr_FR». @возвращает объект пакета ресурсов. */ public static ResourceBundle getBundle(String classNarae) throws IllegalArgumentException, KissingResourceException { return ResourceBundle.getBundle(className, ""); } /** Получает пакет ресурсов с указанным базовым именем. @param baseName Полностью определенное имя класса извлекаемого пакета. Например, базовое имя «I18NDemo_fr_FR» — «HSNDerao». Sparam строка региональной настройки, представляющая региональную настройку, для которой должен быть извлечен пакет ресурсов. Ожидаемая форма lt;языкgt;.lt;странаgt; в соответствии с ISO 639 и ISO 3166, соответственно. @выдает пакет ресурсов для возвращения */ public static ResourceBundle getBundle(String baseName, String locale) throws IllegalArgumentException, MissingResourceException { Class c; if (baseName == null) { throw new IllegalArgumentException("No basename."); { String className = baseName + «_» + locale; ResourceBundle bundle = null; try { с = Class.forName(className); bundle = (ResourceBundle) с. newlnstance(); } catch (ClassNotFoundException cnfe) throw new MissingResourceException("Class not found."); } catch (InstantiationException ie) { throw new MissingResourceException("Can11 instantiate."); } catch (IllegalAccessException iae) { throw new MissingResourceException("Can1t access."); } return bundle; } /** Извлекает объект с указанным ключом. Если ключ не найден, ищется родительский пакет. @param key Ключ объекта @выдает объект с указанным ключом */ public final Object getObject(String key) throws MissingResourceException } Object obj; if (key == null) { throw new NullPointerException(); } obj = handleGetObject(key); if (obj == null SS parent 1= null) { obj = parent.getObject(key); } if (obj == null) { throw new MissingResourceException (); return obj; } /** Ищет данный пакет ресурсов для объекта с указанным ключом. @param key Ключ поиска желаемого объекта. @выдает объект с указанным ключом. */ protected abstract Object handleGetObject(String key); } /** Этот класс определяет пакет ресурсов как подходящий массив ресурсов. Он воспроизводит класс того же имени, определяемый платформой J2SE, java.util.ListResourceBundle. Данный класс абстрактен. Приложения вынуждены создавать его подклассы и определять конкретные классы, которые содержат локализованные ресурсы. 0пределенные подклассы конкретного приложения должны быть названы так, чтобы имя содержало язык и страну региональной настройки, в соответствии со стандартами ISO 639 и ISO 3166 для языковых и страновых кодов соответственно. */ открытый абстрактный класс ListResourceBundle дополняет ResourceBundle /** Конструктор No-arg. */ public ListResourceBundle() super(); // Массив ресурсов в формате ключ-значение, private static final Object [][] contents = null; /** Получает массив ресурсов. @возвращает двухмерный массив пар ключ-значение, который определяет эту группу. */ public abstract Object [][] getContents(); /** Получает объект, который представляет значение, связанное с указанным ключом. @param key Ключ пары ключ-значение. @выдает объект, который представляет значение пары ключ-значение. */ public final Object handleGetObject(String key) { Object value = null; if. (key == null) { return null; } Object [][] pairs = getContents (); for (int i = 0; i lt; pairs. length; i + +) if (key.equals(pairs [i] [0])) value = (pairs [i] [1]); } } return value; } } Смысл данной разработки заключается в том, что разработчики приложения создают подклассы ListResourceBundle. Каждый подкласс представляет собой агрегацию локализированных ресурсов для определенной региональной настройки. В листинге 9.9 показан конкретный подкласс ListResourceBundle, который предоставляет ресурсы приложения, локализованные под англоязычный регион. Отметьте, как имя класса отражает поддерживаемую региональную настройку. Эта схема присвоения имен не только облегчает управление классом во время разработки, она также помогает обнаруживать местонахождение и загружать класс во время работы приложения. import Java. io.lOException; /** Данный класс определяет локализованные ресурсы приложения I18NDemo3. Вы извлекаете ресурс, вызывая метод getObject() в классе ResourceBundle. */ public class I18NDemoResources_en_US extends ListResourceBundle // Содержит один из локализованных ресурсов. Нам необходимо // инициализировать данную переменную в статическом // инициализаторе данного класса, private static Image applcon; private Object [][] contents = { ("title", "Hello, World"}, // Form title. ("greeting", "My third MIDlet"}, // Form text. ("alert_title", "Button Pressed"), // Alert title. {"alert_text", "A button was pressed!"),// Alert text. {"exit", "Exit"}, // «Exit» menu item. {"menu", "Menu"}, // «Menu» soft button. {"cancel", "Cancel"}, // «Cancel» menu item. {"stop", "Stop"}, // «Stop» menu item. {"ok", "OK"}, // «OK» menu item. {"alert", "Alert"}, // «Alert» soft button. {"sayhi","Say Hi"}, // "Say Hi" menu item. {"screen", "Screen"}, // «Screen» menu item. {"item", "Item"}, // «Item» menu item. {"help", "Help"}, // «Help» menu item. {"app_icon", applcon} // Application icon. }; /** Конструктор No-arg. */ public I18NDemoResources_en_US() { super(); } public Object ij[] getContents() { return contents; } // Необходим статический инициализатор для инициализации // переменных, которые не могут быть инициализированы в // массиве содержимого. Например, мы не можем выделить что-либо // в массиве содержимого для создания изображения и, // выполнить требуемую обработку исключений. static { try { applcon = Image.createlmage("i!8n-en_US.png"); } catch (lOException ioe) { System.оut.println(ioe.getMessage))); ioe.printStackTrace(); } } } Классы, которые определяют локализованные ресурсы для других региональных настроек, должны создавать подкласс непосредственно класса Если класс определяет другие ресурсы кроме текстовых строк, тогда при создании экземпляра класса должны быть созданы объекты, соответствующие региональной настройке. Последний объект в списке является примером нетекстового ресурса, который инициализируется при создании экземпляра класса. Класс использует статический инициализатор Java для создания экземпляра статических нестроковых объектов при загрузке класса. Наша программа должна использовать статический инициализатор, потому что каждый класс локализованного ресурса создает определяемое региональной настройкой изображение. import javax.microedition.lcdui.Image; import Java.io.lOException; / ** Класс, представляющий локализованные ресурсы для французского языка региона Франции. Обратите внимание на использование последовательностей переключения уникода в строковых литералах. Использование последовательностей переключения уникода в строковых литералах означает, что мы можем записать этот файл с помощью одних только символов ASCII, делая его эксплуатацию более легкой. Легко добавлять комментарии для создания удобочитаемых строк. */ public class I18NDemoResources_fr_FR extends ListResourceBundle { // Содержит один из локализованных ресурсов. Нам необходимо // инициализировать данную переменную в статическом // инициализаторе данного класса. private static Image applcon; private Object [][] contents = { {"title", "All\uOOf4, tout le Monde"), // Form title. // Создаем текст: "My third MIDlet". ("greeting", "Mon troisi\uOOe8me MIDlet"), // «Кнопка была нажата» ("Button was Pressed"). {"alert_title", "Bouton a \uCOe9t\uOOe9 press\uOOe9"), // «Кнопка была нажата» ("The button was pressed"). {"alert_text", "Le bouton a \uOOe9t\uOOe9 press\uOOe9!"}, ("exit", "Sortie"), // Пункт меню «Выход» ("Exit"). ("menu", "Menu"), // Экранная клавиша «Меню» ("Menu"). ("cancel", "Quitter"), // Пункт меню «Отмена» ("Cancel"). ("stop", "Arreter"), // Пункт меню «Стоп» ("Stop"). ("ok", "OK"), // Пункт меню «OK». ("alert", "Alerte"), // Экранная клавиша «Уведомление» ("Alert"). i" sayhi","Dis bonjour"), // Пункт меню «Скажи- привет» ("Say Hi"). ("screen", "Ecran"), // Пункт меню «Экран» ("Screen"). {"item", "Item"), //.Пункт меню «Предмет» ("Item"). ("help", "Aider"), // Пункт меню «Помощь» ("Help"). ("app_icon", applcon) // Значок приложения. }; /** Конструктор No-arg. */ public I18NDemoResources_fr_FR() { super(); /** Получает содержимое пакета ресурсов. @возвращает массив пар ключ-значение. public Object [][] getContents() { return contends; } // Обратите внимание, что статический инициализатор создает // экземпляры класса Image с другими изображениями, нежели он // использует в региональной настройке en_US. static { try { applcon = Image.createlmage("i!8n-fr_FR.png"); } catch (lOException ioe) { System.out.printIn(ioe.getMessage()); io.e.printStackTracel); } } } В листинге 9.11 показана программа I18NDemo3, которая использует данный набор классов пакетов ресурсов. Метод startAppO данного MID-лета создает экземпляр соответствующего класса пакета ресурсов. Он создает имя класса, связывая базовое имя семейства файлов локализованных ресурсов, I18NDemoResources, с конечной региональной настройкой. С помощью всего лишь нескольких операторов приложение получает доступ ко всем локализованным ресурсам. import javax.microedition.midlet.MIDlet; import javax.microedition.Icdui.Display; import javax.microedition.Icdui.Displayable; import]avax.microedition.Icdui.Form; import Java.util.Hashtable; Третья версия приложения IlSNDemo. Данная версия IlSNDemo использует пакет ресурсов для определения локализованных ресурсов. Приложение определяет текущую региональную настройку и пытается загрузить связанный с ней пакет, содержащий соответствующие локализованные ресурсы. Если оно не может найти эти ресурсы, оно загружает ресурсы U.S. English, представленные языком en_US и страной назначения. Этот подход наиболее предпочтителен. Легко поддерживаются локализованные ресурсы, отличные от строк. */ public class I18NDemo3 extends MIDlet { // Региональная застройка, указанная для выполнения // данного МID-лета. private String locale; // Пакет ресурсов, который содержит локализованные ресурсы // для выполнения данного приложения, private static ResourceBundle bundle; { // Displayable. Этот компонент отображается // на экране. private HelloForm3 form; // Экземпляр Display. Этот объект управляет всеми // компонентами Displayable для данного MID-лета. private Display display; // Экземпляр MID-лета. private static!18NDerao3 instance; /** Конструктор No-arg. */ public I18NDemo3() { super(); instance = this; } /** Получает экземпляр данного класса, который существует в действующем приложении. @выдает экземпляр, созданный при запуске приложения. */ public static I18NDemo3 getlnstance() { if (instance == null) { instance — new I18NDemo3(); } return instance; } /** Получает пакет ресурсов, используемый данным MID-летом. Этот метод полезен для других классов, которым необходим доступ к локализованным ресурсам приложения. @выдает локализованные ресурсы MID-лета. */ public static ListResourceBundle getResourceBundle () { return (ListResourceBundle) bundle; } /** Запускает MID-лет. Определяет текущую региональную настройку среды исполнения и использует ее для создания имени пакета локализованных ресурсов. Использует это имя для создания имени класса Java, который затем загружается с помощью Class. Если нет соответствия пакету ресурсов, по умолчанию используется пакет ресурсов U.S. English. */ public void startApp() { // Извлекает региональную настройку из программного обеспечения // AMS.Региональная настройка должна быть установлена // до выполнения данного MID-лета. locale = System.getProperty("microedition.locale"); bundle = null; cry { bundle = ResourceBundle.getBundle("IlSNDemoResources", locale); if (bundle == null) { bundle = ResourceBundle.getBundle("IlBNDemoResources", "en_US"); } } catch (MissingResourceException mre) mre.printStackTrace(); } try } / Создаем элемент Displayable. Получаем локализованную // String, которая представляет заголовок Form. String formTitle = (String) bundle.getObject("title"); form = new HelloForm3(formTitle); } catch (MissingResourceException mre) { rare.printStackTrace(); } // Это приложение просто отображает одну форму, созданную ранее, display = Display.getDisplay(this); display.setCurrent(form); } /** Выдает значение, связанное с указанным ключом из списка определяемых пользователем ресурсов MID-лета в файле JAD приложения. @param key Ключ пары ключ-значение. @выдает значение, связанное с указанным ключом. */ public Object getResource(String key) } Object resource = null; try { resource = bundle.getObject(key); } catch (MissingResourceException mre) } } return resource; /** Выход из MID-лета. Уведомляет реализацию, что она может прервать работу всех ресурсов приложения. Реализация вызывает destroyApp(). */ public void quit() { notifyDestroyed(); /* public void destroyApp(boolean destroy) { { public void pauseApp() { } } На рисунке 9.1 показано основное окно, созданное программой I18NDemo3 при ее запуске в региональной настройке en_US. Программа динамически извлекает локализованные ресурсы, описанные в листинге 9.9. На рисунке 9.2 показан экран меню того же приложения, запущенного в региональной настройке fr_FR, которая использует локализованные ресурсы, описанные в листинге 9.10. Код приложения I18NDemo3 абсолютно не изменяется. Он просто динамически определяет контекст региональной настройки при инициализации и загружает соответствующий пакет ресурсов. Важным моментом этой разработки является прозрачность и организационная простота, осуществленная с помощью использования последовательностей переключения кода Unicode Java для кодирования не-ASCII строковых литералов в подклассах ListResourceBundle. Эти файлы содержат классы Java, которые вы откомпилировали вместе с остальным исходным кодом приложения. Компилятор преобразует последовательности переключения уникода в строковых литералах на двоичные значения уникода. Поскольку компиляторы Java понимают последовательности переключения уникода, вам не придется выполнять какое-либо преобразование кодировки для получения локализованного текста в форме, требуемой при выполнении, а именно в форме двоичных значений символьной кодировки уникода. Исследование листингов 9.9 и 9.10 может не убедить вас в выгодах использования последовательностей переключения уникода. Как-никак, большинство текстовых редакторов и операционных систем изначально поддерживают западноевропейские языки, такие, как французский. По этой причине легче создавать локализованные ресурсы для западноевропейских региональных настроек без повторной сортировки последовательностей переключения уникода. Например, пользователи могут создавать французские символы, вводя двухклавишные последовательности переключения в большинстве текстовых редакторов или вставляя их с помощью специальной функции меню. Возможно, следующий пример более явно отразит выгоды использования последовательностей переключения уникода. В листинге 9.12 показан класс I18NDemoResources_ru_RU, который определяет локализованные ресурсы для русского языка. На рисунке 9.3 показан внешний вид экрана, показанного на рисунке 9.2, когда региональная настройка устанавливается на ru_RU, которая представляет собой русский язык. Ввод русских символов с помощью системы западных языков более сложен, чем ввод французских символов. Однако структуру класса I18NDemoResources_ru_RU и инструменты, требуемые для его создания, не приходится изменять для поддержки использования кириллицы. import javax.microedition.Icdui.Image; import Java.io.lOException; /* Данный класс определяет локализованные ресурсы для приложения I18NDemo3. Вы извлекаете ресурс, вызывая метод getObject() в классе ResourceBundle. */ public class I18NDemoResources_ru_RU extends ListResourceBundle { // Содержит один из локализованных ресурсов. Нам необходимо // инициализировать эту переменную в статическом инициализаторе // данного класса. private static Image applcon; private Object [][] contents = // "Привет, мир". ("title", "\u0417\u0434\u0440\u0430\u0441\u0442\u0432\u0443\u0439, \u041c\u0446\uO*440!"), // "Мой третий MID-лет". {"greeting", "\u041c\043e\u0439 \u0442\u0440\u0435\u0442\u0438\u0439 MIDlet!"}, // "Кнопка нажата". {"alert_title", "\u041a\u043d\u043e\u043f\u043a\u0430 \u041d\u0430\u0436\u0430\u0442\u0430"}, // "Кнопка была нажата!". ("alert_text", "\u041a\u043e\u043e\u043f\u043a\u0430 \u0411\u044b\u043b\u0430 \u043d\u0430\u0436\u0430\u0442\u0430!"}, // Экранная клавиша «Выход». ("exit", "\u0412\u044b\u0445\u043e\u0434"}, { // Экранная клавиша «Меню». ("menu", "\u041c\u0435\u043d\u044e"}, // Пункт меню «Отмена». {"cancel", "\u041f\u0440\u0435\u043a\u0440\u0430\u0442\u0446\u0442\u044c"), // Пункт меню «Стоп». ("stop", "\u0421\u0442\u043e\u043f"}, // Пункт меню «ОК». {"ok", "OK"}, // Экранная клавиша «Предупреждение». ("alert", "\u0412\u043d\u0446\u043c\u0430\u043d\u0446\u0435"), // Пункт меню "Скажи привет". ("sayhi","\u0421\u043a\u0430\u0436\u0446 \u043f\u0440\u0446\u0432\u0435\u0442"), it Пункт меню «Экран». {"screen", "\u042d\u043a\u0440\u0430\u043d"), // Пункт меню «Предмет». ("item", "\u041f\u0440\u0435\u0434\u04c3\u0435\u0442"), // Пункт меню «Помощь». ("help", "\u041f\u043e\u043c\u043e\u0449\u044c"}, // Значок приложения. ("app_icon", applcon} }; /** Конструктор No-arg. */ public I18NDemoResources_ru_RU() super(); } public Object [][] getContents() } return contents; } // Необходим статический инициализатор для инициализации // переменной, которая не может быть инициализирована // в массиве содержимого. Например, мы не можем выделить // что-либо в массиве содержимого для создания изображения и // выполнить требуемую обработку исключений. static { try { applcon = Image.createlmage("i!8n-ru_RU.png"); } catch (lOExce'ption ioe) { System.out.print In(ioe.getMessage()); ioe.printStackTrace(); } } } Если вы все еще не убеждены, взгляните на листинг 9.13, который показывает ресурсы того же самого приложения, локализованные на японский язык. Класс I18NdemoResources_ja JP был создан с помощью того же текстового редактора, основанного на ASCII. Японские символы не могут быть введены в традиционном текстовом редакторе без поддержки IME. И, если вы используете IME, вы должны убедиться, что используете уникод для записи строковых литералов в файл. В противном случае вашему приложению придется выполнять преобразование посимвольной кодировки. import javax.microedition.Icdui.Image; import Java.io.lOException; /** Данный класс определяет локализованные ресурсы для приложения I18NDemo3. Вы извлекаете ресурс, вызывая метод getObject() в классе ResourceBundle. */ public class I18NDemoResources_ja_JP extends ListResourceBundle { // Содержит один из локализованных ресурсов. Нам необходимо // инициализировать эту переменную в статическом инициализаторе // данного класса. private static Image applcon; private Object [][] contents = { // "Привет, мир" {"title", "\u24f64\u3055\u3093, \u3053\u3093\u306b\u3061\u306f"), // "Мой третий MID-лет". ("greeting", "\u79cl\u306e 3 \u3063\u3081\u306e MIDlet"}, // "Кнопка нажата". {"alert_title") "\u30dc\u30bf\u30f3\u304c\u62bc\u3055\u308c\u307e\u3057\u305f"}, // "Кнопка была нажата". "alert_text", "\u30dc\u30bf\u30f3\u304c\u62bc\u3055\u308c\u3C7e\u3057\u305f!"} // Пункт меню «Выход», {"exit", "\u51fa\53e3"}, // Экранная клавиша «Меню». ("menu", "\u30el\u30cb\u30e6\u30fc"), // Пункт меню «Отмена». ("cancel", "\u3Cad\u30e4\u30f3\u30bb\u30eb"), // Пункт меню «Стоп». {"stop", "\u505c\u6b62"), // Пункт меню «ОК». ("ok", "OK"}, // Экранная клавиша «Предупреждение», {"alert", "Alert"), // Пункт меню "Скажи привет", ("sayhi","\u30cf\u30a4"}, // Пункт меню «Экран». {"screen", "\u30b9\u30af\u30ea\u30f3"), // Пункт меню «Предмет», {"item", "\u9805\u76ee"), // Пункт меню «Помощь». ("help", "\u308d"}, // Значок приложения. {"app_icon", applcon) /** Конструктор No-arg. */ public I18NDemoResources_ja JP() { super(); ) public Object [][] getContents () { return contents; { // Необходим статический инициализатор для инициализации // переменной, которая не может быть инициализирована в // массиве содержимого. Например, мы не можем выделить что-либо // в массиве содержимого для создания изображения и выполнить // требуемую обработку исключений. static { try { applcon = Image.createlmage("i!8n-ja_JP.png"); { catch (lOException ioe) { System.out.println(ioe.getMessage()); ioe.printStackTrace(); } } } В листинге 9.14 показан файл I18NDemoResources_zh_CH. Java, который определяет локализованные ресурсы для упрощенного китайского языка. import javax.microedition.Icdui.Image; import Java.io.lOException; /** Данный класс определяет локализованные ресурсы для приложения I18NDemo3. Вы извлекаете ресурс, вызывая метод getObjectO в классе ResourceBundle. */ public class I18NDemoResources_zh_CN extends ListResourceBundle { // Содержит один из локализованных ресурсов. Нам необходимо // инициализировать эту переменную в статическом инициализаторе // данного класса. private static Image applcon; private Object [][] contents = { // Заголовок формы "Hello, World". ("title", "\u54c8\u7f57\u4el6\754c"), // Текст формы "My third MIDlet". ("greeting", "\u62ll\u7684\7b2c\u4e09\u4187 MIDlet"}, // Заголовок уведомления "Button Pressed". ("alert_title", "\u6309\u4eOb\u6309\u9215"], // Текст уведомления "A button was pressed!". ("alert_text", "\u6309\u4eOO\u4187\u6309\u9215!"}, // Пункт меню «Exit». ("exit", "\u767b\u51fa"}, // Экранная клавиша «Menu», ("menu", "\u76ee\u5f54"}, // Пункт меню «Cancel», {"cancel", "\u53d6\u6d88"j, // Пункт меню «Stop», ("stop", "\u505c\u6b62"}, // Пункт меню «OK». {"ok", "OK"), // Экранная клавиша «Alert», {"alert", "\u8b66\u793a"), // Пункт меню "Say Hi", ("sayhi", "\u55e8"}, // Пункт меню «Screen». ("screen", "\u87a2\u5e55"), // Пункт меню «Item», ("item", "\u9879\u76ee"}, // Пункт меню «Help», {"help", "\u8bf4\u660e"}, // Значок приложения. {"app_icon", applcon} }; /** Конструктор No-arg. */ public I18NDemoResources_zh CN() { super!); { public Object [][] getContents () { return contents; } // Необходим статический инициализатор для инициализации // переменной, которая не может быть инициализирована в // массиве содержимого. Например, мы не можем выделить что-либо // в массиве содержимого для создания изображения и выполнить // требуемую обработку исключений. static { try { applcon = Imagb.createlraage("i!8n-zh_CN.png"); } catch (lOException ioe) { System.out.println(ioe.getMessage!)); ioe.printStackTrace(); } } } Использование классификационных файлов Java имеет несколько преимуществ перед двумя предыдущими разработками. Прежде всего, оно позволяет избежать создания комплексной структуры потоков и анализа текстовых файлов, которые вы видели в предыдущем подходе. Доступ к ресурсам так же прост, как создание экземпляра класса. Более важно то, что пакеты ресурсов могут быть легко приспособлены к любому из объектов Java — не только к строкам — как локализованные ресурсы. Первым двум подходам, представленным в этой главе, приходилось определять атрибуты, чьи значения были именами классов, экземпляры которых нужно было создавать, и затем создавать экземпляры данных классов после считывания и анализа файла ресурса. Подход, основанный на пакетах ресурсов, создает экземпляры всех объектов неявно, когда пакет ресурсов создан. И классы пакетов ресурсов оставляют небольшой след, используя меньше ресурсов рабочей памяти, чем предыдущий подход. Подход пакетов ресурсов также содействует легкому переносу приложений в среду J2SE. Реализации классов пакетов ресурсов, показанных в листингах 9.7 и 9.8, создают только подмножество необходимых свойств. Но их строгое следование интерфейсам версий J2SE означает, что подклассы ListResourceBundle вашего приложения совместимы снизу вверх. Пакеты ресурсов также способствуют лучшей восстанавливаемости и большей понятности. Зависящие от приложения подклассы ListResourceBundle могут быть легко восстановлены только лишь с помощью текстового редактора, основанного на ASCII. Любой ASCII-текстовой редактор может считывать и записывать символы ASCII или последовательности переключения кода Unicode Java, присутствующие в пакетах ресурсов. Кроме того, поскольку это исходные файлы Java, разработчики могут вставлять комментарии, которые ясно описывают каждый ресурс и контекст, в котором приложение его использует. И последнее преимущество, предлагаемое подходом пакетов ресурсов, заключается в том, что вы можете очень просто определять несколько пакетов ресурсов на одну региональную настройку. Вы можете определить, например, один пакет для текста, который появляется на компонентах пользовательского интерфейса, другой специально для сообщений об ошибке, один для изображений и так далее. Конечно, вы можете систематизировать их в соответствии с вашим приложением. Использование классификационных файлов Java для определения локализованных ресурсов предлагает прозрачность разработки, восстанавливаемость, расширяемость и приспособляемость к любому виду обьектов локализации Java. Несмотря на ати преимущества, однако, вы должны знать о компромиссных решениях, имеющихся наряду с этими двумя подходами, представленными в данной главе. Установка нескольких файлов классов Java для локализованных ресурсов может потребовать больше ресурсов хранения устройства, чем вы можете себе позволить. Наиболее очевидной проблемой при разработке MIDP является потребление памяти. Хотя два первых подхода неудобны по нескольким причинам, они затрачивают меньше ресурсов памяти, чем подход классификационного файла Java. Иногда, когда вы не можете позволить себе лишние траты памяти, вы можете позволить приложению затратить несколько дополнительных секунд при запуске на считывание и анализ локализованных ресурсов. Одним из возможных компромиссов является игнорирование иерархии наследования ResourceBundle и предоставление единственного класса, который содержит локализованные ресурсы для каждой региональной настройки. Предоставьте соответствующий локализованный классификационный файл указанной региональной настройке приложения. Здесь вы жертвуете гибкостью ради производительности. Вы также теряете совместимость снизу вверх с J2SE, но это может быть неважно. Все три стратегии разработки, представленные в этой главе, включают добавление локализованных ресурсов к остальному коду приложения. В реальных средах беспроводных сетей все может работать по-другому. Некоторые беспроводные транспортировщики уже поддерживают системы инициализации приложений, которые поддерживают динамическое обнаружение, извлечение и установку приложений Java на мобильных устройствах. Возможно, что вскоре все транспортировщики будут иметь такие системы. Инициализация приложений является темой главы 10. Скорее всего, эти системы будут предоставлять способ для устройств сообщения информации о среде их исполнения и получения от сервера нужных им ресурсов. Например, AMS устройства может указывать контекст региональной настройки устройства и загружать из инициализирующей системы только локализованные ресурсы для данной региональной настройки. Данное взаимодействие между AMS устройства и инициализирующим сервером предотвращает необходимость установки локализованных ресурсов для многочисленных региональных настроек на устройстве. Оно также обеспечивает способ для AMS, с помощью которого можно показывать пользователю, поддерживается ли региональная настройка, до запуска приложения. Тем не менее, разработчики могут найти, что легче упаковать откомпилированные локализованные файлы классов вместе с остальным кодом приложения. Проблемы разработки приложения, его установки, доставки и восстановления должны рассматриваться как часть каждой разработки. Интернационализация — это действия по предоставлению приложению возможности динамического извлечения и использования чувствительных к региональным настройкам ресурсов при работе. Интернационализация является важным свойством приложений MIDP. Интернационализированное приложение предназначено для большей пользовательской аудитории. Интернационализация приложения означает предоставление ему при выполнении возможности извлечения ресурсов, которые совместимй с контекстом региональной настройки, в которой приложение работает. Локализация — это процесс предоставления ресурсов одному или нескольким контекстам региональной настройки. — работа с сообщениями; — задание формата даты, времени, числовых и денежных значений; — поддержка календаря; — чувствительные к региональной специфике значки, изображения и цвета. Возможности, доступные в платформе MIDP, влияют на выбор варианта разработки интернационализации и затрагивают осуществимость реализации определенных разработок приложений MIDP. Платформа MIDP предоставляет три следующих основных механизма, которые могут быть использованы для создания возможностей интернационализации: — определяемые пользователем атрибуты набора MID-летов: — файл дескриптора приложения; поддержка извлечения ресурсов (файлов) из файла JAR набора MID-летов: Class. getResourceAsStream(StringresourceName); — преобразование символьных кодировок: пакет java.io. Разработчики приложений MIDP должны также учитывать факторы производительности, восстанавливаемости и установки при разработке решений интернационализации и локализации. |
||||||
|