EBS 国际化多语言开发
多语言开发
国际化支持
说明:
EBS的国际化支持,也叫多语言支持,包含多个层面:
1、数据库级别:字符集支持多国语言,如UTF8支持全球所有语言
2、数据级别:采用_B表+_TL表+ENV(‘LANG’)环境变量+_VL表+“小地球”来实现
3、消息级别:所有消息,通过分语种维护的消息字典获取
4、文件级别:采用分语种目录的形式来实现Forms、Reports的国际化支持
Form自身的多语言版本
EBS时运行时“找fmx文件”,实际上是根据用户登录时选择的语言,首先到\(<应用简称>_TOP/forms/<语言代码>下找fmx文件,如果没有则继续在\)<应用简称>_TOP/forms/US下找。
也就是说,需要我们维护不同语言的Form编译到不同的目录。不过Oracle提供了工具“Oracle Translation Builder (OTB)”,可以将多个语言的字符串保存入1个fmb文件,这样在设计时根据NLS_LANG 自动显示该语言的内容,编译时、运行时也是同样道理。
数据多语言开发步骤:
要求熟练掌握基于Template、基于View的开发过程;要求熟悉EBS中“小地球”的操作。
下面结合例子直接说明开发步骤和注意点,假定只有2个字段,1个需要维护多语言信息,不考虑弹性域字段。
数据库对象的要求:基表B
create table SECOM.CUX_MULTILINGUAL_DEMO_B
(
MULTILINGUAL_DEMO_ID NUMBER not null,
MULTILINGUAL_DEMO_CODE VARCHAR2(30) not null,
CREATED_BY NUMBER(15) not null,
CREATION_DATE DATE not null,
LAST_UPDATED_BY NUMBER(15) not null,
LAST_UPDATE_DATE DATE not null,
LAST_UPDATE_LOGIN NUMBER(15)
);
create unique index SECOM.CUX_MULTILINGUAL_DEMO_B_U1 on SECOM.CUX_MULTILINGUAL_DEMO_B (MULTILINGUAL_DEMO_ID);
Create Sequence SECOM.CUX_MULTILINGUAL_DEMO_B_S;
Create Synonym CUX_MULTILINGUAL_DEMO_BS For SECOM.CUX_MULTILINGUAL_DEMO_B;
Create Synonym CUX_MULTILINGUAL_DEMO_B_SS For SECOM.CUX_MULTILINGUAL_DEMO_B_S;
数据库对象的要求:多语言TL表
主键字段:基表主键+LANGUAGE。
其他字段:需要维护多语言的字段+Who字段+SOURCE_LANG。
create table SECOM.CUX_MULTILINGUAL_DEMO_TL
(
MULTILINGUAL_DEMO_ID NUMBER not null,
DESCRIPTION VARCHAR2(255),
LANGUAGE VARCHAR2(4) not null,
CREATED_BY NUMBER(15) not null,
CREATION_DATE DATE not null,
LAST_UPDATED_BY NUMBER(15) not null,
LAST_UPDATE_DATE DATE not null,
LAST_UPDATE_LOGIN NUMBER(15),
SOURCE_LANG VARCHAR2(4) not null
);
create unique index SECOM.CUX_MULTILINGUAL_DEMO_TL_U1 on SECOM.CUX_MULTILINGUAL_DEMO_TL(MULTILINGUAL_DEMO_ID, LANGUAGE);
Create Synonym CUX_MULTILINGUAL_DEMO_TL For SECOM.CUX_MULTILINGUAL_DEMO_TL;
数据库对象的要求:视图VL
该视图根据登录用户的语言过滤数据:
Create Or Replace View CUX_MULTILINGUAL_DEMO_VL As
SELECT b.ROWID row_id,
b.multilingual_demo_id,
b.multilingual_demo_code,
t.description,
b.created_by,
b.creation_date,
b.last_updated_by,
b.last_update_date,
b.last_update_login
FROM scf.cux_multilingual_demo_b b, scf.cux_multilingual_demo_tl t
WHERE b.multilingual_demo_id = t.multilingual_demo_id
AND t.LANGUAGE = userenv('LANG');
数据库对象的要求:表操作API
需要同时操作TL表,同时提供add_language过程,供EBS启用新语言时使用。以下代码仅关注“--Process TL table here”部分即可:
点击查看代码
CREATE OR REPLACE PACKAGE cux_multilingual_demo_pkg AUTHID CURRENT_USER AS
/*=====================================
** PROCEDURE: insert_row()
**=====================================*/
PROCEDURE insert_row(x_row_id IN OUT VARCHAR2,
x_multilingual_demo_id IN OUT NUMBER,
p_multilingual_demo_code IN VARCHAR2,
p_description IN VARCHAR2,
p_creation_date IN DATE,
p_created_by IN NUMBER,
p_last_update_date IN DATE,
p_last_updated_by IN NUMBER,
p_last_update_login IN NUMBER);
/*=====================================
** PROCEDURE: lock_row()
**=====================================*/
PROCEDURE lock_row(p_multilingual_demo_id IN NUMBER,
p_multilingual_demo_code IN VARCHAR2,
p_description IN VARCHAR2 DEFAULT NULL);
/*=====================================
** PROCEDURE: update_row()
**=====================================*/
PROCEDURE update_row(p_multilingual_demo_id IN NUMBER,
p_multilingual_demo_code IN VARCHAR2,
p_description IN VARCHAR2,
p_last_update_date IN DATE,
p_last_updated_by IN NUMBER,
p_last_update_login IN NUMBER);
/*=====================================
** PROCEDURE: delete_row()
**=====================================*/
PROCEDURE delete_row(p_multilingual_demo_id IN NUMBER);
/*=====================================
** PROCEDURE: add_language()
**=====================================*/
PROCEDURE add_language;
END cux_multilingual_demo_pkg;
/
CREATE OR REPLACE PACKAGE BODY cux_multilingual_demo_pkg AS
/*=====================================
** PROCEDURE: insert_row()
**=====================================*/
PROCEDURE insert_row(x_row_id IN OUT VARCHAR2,
x_multilingual_demo_id IN OUT NUMBER,
p_multilingual_demo_code IN VARCHAR2,
p_description IN VARCHAR2,
p_creation_date IN DATE,
p_created_by IN NUMBER,
p_last_update_date IN DATE,
p_last_updated_by IN NUMBER,
p_last_update_login IN NUMBER)
IS
CURSOR c IS
SELECT ROWID
FROM cux_multilingual_demo_b
WHERE multilingual_demo_id = x_multilingual_demo_id;
BEGIN
IF x_multilingual_demo_id IS NULL THEN
SELECT cux_multilingual_demo_b_s.NEXTVAL
INTO x_multilingual_demo_id
FROM dual;
END IF;
INSERT INTO cux_multilingual_demo_b
(multilingual_demo_id,
multilingual_demo_code,
created_by,
creation_date,
last_updated_by,
last_update_date,
last_update_login)
VALUES
(x_multilingual_demo_id,
p_multilingual_demo_code,
p_created_by,
p_creation_date,
p_last_updated_by,
p_last_update_date,
p_last_update_login);
--Process TL table here
INSERT INTO cux_multilingual_demo_tl
(multilingual_demo_id,
description,
created_by,
creation_date,
last_updated_by,
last_update_date,
last_update_login,
LANGUAGE,
source_lang)
SELECT x_multilingual_demo_id,
p_description,
p_created_by,
p_creation_date,
p_last_updated_by,
p_last_update_date,
p_last_update_login,
l.language_code,
userenv('LANG')
FROM fnd_languages l
WHERE l.installed_flag IN ('I', 'B')
AND NOT EXISTS
(SELECT NULL
FROM cux_multilingual_demo_tl t
WHERE t.multilingual_demo_id = x_multilingual_demo_id
AND t.LANGUAGE = l.language_code);
OPEN c;
FETCH c
INTO x_row_id;
IF (c%NOTFOUND) THEN
CLOSE c;
RAISE no_data_found;
END IF;
CLOSE c;
END insert_row;
/*=====================================
** PROCEDURE: lock_row()
**=====================================*/
PROCEDURE lock_row(p_multilingual_demo_id IN NUMBER,
p_multilingual_demo_code IN VARCHAR2,
p_description IN VARCHAR2 DEFAULT NULL)
IS
CURSOR c IS
SELECT multilingual_demo_id, multilingual_demo_code
FROM cux_multilingual_demo_b
WHERE multilingual_demo_id = p_multilingual_demo_id
FOR UPDATE OF multilingual_demo_id NOWAIT;
rec c%ROWTYPE;
--Process TL table here
CURSOR c1 IS
SELECT description
FROM cux_multilingual_demo_tl
WHERE multilingual_demo_id = p_multilingual_demo_id
AND LANGUAGE = userenv('LANG')
FOR UPDATE OF multilingual_demo_id NOWAIT;
tlrec c1%ROWTYPE;
BEGIN
OPEN c;
FETCH c
INTO rec;
IF (c%NOTFOUND) THEN
CLOSE c;
fnd_message.set_name('FND', 'FORM_RECORD_DELETED');
app_exception.raise_exception;
END IF;
CLOSE c;
IF ((rec.multilingual_demo_id = p_multilingual_demo_id) AND
((rec.multilingual_demo_code = p_multilingual_demo_code) OR
((rec.multilingual_demo_code IS NULL) AND
(p_multilingual_demo_code IS NULL)))) THEN
NULL;
ELSE
fnd_message.set_name('FND', 'FORM_RECORD_CHANGED');
app_exception.raise_exception;
END IF;
--Process TL table here
OPEN c1;
FETCH c1
INTO tlrec;
IF (c1%NOTFOUND) THEN
CLOSE c1;
fnd_message.set_name('FND', 'FORM_RECORD_DELETED');
app_exception.raise_exception;
END IF;
CLOSE c1;
IF (((tlrec.description = p_description) OR
((tlrec.description IS NULL) AND (p_description IS NULL)))) THEN
NULL;
ELSE
fnd_message.set_name('FND', 'FORM_RECORD_CHANGED');
app_exception.raise_exception;
END IF;
END lock_row;
/*=====================================
** PROCEDURE: update_row()
**=====================================*/
PROCEDURE update_row(p_multilingual_demo_id IN NUMBER,
p_multilingual_demo_code IN VARCHAR2,
p_description IN VARCHAR2,
p_last_update_date IN DATE,
p_last_updated_by IN NUMBER,
p_last_update_login IN NUMBER)
IS
BEGIN
UPDATE cux_multilingual_demo_b
SET multilingual_demo_id = p_multilingual_demo_id,
multilingual_demo_code = p_multilingual_demo_code,
last_update_date = p_last_update_date,
last_updated_by = p_last_updated_by,
last_update_login = p_last_update_login
WHERE multilingual_demo_id = p_multilingual_demo_id;
IF (SQL%NOTFOUND) THEN
RAISE no_data_found;
END IF;
--Process TL table here
UPDATE cux_multilingual_demo_tl
SET description = p_description,
source_lang = userenv('LANG'),
last_update_date = p_last_update_date,
last_updated_by = p_last_updated_by,
last_update_login = p_last_update_login
WHERE multilingual_demo_id = p_multilingual_demo_id
AND userenv('LANG') IN (LANGUAGE, source_lang);
IF (SQL%NOTFOUND) THEN
RAISE no_data_found;
END IF;
END update_row;
/*=====================================
** PROCEDURE: delete_row()
**=====================================*/
PROCEDURE delete_row(p_multilingual_demo_id IN NUMBER)
IS
BEGIN
DELETE FROM cux_multilingual_demo_b
WHERE multilingual_demo_id = p_multilingual_demo_id;
IF (SQL%NOTFOUND) THEN
RAISE no_data_found;
END IF;
--Process TL table here
DELETE FROM cux_multilingual_demo_tl
WHERE multilingual_demo_id = p_multilingual_demo_id;
IF (SQL%NOTFOUND) THEN
RAISE no_data_found;
END IF;
END delete_row;
/*=====================================
** PROCEDURE: add_language()
**=====================================*/
PROCEDURE add_language IS
BEGIN
INSERT INTO cux_multilingual_demo_tl
(multilingual_demo_id,
description,
created_by,
creation_date,
last_updated_by,
last_update_date,
last_update_login,
LANGUAGE,
source_lang)
SELECT b.multilingual_demo_id,
b.description,
b.created_by,
b.creation_date,
b.last_updated_by,
b.last_update_date,
b.last_update_login,
l.language_code,
b.source_lang
FROM cux_multilingual_demo_tl b, fnd_languages l
WHERE l.installed_flag IN ('I', 'B')
AND b.LANGUAGE = userenv('LANG')
AND NOT EXISTS
(SELECT NULL
FROM cux_multilingual_demo_tl t
WHERE t.multilingual_demo_id = b.multilingual_demo_id
AND t.LANGUAGE = l.language_code);
END add_language;
END cux_multilingual_demo_pkg;
案例
一、摘要
Oracle EBS系统是一个多语言的系统,能够实现不同语言环境登录显示不同的语言信息,系统中多语言涉及到两个主要的方面:
- 界面的多语言
任何给用户看到的地方,都需要实现多语言来满足不同国家和地区的使用。界面的多语言使得不同的语言环境看到不同语言的界面
- 基础数据的多语言
很多用户看到的数据都是通过基础设置而来的,因此不但需要实现界面的多语言,还需要实现基础数据也多语言化。数据的多语言使得不同的语言环境看到不同语言的数据
二、功能预览
- 当以英文环境登录系统时
(1).界面多语言:可以查询出如上图的数据,我们注意看 User Form Name 和 Description 两个列。
(2).数据多语言:可以看到其中的数据都是英文定义的,如第一行叫FNDADDSQ的Form,User Form Name 为Register Sequence

