# stranslate

#### Sigbert Klinke

sigbert@hu-berlin.de

#### 28 Januar, 2024

# General

In R there are several ways to support multilingual messages:

- the standard R tools with `po` files, and
- the supporting libraries `potools` and `poio`.

You can access translation services that usually require a paid account, such as

- `deeplr` - a wrapper for the `DeepL` Pro API,
- `googleLanguageR` - calls Google’s ‘Natural Language’ API, ‘Cloud Translation’ API and so on, and
- `gtranslate` - translating between different languages (which does not require a Google account).

There are also key-based approaches such as the `translated` library, which is based on JSON files. The `stranslate` package also uses a key-based approach, but is based on plain text files (as a lazy guy, I don’t want to follow the JSON typing rules ;).

# Using `stranslate`

## A first example

The first step is to load one or more files with translations

    library("stranslate")
    file <- system.file("messages", "messages.txt", package="stranslate")
    loadMsg(file) # load the translation(s)

and to show what has been loaded

    listMsg()     # show all loaded keys and domains
    #>
    #>  Current domain  : domain
    #>  Current language: lang -> en
    #>
    #>  Domain: d
    #>  Language: l
    #>       COPYRIGHT, LANGUAGE, DOMAIN, CURRENT_LANGUAGE, CURRENT_DOMAIN
    #>       MISSING_KEYS, UNDEFINED_KEYS, KEY_NOT_FOUND, KEY_LENGTH, DOMAIN_UNIQUE
    #>       lang, key
    #>  Language: l
    #>       COPYRIGHT, LANGUAGE, DOMAIN, CURRENT_LANGUAGE, CURRENT_DOMAIN
    #>       MISSING_KEYS, UNDEFINED_KEYS, KEY_LENGTH, KEY_NOT_FOUND, DOMAIN_UNIQUE
    #>       lang, key
    #>
    #>  Domain: d
    #>  Language: l
    #>       ROUND, PHOTO, COUNT, STREAM, user, gender
    #>  Language: l
    #>       ROUND, PHOTO, COUNT, STREAM, user, gender

We can access from the `default` domain some messages

    getMsg(ROUND=3)
    #> [1] "Round your result to 3 decimal places"
    getMsg(ROUND=3, .lang="de")
    #> [1] "Runden Sie ihr Ergebnis auf 3 Nachkommastellen"
    getMsg(ROUND=1)
    #> [1] "Round your result to one decimal place"
    getMsg(ROUND=1, .lang="de")
    #> [1] "Runden Sie ihr Ergebnis auf eine Nachkommastelle"

Instead of `ROUND` you could use `'ROUND'`, especially if you write a package. Since English is the fallback language, an English translation must be there.

And a multi-line example:

    loadMsg(system.file("messages", "Rdewiki.txt", package="stranslate")) # load the translation(s)
    getMsg("Rdewiki", .lang="en")
    #> [1] "R is a free programming language for statistical calculations and graphics. It\nwas newly developed in 1992 by statisticians for users with statistical tasks.\nThe syntax is based on the programming language S, with which R is largely\ncompatible, and the semantics are based on Scheme. As a standard distribution,\nR is supplied with an interpreter as a command line environment with reduced\ngraphical buttons. Thus R is currently available on the most important platforms;\nthe environment is also explicitly referred to as R by the developers. R is part\nof the GNU project. RStudio is also offered as an integrated development\nenvironment and to increase R's user-friendliness.\nTranslated with https://www.deepl.com/translator"
    getMsg(Rdewiki, .lang="de")
    #> [1] "R ist eine freie Programmiersprache für statistische Berechnungen und Grafiken.\nSie wurde 1992 von Statistikern für Anwender mit statistischen Aufgaben neu\nentwickelt. Die Syntax orientiert sich an der Programmiersprache S, mit der R\nweitgehend kompatibel ist, und die Semantik an Scheme. Als Standarddistribution\nwird R mit einem Interpreter als Kommandozeilenumgebung mit reduzierten\ngrafischen Schaltflächen angeboten. So ist R aktuell auf den wichtigsten\nPlattformen verfügbar; die Umgebung wird von den Entwicklern ausdrücklich\nebenfalls als R bezeichnet. R ist Teil des GNU-Projekt. Als integrierte\nEntwicklungsumgebung und um die Benutzerfreundlichkeit von R zu erhöhen, wird\naußerdem RStudio angeboten.\nQuelle: https://de.wikipedia.org/wiki/R_(Programmiersprache)\n"

