Created
October 3, 2022 15:13
-
-
Save lgaud/1503722b5b332a9a94d1e1b20b9cf052 to your computer and use it in GitHub Desktop.
This example uses Azure Cognitive Services Translator to translate Notion pages into a new Notion page. Code example for www.pynotion.com.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
from dotenv import load_dotenv | |
import requests | |
""" | |
This example uses Azure Cognitive Services Translator to translate Notion pages into a new Notion page. | |
See full explanation at: https://www.pynotion.com/translate-with-azure-translator-part-2 | |
Create a Translator resource in Azure, and set the key and region in your environment or a .env file as | |
COG_SERVICE_KEY and COG_SERVICE_REGION. The Free tier (F0) should be plenty for demonstration purposes. | |
This code also works with a multi-service Cognitive Services resource (which does not have a free tier). | |
More details on creating a Translator Resource | |
https://learn.microsoft.com/en-us/azure/cognitive-services/translator/quickstart-translator?tabs=python | |
Create a Notion integration, and set an environment variable or entry in the .env file for NOTION_KEY. | |
Share a page you'd like to run through the translator with the integration, and take a note of the id | |
from the URL to use as the page_id argument. | |
""" | |
class TranslatorClient(): | |
def __init__(self, cog_service_key, cog_service_region): | |
self.cog_key = cog_service_key | |
self.cog_region = cog_service_region | |
self.translator_endpoint = 'https://api.cognitive.microsofttranslator.com' | |
self.default_headers = { | |
'Ocp-Apim-Subscription-Key': self.cog_key, | |
'Ocp-Apim-Subscription-Region': self.cog_region, | |
'Content-type': 'application/json' | |
} | |
self.session = requests.Session() | |
self.session.headers.update(self.default_headers) | |
def translate(self, text, source_language, target_language): | |
url = self.translator_endpoint + '/translate' | |
# Specify Query Parameters | |
params = { | |
'api-version': '3.0', # Required | |
'from': source_language, # Optional, will auto-detect in most cases | |
'to': target_language # Required. | |
} | |
body = [{ | |
'text': text | |
}] | |
# Send the request and get response | |
request = self.session.post(url, params=params, json=body) | |
# Parse the JSON Response | |
response = request.json() | |
translation = response[0]["translations"][0]["text"] | |
# Return the translation | |
return translation | |
class NotionClient(): | |
def __init__(self, notion_key): | |
self.notion_key = notion_key | |
self.default_headers = {'Authorization': f"Bearer {self.notion_key}", | |
'Content-Type': 'application/json', 'Notion-Version': '2022-06-28'} | |
self.session = requests.Session() | |
self.session.headers.update(self.default_headers) | |
def get_property(self, page_id, property_id): | |
url = f"https://api.notion.com/v1/pages/{page_id}/properties/{property_id}" | |
response = self.session.get(url) | |
return response.json() | |
def get_title_text(self, page_id): | |
title_property = self.get_property(page_id, "title")["results"][0] | |
return title_property["title"]["plain_text"] | |
def get_notion_blocks(self, block_id, start_cursor=None, page_size=None): | |
url = f'https://api.notion.com/v1/blocks/{block_id}/children' | |
params = {} | |
if start_cursor is not None: | |
params["start_cursor"] = start_cursor | |
if page_size is not None: | |
params["page_size"] = page_size | |
response = self.session.get(url, params=params) | |
return response.json() | |
def get_text(self, rich_text_object): | |
# Concatenates a rich text array into plain text | |
text = "" | |
for rt in rich_text_object["rich_text"]: | |
text += rt["plain_text"] | |
return text | |
def get_block_text(self, block): | |
if block["type"] == "paragraph": | |
return self.get_text(block["paragraph"]) | |
if block["type"] == "heading_1": | |
return self.get_text(block["heading_1"]) | |
if block["type"] == "heading_2": | |
return self.get_text(block["heading_2"]) | |
if block["type"] == "heading_3": | |
return self.get_text(block["heading_3"]) | |
return None | |
def update_block_text(self, block, new_text): | |
block[block["type"]]["rich_text"] = [ | |
{"type": "text", "text": {"content": new_text}}] | |
return block | |
def create_page(self, parent_page_id, children, title): | |
create_page_body = { | |
"parent": {"page_id": parent_page_id}, | |
"properties": { | |
"title": { | |
"title": [{"type": "text", "text": {"content": title}}] | |
} | |
}, | |
"children": children | |
} | |
create_response = self.session.post( | |
"https://api.notion.com/v1/pages", json=create_page_body) | |
return create_response | |
class NotionTranslator(): | |
def __init__(self, notion_client, translate_client, source_language, target_language): | |
self.notion_client = notion_client | |
self.translate_client = translate_client | |
self.source_language = source_language | |
self.target_language = target_language | |
def translate_title(self, source_page_id): | |
title = self.notion_client.get_title_text(source_page_id) | |
translated_title = self.translate_client.translate( | |
title, self.source_language, self.target_language) | |
return translated_title | |
def translate_all_blocks(self, source_page_id): | |
blocks_response = self.notion_client.get_notion_blocks( | |
source_page_id) | |
translated_blocks = self.translate_blocks( | |
blocks_response.get("results")) | |
while blocks_response.get("has_more"): | |
blocks_response = self.notion_client.get_notion_blocks( | |
source_page_id, blocks_response.get("next_cursor")) | |
translated_blocks.extend(self.translate_blocks( | |
blocks_response.get("results"))) | |
return translated_blocks | |
def create_translated_page(self, source_page_id, target_page_id=None): | |
# Create as a child of source page if a parent is not set | |
if target_page_id is None: | |
target_page_id = source_page_id | |
translated_title = self.translate_title(source_page_id) | |
translated_content = self.translate_all_blocks(source_page_id) | |
response = self.notion_client.create_page( | |
target_page_id, translated_content, translated_title) | |
return response | |
def translate_blocks(self, blocks): | |
translated_blocks = [] | |
for block in blocks: | |
source_text = self.notion_client.get_block_text(block) | |
if source_text is not None: | |
translated = self.translate_client.translate( | |
source_text, self.source_language, self.target_language) | |
translated_blocks.append( | |
self.notion_client.update_block_text(block, translated)) | |
elif block["type"] == "child_page": | |
pass | |
elif block["type"] != "unsupported": | |
translated_blocks.append(block) | |
return translated_blocks | |
def main(notion_page_id, source_language="en", target_language="fr"): | |
notion_client = NotionClient(os.getenv('NOTION_KEY')) | |
translate_client = TranslatorClient( | |
os.getenv('COG_SERVICE_KEY'), os.getenv('COG_SERVICE_REGION')) | |
translator = NotionTranslator( | |
notion_client, translate_client, source_language, target_language) | |
response = translator.create_translated_page(notion_page_id) | |
if (response.ok): | |
print("Page translated successfully!") | |
print(response.json()["url"]) | |
else: | |
print("Error translating page") | |
if __name__ == "__main__": | |
import argparse | |
load_dotenv(override=True) | |
parser = argparse.ArgumentParser( | |
description="Translate a Notion page's title. Supported language codes are listed at https://learn.microsoft.com/en-us/azure/cognitive-services/translator/language-support") | |
parser.add_argument('page_id', type=str, | |
help='A Notion page ID to translate') | |
parser.add_argument('target', | |
help='language code to translate the page to') | |
parser.add_argument('--source', default="en", | |
help="language code for the original language of the page") | |
args = parser.parse_args() | |
main(args.page_id, args.source, args.target) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment