Generalising a second-order macro with loopsExpansion of macro in environment argumentLaTeX Loops in...

Which aircraft had such a luxurious-looking navigator's station?

Has the Isbell–Freyd criterion ever been used to check that a category is concretisable?

When should a commit not be version tagged?

How to speed up a process

Where is this triangular-shaped space station from?

How can I handle a player who pre-plans arguments about my rulings on RAW?

Can I become debt free or should I file for bankruptcy? How do I manage my debt and finances?

How to mitigate "bandwagon attacking" from players?

I am on the US no-fly list. What can I do in order to be allowed on flights which go through US airspace?

Logistics of a hovering watercraft in a fantasy setting

Is there any relevance to Thor getting his hair cut other than comedic value?

Must a tritone substitution use a dominant seventh chord?

Why does the author believe that the central mass that gas cloud HCN-0.009-0.044 orbits is smaller than our solar system?

Whom do I have to contact for a ticket refund in case of denied boarding (in the EU)?

Is my plan for fixing my water heater leak bad?

Why is working on the same position for more than 15 years not a red flag?

Auto Insert date into Notepad

Is divide-by-zero a security vulnerability?

What can I substitute for soda pop in a sweet pork recipe?

Is there a frame of reference in which I was born before I was conceived?

What to do when being responsible for data protection in your lab, yet advice is ignored?

How to avoid being sexist when trying to employ someone to function in a very sexist environment?

What if I store 10TB on azure servers and then keep the vm powered off?

How can atoms be electrically neutral when there is a difference in the positions of the charges?



Generalising a second-order macro with loops


Expansion of macro in environment argumentLaTeX Loops in newcommandDeclaring variables and writing loopsProblem using macro with TikZ foreachDefine macro for sequence/list/tuple macrosDefining newtheorems using loopsExpandable macro with loops and advanced string functions?Loops with Latex?For-loops with different iterationsDefine variable without newcommandDynamic Conditional Variables













1















I'd like to construct a macro that can be used to define commands. Those commands behave like variables or structs of variables, such that they can contain multiple values. The "member" is passed via an optional argument. I use it to define template environments and to have different strings for different languages, which need to be present in the document simultaneously.



Here's what I had and works.



