1
0
mirror of https://github.com/upscayl/upscayl.git synced 2024-11-23 23:21:05 +01:00

#885 Paste Image from Clipboard (#995)

This feature fix does the following:

1. modifies handlePaste function to save the image present in clipboard
into the user selected output folder and process it.
2. names the image with hh-mm-ss timestamp and gives output file in
provided folder.

Here is an example with nothing in the clipboard showing an invalid
image error:


![image](https://github.com/user-attachments/assets/c8596354-90d9-497f-9e08-b95ebf955b81)

Here is an example with an image in the clipboard saving in user
selected folder:


![image](https://github.com/user-attachments/assets/3c7e5f84-7fe1-4baa-a282-6b921641b9af)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Release Notes

- **New Features**
- Users can now paste images directly from the clipboard using the
shortcut Ctrl + V or Cmd + V.
- Enhanced error messages provide clearer guidance when no image is
found in the clipboard or when an output folder is not selected.
- New instructions for pasting images have been added to localization
files in multiple languages.

- **Bug Fixes**
- Improved handling of pasted images with better error notifications for
invalid formats.

- **Documentation**
- Updated localization files for multiple languages, enhancing user
instructions and error descriptions related to image handling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Abhishek Gaonkar 2024-11-17 11:53:40 +05:30 committed by GitHub
commit c5f4a7eedb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 231 additions and 58 deletions

View File

@ -25,6 +25,9 @@ const ELECTRON_COMMANDS = {
OS: "Get OS", OS: "Get OS",
SCALING_AND_CONVERTING: "Adding some finishing touches", SCALING_AND_CONVERTING: "Adding some finishing touches",
UPSCAYL_ERROR: "Upscaling Error", UPSCAYL_ERROR: "Upscaling Error",
PASTE_IMAGE: "Paste Image from clipboard",
PASTE_IMAGE_SAVE_SUCCESS: "Clipboard Image saved successfully",
PASTE_IMAGE_SAVE_ERROR: "Clipboard Image save failed",
} as const; } as const;
export { ELECTRON_COMMANDS }; export { ELECTRON_COMMANDS };

View File

@ -1,12 +1,15 @@
export default function getDirectoryFromPath(filePath: string): string { export default function getDirectoryFromPath(
filePath: string,
popFileName: boolean = true,
): string {
// Define the path separator based on the operating system // Define the path separator based on the operating system
const separator = filePath.includes("/") ? "/" : "\\"; const separator = filePath.includes("/") ? "/" : "\\";
// Split the file path by the path separator // Split the file path by the path separator
const pathParts = filePath.split(separator); const pathParts = filePath.split(separator);
// Remove the last element to get the directory // Remove the last element to get the directory if popFileName is true
pathParts.pop(); if (popFileName) pathParts.pop();
// Join the remaining parts back together to form the directory path // Join the remaining parts back together to form the directory path
const directoryPath = pathParts.join(separator); const directoryPath = pathParts.join(separator);

1
common/image-formats.ts Normal file
View File

@ -0,0 +1 @@
export const imageFormats = ["png", "jpg", "jpeg", "webp"] as const;

View File

@ -0,0 +1,61 @@
import { getMainWindow } from "../main-window";
import logit from "../utils/logit";
import fs from "fs";
import path from "path";
import { ELECTRON_COMMANDS } from "../../common/electron-commands";
import { ImageFormat } from "../types/types";
import { imageFormats } from "../../common/image-formats";
interface IClipboardFileParameters {
name: string;
path: string;
extension: ImageFormat;
size: number;
type: string;
encodedBuffer: string;
}
const isImageFormatValid = (format: string): format is ImageFormat => {
return (imageFormats as readonly string[]).includes(format);
};
const createTempFileFromClipboard = async (
inputFileParams: IClipboardFileParameters,
): Promise<string> => {
const tempFilePath = path.join(inputFileParams.path, inputFileParams.name);
const buffer = Buffer.from(inputFileParams.encodedBuffer, "base64");
await fs.promises.writeFile(tempFilePath, buffer);
return tempFilePath;
};
const pasteImage = async (
event: Electron.IpcMainEvent,
file: IClipboardFileParameters,
) => {
const mainWindow = getMainWindow();
if (!mainWindow) return;
if (!file || !file.name || !file.encodedBuffer) return;
if (isImageFormatValid(file.extension)) {
try {
const imageFilePath = await createTempFileFromClipboard(file);
mainWindow.webContents.send(
ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_SUCCESS,
imageFilePath,
);
} catch (error: any) {
logit(error.message);
mainWindow.webContents.send(
ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_ERROR,
error.message,
);
}
} else {
mainWindow.webContents.send(
ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_ERROR,
"Unsupported Image Format",
);
}
};
export default pasteImage;

View File

@ -19,6 +19,7 @@ import doubleUpscayl from "./commands/double-upscayl";
import autoUpdate from "./commands/auto-update"; import autoUpdate from "./commands/auto-update";
import { FEATURE_FLAGS } from "../common/feature-flags"; import { FEATURE_FLAGS } from "../common/feature-flags";
import settings from "electron-settings"; import settings from "electron-settings";
import pasteImage from "./commands/paste-image";
// INITIALIZATION // INITIALIZATION
log.initialize({ preload: true }); log.initialize({ preload: true });
@ -94,6 +95,8 @@ ipcMain.on(ELECTRON_COMMANDS.FOLDER_UPSCAYL, batchUpscayl);
ipcMain.on(ELECTRON_COMMANDS.DOUBLE_UPSCAYL, doubleUpscayl); ipcMain.on(ELECTRON_COMMANDS.DOUBLE_UPSCAYL, doubleUpscayl);
ipcMain.on(ELECTRON_COMMANDS.PASTE_IMAGE, pasteImage);
if (!FEATURE_FLAGS.APP_STORE_BUILD) { if (!FEATURE_FLAGS.APP_STORE_BUILD) {
autoUpdater.on("update-downloaded", autoUpdate); autoUpdater.on("update-downloaded", autoUpdate);
} }

View File

@ -1 +1,3 @@
export type ImageFormat = "png" | "jpg" | "webp"; import { imageFormats } from "../../common/image-formats";
export type ImageFormat = (typeof imageFormats)[number];

View File

@ -1,8 +1,8 @@
"use client"; "use client";
import useLogger from "../hooks/use-logger"; import useLogger from "../hooks/use-logger";
import { useState, useMemo } from "react"; import { useState, useMemo, useEffect } from "react";
import { ELECTRON_COMMANDS } from "@common/electron-commands"; import { ELECTRON_COMMANDS } from "@common/electron-commands";
import { useAtomValue, useSetAtom } from "jotai"; import { useAtom, useAtomValue } from "jotai";
import { import {
batchModeAtom, batchModeAtom,
lensSizeAtom, lensSizeAtom,
@ -63,7 +63,7 @@ const MainContent = ({
const { toast } = useToast(); const { toast } = useToast();
const version = useUpscaylVersion(); const version = useUpscaylVersion();
const setOutputPath = useSetAtom(savedOutputPathAtom); const [outputPath, setOutputPath] = useAtom(savedOutputPathAtom);
const progress = useAtomValue(progressAtom); const progress = useAtomValue(progressAtom);
const batchMode = useAtomValue(batchModeAtom); const batchMode = useAtomValue(batchModeAtom);
@ -163,45 +163,108 @@ const MainContent = ({
}; };
const handlePaste = (e: React.ClipboardEvent<HTMLDivElement>) => { const handlePaste = (e: React.ClipboardEvent<HTMLDivElement>) => {
console.log("📋 Pasted: ", e);
resetImagePaths();
e.preventDefault(); e.preventDefault();
const items = e.clipboardData.items; if (outputPath) {
const files = e.clipboardData.files; resetImagePaths();
console.log("🚀 => files:", files); if (e.clipboardData.files.length) {
const fileObject = e.clipboardData.files[0];
const currentDate = new Date(Date.now());
const currentTime = `${currentDate.getHours()}-${currentDate.getMinutes()}-${currentDate.getSeconds()}`;
const fileName = `.temp-${currentTime}-${fileObject.name || "image"}`;
const file = {
name: fileName,
path: outputPath,
extension: fileName.split(".").pop() as ImageFormat,
size: fileObject.size,
type: fileObject.type.split("/")[0],
encodedBuffer: "",
};
if (items.length === 0 || files.length === 0) { logit(
toast({ "📋 Pasted file: ",
title: t("ERRORS.INVALID_IMAGE_ERROR.TITLE"), JSON.stringify({
description: t("ERRORS.INVALID_IMAGE_ERROR.ADDITIONAL_DESCRIPTION"), name: file.name,
}); path: file.path,
return; extension: file.extension,
} }),
const type = items[0].type; );
const filePath = files[0].path;
const extension = files[0].name if (
.split(".") file.type === "image" &&
.at(-1) VALID_IMAGE_FORMATS.includes(file.extension)
.toLowerCase() as ImageFormat; ) {
logit("📋 Pasted file: ", JSON.stringify({ type, filePath, extension })); const reader = new FileReader();
if (!type.includes("image") && !VALID_IMAGE_FORMATS.includes(extension)) { reader.onload = async (event) => {
toast({ const result = event.target?.result;
title: t("ERRORS.INVALID_IMAGE_ERROR.TITLE"), if (typeof result === "string") {
description: t("ERRORS.INVALID_IMAGE_ERROR.ADDITIONAL_DESCRIPTION"), file.encodedBuffer = Buffer.from(result, "utf-8").toString(
}); "base64",
);
} else if (result instanceof ArrayBuffer) {
file.encodedBuffer = Buffer.from(new Uint8Array(result)).toString(
"base64",
);
} else { } else {
setImagePath(filePath); logit("🚫 Invalid file pasted");
const dirname = getDirectoryFromPath(filePath); toast({
logit("🗂 Setting output path: ", dirname); title: t("ERRORS.INVALID_IMAGE_ERROR.TITLE"),
if (!FEATURE_FLAGS.APP_STORE_BUILD) { description: t(
if (!rememberOutputFolder) { "ERRORS.INVALID_IMAGE_ERROR.CLIPBOARD_DESCRIPTION",
setOutputPath(dirname); ),
});
} }
window.electron.send(ELECTRON_COMMANDS.PASTE_IMAGE, file);
};
reader.readAsArrayBuffer(fileObject);
} else {
logit("🚫 Invalid file pasted");
toast({
title: t("ERRORS.INVALID_IMAGE_ERROR.TITLE"),
description: t("ERRORS.INVALID_IMAGE_ERROR.CLIPBOARD_DESCRIPTION"),
});
} }
validateImagePath(filePath); } else {
logit("🚫 Invalid file pasted");
toast({
title: t("ERRORS.INVALID_IMAGE_ERROR.TITLE"),
description: t("ERRORS.INVALID_IMAGE_ERROR.CLIPBOARD_DESCRIPTION"),
});
}
} else {
toast({
title: t("ERRORS.NO_OUTPUT_FOLDER_ERROR.TITLE"),
description: t("ERRORS.NO_OUTPUT_FOLDER_ERROR.DESCRIPTION"),
});
} }
}; };
useEffect(() => {
// Events
const handlePasteEvent = (e) => handlePaste(e);
const handlePasteImageSaveSuccess = (_: any, imageFilePath: string) => {
setImagePath(imageFilePath);
validateImagePath(imageFilePath);
};
const handlePasteImageSaveError = (_: any, error: string) => {
toast({
title: t("ERRORS.NO_IMAGE_ERROR.TITLE"),
description: error,
});
};
window.addEventListener("paste", handlePasteEvent);
window.electron.on(
ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_SUCCESS,
handlePasteImageSaveSuccess,
);
window.electron.on(
ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_ERROR,
handlePasteImageSaveError,
);
return () => {
window.removeEventListener("paste", handlePasteEvent);
};
}, [t, outputPath]);
return ( return (
<div <div
className="relative flex h-screen w-full flex-col items-center justify-center" className="relative flex h-screen w-full flex-col items-center justify-center"
@ -210,7 +273,6 @@ const MainContent = ({
onDragEnter={handleDragEnter} onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave} onDragLeave={handleDragLeave}
onDoubleClick={batchMode ? selectFolderHandler : selectImageHandler} onDoubleClick={batchMode ? selectFolderHandler : selectImageHandler}
onPaste={handlePaste}
> >
<MacTitlebarDragRegion /> <MacTitlebarDragRegion />

View File

@ -19,6 +19,8 @@ function InstructionsCard({ version, batchMode }) {
) : ( ) : (
<p className="w-full pb-5 text-center text-base-content/80 md:w-96"> <p className="w-full pb-5 text-center text-base-content/80 md:w-96">
{t("APP.RIGHT_PANE_INFO.SELECT_IMAGES_DESCRIPTION")} {t("APP.RIGHT_PANE_INFO.SELECT_IMAGES_DESCRIPTION")}
<br />
{t("APP.RIGHT_PANE_INFO.PASTE_IMAGE_DESCRIPTION")}
</p> </p>
)} )}
<p className="badge badge-primary text-sm">Upscayl v{version}</p> <p className="badge badge-primary text-sm">Upscayl v{version}</p>

View File

@ -147,7 +147,8 @@
"SELECT_FOLDER": "Select a Folder to Upscayl", "SELECT_FOLDER": "Select a Folder to Upscayl",
"SELECT_IMAGE": "Select an Image to Upscayl", "SELECT_IMAGE": "Select an Image to Upscayl",
"SELECT_FOLDER_DESCRIPTION": "Make sure that the folder doesn't contain anything except PNG, JPG, JPEG & WEBP images.", "SELECT_FOLDER_DESCRIPTION": "Make sure that the folder doesn't contain anything except PNG, JPG, JPEG & WEBP images.",
"SELECT_IMAGES_DESCRIPTION": "Select or drag and drop a PNG, JPG, JPEG or WEBP image." "SELECT_IMAGES_DESCRIPTION": "Select or drag and drop a PNG, JPG, JPEG or WEBP image.",
"PASTE_IMAGE_DESCRIPTION": "Hit Ctrl + V or Cmd + V to Paste image from Clipboard"
}, },
"PROGRESS": { "PROGRESS": {
"PROCESSING_TITLE": "Processing the image...", "PROCESSING_TITLE": "Processing the image...",
@ -192,10 +193,15 @@
"GENERIC_ERROR": { "GENERIC_ERROR": {
"TITLE": "Error" "TITLE": "Error"
}, },
"NO_OUTPUT_FOLDER_ERROR": {
"TITLE": "Set Output Folder",
"DESCRIPTION": "Please select an output folder first"
},
"INVALID_IMAGE_ERROR": { "INVALID_IMAGE_ERROR": {
"TITLE": "Invalid Image", "TITLE": "Invalid Image",
"DESCRIPTION": "Please select an image with a valid extension like PNG, JPG, JPEG, JFIF or WEBP.", "DESCRIPTION": "Please select/paste an image with a valid extension like PNG, JPG, JPEG, JFIF or WEBP.",
"ADDITIONAL_DESCRIPTION": "Please drag and drop an image" "ADDITIONAL_DESCRIPTION": "Please drag and drop an image",
"CLIPBOARD_DESCRIPTION": "No Image file found in Clipboard to paste!"
}, },
"NO_IMAGE_ERROR": { "NO_IMAGE_ERROR": {
"TITLE": "No image selected", "TITLE": "No image selected",

View File

@ -147,7 +147,8 @@
"SELECT_FOLDER": "Selecciona una carpeta para aumentar", "SELECT_FOLDER": "Selecciona una carpeta para aumentar",
"SELECT_IMAGE": "Selecciona una imagen para aumentar", "SELECT_IMAGE": "Selecciona una imagen para aumentar",
"SELECT_FOLDER_DESCRIPTION": "Asegúrate de que la carpeta no contenga nada excepto imágenes PNG, JPG, JPEG y WEBP.", "SELECT_FOLDER_DESCRIPTION": "Asegúrate de que la carpeta no contenga nada excepto imágenes PNG, JPG, JPEG y WEBP.",
"SELECT_IMAGES_DESCRIPTION": "Selecciona o arrastra y suelta una imagen PNG, JPG, JPEG o WEBP." "SELECT_IMAGES_DESCRIPTION": "Selecciona o arrastra y suelta una imagen PNG, JPG, JPEG o WEBP.",
"PASTE_IMAGE_DESCRIPTION": "Presiona Ctrl + V o Cmd + V para pegar la imagen desde el portapapeles"
}, },
"PROGRESS": { "PROGRESS": {
"PROCESSING_TITLE": "Procesando la imagen...", "PROCESSING_TITLE": "Procesando la imagen...",
@ -192,10 +193,15 @@
"GENERIC_ERROR": { "GENERIC_ERROR": {
"TITLE": "Error" "TITLE": "Error"
}, },
"NO_OUTPUT_FOLDER_ERROR": {
"TITLE": "Establecer carpeta de salida",
"DESCRIPTION": "Por favor, selecciona primero una carpeta de salida"
},
"INVALID_IMAGE_ERROR": { "INVALID_IMAGE_ERROR": {
"TITLE": "Imagen inválida", "TITLE": "Imagen inválida",
"DESCRIPTION": "Por favor, selecciona una imagen con una extensión válida como PNG, JPG, JPEG, JFIF o WEBP.", "DESCRIPTION": "Por favor, selecciona/pega una imagen con una extensión válida como PNG, JPG, JPEG, JFIF o WEBP.",
"ADDITIONAL_DESCRIPTION": "Por favor, arrastra y suelta una imagen" "ADDITIONAL_DESCRIPTION": "Por favor, arrastra y suelta una imagen",
"CLIPBOARD_DESCRIPTION": "¡No se encontró ningún archivo de imagen en el portapapeles para pegar!"
}, },
"NO_IMAGE_ERROR": { "NO_IMAGE_ERROR": {
"TITLE": "No se ha seleccionado imagen", "TITLE": "No se ha seleccionado imagen",

View File

@ -147,7 +147,8 @@
"SELECT_FOLDER": "Sélectionnez un dossier à suréchantillonner", "SELECT_FOLDER": "Sélectionnez un dossier à suréchantillonner",
"SELECT_IMAGE": "Sélectionnez une image à suréchantillonner", "SELECT_IMAGE": "Sélectionnez une image à suréchantillonner",
"SELECT_FOLDER_DESCRIPTION": "Assurez-vous que le dossier ne contient rien d'autre que des images PNG, JPG, JPEG et WEBP.", "SELECT_FOLDER_DESCRIPTION": "Assurez-vous que le dossier ne contient rien d'autre que des images PNG, JPG, JPEG et WEBP.",
"SELECT_IMAGES_DESCRIPTION": "Sélectionnez ou glissez-déposez une image PNG, JPG, JPEG ou WEBP." "SELECT_IMAGES_DESCRIPTION": "Sélectionnez ou glissez-déposez une image PNG, JPG, JPEG ou WEBP.",
"PASTE_IMAGE_DESCRIPTION": "Appuyez sur Ctrl + V ou Cmd + V pour coller l'image depuis le presse-papiers"
}, },
"PROGRESS": { "PROGRESS": {
"PROCESSING_TITLE": "Traitement de l'image...", "PROCESSING_TITLE": "Traitement de l'image...",
@ -192,10 +193,15 @@
"GENERIC_ERROR": { "GENERIC_ERROR": {
"TITLE": "Erreur" "TITLE": "Erreur"
}, },
"NO_OUTPUT_FOLDER_ERROR": {
"TITLE": "Définir le dossier de sortie",
"DESCRIPTION": "Veuillez d'abord sélectionner un dossier de sortie"
},
"INVALID_IMAGE_ERROR": { "INVALID_IMAGE_ERROR": {
"TITLE": "Image invalide", "TITLE": "Image invalide",
"DESCRIPTION": "Veuillez sélectionner une image avec une extension valide comme PNG, JPG, JPEG, JFIF ou WEBP.", "DESCRIPTION": "Veuillez sélectionner/coller une image avec une extension valide comme PNG, JPG, JPEG, JFIF ou WEBP.",
"ADDITIONAL_DESCRIPTION": "Veuillez glisser-déposer une image" "ADDITIONAL_DESCRIPTION": "Veuillez faire glisser et déposer une image",
"CLIPBOARD_DESCRIPTION": "Aucun fichier image trouvé dans le presse-papiers à coller !"
}, },
"NO_IMAGE_ERROR": { "NO_IMAGE_ERROR": {
"TITLE": "Aucune image sélectionnée", "TITLE": "Aucune image sélectionnée",

View File

@ -147,7 +147,8 @@
"SELECT_FOLDER": "Upscaylするフォルダを選択", "SELECT_FOLDER": "Upscaylするフォルダを選択",
"SELECT_IMAGE": "Upscaylする画像を選択", "SELECT_IMAGE": "Upscaylする画像を選択",
"SELECT_FOLDER_DESCRIPTION": "フォルダにはPNG、JPG、JPEG、WEBPの画像以外のものが含まれていないことを確認してください。", "SELECT_FOLDER_DESCRIPTION": "フォルダにはPNG、JPG、JPEG、WEBPの画像以外のものが含まれていないことを確認してください。",
"SELECT_IMAGES_DESCRIPTION": "PNG、JPG、JPEG、またはWEBP画像を選択するか、ドラッグアンドドロップしてください。" "SELECT_IMAGES_DESCRIPTION": "PNG、JPG、JPEG、またはWEBP画像を選択するか、ドラッグアンドドロップしてください。",
"PASTE_IMAGE_DESCRIPTION": "Ctrl + V または Cmd + V を押してクリップボードから画像を貼り付けます"
}, },
"PROGRESS": { "PROGRESS": {
"PROCESSING_TITLE": "画像を処理中...", "PROCESSING_TITLE": "画像を処理中...",
@ -192,10 +193,15 @@
"GENERIC_ERROR": { "GENERIC_ERROR": {
"TITLE": "エラー" "TITLE": "エラー"
}, },
"NO_OUTPUT_FOLDER_ERROR": {
"TITLE": "出力フォルダを設定",
"DESCRIPTION": "まず出力フォルダを選択してください"
},
"INVALID_IMAGE_ERROR": { "INVALID_IMAGE_ERROR": {
"TITLE": "無効な画像", "TITLE": "無効な画像",
"DESCRIPTION": "PNG、JPG、JPEG、JFIF、またはWEBPなどの有効な拡張子を持つ画像を選択してください。", "DESCRIPTION": "PNG、JPG、JPEG、JFIF、または WEBP のような有効な拡張子の画像を選択/貼り付けてください。",
"ADDITIONAL_DESCRIPTION": "画像をドラッグアンドドロップしてください" "ADDITIONAL_DESCRIPTION": "画像をドラッグアンドドロップしてください",
"CLIPBOARD_DESCRIPTION": "クリップボードに貼り付ける画像ファイルが見つかりません!"
}, },
"NO_IMAGE_ERROR": { "NO_IMAGE_ERROR": {
"TITLE": "画像が選択されていません", "TITLE": "画像が選択されていません",

View File

@ -147,7 +147,8 @@
"SELECT_FOLDER": "Выберите папку для увеличения", "SELECT_FOLDER": "Выберите папку для увеличения",
"SELECT_IMAGE": "Выберите изображение для увеличения", "SELECT_IMAGE": "Выберите изображение для увеличения",
"SELECT_FOLDER_DESCRIPTION": "Убедитесь, что в папке нет ничего, кроме изображений PNG, JPG, JPEG и WEBP.", "SELECT_FOLDER_DESCRIPTION": "Убедитесь, что в папке нет ничего, кроме изображений PNG, JPG, JPEG и WEBP.",
"SELECT_IMAGES_DESCRIPTION": "Выберите или перетащите изображение в формате PNG, JPG, JPEG или WEBP." "SELECT_IMAGES_DESCRIPTION": "Выберите или перетащите изображение в формате PNG, JPG, JPEG или WEBP.",
"PASTE_IMAGE_DESCRIPTION": "Нажмите Ctrl + V или Cmd + V, чтобы вставить изображение из буфера обмена"
}, },
"PROGRESS": { "PROGRESS": {
"PROCESSING_TITLE": "Обработка изображения...", "PROCESSING_TITLE": "Обработка изображения...",
@ -192,10 +193,15 @@
"GENERIC_ERROR": { "GENERIC_ERROR": {
"TITLE": "Ошибка" "TITLE": "Ошибка"
}, },
"NO_OUTPUT_FOLDER_ERROR": {
"TITLE": "Установить папку вывода",
"DESCRIPTION": "Пожалуйста, сначала выберите папку вывода"
},
"INVALID_IMAGE_ERROR": { "INVALID_IMAGE_ERROR": {
"TITLE": "Неверное изображение", "TITLE": "Неверное изображение",
"DESCRIPTION": "Пожалуйста, выберите изображение с правильным расширением, таким как PNG, JPG, JPEG, JFIF или WEBP.", "DESCRIPTION": "Пожалуйста, выберите/вставьте изображение с допустимым расширением, таким как PNG, JPG, JPEG, JFIF или WEBP.",
"ADDITIONAL_DESCRIPTION": "Пожалуйста, перетащите изображение" "ADDITIONAL_DESCRIPTION": "Пожалуйста, перетащите и отпустите изображение",
"CLIPBOARD_DESCRIPTION": "Файл изображения не найден в буфере обмена для вставки!"
}, },
"NO_IMAGE_ERROR": { "NO_IMAGE_ERROR": {
"TITLE": "Изображение не выбрано", "TITLE": "Изображение не выбрано",

View File

@ -147,7 +147,8 @@
"SELECT_FOLDER": "选择要增强的文件夹", "SELECT_FOLDER": "选择要增强的文件夹",
"SELECT_IMAGE": "选择要增强的图片", "SELECT_IMAGE": "选择要增强的图片",
"SELECT_FOLDER_DESCRIPTION": "文件夹中只支持 PNG、JPG、JPEG 和 WEBP 图片。", "SELECT_FOLDER_DESCRIPTION": "文件夹中只支持 PNG、JPG、JPEG 和 WEBP 图片。",
"SELECT_IMAGES_DESCRIPTION": "选择或拖放 PNG、JPG、JPEG 或 WEBP 图片。" "SELECT_IMAGES_DESCRIPTION": "选择或拖放 PNG、JPG、JPEG 或 WEBP 图片。",
"PASTE_IMAGE_DESCRIPTION": "按 Ctrl + V 或 Cmd + V 来粘贴图像"
}, },
"PROGRESS": { "PROGRESS": {
"PROCESSING_TITLE": "正在处理图片...", "PROCESSING_TITLE": "正在处理图片...",
@ -192,10 +193,15 @@
"GENERIC_ERROR": { "GENERIC_ERROR": {
"TITLE": "错误" "TITLE": "错误"
}, },
"NO_OUTPUT_FOLDER_ERROR": {
"TITLE": "选择输出文件夹",
"DESCRIPTION": "请先选择一个输出文件夹"
},
"INVALID_IMAGE_ERROR": { "INVALID_IMAGE_ERROR": {
"TITLE": "图片无效", "TITLE": "图片无效",
"DESCRIPTION": "请选择一个扩展名为 PNG、JPG、JPEG、JFIF 或 WEBP 的有效图片", "DESCRIPTION": "请选择/粘貼一个扩展名为 PNG、JPG、JPEG、JFIF 或 WEBP 的有效图片",
"ADDITIONAL_DESCRIPTION": "请拖放图片" "ADDITIONAL_DESCRIPTION": "请拖放一张图片",
"CLIPBOARD_DESCRIPTION": "剪贴板中未找到可粘贴的图像文件!"
}, },
"NO_IMAGE_ERROR": { "NO_IMAGE_ERROR": {
"TITLE": "未选择图片", "TITLE": "未选择图片",