This commit is contained in:
Anthony Stirling 2025-10-01 00:03:26 +01:00 committed by GitHub
parent 0a242f6a57
commit a15b0e33d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 3641 additions and 389 deletions

345
.github/scripts/check_language_json.py vendored Normal file
View File

@ -0,0 +1,345 @@
"""
Author: Ludy87
Description: This script processes JSON translation files for localization checks. It compares translation files in a branch with
a reference file to ensure consistency. The script performs two main checks:
1. Verifies that the number of translation keys in the translation files matches the reference file.
2. Ensures that all keys in the translation files are present in the reference file and vice versa.
The script also provides functionality to update the translation files to match the reference file by adding missing keys and
adjusting the format.
Usage:
python check_language_json.py --reference-file <path_to_reference_file> --branch <branch_name> [--actor <actor_name>] [--files <list_of_changed_files>]
"""
# Sample for Windows:
# python .github/scripts/check_language_json.py --reference-file frontend/public/locales/en-GB/translation.json --branch "" --files frontend/public/locales/de-DE/translation.json frontend/public/locales/fr-FR/translation.json
import copy
import glob
import os
import argparse
import re
import json
def find_duplicate_keys(file_path, keys=None, prefix=""):
"""
Identifies duplicate keys in a JSON file (including nested keys).
:param file_path: Path to the JSON file.
:param keys: Dictionary to track keys (used for recursion).
:param prefix: Prefix for nested keys.
:return: List of tuples (key, first_occurrence_path, duplicate_path).
"""
if keys is None:
keys = {}
duplicates = []
with open(file_path, "r", encoding="utf-8") as file:
data = json.load(file)
def process_dict(obj, current_prefix=""):
for key, value in obj.items():
full_key = f"{current_prefix}.{key}" if current_prefix else key
if isinstance(value, dict):
process_dict(value, full_key)
else:
if full_key in keys:
duplicates.append((full_key, keys[full_key], full_key))
else:
keys[full_key] = full_key
process_dict(data, prefix)
return duplicates
# Maximum size for JSON files (e.g., 500 KB)
MAX_FILE_SIZE = 500 * 1024
def parse_json_file(file_path):
"""
Parses a JSON translation file and returns a flat dictionary of all keys.
:param file_path: Path to the JSON file.
:return: Dictionary with flattened keys.
"""
with open(file_path, "r", encoding="utf-8") as file:
data = json.load(file)
def flatten_dict(d, parent_key="", sep="."):
items = {}
for k, v in d.items():
new_key = f"{parent_key}{sep}{k}" if parent_key else k
if isinstance(v, dict):
items.update(flatten_dict(v, new_key, sep=sep))
else:
items[new_key] = v
return items
return flatten_dict(data)
def unflatten_dict(d, sep="."):
"""
Converts a flat dictionary with dot notation keys back to nested dict.
:param d: Flattened dictionary.
:param sep: Separator used in keys.
:return: Nested dictionary.
"""
result = {}
for key, value in d.items():
parts = key.split(sep)
current = result
for part in parts[:-1]:
if part not in current:
current[part] = {}
current = current[part]
current[parts[-1]] = value
return result
def write_json_file(file_path, updated_properties):
"""
Writes updated properties back to the JSON file.
:param file_path: Path to the JSON file.
:param updated_properties: Dictionary of updated properties to write.
"""
nested_data = unflatten_dict(updated_properties)
with open(file_path, "w", encoding="utf-8", newline="\n") as file:
json.dump(nested_data, file, ensure_ascii=False, indent=2)
file.write("\n") # Add trailing newline
def update_missing_keys(reference_file, file_list, branch=""):
"""
Updates missing keys in the translation files based on the reference file.
:param reference_file: Path to the reference JSON file.
:param file_list: List of translation files to update.
:param branch: Branch where the files are located.
"""
reference_properties = parse_json_file(reference_file)
for file_path in file_list:
basename_current_file = os.path.basename(os.path.join(branch, file_path))
if (
basename_current_file == os.path.basename(reference_file)
or not file_path.endswith(".json")
or not os.path.dirname(file_path).endswith("locales")
):
continue
current_properties = parse_json_file(os.path.join(branch, file_path))
updated_properties = {}
for ref_key, ref_value in reference_properties.items():
if ref_key in current_properties:
# Keep the current translation
updated_properties[ref_key] = current_properties[ref_key]
else:
# Add missing key with reference value
updated_properties[ref_key] = ref_value
write_json_file(os.path.join(branch, file_path), updated_properties)
def check_for_missing_keys(reference_file, file_list, branch):
update_missing_keys(reference_file, file_list, branch)
def read_json_keys(file_path):
if os.path.isfile(file_path) and os.path.exists(file_path):
return parse_json_file(file_path)
return {}
def check_for_differences(reference_file, file_list, branch, actor):
reference_branch = branch
basename_reference_file = os.path.basename(reference_file)
report = []
report.append(f"#### 🔄 Reference Branch: `{reference_branch}`")
reference_keys = read_json_keys(reference_file)
has_differences = False
only_reference_file = True
file_arr = file_list
if len(file_list) == 1:
file_arr = file_list[0].split()
base_dir = os.path.abspath(
os.path.join(os.getcwd(), "frontend", "public", "locales")
)
for file_path in file_arr:
file_normpath = os.path.normpath(file_path)
absolute_path = os.path.abspath(file_normpath)
# Verify that file is within the expected directory
if not absolute_path.startswith(base_dir):
raise ValueError(f"Unsafe file found: {file_normpath}")
# Verify file size before processing
if os.path.getsize(os.path.join(branch, file_normpath)) > MAX_FILE_SIZE:
raise ValueError(
f"The file {file_normpath} is too large and could pose a security risk."
)
basename_current_file = os.path.basename(os.path.join(branch, file_normpath))
locale_dir = os.path.basename(os.path.dirname(file_normpath))
if (
basename_current_file == basename_reference_file
and locale_dir == "en-GB"
):
continue
if not file_normpath.endswith(".json") or basename_current_file != "translation.json":
continue
only_reference_file = False
report.append(f"#### 📃 **File Check:** `{locale_dir}/{basename_current_file}`")
current_keys = read_json_keys(os.path.join(branch, file_path))
reference_key_count = len(reference_keys)
current_key_count = len(current_keys)
if reference_key_count != current_key_count:
report.append("")
report.append("1. **Test Status:** ❌ **_Failed_**")
report.append(" - **Issue:**")
has_differences = True
if reference_key_count > current_key_count:
report.append(
f" - **_Mismatched key count_**: {reference_key_count} (reference) vs {current_key_count} (current). Translation keys are missing."
)
elif reference_key_count < current_key_count:
report.append(
f" - **_Too many keys_**: {reference_key_count} (reference) vs {current_key_count} (current). Please verify if there are additional keys that need to be removed."
)
else:
report.append("1. **Test Status:** ✅ **_Passed_**")
# Check for missing or extra keys
current_keys_set = set(current_keys.keys())
reference_keys_set = set(reference_keys.keys())
missing_keys = current_keys_set.difference(reference_keys_set)
extra_keys = reference_keys_set.difference(current_keys_set)
missing_keys_list = list(missing_keys)
extra_keys_list = list(extra_keys)
if missing_keys_list or extra_keys_list:
has_differences = True
missing_keys_str = "`, `".join(missing_keys_list)
extra_keys_str = "`, `".join(extra_keys_list)
report.append("2. **Test Status:** ❌ **_Failed_**")
report.append(" - **Issue:**")
if missing_keys_list:
report.append(
f" - **_Extra keys in `{locale_dir}/{basename_current_file}`_**: `{missing_keys_str}` that are not present in **_`{basename_reference_file}`_**."
)
if extra_keys_list:
report.append(
f" - **_Missing keys in `{locale_dir}/{basename_current_file}`_**: `{extra_keys_str}` that are not present in **_`{basename_reference_file}`_**."
)
else:
report.append("2. **Test Status:** ✅ **_Passed_**")
if find_duplicate_keys(os.path.join(branch, file_normpath)):
has_differences = True
output = "\n".join(
[
f" - `{key}`: first at {first}, duplicate at `{duplicate}`"
for key, first, duplicate in find_duplicate_keys(
os.path.join(branch, file_normpath)
)
]
)
report.append("3. **Test Status:** ❌ **_Failed_**")
report.append(" - **Issue:**")
report.append(" - duplicate entries were found:")
report.append(output)
else:
report.append("3. **Test Status:** ✅ **_Passed_**")
report.append("")
report.append("---")
report.append("")
if has_differences:
report.append("## ❌ Overall Check Status: **_Failed_**")
report.append("")
report.append(
f"@{actor} please check your translation if it conforms to the standard. Follow the format of [en-GB/translation.json](https://github.com/Stirling-Tools/Stirling-PDF/blob/V2/frontend/public/locales/en-GB/translation.json)"
)
else:
report.append("## ✅ Overall Check Status: **_Success_**")
report.append("")
report.append(
f"Thanks @{actor} for your help in keeping the translations up to date."
)
if not only_reference_file:
print("\n".join(report))
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Find missing keys")
parser.add_argument(
"--actor",
required=False,
help="Actor from PR.",
)
parser.add_argument(
"--reference-file",
required=True,
help="Path to the reference file.",
)
parser.add_argument(
"--branch",
type=str,
required=True,
help="Branch name.",
)
parser.add_argument(
"--check-file",
type=str,
required=False,
help="List of changed files, separated by spaces.",
)
parser.add_argument(
"--files",
nargs="+",
required=False,
help="List of changed files, separated by spaces.",
)
args = parser.parse_args()
# Sanitize --actor input to avoid injection attacks
if args.actor:
args.actor = re.sub(r"[^a-zA-Z0-9_\\-]", "", args.actor)
# Sanitize --branch input to avoid injection attacks
if args.branch:
args.branch = re.sub(r"[^a-zA-Z0-9\\-]", "", args.branch)
file_list = args.files
if file_list is None:
if args.check_file:
file_list = [args.check_file]
else:
file_list = glob.glob(
os.path.join(
os.getcwd(),
"frontend",
"public",
"locales",
"*",
"translation.json",
)
)
update_missing_keys(args.reference_file, file_list)
else:
check_for_differences(args.reference_file, file_list, args.branch, args.actor)

118
.github/workflows/sync_files_v2.yml vendored Normal file
View File

