2

I was wondering if it would be possible to write a command that would do a global find-and-replace of certain words in order to quickly set the document to older orthographic conventions. I can't find anything that does this globally. Although I could do \StrSubstitute on the entire document via xstring but I feel like there should be a way to do it where one could put the command—let's say \oldorthography—in the preamble or beginning of the document and it would do it all for you. The MWE and imagined output I'm envisioning is below; I just don't know how to make it work.

\documentclass{article}
\usepackage{xstring}

\newcommand{\oldorthography}{
\StrSubstitute{main.tex}{dais}{daïs}[\nova]
\StrSubstitute{\nova}{cooperate}{coöperation}[\nova]
\StrSubstitute{\nova}{anasthesia}{anasthæsia}[\nova]
\StrSubstitute{\nova}{hotel}{hôtel}
}

\oldorthography
\begin{document}
\obeylines%just to save space
dais
cooperate
anasthesia
hotel
\end{document}

would output

enter image description here

The code without the option enabled would be the exact same thing without the actual command \oldorthography and would output

enter image description here

I'm sure it's a really basic thing I just don't know about TeX but if anyone has any insight, that would be super helpful. Thanks!!

0

3 Answers 3

1

Run with lualatex:

\documentclass{article}
\usepackage{chickenize}

\addtosubstitutions{dais}{daïs}
\addtosubstitutions{cooperate}{coöperation}
\addtosubstitutions{anasthesia}{anasthæsia}
\addtosubstitutions{hotel}{hôtel}
% just to show how one can override the substitution in word parts
\addtosubstitutions{hôtels}{hotels}

\substitutewords % comment to disable substitutions

\begin{document}

dais
cooperate
anasthesia
hotel
hotels

\end{document}

Beware that the substitution is also in word parts, so some care is needed if a word to be substituted may be a part of a word not to be substituted and I added an admittedly silly example.

enter image description here

2
  • This mostly works great, but one thing I noticed is that it also transforms control sequences. So like if I add \addtosubstitutions{standardize}{ſtandardize} and then try to use a control sequence \standardize it doesn't work. Is there a way around this? May 6, 2021 at 12:22
  • 1
    @generallyconfuzzled With \newcommand{\standardize}{whatever}, do \addtosubstitutions{\\standardize}{\standardize} and, later, \addtosubstitutions{standardize}{ſtandardize} But the best is to avoid this.
    – egreg
    May 6, 2021 at 14:46
2

You seem to wish TeX to analyze the entire .tex-source code/.tex-input-file and doing some (orthographic) adjustments before actually processing it the usual way.

This is not how traditional TeX-engines are designed to work.

Using Knuth's analogy of TeX being a beast with eyes and a digestive tract you can simplify things a bit and say that TeX is designed to work as follows:

