Nested Tag Substitution
Finding a template is only half of resolution. The template body is then
expanded: anything between { and } is itself a localization tag, resolved
against the same localizer, and its result is spliced in. This is what makes a
handful of templates cover thousands of tags.
Numeric Terms Are Indices into the Tag Being Resolved
Inside {}, a term that is a whole number is replaced by the term at that
zero-based index of the tag currently being resolved. Everything else is
taken literally.
Plural={1}s
Resolving Plural'Story:
- Terms are
["Plural", "Story"]. {1}→ term 1 →Story.- The nested tag
Storyis resolved. With no template forStory, it resolves to itself. - Result:
Storys.
{0} is the whole first term; an index past the end of the tag expands to the
empty string, which is the mechanism behind optional trailing terms described in
Tag resolution.
The Substituted Value Is Resolved, Not Re-Resolved
The value spliced in is the resolution of the nested tag. It is not fed back through the resolver a second time. This matters when a translated value happens to collide with another tag:
Add=Add {1}
Story=Backlog Item
Backlog Item=bogus
Add'Story yields Add Backlog Item, not Add bogus.
Literal Tags Nest Too
A nested tag does not have to reference an index. It can name another tag outright, and that tag's template is expanded in turn — recursively, until no braces remain.
Add'Story=Add {Story}
Story=Backlog Item
Add'Story → Add Backlog Item.
Indices and literals combine freely:
Plural={1}s
Report2=Report {Plural'1}/{Plural'2}
Plural'Story=Stories
Scope=Project
Report2'Scope'Story → Report Projects/Stories.
Braces Nest, Innermost First
Expansion runs repeatedly until no {…} pairs remain, and the innermost pair is
always matched first. That lets you build a tag out of another tag's result:
Plural={1}s
Plural'AssetType={Plural'{1'2}'x}
AssetType'Story=Backlog Item
Resolving Plural'AssetType'Story, whose terms are
["Plural", "AssetType", "Story"]:
- Innermost
{1'2}→AssetType'Story→ resolves toBacklog Item. - The body is now
{Plural'Backlog Item'x}. - That tag's stack walk finds
Plural, whose{1}picks term 1 ofPlural'Backlog Item'x—Backlog Item. - Result:
Backlog Items.
The literal 'x term is padding: it keeps Backlog Item at index 1 so the
generic Plural template still sees it in the right slot.
Literal Braces Are Doubled
To emit a { or } in the output, double it. Doubled braces are pulled out
before expansion starts and restored afterwards, so they never participate in
tag matching.
open=open {{ brace
close=close }} brace
match=open {{and}} close
nest=open {{{and}}} close
and=xyz
| Tag | Result | Why |
|---|---|---|
open | open { brace | {{ is a literal { |
close | close } brace | }} is a literal } |
match | open {and} close | Both braces are literal, so and is plain text |
nest | open {xyz} close | The outer braces are literal; the inner pair is a real tag |
{} — an empty pair — resolves the empty tag, which is pre-seeded to the empty
string, so it emits nothing.
Recursion Is Guarded, Not Forbidden
The localizer keeps a stack of the tags it is currently resolving. If expansion reaches a tag already on that stack, the tag is emitted literally instead of being resolved again. There is no exception and no stack overflow.
bogus=recursive {0} recursive
bogus → recursive bogus recursive. {0} is bogus, which is already being
resolved, so it is written out as text.
A cycle between two templates does not raise an error — it produces output
containing a raw tag. If a loc-2.v1 response contains an unexpected internal
name embedded mid-sentence, suspect a cycle in the templates rather than a
transport problem.
Reading a Real Response
Putting it together, this is why the following is a perfectly ordinary result on
an instance where the Story asset type has been renamed:
curl "https://www.example.com/VersionOne/loc-2.v1?[Plural'Story,Command'Delete'Story]"
{
"Plural'Story": "Backlog Items",
"Command'Delete'Story": "Remove Backlog Items"
}
Command'Delete is defined as Remove {Plural'2}. Term 2 of
Command'Delete'Story is Story, so the nested tag is Plural'Story, which
resolves through its own stack walk. One request, three template lookups, and
none of the strings involved is stored anywhere in that final form.
Related Topics
- Tag resolution — how the template is found in the first place
- Culture fallback — which localizer nested tags resolve against
- Template file format — writing templates that contain braces