第六章 使用系统类提供国家语言支持 - 创建自定义语言环境
第六章 使用系统类提供国家语言支持 - 创建自定义语言环境
创建自定义语言环境
此示例将提供一个模板,用于使用自定义表创建自定义语言环境。自定义表将在 EBCDIC
(美国使用的通用格式)和 Latin-1
(ISO-8859–1
) 之间转换。
对于任何其他表,首先我们需要获得字符映射的定义。对于此示例,我们使用来自网站 http://source.icu-project.org(International Components for Unicode
) 的数据文件。相关数据文件是一个文本文件,其注释行以井号 (#
) 开头,然后是一系列格式的翻译定义行:
<Uuuuu> \xee |0
该文件的一小段如下所示:
#
#UNICODE EBCDIC_US
#_______ _________
<U0000> \x00 |0
<U0001> \x01 |0
<U0002> \x02 |0
<U0003> \x03 |0
<U0004> \x37 |0
<U0005> \x2D |0
...
这些行表明 Unicode
字符 Uaaaa
映射到 EBCDIC
字符 \xbb
(其中 aaaa
和 bb
以十六进制表示)。我们假设该表是可逆的,并且 EBCDIC
字符 \xbb
映射回 Unicode
字符 Uaaaa
。这使我们能够在单次扫描中从同一数据文件创建两面(即 EBCDIC-to-Latin1
和 Latin1-to-EBCDIC
)。因为 Unicode
范围只是从 0
到 255
,所以这实际上是一个 Latin-1
表。
该过程首先创建 SubTable
对象,然后是 Table
,最后是 Locale
。对于第一步,该过程创建两个 SubTables
对象,初始化它们的 Name
和 Type
属性,然后用从定义文件中读取的数据填充 FromTo
映射数组。
子表名称采用 Type–FromEncoding–ToEncoding
形式。常规 I/O
转换的类型是“XLT
”,因此子表名称将是 XLT-yEBCDIC-Latin1
和 XLT-yLatin1-EBCDIC
。
以下代码创建 SubTables
对象。在真实世界的程序中,代码将执行许多一致性检查,为了清楚起见,此处省略了这些检查。此示例删除相同对象(子表、表和区域设置)的现有先前版本,以便您可以多次运行该示例。更恰当地说,您应该使用类方法 Exists()
检查以前的对象是否存在,如果它们已经存在,则采取不同的操作。
// Names for the new SubTables (save for later)
Set nam1 = "XLT-Latin1-yEBCDIC"
Set nam2 = "XLT-yEBCDIC-Latin1"
// Delete existing SubTables instances with same ids
Do ##class(Config.NLS.SubTables).Delete(nam1)
Do ##class(Config.NLS.SubTables).Delete(nam2)
// Create two SubTable objects
Set sub1 = ##class(Config.NLS.SubTables).%New()
Set sub2 = ##class(Config.NLS.SubTables).%New()
// Set Name and Description
Set sub1.Name = nam1
Set sub1.Description = "ICU Latin-1->EBCDIC sub-table"
Set sub2.Name = nam2
Set sub2.Description = "ICU EBCDIC ->Latin-1 sub-table"
SubTables
对象包含一个属性 type
,它是一个小整数,指示我们是否正在处理多字节转换。此示例将类型设置为零,表示单字节映射。初始化映射,以便将数据文件中未定义的代码点(字符)映射到它们自己。
// Set Type (single-to-single)
Set sub1.Type = 0
Set sub2.Type = 0
// Initialize FromTo arrays
For i = 0 : 1 : 255
{
Do sub1.FromTo.SetAt(i, i)
Do sub2.FromTo.SetAt(i, i)
}
接下来应用程序读取文件。文件中的定义会覆盖那些设置为默认映射的定义。函数 $ZHEX()
将代码从十六进制转换为十进制。
// Assume file is in the mgr directory
Set file = "glibc-EBCDIC_US-2.1.2.ucm"
// Set EOF exit trap
Set $ZTRAP = "EOF"
// Make that file the default device
Open file
Use file
For
{
Read x
If x?1"<U"4AN1">".E
{
Set uni = $ZHEX($E(x,3,6)),ebcdic = $ZHEX($E(x,12,13))
Do sub1.FromTo.SetAt(ebcdic,uni)
Do sub2.FromTo.SetAt(uni,ebcdic)
}
}
EOF // No further data
Set $ZT = ""
Close file
// Save SubTable objects
Do sub1.%Save()
Do sub2.%Save()
字符映射现已完成。下一步是创建引用刚刚定义的 SubTables
对象的 Table
对象。表对象实际上是子表的描述符,只有几个属性。下面的代码建立了两者之间的联系:
// Delete existing Tables instances with same ids
Do ##class(Config.NLS.SubTables).Delete("XLT", "Latin1", "yEBCDIC")
Do ##class(Config.NLS.SubTables).Delete("XLT", "yEBCDIC", "Latin1")
// Create two Table objects
Set tab1 = ##class(Config.NLS.Tables).%New()
Set tab2 = ##class(Config.NLS.Tables).%New()
// Set description
Set tab1.Description = "ICU loaded Latin-1 -> EBCDIC table"
Set tab2.Description = "ICU generated EBCDIC -> Latin-1 table"
// Set From/To encodings
Set tab1.NameFrom = "Latin1"
Set tab1.NameTo = "yEBCDIC"
Set tab2.NameFrom = "yEBCDIC"
Set tab2.NameTo = "Latin1"
// Set SubTable
Set tab1.SubTableName = nam1
Set tab2.SubTableName = nam2
// Set Type
Set tab1.Type = "XLT"
Set tab2.Type = "XLT"
// Set Default Action
// 1 = Replace with replacement value
Set tab1.XLTDefaultAction = 1
Set tab2.XLTDefaultAction = 1
// Set Replacement value of "?"
Set tab1.XLTReplacementValue = $ASCII("?")
Set tab2.XLTReplacementValue = $ASCII("?")
// Set Reversibility
// 1 = Reversible
// 2 = Generated
Set tab1.XLTReversibility = 1
Set tab2.XLTReversibility = 2
// Set Translation Type
// 0 = non-modal to non-modal
Set tab1.XLTType = 0
Set tab2.XLTType = 0
// Save Table objects
Do tab1.%Save()
Do tab2.%Save()
定义好表后,构建的最后一步是定义一个将包含新表的语言环境对象。应用程序创建一个空的 Locale
对象并像对表和子表所做的那样填充每个属性。然而,Locale
更大更复杂。进行此类简单更改的最简单方法是复制现有语言环境并仅更改我们需要的内容。此过程使用 enu8
作为源语言环境并将新语言环境命名为 yen8
。开头的 y
清楚地表明这是一个自定义语言环境,不应在升级时删除。
// Delete existing Locales instance with the same id
Do ##class(Config.NLS.Locales).Delete("yen8")
// Open source locale
Set oldloc = ##class(Config.NLS.Locales).%OpenId("enu8")
// Create clone
Set newloc = oldloc.%ConstructClone()
// Set new Name and Description
Set newloc.Name = "yen8"
Set newloc.Description = "New locale with EBCDIC table"
设置好语言环境后,该进程现在将 EBCDIC
表添加到启动时加载的 I/O
表列表中。这是通过在数组属性 XLTTables
中插入一个节点来完成的,如下所示:
XLTTables(<TableName>) = <components>
-
tablename
标识此语言环境的一对输入和输出表。因为名称不需要以
y
开头,所以我们使用EBCDIC
。 -
components
是一个四项列表,如下所示:
- 输入“
From
”编码 - 输入“
To
”编码 - 输出“
From
”编码 - 输出“
To
”编码
以下代码将表格添加到可用语言环境列表中:
// Add new table to locale
Set component = $LISTBUILD("yEBCDIC", "Latin1", "Latin1", "yEBCDIC")
Do newloc.XLTTables.SetAt(component, "EBCDIC")
在 IRIS
可以使用语言环境之前,必须将其编译成其内部形式。这有时也称为验证语言环境。 IsValid()
类方法进行详细分析并返回两个数组,一个用于错误,一个用于警告,如果语言环境未正确定义,则带有人类可读的消息。
// Check locale consistency
If '##class(Config.NLS.Locales).IsValid("yen8", .Errors, .Warns)
{
Write !,"Errors: "
ZWrite Errors
Write !,"Warnings: "
ZWrite Warns
Quit
}
// Compile new locale
Set status = ##class(Config.NLS.Locales).Compile("yen8")
If (##class(%SYSTEM.Status).IsError(status))
{
Do $System.OBJ.DisplayError(status)
}
Else
{
Write !,"Locale yen8 successfully created."
}