def translate( self, user, new_target, new_state, change_action=None, propagate: bool = True, author=None, ): """ Store new translation of a unit. Propagation is currently disabled on import. """ # Fetch current copy from database and lock it for update self.old_unit = Unit.objects.select_for_update().get(pk=self.pk) # Handle simple string units if isinstance(new_target, str): new_target = [new_target] # Apply autofixes if not self.translation.is_template: new_target, self.fixups = fix_target(new_target, self) # Update unit and save it self.target = join_plural(new_target) not_empty = bool(max(new_target)) # Newlines fixup if "dos-eol" in self.all_flags: self.target = NEWLINES.sub("\r\n", self.target) if not_empty: self.state = new_state else: self.state = STATE_EMPTY self.original_state = self.state saved = self.save_backend(user, change_action=change_action, propagate=propagate, author=author) # Enforced checks can revert the state to needs editing (fuzzy) if (self.state >= STATE_TRANSLATED and self.translation.component.enforced_checks and self.all_checks_names & set(self.translation.component.enforced_checks)): self.state = self.original_state = STATE_FUZZY self.save(run_checks=False, same_content=True, update_fields=["state"]) if (propagate and user and self.target != self.old_unit.target and self.state >= STATE_TRANSLATED and self.translation.component.is_glossary): transaction.on_commit( lambda: handle_unit_translation_change.delay(self.id, user.id)) return saved
def update_from_unit(self, unit, pos, created): # noqa: C901 """Update Unit from ttkit unit.""" translation = self.translation component = translation.component self.is_batch_update = True self.source_updated = True # Get unit attributes try: location = unit.locations flags = unit.flags source = unit.source self.check_valid(split_plural(source)) if not translation.is_template and translation.is_source: # Load target from source string for bilingual source translations target = source else: target = unit.target self.check_valid(split_plural(target)) context = unit.context self.check_valid([context]) note = unit.notes previous_source = unit.previous_source except Exception as error: report_error(cause="Unit update error") translation.component.handle_parse_error(error, translation) # Ensure we track source string for bilingual, this can not use # Unit.is_source as that depends on source_unit attribute, which # we set here old_source_unit = self.source_unit if not translation.is_source: self.update_source_unit(component, source, context, pos, note, location, flags) # Calculate state state = self.get_unit_state(unit, flags) original_state = self.get_unit_state(unit, None) # Has source changed same_source = source == self.source and context == self.context # Monolingual files handling (without target change) if (not created and state != STATE_READONLY and unit.template is not None and target == self.target): if not same_source and state in (STATE_TRANSLATED, STATE_APPROVED): if self.previous_source == self.source and self.fuzzy: # Source change was reverted previous_source = "" state = STATE_TRANSLATED else: # Store previous source and fuzzy flag for monolingual if previous_source == "": previous_source = self.source state = STATE_FUZZY elif self.state in (STATE_FUZZY, STATE_APPROVED): # We should keep calculated flags if translation was # not changed outside previous_source = self.previous_source state = self.state original_state = self.original_state # Update checks on fuzzy update or on content change same_target = target == self.target same_state = state == self.state and flags == self.flags same_metadata = (location == self.location and note == self.note and pos == self.position) same_data = (not created and same_source and same_target and same_state and original_state == self.original_state and flags == self.flags and previous_source == self.previous_source and self.source_unit == old_source_unit and old_source_unit is not None) # Check if we actually need to change anything if same_data and same_metadata: return # Store updated values self.original_state = original_state self.position = pos self.location = location self.flags = flags self.source = source self.target = target self.state = state self.context = context self.note = note self.previous_source = previous_source self.update_priority(save=False) # Metadata update only, these do not trigger any actions in Weblate and # are display only if same_data and not same_metadata: self.save(same_content=True, only_save=True) return # Sanitize number of plurals if self.is_plural: self.target = join_plural(self.get_target_plurals()) if created: unit_pre_create.send(sender=self.__class__, unit=self) # Save into database self.save( force_insert=created, same_content=same_source and same_target, run_checks=not same_source or not same_target or not same_state, ) # Track updated sources for source checks if translation.is_template: component.updated_sources[self.id] = self # Indicate source string change if not same_source and previous_source: Change.objects.create( unit=self, action=Change.ACTION_SOURCE_CHANGE, old=previous_source, target=self.source, ) # Update translation memory if needed if (self.state >= STATE_TRANSLATED and (not translation.is_source or component.intermediate) and (created or not same_source or not same_target)): transaction.on_commit( lambda: handle_unit_translation_change.delay(self.id))