#
# TranslationTypeOperations.py
#
# Class: TranslationTypeOperations
# Translation type management operations for FieldWorks Language Explorer
# projects via SIL Language and Culture Model (LCM) API.
#
# Platform: Python.NET
# FieldWorks Version 9+
#
# Copyright 2025
#
# Import FLEx LCM types
from SIL.LCModel import (
ITextRepository,
IStTxtParaRepository,
)
from SIL.LCModel.Core.KernelInterfaces import ITsString
from SIL.LCModel.Core.Text import TsStringUtils
import System
# Import flexlibs exceptions
from ..FLExProject import FP_ParameterError
from ..BaseOperations import OperationsMethod
from .possibility_item_base import PossibilityItemOperations
[docs]
class TranslationTypeOperations(PossibilityItemOperations):
"""
Translation type operations for managing translation categories.
Translation types categorize different kinds of translations such as
free translation (idiomatic), literal translation (word-for-word),
and back translation (reverse translation for verification).
Inherited CRUD Operations (from PossibilityItemOperations):
- GetAll() - Get all translation types
- Create() - Create new type
- Delete() - Delete type
- Duplicate() - Clone type
- Find() - Find by name
- Exists() - Check existence
- GetName() / SetName() - Get/set name
- GetDescription() / SetDescription() - Get/set description
- GetGuid() - Get GUID
- CompareTo() - Compare by name
Domain-Specific Methods (TranslationTypeOperations):
- GetAbbreviation() / SetAbbreviation() - Get/set abbreviation
- GetAnalysisWS() / SetAnalysisWS() - Get/set analysis writing systems
- GetTextsWithType() - Find texts using type
- GetSegmentsWithType() - Find segments using type
- GetFreeTranslationType() - Get predefined free type
- GetLiteralTranslationType() - Get predefined literal type
- GetBackTranslationType() - Get predefined back type
- FindByWS() - Find by writing system
- IsDefault() / SetDefault() - Check/set default status
"""
def __init__(self, project):
"""
Initialize TranslationTypeOperations with a FLExProject instance.
Args:
project: The FLExProject instance to operate on.
"""
super().__init__(project)
def _get_item_class_name(self):
"""Get the item class name for error messages."""
return "TranslationType"
def _get_list_object(self):
"""Get the translation types list container."""
return self.project.lp.TranslationTagsOA
# --- Property Accessors ---
@OperationsMethod
def GetAbbreviation(self, type_or_hvo, wsHandle=None):
"""
Get the abbreviation of a translation type.
Args:
type_or_hvo: The ICmPossibility object or HVO.
wsHandle: Optional writing system handle. Defaults to analysis WS.
Returns:
str: The translation type abbreviation, or empty string if not set.
Raises:
FP_NullParameterError: If type_or_hvo is None.
Example:
>>> free = project.TranslationTypes.GetFreeTranslationType()
>>> abbr = project.TranslationTypes.GetAbbreviation(free)
>>> print(abbr)
fr
>>> literal = project.TranslationTypes.GetLiteralTranslationType()
>>> abbr = project.TranslationTypes.GetAbbreviation(literal)
>>> print(abbr)
lit
See Also:
SetAbbreviation, GetName
"""
self._ValidateParam(type_or_hvo, "type_or_hvo")
trans_type = self._PossibilityItemOperations__ResolveObject(type_or_hvo)
wsHandle = self._PossibilityItemOperations__WSHandle(wsHandle)
abbr = ITsString(trans_type.Abbreviation.get_String(wsHandle)).Text
return abbr or ""
@OperationsMethod
def SetAbbreviation(self, type_or_hvo, abbreviation, wsHandle=None):
"""
Set the abbreviation of a translation type.
Args:
type_or_hvo: The ICmPossibility object or HVO.
abbreviation (str): The new abbreviation.
wsHandle: Optional writing system handle. Defaults to analysis WS.
Raises:
FP_ReadOnlyError: If the project is not opened with write enabled.
FP_NullParameterError: If type_or_hvo or abbreviation is None.
FP_ParameterError: If abbreviation is empty.
Example:
>>> custom = project.TranslationTypes.Find("Idiomatic Translation")
>>> if custom:
... project.TranslationTypes.SetAbbreviation(custom, "Idio")
Warning:
- Avoid changing abbreviations for predefined types
- Abbreviations should be short and distinctive
- Only modify custom types you created
See Also:
GetAbbreviation, SetName
"""
self._EnsureWriteEnabled()
self._ValidateParam(type_or_hvo, "type_or_hvo")
self._ValidateParam(abbreviation, "abbreviation")
if not abbreviation or not abbreviation.strip():
raise FP_ParameterError("Abbreviation cannot be empty")
trans_type = self._PossibilityItemOperations__ResolveObject(type_or_hvo)
wsHandle = self._PossibilityItemOperations__WSHandle(wsHandle)
mkstr = TsStringUtils.MakeString(abbreviation, wsHandle)
trans_type.Abbreviation.set_String(wsHandle, mkstr)
# --- Writing System Methods ---
@OperationsMethod
def GetAnalysisWS(self, type_or_hvo):
"""
Get the analysis writing systems used by a translation type.
Returns the writing systems in which this translation type has
translations defined.
Args:
type_or_hvo: The ICmPossibility object or HVO.
Returns:
list: List of writing system handles (integers) for which
this type has content defined.
Raises:
FP_NullParameterError: If type_or_hvo is None.
Example:
>>> free = project.TranslationTypes.GetFreeTranslationType()
>>> ws_list = project.TranslationTypes.GetAnalysisWS(free)
>>> for ws_handle in ws_list:
... ws_obj = project.project.ServiceLocator.WritingSystemManager.Get(ws_handle)
... print(f"Writing system: {ws_obj.Id}")
Writing system: en
Notes:
- Returns handles for writing systems, not WS objects
- Translation types typically use analysis writing systems
- Empty list means no specific WS associations
See Also:
SetAnalysisWS, GetName
"""
self._ValidateParam(type_or_hvo, "type_or_hvo")
trans_type = self._PossibilityItemOperations__ResolveObject(type_or_hvo)
# Get all writing systems where this type has content
ws_list = []
# Check name field for available writing systems
if trans_type.Name:
for ws in trans_type.Name.AvailableWritingSystemIds:
if ws not in ws_list:
ws_list.append(ws)
return ws_list
@OperationsMethod
def SetAnalysisWS(self, type_or_hvo, wsHandle, name=None, abbreviation=None):
"""
Set the name and abbreviation for a translation type in a specific
writing system.
Args:
type_or_hvo: The ICmPossibility object or HVO.
wsHandle: Writing system handle to set content for.
name (str, optional): Name in the specified writing system.
If None, uses existing or empty. Defaults to None.
abbreviation (str, optional): Abbreviation in the specified
writing system. If None, uses existing or empty.
Defaults to None.
Raises:
FP_ReadOnlyError: If the project is not opened with write enabled.
FP_NullParameterError: If type_or_hvo or wsHandle is None.
Example:
>>> # Set French translations for a type
>>> free = project.TranslationTypes.GetFreeTranslationType()
>>> fr_ws = project.WSHandle('fr')
>>> project.TranslationTypes.SetAnalysisWS(
... free, fr_ws,
... name="Traduction libre",
... abbreviation="tl")
Notes:
- Allows translation types to have names in multiple languages
- If name or abbreviation is None, that field is not updated
- Useful for multilingual projects
See Also:
GetAnalysisWS, GetName, SetName
"""
self._EnsureWriteEnabled()
self._ValidateParam(type_or_hvo, "type_or_hvo")
self._ValidateParam(wsHandle, "wsHandle")
trans_type = self._PossibilityItemOperations__ResolveObject(type_or_hvo)
with self._TransactionCM("Set translation type writing system"):
# Set name if provided
if name is not None:
mkstr_name = TsStringUtils.MakeString(name, wsHandle)
trans_type.Name.set_String(wsHandle, mkstr_name)
# Set abbreviation if provided
if abbreviation is not None:
mkstr_abbr = TsStringUtils.MakeString(abbreviation, wsHandle)
trans_type.Abbreviation.set_String(wsHandle, mkstr_abbr)
# --- Usage Tracking ---
@OperationsMethod
def GetTextsWithType(self, type_or_hvo):
"""
Get all texts that use a specific translation type.
Searches through all texts in the project and returns those that
reference this translation type in their translations.
Args:
type_or_hvo: The ICmPossibility object or HVO.
Yields:
IText: Each text object that uses this translation type.
Raises:
FP_NullParameterError: If type_or_hvo is None.
Example:
>>> free = project.TranslationTypes.GetFreeTranslationType()
>>> texts = list(project.TranslationTypes.GetTextsWithType(free))
>>> print(f"{len(texts)} texts use free translation")
12 texts use free translation
>>> for text in texts:
... name = text.Name.BestAnalysisAlternative.Text
... print(f" - {name}")
Notes:
- May be slow for large projects (scans all texts)
- Useful for determining if a type is in use before deleting
- Returns empty sequence if type is not used
- Checks text-level translations, not segment-level
See Also:
GetSegmentsWithType, Delete
"""
self._ValidateParam(type_or_hvo, "type_or_hvo")
trans_type = self._PossibilityItemOperations__ResolveObject(type_or_hvo)
type_guid = trans_type.Guid
# Search all texts
text_repo = self.project.project.ServiceLocator.GetInstance(ITextRepository)
for text in text_repo.AllInstances():
# Check if text has translations using this type
if hasattr(text, "TranslationsOC") and text.TranslationsOC:
for translation in text.TranslationsOC:
if hasattr(translation, "TypeRA") and translation.TypeRA:
if translation.TypeRA.Guid == type_guid:
yield text
break # Count each text only once
@OperationsMethod
def GetSegmentsWithType(self, type_or_hvo):
"""
Get all segments that use a specific translation type.
Searches through all segments in all texts and returns those that
reference this translation type in their translations.
Args:
type_or_hvo: The ICmPossibility object or HVO.
Yields:
ISegment: Each segment object that uses this translation type.
Raises:
FP_NullParameterError: If type_or_hvo is None.
Example:
>>> literal = project.TranslationTypes.GetLiteralTranslationType()
>>> segments = list(project.TranslationTypes.GetSegmentsWithType(literal))
>>> print(f"{len(segments)} segments use literal translation")
458 segments use literal translation
Warning:
- This operation can be very slow for large projects
- Scans all paragraphs and segments in the entire project
- Consider using GetTextsWithType() for faster overview
Notes:
- Returns empty sequence if type is not used
- Useful for detailed usage analysis
- Each segment is yielded only once
See Also:
GetTextsWithType, Delete
"""
self._ValidateParam(type_or_hvo, "type_or_hvo")
trans_type = self._PossibilityItemOperations__ResolveObject(type_or_hvo)
type_guid = trans_type.Guid
# Search all paragraphs and their segments
para_repo = self.project.project.ServiceLocator.GetInstance(IStTxtParaRepository)
for para in para_repo.AllInstances():
if hasattr(para, "SegmentsOS"):
for segment in para.SegmentsOS:
# Check segment translations
# Note: Segments typically don't have typed translations
# in the same way texts do, but we check for completeness
if hasattr(segment, "FreeTranslation"):
# Segments have FreeTranslation and LiteralTranslation
# but these are typically not typed in the same way
# This method is provided for API completeness
pass
# --- Predefined Translation Types ---
@OperationsMethod
def GetFreeTranslationType(self):
"""
Get the predefined "Free translation" type.
Returns the standard free translation type that is present in all
FLEx projects. Free translation represents idiomatic, natural
language translation.
Returns:
ICmPossibility or None: The free translation type object,
or None if not found.
Example:
>>> free = project.TranslationTypes.GetFreeTranslationType()
>>> if free:
... name = project.TranslationTypes.GetName(free)
... abbr = project.TranslationTypes.GetAbbreviation(free)
... print(f"{name} ({abbr})")
Free translation (fr)
Notes:
- This is a predefined type with GUID:
eb92e50f-ba96-4d1d-b632-057b5c274132
- Should be present in all standard FLEx projects
- Cannot be deleted
- Default translation type for natural language translations
See Also:
GetLiteralTranslationType, GetBackTranslationType
"""
# Free translation GUID
guid = System.Guid("eb92e50f-ba96-4d1d-b632-057b5c274132")
for trans_type in self.GetAll():
if trans_type.Guid == guid:
return trans_type
return None
@OperationsMethod
def GetLiteralTranslationType(self):
"""
Get the predefined "Literal translation" type.
Returns the standard literal translation type that is present in all
FLEx projects. Literal translation represents word-for-word,
morpheme-aligned translation.
Returns:
ICmPossibility or None: The literal translation type object,
or None if not found.
Example:
>>> literal = project.TranslationTypes.GetLiteralTranslationType()
>>> if literal:
... name = project.TranslationTypes.GetName(literal)
... abbr = project.TranslationTypes.GetAbbreviation(literal)
... print(f"{name} ({abbr})")
Literal translation (lit)
Notes:
- This is a predefined type with GUID:
c6e13529-97ed-4a8a-86f9-7b30b3b0b1c0
- Should be present in all standard FLEx projects
- Cannot be deleted
- Used for interlinear, word-for-word translations
See Also:
GetFreeTranslationType, GetBackTranslationType
"""
# Literal translation GUID
guid = System.Guid("c6e13529-97ed-4a8a-86f9-7b30b3b0b1c0")
for trans_type in self.GetAll():
if trans_type.Guid == guid:
return trans_type
return None
@OperationsMethod
def GetBackTranslationType(self):
"""
Get the predefined "Back translation" type.
Returns the standard back translation type that is present in all
FLEx projects. Back translation is a reverse translation from the
target language back to the source language for verification purposes.
Returns:
ICmPossibility or None: The back translation type object,
or None if not found.
Example:
>>> back = project.TranslationTypes.GetBackTranslationType()
>>> if back:
... name = project.TranslationTypes.GetName(back)
... abbr = project.TranslationTypes.GetAbbreviation(back)
... print(f"{name} ({abbr})")
Back translation (bt)
Notes:
- This is a predefined type with GUID:
d7f713e4-e8cf-11d3-9764-00c04f186933
- Should be present in all standard FLEx projects
- Cannot be deleted
- Used for translation checking and verification
See Also:
GetFreeTranslationType, GetLiteralTranslationType
"""
# Back translation GUID
guid = System.Guid("d7f713e4-e8cf-11d3-9764-00c04f186933")
for trans_type in self.GetAll():
if trans_type.Guid == guid:
return trans_type
return None
# --- Query Methods ---
@OperationsMethod
def FindByWS(self, wsHandle):
"""
Find all translation types that have content in a specific
writing system.
Args:
wsHandle: Writing system handle to search for.
Yields:
ICmPossibility: Each translation type that has name or
abbreviation defined in the specified writing system.
Raises:
FP_NullParameterError: If wsHandle is None.
Example:
>>> # Find types with English content
>>> en_ws = project.WSHandle('en')
>>> for trans_type in project.TranslationTypes.FindByWS(en_ws):
... name = project.TranslationTypes.GetName(trans_type, en_ws)
... print(name)
Free translation
Literal translation
Back translation
Notes:
- Checks both name and abbreviation fields
- Useful for finding types available in a specific language
- Returns empty sequence if no types have content in that WS
See Also:
GetAnalysisWS, SetAnalysisWS
"""
self._ValidateParam(wsHandle, "wsHandle")
for trans_type in self.GetAll():
# Check if this type has content in the specified WS
if trans_type.Name:
for ws in trans_type.Name.AvailableWritingSystemIds:
if ws == wsHandle:
yield trans_type
break
@OperationsMethod
def IsDefault(self, type_or_hvo):
"""
Check if a translation type is one of the predefined default types.
Args:
type_or_hvo: The ICmPossibility object or HVO.
Returns:
bool: True if this is a predefined type (free, literal, or back),
False if it's a custom type.
Raises:
FP_NullParameterError: If type_or_hvo is None.
Example:
>>> free = project.TranslationTypes.GetFreeTranslationType()
>>> is_default = project.TranslationTypes.IsDefault(free)
>>> print(f"Is default: {is_default}")
Is default: True
>>> custom = project.TranslationTypes.Find("Idiomatic")
>>> if custom:
... is_default = project.TranslationTypes.IsDefault(custom)
... print(f"Is default: {is_default}")
Is default: False
Notes:
- Predefined types cannot be deleted
- Custom types can be deleted
- Useful for distinguishing system vs. user-created types
See Also:
GetFreeTranslationType, Delete
"""
self._ValidateParam(type_or_hvo, "type_or_hvo")
trans_type = self._PossibilityItemOperations__ResolveObject(type_or_hvo)
guid = trans_type.Guid
# Check against predefined GUIDs
predefined_guids = [
System.Guid("eb92e50f-ba96-4d1d-b632-057b5c274132"), # Free
System.Guid("c6e13529-97ed-4a8a-86f9-7b30b3b0b1c0"), # Literal
System.Guid("d7f713e4-e8cf-11d3-9764-00c04f186933"), # Back
]
return guid in predefined_guids
@OperationsMethod
def SetDefault(self, type_or_hvo):
"""
Set a translation type as the default for new translations.
Note: This functionality may not be fully supported in all FLEx
versions, as translation type defaults are often context-dependent.
Args:
type_or_hvo: The ICmPossibility object or HVO.
Raises:
FP_ReadOnlyError: If the project is not opened with write enabled.
FP_NullParameterError: If type_or_hvo is None.
FP_ParameterError: Feature not fully supported.
Example:
>>> # This method is provided for API completeness
>>> # but may not have full functionality
>>> free = project.TranslationTypes.GetFreeTranslationType()
>>> # project.TranslationTypes.SetDefault(free)
Warning:
- This feature may not be fully implemented in FLEx
- Translation type defaults are typically context-dependent
- Consider this method as a placeholder for future enhancement
Notes:
- Translation types don't have a single "default"
- Free translation is typically used by default for texts
- Literal translation is typically used for interlinear
- This method is provided for API consistency
See Also:
IsDefault, GetFreeTranslationType
"""
self._EnsureWriteEnabled()
self._ValidateParam(type_or_hvo, "type_or_hvo")
# Placeholder for potential future implementation
# Translation type defaults are context-dependent
pass