How to handle translations, site w multiple languages

I have to develop a site in two languages and remembered this thread. I had a feeling a straighforward solution was suggested, but now I look at it more closely, I’m not sure how it works.

I have a simple table like this, using @George’s suggestion:

image

I have a query which returns records in this table where a cookie = langkey. I have two buttons which set this cookie to ‘es’ or ‘en’. If I put this on a page:
{{sc_lang.data.qry_lang[0].text}} | {{sc_lang.data.qry_lang[1].text}}
buscar | contacto is displayed if I click one button, and the English words are displayed if I click the other.

Obviously I don’t want to use the array index to get the correct term. How can I use the textkey value instead? Or perhaps this isn’t the right approach. I can see there are quite a few ways to do this, but if I could just a cookie and something like {{sc_lang.data.qry_lang['search'].text}} - that would seem a good solution.

ok @TomD - this could be made easy with an extra custom data formatter (that we can add to the standards anyway) to be able to convert the array to keyed object for easy access and lookup

  1. add a script blok in the head with this code:
<script>
dmx.Formatters('array', {
		toKeyedObject: function(array, key, value) {
			var newObj = {};
			for (var ai=0; ai < array.length; ai++) {
				newObj[array[ai][key]] = array[ai][value];

			}
			return newObj;
  }
 });
</script>
  1. create in the beginning of the page, after the server connect load of the texts, a new app connect variable with your translation data and apply the formatter to it:
<dmx-value id="lang" dmx-bind:value="sc_lang.data.qry_lang.toKeyedObject('textkey', 'text')"></dmx-value>
  1. then you can use it where the text is needed with the last word the key:
{{lang.value.contact}}

or bind it as inner text

<p dmx-text="lang.value.contact"></p>

So that is how it should work. We can improve some things, but that is the general workflow

10 Likes

Many thanks @George - that’s a great solution. I think this will be a very useful feature generally and works perfectly for language switching.

I was wondering about how to set defaults in case no cookies are set, at least for critical text - now it’s just a matter of setting a defaut in the normal way with the data formatter. For inner text, I was pleased to see dmx-html also works too.

4 Likes

Wappler Translation Tutorial

Took me hours to gather all informations of the 3 steps you mentionned but it was fun to learn many other things along the way. And I wanted to contribute to thank this amazing community.

So here is a quick tutorial (hopefully) from A to Z.
I’m not really comfortable with Wappler terminology yet but the step by step approach should allow anyone to understand the workflow.

Step by step video :

Ressource :
SQL dump : https://www.dropbox.com/s/kb3i7oiwa1wb86y/translations.sql?dl=0

Any feedback/improvements are welcomed ! :slight_smile:

9 Likes

Thank you Jeoff, very well presented and I love the accent.

Hi,
Cannot make it work. Translations are properly loaded from the database, but data doesn’t to be stored in the array (after toKeyedObject) to be displayed after. Is there a way to control this array (if exists and what is in) with chrome debugger ?

1 Like

Usually, this is because the formatter script is not included in the code :

If you can send a screenshot or the actual page link, it will be easier to help you though ! :wink:

I have already added it with no success. But if i add it, do i need to add this script also as @george wrote previously ? Should we have both ?
image

No it’s not needed anymore as it’s shipped with the formatter script now.

Ok, thank’s for you help, i think i will have to start from scratch again.
I’m still interested to know if there is a solution to control what’s going on in the chrome debugger

@jeoff75, how do I convert an array into keyed object? I tried looking for this formatter, but no luck. Thanks.

Look at the source here :
https://login-boilerplate.usa-sandbox.space/app_create_user.php

I’ll try to upload the project to git this week-end so that you can have a look from within wappler.

2 Likes

Thanks lot mate. I did watch the video you put together and figured it out, it was a lifesaver. The toKeyedObject is not available through the UI formatter, entered it manually, thanks again :+1:t4:

It is available in the formatter - not originally, but I think it was added quite a few versions ago:

image

Thanks @TomD

Not finding at my end on an array variable. I wonder what the difference, is your screenshot from a server action?

Also, would you know if Keyed Object just another term for Keyed Array aka Associate Arrays in javascript?

My screenshot was not from a server action. I think the difference is that I used a variable rather than an array component. This was the variable and value:
<dmx-value id="lang" dmx-bind:value="sc_lang.data.qry_lang.toKeyedObject('textkey', 'text')"></dmx-value>

image
The query returns an array of keys and text (translations). Eg this displays the translation for ‘terms’ (based on a cookie storing the selected language):
{{lang.value.terms}}

although I actually include a default: {{lang.value.terms.default("Terms")}} (in case the cookie is not set).

1 Like

Just implemented all of this, and it worked like a dream!

Many thanks to @jeoff75 for the great video and cool accent, @George for putting the toKeyedObject function into Wappler, and @TomD for the screen shot of how to access it!

@George, it would be great when something new is put in Wappler if posts like this one could be updated to note the change… it looks like it could have saved @djeorges quite some hassle!

It took me ages to find the toKeyedObject conversion in the Wappler GUI… so here is what to do:

  1. When assigning to the variable “lang” (as George’s example):
  2. Click on dynamic value (lightning icon),
  3. Select Server Connect -> Data -> next icon below.
  4. Click on the magic wand next to the field text at the bottom right of the screen.
  5. Right click on the server connect “burger” icon
  6. Collections -> To Keyed Object
  7. Key: select your database field which is the index to your app text
  8. Value: select your database field which is the actual app text

My Implementation

A few points about how I implemented it that I hope will help those following this path:

  • My index is a number. Felt like it may be faster on indexing and saved me inventing yet another naming scheme. Numbers are 4 digit, in the form xyyz, where x is the area of the app (1=contacts, 2=orders, 3=finances, 4=products), yy is the specific field/view being described (01=first name, 02=last name), and z is the aspect to describe (0=item title/content, 1=description, 2=placeholder, 9=help text).
    So for example, help text for First Name field is 1019 and placeholder for Last Name field is 1022.

  • I use the 5 digit language codes, so en-GB, en-US, es-ES etc. It is important an american user sees the word “color” rather than “colour” to feel really at home with the app.

  • Sometimes I won’t have all the translations for every piece of text in the app, especially when translating en-US for example, so I have the idea of a fall-back language… most commonly en-GB, but could be es-ES.

  • To implement the fall-back language, I access the complete text for the application (apptext) via a mySQL procedure get_apptext(language). When called, that finds all the apptext in that language and then then substitutes all that is missing from the fall back language.

  • I will have users who have “is_language_editor” and “edit_language” fields. When both are enabled, there will be tiny button next to each piece of text which will enable a modal where they can go in and edit their language version for that piece of text.

  • The app will be developed in en-GB which I call the base language. I plan for each field in the database to have a modified_date field, and some system where the language editors can see when the en-GB modified date field for an apptext is later than that for their language, so they are highlighted to the need to update their content. Details of that to be worked out!

I hope all that helps!

Best wishes,
Antony.

1 Like

Well we try but sometimes we miss some topics as there are many.

You can actually post such tips directly as docs! Just make a post under the #docs category and it will be automatically included in our official docs.

You can also add it to the index by linking to it from an “about xxx category@ from the docs category that it fits.

We plan to restructure the docs categories and organization soon and any help will be greatly appreciated.

2 Likes