(I rewrote this answer rather significantly after discussions with OP and after receiving very significant coding help from @EgorSkriptunoff)
Here's a solution that doesn't pre-specify a list of all abbreviations for which thinspace should be inserted after interior periods (aka "full stops"). Instead, it sets up a pattern matching function to capture u.a., u.a.m., u.v.a.m., z.Zt., Bem.d.Red. and many more such cases. (See the function insert_thinspaces in the code below for the exact pattern matches that are performed.)
Observe also the use of unicode.utf8.gsub instead of string.gsub inside the Lua function insert_thinspaces. This lets the code deal correctly with non-ASCII-encoded letters, such as ä and Ä, which may occur in abbreviations.
On the downside (potentially), this solution method doesn't capture abbreviations if they occur at the start of a sentence, e.g., Z.T. or U.U.; for what it's worth, your parallel answer currently doesn't catch such cases either, right?
The Lua function is assigned to the process_input_buffer callback via a LaTeX macro called \ExpandAbbrOn. If, for any reason, you need to suspend operation of the Lua function, simply execute the instruction \ExpandAbbrOff.
The code checks if the string to be processed lies inside a verbatim-like environment such as verbatim, Verbatim, and lstlisting; if that's the case, no processing is performed. And, with the latest iteration, the code now also ignores material that's in the arguments of inline-verbatim-like macros, such as \verb, \Verb, \lstinline, and \url. For sure, the contents of URL strings should never be processed by the Lua function, right?

