Wpml language switch to related post

Hi everyone!
I don’t knew if there’s just a similar source but i think it’s important to share thath.

I create a custom language switch from posts, page, custom post type, categories, custom taxonomies translations.
I’m using Wpml as wordpress plugin.

It’s not very clean code, but it’s working :tada: :tada: :tada: :tada:

First i need to do some modify to the Rest Api

<?php
//functions.php -> wordpress
//register the translate id in Rest Api post, page, custom post type

add_action( 'rest_api_init', function () {
	register_rest_field( 'post', 'translate_api', mol_related_translation_api() );
    register_rest_field( 'page', 'translate_api', mol_related_translation_api() );
	register_rest_field( 'mostre', 'translate_api', mol_related_translation_api() ); // Optional: Custom posttype
	register_rest_field( 'eventi', 'translate_api', mol_related_translation_api() ); // Optional: Custom posttype
});

function mol_related_translation_api() {
	return array(
		'methods'         => 'GET',
		'get_callback'    => 'related_translate_api',
		'schema'          => null,
	);
}

function related_translate_api( $data ) {
	$post_id = $data['id'];
    $currentLang = apply_filters( 'wpml_post_language_details', '', $post_id )['language_code'];
    if($currentLang === 'it') {
        $otherLang = 'en';
    } else {
        $otherLang = 'it';
    }

    $postData = apply_filters( 'wpml_object_id', $post_id, get_post_type(), false, $otherLang );
    $langArray = array(
        'key'   => apply_filters( 'wpml_post_language_details', '', $postData )['language_code'],
        'id'    => $postData,
        'slug'  => get_post($postData)->post_name,
        'path'  => wp_make_link_relative(get_the_permalink($postData))
    );

    return $langArray;
}


///register the is_home attribute in a page post type

add_action( 'rest_api_init', function () {
    register_rest_field( 'page', 'is_home', mol_is_site_home_page() );
});

function mol_is_site_home_page() {
	return array(
		'methods'         => 'GET',
		'get_callback'    => 'register_is_site_home_api_rest',
		'schema'          => null,
	);
}

function register_is_site_home_api_rest( $data ) {
	$post_id = $data['id'];
    
    if ( intval(get_option( 'page_on_front' )) === $post_id ) {
        $PPPosts = true;
    } else {
        $PPPosts = false;
    }

    
    return $PPPosts;
}



/// register translate params for all used taxonomies and categories

add_action( 'rest_api_init', function () {
    register_rest_field( 'category', 'taxonomy_translate', mol_test_category() );
    register_rest_field( 'tipodievento', 'taxonomy_translate', mol_test_category() );
});

function mol_test_category() {
	return array(
		'methods'         => 'GET',
		'get_callback'    => 'register_test_category',
		'schema'          => null,
	);
}

function register_test_category( $data ) {
	$term_id = $data['id'];


    $term = get_term( $term_id );
    $taxonomy = $term->taxonomy;

    $currentLang = apply_filters( 'wpml_current_language', null );
    if($currentLang === 'it') {
        $otherLang = 'en';
    } else {
        $otherLang = 'it';
    }

    global $translateId;
    $translateId = apply_filters( 'wpml_object_id', $term_id, $taxonomy, true , $otherLang );
    

    $translated_url = apply_filters( 'wpml_permalink', get_term_link($term_id) , $otherLang, true);
    $slugParts = explode("/", $translated_url);

    if($translateId !== $term_id) {
        $langArray = array(
            'key'   => $otherLang,
            'id'    => $translateId,
            'slug'  => $slugParts[count($slugParts) - 2],
            'path'  => wp_make_link_relative($translated_url)
        );
    } else {
        $langArray = array(
            'key'   => null,
            'id'    => null,
            'slug'  => null,
            'path'  => null
        );
    }

    
    return $langArray;
}


?>

In my index.js i need to set a couple of parameters to determinate language and current navigation.

const myTheme = {
  name: "my-theme",
  roots: {
    theme: Root,
  },
  state: {
    theme: {
      ...
      lang: "it",
      rapidNavIta:'navigazione-rapida',
      rapidNavEng:'rapid-navigation',
      relativePathEng:'',
      relativePathIta:'',
      isInEng:false,
      isInIta:false,
      ...
    },
  },
  actions: {
    theme: {
      toggleLang:({state}) => {
        state.theme.lang = state.lang
      },
      toggleUrl:({state}) => {
        state.theme.isUrlVisible = !state.theme.isUrlVisible
      },
      beforeSSR: async ({ state, actions }) => {
        await actions.source.fetch("/pages");
        await actions.source.fetch("/posts")
        await actions.source.fetch("/eventi");
      },
    },
  },
  libraries: {
    html2react: {
      processors: [...]
    },
    source: {
      handlers: [...],
    },
  }
}


export default myTheme

The language switcher

