angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

Verfijnen van meertaligheid: toevoegen van een taaluitval als optie

Wanneer de taal fallback is uitgeschakeld, wordt het item niet getoond als de vertaling niet beschikbaar is.

11 september 2019
post main image
unsplash.com/@miabaker

In een vorige post beschreef ik de eerste versie van de meertalige database die op deze website werd gebruikt. Voor elke tabel met velden die vertaald moeten worden, voegen we een 'vertaling' tabel met deze velden toe. Ik heb ook een fallback in de taal geïmplementeerd: als een item, zoals een blogbericht, niet bestaat in de geselecteerde taal, dan wordt het item van de (systeembrede) standaardtaal getoond. Dit werkt prima, maar nu wil ik een optionele uitwijkmogelijkheid toevoegen waardoor sommige berichten alleen in het Spaans, andere alleen in het Frans, etc. verschijnen.

We kunnen dit eenvoudig implementeren door de uitwijktaal te veranderen in een onbekende/niet-bestaande taal. Als we nu een post in het Spaans toevoegen, is de post in andere talen niet beschikbaar, precies wat we willen. Het enige probleem is dat we nu geen uitwijktaal meer hebben. Is dit een probleem?

Overweeg het volgende geval. We hebben een post in het Spaans, en we willen dat de niet-bestaande post in het Frans terugvalt naar de standaard taal Engels. Maar we willen niet dat de niet-bestaande post in het Duits terugvalt naar de standaardtaal. Dit is met de huidige implementatie niet mogelijk. Hoe vaak zal zo'n aandoening zich voordoen? Niet vaak, maar ik kan me voorstellen dat het kan gebeuren, en ik wil me hierop voorbereiden. Dus hoe implementeren we dit?

Enkele uitwijkmogelijkheden

Er zijn vele uitwijkmogelijkheden voor een meertalige database mogelijk. We hebben altijd ergens een schakelaar nodig die zegt:

  • terugval op het standaard taalitem als er geen vertaling aanwezig is voor de geselecteerde taal
  • geen terugval, wat betekent dat dit item niet beschikbaar is in de geselecteerde taal.

Voor een uitwijkmogelijkheid hebben we opties zoals:

  • recorddaling, bv. titel, beschrijving, enz.
  • een reserve op veldniveau, bijvoorbeeld de titel van het veld

We kunnen het ook hebben:

  • een enkele uitwijking, bv. er is maar één uitwijktaal...
  • recursieve uitwijking, bv. als deze niet bestaat in het Portugees, als deze niet bestaat in het Spaans, als deze niet bestaat in het Engels.

De fallback taal kan dat zijn:

  • systeembreed
  • op recordniveau
  • op veldniveau

Zonder in detail te treden kunt u zich voorstellen dat we door het implementeren van maximale flexibiliteit, alle opties, een zeer complex systeem gaan creëren dat complexe query's vereist. Het kan zelfs nodig zijn om een complexe preprocessor te bouwen voor de front-end om langzame paginaweergaven te voorkomen vanwege alle taaloptieverwerking.

Keuzes maken

Terug naar de basis. Ik wil voor deze website:

  • Ondersteuning voor meerdere talen
  • Er is een systeembrede standaardtaal beschikbaar
  • Er is een systeembrede fallback taal...
  • De standaardactie moet een taaluitval zijn.
  • Per taalitem moet het mogelijk zijn om de taaluitval uit te schakelen, waardoor het item niet beschikbaar is als het niet gepubliceerd is.

Ik heb de volgende talen gedefinieerd:

  • language_selected: gebruikte taal
  • language_default: de standaardtaal, een statische instelling voor het hele systeem.
  • language_fallback: de fallback taal, een dynamische systeembrede instelling gebaseerd op de geselecteerde taal.

Meestal zal de uitwijktaal de standaardtaal zijn, maar in de toekomst kunnen we deze ook veranderen naar een andere taal:

Voorbeeld 1: Fr-Be terugval naar fr-FR

  • standaard taal: en-US
  • geselecteerde taal: fr-BE
  • terugvaltaal: fr-FR

Voorbeeld #2: es-ES met terugval naar en-GB

  • standaard taal: en-GB
  • geselecteerde taal: es-ES
  • fallback taal: en-GB

Uitvoering