## A more complex example

As you could see a key can have a parameter, also additional parameters are allowed. The current example is taken from Project fluent.

    getMsg(PHOTO=5, user="Anne", gender="female")
    #> [1] "Anne added 5 new photos to her stream"
    getMsg(PHOTO=5, user="Anne", gender="female", .lang="de")
    #> [1] "Anne fügt 5 neue Fotos ihrem Stream zu"
    getMsg(PHOTO=0, user="Bert", gender="male")
    #> [1] "Bert added 0 new photos to his stream"
    getMsg(PHOTO=1, user="Bert", gender="male", .lang="de")
    #> [1] "Bert fügt ein neues Foto seinem Stream zu"

## Languages

If you want to know which languages will be used then use `language`.

    # use German spoken in South Tirol (Italy)
    # but available onby standard german and german spoken in Austria
    language('de_IT', c('de', 'de_AT'))
    #> [1] "de" "en"

Since `de_IT` is not available, de is used first. If a key is not available in `de`, `en` is used. This is the reason why you need to provide an English translation. Although all languages are equal, English is particularly important in R. Most commands and functions are based on English words.

## Domains

A domain separates messages in different subsets. There are two domains:

- `stranslate` used by the package `stranslate` and
- `default` the user domain.

If you do not want to load messages into the default domain, use

    loadMsg(file, .domain="mydomain") # load the translation(s)
    listMsg()
    #>
    #>  Current domain  : domain
    #>  Current language: lang -> en
    #>
    #>  Domain: d
    #>  Language: l
    #>       COPYRIGHT, LANGUAGE, DOMAIN, CURRENT_LANGUAGE, CURRENT_DOMAIN
    #>       MISSING_KEYS, UNDEFINED_KEYS, KEY_NOT_FOUND, KEY_LENGTH, DOMAIN_UNIQUE
    #>       lang, key
    #>  Language: l
    #>       COPYRIGHT, LANGUAGE, DOMAIN, CURRENT_LANGUAGE, CURRENT_DOMAIN
    #>       MISSING_KEYS, UNDEFINED_KEYS, KEY_LENGTH, KEY_NOT_FOUND, DOMAIN_UNIQUE
    #>       lang, key
    #>
    #>  Domain: d
    #>  Language: l
    #>       ROUND, PHOTO, COUNT, STREAM, user, gender
    #>  Language: l
    #>       ROUND, PHOTO, COUNT, STREAM, user, gender
    #>
    #>  Domain: d
    #>  Language: l
    #>       ROUND, PHOTO, COUNT, STREAM, Rdewiki, user, gender
    #>  Language: l
    #>       ROUND, PHOTO, COUNT, STREAM, Rdewiki, user, gender

If you want to use your domain as the default domain, do the following

    options(stranslate.domain='mydomain')

of use the `.domain='mydomain'` parameter in `getMsg`.

# Making translations

## Create translations in a `txt` file

In the translation file is the first character in a line important:

- If the first character is `#` then a one line comment starts.
- If the first character is `<` then you are changing to this language.
- If the first character is a alphanumeric character, a dot or a underscore then a key starts.
- If the first character is a whitespace then a continuation line starts.
- If the first character is `?` then condition on the key starts.

    # switch to german
    < de
    # start key with its default message, note that `r ROUND` is replaced by its value
    ROUND  Runden Sie ihr Ergebnis auf `r ROUND` Nachkommastellen
    # if ROUND==0 then use this message
    ?0     Runden Sie ihr Ergebnis auf eine ganze Zahl
    # if ROUND==1 then use this message
    ?1     Runden Sie ihr Ergebnis auf eine Nachkommastelle
    # otherwise use the default message

