How to fetch menus in different languages in a multi-language site? Problem with subdirectory

Hello Frontity Community,

I’m trying to create a multi-language site using the WP plugin WPML in German and English. We are also using Advanced Custom Fields to display our own modules in the pages. So I created 2 different sites in the same project, inside the frontity settings file. It looks like this:

const settings = [
  {
    name: "my-example-site",
    match: ["http?:\\/\\/[^/]+\\/de([^-\\w]|$)"],
    state: {
      frontity: {
        url: "http://localhost:3000/de",
        title: "My Example Site",
        description: "Some text here",
      }
    },
    packages: [
      {
        name: "my-theme",
      },
      {
        name: "@frontity/wp-source",
        state: {
          source: {
            url: "http://myexample.com/de",
            homepage: "/home",
            subdirectory: "de",
          },
          theme: {
            autoPrefetch: "hover",
          },
        }
      },
      "@frontity/tiny-router",
      "@frontity/html2react"
    ]
  },
  {
    name: "my-example-site-en",
    match: ["http?:\\/\\/[^/]+\\/en([^-\\w]|$)"],
    state: {
      frontity: {
        url: "http://localhost:3000/en",
        title: "My Example Site EN",
        description: "Some text here",
      }
    },
    packages: [
      {
        name: "my-theme",
      },
      {
        name: "@frontity/wp-source",
        state: {
          source: {
            url: "http://myexample.com/en",
            homepage: "/home",
            subdirectory: "en"
          },
          theme: {
            autoPrefetch: "hover",
          },
        }
      },
      "@frontity/tiny-router",
      "@frontity/html2react"
    ]
  }
]

In the Page component I get the current language (string) and the current available translations (array) for each page directly from the WP API and then I’m firing 2 actions to set them in the state. After that, the languageToggler component takes both (current language and current translations) and display them inside a select tag, so that I can change the language of each page. This will fire another action to change the state and force a redirection with window.location

Problems I’m getting:

To get the menus I followed this frontity-talk to fetch the menus from WP and it works. The problem comes from the ‘subdirectory’.

When I type subdirectory: "de" and subdirectory: "en" in the settings, the menu is not getting fetched - errorStatusText: "No handler has matched for the given link: "/menu/main-menu/""
Without it, the menu appears. But I need the subdirectories in order to display also the content of the pages and no only the menu.

Do you have maybe any idea how to solve this issue? Thank you very much in advance!

Hi @jaime

Welcome to the Frontity community.

You may have to create separate handlers to fetch the menus for the two different sites. Alternatively you could change the pattern that must be matched for the handler function to execute to accommodate both cases.

See the docs here for more info.

Hope this helps.

Try to change the handler pattern to @menu/:slug, so it won’t try to find a page/post at that location. That seems to work for me.

PS. I’m curious how you force to fully reload the page when switching the language. It’s the only thing I haven’t figured out yet.

Hey Johan, you saved my day. It’s totally working!

If you are hardcoding the language options, you should have something like this in your index.js:

state: {
    theme: {
      translations: [
        {
          href: "https://myexample.com/de/",
          abbreviation: "de"
        },
        {
          href: "https://myexample.com/en/",
          abbreviation: "en"
        },
        {
          href: "https://myexample.com/fr/",
          abbreviation: "fr"
        },
        {
          href: "https://myexample.com/it/",
          abbreviation: "it"
        }
      ],
      language: "",
      redirectedSite: "",
    },
  },

In my LanguageToggler component I am getting the main URL of my site, the API url and the available translations. In both of the urls I’m slicing the language at the end in order to get the url clean:

const mainURL = state.frontity.url.slice(0, state.frontity.url.length - 2);
const apiURL = state.source.url.slice(0, state.source.url.length - 2);
const translations = state.theme.translations;

and then I’m mapping the array translations to display the languages abbreviations inside a select tag. As soon as I’m selecting any of the available languages I run an action to change the language of the site:

onChange={e => {
    e.preventDefault();
    actions.theme.changeLanguage(e.target.value, mainURL, apiURL);
}}

This action will look like this in the index.js:

changeLanguage: ({ state }) => (lang, mainURL, apiURL) => {
    // I filter the language I'm choosing from the array 'translations'
    const selectedLanguage = state.theme.translations.filter(translation => translation.abbreviation === lang)[0];
    // I change the language to the new one
    state.theme.language = lang;
    // here I get the new url where we are going to redirect with the mainURL (http://localhost:3000/) + the path without the API url (example: 'en/contact')
    const newURL = mainURL + selectedLanguage.href.replace(apiURL, '');
     // and I store this new url in the state
    state.theme.redirectedSite = newURL;
},

Now in my Root Component, as window is undefined on the first render, I create a variable and use useEffect when the DOM did mount to change it to true:

const [languageChanged, setLanguageChanged] = useState(false);
useEffect(() => {
    setLanguageChanged(true);
}, [])

in addition I create another useEffect to redirect to the new URL when the state.theme.language did change:

useEffect(() => {
    if (languageChanged) {
      window.location.assign(state.theme.redirectedSite);
    }
}, [state.theme.language])

I changed a bit this example since mine is a bit specific considering that I’m using ACF and the WP plugin WPML for the multilanguage. I hope this can guide you a bit to achieve your goal.

1 Like