Repository: strapi/strapi
File: packages/plugins/i18n/server/src/services/ai-localizations.ts (TypeScript)

Code before:
            });

            // Merge AI content and media fields, works only on first level media fields (root level)
            const mergedData = structuredClone(content);
            for (const field of mediaFields) {
              // Only copy media if not already set in derived locale
              if (!derivedDoc || !derivedDoc[field]) {
                mergedData[field] = document[field];
              } else {
                mergedData[field] = derivedDoc[field];
              }
            }

            if (!derivedDoc) {
              const i18nService = strapi.plugin('i18n')?.service('content-types');
              // Mutates mergedData directly to copy the non-localized fields from the source document
              i18nService.fillNonLocalizedAttributes(mergedData, document, {
                model,
              });
              await strapi.documents(model).create({
                data: { documentId, ...mergedData },
                locale,
              });
            } else {
              /**
               * TODO:
               * We should use the document service here, however currently,
               * updating a localized entry marks the source document as modified.
               * @see https://github.com/strapi/strapi/issues/22924
               *
               * Falling back to the query engine to preserve the correct status
               */
              await strapi.db.query(model).update({
                where: { documentId, locale },
                data: mergedData,
              });
            }

            await aiLocalizationJobsService.upsertJobForDocument({
              documentId,
              contentType: model,
              sourceLocale: document.locale,
              targetLocales,
              status: 'completed',
            });
          })
        );
      } catch (error) {
        await aiLocalizationJobsService.upsertJobForDocument({
          documentId,
          contentType: model,

Review comment (bug):
I think this fixes the issues for our use case. We should definitely prioritize fixing this though after the release. I think the fix might actually be in the doc service. Don't have the time to debug that and a bit nervous to make any changes at that level just before a release.

Code after:
            });

            // Merge AI content and media fields, works only on first level media fields (root level)
            const mergedData = structuredClone(content);
            for (const field of mediaFields) {
              // Only copy media if not already set in derived locale
              if (!derivedDoc || !derivedDoc[field]) {
                mergedData[field] = document[field];
              } else {
                mergedData[field] = derivedDoc[field];
              }
            }

            await strapi.documents(model).update({
              documentId,
              locale,
              fields: [],
              data: mergedData,
            });

            await aiLocalizationJobsService.upsertJobForDocument({
              documentId,
              contentType: model,
              sourceLocale: document.locale,
              targetLocales,
              status: 'completed',
            });
          })
        );
      } catch (error) {
        await aiLocalizationJobsService.upsertJobForDocument({
          documentId,
          contentType: model,
          sourceLocale: document.locale,
          targetLocales,
          status: 'failed',
        });
        strapi.log.error('AI Localizations generation failed', error);
      }
    },
    setupMiddleware() {
      strapi.documents.use(async (context, next) => {
        const result = await next();

        const metricsService = strapi.plugin('i18n').service('metrics');
        if (context.action === 'publish') {
          metricsService.sendWithAIEventProperty('didPublishEntry');
          return result;
        }

        if (context.action === 'update' || context.action === 'create') {