Localize Axios HTTP Client Error Responses

Handling API errors gracefully requires translating raw HTTP status codes or backend error codes into user-friendly, localized messages based on the user's active language. This article demonstrates how to implement a centralized error-mapping architecture using Axios interceptors, translation dictionaries, and fallback strategies to display localized error messages throughout your application.

The Localization Workflow

A robust error-localization workflow consists of three primary components:

  1. Standardized Error Identification: Relying on HTTP status codes (e.g., 404, 500) or specific backend application error codes (e.g., AUTH_INVALID_CREDENTIALS).
  2. Translation Dictionaries: Key-value pairs matching error codes to localized strings.
  3. Axios Response Interceptors: Middleware that intercepts failed requests, maps the error to the correct translated string, and normalizes the error object before it reaches UI components.

Step 1: Define Localization Dictionaries

Create translation files containing keys for common HTTP status codes and specific application error codes.

locales/en.json

{
  "errors": {
    "network_error": "Network unavailable. Please check your internet connection.",
    "timeout": "The server took too long to respond. Please try again.",
    "unknown": "An unexpected error occurred. Please try again later.",
    "http": {
      "400": "Invalid request. Please check your input.",
      "401": "Your session has expired. Please log in again.",
      "403": "You do not have permission to perform this action.",
      "404": "The requested resource could not be found.",
      "422": "Validation failed.",
      "500": "Internal server error. Please try again later."
    },
    "app": {
      "USER_NOT_FOUND": "No account found with the provided email address.",
      "INSUFFICIENT_FUNDS": "Your account balance is insufficient for this transaction."
    }
  }
}

locales/es.json

{
  "errors": {
    "network_error": "Red no disponible. Por favor, compruebe su conexión a internet.",
    "timeout": "El servidor tardó demasiado en responder. Inténtelo de nuevo.",
    "unknown": "Ocurrió un error inesperado. Por favor, inténtelo más tarde.",
    "http": {
      "400": "Solicitud no válida. Por favor, compruebe sus datos.",
      "401": "Su sesión ha caducado. Por favor, inicie sesión nuevamente.",
      "403": "No tiene permiso para realizar esta acción.",
      "404": "No se pudo encontrar el recurso solicitado.",
      "422": "Error de validación.",
      "500": "Error interno del servidor. Por favor, inténtelo más tarde."
    },
    "app": {
      "USER_NOT_FOUND": "No se encontró ninguna cuenta con el correo proporcionado.",
      "INSUFFICIENT_FUNDS": "El saldo de su cuenta es insuficiente para esta transacción."
    }
  }
}

Step 2: Implement the Error Mapping Helper

Create a helper function that resolves an Axios error to a localized message key. This example uses a generic translation function t(key) compatible with libraries like i18next.

// errorMapper.js
import i18n from './i18n'; // Your i18n instance

export function getLocalizedErrorMessage(error) {
  // 1. Handle network and timeout errors
  if (error.code === 'ECONNABORTED') {
    return i18n.t('errors.timeout');
  }
  if (!error.response) {
    return i18n.t('errors.network_error');
  }

  const { status, data } = error.response;

  // 2. Check for application-specific error code from backend
  if (data && data.errorCode) {
    const appKey = `errors.app.${data.errorCode}`;
    if (i18n.exists(appKey)) {
      return i18n.t(appKey, data.params || {});
    }
  }

  // 3. Check for standard HTTP status code
  const httpKey = `errors.http.${status}`;
  if (i18n.exists(httpKey)) {
    return i18n.t(httpKey);
  }

  // 4. Default fallback
  return i18n.t('errors.unknown');
}

Step 3: Attach the Axios Response Interceptor

Configure an Axios instance with a response interceptor to automatically attach the localized message to rejected promises.

// apiClient.js
import axios from 'axios';
import { getLocalizedErrorMessage } from './errorMapper';

const apiClient = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 10000,
});

apiClient.interceptors.response.use(
  (response) => response,
  (error) => {
    // Map the error to a localized user-facing message
    const localizedMessage = getLocalizedErrorMessage(error);

    // Attach the message directly to the error object
    error.localizedMessage = localizedMessage;

    return Promise.reject(error);
  }
);

export default apiClient;

Step 4: Consume Localized Errors in the UI

UI components can now read the error.localizedMessage property directly without needing to duplicate error-parsing logic.

import apiClient from './apiClient';

async function handleFormSubmit(payload) {
  try {
    const response = await apiClient.post('/users/update', payload);
    return response.data;
  } catch (error) {
    // Display the localized string to the user
    showToastNotification(error.localizedMessage);
  }
}

Best Practices