Source code for flexicon.code.System.WritingSystemOperations

#
#   WritingSystemOperations.py
#
#   Class: WritingSystemOperations
#          Writing system 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 SpecialWritingSystemCodes
from SIL.LCModel.Core.KernelInterfaces import ITsString
from SIL.LCModel.Core.Text import TsStringUtils
from SIL.WritingSystems import WritingSystemDefinition  # Fixed: was IWritingSystemDefinition

# Import flexlibs exceptions
from ..FLExProject import (
    FP_ParameterError,
    FP_WritingSystemError,
)
from ..BaseOperations import BaseOperations, OperationsMethod, wrap_enumerable


[docs] class WritingSystemOperations(BaseOperations): """ This class provides operations for managing writing systems in a FieldWorks project. Writing systems define how text is displayed, including language, script, font settings, and directionality (left-to-right or right-to-left). This class should be accessed via FLExProject.WritingSystems property. Usage:: from flexlibs2 import FLExProject project = FLExProject() project.OpenProject("my project", writeEnabled=True) # Get all writing systems for ws in project.WritingSystems.GetAll(): name = project.WritingSystems.GetDisplayName(ws) tag = project.WritingSystems.GetLanguageTag(ws) print(f"{name} ({tag})") # Get vernacular and analysis writing systems vern_wss = project.WritingSystems.GetVernacular() anal_wss = project.WritingSystems.GetAnalysis() # Configure a writing system ws = list(vern_wss)[0] project.WritingSystems.SetFontName(ws, "Charis SIL") project.WritingSystems.SetFontSize(ws, 12) project.WritingSystems.SetRightToLeft(ws, False) # Check and set defaults default_vern = project.WritingSystems.GetDefaultVernacular() default_anal = project.WritingSystems.GetDefaultAnalysis() project.CloseProject() """ def __init__(self, project): """ Initialize WritingSystemOperations with a FLExProject instance. Args: project: The FLExProject instance to operate on. """ super().__init__(project) # --- Core CRUD Operations --- @wrap_enumerable @OperationsMethod def GetAll(self): """ Get all active writing systems in the project. Returns all writing systems that are currently active (both vernacular and analysis) in the project. Yields: IWritingSystemDefinition: Each writing system object Example: >>> for ws in project.WritingSystems.GetAll(): ... name = project.WritingSystems.GetDisplayName(ws) ... tag = project.WritingSystems.GetLanguageTag(ws) ... font = project.WritingSystems.GetFontName(ws) ... print(f"{name} ({tag}): {font}") English (en): Calibri French (fr): Arial Vernacular (qaa-x-kal): Charis SIL Notes: - Returns only writing systems that are active in the project - Includes both vernacular and analysis writing systems - Use GetVernacular() or GetAnalysis() to filter by type See Also: GetVernacular, GetAnalysis, Exists """ vern_ws_set = self._GetAllVernacularWSTags() anal_ws_set = self._GetAllAnalysisWSTags() active_tags = vern_ws_set | anal_ws_set for ws in self.project.project.ServiceLocator.WritingSystems.AllWritingSystems: if ws.Id in active_tags: yield ws @OperationsMethod def GetVernacular(self): """ Get all vernacular writing systems. Vernacular writing systems are those used for the language(s) being studied or documented. Yields: IWritingSystemDefinition: Each vernacular writing system Example: >>> for ws in project.WritingSystems.GetVernacular(): ... name = project.WritingSystems.GetDisplayName(ws) ... tag = project.WritingSystems.GetLanguageTag(ws) ... print(f"Vernacular: {name} ({tag})") Vernacular: Kalaba (qaa-x-kal) Vernacular: Kalaba-IPA (qaa-x-kal-fonipa) Notes: - Vernacular writing systems are for the object language - These are the primary languages of the lexicon entries - Most projects have 1-3 vernacular writing systems See Also: GetAnalysis, GetDefaultVernacular, SetDefaultVernacular """ vern_ws_set = self._GetAllVernacularWSTags() for ws in self.project.project.ServiceLocator.WritingSystems.AllWritingSystems: if ws.Id in vern_ws_set: yield ws @OperationsMethod def GetAnalysis(self): """ Get all analysis writing systems. Analysis writing systems are those used for linguistic analysis, glosses, and translations (typically major languages like English, French, Spanish, etc.). Yields: IWritingSystemDefinition: Each analysis writing system Example: >>> for ws in project.WritingSystems.GetAnalysis(): ... name = project.WritingSystems.GetDisplayName(ws) ... tag = project.WritingSystems.GetLanguageTag(ws) ... print(f"Analysis: {name} ({tag})") Analysis: English (en) Analysis: French (fr) Analysis: Spanish (es) Notes: - Analysis writing systems are for metadata languages - Used for glosses, definitions, and translations - Most projects have 1-3 analysis writing systems See Also: GetVernacular, GetDefaultAnalysis, SetDefaultAnalysis """ anal_ws_set = self._GetAllAnalysisWSTags() for ws in self.project.project.ServiceLocator.WritingSystems.AllWritingSystems: if ws.Id in anal_ws_set: yield ws @OperationsMethod def Create(self, language_tag, name, is_vernacular=True): """ Create a new writing system in the project. Args: language_tag (str): Language tag (e.g., "en", "fr", "qaa-x-kal") name (str): Display name for the writing system is_vernacular (bool): True for vernacular, False for analysis. Defaults to True. Returns: IWritingSystemDefinition: The newly created writing system Raises: FP_ReadOnlyError: If project is not opened with write enabled FP_NullParameterError: If language_tag or name is None FP_ParameterError: If language_tag is empty or already exists Example: >>> # Create a vernacular writing system >>> ws = project.WritingSystems.Create("qaa-x-kal", "Kalaba") >>> print(project.WritingSystems.GetDisplayName(ws)) Kalaba >>> # Create an analysis writing system >>> ws = project.WritingSystems.Create("es", "Spanish", is_vernacular=False) >>> project.WritingSystems.SetFontName(ws, "Arial") >>> project.WritingSystems.SetFontSize(ws, 12) >>> # Create IPA writing system >>> ws = project.WritingSystems.Create("qaa-x-kal-fonipa", "Kalaba IPA") >>> project.WritingSystems.SetFontName(ws, "Charis SIL") Notes: - Language tags should follow BCP 47 standard - Use "qaa-x-" prefix for undocumented languages - The writing system is automatically added to vernacular or analysis list based on is_vernacular parameter - Default font settings may be inherited from system defaults See Also: Delete, Exists, GetAll """ self._EnsureWriteEnabled() self._ValidateParam(language_tag, "language_tag") self._ValidateParam(name, "name") if not language_tag or not language_tag.strip(): raise FP_ParameterError("Language tag cannot be empty") # Check if writing system already exists if self.Exists(language_tag): raise FP_ParameterError(f"Writing system '{language_tag}' already exists") ws_manager = self.project.project.ServiceLocator.WritingSystemManager with self._TransactionCM(f"Create writing system '{language_tag}'"): # Create + register the WS with the manager. Set() stores it in the # repository, assigns a runtime Handle, and persists the .ldml # backing file on Save. Without Set(), Create() returns a detached # object -- adding the tag to CurXxxWss would yield an orphan # reference and the next OpenProject would hit a modal # "Unable to create writing system: <tag>" dialog from liblcm. ws = ws_manager.Create(language_tag) ws.Abbreviation = language_tag try: ws.DisplayLabel = name except Exception: pass ws_manager.Set(ws) # AddToCurrent* updates BOTH the full XxxWss list AND the current # list via the proper change-notification path. Raw assignment to # CurXxxWss (the previous implementation) bypassed the WS-list # collection bookkeeping and left the full list out of sync. if is_vernacular: self.project.lp.AddToCurrentVernacularWritingSystems(ws) else: self.project.lp.AddToCurrentAnalysisWritingSystems(ws) return ws @OperationsMethod def Delete(self, ws_handle_or_tag): """ Remove a writing system from the project. Args: ws_handle_or_tag: Either a writing system handle (int) or language tag (str) Raises: FP_ReadOnlyError: If project is not opened with write enabled FP_NullParameterError: If ws_handle_or_tag is None FP_WritingSystemError: If writing system not found FP_ParameterError: If trying to delete the default WS Example: >>> # Delete by language tag >>> if project.WritingSystems.Exists("qaa-x-old"): ... project.WritingSystems.Delete("qaa-x-old") >>> # Delete by handle >>> ws = list(project.WritingSystems.GetVernacular())[2] ... handle = ws.Handle ... project.WritingSystems.Delete(handle) Notes: - Cannot delete default vernacular or analysis writing systems - Should check that no data uses this writing system before deleting - This operation cannot be undone - Writing system is removed from both the store and active lists See Also: Create, Exists """ self._EnsureWriteEnabled() self._ValidateParam(ws_handle_or_tag, "ws_handle_or_tag") # Get the writing system object if isinstance(ws_handle_or_tag, str): language_tag = ws_handle_or_tag ws = self._GetWSByTag(language_tag) if not ws: raise FP_WritingSystemError(language_tag) else: ws = self._GetWSByHandle(ws_handle_or_tag) if not ws: raise FP_WritingSystemError(str(ws_handle_or_tag)) language_tag = ws.Id # Check if it's a default writing system default_vern = self.project.lp.DefaultVernacularWritingSystem default_anal = self.project.lp.DefaultAnalysisWritingSystem if ws.Handle == default_vern.Handle: raise FP_ParameterError("Cannot delete the default vernacular writing system") if ws.Handle == default_anal.Handle: raise FP_ParameterError("Cannot delete the default analysis writing system") with self._TransactionCM(f"Delete writing system '{language_tag}'"): # Removal contract from IWritingSystemContainer: "first remove from # AnalysisWritingSystems [the full list], then from # CurrentAnalysisWritingSystems [the current list]." Same for # vernacular. Going through the collections (rather than # rewriting the CurXxxWss space-delimited string) keeps the full # list and current list in sync. for full_list, current_list in ( (self.project.lp.VernacularWritingSystems, self.project.lp.CurrentVernacularWritingSystems), (self.project.lp.AnalysisWritingSystems, self.project.lp.CurrentAnalysisWritingSystems), ): if full_list.Contains(ws): full_list.Remove(ws) if current_list.Contains(ws): current_list.Remove(ws) # --- Configuration Methods --- @OperationsMethod def GetFontName(self, ws): """ Get the default font name for a writing system. Args: ws: IWritingSystemDefinition object or language tag (str) Returns: str: Font name (e.g., "Charis SIL", "Arial") Raises: FP_NullParameterError: If ws is None FP_WritingSystemError: If writing system not found Example: >>> ws = list(project.WritingSystems.GetVernacular())[0] >>> font = project.WritingSystems.GetFontName(ws) >>> print(f"Font: {font}") Font: Charis SIL >>> # By language tag >>> font = project.WritingSystems.GetFontName("en") >>> print(font) Calibri See Also: SetFontName, GetFontSize """ ws_obj = self._ResolveWS(ws) if not ws_obj: raise FP_WritingSystemError(str(ws)) # Get default font name if hasattr(ws_obj, "DefaultFontName") and ws_obj.DefaultFontName: return ws_obj.DefaultFontName elif hasattr(ws_obj, "DefaultFont") and ws_obj.DefaultFont: return ws_obj.DefaultFont else: return "" @OperationsMethod def SetFontName(self, ws, font_name): """ Set the default font name for a writing system. Args: ws: IWritingSystemDefinition object or language tag (str) font_name (str): Font name (e.g., "Charis SIL", "Arial") Raises: FP_ReadOnlyError: If project is not opened with write enabled FP_NullParameterError: If ws or font_name is None FP_WritingSystemError: If writing system not found Example: >>> ws = list(project.WritingSystems.GetVernacular())[0] >>> project.WritingSystems.SetFontName(ws, "Charis SIL") >>> # Set by language tag >>> project.WritingSystems.SetFontName("en", "Calibri") >>> # Set IPA font >>> project.WritingSystems.SetFontName("qaa-x-kal-fonipa", "Doulos SIL") Notes: - Font must be installed on the system - Changes affect all text displayed in this writing system - Recommended fonts for IPA: Charis SIL, Doulos SIL See Also: GetFontName, SetFontSize """ self._EnsureWriteEnabled() self._ValidateParam(font_name, "font_name") ws_obj = self._ResolveWS(ws) if not ws_obj: raise FP_WritingSystemError(str(ws)) # Set default font name if hasattr(ws_obj, "DefaultFontName"): ws_obj.DefaultFontName = font_name elif hasattr(ws_obj, "DefaultFont"): ws_obj.DefaultFont = font_name @OperationsMethod def GetFontSize(self, ws): """ Get the default font size for a writing system. Args: ws: IWritingSystemDefinition object or language tag (str) Returns: float: Font size in points (e.g., 12.0) Raises: FP_NullParameterError: If ws is None FP_WritingSystemError: If writing system not found Example: >>> ws = list(project.WritingSystems.GetVernacular())[0] >>> size = project.WritingSystems.GetFontSize(ws) >>> print(f"Font size: {size} pt") Font size: 12.0 pt See Also: SetFontSize, GetFontName """ ws_obj = self._ResolveWS(ws) if not ws_obj: raise FP_WritingSystemError(str(ws)) # Get default font size if hasattr(ws_obj, "DefaultFontSize") and ws_obj.DefaultFontSize: return float(ws_obj.DefaultFontSize) else: return 12.0 # Default size @OperationsMethod def SetFontSize(self, ws, size): """ Set the default font size for a writing system. Args: ws: IWritingSystemDefinition object or language tag (str) size (float or int): Font size in points (e.g., 12, 14.5) Raises: FP_ReadOnlyError: If project is not opened with write enabled FP_NullParameterError: If ws or size is None FP_WritingSystemError: If writing system not found FP_ParameterError: If size is not positive Example: >>> ws = list(project.WritingSystems.GetVernacular())[0] >>> project.WritingSystems.SetFontSize(ws, 14) >>> # Set by language tag >>> project.WritingSystems.SetFontSize("en", 12) >>> # Larger size for better readability >>> project.WritingSystems.SetFontSize("qaa-x-kal", 16) Notes: - Size should typically be between 8 and 72 points - Common sizes: 10, 12, 14, 16 - Changes affect all text displayed in this writing system See Also: GetFontSize, SetFontName """ self._EnsureWriteEnabled() self._ValidateParam(size, "size") if size <= 0: raise FP_ParameterError("Font size must be positive") ws_obj = self._ResolveWS(ws) if not ws_obj: raise FP_WritingSystemError(str(ws)) # Set default font size if hasattr(ws_obj, "DefaultFontSize"): ws_obj.DefaultFontSize = float(size) @OperationsMethod def GetRightToLeft(self, ws): """ Get the right-to-left directionality setting for a writing system. Args: ws: IWritingSystemDefinition object or language tag (str) Returns: bool: True if right-to-left, False if left-to-right Raises: FP_NullParameterError: If ws is None FP_WritingSystemError: If writing system not found Example: >>> ws = list(project.WritingSystems.GetVernacular())[0] >>> rtl = project.WritingSystems.GetRightToLeft(ws) >>> print(f"RTL: {rtl}") RTL: False >>> # Check Arabic >>> if project.WritingSystems.Exists("ar"): ... rtl = project.WritingSystems.GetRightToLeft("ar") ... print(f"Arabic is RTL: {rtl}") Arabic is RTL: True Notes: - RTL languages include Arabic, Hebrew, Persian, Urdu - Most languages are left-to-right (LTR) See Also: SetRightToLeft """ ws_obj = self._ResolveWS(ws) if not ws_obj: raise FP_WritingSystemError(str(ws)) # Get RTL setting if hasattr(ws_obj, "RightToLeftScript"): return bool(ws_obj.RightToLeftScript) elif hasattr(ws_obj, "RightToLeft"): return bool(ws_obj.RightToLeft) else: return False # Default to LTR @OperationsMethod def SetRightToLeft(self, ws, is_rtl): """ Set the right-to-left directionality for a writing system. Args: ws: IWritingSystemDefinition object or language tag (str) is_rtl (bool): True for right-to-left, False for left-to-right Raises: FP_ReadOnlyError: If project is not opened with write enabled FP_NullParameterError: If ws or is_rtl is None FP_WritingSystemError: If writing system not found Example: >>> # Set Arabic to RTL >>> if project.WritingSystems.Exists("ar"): ... project.WritingSystems.SetRightToLeft("ar", True) >>> # Set English to LTR >>> project.WritingSystems.SetRightToLeft("en", False) Notes: - RTL languages: Arabic, Hebrew, Persian, Urdu, etc. - This affects text rendering direction in the UI - Most languages should be LTR (False) See Also: GetRightToLeft """ self._EnsureWriteEnabled() self._ValidateParam(is_rtl, "is_rtl") ws_obj = self._ResolveWS(ws) if not ws_obj: raise FP_WritingSystemError(str(ws)) # Set RTL setting if hasattr(ws_obj, "RightToLeftScript"): ws_obj.RightToLeftScript = bool(is_rtl) elif hasattr(ws_obj, "RightToLeft"): ws_obj.RightToLeft = bool(is_rtl) # --- Default Settings --- @OperationsMethod def SetDefaultVernacular(self, ws): """ Set the default vernacular writing system. Args: ws: IWritingSystemDefinition object or language tag (str) Raises: FP_ReadOnlyError: If project is not opened with write enabled FP_NullParameterError: If ws is None FP_WritingSystemError: If writing system not found FP_ParameterError: If ws is not a vernacular writing system Example: >>> # Set by object >>> ws = list(project.WritingSystems.GetVernacular())[0] >>> project.WritingSystems.SetDefaultVernacular(ws) >>> # Set by language tag >>> project.WritingSystems.SetDefaultVernacular("qaa-x-kal") Notes: - The writing system must be in the vernacular list - This is the primary writing system for lexical entries - Used as fallback when no specific WS is specified See Also: GetDefaultVernacular, SetDefaultAnalysis """ self._EnsureWriteEnabled() ws_obj = self._ResolveWS(ws) if not ws_obj: raise FP_WritingSystemError(str(ws)) # Verify it's a vernacular WS vern_tags = self._GetAllVernacularWSTags() if ws_obj.Id not in vern_tags: raise FP_ParameterError(f"Writing system '{ws_obj.Id}' is not a vernacular writing system") # Set as default self.project.lp.DefaultVernacularWritingSystem = ws_obj @OperationsMethod def SetDefaultAnalysis(self, ws): """ Set the default analysis writing system. Args: ws: IWritingSystemDefinition object or language tag (str) Raises: FP_ReadOnlyError: If project is not opened with write enabled FP_NullParameterError: If ws is None FP_WritingSystemError: If writing system not found FP_ParameterError: If ws is not an analysis writing system Example: >>> # Set by object >>> ws = list(project.WritingSystems.GetAnalysis())[0] >>> project.WritingSystems.SetDefaultAnalysis(ws) >>> # Set by language tag >>> project.WritingSystems.SetDefaultAnalysis("en") Notes: - The writing system must be in the analysis list - This is the primary writing system for glosses and definitions - Used as fallback when no specific WS is specified See Also: GetDefaultAnalysis, SetDefaultVernacular """ self._EnsureWriteEnabled() ws_obj = self._ResolveWS(ws) if not ws_obj: raise FP_WritingSystemError(str(ws)) # Verify it's an analysis WS anal_tags = self._GetAllAnalysisWSTags() if ws_obj.Id not in anal_tags: raise FP_ParameterError(f"Writing system '{ws_obj.Id}' is not an analysis writing system") # Set as default self.project.lp.DefaultAnalysisWritingSystem = ws_obj @OperationsMethod def GetDefaultVernacular(self): """ Get the default vernacular writing system. Returns: IWritingSystemDefinition: The default vernacular writing system Example: >>> ws = project.WritingSystems.GetDefaultVernacular() >>> name = project.WritingSystems.GetDisplayName(ws) >>> tag = project.WritingSystems.GetLanguageTag(ws) >>> print(f"Default vernacular: {name} ({tag})") Default vernacular: Kalaba (qaa-x-kal) Notes: - This is the primary vernacular writing system - Used as default for lexical entries - Every project must have a default vernacular WS See Also: SetDefaultVernacular, GetDefaultAnalysis """ return self.project.lp.DefaultVernacularWritingSystem @OperationsMethod def GetDefaultAnalysis(self): """ Get the default analysis writing system. Returns: IWritingSystemDefinition: The default analysis writing system Example: >>> ws = project.WritingSystems.GetDefaultAnalysis() >>> name = project.WritingSystems.GetDisplayName(ws) >>> tag = project.WritingSystems.GetLanguageTag(ws) >>> print(f"Default analysis: {name} ({tag})") Default analysis: English (en) Notes: - This is the primary analysis writing system - Used as default for glosses and definitions - Every project must have a default analysis WS See Also: SetDefaultAnalysis, GetDefaultVernacular """ return self.project.lp.DefaultAnalysisWritingSystem # --- Utility Methods --- @OperationsMethod def GetDisplayName(self, ws): """ Get the display name (UI label) for a writing system. Args: ws: IWritingSystemDefinition object or language tag (str) Returns: str: Display name (e.g., "English", "Kalaba") Raises: FP_NullParameterError: If ws is None FP_WritingSystemError: If writing system not found Example: >>> ws = list(project.WritingSystems.GetAll())[0] >>> name = project.WritingSystems.GetDisplayName(ws) >>> print(name) English >>> # By language tag >>> name = project.WritingSystems.GetDisplayName("en") >>> print(name) English See Also: GetLanguageTag, GetAll """ ws_obj = self._ResolveWS(ws) if not ws_obj: raise FP_WritingSystemError(str(ws)) return ws_obj.DisplayLabel or ws_obj.Id @OperationsMethod def GetLanguageTag(self, ws): """ Get the language tag (BCP 47 identifier) for a writing system. Args: ws: IWritingSystemDefinition object Returns: str: Language tag (e.g., "en", "fr", "qaa-x-kal") Raises: FP_NullParameterError: If ws is None Example: >>> ws = list(project.WritingSystems.GetVernacular())[0] >>> tag = project.WritingSystems.GetLanguageTag(ws) >>> print(tag) qaa-x-kal Notes: - Returns BCP 47 language tag - Format: language[-script][-region][-variant][-extension] - Examples: "en", "en-US", "qaa-x-kal", "zh-Hans" See Also: GetDisplayName, Exists """ self._ValidateParam(ws, "ws") return ws.Id @OperationsMethod def Exists(self, language_tag): """ Check if a writing system with the given language tag exists and is active. Args: language_tag (str): Language tag to check (e.g., "en", "qaa-x-kal") Returns: bool: True if writing system exists and is active, False otherwise Raises: FP_NullParameterError: If language_tag is None Example: >>> if project.WritingSystems.Exists("en"): ... print("English writing system is active") English writing system is active >>> if not project.WritingSystems.Exists("ar"): ... print("Arabic writing system not found") Arabic writing system not found Notes: - Only checks active (vernacular or analysis) writing systems - Comparison is case-insensitive - Handles both '-' and '_' in tags See Also: GetAll, Create, Delete """ self._ValidateParam(language_tag, "language_tag") return self._GetWSByTag(language_tag) is not None @OperationsMethod def GetBestString(self, string_obj): """ Extract the best analysis or vernacular string from a MultiString or MultiUnicode object. This method intelligently selects the most appropriate string alternative from a multi-writing-system text object, preferring analysis writing systems first, then falling back to vernacular writing systems. Args: string_obj: IMultiUnicode or IMultiString object containing text in multiple writing systems Returns: str: The best available string, or empty string if no valid text found Raises: FP_NullParameterError: If string_obj is None FP_ParameterError: If string_obj is not IMultiUnicode or IMultiString Example: >>> # Get the best string from a lexeme form >>> entry = project.LexEntries.GetEntry("test") >>> best_text = project.WritingSystems.GetBestString(entry.LexemeFormOA.Form) >>> print(best_text) test >>> # Get best gloss from a sense >>> sense = entry.SensesOS[0] >>> best_gloss = project.WritingSystems.GetBestString(sense.Gloss) >>> print(best_gloss) a sample word Notes: - Prefers analysis writing systems over vernacular - Returns empty string for "***" (FLEx's null string marker) - Useful for extracting displayable text from multi-WS fields - This is a convenience method for common text extraction patterns See Also: GetAll, GetAnalysis, GetVernacular """ self._ValidateParam(string_obj, "string_obj") # Import types locally to avoid circular imports from SIL.LCModel.Core.KernelInterfaces import IMultiUnicode, IMultiString if not isinstance(string_obj, (IMultiUnicode, IMultiString)): raise FP_ParameterError("GetBestString: string_obj must be IMultiUnicode or IMultiString") # Get the best alternative (analysis preferred, then vernacular) s = string_obj.BestAnalysisVernacularAlternative.Text # Return empty string for FLEx's null marker "***" return "" if s == "***" else s # --- Private Helper Methods --- def _GetAllVernacularWSTags(self): """ Get set of all vernacular writing system tags. Returns: set: Set of vernacular WS language tags """ return set(self.project.lp.CurVernWss.split()) def _GetAllAnalysisWSTags(self): """ Get set of all analysis writing system tags. Returns: set: Set of analysis WS language tags """ return set(self.project.lp.CurAnalysisWss.split()) def _NormalizeLangTag(self, language_tag): """ Normalize language tag for comparison (lowercase, underscores to hyphens). Args: language_tag (str): Language tag to normalize Returns: str: Normalized tag """ return language_tag.replace("_", "-").lower() def _GetWSByTag(self, language_tag): """ Get writing system object by language tag. Args: language_tag (str): Language tag Returns: IWritingSystemDefinition or None: Writing system or None if not found """ normalized_tag = self._NormalizeLangTag(language_tag) for ws in self.project.project.ServiceLocator.WritingSystems.AllWritingSystems: if self._NormalizeLangTag(ws.Id) == normalized_tag: return ws return None def _GetWSByHandle(self, handle): """ Get writing system object by handle. Args: handle (int): Writing system handle Returns: IWritingSystemDefinition or None: Writing system or None if not found """ for ws in self.project.project.ServiceLocator.WritingSystems.AllWritingSystems: if ws.Handle == handle: return ws return None def _ResolveWS(self, ws): """ Resolve a writing system parameter to a WS object. Args: ws: Either IWritingSystemDefinition object or language tag (str) Returns: IWritingSystemDefinition or None: Writing system object or None """ self._ValidateParam(ws, "ws") if isinstance(ws, str): return self._GetWSByTag(ws) else: # Already a writing system object return ws @OperationsMethod def Duplicate(self, item_or_hvo, insert_after=True): """ Duplicate operation is not applicable for writing systems. Writing systems are project-level configuration objects with unique identifiers and cannot be duplicated in the traditional sense. Raises: NotImplementedError: Always raised - writing systems cannot be duplicated. Notes: - Use Create() to create a new writing system with similar properties - Writing systems have unique language tags (BCP 47) - Each writing system must have a unique identifier - Duplication would create ambiguity in the WS system See Also: Create, Delete, GetAll """ raise NotImplementedError("Writing systems cannot be duplicated. Use Create() to create a new writing system.") # ========== SYNC INTEGRATION METHODS ========== @OperationsMethod def GetSyncableProperties(self, item): """ Get syncable properties - NOT IMPLEMENTED for writing systems. Writing systems are linguistic configuration unique to each project and should not be synced between projects. Raises: NotImplementedError: Writing systems are not syncable """ raise NotImplementedError( "Writing systems cannot be synced between projects. " "Writing systems are linguistic configuration unique to each project." ) @OperationsMethod def CompareTo(self, item1, item2, ops1=None, ops2=None): """ Compare writing systems - NOT IMPLEMENTED. Writing systems are linguistic configuration unique to each project and should not be synced between projects. Raises: NotImplementedError: Writing systems are not syncable """ raise NotImplementedError( "Writing systems cannot be compared for sync. " "Writing systems are linguistic configuration unique to each project." )