Ik wil dat de queries het systeem niet vertragen, maar als ze dat wel doen kunnen we altijd 'query resultaat caching' en/of query preprocessing toevoegen.

Op dit moment zijn er twee inhoudsopgave tabellen:

  • Inhoud Item
  • InhoudItemVertaling van het artikel

We beginnen altijd met het selecteren van een of meer records uit ContentItem. Als het ContentItemTranslation-record voor de geselecteerde taal niet aanwezig (of gepubliceerd) is voor de geselecteerde taal, dan moeten we terugvallen, maar alleen als de terugval is ingeschakeld voor dit item. Tijd om te kijken naar de query die gebruikt is om de items op te halen. We onderscheiden twee mogelijkheden:

  • ContentItemTranslation record bestaat niet.
  • InhoudItemVertaling record bestaat

In beide gevallen kunnen we een join of union doen, het eerste deel krijgt het record voor de geselecteerde taal en het tweede deel krijgt de taal voor de standaard taal. Het probleem is hoe we de terugval kunnen uitschakelen. Als het ContentItemTranslation record bestaat, kunnen we hier een vlag 'do_not_fallback' plaatsen, maar wat als het ContentItemTranslation record niet bestaat? Dan hebben we ergens een andere taalspecifieke vlag nodig en opnieuw kan deze vlag al dan niet bestaan.

Een manier om dit te doen is nog een tabel toe te voegen ContentItemTranslationDoNotFallback met slechts één vlag = do_not_fallback. Net als bij ContentItemTranslation, kunnen ContentItemTranslationDoNotFallback records wel of niet aanwezig zijn. Dan hebben we drie tafels:

  • Inhoud Item
  • InhoudItemVertaling van het artikel
  • InhoudItemTranslationDoNotFallback

We willen dat de standaardactie terugvalt naar de uitwijktaal als het 'vertaalbestand niet bestaat'. Dit ziet er allemaal vrij eenvoudig uit. Maar dit introduceert ook een extra record van een nieuwe tabel die al dan niet aanwezig kan zijn.

Een andere manier om te bereiken wat we willen is het toevoegen van de 'do_not_fallback' vlag aan het vertaalrecord. Ik bedoel, als we een record moeten toevoegen aan een nieuwe tabel ContentItemTranslationDoNotFallback om 'do_not_fallback' aan te geven, waarom dan niet gebruik maken van de ContentItemTranslation tabel record voor dit doel? Als het niet bestaat, voegen we het toe, anders gebruiken we het gewoon.

In dit geval is de standaardactie nog steeds een terugval naar de uitwijktaal als de 'vertaling' record niet bestaat. We voegen nu een vertaalrecord in, niet alleen wanneer we een vertaalwaarde hebben, maar ook wanneer we een uitwijkmogelijkheid voor dit record willen hebben. In het vertaalrecord gebruiken we de gepubliceerde / actieve vlag om aan te geven of de vertaling bestaat. De onderstaande tabel toont de acties voor de geselecteerde taal.

Tabel
InhoudItemItemVertaling
InhoudItemVertaling
gepubliceerd
InhoudItemTranslation
do_not_fallback
Actie
1 Bestaat niet - - Geen vertaalde waarde, uitwijking, terugval
2 Bestaat Waar Waar of onwaar Gebruik vertaalde waarde
3

Bestaat

Vals Vals Geen vertaalde waarde, uitwijking, terugval
4 Bestaat Vals Waar Geen vertaalde waarde, geen terugval, item niet beschikbaar

SQL vraag

Hieronder staat een mogelijke SQL query implementatie. Merk op dat ik gebruik maak van SELECT's en UNION hier die zorgen voor een eenvoudige selectie van objecten bij het gebruik van een SQLAlchemy query. In de huidige implementatie heb ik twee vlaggen, verwijderde en status, die allebei vals moeten zijn. content_item_parent_id = 0 betekent het hoofdvertaalrecord.

SET @language_fallback_id = 1;
SET @language_selected_id = 2;

### [1] = fallback: translation record does not exist

