Multilingual database (working method place in this thread)

Hi all. I’ve started my solution for a multilingual database. I saw ‘plugins’ for multilingual WIX sites, but that was with translations of files. I personally don’t like the approach, but understand the ease of it. This post is on my solution for a flexible multilingual site with database driven content. When I’m further, I’ll update the ‘todos’. I’m just starting my WIX site, but also try to implement these major parts from point one. Feel free to comment.

Todos:

  • check if session usage is working in the first place
  • add ‘default’ to the page code in case session is not possible

Database Setup:

  • language table consisting of ‘language_code’, ‘flag’, ‘description’. Here you can add any other language
  • A data table with the ‘non translated’ table. These are columns like ‘active’, ‘record_id’, ‘reference_to_some_table’, ‘some_number_values’
  • A data translation table with the ‘translated’ columns. These are columns like ‘description’, ‘details’. Anything which is different in a different language. Important, this table must contain a link to the ‘language_code’. In my case the primary key of this table is the ‘language_code’ suffixed with the ‘record_id’

Extra code on ‘site’
I’m storing site settings in a table, and one is the default language. I want to put that in a session parameter on site load. Also, I clear the session when it is loaded. So in the ‘site’ code I put this.

— Site Code —
import {session} from ‘wix-storage’;

$w.onReady(function () {
// Get Site Settings and adjust site accordingly
wixData.query(“site_settings”)
.eq(“setting_code”, “venture_the_trails”)
.find()
.then((oSiteSettings) => {

// Set session information
session.clear();
if (oSiteSettings.items[0].setting_language && oSiteSettings.items[0].setting_language.length === 2) {
session.setItem(“vttLanguage”, oSiteSettings.items[0].setting_language);
} else {
session.setItem(“vttLanguage”, “en”);
}

});
});
— Site Code —

As you can see, I also added a default check to see my parameter is actually filled and has two characters, else it defaults to ‘en’.

Then on a page with a dataset, I add ‘page’ code to add extra filters. I’m still tweaking this part, but my starting point is functional.

— Page Code —
import wixData from ‘wix-data’;
import {session} from ‘wix-storage’;

$w.onReady(function () {
// Add language filter to dataset
$w(“#dataset1”).setFilter( wixData.filter()
.startsWith(“translation_id”, session.getItem(“vttLanguage”))
);
});
— Page Code —

So here I get the dataset (just an example in this case) and set the filter for it. Currently I do a ‘startsWith’ on the ‘translation_id’ column. The values here are e.g. ‘en_rec1’, ‘en_rec2’, ‘nl_rec1’. I want to switch this to my ‘language_code’ column with ‘eq’ but that doesn’t work yet. I just read out the session info.

4 Likes

Wow! Great work there!

Hi Edgar,
What you are trying to achieve is very interesting. Only I did not understand - for each language will have its own unique URL? Or will the data be loaded dynamically, and the URL will be the same for different languages?

Hello Eugene. Currently how I’m making it, it will work with same URL for each language. I’m setting a session variable (and you could then also store user information with this value if you use a sign in method). Personally I don’t like the URL containing all information and nearly always revert to keeping the main URL. I know, this has some cons, but in my case those don’t matter.

OK, thanks! It will be interesting to look at the final result. I agree that in some projects the SEO doesn’t matter.

Okay to continue. I’ve made a multilinual start for the repeater. Please note, I’ve adjusted my tables a bit, for I can’t get it working yet with references.

So I placed a repeater element on a page and name the objects within each item. This is fairly straight forward. Then I made a backend function. It executes a query, transforms the received columns to a specific dataset and returns it.

import wixData from 'wix-data';

export function getTrails(strLanguage) {
	return wixData.query("trails")
		.ascending("trail_at")
		.find()
	  	.then((resultTrails) => {
	  		const oTrails = resultTrails.items;
	  		let arrTrails = [];
	  		
	  		// Transform to required columns
	  		for (const iTrail in oTrails) {	  			
	  			if (strLanguage === "nl") {
		  			arrTrails.push({
		  				"_id": iTrail,
		  				"country_flag": oTrails[iTrail].country_flag,
		  				"trail_at": oTrails[iTrail].trail_at,
		  				"trail_title": oTrails[iTrail].trail_title,
		  				"trail_description": oTrails[iTrail].trail_description_nl
		  			});
	  			} else {
		  			arrTrails.push({
		  				"_id": iTrail,
		  				"country_flag": oTrails[iTrail].country_flag,
		  				"trail_at": oTrails[iTrail].trail_at,
		  				"trail_title": oTrails[iTrail].trail_title,
		  				"trail_description": oTrails[iTrail].trail_description_en
		  			});	  				
	  			}
	  		}
	  		
	  		return arrTrails;
  		});
}

