Important announcements from CouchCMS team
73 posts Page 1 of 8
Previous 1, 2, 3, 4, 5 ... 8 Next

As most members would know, the definitive resource on this forum for information about creating multi-lingual sites using Couch is - viewtopic.php?f=8&t=74

That thread primarily discusses two distinct methods of implementing multi-lingual sites -
2. Using separate templates for each language
viewtopic.php?f=8&p=330#p330 (and also

3. Using a single template but creating separate editable regions in it for each language

Please note that I have numbered the methods above beginning with '2' - this is to maintain compatibility with the original thread where three methods are discussed and the two above are at the second and third position respectively (and are also repeatedly referred to by these numbers).

By far the consensus seems to be that, all things considered, the second method is the best. However it is complicated for the clients as they now have multiple templates to contend with. On the other hand, the third method is much simpler and easier for the client to work with but has some drawbacks that can prove to be deal-breakers in some cases.

Following quotes taken from our forum highlight those drawbacks
@Musman wrote:
About 'my_lang' based on session approach, everybody who use it should be awared of no-compatibility with 'cache' feature and with searching engines,
also may be problems with 'search' feature.. I've made a website using this approach, but will make another with folder approach, it's much more reliable.

@JD wrote:
Although I like the simplicity of dynamically loading the editable fields according to the set language cookie and managing all fields in one template,
I read some SEO sources that recommend against this approach as a page might not rank well.

Actually, the reason for almost all the drawbacks mentioned above boils down to one single characteristic of the third method -
since the selected language is hidden as a 'session' variable, the URL of pages remain the same irrespective of which language is being displayed on them.

So, for example, a visitor goes to a page '' and is shown contents in English. She clicks the language-switcher menu to select 'Portuguese' and the page refreshes to show contents in Portuguese. However, the URL of the page still shows '' i.e. the *same* as English version.

Not difficult to see why this has negative implications for SEO and why caching would not work with this approach (both use URLs to differentiate between resources and here the same URL points to two different resources).

An effective fix for this problem would be to manifestly show the language of the contents in the URL e.g. -
Code: Select all

Same links with prettyURLs -
Code: Select all

Notice the 'en' and 'pt' in the links above.
And now since the different languages use distinct URLs, our problem would be resolved.

I am attaching a little addon here that does precisely that (and more).
(3.53 KiB) Downloaded 2388 times

This addon should also make the entire process of implementing the 'third' method ultra-simple as everything is neatly packaged in an addon (no 'switch.php', 'lang_switcher_menu.html' etc. required).

Some common requirements of a multi-lingual site are reflected in the following request by @JD -
Ideally, the site should detect the default language of the visitor's browser and switch accordingly to the language version (EN, DE, ES).
If, however, the visitor is not using one of the available site languages, the English version should be served as a standard.

Furthermore, I would like that the user is able to switch between those languages at any time on any site from the menu (little flag buttons to choose language version)

This addon should make it a breeze to get those done.
So let us get going and see how that happens.

Before we install this addon, please make sure your setup fulfills the following prerequisites -

