Edellisessä kieliversionnin toteuttamista käsittelevässä jutussa tehtiin canvas Power Apps, jonka käyttöliittymä oli monikielinen. Viime viikolla osallistuin työpaikan hackathoniin, jossa tiimillämme oli tavoitteet korkeammalla. Mietimme konseptia, jolla voisi toteuttaa kieliversioinnin jossa

  • Käyttöliittymän lisäksi myös sisältö on kieliversioitu
  • Uuden kielen lisääminen ei aiheuta muutoksia tietomalliin
  • Samaa ratkaisu toimii mahdollisimman laajasti eri sovelluksissa (canvas Power Apps, model-driven apps, Power Pages ja Power BI)

Toteutuskonsepti Power Pagesista

Kävimme erilaisia toteutusvaihehtoja aikamme läpi. Päädyimme lopulta lainaamaan idean Power Pagesista. Siellä sivustolle määritellään tuettavat kielet.

Tuettujen kielten perusteella kaikkiin kieliversioitaviin elementteihin ilmestyy oma kenttä per kieli.

Miten tämä on toteutettu? Käytännössä sarakkeen eri kieliversiot tallennetaan JSON-muodossa yhteen sarakkeeseen. Käyttöliitttymässä kukin kieli vain esitetään omissa kentissään. Kun lisäämme uuden tuettavan kielen, ilmestyy sille oma kenttä automaattisesti lomakkeelle.

Käytetään nyt tätä samaa menetelmää canvas Power Appsin kieliversiointiin. Sekä käyttöliittymäelementtien että varsinaisen sisällön. Ratkaisun kauneus piilee siinä että nyt kieliversioita ylläpidetään samalla tavalla model-driven appsin, canvas appsin, että Power Pagesin osalta.

Canvas Power Appsin käyttöliittymäelementtien kieliversiointi

Aloitetaan helpoimmasta eli käyttöliittymäelementtien kieliversioinnista. Luodaan niitä varten oma taulu (UI Strings). Taulussa on kaksi kenttää

  • Sisäinen nimi (code), jolla teksti haetaan
  • Käännökset (translations), johon kieliversiot tallennetaan kielikoodi (LCID) ja arvo (Value) pareina

Käyttöliittymätekstien ylläpito näyttää model-driven sovelluksessa seuraavalta. JSON:in sisältävä Translations-kenttä on esillä vain havainnollisuuden vuoksi. Oikeasti se piilotettaisiin.

Translations-kentän JSON puretaan lomakkeella omiksi kentiksiin web resurssin avulla.

Käännösten toteuttaminen funktiolla

Aloitetaan lisäämällä käyttöliittymään kielivalinta.

Luodaan kieliversioille vakio (constUIStrings), jonne UI Strings -taulussa olevat arvot luetaan. Näin jokainen käännöksen hakeminen ei muodosta kutsua Dataverseen.

Sitten varsinaiseen työhön. Luodaan uusi komponentti käännöksiä varten (cmpLanguages). Lisätään sille funktio-tyypinen ominaisuus UIStrings, jolle annetaan parametreina haettava käyttöliittymäteksti (UIString) sekä käytettävä kieli (LanguageCode). Funktion arvo on sitten haettavan tekstin oikea kieliversio.

Paluuarvo, eli UIStrings-ominaisuuden arvo, muodostetaan seuraavasti.

With(
    {
        MyJSON: ForAll(
            Table(
                ParseJSON(
                    LookUp(
                        constUIStrings,
                        Code = UIString
                    ).Translations
                )
            ),
            {
                LCID: Text(ThisRecord.Value.LCID),
                Value: Text(ThisRecord.Value.Value)
            }
        )
    },
    Coalesce(
        LookUp(
            MyJSON,
            LCID = LanguageCode
        ).Value,
        LookUp(
            MyJSON,
            LCID = "1033"
        ).Value
    )
)

Käytännössä:

  • Haetaan constUIStrings kokoelmasta oikea termi (esim Save)
  • Muodostetaan sen translations-kenttään tallennetusta tyypittämättömästä JSON:sta tyypitetty JSON
  • Haetaan muodostetusta JSON:sta oikeankielinen teksti. Mikäli sitä ei löydyn palautetaan englanninkielinen versio (kielikoodi 1033).

Funktiota käytetään kaikkialla missä käyttöliittymäelementeissä esitetään tekstiä. Alla haetaan painikkeelle otsikko.

Näin teksti vaihtuu kieltä vaihdettaessa.

Tämä oli se helppo osuus. Seuraavaksi siihen hankalampaan.

Canvas Power Appsin sisällön kieliversiointi

Nyt kaikki sisältö tuotetaan usealla kielellä. Alla esimkerkki miltä teema ja sen kuvaus näyttää model-drien appsissa kolmella kielellä.

Tämä sisältö tulee esittää fiksusti myös canvas Power Appsissa. Osaa sisällöstä tulisi voida myös päivittää siellä.

Käännösten toteuttaminen funktiolla

Aloitetaan tekemällä funktio (Translate), joka saa parametrina sisältötekstin (esimerkiksi tuotteen nimen) kieliversiot JSON-formaatissa (se Translations-kenttä) sekä halutun kielen kielikoodin.

Funktion arvo on lähes sama kuin edellisenkin. Tällä kertaa saamme suoraan parametrina halutun tekstin käännökset JSON-muodossa (UITitleAsJSON). Koodi on näin hieman lyhyempi.

