Skip to content

feat: add missing translations for hardcoded API error messages#16722

Draft
joan-teriihoania wants to merge 2 commits into
payloadcms:mainfrom
joan-teriihoania:feature/missing-translations
Draft

feat: add missing translations for hardcoded API error messages#16722
joan-teriihoania wants to merge 2 commits into
payloadcms:mainfrom
joan-teriihoania:feature/missing-translations

Conversation

@joan-teriihoania
Copy link
Copy Markdown

@joan-teriihoania joan-teriihoania commented May 22, 2026

Note

I am unsure why there were missing translation keys, or why some keys were created but not used. I assumed it was missed or forgotten during implementation. I've created this PR in case it was. Do let me know! This is my first contribution!

This implementation addresses some of the lack in internationalised error messages in Payload CMS by replacing hardcoded English error strings with translation keys. Previously, API errors were thrown with static English text, even if some had translations available. This made the CMS less accessible to non-English speaking users and limited its global reach. After a quick look at the codebase, I found 73 occurrences of hardcoded error messages across various packages, including core Payload, plugins, storage adapters, and database adapters. While I only needed a subset translated, I elected to translate all of them for consistency and to future-proof the codebase.

All API errors now follow this pattern:

// Before
throw new APIError('Missing required data.', httpStatus.BAD_REQUEST)

// After
throw new APIError(
  req.t ? req.t('error:missingRequiredData') : 'Missing required data.',
  httpStatus.BAD_REQUEST,
)

This uses the payload request's t function for translation when available, and falls back to the original English string if not to ensure that all errors are translatable without breaking existing functionality. And to make use of the existing language detection and infrastructure.

I targeted only hardcoded error strings thrown in an APIError since those are, conceptually, designed to be displayed to the users, and thus should be localised when possible.

Changes

