Класс HelloForm2 теперь
Листинг 9.6. Класс HelloForm2 теперь использует API I18Nderao2.getResource() для извлечения локализованных ресурсов
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 символьный поток срздает определяемый приложением метод loadResources ().
Чтобы преобразовывать из байтов в символы, вы должны знать символьную кодировку файла ресурса, который вы считываете. В листинге 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. Он адекватен для франкоязычных ресурсов, но в то же время он может не поддерживать корейский язык, например, тогда как уникод поддерживает. Вы можете использовать некоторые другие многобайтные кодировки, но затем вам придется положиться на редакторы методов ввода и другие инструменты для считывания и записи файла ресурса в этой кодировке.
Если вы используете другие многобайтные кодировки, вам придется учитывать проблемы совместимости и возможность отладки. Есть ли у вас инструменты - текстовые редакторы, редакторы методов ввода и так далее - для поддержки всех ваших региональных настроек? Есть ли у вас те же инструменты, что и у вашей команды по локализации или, по крайней мере, совместимые с ними? Кто будет следить за правильностью работы вашего приложения? Есть ли у них те же инструменты, что и у остальных? Все эти аспекты надо рассматривать при выборе метода кодировки.
Когда вы создали ваш объект InputStreamReader, который является символьно ориентированным потоком, вы можете извлекать символы из него с помощью методов read(). Они происходят из класса, стоящего над ними, java.io.Reader. Эти методы выдают char уникода. В таблице 9.1 перечислены методы класса Reader.