# Modifying page-number formatting in in-text citations when using natbib

I use natbib in author-year format, along with a custom .bst file. It formats bibliographies as I want. But in the text of my documents, I have many citations like \citet[12-30]{lamport94}. They produce the expected output: "Lamport (1994, 12-30)."

I want to change the formatting of the argument in brackets so that it displays with an en dash instead of a hyphen. That is, I want the output to be "Lamport (1994, 12–30)." But I don't want to modify all of my citations "by hand." Instead, I want to add a command or modify my .bst file so that I can automatically convert the hyphens to en dashes. How may I do this?

I already have, in my .bst file, a command that replaces hyphens with en dashes in a given string. But I can't see how to change my .bst file to modify the text that lies within the optional brackets of a \citet or \citep command. For example, I don't see how to use my .bst file to format the "xxx" in citet[xxx]{lamport94}, or how to use my .bst file to modify the page range in \citet[12-30]{lamport94}.

• Future visitors may be interested to know that this 'page range normalisation' has been a feature in biblatex since version 3.11, see github.com/plk/biblatex/issues/293. – moewe Jul 23 '19 at 6:15

It is possible to modify the citet command to perform a string substitution from - to -- in the optional command, using \StrSubstitute from the xstring package.

In natbib the citing commands are structured as a branching sequence of commands that are called. Some of those commands are short and can be redefined entirely without needing much code in your source file. In the MWE below I have selected \NAT@@citetp as a command to modify, which is the third command in the sequence (after \citet itself and \NAT@citetp). This command is only one line and it exposes the optional command, therefore the string substitution can be performed.

Code:

\documentclass[12pt]{article}
\usepackage[round]{natbib}
\usepackage{xstring}
\makeatletter
\def\NAT@@citetp[#1]{%
\StrSubstitute{#1}{-}{--}[\newdash]%
% original code: \@ifnextchar[{\@citex[#1]}{\@citex[][#1]}
\@ifnextchar[{\@citex[\newdash]}{\@citex[][\newdash]}}
\makeatother
\begin{document}

\noindent Changing\\
Lamport (1994, 12-30) into\\
\citet[12-30]{lamport94}

\begin{thebibliography}{1}
\providecommand{\natexlab}[1]{#1}
\providecommand{\url}[1]{\texttt{#1}}
\expandafter\ifx\csname urlstyle\endcsname\relax
\providecommand{\doi}[1]{doi: #1}\else
\providecommand{\doi}{doi: \begingroup \urlstyle{rm}\Url}\fi

\bibitem[Lamport(1994)]{lamport94}
Leslie Lamport,
\emph{\LaTeX: A Document Preparation System}.
2nd Edition,
1994.
\end{thebibliography}
\end{document}


Result:

For postnotes it is possible to redefine the \NAT@cite command (using \renewcommand because this command does not have a \def version):

\documentclass[12pt]{article}
\usepackage[round]{natbib}
\usepackage{xstring}
\makeatletter
\def\NAT@@citetp[#1]{%
\StrSubstitute{#1}{-}{--}[\newdash]%
\@ifnextchar[{\@citex[\newdash]}{\@citex[][\newdash]}}
\renewcommand\NAT@cite[3]{%
\ifNAT@swa\NAT@@open\if*#2*\else #2\NAT@spacechar\fi
#1\if*#3*\else\NAT@cmt\StrSubstitute{#3}{-}{--}\fi\NAT@@close\else #1\fi\endgroup}
\makeatother
\begin{document}

\noindent Changing\\
Lamport (1994, 12-30) into\\
\citet[12-30]{lamport94}

\noindent postnote: \citep[see, e.g.,][12-30]{lamport94}

\begin{thebibliography}{1}
\providecommand{\natexlab}[1]{#1}
\providecommand{\url}[1]{\texttt{#1}}
\expandafter\ifx\csname urlstyle\endcsname\relax
\providecommand{\doi}[1]{doi: #1}\else
\providecommand{\doi}{doi: \begingroup \urlstyle{rm}\Url}\fi

\bibitem[Lamport(1994)]{lamport94}
Leslie Lamport,
\emph{\LaTeX: A Document Preparation System}.

• This solution works for \citet[12-30]{lamport94} but not for, say, \citep[see, e.g.,][12-30]{lamport94}. Can you think of a way to modify your code so that it operates on the second of two optional arguments of \citet and \citep? – Mico Jul 22 '19 at 18:05
• @Mico that can be done by redefining \NAT@cite, see edit. – Marijn Jul 22 '19 at 21:52