As you can see the language parameter is passed to the function. Storage doesn’t work backend offcourse. Based on the language I use different columns. Note I also did a date to string conversion (have to get momentjs to work). The client side code has this

import wixData from 'wix-data';
import {getTrails} from 'backend/vttTrails';
import {session} from 'wix-storage';

export function getList() {
	getTrails(session.getItem("vttLanguage")).then((oTrails) => {
		$w("#repeaterTrailList").data = oTrails;
		$w("#repeaterTrailList").onItemReady(($w, itemData, index) => {
			$w("#txtTitle").text = itemData.trail_title;
			$w("#txtDescription").text = itemData.trail_description;
			$w("#imgCountry").src = itemData.country_flag;
			$w("#txtDate").text = itemData.trail_at.getFullYear().toString() + "-" + itemData.trail_at.getMonth().toString() + "-" + itemData.trail_at.getDate().toString();
		});
		$w("#repeaterTrailList").show();
	
		return;
	});	
}

Here the data is assigned to the repeater. Then specific columns are linked to specific elements. This way you should be able to do other datadriven manipulations. I will need to add one or two myself. Think of coloring or show/hide items.

Functional Solution:
I have this running. Not 100% following the initial concept, but it works for now. Changes I needed to make where in my table structure. Currently it doesn’t seem doable with a split table structure for you data (a table with non translatable data and one with). So I went for the methode prefixing translated data column with my languages ‘en’ and ‘nl’. A new language means adjusting all tables. In my case not an issue, I won’t add more for some time.

Then first of all, I switched to local storage. Meaning when a user returns (even guests) their preferred language is still known. So first, set the default site language in storage

import wixData from 'wix-data';
import {local} from 'wix-storage';

$w.onReady(function () {	  		
	// Get Site Settings and adjust site accordingly
	wixData.query("site_settings")
	.eq("setting_code", "venture_the_trails")
	.find()
  	.then((oSiteSettings) => {
  		// Set session information
  		const strLanguage = local.getItem("vttLanguage");
  		
                if (strLanguage === null || strLanguage === "") {
  		    if (oSiteSettings.items[0].setting_language && oSiteSettings.items[0].setting_language.length === 2) {
  		        local.setItem("vttLanguage", oSiteSettings.items[0].setting_language);
  		    } else {
  			local.setItem("vttLanguage", "en");  			
  		    }
                }
                if (local.getItem("vttLanguage") === "nl") {
                    $w("#imgDutch").hide();
                    $w("#imgEnglish").show();
                } else { 
                    $w("#imgDutch").show(); 
                    $w("#imgEnglish").hide(); 
                } 
  	});
});

Here I collect the language from my site settings table. It is stored in the column ‘setting_language’. The code checks if it is filled, and two characters long. It will use the value and otherwise it will switch to English. Naturally this is only done if the language is not set yet. It also sets the toggle elements accordingly. Now, this part can be more dynamic. You could met a constant with the element name e.g. ‘#img_nl’ which you could set with ‘const curImgLang = “#img_” + local.getItem(‘vttLanguage’);’ and use that but I didn’t see the point yet for I don’t know how to set all elements starting with a specific entry in one piece of code.

So now the language is know, it’s nice to add a toggle. I just placed to flag icons over each other in the header. The dutch flag called ‘#imgDutch’ and the other ‘#imgEnglish’. Both hidden on load so not to trigger ‘flashing’ of icon if it’s the wrong one displayed first. To both images I added a onClick event, ‘toggleEnglish’ and ‘toggleDutch’. Again, two simular functions, you can put it in one, but it won’t matter in the amount of lines.

import wixData from 'wix-data';
import {local} from 'wix-storage';
import wixLocation from 'wix-location';

export function toggleDutch(event, $w) {
	local.setItem("vttLanguage", "nl");
	$w("#imgDutch").hide();
	$w("#imgEnglish").show();
	wixLocation.to("/");
}

What happens here is the dutch toggle is called. It sets the new language in storage, then toggles the flags and finally redirects home. One glitch here: it doesn’t redirect itself just yet.

