I18N Messages and Logging (国际化消息和日志)
作者 John Mazzitelli
2006 年 12 月 6 日
翻译:suli
原文地址:http://www.onjava.com/pub/a/onjava/2006/12/06/i18n-messages-and-logging.html
对于许多软件开发者来说,一提到国际化(亦称为 i18n)支持就要发出一声呻吟。 要使编写的代码能够面向外国使用者,确实需要费一翻思量,因为在现有软件的代码中添加国际化支持可不是一件轻而易举的事。 如果您感觉到软件需要支持不同语言和语言环境,哪怕这种可能性很小,从一开始就做国际化项目的准备,比起项目开始后再试图添加国际化支持也要明智得多。
有人问“国际化”是什么意思? 国际化远不止于将用户界面消息翻译成不同的语言。 它还涉及到处理不同的字符编码、日期/时间/货币的显示形式、以及跨多区域时存在的一些其他差异。
介绍 i18nlog
本文的目的并不在于讨论那些关于国际化的琐碎的方面,而是通过研究一个称为 "I18N Messages and Logging" (简称 i18nlog
) 的开源项目,来介绍引入国际化功能时需要执行的一些必要的任务
i18nlog
允许您在 Java 应用程序内集成国际化的消息,这是通过向以下内容提供 API 来完成的:
- 标注 Java 类以识别国际化消息
- 从所有支持的语言环境资源包中获取国际化消息
- 创建特定于语言环境的异常,并在其中使用国际化的消息
- 使用任何日志框架创建国际化消息日志
- 自动生成特定于语言环境的资源包
- 自动生成帮助及参考文档
定义国际化消息
国际化软件时一项最为乏味的工作莫过于维护资源绑定包了。 资源绑定包是包含 "name=value" 这种信息对的属性文件 (.properties),其中 "name" 是资源绑定包的关键字字符串 (key string),而 "value" 是翻译过的消息字符串本身。 习惯上为每种语言创建一个资源绑定包,在每个绑定包中关键字的设置是唯一的,而各个关键字相关的值就要翻译成各种语言了。 资源绑定文件的名称应该指明它是为哪种语言创建的,例如,mybundle_en.properties 文件中的消息是用英语写的,而 mybundle_de.properties 包含德语消息。
i18nlog
提供了一些用于定义资源绑定消息及其关键字字符串的标注--用于将资源注入到 i18nlog
的自定义 Ant 任务中,您可以自动生成资源绑定包,而不必为确保属性文件与访问属性文件的 Java 代码之间的一致性作过多的工作。
@I18NMessage
标注被放在常量上,这些常量就是资源绑定包的关键字字符串使用的常量。 使用这些常量可以迫使执行编译时检查;例如代码中引入的拼写错误(如常量名称的拼写错误)和使用过时消息或已删除消息,这些错误在编译时就可以被探测到。 以下是使用此标注的示例:
java 代码
- @I18NMessage( "Hello, {0}. You last visited on {1,date}" )
- public static final String MSG_WELCOME = "example.welcome-msg";
上面的示例定义了一条国际化消息。 常量的值定义了资源绑定包的关键字字符串(key string)。 标注的值是一条实际翻译好的消息。 您可以将这些标注过的常量放到应用程序的任何类或接口中。 可以将它们放在单独的类或接口中(将所有消息定义集中到一个地点),也可以放在使用到它们的类中。
@I18NResourceBundle
标注用于定义存放消息的资源绑定包文件(.properties 文件)。 它可以标注整个类或接口,也可以标注特定的部分。 如果您标注了一个类或接口,则该类或接口中的所有 @I18NMessage
标注都将被存储在该标注定义的资源绑定包中(默认情况下)。 如果只将标注放在特定的常量上,那么它就只是那个常量的绑定包。 以下是使用此标注的示例:
java 代码
- @I18NResourceBundle( baseName = "messages",
- defaultLocale = "en" )
以上代码的意思是,所有被 @I18NMessage
标注的相关消息都将放置在名为 messages_en.properties 绑定包文件中。 下面是一个更复杂的示例(包含一系列国际化消息的接口):
java 代码
- @I18NResourceBundle( baseName = "messages",
- defaultLocale = "en" )
- public interface Messages {
- @I18NMessage( "Hello, {0}. You last visited on {1,date}" )
- String MSG_WELCOME = "welcome-msg";
-
- @I18NMessage( "An error occurred, please try again" )
- String MSG_ERR = "error-occurred";
-
- @I18NMessage( "The value is {0}" )
- String MSG_VALUE = "value";
- }
检索国际化消息
定义了国际化常量后,就可以使用 i18nlog
的核心类提供的 API:mazz.i18n.Msg
。 该 API 用于装载存放于资源绑定属性文件中的消息。 它还用于将值作为变量参数进行传递,从而替换消息中的占位符(例如, {0}
, {1,date}
)。 另外,此 API 将您从直接使用 JDK 类进行编码的工作中解放出来,也许您需要阅读一下 Javadoc 的 java.text.MessageFormat
部分,以获得对 i18nlog
工作原理的初步认识,尤其是它如何使用本地化的数据替换占位符。
下面是 Msg
类的使用实例:
java 代码
-
-
- System.out.println( Msg.createMsg( Messages.MSG_WELCOME,
- name,
- date ) );
和
java 代码
-
- Msg msg = new Msg( new Msg.BundleBaseName("messages") );
- try {
- String hello = msg.getMsg(Messages.MSG_WELCOME, name, date );
- ... do something ...
- }
- catch (Exception e) {
- throw new RuntimeException( msg.getMsg( Messages.MSG_ERR ) );
- }
name
和 date
参数是传递一个任意参数列表的示例--每个对象代表各个位置上要被替换掉的占位符({0}
和 {1,date}
分别基于前面给出的 Messages.MSG_WELCOME
中的定义)。
Msg
对象会根据“绑定包的名称”及其当前的语言环境设置获知使用哪种语言环境的资源绑定包。 “绑定包的名称”是绑定包文件的名称减去语言环境符号和扩展名(在上面的示例中,绑定包的名称是 messages
)。 您可以通过传递到 Msg
的构造函数或者特定的静态工厂方法的方式,来定义 Msg
对象使用的“绑定包的名称”,如果不进行明确地指定,将使用默认的 messages
。 “绑定包的名称”在 Msg
对象的生命周期内将一直存在,可以通过调用 Msg
对象的 setLocale()
来切换语言环境(默认语言环境是 JVM 使用的语言环境)。 如果您使用了 Msg
对象并且要求根据(被分配了 Msg 对象的)使用者的需要来切换语言环境,这将是非常有用的。
国际化的消息和日志
第 1, 2 页
本地化的异常
i18nlog
提供了两个基本的异常类(分别用于已查看和未查看的异常-- LocalizedException
和 LocalizedRuntimeException
),可以这两个类创建自己的本地化异常子类。 这些类都具有构造函数,其函数签名与 Msg
类非常相似。 在使用构造函数时,通过调用资源绑定包的关键字和占位符的变量参数列表的方法来指定异常消息,同时还可以选择绑定包的名称和语言环境。 这样一来,异常消息就可以使用本地化的各种语言,其原理就是利用了 Msg
可以检索本地化的消息。
<!---->
创建国际化消息的日志
i18nlog
提供了一种方法,可以通过该方法记录国际化的日志信息。 日志系统的主类是 mazz.i18n.Logger
。 它提供了 trace
、debug
、info
、warn
、error
和 fatal
方法等一些典型的设置。 需要指出的是,与传递一个消息本身组成的字符串不同,您为占位符传递的是资源绑定包的关键字字符串和参数列表。 它使用具有保护作用的 mazz.i18n.Msg
类来获取实际的本地化消息。
通过使用工厂类 mazz.i18n.LoggerFactory
来获得 Logger
对象,与 log4j
等获得对象的方式基本相同。 而不同的是记录日志消息的方法,该方法与通过 mazz.i18n.Msg
对象获取消息非常类似:
java 代码
- public static final mazz.i18n.Logger LOG =
- mazz.i18n.LoggerFactory.getLogger(MyClass.class);
- ...
- LOG.debug(Messages.MSG_VALUE, value);
- ...
- try {
- ...
- }
- catch (Exception e) {
- LOG.warn(e, Messages.MSG_ERR);
- }
如果没有启用日志级别,则不会查找绑定包,也不会执行任何字符串连接。这一条件有效地加快了日志调用的速度。 如果日志消息与特定的表达式相关联,则需要将表达式的第一个参数传递给日志方法。 如果启用了栈倾卸设置,这将允许栈跟踪倾卸消息(请往下看)。
i18nlog
的日志框架中还添加了许多其他的功能,这些功能并非潜在的、第三方的日志框架所能媲美。 首先要介绍的功能就是,可以告诉 Logger
是否倾卸异常的栈跟踪。 您可能需要查看一个特定运行任务的所有异常的栈跟踪,也可能不想看。 请注意,对于在 FATAL
日志级别上的异常,不能禁用此功能,使用该方法记录的致命异常必需允许倾卸栈跟踪。 对于其他的日志级别,日志程序只有在系统属性 i18nlog.dump-stack-traces
设置为 true
(或者在代码中调用 Logger.setDumpStackTraces(true)
)的时候才会倾泄栈跟踪。
i18nlog
日志框架附加的第二项功能是,在记录与一个消息关联的资源绑定包关键字的同时,记录消息本身。 资源绑定包关键字对于所有语言环境都是相同的,也就是说,无论消息是用何种语言写成的,关键字是不变的。 可以把这些关键字想象成“消息的 ID”或“错误的代码”。 这在生成涉及到这些代码的帮助文档时非常有用,用户将可以参考文档中的额外帮助文本,以得知到底要传递什么消息(关于如何生成这种文档,请查看下文)。 在默认情况下,此功能处于启用状态,要禁用此功能,只需将系统属性 i18nlog.dump-keys
设置为 false
或者在代码中调用 Logger.setDumpLogKeys(false)
。
提供消息 ID、避免在已禁用日志级别上拼接字符串,这些 i18nlog
提供的日志机制也许已经足够了。 也许有人会争论说,将(除用户界面消息以外的)日志消息国际化是一种负担而且也没有必要。 我有时也感到很难说服这种观点。 如果项目没有这种需求,您当然不必使用国际化日志。 即使不使用 i18nlog
提供的日志功能,还可以使用它提供的其他功能嘛。
但是,我还是可以举出一些具体的例子来证明使用国际化消息的好处。 请注意,i18nlog
允许定义一个不同的语言环境供日志程序使用(日志语言环境),该语言环境区别于 Msg
实例使用的语言环境。 这就可以帮助使用我的开发团队的语言来记录日志消息,而我的用户界面使用用户可以阅读的语言(可能与开发团队使用的语言不同)。 例如,我的用户说德语,而软件是由说法语的法国团队开发的。 在这种情况下,德国用户碰到一个问题时,就可以正常地给法国开发团队发送日志,软件可以默认地将语言环境设置为 Locale.FRENCH
。 另一方面,如果德国用户希望自行调试问题,法语的日志消息根本不会有任何帮助。 在这种情况下,德国用户简单地通过设置系统属性,将日志消息记录为德语。 关于如何切换日志语言环境的信息,请参考 mazz.i18n.LoggerLocale
Javadoc。
自动生成资源绑定包
i18nlog
提供了一个 Ant 任务,用于自动生成资源绑定包属性文件,以避免开发者担负手动向属性文件中添加消息、清理过时消息的任务。
该 Ant 任务将扫描类以查找 @I18N
标注,并根据这些标注为您创建资源绑定包。 这意味着,无论添加多少 @I18NMessage
标注字段,它们都将被加入到资源绑定包中。 如果您删除了一个国际化消息常量,则该消息也将从 Ant 任务生成的资源绑定包结果中删除。 要运行该 Ant 任务,需要在 Ant 脚本中添加以下类似代码:
xml 代码
- <taskdef name="i18n"
- classpathref="i18nlog-jar.classpath"
- classname="mazz.i18n.ant.I18NAntTask" />
-
- <i18n outputdir="${classes.dir}" verify="true" verbose="true">
- <classpath refid="my.classpath" />
- <classfileset dir="${classes.dir}"/>
- i18n>
必需让 Ant 任务知道含有国际化标注类及其依赖关系的类的类路径 <classpath></classpath>
,还必须给出类文件集 <classfileset></classfileset>
,其中包含文件集的文件列表供扫描国际化标注之用。 建议您在第一次运行 Ant 任务的时候采用“冗长”模式,以便知道 Ant 任务的执行内容。 一旦得到了想要的效果,再将“冗长”模式关闭。 执行此任务之后,资源绑定包属性文件将出现在指定的输出目录中。
生成帮助文档
使用此 Ant 任务的另一个可选功能是生成帮助文档,该文档是由所有对资源绑定包关键字名称及其消息值的引用组成的,另外还包含了对这些消息的描述。 这是一个可在 @I18NMessage
标注中指定的可选属性,help
属性。 属性值可以是深入描述消息的任何字符串。 可以将文档想象成在特定情况下将传递的具体消息。 自动生成的帮助文档可以提供消息关键字、消息本身以及消息描述之间的交叉引用:
java 代码
- @I18NMessage( value="The value is {0}",
- help="This will show you the value of your"
- +" current counter. If this value is over"
- +" 1000, you should reset it.")
- String MSG_VALUE = "value";
-
- @I18NMessage( value="Memory has {0} free bytes left",
- help="The VM is very low on memory. Increase -Xmx"
- String MSG_LOW_MEM = "low-memory";
大多数情况下,您可以将其作为“消息的 ID”或者“错误的代码”列表使用,可以将其中的资源绑定包关键字当成一个“消息 ID”或者“错误的代码”。 要生成帮助文档,需要在 <i18n></i18n>
任务中使用 <helpdoc></helpdoc>
内部标记:
xml 代码
- <i18n outputdir="${classes.dir}">
- <classpath refid="my.classpath" />
- <classfileset dir="${classes.dir}"/>
- <helpdoc outputdir="${doc.dir}/help"/>
- i18n>
对于每个生成的资源绑定包,都可以在 <helpdoc>
中指定的目录下找到一个相应的帮助文档。 文档是根据用于描述文档样式的模板生成的。 默认情况下,模板是一个简单的 HTML 页,使用 <table>
标记消息代码,而输出就是帮助文档。 在 <helpdoc>
标记中还可以指定一些其他的属性来自定义一个模板,以满足自行定制帮助文档外观的需求。 有关更多信息,请查阅 mazz.i18n.ant.Helpdoc
类的 Javadoc。
在帮助文档的生成结束之后,您就可以得到一个(或一些)包含消息关键字代码、消息内容及任何 "help" 属性定义内容的文档了。
本地化
我们已经讨论了许多关于如何通过获取翻译的消息和本地化的消息国际化软件的内容。 嵌入国际化功能之后,剩下的工作就是手动处理那些需要被本地化的资源绑定属性文件(由 <i18n>
Ant 任务生成或者自行手动编写)了。 必须确保将所有资源绑定包的消息翻译成需要支持的语言,而且这些消息包含的数据也进行相应的本地化。 通过定义占位符属性(例如,{0,date}
将使用目标语言环境的格式和语言输出日期字符串)可以完成许多本地化方面的工作。 不言而喻的是,必须找一家优秀的翻译和本地化公司。
小结
这篇文章介绍了如何通过一个新的开源项目 i18nlog
在应用程序中溶入国际化功能。 使用该开源项目提供的工具和 API 可以自动管理资源绑定包属性文件(.properties),检索和管理这些绑定包中的本地化消息,甚至还可以生成供最终用户使用的帮助文档。
资源
John Mazzitelli 是一位 JBoss 开发者,Red Hat 的带头人,目前正致力于 “Boss Operations Network 管理平台”的实现。
分享到:
相关推荐
2. **日志和调试** 通过配置日志,可以记录与国际化相关的错误和警告,帮助开发者定位问题。 **六、Django 文档资源** 为了更深入地了解 Django 的国际化和本地化,可以参考官方文档中的 "Django ...
5. **统一的错误响应格式**:设计一个统一的JSON格式来封装错误信息,包括错误码、错误消息、可能的解决建议等,便于前端展示和日志记录。 6. **测试**:编写单元测试和集成测试,确保在不同语言环境下错误信息的...
在.NET开发中,实现应用程序的国际化是一项重要的任务,它允许软件与不同地区和语言的用户进行有效交流。".NET国际化语言封装类库"是专为此目的设计的一个工具,旨在简化.NET平台上的多语言支持。这个类库使得开发者...
2. 根据获取的语言设置`ActionContext`的`Locale`属性,这会影响到后续Action和视图中国际化消息的加载。 3. 确保在拦截器链的适当位置放置这个拦截器,以便在Action执行之前完成语言设置。 例如,以下是一个简单的...
例如,Java中的`java.util.Locale`和`ResourceBundle`类用于实现国际化,而像log4j这样的日志框架则提供了处理和记录异常的能力。 “international”这个文件名可能指向的是一个包含不同语言资源的目录,或者是演示...
6. **错误与日志**:国际化不仅涉及用户界面,还应涵盖错误消息和日志。确保这些信息也能翻译成所选语言,以便于非英语用户理解和解决问题。 7. **文档与帮助**:为用户提供多语言的文档和帮助,包括SSH命令的使用...
4. **后台输出数据和日志国际化**: 后台系统生成的数据和日志文件应支持多种语言,以便于非英语环境下的阅读和分析。 5. **业务数据国际化**: - 货币单位:根据用户所在地区显示相应的货币符号。 - 字典数据:...
代码国际化,也称为i18n(取"internationalization"的首字母和末尾数字,意为第18个字符之后是'n'),是软件开发中的一个重要环节,它确保程序能够适应不同语言和文化环境。在国际化的代码中,文本通常被提取出来,...
本项目"I18N Messages and Logging"显然就是这样一个工具,它提供了一个API来处理国际化消息和日志记录,并且支持自动创建语言资源包。 首先,让我们深入了解I18N。I18N的核心是将软件中的文本与代码分离,以便于...
- `functions_de.properties`, `functions.properties`, `functions_da.properties`:这些可能是多语言资源文件,用于国际化和本地化,它们可能包含日志消息的文本。 - `META-INF`:这个目录通常包含关于应用元...
Struts2是一个强大的Java web应用程序框架,它提供了一种组织MVC(模型-视图-控制器)架构的方式,使得开发者可以更...开发者可以借此学习和理解MVC模式、Struts2的工作流程、JDBC数据库操作以及web应用的国际化实现。
在NopCommerce这样的多语言电商平台上,本地化是提供国际化用户体验的关键。4.40.4版本中,本地化服务已经得到了进一步优化,允许开发者轻松地定义和管理各种语言资源,包括页面文本、错误消息、提示语等。本地化...
此外,对于参与国际无线电比赛或认证项目(如DXCC,即国际电台确认计划)的HAM,准确的通联日志是获取积分和证书的关键。模板的标准化确保了记录的一致性,使得审核过程更加顺畅。 总结起来,"通联日志模板"是业余...
国际化(Internationalization,简称i18n)是软件设计的一个关键方面,它允许软件适应不同国家和地区的语言、格式和文化差异。在Java中,国际化通常通过ResourceBundle类来实现,它可以加载不同语言版本的资源配置...
国际化的核心是资源包(Resource Bundle),它是一系列键值对的集合,存储了应用程序中用到的各种文本字符串,如按钮文字、错误消息等。这些键值对可以在不同的语言环境下被替换,以展示相应的本地化内容。在Struts...
9. **多语言支持**:考虑到国际化的项目需求,可能支持多种语言的日志显示和搜索。 这个名为"内存日志工具包"的压缩文件很可能包含了上述功能的实现,可能是一个独立的软件应用程序或者是一系列脚本和工具的集合。...
国际化(i18n)是指使软件能够适应不同国家和地区的语言及文化习惯,以满足全球用户的需求。在Struts 2中实现国际化,可以为你的Web应用提供多语言支持,提升用户体验。 首先,我们需要了解Struts 2国际化的基本...
这关乎用户界面和国际化设置,因为不同的地区和用户可能有不同的时间格式偏好。 日志文件`log.txt`和`log_5660_1.txt`可能包含了整个测试过程的输出,包括每个测试步骤的状态(如成功、失败或警告),以及任何错误...
- 在多语言环境下,系统架构需要支持不同语言的数据处理与展示,这可能涉及到多语言编码问题和国际化问题。 - 跨语言处理还可能需要考虑本地化服务,例如根据用户所在地域提供不同的数据处理逻辑和接口。 4. 零...