Found it out myself. As a test case, I set up a vanilla multi-language site and created a custom TextLanguage field "custom1" for the images on "home".
I wanted to change and save a single language value directly, e.g.
$mlFieldValues->setLanguageValue("de", "new Value") ;
$p->of(false);
$p->save();
But this deletes all other language values.
Instead, I have to 1. read in all current values, 2. change whatever language value has to be changed and 3. save the entire object.
$newValues = ["de"=>"DE overwritten by script", "fi"=>"FI overwritten by script"];
$p = $pages->get(1);
$img = $p->images->first();
$mlFieldValues = $img->custom1;
print "\n\nBEFORE:\n";print_r($mlFieldValues);
print "\n\nNEW VALUES:\n"; print_r($newValues);
$p->of(false);
foreach($newValues as $lang => $newValue){
$mlFieldValues->setLanguageValue($lang, $newValue) ;
}
$img->custom1 = $mlFieldValues;
$p->save();
print "\n\nAFTER:\n";print_r($mlFieldValues);
Output:
BEFORE:
ProcessWire\LanguagesPageFieldValue Object
(
[default] => previous english value
[de] => previous german value
[fi] => previous finnish value
)
NEW VALUES:
Array
(
[de] => DE overwritten by script
[fi] => FI overwritten by script
)
AFTER:
ProcessWire\LanguagesPageFieldValue Object
(
[default] => previous english value
[de] => DE overwritten by script
[fi] => FI overwritten by script
)