With(
    {
        MyJSON: ForAll(
            Table(ParseJSON(UITitlesAsJSON)),
            {
                LCID: Text(ThisRecord.Value.LCID),
                Value: Text(ThisRecord.Value.Value)
            }
        )
    },
    Coalesce(
        LookUp(
            MyJSON,
            LCID = LanguageCode
        ).Value,
        LookUp(
            MyJSON,
            LCID = "1033"
        ).Value & " en"
    )
)

Nyt voimme kääntää lennossa esimerkiksi galleriassa näytettävää sisältöä.

Valintalistojen taustalla olevaan taluun lisäämme uuden sarakkeen (UIDisplayName), jonka arvona on käännetty teksti. Ja tämän näytämme sitten käyttöliittymässä.

Monikielisen sisällön muokkaaminen

Lopuksi vielä hankalin, eli monikielisten sisältöjen muokkaaminen.

Tehdään jälleen uusi funktio (UpdateTranslation). Se saa parametrinaan

  • JSON muotoisen tekstin, joka sisältää nykyiset käännökset
  • Päivitetyn arvon
  • Päivitettävän kieliversion kielikoodin

Funktio palautttaa päivitetyn JSON:in.

With(
    {
        varMyJSON: ForAll(
            Table(ParseJSON(CurrentUIStringJSON)),
            If(
                ThisRecord.Value.LCID = LanguageCode,
                {
                    LCID: Text(ThisRecord.Value.LCID),
                    Value: Text(UpdatedValue)
                },
                {
                    LCID: Text(ThisRecord.Value.LCID),
                    Value: Text(ThisRecord.Value.Value)
                }
            )
        )
    },
    JSON(varMyJSON)
)

Viimeiseksi teemme vielä funktion (TranslationExists), joka tarkistaa löytyykö annetulla kielikoodilla jo kieliversio. Parametrina funktio saa

  • JSON muotoisen tekstin, joka sisältää nykyiset käännökset
  • Kieliversion kielikoodin

Funktion paluarvo on true tai false.

With(
    {
        varMyJSON: ForAll(
            Table(ParseJSON(UIStringJSON)),
            {
                LCID: Text(ThisRecord.Value.LCID),
                Value: Text(ThisRecord.Value.Value)
            }
        )
    },
    !IsBlank(
        LookUp(
            varMyJSON,
            LCID = LanguageCode
        )
    )

Miten näitä funktioita käytetään?

Sovelluksessamme on lomake, jolla muokataan otsikon arvoa. Otsikon kieliversiot ovat tallennettu Name Translations -kenttään, joka on nyt näkyvissä vain havainnollistamaan mitä tapahtuu. Nimi-kenttää ei lomakkeella siis päivitetä, ainoastaan Name Translations -kenttää.

Käyttäjän valitessa kielen, haetaan translate-funktiolla nimi-kenttään oikea kieliversio.

Lomakkeeen tallennuksen yhteydessä rakennetaan Name Translations-kentän arvo aina uudelleen. Mikäli kieliversio löytyy jo valmiiksi, päivitetään sen arvo UpdateTranslation funktiolla. Mikäli ei, lisätään JSON:in perään puuttuva kieli arvoineen.

Koodina tämä näyttää seuraavalta.

If(
    cmpLanguages_1.TranslationExists(
        ThisItem.'Name Translations',
        rdSelectedLanguage.Selected.Value
    ),
    
    cmpLanguages_1.UpdateTranslation(
        ThisItem.'Name Translations',
        inpLabel.Text,
        rdSelectedLanguage.Selected.Value
    ),
        Left(
            ThisItem.'Name Translations',
            Len(ThisItem.'Name Translations') - 1
        ) & ",{""LCID"":"""& rdSelectedLanguage.Selected.Value &""",""Value"":"""& inpLabel.Text&"""}" & "]"
)

Olet oikeassa. Koko komeus olisi siistimpää paketoida UpdateTranslation -funktion sisään. Siinä tuli kuitenkin ongelmia, joita ei hackathonin puitteissa ollut järkeä lähteä ratkomaan. Pahoittelut.

Yhteenveto

Nyt meillä on sovellus, jonka kielisyyden voi vaihtaa jolloin sekä käyttöliittymä että sisällöt kääntyvät. Käytettäviä kieliä voi lisätä ilman muutoksia koodin tai tietomalliin. Samaa konseptia voi käyttää canvas Power Appsissa, model-driven appsissa (sisältö), Power Pagesissa sekä soveltuvin osin Power BI:ssä (sisältö).

Mutta on tässä vielä tekemistä. Esimerkiksi:

  • Model-driven appsin name-kenttää käytetään näkymissä ja valintalistoissa. Saadaanko se helposti kääntymään lennossa, käytetäänkö aina esim englanninkielistä käännöstä vai miten toimitaan?
  • Miten saadaan monipuolisemman tekstisisällön (Rich text -kentät) kieliversiointi toimimaan?
  • Mikäli canvas appsin tallennus sotkee translations-kentän JSON:in, lakkaa rivin kyseisen kentän käännökset toimimasta. Eli tarkkana tiettyjen merkkien kanssa.
  • Suorituskyky. Jos canvasin näytöllä haetaan kieliversioita tyypittämättömästä JSON:sta vaikkapa sadassa eri paikassa, näkyykö se suorituskyvyssä?

Kaikesta huolimatta tämä oli todella hauska aihe työstää mukavssa porukassa!