<p>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.</p>
<pre class="lang-php prettyprint-override"><code>$entry-&gt;setFieldValue($field-&gt;handle, $translations[$lang]);
</code></pre>
<p>If it is, then I would expect the save to work. One thing to be aware of, is that the <code>saveElement</code> method accepts a boolean value as the third parameter which specifies whether to propagate the entry. This is <code>true</code> by default, meaning that the entry is propogated for each site each time you save it. Maybe consider setting it to <code>false</code> or better yet, modifying your saving strategy to be more performant.</p>
<pre class="lang-php prettyprint-override"><code>if (('en' !== $lang) &amp;&amp; !$elements-&gt;saveElement($entry, true, false)) {
</code></pre>
<p>See the method signature at: <a href="https://github.com/craftcms/cms/blob/e55041d4c24ea827b13ee4367d3fae52df6ab80f/src/services/Elements.php#L746" rel="nofollow noreferrer">https://github.com/craftcms/cms/blob/e55041d4c24ea827b13ee4367d3fae52df6ab80f/src/services/Elements.php#L746</a></p>

CC BY-SA 4.0
2021-02-15T10:46:47.197Z
I guess my question was: Am I correct in assuming that the only difference between the entries is indeed just the siteId, even when the entry id stays the same?
dustfeather
CC BY-SA 4.0
2021-02-15T19:32:58.947Z
In that case, yes.
Ben Croker
CC BY-SA 4.0
2021-02-12T20:44:07.443Z
2021-02-13T07:35:40.147Z
2021-02-13T07:35:40.147Z
Ben Croker
<p>I'm trying to write a plugin that translate entries programmatically based on subsites that have different language setup.</p>
<p>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.</p>
<p>Now, I tried the same approach programmatically. I get all the entries for all the subsites like this:</p>
<pre class="lang-php prettyprint-override"><code>$sites = Craft::$app-&gt;getSites()
                    -&gt;getAllSites();
/** @var Entry[] $entries */
$entries = [];
foreach ($sites as $site) {
    /** @var Entry $entry */
    $entries[$site-&gt;language] = Entry::find()
                                     -&gt;id($id)
                                     -&gt;siteId($site-&gt;id)
                                     -&gt;one();
}
</code></pre>
<p>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.</p>
<pre class="lang-php prettyprint-override"><code>//this is how we get the english fields
$fields = $entries['en']-&gt;getType()
                        -&gt;getFieldLayout()
                        -&gt;getFields();

//this is how we translate each field for each language
foreach ($fields as $field) {
    if ($field-&gt;translationMethod === \craft\base\Field::TRANSLATION_METHOD_LANGUAGE) {
        //get english value
        $enValue = $entries['en']-&gt;getFieldValue($field-&gt;handle);
        if (!empty($enValue)) {
            //translate in all languages
            $translations = $this-&gt;translate($enValue, array_keys($entries));
            foreach ($entries as $lang =&gt; $entry) {
                $fieldValue = $entry-&gt;getFieldValue($field-&gt;handle);
                //only translate if the destination language/field is in English (not translated yet)
                if (('en' !== $lang) &amp;&amp; !empty($enValue) &amp;&amp; ($enValue === $fieldValue)) {
                    if (!empty($translations[$lang])) {
                        //setFieldValue for each language
                        $entry-&gt;setFieldValue($field-&gt;handle, $translations[$lang]);
                    }
                }
            }
        }
    }
}
</code></pre>
<p>This is how I try to save the entries at the end of it all</p>
<pre class="lang-php prettyprint-override"><code>//save all non-eng entries
$elements = new Elements();
foreach ($entries as $lang =&gt; $entry) {
    if (('en' !== $lang) &amp;&amp; !$elements-&gt;saveElement($entry)) {
        echo json_encode($entry-&gt;getErrorSummary(true));
        die();
    }
}
</code></pre>
<p>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.</p>

CC BY-SA 4.0
2021-02-11T13:44:21.437Z
`$elements->saveElement($entry, true, false)` does the same thing, by the way.
dustfeather
CC BY-SA 4.0
2021-02-11T18:33:43.857Z
[This](https://craftcms.stackexchange.com/a/30113/1016) might be relevant (ie using ``enabledForSite``)...
RitterKnight
CC BY-SA 4.0
2021-02-11T23:40:24.470Z
None of those 2 solutions work with my example, tested them both. Probably because I'm not saving a new entry, but an existing one, who has already been propagated on the first save (when it was created).
dustfeather
CC BY-SA 4.0
2021-02-12T15:02:18.827Z
Have you checked that the site ID is correctly set on the entry before saving?
Ben Croker
CC BY-SA 4.0
2021-02-12T17:05:44.477Z
@BenCroker shouldn't it already be set since I'm pulling an already saved entry from the DB?
dustfeather
CC BY-SA 4.0
2021-02-12T20:32:01.253Z
Perhaps, but I would still check to be sure.
Ben Croker
<localization>
How to programmatically translate an entry in craft 3
# How to programmatically translate an entry in craft 3

(Asked by: username_0 on 2021-02-11)

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.

**Tags:** localization
> **Comments:**
> **username_0**: `$elements->saveElement($entry, true, false)` does the same thing, by the way.  
> **username_1**: [This](https://craftcms.stackexchange.com/a/30113/1016) might be relevant (ie using ``enabledForSite``)...  
> **username_0**: None of those 2 solutions work with my example, tested them both. Probably because I'm not saving a new entry, but an existing one, who has already been propagated on the first save (when it was created).  
> **username_2**: Have you checked that the site ID is correctly set on the entry before saving?  
> **username_0**: @BenCroker shouldn't it already be set since I'm pulling an already saved entry from the DB?  
> **username_2**: Perhaps, but I would still check to be sure.  

---

## 1 answers

### Answer by username_2 on 2021-02-12. Score: 4 | Accepted answer

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

> **Comments:**
> **username_0**: I guess my question was: Am I correct in assuming that the only difference between the entries is indeed just the siteId, even when the entry id stays the same?  
> **username_2**: In that case, yes.  

---