So now the site knows different languages so you are ready to have dynamic related content. Lets for this sample use a simple repeater. The item data is language dependent. I placed a repeater on a page and named item elements. I haven’t made filters yet. I didn’t connect anything to data. So the page is then ready to display data but still needs to get it assigned. Since we don’t want to overload the client with data, I made a backed script to fetch data and return only what is required.

import wixData from 'wix-data';

export function getTrails(strLanguage) {
	return wixData.query("trails")
		.ascending("trail_at")
		.find()
	  	.then((resultTrails) => {
	  		const oTrails = resultTrails.items;
	  		let arrTrails = [];
	  		
	  		// Transform to required columns
	  		for (const iTrail in oTrails) {	  			
	  			if (strLanguage === "nl") {
		  			arrTrails.push({
		  				"_id": iTrail,
		  				"country_flag": oTrails[iTrail].country_flag,
		  				"city": oTrails[iTrail].city,
		  				"trail_title": oTrails[iTrail].trail_title,
		  				"trail_description": oTrails[iTrail].trail_description_nl
		  			});
	  			} else {
		  			arrTrails.push({
		  				"_id": iTrail,
		  				"country_flag": oTrails[iTrail].country_flag,
		  				"city": oTrails[iTrail].city,
		  				"trail_title": oTrails[iTrail].trail_title,
		  				"trail_description": oTrails[iTrail].trail_description_en
		  			});	  				
	  			}
	  		}
	  		
	  		return arrTrails;
  		});
}

So here I query the table ‘trails’. The language is passed from the client, you’ll see it later. I haven’t added any filtering here, just a sort on a date column. Now remember I have columns for each language and I don’t want all columns returned. So the query result is processed to create an array with specific data. As you can see, it will use the language to get the right column and put it in a specific named array element.

This function is called from the client side

import {getTrails} from 'backend/vttTrails';
import {local} from 'wix-storage';

export function getList() {
	getTrails(local.getItem("vttLanguage")).then((oTrails) => {
		$w("#repeaterTrailList").data = oTrails;
		$w("#repeaterTrailList").onItemReady(($w, itemData, index) => {
			$w("#txtTitle").text = itemData.trail_title;
			$w("#txtDescription").text = itemData.trail_description;
			$w("#imgCountry").src = itemData.country_flag;
			$w("#txtCity").text = itemData.city;
		});
		$w("#repeaterTrailList").show();
	
		return;
	});	
}

The client calls the backend function, with the language. The result is then linked to my repeater element. When that is done, each item element is linked to a specific column. Advantage here, you could transform the date to readable string data, change properties based on content. Then when done the repeater is made visible. I always first hide elements which need to be initiated. This way I could place a loader element first.

Naturally I will still need to tweak some things, but this works for me for now.

Hi Edgar,

I’m having a similar challenge as you did a year ago with Multilingual database and I don’t know any coding.

I‘ve managed to build my website from scratch in Wix and I’ve added a database that works in conjunction with few dynamic pages.

My next step is to translate my website and I’m using the new wix Multilingual feature. However, I’m now running into the problem of translating my database.

Could you please help me with this task? Please reach me at vladimir@ptyrealtors.net for more details.

Thanks,

Vladimir

Hi Vladimir and Edgar,
Is there any help for multilingual for dataset? I am so stuck here

Hi, Can someone clarify or give an example of this?:

  • language table consisting of ‘language_code’, ‘flag’, ‘description’. Here you can add any other language.
    Also, The query is referring to a site specific database.
    wixData.query(“site_settings”)
    .eq(“setting_code”, “venture_the_trails”)

Can someone show examples of all the databases? It would help to be able to know how I should modify my work.
Thank you.

Asking again,
Hi, Can someone clarify or give an example of this?:

  • language table consisting of ‘language_code’, ‘flag’, ‘description’. Here you can add any other language.
    Also, The query is referring to a site specific database.
    wixData.query(“site_settings”)
    .eq(“setting_code”, “venture_the_trails”)

Can someone show examples of all the databases? It would help to be able to know how I should modify my work. I just want to know what fields I am setting up. If I could get an answer soon that would be helpful

A great example: “Settings Database needs these fields…” “Content database needs these fields…”

I am looking to translate my english text into Spanish without having to have a separate database for the Spanish content. I have multiligual enabled too, but it is not reading database content yet.

Thank you.

Not sure if this will be helpful but check this process out - not great for large content collections or sites with a ton of languages, but might also work with large content structured if you have a bit more technical skills to automate some of the redirect mapping needed. Hope this helps. URL with step-by step process: lazarinastoyDOTcom + how-to-translate-content-collections-with-wix