skip to Main Content

I’d like to use DeepL to translate my Laravel Post model. The problem is, their API requires that we submit a flat array of translations. E.g. our payload would be –

{
    "target_lang": "ES",
    "text": [
        "The post title",
        "A quote goes here (translatable)",
        "<p>A content block goes here (translatable)</p>",
        "A quote goes here",
        "<p>A content block goes here (translatable)</p>"
    ]
}

However, my actual post uses a ‘content’ array to define blocks of content, like below:

[
  {
    "type": "quote",
    "data": {
      "name": "A quote name",
      "quote": "A quote goes here (translatable)"
    }
  },
  {
    "type": "content",
    "data": {
      "content": "<p>A content block goes here (translatable)</p>"
    }
  },
  {
    "type": "quote",
    "data": {
      "name": "A quote name (not translatable)",
      "quote": "A quote goes here"
    }
  },
  {
    "type": "content",
    "data": {
      "content": "<p>A content block goes here (translatable)</p>"
    }
  }
]

The data above is very flexible and changeable but always hinges on the block type. I can easily flatten the data down using something like below:

public function translatableAttributes(): array
{
    return [
        $this->title,
        ...collect($this->content)
            ->mapWithKeys(fn (array &$content) => match ($content['type']) {
                'quote' => $content['data']['quote'],
                'content' => $content['data']['content'],
            })->flatten()->toArray(),
    ];
}

But the big issue I’m having is pushing that back into the array. I just can’t think of a way to do this while maintaining its flexibility.

I’ve thought of perhaps implementing promises and then resolving them for each key but again can’t think how to implement this.

I could make separate API calls but again this is very inefficient when I can translate 50 strings at a time in one API call.

2

Answers


  1. I would suggest getting by with a simple array_map()

    public function translatableAttributes(): array
    {
        return [
            $this->title,
            ...array_map(
                fn($content) => $content['data'][$content['type']],
                $this->content,
            ),
        ];
    }
    
    public function applyTranslations(array $translations): void
    {
        $this->title = $translations[0];
        $this->content = array_map(
            static function ($content, $translation) {
                $content['data'][$content['type']] = $translation;
                return $content;
            },
            $this->content,
            array_slice($translations, 1),
        );
    }
    

    And more complex, but more flexible (if, for example, there are several fields for translation in one content; then we will need to use foreach in getTranslatableKeys())

    public function translatableAttributes(): array
    {
        return [
            $this->title,
            ...array_map(
                fn($key) => Arr::get($this->content, $key),
                $this->getTranslatableKeys(),
            ),
        ];
    }
    
    private function getTranslatableKeys(): array
    {
        return array_map(
            fn($content, $index) => $index.'.data.'.$content['type'],
            $this->content,
            array_keys($this->content),
        );
    }
    
    public function applyTranslations(array $translations): void
    {
        $this->title = $translations[0];
        foreach ($this->getTranslatableKeys() as $index => $key) {
            Arr::set($this->content, $key, $translations[$index + 1]);
        }
    }
    
    Login or Signup to reply.
  2. A body-less foreach loop with array destructuring syntax can push the desired values by dynamic keypath into the result array.

    Code: (Demo) (or with a loop body)

    $payload = [
        'target_lang' => 'ES',
        'text' => ['The post title']
    ];
    foreach ($array as ['type' => $t, 'data' => [$t => $payload['text'][]]]);
    var_export($payload);
    

    Output:

    array (
      'target_lang' => 'ES',
      'text' => 
      array (
        0 => 'The post title',
        1 => 'A quote goes here (translatable)',
        2 => '<p>A content block goes here (translatable)</p>',
        3 => 'A quote goes here',
        4 => '<p>A content block goes here (translatable)</p>',
      ),
    )
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search