Search

I hope I am not telling things you already know :P

I know there are extensions handling multilingual things, but somehow I felt to try something else.

I'd like to know other opinions on my multilanguage-approach:

  1. Created a super-tiny extension that grabs language-code from URL, validates and adds it to the param pool: $language
  2. Added section with Text-Boxes for each language. Field naming convention was "content-en", "content-..."
  3. Created Datasource that included all relevant fields and as well all fields of all languages.
  4. Added a "dummy"-filter to datasource -> @id = not:{$language} for the sole purpose to get unique results when using Cacheable Datasource Extension.
  5. Made the Datasource custom (allowEditorToParse() -> false), and applied transformations: The result is an XML-Output that removes fields of languages different to the current $language. Renamed the fields of the remaining language from e.g. "content-en" to "content", also moved relevant handles over. 20 Lines code, re-useable for all datasources. So the XML will always output one "content"-node with he current language and handles.

Finally applied XSL without complicated expressions like "*[local-name() = concat('content-', $language)]", just "content".

I made an own multilanguage approach myself purely working with the core.

In the base I have a main language section and a translation section which are associated through the new association field.

In short I got three datasources:

  • ds-main, which output only the system id based on page param (lang/cat/title)
  • ds-translation, which output the the fields in that language based on page param (lang/cat/title) and output the association id from the main language
  • ds-fallback, which output the entry based on the system id of main or the association id

In my template I got a simple choose condition:

  <xsl:choose>
  <!-- CHECK IF THERE IS A TRANSLATION FROM AT LEAST THE BODY TEXT -->
    <xsl:when test="//ds-translation/entry/body !=''">
        <xsl:apply-templates select="//ds-translation/entry" mode="main-article"/>
    </xsl:when>
  <!-- FALLBACK TO MAIN LANGUAGE -->
    <xsl:otherwise>
  <!-- DISPLAY NO TRANSLATION MESSAGE -->
      <xsl:if test="$language != $default-lang or //ds-translation/entry/body =''" >
        <p class="small no-translation">
          <xsl:value-of select="//translations/entry/no-translation" />
        </p>
      </xsl:if>
  <!-- DISPLAY FALLBACK ENTRY -->
      <xsl:apply-templates select="//ds-fallback/entry" mode="main-article"/>
    </xsl:otherwise>
  </xsl:choose>

For the navigation I got a similar appraoch but based on a reference datasource which contains all entries with a bare minimum of the fields.

For the language parameter I have a translation section which is also used for translations of 'system messages' like this entry is not available in your language.

But your approach sounds interesting

I like your way, because it's bare symphony :) Good to know that it's working out of the box as well.

I will think about applying it your way or maybe find a mix between both, because actually I wanted to get rid of XSL-Conditions. They can multiply and spread over all xsl files.

It might be best if you create one template that does all the checks and outputs the right text and simple pass in the field path you want with a call template. Basically a xslt utility :)

This way you limit how many copies of the conditions there are and just use the same modified call template over and over for grabbing text

Did you have a look at Multilingual?

It doesn't do exactly the same things, but it's actually quite similar.

Grabs the current language from the URL and adds it to parameters, uses standard fields for each language, modifies datasource output during runtime...

Did you have a look at Multilingual?
It doesn't do exactly the same things, but it's actually quite similar.

Yes, looked up all your ideas and extensions and used them. It was my first start and base of getting into the-language-topic ;) which is kinda important for scalable websites to me.

I realised that the actual amount of
a.) database-queries
b.) generated xml-length
increases with each language.

As in your extension-description:

<entry>
    <title lang="en" translated="yes" handle="the-extremes-of-good-and-evil">
        The Extremes of Good and Evil
    </title>
    <title lang="de" translated="yes" handle="das-hoechste-gut-und-uebel">
        Das höchste Gut und Übel
    </title>
</entry>

the result can grow very large depending on language count and content.

... Well, now I'm used to edit each datasource, which is a bit work, but the result will always be:

<entry>
    <title handle="(correct-handle)">
        (correct-content)
    </title>
</entry>

and a little bit xpath is avoided, when this:

<xsl:value-of select="title[@lang = $language]"/>

becomes to this:

<xsl:value-of select="title"/>

Since I conditionally add $this->dsParamINCLUDEDELEMENTS in the datasources, only the relevant database-queries are executed. (This contradicts with my 1st post above, I have extended it a bit)

Off-Topic: I'm using symphony since like 1 year and it's really awesome, you guys designed the system very well from the point of a developer. I really love working with it :) I hope to get a lot deeper into the details and can contribute something as well.

the result can grow very large depending on language count and content.

True, see my comments regarding performance here...

