#To edit and compare internal_properties, use WINDEV integrated tools. #Internal properties refer to the properties of controls in windows, reports, etc. info : name : CSubtitle major_version : 30 minor_version : 0 type : 4 description : "" subtype : 0 options : 256 class : identifier : 0x181e7a1f02f43ce8 internal_properties : HwAAAB4AAAD+1kTo6KJy2WQU0Y4fbFD6aQ7NWfD4KKkN7Ml67nIqLXIRxJ1vjfJ1 code_elements : type_code : 10 p_codes : - code : |1+ /* Copyright 2025 Alexandre Leclerc. MPL 2.0. See https://mozilla.org/MPL/2.0/. */ // Allow to read and write SRT files... CSubtitle est une Classe public m_sFilename est une chaine m_tabContent est un tableau local de CSubtitleEntry // CAUTION: "local" and not "dynamic": When the class is copied, each record must be copied and unique (due to some destructive processes). m_nMaxCharPerLine est un entier = 60 // When splitting sentences, use this a max number of characters there should be per line fin TextFormat est une énumération tfContinuousText = 1 tfParagraphOnTimestampBreak = 2 tfParagraphOnSentence = 3 fin ETimecodeRangeAction est une énumération traKeepEntry // Will keep the whole entry traRemoveEntry // Will remove the whole entry traTrimEntry // Will trim the entry, if it is at least 1000ms long in duration fin type : 131072 procedures : - name : Constructeur procedure_id : 1737960779823791336 type_code : 27 code : |1+ procédure Constructeur() type : 589824 - name : Destructeur procedure_id : 1737960779823856872 type_code : 28 code : |1+ procédure Destructeur() type : 655360 - name : LoadFromFile procedure_id : 1737960779823922408 type_code : 12 code : |1+ // Résumé : // Paramètres : // sFilename (chaîne ANSI) : // Valeur de retour : // booléen : // procédure LoadFromFile(sFilename est chaine) si pas fFichierExiste(sFilename) ALORS ErreurDéclenche(101,"File does not exist.") renvoyer faux FIN // Validate file format sFormat est une chaine = minuscule(fExtraitChemin(sFilename,fExtension)) si pas sFormat dans (".srt", ".sbv") alors ErreurDéclenche(102,"This is not an supported format (.SRT or .SBV files).") RENVOYER Faux FIN // Open file content sContent est une chaine = UTF8VersChaîne(fChargeTexte(sFilename)) si sContent = "" _ET_ ErreurDétectée ALORS erreur(erreurinfo(errComplet)) renvoyer faux FIN m_sFilename = sFilename tableausupprimetout(m_tabContent) // Create entries selon sFormat CAS ".srt" : _LoadDecodeSRT(sContent) cas ".sbv" : _LoadDecodeSBV(sContent) FIN renvoyer Vrai type : 458752 - name : ExportToSRT procedure_id : 1737960779823987944 type_code : 12 code : |1+ // Résumé : // Paramètres : // sFilename (chaîne ANSI) : // Valeur de retour : // booléen : // procédure ExportToSRT(sFilename est chaîne) si m_tabContent..Occurrence = 0 alors ErreurDéclenche(103,"There is no content to save.") renvoyer faux FIN // Build content to save sContent est une chaine pour tout e de m_tabContent sContent += [rc+rc] + e.m_nID + rc + e.TimecodeToString(CSubtitleEntry.tfSRT) + rc + e.m_sText FIN renvoyer fSauveTexte(sFilename,ChaîneVersUTF8(sContent)) type : 458752 - name : ContentToNumberedText procedure_id : 1737960779824053480 type_code : 12 code : |1+ // Résumé : Will return all the content in the form of a text with numbers matching the IDs of the timestamps. // Paramètres : // eParagraphFormat (CSRTFile.TextFormat) : Optional paragraph formatting. // Valeur de retour : // chaîne ANSI : // procédure ContentToNumberedText(eParagraphFormat est un TextFormat = tfContinuousText) : chaine sContent est une chaîne SI eParagraphFormat = tfParagraphOnTimestampBreak ALORS POUR TOUT e,i DE m_tabContent // Is this clip consecutive with the next one? If no, we insert a paragraph break sContent += [" "] + "{" + e.m_nID + "}" + [" "] + e.TextAsSingleLine() SI i+1 <= m_tabContent..Occurrence ALORS SI PAS m_tabContent[i].m_duTimecodeEnd = m_tabContent[i+1].m_duTimecodeStart ALORS sContent += RC FIN FIN FIN sContent = remplace(sContent,rc+" ",rc) SINON POUR TOUT e DE m_tabContent sContent += [" "] + "{" + e.m_nID + "}" + [" "] + e.TextAsSingleLine() FIN SI eParagraphFormat = tfParagraphOnSentence ALORS sContent = Remplace(sContent, ". ", "."+CRLF) sContent = Remplace(sContent, "! ", "!"+CRLF) sContent = Remplace(sContent, "? ", "?"+CRLF) FIN FIN // Delete all unbreakable spaces that might be in the file. This messes things up for nothing // We also cleanup all double spaces sContent = Remplace(sContent," "," ") // Delete unbreakable spaces sContent = Remplace(sContent," "," ") // Delete all double spaces sContent = Remplace(sContent,RC+" ",RC) // Delete all cases where a new line starts with a space renvoyer sContent type : 458752 - name : ContentFromNumberedText procedure_id : 1737960779824119016 type_code : 12 code : |1+ // Résumé : Will take a numbered text with nested IDs and put back the text as the next SRT content. // Paramètres : // sTextBlock (chaîne ANSI) : Text block to use // bDeleteEntriesNotFound (booléen - valeur par défaut=0) : If an entry is not found in the text block, it will be deleted from the SRT file. Otherwise it will stay there. // bRemoveCommentsAndCRLF (booléen - valeur par défaut=1) : Considers a ligne begining with a "#" as a comment. Removes all CRLF in the text to have it as a valid TextBlock. This feature is useful if the textblock has been reviewed and formated during revision. // Valeur de retour : // Aucune // procédure ContentFromNumberedText(local sTextBlock est une chaine, bDeleteEntriesNotFound est un booléen = Faux, bRemoveCommentsAndCRLF est un booléen = Vrai) si bRemoveCommentsAndCRLF ALORS sNewText est une chaine s est une chaine // All # are considered as comment, even if they are MD titles. (This is on purpose). // An easy way to go around this is to simply add one space before #, and it will be treated as text part of the subtitles. POUR TOUTE CHAÎNE s DE sTextBlock SÉPARÉE PAR rc si s [= "#" alors continue FIN sNewText += s + rc FIN // Remove all markdown from the text sNewText = MarkdownVersTexte(sNewText) // We could also remove all HTML code from the text... but have to test that later. sNewText = HTMLVersTexte(sNewText) // Remove double spaces that might have been created... sNewText = Remplace(sNewText, " ", " ") // We are done with removing special formating sTextBlock = sNewText FIN // Find each entry in the text block to get it's updated value pour tout e de m_tabContent sID est une chaine = "{"+e.m_nID+"}" // Find the entry point i est un entier = position(sTextBlock, sID) si i = 0 ALORS si bDeleteEntriesNotFound ALORS TableauSupprime(m_tabContent, ElémentCourant) FIN continue FIN // Find the end of the entry point o est un entier = position(sTextBlock, "{", i+1) si o = 0 alors o = taille(sTextBlock)+1 FIN sSubtitle est une chaine = sansespace(sTextBlock[[i+Taille(sID) À o-1]]) // Update the content e.p_sText = sSubtitle FIN type : 458752 - name : ClipMergeOptimization procedure_id : 1737960779824250088 type_code : 12 code : |1+ // Résumé : Will try to merge clips in an optimized way, respecting a maximum length duration. // Paramètres : // nMaxClipDuration (entier) : Maximum clip duration (in sec) that cannot be exceded. // TranslationSRT (CSRTFile dynamique - valeur par défaut=0) : // bIgnoreSentenceOptimization (booléen - valeur par défaut=0) : // Valeur de retour : // Aucune // procédure ClipMergeOptimization(nMaxClipDuration est une entier, TranslationSRT est un CSubtitle dynamique = Null, bIgnoreSentenceOptimization est un booléen = Faux) // - We can try to optimize clip length for Dubbing in ElevenLabs. This will merge clips together to have a better length and a better rendering. // - Logic we will try to accomplish: // - Make a clip "stop" if there is a "end" punctuation to the clip (.!?) [option to disable if desired?]. // - Make a clip stop before the maximum clip duration is reached (important). // - Do not merge clips that are not immediately consecutive. // This is not our responsability to check if both SRT files are compatible, but we want to avoid a crash with different index count. si TranslationSRT <> Null _ET_ TranslationSRT.m_tabContent..Occurrence <> m_tabContent..Occurrence ALORS retour FIN e est un CSubtitleEntry dynamique bMerged est un booléen BOUCLE SI m_tabContent..Occurrence <= 1 ALORS SORTIR FIN i est un entier = 1 bMerged = Faux BOUCLE // Have we reached the end? si i+1 > m_tabContent..Occurrence ALORS sortir FIN // Is this clip consecutive with the next one? If no, we cannot merge. si pas m_tabContent[i].m_duTimecodeEnd = m_tabContent[i+1].m_duTimecodeStart alors i++ continuer fin // Can both clip be merge length-wise? If it is too long, we cannot merge. d est une durée = m_tabContent[i].TimecodeDuration() + m_tabContent[i+1].TimecodeDuration() si d..EnSecondes > nMaxClipDuration alors i++ CONTINUER FIN // Avoid a merge? — Is clip punctuation too good for a merge? (Check the next one if it is also good.) si bIgnoreSentenceOptimization = Faux alors // Optimize strings for verification. We eliminate quotations are they are considered “transparent” punctuation. (And non-breaking, narrow non-breaking and thin space.) s1 est une chaine = sansespace( remplace(m_tabContent[i].m_sText, ["""", "«", "»", "“", "”", CaractUnicode(0x00A0), CaractUnicode(0x202F), CaractUnicode(0x2009)],"") ) s2 est une chaîne = SansEspace( Remplace(m_tabContent[i+1].m_sText, ["""", "«", "»", "“", "”", CaractUnicode(0x00A0), CaractUnicode(0x202F), CaractUnicode(0x2009)],"") ) si droite(s1,1) dans (".","!","?") ALORS // Unless the other sentence ends very well, we do not merge. SI pas droite(s2,1) DANS (".","!","?") ALORS i++ CONTINUER FIN sinon // If this is nevertheless a not too bad spot to end, and there is nothing better after, we stop here too SI droite(s1,1) DANS (";",",",":","—","–") ALORS // So if we have a punctuation here, but nothing in the next one, we do not merge. We keep this ending SI PAS droite(s2,1) DANS (".","!","?",";",",",":","—","–") ALORS i++ CONTINUER FIN FIN FIN FIN // Merge // At this point me made all the tests we desired and we are good to merge m_tabContent[i].MergeWith(m_tabContent[i+1]) tableausupprime(m_tabContent,i+1) // If we have a translation SRT, we have to sync all the merges there too. We hope it works for it too. si TranslationSRT <> Null alors TranslationSRT.m_tabContent[i].MergeWith(TranslationSRT.m_tabContent[i+1]) TableauSupprime(TranslationSRT.m_tabContent,i+1) FIN // We do not go to the next element as this element is still active for another potential merge bMerged = Vrai FIN À FAIRE TANTQUE bMerged // We change the numbering of the IDs for the SRT file (in case the user wants to save this as an SRT file) pour tout e,i de m_tabContent e.m_nID = i FIN type : 458752 - name : ExportToSBV procedure_id : 1737960779824315624 type_code : 12 code : |1+ // Résumé : Export to SubViewer format // Paramètres : // sFilename (chaîne ANSI) : // Valeur de retour : // booléen : // procédure ExportToSBV(sFilename est chaîne) SI m_tabContent..Occurrence = 0 ALORS ErreurDéclenche(103,"There is no content to save.") RENVOYER Faux FIN // Build content to save // https://wiki.videolan.org/SubViewer/ // sContent est une chaîne POUR TOUT e DE m_tabContent sContent += [RC+RC] + e.TimecodeToString(CSubtitleEntry.tfSVB) + RC + e.m_sText FIN // To write in version 2 of subviewer, replace CRLF with "[br]". RENVOYER fSauveTexte(sFilename,ChaîneVersUTF8(sContent)) type : 458752 - name : _LoadDecodeSRT procedure_id : 1737960779824381160 type_code : 12 code : |1+ // Résumé : // Paramètres : // sContent (chaîne ANSI) : // Valeur de retour : // Aucune // procédure protégée _LoadDecodeSRT(sContent est une chaine) e est un CSubtitleEntry dynamique // Load each lines on the SRT file and fill the content. POUR TOUTE CHAÎNE sLine DE sContent SÉPARÉE PAR RC // New srt entry (his is a integer number) SI Val(sLine) > 0 _ET_ Val(sLine) = sLine ALORS // Add any previous entry SI e <> Null _ET_ e.m_nID > 0 ALORS TableauAjoute(m_tabContent,e) FIN // Create new entry e = allouer un CSubtitleEntry e.m_nID = sLine CONTINUE FIN // Is this the timecode line? SI sLine [=] " --> " ALORS e.TimecodeFromString(CSubtitleEntry.tfSRT,sLine) CONTINUE FIN // This is obviously text line SI sLine <> "" ALORS e.m_sText += [RC] + sLine FIN FIN // Just in case the last SRT entry was not written (because the last line has no empty line after) SI e <> Null _ET_ e.m_nID > 0 ALORS TableauAjoute(m_tabContent,e) e = Null FIN type : 458752 - name : _LoadDecodeSBV procedure_id : 1737960779824446696 type_code : 12 code : |1+ // Résumé : // Paramètres : // sContent (chaîne ANSI) : // Valeur de retour : // Aucune // procédure protégée _LoadDecodeSBV(sContent est une chaine) e est un CSubtitleEntry dynamique = allouer un CSubtitleEntry // SBV format is expected to have: // - first line as timecode // - other lines as text // - empty line as end of timecode indicator // Load each lines on the SRT file and fill the content. bSkip est un booléen POUR TOUTE CHAÎNE sLine DE sContent SÉPARÉE PAR RC // Skipping potential "additionnal information" section and control codes si sLine = "[INFORMATION]" alors bSkip = Vrai continue FIN si bSkip alors si sLine = "[END INFORMATION]" alors bSkip = Faux FIN continue FIN si sLine [= "[" alors continue FIN // Empty line (new entry?) SI sLine = "" ALORS si e <> Null _ET_ e.m_duTimecodeEnd..EnMillisecondes <> 0 alors // Add any previous entry e.m_nID = TableauOccurrence(m_tabContent) + 1 // SBV files do not have an ID. We create one because we need it internally for different processses. TableauAjoute(m_tabContent,e) e = Null fin // Create new entry si e = Null alors e = allouer un CSubtitleEntry fin CONTINUE FIN // The first non-empty line is always the timecode si e.m_duTimecodeEnd..EnMillisecondes = 0 alors // Transform the sbv timecode to SRT timecode e.TimecodeFromString(CSubtitleEntry.tfSVB,sLine) continue FIN // This is a text line e.m_sText += [RC] + replace(sLine,"[br]",crlf) // SBV version 2 allows CRLF to be replaced with [br] placeholder. We want the text as SRT. FIN // Just in case the last SRT entry was not written (because the last line has no empty line after) SI e <> Null _ET_ e.m_duTimecodeEnd..EnMillisecondes <> 0 ALORS e.m_nID = TableauOccurrence(m_tabContent) + 1 // SBV files do not have an ID. We create one because we need it internally for different processses. TableauAjoute(m_tabContent,e) e = Null FIN type : 458752 - name : CalculateTimecodeContinuity procedure_id : 1737960779824512232 type_code : 12 code : |1+ // Résumé : Calculate timecode continuity. // Paramètres : // Aucun // Valeur de retour : // Aucune // procédure CalculateTimecodeContinuity() pour i = 2 _à_ m_tabContent..Occurrence m_tabContent[i].m__bTimecodeNotContinuous = m_tabContent[i-1].m_duTimecodeEnd <> m_tabContent[i].m_duTimecodeStart FIN type : 458752 - name : GetLastTimecodeEnd procedure_id : 1737960779824577768 type_code : 12 code : |1+ // Résumé : Get the last timecode ending time. // Paramètres : // Aucun // Valeur de retour : // durée : // procédure GetLastTimecodeEnd() : durée si m_tabContent..Occurrence > 0 alors renvoyer m_tabContent[m_tabContent..Occurrence].m_duTimecodeEnd FIN renvoyer "" type : 458752 - name : TimecodesIdenticalWith procedure_id : 1737960779824643304 type_code : 12 code : |1+ // Résumé : Check if the timecodes are identical with another file. // Paramètres : // c (CSRTFile dynamique) : Other file to check against. // Valeur de retour : // booléen : True if both files have identical timecodes. // procédure TimecodesIdenticalWith(c est un CSubtitle dynamique) : booléen si c = Null alors renvoyer vrai FIN SI m_tabContent..Occurrence <> c.m_tabContent..Occurrence ALORS renvoyer Faux FIN e est un CSubtitleEntry dynamique POUR TOUT e,i DANS m_tabContent SI e.m_duTimecodeStart <> c.m_tabContent[i].m_duTimecodeStart _OU_ e.m_duTimecodeEnd <> c.m_tabContent[i].m_duTimecodeEnd ALORS renvoyer Faux FIN FIN renvoyer vrai type : 458752 - name : ExportToCSV procedure_id : 1737960779824708840 type_code : 12 code : |1+ // Résumé : Will export the file to CSV compatible with ElevenLabs Dubbing project. // Paramètres : // sFilename (chaîne ANSI) : Filename to write to. // nOptimizeClips (entier - valeur par défaut=-1) : -1 if clips should not be opimized, all other values will trigger ClipMergeOptimization. // cTranslation (CSRTFile dynamique - valeur par défaut=0) : Translation file to add to the export. Both files must have identical timecodes. // Valeur de retour : // booléen : // procédure ExportToCSV(sFilename est une chaîne, nOptimizeClips est un entier = -1, cTranslation est un CSubtitle dynamique = Null) : booléen si pas TimecodesIdenticalWith(cTranslation) alors ErreurDéclenche(1,"Both subtitle files must have identical timecodes. The selected files do not match.") renvoyer faux FIN si nOptimizeClips > -1 alors ClipMergeOptimization(nOptimizeClips, cTranslation) FIN // Now we create the CSV file // speaker,start_time,end_time,transcription,translation sCSV est une chaîne = `speaker,start_time,end_time,transcription,translation` POUR TOUT e,i DANS m_tabContent s est une chaine = e.m__sSpeaker = "" ? "Speaker 1" sinon e.m__sSpeaker sCSV += [RC] + ChaîneConstruit(`"%1","%2","%3","%4","%5"`, ... s, ... e.TimecodeToString(CSubtitleEntry.tfSRT,CSubtitleEntry.tpStart), ... e.TimecodeToString(CSubtitleEntry.tfSRT,CSubtitleEntry.tpEnd), ... Remplace(e.TextAsSingleLine(),`"`,`""`), ... cTranslation <> Null ? Remplace(cTranslation.m_tabContent[i].TextAsSingleLine(),`"`,`""`) sinon "" ) FIN // Save the CSV file renvoyer fSauveTexte(sFilename,ChaîneVersUTF8(sCSV)) type : 458752 - name : RemoveTimecodesOutOfRange procedure_id : 1737960779824774376 type_code : 12 code : |1+ // Résumé : Will delete all timecode entries that are not withing the desired range. This is a destructive process, so work on a copy. // Paramètres : // duStart (durée) : Start point (inclusive). Before this point, everything will be deleted. // duEnd (durée) : End point (inclusive). After this point, everything will be deleted. // duTimecodeOffest (durée - valeur par défaut=0) : If timecodes should be corrected, specify the offset. It will be deducted from the timecodes. // eActionOnStraddlingTimecodes (CSRTFile.ETimecodeRangeAction) : Action to be taken if a timecode is stranddling on the range. Default action is to trim (adjust) the timecode so to fit. // Valeur de retour : // Aucune // procédure RemoveTimecodesOutOfRange(duStart est une durée, duEnd est une durée, duTimecodeOffest est une durée = 0, eActionOnStraddlingTimecodes est une ETimecodeRangeAction = traTrimEntry) // Prune out timecodes pour i = m_tabContent..Occurrence _A_ 1 pas -1 e est un CSubtitleEntry dynamique <- m_tabContent[i] // Entry is completely outside of the range si e.m_duTimecodeEnd < duStart _OU_ e.m_duTimecodeStart > duEnd ALORS // Remove TableauSupprime(m_tabContent,i) continue fin // Starting point is outside of the starting range si e.m_duTimecodeStart < duStart alors selon eActionOnStraddlingTimecodes CAS traRemoveEntry: TableauSupprime(m_tabContent,i) CAS traTrimEntry: e.m_duTimecodeStart = duStart si (e.m_duTimecodeEnd - e.m_duTimecodeStart) < 1000 alors TableauSupprime(m_tabContent,i) FIN FIN continue fin // Ending point is outside of the ending range SI e.m_duTimecodeEnd > duEnd ALORS SELON eActionOnStraddlingTimecodes CAS traRemoveEntry: TableauSupprime(m_tabContent,i) CAS traTrimEntry: e.m_duTimecodeEnd = duEnd SI e.m_duTimecodeEnd - e.m_duTimecodeStart < 1000 ALORS TableauSupprime(m_tabContent,i) FIN FIN continue FIN FIN // Correct timecode si duTimecodeOffest..EnMillisecondes > 0 alors pour tout e de m_tabContent e.m_duTimecodeStart = e.m_duTimecodeStart - duTimecodeOffest e.m_duTimecodeEnd = e.m_duTimecodeEnd - duTimecodeOffest FIN FIN type : 458752 - name : CalculateSpeakerChange procedure_id : 1738311005448731556 type_code : 12 code : |1+ // Résumé : Calculate speaker switching. // Paramètres : // sSpeaker1 (chaîne ANSI - valeur par défaut="Speaker 1") : Name of Speaker 1 // sSpeaker2 (chaîne ANSI - valeur par défaut="Speaker 2") : Name of Speaker 2 // Valeur de retour : // Aucune // procédure CalculateSpeakerChange(sSpeaker1 est une chaine = "Speaker 1", sSpeaker2 est une chaine = "Speaker 2") bSwitch est un booléen n est un entier = m_tabContent..Occurrence POUR i = 1 _À_ n m_tabContent[i].m__sSpeaker = bSwitch = Faux ? sSpeaker1 SINON sSpeaker2 si i+1 <= n _ET_ m_tabContent[i].m_duTimecodeEnd <> m_tabContent[i+1].m_duTimecodeStart ALORS bSwitch = pas bSwitch FIN FIN type : 458752 - name : TimecodesToYAMLSequenceOfCompactMappings procedure_id : 1740092690090315318 type_code : 12 code : |1+ // Résumé : Exports the timecode information to YAML document // Paramètres : // Aucun // Valeur de retour : // chaîne ANSI : // // y (YAML) : Yaml variable to fill with the timecodes (direct filling, considered as an array) procédure TimecodesToYAMLSequenceOfCompactMappings() : chaine LF est un chaine = Caract(10) s est une chaine pour tout e,i de m_tabContent s += [LF] + "- " + "{" + " id: " + e.m_nID + ", i: " + DuréeVersChaîne(e.m_duTimecodeStart,"HH:MM:SS.CCC") + ", o: " + DuréeVersChaîne(e.m_duTimecodeEnd,"HH:MM:SS.CCC") + " }" FIN renvoyer s type : 458752 - name : TimecodesFromYAML procedure_id : 1740112968090671115 type_code : 12 code : |1+ // Résumé : Import timecodes from YAML document. // Paramètres : // y (YAML) : YAML node containing a sequence of mappings (id, i, o) // Valeur de retour : // booléen : // procédure TimecodesFromYAML(y est un yaml) // We allow overriding existing timecodes, only if the count matches bNewEntries est un booléen = Vrai si m_tabContent..Occurrence > 0 alors si m_tabContent..Occurrence <> y..Occurrence alors ErreurDéclenche(100, "The number of timecodes does not match. Unable to import the new timecodes.") renvoyer faux FIN bNewEntries = Faux FIN pour i = 1 _a_ y..Occurrence si bNewEntries alors ajoute(m_tabContent) FIN m_tabContent[i].m_nid = y[i].id m_tabContent[i].m_duTimecodeStart = ChaîneVersDurée(y[i].i..Valeur, y[i].i..Type = 25 ? "HHMMSSLLL" SINON "HH:MM:SS.LLL") m_tabContent[i].m_duTimecodeEnd = ChaîneVersDurée(y[i].o..Valeur, y[i].i..Type = 25 ? "HHMMSSLLL" SINON "HH:MM:SS.LLL") FIN renvoyer vrai type : 458752 procedure_templates : [] property_templates : [] code_parameters : internal_properties : HwAAAB4AAAB7MB8NZB5rGUbyk77+IjQnJ74vm430Ar3yq0zmP05sGBBw0ur17uG6ZWry original_name : Classe1 resources : string_res : identifier : 0x17e5b16407d0f1fb internal_properties : HwAAAB4AAAA809Qj/IAi+r8QXyrnW7sarQeYORCUjKBkmMeTFexSj5AuvTfTUpN0Eg== custom_note : internal_properties : HwAAAB4AAADnl3uxgA6ylw4vtqUKEOJQD3VAAOKeNUmhPNojcRFoDpHEcUyYAw==