字符集问题

像其它大多数的Java应用程序一样,FreeMarker使用 "UNICODE 文本"(UTF-16)来工作。 不过,也有必须处理 字符集 的情况, 因为它不得不和外界交换数据,这就会使用到很多字符集。

输入的字符集

当 FreeMarker 要加载模板文件(或没有解析的文本文件)时, 那就必须要知道文件使用的字符集,因为文件的存储是原生的字节序列。 可以使用 encoding 配置 来确定字符集。 这个配置项只在 FreeMarker 使用 Configuration 对象的 getTemplate 方法加载模板(解析过的或没有解析过的)时起作用。请注意 include 指令 在内部也使用了这个方法,所以 encoding 的值对一个已经加载的模板,而且如果这个模板包含 include 指令的调用来说很重要。

encoding 配置的getter和setter方法在第一个(配置)层面很特殊。 getter方法猜想返回值是基于 Locale(本地化,译者注)传递的参数; 它在地图区域编码表(称为编码地图)中查询编码,如果没有找到该区域,就返回默认编码。 可以使用配置对象的 setEncoding(Locale locale, String encoding) 方法来填充编码表;编码表初始化时是空的。默认的初始编码是系统属性 file.encoding 的值,但是可以通过 setDefaultEncoding 方法来设置一个不同的默认值,而不是依赖它。 对于新项目来说,默认的编码设置就是 utf-8

也可以在模板层或运行环境层(当指定编码值作为 getTemplate 方法的参数时,应该在模板层覆盖 encoding 设置)直接给定值来覆盖 encoding 的设置。如果不覆盖它,那么 locale 设置的有效值将会是 configuration.getEncoding(Locale) 方法的返回值。

而且,代替这种基于字符集猜测的机制,也可以在模板文件中使用 ftl 指令, 比如 <#ftl encoding="utf-8"> 来指定特定的字符集。

请注意,模板使用的字符集和模板生成的输出内容的字符集是独立的 (除非包含 FreeMarker 的软件故意将设置输出内容的字符集和模板字符集设置成相同的)。

输出的字符集

Note:

output_encoding 设置/参数和 内建函数url 从FreeMarker 2.3.1版本开始才可以使用,而在2.3以前的版本中是不存在的。

原则上,FreeMarker 不处理输出内容的字符集问题, 因为 FreeMarker 将输出内容都写入了 java.io.Writer 对象中。而 Writer 对象是由封装了 FreeMarker(比如Web应用框架) 的软件生成的, 那么输出内容的字符集就是由封装软件来控制的。而FreeMarker有一个称为 output_encoding(开始于 FreeMarker 2.3.1 版本之后)的设置。 封装软件应该使用这个设置(Writer对象使用的字符集) 来通知 FreeMarker 在输出中(否则 FreeMarker 不能找到它)使用哪种字符集。 有一些新的特性,如内建函数url特殊变量output_encoding 也利用这个信息。因此,如果封装软件没有设置字符集这个信息, 那么 FreeMarker 需要知道输出字符集的特性就不能被利用了。

如果你使用 FreeMarker 来编写软件, 你也许想知道在输出内容中到底选择了哪种字符集。 当然这取决于运行 FreeMarker 输出内容的计算机本身, 但是如果用户对这个问题可以变通, 那么通用的实践是使用模板文件的字符集作为输出的字符集,或者使用UTF-8。 通常使用UTF-8是最佳的实践,因为任意的文本可能来自数据模型, 那就可能包含不能被模板字符集所编码的字符。

如果使用了 Template.createProcessingEnvironment(...)Environment.process(...) 方法来代替 Template.process(...) 方法, FreeMarker 的设置可以对任意独立执行的模板进行。 因此,你可以对每个独立执行的模板设置 output_encoding 信息:

Writer w = new OutputStreamWriter(out, outputCharset);
Environment env = template.createProcessingEnvironment(dataModel, w);
env.setOutputEncoding(outputCharset);
env.process();