strapi / strapi

🚀 Strapi is the leading open-source headless CMS. It’s 100% JavaScript/TypeScript, fully customizable, and developer-first.
https://strapi.io
Other
63.19k stars 7.97k forks source link

Translating content literals for admin does not work as before #20461

Open SalahAdDin opened 4 months ago

SalahAdDin commented 4 months ago

Bug report

Required System information

Launched In: 382 ms
Environment: development
OS: linux-x64
Strapi Version: 5.0.0-beta.5
Node/Yarn Version: yarn/4.1.1 npm/? node/v20.12.2 linux x64
Edition: Community
Database: SQLite
[2024-06-07 00:15:05.490] info: Shutting down Strapi
[2024-06-07 00:15:05.491] info: Strapi has been shut down

Describe the bug

We are working on localizing all the content types we create for our project, we followed the existing documentation, but it seems to be deprecated.

When checking the literals on the admin panel, only the content type names are translated, but the rest of the literals are in English yet.

This does not happen in Strapi 4.

Steps to reproduce the behavior

  1. Add the localization files to the folder.
  2. Write the function for loading the translation files on the admin.
  3. Load the translations on the admin app file.
  4. Run the server.
  5. Go to the admin panel.
  6. Check the content manager.
  7. Click on a content type.
  8. Check the missing translations.

Expected behavior

All the translation literals should be shown properly as it happens in Strapi 4.

Screenshots

Strapi 5: image

image

Strapi 4: image

image

Code snippets

You can check the files in this commit.

Additional context

It also has a lot of reports of missing translation literals:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://analytics.strapi.io/api/v2/track. (Reason: CORS request did not succeed). Status code: (null).

Error: [@formatjs/intl Error MISSING_TRANSLATION] Missing message: "cloud.plugin.name" for locale "es", using default message (Deploy) as fallback.
    IntlError2 error.js:19
    MissingTranslationError2 error.js:79
    formatMessage message.js:65
    formatMessage2 createIntl.js:29
    children LeftMenu.tsx:175
    LeftMenu LeftMenu.tsx:155
    React 16