TeX's eyes look at .tex-input-files line by line and TeX places characters (not tokens yet!) corresponding to those seen in a line of .tex-input into its mouth.
So TeX's mouth receives sequences of characters more or less corresponding to those seen when "looking" at a line of .tex-input.
TeX's mouth "chews" these sequences of characters into smaller bits, so-called tokens (character-tokens/control-sequence-tokens). In other words: TeX's mouth takes the single characters as sequences of directives for producing so-called tokens (character-tokens/control-sequence-tokens).
The mouth produces the tokens and sends them down the gullet.
Expandable tokens, e.g., macro-tokens, are expanded while going through the gullet. (Unless the gullet was triggered to suppress expansion, which is the case, e.g., with tokens forming the definition-text of a macro defined in terms of \def. LaTeX's \newcommand is a macro which is based on \def.) The gullet is the place of expansion.
After going through the gullet tokens reach the stomach where executable tokens, e.g., things like \def, are executed/where assignments are carried out, boxes are build, paragraphs are broken into lines, lines are broken across pages, and the .pdf-output-file is created.

Tailored to your question the gist of all this (over?)simplification of traditional TeX engines' way of working is:

TeX's concepts of TeX's eyes line-wise processing files of .tex-input and TeX's mouth character-wise-processing the single lines of .tex-input actually contradicts the idea of having TeX "look" at an entire .tex-input-file as a whole or at an entire line of .tex-input as a whole for doing whatsoever adjustments (e.g., replacements or the like) before "digesting" things the usual way.

You probably can use a LuaTeX-based engine and have it pre-preprocess things by Lua-features (whereby replacing could take place) before having them passed to TeX's -eh- usual digestion-mechanisms. (This is the approach exhibited in egreg's answer.) Alternatively you probably can have TeX read the entire .tex-input-file in verbatim-catcode-régime and have it do the replacement to the resulting set of tokens (e.g., via xstring's \StrSubstitute or by the routines provided by expl3) and then switch back to normal catcode-régime and pass the result to \scantokens. But this answer of mine to an older question which is also about replacing things in a document provides a short survey on some scenarios where a simple search-replace (no matter how it is done) of phrases occurring in the .tex-source code might not be sufficient.

How about introducing an \if-switch for forking the orthographic style and having TeX define macros for each word, depending on that \if-switch?

A problem with such an approach might be that after tokenizing a control-word-token TeX usually ignores spaces occurring in the .tex-input. There are situations where this doesn't matter, e.g., at the end of a sentence or subordinate clause, when the word in question is trailed by some punctuation-mark or comma. There are also situations where a trailing space should not be ignored—e.g., when more words follow. You could use the xspace-package. In a very high percentage of cases its command \xspace makes the right decision regarding insertion of a space behind a word. In the example below I chose another approach: Each control-word-token denoting a word whose orthography may be changed must be trailed by a slash / which gets gobbled. Slashes being tokenized as character-tokens implies that spaces after slashes will not be ignored. However, the approach of defining a macro for each word whose orthography shall be changeable does not take into account scenarios like things occurring inside a verbatim-environment or occurring in arguments of \verb-commands and the like, i.e., scenarios where things are not expanded but processed verbatim. Also you need to take care that with things like \uppercase/\lowercase the macros are expanded before the case-changing-routine is carried out. LaTeX's \MakeUppercase/\MakeLowercase do this for you.

The way I defined things in the example below also lets you change/switch orthography within the document/for parts of the document only. If you do this, take care with so-called moving-arguments (i.e. with arguments that wind up in several places of the document, e.g., section-titles that wind up in the main text, in the table of contents, and probably in page-headers, and probably in bookmarks of .pdf-files) to obtain the right orthographic variants in the right places of the document/of the .pdf-output-file.

\documentclass{article}

\newif\ifoldorthography

\makeatletter
\@ifdefinable\DefineOrthographyDependantCommand{%
  \DeclareRobustCommand\DefineOrthographyDependantCommand[3]{%
    \@ifdefinable#1{%
      \def#1/{\ifoldorthography\expandafter\@secondoftwo\else\expandafter\@firstoftwo\fi{#2}{#3}}%
    }%
  }%
}%
\makeatother

\DefineOrthographyDependantCommand\dais{dais}{daïs}%
\DefineOrthographyDependantCommand\cooperate{cooperate}{coöperate}%
\DefineOrthographyDependantCommand\anasthesia{anasthesia}{anasthæsia}%
\DefineOrthographyDependantCommand\hotel{hotel}{hôtel}


\begin{document}

\oldorthographytrue

old orthography:

\dais/ \cooperate/ \anasthesia/ \hotel/

\dais/, \cooperate/, \anasthesia/, \hotel/.

\ifoldorthography
\begin{verbatim}
daïs
coöperate
anasthæsia
hôtel
\end{verbatim}
\else
\begin{verbatim}
dais
cooperate
anasthesia
hotel
\end{verbatim}
\fi

\bigskip\hrule\bigskip

\oldorthographyfalse

current orthography:

\dais/ \cooperate/ \anasthesia/ \hotel/

\dais/, \cooperate/, \anasthesia/, \hotel/.

\ifoldorthography
\begin{verbatim}
daïs
coöperate
anasthæsia
hôtel
\end{verbatim}
\else
\begin{verbatim}
dais
cooperate
anasthesia
hotel
\end{verbatim}
\fi

\end{document}

enter image description here

1

The original answer started as an exact steal of my answer at Automatic text highlighting based on a dictionary. The only difference was that rather than highlighting the specified dictionary words with color, I transformed them.

More recently, I have EDITED the answer based on a comment, to allow for letter sequences to be specified as part of the transformation. Previously, only exact matches to the "transformation dictionary" words would be transformed. Now, that capability is expanded so that, for example, every occurrence of ai is replaced with even when it falls within a word. From the code's point of view, this means implementing a transform for \autohighlightStyleB, which had been left empty in the original answer.

Therefore, definitions for highlighting had to be changed to

