{"id":55000438,"date":"2024-08-01T00:00:00","date_gmt":"2024-08-14T14:55:37","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=438"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"PowerPointUebersetzung_per_COMAddIn","status":"publish","type":"post","link":"https:\/\/vbentwickler.de\/PowerPointUebersetzung_per_COMAddIn\/","title":{"rendered":"PowerPoint-&Uuml;bersetzung per COM-Add-In"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg08.met.vgwort.de\/na\/6e9320f3ce36462b82dda97439c9ae63\" width=\"1\" height=\"1\" alt=\"\"><b>Im Artikel &#8220;PowerPoint: Texte automatisiert &uuml;bersetzen&#8221; (www.vbentwickler.de\/437) haben wir VBA-Code produziert, mit dem wir alle Abs&auml;tze aller Folien in einem PowerPoint-Dokument automatisch &uuml;bersetzen k&ouml;nnen. Dabei nutzen wir den Dienst DeepL. Leider m&uuml;ssen wir, um diesen Code in einem PowerPoint-Dokument verwenden zu k&ouml;nnen, das Modul erst in das jeweilige Dokument integrieren. Wenn man oft PowerPoint-Folien &uuml;bersetzen muss, ist das recht aufw&auml;ndig. Da nehmen wir lieber den Aufwand in Kauf, einmal ein COM-Add-In f&uuml;r diesen Zweck zu programmieren, dass wir dann auch noch an Dich weitergeben k&ouml;nnen, damit Du es f&uuml;r Dich und Deine Mitarbeiter und\/oder Kunden einsetzen kannst.<\/b><\/p>\n<h2>Das Werkzeug f&uuml;r COM-Add-Ins: twinBASIC<\/h2>\n<p>Wie &uuml;blich nutzen wir das in der 32-Bit-Version sogar kostenlose Produkt twinBASIC f&uuml;r die Erstellung des COM-Add-Ins. Im Download findest Du, auch wenn Du Dir noch nicht die kostenpflichtige Version von twinBASIC gekauft hast, aber auch eine 64-Bit-Version der DLL, die das COM-Add-In enth&auml;lt. Da wir keine DLL-Funktionen nutzen, ist der Code f&uuml;r beide Varianten allerdings identisch.<\/p>\n<p>twinBASIC findest Du beispielsweise unter dem folgenden Link:<\/p>\n<pre>https:\/\/github.com\/twinbasic\/twinbasic\/releases\/tag\/beta-x-0585<\/pre>\n<p>Nach dem Start (es ist keine Installation n&ouml;tig) erscheint der Dialog aus Bild 1. Hier w&auml;hlen wir unter Samples den Eintrag <b>Sample 5. MyCOMAddin <\/b>aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_438_002.png\" alt=\"Auswahl des Projekttyps\" width=\"499,6267\" height=\"400,6353\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Auswahl des Projekttyps<\/span><\/b><\/p>\n<p>Anschlie&szlig;end erscheint der Dialog aus Bild 2, wo wir den Namen des Projekts festlegen &#8211; hier auf <b>amvPowerPointTranslator<\/b>. Danach finden wir bereits die Entwicklungsumgebung vor, in der wir die ersten grundlegenden &Auml;nderungen durchf&uuml;hren.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_438_001.png\" alt=\"Projektname f&uuml;r das neue Projekt\" width=\"424,6267\" height=\"248,3666\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Projektname f&uuml;r das neue Projekt<\/span><\/b><\/p>\n<h2>&Auml;nderungen am Beispielprojekt f&uuml;r das COM-Add-In<\/h2>\n<p>Als Erstes stellen wir den Klassennamen sowohl im Code der Klasse als auch f&uuml;r die Klassendatei auf <b>amvPowerPointTranslator<\/b> ein (siehe Bild 3). Nach dem &Auml;ndern im Modul speichern wir das Projekt zun&auml;chst, damit wir im n&auml;chsten Schritt das Modul schlie&szlig;en k&ouml;nnen, um den Namen der <b>.twin<\/b>-Datei anzupassen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_438_003.png\" alt=\"Eintragen des neuen Klassennamens\" width=\"549,6265\" height=\"275,683\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Eintragen des neuen Klassennamens<\/span><\/b><\/p>\n<h2>Verweis auf PowerPoint hinzuf&uuml;gen<\/h2>\n<p>Damit wir die PowerPoint-Elemente unter Verwendung von IntelliSense nutzen k&ouml;nnen, f&uuml;gen wir dem Projekt unter <b>Project Settings <\/b>im Bereich <b>Library References <\/b>einen Verweis auf die Bibliothek <b>Microsoft PowerPoint 16.0 Object Library <\/b>hinzu (siehe Bild 4).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_438_004.png\" alt=\"Auswahl der PowerPoint-Bibliothek\" width=\"499,6267\" height=\"221,0738\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Auswahl der PowerPoint-Bibliothek<\/span><\/b><\/p>\n<h2>Funktionen f&uuml;r die Registrierung des COM-Add-In anpassen<\/h2>\n<p>Die Beispielfunktionen zum Durchf&uuml;hren der Registrierung in der Datei <b>DllRegistration.twin <\/b>sind f&uuml;r den Einsatz in Access- und Excel-Anwendungen ausgelegt. Dies passen wir noch an. Dabei stellen wir vor allem die Konstante <b>AddinClassName <\/b>ebenfalls auf <b>amvPowerPointTranslator <\/b>ein.<\/p>\n<p>Au&szlig;erdem &auml;ndern wir die Konstante <b>RootRegistryFolder_Excel <\/b>auf den Namen <b>RootRegistryFolder_PowerPoint <\/b>und f&uuml;gen auch in dem Wert den Eintrag <b>PowerPoint <\/b>statt <b>Excel <\/b>ein. Die Konstante <b>RootRegistryFolder_Access <\/b>l&ouml;schen wir.<\/p>\n<p>In den beiden Funktionen <b>DllRegisterServer <\/b>und <b>DllUnregisterServer <\/b>ersetzen wir <b>RootRegistryFolder_Excel <\/b>durch <b>RootRegistryFolder_PowerPoint <\/b>und l&ouml;schen alle Eintr&auml;ge f&uuml;r <b>RootRegistryFolder_Access<\/b>. Das Ergebnis sieht in gek&uuml;rzter Form wie in Listing 1 aus.<\/p>\n<pre>Module DllRegistration\r\n     Const AddinProjectName<span style=\"color:blue;\"> As String<\/span> = VBA.Compilation.CurrentProjectName\r\n     Const AddinClassName<span style=\"color:blue;\"> As String<\/span> = \"amvPowerPointTranslator\"\r\n     Const AddinQualifiedClassName<span style=\"color:blue;\"> As String<\/span> = AddinProjectName & \".\" & AddinClassName\r\n     Const RootRegistryFolder_PowerPoint<span style=\"color:blue;\"> As String<\/span> = \"HKCU\\SOFTWARE\\Microsoft\\Office\\PowerPoint\\Addins\\\" _\r\n         & AddinQualifiedClassName & \"\\\"\r\n  \r\n     <span style=\"color:blue;\">Public <\/span>Function DllRegisterServer()<span style=\"color:blue;\"> As Boolean<\/span>\r\n         <span style=\"color:blue;\">On Error GoTo<\/span> RegError\r\n         <span style=\"color:blue;\">Dim <\/span>wscript<span style=\"color:blue;\"> As Object<\/span> = CreateObject(\"wscript.shell\")\r\n         wscript.RegWrite RootRegistryFolder_PowerPoint & \"FriendlyName\", AddinProjectName, \"REG_SZ\"\r\n         wscript.RegWrite RootRegistryFolder_PowerPoint & \"Description\", AddinProjectName, \"REG_SZ\"\r\n         wscript.RegWrite RootRegistryFolder_PowerPoint & \"LoadBehavior\", 3, \"REG_DWORD\"\r\n         ...\r\n     End Function\r\n  \r\n     <span style=\"color:blue;\">Public <\/span>Function DllUnregisterServer()<span style=\"color:blue;\"> As Boolean<\/span>\r\n         <span style=\"color:blue;\">On Error GoTo<\/span> RegError\r\n         <span style=\"color:blue;\">Dim <\/span>wscript<span style=\"color:blue;\"> As Object<\/span> = CreateObject(\"wscript.shell\")\r\n         wscript.RegDelete RootRegistryFolder_PowerPoint & \"FriendlyName\"\r\n         wscript.RegDelete RootRegistryFolder_PowerPoint & \"Description\"\r\n         wscript.RegDelete RootRegistryFolder_PowerPoint & \"LoadBehavior\"\r\n         wscript.RegDelete RootRegistryFolder_PowerPoint\r\n         ...\r\n     End Function\r\nEnd Module<\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Funktionen zum Registrieren und Deregistrieren<\/span><\/b><\/p>\n<h2>Klasse amvPowerPointTranslator anpassen<\/h2>\n<p>Auch den Inhalt der Datei <b>amvPowerPointTranslator.twin <\/b>passen wir an. Hier l&ouml;schen wir die Variable <b>applicationObject <\/b>durch <b>objPowerPoint <\/b>und legen ihren Datentyp auf <b>PowerPoint.Application <\/b>fest. Diese weisen wir in der Ereignisprozedur <b>OnConnection <\/b>wie folgt zu:<\/p>\n<pre><span style=\"color:blue;\">Sub <\/span>OnConnection(ByVal Application<span style=\"color:blue;\"> As Object<\/span>, _\r\n         ByVal ConnectMode<span style=\"color:blue;\"> As <\/span>ext_ConnectMode, _\r\n         ByVal AddInInst<span style=\"color:blue;\"> As Object<\/span>, _\r\n         ByRef custom<span style=\"color:blue;\"> As Variant<\/span>()) _\r\n     Implements IDTExtensibility2.OnConnection\r\n     <span style=\"color:blue;\">Set<\/span> objPowerPoint = Application\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>In der Prozedur <b>OnHelloWorldClicked <\/b>finden wir auch noch ein Vorkommen der Variablen <b>applicationObject<\/b>, das wir durch <b>objPowerPoint <\/b>ersetzen. Den Rest belassen wir erst einmal so, wie er ist, und kompilieren das Projekt erstmalig mit dem Befehl <b>File|Build<\/b>.<\/p>\n<p>Der Kompiliervorgang sollte genau wie die Registrierung nun erfolgreich verlaufen sein. Sollte das nicht der Fall, nutze entweder das Beispielprojekt aus dem Download oder gehe die bisherige Anleitung nochmals durch.<\/p>\n<p>Normalerweise sollte aber nun beim &Ouml;ffnen von PowerPoint ein Tab namens <b>twinBASIC Test <\/b>erscheinen und ein Klick auf die enthaltene Schaltfl&auml;che sollte die Meldung aus Bild 5 anzeigen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_438_005.png\" alt=\"Das COM-Add-In ist bereits startbereit.\" width=\"700\" height=\"126,8924\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: Das COM-Add-In ist bereits startbereit.<\/span><\/b><\/p>\n<h2>Funktion implementieren<\/h2>\n<p>Nun brauchen wir eigentlich erst einmal nur die Funktionen aus der L&ouml;sung aus dem Artikel <b>PowerPoint: Texte automatisiert &uuml;bersetzen <\/b>(<b>www.vbentwickler.de\/437<\/b>) in ein neues Modul im twinBASIC-Projekt einzuf&uuml;gen.<\/p>\n<p>Daf&uuml;r ben&ouml;tigen wir alle vier Module:<\/p>\n<ul>\n<li><b>mdlDeepL<\/b><\/li>\n<li><b>mdlJSON<\/b><\/li>\n<li><b>mdlJSONDOM<\/b><\/li>\n<li><b>mdlUebersetzen<\/b><\/li>\n<\/ul>\n<p>Diese k&ouml;nnen wir leider nicht per Drag and Drop einf&uuml;gen, daher m&uuml;ssen wir jeweils ein neues Modul erstellen und den Code des jeweiligen Moduls dort hineinkopieren.<\/p>\n<p>Neue Module erstellen wir wie in Bild 6 und f&uuml;llen dann die Inhalte der Module des VBA-Projekts der <b>PowerPoint<\/b>-Datei zwischen die Anweisungen <b>Module &#8230; <\/b>und <b>End Module <\/b>ein.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_438_006.png\" alt=\"Neues Modul hinzuf&uuml;gen\" width=\"649,627\" height=\"450,6844\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 6: Neues Modul hinzuf&uuml;gen<\/span><\/b><\/p>\n<p>Au&szlig;erdem ben&ouml;tigen wir noch zwei weitere Verweise auf die Bibliotheken <b>Microsoft Scripting Runtime <\/b>und <b>Microsoft XML, v6.0<\/b>, die wir wie oben gezeigt hinzuf&uuml;gen.<\/p>\n<p>Die Variable <b>objPowerPoint <\/b>nehmen wir nun aus der Klasse <b>amvPowerPointTranslator.twin <\/b>heraus, da wir dort nicht von anderen Modulen aus darauf zugreifen k&ouml;nnen, und f&uuml;gen sie dem Modul <b>mdlUebersetzen <\/b>hinzu:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>objPowerPoint<span style=\"color:blue;\"> As <\/span>PowerPoint.Application<\/pre>\n<h2>Aufruf der Prozedur Translate<\/h2>\n<p>Die gleich angepasste Prozedur <b>Translate<\/b> aus dem oben genannten PowerPoint-Artikel rufen wir auf, wenn der Benutzer auf den einzigen bisherigen Button des twinBASIC-Projekts klickt:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>OnHelloWorldClicked(Control<span style=\"color:blue;\"> As <\/span>IRibbonControl)\r\n     <span style=\"color:blue;\">MsgBox<\/span> \"Hello world, from twinBASIC!\" & <span style=\"color:blue;\">vbCrLf<\/span> & _\r\n                 \"(via \" & objPowerPoint.Name & \")\"\r\n     <span style=\"color:blue;\">Call<\/span> Translate()\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Code der L&ouml;sung anpassen<\/h2>\n<p>Danach passen wir den Code unserer L&ouml;sung an. Wir k&ouml;nnen vorab pr&uuml;fen, ob wir schon Stellen finden, die wir f&uuml;r den Betrieb in einem COM-Add-In anpassen m&uuml;ssen. Wir k&ouml;nnen aber auch einfach mal starten und schauen, was passiert. Dabei sto&szlig;en wir auch gleich auf einen Fehler. Um uns die Arbeit ein wenig zu erleichtern, f&uuml;gen wir eine einfache Fehlerbehandlung zun&auml;chst zur Prozedur <b>Translate <\/b>hinzu:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>Translate()\r\n     <span style=\"color:blue;\">On Error GoTo<\/span> Fehler\r\n     ...\r\n     <span style=\"color:blue;\">Exit Sub<\/span>\r\nFehler:\r\n     <span style=\"color:blue;\">MsgBox<\/span> Err.Number & \" \" & Err.Description & \" \" & Erl\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Au&szlig;erdem nummerieren wir die Zeilen, damit die <b>MsgBox<\/b>-Funktion nicht nur die Fehlermeldung und die Fehlermeldung ausgibt, sondern mit der <b>Erl<\/b>-Funktion auch die Zeile, die den Fehler ausgel&ouml;st hat.<\/p>\n<p>Der erste Fehler betrifft gleich diese Zeile:<\/p>\n<pre><span style=\"color:blue;\">Set<\/span> objPowerPoint = Application<\/pre>\n<p>Die Prozedur wurde vorher in PowerPoint eingesetzt, wo <b>Application <\/b>einen Verweis auf die aktuelle Anwendung liefert. Hier haben wir die Variable <b>objPowerPoint <\/b>bereits in der Prozedur <b>OnConnection <\/b>mit dem mit dem Parameter <b>Application <\/b>gelieferten Verweis auf die ausl&ouml;sende Anwendung gef&uuml;llt. Deshalb k&ouml;nnen wir hier die Variable <b>objPowerPoint <\/b>und die entsprechende Zuweisung l&ouml;schen.<\/p>\n<p>Danach funktioniert die Prozedur schon wie gew&uuml;nscht &#8211; wir finden keine weiteren Fehler.<\/p>\n<h2>Parameter hinzuf&uuml;gen<\/h2>\n<p>Nun wollen wir noch entsprechende Parameter hinzuf&uuml;gen, damit wir beispielsweise &uuml;ber Ribbon die Sprache des zu &uuml;bersetzenden Textes und die des &Uuml;bersetzungsziels abfragen k&ouml;nnen.<\/p>\n<p>Dazu f&uuml;gen wir der Prozedur <b>Translate<\/b> wie in Listing 2 zwei Parameter hinzu. Der erste erwartet eine der Konstanten der Auflistung <b>intSourceLanguage <\/b>und der zweite eine der Konstanten der Auflistung <b>intTargetLanguage<\/b>. Diese Werte werden dann beim Aufruf der <b>TranslateDeepL<\/b>-Funktion weitergegeben. Der Aufruf sieht dadurch beispielsweise wie folgt aus:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>Translate(intSourceLanguage<span style=\"color:blue;\"> As <\/span>SourceLanguage, intTargetLanguage<span style=\"color:blue;\"> As <\/span>TargetLanguage)\r\n     <span style=\"color:blue;\">Dim <\/span>objPresentation<span style=\"color:blue;\"> As <\/span>PowerPoint.Presentation\r\n     <span style=\"color:blue;\">Dim <\/span>objSlide<span style=\"color:blue;\"> As <\/span>PowerPoint.Slide\r\n     <span style=\"color:blue;\">Dim <\/span>objShape<span style=\"color:blue;\"> As <\/span>PowerPoint.Shape\r\n     <span style=\"color:blue;\">Dim <\/span>objTextFrame<span style=\"color:blue;\"> As <\/span>PowerPoint.TextFrame\r\n     <span style=\"color:blue;\">Dim <\/span>objTextRange<span style=\"color:blue;\"> As <\/span>PowerPoint.TextRange\r\n     <span style=\"color:blue;\">Dim <\/span>objParagraph<span style=\"color:blue;\"> As <\/span>PowerPoint.TextRange\r\n     <span style=\"color:blue;\">Dim <\/span>strText<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">On Error GoTo<\/span> Fehler\r\n15        <span style=\"color:blue;\">Set<\/span> objPresentation = objPowerPoint.ActivePresentation\r\n16        For Each objSlide In objPresentation.Slides\r\n17            For Each objShape In objSlide.Shapes\r\n18                If objShape.HasTextFrame Then\r\n19                    <span style=\"color:blue;\">Set<\/span> objTextFrame = objShape.TextFrame\r\n20                    If objTextFrame.HasText Then\r\n21                        <span style=\"color:blue;\">Set<\/span> objTextRange = objTextFrame.TextRange\r\n22                        For Each objParagraph In objTextRange.Paragraphs\r\n23                            strText = <span style=\"color:blue;\">Replace<\/span>(objParagraph.Text, <span style=\"color:blue;\">vbCr<\/span>, \"\")\r\n24                            strText = TranslateDeepl(strText, intSourceLanguage, intTargetLanguage)\r\n25                            objParagraph.Text = strText\r\n26                        <span style=\"color:blue;\">Next<\/span> objParagraph\r\n27                    End If\r\n28                End If\r\n29            <span style=\"color:blue;\">Next<\/span> objShape\r\n30        <span style=\"color:blue;\">Next<\/span> objSlide\r\n     <span style=\"color:blue;\">Exit Sub<\/span>\r\nFehler:\r\n     <span style=\"color:blue;\">MsgBox<\/span> Err.Number & \" \" & Err.Description & \" \" & Erl\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: Die angepasste &Uuml;bersetzungsfunktion<\/span><\/b><\/p>\n<pre><span style=\"color:blue;\">Call<\/span> Translate(SourceLanguage.sGerman, _\r\n     TargetLanguage.tBritishEnglish)<\/pre>\n<h2>Ribbon zur Auswahl der Sprachen anpassen<\/h2>\n<p>Nun wollen wir das Ribbon so anpassen, dass der Benutzer dort die Quell- und die Zielsprache einstellen kann. Schauen wir uns dazu die neue Version der Funktion <b>GetCustomUI<\/b> an, die wir in Listing 3 finden. Die Funktion stellt den Ribbon-Code f&uuml;r die Ribbon-Erweiterung in einer Variablen namens <b>strXML <\/b>zusammen. Wichtige Elemente sind zum Beispiel <b>customUI<\/b>, das die Attribute <b>loadImage <\/b>und <b>onLoad <\/b>enth&auml;lt. Beide werden gleich beim Laden der Ribbondefinition ausgef&uuml;hrt.<\/p>\n<pre><span style=\"color:blue;\">Private Function <\/span>GetCustomUI(ByVal RibbonID<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As String<\/span> Implements IRibbonExtensibility.GetCustomUI\r\n     <span style=\"color:blue;\">Dim <\/span>strXML<span style=\"color:blue;\"> As String<\/span>\r\n     strXML &= \"&lt;customUI xmlns=\"\"http:\/\/schemas.microsoft.com\/office\/2006\/01\/customui\"\" \" _\r\n         & \"loadImage=\"\"LoadImage\"\" onLoad=\"\"onLoad\"\"&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strXML &= \"  &lt;ribbon startFromScratch=\"\"false\"\"&gt; \" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strXML &= \"    &lt;tabs&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strXML &= \"      &lt;tab id=\"\"tabTranslate\"\" label=\"\"amvWordTranslate\"\"&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strXML &= \"        &lt;group id=\"\"grpUebersetzen\"\" label=\"\"&Uuml;bersetzen\"\"&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strXML &= \"          &lt;button id=\"\"btnTranslate\"\" size=\"\"large\"\" label=\"\"&Uuml;bersetzen\"\" image=\"\"translate.ico\"\" \" _\r\n         & \"onAction=\"\"onAction\"\" getEnabled=\"\"GetEnabled\"\"\/&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strXML &= \"          &lt;dropDown label=\"\"Originalsprache:\"\" id=\"\"drpOriginal\"\" onAction=\"\"onActionDropDown\"\" \" _\r\n         & \"sizeString=\"\"Portugiesisch\"\" getSelectedItemIndex=\"\"getSelectedItemIndex\"\"&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strXML &= \"            &lt;item id=\"\"itmSGerman\"\" label=\"\"Deutsch\"\"\/&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strXML &= \"            &lt;item id=\"\"itmSEnglish\"\" label=\"\"Englisch\"\"\/&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     ...\r\n     strXML &= \"          &lt;\/dropDown&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strXML &= \"          &lt;dropDown label=\"\"Zielsprache:\"\" id=\"\"drpZiel\"\" onAction=\"\"onActionDropDown\"\" \" _\r\n         & \"sizeString=\"\"Portugiesisches Portugiesisch\"\" getSelectedItemIndex=\"\"getSelectedItemIndex\"\"&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strXML &= \"            &lt;item id=\"\"itmTGerman\"\" label=\"\"Deutsch\"\"\/&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strXML &= \"            &lt;item id=\"\"itmTBritishEnglish\"\" label=\"\"Britisches Englisch\"\"\/&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strXML &= \"            &lt;item id=\"\"itmTAmericanEnglish\"\" label=\"\"Amerikanisches Englisch\"\"\/&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strXML &= \"            &lt;item id=\"\"itmTChinese\"\" label=\"\"Chinesisch\"\"\/&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strXML &= \"          &lt;\/dropDown&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strXML &= \"           &lt;labelControl id=\"\"lblProgress\"\" getLabel=\"\"getLabel\"\" \/&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strXML &= \"        &lt;\/group&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strXML &= \"      &lt;\/tab&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strXML &= \"    &lt;\/tabs&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strXML &= \"  &lt;\/ribbon&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strXML &= \"&lt;\/customUI&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     Return strXML\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 3: Funktion zum Zusammenstellen des Ribbons<\/span><\/b><\/p>\n<h2>Bilder laden<\/h2>\n<p>Die Funktion <b>LoadImage <\/b>wird f&uuml;r jedes <b>image<\/b>-Attribut eines der folgenden Elemente ausgel&ouml;st. Sie nimmt die Bezeichnung des Bildes entgegen und l&auml;dt dieses aus den Ressourcen des twinBASIC-Projekts:<\/p>\n<pre><span style=\"color:blue;\">Function <\/span>LoadImage(imageId<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As <\/span>IPictureDisp\r\n     Return LoadResPicture(\"translate.ico\", _\r\n         vbResBitmapFromIcon, 32, 32)\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>Die Bilddatei m&uuml;ssen wir zuvor den Ressourcen hinzuf&uuml;gen (siehe Bild 7).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_438_007.png\" alt=\"Eine .ico-Datei in den Ressourcen des Projekts\" width=\"424,6267\" height=\"301,0414\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 7: Eine .ico-Datei in den Ressourcen des Projekts<\/span><\/b><\/p>\n<h2>Ribbon-Definition referenzieren<\/h2>\n<p>Wenn wir zur Laufzeit dynamisch benutzerdefinierte Elemente im Ribbon anpassen wollen, ben&ouml;tigen wir einen Verweis auf die Ribbondefinition. Diese holen wir uns &uuml;ber die folgende Funktion, die durch das Attribut <b>onLoad <\/b>ebenfalls beim Laden des Ribbons ausgel&ouml;st wird:<\/p>\n<pre><span style=\"color:blue;\">Function <\/span>OnLoad(ribbon<span style=\"color:blue;\"> As <\/span>IRibbonUI)\r\n     <span style=\"color:blue;\">Set<\/span> objRibbon = ribbon\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>Die hier verwendete Variable schreiben wir in das Standardmodul <b>mdlTools<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Public <\/span>objRibbon<span style=\"color:blue;\"> As <\/span>IRibbonUI<\/pre>\n<h2>Button zum Starten der &Uuml;bersetzung<\/h2>\n<p>Die restliche Ribbon-Definition enth&auml;lt ein <b>button<\/b>-Element mit der Besonderheit, dass es w&auml;hrend des &Uuml;bersetzungsvorgangs deaktiviert dargestellt werden soll.<\/p>\n<p>Deshalb weisen wir dem Attribut <b>getEnabled <\/b>eine Funktion zu, die jeweils beim Aktualisieren des Ribbons aufgerufen werden und den aktuellen <b>Enabled<\/b>-Zustand liefern soll. Die <b>getEnabled<\/b>-Funktion bezieht ihren R&uuml;ckgabewert aus der Variablen <b>bolTranslationRunning<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Function <\/span>GetEnabled(control<span style=\"color:blue;\"> As <\/span>IRibbonControl)<span style=\"color:blue;\"> As Boolean<\/span>\r\n     GetEnabled = <span style=\"color:blue;\">Not<\/span> bolTranslationRunning\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>Diese deklarieren wir ebenfalls wie folgt im Modul <b>mdlTools<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Public <\/span>bolTranslationRunning<span style=\"color:blue;\"> As Boolean<\/span><\/pre>\n<p>Diese wird in der Prozedur <b>Translate <\/b>beim Start auf <b>True <\/b>eingestellt und kurz vor dem Beenden auf <b>False<\/b> &#8211; dazu sp&auml;ter mehr.<\/p>\n<h2>Beim Anklicken des Buttons<\/h2>\n<p>Au&szlig;erdem enth&auml;lt das <b>button<\/b>-Element das <b>onAction<\/b>-Attribut mit der Prozedur, die beim Anklicken ausgel&ouml;st werden soll. Diese Prozedur ruft lediglich die Prozedur <b>Translate <\/b>auf und &uuml;bergibt die Quell- und die Zielsprache:<\/p>\n<pre><span style=\"color:blue;\">Sub <\/span>OnAction(control<span style=\"color:blue;\"> As <\/span>IRibbonControl)\r\n     <span style=\"color:blue;\">Call<\/span> Translate(intSourceLanguage, intTargetLanguage)\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Quell- und Zielsprache ausw&auml;hlen<\/h2>\n<p>Diese werden initial in einer Prozedur festgelegt, die durch die beiden nachfolgend definierten <b>dropDown<\/b>-Elemente aufgerufen werden.<\/p>\n<p><b>intSourceLanguage <\/b>erh&auml;lt hier den Wert <b>1 <\/b>f&uuml;r <b>Deutsch <\/b>und <b>intTargetLanguage <\/b>den Wert <b>4 <\/b>f&uuml;r <b>British English<\/b>. Au&szlig;erdem werden hier die beim Anzeigen einzustellenden Indizes f&uuml;r die beiden <b>dropDown<\/b>-Elemente als R&uuml;ckgabewert angegeben:<\/p>\n<pre><span style=\"color:blue;\">Function <\/span>GetSelectedItemIndex(control<span style=\"color:blue;\"> As <\/span>IRibbonControl) _\r\n        <span style=\"color:blue;\"> As Integer<\/span>\r\n     Select Case control.Id\r\n         <span style=\"color:blue;\">Case <\/span>\"drpOriginal\"\r\n             GetSelectedItemIndex = 0\r\n             intSourceLanguage = 1\r\n         <span style=\"color:blue;\">Case <\/span>\"drpZiel\"\r\n             GetSelectedItemIndex = 1\r\n             intTargetLanguage = 4\r\n     <span style=\"color:blue;\">End Select<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>Die folgenden Zeilen stellen die verschiedenen Sprachen f&uuml;r die beiden <b>dropDown<\/b>-Elemente zusammen.<\/p>\n<p>Au&szlig;erdem folgt noch ein <b>labelControl<\/b>-Element, das mit dem <b>getLabel<\/b>-Attribut ausgestattet ist und den Fortschritt anzeigen soll.<\/p>\n<h2>Aktualisieren der Sprache<\/h2>\n<p>Wenn der Benutzer f&uuml;r eines der beiden <b>dropDown<\/b>-Elemente eine Sprache ausw&auml;hlt, soll die Konstante f&uuml;r diese Sprache in die Variablen <b>intSourceLanguage <\/b>beziehungsweise <b>intTargetLanguage <\/b>eingetragen werden (siehe Bild 8).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_438_008.png\" alt=\"Auswahl der Zielsprache\" width=\"424,6267\" height=\"414,9099\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 8: Auswahl der Zielsprache<\/span><\/b><\/p>\n<h2>Erweiterung der Translate-Prozedur<\/h2>\n<p>F&uuml;r die Aktualisierung des Ribbons haben wir die Prozedur <b>Translate <\/b>noch an einige Stellen erweitert (siehe Listing 4).<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>Translate(intSourceLanguage<span style=\"color:blue;\"> As <\/span>SourceLanguage, intTargetLanguage<span style=\"color:blue;\"> As <\/span>TargetLanguage)\r\n     ...\r\n     bolTranslationRunning = <span style=\"color:blue;\">True<\/span>\r\n     objRibbon.Invalidate\r\n     <span style=\"color:blue;\">Set<\/span> objPresentation = objPowerPoint.ActivePresentation\r\n     intSlides = objPresentation.Slides.Count\r\n     For Each objSlide In objPresentation.Slides\r\n         intCurrentSlide = intCurrentSlide + 1\r\n         objRibbon.Invalidate()\r\n         DoEvents\r\n         For Each objShape In objSlide.Shapes\r\n             ...\r\n         <span style=\"color:blue;\">Next<\/span> objShape\r\n     <span style=\"color:blue;\">Next<\/span> objSlide\r\nMyExit:\r\n     bolTranslationRunning = <span style=\"color:blue;\">False<\/span>\r\n     objRibbon.Invalidate\r\n     <span style=\"color:blue;\">Exit Sub<\/span>\r\nFehler:\r\n     <span style=\"color:blue;\">MsgBox<\/span> Err.Number & \" \" & Err.Description & \" \" & Erl\r\n     GoTo MyExit\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: Neue Version der Translate-Prozedur<\/span><\/b><\/p>\n<p>Als Erstes stellen wir gleich zu Beginn die Variable <b>bolTranslationRunning <\/b>auf den Wert <b>True <\/b>ein, damit die Schaltfl&auml;che w&auml;hrend der &Uuml;bersetzung deaktiviert wird. Um diese Anzeigeaktualisierung zu forcieren, rufen wir direkt danach die <b>Invalidate<\/b>-Methode des <b>IRibbonUI<\/b>-Objekts auf.<\/p>\n<p>Vor dem Eintritt in die <b>For Each<\/b>-Schleife zum Durchlaufen der Folien ermitteln wir die Anzahl der Slides und schreiben diese in die Variable <b>intSlides<\/b>.<\/p>\n<p>Innerhalb der <b>For Each<\/b>-Schleife erh&ouml;hen wir dann <b>intCurrentSlide <\/b>um <b>1 <\/b>und rufen erneut <b>objRibbon.Invalidate <\/b>auf. <b>DoEvents <\/b>soll daf&uuml;r sorgen, dass die Aktualisierung nicht untergeht.<\/p>\n<p>Kurz vor dem Ende der Prozedur wird schlie&szlig;lich noch <b>bolTranslationRunning <\/b>auf <b>False <\/b>eingestellt und erneut <b>objRibbon.Invalidate <\/b>aufgerufen, damit die Schaltfl&auml;che wieder aktiviert wird.<\/p>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Mit dieser L&ouml;sung haben wir eine M&ouml;glichkeit geschaffen, mit einem Mausklick alle Texte in den Slides einer PowerPoint-Pr&auml;sentation von der aktuellen in die gew&uuml;nschte Zielsprache zu &uuml;bersetzen.<\/p>\n<p>In einer Erweiterung k&ouml;nnte man noch Funktionen hinzuf&uuml;gen, die nur die aktuelle Folie oder auch nur einen Teil des Textes &uuml;bersetzen.<\/p>\n<p>Dazu m&uuml;sste man nur zus&auml;tzliche Steuerelemente zum Ribbon hinzuf&uuml;gen und die bisher verwendeten Prozedur anpassen.<\/p>\n<p>Wir k&ouml;nnten den Vorgang auch noch wesentlich beschleunigen. Bislang rufen wir f&uuml;r jeden Absatz die Funktion <b>TranslateDeepl<\/b> auf. Wir k&ouml;nnten die Texte auch auf irgendeine Art zusammenfassen, in einem Aufruf &uuml;bersetzen lassen und anschlie&szlig;end wieder zur&uuml;ckschreiben.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Im Artikel &#8220;PowerPoint: Texte automatisiert &uuml;bersetzen&#8221; (www.vbentwickler.de\/437) haben wir VBA-Code produziert, mit dem wir alle Abs&auml;tze aller Folien in einem PowerPoint-Dokument automatisch &uuml;bersetzen k&ouml;nnen. Dabei nutzen wir den Dienst DeepL. Leider m&uuml;ssen wir, um diesen Code in einem PowerPoint-Dokument verwenden zu k&ouml;nnen, das Modul erst in das jeweilige Dokument integrieren. Wenn man oft PowerPoint-Folien &uuml;bersetzen muss, ist das recht aufw&auml;ndig. Da nehmen wir lieber den Aufwand in Kauf, einmal ein COM-Add-In f&uuml;r diesen Zweck zu programmieren, dass wir dann auch noch an Dich weitergeben k&ouml;nnen, damit Du es f&uuml;r Dich und Deine Mitarbeiter und\/oder Kunden einsetzen kannst.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[662024,66042024,44000038,44000030,44000025],"tags":[],"yst_prominent_words":[],"class_list":["post-55000438","post","type-post","status-publish","format-standard","hentry","category-662024","category-66042024","category-Office_programmieren","category-PowerPoint_programmieren","category-VBAProgrammierung"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000438","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/comments?post=55000438"}],"version-history":[{"count":0,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000438\/revisions"}],"wp:attachment":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/media?parent=55000438"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/categories?post=55000438"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/tags?post=55000438"},{"taxonomy":"yst_prominent_words","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/yst_prominent_words?post=55000438"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}