# Translator

Source code is hosted on [GitHub](https://github.com/corets/schema)

* Works for **server side and client side** translations handling
* Features **static translations access**, without using string based keys
* Extremely **easy to use and customise**
* Not bloated with unnecessary features

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/translator
```

{% endtab %}

{% tab title="npm" %}

```bash
npm install --save @corets/translator
```

{% endtab %}
{% endtabs %}

Seamless React integration is shipped in a separate package:

{% content-ref url="../hooks/use-translator" %}
[use-translator](https://docs.corets.io/hooks/use-translator)
{% endcontent-ref %}

## Static translation access

Translations can be accessed in a static manner thanks to the [`createTranslatorAccessor()`](#createtranslatoraccessor) helper. Check out [@corets/use-translator](https://docs.corets.io/hooks/use-translator#uselocales) docs for an example usage in React.

## Custom interpolations <a href="#custom-interpolations" id="custom-interpolations"></a>

Provide a custom interpolation function used for replacement of placeholders with values:

```typescript
import { createTranslator } from "@corets/translator"

const customInterpolator = (text: string, match: string, replacement: any) => {  
    return text.replace(`[[${match}]]`, replacement)
}
const translator = createTranslator({}, { interpolator: customInterpolator })
```

You can access the default interpolation function anytime:

```typescript
import { translatorInterpolator } from "@corets/translator"
```

## Custom formatting <a href="#custom-formatting" id="custom-formatting"></a>

Provide a custom formatter function used to format replacements before interpolation:

```typescript
import { createTranslator, translatorFormatter } from "@corets/translator"
import dayjs from "dayjs"

const customFormatter = (language: string, replacement: any, replacements: object) => {  
    if (replacement instanceof Date) {    
        const format = replacements?.format ?? "DD.MM.YYYY"    
        
        return dayjs(replacement).format(format)  
    }  
    
    return defaultTranslatorFormatter(language, replacement, replacements)
} 

const translator = createTranslator({}, { formatter: customFormatter })
```

## Custom placeholders <a href="#custom-placeholder-for-missing-keys" id="custom-placeholder-for-missing-keys"></a>

Provide a custom placeholder function used to generate placeholders for missing translation keys:

```typescript
import { createTranslator } from "@corets/translator"

const customPlaceholder = (language: string, key: string, replacements: object) => `${language}.${key}`

const translator = createTranslator({}, { placeholder: customPlaceholder })
```

You can access the default placeholder function anytime:

```typescript
import { translatorPlaceholder } from "@corets/translator"
```

## createTranslator() <a href="#createtranslator" id="createtranslator"></a>

Create a new `Translator` instance:

```typescript
import { createTranslator } from "@corets/translator"

const translations = {  
    en: { title: "Welcome" },  
    de: { title: "Willkommen" }
}

const translator = createTranslator(translations, { language: "en" })
```

Create a translator instance without the factory function:

```typescript
import { Translator } from "@corets/translator"

const translator = new Translator({}, { language: "en" })
```

## createTranslatorAccessor() <a href="#createtranslatoraccessor" id="createtranslatoraccessor"></a>

Create a statically typed facade for your translations. This allows you to access translations in a statically typed manner, no need to use string based translation keys anymore. Trying to access missing translations will lead to a compilation error! 💥

```typescript
import { createTranslator, createTranslatorAccessor } from "@corets/translator"

const translations = {  
    en: { some: { nested: "value", another: "Hello {greeting}"} },  
    de: { some: { nested: "other"} },
}

const translator = createTranslator(translations, { language: "en" })
const locales = createTranslatorAccessor(translator, translations.en)

locales.some.nested.get()
locales.some.another.get({ replace: { greeting: "World" } })
locales.some.nested.get({ language: "de" })
```

This functionality is powered by the accessor library:

{% content-ref url="accessor" %}
[accessor](https://docs.corets.io/services/accessor)
{% endcontent-ref %}

## Translator.config() <a href="#translatorconfig" id="translatorconfig"></a>

Configure translator:

```typescript
import { createTranslator } from "@corets/translator"

const translator = createTranslator({ ... }, { language: "en" })

translator.config({  
    language: "en",  
    fallbackLanguage: "de",  
    interpolate: true,  
    formatter,  
    interpolator,  
    placeholder,
})
```

## Translator.getLanguage() <a href="#translatorgetlanguage" id="translatorgetlanguage"></a>

Get current language:

```typescript
translator.getLanguage()
```

## Translator.setLanguage() <a href="#translatorsetlanguage" id="translatorsetlanguage"></a>

Change current language:

```typescript
translator.setLanguage("en")
```

## Translator.getLanguages() <a href="#translatorgetlanguages" id="translatorgetlanguages"></a>

Get all registered languages:

```typescript
translator.getLanguages()
```

## Translator.getFallbackLanguage() <a href="#translatorgetfallbacklanguage" id="translatorgetfallbacklanguage"></a>

Get fallback language:

```typescript
transaltor.getFallbackLanguage()
```

{% hint style="info" %}
A fallback language is used whenever a translation key is missing for current language.
{% endhint %}

## Translator.setFallbackLanguage() <a href="#translatorsetfallbacklanguage" id="translatorsetfallbacklanguage"></a>

Change fallback language:

```typescript
translator.setFallbackLanguage("de")
```

## Translator.getTranslations() <a href="#translatorgettranslations" id="translatorgettranslations"></a>

Get all registered translations:

```typescript
translator.getTranslations()
```

Returned object looks something like this:

```typescript
{  
    en: { key: "value" },  
    de: { key: "value" },
}
```

## Translator.getTranslationsForLanguage() <a href="#translatorgettranslationsforlanguage" id="translatorgettranslationsforlanguage"></a>

Get all translations for a specific language:

```typescript
translator.getTranslationsForLanguage("en")
```

The returned object looks something like this:

```typescript
{ key: "value" }
```

## Translator.setTranslations() <a href="#translatorsettranslations" id="translatorsettranslations"></a>

Replace all translations, for all languages, with the new ones:

```typescript
translator.setTranslations({ en: { key: "value" }})
```

## Translator.setTranslationsForLanguage() <a href="#translatorsettranslationsforlanguage" id="translatorsettranslationsforlanguage"></a>

Replace all translations for a specific language:

```typescript
translator.setTranslationsForLanguage("en", { key: "value" })
```

## Translator.addTranslations() <a href="#translatoraddtranslations" id="translatoraddtranslations"></a>

Update all translations, in all languages, using a merge:

```typescript
translator.addTranslations({ de: { key: "value" } })
```

{% hint style="info" %}
Contrary to the [Translator.setTranslations()](#translatorsettranslations) method, this one will not replace all translations but do a merge instead.
{% endhint %}

## Translator.addTranslationsForLanguage() <a href="#translatoraddtranslationsforlanguage" id="translatoraddtranslationsforlanguage"></a>

Add translations, for a specific language, using a merge:

```typescript
translator.addTranslationsForLanguage("en", { key: "value" })
```

{% hint style="info" %}
Contrary to the [Translator.setTranslationsForLanguage()](#translatorsettranslationsforlanguage) method, this one will not replace all translations but do a merge instead.
{% endhint %}

## Translator.get() <a href="#translatorget" id="translatorget"></a>

Retrieve a translation:

```typescript
translator.get("key")
```

Retrieve a translation with a nested key:

```typescript
translator.get("nested.key")
```

Retrieve translation for another language:

```typescript
translator.get("key", { language: "de" })
```

Retrieve translation with another fallback language:

```typescript
translator.get("key", { fallbackLanguage: "de" })
```

Retrieve translation without interpolating it:

```typescript
translator.get("key", { interpolate: false })
```

Retrieve a translation and interpolate some values, using array syntax:

```typescript
// given translation: {{1}} and {{2}} are colors
translator.get("key", ["red", "blue"])
```

Retrieve a translation and interpolate some values, using object syntax:

```typescript
// given translations: {{color1}} and {{color2}} are colors
translator.get("key", { color1: "red", color2: "blue" })
```

Retrieve translation with a custom formatter, interpolator and placeholder:

```typescript
translator.get("key", { formatter, interpolator, placeholder })
```

{% hint style="info" %}
Check out documentation for [formatter](#custom-formatting), [interpolator](#custom-interpolations), and [placeholder](#custom-placeholder-for-missing-keys).
{% endhint %}

## Translator.has() <a href="#translatorhas" id="translatorhas"></a>

Check if translation exists:

```typescript
translator.has("key")
```

Check if translation exists for a specific language:

```typescript
translator.has("key", { language: "en" })
```

Check if translation exists in the current language or in a specific fallback language:

```typescript
translator.has("key", { fallbackLanguage: "de" })
```

## Translator.t() <a href="#translatort" id="translatort"></a>

Create a standalone translation function:

```typescript
const t = translator.t()

t("key")

// same as
translator.get("key")
```

Create a translation function for a specific scope:

```typescript
const t = translator.t("nested.scope")

t("key")

// same as
translator.get("nested.scope.key")
```

Override scope with `~`:

```typescript
const t = translator.t("nested.key")

t("~key")

// same as
translator.get("key")
```

{% hint style="info" %}
This method supports the same options object as [Translator.get()](#translatorget).
{% endhint %}

## Translator.listen() <a href="#translatorlisten" id="translatorlisten"></a>

Listen to any kind of changes, like language change, translations change, etc.:

```typescript
const unsubscribe = translator.listen(() => console.log("something has changed"))

unsubscribe()
```