1. Get the current version of Couch from GitHub ( For this addon to work, it'll need at least "Couch v2.0 Build 20170710" (as of this post date, this is the latest version at GitHub).

2. If you'd be testing this addon on an existing Couch site and the site uses prettyURLs, please make sure to turn prettyURLs off (from couch/config.php) before installing this addon.
Don't worry, we'll turn those back on later (as explained at the end of this post). To begin with, we need normal URLs.

With the prerequisites in place, we can begin the installation.
The procedure remains the same as all the other addons.

a. Extract the attached zip to get a folder named 'multi-lang'.
b. Place this folder within the 'couch/addons' folder of your installation.
c. Finally, activate this addon by adding the following entry in 'couch/addons/kfunctions.php' file (if this file is not present at the specified location, rename the 'kfunctions.example.php' file there to 'kfunctions.php') -
Code: Select all
require_once( K_COUCH_DIR.'addons/multi-lang/multi-lang.php' );

Within the 'multi-lang' folder we placed above in the 'addons', you'll find a file named 'config.example.php'.
Rename it to 'config.php'. Open it in your favorite text editor and find the following setting in there -
Code: Select all
// Accepted languages (code => display_name) 
// get codes from
$cfg['langs'] = array(

As you can see, as a sample, it already has three languages configured for use on your site.
You are supposed to make changes to this setting to specify the languages your site will work with. Please note that for every language specified in it, you'll also need to specify its 'language-code' ('lc' as it will referred to in the rest of this discussion). You can see the 'lc' for the three languages above. For other languages, if you are unsure of its code, you may get it from the link shown above.

For the purpose of this discussion, I'll assume that we accept the sample configuration and make our site use the three languages specified above.

OK, with that done, we are ready to use this addon.

Visit any page of your site and observe its URL in the browser's address bar - you'll see that Couch automatically adds 'lc=en' to the URL.
Code: Select all

That 'en' is used because it, being the first language specified in config, is treated as the default language and so when no language is specified in the URL it gets automatically selected. You can, of course, set your desired language as the default by placing it first in the config array we saw above.

Let us quickly create a language switcher to test out the other languages we specified.

Creating the language switcher menu:
If we were to place a <cms:dump_all /> tag in any template, we'll see that this addon makes available to us some variables that can aid us in implementing a multi-lingual site.

Assuming that the page being visited is
and three languages have been specified (en, fr and pt) in config, we'll see the following new variables -
Code: Select all
k_lang: en
k_link_en: http://localhost:8080/test.php?p=710&lc=en
k_link_fr: http://localhost:8080/test.php?p=710&lc=fr
k_link_pt: http://localhost:8080/test.php?p=710&lc=pt
k_supported_langs: Array

A brief description of the variables -
This will always contain the currently selected language (you'll remember that this value is one of the language-codes we specified in the config).

These links point to the current page in all the available languages - notice the 'en', 'fr', 'pt' (our language-codes) appended to 'k_link_'.
In this case, the three links are for the three languages we specified in config. If there were more languages or other languages, the links would reflect those languages.

As you can see, these can be used to create a language-switcher e.g.
Code: Select all
        <a href="<cms:show k_link_en />">English</a>
        <a href="<cms:show k_link_fr />">French</a>
        <a href="<cms:show k_link_pt />">Portuguese</a>

For quickly testing out the addon, you can use the menu above.
However, the next variable provides a more generic method of creating such language-switcher menus.

This is an 'array' (i.e. a compound variable) containing info about all the languages specified in config.
Try using it like this -
Code: Select all
<cms:each k_supported_langs as='lang' key='lc'>
    <cms:show lang />: <cms:show lc /> <br/>

and it should output -
Code: Select all
English: en 
French: fr
Portuguese: pt

As you can see, the <cms:each> tag above iterates through all entries in 'k_supported_langs' and makes available each as two variables - 'lang' and 'lc'. These provide us with the names of all the specified languages and their codes.

We can then use those to create a generic language-switcher menu that will work across all configurations.
Here is a real-world example you may use as is or, as is more likely, use the technique in your own static design to convert an existing menu markup into a functional language switcher -
Code: Select all
<ul id="languages">
    <cms:each k_supported_langs as='lang' key='lc'>
        <li lang="en" id="nav<cms:show lang />">
            <a href="<cms:get "k_link_<cms:show lc/>" />">
                <img src="<cms:show k_site_link />images/languages/flag-<cms:show lc />.jpg" alt="<cms:show lang />">

The code above outputs -
Code: Select all
<ul id="languages">
    <li lang="en" id="navEnglish">
        <a href="http://localhost:8080/test.php?p=710&lc=en">
            <img src="http://localhost:8080/images/languages/flag-en.jpg" alt="English">
    <li lang="en" id="navFrench">
        <a href="http://localhost:8080/test.php?p=710&lc=fr">
            <img src="http://localhost:8080/images/languages/flag-fr.jpg" alt="French">
    <li lang="en" id="navPortuguese">
        <a href="http://localhost:8080/test.php?p=710&lc=pt">
            <img src="http://localhost:8080/images/languages/flag-pt.jpg" alt="Portuguese">

With proper styling, it looks like -
switcher.png (1.53 KiB) Viewed 100990 times

I'd like to draw your attention to two points above -
1. I renamed the images of flags I had, deliberately adding to each their respective language code (e.g. flag-en.jpg, flag-fr.jpg, flag-pt.jpg).
This way in the cms:each loop, I could use the <cms:show lc /> statement (outputting the language code) to show the flag belonging to the language being iterated.

2. More importantly, notice also how the flags are linked to their language versions of the *current* page.
So, if we place this menu in a snippet and make it display on all pages of our site, whatever page a visitor might be on, the flags will provide her with links to the *same* page in different languages.

Please notice how we are using <cms:get> coupled with <cms:show lc/> to output the values of 'k_link_en', 'k_link_fr' and 'k_link_pt' variables containing links of the current page in those languages -
Code: Select all
<cms:get "k_link_<cms:show lc/>" />

As those familiar with the original "third method" (viewtopic.php?f=8&p=9010#p9010) will recall, the use of <cms:get> was pivotal in creating multi-lingual sites.

For those chancing upon this thread for the first time, please allow me a brief detour to explain the use of <cms:get> as this is used extensively further down (others may safely skip this section).

=================== begin detour ===============================
Please consider the following two statements -
Code: Select all
<cms:show k_link_en />
<cms:get "k_link_en" />

Both will output exactly the same result (the English language link of the current page).
However, notice that the syntax of <cms:get> is different fron that of <cms:show> in that it requires "double-quotes" around the variable name - effectively this means that we are specifying a "string" as the variable.

This use of string opens up possibilities that are not available with <cms:show>. For example, consider this -
Code: Select all
<cms:set my_lang='en' />
<cms:get "k_link_<cms:show my_lang />" />

In the code above, notice we are using another variable (my_lang) *inside* the <cms:get> input string. The value contained in 'my_lang' is 'en', so after substitution, the final string that <cms:get> will receive would be "k_link_en" i.e. the English language link.

Now if we were to change the value of 'my_lang' to 'fr', the <cms:get> will fetch fetch the "k_link_fr" link instead.
Point is that we can dynamically change the output of <cms:get> by changing the value of the variable(s) used in its input string.

This makes it possible for us to code 'language agnostic' statements using variables that will change with the current language of the site, for example consider this -
Code: Select all
<cms:get "k_link_<cms:show k_lang />" />

The 'k_lang' variable, as you'll remember, will always contain the language-code of the currently selected language.
So, the statement above will automatically show the current page link in the language currently selected. If the current language is French, it will show the FR link. If the visitor switches over to English, it will automatically show the now current EN link.

It will work without any change everywhere without us needing to find out first as to which language is current and so which variable to show.
To illustrate the difference, following is the equivalent code using <cms:show> for our tri-lingual site -
Code: Select all
<cms:if k_lang='en'>
    <cms:show k_link_en />
<cms:else_if k_lang='fr' />
    <cms:show k_link_fr />
<cms:else_if k_lang='pt' />
    <cms:show k_link_pt />

The code above, apart from being more verbose, is also non-portable as you'll have to change it if you happen to add more languages to the site or change the existing ones.

That ends our detour (hopefully having established the usefulness of <cms:get>).
=================== end detour ===============================

Creating the main site
Once the language switcher is in place, try clicking the different languages and see how the URL in the browser's address bar automatically shows the selected language. So now each language has its own distinct URL for every page on the site.

We are finally set to show the actual contents in the selected language.

This part remains almost exactly the same as that deployed in the original method.
Of all the editable regions created in any template, some are common to all languages (usually the assets e.g. images, thumbnails, files etc.).
For the actual textual contents, we'll create separate editable regions for each language supported by our site.

So suppose a template originally requires only two regions - one image and the other textarea.
For a normal site (i.e. not multi-lingual), we could define the regions as follows
Code: Select all
    <cms:editable name="photo" type="image" />
    <cms:editable name="content" type="textarea" />

For our tri-lingual site, we'll define the regions as follows instead -
Code: Select all
    <cms:editable name="photo" type="image" />
    <cms:editable name="content_en" type="textarea" />
    <cms:editable name="content_fr" type="textarea" />
    <cms:editable name="content_pt" type="textarea" />

As can be seen, we have defined separate regions for the three languages. The important part is that we have appended the respective language-codes to the names of the different regions. This will make it easy for us to show the correct region matching the currently selected language.
Let us do that.

First input some data into the three editable regions in the admin-panel for testing.
Now at the place in your template where you need to show the content, use the following -
Code: Select all
<cms:get "content_<cms:show k_lang />" />

And that is it!
Switch between the languages and see how the content automatically changes to match the selected language.

By using this addon, that is all that you need to do for creating multi-lingual site -
1. Create editable regions with names containing the language-codes
2. Use <cms:get> to show the contents.

I think you'll agree, this addon makes the original method way easier to implement.

One last thing..
Ok, before we end the topic by turning on prettyURLs, there is one last point that I need to discuss.
As you must have realized, this new method banks upon the presence of the 'language-code' in *all* links present on any given page.
This is so that the visitor is always served pages only in the selected language. To illustrate the point, suppose a visitor selects 'pt' as her language. Now all the links on any page she visits should have 'pt' attached else she could land up on a different language thus breaking her context.

This addon tries to automatically add the language-code to most links on pages. I'd say, it succeeds in doing this for 95% of the links (notably all 'k_page_link' and links generated by <cms:link> tag). However, there are a few links which escape this treatment (primarily because making this addon manipulate those links would mean making sweeping changes to Couch's core).

I cannot provide an exhaustive list of variables containing such links but only a modest amount of testing applied to your fully implemented site should easily reveal those. One thing is certain about them though - all of them will definitely have the term 'link' in their names. So you can keep an eye on such variables while coding also.

For such links, this addon provides a tag named <cms:show_with_lc> that does the conversion for you.
The use is pretty easy. Take a look at the following -
Code: Select all
<cms:if k_paginated_bottom >
    <cms:if k_paginate_link_prev >
        <a href="<cms:show k_paginate_link_prev />">prev</a>
    <cms:if k_paginate_link_next >
        <a href="<cms:show k_paginate_link_next />">next</a>

The code above is some regular code involving pagination. Unfortunately, the 'k_paginate_link_prev' and 'k_paginate_link_next' are language-code unaware. To rectify that, we simply need to modify the '<cms:show >' part to '<cms:show_with_lc>' (i.e. 'show with language-code'). Our modified code becomes -
Code: Select all
<cms:if k_paginated_bottom >
    <cms:if k_paginate_link_prev >
        <a href="<cms:show_with_lc k_paginate_link_prev />">prev</a>
    <cms:if k_paginate_link_next >
        <a href="<cms:show_with_lc k_paginate_link_next />">next</a>

As you can see, it required a simple substitution at only two places.
For more complex pagination needs, do the same with 'k_crumb_link' variable. Please see -

Wrapping up
Once the site is complete and you are satisfied with the way things are working, the final thing to do should be turning on the prettyURLs (that is, if you do want to use those on the site).

Follow the standard way of doing this ( -
first edit couch/config.php to turn prettyURLs on. Then generate rewrite rules for putting into .htaccess file (the bottom of the sidebar in admin-panel now has a link for doing this). Copy and paste those rules into a .htaccess file and place that file in your site's root.

If your site was already using prettyURLs before activating this addon then you'll likely already have a .htaccess file you generated previously.
You'll still need to regenerate the rules and place them into the .htaccess file because this addon will add new rules of its own that have to be placed in the .htaccess file.

The last line is important - using prettyURLs with this multi-lingual addon expects a new set of rules in .htaccess file. So, in the future, if you happen to add a new language or modify the language-code of any existing language, you'll need to regenerate the rules and then update the .htaccess file with them.

With that done, access your site and take a look at the URLs it uses now. You should see something like -
Code: Select all

Notice how this addon has added the language-codes *before* the template names so as to make it appear as if the templates were in subfolders named 'en/' and 'pt/' etc. So, in a way, the URLs now match those that we get using the 'second method' of creating multi-lingual sites.

That ends this post. Hope our members find this addon useful.
Please feel free to ask any questions you might have.
I happened to use this addon on a site recently that had no less than five languages (and also needed the provision for adding more languages in the future easily).

I'd like to share here a couple of techniques I utilized in creating that site.
I am making this as a separate post from the original one above because this adds nothing new to the info given there. It only shows how I used that info in implementing a real-world site. Treat these points as simple tips or even ignore them.

1. Grouping together editable regions by languages in the admin-panel.
Five languages (and more to come) meant a lot many regions. I found it very useful to keep all 'asset' regions (i.e. regions common to all languages) on top ungrouped and then place regions of each language below in separate groups.

Since all the five groups would have identical regions (except, of course, for the names), I used the <cms:each> loop we used in the post above to create the switcher menu to iterate through all configured languages and create the regions.

Please take a look at how I defined the regions in one of the templates -
Code: Select all
<cms:template title='About Us'>
    <cms:editable name='image' label='Background Image' type='image' width='2200' enforce_max='1' show_preview='1' quality='100' preview_width='200' />

    <cms:each k_supported_langs as='lang' key='lc'>
        <cms:editable type='group' name="group_<cms:show lc />" label=lang order=k_count >
            <cms:editable type='text' name="title_<cms:show lc />" label='Title' order='1' />
            <cms:repeatable name="content_<cms:show lc />" label='Content' order='3'>
                <cms:editable name='heading' label='Heading' type='text' col_width='250' />
                <cms:editable name='text' label='Text' type='richtext' /> 

As you can see, a single loop with the judicious use of <cms:show lc /> makes it possible to create regions for all languages easily. Also makes it easy to add new sets of regions for more languages in the future.

As an aside, since now we have already used up groups to place all regions of a language together, if we needed to further group some of those regions we won't be able to use type 'group' editable regions in there (groups don't support nesting).

For such cases, I simply used a type 'message' region as follows to create visual separation between fields, e.g. -
Code: Select all
<cms:editable name="sep_<cms:show lc />" type='message' order='5'>
    <hr/><h3>Contact Form</h3>

I am sure you'll be able to do a better job than me :)

2. The template above was a non-clonable one. Even for clonable templates, the same technique was used with no change.
There was one little optimization that I used with clonable templates, though.

if you take a look at the definition of the non-clonable 'About Us' template above, you'll see that I defined a 'Title' region for each of the five languages. Cloned pages, as I am sure you know, already have a 'title' field predefined (along with the 'name' field). So, with clonable templates, I could use the system title field for the main language (in this case English) and then define custom title fields for the other four.

However, if I did that then at the place where I displayed the title on the front-end, I'd have to first check if the selected language is English - if it was, show k_page_title (the system field) else show the custom field for the other language.

Not too difficult but it felt a bit unwieldy from the single statement we could use if all the languages had similar custom fields -
Code: Select all
<h1><cms:get "title_<cms:show k_lang />" /></h1>

Yet, it couldn't just get rid of the system title field because it needs to be shown prominently immediately above the 'name' field to indicate that its value is being used to create the URL.

So I did this - I created custom title fields for all the five languages (using the same loop as we saw above) but made the custom field within the English group hidden. I then setup some code to ensure that every time a page is saved, the value contained in the system title field silently gets saved automatically into the English custom field. This way the custom EN field always had the same value as the system field and now I could use the unified statement I referred to above.

Here is the code I used, in case the technique interests you -
Code: Select all
<cms:template title='Products' clonable='1'>
    <cms:editable name='image' label='Product Image' type='image' width='1250' height='1250' enforce_max='1' show_preview='1' quality='100' preview_width='200' />

    <cms:each k_supported_langs as='lang' key='lc'>
        <cms:editable type='group' name="group_<cms:show lc />" label=lang order=k_count >
            <cms:editable type='text' name="title_<cms:show lc />" label='Title' />
            <cms:editable type='richtext' name="text_<cms:show lc />" label='Text' />
        <cms:field 'title_en' skip='1'  />

Notice the <cms:config_form_view> block that contains the code to hide the field and then populate it with value from the system field.

Hope these tips help someone.
GREAT !!!!!!!!!!!! THANK YOU, KK !!!!!!!!!!!!!!!!!!!!!!!!!!!
where innovation meets technology
Adding to this appraisal thread - awesome addon and helps a lot. Will definitely spend more time doing multi-lang sites. :mrgreen:

There are now two settings for pretty-urls, one in couch's config and the other in multi-lang config. Can we remove the second one and use global setting instead?
Thanks everyone :) I am glad you found this addon helpful.

@trendoman, you asked -
There are now two settings for pretty-urls, one in couch's config and the other in multi-lang config. Can we remove the second one and use global setting instead?

You are referring to the following setting in this addons config file -
Code: Select all
// Set to 1 to use prettyURLs e.g.
$cfg['prettyurls'] = 1;

Actually that setting is a 'safety catch' of sorts. Allow me to explain -

Since, as you have seen, this addon manipulates the canonical prettyURLs used by Couch (BTW, this is the first time any addon is attempting this), there is an outside chance that some existing feature might break.

To be fair, I didn't come across any such feature in whatever testing I tried to do but one can never be sure after messing with established innards of code.

So, I deliberately added this switch to prevent this addon from making changes to the conventional prettyURLs.

Mind you, this does not mean that if something goes wrong you won't be able to use prettyURLs at all.
It only means that you'll be able to use prettyURLs the way they existed before. This addon will only avoid inserting the language-code within the URL (as it does now). Instead it will fall back to adding the lc as a querystring parameter.

For example, if this is a normal prettyURL link -
Code: Select all

With this addon activated at its default setting, the URL will become this -
Code: Select all

If you turn off prettyURLs within this addon's config, the same link would now become -
Code: Select all

As you can see, we are back to our established prettyURL format and this addon still works by using the querystring parameter.

This way we can be sure that in case something messes up, we can continue using normal prettyURLs with the multi-language part working as well.

Hope that answers your query.
Hi, KK,
i have a problem with default language.
in config.php add i have:

but when i want to visit page from browser, i have default url:
Hi @andrejprus,

When a page is visited and there is a valid 'lc' param in the URL used, the specified language gets chosen,

If, however, that 'lc' param is missing (as can happen on the very first page someone visits), Couch first checks if the visitor's browser has any valid preference set for a language (HTTP_ACCEPT_LANGUAGE) and then uses that language to serve the page.

If the browser does not specify a valid language, Couch then uses the first language set in config for serving the page.

In your case, it seems the browser you are using has EN as the preferred language and so that is being selected.

This behavior is desirable as it serves visitors contents in their own language (instead of using yours).
If, however, you do not want it, you can try commenting out the following line (no. 26) from 'multi-lang.php' -
Code: Select all
if( $lc=='' && isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ){
    $lc = strtolower( substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2) );

I'd also like to know your thoughts (and of our other members) whether this default language selection logic is okay or should we add a setting in config to directly use the first language if none is specified?

Do let me know.
I currently have a multilingual page setup with routing, so I might later migrate to the new plugin, but wanted just to let you know my opinion about the browser language - here in Latvia, all people use english operating systems and english browser locale, but they read the webpages in latvian language. So browser language is not a good way to auto-set the language. I would personally set the default language to a specific one (my main audience) and let the user pick his own, if they want.

I'd also like to know your thoughts (and of our other members) whether this default language selection logic is okay or should we add a setting in config to directly use the first language if none is specified? Look at this post

Do let me know.
I have to agree with normis that most browsers are already set to use English as the preferred language even in non-English regions. I think this is because during installation most users just select the first language on the list which is usually English. I think the default language should be the first language in config since people already know their target audience.
Previous 1, 2, 3, 4, 5 ... 8 Next
73 posts Page 1 of 8