# Language and Translation Hooks

https://dev.wisecp.com/es/language-and-translation-hooks

The seven hooks over text: resolving a translation, saving one, sweeping a pack, and adding, removing or configuring a language.

## Overview

Text travels two ways: a key **resolves** and reaches the screen, or a translation **is saved** and reaches the disk. Each end carries a filter.

Beside them sit the events of the pack itself: opening one, deleting one, changing its settings. A new pack is not born empty; it is copied from an existing language.

## Reference

### Changing a resolved text

filteri18n.translation

`Language::g` passed by link

Runs after a translation key resolves to text. **Every** piece of text in the system passes here: it is a hot path, so keep it light.

Parameters 3

$valuestringby linkThe resolved text. What you write over it is the result.

$keystringThe key that was asked for.

$langstringThe language used to resolve it.

Return 1

voidThe return is ignored; you write over the text. Leave it alone and the original comes back.

Listener PHP

```php
Hook::add('filter:i18n.translation', 10, function (&$value, $key, $lang) {
    // Every text passes here: sift on the key first.
    if ($key !== 'website/home/title') return;

    $value = Acme::brandedTitle($lang);
});
```

### Adjusting translations before they are saved

filteri18n.translation_save

`AdminLanguages` passed by link

Runs before translations are written to disk. The array you hold **is what gets written**.

Parameters 2

$valuesarrayby linkKey against value. A value may be plain text or an array carrying content and variables: be ready for both.

$idstringThe target language.

Return 1

voidThe return is ignored; you write over the array.

Listener PHP

```php
Hook::add('filter:i18n.translation_save', 10, function (&$values, $id) {
    // A value may be plain text or an array.
    foreach ($values as $k => $v)
        if (is_string($v)) $values[$k] = trim($v);
});
```

### Following a translation being saved

actioni18n.translation_saved

`AdminLanguages` only what truly changed

Runs after a translation key is saved.

Parameters 2

$keystringThe full key that was saved.

$savedarrayThe languages **actually written to disk**. Languages sent but unchanged are **absent** from this list: it does not answer "which languages were submitted".

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:i18n.translation_saved', 10, function ($key, $saved) {
    // The list holds what CHANGED, not what was submitted.
    foreach ($saved as $lang) Acme::invalidateCache($lang, $key);
});
```

### Following a bulk replacement

actioni18n.bulk_replaced

`AdminLanguages` one language pack

Runs after a word is replaced throughout one language pack.

Parameters 4

$langstringThe **single** language the sweep ran on.

$wordstringThe raw word searched for; it is not a pattern.

$replacementstringThe text written in its place. It is **not read** as a back-reference: what it holds is written literally.

$changedintHow many keys **actually changed**, not how many were selected.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:i18n.bulk_replaced', 10, function ($lang, $word, $replacement, $changed) {
    // The counter holds what changed, not what was selected.
    Acme::auditSweep($lang, $word, $changed);
});
```

### Following a language being added

actionlanguage.added

`AdminLanguages` opened as a copy

Runs after a new language pack is opened. The pack does not start empty: it is built as a **copy** of an existing one.

Parameters 2

$keystringThe key of the new language.

$copied_fromstringThe language the content was copied from. The new pack starts full of the source texts: it does not look "untranslated", it looks **wrong-languaged**.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:language.added', 10, function ($key, $copied_from) {
    // The pack starts full of the source texts: put it in the translation queue.
    Acme::queueTranslation($key, $copied_from);
});
```

### Following a language being deleted

actionlanguage.deleted

`AdminLanguages` after deletion

Runs after a language pack is deleted.

Parameters 1

$keystringThe key of the deleted language.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:language.deleted', 10, function ($key) {
    Acme::dropTranslations($key);
});
```

### Following language settings changing

actionlanguage.settings_updated

`AdminLanguages` after the write

Runs after the settings of a language are saved.

Parameters 2

$idstringThe key of the language updated.

$packagearrayThe settings written: display name, rank, whether it reads right to left, and status.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:language.settings_updated', 10, function ($id, $package) {
    Acme::syncLocale($id, $package);
});
```

## Pitfalls

> **The translation filter is a hot path**
> 
> **Every** piece of text passes through it, hundreds of times on a single page. Sift on the key before doing anything heavy, and leave straight away for calls you do not care about.

> **The saved list is not the submitted list**
> 
> The list carried by the translation save event holds **only the languages whose value actually changed**. Languages submitted unchanged are absent, so do not answer "which languages were sent" from it.

## Related Articles

- [How Hooks Work](https://dev.wisecp.com/en/how-hooks-work)
- [Hook Domains](https://dev.wisecp.com/en/hook-domains)
- [Writing a Hook Listener](https://dev.wisecp.com/en/writing-a-hook-listener)
