As mentioned in the comment, your main issue was that the \n in the replacement text of the \@namedef was not expanded to its value. The replacement text for Faculty:fi for example thus remained literally
{\scriptsize (Use {\tt\textbackslash #2[\n]} to replace this text.)}%
with \n. In most contexts where the macro would be called \n would be undefined and you would get an error. You want the macro replacement text to be constructed so that \n becomes its expansion, i.e. fi or en. The easiest way to get that done properly is probably a helper macro. This answer will show two approaches: One with etoolbox and its list macros and one with your code that uses \expandafter and a helper macro. etoolbox's loops work by passing the value of the loop variable directly as an argument to a helper macro.
In the first argument of \@namedef (i.e. the #2:\n), the \n would be expanded automatically so that it would really become fi there.
Here is a solution using etoolbox and its list macros. Some explanations are inline. The advantage of this approach is that there is no need for expansion of the loop variable (\n) as the loop is directly implemented as a macro (usually requiring a helper macro).
\documentclass[english,finnish]{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{babel}
\usepackage{etoolbox}
\makeatletter
% {<name>}{<lang>}
\newcommand*{\mlv@defaultvalue}[2]{%
\csdef{#1@#2}{%
{\scriptsize (Use {\ttfamily\textbackslash #1[#2]} to replace this text.)}}}
% {<repl. text>}{<name>}{<lang>}
% the unusual order instead of the more natural {<name>}{<lang>}{<repl. text>}
% makes for a more elegant call to \forcsvlist
\newcommand*{\mlv@realvalue}[3]{%
\csdef{#2@#3}{#1}}
% [<langs>]{<name>}
% \forcsvlist{<macro>}{<item_1, item_2, ..., item_n>}
% calls <macro> once with each item_i as last argument
% <macro>{<item_1>}, <macro>{<item_2>}
% <macro> can already bring a few arguments of its own
% it could be <marco>{<fixed_1>}...{<fixed_n>} and then the invocations
% would become <marco>{<fixed_1>}...{<fixed_n>}{<item_1>} etc.
% Since the items are the <lang> argument it must be the last
% argument to \mlv@defaultvalue and \mlv@realvalue
\newcommand\MakeLocaleVar[2][en,fi]{%
\forcsvlist{\mlv@defaultvalue{#2}}{#1}%
\expandafter\newcommand\csname #2\endcsname[2][]{%
\ifblank{##1}
{\forcsvlist{\mlv@realvalue{##2}{#2}}{#1}}%
{\mlv@realvalue{##2}{#2}{##1}}}%
\expandafter\newcommand\csname Emit#2\endcsname[1][en]{\csuse{#2@##1}}%
}
\makeatother
\begin{document}
\MakeLocaleVar{Faculty}
% In main tex: set values
\Faculty{This faculty} % for all values
\Faculty[fi]{Tämä tiedekunta} % for a specific one
% In cls environments: use values
\EmitFaculty[en]
\EmitFaculty[fi]
% Now in addition I'd like to be able to:
\MakeLocaleVar[en,fi,de]{Gaculty}
\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]
\Gaculty{Foo}
\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]
\Gaculty[fi]{Föö}
\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]
\end{document}
![This faculty Tämä tiedekunta//(Use \Gaculty[en] to replace this text.) (Use \Gaculty[fi] to replace this text.) (Use \Gaculty[de] to replace this text.)//Foo Foo Foo//Foo Föö Foo](../../images/f31233ee545401723fc6d576327aa7e3.webp)
If you want to stick to your version with \foreach and \ifthenelse, you can make use of a helper function.
The trick is that we need to expand \n so that we get its actual value. This is possible with \expandafter, but to avoid having to use it with surgical precision to jump over many tokens, a helper function is useful. Since it is slightly easier to expand the first argument of a macro with \expandafters instead of a later argument, the helper function uses the slightly unexpected argument order {<lang>}{<name>}.
\documentclass[english,finnish]{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{babel}
\usepackage{ifthen}
\usepackage{tikz}
\makeatletter
% helper for easier control of expansion
% {<lang>}{<name>}
\newcommand*{\mlv@helper}[2]{%
\global\@namedef{#2:#1}{%
{\scriptsize (Use {\ttfamily\textbackslash #2[#1]} to replace this text.)}%
}%
}
% \n must be expanded to be useful.
% The first argument of \@namedef automatically expands it,
% but the second does not.
% Here we use a helper function to expand \n
% before it is processed.
\newcommand\MakeLocaleVar[2][en,fi]{%
\foreach \n in {#1}{%
\expandafter\mlv@helper\expandafter{\n}{#2}%
}%
\expandafter\newcommand\csname #2\endcsname[2][]{%
\ifthenelse{\equal{##1}{}}{%
\foreach \n in {#1}{%
\global\@namedef{#2:\n}{##2}%
}%
}{%
\global\@namedef{#2:##1}{##2}%
}%
}%
\expandafter\newcommand\csname Emit#2\endcsname[1][en]{\@nameuse{#2:##1}}%
}
\makeatother
\begin{document}
\MakeLocaleVar{Faculty}
% In main tex: set values
\Faculty{This faculty} % for all values
\Faculty[fi]{Tämä tiedekunta} % for a specific one
% In cls environments: use values
\EmitFaculty[en]
\EmitFaculty[fi]
% Now in addition I'd like to be able to:
\MakeLocaleVar[en,fi,de]{Gaculty}
\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]
\Gaculty{Foo}
\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]
\Gaculty[fi]{Föö}
\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]
\end{document}
BTW: I used \ttfamily instead of \tt, because the two-letter font commands are deprecated in LaTeX2e (https://texfaq.org/FAQ-2letterfontcmd, Will two-letter font style commands (\bf , \it , …) ever be resurrected in LaTeX?).
\@namedef{#2:\n}is defined, its replacement text contains\n. So when you callFaculty:filets, say it gets replaced by... Use {\tt\textbackslash #2[\n]} ...with\nand not with the value of\nat the timeFaculty:fiwas defined, i.e.fi. One way to get around this would be by moving the\global\@namedef{#2:\n}to a helper function and expanding the argument of the helper function before it is called. – moewe Mar 04 '19 at 09:41\nbe expanded in the very loop it is ment to be used in? It is expanded in the namedef. Does something prevent that in the text after? – Felix Mar 04 '19 at 09:47\@namedefcarries out a full expansion of its (first) argument, but the replacement text (second argument of\@namedef) is not expanded. I'm working an alternative withetoolboxright now, if you are interested. – moewe Mar 04 '19 at 09:49expandafter? – Felix Mar 04 '19 at 09:50