ATTENTION: All comparison are character comparisons. If in doubt, make sure you convert your parameters yourself, e.g. `getMsg(ROUND=as.character(3))`.

As you can seem we use the Rmarkdown inline notation for R and internally is `knitr::knit` is used. This allows calculations with the parameters.

ATTENTION: Neither R code chunks nor other engines than R are supported.

Other parameters given to `getMsg`, e.g. `getMsg(ROUND=3, user='Anne')` can be used in a message, e.g.

    ROUND  `r user` runde dein Ergebnis auf `r ROUND` Nachkommastellen

You can also call another key in a message with a specific value, e.g.

    PHOTO   `r user` added `r COUNT(PHOTO)` to `r STREAM(gender)` stream
    COUNT   `r PHOTO` new photos
    ?1      a new photo
    STREAM  their
    ?male   his
    ?female her

## Create translations in R

    setLang('en', "R")   # choose language and another domain
    setMsg('ROUND'='Round your result to `r ROUND` decimal places',
           '0'='Round your result to an integer',
           '1'='Round your result to one decimal place')
    setMsg(PHOTO='`r user` added `r COUNT(PHOTO)` to `r STREAM(gender)` stream')
    setMsg(COUNT='`r PHOTO` new photos', '1'='a new photo')
    setMsg(STREAM='their', 'male'='his', 'female'='her')
    #
    setLang('de', 'R')
    setMsg(ROUND='Runden Sie ihr Ergebnis auf `r ROUND` Nachkommastellen',
           '0'='Runden Sie ihr Ergebnis auf eine ganze Zahl',
           '1'='Runden Sie ihr Ergebnis auf eine Nachkommastelle')
    setMsg(PHOTO='`r user` fügt `r COUNT(PHOTO)` `r STREAM(gender)` Stream zu')
    setMsg(COUNT='`r PHOTO` neue Fotos', '1'= 'ein neues Foto')
    setMsg(STREAM='seinem', 'female'='ihrem')
    #
    getMsg(ROUND=3, .domain="R")
    #> [1] "Round your result to 3 decimal places"

# Translation files in `stranslate`

## `messages.txt`

    < de
    ROUND  Runden Sie ihr Ergebnis auf `r ROUND` Nachkommastellen
    ?0     Runden Sie ihr Ergebnis auf eine ganze Zahl
    ?1     Runden Sie ihr Ergebnis auf eine Nachkommastelle

    PHOTO   `r user` fügt `r COUNT(PHOTO)` `r STREAM(gender)` Stream zu
    COUNT   `r COUNT` neue Fotos
    ?1      ein neues Foto
    STREAM  seinem
    ?female ihrem

    < en
    ROUND   Round your result to `r ROUND` decimal places
    ?0      Round your result to an integer
    ?1      Round your result to one decimal place

    PHOTO   `r user` added `r COUNT(PHOTO)` to `r STREAM(gender)` stream
    COUNT   `r COUNT` new photos
    ?1      a new photo
    STREAM  their
    ?male   his
    ?female her