[utils.js:20:16](http://localhost:1337/node_modules/@formatjs/intl/lib/src/utils.js)
Error: [@formatjs/intl Error MISSING_TRANSLATION] Missing message: "cloud.plugin.name" for locale "es", using default message (Deploy) as fallback.
    IntlError2 error.js:19
    MissingTranslationError2 error.js:79
    formatMessage message.js:65
    formatMessage2 createIntl.js:29
    children LeftMenu.tsx:175
    LeftMenu LeftMenu.tsx:155
    React 10
[utils.js:20:16](http://localhost:1337/node_modules/@formatjs/intl/lib/src/utils.js)
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://analytics.strapi.io/api/v2/track. (Reason: CORS request did not succeed). Status code: (null).

Uncaught (in promise) TypeError: NetworkError when attempting to fetch resource.
Error: [@formatjs/intl Error MISSING_TRANSLATION] Missing message: "content-manager.plugin.name" for locale "es", using default message (Content Manager) as fallback.
    IntlError2 error.js:19
    MissingTranslationError2 error.js:79
    formatMessage message.js:65
    formatMessage2 createIntl.js:29
    Layout2 layout.tsx:46
    React 12
[utils.js:20:16](http://localhost:1337/node_modules/@formatjs/intl/lib/src/utils.js)
Error: [@formatjs/intl Error MISSING_TRANSLATION] Missing message: "content-manager.plugin.name" for locale "es", using default message (Content Manager) as fallback.
    IntlError2 error.js:19
    MissingTranslationError2 error.js:79
    formatMessage message.js:65
    formatMessage2 createIntl.js:29
    Layout2 layout.tsx:46
    React 15
[utils.js:20:16](http://localhost:1337/node_modules/@formatjs/intl/lib/src/utils.js)
Error: [@formatjs/intl Error MISSING_TRANSLATION] Missing message: "content-manager.plugin.name" for locale "es", using default message (Content Manager) as fallback.
    IntlError2 error.js:19
    MissingTranslationError2 error.js:79
    formatMessage message.js:65
    formatMessage2 createIntl.js:29
    Layout2 layout.tsx:46
    React 10
[utils.js:20:16](http://localhost:1337/node_modules/@formatjs/intl/lib/src/utils.js)
Error: [@formatjs/intl Error MISSING_TRANSLATION] Missing message: "content-manager.plugin.name" for locale "es", using default message (Content Manager) as fallback.
    IntlError2 error.js:19
    MissingTranslationError2 error.js:79
    formatMessage message.js:65
    formatMessage2 createIntl.js:29
    Layout2 layout.tsx:46
    React 10
[utils.js:20:16](http://localhost:1337/node_modules/@formatjs/intl/lib/src/utils.js)
Error: [@formatjs/intl Error MISSING_TRANSLATION] Missing message: "content-manager.plugin.name" for locale "es", using default message (Content Manager) as fallback.
    IntlError2 error.js:19
    MissingTranslationError2 error.js:79
    formatMessage message.js:65
    formatMessage2 createIntl.js:29
    Layout2 layout.tsx:87
SalahAdDin commented 2 weeks ago

For the List View Page title, we have that the title is not translatable, while in Strapi 4, it is translatable.

SalahAdDin commented 2 weeks ago

Yeah, while in the Strapi 4 the labels are translatable, in Strapi 5 they are not: Strapi 4:

 const tableHeaders: TableHeader[] = React.useMemo(() => {
    const headers = runHookWaterfall(INJECT_COLUMN_IN_TABLE, {
      displayedHeaders,
      layout,
    });

    const formattedHeaders = headers.displayedHeaders.map((header) => {
      if (header.fieldSchema.type === 'relation') {
        return {
          ...header,
          metadatas: {
            ...header.metadatas,
            label: formatMessage({
              id: getTranslation(`containers.ListPage.table-headers.${header.name}`),
              defaultMessage: header.metadatas.label,
            }),
          },
          name: `${header.name}.${header.metadatas.mainField?.name ?? ''}`,
        } satisfies TableHeader;
      }

      return {
        ...header,
        metadatas: {
          ...header.metadatas,
          label: formatMessage({
            id: getTranslation(`containers.ListPage.table-headers.${header.name}`),
            defaultMessage: header.metadatas.label,
          }),
        },
      } satisfies TableHeader;
    });

    if (hasDraftAndPublish) {
      formattedHeaders.push({
        key: '__published_at_temp_key__',
        name: 'publishedAt',
        fieldSchema: {
          type: 'custom',
        },
        metadatas: {
          label: formatMessage({
            id: getTranslation(`containers.ListPage.table-headers.publishedAt`),
            defaultMessage: 'publishedAt',
          }),
          searchable: false,
          sortable: true,
        },
      } satisfies TableHeader);
    }

    if (reviewWorkflowColumns) {
      formattedHeaders.push(
        ...reviewWorkflowColumns.map((column) => {
          return {
            ...column,
            metadatas: {
              ...column.metadatas,
              label: formatMessage(column.metadatas.label),
            },
          } satisfies TableHeader;
        })
      );
    }

    return formattedHeaders;
  }, [
    runHookWaterfall,
    displayedHeaders,
    layout,
    reviewWorkflowColumns,
    hasDraftAndPublish,
    formatMessage,
  ]);

Strapi 5:

/**
   * Run the waterfall and then inject our additional table headers.
   */
  const tableHeaders = React.useMemo(() => {
    const headers = runHookWaterfall(INJECT_COLUMN_IN_TABLE, {
      displayedHeaders,
      layout: list,
    });

    const formattedHeaders = headers.displayedHeaders.map<ListFieldLayout>((header) => {
      return {
        ...header,
        label: typeof header.label === 'string' ? header.label : formatMessage(header.label),
        name: `${header.name}${header.mainField?.name ? `.${header.mainField.name}` : ''}`,
      };
    });

    if (schema?.options?.draftAndPublish) {
      formattedHeaders.push({
        attribute: {
          type: 'custom',
        },
        name: 'status',
        label: formatMessage({
          id: getTranslation(`containers.list.table-headers.status`),
          defaultMessage: 'status',
        }),
        searchable: false,
        sortable: false,
      } satisfies ListFieldLayout);
    }

    return formattedHeaders;
  }, [displayedHeaders, formatMessage, list, runHookWaterfall, schema?.options?.draftAndPublish]);

Why did you guys forget it?

A solution could be:

  const formattedHeaders = headers.displayedHeaders.map<ListFieldLayout>((header) => {
      return {
        ...header,
        label: formatMessage({
              id: getTranslation(`containers.list.table-headers.${header.name}`),
              defaultMessage: header.label,
            }),,
        name: `${header.name}${header.mainField?.name ? `.${header.mainField.name}` : ''}`,
      };
    });

Following the format defined by containers.list.table-headers.status.

What do you think?