有条件地在 biblatex 后记中打印一些内容(标点符号问题)

有条件地在 biblatex 后记中打印一些内容(标点符号问题)

我受到了 biblatex 标点符号追踪器的打击,我想我最好寻求一些帮助。

我正在尝试创建一个宏,该宏旨在用作已翻译某段引文的通用标记。这个宏的自然位置是引用后记,但我不想做得太多,我认为每章或每节都这样做就足够了。

因此,我想出了以下宏:

\newcounter{mytranslationcount}[section]
\newrobustcmd*{\mytranslation}{%
  \ifnum\value{mytranslationcount}=0
    \addcomma\addspace\printtext{my translation, as will be all following ones
      in a foreign language}%
    \stepcounter{mytranslationcount}%
  \fi}

它大部分情况下都能正常工作,但并不完全有效。它确实会在需要时打印预期的文本。但是,它会干扰 的biblatex标点符号跟踪器,当宏不打印任何内容且后注中没有其他内容时,这会导致不想要的输出。在这种情况下,打印后注(从 biblatex 的角度来看它不是空的)会触发标点符号跟踪器,我们会得到一个逗号。而且,由于它后面没有其他内容,我们也会丢失最后一个句号(在脚注的情况下)。

一位 MWE 表示:

\documentclass{article}

\usepackage[style=authoryear,autocite=footnote]{biblatex}

\addbibresource{biblatex-examples.bib}

\newcounter{mytranslationcount}[section]
\newrobustcmd*{\mytranslation}{%
  \ifnum\value{mytranslationcount}=0
    \addcomma\addspace\printtext{my translation, as will be all following ones
      in a foreign language}%
    \stepcounter{mytranslationcount}%
  \fi}

\begin{document}

\section{Section 1}

% Looks good.
\autocite[\pnfmt{3333-3345}\mytranslation]{sigfridsson}

% Also good.
\autocite[\pnfmt{3333-3345}\mytranslation]{sigfridsson}

\section{Section 2}

% Still good.
\autocite[\mytranslation]{sigfridsson}

% This is the offending one.  When there is nothing else in the postnote and
% \mytranslation prints nothing, biblatex has already called in the postnote
% punctuation from the tracker, so that we get an unwanted comma and lose an
% wanted period.
\autocite[\mytranslation]{sigfridsson}

\end{document}

我希望有一个这样的宏,可以在后记中使用,以便以这种方式实现语义价值。 怀着这种希望,我还没有尝试诉诸\AtNextCite(key)或直接篡改postnote宏,但我愿意倾听……

答案1

正如问题本身和评论中已经讨论过的,这里的问题是,即使命令在实际执行时没有打印任何内容,biblatex它看起来也像是一个非空的后记。(使用测试来检查是否为空。)\mytranslationbiblatexetoolbox\ifblank

我能想到的唯一解决方案是更改空白检查以执行后记并测量其打印宽度。 如果宽度为零,则后记为空,如果宽度非零,则后记不为空。

这意味着 postnote 代码被执行了两次(一次用于测量,一次用于实际打印),所以我们必须小心使用计数器。在下面的 MWE I 中,我通过引入一个可用于检查我们是否处于测量或打印步骤的切换来解决这个问题。

