Skip to main content
Severity: Warning — does not affect exit code or fail CI builds.
Glot detects translation keys that cannot be statically analyzed because they use variables, template literals with expressions, or other dynamic patterns.

Detection Rule

A translation key is flagged as unresolved if it:
  1. Uses a variable as the key argument: t(variableName)
  2. Uses a template literal with expressions: t(`prefix.${dynamic}`)
  3. Uses computed properties: t(keys[index])
  4. Uses function calls: t(getKey())
  5. Is not covered by a glot-message-keys annotation
Unresolved keys can’t be checked for existence, potentially causing missing-key errors at runtime.

What Gets Detected

Variable Keys

Keys stored in variables:
// Unresolved
const key = getUserPreference();
<button>{t(key)}</button>

// Also unresolved
const statusKey = `status.${code}`;
<span>{t(statusKey)}</span>

Template Literals

Template strings with dynamic expressions:
// Unresolved
<div>{t(`notifications.${type}`)}</div>

// Also unresolved
<span>{t(`errors.${errorCode}.message`)}</span>

Computed Properties

Array or object access:
// Unresolved
const keys = ['home', 'about', 'contact'];
<a>{t(keys[0])}</a>

// Also unresolved
const translations = { title: 'page.title' };
<h1>{t(translations.title)}</h1>

Function Calls

Keys returned from functions:
// Unresolved
function getGreeting(time) {
  return time < 12 ? 'greetings.morning' : 'greetings.evening';
}

<h1>{t(getGreeting(hour))}</h1>

Output Format

warning: (unresolved)  unresolved-key
  --> ./src/components/Status.tsx:12:15
  |
  | Cannot statically resolve translation key (uses variable or template literal)
  |
The warning indicates the location but can’t show the actual key since it’s computed at runtime.

Severity

Warning - Unresolved keys are informational because:
  • They might be valid at runtime
  • They could lead to missing-key errors
  • Glot cannot verify if the keys exist in locale files
  • They prevent other checks like unused and orphan from running safely

Why This Matters

Static Analysis Limitations

Glot can’t verify unresolved keys exist:
// Glot can verify this
<button>{t('common.submit')}</button>  // ✓ Checked

// Glot cannot verify this
<button>{t(`common.${action}`)}</button>  // ⚠ Unresolved
If action is “submit”, the key works. If it’s “submet” (typo), you get a runtime error that glot can’t detect.

Prevents Cleanup

Unresolved keys block glot clean:
$ npx glot clean
Error: Cannot safely clean unused keys because some keys are resolved dynamically.
Use glot-message-keys annotations to declare dynamic keys.
This is a safety feature to prevent accidentally deleting keys that are used dynamically.

Incomplete Coverage

Other checks can’t analyze unresolved keys:
// glot can't check if these keys exist
const status = getStatus();
<span>{t(`status.${status}`)}</span>
Missing keys only appear at runtime, not during static analysis.

How to Fix

Declare the possible keys explicitly:
// Before (unresolved)
<span>{t(`status.${code}`)}</span>

// After (resolved)
// glot-message-keys "status.active", "status.inactive", "status.pending"
<span>{t(`status.${code}`)}</span>
Now glot can verify these keys exist and track their usage.

Option 2: Use Glob Patterns

For many similar keys, use wildcards:
// Before (unresolved)
<div>{t(`errors.${errorCode}.message`)}</div>

// After (resolved with pattern)
// glot-message-keys "errors.*.message"
<div>{t(`errors.${errorCode}.message`)}</div>
Glot will match any key like errors.404.message, errors.500.message, etc.

Option 3: Use glot fix Command

Automatically insert annotations:
# Preview what will be added
npx glot fix

# Apply the fixes
npx glot fix --apply
The fix command analyzes your code and template patterns to suggest appropriate annotations.

Option 4: Refactor to Static Keys

When possible, use static keys with conditionals:
// Before (dynamic)
<span>{t(`status.${status}`)}</span>

// After (static)
<span>
  {status === 'active' && t('status.active')}
  {status === 'inactive' && t('status.inactive')}
  {status === 'pending' && t('status.pending')}
</span>

// Or with object lookup
const statusKeys = {
  active: 'status.active',
  inactive: 'status.inactive',
  pending: 'status.pending'
} as const;

<span>{t(statusKeys[status])}</span>
While the object lookup is still dynamic, you can annotate it once:
// glot-message-keys "status.active", "status.inactive", "status.pending"
const statusKeys = { /* ... */ };

glot-message-keys Annotation

Use glot-message-keys comments to declare which keys a dynamic expression uses:
// Exact keys
// glot-message-keys "auth.login", "auth.logout", "auth.register"
<button>{t(`auth.${action}`)}</button>

// Glob patterns
// glot-message-keys "errors.*.title", "errors.*.message"
<h2>{t(`errors.${code}.title`)}</h2>
Place annotations before the dynamic key usage. For full syntax reference (relative patterns, glob matching, JSX comments, multiple annotations), see Directives — Dynamic Key Declaration.

Common Patterns

Status Messages

// glot-message-keys "status.loading", "status.success", "status.error"
function StatusMessage({ status }: { status: 'loading' | 'success' | 'error' }) {
  return <div>{t(`status.${status}`)}</div>;
}

Error Messages

// glot-message-keys "errors.*.title", "errors.*.description"
function ErrorDisplay({ code }: { code: number }) {
  return (
    <div>
      <h1>{t(`errors.${code}.title`)}</h1>
      <p>{t(`errors.${code}.description`)}</p>
    </div>
  );
}

Dynamic Pages

// glot-message-keys "pages.home.title", "pages.about.title", "pages.contact.title"
function PageTitle({ page }: { page: string }) {
  return <title>{t(`pages.${page}.title`)}</title>;
}

Pluralization (Avoid Dynamic Keys)

Don’t use dynamic keys for plurals:
// ❌ Bad - uses dynamic keys
const key = count === 1 ? 'item' : 'items';
<span>{t(key)}</span>

// ✅ Good - use ICU message format
<span>{t('items', { count })}</span>

// In locale file:
// "items": "{count, plural, =1 {1 item} other {# items}}"

Examples

Before (unresolved):
function OrderStatus({ status }: { status: string }) {
  // ⚠ Unresolved - glot can't verify these keys exist
  return (
    <div className={`status-${status}`}>
      <span>{t(`order.status.${status}`)}</span>
      <p>{t(`order.description.${status}`)}</p>
    </div>
  );
}
After (resolved):
// glot-message-keys "order.status.*", "order.description.*"
function OrderStatus({ status }: { status: string }) {
  // ✓ Resolved - glot can verify keys match this pattern
  return (
    <div className={`status-${status}`}>
      <span>{t(`order.status.${status}`)}</span>
      <p>{t(`order.description.${status}`)}</p>
    </div>
  );
}
Now glot can:
  • Verify order.status.pending, order.status.shipped, etc. exist
  • Check that keys aren’t orphaned
  • Enable glot clean to safely remove unused keys
Before (unresolved):
function Notification({ type, priority }: Props) {
  // Multiple unresolved keys
  return (
    <div className={`notification-${priority}`}>
      <Icon>{t(`icons.${type}`)}</Icon>
      <h3>{t(`notifications.${type}.${priority}.title`)}</h3>
      <p>{t(`notifications.${type}.${priority}.message`)}</p>
      <button>{t(`actions.${type}.dismiss`)}</button>
    </div>
  );
}
After (resolved):
// glot-message-keys "icons.info", "icons.warning", "icons.error"
// glot-message-keys "notifications.*.*.title", "notifications.*.*.message"
// glot-message-keys "actions.*.dismiss"
function Notification({ type, priority }: Props) {
  return (
    <div className={`notification-${priority}`}>
      <Icon>{t(`icons.${type}`)}</Icon>
      <h3>{t(`notifications.${type}.${priority}.title`)}</h3>
      <p>{t(`notifications.${type}.${priority}.message`)}</p>
      <button>{t(`actions.${type}.dismiss`)}</button>
    </div>
  );
}

Best Practices

1. Minimize Dynamic Keys

Prefer static keys when possible:
// Instead of:
t(`color.${color}`)

// Consider:
const colorKeys = {
  red: 'color.red',
  blue: 'color.blue',
  green: 'color.green'
} as const;
t(colorKeys[color])

2. Document Patterns

Explain complex patterns in comments:
// Dynamic routing pattern for blog posts
// glot-message-keys "blog.*.title", "blog.*.excerpt", "blog.*.content"
function BlogPost({ slug }: { slug: string }) {
  return (
    <article>
      <h1>{t(`blog.${slug}.title`)}</h1>
      <p>{t(`blog.${slug}.excerpt`)}</p>
      <div>{t(`blog.${slug}.content`)}</div>
    </article>
  );
}

3. Keep Annotations Close

Place annotations near the code they describe:
// ✓ Good - annotation right before usage
function Component() {
  // glot-message-keys "prefix.*"
  return <div>{t(`prefix.${key}`)}</div>;
}

// ✗ Bad - annotation far from usage
// glot-message-keys "prefix.*"
function Component() {
  const [state, setState] = useState();
  useEffect(() => { /* ... */ });

  return <div>{t(`prefix.${key}`)}</div>;  // Easy to miss
}

4. Use TypeScript for Safety

Combine with TypeScript for type-safe keys:
type StatusKey = 'active' | 'inactive' | 'pending';

// glot-message-keys "status.active", "status.inactive", "status.pending"
function Status({ status }: { status: StatusKey }) {
  return <span>{t(`status.${status}`)}</span>;
}