---
id: template-sets
title: 'Template Sets and Override Order'
description: 'How Base, edition and Company template sets stack, how later sets override earlier ones, how an empty value deletes an entry, and what Agility actually loads.'
sidebar_label: 'Template Sets and Overrides'
sidebar_position: 6
tags: ['api', 'localization', 'terminology', 'configuration']
---

## The Stacking Rule

A culture's template map is built by applying several **template sets** in
order. Each set is a flat list of `tag=value` templates. A tag defined in a
later set replaces the same tag from an earlier one; tags a set does not mention
are left alone.

```text
Base        ──┐
edition set   ├─ applied in order, later wins
Company     ──┘
```

Using the library's fixtures, where `Base` defines `Story` implicitly (no
template, so it resolves to itself), `Scrum` renames it and `Custom` renames it
again:

| Sets loaded              | `Story`        | `Plural'Story`  | `Plural'Project` | `Fixed`        |
| ------------------------ | -------------- | --------------- | ---------------- | -------------- |
| `Base`                   | `Story`        | `Stories`       | `Projects`       | `Never Change` |
| `Base`, `Scrum`          | `Backlog Item` | `Backlog Items` | `Projects`       | `Never Change` |
| `Base`, `Scrum`, `Custom`| `PBI`          | `PBIs`          | `Projects`       | `Never Change` |


Note the third column: overriding `Story` did not touch `Project`. And the
fourth: a set that never mentions `Fixed` leaves it exactly as it was, however
many sets are stacked on top.

## An Empty Value Deletes, It Does Not Blank

This is the sharpest edge in the whole system.

```text title="en.Scrum.txt"
Story=Backlog Item

; Deliberately remove the definition of Plural'Story
Plural'Story=
```

A template whose value is empty **removes** that tag from the map. It does not
set it to the empty string. Removal re-exposes whatever the term-stack walk
would have found next.

So after that set is applied, `Plural'Story` no longer has a specific entry, the
walk falls back to the generic `Plural={1}s`, term 1 is `Story`, which now
resolves to `Backlog Item` — and the answer is `Backlog Items`, not `Stories`
and not an empty string.


An empty translation is not stored as an empty string — it **removes** the
tag from the culture's map.

:::warning You cannot set a tag to the empty string this way
If you genuinely want a label to render as nothing, you cannot express it in a
template file — an empty value is a deletion. Removal only affects the culture
whose map is being built, so a deleted tag can still be answered by the fallback
culture.
:::

## Sets Are per-Culture

Sets are loaded per culture, so `fr-FR` is assembled from `fr-FR`'s sets only,
then chained to `fr`, then to `en`. A `Company` override written for `en` does
not apply to `fr-FR` unless `fr-FR` has nothing of its own to say about that tag
— which is just the ordinary [culture fallback](/apis/loc-2-v1/culture-fallback)
rule.

If no set at all exists for a culture, the whole culture defers to its fallback.

## Instance-Wide Overrides Sit on Top of Everything

`LocalizationManager` also accepts a dictionary of overrides that is applied to
the **default culture's** localizer after every set has been loaded. Those
override the final result of the whole set stack:

| Tag            | Sets say       | Override says | Result   |
| -------------- | -------------- | ------------- | -------- |
| `Story`        | `PBI`          | `Bone`        | `Bone`   |
| `Plural'Story` | derived        | —             | `Bones`  |
| `Plural'Project` | `Projects`   | —             | `Projects` |

Because they land on the default localizer, overrides are inherited by every
culture that does not override the tag itself.


## The Set Stack Agility Actually Uses

The `Base` / `Custom` pair described in the localization engine's own
documentation is the **sample application's** configuration, not Agility's. Agility builds a
longer stack, in this order:

| # | Provider | Source | Present when |
| - | -------- | ------ | ------------ |
| 1 | `Base` | `Strings/<culture>.Base.txt` | Always |
| 2 | `ExtendedTypesOff` | `Strings/<culture>.ExtendedTypesOff.txt` | The `Defect` asset type is not visible |
| 3 | Edition set | `Strings/<culture>.<Edition>.txt` | The instance is licensed for localization; the set is named for the configured meta edition |
| 4 | `Company` | `Strings/<culture>.Company.txt` | Always attempted; the file is customer-supplied |
| 5 | `RemoveNonEnglishTemplateProvider("AttributeDefinition")` | code | Every culture except `en` |
| 6 | Terminology overrides | the `Localization` database table, filtered to the culture — written by the [terminology override API](/apis/loc-2-v1/overrides) and by the Terminology admin screen | Always |
| 7 | System values | code — `Runtime'MaxUploadMb` | Always |

Editions shipped in the box include `Scrum`, `XP`, `AgileUP`, `Catalyst`,
`DSDM` and `Team`; the English files ship as `Strings/en.*.txt` in the web
application.

Two consequences worth internalising:

- **`Company` is the in-field override file**, the equivalent of the README's
  `Custom`. It ships empty (in fact, absent) and is the supported place to
  rename terminology on disk.
- **The Terminology admin screen wins over every file.** It is applied at
  position 6, after all four file sets. If a rename appears to ignore your
  `Company` file, check the `Localization` table.
- **That table is reachable from the API.** The Terminology screen and
  [`api/localization` / `api/localizations`](/apis/loc-2-v1/overrides) write the same
  store, so a terminology change made through the API takes precedence over every
  file for exactly the same reason. This is the supported way to re-term Agility
  programmatically.


## Debugging an Override That Is Not Taking Effect

Work down this list; it is ordered by how often each cause is the real one.

1. **Something later in the stack overrides it.** Check the Terminology admin
   screen before anything else.
2. **A more specific tag is winning.** You overrode `Plural`, but `Plural'Story`
   is still defined. See [Tag resolution](/apis/loc-2-v1/tag-resolution).
3. **You edited the wrong culture's file.** `en.Company.txt` does not affect
   `fr-FR` if `fr-FR` defines the tag.
4. **The file did not parse.** Wrong encoding, or a missing `=`. See
   [Template file format](/apis/loc-2-v1/template-file-format).
5. **A stale cache.** See [Hot reload and caching](/apis/loc-2-v1/hot-reload).

## Related Topics

- [Template file format](/apis/loc-2-v1/template-file-format) — the syntax of a set file
- [Hot reload and caching](/apis/loc-2-v1/hot-reload) — making an edit take effect
- [Tag resolution](/apis/loc-2-v1/tag-resolution)
- [Culture fallback](/apis/loc-2-v1/culture-fallback)