newcommandMakeLocaleVar[1]{%
global@namedef{#1:en}{{scriptsize (Use {tttextbackslash #1[en]} to replace this text.)}}%
global@namedef{#1:fi}{{scriptsize (Use {tttextbackslash #1[fi]} to replace this text.)}}%
expandafternewcommandcsname #1endcsname[2][]{%
ifthenelse{equal{##1}{}}{%
global@namedef{#1:en}{##2}%
global@namedef{#1:fi}{##2}%
}{%
global@namedef{#1:##1}{##2}%
}%
}%
expandafternewcommandcsname Emit#1endcsname[1][en]{@nameuse{#1:##1}}%
}


First the default values are set. Then a command is created that sets the values according to the optional argument. If none, set value for all locales.



% In cls: define command
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]{Faculty}


I tried to modify the command to accept arbitrary locales, but something's not working.



newcommandMakeLocaleVar[2][en,fi]{%
foreach n in {#1}{%
global@namedef{#2:n}{%
{scriptsize (Use {tttextbackslash #2[n]} to replace this text.)}%
}%
}%
expandafternewcommandcsname #2endcsname[2][]{%
ifthenelse{equal{##1}{}}{%
foreach n in {#1}{%
global@namedef{#2:n}{##2}%
}%
}{%
global@namedef{#2:##1}{##2}%
}%
}%
expandafternewcommandcsname Emit#2endcsname[1][en]{@nameuse{#2:##1}}%
}


If I set values that are used, everything is fine and dandy. It is when values are not set that my custom environments break and the default message shown is just Use Cmd[] to..., so without the locale names.



Any idea as to what is going on?










share|improve this question









New contributor




Felix is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





















  • Looks like an expansion issue. The way @namedef{#2:n} is defined, its replacement text contains n. So when you call Faculty:fi lets, say it gets replaced by ... Use {tttextbackslash #2[n]} ... with n and not with the value of n at the time Faculty:fi was 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
    14 hours ago











  • @moewe I don't quite get it. Why wouldn't the n be 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
    14 hours ago











  • The @namedef carries out a full expansion of its (first) argument, but the replacement text (second argument of @namedef) is not expanded. I'm working an alternative with etoolbox right now, if you are interested.

    – moewe
    14 hours ago











  • @moewe That could be interesting. But can the effect be achieved using expandafter?

    – Felix
    14 hours ago











  • Yes and quite straightforward with a helper function. If you want to avoid the helper its more tricky, but probably doable (and I wouldn't be the one to ask).

    – moewe
    13 hours ago
















1















I'd like to construct a macro that can be used to define commands. Those commands behave like variables or structs of variables, such that they can contain multiple values. The "member" is passed via an optional argument. I use it to define template environments and to have different strings for different languages, which need to be present in the document simultaneously.



Here's what I had and works.



newcommandMakeLocaleVar[1]{%
global@namedef{#1:en}{{scriptsize (Use {tttextbackslash #1[en]} to replace this text.)}}%
global@namedef{#1:fi}{{scriptsize (Use {tttextbackslash #1[fi]} to replace this text.)}}%
expandafternewcommandcsname #1endcsname[2][]{%
ifthenelse{equal{##1}{}}{%
global@namedef{#1:en}{##2}%
global@namedef{#1:fi}{##2}%
}{%
global@namedef{#1:##1}{##2}%
}%
}%
expandafternewcommandcsname Emit#1endcsname[1][en]{@nameuse{#1:##1}}%
}


First the default values are set. Then a command is created that sets the values according to the optional argument. If none, set value for all locales.



% In cls: define command
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]{Faculty}


I tried to modify the command to accept arbitrary locales, but something's not working.



newcommandMakeLocaleVar[2][en,fi]{%
foreach n in {#1}{%
global@namedef{#2:n}{%
{scriptsize (Use {tttextbackslash #2[n]} to replace this text.)}%
}%
}%
expandafternewcommandcsname #2endcsname[2][]{%
ifthenelse{equal{##1}{}}{%
foreach n in {#1}{%
global@namedef{#2:n}{##2}%
}%
}{%
global@namedef{#2:##1}{##2}%
}%
}%
expandafternewcommandcsname Emit#2endcsname[1][en]{@nameuse{#2:##1}}%
}


If I set values that are used, everything is fine and dandy. It is when values are not set that my custom environments break and the default message shown is just Use Cmd[] to..., so without the locale names.



Any idea as to what is going on?










share|improve this question









New contributor




Felix is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





















  • Looks like an expansion issue. The way @namedef{#2:n} is defined, its replacement text contains n. So when you call Faculty:fi lets, say it gets replaced by ... Use {tttextbackslash #2[n]} ... with n and not with the value of n at the time Faculty:fi was 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
    14 hours ago











  • @moewe I don't quite get it. Why wouldn't the n be 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
    14 hours ago











  • The @namedef carries out a full expansion of its (first) argument, but the replacement text (second argument of @namedef) is not expanded. I'm working an alternative with etoolbox right now, if you are interested.

    – moewe
    14 hours ago











  • @moewe That could be interesting. But can the effect be achieved using expandafter?

    – Felix
    14 hours ago











  • Yes and quite straightforward with a helper function. If you want to avoid the helper its more tricky, but probably doable (and I wouldn't be the one to ask).

    – moewe
    13 hours ago














1












1








1








I'd like to construct a macro that can be used to define commands. Those commands behave like variables or structs of variables, such that they can contain multiple values. The "member" is passed via an optional argument. I use it to define template environments and to have different strings for different languages, which need to be present in the document simultaneously.



Here's what I had and works.



newcommandMakeLocaleVar[1]{%
global@namedef{#1:en}{{scriptsize (Use {tttextbackslash #1[en]} to replace this text.)}}%
global@namedef{#1:fi}{{scriptsize (Use {tttextbackslash #1[fi]} to replace this text.)}}%
expandafternewcommandcsname #1endcsname[2][]{%
ifthenelse{equal{##1}{}}{%
global@namedef{#1:en}{##2}%
global@namedef{#1:fi}{##2}%
}{%
global@namedef{#1:##1}{##2}%
}%
}%
expandafternewcommandcsname Emit#1endcsname[1][en]{@nameuse{#1:##1}}%
}


First the default values are set. Then a command is created that sets the values according to the optional argument. If none, set value for all locales.



% In cls: define command
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]{Faculty}


I tried to modify the command to accept arbitrary locales, but something's not working.



newcommandMakeLocaleVar[2][en,fi]{%
foreach n in {#1}{%
global@namedef{#2:n}{%
{scriptsize (Use {tttextbackslash #2[n]} to replace this text.)}%
}%
}%
expandafternewcommandcsname #2endcsname[2][]{%
ifthenelse{equal{##1}{}}{%
foreach n in {#1}{%
global@namedef{#2:n}{##2}%
}%
}{%
global@namedef{#2:##1}{##2}%
}%
}%
expandafternewcommandcsname Emit#2endcsname[1][en]{@nameuse{#2:##1}}%
}


If I set values that are used, everything is fine and dandy. It is when values are not set that my custom environments break and the default message shown is just Use Cmd[] to..., so without the locale names.



Any idea as to what is going on?










share|improve this question









New contributor




Felix is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.












I'd like to construct a macro that can be used to define commands. Those commands behave like variables or structs of variables, such that they can contain multiple values. The "member" is passed via an optional argument. I use it to define template environments and to have different strings for different languages, which need to be present in the document simultaneously.



Here's what I had and works.



newcommandMakeLocaleVar[1]{%
global@namedef{#1:en}{{scriptsize (Use {tttextbackslash #1[en]} to replace this text.)}}%
global@namedef{#1:fi}{{scriptsize (Use {tttextbackslash #1[fi]} to replace this text.)}}%
expandafternewcommandcsname #1endcsname[2][]{%
ifthenelse{equal{##1}{}}{%
global@namedef{#1:en}{##2}%
global@namedef{#1:fi}{##2}%
}{%
global@namedef{#1:##1}{##2}%
}%
}%
expandafternewcommandcsname Emit#1endcsname[1][en]{@nameuse{#1:##1}}%
}


First the default values are set. Then a command is created that sets the values according to the optional argument. If none, set value for all locales.



% In cls: define command
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]{Faculty}


I tried to modify the command to accept arbitrary locales, but something's not working.



newcommandMakeLocaleVar[2][en,fi]{%
foreach n in {#1}{%
global@namedef{#2:n}{%
{scriptsize (Use {tttextbackslash #2[n]} to replace this text.)}%
}%
}%
expandafternewcommandcsname #2endcsname[2][]{%
ifthenelse{equal{##1}{}}{%
foreach n in {#1}{%
global@namedef{#2:n}{##2}%
}%
}{%
global@namedef{#2:##1}{##2}%
}%
}%
expandafternewcommandcsname Emit#2endcsname[1][en]{@nameuse{#2:##1}}%
}


If I set values that are used, everything is fine and dandy. It is when values are not set that my custom environments break and the default message shown is just Use Cmd[] to..., so without the locale names.



Any idea as to what is going on?







macros loops pgffor variable






share|improve this question









New contributor




Felix is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question









New contributor




Felix is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question








edited 14 hours ago







Felix













New contributor




Felix is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked 14 hours ago









FelixFelix

1265




1265




New contributor




Felix is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





Felix is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






Felix is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.













  • Looks like an expansion issue. The way @namedef{#2:n} is defined, its replacement text contains n. So when you call Faculty:fi lets, say it gets replaced by ... Use {tttextbackslash #2[n]} ... with n and not with the value of n at the time Faculty:fi was 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
    14 hours ago











  • @moewe I don't quite get it. Why wouldn't the n be 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
    14 hours ago











  • The @namedef carries out a full expansion of its (first) argument, but the replacement text (second argument of @namedef) is not expanded. I'm working an alternative with etoolbox right now, if you are interested.

    – moewe
    14 hours ago











  • @moewe That could be interesting. But can the effect be achieved using expandafter?

    – Felix
    14 hours ago











  • Yes and quite straightforward with a helper function. If you want to avoid the helper its more tricky, but probably doable (and I wouldn't be the one to ask).

    – moewe
    13 hours ago



















  • Looks like an expansion issue. The way @namedef{#2:n} is defined, its replacement text contains n. So when you call Faculty:fi lets, say it gets replaced by ... Use {tttextbackslash #2[n]} ... with n and not with the value of n at the time Faculty:fi was 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
    14 hours ago











  • @moewe I don't quite get it. Why wouldn't the n be 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
    14 hours ago











  • The @namedef carries out a full expansion of its (first) argument, but the replacement text (second argument of @namedef) is not expanded. I'm working an alternative with etoolbox right now, if you are interested.

    – moewe
    14 hours ago











  • @moewe That could be interesting. But can the effect be achieved using expandafter?

    – Felix
    14 hours ago











  • Yes and quite straightforward with a helper function. If you want to avoid the helper its more tricky, but probably doable (and I wouldn't be the one to ask).

    – moewe
    13 hours ago

















Looks like an expansion issue. The way @namedef{#2:n} is defined, its replacement text contains n. So when you call Faculty:fi lets, say it gets replaced by ... Use {tttextbackslash #2[n]} ... with n and not with the value of n at the time Faculty:fi was 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
14 hours ago





Looks like an expansion issue. The way @namedef{#2:n} is defined, its replacement text contains n. So when you call Faculty:fi lets, say it gets replaced by ... Use {tttextbackslash #2[n]} ... with n and not with the value of n at the time Faculty:fi was 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
14 hours ago













@moewe I don't quite get it. Why wouldn't the n be 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
14 hours ago





@moewe I don't quite get it. Why wouldn't the n be 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
14 hours ago













The @namedef carries out a full expansion of its (first) argument, but the replacement text (second argument of @namedef) is not expanded. I'm working an alternative with etoolbox right now, if you are interested.

– moewe
14 hours ago





The @namedef carries out a full expansion of its (first) argument, but the replacement text (second argument of @namedef) is not expanded. I'm working an alternative with etoolbox right now, if you are interested.

– moewe
14 hours ago













@moewe That could be interesting. But can the effect be achieved using expandafter?

– Felix
14 hours ago





@moewe That could be interesting. But can the effect be achieved using expandafter?

– Felix
14 hours ago













Yes and quite straightforward with a helper function. If you want to avoid the helper its more tricky, but probably doable (and I wouldn't be the one to ask).

– moewe
13 hours ago





Yes and quite straightforward with a helper function. If you want to avoid the helper its more tricky, but probably doable (and I wouldn't be the one to ask).

– moewe
13 hours ago










1 Answer
1






active

oldest

votes


















0














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 {tttextbackslash #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. 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 loop 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.



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 {ttfamilytextbackslash #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 argument
% <macro>{<item_1>}, <macro>{<item_2>}
% since the items are the <lang> argument it must be the last
% argument to mlv@defaultvalue and mlv@realvalue
newcommandMakeLocaleVar[2][en,fi]{%
forcsvlist{mlv@defaultvalue{#2}}{#1}%
expandafternewcommandcsname #2endcsname[2][]{%
ifblank{##1}
{forcsvlist{mlv@realvalue{##2}{#2}}{#1}}%
{mlv@realvalue{##2}{#2}{##1}}}%
expandafternewcommandcsname Emit#2endcsname[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





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.



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 {ttfamilytextbackslash #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.
newcommandMakeLocaleVar[2][en,fi]{%
foreach n in {#1}{%
expandaftermlv@helperexpandafter{n}{#2}%
}%
expandafternewcommandcsname #2endcsname[2][]{%
ifthenelse{equal{##1}{}}{%
foreach n in {#1}{%
global@namedef{#2:n}{##2}%
}%
}{%
global@namedef{#2:##1}{##2}%
}%
}%
expandafternewcommandcsname Emit#2endcsname[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}





share|improve this answer

























    Your Answer








    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "85"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });






    Felix is a new contributor. Be nice, and check out our Code of Conduct.










    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f477656%2fgeneralising-a-second-order-macro-with-loops%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    0














    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 {tttextbackslash #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. 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 loop 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.



    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 {ttfamilytextbackslash #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 argument
    % <macro>{<item_1>}, <macro>{<item_2>}
    % since the items are the <lang> argument it must be the last
    % argument to mlv@defaultvalue and mlv@realvalue
    newcommandMakeLocaleVar[2][en,fi]{%
    forcsvlist{mlv@defaultvalue{#2}}{#1}%
    expandafternewcommandcsname #2endcsname[2][]{%
    ifblank{##1}
    {forcsvlist{mlv@realvalue{##2}{#2}}{#1}}%
    {mlv@realvalue{##2}{#2}{##1}}}%
    expandafternewcommandcsname Emit#2endcsname[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





    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.



    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 {ttfamilytextbackslash #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.
    newcommandMakeLocaleVar[2][en,fi]{%
    foreach n in {#1}{%
    expandaftermlv@helperexpandafter{n}{#2}%
    }%
    expandafternewcommandcsname #2endcsname[2][]{%
    ifthenelse{equal{##1}{}}{%
    foreach n in {#1}{%
    global@namedef{#2:n}{##2}%
    }%
    }{%
    global@namedef{#2:##1}{##2}%
    }%
    }%
    expandafternewcommandcsname Emit#2endcsname[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}





    share|improve this answer






























      0














      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 {tttextbackslash #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. 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 loop 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.



      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 {ttfamilytextbackslash #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 argument
      % <macro>{<item_1>}, <macro>{<item_2>}
      % since the items are the <lang> argument it must be the last
      % argument to mlv@defaultvalue and mlv@realvalue
      newcommandMakeLocaleVar[2][en,fi]{%
      forcsvlist{mlv@defaultvalue{#2}}{#1}%
      expandafternewcommandcsname #2endcsname[2][]{%
      ifblank{##1}
      {forcsvlist{mlv@realvalue{##2}{#2}}{#1}}%
      {mlv@realvalue{##2}{#2}{##1}}}%
      expandafternewcommandcsname Emit#2endcsname[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





      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.



      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 {ttfamilytextbackslash #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.
      newcommandMakeLocaleVar[2][en,fi]{%
      foreach n in {#1}{%
      expandaftermlv@helperexpandafter{n}{#2}%
      }%
      expandafternewcommandcsname #2endcsname[2][]{%
      ifthenelse{equal{##1}{}}{%
      foreach n in {#1}{%
      global@namedef{#2:n}{##2}%
      }%
      }{%
      global@namedef{#2:##1}{##2}%
      }%
      }%
      expandafternewcommandcsname Emit#2endcsname[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}





      share|improve this answer




























        0












        0








        0







        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 {tttextbackslash #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. 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 loop 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.



        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 {ttfamilytextbackslash #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 argument
        % <macro>{<item_1>}, <macro>{<item_2>}
        % since the items are the <lang> argument it must be the last
        % argument to mlv@defaultvalue and mlv@realvalue
        newcommandMakeLocaleVar[2][en,fi]{%
        forcsvlist{mlv@defaultvalue{#2}}{#1}%
        expandafternewcommandcsname #2endcsname[2][]{%
        ifblank{##1}
        {forcsvlist{mlv@realvalue{##2}{#2}}{#1}}%
        {mlv@realvalue{##2}{#2}{##1}}}%
        expandafternewcommandcsname Emit#2endcsname[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





        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.



        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 {ttfamilytextbackslash #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.
        newcommandMakeLocaleVar[2][en,fi]{%
        foreach n in {#1}{%
        expandaftermlv@helperexpandafter{n}{#2}%
        }%
        expandafternewcommandcsname #2endcsname[2][]{%
        ifthenelse{equal{##1}{}}{%
        foreach n in {#1}{%
        global@namedef{#2:n}{##2}%
        }%
        }{%
        global@namedef{#2:##1}{##2}%
        }%
        }%
        expandafternewcommandcsname Emit#2endcsname[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}





        share|improve this answer















        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 {tttextbackslash #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. 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 loop 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.



        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 {ttfamilytextbackslash #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 argument
        % <macro>{<item_1>}, <macro>{<item_2>}
        % since the items are the <lang> argument it must be the last
        % argument to mlv@defaultvalue and mlv@realvalue
        newcommandMakeLocaleVar[2][en,fi]{%
        forcsvlist{mlv@defaultvalue{#2}}{#1}%
        expandafternewcommandcsname #2endcsname[2][]{%
        ifblank{##1}
        {forcsvlist{mlv@realvalue{##2}{#2}}{#1}}%
        {mlv@realvalue{##2}{#2}{##1}}}%
        expandafternewcommandcsname Emit#2endcsname[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





        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.



        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 {ttfamilytextbackslash #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.
        newcommandMakeLocaleVar[2][en,fi]{%
        foreach n in {#1}{%
        expandaftermlv@helperexpandafter{n}{#2}%
        }%
        expandafternewcommandcsname #2endcsname[2][]{%
        ifthenelse{equal{##1}{}}{%
        foreach n in {#1}{%
        global@namedef{#2:n}{##2}%
        }%
        }{%
        global@namedef{#2:##1}{##2}%
        }%
        }%
        expandafternewcommandcsname Emit#2endcsname[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}






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited 13 hours ago

























        answered 13 hours ago









        moewemoewe

        92.6k10115351




        92.6k10115351






















            Felix is a new contributor. Be nice, and check out our Code of Conduct.










            draft saved

            draft discarded


















            Felix is a new contributor. Be nice, and check out our Code of Conduct.













            Felix is a new contributor. Be nice, and check out our Code of Conduct.












            Felix is a new contributor. Be nice, and check out our Code of Conduct.
















            Thanks for contributing an answer to TeX - LaTeX Stack Exchange!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f477656%2fgeneralising-a-second-order-macro-with-loops%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            El tren de la libertad Índice Antecedentes "Porque yo decido" Desarrollo de la...

            Castillo d'Acher Características Menú de navegación

            Connecting two nodes from the same mother node horizontallyTikZ: What EXACTLY does the the |- notation for...