2. 当以中文环境登录系统时
(1).界面多语言:可以查询出如上图的数据,我们注意看 用户表单名和说明两个列。
(2).数据多语言:可以看到其中的数据都是中文定义的,如第一行叫FNDADDSQ的表单,用户表单名为:注册序列

三、数据分类
- 定义
要弄清楚数据多语言转换,需要先将数据进行分类,由于要实现多语言转换的对象是:数据。
这就意味着可能多语言的量会比较大,就拿上面我们定义表单的例子来说,
里面包括了字段:表单、应用、用户表单名和说明总共4个字段,哪些字段需要实现多语言转换,即哪些数据需要不同的环境下看到不同语言的表现?
因此数据会分为3类:
(1). 用户编码/代码基础数据类:为了知道多语言之后,我们所说的是同一个东西,我们需要一个用户能够识别的代码,不管它在什么语言环境下,都是一样的。如:表单FNDADDSQ
(2). 非多语言类:这类信息不要求不同的语言环境下显示不同的信息
(3). 多语言类:这类信息要求不同的语言环境显示不同的信息,如上面的应用、用户表单名和说明,这就是我们需要多语言转换的地方
- 实现步骤
(1). 开发一个具备数据多语言转换定义的界面,如上面的表单定义窗口
(2). 进入多语言数据界面,录入多语言数据
(3). 进入不同的语言环境,显示不同的语言数据
- 多语言转换定义的界面
如何去开发一个具备多语言转换定义的界面我们后面在描述,我们先来看看如何使用一个多语言转换定义的界面。
当录入一条数据的时候,通过工具栏中的一个小地球仪的图标来定义多语言信息,当表单中实现了数据多语言功能后,光标进入相应的数据块,小地球仪就会亮起,否则失效

