Skip to content
Matheus edited this page May 13, 2026 · 1 revision

Welcome to the i18n-hunt wiki!

Supported Patterns

This page documents the source-code and locale-file patterns currently supported by i18n-hunt.

i18n-hunt prefers conservative detection over aggressive guessing. The goal is to minimize false positives while still surfacing genuinely unused translation keys.

Scope

  • Source files: .ts, .tsx, .js, .jsx
  • Locale files: .json
  • Locale keys are flattened from nested JSON objects

Example:

{
  "form": {
    "email": "Email"
  }
}

Flattened key:

form.email

Translation APIs

useTranslation(...) + t(...)

const { t } = useTranslation("Auth/Login");

t("title");

Namespace arrays are also supported:

const { t } = useTranslation(["Auth/Login", "Common"]);

t("sharedFallback");

i18next.t(...)

i18next.t("Auth/Login:i18nextOnly");

Namespace embedded in key

t("Auth/Login:colonUsed");

Namespace override via options object

t("optionNsOnly", { ns: "Auth/Login" });

<Trans />

<Trans i18nKey="transOnly" ns="Common" />

Resolution Types

i18n-hunt classifies key usage into three resolution types.

Static

A key that can be resolved exactly.

t("form.email");

Prefix

A partially dynamic key with a stable leading prefix.

t(`form.${field}`);

Detected prefix:

form.

Any locale key that starts with the detected prefix is protected from being reported as unused.

Dynamic

A key expression that cannot be resolved safely.

t(`${section}.title`);

Dynamic usages are tracked, but they are not used aggressively to mark keys as used.


Template Literals

Template literals with no expressions are treated as static keys:

t(`title`);

Template literals with a stable leading string are treated as prefixes:

t(`form.${field}`);

Template literals without a stable leading prefix are treated as dynamic:

t(`${section}.title`);

Local Constant Resolution

Local const string values can be resolved:

const key = "title";

t(key);

Conditional and Fallback Expressions

Conditional expressions are analyzed when both branches can be resolved:

t(cond ? "a" : "b");

Logical fallbacks are supported:

t(getMaybeKey() || "title");
t(getMaybeNullKey() ?? "form.submit");

Some logical expressions are treated conservatively:

t(flag && "title");

In this case, the usage is treated as dynamic overall.


Collections and Iteration

Arrays

t(["title", "description"]);

Array elements are analyzed individually.

Iterators

When a callback parameter is used as the translation key, supported iterator patterns can be resolved:

["form.email", "form.password"].map((key) => t(key));
OPTIONS.forEach((key) => t(key));

Object maps

const object maps are supported for static and computed member access:

const keyByState = {
  created: "mapCreated",
  deleted: "mapDeleted",
};

t(keyByState[state]);
t(keyByState.deleted);

Function Return Inference

Top-level function declarations returning translation keys can be inferred:

function getErrorKey() {
  return Math.random() > 0.5
    ? "errors.network"
    : "errors.invalidCredentials";
}

t(getErrorKey());

Server Translator Pattern

Server-side translator helpers are supported when the namespace can be resolved:

const t = await getServerTranslate("Accounting");

t("serverOnly");

Exclude Patterns

Source and locale scanning support glob excludes through config.

src_exclude = ["**/*.test.ts", "legacy/**"]
locales_exclude = ["Legacy/**"]

Available exclude options:

  • src_exclude
  • locales_exclude

Current Limitations

  • Dynamic namespaces in useTranslation(...) are ignored.
  • Imported cross-file constants and functions are not fully resolved.
  • Analysis is primarily intra-file.
  • Only .json locale files are supported.
  • Only JavaScript and TypeScript source files are supported.