В Oracle10g
впервые появился инструментарий Globalization
Development
Kit
(GDK
) для Java
и PL/SQL
, упрощающий процесс разработки приложений с поддержкой глобализации. Если вы занимаетесь разработкой многоязычного приложения, определение локального контекста каждого пользователя и вывод информации в соответствии с требованиями локального контекста может стать самой сложной задачей программирования, которую вам предстоит решить. Входящие в GDK
компоненты PL/SQL
разделены на два пакета: UTL_I18N
и UTL_LMS
.
Пакет UTL_I18N
Пакет UTL_I18N
содержит основную рабочую функциональность GDK
. Его подпрограммы перечислены в табл. 1.
Функция GET_LOCAL_LANGUAGES
является одной из самых полезных в этом пакете. Зная территорию пользователя, можно дополнительно сократить список значений при помощи функции UTL_I18N
. GET_LOCAL_LANGUAGES
. Данная возможность замечательно подходит для приложений, в которых администратор должен настраивать конфигурацию для конкретных пользователей. Для демонстрации этой функции будут использоваться следующие данные:
CREATE TABLE user_admin (
id NUMBER(10) PRIMARY KEY,
first_name VARCHAR2(10 CHAR),
last_name VARCHAR2(20 CHAR),
territory VARCHAR2(30 CHAR),
language VARCHAR2(30 CHAR))
/
BEGIN
INSERT INTO user_admin
VALUES (1, 'Stan', 'Smith', 'AMERICA', 'AMERICAN');
INSERT INTO user_admin
VALUES (2, 'Robert', 'Hammon', NULL, 'SPANISH');
INSERT INTO user_admin
VALUES (3, 'Anil', 'Venkat', 'INDIA', NULL);
COMMIT;
END:
/
Таблица 1. Подпрограммы пакета UTL_I18N
Имя | Описание |
ESCAPE_REFERENCE | Документы HTML и XML не всегда поддерживают те же символы, что и база данных. В таких случаях бывает полезно заменить неподдерживаемые символы служебными комбинациями (escapes). Функция получает входную строку и набор символов документа HTML или XML |
GET_COMMON_TIME_ZONES | Возвращает список основных часовых поясов. Функция особенно удобна для вывода списка, из которого пользователь выбирает часовой пояс для своего профиля |
GET_DEFAULT_CHARSET | Возвращает имя набора символов по умолчанию |
GET_DEFAULT_ISO_CURRENCY | Получает обозначение региона и возвращает соответствующий код валюты |
GET_DEFAULT_LINGUISTIC_SORT | Возвращает самый распространенный критерий сортировки для заданного языка |
GET_LOCAL_LANGUAGES | Возвращает локальные языки для заданной территории |
GET_LOCAL_LINGUISTIC_SORTS | Возвращает список имен критериев сортировки для заданного языка |
GET_LOCAL_TERRITORIES | Возвращает список территорий для заданного языка |
GET_LOCAL_TIMEZONES | Возвращает все часовые пояса для заданной территории |
GET_TRANSLATION | Переводит название языка и/или территории на заданный язык и возвращает результат |
MAP_CHARSET | Особенно полезна в приложениях, которые пересылают данные, извлеченные из базы, по электронной почте. Обеспечивает преобразование между набором символов базы данных и набором символов, безопасным для пересылки по электронной почте |
MAP_FROM_SHORT_LANGUAGE | Для полученного сокращенного названия языка возвращает полное название языка в Oracle |
MAP_LANGUAGE_FROM_ISO | Получает имя локального контекста в стандарте ISO и возвращает название языка Oracle |
MAP_LOCALE_TO_ISO | Получает язык и территорию, возвращает имя локального контекста в стандарте ISO |
MAP_TERRITORY_FROM_ISO | Получает локальный контекст ISO , возвращает название территории Oracle |
MAP_TO_SHORT_LANGUAGE | Функция, обратная по отношению к MAP_FROM_SHORT_LANGUAGE : получает полное название языка Oracle , возвращает короткое название |
RAW_TO_CHAR | Перегруженная функция, которая получает тип RAW и возвращает VARCHAR2 |
RAW_TO_NCHAR | Функция идентична RAW_TO_CHAR , но возвращает значение типа NVARCHAR2 |
STRING_TO_RAW | Преобразует VARCHAR2 или NVARCHAR2 к заданному набору символов, возвращает значение типа RAW |
TRANSLITERATE | Возвращает транслитерацию строки, записанной японской каной |
UNESCAPE_REFERENCE | Функция, обратная по отношению к ESCAPE_REFERENCE : распознает служебные комбинации и преобразует их в исходные символы |
Территория вводится в таблице USER_ADMIN
. Вывод списка языков для пользователя Anil
осуществляется следующим анонимным блоком:
DECLARE
-- Создание массива для результирующего набора языков
v_array utl_i18n.string_array;
-- Создание переменной для хранения данных пользователя
v_user user_admin%ROWTYPE;
BEGIN
-- Заполнение переменной данными пользователя Anil
SELECT *
INTO v_user
FROM user_admin
WHERE ID = 3;
-- Получение списка языков для территории
v_array := utl_i18n.get_local_languages (v_user.territory);
DBMS_OUTPUT.put (CHR (10));
DBMS_OUTPUT.put_line ('=======================');
DBMS_OUTPUT.put_line ('User: ' || v_user.first_name || ' '
|| v_user.last_name
);
DBMS_OUTPUT.put_line ('Territory: ' || v_user.territory);
DBMS_OUTPUT.put_line ('=======================');
-- Перебор элементов массива
FOR y IN v_array.FIRST .. v_array.LAST
LOOP
DBMS_OUTPUT.put_line (v_array (y));
END LOOP;
END;
Программа возвращает следующий результат:
=======================
User: Anil Venkat
Territory: INDIA
=======================
ASSAMESE
BANGLA
GUJARATI
HINDI
KANNADA
MALAYALAM
MARATHI
ORIYA
PUNJABI
TAMIL
TELUGU
Работать с таким списком намного удобнее, чем с полным списком всех языков. Аналогичная фильтрация может выполняться и для территорий в том случае, если известен язык. Допустим, у пользователя Robert в данный момент территория не определена, но задан испанский язык (SPANISH). Следующий анонимный блок возвращает список действительных территорий для испанского языка:
DECLARE
-- Создание массива для результирующего набора территорий
v_array utl_i18n.string_array;
-- Создание переменной для хранения данных пользователя
v_user user_admin%ROWTYPE;
BEGIN
-- Заполнение переменной данными пользователя Robert SELECT *
INTO v_user
FROM user_admin
WHERE ID = 2;
-- Получение списка территорий для языка
v_array := utl_i18n.get_local_territories (v_user.LANGUAGE);
DBMS_OUTPUT.put (CHR (10));
DBMS_OUTPUT.put_line ('=======================');
DBMS_OUTPUT.put_line ('User: ' || v_user.first_name || ' '
|| v_user.last_name
);
DBMS_OUTPUT.put_line ('Language: ' || v_user.LANGUAGE);
DBMS_OUTPUT.put_line ('=======================');
-- Перебор элементов массива
FOR y IN v_array.FIRST .. v_array.LAST
LOOP
DBMS_OUTPUT.put_line (v_array (y));
END LOOP;
END;
Результат:
=======================
User: Robert Hammon
Language: SPANISH
=======================
SPAIN
CHILE
COLOMBIA
COSTA RICA
EL SALVADOR
GUATEMALA
MEXICO
NICARAGUA
PANAMA
PERU
PUERTO RICO
VENEZUELA
Зная территорию, можно вывести список языков, действительных часовых поясов и валют; все это значительно упростит настройку конфигурации. Если выбран язык, средства UTL_I18N
позволяют получить для него набор символов по умолчанию, лингвистическую сортировку по умолчанию, локальные территории и короткое название языка.
Пакет обработки ошибок UTL_LMS
UTL_LMS
— второй пакет, входящий в GDK
. В него включены две функции для выборки и форматирования сообщений об ошибках:
-
GET_MESSAGE
— возвращает необработанное сообщение об ошибке для заданного языка (иначе говоря, параметры сообщения не включены в возвращаемый текст). -
FORMAT_MESSAGE
— включает в сообщение дополнительную информацию.
Пример:
DECLARE
v_bad_bad_variable PLS_INTEGER;
v_function_out PLS_INTEGER;
v_message VARCHAR2 (500);
BEGIN
v_bad_bad_variable := 'x';
EXCEPTION
WHEN OTHERS
THEN
v_function_out :=
utl_lms.GET_MESSAGE (06502, 'rdbms', 'ora', NULL, v_message);
-- Вывод неформатированных и отформатированных сообщений
DBMS_OUTPUT.put (CHR (10));
DBMS_OUTPUT.put_line ('Message - Not Formatted');
DBMS_OUTPUT.put_line ('=======================');
DBMS_OUTPUT.put_line (v_message);
DBMS_OUTPUT.put (CHR (10));
DBMS_OUTPUT.put_line ('Message - Formatted');
DBMS_OUTPUT.put_line ('===================');
DBMS_OUTPUT.put_line (utl_lms.format_message (v_message,
': The quick brown fox'
)
);
END;
При вызове UTL_LMS.GET_MESSAGE
язык не был задан, поэтому сообщение возвращается на языке по умолчанию, определяемом параметром NLS_LANGUAGE
.
Message - Not Formatted
=======================
PL/SQL: numeric or value error%s
Message - Formatted
===================
PL/SQL: numeric or value error: The quick brown fox
Так как при вызове UTL_LMS.GET_MESSAGE
может передаваться язык, при получении сообщения я просто передаю язык пользователя приложения.
Варианты реализации GDK
Функции GDK
позволяют выбрать несколько разных вариантов реализации. Если в системе должны поддерживаться только два-три локальных контекста, возможно, реализацию проще всего разделить по контекстам. Скажем, для немецкой системы серверы базы данных и приложений настраиваются для этого локального контекста, а для пользователей из Франции используется совершенно иная среда. Однако чаще требуется реализовать полноценную многоязычную среду, в которой новые контексты могут добавляться без приобретения и настройки отдельного экземпляра системы. Такая реализация потребует дополнительных усилий на начальной стадии, но ею гораздо проще управлять в долгосрочной перспективе.
Метод определения локального контекста пользователя в значительной степени зависит от предполагаемой аудитории и типа разрабатываемого приложения. В следующих подразделах рассматриваются три возможных решения, которые следует принять во внимание при проектировании.
Метод 1. Кнопки локальных контекстов
Этот метод часто встречается на веб-страницах в Интернете. Посетите сайт компании, которая ведет бизнес в разных странах; на главной странице часто размещаются кнопки или ссылки для выбора языка:
in English | en Español | en Français | in Italiano
Данное решение идеально подходит для ограниченного набора локальных контекстов. Пользователь выбирает свой язык и денежную единицу простым щелчком на ссылке, а если при выборе будет допущена ошибка — достаточно щелкнуть на кнопке Назад в браузере и исправить проблему.
В этом сценарии либо для каждого локального контекста создаются отдельные страницы и код, либо настройки для текущего сеанса сохраняются в cookie
или базе данных, чтобы локализация проходила под контролем базы данных. Чаще всего управление локализацией выполняется на уровне приложения.
Метод 2. Администрирование пользователей
Метод 2 отлично подходит для управления настройками локального контекста в приложениях с управляемой пользовательской базой (то есть закрытых, например, для анонимных пользователей Интернета). При небольшом количестве пользователей можно воспользоваться пакетом UTL_I18N
для вывода списка доступных часовых поясов, локальных контекстов, языков и территорий, как было показано ранее. Пользователь или администратор просто выбирает настройки, подходящие для пользователя; при каждом входе пользователя приложение читает эти настройки и выполняет соответствующую локализацию.
А если количество пользователей очень велико? Управлять настройками каждого пользователя по отдельности в этом случае нереально. Можно, по примеру проектировщиков базы данных Oracle
, организовать систему профилей. Реализуйте в своем приложении возможность создания профиля и настройки локального контекста на уровне профиля, а не на уровне пользователя. При добавлении нового пользователя просто назначьте ему профиль. Тем самым вы избавитесь от многих административных хлопот, особенно если позднее вдруг понадобится внести изменения в конфигурацию. Вместо того чтобы изменять данные всех пользователей, достаточно изменить профиль.
Метод 3. Гибридный
Метод 3 сочетает в себе отдельные аспекты методов 1 и 2. Он часто применяется в приложениях интернет-магазинов. Большинство покупателей начинают с просмотра сайта в поисках нужного товара. На этой стадии требовать от них ввода полной информации об их местонахождении преждевременно, но при этом данные должны сортироваться в правильном порядке и выводиться на нужном языке, с правильным форматом денежных величин. Чтобы обеспечить правильность базовой информации локального контекста, предложите решение, описанное в методе 1.
А когда покупатель примет решение о покупке, вы требуете, чтобы он ввел профильные данные с информацией о локальном контексте. Локализация становится более конкретной, в ней используются точная дата/время и финансовая информация из базы данных.