2

I'm trying to write a plugin that translate entries programmatically based on subsites that have different language setup.

Normally what happens is this: you setup multi-site, set each sub-site as having a different language and a different url (like @web/de, @web/sv etc), you set a field as translatable by language, then you edit an entry on each of those sub-sites. And the system knows to separate entries by siteId.

Now, I tried the same approach programmatically. I get all the entries for all the subsites like this:

$sites = Craft::$app->getSites()
                    ->getAllSites();
/** @var Entry[] $entries */
$entries = [];
foreach ($sites as $site) {
    /** @var Entry $entry */
    $entries[$site->language] = Entry::find()
                                     ->id($id)
                                     ->siteId($site->id)
                                     ->one();
}

The I go and get all the fields for the English entry (in my setup is safe to assume that all the sub-sites respect the same field layout/structure). To test this, I dumped the values to compare the English version of the field, with the Swedish version. Values were different, which means I'm looking at the correct fields for the correct sub-entry.

//this is how we get the english fields
$fields = $entries['en']->getType()
                        ->getFieldLayout()
                        ->getFields();

//this is how we translate each field for each language foreach ($fields as $field) { if ($field->translationMethod === \craft\base\Field::TRANSLATION_METHOD_LANGUAGE) { //get english value $enValue = $entries['en']->getFieldValue($field->handle); if (!empty($enValue)) { //translate in all languages $translations = $this->translate($enValue, array_keys($entries)); foreach ($entries as $lang => $entry) { $fieldValue = $entry->getFieldValue($field->handle); //only translate if the destination language/field is in English (not translated yet) if (('en' !== $lang) && !empty($enValue) && ($enValue === $fieldValue)) { if (!empty($translations[$lang])) { //setFieldValue for each language $entry->setFieldValue($field->handle, $translations[$lang]); } } } } } }

This is how I try to save the entries at the end of it all

//save all non-eng entries
$elements = new Elements();
foreach ($entries as $lang => $entry) {
    if (('en' !== $lang) && !$elements->saveElement($entry)) {
        echo json_encode($entry->getErrorSummary(true));
        die();
    }
}

The issue is that it saves the last translated language in all the sub-sites, for some reason, and I don't understand what I'm doing wrong.

dustfeather
  • 105
  • 6

1 Answers1

4

There's no clear question here, instead you've thrown up a bunch of code and said that it's not working. My suggestion would be to double check that the entry fields are indeed being populated with the appropriate translations. So double check whether the following line is actually executed.

$entry->setFieldValue($field->handle, $translations[$lang]);

If it is, then I would expect the save to work. One thing to be aware of, is that the saveElement method accepts a boolean value as the third parameter which specifies whether to propagate the entry. This is true by default, meaning that the entry is propogated for each site each time you save it. Maybe consider setting it to false or better yet, modifying your saving strategy to be more performant.

if (('en' !== $lang) && !$elements->saveElement($entry, true, false)) {

See the method signature at: https://github.com/craftcms/cms/blob/e55041d4c24ea827b13ee4367d3fae52df6ab80f/src/services/Elements.php#L746

Ben Croker
  • 7,341
  • 26
  • 55