LaTeX3 模块l3msg
提供了一些不错的消息传递、错误、警告等功能,并且非常灵活。工作流程与我们之前使用的工作流程不同LaTex2e
软件包不同。这方面的最佳实践是什么?
在我看来,一些好的做法(也用于其他计算机语言)是:
- 在 l3msg 之上包装您自己的错误、警告函数等。
- 创建一些布尔值来表示错误和警告(即使您没有使用它们)。
- 不确定如何最好地安排国际化信息,一种方法是汇总
\msg_new
文件中的所有内容,并根据语言切换加载它们。这将类似于gettext
。第二种想法是将它们存储为标记列表(字符串),并使用translator
类型机制来翻译它们。首选方法是使用更高的层次结构函数:
例如不要这样写:
\msg_set:nnnn {mypackage} { old-version }
{ LaTeX~source~files~more~than~5~year~old.~ Is~dated~
(year:#1~date:#2-#3)
字符串应该在翻译文件中,在目录中标记en
等,格式如下
__str{old-version}{en}{LaTeX~source~files~more~than~5~year~old.}
在德语文件中可能类似如下内容:
__str{old-version}{LaTeX olde ...}}
然后\msg_set
可以修改该函数来调用:
\msg_set:nnnn {mypackage} { old-version }
{__translate{old-version}}
下面显示部分 MWE(仅用于实验)。
\documentclass{article}
\usepackage{expl3}
\parindent0pt
\begin{document}
% Error message example
%
% simulate LaTeX2e \fmversion
\def\fmversion{2000/11/12}
\makeatletter
\ExplSyntaxOn
% create error boolean
\bool_new:N \l_mypackage_error_bool
% redirect package errors here
\cs_new_protected:Npn \mypackage_warning:nxx #1#2#3 {
\bool_set_true:N \l_mypackage_error_bool
\msg_info:nnxx { mypackage } {#1} {#2} {#3}
}
% define some error messages
\msg_set:nnnn {mypackage} { old-version }
{ LaTeX~source~files~more~than~5~year~old.~ Is~dated~(year:#1~date:#2-#3) }
{ Please~update~your~distribution~visit~ctan } %(*@\label{test}@*)
% check version number
\cs_new:Npn \mypackage_check_version:n #1 {
\exp_after:wN \l_mypackage_check_version_aux:w #1\q_stop
}
% check version number auxiliary
\cs_new:Npn \l_mypackage_check_version_aux:w #1/#2/#3\q_stop {
\int_compare:nNnTF { (\tex_year:D-#1)*12 + (\tex_month:D-#2) } > { 65 }
{ FAIL\\ \mypackage_warning:nxx {old-version} {#1} {#1/#2/#3} }
{ PASS\\ }
}
\mypackage_check_version:n \fmversion
\mypackage_check_version:n \fmtversion
\ExplSyntaxOff
\end{document}
答案1
正如您所注意到的,l3msg
其设计与 LaTeX2e 消息系统截然不同。其背后有两个关键的设计理念
信息文本在使用消息的地方不需要。这主要是因为我们今天在 TeX 中有很多可用的“空间”,而 LaTeX2e 编写时却没有。因此,
expl3
通常鼓励使用大量小函数/变量而不是单片代码。对于消息来说,如果一个函数有“在这里使用消息 X”的概念,而不是所有文本,那么它会更有意义。这会产生一些负面效果,例如本地化的可能性,以及每条消息都有一个定义的名称。消息的数量/性质应该由最终用户控制。并不是每个人都希望收到关于他们不关心的内容的警告,ETC。,所以这是一项重要的能力,确实需要培养。
就最佳实践而言,只要牢记上述内容,就应该没问题。关于包装器的想法,我认为这取决于程序员。直接使用内核级代码
\msg_error:nn { mymodule } { drat }
对于一次性来说很好,而且一目了然,但对于较大的模块来说,保持代码简短更有优势
\mymod_error:n { drat } % Or \@@_error:n { draft } using l3docstrip
设置布尔返回值很大程度上取决于错误/警告/...在所涉及代码上下文中的含义:这是我们没有添加“通用”机制的原因之一。这里要注意的一点是关于消息过滤的要点:仅仅因为您将其作为错误处理并不意味着用户不会将其关闭。如果您使用布尔返回系统,那么当然,您可能还需要一个包装器。
关于消息的语言,虽然这是拥有命名消息系统的明显好处,但这并不是其主要原因。因此,这个领域尚未得到充分探索,因此以下内容有些推测。我认为类似机制translator
可能不是这里的出路。消息本地化应该适用于整个运行,因此不需要动态更改它们。因此,可以想象一组消息在不同的文件或类似文件中(每种语言一个),其中文本使用设置\msg_set:nnn(n)
。例如,这可能看起来像
% Core messages always in English
\msg_new:nnn { mymodule } { drat } { Drat! }
...
\file_input:n { mymodule-messages- \l_interface_language_tl }
...
% mymodule-messages-de
\msg_set:nnn { mymodule } { drat } { Achje! }
% mymodule-messages-fr
\msg_set:nnn { mymodule } { drat } { Zut! }
重要的是,改变消息的全部内容不仅仅意味着交换单词(这就是它的作用translator
):语法变化等等使生活变得更加复杂。
长期目标是,消息应该被记录下来,以便实现本地化和自动解析(可以完全关闭“用户”文本,只返回消息名称和参数)。我认为理想的做法是列出消息的名称、消息使用的上下文以及消息所采用的参数的含义。然后可以将其用于各种目的。
问题中未提及但需要考虑的一个领域是“跟踪”代码。最初我们提供了一个trace
消息类,但已将其删除。这里的逻辑是,对于非常低级的跟踪,相当“丰富”的消息重定向系统是l3msg
错误的方法(存在性能成本)。因此,如果您确实希望提供非常详细的跟踪数据,则应该使用低级\iow_log:n
和合适的包装器
% If not tracing
\cs_new_eq:NN \mymodule_trace:n \use_none:n
% If tracing
\cs_new_eq:NN \mymodule_trace:n \iow_log:n