% !TeX program = lualatex
\documentclass{article}
\usepackage[ngerman]{babel}
\usepackage{fancyvrb} % for "Verbatim" env.
\usepackage[obeyspaces]{url} % for "\url" macro
\usepackage{listings} % for "\lstinline" macro
%% Lua-side code:
\usepackage{luacode} % for 'luacode*' environment
\begin{luacode*}
-- Names of verbatim-like environments
local verbatim_env = { "[vV]erbatim" , "lstlisting" }
-- By default, we're *not* in a verbatim-like env.:
local in_verbatim_env = false
-- Specify number of parameters for every macro; use neg. numbers
-- for macros that support matching pair of curly braces {}
local all_macros = {
verb = 1,
Verb = 1,
lstinline = -1,
url = -1
}
-- List all poss. delimiters
local all_delimiters = [[!"#$%&'*+,-./:;<=>?^_`|~()[]{}0123456789]]
-- Quick check if "s" contains an inline-verbatim-like macro:
function quick_check ( s )
if s:find("\\[vV]erb") or s:find("\\url") or s:find("\\lstinline") then
return true
else
return false
end
end
-- Function to process the part of string "s" that
-- does *not* contain inline-verbatim-like macros
local function insert_thinspaces ( s )
s = unicode.utf8.gsub ( s ,
"(%l%.)(%a%l?%l?%.)(%a%l?%l?%.)(%a%l?%l?%.)",
"%1\\,%2\\,%3\\,%4" ) -- e.g. "u.v.a.m.", "w.z.b.w."
s = unicode.utf8.gsub ( s ,
"(%l%.)(%a%l?%l?%.)(%a%l?%l?%.)",
"%1\\,%2\\,%3" ) -- e.g., "a.d.Gr."
s = unicode.utf8.gsub ( s ,
"(%u%l%l?%.)(%a%l?%l?%.)(%a%l?%l?%.)",
"%1\\,%2\\,%3" ) -- e.g., "Anm.d.Red."
s = unicode.utf8.gsub ( s ,
"(%l%.)(%a%l?%l?%.)",
"%1\\,%2" ) -- e.g., "z.T.", "z.Zt.", "v.Chr."
return s
end
-- Finally, the main Lua function:
function expand_abbr ( s )
-- Check if we're entering or exiting a verbatim-like env.;
-- if so, reset the 'in_verbatim_env' "flag" and break.
for i,p in ipairs ( verbatim_env ) do
if s:find( "\\begin{" .. p .. "}" ) then
in_verbatim_env = true
break
elseif s:find( "\\end{" .. p .. "}" ) then
in_verbatim_env = false
break
end
end
-- Potentially modify "s" only if *not* in a verbatim-like env.:
if not in_verbatim_env then
-- Quick check if "s" contains one or more inlike-verbatim-like macros:
if quick_check ( s ) then
-- See https://stackoverflow.com/a/45688711/1014365 for the source
-- of the following code. Many many thanks, @EgorSkriptunoff!!
s = s:gsub("\\([%a@]+)",
function(macro_name)
if all_macros[macro_name] then
return
"\1\\"..macro_name
..(all_macros[macro_name] < 0 and "\2" or "\3")
:rep(math.abs(all_macros[macro_name]) + 1)
end
end
)
repeat
local old_length = #s
repeat
local old_length = #s
s = s:gsub("\2(\2+)(%b{})", "%2%1")
until old_length == #s
s = s:gsub("[\2\3]([\2\3]+)((["..all_delimiters:gsub("%p", "%%%0").."])(.-)%3)", "%2%1")
until old_length == #s
s = ("\2"..s.."\1"):gsub("[\2\3]+([^\2\3]-)\1", insert_thinspaces):gsub("[\1\2\3]", "")
else
-- Since no inline-verbatim-like macro found in "s", invoke
-- the Lua function 'insert_thinspaces' directly.
s = insert_thinspaces ( s )
end
end
return(s)
end
\end{luacode*}
%% LaTeX-side code: Macros to assign 'expand_abbr'
%% to LuaTeX's 'process_input_buffer' callback.
\newcommand\ExpandAbbrOn{\directlua{%
luatexbase.add_to_callback("process_input_buffer",
expand_abbr, "expand_abbreviations")}}
\newcommand\ExpandAbbrOff{\directlua{%
luatexbase.remove_from_callback("process_input_buffer",
"expand_abbreviations")}}
\AtBeginDocument{\ExpandAbbrOn} % enabled by default
%% Just for this example:
\setlength\parindent{0pt}
\obeylines
\begin{document}
Dies ist u.U. ein Test.
\begin{Verbatim}
Dies ist u.U. ein Test.
\end{Verbatim}
z.B. u.a. u.Ä. u.ä. u.U. a.a.O. d.h. i.e. v.a.
i.e.S. z.T. m.E. i.d.F. z.Z. u.v.m. z.Zt.
u.v.a.m. b.z.b.w. v.Chr. a.d.Gr. Anm.d.Red.
\begin{verbatim}
z.B. u.a. u.Ä. u.ä. u.U. a.a.O. d.h. i.e. v.a.
i.e.S. z.T. m.E. i.d.F. z.Z. u.v.m. z.Zt.
u.v.a.m. b.z.b.w. v.Chr. a.d.Gr. Anm.d.Red.
\end{verbatim}
U.S.A. U.K. % should *not* be processed
\lstinline|u.a. u.Ä. u.ä.|; \Verb$u.a. u.Ä. u.ä.$
% nested verbatim-like macros
\Verb+u.U.\lstinline|u.U.|u.U.+ \lstinline+u.U.\Verb@u.U.@u.U.+
% 2 URL strings
u.U. \url{u.U.aaaa.z.T.bbb_u.v.a.m.com} u.U.
u.U. \url?u.U.aaaa.z.T.bbb_u.v.a.m.com? u.U.
\end{document}
<single letter>.<single letter>.? Please advise. – Mico Aug 13 '17 at 18:37etc.pp.(but I haven't seen that abbreviation in any text in my entire life). In general they might exist but are not that common. – Skillmon Aug 13 '17 at 18:51\zB(or in my case,\eg, or\ie) to give the desired spacing.\documentclass{article} \newcommand\zB{z.\,B.} \begin{document} Dies ist \zB{} ein Test. \end{document}That way, if I ever change my mind on the notion of "proper" spacing, a one line fix fixes the whole document. – Steven B. Segletes Aug 14 '17 at 12:16