biblatex-apa已经有一些代码来测量“打印宽度”,所以我从那里获取了大部分代码。这并不像做起来那么简单,\setbox0=\hbox{#1}因为我们需要确保不扰乱标点符号跟踪器。

\documentclass{article}

\usepackage[style=authoryear,autocite=footnote]{biblatex}

\addbibresource{biblatex-examples.bib}

\newtoggle{blx@measuringstep}

\newcounter{mytranslationcount}[section]
\newrobustcmd*{\mytranslation}{%
  \ifnumgreater{\value{mytranslationcount}}{0}
    {}
    {\addcomma\addspace\printtext{my translation, as will be all following ones
       in a foreign language}%
     \iftoggle{blx@measuringstep}
       {}
       {\stepcounter{mytranslationcount}}}}

% Thanks to egreg from https://tex.stackexchange.com/a/53091
% for this test for expanded emptiness so that we can easily opt to not print parens around nothing
% Without this, it is very messy - you have to test all potential fields for defness first and this
% is messy because the fields in the additional info vary betwee entrytypes
\makeatletter
\def\foreverunspace{%
  \ifnum\lastnodetype=11
    \unskip\foreverunspace
  \else
    \ifnum\lastnodetype=12
      \unkern\foreverunspace
    \else
      \ifnum\lastnodetype=13
        \unpenalty\foreverunspace
      \fi
    \fi
  \fi
}

% we need a way to save the state of the punctuation buffer
% cf. \blx@initunit in biblatex.sty for what we need to copy

% this uses the internal implementation of etoolbox toggles
% fingers crossed no one messes with it
\newrobustcmd*{\blx@savetoggle}[1]{%
  \csletcs{apablx@savedtoggle@#1}{etb@tgl@#1}}

\newrobustcmd*{\blx@restoretoggle}[1]{%
  \csletcs{etb@tgl@#1}{apablx@savedtoggle@#1}}

\newrobustcmd*{\blx@savepunctstate}{%
  \blx@savetoggle{blx@block}%
  \blx@savetoggle{blx@unit}%
  \blx@savetoggle{blx@insert}%
  \blx@savetoggle{blx@lastins}%
  \blx@savetoggle{blx@keepunit}%
  \let\apablx@savd@unitpunct\blx@unitpunct
  \let\apablx@savd@puncthook\abx@puncthook}

\newrobustcmd*{\blx@restorepunctstate}{%
  \global\blx@restoretoggle{blx@block}%
  \global\blx@restoretoggle{blx@unit}%
  \global\blx@restoretoggle{blx@insert}%
  \global\blx@restoretoggle{blx@lastins}%
  \global\blx@restoretoggle{blx@keepunit}%
  \global\let\blx@unitpunct\apablx@savd@unitpunct
  \global\let\abx@puncthook\apablx@savd@puncthook}

% printtext that checks if it would print anything
\newrobustcmd{\ifprintempty}[1]{%
  \blx@savepunctstate
  \toggletrue{blx@measuringstep}%
  \setbox0=\hbox{#1\foreverunspace}%
  \togglefalse{blx@measuringstep}%
  \blx@restorepunctstate
  \ifdimequal{\wd0}{\z@}}

\newrobustcmd{\iffieldempty}[1]{%
  \iffieldundef{#1}
    {\@firstoftwo}
    {\expandafter\expandafter
     \expandafter\ifprintempty
     \expandafter\expandafter
     \expandafter{\csname abx@field@#1\endcsname}}}
\makeatother

\renewbibmacro*{postnote}{%
  \iffieldempty{postnote}
    {}
    {\setunit{\printdelim{postnotedelim}}%
     \printfield{postnote}}}

\begin{document}

\section{Section 1}

% Looks good.
\autocite[\pnfmt{3333-3345}\mytranslation]{sigfridsson}

% Also good.
\autocite[\pnfmt{3333-3345}\mytranslation]{sigfridsson}

\section{Section 2}

% Still good.
\autocite[\mytranslation]{sigfridsson}

% OK
\autocite[\mytranslation]{sigfridsson}

\end{document}

4 Sigfridsson 和 Ryde 1998。

答案2

我正在重新审视这个问题,我对替代方案一直都不满意。但是,考虑到这一点,也许这里是跟踪它们的最佳位置。因此,我在下面列出了可能可行的替代方案,每个方案都有其注意事项。

一、测试内容postnote

\documentclass{article}

\usepackage[style=authoryear,autocite=footnote]{biblatex}

\addbibresource{biblatex-examples.bib}

\newcounter{mytranslationcount}[section]
\NewDocumentCommand{\mytranslation}{}{%
  \ifnumequal{\value{mytranslationcount}}{0}
    {%
      \addcomma\addspace\printtext{my translation, as will be all
        following ones in a foreign language}%
      \stepcounter{mytranslationcount}%
    }
    {}%
}
\AtEveryCitekey{%
  \ifboolexpr
    { ( ( test { \iffieldequalstr{postnote}{\mytranslation} }
          or
          test { \iffieldequalstr{postnote}{\mytranslation{}} } )
        and not
        test {\ifnumequal{\value{mytranslationcount}}{0}} ) }
    {\clearfield{postnote}}
    {}%
}

\begin{document}

\section{Section 1}

% Looks good.
\autocite[\pnfmt{3333-3345}\mytranslation{}]{sigfridsson}

% Also good.
\autocite[\pnfmt{3333-3345}\mytranslation]{sigfridsson}

\section{Section 2}

% Still good.
\autocite[\mytranslation{}]{sigfridsson}

% OK.
\autocite[\mytranslation{}]{sigfridsson}

\end{document}

第二,制作\mytranslation一个标志(在引用之外使用):

\documentclass{article}

\usepackage[style=authoryear,autocite=footnote]{biblatex}

\addbibresource{biblatex-examples.bib}

\newcounter{mytranslationcount}[section]
\newboolean{mytranslationbool}
\usepackage{xpatch}
\newcommand*{\mytranslation}{%
  \AtNextCitekey{\booltrue{mytranslationbool}}}
\xapptobibmacro{postnote}{%
  \ifboolexpr{ (
    test {\ifbool{mytranslationbool}}
    and
    test {\ifnumequal{\value{mytranslationcount}}{0}}
    ) }
  {\addcomma\addspace\printtext{my translation, as will be all
        following ones in a foreign language}%
    \stepcounter{mytranslationcount}}
  {}}{}{}

\begin{document}

\section{Section 1}

% Looks good.
\mytranslation\autocite[\pnfmt{3333-3345}]{sigfridsson}

% Also good.
\mytranslation\autocite[\pnfmt{3333-3345}]{sigfridsson}

\section{Section 2}

% Still good.
\mytranslation\autocite{sigfridsson}

% OK.
\mytranslation\autocite{sigfridsson}

\end{document}

第三,使用测量技术的变体@moewe 的回答

\documentclass{article}

\usepackage[style=authoryear,autocite=footnote]{biblatex}

\addbibresource{biblatex-examples.bib}

\newcounter{mytranslationcount}[section]
\newboolean{mytranslationbool}
\newrobustcmd*{\mytranslation}{%
  \ifnumequal{\value{mytranslationcount}}{0}{%
    \addcomma\addspace\printtext{my translation, as will be all
      following ones in a foreign language}%
    \ifbool{mytranslationbool}{\stepcounter{mytranslationcount}}{}}{}}

% Width measurement technique by egreg at:
% https://tex.stackexchange.com/a/53091
\def\foreverunspace{%
  \ifnum\lastnodetype=11
    \unskip\foreverunspace
  \else
    \ifnum\lastnodetype=12
      \unkern\foreverunspace
    \else
      \ifnum\lastnodetype=13
        \unpenalty\foreverunspace
      \fi
    \fi
  \fi
}

% Clear the postnote field if its width is zero.  But the measurement expands
% it, thus we have to protect the counter step in '\mytranslation' behind
% 'mytranslationbool', which we toggle true after measuring.
\AtEveryCitekey{%
  \iffieldundef{postnote}{}{%
    \setbox0=\hbox{\thefield{postnote}\foreverunspace}%
    \ifdim\wd0=0pt
      \clearfield{postnote}
    \fi
    \booltrue{mytranslationbool}%
  }%
}


\begin{document}

\section{Section 1}

% Looks good.
\autocite[\pnfmt{3333-3345}\mytranslation{}]{sigfridsson}

% Also good.
\autocite[\pnfmt{3333-3345}\mytranslation]{sigfridsson}

\section{Section 2}

% Still good.
\autocite[\mytranslation{}]{sigfridsson}

% OK.
\autocite[\mytranslation{}]{sigfridsson}

\end{document}

第四个:

\documentclass{article}

\usepackage[style=authoryear,autocite=footnote]{biblatex}

\addbibresource{biblatex-examples.bib}

\ExplSyntaxOn
\newcounter{mytranslationcount}[section]
\NewDocumentCommand{\mytranslation}{}
  {
    \int_compare:nNnT { \value { mytranslationcount } } = { 0 }
      {
        \addcomma\addspace\printtext{my~translation,~as~will~be~all~
          following~ones~in~a~foreign~language}
        \stepcounter{mytranslationcount}
      }
  }
\AtEveryCitekey
  {
    \tl_set:Nn \l_tmpa_tl { \mytranslation }
    \tl_set:Nn \l_tmpb_tl { \mytranslation{} }
    \iffieldundef { postnote }
      { }
      {
        \bool_lazy_and:nnT
          { \int_compare_p:nNn { \value { mytranslationcount } } > { 0 } }
          {
            \tl_if_eq_p:Nc \l_tmpa_tl { abx@field@postnote } ||
            \tl_if_eq_p:Nc \l_tmpb_tl { abx@field@postnote }
          }
          { \clearfield { postnote } }
      }
  }
\ExplSyntaxOff

\begin{document}

\section{Section 1}

% Looks good.
\autocite[\pnfmt{3333-3345}\mytranslation{}]{sigfridsson}

% Also good.
\autocite[\pnfmt{3333-3345}\mytranslation]{sigfridsson}

\section{Section 2}

% Still good.
\autocite[\mytranslation{}]{sigfridsson}

% OK.
\autocite[\mytranslation]{sigfridsson}

\end{document}

相关内容