@ -0,0 +1,118 @@
name: Sync Files V2
on:
workflow_dispatch:
push:
branches:
- V2
- syncLangTest
paths:
- "build.gradle"
- "README.md"
- "frontend/public/locales/*/translation.json"
- "app/core/src/main/resources/static/3rdPartyLicenses.json"
- "scripts/ignore_translation.toml"
# cancel in-progress jobs if a new job is triggered
# This is useful to avoid running multiple builds for the same branch if a new commit is pushed
# or a pull request is updated.
# It helps to save resources and time by ensuring that only the latest commit is built and tested
# This is particularly useful for long-running jobs that may take a while to complete.
# The `group` is set to a combination of the workflow name, event name, and branch name.
# This ensures that jobs are grouped by the workflow and branch, allowing for cancellation of
# in-progress jobs when a new commit is pushed to the same branch or a new pull request is opened.
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref_name || github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
sync-files:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
with:
egress-policy: audit
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
- name: Setup GitHub App Bot
id: setup-bot
uses: ./.github/actions/setup-bot
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.12"
cache: "pip" # caching pip dependencies
- name: Sync translation JSON files
run: |
python .github/scripts/check_language_json.py --reference-file "frontend/public/locales/en-GB/translation.json" --branch V2
- name: Commit translation files
run: |
git add frontend/public/locales/*/translation.json
git diff --staged --quiet || git commit -m ":memo: Sync translation files" || echo "No changes detected"
- name: Install dependencies
run: pip install --require-hashes -r ./.github/scripts/requirements_sync_readme.txt
- name: Sync README.md
run: |
python scripts/counter_translation_v2.py
- name: Run git add
run: |
git add README.md scripts/ignore_translation.toml
git diff --staged --quiet || git commit -m ":memo: Sync README.md & scripts/ignore_translation.toml" || echo "No changes detected"
- name: Create Pull Request
if: always()
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
with:
token: ${{ steps.setup-bot.outputs.token }}
commit-message: Update files
committer: ${{ steps.setup-bot.outputs.committer }}
author: ${{ steps.setup-bot.outputs.committer }}
signoff: true
branch: sync_readme_v2
base: V2
title: ":globe_with_meridians: [V2] Sync Translations + Update README Progress Table"
body: |
### Description of Changes
This Pull Request was automatically generated to synchronize updates to translation files and documentation for the **V2 branch**. Below are the details of the changes made:
#### **1. Synchronization of Translation Files**
- Updated translation files (`frontend/public/locales/*/translation.json`) to reflect changes in the reference file `en-GB/translation.json`.
- Ensured consistency and synchronization across all supported language files.
- Highlighted any missing or incomplete translations.
#### **2. Update README.md**
- Generated the translation progress table in `README.md`.
- Added a summary of the current translation status for all supported languages.
- Included up-to-date statistics on translation coverage.
#### **Why these changes are necessary**
- Keeps translation files aligned with the latest reference updates.
- Ensures the documentation reflects the current translation progress.
---
Auto-generated by [create-pull-request][1].
[1]: https://github.com/peter-evans/create-pull-request
draft: false
delete-branch: true
labels: github-actions
sign-commits: true
add-paths: |
README.md
frontend/public/locales/*/translation.json

View File

@ -97,7 +97,6 @@ All documentation available at [https://docs.stirlingpdf.com/](https://docs.stir
# 📖 Get Started
Visit our comprehensive documentation at [docs.stirlingpdf.com](https://docs.stirlingpdf.com) for:

View File

@ -87,7 +87,10 @@
"showStack": "Stack-Trace anzeigen",
"copyStack": "Stack-Trace kopieren",
"githubSubmit": "GitHub - Ein Ticket einreichen",
"discordSubmit": "Discord - Unterstützungsbeitrag einreichen"
"discordSubmit": "Discord - Unterstützungsbeitrag einreichen",
"dismissAllErrors": "Alle Fehler ausblenden",
"encryptedPdfMustRemovePassword": "Diese PDF ist verschlüsselt oder passwortgeschützt. Bitte entsperren Sie sie, bevor Sie in PDF/A konvertieren.",
"incorrectPasswordProvided": "Das PDF-Passwort ist falsch oder wurde nicht angegeben."
},
"warning": {
"tooltipTitle": "Warnung"
@ -358,179 +361,223 @@
"sortBy": "Sortieren nach:",
"multiTool": {
"title": "PDF-Multitool",
"desc": "Seiten zusammenführen, drehen, neu anordnen und entfernen"
"desc": "Seiten zusammenführen, drehen, neu anordnen und entfernen",
"tags": "mehrere,werkzeuge"
},
"merge": {
"title": "Zusammenführen",
"desc": "Mehrere PDF-Dateien zu einer einzigen zusammenführen"
"desc": "Mehrere PDF-Dateien zu einer einzigen zusammenführen",
"tags": "kombinieren,zusammenführen,vereinen"
},
"split": {
"title": "Aufteilen",
"desc": "PDFs in mehrere Dokumente aufteilen"
"desc": "PDFs in mehrere Dokumente aufteilen",
"tags": "teilen,trennen,aufteilen"
},
"rotate": {
"title": "Drehen",
"desc": "Drehen Sie Ihre PDFs ganz einfach"
"desc": "Drehen Sie Ihre PDFs ganz einfach",
"tags": "drehen,spiegeln,ausrichten"
},
"convert": {
"title": "Umwandeln",
"desc": "Dateien zwischen verschiedenen Formaten konvertieren"
"desc": "Dateien zwischen verschiedenen Formaten konvertieren",
"tags": "umwandeln,ändern"
},
"pdfOrganiser": {
"title": "Organisieren",
"desc": "Seiten entfernen und Seitenreihenfolge ändern"
"desc": "Seiten entfernen und Seitenreihenfolge ändern",
"tags": "organisieren,umordnen,neu anordnen"
},
"addImage": {
"title": "Bild einfügen",
"desc": "Fügt ein Bild an eine bestimmte Stelle im PDF ein (in Arbeit)"
"desc": "Fügt ein Bild an eine bestimmte Stelle im PDF ein (in Arbeit)",
"tags": "einfügen,einbetten,platzieren"
},
"addAttachments": {
"title": "Anhänge hinzufügen",
"desc": "Eingebettete Dateien (Anhänge) zu einer PDF hinzufügen oder entfernen"
"desc": "Eingebettete Dateien (Anhänge) zu einer PDF hinzufügen oder entfernen",
"tags": "einbetten,anhängen,einfügen"
},
"watermark": {
"title": "Wasserzeichen hinzufügen",
"desc": "Fügen Sie ein eigenes Wasserzeichen zu Ihrem PDF hinzu"
"desc": "Fügen Sie ein eigenes Wasserzeichen zu Ihrem PDF hinzu",
"tags": "stempel,markierung,überlagerung"
},
"removePassword": {
"title": "Passwort entfernen",
"desc": "Den Passwortschutz eines PDFs entfernen"
"desc": "Den Passwortschutz eines PDFs entfernen",
"tags": "entsperren"
},
"compress": {
"title": "Komprimieren",
"desc": "PDF komprimieren um die Dateigröße zu reduzieren"
"desc": "PDF komprimieren um die Dateigröße zu reduzieren",
"tags": "verkleinern,reduzieren,optimieren"
},
"unlockPDFForms": {
"title": "Schreibgeschützte PDF-Formfelder entfernen",
"desc": "Entfernen Sie die schreibgeschützte Eigenschaft von Formularfeldern in einem PDF-Dokument."
"desc": "Entfernen Sie die schreibgeschützte Eigenschaft von Formularfeldern in einem PDF-Dokument.",
"tags": "entsperren,aktivieren,bearbeiten"
},
"changeMetadata": {
"title": "Metadaten ändern",
"desc": "Ändern/Entfernen/Hinzufügen von Metadaten aus einem PDF-Dokument"
"desc": "Ändern/Entfernen/Hinzufügen von Metadaten aus einem PDF-Dokument",
"tags": "bearbeiten,ändern,aktualisieren"
},
"ocr": {
"title": "Führe OCR/Cleanup-Scans aus",
"desc": "Cleanup scannt und erkennt Text aus Bildern in einer PDF-Datei und fügt ihn erneut als Text hinzu"
"desc": "Cleanup scannt und erkennt Text aus Bildern in einer PDF-Datei und fügt ihn erneut als Text hinzu",
"tags": "extrahieren,scannen"
},
"extractImages": {
"title": "Bilder extrahieren",
"desc": "Extrahiert alle Bilder aus einer PDF-Datei und speichert sie als Zip-Archiv"
"desc": "Extrahiert alle Bilder aus einer PDF-Datei und speichert sie als Zip-Archiv",
"tags": "extrahieren,speichern,exportieren"
},
"scannerImageSplit": {
"title": "Gescannte Fotos erkennen/aufteilen",
"desc": "Teilt mehrere Fotos aus einem Foto/PDF auf"
"desc": "Teilt mehrere Fotos aus einem Foto/PDF auf",
"tags": "erkennen,teilen,fotos"
},
"sign": {
"title": "Signieren",
"desc": "Fügt PDF-Signaturen durch Zeichnung, Text oder Bild hinzu"
"desc": "Fügt PDF-Signaturen durch Zeichnung, Text oder Bild hinzu",
"tags": "unterschrift,autogramm"
},
"flatten": {
"title": "Abflachen",
"desc": "Alle interaktiven Elemente und Formulare aus einem PDF entfernen"
"desc": "Alle interaktiven Elemente und Formulare aus einem PDF entfernen",
"tags": "vereinfachen,entfernen,interaktiv"
},
"certSign": {
"title": "Mit Zertifikat signieren",
"desc": "Ein PDF mit einem Zertifikat/Schlüssel (PEM/P12) signieren"
"desc": "Ein PDF mit einem Zertifikat/Schlüssel (PEM/P12) signieren",
"tags": "authentifizieren,PEM,P12,offiziell,verschlüsseln,signieren,zertifikat,PKCS12,JKS,server,manuell,auto"
},
"repair": {
"title": "Reparatur",
"desc": "Versucht, ein beschädigtes/kaputtes PDF zu reparieren"
"desc": "Versucht, ein beschädigtes/kaputtes PDF zu reparieren",
"tags": "reparieren,wiederherstellen"
},
"removeBlanks": {
"title": "Leere Seiten entfernen",
"desc": "Erkennt und entfernt leere Seiten aus einem Dokument"
"desc": "Erkennt und entfernt leere Seiten aus einem Dokument",
"tags": "löschen,bereinigen,leer"
},
"removeAnnotations": {
"title": "Anmerkungen entfernen",
"desc": "Entfernt alle Kommentare/Anmerkungen aus einem PDF"
"desc": "Entfernt alle Kommentare/Anmerkungen aus einem PDF",
"tags": "löschen,bereinigen,entfernen"
},
"compare": {
"title": "Vergleichen",
"desc": "Vergleicht und zeigt die Unterschiede zwischen zwei PDF-Dokumenten an"
"desc": "Vergleicht und zeigt die Unterschiede zwischen zwei PDF-Dokumenten an",
"tags": "unterschied"
},
"removeCertSign": {
"title": "Zertifikatsignatur entfernen",
"desc": "Zertifikatsignatur aus PDF entfernen"
"desc": "Zertifikatsignatur aus PDF entfernen",
"tags": "entfernen,löschen,entsperren"
},
"pageLayout": {
"title": "Mehrseitiges Layout",
"desc": "Mehrere Seiten eines PDF zu einer Seite zusammenführen"
"desc": "Mehrere Seiten eines PDF zu einer Seite zusammenführen",
"tags": "layout,anordnen,kombinieren"
},
"bookletImposition": {
"title": "Broschüren-Layout",
"desc": "Broschüren mit korrekter Seitenreihenfolge und mehrseitigem Layout für Druck und Bindung erstellen"
"desc": "Broschüren mit korrekter Seitenreihenfolge und mehrseitigem Layout für Druck und Bindung erstellen",
"tags": "broschüre,druck,bindung"
},
"scalePages": {
"title": "Seitengröße/Skalierung anpassen",
"desc": "Größe/Skalierung der Seite und/oder des Inhalts ändern"
"desc": "Größe/Skalierung der Seite und/oder des Inhalts ändern",
"tags": "größe ändern,anpassen,skalieren"
},
"addPageNumbers": {
"title": "Seitenzahlen hinzufügen",
"desc": "Hinzufügen von Seitenzahlen an einer bestimmten Stelle"
"desc": "Hinzufügen von Seitenzahlen an einer bestimmten Stelle",
"tags": "nummerieren,paginierung,zählen"
},
"autoRename": {
"title": "PDF-Datei automatisch umbenennen",
"desc": "Benennt eine PDF-Datei automatisch basierend auf der erkannten Überschrift um"
"desc": "Benennt eine PDF-Datei automatisch basierend auf der erkannten Überschrift um",
"tags": "auto-erkennung,kopfzeilen-basiert,organisieren,umbenennen"
},
"adjustContrast": {
"title": "Farben/Kontrast anpassen",
"desc": "Kontrast, Sättigung und Helligkeit einer PDF anpassen"
"desc": "Kontrast, Sättigung und Helligkeit einer PDF anpassen",
"tags": "kontrast,helligkeit,sättigung"
},
"crop": {
"title": "PDF zuschneiden",
"desc": "PDF zuschneiden um die Größe zu verändern (Text bleibt erhalten!)"
"desc": "PDF zuschneiden um die Größe zu verändern (Text bleibt erhalten!)",
"tags": "zuschneiden,schneiden,größe ändern"
},
"autoSplitPDF": {
"title": "PDF automatisch teilen",
"desc": "Physisch gescannte PDF anhand von Splitter-Seiten und QR-Codes aufteilen"
"desc": "Physisch gescannte PDF anhand von Splitter-Seiten und QR-Codes aufteilen",
"tags": "auto,teilen,QR"
},
"sanitize": {
"title": "Bereinigen",
"desc": "Potentiell schädliche Elemente aus PDF-Dateien entfernen"
"desc": "Potentiell schädliche Elemente aus PDF-Dateien entfernen",
"tags": "bereinigen,löschen,entfernen"
},
"getPdfInfo": {
"title": "Alle Informationen anzeigen",
"desc": "Erfasst alle möglichen Informationen in einer PDF"
"desc": "Erfasst alle möglichen Informationen in einer PDF",
"tags": "info,metadaten,details"
},
"pdfToSinglePage": {
"title": "PDF zu einer Seite zusammenfassen",
"desc": "Fügt alle PDF-Seiten zu einer einzigen großen Seite zusammen"
"desc": "Fügt alle PDF-Seiten zu einer einzigen großen Seite zusammen",
"tags": "kombinieren,zusammenführen,einzeln"
},
"showJS": {
"title": "Javascript anzeigen",
"desc": "Alle Javascript Funktionen in einer PDF anzeigen"
"desc": "Alle Javascript Funktionen in einer PDF anzeigen",
"tags": "javascript,code,skript"
},
"redact": {
"title": "Manuell zensieren/schwärzen",
"desc": "Zensiere (Schwärze) eine PDF-Datei durch Auswählen von Text, gezeichneten Formen und/oder ausgewählten Seite(n)"
"desc": "Zensiere (Schwärze) eine PDF-Datei durch Auswählen von Text, gezeichneten Formen und/oder ausgewählten Seite(n)",
"tags": "zensieren,schwärzen,verbergen"
},
"overlayPdfs": {
"title": "PDFs überlagern",
"desc": "PDFs über eine andere PDF überlagern"
"desc": "PDFs über eine andere PDF überlagern",
"tags": "überlagern,kombinieren,stapeln"
},
"splitBySections": {
"title": "PDF nach Abschnitten aufteilen",
"desc": "Jede Seite einer PDF in kleinere horizontale und vertikale Abschnitte unterteilen"
"desc": "Jede Seite einer PDF in kleinere horizontale und vertikale Abschnitte unterteilen",
"tags": "teilen,abschnitte,aufteilen"
},
"addStamp": {
"title": "Stempel zu PDF hinzufügen",
"desc": "Text- oder Bildstempel an festgelegten Positionen hinzufügen"
"desc": "Text- oder Bildstempel an festgelegten Positionen hinzufügen",
"tags": "stempel,markierung,siegel"
},
"removeImage": {
"title": "Bild entfernen",
"desc": "Bild aus PDF entfernen, um die Dateigröße zu verringern"
"desc": "Bild aus PDF entfernen, um die Dateigröße zu verringern",
"tags": "entfernen,löschen,bereinigen"
},
"splitByChapters": {
"title": "PDF-Datei nach Kapiteln aufteilen",
"desc": "Aufteilung einer PDF-Datei in mehrere Dateien auf Basis der Kapitelstruktur."
"desc": "Aufteilung einer PDF-Datei in mehrere Dateien auf Basis der Kapitelstruktur.",
"tags": "teilen,kapitel,struktur"
},
"validateSignature": {
"title": "PDF-Signatur überprüfen",
"desc": "Digitale Signaturen und Zertifikate in PDF-Dokumenten überprüfen"
"desc": "Digitale Signaturen und Zertifikate in PDF-Dokumenten überprüfen",
"tags": "validieren,überprüfen,zertifikat"
},
"swagger": {
"title": "API-Dokumentation",
"desc": "API-Dokumentation anzeigen und Endpunkte testen"
"desc": "API-Dokumentation anzeigen und Endpunkte testen",
"tags": "API,dokumentation,test"
},
"fakeScan": {
"title": "Scan simulieren",
@ -538,42 +585,52 @@
},
"editTableOfContents": {
"title": "Inhaltsverzeichnis bearbeiten",
"desc": "Hinzufügen oder Bearbeiten von Lesezeichen und Inhaltsverzeichnissen in PDF-Dokumenten"
"desc": "Hinzufügen oder Bearbeiten von Lesezeichen und Inhaltsverzeichnissen in PDF-Dokumenten",
"tags": "lesezeichen,inhalt,bearbeiten"
},
"manageCertificates": {
"title": "Zertifikate verwalten",
"desc": "Digitale Zertifikatsdateien für die PDF-Signierung importieren, exportieren oder löschen."
"desc": "Digitale Zertifikatsdateien für die PDF-Signierung importieren, exportieren oder löschen.",
"tags": "zertifikate,importieren,exportieren"
},
"read": {
"title": "Lesen",
"desc": "PDFs anzeigen und kommentieren. Text hervorheben, zeichnen oder Kommentare für Überprüfung und Zusammenarbeit einfügen."
"desc": "PDFs anzeigen und kommentieren. Text hervorheben, zeichnen oder Kommentare für Überprüfung und Zusammenarbeit einfügen.",
"tags": "anzeigen,öffnen,anzeigen"
},
"reorganizePages": {
"title": "Seiten neu anordnen",
"desc": "PDF-Seiten mit visueller Drag-and-Drop-Steuerung neu anordnen, duplizieren oder löschen."
"desc": "PDF-Seiten mit visueller Drag-and-Drop-Steuerung neu anordnen, duplizieren oder löschen.",
"tags": "umordnen,neu anordnen,organisieren"
},
"extractPages": {
"title": "Seiten extrahieren",
"desc": "Spezifische Seiten aus einem PDF-Dokument extrahieren"
"desc": "Spezifische Seiten aus einem PDF-Dokument extrahieren",
"tags": "extrahieren,auswählen,kopieren"
},
"removePages": {
"title": "Entfernen",
"desc": "Ungewollte Seiten aus dem PDF entfernen"
"desc": "Ungewollte Seiten aus dem PDF entfernen",
"tags": "löschen,extrahieren,ausschließen"
},
"autoSizeSplitPDF": {
"title": "Teilen nach Größe/Anzahl",
"desc": "Teilen Sie ein einzelnes PDF basierend auf Größe, Seitenanzahl oder Dokumentanzahl in mehrere Dokumente auf"
"desc": "Teilen Sie ein einzelnes PDF basierend auf Größe, Seitenanzahl oder Dokumentanzahl in mehrere Dokumente auf",
"tags": "auto,teilen,größe"
},
"replaceColorPdf": {
"title": "Farbe ersetzen und invertieren",
"desc": "Ersetzen Sie die Farbe des Texts und Hintergrund der PDF-Datei und invertieren Sie die komplette Farbe der PDF-Datei, um die Dateigröße zu reduzieren"
},
"devApi": {
"desc": "Link zur API-Dokumentation"
"desc": "Link zur API-Dokumentation",
"tags": "API,entwicklung,dokumentation",
"title": "API"
},
"devFolderScanning": {
"title": "Automatische Ordnerüberwachung",
"desc": "Link zum Leitfaden für automatisches Ordner-Scannen"
"desc": "Link zum Leitfaden für automatisches Ordner-Scannen",
"tags": "automatisierung,ordner,scannen"
},
"devSsoGuide": {
"title": "SSO-Anleitung",
@ -593,7 +650,17 @@
},
"automate": {
"title": "Automatisieren",
"desc": "Mehrstufige Arbeitsabläufe durch Verkettung von PDF-Aktionen erstellen. Ideal für wiederkehrende Aufgaben."
"desc": "Mehrstufige Arbeitsabläufe durch Verkettung von PDF-Aktionen erstellen. Ideal für wiederkehrende Aufgaben.",
"tags": "arbeitsablauf,sequenz,automatisierung"
},
"replaceColor": {
"desc": "Farben in PDF-Dokumenten ersetzen oder invertieren",
"title": "Farbe ersetzen & invertieren"
},
"scannerEffect": {
"desc": "Erstellen Sie eine PDF, die aussieht, als wäre sie gescannt worden",
"tags": "scannen,simulieren,erstellen",
"title": "Scanner-Effekt"
}
},
"landing": {
@ -633,8 +700,18 @@
"merge": {
"tags": "zusammenführen,seitenvorgänge,back end,serverseitig",
"title": "Zusammenführen",
"removeDigitalSignature": "Digitale Signatur in der zusammengeführten Datei entfernen?",
"generateTableOfContents": "Inhaltsverzeichnis in der zusammengeführten Datei erstellen?",
"removeDigitalSignature": {
"tooltip": {
"description": "Digitale Signaturen werden beim Zusammenführen von Dateien ungültig. Aktivieren Sie diese Option, um sie aus der endgültigen zusammengeführten PDF zu entfernen.",
"title": "Digitale Signatur entfernen"
}
},
"generateTableOfContents": {
"tooltip": {
"description": "Erstellt automatisch ein klickbares Inhaltsverzeichnis in der zusammengeführten PDF basierend auf den ursprünglichen Dateinamen und Seitenzahlen.",
"title": "Inhaltsverzeichnis generieren"
}
},
"submit": "Zusammenführen",
"sortBy": {
"description": "Dateien werden in der Reihenfolge zusammengeführt, in der sie ausgewählt wurden. Ziehen Sie zum Neuordnen oder sortieren Sie unten.",
@ -860,7 +937,13 @@
"images": "Bilder",
"officeDocs": "Office-Dokumente (Word, Excel, PowerPoint)",
"imagesExt": "Bilder (JPG, PNG, usw.)",
"grayscale": "Graustufen"
"grayscale": "Graustufen",
"dpi": "DPI",
"markdown": "Markdown",
"odtExt": "OpenDocument Text (.odt)",
"pptExt": "PowerPoint (.pptx)",
"rtfExt": "Rich Text Format (.rtf)",
"textRtf": "Text/RTF"
},
"imageToPdf": {
"tags": "konvertierung,img,jpg,bild,foto"
@ -900,7 +983,20 @@
"10": "Ungerade-Gerade-Zusammenführung",
"11": "Alle Seiten duplizieren"
},
"placeholder": "(z.B. 1,3,2 oder 4-8,2,10-12 oder 2n-1)"
"placeholder": "(z.B. 1,3,2 oder 4-8,2,10-12 oder 2n-1)",
"desc": {
"BOOKLET_SORT": "Seiten für den Broschüren-Druck anordnen (letzte, erste, zweite, vorletzte, …).",
"CUSTOM": "Verwenden Sie eine benutzerdefinierte Sequenz von Seitenzahlen oder Ausdrücken, um eine neue Reihenfolge zu definieren.",
"DUPLEX_SORT": "Vorder- und Rückseiten verschachteln, als ob ein Duplex-Scanner alle Vorderseiten und dann alle Rückseiten gescannt hätte (1, n, 2, n-1, …).",
"DUPLICATE": "Jede Seite entsprechend der benutzerdefinierten Anzahl duplizieren (z.B. 4 dupliziert jede Seite 4×).",
"ODD_EVEN_MERGE": "Zwei PDFs durch abwechselnde Seiten zusammenführen: ungerade aus der ersten, gerade aus der zweiten.",
"ODD_EVEN_SPLIT": "Das Dokument in zwei Ausgaben aufteilen: alle ungeraden Seiten und alle geraden Seiten.",
"REMOVE_FIRST": "Die erste Seite aus dem Dokument entfernen.",
"REMOVE_FIRST_AND_LAST": "Sowohl die erste als auch die letzte Seite aus dem Dokument entfernen.",
"REMOVE_LAST": "Die letzte Seite aus dem Dokument entfernen.",
"REVERSE_ORDER": "Das Dokument umkehren, sodass die letzte Seite zur ersten wird usw.",
"SIDE_STITCH_BOOKLET_SORT": "Seiten für den Seitenheft-Broschüren-Druck anordnen (optimiert für die Bindung an der Seite)."
}
},
"addImage": {
"tags": "img,jpg,bild,foto",
@ -929,7 +1025,8 @@
"failed": "Ein Fehler ist beim Hinzufügen des Wasserzeichens zur PDF aufgetreten."
},
"watermarkType": {
"image": "Bild"
"image": "Bild",
"text": "Text"
},
"settings": {
"type": "Wasserzeichen-Typ",
@ -1333,7 +1430,9 @@
},
"trapped": {
"label": "Trapped-Status",
"unknown": "Unbekannt"
"unknown": "Unbekannt",
"false": "Falsch",
"true": "Wahr"
},
"advanced": {
"title": "Erweiterte Optionen"
@ -1522,7 +1621,13 @@
"header": "Bilder extrahieren",
"selectText": "Wählen Sie das Bildformat aus, in das extrahierte Bilder konvertiert werden sollen",
"allowDuplicates": "Doppelte Bilder speichern",
"submit": "Extrahieren"
"submit": "Extrahieren",
"error": {
"failed": "Beim Extrahieren der Bilder aus der PDF ist ein Fehler aufgetreten."
},
"settings": {
"title": "Einstellungen"
}
},
"pdfToPDFA": {
"tags": "archiv,langfristig,standard,konvertierung,speicherung,aufbewahrung",
@ -1599,8 +1704,14 @@
"title": "Signieren",
"header": "PDFs signieren",
"upload": "Bild hochladen",
"draw": "Signatur zeichnen",
"text": "Texteingabe",
"draw": {
"clear": "Löschen",
"title": "Zeichnen Sie Ihre Unterschrift"
},
"text": {
"name": "Name des Unterzeichners",
"placeholder": "Geben Sie Ihren vollständigen Namen ein"
},
"clear": "Leeren",
"add": "Signieren",
"saved": "Gespeicherte Signaturen",
@ -1616,7 +1727,35 @@
"previous": "Vorherige Seite",
"maintainRatio": "Seitenverhältnis beibehalten ein-/ausschalten",
"undo": "Rückgängig",
"redo": "Wiederherstellen"
"redo": "Wiederherstellen",
"activate": "Signatur-Platzierung aktivieren",
"applySignatures": "Signaturen anwenden",
"deactivate": "Signatur-Platzierung beenden",
"error": {
"failed": "Beim Signieren der PDF ist ein Fehler aufgetreten."
},
"image": {
"hint": "Laden Sie ein PNG- oder JPG-Bild Ihrer Unterschrift hoch",
"label": "Unterschriftsbild hochladen",
"placeholder": "Bilddatei auswählen"
},
"instructions": {
"title": "So fügen Sie eine Unterschrift hinzu"
},
"results": {
"title": "Signatur-Ergebnisse"
},
"steps": {
"configure": "Signatur konfigurieren"
},
"submit": "Dokument signieren",
"type": {
"canvas": "Canvas",
"draw": "Zeichnen",
"image": "Bild",
"text": "Text",
"title": "Signaturtyp"
}
},
"flatten": {
"tags": "statisch,deaktivieren,nicht interaktiv,optimieren",
@ -1635,7 +1774,8 @@
"stepTitle": "Abflachungs-Optionen",
"title": "Abflachungs-Optionen",
"flattenOnlyForms.desc": "Nur Formularfelder vereinfachen, andere interaktive Elemente unverändert lassen",
"note": "Das Abflachen entfernt interaktive Elemente aus der PDF und macht sie nicht mehr bearbeitbar."
"note": "Das Abflachen entfernt interaktive Elemente aus der PDF und macht sie nicht mehr bearbeitbar.",
"flattenOnlyForms": "Nur Formulare vereinfachen"
},
"results": {
"title": "Reduzierungs-Ergebnisse"
@ -1693,7 +1833,8 @@
"label": "Pixel-Weißheitsschwellwert"
},
"whitePercent": {
"label": "Weiß-Prozentsatz-Schwellwert"
"label": "Weiß-Prozentsatz-Schwellwert",
"unit": "%"
},
"includeBlankPages": {
"label": "Erkannte leere Seiten einschließen"
@ -1730,7 +1871,17 @@
"tags": "kommentare,hervorheben,notizen,markieren,entfernen",
"title": "Kommentare entfernen",
"header": "Kommentare entfernen",
"submit": "Entfernen"
"submit": "Entfernen",
"error": {
"failed": "Beim Entfernen der Anmerkungen aus der PDF ist ein Fehler aufgetreten."
},
"info": {
"description": "Dieses Werkzeug entfernt alle Anmerkungen (Kommentare, Hervorhebungen, Notizen usw.) aus Ihren PDF-Dokumenten.",
"title": "Über Anmerkungen entfernen"
},
"settings": {
"title": "Einstellungen"
}
},
"compare": {
"tags": "differenzieren,kontrastieren,verändern,analysieren",
@ -2015,7 +2166,9 @@
},
"pageSize": {
"label": "Ziel-Seitengröße",
"keep": "Ursprüngliche Größe beibehalten"
"keep": "Ursprüngliche Größe beibehalten",
"legal": "Legal",
"letter": "Letter"
},
"submit": "Seitenskalierung anpassen",
"error": {
@ -2306,7 +2459,8 @@
"showLayers": "Ebenen anzeigen (Doppelklick, um alle Ebenen auf den Standardzustand zurückzusetzen)",
"colourPicker": "Farbwähler",
"findCurrentOutlineItem": "Aktuelles Gliederungselement finden",
"applyChanges": "Änderungen anwenden"
"applyChanges": "Änderungen anwenden",
"zoom": "Zoom"
}
},
"tableExtraxt": {
@ -2496,7 +2650,8 @@
"magicLinkSent": "Magic Link wurde an {{email}} gesendet! Prüfen Sie Ihre E-Mails und klicken Sie auf den Link zur Anmeldung.",
"passwordResetSent": "Passwort-Reset-Link wurde an {{email}} gesendet! Prüfen Sie Ihre E-Mails und folgen Sie den Anweisungen.",
"failedToSignIn": "Anmeldung mit {{provider}} fehlgeschlagen: {{message}}",
"unexpectedError": "Unerwarteter Fehler: {{message}}"
"unexpectedError": "Unerwarteter Fehler: {{message}}",
"debug": "Debug"
},
"signup": {
"title": "Konto erstellen",
@ -2518,7 +2673,8 @@
"invalidEmail": "Bitte geben Sie eine gültige E-Mail-Adresse ein",
"checkEmailConfirmation": "Prüfen Sie Ihre E-Mails auf einen Bestätigungslink, um die Registrierung abzuschließen.",
"accountCreatedSuccessfully": "Konto erfolgreich erstellt! Sie können sich jetzt anmelden.",
"unexpectedError": "Unerwarteter Fehler: {{message}}"
"unexpectedError": "Unerwarteter Fehler: {{message}}",
"name": "Name"
},
"pdfToSinglePage": {
"title": "PDF zu einer Seite zusammenfassen",
@ -2961,7 +3117,12 @@
"selectedCount": "{{count}} ausgewählt",
"download": "Herunterladen",
"delete": "Löschen",
"unsupported": "Nicht unterstützt"
"unsupported": "Nicht unterstützt",
"fileFormat": "Format",
"fileName": "Name",
"fileVersion": "Version",
"googleDrive": "Google Drive",
"googleDriveShort": "Drive"
},
"storage": {
"temporaryNotice": "Dateien werden temporär in Ihrem Browser gespeichert und können automatisch gelöscht werden",
@ -2992,12 +3153,24 @@
"options": {
"title": "Bereinigungs-Optionen",
"note": "Wählen Sie die Elemente aus, die Sie aus der PDF entfernen möchten. Mindestens eine Option muss ausgewählt werden.",
"removeJavaScript": "JavaScript entfernen",
"removeEmbeddedFiles": "Eingebettete Dateien entfernen",
"removeXMPMetadata": "XMP-Metadaten entfernen",
"removeMetadata": "Dokument-Metadaten entfernen",
"removeLinks": "Links entfernen",
"removeFonts": "Schriftarten entfernen"
"removeJavaScript": {
"desc": "JavaScript-Aktionen und Skripte aus der PDF entfernen"
},
"removeEmbeddedFiles": {
"desc": "Alle in der PDF eingebetteten Dateien entfernen"
},
"removeXMPMetadata": {
"desc": "XMP-Metadaten aus der PDF entfernen"
},
"removeMetadata": {
"desc": "Dokumentinformations-Metadaten (Titel, Autor usw.) entfernen"
},
"removeLinks": {
"desc": "Externe Links und Launch-Aktionen aus der PDF entfernen"
},
"removeFonts": {
"desc": "Eingebettete Schriftarten aus der PDF entfernen"
}
}
},
"addPassword": {
@ -3025,7 +3198,8 @@
"keyLength": {
"label": "Verschlüsselungsschlüssellänge",
"40bit": "40-bit (Niedrig)",
"256bit": "256-bit (Hoch)"
"256bit": "256-bit (Hoch)",
"128bit": "128-bit (Standard)"
}
},
"results": {
@ -3264,5 +3438,58 @@
},
"generateError": "Wir konnten Ihren API-Schlüssel nicht generieren."
}
}
},
"AddAttachmentsRequest": {
"addMoreFiles": "Weitere Dateien hinzufügen...",
"attachments": "Anhänge auswählen",
"info": "Wählen Sie Dateien aus, die Sie Ihrer PDF anhängen möchten. Diese Dateien werden eingebettet und über das Anhangs-Panel der PDF zugänglich sein.",
"placeholder": "Dateien auswählen...",
"results": {
"title": "Anhangs-Ergebnisse"
},
"selectFiles": "Dateien zum Anhängen auswählen",
"selectedFiles": "Ausgewählte Dateien",
"submit": "Anhänge hinzufügen"
},
"applyAndContinue": "Anwenden & Fortfahren",
"discardChanges": "Änderungen verwerfen",
"exportAndContinue": "Exportieren & Fortfahren",
"keepWorking": "Weiterarbeiten",
"logOut": "Abmelden",
"replaceColor": {
"tags": "Farbe ersetzen,Seitenoperationen,Backend,serverseitig"
},
"scannerImageSplit": {
"error": {
"failed": "Beim Extrahieren der Bild-Scans ist ein Fehler aufgetreten."
},
"submit": "Bild-Scans extrahieren",
"title": "Extrahierte Bilder",
"tooltip": {
"headsUp": "Hinweis",
"headsUpDesc": "Überlappende Fotos oder Hintergründe, die farblich sehr nah an den Fotos liegen, können die Genauigkeit verringern - versuchen Sie einen helleren oder dunkleren Hintergrund und lassen Sie mehr Platz.",
"problem1": "Fotos nicht erkannt → Toleranz auf 30-50 erhöhen",
"problem2": "Zu viele Falscherkennungen → Mindestfläche auf 15.000-20.000 erhöhen",
"problem3": "Zuschnitte sind zu eng → Randgröße auf 5-10 erhöhen",
"problem4": "Geneigte Fotos nicht begradigt → Winkelschwelle auf ~5° senken",
"problem5": "Staub-/Rausch-Boxen → Mindest-Konturfläche auf 1000-2000 erhöhen",
"quickFixes": "Schnelle Lösungen",
"setupTips": "Einrichtungstipps",
"tip1": "Verwenden Sie einen einfachen, hellen Hintergrund",
"tip2": "Lassen Sie einen kleinen Abstand (≈1 cm) zwischen den Fotos",
"tip3": "Scannen Sie mit 300-600 DPI",
"tip4": "Reinigen Sie die Scanner-Glasplatte",
"title": "Foto-Teiler",
"useCase1": "Ganze Album-Seiten in einem Durchgang scannen",
"useCase2": "Flachbett-Stapel in separate Dateien aufteilen",
"useCase3": "Collagen in einzelne Fotos aufteilen",
"useCase4": "Fotos aus Dokumenten extrahieren",
"whatThisDoes": "Was dies tut",
"whatThisDoesDesc": "Findet und extrahiert automatisch jedes Foto von einer gescannten Seite oder einem zusammengesetzten Bild - kein manuelles Zuschneiden erforderlich.",
"whenToUse": "Wann zu verwenden"
}
},
"termsAndConditions": "Allgemeine Geschäftsbedingungen",
"unsavedChanges": "Sie haben ungespeicherte Änderungen an Ihrer PDF. Was möchten Sie tun?",
"unsavedChangesTitle": "Ungespeicherte Änderungen"
}

File diff suppressed because it is too large Load Diff

View File

@ -87,7 +87,10 @@
"showStack": "Mostra traccia dello stack",
"copyStack": "Copia traccia dello stack",
"githubSubmit": "GitHub: apri un ticket",
"discordSubmit": "Discord: invia post di supporto"
"discordSubmit": "Discord: invia post di supporto",
"dismissAllErrors": "Chiudi tutti gli errori",
"encryptedPdfMustRemovePassword": "Questo PDF è crittografato o protetto da password. Si prega di sbloccarlo prima di convertire in PDF/A.",
"incorrectPasswordProvided": "La password del PDF è errata o non è stata fornita."
},
"warning": {
"tooltipTitle": "Avviso"
@ -358,179 +361,223 @@
"sortBy": "Ordinamento:",
"multiTool": {
"title": "Multifunzione PDF",
"desc": "Unisci, Ruota, Riordina, e Rimuovi pagine"
"desc": "Unisci, Ruota, Riordina, e Rimuovi pagine",
"tags": "multipli,strumenti"
},
"merge": {
"title": "Unisci",
"desc": "Unisci facilmente più PDF in uno."
"desc": "Unisci facilmente più PDF in uno.",
"tags": "combina,unisci,unifica"
},
"split": {
"title": "Dividi",
"desc": "Dividi un singolo PDF in più documenti."
"desc": "Dividi un singolo PDF in più documenti.",
"tags": "dividi,separa,spezza"
},
"rotate": {
"title": "Ruota",
"desc": "Ruota un PDF."
"desc": "Ruota un PDF.",
"tags": "ruota,capovolgi,orienta"
},
"convert": {
"title": "Converti",
"desc": "Converti file tra diversi formati"
"desc": "Converti file tra diversi formati",
"tags": "trasforma,cambia"
},
"pdfOrganiser": {
"title": "Organizza",
"desc": "Rimuovi/Riordina le pagine in qualsiasi ordine."
"desc": "Rimuovi/Riordina le pagine in qualsiasi ordine.",
"tags": "organizza,riordina,riorganizza"
},
"addImage": {
"title": "Aggiungi Immagine",
"desc": "Aggiungi un'immagine in un punto specifico del PDF (Lavori in corso)"
"desc": "Aggiungi un'immagine in un punto specifico del PDF (Lavori in corso)",
"tags": "inserisci,incorpora,posiziona"
},
"addAttachments": {
"title": "Aggiungi allegati",
"desc": "Aggiungi o rimuovi file incorporati (allegati) da/verso un PDF"
"desc": "Aggiungi o rimuovi file incorporati (allegati) da/verso un PDF",
"tags": "incorpora,allega,includi"
},
"watermark": {
"title": "Aggiungi Filigrana",
"desc": "Aggiungi una filigrana al tuo PDF."
"desc": "Aggiungi una filigrana al tuo PDF.",
"tags": "timbro,marca,sovrapponi"
},
"removePassword": {
"title": "Rimuovi Password",
"desc": "Rimuovi la password dal tuo PDF."
"desc": "Rimuovi la password dal tuo PDF.",
"tags": "sblocca"
},
"compress": {
"title": "Comprimi",
"desc": "Comprimi PDF per ridurne le dimensioni."
"desc": "Comprimi PDF per ridurne le dimensioni.",
"tags": "riduci,comprimi,ottimizza"
},
"unlockPDFForms": {
"title": "Sblocca moduli PDF",
"desc": "Rimuovi la proprietà di sola lettura dei campi del modulo in un documento PDF."
"desc": "Rimuovi la proprietà di sola lettura dei campi del modulo in un documento PDF.",
"tags": "sblocca,abilita,modifica"
},
"changeMetadata": {
"title": "Modifica Proprietà",
"desc": "Modifica/Aggiungi/Rimuovi le proprietà di un documento PDF."
"desc": "Modifica/Aggiungi/Rimuovi le proprietà di un documento PDF.",
"tags": "modifica,cambia,aggiorna"
},
"ocr": {
"title": "OCR / Pulisci scansioni",
"desc": "Pulisci scansioni ed estrai testo da immagini, convertendo le immagini in testo puro."
"desc": "Pulisci scansioni ed estrai testo da immagini, convertendo le immagini in testo puro.",
"tags": "estrai,scansiona"
},
"extractImages": {
"title": "Estrai immagini",
"desc": "Estrai tutte le immagini da un PDF e salvale come zip."
"desc": "Estrai tutte le immagini da un PDF e salvale come zip.",
"tags": "estrai,salva,esporta"
},
"scannerImageSplit": {
"title": "Rileva/Dividi foto scansionate",
"desc": "Divide più foto allinterno di una foto/PDF"
"desc": "Divide più foto allinterno di una foto/PDF",
"tags": "rileva,dividi,foto"
},
"sign": {
"title": "Firma",
"desc": "Aggiungi una firma al PDF da disegno, testo o immagine."
"desc": "Aggiungi una firma al PDF da disegno, testo o immagine.",
"tags": "firma,autografo"
},
"flatten": {
"title": "Appiattisci",
"desc": "Rimuovi tutti gli elementi interattivi e moduli da un PDF."
"desc": "Rimuovi tutti gli elementi interattivi e moduli da un PDF.",
"tags": "semplifica,rimuovi,interattivo"
},
"certSign": {
"title": "Firma con certificato",
"desc": "Firma un PDF con un certificato/chiave (PEM/P12)"
"desc": "Firma un PDF con un certificato/chiave (PEM/P12)",
"tags": "autentica,PEM,P12,ufficiale,cripta,firma,certificato,PKCS12,JKS,server,manuale,auto"
},
"repair": {
"title": "Ripara",
"desc": "Prova a riparare un PDF corrotto."
"desc": "Prova a riparare un PDF corrotto.",
"tags": "ripara,ripristina"
},
"removeBlanks": {
"title": "Rimuovi pagine vuote",
"desc": "Trova e rimuovi pagine vuote da un PDF."
"desc": "Trova e rimuovi pagine vuote da un PDF.",
"tags": "elimina,pulisci,vuote"
},
"removeAnnotations": {
"title": "Rimuovi annotazioni",
"desc": "Rimuove tutti i commenti/annotazioni da un PDF"
"desc": "Rimuove tutti i commenti/annotazioni da un PDF",
"tags": "elimina,pulisci,rimuovi"
},
"compare": {
"title": "Compara",
"desc": "Vedi e compara le differenze tra due PDF."
"desc": "Vedi e compara le differenze tra due PDF.",
"tags": "differenza"
},
"removeCertSign": {
"title": "Rimuovere firma dal certificato",
"desc": "Rimuovi la firma del certificato dal PDF"
"desc": "Rimuovi la firma del certificato dal PDF",
"tags": "rimuovi,elimina,sblocca"
},
"pageLayout": {
"title": "Layout multipagina",
"desc": "Unisci più pagine di un documento PDF in un'unica pagina"
"desc": "Unisci più pagine di un documento PDF in un'unica pagina",
"tags": "layout,disponi,combina"
},
"bookletImposition": {
"title": "Imposizione a libretto",
"desc": "Crea libretti con corretto ordinamento pagine e layout multipagina per stampa e rilegatura"
"desc": "Crea libretti con corretto ordinamento pagine e layout multipagina per stampa e rilegatura",
"tags": "opuscolo,stampa,rilegatura"
},
"scalePages": {
"title": "Regola le dimensioni/scala della pagina",
"desc": "Modificare le dimensioni/scala della pagina e/o dei suoi contenuti."
"desc": "Modificare le dimensioni/scala della pagina e/o dei suoi contenuti.",
"tags": "ridimensiona,adatta,scala"
},
"addPageNumbers": {
"title": "Aggiungi numeri di pagina",
"desc": "Aggiungi numeri di pagina in tutto un documento in una posizione prestabilita"
"desc": "Aggiungi numeri di pagina in tutto un documento in una posizione prestabilita",
"tags": "numero,paginazione,conteggio"
},
"autoRename": {
"title": "Rinomina automatica file PDF",
"desc": "Rinomina automaticamente un file PDF in base allintestazione rilevata"
"desc": "Rinomina automaticamente un file PDF in base allintestazione rilevata",
"tags": "auto-rilevamento,basato su intestazione,organizza,rinomina"
},
"adjustContrast": {
"title": "Regola colori/contrasto",
"desc": "Regola contrasto, saturazione e luminosità di un PDF"
"desc": "Regola contrasto, saturazione e luminosità di un PDF",
"tags": "contrasto,luminosità,saturazione"
},
"crop": {
"title": "Ritaglia PDF",
"desc": "Ritaglia un PDF per ridurne le dimensioni (mantiene il testo!)"
"desc": "Ritaglia un PDF per ridurne le dimensioni (mantiene il testo!)",
"tags": "ritaglia,taglia,ridimensiona"
},
"autoSplitPDF": {
"title": "Pagine divise automaticamente",
"desc": "Dividi automaticamente il PDF scansionato con il codice QR dello divisore di pagina fisico scansionato"
"desc": "Dividi automaticamente il PDF scansionato con il codice QR dello divisore di pagina fisico scansionato",
"tags": "auto,dividi,QR"
},
"sanitize": {
"title": "Sanitizza",
"desc": "Rimuovi elementi potenzialmente dannosi dai PDF"
"desc": "Rimuovi elementi potenzialmente dannosi dai PDF",
"tags": "pulisci,elimina,rimuovi"
},
"getPdfInfo": {
"title": "Ottieni TUTTE le informazioni in PDF",
"desc": "Raccogli tutte le informazioni possibili sui PDF"
"desc": "Raccogli tutte le informazioni possibili sui PDF",
"tags": "info,metadati,dettagli"
},
"pdfToSinglePage": {
"title": "PDF in un'unica pagina di grandi dimensioni",
"desc": "Unisce tutte le pagine PDF in un'unica grande pagina"
"desc": "Unisce tutte le pagine PDF in un'unica grande pagina",
"tags": "combina,unisci,singola"
},
"showJS": {
"title": "Mostra Javascript",
"desc": "Cerca e visualizza qualsiasi JS inserito in un PDF"
"desc": "Cerca e visualizza qualsiasi JS inserito in un PDF",
"tags": "javascript,codice,script"
},
"redact": {
"title": "Redazione manuale",
"desc": "Redige un PDF in base al testo selezionato, alle forme disegnate e/o alle pagina selezionata(e)"
"desc": "Redige un PDF in base al testo selezionato, alle forme disegnate e/o alle pagina selezionata(e)",
"tags": "censura,oscura,nascondi"
},
"overlayPdfs": {
"title": "Sovrapponi PDF",
"desc": "Sovrapponi PDF sopra un altro PDF"
"desc": "Sovrapponi PDF sopra un altro PDF",
"tags": "sovrapponi,combina,impila"
},
"splitBySections": {
"title": "Dividi PDF per sezioni",
"desc": "Divide ogni pagina di un PDF in sezioni orizzontali e verticali più piccole"
"desc": "Divide ogni pagina di un PDF in sezioni orizzontali e verticali più piccole",
"tags": "dividi,sezioni,separa"
},
"addStamp": {
"title": "Aggiungi timbro al PDF",
"desc": "Aggiungi timbri di testo o immagine in posizioni specifiche"
"desc": "Aggiungi timbri di testo o immagine in posizioni specifiche",
"tags": "timbro,marca,sigillo"
},
"removeImage": {
"title": "Rimuovi immagine",
"desc": "Rimuovi le immagini dal PDF per ridurre la dimensione del file"
"desc": "Rimuovi le immagini dal PDF per ridurre la dimensione del file",
"tags": "rimuovi,elimina,pulisci"
},
"splitByChapters": {
"title": "Dividi PDF per capitoli",
"desc": "Dividi un PDF in più file in base alla struttura dei capitoli."
"desc": "Dividi un PDF in più file in base alla struttura dei capitoli.",
"tags": "dividi,capitoli,struttura"
},
"validateSignature": {
"title": "Convalida la firma PDF",
"desc": "Verificare le firme digitali e i certificati nei documenti PDF"
"desc": "Verificare le firme digitali e i certificati nei documenti PDF",
"tags": "convalida,verifica,certificato"
},
"swagger": {
"title": "Documentazione API",
"desc": "Visualizza documentazione API e testa gli endpoint"
"desc": "Visualizza documentazione API e testa gli endpoint",
"tags": "API,documentazione,test"
},
"fakeScan": {
"title": "Finta scansione",
@ -538,31 +585,38 @@
},
"editTableOfContents": {
"title": "Modifica indice",
"desc": "Aggiungi o modifica segnalibri e sommario nei documenti PDF"
"desc": "Aggiungi o modifica segnalibri e sommario nei documenti PDF",
"tags": "segnalibri,contenuti,modifica"
},
"manageCertificates": {
"title": "Gestisci certificati",
"desc": "Importa, esporta o elimina i file certificato usati per firmare i PDF."
"desc": "Importa, esporta o elimina i file certificato usati per firmare i PDF.",
"tags": "certificati,importa,esporta"
},
"read": {
"title": "Leggi",
"desc": "Visualizza e annota PDF. Evidenzia testo, disegna o inserisci commenti per revisione e collaborazione."
"desc": "Visualizza e annota PDF. Evidenzia testo, disegna o inserisci commenti per revisione e collaborazione.",
"tags": "visualizza,apri,mostra"
},
"reorganizePages": {
"title": "Riorganizza pagine",
"desc": "Riorganizza, duplica o elimina pagine PDF con controllo visivo draganddrop."
"desc": "Riorganizza, duplica o elimina pagine PDF con controllo visivo draganddrop.",
"tags": "riordina,riorganizza,organizza"
},
"extractPages": {
"title": "Estrai pagine",
"desc": "Estrai pagine specifiche da un PDF"
"desc": "Estrai pagine specifiche da un PDF",
"tags": "estrai,seleziona,copia"
},
"removePages": {
"title": "Rimuovi",
"desc": "Elimina alcune pagine dal PDF."
"desc": "Elimina alcune pagine dal PDF.",
"tags": "elimina,estrai,escludi"
},
"autoSizeSplitPDF": {
"title": "Divisione automatica per dimensione/numero",
"desc": "Dividi un singolo PDF in più documenti in base alle dimensioni, al numero di pagine o al numero di documenti"
"desc": "Dividi un singolo PDF in più documenti in base alle dimensioni, al numero di pagine o al numero di documenti",
"tags": "auto,dividi,dimensione"
},
"replaceColorPdf": {
"title": "Sostituisci e inverti il colore",
@ -570,11 +624,13 @@
},
"devApi": {
"title": "API",
"desc": "Link alla documentazione API"
"desc": "Link alla documentazione API",
"tags": "API,sviluppo,documentazione"
},
"devFolderScanning": {
"title": "Scansione cartelle automatizzata",
"desc": "Link alla guida per scansione cartelle automatizzata"
"desc": "Link alla guida per scansione cartelle automatizzata",
"tags": "automazione,cartella,scansione"
},
"devSsoGuide": {
"title": "Guida SSO",
@ -594,7 +650,17 @@
},
"automate": {
"title": "Automatizza",
"desc": "Crea flussi multistep concatenando azioni PDF. Ideale per attività ricorrenti."
"desc": "Crea flussi multistep concatenando azioni PDF. Ideale per attività ricorrenti.",
"tags": "flusso di lavoro,sequenza,automazione"
},
"replaceColor": {
"desc": "Sostituisci o inverti i colori nei documenti PDF",
"title": "Sostituisci e inverti colore"
},
"scannerEffect": {
"desc": "Crea un PDF che sembra essere stato scansionato",
"tags": "scansiona,simula,crea",
"title": "Effetto scanner"
}
},
"landing": {
@ -654,7 +720,9 @@
},
"error": {
"failed": "Si è verificato un errore durante lunione dei PDF."
}
},
"generateTableOfContents": "Generare l'indice nel file unito?",
"removeDigitalSignature": "Rimuovere la firma digitale nel file unito?"
},
"split": {
"tags": "Operazioni sulla pagina,divisione,multi pagina,taglio,lato server",
@ -913,7 +981,20 @@
"10": "Unione pari-dispari",
"11": "Duplica tutte le pagine"
},
"placeholder": "(ad es. 1,3,2 o 4-8,2,10-12 o 2n-1)"
"placeholder": "(ad es. 1,3,2 o 4-8,2,10-12 o 2n-1)",
"desc": {
"BOOKLET_SORT": "Disporre le pagine per la stampa a opuscolo (ultima, prima, seconda, penultima, …).",
"CUSTOM": "Utilizzare una sequenza personalizzata di numeri di pagina o espressioni per definire un nuovo ordine.",
"DUPLEX_SORT": "Alternare fronte e retro come se uno scanner duplex avesse scansionato tutti i fronti, poi tutti i retri (1, n, 2, n-1, …).",
"DUPLICATE": "Duplicare ogni pagina secondo il conteggio dell'ordine personalizzato (ad es., 4 duplica ogni pagina 4×).",
"ODD_EVEN_MERGE": "Unire due PDF alternando le pagine: dispari dal primo, pari dal secondo.",
"ODD_EVEN_SPLIT": "Dividere il documento in due output: tutte le pagine dispari e tutte le pagine pari.",
"REMOVE_FIRST": "Rimuovere la prima pagina dal documento.",
"REMOVE_FIRST_AND_LAST": "Rimuovere sia la prima che l'ultima pagina dal documento.",
"REMOVE_LAST": "Rimuovere l'ultima pagina dal documento.",
"REVERSE_ORDER": "Capovolgere il documento in modo che l'ultima pagina diventi la prima e così via.",
"SIDE_STITCH_BOOKLET_SORT": "Disporre le pagine per la stampa a opuscolo con cucitura laterale (ottimizzato per la rilegatura sul lato)."
}
},
"addImage": {
"tags": "img,jpg,immagine,foto",
@ -1347,7 +1428,7 @@
},
"trapped": {
"label": "Stato Trapped",
"unknown": "Unknown",
"unknown": "Sconosciuto",
"true": "True",
"false": "False"
},
@ -1538,7 +1619,13 @@
"header": "Estrai immagini",
"selectText": "Seleziona il formato in cui salvare le immagini estratte",
"allowDuplicates": "Salva le immagini duplicate",
"submit": "Estrai"
"submit": "Estrai",
"error": {
"failed": "Si è verificato un errore durante l'estrazione delle immagini dal PDF."
},
"settings": {
"title": "Impostazioni"
}
},
"pdfToPDFA": {
"tags": "archivio,a lungo termine,standard,conversione,archiviazione,conservazione",
@ -1615,8 +1702,14 @@
"title": "Firma",
"header": "Firma PDF",
"upload": "Carica immagine",
"draw": "Disegna Firma",
"text": "Testo",
"draw": {
"clear": "Cancella",
"title": "Disegna la tua firma"
},
"text": {
"name": "Nome firmatario",
"placeholder": "Inserisci il tuo nome completo"
},
"clear": "Cancella",
"add": "Aggiungi",
"saved": "Firme salvate",
@ -1632,7 +1725,35 @@
"previous": "Pagina precedente",
"maintainRatio": "Attiva il mantenimento delle proporzioni",
"undo": "Annulla",
"redo": "Rifare"
"redo": "Rifare",
"activate": "Attiva posizionamento firma",
"applySignatures": "Applica firme",
"deactivate": "Interrompi posizionamento firme",
"error": {
"failed": "Si è verificato un errore durante la firma del PDF."
},
"image": {
"hint": "Carica un'immagine PNG o JPG della tua firma",
"label": "Carica immagine firma",
"placeholder": "Seleziona file immagine"
},
"instructions": {
"title": "Come aggiungere la firma"
},
"results": {
"title": "Risultati firma"
},
"steps": {
"configure": "Configura firma"
},
"submit": "Firma documento",
"type": {
"canvas": "Canvas",
"draw": "Disegna",
"image": "Immagine",
"text": "Testo",
"title": "Tipo di firma"
}
},
"flatten": {
"tags": "statico,disattivato,non interattivo,ottimizzato",
@ -1651,7 +1772,8 @@
"stepTitle": "Opzioni di flattening",
"title": "Opzioni di flattening",
"flattenOnlyForms.desc": "Appiattisci solo i campi modulo, lasciando intatti gli altri elementi interattivi",
"note": "Il flattening rimuove gli elementi interattivi dal PDF, rendendoli non modificabili."
"note": "Il flattening rimuove gli elementi interattivi dal PDF, rendendoli non modificabili.",
"flattenOnlyForms": "Appiattisci solo i moduli"
},
"results": {
"title": "Risultati Flatten"
@ -1747,7 +1869,17 @@
"tags": "commenti,evidenziazioni,note,markup,rimozione",
"title": "Rimuovi Annotazioni",
"header": "Rimuovi Annotazioni",
"submit": "Rimuovi"
"submit": "Rimuovi",
"error": {
"failed": "Si è verificato un errore durante la rimozione delle annotazioni dal PDF."
},
"info": {
"description": "Questo strumento rimuoverà tutte le annotazioni (commenti, evidenziazioni, note, ecc.) dai tuoi documenti PDF.",
"title": "Informazioni su Rimuovi annotazioni"
},
"settings": {
"title": "Impostazioni"
}
},
"compare": {
"tags": "differenziare,contrastare,cambiare,analisi",
@ -3024,7 +3156,13 @@
"removeXMPMetadata.desc": "Rimuovi i metadati XMP dal PDF",
"removeMetadata.desc": "Rimuovi le informazioni (titolo, autore, ecc.)",
"removeLinks.desc": "Rimuovi link esterni e azioni di avvio dal PDF",
"removeFonts.desc": "Rimuovi i font incorporati dal PDF"
"removeFonts.desc": "Rimuovi i font incorporati dal PDF",
"removeEmbeddedFiles": "Rimuovi file incorporati",
"removeFonts": "Rimuovi caratteri",
"removeJavaScript": "Rimuovi JavaScript",
"removeLinks": "Rimuovi collegamenti",
"removeMetadata": "Rimuovi metadati documento",
"removeXMPMetadata": "Rimuovi metadati XMP"
}
},
"addPassword": {
@ -3294,5 +3432,56 @@
}
},
"termsAndConditions": "Termini e condizioni",
"logOut": "Esci"
"logOut": "Esci",
"AddAttachmentsRequest": {
"addMoreFiles": "Aggiungi altri file...",
"attachments": "Seleziona allegati",
"info": "Seleziona i file da allegare al tuo PDF. Questi file saranno incorporati e accessibili tramite il pannello allegati del PDF.",
"placeholder": "Scegli file...",
"results": {
"title": "Risultati allegati"
},
"selectFiles": "Seleziona file da allegare",
"selectedFiles": "File selezionati",
"submit": "Aggiungi allegati"
},
"applyAndContinue": "Applica e continua",
"discardChanges": "Scarta modifiche",
"exportAndContinue": "Esporta e continua",
"keepWorking": "Continua a lavorare",
"replaceColor": {
"tags": "Sostituisci colore,Operazioni pagina,Back end,lato server"
},
"scannerImageSplit": {
"error": {
"failed": "Si è verificato un errore durante l'estrazione delle scansioni di immagini."
},
"submit": "Estrai scansioni di immagini",
"title": "Immagini estratte",
"tooltip": {
"headsUp": "Attenzione",
"headsUpDesc": "Foto sovrapposte o sfondi molto simili nel colore alle foto possono ridurre la precisione - prova uno sfondo più chiaro o più scuro e lascia più spazio.",
"problem1": "Foto non rilevate → aumentare la tolleranza a 30-50",
"problem2": "Troppe rilevazioni errate → aumentare l'area minima a 15.000-20.000",
"problem3": "I ritagli sono troppo stretti → aumentare la dimensione del bordo a 5-10",
"problem4": "Foto inclinate non raddrizzate → abbassare la soglia angolare a ~5°",
"problem5": "Caselle di polvere/rumore → aumentare l'area minima del contorno a 1000-2000",
"quickFixes": "Correzioni rapide",
"setupTips": "Suggerimenti di configurazione",
"tip1": "Usa uno sfondo semplice e chiaro",
"tip2": "Lascia un piccolo spazio (≈1 cm) tra le foto",
"tip3": "Scansiona a 300-600 DPI",
"tip4": "Pulisci il vetro dello scanner",
"title": "Divisore di foto",
"useCase1": "Scansiona intere pagine di album in una volta",
"useCase2": "Dividi i lotti flatbed in file separati",
"useCase3": "Suddividi collage in singole foto",
"useCase4": "Estrai foto dai documenti",
"whatThisDoes": "Cosa fa",
"whatThisDoesDesc": "Trova ed estrae automaticamente ogni foto da una pagina scansionata o da un'immagine composita - senza ritaglio manuale.",
"whenToUse": "Quando usare"
}
},
"unsavedChanges": "Hai modifiche non salvate al tuo PDF. Cosa vuoi fare?",
"unsavedChangesTitle": "Modifiche non salvate"
}

View File

@ -87,7 +87,10 @@
"showStack": "显示堆栈跟踪",
"copyStack": "复制堆栈跟踪",
"githubSubmit": "GitHub - 提交工单",
"discordSubmit": "Discord - 提交支持帖子"
"discordSubmit": "Discord - 提交支持帖子",
"dismissAllErrors": "关闭所有错误",
"encryptedPdfMustRemovePassword": "此 PDF 已加密或受密码保护。请在转换为 PDF/A 之前将其解锁。",
"incorrectPasswordProvided": "PDF 密码不正确或未提供。"
},
"warning": {
"tooltipTitle": "警告"
@ -358,179 +361,223 @@
"sortBy": "排序:",
"multiTool": {
"title": "PDF 多功能工具",
"desc": "合并、旋转、重新排列和删除 PDF 页面"
"desc": "合并、旋转、重新排列和删除 PDF 页面",
"tags": "多个,工具"
},
"merge": {
"title": "合并",
"desc": "轻松将多个 PDF 合并成一个。"
"desc": "轻松将多个 PDF 合并成一个。",
"tags": "组合,合并,联合"
},
"split": {
"title": "拆分",
"desc": "将 PDF 拆分为多个文档。"
"desc": "将 PDF 拆分为多个文档。",
"tags": "分割,分离,拆分"
},
"rotate": {
"title": "旋转",
"desc": "旋转 PDF。"
"desc": "旋转 PDF。",
"tags": "旋转,翻转,定向"
},
"convert": {
"title": "转换",
"desc": "在不同格式之间转换文件"
"desc": "在不同格式之间转换文件",
"tags": "转换,更改"
},
"pdfOrganiser": {
"title": "整理",
"desc": "按任意顺序删除/重新排列页面。"
"desc": "按任意顺序删除/重新排列页面。",
"tags": "组织,重新排列,重新排序"
},
"addImage": {
"title": "在 PDF 中添加图片",
"desc": "将图像添加到 PDF 的指定位置。"
"desc": "将图像添加到 PDF 的指定位置。",
"tags": "插入,嵌入,放置"
},
"addAttachments": {
"title": "添加附件",
"desc": "向 PDF 添加或移除嵌入文件(附件)"
"desc": "向 PDF 添加或移除嵌入文件(附件)",
"tags": "嵌入,附加,包含"
},
"watermark": {
"title": "添加水印",
"desc": "在 PDF 中添加自定义水印。"
"desc": "在 PDF 中添加自定义水印。",
"tags": "印章,标记,叠加"
},
"removePassword": {
"title": "删除密码",
"desc": "从 PDF 文档中移除密码保护。"
"desc": "从 PDF 文档中移除密码保护。",
"tags": "解锁"
},
"compress": {
"title": "压缩",
"desc": "压缩 PDF 文件以减小文件大小。"
"desc": "压缩 PDF 文件以减小文件大小。",
"tags": "缩小,减少,优化"
},
"unlockPDFForms": {
"title": "解锁PDF表单",
"desc": "移除表单字段只读属性"
"desc": "移除表单字段只读属性",
"tags": "解锁,启用,编辑"
},
"changeMetadata": {
"title": "更改元数据",
"desc": "更改/删除/添加 PDF 文档的元数据。"
"desc": "更改/删除/添加 PDF 文档的元数据。",
"tags": "编辑,修改,更新"
},
"ocr": {
"title": "运行 OCR /清理扫描",
"desc": "清理和识别 PDF 中的图像文本,并将其转换为可编辑文本。"
"desc": "清理和识别 PDF 中的图像文本,并将其转换为可编辑文本。",
"tags": "提取,扫描"
},
"extractImages": {
"title": "提取图像",
"desc": "从 PDF 中提取所有图像并保存到压缩包中。"
"desc": "从 PDF 中提取所有图像并保存到压缩包中。",
"tags": "提取,保存,导出"
},
"scannerImageSplit": {
"title": "检测/拆分扫描照片",
"desc": "从照片/PDF 中拆分出多张照片"
"desc": "从照片/PDF 中拆分出多张照片",
"tags": "检测,拆分,照片"
},
"sign": {
"title": "签名",
"desc": "通过绘图、文字或图像向 PDF 添加签名"
"desc": "通过绘图、文字或图像向 PDF 添加签名",
"tags": "签名,亲笔签名"
},
"flatten": {
"title": "展平",
"desc": "从 PDF 中删除所有互动元素和表单"
"desc": "从 PDF 中删除所有互动元素和表单",
"tags": "简化,删除,交互式"
},
"certSign": {
"title": "使用证书签名",
"desc": "使用证书/密钥PEM/P12对PDF进行签名"
"desc": "使用证书/密钥PEM/P12对PDF进行签名",
"tags": "认证,PEM,P12,官方,加密,签名,证书,PKCS12,JKS,服务器,手动,自动"
},
"repair": {
"title": "修复",
"desc": "尝试修复损坏/损坏的 PDF"
"desc": "尝试修复损坏/损坏的 PDF",
"tags": "修复,恢复"
},
"removeBlanks": {
"title": "删除空白页",
"desc": "检测并删除文档中的空白页"
"desc": "检测并删除文档中的空白页",
"tags": "删除,清理,空白"
},
"removeAnnotations": {
"title": "删除标注",
"desc": "删除 PDF 中的所有标注/评论"
"desc": "删除 PDF 中的所有标注/评论",
"tags": "删除,清理,删除"
},
"compare": {
"title": "比较",
"desc": "比较并显示两个 PDF 文档之间的差异"
"desc": "比较并显示两个 PDF 文档之间的差异",
"tags": "差异"
},
"removeCertSign": {
"title": "移除证书签名",
"desc": "移除 PDF 的证书签名"
"desc": "移除 PDF 的证书签名",
"tags": "删除,删除,解锁"
},
"pageLayout": {
"title": "多页布局",
"desc": "将 PDF 文档的多个页面合并成一页"
"desc": "将 PDF 文档的多个页面合并成一页",
"tags": "布局,排列,组合"
},
"bookletImposition": {
"title": "小册子拼版",
"desc": "创建具有正确页面顺序和多页布局的小册子,用于打印和装订"
"desc": "创建具有正确页面顺序和多页布局的小册子,用于打印和装订",
"tags": "小册子,打印,装订"
},
"scalePages": {
"title": "调整页面尺寸/缩放",
"desc": "调整页面及/或其内容的尺寸/缩放"
"desc": "调整页面及/或其内容的尺寸/缩放",
"tags": "调整大小,调整,缩放"
},
"addPageNumbers": {
"title": "添加页码",
"desc": "在文档的指定位置添加页码"
"desc": "在文档的指定位置添加页码",
"tags": "编号,分页,计数"
},
"autoRename": {
"title": "自动重命名 PDF 文件",
"desc": "基于检测到的页眉自动重命名 PDF 文件"
"desc": "基于检测到的页眉自动重命名 PDF 文件",
"tags": "自动检测,基于标题,组织,重新标记"
},
"adjustContrast": {
"title": "调整颜色/对比度",
"desc": "调整 PDF 的对比度、饱和度和亮度"
"desc": "调整 PDF 的对比度、饱和度和亮度",
"tags": "对比度,亮度,饱和度"
},
"crop": {
"title": "裁剪 PDF",
"desc": "裁剪 PDF 以减小其文件大小(保留文本!)"
"desc": "裁剪 PDF 以减小其文件大小(保留文本!)",
"tags": "裁剪,剪切,调整大小"
},
"autoSplitPDF": {
"title": "自动拆分页面",
"desc": "使用物理扫描页面分割器 QR 代码自动拆分扫描的 PDF"
"desc": "使用物理扫描页面分割器 QR 代码自动拆分扫描的 PDF",
"tags": "自动,拆分,QR"
},
"sanitize": {
"title": "安全清理",
"desc": "移除 PDF 文件中的潜在有害元素"
"desc": "移除 PDF 文件中的潜在有害元素",
"tags": "清理,清除,删除"
},
"getPdfInfo": {
"title": "获取 PDF 的所有信息",
"desc": "获取 PDF 的所有可能的信息"
"desc": "获取 PDF 的所有可能的信息",
"tags": "信息,元数据,详细信息"
},
"pdfToSinglePage": {
"title": "PDF 转单一大页",
"desc": "将所有 PDF 页面合并为一个大的单页"
"desc": "将所有 PDF 页面合并为一个大的单页",
"tags": "组合,合并,单页"
},
"showJS": {
"title": "显示 JavaScript",
"desc": "搜索并显示嵌入到 PDF 中的任何 JavaScript 代码"
"desc": "搜索并显示嵌入到 PDF 中的任何 JavaScript 代码",
"tags": "javascript,代码,脚本"
},
"redact": {
"title": "手动修订",
"desc": "根据选定的文本、绘制的形状和/或选定的页面编辑PDF"
"desc": "根据选定的文本、绘制的形状和/或选定的页面编辑PDF",
"tags": "审查,涂黑,隐藏"
},
"overlayPdfs": {
"title": "叠加 PDF",
"desc": "将一个 PDF 叠加到另一个 PDF 之上"
"desc": "将一个 PDF 叠加到另一个 PDF 之上",
"tags": "叠加,组合,堆叠"
},
"splitBySections": {
"title": "按区块拆分 PDF",
"desc": "将 PDF 的每一页分割为更小的横向与纵向区块"
"desc": "将 PDF 的每一页分割为更小的横向与纵向区块",
"tags": "拆分,部分,分割"
},
"addStamp": {
"title": "向 PDF 添加印章",
"desc": "在指定位置添加文本或图像印章"
"desc": "在指定位置添加文本或图像印章",
"tags": "印章,标记,盖章"
},
"removeImage": {
"title": "删除图像",
"desc": "删除图像减少 PDF 大小"
"desc": "删除图像减少 PDF 大小",
"tags": "删除,删除,清理"
},
"splitByChapters": {
"title": "按章节拆分 PDF",
"desc": "根据其章节结构将 PDF 拆分为多个文件。"
"desc": "根据其章节结构将 PDF 拆分为多个文件。",
"tags": "拆分,章节,结构"
},
"validateSignature": {
"title": "验证 PDF 签名",
"desc": "验证 PDF 文档中的数字签名和证书"
"desc": "验证 PDF 文档中的数字签名和证书",
"tags": "验证,核实,证书"
},
"swagger": {
"title": "API 文档",
"desc": "查看 API 文档并测试端点"
"desc": "查看 API 文档并测试端点",
"tags": "API,文档,测试"
},
"fakeScan": {
"title": "伪扫描",
@ -538,31 +585,38 @@
},
"editTableOfContents": {
"title": "编辑目录",
"desc": "为 PDF 文档添加或编辑目录和书签"
"desc": "为 PDF 文档添加或编辑目录和书签",
"tags": "书签,目录,编辑"
},
"manageCertificates": {
"title": "管理证书",
"desc": "导入、导出或删除用于签名 PDF 的数字证书文件。"
"desc": "导入、导出或删除用于签名 PDF 的数字证书文件。",
"tags": "证书,导入,导出"
},
"read": {
"title": "阅读",
"desc": "查看与批注 PDF。高亮、绘制或插入评论以便审阅协作。"
"desc": "查看与批注 PDF。高亮、绘制或插入评论以便审阅协作。",
"tags": "查看,打开,显示"
},
"reorganizePages": {
"title": "重组页面",
"desc": "通过可视化拖放控制重新排列、复制或删除 PDF 页面。"
"desc": "通过可视化拖放控制重新排列、复制或删除 PDF 页面。",
"tags": "重新排列,重新排序,组织"
},
"extractPages": {
"title": "提取页面",
"desc": "从 PDF 文档中提取特定页面"
"desc": "从 PDF 文档中提取特定页面",
"tags": "提取,选择,复制"
},
"removePages": {
"title": "删除",
"desc": "从 PDF 文档中删除不需要的页面。"
"desc": "从 PDF 文档中删除不需要的页面。",
"tags": "删除,提取,排除"
},
"autoSizeSplitPDF": {
"title": "自动根据大小/数目拆分 PDF",
"desc": "将单个 PDF 拆分为多个文档,基于大小、页数或文档数"
"desc": "将单个 PDF 拆分为多个文档,基于大小、页数或文档数",
"tags": "自动,拆分,大小"
},
"replaceColorPdf": {
"title": "替换和反转颜色",
@ -570,11 +624,13 @@
},
"devApi": {
"title": "API",
"desc": "跳转至 API 文档"
"desc": "跳转至 API 文档",
"tags": "API,开发,文档"
},
"devFolderScanning": {
"title": "自动文件夹扫描",
"desc": "跳转至自动文件夹扫描指南"
"desc": "跳转至自动文件夹扫描指南",
"tags": "自动化,文件夹,扫描"
},
"devSsoGuide": {
"title": "SSO 指南",
@ -594,7 +650,17 @@
},
"automate": {
"title": "自动化",
"desc": "通过串联 PDF 操作构建多步工作流。适合重复性任务。"
"desc": "通过串联 PDF 操作构建多步工作流。适合重复性任务。",
"tags": "工作流,序列,自动化"
},
"replaceColor": {
"desc": "替换或反转 PDF 文档中的颜色",
"title": "替换和反转颜色"
},
"scannerEffect": {
"desc": "创建看起来像扫描的 PDF",
"tags": "扫描,模拟,创建",
"title": "扫描仪效果"
}
},
"landing": {
@ -654,7 +720,9 @@
},
"error": {
"failed": "合并 PDF 时发生错误。"
}
},
"generateTableOfContents": "在合并的文件中生成目录?",
"removeDigitalSignature": "在合并的文件中删除数字签名?"
},
"split": {
"tags": "页面操作,划分,多页面,剪切,服务器端",
@ -815,7 +883,7 @@
"settings": "设置",
"conversionCompleted": "转换完成",
"results": "结果",
"defaultFilename": "converted_file",
"defaultFilename": "已转换文件",
"conversionResults": "转换结果",
"convertFrom": "转换来源",
"convertTo": "转换为",
@ -913,7 +981,20 @@
"10": "奇偶合并",
"11": "复制所有页面"
},
"placeholder": "例如1,3,2 或 4-8,2,10-12 或 2n-1"
"placeholder": "例如1,3,2 或 4-8,2,10-12 或 2n-1",
"desc": {
"BOOKLET_SORT": "排列页面以进行小册子打印(最后,第一,第二,倒数第二,...)。",
"CUSTOM": "使用自定义的页码序列或表达式来定义新顺序。",
"DUPLEX_SORT": "交错正面然后背面,就像双面扫描仪扫描了所有正面,然后所有背面(1, n, 2, n-1, ...)。",
"DUPLICATE": "根据自定义顺序计数复制每页(例如,4 复制每页 4×)。",
"ODD_EVEN_MERGE": "通过交替页面合并两个 PDF:第一个的奇数页,第二个的偶数页。",
"ODD_EVEN_SPLIT": "将文档拆分为两个输出:所有奇数页和所有偶数页。",
"REMOVE_FIRST": "从文档中删除第一页。",
"REMOVE_FIRST_AND_LAST": "从文档中删除第一页和最后一页。",
"REMOVE_LAST": "从文档中删除最后一页。",
"REVERSE_ORDER": "翻转文档,使最后一页变为第一页,依此类推。",
"SIDE_STITCH_BOOKLET_SORT": "排列页面以进行侧缝小册子打印(针对侧面装订进行了优化)。"
}
},
"addImage": {
"tags": "图像、JPG、图片、照片",
@ -937,7 +1018,7 @@
"desc": "向 PDF 添加文本或图像水印",
"completed": "已添加水印",
"submit": "添加水印",
"filenamePrefix": "watermarked",
"filenamePrefix": "已加水印",
"error": {
"failed": "向 PDF 添加水印时发生错误。"
},
@ -1136,7 +1217,7 @@
"placeholder": "例如1,3,5-8,10",
"error": "无效的页码格式。使用数字、范围1-5或数学表达式2n+1"
},
"filenamePrefix": "pages_removed",
"filenamePrefix": "已删除页面",
"files": {
"placeholder": "在主视图中选择一个 PDF 文件以开始"
},
@ -1284,7 +1365,7 @@
"header": "解锁 PDF 表单",
"submit": "Remove",
"description": "该工具将移除 PDF 表单字段的只读限制,使其可编辑、可填写。",
"filenamePrefix": "unlocked_forms",
"filenamePrefix": "已解锁表单",
"files": {
"placeholder": "在主视图中选择一个 PDF 文件以开始"
},
@ -1299,7 +1380,7 @@
"tags": "标题、作者、日期、创建、时间、发布者、制作人、统计数据",
"header": "更改元数据",
"submit": "更改",
"filenamePrefix": "metadata",
"filenamePrefix": "元数据",
"settings": {
"title": "元数据设置"
},
@ -1347,7 +1428,7 @@
},
"trapped": {
"label": "陷印状态",
"unknown": "Unknown",
"unknown": "未知",
"true": "True",
"false": "False"
},
@ -1538,7 +1619,13 @@
"header": "提取图像",
"selectText": "选择图像格式,将提取的图像转换为",
"allowDuplicates": "保存重复图像",
"submit": "提取"
"submit": "提取",
"error": {
"failed": "从 PDF 提取图像时发生错误。"
},
"settings": {
"title": "设置"
}
},
"pdfToPDFA": {
"tags": "归档、长期、标准、转换、存储、保存",
@ -1615,8 +1702,14 @@
"title": "签名",
"header": "签署 PDF",
"upload": "上传图片",
"draw": "绘制签名",
"text": "文本输入",
"draw": {
"clear": "清除",
"title": "绘制您的签名"
},
"text": {
"name": "签署人姓名",
"placeholder": "输入您的全名"
},
"clear": "清除",
"add": "添加",
"saved": "已保存签名",
@ -1632,7 +1725,35 @@
"previous": "上一页",
"maintainRatio": "切换保持长宽比",
"undo": "撤销",
"redo": "重做"
"redo": "重做",
"activate": "激活签名放置",
"applySignatures": "应用签名",
"deactivate": "停止放置签名",
"error": {
"failed": "签署 PDF 时发生错误。"
},
"image": {
"hint": "上传 PNG 或 JPG 格式的签名图像",
"label": "上传签名图像",
"placeholder": "选择图像文件"
},
"instructions": {
"title": "如何添加签名"
},
"results": {
"title": "签名结果"
},
"steps": {
"configure": "配置签名"
},
"submit": "签署文档",
"type": {
"canvas": "画布",
"draw": "绘制",
"image": "图像",
"text": "文本",
"title": "签名类型"
}
},
"flatten": {
"tags": "静态、停用、非交互、简化",
@ -1640,7 +1761,7 @@
"header": "展平 PDF",
"flattenOnlyForms": "仅展平表格",
"submit": "展平",
"filenamePrefix": "flattened",
"filenamePrefix": "已扁平化",
"files": {
"placeholder": "在主视图中选择一个 PDF 文件以开始"
},
@ -1651,7 +1772,8 @@
"stepTitle": "扁平化选项",
"title": "扁平化选项",
"flattenOnlyForms.desc": "仅扁平化表单字段,保留其他交互元素",
"note": "扁平化会移除 PDF 的交互元素,使其不可编辑。"
"note": "扁平化会移除 PDF 的交互元素,使其不可编辑。",
"flattenOnlyForms": "仅扁平化表单"
},
"results": {
"title": "扁平化结果"
@ -1687,7 +1809,7 @@
"header": "修复 PDF",
"submit": "修复",
"description": "该工具将尝试修复损坏或受损的 PDF 文件。无需额外设置。",
"filenamePrefix": "repaired",
"filenamePrefix": "已修复",
"files": {
"placeholder": "在主视图中选择一个 PDF 文件以开始"
},
@ -1747,7 +1869,17 @@
"tags": "评论、高亮、笔记、标注、删除",
"title": "删除标注",
"header": "删除标注",
"submit": "删除"
"submit": "删除",
"error": {
"failed": "从 PDF 删除注释时发生错误。"
},
"info": {
"description": "此工具将从您的 PDF 文档中删除所有注释(评论、高亮、笔记等)。",
"title": "关于删除注释"
},
"settings": {
"title": "设置"
}
},
"compare": {
"tags": "区分、对比、更改、分析",
@ -1779,7 +1911,7 @@
"certSign": {
"tags": "身份验证、PEM、P12、官方、加密",
"title": "证书签名",
"filenamePrefix": "signed",
"filenamePrefix": "已签名",
"signMode": {
"stepTitle": "签名模式",
"tooltip": {
@ -1903,7 +2035,7 @@
"selectPDF": "选择 PDF 文件:",
"submit": "移除签名",
"description": "该工具将从您的 PDF 文档中移除数字证书签名。",
"filenamePrefix": "unsigned",
"filenamePrefix": "未签名",
"files": {
"placeholder": "在主视图中选择一个 PDF 文件以开始"
},
@ -1923,7 +2055,7 @@
"submit": "提交"
},
"bookletImposition": {
"tags": "booklet,imposition,printing,binding,folding,signature",
"tags": "小册子,拼版,打印,装订,折叠,签名",
"title": "小册子拼版",
"header": "小册子拼版",
"submit": "创建小册子",
@ -2024,7 +2156,7 @@
"submit": "提交"
},
"adjustPageScale": {
"tags": "resize,modify,dimension,adapt",
"tags": "调整大小,修改,尺寸,适应",
"title": "调整页面比例",
"header": "调整页面比例",
"scaleFactor": {
@ -2547,7 +2679,7 @@
"header": "将 PDF 转换为单页",
"submit": "转为单页",
"description": "该工具会将 PDF 的所有页面合并为一张超长单页。宽度保持与原页面相同,高度为所有页面高度之和。",
"filenamePrefix": "single_page",
"filenamePrefix": "单页",
"files": {
"placeholder": "在主视图中选择一个 PDF 文件以开始"
},
@ -2768,7 +2900,7 @@
"title": "API 文档",
"header": "API 文档",
"desc": "查看并测试 Stirling PDF 的 API 端点",
"tags": "api,documentation,swagger,endpoints,development"
"tags": "api,文档,swagger,端点,开发"
},
"cookieBanner": {
"popUp": {
@ -3006,7 +3138,7 @@
"completed": "安全清理成功完成",
"error.generic": "安全清理失败",
"error.failed": "安全清理 PDF 时发生错误。",
"filenamePrefix": "sanitised",
"filenamePrefix": "已清理",
"sanitizationResults": "安全清理结果",
"steps": {
"files": "文件",
@ -3024,7 +3156,13 @@
"removeXMPMetadata.desc": "从 PDF 中移除 XMP 元数据",
"removeMetadata.desc": "移除文档信息元数据(标题、作者等)",
"removeLinks.desc": "移除外部链接与启动动作",
"removeFonts.desc": "从 PDF 中移除嵌入字体"
"removeFonts.desc": "从 PDF 中移除嵌入字体",
"removeEmbeddedFiles": "删除嵌入文件",
"removeFonts": "删除字体",
"removeJavaScript": "删除 JavaScript",
"removeLinks": "删除链接",
"removeMetadata": "删除文档元数据",
"removeXMPMetadata": "删除 XMP 元数据"
}
},
"addPassword": {
@ -3032,7 +3170,7 @@
"desc": "使用密码加密您的 PDF 文档。",
"completed": "已应用密码保护",
"submit": "加密",
"filenamePrefix": "encrypted",
"filenamePrefix": "已加密",
"error": {
"failed": "加密 PDF 时发生错误。"
},
@ -3141,7 +3279,7 @@
"placeholder": "输入当前密码",
"completed": "密码已配置"
},
"filenamePrefix": "decrypted",
"filenamePrefix": "已解密",
"error": {
"failed": "移除 PDF 密码时发生错误。"
},
@ -3294,5 +3432,56 @@
}
},
"termsAndConditions": "条款与条件",
"logOut": "退出登录"
"logOut": "退出登录",
"AddAttachmentsRequest": {
"addMoreFiles": "添加更多文件...",
"attachments": "选择附件",
"info": "选择要附加到 PDF 的文件。这些文件将被嵌入并可通过 PDF 的附件面板访问。",
"placeholder": "选择文件...",
"results": {
"title": "附件结果"
},
"selectFiles": "选择要附加的文件",
"selectedFiles": "已选择的文件",
"submit": "添加附件"
},
"applyAndContinue": "应用并继续",
"discardChanges": "放弃更改",
"exportAndContinue": "导出并继续",
"keepWorking": "继续工作",
"replaceColor": {
"tags": "替换颜色,页面操作,后端,服务器端"
},
"scannerImageSplit": {
"error": {
"failed": "提取图像扫描时发生错误。"
},
"submit": "提取图像扫描",
"title": "已提取的图像",
"tooltip": {
"headsUp": "注意",
"headsUpDesc": "重叠的照片或颜色与照片非常接近的背景会降低准确性 - 尝试使用更浅或更深的背景并留出更多空间。",
"problem1": "未检测到照片 → 将容差增加到 30-50",
"problem2": "误检测太多 → 将最小面积增加到 15,000-20,000",
"problem3": "裁剪太紧 → 将边框大小增加到 5-10",
"problem4": "倾斜的照片未矫正 → 将角度阈值降低到 ~5°",
"problem5": "灰尘/噪声框 → 将最小轮廓面积增加到 1000-2000",
"quickFixes": "快速修复",
"setupTips": "设置提示",
"tip1": "使用简单的浅色背景",
"tip2": "在照片之间留出小间隙(≈1 厘米)",
"tip3": "以 300-600 DPI 扫描",
"tip4": "清洁扫描仪玻璃",
"title": "照片分割器",
"useCase1": "一次扫描整个相册页面",
"useCase2": "将平板批次拆分为单独的文件",
"useCase3": "将拼贴画拆分为单独的照片",
"useCase4": "从文档中提取照片",
"whatThisDoes": "功能说明",
"whatThisDoesDesc": "自动查找并从扫描页面或合成图像中提取每张照片 - 无需手动裁剪。",
"whenToUse": "何时使用"
}
},
"unsavedChanges": "您的 PDF 有未保存的更改。您想做什么?",
"unsavedChangesTitle": "未保存的更改"
}

View File

@ -0,0 +1,204 @@
"""A script to update language progress status in README.md based on
JSON translation file comparison.
This script compares the default translation JSON file with others in the locales directory to
determine language progress.
It then updates README.md based on provided progress list.
Author: Ludy87
Example:
To use this script, simply run it from command line:
$ python counter_translation_v2.py
""" # noqa: D205
import glob
import os
import re
import json
import tomlkit
import tomlkit.toml_file
def convert_to_multiline(data: tomlkit.TOMLDocument) -> tomlkit.TOMLDocument:
"""Converts 'ignore' and 'missing' arrays to multiline arrays and sorts the first-level keys of the TOML document.
Enhances readability and consistency in the TOML file by ensuring arrays contain unique and sorted entries.
Parameters:
data (tomlkit.TOMLDocument): The original TOML document containing the data.
Returns:
tomlkit.TOMLDocument: A new TOML document with sorted keys and properly formatted arrays.
""" # noqa: D205
sorted_data = tomlkit.document()
for key in sorted(data.keys()):
value = data[key]
if isinstance(value, dict):
new_table = tomlkit.table()
for subkey in ("ignore", "missing"):
if subkey in value:
# Convert the list to a set to remove duplicates, sort it, and convert to multiline for readability
unique_sorted_array = sorted(set(value[subkey]))
array = tomlkit.array()
array.multiline(True)
for item in unique_sorted_array:
array.append(item)
new_table[subkey] = array
sorted_data[key] = new_table
else:
# Add other types of data unchanged
sorted_data[key] = value
return sorted_data
def write_readme(progress_list: list[tuple[str, int]]) -> None:
"""Updates the progress status in the README.md file based
on the provided progress list.
Parameters:
progress_list (list[tuple[str, int]]): A list of tuples containing
language and progress percentage.
Returns:
None
""" # noqa: D205
with open("README.md", encoding="utf-8") as file:
content = file.readlines()
for i, line in enumerate(content[2:], start=2):
for progress in progress_list:
language, value = progress
if language in line:
if match := re.search(r"\!\[(\d+(\.\d+)?)%\]\(.*\)", line):
content[i] = line.replace(
match.group(0),
f"![{value}%](https://geps.dev/progress/{value})",
)
with open("README.md", "w", encoding="utf-8", newline="\n") as file:
file.writelines(content)
def parse_json_file(file_path):
"""
Parses a JSON translation file and returns a flat dictionary of all keys.
:param file_path: Path to the JSON file.
:return: Dictionary with flattened keys and values.
"""
with open(file_path, "r", encoding="utf-8") as file:
data = json.load(file)
def flatten_dict(d, parent_key="", sep="."):
items = {}
for k, v in d.items():
new_key = f"{parent_key}{sep}{k}" if parent_key else k
if isinstance(v, dict):
items.update(flatten_dict(v, new_key, sep=sep))
else:
items[new_key] = v
return items
return flatten_dict(data)
def compare_files(
default_file_path, file_paths, ignore_translation_file
) -> list[tuple[str, int]]:
"""Compares the default JSON translation file with other
translation files in the locales directory.
Parameters:
default_file_path (str): The path to the default translation JSON file.
file_paths (list): List of paths to translation JSON files.
ignore_translation_file (str): Path to the TOML file with ignore rules.
Returns:
list[tuple[str, int]]: A list of tuples containing
language and progress percentage.
""" # noqa: D205
default_keys = parse_json_file(default_file_path)
num_keys = len(default_keys)
result_list = []
sort_ignore_translation: tomlkit.TOMLDocument
# read toml
with open(ignore_translation_file, encoding="utf-8") as f:
sort_ignore_translation = tomlkit.parse(f.read())
for file_path in file_paths:
# Extract language code from directory name
locale_dir = os.path.basename(os.path.dirname(file_path))
# Convert locale format from hyphen to underscore for TOML compatibility
# e.g., en-GB -> en_GB, sr-LATN-RS -> sr_LATN_RS
language = locale_dir.replace("-", "_")
fails = 0
if language in ["en_GB", "en_US"]:
result_list.append(("en_GB", 100))
result_list.append(("en_US", 100))
continue
if language not in sort_ignore_translation:
sort_ignore_translation[language] = tomlkit.table()
if (
"ignore" not in sort_ignore_translation[language]
or len(sort_ignore_translation[language].get("ignore", [])) < 1
):
sort_ignore_translation[language]["ignore"] = tomlkit.array(
["language.direction"]
)
current_keys = parse_json_file(file_path)
# Compare keys
for default_key, default_value in default_keys.items():
if default_key not in current_keys:
# Key is missing entirely
if default_key not in sort_ignore_translation[language]["ignore"]:
print(f"{language}: Key '{default_key}' is missing.")
fails += 1
elif (
default_value == current_keys[default_key]
and default_key not in sort_ignore_translation[language]["ignore"]
):
# Key exists but value is untranslated (same as reference)
print(f"{language}: Key '{default_key}' is missing the translation.")
fails += 1
elif default_value != current_keys[default_key]:
# Key is translated, remove from ignore list if present
if default_key in sort_ignore_translation[language]["ignore"]:
sort_ignore_translation[language]["ignore"].remove(default_key)
print(f"{language}: {fails} out of {num_keys} keys are not translated.")
result_list.append(
(
language,
int((num_keys - fails) * 100 / num_keys),
)
)
ignore_translation = convert_to_multiline(sort_ignore_translation)
with open(ignore_translation_file, "w", encoding="utf-8", newline="\n") as file:
file.write(tomlkit.dumps(ignore_translation))
unique_data = list(set(result_list))
unique_data.sort(key=lambda x: x[1], reverse=True)
return unique_data
if __name__ == "__main__":
directory = os.path.join(os.getcwd(), "frontend", "public", "locales")
translation_file_paths = glob.glob(os.path.join(directory, "*", "translation.json"))
reference_file = os.path.join(directory, "en-GB", "translation.json")
scripts_directory = os.path.join(os.getcwd(), "scripts")
translation_state_file = os.path.join(scripts_directory, "ignore_translation.toml")
write_readme(
compare_files(reference_file, translation_file_paths, translation_state_file)
)