In total, there were 73 occurences of hardcoded error message strings throwing an APIError. Out of those, some strings already had a key in the translation files, but simply didn't use it. And others didn't have translation keys and thus needed to be added. Of the latter, there were 46 new translation keys added to packages/translations/src/languages/*.ts in the error namespace:

  • actionWillLockoutPreset - "This action will lock you out of this preset."
  • authenticationRequiredForExport - "User authentication is required to create exports."
  • userRequiredForImport - "User is required for import operations"
  • unauthorized - "Unauthorized" (already existed, now used in more places)
  • attachmentContentInvalid - "Attachment content must be a string or a buffer"
  • attachmentMissingContentAndPath - "Attachment is missing both content and path"
  • attachmentMissingFilename - "Attachment is missing filename"
  • expectedResponseFromUploadHandler - "Expected response from the upload handler."
  • failedToFetchFromURL - "Failed to fetch the file from the provided URL."
  • filenameRequired - "A file name is required."
  • invalidFilename - "Invalid filename"
  • invalidFileURL - "Invalid file url"
  • noFileDataForImport - "No file data provided for import"
  • pastingFromURLNotEnabled - "Pasting from URL is not enabled for this collection."
  • redirectTargetNotAllowed - "Redirect target is not allowed."
  • requestNotEligibleForFileUpload - "Request is not eligible for file upload"
  • tooManyRedirects - "Too many redirects."
  • uploadConfigHandlersMissing - "uploadConfig.handlers is not present for {{collection}}"
  • uploadthingUnknownError - "Error uploading file with uploadthing: Unknown error"
  • urlNotAllowed - "The provided URL is not allowed."
  • validURLRequired - "A valid URL string is required."
  • busboyMultipartError - "Busboy error parsing multipart request"
  • collectionSlugRequired - "Collection slug is required"
  • contentTypeExpectedJSON - "Content-Type expected to be application/json"
  • eitherCollectionOrGlobalRequired - "Either collection or globalSlug must be passed."
  • fieldMustBeSpecified - "field must be specified"
  • fieldsNotInitialized - "Fields are not initialized."
  • invalidJSON - "Invalid JSON"
  • invalidJSONFormat - "Invalid JSON format"
  • joinFieldsNotAllowedInArraysBlocksOrGlobals - "Join fields cannot be added to arrays, blocks or globals."
  • missingIDOfVersionToRestore - "Missing ID of version to restore."
  • noConnectionToDatabase - "beginTransaction called while no connection to the database exists"
  • noPayloadProvided - "No payload was provided"
  • notSupported - "Not supported"
  • orderableJoinsMustTargetSingleCollection - "Orderable joins must target a single collection"
  • relationshipFieldNotFound - "Relationship field was not found"
  • relationshipNotFound - "Relationship was not found"
  • requestDataRequired - "Request data is required."
  • requestURLMissing - "Request URL is missing."
  • somethingWentWrong - "Something went wrong."
  • unexpectedArrayCollectionSlug - "Unexpected array of collectionSlug, parent must be provided"
  • unreachable - "Unreachable"
  • vercelBlobUploadError - "storage-vercel-blob client upload route error"

Files intentionally not updated

The following files were found during the search but were not updated because they lack access to the PayloadRequest object or are test/example files. By their very nature as tests, examples, or utilities, they are not always able to access the request object and its t function, and thus would require a more complex refactor to support internationalization.

Test Files

  • test/plugin-sentry/config.ts
  • test/uploads/collections/BulkUploadsHookError/index.ts
  • test/versions/collections/DraftsWithChangeHook.ts
  • test/versions/collections/ErrorOnUnpublish.ts
  • test/collections-rest/config.ts
  • packages/payload/src/utilities/formatErrors.spec.ts

Codemod/Example Files

  • packages/codemod/src/transforms/migrate-aliased-exports/merge.input.ts
  • packages/codemod/src/transforms/migrate-aliased-exports/merge.output.ts
  • examples/multi-tenant/src/collections/Users/endpoints/externalUsersLogin.ts

Low-Level utilities

  • packages/payload/src/utilities/sanitizeFilename.ts
  • packages/payload/src/fields/config/sanitizeJoinField.ts
  • packages/ui/src/utilities/buildTableState.ts
  • packages/db-mongodb/src/transactions/beginTransaction.ts
  • packages/db-mongodb/src/queries/getBuildQueryPlugin.ts
  • packages/db-mongodb/src/queries/buildSearchParams.ts
  • packages/drizzle/src/queries/getTableColumnFromPath.ts
  • packages/email-resend/src/index.ts
  • packages/payload/src/uploads/fetchAPI-multipart/index.ts
  • packages/payload/src/uploads/fetchAPI-multipart/processMultipart.ts
  • packages/payload/src/database/getLocalizedPaths.ts
  • packages/storage-uploadthing/src/uploadFile.ts

Stats for the nerds

  • Total API errors found: 73 occurrences
  • Production files updated: 31 files
  • Translation keys added: 46 keys
  • Packages affected: 10 packages
    • payload (core)
    • plugin-import-export
    • plugin-multi-tenant
    • storage-s3
    • storage-vercel-blob
    • storage-uploadthing
    • storage-gcs
    • storage-azure
    • drizzle
    • db-mongodb

Next steps

There are two steps that could follow this implementation, or that other people could add on it: 1) generating translations for the new keys in other languages, and 2) refactoring the files that were intentionally not updated to also support i18n.

I know that we have the option to use pnpm run translateNewKeys with an OPENAI_KEY but I do not have that available, and even then I would be severely limited. If someone is able to generate translations for the new keys, that would be a great help. I've added the keys using Sonnet 4.5, but I can't be sure of the quality.

As for the remaining files, I'm not sure what to do with them. I've elected to not update them, but do let me know what you think.

Aditionnally, I am planning on making the same PR on the 3.x branch. Since I don't believe there were any huge changes made between v3 and v4 on internationalisation, I assume that "porting" these diffs to v3 would be easy enough and would require minimal effort.

@joan-teriihoania joan-teriihoania changed the title feat: Add missing translations for hardcoded API error messages feat: add missing translations for hardcoded API error messages May 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant