{"id":55000437,"date":"2024-08-01T00:00:00","date_gmt":"2024-08-14T14:55:01","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=437"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"PowerPoint_Texte_automatisiert_uebersetzen","status":"publish","type":"post","link":"https:\/\/vbentwickler.de\/PowerPoint_Texte_automatisiert_uebersetzen\/","title":{"rendered":"PowerPoint: Texte automatisiert &uuml;bersetzen"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg08.met.vgwort.de\/na\/897c6713d7bc4d1599e085b1535fcde1\" width=\"1\" height=\"1\" alt=\"\"><b>Neulich war es mal wieder so weit: Eine PowerPoint-Pr&auml;sentation musste her. Und das auch noch auf Englisch. Okay, das Schul-Englisch ist zum Verstehen und schriftliche Kommunikation ausreichend, aber eine PowerPoint-Pr&auml;sention f&uuml;r englischsprachiges Fachpublikum sollte schon ann&auml;hrend perfekt sein. Wozu gibt es &Uuml;bersetzungsdienste? Also habe ich meine Texte auf Deutsch zurechtgelegt und diese von der KI &uuml;bersetzen lassen. Dann habe ich alles in die PowerPoint-Pr&auml;sentation eingef&uuml;gt und noch die Animationen hinzugef&uuml;gt, damit beispielsweise Stichpunkte Schritt f&uuml;r Schritt eingeblendet werden k&ouml;nnen. All das hat so gut geklappt, dass ich die Pr&auml;sentation anschlie&szlig;end auch noch f&uuml;r ein Video aufbereiten wollte &#8211; diesmal jedoch auf Deutsch. Also habe ich erstmal eine komplette Seite kopiert, &uuml;bersetzen lassen und wieder zur&uuml;ckgeschrieben. Das habe ich f&uuml;r einige Folien gemacht und dann schnell festgestellt, dass so alle Animationen verloren gehen. Der n&auml;chste Ansatz dann: Absatz f&uuml;r Absatz in die Zwischenablage, &uuml;bersetzen lassen, wieder zur&uuml;ckschreiben. So blieben die Animationen erhalten, aber es war zu viel Handarbeit. Wozu beherrsche ich &#8211; im Gegensatz zu Englisch &#8211; eigentlich perfekt VBA? Also habe ich mich an die Programmierung der &Uuml;bersetzung der enthaltenen Texte begeben. Das Ergebnis siehst Du in diesem Artikel!<\/b><\/p>\n<p>PowerPoint ist in diesem Magazin bisher noch gar nicht vorgekommen &#8211; h&ouml;chstens als Randnotiz in Zusammenhang mit der Office-Programmierung. In diesem Artikel wollen wir jedoch direkt einmal eine praktische L&ouml;sung liefern.<\/p>\n<p>Ausgangspunkt ist ein Dokument mit Folien wie der aus Bild 1. Wir haben hier verschiedene Abs&auml;tze mit englischem Text, die wir gern &uuml;bersetzen w&uuml;rden. Dabei gibt es verschiedene Vorgehensweisen:<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_437_001.png\" alt=\"Ein zu &uuml;bersetzendes PowerPoint-Dokument\" width=\"649,627\" height=\"439,4104\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Ein zu &uuml;bersetzendes PowerPoint-Dokument<\/span><\/b><\/p>\n<ul>\n<li>Die erste ist, einfach manuell in das Dokument zu gehen und die Texte Wort f&uuml;r Wort zu &uuml;bersetzen. Das ist recht viel Arbeit und kostet entsprechend Zeit.<\/li>\n<li>Die zweite ist, die Texte seitenweise zu kopieren und  von der KI &uuml;bersetzen zu lassen. Wenn man die &uuml;bersetzte Seite dann zur&uuml;ckkopiert, verliert man jedoch eventuell f&uuml;r einzelne Abs&auml;tze festgelegte Animationen.<\/li>\n<li>Die dritte ist, sich einmal im Ribbon von PowerPoint umzuschauen und festzustellen, dass es dort einen Eintrag namens &Uuml;bersetzen gibt. Markieren wir damit die vollst&auml;ndige Seite, zeigt dieser wie in Bild 2 die englische und die ins Deutsche &uuml;bersetzte Version des Textes an. Wenn wir hier allerdings auf Einf&uuml;gen klicken, stellen wir fest, dass auch hier einfach der ganze Text &uuml;bersetzt und wieder zur&uuml;ckgeschrieben wird &#8211; ohne Ber&uuml;cksichtigung der Animationen.<\/li>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_437_002.png\" alt=\"Der eingebaute &Uuml;bersetzter\" width=\"700\" height=\"365,2173\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Der eingebaute &Uuml;bersetzter<\/span><\/b><\/p>\n<li>Die vierte Stufe w&auml;re, die Texte absatzweise zu markieren und den Rest den eingebauten &Uuml;bersetzer erledigen zu lassen. Auf diese Weise habe ich zuvor mit der externen KI gearbeitet, aber auch dabei sind Fehler passiert: Wenn man den Zeilenumbruch mit kopiert und die &Uuml;bersetzung f&uuml;r den vollst&auml;ndigen Absatz einf&uuml;gt, gehen wiederum die Animationen verloren. Man muss also genau pr&uuml;fen, ob man das Zeilenumbruchzeichen nicht mit kopiert. Wie kann man das verhindern? Indem man darauf achtet, dass die Markierung sich nur genau bis zum letzten sichtbaren Zeichen des Absatzes erstreckt. Wenn der Zeilenumbruch ebenfalls markiert ist, enth&auml;lt die Markierung noch einen kleinen Bereich hinter dem letzten Zeichen. Das passiert beispielsweise, wenn man den Absatz durch einen dreifachen Mausklick selektiert. Auch diese Variante kostet immer noch recht viel Zeit (siehe Bild 3).<\/li>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_437_003.png\" alt=\"Texte absatzweises &uuml;bersetzen\" width=\"700\" height=\"337,8079\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Texte absatzweises &uuml;bersetzen<\/span><\/b><\/p>\n<li>Die f&uuml;nfte und nachfolgend vorgestellte Variante ist, alle Textabschnitte der PowerPoint-Pr&auml;sentation automatisiert zu durchlaufen, die Texte zu &uuml;bersetzen und diese wieder zur&uuml;ckzuschreiben, ohne dass Animationen verloren gehen.<\/li>\n<\/ul>\n<p>Diesen Ansatz schauen wir und nun an. Dazu ben&ouml;tigen wir ein paar Grundlagen zur PowerPoint-Programmierung und werfen einen Blick auf das Objektmodell dieser Anwendung.<\/p>\n<h2>Das Objektmodell von PowerPoint<\/h2>\n<p>Wir werden uns nicht das vollst&auml;ndige Objektmodell ansehen, sondern nur die f&uuml;r uns relevanten Abschnitte. Bevor wir damit starten, legen wir ein neues Standardmodul im VBA-Editor von PowerPoint an, den wir mit der Tastenkombination <b>Alt + F11 <\/b>&ouml;ffnen.<\/p>\n<p>Hier stellen wir fest, dass es keine Standardklassen oder -module gibt, die bereits beim Anlegen eines PowerPoint-Dokuments vorhanden sind &#8211; so, wie es beispielsweise bei Excel oder Word der Fall ist. Also k&ouml;nnen wir mit einem frischen, leeren VBA-Projekt starten.<\/p>\n<h2>PowerPoint referenzieren<\/h2>\n<p>Das <b>Application<\/b>-Objekt ist das oberste Element im Objektmodell von PowerPoint. Wir deklarieren es mit der folgenden Zeile:<\/p>\n<pre><span style=\"color:blue;\">Dim <\/span>objPowerPoint<span style=\"color:blue;\"> As <\/span>PowerPoint.Application<\/pre>\n<p>Je nachdem, ob wir von einem Modul innerhalb des PowerPoint-Dokuments auf das <b>Application<\/b>-Objekt zugreifen oder von au&szlig;en, &auml;ndert sich die Referenzierung.<\/p>\n<p>F&uuml;r unser Beispiel reicht es erst einmal, wenn wir wie folgt darauf zugreifen:<\/p>\n<pre><span style=\"color:blue;\">Set<\/span> objPowerPoint = Application<\/pre>\n<h2>PowerPoint-Pr&auml;sentation referenzieren<\/h2>\n<p>Damit haben wir bereits Zugriff auf die Anwendung. Nun wollen wir auch noch das aktuell angezeigte Dokument referenzieren. Dazu nutzen wir wie nachfolgend gezeigt eine Objektvariable des Typs <b>PowerPoint.Presentation<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Dim <\/span>objPresentation<span style=\"color:blue;\"> As <\/span>PowerPoint.Presentation<\/pre>\n<p>Die aktuell ge&ouml;ffnete Pr&auml;sentation erhalten wir mit der Funktion <b>ActivePresentation<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Set<\/span> objPresentation = objPowerPoint.ActivePresentation<\/pre>\n<h2>Slides durchlaufen<\/h2>\n<p>Eine PowerPoint-Pr&auml;sentation besteht zuerst einmal aus den Folien. Diese k&ouml;nnen wir mit einer Variablen des Typs <b>Slide <\/b>referenzieren:<\/p>\n<pre><span style=\"color:blue;\">Dim <\/span>objSlide<span style=\"color:blue;\"> As <\/span>PowerPoint.Slide<\/pre>\n<p>Auf die einzelnen Slides greifen wir zum Beispiel &uuml;ber den Index zu. Dieser ist praktischerweise 1-basiert, sodass wir die Slides entsprechend der Nummer der Slide-&Uuml;bersicht am linken Rand des PowerPoint-Fensters ansprechen k&ouml;nnen. Mit der <b>Count<\/b>-Methode k&ouml;nnen wir die Anzahl der Folien ermitteln:<\/p>\n<pre><span style=\"color:blue;\">Debug.Print<\/span> objPresentation.Slides.Count<\/pre>\n<p>Wir wollen diese aber ohnehin nacheinander durchlaufen, sodass uns eine <b>For Each<\/b>-Schleife dienen w&uuml;rde. Hier geben wir zuerst einmal den Wert der Eigenschaft <b>SlideNumber <\/b>aus:<\/p>\n<pre>For Each objSlide in objPresentation.Slides\r\n     <span style=\"color:blue;\">Debug.Print<\/span> objSlide.SlideNumber\r\n<span style=\"color:blue;\">Next<\/span> objSlide<\/pre>\n<h2>Shapes durchlaufen<\/h2>\n<p>Unseren Texten noch ein wenig n&auml;her kommen wir mit der <b>Shapes<\/b>-Auflistung. Shapes sind alle Elemente, die wir &uuml;ber die Benutzeroberfl&auml;che direkt anklicken k&ouml;nnen.<\/p>\n<p>Diese werden auch im Taskpane names <b>Auswahl <\/b>angezeigt (siehe Bild 4). Diesen blendest Du am einfachsten ein, wenn Du oben in der Suche die Zeichenfolge Pane eingibst und dann auf Auswahlbereich anzeigen klickst.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_437_004.png\" alt=\"Auflistung der Shape-Elemente in der Benutzeroberfl&auml;che\" width=\"700\" height=\"318,2333\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Auflistung der Shape-Elemente in der Benutzeroberfl&auml;che<\/span><\/b><\/p>\n<p>Um mit den Shapes zu arbeiten, deklarieren wir eine Variable des Typs <b>Shape<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Dim <\/span>objShape<span style=\"color:blue;\"> As <\/span>PowerPoint.Shape<\/pre>\n<p>Diese Elemente k&ouml;nnen wir f&uuml;r jedes <b>Slide<\/b>-Objekt ebenfalls in einer <b>For Each<\/b>-Schleife durchlaufen. In diesem Fall wollen wir einfach nur den Namen eines jeden <b>Shape<\/b>-Elements ausgeben:<\/p>\n<pre>For Each objShape In objSlide.Shapes\r\n     <span style=\"color:blue;\">Debug.Print<\/span> objShape.Name\r\n<span style=\"color:blue;\">Next<\/span> objShape<\/pre>\n<p>Die <b>Shape<\/b>-Elemente k&ouml;nnen verschiedene untergeordnete Elemente enthalten beziehungsweise verschiedenen Typs sein.<\/p>\n<p>Den Typ finden wir mit der Eigenschaft <b>Type <\/b>heraus. In der folgenden Schleife geben wir den Namen und den Typ der Shapes eines Slides im Direktbereich des VBA-Editors aus:<\/p>\n<pre>For Each objShape In objSlide.Shapes\r\n     <span style=\"color:blue;\">Debug.Print<\/span> objShape.Name, \"Type: \" & objShape.Type\r\n<span style=\"color:blue;\">Next<\/span> objShape<\/pre>\n<p>F&uuml;r die erste Seite unseres Beispiels erhalten wir die folgende Ausgabe:<\/p>\n<pre>Title 1       Type: 14\r\nContent Placeholder 2       Type: 14\r\nPicture 3     Type: 13\r\nPicture 8     Type: 13<\/pre>\n<p>Zusammen mit den &uuml;brigen Folien finden wir die <b>Type<\/b>-Werte <b>1<\/b>, <b>13<\/b>, <b>14 <\/b>und <b>17 <\/b>vor. Im Objektkatalog (zu &ouml;ffnen mit <b>F2<\/b>) finden wir heraus, was diese Werte bedeuten. Dazu suchen wir zun&auml;chst nach dem <b>Shape<\/b>-Element und dann nach der <b>Type<\/b>-Eigenschaft (siehe Bild 5).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_437_005.png\" alt=\"Werte f&uuml;r die Shape-Typen\" width=\"499,6267\" height=\"448,2182\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: Werte f&uuml;r die Shape-Typen<\/span><\/b><\/p>\n<p>Hier finden wir f&uuml;r die von uns verwendeten Objekte die folgenden Typen:<\/p>\n<ul>\n<li><b>1<\/b>: <b>msoAutoShape<\/b><\/li>\n<li><b>13<\/b>: <b>msoPicture<\/b><\/li>\n<li><b>14<\/b>: <b>msoPlaceholder<\/b><\/li>\n<li><b>17<\/b>: <b>msoTextBox<\/b><\/li>\n<\/ul>\n<p>Wir wollen uns nur um die <b>Shape<\/b>-Elemente mit Text k&uuml;mmern. Diese finden wir allerdings nicht zuverl&auml;ssig &uuml;ber den <b>Type<\/b>-Wert heraus, denn es gibt sowohl Elemente des Typs <b>Placeholder <\/b>als auch <b>TextBox <\/b>mit Texten.<\/p>\n<p>Es gibt jedoch noch eine weitere f&uuml;r uns nutzbare Eigenschaft namens <b>HasTextFrame<\/b>. Hat diese den Wert <b>-1 <\/b>beziehungsweise <b>True<\/b>, finden wir dort Text vor.<\/p>\n<h2>Textinhalte mit dem TextFrame-Element markieren<\/h2>\n<p>Um dem Zugriff auf die Texte noch etwas n&auml;her zu kommen, k&ouml;nnen wir das <b>TextFrame<\/b>-Element nutzen.  Dieses deklarieren wir wie folgt:<\/p>\n<pre><span style=\"color:blue;\">Dim <\/span>objTextFrame<span style=\"color:blue;\"> As <\/span>PowerPoint.TextFrame<\/pre>\n<p>Danach k&ouml;nnen wir &uuml;ber die Eigenschaft <b>TextFrame <\/b>des <b>Shape<\/b>-Objekts auf dieses Objekt zugreifen:<\/p>\n<pre><span style=\"color:blue;\">If <\/span>objShape.HasTextFrame<span style=\"color:blue;\"> Then<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> objTextFrame = objShape.TextFrame\r\n<span style=\"color:blue;\">End If<\/span><\/pre>\n<h2>Befindet sich Text im TextFrame?<\/h2>\n<p>Allerdings kann es auch Elemente geben, die zwar f&uuml;r Text ausgelegt sind, aber keine Texte enthalten. Doch es gibt eine Eigenschaft f&uuml;r diesen Fall, n&auml;mlich <b>HasText<\/b>.<\/p>\n<p>Damit kommen wir den Texten noch ein wenig n&auml;her. Dann n&auml;mlich k&ouml;nnen wir das im <b>TextFrame<\/b>-Element enthaltene <b>TextRange<\/b>-Element referenzieren, das wir zun&auml;chst deklarieren:<\/p>\n<pre><span style=\"color:blue;\">Dim <\/span>objTextRange<span style=\"color:blue;\"> As <\/span>PowerPoint.TextRange<\/pre>\n<p>Diese f&uuml;llen wir wie folgt:<\/p>\n<pre><span style=\"color:blue;\">If <\/span>objTextFrame.HasText<span style=\"color:blue;\"> Then<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> objTextRange = objTextFrame.TextRange\r\n<span style=\"color:blue;\">End If<\/span><\/pre>\n<p>Das <b>TextRange<\/b>-Objekt enth&auml;lt den kompletten Text in diesem <b>TextFrame<\/b>-Element.<\/p>\n<h2>Einzelne Abs&auml;tze im TextRange finden<\/h2>\n<p>In Word beispielsweise w&uuml;rden wir nun ein <b>Paragraph<\/b>-Objekt verwenden, um die einzelnen Elemente der <b>Paragraphs<\/b>-Auflistung zu durchlaufen.<\/p>\n<p>Im PowerPoint-Objektmodell sieht das ein wenig anders aus. Hier erh&auml;lt man &uuml;ber die <b>Paragraphs<\/b>-Auflistung wiederum <b>TextRange<\/b>-Objekte. Man kann es sich so vorstellen, dass der komplette Inhalt nun nach Zeilenumbr&uuml;chen aufgeteilt wird und durch neue <b>TextRange<\/b>-Objekte definiert wird.<\/p>\n<p>Also deklarieren wir die Variablen zum Erfassen der einzelnen Abs&auml;tze wie folgt:<\/p>\n<pre><span style=\"color:blue;\">Dim <\/span>objParagraph<span style=\"color:blue;\"> As <\/span>PowerPoint.TextRange<\/pre>\n<p>Damit k&ouml;nnen wir nun alle Abs&auml;tze mit folgender Schleife durchlaufen und ihre Inhalte im Direktbereich ausgeben:<\/p>\n<pre>For Each objParagraph In objTextRange.Paragraphs\r\n     <span style=\"color:blue;\">Debug.Print<\/span> objParagraph.Text\r\n<span style=\"color:blue;\">Next<\/span> objParagraph<\/pre>\n<p>Dies gibt jedoch noch die Abs&auml;tze mit den Zeilenumbruchszeichen aus. Im Falle von PowerPoint werden diese durch das Zeichen <b>vbCr <\/b>gesetzt. Wenn wir nun nur noch die einzelnen Abs&auml;tze ausgeben wollen, ersetzen wir noch das Zeichen <b>vbCr <\/b>durch eine leere Zeichenkette:<\/p>\n<pre><span style=\"color:blue;\">Debug.Print<\/span> <span style=\"color:blue;\">Replace<\/span>(objParagraph.Text, <span style=\"color:blue;\">vbCr<\/span>, \"\")<\/pre>\n<h2>Ausgabe der Abs&auml;tze im &Uuml;berblick<\/h2>\n<p>In einer Prozedur zusammengefasst sehen unsere bisherigen Erkenntnisse wie in Listing 1 aus.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>AlleTexteAusgeben()\r\n     <span style=\"color:blue;\">Dim <\/span>objPowerPoint<span style=\"color:blue;\"> As <\/span>PowerPoint.Application\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;\">Set<\/span> objPowerPoint = Application\r\n     <span style=\"color:blue;\">Set<\/span> objPresentation = objPowerPoint.ActivePresentation\r\n     For Each objSlide In objPresentation.Slides\r\n         For Each objShape In objSlide.Shapes\r\n             <span style=\"color:blue;\">If <\/span>objShape.HasTextFrame<span style=\"color:blue;\"> Then<\/span>\r\n                 <span style=\"color:blue;\">Set<\/span> objTextFrame = objShape.TextFrame\r\n                 <span style=\"color:blue;\">If <\/span>objTextFrame.HasText<span style=\"color:blue;\"> Then<\/span>\r\n                     <span style=\"color:blue;\">Set<\/span> objTextRange = objTextFrame.TextRange\r\n                     For Each objParagraph In objTextRange.Paragraphs\r\n                         <span style=\"color:blue;\">Debug.Print<\/span> <span style=\"color:blue;\">Replace<\/span>(objParagraph.Text, <span style=\"color:blue;\">vbCr<\/span>, \"\")\r\n                     <span style=\"color:blue;\">Next<\/span> objParagraph\r\n                 <span style=\"color:blue;\">End If<\/span>\r\n             <span style=\"color:blue;\">End If<\/span>\r\n         <span style=\"color:blue;\">Next<\/span> objShape\r\n     <span style=\"color:blue;\">Next<\/span> objSlide\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Ausgeben aller Texte einer PowerPoint-Pr&auml;sentation<\/span><\/b><\/p>\n<p>Wir deklarieren alle notwendigen Elemente und arbeiten uns dann von ganz oben, also von der PowerPoint-Anwendung &uuml;ber das Dokument, die einzelnen Folien, die enthaltenen Shapes und TextFrames bis zu den TextRanges und Abs&auml;tzen vor und geben diese schlie&szlig;lich im Direktbereich des VBA-Editors aus.<\/p>\n<p>Diese Ausgabe m&uuml;ssen wir nun noch ersetzen durch die Funktion zum &Uuml;bersetzen des jeweiligen Absatzes und diesen dann statt des vorhandenen Absatzes zur&uuml;ckschreiben, ohne dass die f&uuml;r die einzelnen Abs&auml;tze festgelegten Animationen verloren gehen.<\/p>\n<h2>&Uuml;bersetzen der Texte<\/h2>\n<p>Das &Uuml;bersetzen der Texte erledigen wir mit der Rest-API des &Uuml;bersetzungsdienstes <b>DeepL<\/b>. Diesen haben wir bereits im Artikel <b>Texte &uuml;bersetzen mit DeepL <\/b>(<b>www.vbentwickler.de\/322<\/b>) vorgestellt.<\/p>\n<p>Im Download zu diesem Artikel findest Du ein Modul, das wir in das VBA-Projekt unserer PowerPoint-Datei einf&uuml;gen. Hier sind noch wenige Anpassungen zu erledigen. Als Erstes ben&ouml;tigst Du einen API-Key, um auf die Rest-API zugreifen zu k&ouml;nnen.<\/p>\n<p>Diesen holst Du Dir kostenlos auf der Seite <b>deepl.com<\/b>.<\/p>\n<h2>DeepL-Account holen<\/h2>\n<p>Um die Rest-API zu nutzen, klickst Du auf der Seite auf <b>Kostenloses Probeabo starten <\/b>und wechselst unter <b>Finden Sie Ihr passendes Paket <\/b>auf <b>DeepL API<\/b>.<\/p>\n<p>Hier kannst Du die Variante <b>DeepL API Free <\/b>w&auml;hlen (siehe Bild 6). Nach der Eingabe der notwendigen Daten wechseln wir unter Ihr Konto auf den Bereich API-Keys und kopieren dort den API-Key in die Zwischenablage (siehe Bild 7).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_437_006.png\" alt=\"Registrieren f&uuml;r die kostenlose Rest-API von DeepL\" width=\"649,627\" height=\"753,1015\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 6: Registrieren f&uuml;r die kostenlose Rest-API von DeepL<\/span><\/b><\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_437_007.png\" alt=\"Kopieren des API-Keys in die Zwischenablage\" width=\"649,627\" height=\"439,6714\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 7: Kopieren des API-Keys in die Zwischenablage<\/span><\/b><\/p>\n<p>In dem in unsere PowerPoint-Datei eingef&uuml;gten Modul tragen wir diesen Key ganz oben in die Zeile mit der Konsanten <b>cStrAuthKey <\/b>ein:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>Const cStrAuthKey<span style=\"color:blue;\"> As String<\/span> = _\r\n     \"059f6812-e199-40c3-88ea-9aaaaaaa9bc:fx\"<\/pre>\n<p>Au&szlig;erdem ist in diesem Modul nur eine kleine &Auml;nderung vorzunehmen. Im Artikel zu diesem Modul ging es darum, von Deutsch nach Englisch zu &uuml;bersetzen. Die R&uuml;ckgabe der Rest-API enthielt einen Hinweis auf die entdeckte Ausgangssprache (<b>DE<\/b>):<\/p>\n<pre>{\"translations\":[{\"detected_source_language\":\"DE\",\"text\":\"This is a sample text.\"}]}<\/pre>\n<p>Diesen Teil haben wir mit der <b>Replace<\/b>-Funktion einfach vorn abgeschnitten und dann auch noch den hinteren Teil entfernt, sodass nur noch der &uuml;bersetzte Text &uuml;brigblieb.<\/p>\n<p>Das l&ouml;sen wir in der aktuellen Version etwas eleganter, indem wir unsere Techniken zur Verarbeitung von JSON-Dokumenten nutzen.<\/p>\n<p>Diese stellen wir in den Artikeln <b>Mit JSON arbeiten <\/b>(<b>www.vbentwickler.de\/361<\/b>) und <b>JSON-Dokumente per Objektmodell zusammenstellen <\/b>(<b>www.vbentwickler.de\/412<\/b>) vor.<\/p>\n<p>Die aus diesen beiden Artikeln entstandenen und zwischenzeitlich optimieren Module <b>mdlJSON <\/b>und <b>mdlJSONDOM <\/b>haben wir ebenfalls der Beispieldatei hinzugef&uuml;gt.<\/p>\n<p>Um diese und DeepL einzusetzen, ben&ouml;tigen wir au&szlig;erdem zwei Verweise namens <b>Microsoft XML, v6.0<\/b> und <b>Microsoft Scripting Runtime<\/b> im VBA-Editor (siehe Bild 8).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_437_008.png\" alt=\"Verweise f&uuml;r das VBA-Projekt\" width=\"499,6267\" height=\"393,8742\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 8: Verweise f&uuml;r das VBA-Projekt<\/span><\/b><\/p>\n<h2>&Uuml;bersetzungsfunktion mit DeepL<\/h2>\n<p>Die &uuml;berarbeitete Version der Funktion <b>TranslateDeepl<\/b> sehen wir in Listing 2. Diese nimmt den zu &uuml;bersetzenden Text, die Ausgangssprache und die Zielsprache als Parameter entgegen.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>TranslateDeepl(strToTranslate<span style=\"color:blue;\"> As String<\/span>, intSourceLang<span style=\"color:blue;\"> As <\/span>SourceLanguage, intTargetLang<span style=\"color:blue;\"> As <\/span>_\r\n         TargetLanguage)<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strRequest<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strResponse<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strAuth_Key<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strText<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strSource_Lang<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strTarget_Lang<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strStatus<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strTranslated<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>objJSON<span style=\"color:blue;\"> As Object<\/span>    \r\n     strAuth_Key = \"?auth_key=\" & GetAuthKey\r\n     strText = \"&text=\" & URLEncode(strToTranslate)\r\n     strSource_Lang = \"&source_lang=\" & GetLanguageCodeSource(intSourceLang)\r\n     strTarget_Lang = \"&target_lang=\" & GetLanguageCodeTarget(intTargetLang)    \r\n     strRequest = cStrAPIEndpoint & \"\/v2\/translate\" & strAuth_Key & strText & strSource_Lang & strTarget_Lang _\r\n         & \"&tag_handling=xml\"    \r\n     <span style=\"color:blue;\">If <\/span>HTTPRequest(strRequest, strResponse, strStatus) = 200<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Set<\/span> objJSON = ParseJson(strResponse)\r\n         strTranslated = objJSON.Item(\"translations\").Item(1).Item(\"text\")\r\n         TranslateDeepl = strTranslated\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"Fehler beim Aufruf: \" & strStatus\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: &Uuml;bersetzen von Texten mit DeepL<\/span><\/b><\/p>\n<p>Dann setzt sie die URL zusammen, die wir an die Rest-API schicken. Diese enth&auml;lt beispielsweise den API-Key, den zu &uuml;bersetzenden Text, die Ausgangssprache und die Zielsprache.<\/p>\n<p>Diese URL wird mithilfe der Funktion <b>HTTPRequest <\/b>an Deepl geschickt.<\/p>\n<p>Diese liefert das Ergebnis in Form der <b>String<\/b>-Variablen <b>strResponse <\/b>zur&uuml;ck, die wir mit der Funktion <b>ParseJson <\/b>in ein Konstrukt aus Dictionarys und Collections &uuml;berf&uuml;hren. Daraus lesen wir die eigentliche &Uuml;bersetzung aus und schreiben diese in die Variable <b>strTranslated<\/b>. Den Inhalt geben wir schlie&szlig;lich als Funktionsergebnis an die aufrufende Prozedur zur&uuml;ck.<\/p>\n<p>Nun m&uuml;ssen wir nur noch die Funktion <b>TranslateDeepl <\/b>aus der Prozedur zum &Uuml;bersetzen heraus aufrufen.<\/p>\n<p>Dazu passen wir die innere Schleife wie folgt an:<\/p>\n<pre>For Each objParagraph In objTextRange.Paragraphs\r\n     strText = <span style=\"color:blue;\">Replace<\/span>(objParagraph.Text, <span style=\"color:blue;\">vbCr<\/span>, \"\")\r\n     strText = TranslateDeepl(strText, sEnglish, tGerman)\r\n     objParagraph.Text = strText\r\n<span style=\"color:blue;\">Next<\/span> objParagraph<\/pre>\n<p>Wir lesen also den Inhalt des Absatzes ohne das Zeilenumbruchszeichen in die Variable <b>strText <\/b>ein. <\/p>\n<p>Den Inhalt geben wir dann an die Funktion <b>TranslateDeepL<\/b>, die uns den &uuml;bersetzten Text zur&uuml;ckliefert. Diesen schrieben wir wieder in die Variable <b>strText<\/b>.<\/p>\n<p>Nun brauchen wir nur noch daf&uuml;r zu sorgen, dass der Text statt des vorherigen Textes im PowerPoint-Dokument landet &#8211; und das, ohne dass die Animationen verloren gehen.<\/p>\n<p>Spannenderweise reicht es dazu bereits aus, einfach nur den Inhalt von <b>strText <\/b>der <b>Text<\/b>-Eigenschaft des <b>TextFrame<\/b>-Elements <b>objParagraph <\/b>zuzuweisen.<\/p>\n<h2>Ausprobieren der &Uuml;bersetzungsfunktion<\/h2>\n<p>Bevor wir die Funktion ausprobieren, sollten wir den vorherigen Stand des Dokuments speichern &#8211; oder wir arbeiten direkt mit einer Kopie des Dokuments.<\/p>\n<p>Nach dem &Uuml;bersetzen, was durchaus einige Sekunden dauern kann, weil der Webservice einige Male aufgerufen werden muss, schauen wir uns das Ergebnis an und stellen erst einmal fest, dass die Animationen alle erhalten geblieben sind.<\/p>\n<p>Der einzige Makel, den wir feststellen konnten, ist die &Auml;nderung des Kaufmanns-Und in <b>&amp;<\/b>. Das haben wir manuell korrigiert.<\/p>\n<p>Das Problem tritt auf, weil wir in der bisherigen DeepL-L&ouml;sung zwar den zu &uuml;bersetzenden Text URL-kodieren, damit Zeichen wie eben das Kaufmanns-Und nicht falsch interpretiert werden k&ouml;nnen, dann aber den zur&uuml;ckgegebenen Text nicht wieder dekodieren. Wir belassen es an dieser Stelle dabei.<\/p>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Damit haben wir eine L&ouml;sung programmiert, mit der wir die einzelnen Abs&auml;tze einer PowerPoint-Pr&auml;sentation von einer in die andere Sprache &uuml;bersetzen k&ouml;nnen, ohne dass die Animationen verloren gehen.<\/p>\n<p>Allerdings befindet sich diese L&ouml;sung aktuell innerhalb des VBA-Moduls einer PowerPoint-Pr&auml;sentation. Diese Funktion wollen wir deshalb noch in ein COM-Add-In ausgliedern, das wir dann &uuml;ber das Ribbon von PowerPoint aufrufen k&ouml;nnen.<\/p>\n<p>Hier k&ouml;nnen wir beispielsweise anbieten, nur den Inhalt des aktuell markierten Shapes, der ganzen Seite oder auch des ganzen Dokuments in die gew&uuml;nschte Sprache zu &uuml;bersetzen.<\/p>\n<p>Mehr dazu liest Du im Artikel <b>PowerPoint-&Uuml;bersetzung per COM-Add-In <\/b>(<b>www.vbentwickler.de\/438<\/b>).<\/p>\n<h2>Downloads zu diesem Beitrag<\/h2>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>Presentation_amvExplorer.pptm<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/5A560AEF-CE5F-44CC-A75D-CC82D65797BB\/vbe_437.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Neulich war es mal wieder so weit: Eine PowerPoint-Pr&auml;sentation musste her. Und das auch noch auf Englisch. Okay, das Schul-Englisch ist zum Verstehen und schriftliche Kommunikation ausreichend, aber eine PowerPoint-Pr&auml;sention f&uuml;r englischsprachiges Fachpublikum sollte schon ann&auml;hrend perfekt sein. Wozu gibt es &Uuml;bersetzungsdienste? Also habe ich meine Texte auf Deutsch zurechtgelegt und diese von der KI &uuml;bersetzen lassen. Dann habe ich alles in die PowerPoint-Pr&auml;sentation eingef&uuml;gt und noch die Animationen hinzugef&uuml;gt, damit beispielsweise Stichpunkte Schritt f&uuml;r Schritt eingeblendet werden k&ouml;nnen. All das hat so gut geklappt, dass ich die Pr&auml;sentation anschlie&szlig;end auch noch f&uuml;r ein Video aufbereiten wollte &#8211; diesmal jedoch auf Deutsch. Also habe ich erstmal eine komplette Seite kopiert, &uuml;bersetzen lassen und wieder zur&uuml;ckgeschrieben. Das habe ich f&uuml;r einige Folien gemacht und dann schnell festgestellt, dass so alle Animationen verloren gehen. Der n&auml;chste Ansatz dann: Absatz f&uuml;r Absatz in die Zwischenablage, &uuml;bersetzen lassen, wieder zur&uuml;ckschreiben. So blieben die Animationen erhalten, aber es war zu viel Handarbeit. Wozu beherrsche ich &#8211; im Gegensatz zu Englisch &#8211; eigentlich perfekt VBA? Also habe ich mich an die Programmierung der &Uuml;bersetzung der enthaltenen Texte begeben. Das Ergebnis siehst Du in diesem Artikel!<\/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,44000035,44000030,44000025],"tags":[],"yst_prominent_words":[],"class_list":["post-55000437","post","type-post","status-publish","format-standard","hentry","category-662024","category-66042024","category-COMDLLs_programmieren","category-PowerPoint_programmieren","category-VBAProgrammierung"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000437","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=55000437"}],"version-history":[{"count":0,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000437\/revisions"}],"wp:attachment":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/media?parent=55000437"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/categories?post=55000437"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/tags?post=55000437"},{"taxonomy":"yst_prominent_words","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/yst_prominent_words?post=55000437"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}