How to divide the state by selected language?

Hello developers!

I translate my Frontity site using the Polylang plugin, and create a separate version of each post for each language.
In order not to load all posts in all languages in the initial state, I need to filter them on the backend by some parameter in $ _REQUEST / $ _COOKIE.

Is there some way to pass the selected language from the user localStorage or cookies on the client to the initial request on the server?

Sorry but it seems I’ve found solution :slight_smile:

beforeCSR: ({ state, actions }) => {

  if (typeof window !== 'undefined' && typeof window.localStorage['frontity_lang'] !== 'undefined') {
    state.source.params.lang = window.localStorage['frontity_lang']
  }
},

No, it doesn’t work :confused:

I see that it is possible to pass the language through “?frontity_lang” get parameter and get it on the server as follows.

beforeSSR: async ({ state, libraries, actions }) => {

  state.source.params.lang = typeof state.frontity.options.lang !== 'undefined' ? state.frontity.options.lang : 'en_US';

It remains to figure out how to transfer the language to this property programmatically.

Found a solution :partying_face:
Cookies can be obtained from Koa ctx in beforeSSR, then I pass language to request

state.source.params.lang = ctx.cookies.get('abc_lang');
2 Likes

Hello,

I’m trying to build a multilingual theme but having the problem where both English and Welsh posts are loaded into the state.
Your problem sounds very similar to mine however I’m unable to get your solution working.
Is there any chance you could give a bit more detail on your solution? if I could see your index.js that would be very helpful.

Hi! I’ll be glad to help.

import Theme from './components'
import image from '@frontity/html2react/processors/image'
import iframe from '@frontity/html2react/processors/iframe'
import actions from './actions'

const theme = {
  name: 'dc-theme',
  roots: {
    /**
     *  In Frontity, any package can add React components to the site.
     *  We use roots for that, scoped to the `theme` namespace.
     */
    theme: Theme,
  },
  state: {
    /**
     * State is where the packages store their default settings and other
     * relevant state. It is scoped to the `theme` namespace.
     */
    theme: {
      lang: 'en_US',
      autoPrefetch: 'no',
      resumeUrl: '/Evhen-Veliky-Resume.pdf',
      featured: {
        showOnList: false,
        showOnPost: false,
      },
      loadedLanguages: {
        ru_RU: false,
        en_US: false,
      },
      messageInput: '',
      contactForm: {
        endpoint: '/dcma/v1/proceed',
        sending: false,
        success: false,
        error: false,
        fields: {
          contacts: '',
          message: ''
        }
      },
      counter: {
        endpoint: '/dcma/v1/counter/increment_visits',
      },
      playing: false
    },
  },
  /**
   * Actions are functions that modify the state or deal with other parts of
   * Frontity like libraries.
   */
  actions: actions,
  libraries: {
    html2react: {
      /**
       * Add a processor to `html2react` so it processes the `<img>` tags
       * inside the content HTML. You can add your own processors too
       */
      processors: [image, iframe],
    },
  },
}

export default theme
1 Like

The essence of my solution is to parse cookies on the server side and passing the selected language to the request:

    beforeSSR: ({ state, libraries, actions }) => {
    . . .
      const parseCookie = (ctx, state, actions) => {

        state.source.params.lang = ctx.cookies.get('dc_lang') || 'en_US'
        actions.theme.i18nInit(state.source.params.lang.slice(0, 2))
      }

      return async ({ ctx }) => {
        await Promise.all([
          loadAnyway(ctx, state, actions),
          parseCookie(ctx, state, actions),
          actions.source.fetch('wpAssets')
        ])
      }

Then on WP side I filter posts by the selected language:

public function __construct() {
	add_action( 'pre_get_posts', [$this, 'filter']);
}

/**
 * @param \WP_Query &$query
 */
public function filter ( &$query ) {

	if (isset($_GET['lang']) and $_GET['lang'] == 'ru_RU') {
		$query->query['lang']      = 'ru';
		$query->query_vars['lang'] = 'ru';
	}

	if (isset($_GET['lang']) and $_GET['lang'] == 'en_US') {
		$query->query['lang']      = 'en';
		$query->query_vars['lang'] = 'en';
	}
}
1 Like

@veliky.dev, nice solution to handle languages in a Frontity project :+1:

In case you want to check alternative solutions here you some related threads to implementing languages in a Frontity project:

@luisherranz any thoughts on how to do this without the need of using cookies?

Very interesting indeed :slightly_smiling_face:

@veliky.dev why do you use cookies? Do the different versions need to share the same URL?

I mean, does it have to load https://domain.com/some-post in different languages, as opposed to using something like https://domain.com/en/some-post for English and https://domain.com/ru/some-post for Russian?

I ask this because for Google is usually better to have different URLs, and if you do that I think you should be able to use two different sites, one for English and the other for Russian.

Remember that Frontity is multisite if you use array in your frontity.settings.js file, and each site can have its own settings.

Hope this is a compliment :smile:
I use cookies because I want the state to reload with posts in a different language when I click on the language switch button. This is the only way to transfer the selected language that I found.
I have different URLs, because the slugs of my posts are different in different languages. for example:


So I don’t need prefixes. In your opinion, this solution has weak points?

You should do two instance & switch, if you have a instance primarily RU, It’ll rank higher than other sites who are in EN, that’s SEO trick. I can expand on this but you can look it up for now.

The approach is very intersting indeed :slightly_smiling_face:

If I am not mistaken, the cookie needs to be processed by the server, which means that you cannot cache and serve the same HTML to all the users, can you?

Why not use the polylang_current_lang prop of your posts to send the correct language?

You can wait until data.isReady is true in your beforeSSR, although is a bit tricky right now because we don’t have nice APIs for that. But it is possible using observe:

import { observe } from "frontity";

const when = (fn) => new Promise((resolve) => observe(() => fn() && resolve()));

const beforeSSR = async ({ state }) => {
  // Wait until data is ready.
  await when(() => {
    const data = state.source.get(state.router.link);
    return data.isReady;
  });

  // Get data. Now we know that it is ready.
  const data = state.source.get(state.router.link);

  // Extract language from `polylang_current_lang` and do the logic...
};

– CodeSandbox

But that won’t work for the home because they use the same URL so maybe you would need to use /en or /ru after all…