Erweiterung meines Blogs mit Übersetzungen durch Amazon Nova

Diese Datei wurde automatisch von KI übersetzt, es können Fehler auftreten
Lesen Sie diesen Beitrag auf Englisch? Oder vielleicht auf Spanisch? Oder warum nicht auf Italienisch?
In meinem vorherigen Beitrag über die Automatisierung von Aufgaben für meinen Blog habe ich automatisches Korrekturlesen mit Amazon Nova hinzugefügt. Obwohl das Schreiben auf Englisch mir ermöglicht, mit einer großen technischen Zielgruppe in Verbindung zu treten, gibt es eine noch größere globale Community, die von meinen Inhalten profitieren könnte, wenn sie in ihrer Muttersprache verfügbar wären.
Ich habe meinen Blog bereits mit Sprachgenerierung mit Amazon Polly erweitert. Das habe ich getan, damit Menschen, die Schwierigkeiten beim Lesen haben, wie zum Beispiel Menschen mit Legasthenie, von meinen Inhalten lernen können, ohne Schwierigkeiten beim Lesen zu haben, sondern sie stattdessen anzuhören.
Dieses Mal breche ich Sprachbarrieren und mache meine technischen Inhalte für Entwickler und Cloud-Enthusiasten auf der ganzen Welt durch automatische Übersetzung mit Amazon Nova zugänglich.
Vision
Die technische Community ist unglaublich vielfältig, und obwohl Englisch in der Tech-Welt meist verwendet wird, ist bei weitem nicht jeder Muttersprachler. Ich bin es nicht, und bei weitem nicht jeder fühlt sich zu 100 % wohl dabei, Inhalte auf Englisch zu konsumieren. Sie ziehen es vor, sie in ihrer Muttersprache zu lesen. Komplexe technische Konzepte können schon schwierig genug sein, ohne die zusätzliche Hürde des Sprachverständnisses.
In meinem Beitrag Serverlose Statistiklösung mit Lambda@Edge erkläre ich, wie ich ein serverloses Analysesystem für meinen Blog aufgebaut habe, mit dem ich sehen konnte, woher meine Leser kommen und vieles mehr.
Wenn ich mir meine Analysen ansehe, kann ich viele Leser aus Deutschland, Österreich, Frankreich, Italien, Spanien (und anderen spanischsprachigen Ländern) sowie Brasilien und Portugal sehen. Deshalb wollte ich eine Lösung schaffen, bei der die Leute wählen können, ob sie auf Englisch oder in einer anderen Sprache lesen möchten.
Für diese erste Implementierung eines Übersetzungsdienstes habe ich mich dafür entschieden, fünf Sprachen zu unterstützen, die einen großen Teil meiner Leser repräsentieren: Deutsch, Spanisch, Französisch, Italienisch und Portugiesisch. In einer zweiten Phase werde ich möglicherweise zusätzliche Sprachen wie Japanisch hinzufügen.
Wichtig war, dass die Lösung:
Technische Genauigkeit beibehalten: Technische Begriffe und AWS-Dienstnamen müssen korrekt übersetzt werden.
Markdown-Struktur beibehalten: Alle Formatierungen, Codeblöcke, Links und Bilder müssen intakt bleiben.
Meinen Schreibstil beibehalten: Die Übersetzung sollte natürlich klingen und gleichzeitig die technische Tiefe bewahren.
Effizient skalieren: Unterstützung mehrerer Sprachen ohne Vervielfachung meiner Arbeitsbelastung.
Nahtlos integrieren: Passt in meine bestehende serverlose und ereignisgesteuerte Pipeline.
Amazon Nova für Übersetzungen
Nachdem ich Amazon Nova Pro erfolgreich für das Korrekturlesen verwendet habe, war ich mir sicher, dass es auch meine Übersetzungsaufgaben bewältigen kann. Die multimodalen Fähigkeiten von Nova Pro und das Verständnis des Kontexts machen es für die Übersetzung technischer Inhalte gut geeignet. Mehr über Nova, verschiedene Modelle und BedRock erfahren Sie in meinem Beitrag automatisches Korrekturlesen mit Amazon Nova.
Lösungsarchitektur
Basierend auf meiner bestehenden ereignisgesteuerten Architektur habe ich die Pipeline um die Übersetzung erweitert. Als ich die Pipeline erstellte, baute ich sie auf eine sehr modulare Weise auf, sodass sie leicht erweitert werden konnte. Dieser Ansatz zahlt sich jetzt wirklich aus, da ich Erweiterungen nahtlos und mit sehr wenig Aufwand in den gleichen Ablauf einfügen kann.
Die Übersetzungen laufen parallel zur Generierung von Sprache und Quiz, falls ein Pull-Request ausgeführt wird. Ich habe mich jedoch entschieden, sie vom Standard-Saga-Flow zu trennen, den ich für Sprache und Quiz verwende. Der Grund dafür ist, dass ich die Übersetzungen nicht nur bei Pull-Requests ausführen, sondern auch Übersetzungen älterer Beiträge aufrufen möchte.
Im Falle des Pull-Request-Kontexts beginnt der Ablauf nach Abschluss des Korrekturlesens durch ein Ereignis von GitHub Actions. Ich kann den Ablauf aber auch aufrufen und starten, indem ich ein Ereignis mit einem Branch-Namen und dem zu übersetzenden Blog an den Informationsdienst sende, der dann den gesamten Ablauf startet. Eine Ergänzung in diesem Fall ist, dass ich in einem letzten Schritt auch einen neuen Pull-Request mit den Übersetzungen für den alten Beitrag öffne.
Die Übersetzungen werden in sprachspezifischen Verzeichnissen (z. B. /de/
, /es/
, /fr/
) gespeichert, was mir ermöglicht, alles während des Build-Prozesses zu organisieren. Ich verwende 11ty, um statisches HTML aus meinen in Markdown geschriebenen Beiträgen zu generieren.
Technischer Tiefgang
Die Übersetzung wird als eigener Dienst implementiert und basiert auf Step Functions als Hauptdienst und Orchestrator. Die Übersetzung beginnt entweder damit, dass ich einen neuen Branch erstelle und den Ablauf für einen älteren Beitrag starte, oder sie wird automatisch gestartet, wenn ein neuer Pull-Request für einen neuen Beitrag geöffnet wird. So entsteht ein Übersetzungsablauf.
Übersetzungs-Step-Function
Der Kern des Übersetzungsdienstes ist diese Step Function, die den gesamten Ablauf orchestriert. Wenn Sie mir schon länger folgen, wissen Sie, dass ich normalerweise versuche, Step Functions SDK-Integrationen so weit wie möglich zu verwenden, aber für den Übersetzungsablauf muss ich mich stark auf meinen zweitliebsten Dienst, Lambda, verlassen.
Der Workflow besteht aus mehreren Schlüsselzuständen:
- CollectInfo: Normalisiert Eingabedaten und bereitet sie für Übersetzungen vor, liest Sprachen aus dem Ereignis oder verwendet Standardwerte.
- CheckoutMarkdownFile: Lädt die Quell-Markdown-Datei von GitHub herunter.
- Iterate Languages: Führt Übersetzungen für die Zielsprachen durch, begrenzt auf 1 parallele Übersetzung im Moment.
- CreateCommit: Committet alle übersetzten Dateien zurück in das Repository, öffnet bei Bedarf einen neuen Pull-Request.
Bei der Definition der Step Function verwende ich die Unterstützung für die JSONata-Abfragesprache, die zusammen mit Variablen eine der besten Ergänzungen zu Step Functions ist. Der CollectInfo
-Zustand speichert Informationen in Variablen, die für alle zukünftigen Zustände verfügbar sind. Bei der Iteration über Sprachen verwende ich einen Map
-Zustand mit einer auf 1 begrenzten Grenze.
Comment: Übersetzen von Blog-Inhalten mit Bedrock.
QueryLanguage: JSONata
StartAt: CollectInfo
States:
CollectInfo:
Type: Pass
Assign:
Languages: "{% $exists($states.input.detail.Data.Translate.Languages) ? $states.input.detail.Data.Translate.Languages : ['de','es','fr','it','pt'] %}"
GitInfo:
CommitSha: "{% $exists($states.input.detail.Data.GitInfo.CommitSha) ? $states.input.detail.Data.GitInfo.CommitSha : $states.input.detail.Data.PullRequestInfo.PullRequestCommitSha %}"
Branch: "{% $exists($states.input.detail.Data.GitInfo.Branch) ? $states.input.detail.Data.GitInfo.Branch : $states.input.detail.Data.PullRequestInfo.PullRequestBranch %}"
MarkdownInfo:
FilePath: "{% $states.input.detail.Data.MarkdownFile.path %}"
FileSlug: "{% $states.input.detail.Data.MarkdownFile.fileSlug %}"
Next: CheckoutMarkdownFile
CheckoutMarkdownFile:
Type: Task
Resource: arn:aws:states:::lambda:invoke
Arguments:
FunctionName: ${CheckoutMarkdownFileFunctionArn}
Payload:
GitInfo: "{% $GitInfo %}"
MarkdownInfo: "{% $MarkdownInfo %}"
Next: Iterate Languages
Iterate Languages:
Type: Map
ItemProcessor:
ProcessorConfig:
Mode: INLINE
StartAt: TranslateMarkdownFile
States:
TranslateMarkdownFile:
Type: Task
Resource: arn:aws:states:::lambda:invoke
Arguments:
FunctionName: ${TranslateBedrockFunctionArn}
Payload:
Key: "{%$MarkdownInfo.FileSlug & '/' & $MarkdownInfo.FilePath %}"
S3Bucket: ${ETLBucket}
Language: "{% $states.input %}"
End: true
Next: CreateCommit
MaxConcurrency: 1
Items: "{% $Languages %}"
CreateCommit:
Type: Task
Resource: arn:aws:states:::lambda:invoke
Arguments:
FunctionName: ${CreateCommitFunctionArn}
Payload:
GitInfo: "{% $GitInfo %}"
MarkdownInfo: "{% $MarkdownInfo %}"
End: true
Lambda-Funktionen
Bei den Lambda-Funktionen gibt es drei Hauptfunktionen in der Lösung. Die Funktion, die Octokit zum Auschecken des Codes verwendet, und die Funktion, die den neuen Commit erstellt. Beide wurden in früheren Beiträgen zu diesem Thema erklärt und werden dieses Mal ausgelassen.
Übersetzungsfunktion
Dies ist das Herzstück der gesamten Lösung und wird mit Bedrock interagieren, um den Beitrag zu übersetzen. Der übersetzte Beitrag wird in einem sprachspezifischen Verzeichnis gespeichert.
import json
import os
import boto3
from botocore.exceptions import ClientError
def handler(event, context):
try:
bucket = event.get("S3Bucket")
key = event.get("Key")
lang = event.get("Language", "error")
if lang not in ["de", "es", "fr", "it", "pt"]:
raise ValueError(
f"Nicht unterstützte Sprache '{lang}'. Unterstützte Sprachen sind: de, es, fr, it, pt."
)
s3_client = boto3.client("s3")
bedrock_client = boto3.client("bedrock-runtime", region_name="eu-west-1")
response = s3_client.get_object(Bucket=bucket, Key=key)
file_content = response["Body"].read().decode("utf-8")
prompt = f"""Sie sind ein professioneller technischer Redakteur und Übersetzer, Experte für AWS und Cloud Computing. Bitte übersetzen Sie sorgfältig den folgenden Markdown-Blogbeitrag in {lang}.
Anweisungen:
- Behalten Sie die ursprüngliche Markdown-Formatierung bei
- Behalten Sie den Ton und Stil bei
- Ändern Sie nicht die Bedeutung oder Struktur des Inhalts
- Geben Sie nur den übersetzten Markdown-Text ohne zusätzliche Kommentare zurück
- Übersetzen Sie keine Codeblöcke oder Inline-Code
- Achten Sie auf korrekte Grammatik, Rechtschreibung und Zeichensetzung
- Wenn der Inhalt bereits in {lang} ist, ignorieren Sie ihn und geben Sie den Inhalt wie er ist zurück
- Umgeben Sie die übersetzte Version nicht mit ```markdown
Hier ist der zu übersetzende Markdown-Inhalt:
{file_content}"""
# Bereiten Sie die Anfrage für das Nova Pro-Modell vor
request_body = {
"messages": [{"role": "user", "content": [{"text": prompt}]}],
"inferenceConfig": {
"temperature": 0.1, # Niedrige Temperatur für konsistente Übersetzungen
"topP": 0.9,
"maxTokens": 10240, # Anpassung basierend auf der erwarteten Ausgabelänge
},
}
bedrock_response = bedrock_client.invoke_model(
modelId="eu.amazon.nova-pro-v1:0",
body=json.dumps(request_body),
contentType="application/json",
)
response_body = json.loads(bedrock_response["body"].read())
translated_content = response_body["output"]["message"]["content"][0]["text"]
new_key = f"{lang}/{key}"
s3_client.put_object(
Bucket=bucket,
Key=new_key,
Body=translated_content.encode("utf-8"),
ContentType="text/markdown",
)
return {
"statusCode": 200,
"body": "Übersetzung abgeschlossen",
}
except Exception as e:
print(f"Unerwarteter Fehler: {str(e)}")
return {
"statusCode": 500,
"body": json.dumps({"error": "Interner Serverfehler", "message": str(e)}),
}
Übersetzungsqualität und Prompt-Engineering
Während der Implementierung der Korrekturleselösung habe ich gelernt, wie wichtig der Teil des Prompt-Engineerings für das Endergebnis ist. Die KI tut immer noch nur das, was Sie ihr sagen, und wenn Ihr Prompt nicht spezifisch genug ist, kann die Qualität des Ergebnisses variieren. Unter Verwendung der Erkenntnisse aus dem Korrekturlesen enthielt mein Prompt einige spezifische Anforderungen.
Am Ende lieferte mir der folgende Prompt einige großartige Ergebnisse:
Sie sind ein professioneller technischer Redakteur und Übersetzer, Experte für AWS und Cloud Computing.
Bitte übersetzen Sie sorgfältig den folgenden Markdown-Blogbeitrag in {lang}.
Anweisungen:
- Behalten Sie die ursprüngliche Markdown-Formatierung bei
- Behalten Sie den Ton und Stil bei
- Ändern Sie nicht die Bedeutung oder Struktur des Inhalts
- Geben Sie nur den übersetzten Markdown-Text ohne zusätzliche Kommentare zurück
- Übersetzen Sie keine Codeblöcke oder Inline-Code
- Achten Sie auf korrekte Grammatik, Rechtschreibung und Zeichensetzung
- Wenn der Inhalt bereits in {lang} ist, ignorieren Sie ihn und geben Sie den Inhalt wie er ist zurück
- Umgeben Sie die übersetzte Version nicht mit ```markdown
Hier ist der zu übersetzende Markdown-Inhalt:
11ty-Änderungen
Ich verwende 11ty als statischen HTML-Generator, alle Blogs werden in Markdown geschrieben und dann in HTML konvertiert. Um verschiedene Sprachen unterstützen zu können, musste ich einige Änderungen an meinem 11ty-Build und -Layout vornehmen. Zunächst habe ich dem Front Matter ein neues Feld hinzugefügt, das angibt, in welcher Sprache der Beitrag verfasst ist. Dann musste ich ändern, wie meine Post-Collection Blog-Beiträge abruft.
Ich habe einen Filter hinzugefügt, damit ich nur die Beiträge auf Englisch abruf, damit ich nicht alle Beiträge in der Sammlung erhalte.
export default (coll) => {
const posts = coll
.getFilteredByGlob("src/posts/*.md")
.filter((p) => (p.data.lang || "en") === "en");
return posts.reverse();
};
Als Nächstes musste ich alle Übersetzungen für einen Beitrag lokalisieren. Ich habe den Datei-Slug als Schlüssel verwendet, um diese Sammlung zu erstellen.
config.addCollection("postsBySlug", (collection) => {
const posts = collection.getFilteredByGlob("src/posts/**/*.md");
const postsBySlug = {};
posts.forEach((post) => {
const pathParts = post.inputPath.split("/");
const filename = pathParts[pathParts.length - 1];
const baseSlug = filename.replace(".md", "");
if (!postsBySlug[baseSlug]) {
postsBySlug[baseSlug] = [];
}
postsBySlug[baseSlug].push(post);
});
return postsBySlug;
});
Damit konnte ich einen Sprachindikator erstellen und einschließen, der der Beitrags-Grid Flaggen hinzufügt, die auf Übersetzungen hinweisen. Jede Flagge ist anklickbar und führt Sie direkt zum übersetzten Beitrag.
{% set item = post or talk %}
{% set pathParts = item.inputPath.split('/') %}
{% set filename = pathParts[pathParts.length - 1] %}
{% set baseSlug = filename.replace('.md', '') %}
{% set currentLang = item.data.lang or 'en' %}
{# Sprachindikator nur für Beiträge anzeigen (nicht für Talks oder Links) #}
{% if item.inputPath.includes('/posts/') %}
{% set availablePosts = collections.postsBySlug[baseSlug] %}
{% if availablePosts.length > 1 %}
<div class="mx-auto flex items-center flex-wrap">
<div class="justify-start items-center flex-grow w-full md:w-auto md:flex">
<p class="text-gray-700 mb-1 font-medium" title="Verfügbare Sprachen">
<i class="fas fa-globe text-teal-600 fill-current"></i>
{% for translatedPost in availablePosts %}
{% set postLang = translatedPost.data.lang or 'en' %}
<a href="{{ translatedPost.url }}" class="text-sm ml-1 hover:scale-110 transition-transform duration-200 inline-block" title="{{ postLang | getLanguageName }}">{{ postLang | getLanguageFlag }}</a>
{% endfor %}
</p>
</div>
</div>
{% endif %}
{% endif %}
Das Ergebnis:
Schließlich habe ich dem eigentlichen Beitrags-Layout ein Dropdown-Menü zusammen mit einem Haftungsausschluss hinzugefügt, dass der Beitrag von einer KI übersetzt wurde.
Mit diesen Änderungen können Sie auswählen, in welcher Sprache Sie jeden Beitrag lesen möchten.
Fazit
Die automatisierte Übersetzung zu meinem Blog hinzuzufügen, fühlte sich wie der nächste Schritt an, um meine Inhalte für alle zugänglich zu machen. Durch die Nutzung von Amazon Nova Pro und meiner bestehenden serverlosen Architektur habe ich eine skalierbare Übersetzungslösung geschaffen, ohne die betriebliche Komplexität zu erhöhen. Ob Sie nun einen technischen Blog, eine Dokumentation oder eine beliebige Content-Plattform erstellen, automatisierte Übersetzungen könnten Ihre Reichweite erweitern.
Warum habe ich nicht die gesamte Website übersetzt? Das war eine Route, die ich zuerst erkundet habe, aber ich hatte das Gefühl, dass der Hauptvorteil darin bestehen würde, die Beiträge übersetzen zu lassen. Es gibt mir auch die Freiheit zu entscheiden, ob ich alle älteren Beiträge übersetzen möchte oder nicht, und ich kann Übersetzungen nachträglich hinzufügen. Eines Tages werde ich vielleicht alles übersetzen, aber im Moment lag der Fokus auf den einzelnen Beiträgen.
Abschließende Worte
Vergessen Sie nicht, mir auf LinkedIn zu folgen und für weitere Inhalte lesen Sie den Rest meiner Blogs.
Wie Werner sagt! Jetzt bauen Sie los!
Dieser Beitrag wurde ursprünglich auf Englisch geschrieben und automatisch in mehrere Sprachen übersetzt, indem das hier beschriebene System verwendet wurde. Der Übersetzungsprozess dauerte weniger als 5 Minuten und erforderte keinen manuellen Eingriff.