## `Rdewiki.txt`

    < de
    Rdewiki R ist eine freie Programmiersprache für statistische Berechnungen und Grafiken.
            Sie wurde 1992 von Statistikern für Anwender mit statistischen Aufgaben neu
            entwickelt. Die Syntax orientiert sich an der Programmiersprache S, mit der R
            weitgehend kompatibel ist, und die Semantik an Scheme. Als Standarddistribution
            wird R mit einem Interpreter als Kommandozeilenumgebung mit reduzierten
            grafischen Schaltflächen angeboten. So ist R aktuell auf den wichtigsten
            Plattformen verfügbar; die Umgebung wird von den Entwicklern ausdrücklich
            ebenfalls als R bezeichnet. R ist Teil des GNU-Projekt. Als integrierte
            Entwicklungsumgebung und um die Benutzerfreundlichkeit von R zu erhöhen, wird
            außerdem RStudio angeboten.
            Quelle: https://de.wikipedia.org/wiki/R_(Programmiersprache)

    < en
    Rdewiki R is a free programming language for statistical calculations and graphics. It
            was newly developed in 1992 by statisticians for users with statistical tasks.
            The syntax is based on the programming language S, with which R is largely
            compatible, and the semantics are based on Scheme. As a standard distribution,
            R is supplied with an interpreter as a command line environment with reduced
            graphical buttons. Thus R is currently available on the most important platforms;
            the environment is also explicitly referred to as R by the developers. R is part
            of the GNU project. RStudio is also offered as an integrated development
            environment and to increase R's user-friendliness.
            Translated with https://www.deepl.com/translator

## `stranslate.txt`

    < de
    COPYRIGHT        (C) 2023+ S. Klinke, Humboldt-Universität zu Berlin
    LANGUAGE         Sprache: `r LANGUAGE`
    DOMAIN           Domäne : `r DOMAIN`
    CURRENT_LANGUAGE Aktuelle Sprache: `r CURRENT_LANGUAGE`
    CURRENT_DOMAIN   Aktuelle Domäne : `r CURRENT_DOMAIN`
    MISSING_KEYS     Für die Sprache '`r lang`' fehlt der/die Schlüssel: `r MISSING_KEYS`
    UNDEFINED_KEYS   Undefinierte Schlüssel: `r UNDEFINED_KEYS`
    KEY_LENGTH       'key' muss die Länge 1 haben, vielleicht funktioniert '"key"'
    KEY_NOT_FOUND    Schlüssel '`r key`' nicht in '`r lang`' gefunden, vielleicht meinten Sie:
    DOMAIN_UNIQUE    Es muss eine eindeutige Domäne angegeben werden

    < en
    COPYRIGHT        (C) 2023+ S. Klinke, Humboldt-Universität zu Berlin
    LANGUAGE         Language: `r LANGUAGE`
    DOMAIN           Domain: `r DOMAIN`
    CURRENT_LANGUAGE Current language: `r CURRENT_LANGUAGE`
    CURRENT_DOMAIN   Current domain  : `r CURRENT_DOMAIN`
    MISSING_KEYS     Missing for language '`r lang`' the key(s): `r MISSING_KEYS`
    UNDEFINED_KEYS   Undefined key(s): `r UNDEFINED_KEYS`
    KEY_NOT_FOUND    Key '`r key`' not found in '`r lang`', maybe you meant:
    KEY_LENGTH       'key' must have length 1, maybe '"key"' works
    DOMAIN_UNIQUE    A unique domain must be specified

## `messages.R`

    setLang('en') # adds to domain 'default'
    setMsg('ROUND'='Round your result to `r ROUND` decimal places',
           '0'='Round your result to an integer',
           '1'='Round your result to one decimal place')
    setMsg(PHOTO='`r user` added `r COUNT(PHOTO)` to `r STREAM(gender)` stream')
    setMsg(COUNT='`r PHOTO` new photos',
           '1'='a new photo')
    setMsg(STREAM='their',
           'male'='his',
           'female'='her')
    #
    setLang('de') # adds to domain 'default'
    setMsg(ROUND='Runden Sie ihr Ergebnis auf `r ROUND` Nachkommastellen',
           '0'='Runden Sie ihr Ergebnis auf eine ganze Zahl',
           '1'='Runden Sie ihr Ergebnis auf eine Nachkommastelle')
    setMsg(PHOTO='`r user` fügt `r COUNT(PHOTO)` `r STREAM(gender)` Stream zu')
    setMsg(COUNT='`r PHOTO` neue Fotos',
           '1'= 'ein neues Foto')
    setMsg(STREAM='seinem',
           'female'='ihrem')