(
SELECT 
  ci.id ci_id, cit.id cit_id, cit.title cit_title
FROM content_item ci, content_item_translation cit
WHERE 
  ci.content_item_type = 1
  AND ci.content_item_parent_id = 0
  AND ci.published = 1
  AND cit.content_item_id = ci.id
  AND cit.content_item_parent_id = 0
  AND cit.published = 1
  AND cit.language_id = @language_fallback_id
  AND NOT EXISTS
    (
    # return 1 when exists
    SELECT 1 
    FROM content_item_translation cit2
    WHERE 
          cit2.content_item_id = ci.id
      AND cit2.content_item_parent_id = 0
      AND cit2.language_id = @language_selected_id
      )
)

UNION ALL

### [2] = use translation: translation record exists and published = True

(
SELECT 
  ci.id ci_id, cit.id cit_id, cit.title cit_title
FROM content_item ci, content_item_translation cit
WHERE 
  ci.content_item_type = 1
  AND ci.content_item_parent_id = 0
  AND ci.published = 1
  AND cit.content_item_id = ci.id
  AND cit.content_item_parent_id = 0
  AND cit.published = 1
  AND cit.language_id = @language_selected_id
)

UNION ALL

### [3] = fallback: translation record exists and published = False & do_not_fallback = False

(
SELECT 
  ci.id ci_id, cit.id cit_id, cit.title cit_title
FROM content_item ci, content_item_translation cit
WHERE 
  ci.content_item_type = 1
  AND ci.content_item_parent_id = 0
  AND ci.published = 1
  AND cit.content_item_id = ci.id
  AND cit.content_item_parent_id = 0
  AND cit.published = 1
  AND cit.language_id = @language_fallback_id
  AND EXISTS
    (
    # return 1 when published = False & do_not_fallback = False
    SELECT 1 
    FROM content_item ci2, content_item_translation cit2
    WHERE 
          ci2.id = ci.id
      AND ci2.content_item_type = 1
      AND ci2.content_item_parent_id = 0
      AND ci2.published = 1
      AND cit2.content_item_id = ci2.id
      AND cit2.content_item_parent_id = 0
      AND cit2.published = 0
      AND cit2.do_not_fallback = 0
      AND cit2.language_id = @language_selected_id
      )
)

ORDER BY ci_id DESC;

Voorlopig heb ik namelijk SQLAlchemy besloten om raw sql te gebruiken om het aantal en de id's voor paginering op te vragen en deze id's vervolgens te gebruiken om de objecten te selecteren met behulp van een tweede query. Ja, ik ben het een beetje beu om SQLAlchemy steeds te moeten vertalen SQL , het kost veel tijd en waarvoor? Toch geloof ik dat het gebruik van ORM code veel kan helpen bij het verminderen van code, maar niet altijd. Dus het gaat erom het op de juiste manier te gebruiken.

Beheerder

Uiteraard moet de beheerder de status van de contentitems kunnen bekijken. De status van het vertaalde content item is per taal als volgt aangegeven:

Gepubliceerd

Niet gepubliceerd, uitwijkmogelijkheid

Niet gepubliceerd, geen uitwijkmogelijkheid, niet beschikbaar.

Samenvatting

We hebben de optie 'do_not_fallback' toegevoegd aan de inhoud van onze database. Dit bracht meer complexiteit met zich mee, maar de extra overhead ziet er niet echt enorm uit. Vergeleken met de situatie waarin we altijd terugvielen naar de standaardtaal, zijn de veranderingen:

  • De telling van de records (items) is niet langer de telling van het aantal records voor de standaardtaal.
  • Wanneer we een item bekijken en de taal overschakelen naar een niet-fallback-item zonder vertaling, dan moeten we laten zien dat het item niet beschikbaar is in de geselecteerde taal.
  • De 'meest bekeken' (blog posts) functie, de 'zoek' (blog posts) functie, etc. moet nu ook filteren op niet-beschikbare items.

Wederom veel werk, maar ik heb het gevoel dat dit nu de meertalige functionaliteit is die ik voor de rest van dit project kan gebruiken.

Links / credits

Fallback
https://www.i18next.com/principles/fallback

Language Fallback
http://sitecore-masters.com/en/language-fallback/

Multi Language i18n & Localization in Pimcore
https://pimcore.com/docs/5.x/Development_Documentation/Multi_Language_i18n/index.html

Lees meer

Multilanguage

Laat een reactie achter

Reageer anoniem of log in om commentaar te geven.

Opmerkingen

Laat een antwoord achter

Antwoord anoniem of log in om te antwoorden.