点击小地球仪,会弹出数据多语言转换窗口,如下图,注意看转换窗口的行和列:
(1). 行(Row):Oracle EBS系统安装了多少个语言,这个转换窗口就会显示出多少行,如我的环境安装了中文和英文,所以有两行
(2). 列(Column):这个例子中有两列,分别是用户表单名和说明,这个说明开发表单时候设置的有哪些列是可以进行多语言转换的,因此理论上面可以有无限个列
- 多语言显示
如定义FNDADDSQ表单的时候,点击小地球仪之后显示如下的多语言转换界面:
(1). 第一行是英文对应的用户表单名和说明
(2). 第二行是中文对应的用户表单名和说明

四、多语言转换表
Oracle Erp中如果需要实现数据多语言的转换功能,对应的后台表结构的设计有一定的规则 ,包括的数据库对象:两张基表加一张视图
- 多语言表的实现
(1). 基本信息表:存储基本信息的基础记录,包括信息的主键
(2). 多语言表:命名以 _TL结尾,存储基础记录对应的多语言信息,包括主键、需要多语言转换的列和语言代码列LANGUAGE
(3). 多语言视图:命名以 _VL结尾,根据语音环境取出基于语言的信息,包括主键和语言信息
- 多语言表结构逻辑关系

所以根据上面数据库对象的命名 规则,只要在Oracle EBS的数据库系统中看到_TL, _VL对象出现的时候,它肯定是实现了多语言数据转换功能。
多语言视图同时连接基本信息表和多语言表,并限制多语言表中的语言字段等于环境变量中取得的语言代码,从而达到多语言视图取出的数据是当前语言环境下的信息。
五、多语言转换示例
- 表单基本信息表
SQL> desc fnd_form;
Name Type Nullable Default Comments
—————— ———— ——– ——- ——–
APPLICATION_ID NUMBER
FORM_ID NUMBER
FORM_NAME VARCHAR2(30)
LAST_UPDATE_DATE DATE
LAST_UPDATED_BY NUMBER(15)
CREATION_DATE DATE
CREATED_BY NUMBER(15)
LAST_UPDATE_LOGIN NUMBER(15)
AUDIT_ENABLED_FLAG VARCHAR2(1)
2. 表单多语言转换表,包括多语言的两列:user_form_name, description
SQL> desc fnd_form_tl;
Name Type Nullable Default Comments
—————– ————- ——– ——- ——–
APPLICATION_ID NUMBER
FORM_ID NUMBER
LANGUAGE VARCHAR2(30)
USER_FORM_NAME VARCHAR2(80)
CREATED_BY NUMBER(15)
CREATION_DATE DATE
LAST_UPDATED_BY NUMBER(15)
LAST_UPDATE_DATE DATE
LAST_UPDATE_LOGIN NUMBER(15) Y
DESCRIPTION VARCHAR2(240) Y
SOURCE_LANG VARCHAR2(4)
- 表单多语言视图
SQL> desc fnd_form_vl;
Name Type Nullable Default Comments
—————— ————- ——– ——- ——–
ROW_ID ROWID Y
APPLICATION_ID NUMBER
FORM_ID NUMBER
FORM_NAME VARCHAR2(30)
LAST_UPDATE_DATE DATE
LAST_UPDATED_BY NUMBER(15)
CREATION_DATE DATE
CREATED_BY NUMBER(15)
LAST_UPDATE_LOGIN NUMBER(15)
AUDIT_ENABLED_FLAG VARCHAR2(1)
USER_FORM_NAME VARCHAR2(80)
DESCRIPTION VARCHAR2(240) Y
4. 表单多语言视图声明定义
SELECT b.ROWID row_id,
b.application_id,
b.form_id,
b.form_name,
b.last_update_date,
b.last_updated_by,
b.creation_date,
b.created_by,
b.last_update_login,
b.audit_enabled_flag,
t.user_form_name,
t.description
FROM fnd_form_tl t, fnd_form b
WHERE b.application_id = t.application_id
AND b.form_id = t.form_id
AND t.LANGUAGE = userenv(‘LANG’);
六、Form触发器
- 在需要使用多语言项所在的BLOCK内增加触发器:PRE-BLOCK
触发器中添加如下的代码启用Translation按钮:
app_special.enable(‘TRANSLATE’);
- 在需要进行多语言转换的所在的BLOCK内增加触发器:TRANSLATION
加入如下PROCEDURE进行多语言字段的初始
FND_MULTILINGUAL.EDIT(‘FND_FORM’,
‘FORM_ID, APPLICATION_ID’,
‘USER_FORM_NAME, DESCRIPTION’,
‘*FND:ML_USER_FORM_NAME, *FND:ML_DESCRIPTION’);
- 参数描述
(1).FND_FORM:
多语言数据块的名称
(2).FORM_ID,APPLICATION_ID:
多语言表的主键列,多个列用逗号(,)隔开
(3).USER_FORM_NAME,DESCRIPTION:
需要进行多语言转换的列
(4).*FND:ML_USER_FORM_NAME, *FND:ML_DESCRIPTION:
多语言转换窗口中每列对应的标题(如下图),为了实现它也多语言,因此这里是消息字典,*FND是消息字典对应的应用,ML_USER_FORM_NAME是消息的名称
- 而外增加如下触发器及代码
4.1 Form Level
(1).POST-FORMS-COMMIT
fnd_multilingual.save;
4.2 Block Level
(1).POST-INSERT
fnd_multilingual.key;
(2).WHEN-CLEAR-BLOCK
fnd_multilingual.free_block;
(3).WHEN-REMOVE-RECORD
fnd_multilingual.free_record;
七、Table Handler
当定义一条多语言的数据时,同时会往基本信息表和多语言表插入数据。
往基本信息插入一条数据,多语言表插入多条数据,系统安装了多少种语言环境,就会插入多少条。
因此对于多语言转换的插入语句的写法有一点特别的地方,为了处理的方便,一般都将增删改的操作写到数据库的存储过程中。
- 插入 - Insert
在Form块的on-insert触发器中,要同时往基本信息表和多语言表(_TL)插入数据,因此insert的存储过程中需要根据Oracle EBS系统安装的语言来决定往多语言表中插入多少条记录。通过FND_LANGUAGES表来判断。
伪代码:
INSERT INTO FND_FORM;
INSERT INTO FND_FORM_TL
SELECT
FROM FND_LANGUAGES
WHERE
INSTALLED_FLAG in (‘I’, ‘B’)
2. 更新 - Update
更新的时候需要同时更新基本信息表和多语言表;在on-lock触发器中,需要同时对基本信息表和多语言表锁定。
伪代码:
UPDATE FND_FORM
UPDATE FND_FORM_TL
WHERE userenv(‘LANG’) in (LANGUAGE, SOURCE_LANG)
3. 删除 - Delete
删除的时候也需要同时删除基本信息和多语言信息。
伪代码:
DELETE FND_FORM
DELETE FND_FORM_TL
代码请查看数据库包fnd_form_pkg

浙公网安备 33010602011771号