\newcommand\autohighlightStyleA{\transformword}% IS WORD
\newcommand\autohighlightStyleB{\transforminword}% INCLUDES WORD
\newcommand\transformword[1]{\csname T\detokenize{#1}\endcsname}
\newcommand\transforminword[1]{\readlist\dictcompB{#1}%
  \foreachitem\z\in\dictcompB{%
    \ifnum\zcnt=1\relax\else\csname T\detokenize
      \expandafter\expandafter\expandafter
      {\dictcompBsep[\zcnt-1]}\endcsname\fi
    \z
  }%
}

Of course I also had to provide for the search and then define the transformation for each word or letter sequence of interest:

\setsepchar{ai||oo||oe||anasthesia||hotel}
\def\Tai{aï}
\def\Too{oö}
\def\Toe{ö}
\def\Tanasthesia{anasthæsia}
\def\Thotel{hôtel}

This newer feature, while more powerful, is more dangerous, as it will perhaps transform parts of words you didn't intend. For exceptions to the rule, you can use the delimiters |...| to prevent any transformation of the included text.

You can turn it on at the beginning of the document and turn it off at the end. The only code you may have to exit before and re-enter after is code that changes catcodes. Since the tokencycle digests and analyzes each token in the input stream before it is typeset, its catcode gets set during the analysis phase, and thus becomes impervious to catcode changes thereafter.

Here is the MWE, which requires the version of tokcycle of 2021-03-10. The pseudo-environment is called \transform...\endtransform. Note that "toe" has been excluded from transformation with the use of |toe|.:

\documentclass{article}
\usepackage{tokcycle}[2021-03-10]
\usepackage{listofitems}
\setsepchar{ai||oo||oe||anasthesia||hotel}
\def\Tai{aï}
\def\Too{oö}
\def\Toe{ö}
\def\Tanasthesia{anasthæsia}
\def\Thotel{hôtel}
\newcommand\testdict{%
  \if\relax\detokenize\expandafter{\currentword}\relax\else
    {\ignoreemptyitems
      \greadlist\dictcompA{\currentword}}%
    \readlist\dictcompB{\currentword}%
    \ifnum\listlen\dictcompA[]=0\relax
      \addcytoks[1]{\autohighlightStyleA}%
      \addcytoks[1]{\expandafter{\currentword}}%
    \else
      \ifnum\listlen\dictcompB[]>1\relax
        \addcytoks[1]{\autohighlightStyleB}%
        \addcytoks[1]{\expandafter{\currentword}}%
      \else
        \addcytoks[1]{\currentword}%
      \fi
    \fi
  \fi
  \gdef\currentword{}%
}
\makeatletter
\xtokcycleenvironment\transform
{\tctestifcatnx A##1{\g@addto@macro\currentword{##1}}
  {\testdict\addcytoks{##1}}}
{\testdict\groupedcytoks{\processtoks{##1}\testdict}}
{\g@addto@macro\currentword{##1}}
{\testdict\addcytoks{##1}}
{\stripgroupingtrue\def\currentword{}}
{}
\makeatother
\newcommand\autohighlightStyleA{\transformword}% IS WORD
\newcommand\autohighlightStyleB{\transforminword}% INCLUDES WORD
\newcommand\transformword[1]{\csname T\detokenize{#1}\endcsname}
\newcommand\transforminword[1]{\readlist\dictcompB{#1}%
  \foreachitem\z\in\dictcompB{%
    \ifnum\zcnt=1\relax\else\csname T\detokenize
      \expandafter\expandafter\expandafter
      {\dictcompBsep[\zcnt-1]}\endcsname\fi
    \z
  }%
}
\begin{document}
\transform
While speaking from the dais,
I decided to cooperate with Doctor Roentgen calling for help
on a patient with a broken |toe|.

He was asking for anasthesia
and so I directed him to the best hotel,
here in the city of many hotels.
\endtransform

\end{document}

enter image description here

3
  • This is great! Works perfectly for what I need. Do you know if there's a way to make it so it works on parts of words? On future documents there's a chance I might want to substitute most letters s for ſ, and this looks really promising. Thanks again! May 6, 2021 at 12:24
  • @generallyconfuzzled please see my edit. May 6, 2021 at 13:27
  • @generallyconfuzzled Perhaps you are not aware. The way you reward answers on this site (not only to your questions, but to any answer you like), you can click the up-arrow to the left of an answer (or question, except your own) to "up-vote" it. It is also considered polite protocol that, if one of the several answers you receive for a question is best suited to answer your question, you may "accept" that answer by clicking the check to the left of the question. Only one answer may be accepted per question, but there are no such restrictions on up-voting. May 6, 2021 at 13:42

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .