facebook/docusaurus · error · Error

Docusaurus translation declarations must have at least a tra

Error message

Docusaurus translation declarations must have at least a translation id or a default translation message

What it means

The imperative translate({message, id}) function calls getLocalizedMessage, which requires at least one of id or message. If both are undefined it throws 'Docusaurus translation declarations must have at least a translation id or a default translation message'. This catches incomplete translation calls early so untranslated content never silently ships.

Source

Thrown at packages/docusaurus/src/client/exports/Translate.tsx:22

 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import React, {type ReactNode} from 'react';
import {interpolate, type InterpolateValues} from '@docusaurus/Interpolate';
// Can't read it from context, due to exposing imperative API
import codeTranslations from '@generated/codeTranslations';
import type {TranslateParam, TranslateProps} from '@docusaurus/Translate';

function getLocalizedMessage({
  id,
  message,
}: {
  message?: string;
  id?: string;
}): string {
  if (typeof id === 'undefined' && typeof message === 'undefined') {
    throw new Error(
      'Docusaurus translation declarations must have at least a translation id or a default translation message',
    );
  }

  return codeTranslations[(id ?? message)!] ?? message ?? id!;
}

// Imperative translation API is useful for some edge-cases:
// - translating page titles (meta)
// - translating string props (input placeholders, image alt, aria labels...)
export function translate<Str extends string>(
  {message, id}: TranslateParam<Str>,
  values?: InterpolateValues<Str, string | number>,
): string {
  const localizedMessage = getLocalizedMessage({message, id});
  return interpolate(localizedMessage, values);
}

View on GitHub (pinned to 3f483e80e3)

Solutions

  1. Always pass a message (the default/fallback English string) and optionally an id.
  2. If params are dynamic, validate before calling: if (!id && !message) throw or supply a fallback.
  3. Use a wrapper that requires at least one of the two keys at the type level.

Example fix

// before
translate({});
// after
translate({message: 'Hello, {name}', id: 'greeting'}, {name});
Defensive patterns

Strategy: validation

Validate before calling

function safeTranslate(
  p: {id?: string; message?: string},
  values?: Record<string, string | number>,
) {
  if (p.id === undefined && p.message === undefined) {
    throw new Error('translate() needs at least id or message');
  }
  return translate(p as {message: string}, values);
}

Type guard

const hasIdOrMessage = (
  p: {id?: unknown; message?: unknown},
): p is {id?: string; message?: string} =>
  p.id !== undefined || p.message !== undefined;

Prevention

When it happens

Trigger: Call translate({}) or translate({id: undefined, message: undefined}); build the param object dynamically and both keys end up undefined; refactor that drops the message literal.

Common situations: Generating translate params from a map where a key is missing; conditional that omits both id and message; copy-paste leaving an empty object.

Related errors


AI-assisted analysis of facebook/docusaurus@3f483e80e3 (2026-08-12). Data as JSON: /api/errors/4f96e456ea8d4684. Report an issue: GitHub.