From ce7839463ec3352ae509b09bcaecb9c108421775 Mon Sep 17 00:00:00 2001 From: Gilles Migliori Date: Sun, 14 Jan 2024 11:13:30 +0100 Subject: [PATCH] add an example of document upload and translation with progress bar --- .example.env | 1 + .gitignore | 4 +- composer.json | 3 +- examples/ajax/start-translation.php | 146 +++++++ .../ajax/wait-for-translation-complete.php | 63 ++++ .../translator-upload-with-progress-bar.html | 356 ++++++++++++++++++ 6 files changed, 571 insertions(+), 2 deletions(-) create mode 100644 .example.env create mode 100644 examples/ajax/start-translation.php create mode 100644 examples/ajax/wait-for-translation-complete.php create mode 100644 examples/translator-upload-with-progress-bar.html diff --git a/.example.env b/.example.env new file mode 100644 index 0000000..29bc130 --- /dev/null +++ b/.example.env @@ -0,0 +1 @@ +DEEPL_API_KEY="YOUR-API-KEY-HERE" diff --git a/.gitignore b/.gitignore index 18986ed..7d8d174 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,6 @@ reports/ .php-cs-fixer.cache # Ignore cached PHPUnit results. -.phpunit.result.cache \ No newline at end of file +.phpunit.result.cache +.env +.vscode diff --git a/composer.json b/composer.json index 581147c..24f70a7 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,8 @@ "ext-mbstring": "*", "psr/http-client-implementation": "*", "psr/http-factory-implementation": "*", - "php-http/multipart-stream-builder": "^1.3" + "php-http/multipart-stream-builder": "^1.3", + "vlucas/phpdotenv": "^5.6" }, "require-dev": { "phpunit/phpunit": "^9", diff --git a/examples/ajax/start-translation.php b/examples/ajax/start-translation.php new file mode 100644 index 0000000..a88cca2 --- /dev/null +++ b/examples/ajax/start-translation.php @@ -0,0 +1,146 @@ + 'error', + 'filename' => '', + 'msg' => '', + 'response' => null +]; + +try { + // Load the DeepL API key from the .env file + $dotenv = \Dotenv\Dotenv::createImmutable($_SERVER['DOCUMENT_ROOT']); + $dotenv->load(); + $apiKey = $_ENV['DEEPL_API_KEY']; + + if (!$apiKey) { + throw new \Exception('No API key specified. You must create a ".env" file in the root of the project with DEEPL_API_KEY="YOUR_DEEPL_API_KEY"'); + } + + // Check that the request is a POST request + if ($_SERVER['REQUEST_METHOD'] !== 'POST') { + throw new \Exception('Invalid request method.'); + } + + // Check that the required parameters are present + if (!isset($_POST['upload-type'])) { + throw new \Exception('No upload type specified.'); + } + if (!isset($_POST['source-language']) || !isset($_POST['target-language']) || !preg_match('/^[a-zA-Z-]{2,5}$/', $_POST['source-language']) || !preg_match('/^[a-zA-Z-]{2,5}$/', $_POST['target-language'])) { + throw new \Exception('Invalid source or target language specified.'); + } + + // Define the directory where uploaded files will be stored + $srcDir = rtrim(__DIR__, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '../uploads/'; + + // Handle the uploaded file + if ($_POST['upload-type'] === 'upload-file') { + if (!isset($_FILES['uploaded-file'])) { + throw new \Exception('No uploaded file specified.'); + } + + $file = $_FILES['uploaded-file']; + + if ($file['error'] !== UPLOAD_ERR_OK) { + throw new \Exception('Error uploading file: ' . $file['error']); + } + + $fileName = $file['name']; + $fileExtension = pathinfo($fileName, PATHINFO_EXTENSION); + + if (!in_array($fileExtension, ['html', 'txt'])) { + throw new \Exception('Only .html or .txt files are allowed. Please upload a valid file.'); + } + + $fileMimeType = mime_content_type($file['tmp_name']); + + if ($fileMimeType !== 'text/plain' && $fileMimeType !== 'text/html') { + throw new \Exception('Invalid file type. Only html or text files are allowed.'); + } + + $srcFile = $srcDir . basename($fileName); + + if (!move_uploaded_file($file['tmp_name'], $srcFile)) { + throw new \Exception('Error moving uploaded file to target directory.'); + } + $output['filename'] = basename($fileName); + } elseif ($_POST['upload-type'] === 'enter-url') { + $url = filter_var($_POST['url-input'], FILTER_SANITIZE_URL); + + // Remove 'http://' or 'https://' from $_POST['url-input'] + $fileName = preg_replace('/^https?:\/\//', '', $_POST['url-input']); + + // Remove all the slashes + $fileName = str_replace('/', '', $fileName); + + // If $fileName doesn't end with a '.html' extension, add it + if (!preg_match('/\.html$/', $fileName)) { + $fileName.= '.html'; + } + + $inputFile = file_get_contents($_POST['url-input']); + file_put_contents($srcDir. $fileName, $inputFile); + + $srcFile = $srcDir. $fileName; + + $output['filename'] = $fileName; + } else { + throw new \Exception('Invalid upload type.'); + } + + // Initialize the DeepL translator + try { + $deepl_translator = new \DeepL\Translator($apiKey); + + // Translate the uploaded file + $options = [ + 'formality' => 'prefer_more', + 'tag_handling' => 'html', + 'ignore_tags' => ['script', 'noscript', 'style', 'pre', 'code'] + ]; + + $document_handle = $deepl_translator->uploadDocument( + $srcFile, + $_POST['source-language'], + $_POST['target-language'], + $options + ); + + $document_status = $deepl_translator->getDocumentStatus($document_handle); + + $output['status'] = 'success'; + $output['response'] = [ + 'documentHandle' => $document_handle, + 'documentStatus' => $document_status + ]; + } catch (\DeepL\DocumentTranslationException $error) { + // If the error occurs after the document was already uploaded, + // documentHandle will contain the document ID and key + throw new \Exception('Error occurred while translating document: ' . ($error->getMessage() ?? 'unknown error')); + } +} catch (\Exception $e) { + $output['status'] = 'error'; + $output['msg'] = $e->getMessage(); +} + +echo json_encode($output); diff --git a/examples/ajax/wait-for-translation-complete.php b/examples/ajax/wait-for-translation-complete.php new file mode 100644 index 0000000..3766196 --- /dev/null +++ b/examples/ajax/wait-for-translation-complete.php @@ -0,0 +1,63 @@ + 'error', + 'msg' => '', + 'documentStatus' => null, + 'downloadUrl' => null +]; + +try { + $dotenv = \Dotenv\Dotenv::createImmutable($_SERVER['DOCUMENT_ROOT']); + $dotenv->load(); + $apiKey = $_ENV['DEEPL_API_KEY']; + if (!$apiKey) { + throw new \Exception('No API key specified. You must create a ".env" file in the root of the project with DEEPL_API_KEY="YOUR_DEEPL_API_KEY"'); + } + + if (!isset($_POST['document-handle'])) { + throw new \Exception('No document handle specified.'); + } + + $dh = json_decode($_POST['document-handle'], true); + if (!$dh) { + throw new \Exception('Invalid document handle'); + } + + $document_handle = new \DeepL\DocumentHandle($dh['documentId'], $dh['documentKey']); + + try { + $output['status'] = 'success'; + + $deepl_translator = new \DeepL\Translator($apiKey); + + $document_status = $deepl_translator->waitUntilDocumentTranslationComplete($document_handle); + + $output['documentStatus'] = $document_status; + + if ($document_status->status == 'done') { + $ext = pathinfo($_POST['filename'], PATHINFO_EXTENSION); + $name = pathinfo($_POST['filename'], PATHINFO_FILENAME); + + $newFilename = $name. '-' . $_POST['target-language'] . '-translated.' . $ext; + + if (file_exists('../uploads/' . $newFilename)) { + unlink('../uploads/'. $newFilename); + } + + $deepl_translator->downloadDocument($document_handle, '../uploads/' . $newFilename); + $output['downloadUrl'] = 'uploads/'. $newFilename; + } + } catch (\Exception $e) { + $output['status'] = 'error'; + $output['msg'] = $e->getMessage(); + } +} catch (\Exception $e) { + $output['status'] = 'error'; + $output['msg'] = $e->getMessage(); +} + +echo json_encode($output); diff --git a/examples/translator-upload-with-progress-bar.html b/examples/translator-upload-with-progress-bar.html new file mode 100644 index 0000000..a081e30 --- /dev/null +++ b/examples/translator-upload-with-progress-bar.html @@ -0,0 +1,356 @@ + + + + + + DeepL PHP API - upload and translate a document with a progress bar + + + + + + + + + + + + + +
+ +

DeepL PHP API -
File upload & translation with Javascript Fetch() and progress bar

+ + +
    +
  1. Choose a .txt or .html file to upload.
  2. +
  3. The application calls the Deeppl API, which starts the translation and returns the estimated translation duration.
  4. +
  5. The progress bar is triggered.
  6. +
  7. The application sends a 2nd call to the API and waits for the result of the translation.
  8. +
+ + +
+ + +
+ + +
+
+ +
+ + +
+
+ + +
+
+
+ + +
+
+ + + + +
+
+ + + + +
+
+ + +
+
+ + + + +
+ +
+ + + + +
+
+ + +
+
+ + +
+
+
+ + +
+ +
0%
+
+
+ + + + + +