From aee625bcfa5f361f973de0e57f2342712ccdc75c Mon Sep 17 00:00:00 2001 From: SylvainGibert Date: Mon, 15 Dec 2025 09:17:10 +0100 Subject: [PATCH 1/4] v0.0.0 --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index ef1887a9d..4a1c220f7 100644 --- a/package.json +++ b/package.json @@ -52,5 +52,6 @@ }, "run-if-changed": { "renovate.json5": "yarn renovate-config-validator" - } + }, + "version": "0.0.0" } From cac2f1f37dd2187d1334aea87c4448e0ea05a207 Mon Sep 17 00:00:00 2001 From: SylvainGibert Date: Mon, 15 Dec 2025 14:22:35 +0100 Subject: [PATCH 2/4] fix(gatsby-source-strapi): Add support for i18n locale=all and fix node ID conflicts for strapi v5 --- packages/gatsby-source-strapi/src/fetch.js | 152 ++++++++++++------ .../gatsby-source-strapi/src/normalize.js | 22 ++- 2 files changed, 117 insertions(+), 57 deletions(-) diff --git a/packages/gatsby-source-strapi/src/fetch.js b/packages/gatsby-source-strapi/src/fetch.js index 569769d12..75bde2f48 100644 --- a/packages/gatsby-source-strapi/src/fetch.js +++ b/packages/gatsby-source-strapi/src/fetch.js @@ -83,8 +83,8 @@ export const fetchEntity = async ( }, }, }); - for (const localization of response.data.attributes.localizations.data) { - otherLocales.push(localization.attributes.locale); + for (const localization of response.data.localizations) { + otherLocales.push(localization.locale); } } else { // Only one locale @@ -140,65 +140,119 @@ export const fetchEntities = async ( }, }; - // Use locale from pluginOptions if it's defined - if (pluginOptions?.i18n?.locale) { + // Handle internationalization + const locale = pluginOptions?.i18n?.locale; + const localesToFetch = []; + + if (locale) { delete queryParams.locale; - queryParams.locale = pluginOptions.i18n.locale; + + if (locale === "all") { + // Get all available locales from first entity + const { data: previewResponse } = await axiosInstance({ + ...options, + params: { + ...options.params, + pagination: { pageSize: 1 }, + populate: { + localizations: { + fields: ["locale"], + }, + }, + }, + }); + + // Add default locale from first entry + if (previewResponse.data?.[0]) { + const firstEntry = previewResponse.data[0]; + const localesSet = new Set(); + + // Add current entry's locale + if (firstEntry.locale) { + localesSet.add(firstEntry.locale); + } + + // Add other locales from localizations array or data property + const localizations = firstEntry.localizations?.data || firstEntry.localizations || []; + for (const localization of localizations) { + const localeValue = localization.locale || localization.attributes?.locale; + if (localeValue) { + localesSet.add(localeValue); + } + } + + localesToFetch.push(...localesSet); + } + } else { + // Only one locale + localesToFetch.push(locale); + } + } else { + // No locale specified, fetch default + localesToFetch.push(undefined); } try { - reporter.info( - `Starting to fetch data from Strapi - ${ - options.url - } with ${options.paramsSerializer.serialize(options.params)}`, - ); + // Fetch data for each locale + const allLocalesData = []; + + for (const currentLocale of localesToFetch) { + const localeOptions = { + ...options, + params: { + ...options.params, + ...(currentLocale && { locale: currentLocale }), + }, + }; - const { data: response } = await axiosInstance(options); + const { data: response } = await axiosInstance(localeOptions); - const data = response?.data || response; - const meta = response?.meta; + const data = response?.data || response; + const meta = response?.meta; - const page = Number.parseInt(meta?.pagination.page || 1, 10); - const pageCount = Number.parseInt(meta?.pagination.pageCount || 1, 10); + const page = Number.parseInt(meta?.pagination.page || 1, 10); + const pageCount = Number.parseInt(meta?.pagination.pageCount || 1, 10); - const pagesToGet = Array.from({ - length: pageCount - page, - }).map((_, index) => index + page + 1); + const pagesToGet = Array.from({ + length: pageCount - page, + }).map((_, index) => index + page + 1); - const fetchPagesPromises = pagesToGet.map((page) => { - return (async () => { - const fetchOptions = { - ...options, - params: { - ...options.params, - pagination: { - ...options.params.pagination, - page, + const fetchPagesPromises = pagesToGet.map((page) => { + return (async () => { + const fetchOptions = { + ...localeOptions, + params: { + ...localeOptions.params, + pagination: { + ...localeOptions.params.pagination, + page, + }, }, - }, - }; - - reporter.info( - `Starting to fetch page ${page} from Strapi - ${ - fetchOptions.url - } with ${options.paramsSerializer.serialize(fetchOptions.params)}`, - ); - - try { - const { - data: { data }, - } = await axiosInstance(fetchOptions); - - return data; - } catch (error) { - reporter.panic(`Failed to fetch data from Strapi ${fetchOptions.url}`, error); - } - })(); - }); + }; + + reporter.info( + `Starting to fetch page ${page} from Strapi - ${ + fetchOptions.url + } with ${options.paramsSerializer.serialize(fetchOptions.params)}`, + ); - const results = await Promise.all(fetchPagesPromises); + try { + const { + data: { data }, + } = await axiosInstance(fetchOptions); + + return data; + } catch (error) { + reporter.panic(`Failed to fetch data from Strapi ${fetchOptions.url}`, error); + } + })(); + }); + + const results = await Promise.all(fetchPagesPromises); + allLocalesData.push(...data, ...flattenDeep(results)); + } - const cleanedData = [...data, ...flattenDeep(results)].map((entry) => + const cleanedData = allLocalesData.map((entry) => cleanData(entry, { ...context, contentTypeUid: uid }, version), ); diff --git a/packages/gatsby-source-strapi/src/normalize.js b/packages/gatsby-source-strapi/src/normalize.js index b10c2ddae..8b3b7c511 100644 --- a/packages/gatsby-source-strapi/src/normalize.js +++ b/packages/gatsby-source-strapi/src/normalize.js @@ -12,9 +12,8 @@ import { getContentTypeSchema, makeParentNodeName } from "./helpers"; const prepareJSONNode = (json, context) => { const { createContentDigest, createNodeId, parentNode, attributeName } = context; - const jsonNodeId = createNodeId( - `${parentNode.strapi_document_id_or_regular_id}-${parentNode.internal.type}-${attributeName}-JSONNode`, - ); + // Use parentNode.id instead of strapi_document_id_or_regular_id to ensure uniqueness across locales + const jsonNodeId = createNodeId(`${parentNode.id}-${attributeName}-JSONNode`); const JSONNode = { ...(_.isPlainObject(json) ? { ...json } : { strapi_json_value: json }), @@ -48,7 +47,12 @@ const prepareRelationNode = (relation, context) => { const nodeType = makeParentNodeName(schemas, targetSchemaUid); const strapi_document_id_or_regular_id = relation.documentId || relation.id; // support both v5 and v4 - const relationNodeId = createNodeId(`${nodeType}-${strapi_document_id_or_regular_id}`); + + // Include locale in node ID to support multiple locales for the same entity + const localeIdentifier = relation.locale ? `-${relation.locale}` : ""; + const relationNodeId = createNodeId( + `${nodeType}-${strapi_document_id_or_regular_id}${localeIdentifier}`, + ); const node = { ...relation, @@ -76,9 +80,8 @@ const prepareRelationNode = (relation, context) => { */ const prepareTextNode = (text, context) => { const { createContentDigest, createNodeId, parentNode, attributeName } = context; - const textNodeId = createNodeId( - `${parentNode.strapi_document_id_or_regular_id}-${parentNode.internal.type}-${attributeName}-TextNode`, - ); + // Use parentNode.id instead of strapi_document_id_or_regular_id to ensure uniqueness across locales + const textNodeId = createNodeId(`${parentNode.id}-${attributeName}-TextNode`); const textNode = { id: textNodeId, @@ -143,8 +146,11 @@ export const createNodes = (entity, context, uid) => { // also, support both v5 documentId and v4 id const strapi_document_id_or_regular_id = entity.documentId || entity.id; + // Include locale in node ID to support multiple locales for the same entity + const localeIdentifier = entity.locale ? `-${entity.locale}` : ""; + let entryNode = { - id: createNodeId(`${nodeType}-${strapi_document_id_or_regular_id}`), + id: createNodeId(`${nodeType}-${strapi_document_id_or_regular_id}${localeIdentifier}`), documentId: entity.documentId, strapi_id: entity.id, strapi_document_id_or_regular_id, From 25f7ef7bdf7da23274cd9a3e1b82570b63d3a673 Mon Sep 17 00:00:00 2001 From: Sylvain Gibert Date: Mon, 15 Dec 2025 15:01:24 +0100 Subject: [PATCH 3/4] Create little-socks-approve.md --- .changeset/little-socks-approve.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/little-socks-approve.md diff --git a/.changeset/little-socks-approve.md b/.changeset/little-socks-approve.md new file mode 100644 index 000000000..93ed80eb9 --- /dev/null +++ b/.changeset/little-socks-approve.md @@ -0,0 +1,5 @@ +--- +"gatsby-source-strapi": patch +--- + +fix(gatsby-source-strapi): Strapi v5 Fix "locale=all" i18n feature for Single Types and Collection Types From a6fa131509d5fcd33b57c7102edc7b103402613f Mon Sep 17 00:00:00 2001 From: SylvainGibert Date: Tue, 16 Dec 2025 15:35:44 +0100 Subject: [PATCH 4/4] revert json change + revert omitted update package.json --- package.json | 3 +-- packages/gatsby-source-strapi/src/normalize.js | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 4a1c220f7..ef1887a9d 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,5 @@ }, "run-if-changed": { "renovate.json5": "yarn renovate-config-validator" - }, - "version": "0.0.0" + } } diff --git a/packages/gatsby-source-strapi/src/normalize.js b/packages/gatsby-source-strapi/src/normalize.js index 8b3b7c511..5c1fde7a4 100644 --- a/packages/gatsby-source-strapi/src/normalize.js +++ b/packages/gatsby-source-strapi/src/normalize.js @@ -12,8 +12,9 @@ import { getContentTypeSchema, makeParentNodeName } from "./helpers"; const prepareJSONNode = (json, context) => { const { createContentDigest, createNodeId, parentNode, attributeName } = context; - // Use parentNode.id instead of strapi_document_id_or_regular_id to ensure uniqueness across locales - const jsonNodeId = createNodeId(`${parentNode.id}-${attributeName}-JSONNode`); + const jsonNodeId = createNodeId( + `${parentNode.strapi_document_id_or_regular_id}-${parentNode.internal.type}-${attributeName}-JSONNode`, + ); const JSONNode = { ...(_.isPlainObject(json) ? { ...json } : { strapi_json_value: json }),