A critical vulnerability was discovered in React Server Components (Next.js). Our systems remain protected but we advise to update packages to newest version. Learn More

How fallback language logic works with Opti GraphQL

Vote:
 

Hi eveyone,

I am using Cms 12.33.1 and Optimizely.ContentGraph.Cms 3.19.2. I am using Opti GraphQL for my headless site and want to have multilinguale function.
But I am wondering if I can get content from fallback language if there is no existed content in target language via Opti GraphQL by default or not?
How can I do setup to make fallback logic work with Opti GraphQL?

Please let me know if you know it or have experienced with it.

Thanks a lot

#340094
Aug 25, 2025 12:18
Vote:
 

Try this to see if it works. If your query has locale parameter (something as below example), the graph client should have all the locales

query ArticlesContentAreaQuery(
    $locale: Locales = ALL
    ) {
  ArticlePage(
    locale: [$locale]){
    items {
      Name
      MainContentArea {
        ContentLink {
          Expanded {
            ... on ContactPage {
              Name
              Email
            }
          }
        }
      }
    }
  }
}

in the query, you can have        

var queryResponse = await _contentGraphClient.ArticlesContentAreaQuery.ExecuteAsync(currentPage.GetLocales()); 

Add an extension method GetLocales()

public static Locales GetLocales(this object content)
{
    if(content is ILocalizable localizableContent)
   {
      return localizableContent.Language.Name;
   }
 
  else
  {
     return Locales.All
  }
}
 

 

 
#340101
Aug 25, 2025 18:12
Vote:
 

Thanks for your help. I did something like that with adding more custom properties in Graph as well

#340128
Aug 29, 2025 2:55
Vote:
 

Actually I am using https://github.com/remkoj/optimizely-dxp-clients for my front-end site.

Here is my customization for fallback:

  • GraphQL query in FE
query getContentByPathWithinFallback($path: [String!]!, $locale: String, $siteId: String) {
  content: Content(
    where: {
      SiteId: { eq: $siteId }
      _or: [
        { _and: [{ FallbackLanguageContents: { LanguageName: { eq: $locale, boost: 1 } } }, { FallbackLanguageContents: { RelativePath: { in: $path } } }] }
        { _and: [{ Language: { Name: { eq: $locale, boost: 2 } } }, { RelativePath: { in: $path } }] }
      ]
    }
    locale: ALL
  ) {
    items: item {
      ...IContentData
      ...PageData
    }
  }
}
fragment PageData on IContent {
  ...IContentData
}
fragment IContentData on IContent {
  contentType: ContentType
  _metadata: ContentLink {
    id: Id
    version: WorkId
    key: GuidValue
  }
  locale: Language {
    name: Name
  }
  path: RelativePath
  _type: __typename
}

I use boost value here to make sure that always get content with matched language completely first then get fallback content.

  • Custom graph api model in BE
public class FallbackLanguageContentsApiModelProperty(
    ILanguageBranchRepository languageBranchRepository,
    IUrlResolver urlResolver,
    ICustomUrlService customUrlService,
    IContentLanguageSettingsHandler contentLanguageSettingsHandler) : IContentApiModelProperty
{
    public object GetValue(ContentApiModel contentApiModel)
    {
        if (contentApiModel.ContentLink != null)
        {
            var enabledLanguages = languageBranchRepository.ListEnabled();
            var pagesThatFallBackContentToCurrentPage = new List<FallbackLanguageContent>();
            var cRef = contentApiModel.ContentLink.ToContentReference();

            foreach (var enabledLanguage in enabledLanguages)
            {
                var fallbackLanguages = contentLanguageSettingsHandler.GetFallbackLanguages(cRef, enabledLanguage.Culture.Name);
                if (fallbackLanguages.Any() && fallbackLanguages.Contains(contentApiModel.Language.Name))
                {
                    var lang = enabledLanguage.Culture.Name;
                    var url = urlResolver.GetUrl(cRef, lang);

                    pagesThatFallBackContentToCurrentPage.Add(new FallbackLanguageContent()
                    {
                        LanguageName = lang,
                        RelativePath = url != null ? customUrlService.GetRelativeUrl(url) : string.Empty
                    });
                }
            }

            return pagesThatFallBackContentToCurrentPage;
        }
        else
        {
            return new List<FallbackLanguageContent>();
        }
    }

    public string Name => "FallbackLanguageContents";
}

internal class FallbackLanguageContent
{
    public required string LanguageName { get; set; }
    public required string RelativePath { get; set; }
}
#340132
Aug 29, 2025 3:48
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.