kontent-ai / gatsby-packages

Monorepo with Gatsby Kontent packages.
https://www.gatsbyjs.org/plugins/?=%40kentico%2Fkontent-
MIT License
33 stars 24 forks source link

Queries don’t fallback to default language #66

Closed bwlng closed 5 years ago

bwlng commented 5 years ago

Brief bug description

It doesn’t appear that the source plugin includes fallback content as described in the Localization in Kentico Cloud portions of the docs.

Repro steps

When querying data, include otherLanguages in the query and include codenames for additional languages in the deliveryClientConfig object in gatsby-config.js. Content Items won't be included if a translation hasn't been created.

{
  allKenticoCloudItemPage {
    edges {
      node {
        id
        system {
          type
          language
        }
        elements {
          url_slug {
            value
          }
        }
        otherLanguages {
          system {
            language
          }
        }
      }
    }
  }
}

Expected behavior

If the language is configured to use a default fallback, content should be included from the default language.

Is there a workaround or config I'm missing?

Simply007 commented 5 years ago

Hello @bwlng,

could you specify the source plugin version you are using and provide the configuration in gatsby-config.js for the source plugin?

Just FYI: there are the places, where the data are loaded:

Would you mind to describe your scenario, so that I have the context for the advice?

bwlng commented 5 years ago

Hi @Simply007 — I'm using version 3.1.0 of the source plugin. This is what my config looks like:

    {
      resolve: `gatsby-source-kentico-cloud`,
        options: {
          deliveryClientConfig: {
            previewApiKey: process.env.KENTICO_CLOUD_PREVIEW_API_KEY,
            projectId: process.env.KENTICO_CLOUD_PROJECT_ID, // Fill in your Project ID
            enablePreviewMode: Boolean(process.env.KENTICO_CLOUD_ENABLE_PREVIEW_MODE || false),
            typeResolvers: [],
          },
          languageCodenames: [
            `en-US`, // Languages in Kentico (Project settings -> Localization)
            `de`,
            `es`,
            `fr`,
            `it`,
            `pl`,
            `pt-BR`,
            `jp`,
            `zh-CN`

          ]
      }
    },

It does look like the nodes are created — they are logged to the console when I run gatsby develop, but I can't seem to retrieve them from a GraphQL query.

I'm hoping that a query like the one below would display all Page content items with fallback content from the default language specified in Kentico Cloud. Similarly, for queries were a content item as been explicitly translated, but a linked item hasn't, I'd like the "not translated" linked item to be included with the default language variant.

{
  allKenticoCloudItemPage {
    edges {
      node {
        system {
          type
          language
        }
        elements {
          title {
            value
          }
        }
        otherLanguages {
           elements {
            title {
              value
            }
          }
        }
      }
    }
  }
}

The above only shows nodes that have been explicitly translated.

What would a query look like that will include the data that is loaded?

Simply007 commented 5 years ago

Hello @bwlng,

I have briefly gone through the code and the otherLanguages property. It is being filled only by the items that are in the response with a specific language set. When you load the data and the language fallback is used content item has system.language set to the original language (not de but en-US) in your case.

There is a possibility to transform the node in the onCreateNode lifecycle method prepare the structure you need in your code.

You could try to tune up the source plugin locally, but if there is any chance of changing this behavior via source code modification, it would lead to the breaking change.

I am currently leaving for vacation until 18th of September so that I not able to try or investigate the recommendation right now, but I think the onCreateNode lifecycle node could help you out!

yuriys-kentico commented 5 years ago

A workaround is more-or-less putting all translated variants into otherLanguages until @Simply007 can work out proper support for fallback language variants:

exports.onCreateNode = ({ node }) => {
  if (node.system && node.system.language) {
    let otherLanguages = [node.id]

    if (
      node.otherLanguages___NODE &&
      node.otherLanguages___NODE[0] !== node.id
    ) {
      otherLanguages = [node.id, ...node.otherLanguages___NODE]
    }

    node.otherLanguages___NODE = otherLanguages
  }
}

This should make sure that otherLanguages contains at least the default language variant, even if there are no translated variants. Then, in your components or how you use your queries, always check system.language to make sure the variant is the language that you want.

bwlng commented 5 years ago

@yuriys-kentico Thanks for the suggestion. I tried the workaround and it does make sure that otherLanguages always has a value, but in my testing it seems to either duplicate the current content item into otherLanguages (in instances where I'm querying the default language) or include the default language in instances where it already when have been included (when I'm querying de for example).

I think the simplest example of what I'm trying to accomplish is a query like this from the standard API: https://preview-deliver.kenticocloud.com/PROJ_ID/items/homepage?language=es

Where the homepage item hasn't been translated to es yet. With the standard API it will return the content item from the default language (en-US in my case). Is there a similar workaround where a query in a Gatsby project would return the fallback language?

bwlng commented 5 years ago

@yuriys-kentico @Simply007 Is it possible to determine the language a node was created from by the time the node is available in onCreateNode? It seems that the node is created with the an ID created from the system language and and the item’s codename, so if fallback content is used, the node will overwrite previous nodes with the same values for contentItem.system.codename and contentItem.system.language.

Would there be any negative side effects if each node included the preferred language (i.e. the language parameter used during the client request)?

These modifications to itemNodes.js seem to accomplish what I'm aiming for.

Default language nodes:

const getFromDefaultLanguage = async (client, defaultLanguageCodename, contentTypeNodes, createNodeId) => {
  …
  const contentItemNodes = itemsFlatted.map(contentItem => {
    try {
      // Add the language parameter used on the client request to a system field
      contentItem.system.preferred_language = defaultLanguageCodename
      return createContentItemNode(createNodeId, contentItem, contentTypeNodes);
    } catch (error) {
      console.error(error);
    }
  });
  …
};

Non-default language nodes

const getFromNonDefaultLanguage = async (client, nonDefaultLanguageCodenames, contentTypeNodes, createNodeId) => {
  …

  for (const languageCodename of nonDefaultLanguageCodenames) {
    const languageResponse = await client.items().languageParameter(languageCodename).getPromise();

    …
    const contentItemsNodes = languageItemsFlatted.map(languageItem => {
      // Add the language parameter used on the client request to a system field
      languageItem.system.preferred_language = languageCodename
      return createContentItemNode(createNodeId, languageItem, contentTypeNodes)
    });
    nonDefaultLanguageItemNodes.set(languageCodename, contentItemsNodes);
  }

  ;
  return nonDefaultLanguageItemNodes;
};

Use the new preferred_language param for the nodeId:

const createContentItemNode = (createNodeId, contentItem, contentTypeNodes) => {
  …
  const codenameParamCase = changeCase.paramCase(contentItem.system.codename);
  // Use system.preferred_language instead of system.language 
  const languageParamCase = changeCase.paramCase(contentItem.system.preferred_language);
  const nodeId = createNodeId(`kentico-cloud-item-${codenameParamCase}-${languageParamCase}`);
  …
};

Any other way to achieve a similar solution w/o modifying the source plugin?

Simply007 commented 5 years ago

The issue is currently being solved in cooperation with @bwlng.

There is also a rebranding from Kentico Cloud to Kontent in progress.

There is already a beta with the fix:

Once the new version is released with the readme + upgrade guide I am about to close this issue.