Make SELECT return data in default language when no translation can be found

Asked

Viewed 428 times

1

I’m building a multi-language base.

I have a select with the chosen language, but there is no guarantee that 100% of the content is translated, so I need to do a sort of or to the default language as a secondary alternative.

select ...
 where ...
   and (idioma = 'pt-br' or idioma = 'en-us')

There is a relationship in this query, but that’s beside the point. The problem is that when I have entries in both languages, select always returns the with smaller ID, in case en-us:

id | idioma | texto
1  | en-us  | ...
2  | pt-br  | ...
3  | it     | ...

What I need is to prioritize the desired language, and bring the default only when the desired one is not found.

  • 1

    If I understood what you want to do, I could just give you one ORDER BY id and a LIMIT 1.

  • @Oeslei, ORDER BY language = 'pt-br'?

  • That. I had misread and understood that you wanted the smallest id. But that’s exactly what you put in. But order by doesn’t work that way. There’s a way to specify strings in order by, but I don’t remember now.

  • I’ll test too, I feed soon.

  • ORDER BY language = 'default language' is to solve. It can be != or DESC, if it is only one, or if there are several, you can sort by FIND_IN_SET. If you’re using a programming language to manage SQL, you can even leave the Ids string of a ready-to-use order in a Session (or variable) to avoid JOIN. (something like ORDER BY FIND_IN_SET( id_linguagem_do_post, $string_com_ids_em_ordem de preferencia )

  • Thanks to the @Bacco tip, I’m limited with DB. There are quite a lot of references posted, and I’m seeing one by one - I’ll now see your about FIND_IN_SET. TKS

  • FIND_IN_SET will look something like this, if the desired language is it, then pt and last en-us if you don’t find the previous ones: ORDER BY FIND_IN_SET( id_linguagem_da_postagem, '3,2,1') LIMIT 1 - can be 'it, en, en-us' too, if you want to use with JOIN, but if you use PHP or some other extra language, vc mounts the '3,2,1' once only as soon as the user logs in, and saves in the application for all queries instead of overloading the SGDB to any query.

Show 2 more comments

4 answers

5

If I understand correctly, you have no guarantee of which ID will be returned. It’s hard to give an accurate answer without knowing more about your bank, but I would make a JOIN for content in each language (chosen and default), and a query in this line:

SELECT
   -- ...
   COALESCE(conteudo_en.texto, conteudo_pt.texto) AS texto
FROM paginas pagina
   INNER JOIN conteudos conteudo_pt
   ON conteudo_pt.pagina_id = pagina.id
   AND conteudo_pt.idioma = 'pt-br'
   LEFT OUTER JOIN conteudos conteudo_en
   ON conteudo_en.pagina_id = pagina.id
   AND conteudo_en.idioma = 'en-us'
WHERE pagina.id = 1
  • That’s the problem, is FROM cidade and relationship in state and country to pick up the translation. Grossly: estado.id = cidade.estado, pais.id = estado.id

  • Even a simple queri it returns the smallest id: select * from cidade where idioma = 'en-us' or idioma = 'pt-br' and sid = 1 limit 1 and even changing the order does not interfere with the result. There is no priority in the OR clause?

  • You will need some kind of parole. In the case of my example with JOIN used COALESCE, but it may be the case of making a IF or CASE in the SELECT. There is no priority in the OR (that I know). You can edit the question including the database structure and the complete query?

  • The translation is not in column as conteudo_en, stays in line, so I guess COALESCE won’t do. One suggestion I had on SOEN was to double the Join one for each language, but I think it’s a bit dirty, if I have 5 Join’s I will have 10... I will try to simplify or use a simpler example, because DB is kind of big.

  • In my case, conteudo_en not a column, a table (an alias, actually)... @Papacharlie

  • Sorry, it’s true, I hadn’t noticed. I’ll see if I set your example here to rotate.

  • Inevitably I’ll need another LEFT OUTER JOIN for the default language when the chosen one is not available?

  • 1

    In the pattern you use INNER, and LEFT on the chosen one. But this solution does require both joins, @Papacharlie

  • In this query I will have the return of the languages in EN and PT, and I will use only one (or content_en or content_pt) correct? It is not expensive in large data?

Show 4 more comments

3

You can use a ORDER BY and LIMIT 1, as in the example below:

ORDER BY FIELD(idioma, 'en-us', 'pt-br'), idioma LIMIT 1

In the function values FIELD you put first the language you are searching for and then the default language. More information about the function FIELD.

  • If the chosen language is air or pt, the order in relation to en it will be different... then it would not be without DESC?

  • 1

    @Papacharlie You’re right! I ended up neglecting in copy/Paste, but I’ve updated the answer.

2

The solution with Function would be something of the type (without accurate syntax)

FUNCTION OBTER_PAGINA (PID,PIDIOMA)

 SELECT TEXTO
 FROM   TABELA
 WHERE ID = PID
 AND   IDIOMA = PIDIOMA;

 SE LOCALIZOU RETORNA TEXTO
 SE NAO LOCALIZOU
  SELECT TEXTO
  FROM   TABELA
  WHERE ID = PID
  AND   IDIOMA = 'EN-US;
  SE LOCALIZOU RETORNA TEXTO
  SE NAO LOCALIZOU RETORNA " "

No select something like

SELECT OBTER_PAGINA(1234,'PT-BR') FROM ...

It is not a code EXACTLY but basic idea to work the question, the advantage is that the implementation of the text search is encapsulated in FUNCTION.

  • in this case the query is exactly the same, only changes the language, right? I never used this form, you have some DOC for me to consult?

  • 1

    http://sql-info.de/mysql/examples/create-function-examples.html http://dev.mysql.com/doc/refman/5.1/en/adding-functions.html No, as I don’t work with Mysql, but I think the basic manual will help you.

1

You can search for the desired language (en-us) or the default language (en) case the desired language is not present. For this last condition you can use the clasp not exists about a subquery connected with the main query.

It is not clear to me which column you will rely on to fetch the translation, so I will use a hypothetical table and you can use the technique by adapting the code to your actual table.

Table:

id | idioma | texto_original | texto_traduzido
1  | en-us  | ...            | ...
2  | pt-br  | ...            | ...
3  | it     | ...            | ...

SQL command:

select 
    texto_traduzido
from 
    traduções
where
    (
        texto_original = 'casa'
        and idioma = 'en-us'
    )
    or 
    (
        texto_original = 'casa'
        and idioma = 'pt-br'
        and not exists
        (
            select 
                texto_original
            from 
                traduções as idioma_desejado
            where
                idioma_desejado.texto_original = traduções.texto_original
                and idioma_desejado.idioma = 'en-us'
        )
    ) 

My indentation made the query great but it’s actually quite simple.

  • It’s something simple, there’s no texto_original, only language and text, so you need to find the chosen language or the default, for cases where there is no translation. In the example, the 1st and idioma shouldn’t be PT-BR? I am very shallow with MYSQL.

  • @Papacharlie What is the filter? ID? In this case ID repeats for each language, right? I invented "idiom_original" because I don’t know which filter, besides the language, you will use to select a text.

  • I got the idea.

  • Anyway the reply of @bfavaretto should already solve; I only gave an option. This my query using JOIN would look like this: select coalesce(idioma_desejado.texto_traduzido, idioma_padrao.texto_traduzido) from traduções as idioma_padrao left join traduções as idioma_desejado on idioma_desejado.texto_original = idioma_padrao.texto_original and idioma_desejado.idioma = 'en-us' where idioma_padrao.idioma = 'pt-br' and idioma_padrao.texto_original = 'casa'. If you were unable to apply either, complete your question with the table data and your current query.

  • All help is welcome, I have my eye on them all. The problem of putting all DB is that it is big, it has relationships and I chose to explain it more simply so I could adapt to every need. But if really I need I switch to the nearest and easiest to explain.

  • Many colleagues do not like this type of solution but I would test a FUNCTION that would receive the search parameters and return the string treating this language issue , tests with en en ....

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.