I already thought about moving in a direction similar to your approach for multilingual projects.

But...

How do you handle fallbacks for missing translations?

How do you create something like a language switcher, where you need all translations for a particular URL on the same page?

How do you handle fallbacks for missing translations? ... language switcher

A fallback and reference datasource can do the trick. But that was indeed for me the hardest part for an own multilanguage approach

How do you handle fallbacks for missing translations?

Currently, not at all :)
If the client decides for a multilingual-site, he gets it and I think he should care about his contents ;)
I have made all fields "required" for now, so there is no fallback needed.

I think a simple checkbox like "Content is available in $this language" for the client will do the job?
So symphony can turn of contents and build available urls only. That's what I would do.

How do you create something like a language switcher, where you need all translations for a particular URL on the same page?

I never considered this case. Has anyone ever needed that? :)

Currently, not at all :)

Came to the same conclusion. Only encourages laziness and content doesn't get translated at all if the client has the option to "do it later".

I never considered this case. Has anyone ever needed that? :)

How do you switch to another language on your site? oO

How do you switch to another language on your site? oO

1.) created a small xml file with available languages and some extra info like this:

<?xml version="1.0" encoding="UTF-8" ?>
<languages>
    <item code="en" default="yes">
        <title>English</title>
        <date-time-format>D M Y, t</date-time-format>
        <date-format>D M Y</date-format>
    </item>
    <item code="de">
        <title>Deutsch</title>
        <date-time-format>d.n.Y, T</date-time-format>
        <date-format>d.n.Y</date-format>
    </item>
</languages>

I desired a url-format like this:

default language:

http://www.domain.com/something/ or
http://www.domain.com/en/something/

other languages:

http://www.domain.com/de/something/

2.) used URL-Router to translate the language URL-Parameter (directory/symphony-style) to a real argument, it looks like this:

from:

/^/(?:([a-z]{2,2})(?:[/]|$))(?:(.*)[/?]|$)(?:(?:[/?]|$)(.*)|$)$/m

to:

/$2/?language=$1&$3

There is as well a more-precise regexp for this. I am in no way a regexp-expert, so relied on help as well. An expert @ stackoverflow solved this for me:

'~^(?![^Srn]*(?:r|$))(?|(?!/[a-z]{2}[^Srn]*(?:/|(?:r|$)))/?((?:(?!/[a-z]{2}[^Srn]*(?:/|(?:r|$))|?|/[^Srn]*(?:r|$))S)*)|())(?|/([a-z]{2})(?=/|[^Srn]*(?:r|$))|())(?|/((?:(?!/[^Srn]*(?:r|$))[^?s])+)|())(?|/?((?:(?!/[^Srn]*(?:r|$))S)*)|())/?[^Srn]*(?:$|(?=r))~m

Check the topic @ stackoverflow: http://stackoverflow.com/questions/26126877/regexp-language-routing

3.) Anyway: now it's possible to validate a given language-code (or not given) and let symhpony know about it. I've done it with a small extension I wrote. Grab the url-argument, check in xml-file and set a /data/param/language. A valid language-code is available now in datasources:

($clientLanguage = Datasource::findParameterInEnv('language', $this->_env);)

and your xsl-templates as a /data/param

I was surprised symphony did not prevent to let this be done :D

http://www.domain.com/de/something/

So you don't actually translate the URL handles for each language?

Yes, you got me there.

... but its possible with more code-extension, we're already working on the datasource-files anyway.

To achieve translated URLs, the applied filter (-field) must be changed.
Filters can be modified in the datasource execute-method:

$this->dsParamFILTERS['DYNAMIC-FIELD-ID'] = Datasource::findParameterInEnv('PARAM', $this->_env);

DYNAMIC-FIELD-ID = manually look up field-id's for e.g. title-en, title-de
PARAM = your param which you added to the blueprint-page

So we can apply filters to different fields for different languages.
Actually I have no idea if its possible with an existing extension, but inside a datasource it is.

Edit: same applies to dynamic included fields:

array_push($this->dsParamINCLUDEDELEMENTS, 'title-' . $clientLanguage);

So we can apply filters to different fields for different languages. Actually I have no idea if its possible with an existing extension, but inside a datasource it is.

It is... ;)

Create an account or sign in to comment.

Symphony • Open Source XSLT CMS

Server Requirements

  • PHP 5.3-5.6 or 7.0-7.3
  • PHP's LibXML module, with the XSLT extension enabled (--with-xsl)
  • MySQL 5.5 or above
  • An Apache or Litespeed webserver
  • Apache's mod_rewrite module or equivalent

Compatible Hosts

Sign in

Login details