const LanguageSwitch = ({ actions, state }) => {
    const { lang } = state.theme;
    return (
        <>
            <Box>      
                {state.theme.isInEng && 
                    <a
                        href={state.theme.relativePathEng !== '' ? `${state.theme.relativePathEng}` : '/en/'}
                        className="toggleicon"  
                        onClick={actions.theme.toggleLang}
                    >EN</a>
                }
                {state.theme.isInIta && 
                    <a 
                    // href="/" 
                    href={state.theme.relativePathIta !== '' ? `${state.theme.relativePathIta}` : '/'}
                    className="toggleicon">
                        IT
                    </a>
                }
            </Box>
        </>
    )
}

I made a function to set the language switcher to related translated post. After i call thath function in useEffect in every template (Page, Post, Archive, Tax)

export const setLanguageInfos = (endpoint, state) => {
    console.log('SOURCE')
    if(endpoint['translate_api']['id'] !== null) {
        if(endpoint['translate_api']['key'] == 'en') {
          state.isInIta=false
          state.isInEng=true
          if(endpoint['is_home']) {
                state.relativePathEng='/en';
          } else {
                state.relativePathEng=endpoint['translate_api']['path']
          }
        } else {
          state.isInIta=true;
          state.isInEng=false
          state.relativePathIta=endpoint['translate_api']['path']
          if(endpoint['is_home']) {
            state.relativePathIta='/'
            } else {
                state.relativePathIta=endpoint['translate_api']['path']
            }
        }
    } else {
        state.isInIta=false
        state.isInEng=false
    }
}

The post.js call a useEffect and pass parameters to function setLanguageInfos to get the translate version of post and set the language switcher.

import React, {useEffect} from "react"
import { connect } from "frontity"
import dayjs from "dayjs"


const Post = ({ state, libraries }) => {
  const data = state.source.get(state.router.link)
  const post = state.source[data.type][data.id]
  const formattedDate = dayjs(post.date).format("DD MMMM YYYY")
  const Html2React = libraries.html2react.Component

  useEffect(() => {
    setLanguageInfos(post, state.theme)
  },[])

  return (
    <div>
        <p>
            <strong>Posted: </strong>
            {formattedDate}
        </p>
        <h2>{post.title.rendered}</h2>
        <Html2React html={post.content.rendered} />
    </div>
  )
}

export default connect(Post)

This is a page.js how i set the lang switch like as post.js

import React, {useEffect} from "react"
import { connect } from "frontity"

//functions 
import { setLanguageInfos } from "./functions/setLanguageInfos"

const Page = ({ state, libraries }) => {
  const data = state.source.get(state.router.link)
  const page = state.source[data.type][data.id]
  const Html2React = libraries.html2react.Component
  const dataPosts = state.source.get('/')
  

  useEffect(() => {
    setLanguageInfos(page, state.theme)
  },[])

  return (
    <div>
      PAGE
       <h2>{page.title.rendered}</h2>
        <Html2React html={page.content.rendered} />
    </div>
  )
}

export default connect(Page)

This is the Archive.js / Taxonomy.js


import React, {useEffect} from "react"
import { connect } from "frontity"
import Link from "@frontity/components/link"

import List from "./List"

const IsArchive = ({ state, actions }) => {
  const data = state.source.get(state.router.link)
  const postTypes = state.source.postTypes
  const taxonomy = state.source[data.taxonomy][data.id]
  


    const setRelativePath = () => {
      if(data.isTerm) {
          if(taxonomy['key'] !== null) {
              state.theme.relativePathEng=`${taxonomy['taxonomy_translate']['path']}`
              state.theme.relativePathIta=`${taxonomy['taxonomy_translate']['path']}`

            if(state.lang === 'en') {
              state.theme.isInIta=true
              state.theme.isInEng=false
            } else {
              state.theme.isInIta=false
              state.theme.isInEng=true
            }
          } else {
              state.theme.isInIta=false
              state.theme.isInEng=false
          }
      } else {
        if(data.route === '/news/' || data.route === '/en/news/') {
            state.theme.relativePathEng='/en/news'
            state.theme.relativePathIta='/news'
            if(state.lang === 'en') {
              state.theme.isInIta=true
              state.theme.isInEng=false
            } else {
              state.theme.isInIta=false
              state.theme.isInEng=true
            }
        } else {
            postTypes && postTypes.map(postType => {
                if(data.route === `/${postType.endpoint}/` || data.route === `/en/${postType.endpoint}/`) {
                    state.theme.relativePathEng=`/en/${postType.endpoint}`
                    state.theme.relativePathIta=`/${postType.endpoint}`
                    if(state.lang === 'en') {
                      state.theme.isInIta=true
                      state.theme.isInEng=false
                    } else {
                      state.theme.isInIta=false
                      state.theme.isInEng=true
                    }
                }
            })
        }
      }
    }

    useEffect(() => {
        setRelativePath()
    },[])

  useEffect(() => {
    setRelativePath()
  },[state.lang])


  return (
    <div>
        PFP
      <List/>
    </div>
  )
}

export default connect(IsArchive)

Enjoy :slight_smile: