diff --git a/.github/README.md b/.github/README.md new file mode 100644 index 0000000000..97cb440866 --- /dev/null +++ b/.github/README.md @@ -0,0 +1,12 @@ +# CI Configuration + +## CI Lite Mode + +Skip non-essential CI workflows by setting a repository variable: + +**Settings → Secrets and variables → Actions → Variables → New repository variable** + +- Name: `CI_PROFILE` +- Value: `lite` + +Skips resource-intensive builds, releases, and OSS-specific workflows. Useful for deployment-only forks or faster CI runs. diff --git a/.github/scripts/check_language_properties.py b/.github/scripts/check_language_properties.py deleted file mode 100644 index a77c378a66..0000000000 --- a/.github/scripts/check_language_properties.py +++ /dev/null @@ -1,403 +0,0 @@ -""" -Author: Ludy87 -Description: This script processes .properties 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 lines (including comments and empty lines) 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_properties.py --reference-file --branch [--actor ] [--files ] -""" -# Sample for Windows: -# python .github/scripts/check_language_properties.py --reference-file src\main\resources\messages_en_GB.properties --branch "" --files src\main\resources\messages_de_DE.properties src\main\resources\messages_uk_UA.properties - -import copy -import glob -import os -import argparse -import re - - -def find_duplicate_keys(file_path): - """ - Identifies duplicate keys in a .properties file. - :param file_path: Path to the .properties file. - :return: List of tuples (key, first_occurrence_line, duplicate_line). - """ - keys = {} - duplicates = [] - - with open(file_path, "r", encoding="utf-8") as file: - for line_number, line in enumerate(file, start=1): - stripped_line = line.strip() - - # Skip empty lines and comments - if not stripped_line or stripped_line.startswith("#"): - continue - - # Split the line into key and value - if "=" in stripped_line: - key, _ = stripped_line.split("=", 1) - key = key.strip() - - # Check if the key already exists - if key in keys: - duplicates.append((key, keys[key], line_number)) - else: - keys[key] = line_number - - return duplicates - - -# Maximum size for properties files (e.g., 200 KB) -MAX_FILE_SIZE = 200 * 1024 - - -def parse_properties_file(file_path): - """ - Parses a .properties file and returns a structured list of its contents. - :param file_path: Path to the .properties file. - :return: List of dictionaries representing each line in the file. - """ - properties_list = [] - with open(file_path, "r", encoding="utf-8") as file: - for line_number, line in enumerate(file, start=1): - stripped_line = line.strip() - - # Handle empty lines - if not stripped_line: - properties_list.append( - {"line_number": line_number, "type": "empty", "content": ""} - ) - continue - - # Handle comments - if stripped_line.startswith("#"): - properties_list.append( - { - "line_number": line_number, - "type": "comment", - "content": stripped_line, - } - ) - continue - - # Handle key-value pairs - match = re.match(r"^([^=]+)=(.*)$", line) - if match: - key, value = match.groups() - properties_list.append( - { - "line_number": line_number, - "type": "entry", - "key": key.strip(), - "value": value.strip(), - } - ) - - return properties_list - - -def write_json_file(file_path, updated_properties): - """ - Writes updated properties back to the file in their original format. - :param file_path: Path to the .properties file. - :param updated_properties: List of updated properties to write. - """ - updated_lines = {entry["line_number"]: entry for entry in updated_properties} - - # Sort lines by their numbers and retain comments and empty lines - all_lines = sorted(set(updated_lines.keys())) - - original_format = [] - for line in all_lines: - if line in updated_lines: - entry = updated_lines[line] - else: - entry = None - ref_entry = updated_lines[line] - if ref_entry["type"] in ["comment", "empty"]: - original_format.append(ref_entry) - elif entry is None: - # Add missing entries from the reference file - original_format.append(ref_entry) - elif entry["type"] == "entry": - # Replace entries with those from the current JSON - original_format.append(entry) - - # Write the updated content back to the file - with open(file_path, "w", encoding="utf-8", newline="\n") as file: - for entry in original_format: - if entry["type"] == "comment": - file.write(f"{entry['content']}\n") - elif entry["type"] == "empty": - file.write(f"{entry['content']}\n") - elif entry["type"] == "entry": - file.write(f"{entry['key']}={entry['value']}\n") - - -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 .properties file. - :param file_list: List of translation files to update. - :param branch: Branch where the files are located. - """ - reference_properties = parse_properties_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(".properties") - or not basename_current_file.startswith("messages_") - ): - continue - - current_properties = parse_properties_file(os.path.join(branch, file_path)) - updated_properties = [] - for ref_entry in reference_properties: - ref_entry_copy = copy.deepcopy(ref_entry) - for current_entry in current_properties: - if current_entry["type"] == "entry": - if ref_entry_copy["type"] != "entry": - continue - if ref_entry_copy["key"].lower() == current_entry["key"].lower(): - ref_entry_copy["value"] = current_entry["value"] - updated_properties.append(ref_entry_copy) - 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_properties(file_path): - if os.path.isfile(file_path) and os.path.exists(file_path): - with open(file_path, "r", encoding="utf-8") as file: - return file.read().splitlines() - return [""] - - -def check_for_differences(reference_file, file_list, branch, actor): - reference_branch = reference_file.split("/")[0] - basename_reference_file = os.path.basename(reference_file) - - report = [] - report.append(f"#### šŸ”„ Reference Branch: `{reference_branch}`") - reference_lines = read_properties(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(), "app", "core", "src", "main", "resources") - ) - - 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)) - if ( - basename_current_file == basename_reference_file - or ( - # only local windows command - not file_normpath.startswith( - os.path.join( - "", "app", "core", "src", "main", "resources", "messages_" - ) - ) - and not file_normpath.startswith( - os.path.join( - os.getcwd(), - "app", - "core", - "src", - "main", - "resources", - "messages_", - ) - ) - ) - or not file_normpath.endswith(".properties") - or not basename_current_file.startswith("messages_") - ): - continue - only_reference_file = False - report.append(f"#### šŸ“ƒ **File Check:** `{basename_current_file}`") - current_lines = read_properties(os.path.join(branch, file_path)) - reference_line_count = len(reference_lines) - current_line_count = len(current_lines) - - if reference_line_count != current_line_count: - report.append("") - report.append("1. **Test Status:** āŒ **_Failed_**") - report.append(" - **Issue:**") - has_differences = True - if reference_line_count > current_line_count: - report.append( - f" - **_Mismatched line count_**: {reference_line_count} (reference) vs {current_line_count} (current). Comments, empty lines, or translation strings are missing." - ) - elif reference_line_count < current_line_count: - report.append( - f" - **_Too many lines_**: {reference_line_count} (reference) vs {current_line_count} (current). Please verify if there is an additional line that needs to be removed." - ) - else: - report.append("1. **Test Status:** āœ… **_Passed_**") - - # Check for missing or extra keys - current_keys = [] - reference_keys = [] - for line in current_lines: - if not line.startswith("#") and line != "" and "=" in line: - key, _ = line.split("=", 1) - current_keys.append(key) - for line in reference_lines: - if not line.startswith("#") and line != "" and "=" in line: - key, _ = line.split("=", 1) - reference_keys.append(key) - - current_keys_set = set(current_keys) - reference_keys_set = set(reference_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: - spaces_keys_list = [] - for key in missing_keys_list: - if " " in key: - spaces_keys_list.append(key) - if spaces_keys_list: - spaces_keys_str = "`, `".join(spaces_keys_list) - report.append( - f" - **_Keys containing unnecessary spaces_**: `{spaces_keys_str}`!" - ) - report.append( - f" - **_Extra keys in `{basename_current_file}`_**: `{missing_keys_str}` that are not present in **_`{basename_reference_file}`_**." - ) - if extra_keys_list: - report.append( - f" - **_Missing keys in `{basename_reference_file}`_**: `{extra_keys_str}` that are not present in **_`{basename_current_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 line {first}, duplicate at `line {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 [messages_en_GB.properties](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/app/core/src/main/resources/messages_en_GB.properties)" - ) - 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(), - "app", - "core", - "src", - "main", - "resources", - "messages_*.properties", - ) - ) - update_missing_keys(args.reference_file, file_list) - else: - check_for_differences(args.reference_file, file_list, args.branch, args.actor) diff --git a/.github/scripts/check_language_json.py b/.github/scripts/check_language_toml.py similarity index 84% rename from .github/scripts/check_language_json.py rename to .github/scripts/check_language_toml.py index 3921bdaa57..494f90962e 100644 --- a/.github/scripts/check_language_json.py +++ b/.github/scripts/check_language_toml.py @@ -1,6 +1,6 @@ """ Author: Ludy87 -Description: This script processes JSON translation files for localization checks. It compares translation files in a branch with +Description: This script processes TOML 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. @@ -9,10 +9,10 @@ The script also provides functionality to update the translation files to match adjusting the format. Usage: - python check_language_json.py --reference-file --branch [--actor ] [--files ] + python check_language_toml.py --reference-file --branch [--actor ] [--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 +# python .github/scripts/check_language_toml.py --reference-file frontend/public/locales/en-GB/translation.toml --branch "" --files frontend/public/locales/de-DE/translation.toml frontend/public/locales/fr-FR/translation.toml import copy import glob @@ -20,12 +20,14 @@ import os import argparse import re import json +import tomllib # Python 3.11+ (stdlib) +import tomli_w # For writing TOML files 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. + Identifies duplicate keys in a TOML file (including nested keys). + :param file_path: Path to the TOML 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). @@ -35,8 +37,9 @@ def find_duplicate_keys(file_path, keys=None, prefix=""): duplicates = [] - with open(file_path, "r", encoding="utf-8") as file: - data = json.load(file) + # Load TOML file + with open(file_path, 'rb') as file: + data = tomllib.load(file) def process_dict(obj, current_prefix=""): for key, value in obj.items(): @@ -54,18 +57,18 @@ def find_duplicate_keys(file_path, keys=None, prefix=""): return duplicates -# Maximum size for JSON files (e.g., 500 KB) +# Maximum size for TOML files (e.g., 500 KB) MAX_FILE_SIZE = 500 * 1024 -def parse_json_file(file_path): +def parse_toml_file(file_path): """ - Parses a JSON translation file and returns a flat dictionary of all keys. - :param file_path: Path to the JSON file. + Parses a TOML translation file and returns a flat dictionary of all keys. + :param file_path: Path to the TOML file. :return: Dictionary with flattened keys. """ - with open(file_path, "r", encoding="utf-8") as file: - data = json.load(file) + with open(file_path, 'rb') as file: + data = tomllib.load(file) def flatten_dict(d, parent_key="", sep="."): items = {} @@ -99,38 +102,37 @@ def unflatten_dict(d, sep="."): return result -def write_json_file(file_path, updated_properties): +def write_toml_file(file_path, updated_properties): """ - Writes updated properties back to the JSON file. - :param file_path: Path to the JSON file. + Writes updated properties back to the TOML file. + :param file_path: Path to the TOML 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 + with open(file_path, "wb") as file: + tomli_w.dump(nested_data, file) 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 reference_file: Path to the reference TOML 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) + reference_properties = parse_toml_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 file_path.endswith(".toml") or not os.path.dirname(file_path).endswith("locales") ): continue - current_properties = parse_json_file(os.path.join(branch, file_path)) + current_properties = parse_toml_file(os.path.join(branch, file_path)) updated_properties = {} for ref_key, ref_value in reference_properties.items(): @@ -141,16 +143,16 @@ def update_missing_keys(reference_file, file_list, branch=""): # Add missing key with reference value updated_properties[ref_key] = ref_value - write_json_file(os.path.join(branch, file_path), updated_properties) + write_toml_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): +def read_toml_keys(file_path): if os.path.isfile(file_path) and os.path.exists(file_path): - return parse_json_file(file_path) + return parse_toml_file(file_path) return {} @@ -160,7 +162,7 @@ def check_for_differences(reference_file, file_list, branch, actor): report = [] report.append(f"#### šŸ”„ Reference Branch: `{reference_branch}`") - reference_keys = read_json_keys(reference_file) + reference_keys = read_toml_keys(reference_file) has_differences = False only_reference_file = True @@ -197,12 +199,12 @@ def check_for_differences(reference_file, file_list, branch, actor): ): continue - if not file_normpath.endswith(".json") or basename_current_file != "translation.json": + if not file_normpath.endswith(".toml") or basename_current_file != "translation.toml": 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)) + current_keys = read_toml_keys(os.path.join(branch, file_path)) reference_key_count = len(reference_keys) current_key_count = len(current_keys) @@ -272,7 +274,7 @@ def check_for_differences(reference_file, file_list, branch, actor): 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)" + f"@{actor} please check your translation if it conforms to the standard. Follow the format of [en-GB/translation.toml](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/frontend/public/locales/en-GB/translation.toml)" ) else: report.append("## āœ… Overall Check Status: **_Success_**") @@ -286,7 +288,7 @@ def check_for_differences(reference_file, file_list, branch, actor): if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Find missing keys") + parser = argparse.ArgumentParser(description="Find missing keys in TOML translation files") parser.add_argument( "--actor", required=False, @@ -337,9 +339,9 @@ if __name__ == "__main__": "public", "locales", "*", - "translation.json", + "translation.toml", ) ) update_missing_keys(args.reference_file, file_list) else: - check_for_differences(args.reference_file, file_list, args.branch, args.actor) \ No newline at end of file + check_for_differences(args.reference_file, file_list, args.branch, args.actor) diff --git a/.github/workflows/PR-Demo-Comment-with-react.yml b/.github/workflows/PR-Demo-Comment-with-react.yml index bfde13275f..c7aa66d4eb 100644 --- a/.github/workflows/PR-Demo-Comment-with-react.yml +++ b/.github/workflows/PR-Demo-Comment-with-react.yml @@ -14,6 +14,7 @@ jobs: permissions: issues: write if: | + vars.CI_PROFILE != 'lite' && github.event.issue.pull_request && ( contains(github.event.comment.body, 'prdeploy') || diff --git a/.github/workflows/check_properties.yml b/.github/workflows/check_toml.yml similarity index 77% rename from .github/workflows/check_properties.yml rename to .github/workflows/check_toml.yml index 73232eee91..8fc1a75b8b 100644 --- a/.github/workflows/check_properties.yml +++ b/.github/workflows/check_toml.yml @@ -1,19 +1,14 @@ -name: Check Properties Files on PR +name: Check TOML Translation Files on PR + +# This workflow validates TOML translation files on: pull_request_target: types: [opened, synchronize, reopened] paths: - - "app/core/src/main/resources/messages_*.properties" + - "frontend/public/locales/*/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.event.pull_request.number || github.ref_name || github.ref }} cancel-in-progress: true @@ -73,22 +68,22 @@ jobs: run: | echo "Fetching PR changed files..." echo "Getting list of changed files from PR..." - # Check if PR number exists - if [ -z "${{ steps.get-pr-data.outputs.pr_number }}" ]; then - echo "Error: PR number is empty" - exit 1 - fi - # Get changed files and filter for properties files, handle case where no matches are found - gh pr view ${{ steps.get-pr-data.outputs.pr_number }} --json files -q ".files[].path" | grep -E '^app/core/src/main/resources/messages_[a-zA-Z_]{2}_[a-zA-Z_]{2,7}\.properties$' > changed_files.txt || echo "No matching properties files found in PR" - # Check if any files were found - if [ ! -s changed_files.txt ]; then - echo "No properties files changed in this PR" - echo "Workflow will exit early as no relevant files to check" - exit 0 - fi - echo "Found $(wc -l < changed_files.txt) matching properties files" + # Check if PR number exists + if [ -z "${{ steps.get-pr-data.outputs.pr_number }}" ]; then + echo "Error: PR number is empty" + exit 1 + fi + # Get changed files and filter for TOML translation files + gh pr view ${{ steps.get-pr-data.outputs.pr_number }} --json files -q ".files[].path" | grep -E '^frontend/public/locales/[a-zA-Z-]+/translation\.toml$' > changed_files.txt || echo "No matching TOML files found in PR" + # Check if any files were found + if [ ! -s changed_files.txt ]; then + echo "No TOML translation files changed in this PR" + echo "Workflow will exit early as no relevant files to check" + exit 0 + fi + echo "Found $(wc -l < changed_files.txt) matching TOML files" - - name: Determine reference file test + - name: Determine reference file id: determine-file uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: @@ -125,11 +120,11 @@ jobs: pull_number: prNumber, }); - // Filter for relevant files based on the PR changes + // Filter for relevant TOML files based on the PR changes const changedFiles = files .filter(file => file.status !== "removed" && - /^app\/core\/src\/main\/resources\/messages_[a-zA-Z_]{2}_[a-zA-Z_]{2,7}\.properties$/.test(file.filename) + /^frontend\/public\/locales\/[a-zA-Z-]+\/translation\.toml$/.test(file.filename) ) .map(file => file.filename); @@ -169,16 +164,16 @@ jobs: // Determine reference file let referenceFilePath; - if (changedFiles.includes("app/core/src/main/resources/messages_en_GB.properties")) { + if (changedFiles.includes("frontend/public/locales/en-GB/translation.toml")) { console.log("Using PR branch reference file."); const { data: fileContent } = await github.rest.repos.getContent({ owner: prRepoOwner, repo: prRepoName, - path: "app/core/src/main/resources/messages_en_GB.properties", + path: "frontend/public/locales/en-GB/translation.toml", ref: branch, }); - referenceFilePath = "pr-branch-messages_en_GB.properties"; + referenceFilePath = "pr-branch-translation-en-GB.toml"; const content = Buffer.from(fileContent.content, "base64").toString("utf-8"); fs.writeFileSync(referenceFilePath, content); } else { @@ -186,11 +181,11 @@ jobs: const { data: fileContent } = await github.rest.repos.getContent({ owner: repoOwner, repo: repoName, - path: "app/core/src/main/resources/messages_en_GB.properties", + path: "frontend/public/locales/en-GB/translation.toml", ref: "main", }); - referenceFilePath = "main-branch-messages_en_GB.properties"; + referenceFilePath = "main-branch-translation-en-GB.toml"; const content = Buffer.from(fileContent.content, "base64").toString("utf-8"); fs.writeFileSync(referenceFilePath, content); } @@ -198,11 +193,20 @@ jobs: console.log(`Reference file path: ${referenceFilePath}`); core.exportVariable("REFERENCE_FILE", referenceFilePath); + - name: Set up Python + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + with: + python-version: "3.12" + + - name: Install Python dependencies + run: | + pip install tomli-w + - name: Run Python script to check files id: run-check run: | - echo "Running Python script to check files..." - python .github/scripts/check_language_properties.py \ + echo "Running Python script to check TOML files..." + python .github/scripts/check_language_toml.py \ --actor ${{ github.event.pull_request.user.login }} \ --reference-file "${REFERENCE_FILE}" \ --branch "pr-branch" \ @@ -213,7 +217,7 @@ jobs: id: capture-output run: | if [ -f result.txt ] && [ -s result.txt ]; then - echo "Test, capturing output..." + echo "Capturing output..." SCRIPT_OUTPUT=$(cat result.txt) echo "SCRIPT_OUTPUT<> $GITHUB_ENV echo "$SCRIPT_OUTPUT" >> $GITHUB_ENV @@ -227,7 +231,7 @@ jobs: echo "FAIL_JOB=false" >> $GITHUB_ENV fi else - echo "No update found." + echo "No output found." echo "SCRIPT_OUTPUT=" >> $GITHUB_ENV echo "FAIL_JOB=false" >> $GITHUB_ENV fi @@ -249,7 +253,7 @@ jobs: issue_number: issueNumber }); - const comment = comments.data.find(c => c.body.includes("## šŸš€ Translation Verification Summary")); + const comment = comments.data.find(c => c.body.includes("## 🌐 TOML Translation Verification Summary")); // Only update or create comments by the action user const expectedActor = "${{ steps.setup-bot.outputs.app-slug }}[bot]"; @@ -260,7 +264,7 @@ jobs: owner: repoOwner, repo: repoName, comment_id: comment.id, - body: `## šŸš€ Translation Verification Summary\n\n\n${SCRIPT_OUTPUT}\n` + body: `## 🌐 TOML Translation Verification Summary\n\n\n${SCRIPT_OUTPUT}\n` }); console.log("Updated existing comment."); } else if (!comment) { @@ -269,7 +273,7 @@ jobs: owner: repoOwner, repo: repoName, issue_number: issueNumber, - body: `## šŸš€ Translation Verification Summary\n\n\n${SCRIPT_OUTPUT}\n` + body: `## 🌐 TOML Translation Verification Summary\n\n\n${SCRIPT_OUTPUT}\n` }); console.log("Created new comment."); } else { @@ -287,6 +291,6 @@ jobs: run: | echo "Cleaning up temporary files..." rm -rf pr-branch - rm -f pr-branch-messages_en_GB.properties main-branch-messages_en_GB.properties changed_files.txt result.txt + rm -f pr-branch-translation-en-GB.toml main-branch-translation-en-GB.toml changed_files.txt result.txt echo "Cleanup complete." continue-on-error: true # Ensure cleanup runs even if previous steps fail diff --git a/.github/workflows/multiOSReleases.yml b/.github/workflows/multiOSReleases.yml index 636a34bc09..07c03b0830 100644 --- a/.github/workflows/multiOSReleases.yml +++ b/.github/workflows/multiOSReleases.yml @@ -31,6 +31,7 @@ permissions: jobs: determine-matrix: + if: ${{ vars.CI_PROFILE != 'lite' }} runs-on: ubuntu-latest outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} diff --git a/.github/workflows/push-docker-v2.yml b/.github/workflows/push-docker-v2.yml index 5f2b70f50d..061cf40edb 100644 --- a/.github/workflows/push-docker-v2.yml +++ b/.github/workflows/push-docker-v2.yml @@ -24,6 +24,7 @@ permissions: jobs: push: + if: ${{ vars.CI_PROFILE != 'lite' }} runs-on: ubuntu-24.04-8core permissions: packages: write diff --git a/.github/workflows/push-docker.yml b/.github/workflows/push-docker.yml index 3f2a0c8d07..ecf3fdc954 100644 --- a/.github/workflows/push-docker.yml +++ b/.github/workflows/push-docker.yml @@ -24,6 +24,7 @@ permissions: jobs: push: + if: ${{ vars.CI_PROFILE != 'lite' }} runs-on: ubuntu-latest permissions: packages: write diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index ca037b7c0d..d83accd49c 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -17,6 +17,7 @@ permissions: read-all jobs: analysis: + if: ${{ vars.CI_PROFILE != 'lite' }} name: Scorecard analysis runs-on: ubuntu-latest permissions: diff --git a/.github/workflows/sonarqube.yml b/.github/workflows/sonarqube.yml index 146eb4b394..dd419b310e 100644 --- a/.github/workflows/sonarqube.yml +++ b/.github/workflows/sonarqube.yml @@ -27,6 +27,7 @@ permissions: jobs: sonarqube: + if: ${{ vars.CI_PROFILE != 'lite' }} runs-on: ubuntu-latest steps: - name: Harden Runner diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index c3c0b110ac..c53bb4a4bb 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -10,6 +10,7 @@ permissions: jobs: stale: + if: ${{ vars.CI_PROFILE != 'lite' }} runs-on: ubuntu-latest permissions: issues: write diff --git a/.github/workflows/swagger.yml b/.github/workflows/swagger.yml index 16f0a3088d..6e9cdb4354 100644 --- a/.github/workflows/swagger.yml +++ b/.github/workflows/swagger.yml @@ -23,6 +23,7 @@ permissions: jobs: push: + if: ${{ vars.CI_PROFILE != 'lite' }} runs-on: ubuntu-latest steps: - name: Harden Runner diff --git a/.github/workflows/sync_files.yml b/.github/workflows/sync_files.yml deleted file mode 100644 index 1233ac701a..0000000000 --- a/.github/workflows/sync_files.yml +++ /dev/null @@ -1,122 +0,0 @@ -name: Sync Files - -on: - workflow_dispatch: - push: - branches: - - main - paths: - - "build.gradle" - - "README.md" - - "app/core/src/main/resources/messages_*.properties" - - "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 - env: - # Prevents sdist builds → no tar extraction - PIP_ONLY_BINARY: ":all:" - PIP_DISABLE_PIP_VERSION_CHECK: "1" - steps: - - name: Harden Runner - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 - with: - egress-policy: audit - - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.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@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 - with: - python-version: "3.12" - cache: "pip" # caching pip dependencies - - - name: Sync translation property files - run: | - python .github/scripts/check_language_properties.py --reference-file "app/core/src/main/resources/messages_en_GB.properties" --branch main - - - name: Commit translation files - run: | - git add app/core/src/main/resources/messages_*.properties - git diff --staged --quiet || git commit -m ":memo: Sync translation files" || echo "No changes detected" - - - name: Install dependencies - # Wheels-only + Hash-Pinning - run: | - pip install --require-hashes --only-binary=:all: -r ./.github/scripts/requirements_sync_readme.txt - - - name: Sync README.md - run: | - python scripts/counter_translation.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 - title: ":globe_with_meridians: Sync Translations + Update README Progress Table" - body: | - ### Description of Changes - - This Pull Request was automatically generated to synchronize updates to translation files and documentation. Below are the details of the changes made: - - #### **1. Synchronization of Translation Files** - - Updated translation files (`messages_*.properties`) to reflect changes in the reference file `messages_en_GB.properties`. - - 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 - app/core/src/main/resources/messages_*.properties diff --git a/.github/workflows/sync_files_v2.yml b/.github/workflows/sync_files_v2.yml index 84645c59e9..935252be25 100644 --- a/.github/workflows/sync_files_v2.yml +++ b/.github/workflows/sync_files_v2.yml @@ -1,15 +1,15 @@ -name: Sync Files V2 +name: Sync Files (TOML) on: workflow_dispatch: push: branches: - - V2 + - main - syncLangTest paths: - "build.gradle" - "README.md" - - "frontend/public/locales/*/translation.json" + - "frontend/public/locales/*/translation.toml" - "app/core/src/main/resources/static/3rdPartyLicenses.json" - "scripts/ignore_translation.toml" @@ -52,21 +52,25 @@ jobs: python-version: "3.12" cache: "pip" # caching pip dependencies - - name: Sync translation JSON files + - name: Install Python dependencies run: | - python .github/scripts/check_language_json.py --reference-file "frontend/public/locales/en-GB/translation.json" --branch V2 + pip install tomli-w + + - name: Sync translation TOML files + run: | + python .github/scripts/check_language_toml.py --reference-file "frontend/public/locales/en-GB/translation.toml" --branch main - 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" + git add frontend/public/locales/*/translation.toml + git diff --staged --quiet || git commit -m ":memo: Sync translation files (TOML)" || echo "No changes detected" - - name: Install dependencies + - name: Install README dependencies run: pip install --require-hashes -r ./.github/scripts/requirements_sync_readme.txt - name: Sync README.md run: | - python scripts/counter_translation_v2.py + python scripts/counter_translation_v3.py - name: Run git add run: | @@ -82,21 +86,22 @@ jobs: 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" + branch: sync_readme_v3 + base: main + title: ":globe_with_meridians: 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: + This Pull Request was automatically generated to synchronize updates to translation files and documentation. 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`. + - Updated translation files (`frontend/public/locales/*/translation.toml`) to reflect changes in the reference file `en-GB/translation.toml`. - Ensured consistency and synchronization across all supported language files. - Highlighted any missing or incomplete translations. + - **Format**: TOML #### **2. Update README.md** - - Generated the translation progress table in `README.md`. + - Generated the translation progress table in `README.md` using `counter_translation_v3.py`. - Added a summary of the current translation status for all supported languages. - Included up-to-date statistics on translation coverage. @@ -115,4 +120,5 @@ jobs: sign-commits: true add-paths: | README.md - frontend/public/locales/*/translation.json \ No newline at end of file + frontend/public/locales/*/translation.toml + scripts/ignore_translation.toml \ No newline at end of file diff --git a/.github/workflows/tauri-build.yml b/.github/workflows/tauri-build.yml index 4e153d5198..d286839505 100644 --- a/.github/workflows/tauri-build.yml +++ b/.github/workflows/tauri-build.yml @@ -28,6 +28,7 @@ permissions: jobs: determine-matrix: + if: ${{ vars.CI_PROFILE != 'lite' }} runs-on: ubuntu-latest outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} @@ -636,6 +637,8 @@ jobs: if [ "${{ needs.build.result }}" = "success" ]; then echo "āœ… All Tauri builds completed successfully!" echo "Artifacts are ready for distribution." + elif [ "${{ needs.build.result }}" = "skipped" ]; then + echo "ā­ļø Tauri builds skipped (CI lite mode enabled)" else echo "āŒ Some Tauri builds failed." echo "Please check the logs and fix any issues." diff --git a/.github/workflows/testdriver.yml b/.github/workflows/testdriver.yml index 828c84d621..12d5bc48d0 100644 --- a/.github/workflows/testdriver.yml +++ b/.github/workflows/testdriver.yml @@ -21,6 +21,7 @@ permissions: jobs: deploy: + if: ${{ vars.CI_PROFILE != 'lite' }} runs-on: ubuntu-latest steps: - name: Harden Runner diff --git a/ADDING_TOOLS.md b/ADDING_TOOLS.md index ef1501bfc9..d24641b000 100644 --- a/ADDING_TOOLS.md +++ b/ADDING_TOOLS.md @@ -202,10 +202,10 @@ const [ToolName] = (props: BaseToolProps) => { ## 5. Add Translations Update translation files. **Important: Only update `en-GB` files** - other languages are handled separately. -**File to update:** `frontend/public/locales/en-GB/translation.json` +**File to update:** `frontend/public/locales/en-GB/translation.toml` **Required Translation Keys**: -```json +```toml { "home": { "[toolName]": { @@ -251,7 +251,7 @@ Update translation files. **Important: Only update `en-GB` files** - other langu ``` **Translation Notes:** -- **Only update `en-GB/translation.json`** - other locale files are managed separately +- **Only update `en-GB/translation.toml`** - other locale files are managed separately - Use descriptive keys that match your component's `t()` calls - Include tooltip translations if you created tooltip hooks - Add `options.*` keys if your tool has settings with descriptions diff --git a/app/common/src/main/java/stirling/software/common/model/ApplicationProperties.java b/app/common/src/main/java/stirling/software/common/model/ApplicationProperties.java index 6a6ee8453b..35ddddeaac 100644 --- a/app/common/src/main/java/stirling/software/common/model/ApplicationProperties.java +++ b/app/common/src/main/java/stirling/software/common/model/ApplicationProperties.java @@ -112,7 +112,6 @@ public class ApplicationProperties { @Data public static class Security { private Boolean enableLogin; - private Boolean csrfDisabled; private InitialLogin initialLogin = new InitialLogin(); private OAUTH2 oauth2 = new OAUTH2(); private SAML2 saml2 = new SAML2(); @@ -575,6 +574,16 @@ public class ApplicationProperties { private String username; @ToString.Exclude private String password; private String from; + // STARTTLS upgrades a plain SMTP connection to TLS after connecting (RFC 3207) + private Boolean startTlsEnable = true; + private Boolean startTlsRequired; + // SSL/TLS wrapper for implicit TLS (typically port 465) + private Boolean sslEnable; + // Hostnames or patterns (e.g., "smtp.example.com" or "*") to trust for TLS certificates; + // defaults to "*" (trust all) when not set + private String sslTrust; + // Enables hostname verification for TLS connections + private Boolean sslCheckServerIdentity; } @Data diff --git a/app/common/src/main/java/stirling/software/common/service/PostHogService.java b/app/common/src/main/java/stirling/software/common/service/PostHogService.java index 310fc43ab2..786c04a437 100644 --- a/app/common/src/main/java/stirling/software/common/service/PostHogService.java +++ b/app/common/src/main/java/stirling/software/common/service/PostHogService.java @@ -254,10 +254,7 @@ public class PostHogService { properties, "security_enableLogin", applicationProperties.getSecurity().getEnableLogin()); - addIfNotEmpty( - properties, - "security_csrfDisabled", - applicationProperties.getSecurity().getCsrfDisabled()); + addIfNotEmpty(properties, "security_csrfDisabled", true); addIfNotEmpty( properties, "security_loginAttemptCount", diff --git a/app/common/src/main/java/stirling/software/common/util/RequestUriUtils.java b/app/common/src/main/java/stirling/software/common/util/RequestUriUtils.java index a0adec7de2..f1763e4313 100644 --- a/app/common/src/main/java/stirling/software/common/util/RequestUriUtils.java +++ b/app/common/src/main/java/stirling/software/common/util/RequestUriUtils.java @@ -162,10 +162,9 @@ public class RequestUriUtils { // enableLogin) || trimmedUri.startsWith( "/api/v1/ui-data/footer-info") // Public footer configuration - || trimmedUri.startsWith("/v1/api-docs") || trimmedUri.startsWith("/api/v1/invite/validate") || trimmedUri.startsWith("/api/v1/invite/accept") - || trimmedUri.contains("/v1/api-docs"); + || trimmedUri.startsWith("/v1/api-docs"); } private static String stripContextPath(String contextPath, String requestURI) { diff --git a/app/core/src/main/java/stirling/software/SPDF/config/InitialSetup.java b/app/core/src/main/java/stirling/software/SPDF/config/InitialSetup.java index 0a63a6f486..88755f9504 100644 --- a/app/core/src/main/java/stirling/software/SPDF/config/InitialSetup.java +++ b/app/core/src/main/java/stirling/software/SPDF/config/InitialSetup.java @@ -34,7 +34,6 @@ public class InitialSetup { public void init() throws IOException { initUUIDKey(); initSecretKey(); - initEnableCSRFSecurity(); initLegalUrls(); initSetAppVersion(); GeneralUtils.extractPipeline(); @@ -60,18 +59,6 @@ public class InitialSetup { } } - public void initEnableCSRFSecurity() throws IOException { - if (GeneralUtils.isVersionHigher( - "0.46.0", applicationProperties.getAutomaticallyGenerated().getAppVersion())) { - Boolean csrf = applicationProperties.getSecurity().getCsrfDisabled(); - if (!csrf) { - GeneralUtils.saveKeyToSettings("security.csrfDisabled", false); - GeneralUtils.saveKeyToSettings("system.enableAnalytics", true); - applicationProperties.getSecurity().setCsrfDisabled(false); - } - } - } - public void initLegalUrls() throws IOException { // Initialize Terms and Conditions String termsUrl = applicationProperties.getLegal().getTermsAndConditions(); @@ -95,7 +82,7 @@ public class InitialSetup { isNewServer = existingVersion == null || existingVersion.isEmpty() - || existingVersion.equals("0.0.0"); + || "0.0.0".equals(existingVersion); String appVersion = "0.0.0"; Resource resource = new ClassPathResource("version.properties"); diff --git a/app/core/src/main/java/stirling/software/SPDF/config/OpenApiConfig.java b/app/core/src/main/java/stirling/software/SPDF/config/OpenApiConfig.java index a25287c4d6..514b9231c6 100644 --- a/app/core/src/main/java/stirling/software/SPDF/config/OpenApiConfig.java +++ b/app/core/src/main/java/stirling/software/SPDF/config/OpenApiConfig.java @@ -62,10 +62,14 @@ public class OpenApiConfig { // Add server configuration from environment variable String swaggerServerUrl = System.getenv("SWAGGER_SERVER_URL"); + Server server; if (swaggerServerUrl != null && !swaggerServerUrl.trim().isEmpty()) { - Server server = new Server().url(swaggerServerUrl).description("API Server"); - openAPI.addServersItem(server); + server = new Server().url(swaggerServerUrl).description("API Server"); + } else { + // Use relative path so Swagger uses the current browser origin to avoid CORS issues when accessing via different ports + server = new Server().url("/").description("Current Server"); } + openAPI.addServersItem(server); // Add ErrorResponse schema to components Schema errorResponseSchema = diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/SettingsController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/SettingsController.java index 9657d8f150..1d9f63818b 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/SettingsController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/SettingsController.java @@ -124,7 +124,6 @@ public class SettingsController { ApplicationProperties.Security security = applicationProperties.getSecurity(); settings.put("enableLogin", security.getEnableLogin()); - settings.put("csrfDisabled", security.getCsrfDisabled()); settings.put("loginMethod", security.getLoginMethod()); settings.put("loginAttemptCount", security.getLoginAttemptCount()); settings.put("loginResetTimeMinutes", security.getLoginResetTimeMinutes()); @@ -159,12 +158,6 @@ public class SettingsController { .getSecurity() .setEnableLogin((Boolean) settings.get("enableLogin")); } - if (settings.containsKey("csrfDisabled")) { - GeneralUtils.saveKeyToSettings("security.csrfDisabled", settings.get("csrfDisabled")); - applicationProperties - .getSecurity() - .setCsrfDisabled((Boolean) settings.get("csrfDisabled")); - } if (settings.containsKey("loginMethod")) { GeneralUtils.saveKeyToSettings("security.loginMethod", settings.get("loginMethod")); applicationProperties diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPdfJsonController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPdfJsonController.java similarity index 99% rename from app/proprietary/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPdfJsonController.java rename to app/core/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPdfJsonController.java index ef29c72e5b..3b9f98c8cd 100644 --- a/app/proprietary/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPdfJsonController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPdfJsonController.java @@ -31,12 +31,10 @@ import stirling.software.common.model.api.PDFFile; import stirling.software.common.service.JobOwnershipService; import stirling.software.common.util.ExceptionUtils; import stirling.software.common.util.WebResponseUtils; -import stirling.software.proprietary.security.config.PremiumEndpoint; @Slf4j @ConvertApi @RequiredArgsConstructor -@PremiumEndpoint public class ConvertPdfJsonController { private final PdfJsonConversionService pdfJsonConversionService; diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/ConfigController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/ConfigController.java index 4f31b83ef9..43aaecd9d7 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/ConfigController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/ConfigController.java @@ -74,6 +74,7 @@ public class ConfigController { configData.put("appNameNavbar", applicationProperties.getUi().getAppNameNavbar()); configData.put("languages", applicationProperties.getUi().getLanguages()); configData.put("logoStyle", applicationProperties.getUi().getLogoStyle()); + configData.put("defaultLocale", applicationProperties.getSystem().getDefaultLocale()); // Security settings // enableLogin requires both the config flag AND proprietary features to be loaded diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/web/ReactRoutingController.java b/app/core/src/main/java/stirling/software/SPDF/controller/web/ReactRoutingController.java index db0d95a36c..6373e07520 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/web/ReactRoutingController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/web/ReactRoutingController.java @@ -11,6 +11,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; +import jakarta.annotation.PostConstruct; import jakarta.servlet.http.HttpServletRequest; @Controller @@ -19,9 +20,25 @@ public class ReactRoutingController { @Value("${server.servlet.context-path:/}") private String contextPath; - @GetMapping(value = {"/", "/index.html"}, produces = MediaType.TEXT_HTML_VALUE) - public ResponseEntity serveIndexHtml(HttpServletRequest request) - throws IOException { + private String cachedIndexHtml; + private boolean indexHtmlExists = false; + + @PostConstruct + public void init() { + // Only cache if index.html exists (production builds) + ClassPathResource resource = new ClassPathResource("static/index.html"); + if (resource.exists()) { + try { + this.cachedIndexHtml = processIndexHtml(); + this.indexHtmlExists = true; + } catch (IOException e) { + // Failed to cache, will process on each request + this.indexHtmlExists = false; + } + } + } + + private String processIndexHtml() throws IOException { ClassPathResource resource = new ClassPathResource("static/index.html"); try (InputStream inputStream = resource.getInputStream()) { @@ -41,14 +58,24 @@ public class ReactRoutingController { ""; html = html.replace("", contextPathScript + ""); - return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(html); + return html; } } + @GetMapping( + value = {"/", "/index.html"}, + produces = MediaType.TEXT_HTML_VALUE) + public ResponseEntity serveIndexHtml(HttpServletRequest request) throws IOException { + if (indexHtmlExists && cachedIndexHtml != null) { + return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(cachedIndexHtml); + } + // Fallback: process on each request (dev mode or cache failed) + return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(processIndexHtml()); + } + @GetMapping( "/{path:^(?!api|static|robots\\.txt|favicon\\.ico|manifest.*\\.json|pipeline|pdfjs|pdfjs-legacy|fonts|images|files|css|js|assets|locales|modern-logo|classic-logo|Login|og_images|samples)[^\\.]*$}") - public ResponseEntity forwardRootPaths(HttpServletRequest request) - throws IOException { + public ResponseEntity forwardRootPaths(HttpServletRequest request) throws IOException { return serveIndexHtml(request); } diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/model/api/PdfJsonConversionProgress.java b/app/core/src/main/java/stirling/software/SPDF/model/api/PdfJsonConversionProgress.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/model/api/PdfJsonConversionProgress.java rename to app/core/src/main/java/stirling/software/SPDF/model/api/PdfJsonConversionProgress.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonAnnotation.java b/app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonAnnotation.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonAnnotation.java rename to app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonAnnotation.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonCosValue.java b/app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonCosValue.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonCosValue.java rename to app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonCosValue.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonDocument.java b/app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonDocument.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonDocument.java rename to app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonDocument.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonDocumentMetadata.java b/app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonDocumentMetadata.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonDocumentMetadata.java rename to app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonDocumentMetadata.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonFont.java b/app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonFont.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonFont.java rename to app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonFont.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonFontCidSystemInfo.java b/app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonFontCidSystemInfo.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonFontCidSystemInfo.java rename to app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonFontCidSystemInfo.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonFontConversionCandidate.java b/app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonFontConversionCandidate.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonFontConversionCandidate.java rename to app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonFontConversionCandidate.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonFontConversionStatus.java b/app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonFontConversionStatus.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonFontConversionStatus.java rename to app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonFontConversionStatus.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonFontType3Glyph.java b/app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonFontType3Glyph.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonFontType3Glyph.java rename to app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonFontType3Glyph.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonFormField.java b/app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonFormField.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonFormField.java rename to app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonFormField.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonImageElement.java b/app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonImageElement.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonImageElement.java rename to app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonImageElement.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonMetadata.java b/app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonMetadata.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonMetadata.java rename to app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonMetadata.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonPage.java b/app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonPage.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonPage.java rename to app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonPage.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonPageDimension.java b/app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonPageDimension.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonPageDimension.java rename to app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonPageDimension.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonStream.java b/app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonStream.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonStream.java rename to app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonStream.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonTextColor.java b/app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonTextColor.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonTextColor.java rename to app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonTextColor.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonTextElement.java b/app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonTextElement.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/model/json/PdfJsonTextElement.java rename to app/core/src/main/java/stirling/software/SPDF/model/json/PdfJsonTextElement.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/PdfJsonConversionService.java b/app/core/src/main/java/stirling/software/SPDF/service/PdfJsonConversionService.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/PdfJsonConversionService.java rename to app/core/src/main/java/stirling/software/SPDF/service/PdfJsonConversionService.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/PdfJsonCosMapper.java b/app/core/src/main/java/stirling/software/SPDF/service/PdfJsonCosMapper.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/PdfJsonCosMapper.java rename to app/core/src/main/java/stirling/software/SPDF/service/PdfJsonCosMapper.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/PdfJsonFallbackFontService.java b/app/core/src/main/java/stirling/software/SPDF/service/PdfJsonFallbackFontService.java similarity index 88% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/PdfJsonFallbackFontService.java rename to app/core/src/main/java/stirling/software/SPDF/service/PdfJsonFallbackFontService.java index 4cf0fc8a11..107abbe2b2 100644 --- a/app/proprietary/src/main/java/stirling/software/SPDF/service/PdfJsonFallbackFontService.java +++ b/app/core/src/main/java/stirling/software/SPDF/service/PdfJsonFallbackFontService.java @@ -33,8 +33,12 @@ public class PdfJsonFallbackFontService { public static final String FALLBACK_FONT_CJK_ID = "fallback-noto-cjk"; public static final String FALLBACK_FONT_JP_ID = "fallback-noto-jp"; public static final String FALLBACK_FONT_KR_ID = "fallback-noto-korean"; + public static final String FALLBACK_FONT_TC_ID = "fallback-noto-tc"; public static final String FALLBACK_FONT_AR_ID = "fallback-noto-arabic"; public static final String FALLBACK_FONT_TH_ID = "fallback-noto-thai"; + public static final String FALLBACK_FONT_DEVANAGARI_ID = "fallback-noto-devanagari"; + public static final String FALLBACK_FONT_MALAYALAM_ID = "fallback-noto-malayalam"; + public static final String FALLBACK_FONT_TIBETAN_ID = "fallback-noto-tibetan"; // Font name aliases map PDF font names to available fallback fonts // This provides better visual consistency when editing PDFs @@ -59,6 +63,22 @@ public class PdfJsonFallbackFontService { Map.entry("dejavuserif", "fallback-dejavu-serif"), Map.entry("dejavumono", "fallback-dejavu-mono"), Map.entry("dejavusansmono", "fallback-dejavu-mono"), + // Traditional Chinese fonts (Taiwan, Hong Kong, Macau) + Map.entry("mingliu", "fallback-noto-tc"), + Map.entry("pmingliu", "fallback-noto-tc"), + Map.entry("microsoftjhenghei", "fallback-noto-tc"), + Map.entry("jhenghei", "fallback-noto-tc"), + Map.entry("kaiti", "fallback-noto-tc"), + Map.entry("kaiu", "fallback-noto-tc"), + Map.entry("dfkaib5", "fallback-noto-tc"), + Map.entry("dfkai", "fallback-noto-tc"), + // Simplified Chinese fonts (Mainland China) - more common + Map.entry("simsun", "fallback-noto-cjk"), + Map.entry("simhei", "fallback-noto-cjk"), + Map.entry("microsoftyahei", "fallback-noto-cjk"), + Map.entry("yahei", "fallback-noto-cjk"), + Map.entry("songti", "fallback-noto-cjk"), + Map.entry("heiti", "fallback-noto-cjk"), // Noto Sans - Google's universal font (use as last resort generic fallback) Map.entry("noto", "fallback-noto-sans"), Map.entry("notosans", "fallback-noto-sans")); @@ -83,6 +103,12 @@ public class PdfJsonFallbackFontService { "classpath:/static/fonts/NotoSansKR-Regular.ttf", "NotoSansKR-Regular", "ttf")), + Map.entry( + FALLBACK_FONT_TC_ID, + new FallbackFontSpec( + "classpath:/static/fonts/NotoSansTC-Regular.ttf", + "NotoSansTC-Regular", + "ttf")), Map.entry( FALLBACK_FONT_AR_ID, new FallbackFontSpec( @@ -95,6 +121,24 @@ public class PdfJsonFallbackFontService { "classpath:/static/fonts/NotoSansThai-Regular.ttf", "NotoSansThai-Regular", "ttf")), + Map.entry( + FALLBACK_FONT_DEVANAGARI_ID, + new FallbackFontSpec( + "classpath:/static/fonts/NotoSansDevanagari-Regular.ttf", + "NotoSansDevanagari-Regular", + "ttf")), + Map.entry( + FALLBACK_FONT_MALAYALAM_ID, + new FallbackFontSpec( + "classpath:/static/fonts/NotoSansMalayalam-Regular.ttf", + "NotoSansMalayalam-Regular", + "ttf")), + Map.entry( + FALLBACK_FONT_TIBETAN_ID, + new FallbackFontSpec( + "classpath:/static/fonts/NotoSerifTibetan-Regular.ttf", + "NotoSerifTibetan-Regular", + "ttf")), // Liberation Sans family Map.entry( "fallback-liberation-sans", @@ -484,6 +528,20 @@ public class PdfJsonFallbackFontService { */ public String resolveFallbackFontId(int codePoint) { Character.UnicodeBlock block = Character.UnicodeBlock.of(codePoint); + + // Bopomofo is primarily used in Taiwan for Traditional Chinese phonetic annotation + if (block == Character.UnicodeBlock.BOPOMOFO + || block == Character.UnicodeBlock.BOPOMOFO_EXTENDED) { + return FALLBACK_FONT_TC_ID; + } + + // Compatibility ideographs are primarily used by Traditional Chinese encodings (e.g., Big5, + // HKSCS) so prefer the Traditional Chinese fallback here. + if (block == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS + || block == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT) { + return FALLBACK_FONT_TC_ID; + } + if (block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A || block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B @@ -492,19 +550,23 @@ public class PdfJsonFallbackFontService { || block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_E || block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F || block == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION - || block == Character.UnicodeBlock.BOPOMOFO - || block == Character.UnicodeBlock.BOPOMOFO_EXTENDED || block == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) { return FALLBACK_FONT_CJK_ID; } Character.UnicodeScript script = Character.UnicodeScript.of(codePoint); return switch (script) { + // HAN script is used by both Simplified and Traditional Chinese + // Default to Simplified (mainland China, 1.4B speakers) as it's more common + // Traditional Chinese PDFs are detected via font name aliases (MingLiU, PMingLiU, etc.) case HAN -> FALLBACK_FONT_CJK_ID; case HIRAGANA, KATAKANA -> FALLBACK_FONT_JP_ID; case HANGUL -> FALLBACK_FONT_KR_ID; case ARABIC -> FALLBACK_FONT_AR_ID; case THAI -> FALLBACK_FONT_TH_ID; + case DEVANAGARI -> FALLBACK_FONT_DEVANAGARI_ID; + case MALAYALAM -> FALLBACK_FONT_MALAYALAM_ID; + case TIBETAN -> FALLBACK_FONT_TIBETAN_ID; default -> FALLBACK_FONT_ID; }; } diff --git a/app/core/src/main/java/stirling/software/SPDF/service/SharedSignatureService.java b/app/core/src/main/java/stirling/software/SPDF/service/SharedSignatureService.java index 6c349581db..7043118a46 100644 --- a/app/core/src/main/java/stirling/software/SPDF/service/SharedSignatureService.java +++ b/app/core/src/main/java/stirling/software/SPDF/service/SharedSignatureService.java @@ -179,7 +179,7 @@ public class SharedSignatureService { StandardOpenOption.TRUNCATE_EXISTING); // Store reference to image file - response.setDataUrl("/api/v1/general/sign/" + imageFileName); + response.setDataUrl("/api/v1/general/signatures/" + imageFileName); } log.info("Saved signature {} for user {}", request.getId(), username); @@ -207,7 +207,7 @@ public class SharedSignatureService { sig.setLabel(id); // Use ID as label sig.setType("image"); // Default type sig.setScope("personal"); - sig.setDataUrl("/api/v1/general/sign/" + fileName); + sig.setDataUrl("/api/v1/general/signatures/" + fileName); sig.setCreatedAt( Files.getLastModifiedTime(path).toMillis()); sig.setUpdatedAt( @@ -238,7 +238,7 @@ public class SharedSignatureService { sig.setLabel(id); // Use ID as label sig.setType("image"); // Default type sig.setScope("shared"); - sig.setDataUrl("/api/v1/general/sign/" + fileName); + sig.setDataUrl("/api/v1/general/signatures/" + fileName); sig.setCreatedAt( Files.getLastModifiedTime(path).toMillis()); sig.setUpdatedAt( diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/JobOwnershipServiceImpl.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/JobOwnershipServiceImpl.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/JobOwnershipServiceImpl.java rename to app/core/src/main/java/stirling/software/SPDF/service/pdfjson/JobOwnershipServiceImpl.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/NoOpJobOwnershipService.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/NoOpJobOwnershipService.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/NoOpJobOwnershipService.java rename to app/core/src/main/java/stirling/software/SPDF/service/pdfjson/NoOpJobOwnershipService.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/PdfJsonFontService.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/PdfJsonFontService.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/PdfJsonFontService.java rename to app/core/src/main/java/stirling/software/SPDF/service/pdfjson/PdfJsonFontService.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/PdfJsonImageService.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/PdfJsonImageService.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/PdfJsonImageService.java rename to app/core/src/main/java/stirling/software/SPDF/service/pdfjson/PdfJsonImageService.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/PdfJsonMetadataService.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/PdfJsonMetadataService.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/PdfJsonMetadataService.java rename to app/core/src/main/java/stirling/software/SPDF/service/pdfjson/PdfJsonMetadataService.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/PdfLazyLoadingService.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/PdfLazyLoadingService.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/PdfLazyLoadingService.java rename to app/core/src/main/java/stirling/software/SPDF/service/pdfjson/PdfLazyLoadingService.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3ConversionRequest.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3ConversionRequest.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3ConversionRequest.java rename to app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3ConversionRequest.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3ConversionStrategy.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3ConversionStrategy.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3ConversionStrategy.java rename to app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3ConversionStrategy.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3FontConversionService.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3FontConversionService.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3FontConversionService.java rename to app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3FontConversionService.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3FontSignatureCalculator.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3FontSignatureCalculator.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3FontSignatureCalculator.java rename to app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3FontSignatureCalculator.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3GlyphContext.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3GlyphContext.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3GlyphContext.java rename to app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3GlyphContext.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3GlyphExtractor.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3GlyphExtractor.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3GlyphExtractor.java rename to app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3GlyphExtractor.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3GraphicsEngine.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3GraphicsEngine.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3GraphicsEngine.java rename to app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3GraphicsEngine.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3LibraryStrategy.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3LibraryStrategy.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3LibraryStrategy.java rename to app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/Type3LibraryStrategy.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/library/Type3FontLibrary.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/library/Type3FontLibrary.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/library/Type3FontLibrary.java rename to app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/library/Type3FontLibrary.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/library/Type3FontLibraryEntry.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/library/Type3FontLibraryEntry.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/library/Type3FontLibraryEntry.java rename to app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/library/Type3FontLibraryEntry.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/library/Type3FontLibraryMatch.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/library/Type3FontLibraryMatch.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/library/Type3FontLibraryMatch.java rename to app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/library/Type3FontLibraryMatch.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/library/Type3FontLibraryPayload.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/library/Type3FontLibraryPayload.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/library/Type3FontLibraryPayload.java rename to app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/library/Type3FontLibraryPayload.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/model/Type3GlyphOutline.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/model/Type3GlyphOutline.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/model/Type3GlyphOutline.java rename to app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/model/Type3GlyphOutline.java diff --git a/app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/tool/Type3SignatureTool.java b/app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/tool/Type3SignatureTool.java similarity index 100% rename from app/proprietary/src/main/java/stirling/software/SPDF/service/pdfjson/type3/tool/Type3SignatureTool.java rename to app/core/src/main/java/stirling/software/SPDF/service/pdfjson/type3/tool/Type3SignatureTool.java diff --git a/app/core/src/main/resources/settings.yml.template b/app/core/src/main/resources/settings.yml.template index 5ea28f71e8..5a50ef9035 100644 --- a/app/core/src/main/resources/settings.yml.template +++ b/app/core/src/main/resources/settings.yml.template @@ -12,7 +12,6 @@ security: enableLogin: true # set to 'true' to enable login - csrfDisabled: false # set to 'true' to disable CSRF protection (not recommended for production) loginAttemptCount: 5 # lock user account after 5 tries; when using e.g. Fail2Ban you can deactivate the function with -1 loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts loginMethod: all # Accepts values like 'all' and 'normal'(only Login with Username/Password), 'oauth2'(only Login with OAuth2) or 'saml2'(only Login with SAML2) @@ -105,6 +104,11 @@ mail: username: '' # SMTP server username password: '' # SMTP server password from: '' # sender email address + startTlsEnable: true # enable STARTTLS (explicit TLS upgrade after connecting) when supported by the SMTP server + startTlsRequired: false # require STARTTLS; connection fails if the upgrade command is not supported + sslEnable: false # enable SSL/TLS wrapper for implicit TLS (typically used with port 465) + sslTrust: '' # optional trusted host override, e.g. "smtp.example.com" or "*"; defaults to "*" (trust all) when empty + sslCheckServerIdentity: false # enable hostname verification when using SSL/TLS legal: termsAndConditions: https://www.stirling.com/legal/terms-of-service # URL to the terms and conditions of your application (e.g. https://example.com/terms). Empty string to disable or filename to load from local file in static folder diff --git a/app/core/src/main/resources/static/fonts/NotoSansDevanagari-Regular.ttf b/app/core/src/main/resources/static/fonts/NotoSansDevanagari-Regular.ttf new file mode 100644 index 0000000000..9a8c4b105e Binary files /dev/null and b/app/core/src/main/resources/static/fonts/NotoSansDevanagari-Regular.ttf differ diff --git a/app/core/src/main/resources/static/fonts/NotoSansMalayalam-Regular.ttf b/app/core/src/main/resources/static/fonts/NotoSansMalayalam-Regular.ttf new file mode 100644 index 0000000000..a5a0fe65ab Binary files /dev/null and b/app/core/src/main/resources/static/fonts/NotoSansMalayalam-Regular.ttf differ diff --git a/app/core/src/main/resources/static/fonts/NotoSansTC-Regular.ttf b/app/core/src/main/resources/static/fonts/NotoSansTC-Regular.ttf new file mode 100644 index 0000000000..75e2b0806a Binary files /dev/null and b/app/core/src/main/resources/static/fonts/NotoSansTC-Regular.ttf differ diff --git a/app/core/src/main/resources/static/fonts/NotoSerifTibetan-Regular.ttf b/app/core/src/main/resources/static/fonts/NotoSerifTibetan-Regular.ttf new file mode 100644 index 0000000000..06b817d5ca Binary files /dev/null and b/app/core/src/main/resources/static/fonts/NotoSerifTibetan-Regular.ttf differ diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/controller/api/ProprietaryUIDataController.java b/app/proprietary/src/main/java/stirling/software/proprietary/controller/api/ProprietaryUIDataController.java index d335eb9c25..88fddb7eaf 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/controller/api/ProprietaryUIDataController.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/controller/api/ProprietaryUIDataController.java @@ -205,6 +205,10 @@ public class ProprietaryUIDataController { data.setLoginMethod(securityProps.getLoginMethod()); data.setAltLogin(!providerList.isEmpty() && securityProps.isAltLogin()); + // Add language configuration for login page + data.setLanguages(applicationProperties.getUi().getLanguages()); + data.setDefaultLocale(applicationProperties.getSystem().getDefaultLocale()); + return ResponseEntity.ok(data); } @@ -328,6 +332,7 @@ public class ProprietaryUIDataController { data.setGrandfatheredUserCount(grandfatheredCount); data.setLicenseMaxUsers(licenseMaxUsers); data.setPremiumEnabled(premiumEnabled); + data.setMailEnabled(applicationProperties.getMail().isEnabled()); return ResponseEntity.ok(data); } @@ -376,7 +381,7 @@ public class ProprietaryUIDataController { data.setUsername(username); data.setRole(user.get().getRolesAsString()); data.setSettings(settingsJson); - data.setChangeCredsFlag(user.get().isFirstLogin()); + data.setChangeCredsFlag(user.get().isFirstLogin() || user.get().isForcePasswordChange()); data.setOAuth2Login(isOAuth2Login); data.setSaml2Login(isSaml2Login); @@ -491,6 +496,8 @@ public class ProprietaryUIDataController { private boolean altLogin; private boolean firstTimeSetup; private boolean showDefaultCredentials; + private List languages; + private String defaultLocale; } @Data @@ -510,6 +517,7 @@ public class ProprietaryUIDataController { private int grandfatheredUserCount; private int licenseMaxUsers; private boolean premiumEnabled; + private boolean mailEnabled; } @Data diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/controller/api/SignatureController.java b/app/proprietary/src/main/java/stirling/software/proprietary/controller/api/SignatureController.java index f42b23a972..a073c21371 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/controller/api/SignatureController.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/controller/api/SignatureController.java @@ -1,7 +1,12 @@ package stirling.software.proprietary.controller.api; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.List; +import java.util.Map; +import java.util.stream.Stream; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -18,6 +23,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import stirling.software.common.annotations.api.UserApi; +import stirling.software.common.configuration.InstallationPathConfig; import stirling.software.proprietary.model.api.signature.SavedSignatureRequest; import stirling.software.proprietary.model.api.signature.SavedSignatureResponse; import stirling.software.proprietary.security.service.UserService; @@ -38,6 +44,7 @@ public class SignatureController { private final SignatureService signatureService; private final UserService userService; + private static final String ALL_USERS_FOLDER = "ALL_USERS"; /** * Save a new signature for the authenticated user. Enforces storage limits and authentication @@ -84,19 +91,105 @@ public class SignatureController { } /** - * Delete a signature owned by the authenticated user. Users can only delete their own personal - * signatures, not shared ones. + * Update a signature label. Users can update labels for their own personal signatures and for + * shared signatures. + */ + @PostMapping("/{signatureId}/label") + @PreAuthorize("!hasAuthority('ROLE_DEMO_USER')") + public ResponseEntity updateSignatureLabel( + @PathVariable String signatureId, @RequestBody Map body) { + try { + String username = userService.getCurrentUsername(); + String newLabel = body.get("label"); + + if (newLabel == null || newLabel.trim().isEmpty()) { + log.warn("Invalid label update request"); + return ResponseEntity.badRequest().build(); + } + + signatureService.updateSignatureLabel(username, signatureId, newLabel); + log.info("User {} updated label for signature {}", username, signatureId); + return ResponseEntity.noContent().build(); + } catch (IOException e) { + log.warn("Failed to update signature label: {}", e.getMessage()); + return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); + } + } + + /** + * Delete a signature owned by the authenticated user. Users can delete their own personal + * signatures. Admins can also delete shared signatures. */ @DeleteMapping("/{signatureId}") + @PreAuthorize("!hasAuthority('ROLE_DEMO_USER')") public ResponseEntity deleteSignature(@PathVariable String signatureId) { try { String username = userService.getCurrentUsername(); - signatureService.deleteSignature(username, signatureId); - log.info("User {} deleted signature {}", username, signatureId); - return ResponseEntity.noContent().build(); + boolean isAdmin = userService.isCurrentUserAdmin(); + + // Validate filename to prevent path traversal + if (signatureId.contains("..") + || signatureId.contains("/") + || signatureId.contains("\\")) { + log.warn("Invalid signature ID: {}", signatureId); + return ResponseEntity.badRequest().build(); + } + + // Try to delete from personal folder first + try { + signatureService.deleteSignature(username, signatureId); + log.info("User {} deleted personal signature {}", username, signatureId); + return ResponseEntity.noContent().build(); + } catch (IOException e) { + // If not found in personal folder, check if it's in shared folder + if (isAdmin) { + // Admin can delete from shared folder + if (deleteFromSharedFolder(signatureId)) { + log.info("Admin {} deleted shared signature {}", username, signatureId); + return ResponseEntity.noContent().build(); + } + } + // If not admin or not found in shared folder either, return 404 + throw e; + } } catch (IOException e) { log.warn("Failed to delete signature {} for user: {}", signatureId, e.getMessage()); return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); } } + + /** + * Delete a signature from the shared (ALL_USERS) folder. Only admins should call this method. + */ + private boolean deleteFromSharedFolder(String signatureId) throws IOException { + String signatureBasePath = InstallationPathConfig.getSignaturesPath(); + Path sharedFolder = Paths.get(signatureBasePath, ALL_USERS_FOLDER); + boolean deleted = false; + + if (Files.exists(sharedFolder)) { + try (Stream stream = Files.list(sharedFolder)) { + List matchingFiles = + stream.filter( + path -> + path.getFileName() + .toString() + .startsWith(signatureId + ".")) + .toList(); + for (Path file : matchingFiles) { + Files.delete(file); + deleted = true; + log.info("Deleted shared signature file: {}", file); + } + } + + // Also delete metadata file if it exists + Path metadataPath = sharedFolder.resolve(signatureId + ".json"); + if (Files.exists(metadataPath)) { + Files.delete(metadataPath); + log.info("Deleted shared signature metadata: {}", metadataPath); + } + } + + return deleted; + } } diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/MailConfig.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/MailConfig.java index 6a565cade3..6538b7ee97 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/MailConfig.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/MailConfig.java @@ -33,7 +33,8 @@ public class MailConfig { // Creates a new instance of JavaMailSenderImpl, which is a Spring implementation JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); - mailSender.setHost(mailProperties.getHost()); + String host = mailProperties.getHost(); + mailSender.setHost(host); mailSender.setPort(mailProperties.getPort()); mailSender.setDefaultEncoding("UTF-8"); @@ -70,8 +71,32 @@ public class MailConfig { log.info("SMTP authentication disabled - no credentials provided"); } + boolean startTlsEnabled = + mailProperties.getStartTlsEnable() == null || mailProperties.getStartTlsEnable(); // Enables STARTTLS to encrypt the connection if supported by the SMTP server - props.put("mail.smtp.starttls.enable", "true"); + props.put("mail.smtp.starttls.enable", Boolean.toString(startTlsEnabled)); + if (mailProperties.getStartTlsRequired() != null) { + props.put( + "mail.smtp.starttls.required", mailProperties.getStartTlsRequired().toString()); + } + + if (mailProperties.getSslEnable() != null) { + props.put("mail.smtp.ssl.enable", mailProperties.getSslEnable().toString()); + } + + // Trust the configured host to allow STARTTLS with self-signed certificates + String sslTrust = mailProperties.getSslTrust(); + if (sslTrust == null || sslTrust.trim().isEmpty()) { + sslTrust = "*"; + } + if (sslTrust != null && !sslTrust.trim().isEmpty()) { + props.put("mail.smtp.ssl.trust", sslTrust); + } + if (mailProperties.getSslCheckServerIdentity() != null) { + props.put( + "mail.smtp.ssl.checkserveridentity", + mailProperties.getSslCheckServerIdentity().toString()); + } // Returns the configured mail sender, ready to send emails return mailSender; diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java index a3a5eee4f0..2a0cd57348 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java @@ -1,7 +1,6 @@ package stirling.software.proprietary.security.configuration; import java.util.List; -import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -25,8 +24,6 @@ import org.springframework.security.saml2.provider.service.web.authentication.Op import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository; -import org.springframework.security.web.csrf.CookieCsrfTokenRepository; -import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; import org.springframework.security.web.savedrequest.NullRequestCache; import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher; import org.springframework.web.cors.CorsConfiguration; @@ -47,7 +44,6 @@ import stirling.software.proprietary.security.database.repository.PersistentLogi import stirling.software.proprietary.security.filter.IPRateLimitingFilter; import stirling.software.proprietary.security.filter.JwtAuthenticationFilter; import stirling.software.proprietary.security.filter.UserAuthenticationFilter; -import stirling.software.proprietary.security.model.User; import stirling.software.proprietary.security.oauth2.CustomOAuth2AuthenticationFailureHandler; import stirling.software.proprietary.security.oauth2.CustomOAuth2AuthenticationSuccessHandler; import stirling.software.proprietary.security.saml2.CustomSaml2AuthenticationFailureHandler; @@ -198,9 +194,7 @@ public class SecurityConfiguration { http.cors(cors -> cors.disable()); } - if (securityProperties.getCsrfDisabled() || !loginEnabledValue) { - http.csrf(CsrfConfigurer::disable); - } + http.csrf(CsrfConfigurer::disable); if (loginEnabledValue) { boolean v2Enabled = appConfig.v2Enabled(); @@ -210,48 +204,6 @@ public class SecurityConfiguration { .addFilterBefore(rateLimitingFilter, UsernamePasswordAuthenticationFilter.class) .addFilterBefore(jwtAuthenticationFilter, UserAuthenticationFilter.class); - if (!securityProperties.getCsrfDisabled()) { - CookieCsrfTokenRepository cookieRepo = - CookieCsrfTokenRepository.withHttpOnlyFalse(); - CsrfTokenRequestAttributeHandler requestHandler = - new CsrfTokenRequestAttributeHandler(); - requestHandler.setCsrfRequestAttributeName(null); - http.csrf( - csrf -> - csrf.ignoringRequestMatchers( - request -> { - String uri = request.getRequestURI(); - - // Ignore CSRF for auth endpoints - if (uri.startsWith("/api/v1/auth/")) { - return true; - } - - String apiKey = request.getHeader("X-API-KEY"); - // If there's no API key, don't ignore CSRF - // (return false) - if (apiKey == null || apiKey.trim().isEmpty()) { - return false; - } - // Validate API key using existing UserService - try { - Optional user = - userService.getUserByApiKey(apiKey); - // If API key is valid, ignore CSRF (return - // true) - // If API key is invalid, don't ignore CSRF - // (return false) - return user.isPresent(); - } catch (Exception e) { - // If there's any error validating the API - // key, don't ignore CSRF - return false; - } - }) - .csrfTokenRepository(cookieRepo) - .csrfTokenRequestHandler(requestHandler)); - } - http.sessionManagement( sessionManagement -> { if (v2Enabled) { @@ -331,7 +283,9 @@ public class SecurityConfiguration { formLogin -> formLogin .loginPage("/login") // Redirect here when unauthenticated - .loginProcessingUrl("/perform_login") // Process form posts here (not /login) + .loginProcessingUrl( + "/perform_login") // Process form posts here (not + // /login) .successHandler( new CustomAuthenticationSuccessHandler( loginAttemptService, diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/UserController.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/UserController.java index f2b3fd5101..6a18aeff06 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/UserController.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/UserController.java @@ -7,6 +7,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.UUID; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -18,6 +19,7 @@ import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; import org.springframework.web.bind.annotation.*; +import jakarta.mail.MessagingException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.transaction.Transactional; @@ -236,6 +238,8 @@ public class UserController { return ResponseEntity.status(HttpStatus.UNAUTHORIZED) .body(Map.of("error", "incorrectPassword", "message", "Incorrect password")); } + // Set flags before changing password so they're saved together + user.setForcePasswordChange(false); userService.changePassword(user, newPassword); userService.changeFirstUse(user, false); // Logout using Spring's utility @@ -584,6 +588,79 @@ public class UserController { return ResponseEntity.ok(Map.of("message", "User role updated successfully")); } + @PreAuthorize("hasRole('ROLE_ADMIN')") + @PostMapping("/admin/changePasswordForUser") + public ResponseEntity changePasswordForUser( + @RequestParam(name = "username") String username, + @RequestParam(name = "newPassword", required = false) String newPassword, + @RequestParam(name = "generateRandom", defaultValue = "false") boolean generateRandom, + @RequestParam(name = "sendEmail", defaultValue = "false") boolean sendEmail, + @RequestParam(name = "includePassword", defaultValue = "false") boolean includePassword, + @RequestParam(name = "forcePasswordChange", defaultValue = "false") + boolean forcePasswordChange, + HttpServletRequest request, + Authentication authentication) + throws SQLException, UnsupportedProviderException, MessagingException { + Optional userOpt = userService.findByUsernameIgnoreCase(username); + if (userOpt.isEmpty()) { + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .body(Map.of("error", "User not found.")); + } + + String currentUsername = authentication.getName(); + if (currentUsername.equalsIgnoreCase(username)) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(Map.of("error", "Cannot change your own password.")); + } + + User user = userOpt.get(); + + String finalPassword = newPassword; + if (generateRandom) { + finalPassword = UUID.randomUUID().toString().replace("-", "").substring(0, 12); + } + + if (finalPassword == null || finalPassword.trim().isEmpty()) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(Map.of("error", "New password is required.")); + } + + // Set force password change flag before changing password so both are saved together + user.setForcePasswordChange(forcePasswordChange); + userService.changePassword(user, finalPassword); + + // Invalidate all active sessions to force reauthentication + userService.invalidateUserSessions(username); + + if (sendEmail) { + if (emailService.isEmpty() || !applicationProperties.getMail().isEnabled()) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(Map.of("error", "Email is not configured.")); + } + + String userEmail = user.getUsername(); + // Check if username is a valid email format + if (userEmail == null || userEmail.isBlank() || !userEmail.contains("@")) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body( + Map.of( + "error", + "User's email is not a valid email address. Notifications are disabled.")); + } + + String loginUrl = buildLoginUrl(request); + emailService + .get() + .sendPasswordChangedNotification( + userEmail, + user.getUsername(), + includePassword ? finalPassword : null, + loginUrl); + } + + return ResponseEntity.ok(Map.of("message", "User password updated successfully")); + } + @PreAuthorize("hasRole('ROLE_ADMIN')") @PostMapping("/admin/changeUserEnabled/{username}") public ResponseEntity changeUserEnabled( diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/JwtAuthenticationFilter.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/JwtAuthenticationFilter.java index b481da51cf..a2c3381f13 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/JwtAuthenticationFilter.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/JwtAuthenticationFilter.java @@ -26,6 +26,7 @@ import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import stirling.software.common.model.ApplicationProperties; @@ -39,6 +40,7 @@ import stirling.software.proprietary.security.service.JwtServiceInterface; import stirling.software.proprietary.security.service.UserService; @Slf4j +@RequiredArgsConstructor public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtServiceInterface jwtService; @@ -47,19 +49,6 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { private final AuthenticationEntryPoint authenticationEntryPoint; private final ApplicationProperties.Security securityProperties; - public JwtAuthenticationFilter( - JwtServiceInterface jwtService, - UserService userService, - CustomUserDetailsService userDetailsService, - AuthenticationEntryPoint authenticationEntryPoint, - ApplicationProperties.Security securityProperties) { - this.jwtService = jwtService; - this.userService = userService; - this.userDetailsService = userDetailsService; - this.authenticationEntryPoint = authenticationEntryPoint; - this.securityProperties = securityProperties; - } - @Override protected void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) @@ -68,7 +57,11 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { filterChain.doFilter(request, response); return; } - if (isStaticResource(request.getContextPath(), request.getRequestURI())) { + + String requestURI = request.getRequestURI(); + String contextPath = request.getContextPath(); + + if (isStaticResource(contextPath, requestURI)) { filterChain.doFilter(request, response); return; } @@ -77,10 +70,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { String jwtToken = jwtService.extractToken(request); if (jwtToken == null) { - // Allow specific auth endpoints to pass through without JWT - String requestURI = request.getRequestURI(); - String contextPath = request.getContextPath(); - + // Allow auth endpoints to pass through without JWT if (!isPublicAuthEndpoint(requestURI, contextPath)) { // For API requests, return 401 JSON String acceptHeader = request.getHeader("Accept"); diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/UserAuthenticationFilter.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/UserAuthenticationFilter.java index fe57b39975..182b4cbfe4 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/UserAuthenticationFilter.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/UserAuthenticationFilter.java @@ -241,24 +241,6 @@ public class UserAuthenticationFilter extends OncePerRequestFilter { filterChain.doFilter(request, response); } - private static boolean isPublicAuthEndpoint(String requestURI, String contextPath) { - // Remove context path from URI to normalize path matching - String trimmedUri = - requestURI.startsWith(contextPath) - ? requestURI.substring(contextPath.length()) - : requestURI; - - // Public auth endpoints that don't require authentication - return trimmedUri.startsWith("/login") - || trimmedUri.startsWith("/auth/") - || trimmedUri.startsWith("/oauth2") - || trimmedUri.startsWith("/saml2") - || trimmedUri.startsWith("/api/v1/auth/login") - || trimmedUri.startsWith("/api/v1/auth/refresh") - || trimmedUri.startsWith("/api/v1/auth/logout") - || trimmedUri.startsWith("/api/v1/proprietary/ui-data/login"); - } - private enum UserLoginType { USERDETAILS("UserDetails"), OAUTH2USER("OAuth2User"), diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/model/User.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/model/User.java index 8207eae28b..c53893a8b4 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/model/User.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/model/User.java @@ -59,6 +59,9 @@ public class User implements UserDetails, Serializable { @Column(name = "hasCompletedInitialSetup") private Boolean hasCompletedInitialSetup = false; + @Column(name = "forcePasswordChange") + private Boolean forcePasswordChange = false; + @Column(name = "roleName") private String roleName; @@ -117,6 +120,14 @@ public class User implements UserDetails, Serializable { this.hasCompletedInitialSetup = hasCompletedInitialSetup; } + public boolean isForcePasswordChange() { + return forcePasswordChange != null && forcePasswordChange; + } + + public void setForcePasswordChange(boolean forcePasswordChange) { + this.forcePasswordChange = forcePasswordChange; + } + public void setAuthenticationType(AuthenticationType authenticationType) { this.authenticationType = authenticationType.toString().toLowerCase(); } diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java index e1e6703945..793c6b62fa 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java @@ -27,6 +27,7 @@ import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import stirling.software.common.model.ApplicationProperties; import stirling.software.common.model.exception.UnsupportedProviderException; @@ -39,6 +40,7 @@ import stirling.software.proprietary.security.service.JwtServiceInterface; import stirling.software.proprietary.security.service.LoginAttemptService; import stirling.software.proprietary.security.service.UserService; +@Slf4j @RequiredArgsConstructor public class CustomOAuth2AuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { @@ -77,12 +79,18 @@ public class CustomOAuth2AuthenticationSuccessHandler if (user != null && !licenseSettingsService.isOAuthEligible(user)) { // User is not grandfathered and no paid license - block OAuth login + log.warn( + "OAuth login blocked for existing user '{}' - not eligible (not grandfathered and no paid license)", + username); response.sendRedirect( request.getContextPath() + "/logout?oAuth2RequiresLicense=true"); return; } } else if (!licenseSettingsService.isOAuthEligible(null)) { // No existing user and no paid license -> block auto creation + log.warn( + "OAuth login blocked for new user '{}' - not eligible (no paid license for auto-creation)", + username); response.sendRedirect(request.getContextPath() + "/logout?oAuth2RequiresLicense=true"); return; } diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/oauth2/OAuth2Configuration.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/oauth2/OAuth2Configuration.java index a053c1ead2..2d5f94620a 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/oauth2/OAuth2Configuration.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/oauth2/OAuth2Configuration.java @@ -67,10 +67,15 @@ public class OAuth2Configuration { keycloakClientRegistration().ifPresent(registrations::add); if (registrations.isEmpty()) { - log.error("No OAuth2 provider registered"); + log.error("No OAuth2 provider registered - check your OAuth2 configuration"); throw new NoProviderFoundException("At least one OAuth2 provider must be configured."); } + log.info( + "OAuth2 ClientRegistrationRepository created with {} provider(s): {}", + registrations.size(), + registrations.stream().map(ClientRegistration::getRegistrationId).toList()); + return new InMemoryClientRegistrationRepository(registrations); } @@ -165,7 +170,6 @@ public class OAuth2Configuration { githubClient.getUseAsUsername()); boolean isValid = validateProvider(github); - log.info("Initialised GitHub OAuth2 provider"); return isValid ? Optional.of( @@ -208,7 +212,19 @@ public class OAuth2Configuration { null, null); - return !isStringEmpty(oidcProvider.getIssuer()) || validateProvider(oidcProvider) + boolean isValid = + !isStringEmpty(oidcProvider.getIssuer()) || validateProvider(oidcProvider); + if (isValid) { + log.info( + "Initialised OIDC OAuth2 provider: registrationId='{}', issuer='{}', redirectUri='{}'", + name, + oauth.getIssuer(), + REDIRECT_URI_PATH + name); + } else { + log.warn("OIDC OAuth2 provider validation failed - provider will not be registered"); + } + + return isValid ? Optional.of( ClientRegistrations.fromIssuerLocation(oauth.getIssuer()) .registrationId(name) @@ -217,7 +233,7 @@ public class OAuth2Configuration { .scope(oidcProvider.getScopes()) .userNameAttributeName(oidcProvider.getUseAsUsername().getName()) .clientName(clientName) - .redirectUri(REDIRECT_URI_PATH + "oidc") + .redirectUri(REDIRECT_URI_PATH + name) .authorizationGrantType(AUTHORIZATION_CODE) .build()) : Optional.empty(); diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/CustomSaml2AuthenticationSuccessHandler.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/CustomSaml2AuthenticationSuccessHandler.java index 0f350d7b4d..e8bce579a0 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/CustomSaml2AuthenticationSuccessHandler.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/CustomSaml2AuthenticationSuccessHandler.java @@ -67,19 +67,25 @@ public class CustomSaml2AuthenticationSuccessHandler boolean userExists = userService.usernameExistsIgnoreCase(username); - // Check if user is eligible for SAML (grandfathered or system has paid license) + // Check if user is eligible for SAML (grandfathered or system has ENTERPRISE license) if (userExists) { stirling.software.proprietary.security.model.User user = userService.findByUsernameIgnoreCase(username).orElse(null); - if (user != null && !licenseSettingsService.isOAuthEligible(user)) { - // User is not grandfathered and no paid license - block SAML login + if (user != null && !licenseSettingsService.isSamlEligible(user)) { + // User is not grandfathered and no ENTERPRISE license - block SAML login + log.warn( + "SAML2 login blocked for existing user '{}' - not eligible (not grandfathered and no ENTERPRISE license)", + username); response.sendRedirect( request.getContextPath() + "/logout?saml2RequiresLicense=true"); return; } - } else if (!licenseSettingsService.isOAuthEligible(null)) { - // No existing user and no paid license -> block auto creation + } else if (!licenseSettingsService.isSamlEligible(null)) { + // No existing user and no ENTERPRISE license -> block auto creation + log.warn( + "SAML2 login blocked for new user '{}' - not eligible (no ENTERPRISE license for auto-creation)", + username); response.sendRedirect( request.getContextPath() + "/logout?saml2RequiresLicense=true"); return; diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/EmailService.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/EmailService.java index 8df76fca3c..d4ecf81618 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/EmailService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/EmailService.java @@ -223,4 +223,53 @@ public class EmailService { sendPlainEmail(to, subject, body, true); } + + @Async + public void sendPasswordChangedNotification( + String to, String username, String newPassword, String loginUrl) throws MessagingException { + String subject = "Your Stirling PDF password has been updated"; + + String passwordSection = + newPassword == null + ? "" + : """ +
+

Temporary Password: %s

+
+ """ + .formatted(newPassword); + + String body = + """ + +
+
+
+ \"Stirling +
+
+

Your password was changed

+

Hello %s,

+

An administrator has updated the password for your Stirling PDF account.

+ %s +

If you did not expect this change, please contact your administrator immediately.

+ +

Or copy and paste this link in your browser:

+
+ %s +
+
+
+ © 2025 Stirling PDF. All rights reserved. +
+
+
+ + """ + .formatted(username, passwordSection, loginUrl, loginUrl); + + sendPlainEmail(to, subject, body, true); + } } diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/service/SignatureService.java b/app/proprietary/src/main/java/stirling/software/proprietary/service/SignatureService.java index 6f849ebfe6..63ee285123 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/service/SignatureService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/service/SignatureService.java @@ -2,6 +2,7 @@ package stirling.software.proprietary.service; import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -13,6 +14,8 @@ import java.util.stream.Stream; import org.springframework.stereotype.Service; +import com.fasterxml.jackson.databind.ObjectMapper; + import lombok.extern.slf4j.Slf4j; import stirling.software.common.configuration.InstallationPathConfig; @@ -31,6 +34,7 @@ public class SignatureService implements PersonalSignatureServiceInterface { private final String SIGNATURE_BASE_PATH; private final String ALL_USERS_FOLDER = "ALL_USERS"; + private final ObjectMapper objectMapper = new ObjectMapper(); // Storage limits per user private static final int MAX_SIGNATURES_PER_USER = 20; @@ -88,6 +92,14 @@ public class SignatureService implements PersonalSignatureServiceInterface { response.setCreatedAt(timestamp); response.setUpdatedAt(timestamp); + // Copy text signature properties if present + if ("text".equals(request.getType())) { + response.setSignerName(request.getSignerName()); + response.setFontFamily(request.getFontFamily()); + response.setFontSize(request.getFontSize()); + response.setTextColor(request.getTextColor()); + } + // Extract and save image data String dataUrl = request.getDataUrl(); if (dataUrl != null && dataUrl.startsWith("data:image/")) { @@ -133,6 +145,19 @@ public class SignatureService implements PersonalSignatureServiceInterface { response.setDataUrl("/api/v1/general/signatures/" + imageFileName); } + // Save metadata JSON file + String metadataFileName = request.getId() + ".json"; + Path metadataPath = targetFolder.resolve(metadataFileName); + verifyPathWithinDirectory(metadataPath, targetFolder); + + String metadataJson = objectMapper.writeValueAsString(response); + Files.writeString( + metadataPath, + metadataJson, + StandardCharsets.UTF_8, + StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING); + log.info("Saved signature {} for user {} (scope: {})", request.getId(), username, scope); return response; } @@ -179,6 +204,13 @@ public class SignatureService implements PersonalSignatureServiceInterface { log.info("Deleted signature file: {}", file); } } + + // Also delete metadata file if it exists + Path metadataPath = personalFolder.resolve(signatureId + ".json"); + if (Files.exists(metadataPath)) { + Files.delete(metadataPath); + log.info("Deleted signature metadata: {}", metadataPath); + } } if (!deleted) { @@ -186,6 +218,50 @@ public class SignatureService implements PersonalSignatureServiceInterface { } } + /** Update a signature label. */ + public void updateSignatureLabel(String username, String signatureId, String newLabel) + throws IOException { + validateFileName(signatureId); + + // Try personal folder first + Path personalFolder = Paths.get(SIGNATURE_BASE_PATH, username); + Path metadataPath = personalFolder.resolve(signatureId + ".json"); + + if (Files.exists(metadataPath)) { + updateMetadataLabel(metadataPath, newLabel); + log.info("Updated label for personal signature {} (user: {})", signatureId, username); + return; + } + + // If not found in personal, try shared folder + Path sharedFolder = Paths.get(SIGNATURE_BASE_PATH, ALL_USERS_FOLDER); + Path sharedMetadataPath = sharedFolder.resolve(signatureId + ".json"); + + if (Files.exists(sharedMetadataPath)) { + updateMetadataLabel(sharedMetadataPath, newLabel); + log.info("Updated label for shared signature {}", signatureId); + return; + } + + throw new FileNotFoundException("Signature metadata not found"); + } + + private void updateMetadataLabel(Path metadataPath, String newLabel) throws IOException { + String metadataJson = Files.readString(metadataPath, StandardCharsets.UTF_8); + SavedSignatureResponse sig = + objectMapper.readValue(metadataJson, SavedSignatureResponse.class); + sig.setLabel(newLabel); + sig.setUpdatedAt(System.currentTimeMillis()); + + String updatedJson = objectMapper.writeValueAsString(sig); + Files.writeString( + metadataPath, + updatedJson, + StandardCharsets.UTF_8, + StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING); + } + // Private helper methods private void enforceStorageLimits(String username, String dataUrlToAdd) throws IOException { @@ -245,16 +321,31 @@ public class SignatureService implements PersonalSignatureServiceInterface { String fileName = path.getFileName().toString(); String id = fileName.substring(0, fileName.lastIndexOf('.')); - SavedSignatureResponse sig = new SavedSignatureResponse(); - sig.setId(id); - sig.setLabel(id); - sig.setType("image"); - sig.setScope(scope); - sig.setCreatedAt(Files.getLastModifiedTime(path).toMillis()); - sig.setUpdatedAt(Files.getLastModifiedTime(path).toMillis()); + // Try to load metadata from JSON file + Path metadataPath = folder.resolve(id + ".json"); + SavedSignatureResponse sig; - // Set unified URL path (works for both personal and shared) - sig.setDataUrl("/api/v1/general/signatures/" + fileName); + if (Files.exists(metadataPath)) { + // Load from metadata file + String metadataJson = + Files.readString( + metadataPath, StandardCharsets.UTF_8); + sig = + objectMapper.readValue( + metadataJson, SavedSignatureResponse.class); + } else { + // Fallback for old signatures without metadata + sig = new SavedSignatureResponse(); + sig.setId(id); + sig.setLabel(id); + sig.setType("image"); + sig.setScope(scope); + sig.setCreatedAt( + Files.getLastModifiedTime(path).toMillis()); + sig.setUpdatedAt( + Files.getLastModifiedTime(path).toMillis()); + sig.setDataUrl("/api/v1/general/signatures/" + fileName); + } signatures.add(sig); } catch (IOException e) { diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/service/UserLicenseSettingsService.java b/app/proprietary/src/main/java/stirling/software/proprietary/service/UserLicenseSettingsService.java index 1cddefde3b..aa794e6997 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/service/UserLicenseSettingsService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/service/UserLicenseSettingsService.java @@ -21,6 +21,7 @@ import stirling.software.common.model.ApplicationProperties; import stirling.software.proprietary.model.UserLicenseSettings; import stirling.software.proprietary.security.configuration.ee.KeygenLicenseVerifier.License; import stirling.software.proprietary.security.configuration.ee.LicenseKeyChecker; +import stirling.software.proprietary.security.model.User; import stirling.software.proprietary.security.repository.UserLicenseSettingsRepository; import stirling.software.proprietary.security.service.UserService; @@ -343,17 +344,76 @@ public class UserLicenseSettingsService { * @param user The user to check * @return true if the user can use OAuth/SAML */ - public boolean isOAuthEligible(stirling.software.proprietary.security.model.User user) { + public boolean isOAuthEligible(User user) { + String username = (user != null) ? user.getUsername() : ""; + log.info("OAuth eligibility check for user: {}", username); + // Grandfathered users always have OAuth access if (user != null && user.isOauthGrandfathered()) { log.debug("User {} is grandfathered for OAuth", user.getUsername()); return true; } - // Users can use OAuth/SAML only if system has ENTERPRISE license - boolean hasEnterpriseLicense = hasEnterpriseLicense(); - log.debug("OAuth eligibility check: hasEnterpriseLicense={}", hasEnterpriseLicense); - return hasEnterpriseLicense; + // todo: remove + if (user != null) { + log.info( + "User {} is NOT grandfathered (isOauthGrandfathered={})", + username, + user.isOauthGrandfathered()); + } else { + log.info("New user attempting OAuth login - checking license requirement"); + } + + // Users can use OAuth with SERVER or ENTERPRISE license + boolean hasPaid = hasPaidLicense(); + log.info( + "OAuth eligibility result: hasPaidLicense={}, user={}, eligible={}", + hasPaid, + username, + hasPaid); + return hasPaid; + } + + /** + * Checks if a user is eligible to use SAML authentication. + * + *

A user is eligible if: + * + *

    + *
  • They are grandfathered for OAuth (existing user before policy change), OR + *
  • The system has an ENTERPRISE license (SAML is enterprise-only) + *
+ * + * @param user The user to check + * @return true if the user can use SAML + */ + public boolean isSamlEligible(User user) { + String username = (user != null) ? user.getUsername() : ""; + log.info("SAML2 eligibility check for user: {}", username); + + // Grandfathered users always have SAML access + if (user != null && user.isOauthGrandfathered()) { + log.info("User {} is grandfathered for SAML2 - ELIGIBLE", username); + return true; + } + + if (user != null) { + log.info( + "User {} is NOT grandfathered (isOauthGrandfathered={})", + username, + user.isOauthGrandfathered()); + } else { + log.info("New user attempting SAML2 login - checking license requirement"); + } + + // Users can use SAML only with ENTERPRISE license + boolean hasEnterprise = hasEnterpriseLicense(); + log.info( + "SAML2 eligibility result: hasEnterpriseLicense={}, user={}, eligible={}", + hasEnterprise, + username, + hasEnterprise); + return hasEnterprise; } /** @@ -495,8 +555,12 @@ public class UserLicenseSettingsService { if (checker == null) { return false; } + License license = checker.getPremiumLicenseEnabledResult(); - return license == License.SERVER || license == License.ENTERPRISE; + boolean hasPaid = (license == License.SERVER || license == License.ENTERPRISE); + log.info("License check result: type={}, requiresPaid=true, hasPaid={}", license, hasPaid); + + return hasPaid; } /** @@ -510,7 +574,19 @@ public class UserLicenseSettingsService { if (checker == null) { return false; } + License license = checker.getPremiumLicenseEnabledResult(); + log.info( + "License check result: type={}, requiresEnterprise=true, hasEnterprise={}", + license, + (license == License.ENTERPRISE)); + + if (license != License.ENTERPRISE) { + log.warn( + "SAML2 requires ENTERPRISE license but found: {}. SAML2 login will be blocked.", + license); + } + return license == License.ENTERPRISE; } } diff --git a/app/proprietary/src/test/java/stirling/software/proprietary/security/oauth2/OAuth2ConfigurationTest.java b/app/proprietary/src/test/java/stirling/software/proprietary/security/oauth2/OAuth2ConfigurationTest.java new file mode 100644 index 0000000000..750696b770 --- /dev/null +++ b/app/proprietary/src/test/java/stirling/software/proprietary/security/oauth2/OAuth2ConfigurationTest.java @@ -0,0 +1,162 @@ +package stirling.software.proprietary.security.oauth2; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for OAuth2Configuration redirect URI logic. + * + *

These tests validate the critical fix for GitHub issue #5141: The redirect URI path segment + * MUST match the registration ID. Previously, the redirect URI was hardcoded to 'oidc', causing + * InvalidClientRegistrationIdException when custom provider names were used. + * + *

Note: These are conceptual tests documenting the expected behavior. Full integration testing + * with actual OIDC discovery would require: 1. Mock HTTP server for OIDC discovery endpoints 2. + * Valid OIDC configuration responses 3. Network mocking infrastructure + */ +class OAuth2ConfigurationTest { + + /** + * Tests the redirect URI pattern for OIDC provider configurations. + * + *

Critical behavior (GitHub issue #5141 fix): The redirect URI path segment MUST match the + * registration ID. For example: - Provider name: "authentik" → Redirect URI: + * "/login/oauth2/code/authentik" - Provider name: "mycompany" → Redirect URI: + * "/login/oauth2/code/mycompany" - Provider name: "oidc" → Redirect URI: + * "/login/oauth2/code/oidc" + * + *

Previously, the redirect URI was hardcoded to 'oidc', causing Spring Security to look for + * a registration with ID 'oidc' when the provider redirected back. This caused + * InvalidClientRegistrationIdException when custom provider names were used. + */ + @Test + void testRedirectUriPattern_usesProviderNameNotHardcodedOidc() { + // Verify the redirect URI pattern constant + String redirectUriBase = "{baseUrl}/login/oauth2/code/"; + + // Test cases: provider name → expected redirect URI + String[][] testCases = { + {"authentik", redirectUriBase + "authentik"}, + {"mycompany", redirectUriBase + "mycompany"}, + {"oidc", redirectUriBase + "oidc"}, + {"okta", redirectUriBase + "okta"}, + {"auth0", redirectUriBase + "auth0"} + }; + + for (String[] testCase : testCases) { + String providerName = testCase[0]; + String expectedRedirectUri = testCase[1]; + + // The fix ensures: .redirectUri(REDIRECT_URI_PATH + name) + // instead of: .redirectUri(REDIRECT_URI_PATH + "oidc") + String actualRedirectUri = redirectUriBase + providerName; + + assertEquals( + expectedRedirectUri, + actualRedirectUri, + String.format( + "Redirect URI for provider '%s' must use provider name, not hardcoded 'oidc'", + providerName)); + } + } + + /** + * Documents the critical fix for OAuth2 redirect URI mismatch. + * + *

This test validates the logic that was changed in OAuth2Configuration.java line 220: + * + *

+     * // BEFORE (bug):
+     * .redirectUri(REDIRECT_URI_PATH + "oidc")  // Always "oidc"
+     *
+     * // AFTER (fix):
+     * .redirectUri(REDIRECT_URI_PATH + name)  // Dynamic provider name
+     * 
+ */ + @Test + void testCriticalFix_redirectUriMatchesRegistrationId() { + // The redirect URI path segment extraction by Spring Security + String callbackUrl = "http://localhost:8080/login/oauth2/code/authentik?code=abc123"; + + // Spring extracts the path segment between "code/" and "?" + String extractedRegistrationId = extractRegistrationIdFromCallback(callbackUrl); + + // The extracted ID MUST match an actual registration ID + assertEquals("authentik", extractedRegistrationId); + + // If we had used hardcoded "oidc", the callback would be: + String buggyCallbackUrl = "http://localhost:8080/login/oauth2/code/oidc?code=abc123"; + String buggyExtractedId = extractRegistrationIdFromCallback(buggyCallbackUrl); + + // This would look for registration with ID "oidc" but we registered "authentik" + assertEquals("oidc", buggyExtractedId); + + // The mismatch: registrationId="authentik", but Spring looks for "oidc" + // Result: InvalidClientRegistrationIdException + assertNotNull(buggyExtractedId, "This demonstrates the bug that was fixed"); + } + + /** Helper method simulating Spring's extraction of registration ID from callback URL */ + private String extractRegistrationIdFromCallback(String callbackUrl) { + // Simplified version of what Spring Security does + // Actual: OAuth2AuthorizationRequestRedirectFilter extracts from path + String path = callbackUrl.split("\\?")[0]; + String[] parts = path.split("/"); + return parts[parts.length - 1]; // Last path segment + } + + /** + * Validates the frontend-backend flow for custom provider names. + * + *

Complete flow: 1. Backend: Provider configured as "authentik" in settings.yml 2. Backend: + * ClientRegistration created with registrationId="authentik" 3. Backend: Redirect URI set to + * "{baseUrl}/login/oauth2/code/authentik" 4. Backend: Login endpoint returns providerList with + * "/oauth2/authorization/authentik" 5. Frontend: Extracts "authentik" from path and uses it for + * OAuth login 6. Frontend: Redirects to "/oauth2/authorization/authentik" 7. Backend: Spring + * Security redirects to provider with redirect_uri containing "authentik" 8. Provider: + * Redirects back to "/login/oauth2/code/authentik?code=..." 9. Backend: Spring Security + * extracts "authentik" from callback URL 10. Backend: Looks up ClientRegistration with ID + * "authentik" āœ… SUCCESS + * + *

If redirect URI was hardcoded to "oidc" (the bug): Step 7: Provider redirects to + * "/login/oauth2/code/oidc?code=..." Step 9: Spring Security looks for registration ID "oidc" + * Step 10: FAIL - No registration found with ID "oidc" (we registered "authentik") Result: + * InvalidClientRegistrationIdException + */ + @Test + void testEndToEndFlow_registrationIdConsistency() { + String providerName = "authentik"; + + // Step 2: Registration ID + String registrationId = providerName; + assertEquals("authentik", registrationId); + + // Step 3: Redirect URI (MUST use same name) + String redirectUri = "{baseUrl}/login/oauth2/code/" + providerName; + assertEquals("{baseUrl}/login/oauth2/code/authentik", redirectUri); + + // Step 4: Provider list endpoint + String authorizationPath = "/oauth2/authorization/" + providerName; + assertEquals("/oauth2/authorization/authentik", authorizationPath); + + // Step 5: Frontend extracts provider ID + String frontendProviderId = + authorizationPath.substring(authorizationPath.lastIndexOf('/') + 1); + assertEquals("authentik", frontendProviderId); + + // Step 6-8: OAuth flow (external) + + // Step 9: Callback URL from provider + String callbackUrl = + "http://localhost:8080/login/oauth2/code/" + providerName + "?code=abc123"; + String extractedId = extractRegistrationIdFromCallback(callbackUrl); + + // Step 10: Registration lookup + assertEquals( + registrationId, + extractedId, + "Registration ID from callback MUST match original registration ID"); + } +} diff --git a/app/proprietary/src/test/java/stirling/software/proprietary/security/service/MailConfigTest.java b/app/proprietary/src/test/java/stirling/software/proprietary/security/service/MailConfigTest.java index 3db3493f4b..5df56f8eff 100644 --- a/app/proprietary/src/test/java/stirling/software/proprietary/security/service/MailConfigTest.java +++ b/app/proprietary/src/test/java/stirling/software/proprietary/security/service/MailConfigTest.java @@ -27,6 +27,11 @@ class MailConfigTest { when(mailProps.getPort()).thenReturn(587); when(mailProps.getUsername()).thenReturn("user@example.com"); when(mailProps.getPassword()).thenReturn("password"); + when(mailProps.getStartTlsEnable()).thenReturn(null); + when(mailProps.getStartTlsRequired()).thenReturn(null); + when(mailProps.getSslEnable()).thenReturn(null); + when(mailProps.getSslTrust()).thenReturn(null); + when(mailProps.getSslCheckServerIdentity()).thenReturn(null); } @Test @@ -50,6 +55,32 @@ class MailConfigTest { () -> assertEquals("password", impl.getPassword()), () -> assertEquals("UTF-8", impl.getDefaultEncoding()), () -> assertEquals("true", props.getProperty("mail.smtp.auth")), - () -> assertEquals("true", props.getProperty("mail.smtp.starttls.enable"))); + () -> assertEquals("true", props.getProperty("mail.smtp.starttls.enable")), + () -> assertEquals(null, props.getProperty("mail.smtp.starttls.required")), + () -> assertEquals(null, props.getProperty("mail.smtp.ssl.enable")), + () -> assertEquals("*", props.getProperty("mail.smtp.ssl.trust"))); + } + + @Test + void shouldRespectExplicitTlsOverrides() { + ApplicationProperties appProps = mock(ApplicationProperties.class); + when(mailProps.getStartTlsEnable()).thenReturn(false); + when(mailProps.getStartTlsRequired()).thenReturn(true); + when(mailProps.getSslEnable()).thenReturn(true); + when(mailProps.getSslTrust()).thenReturn("*"); + when(mailProps.getSslCheckServerIdentity()).thenReturn(true); + when(appProps.getMail()).thenReturn(mailProps); + + MailConfig config = new MailConfig(appProps); + JavaMailSenderImpl impl = (JavaMailSenderImpl) config.javaMailSender(); + + Properties props = impl.getJavaMailProperties(); + + assertAll( + () -> assertEquals("false", props.getProperty("mail.smtp.starttls.enable")), + () -> assertEquals("true", props.getProperty("mail.smtp.starttls.required")), + () -> assertEquals("true", props.getProperty("mail.smtp.ssl.enable")), + () -> assertEquals("*", props.getProperty("mail.smtp.ssl.trust")), + () -> assertEquals("true", props.getProperty("mail.smtp.ssl.checkserveridentity"))); } } diff --git a/app/proprietary/src/test/java/stirling/software/proprietary/service/UserLicenseSettingsServiceTest.java b/app/proprietary/src/test/java/stirling/software/proprietary/service/UserLicenseSettingsServiceTest.java index 139146d707..7f9445ad7c 100644 --- a/app/proprietary/src/test/java/stirling/software/proprietary/service/UserLicenseSettingsServiceTest.java +++ b/app/proprietary/src/test/java/stirling/software/proprietary/service/UserLicenseSettingsServiceTest.java @@ -267,4 +267,222 @@ class UserLicenseSettingsServiceTest { verify(userService, times(1)).grandfatherAllOAuthUsers(); verify(userService, times(1)).grandfatherPendingSsoUsersWithoutSession(); } + + // ===== OAuth Eligibility Tests ===== + + @Test + void isOAuthEligible_grandfatheredUser_returnsTrue() { + // Grandfathered user should be eligible regardless of license + stirling.software.proprietary.security.model.User user = + new stirling.software.proprietary.security.model.User(); + user.setUsername("grandfathered-user"); + user.setOauthGrandfathered(true); + + when(licenseKeyChecker.getPremiumLicenseEnabledResult()).thenReturn(License.NORMAL); + + boolean result = service.isOAuthEligible(user); + + assertEquals(true, result, "Grandfathered user should be eligible for OAuth"); + } + + @Test + void isOAuthEligible_nonGrandfatheredUserWithServerLicense_returnsTrue() { + // Non-grandfathered user with SERVER license should be eligible + stirling.software.proprietary.security.model.User user = + new stirling.software.proprietary.security.model.User(); + user.setUsername("test-user"); + user.setOauthGrandfathered(false); + + when(licenseKeyChecker.getPremiumLicenseEnabledResult()).thenReturn(License.SERVER); + + boolean result = service.isOAuthEligible(user); + + assertEquals(true, result, "Non-grandfathered user with SERVER license should be eligible"); + } + + @Test + void isOAuthEligible_nonGrandfatheredUserWithEnterpriseLicense_returnsTrue() { + // Non-grandfathered user with ENTERPRISE license should be eligible + stirling.software.proprietary.security.model.User user = + new stirling.software.proprietary.security.model.User(); + user.setUsername("test-user"); + user.setOauthGrandfathered(false); + + when(licenseKeyChecker.getPremiumLicenseEnabledResult()).thenReturn(License.ENTERPRISE); + + boolean result = service.isOAuthEligible(user); + + assertEquals( + true, result, "Non-grandfathered user with ENTERPRISE license should be eligible"); + } + + @Test + void isOAuthEligible_nonGrandfatheredUserWithNoLicense_returnsFalse() { + // Non-grandfathered user without license should NOT be eligible + stirling.software.proprietary.security.model.User user = + new stirling.software.proprietary.security.model.User(); + user.setUsername("test-user"); + user.setOauthGrandfathered(false); + + when(licenseKeyChecker.getPremiumLicenseEnabledResult()).thenReturn(License.NORMAL); + + boolean result = service.isOAuthEligible(user); + + assertEquals( + false, + result, + "Non-grandfathered user without paid license should NOT be eligible"); + } + + @Test + void isOAuthEligible_newUserWithServerLicense_returnsTrue() { + // New user (null) with SERVER license should be eligible for auto-creation + when(licenseKeyChecker.getPremiumLicenseEnabledResult()).thenReturn(License.SERVER); + + boolean result = service.isOAuthEligible(null); + + assertEquals( + true, result, "New user with SERVER license should be eligible for auto-creation"); + } + + @Test + void isOAuthEligible_newUserWithNoLicense_returnsFalse() { + // New user (null) without license should NOT be eligible + when(licenseKeyChecker.getPremiumLicenseEnabledResult()).thenReturn(License.NORMAL); + + boolean result = service.isOAuthEligible(null); + + assertEquals( + false, + result, + "New user without paid license should NOT be eligible for auto-creation"); + } + + @Test + void isOAuthEligible_licenseCheckerUnavailable_returnsFalse() { + // If LicenseKeyChecker is unavailable, OAuth should be blocked + when(licenseKeyCheckerProvider.getIfAvailable()).thenReturn(null); + + stirling.software.proprietary.security.model.User user = + new stirling.software.proprietary.security.model.User(); + user.setUsername("test-user"); + user.setOauthGrandfathered(false); + + boolean result = service.isOAuthEligible(user); + + assertEquals( + false, result, "OAuth should be blocked when LicenseKeyChecker is unavailable"); + } + + // ===== SAML Eligibility Tests ===== + + @Test + void isSamlEligible_grandfatheredUser_returnsTrue() { + // Grandfathered user should be eligible for SAML regardless of license + stirling.software.proprietary.security.model.User user = + new stirling.software.proprietary.security.model.User(); + user.setUsername("grandfathered-user"); + user.setOauthGrandfathered(true); + + when(licenseKeyChecker.getPremiumLicenseEnabledResult()).thenReturn(License.NORMAL); + + boolean result = service.isSamlEligible(user); + + assertEquals(true, result, "Grandfathered user should be eligible for SAML"); + } + + @Test + void isSamlEligible_nonGrandfatheredUserWithEnterpriseLicense_returnsTrue() { + // Non-grandfathered user with ENTERPRISE license should be eligible + stirling.software.proprietary.security.model.User user = + new stirling.software.proprietary.security.model.User(); + user.setUsername("test-user"); + user.setOauthGrandfathered(false); + + when(licenseKeyChecker.getPremiumLicenseEnabledResult()).thenReturn(License.ENTERPRISE); + + boolean result = service.isSamlEligible(user); + + assertEquals( + true, + result, + "Non-grandfathered user with ENTERPRISE license should be eligible for SAML"); + } + + @Test + void isSamlEligible_nonGrandfatheredUserWithServerLicense_returnsFalse() { + // Non-grandfathered user with SERVER license should NOT be eligible for SAML + stirling.software.proprietary.security.model.User user = + new stirling.software.proprietary.security.model.User(); + user.setUsername("test-user"); + user.setOauthGrandfathered(false); + + when(licenseKeyChecker.getPremiumLicenseEnabledResult()).thenReturn(License.SERVER); + + boolean result = service.isSamlEligible(user); + + assertEquals( + false, + result, + "Non-grandfathered user with SERVER license should NOT be eligible for SAML"); + } + + @Test + void isSamlEligible_nonGrandfatheredUserWithNoLicense_returnsFalse() { + // Non-grandfathered user without license should NOT be eligible + stirling.software.proprietary.security.model.User user = + new stirling.software.proprietary.security.model.User(); + user.setUsername("test-user"); + user.setOauthGrandfathered(false); + + when(licenseKeyChecker.getPremiumLicenseEnabledResult()).thenReturn(License.NORMAL); + + boolean result = service.isSamlEligible(user); + + assertEquals( + false, + result, + "Non-grandfathered user without ENTERPRISE license should NOT be eligible for SAML"); + } + + @Test + void isSamlEligible_newUserWithEnterpriseLicense_returnsTrue() { + // New user (null) with ENTERPRISE license should be eligible for auto-creation + when(licenseKeyChecker.getPremiumLicenseEnabledResult()).thenReturn(License.ENTERPRISE); + + boolean result = service.isSamlEligible(null); + + assertEquals( + true, + result, + "New user with ENTERPRISE license should be eligible for SAML auto-creation"); + } + + @Test + void isSamlEligible_newUserWithServerLicense_returnsFalse() { + // New user (null) with SERVER license should NOT be eligible for SAML + when(licenseKeyChecker.getPremiumLicenseEnabledResult()).thenReturn(License.SERVER); + + boolean result = service.isSamlEligible(null); + + assertEquals( + false, + result, + "New user with SERVER license should NOT be eligible for SAML (requires ENTERPRISE)"); + } + + @Test + void isSamlEligible_licenseCheckerUnavailable_returnsFalse() { + // If LicenseKeyChecker is unavailable, SAML should be blocked + when(licenseKeyCheckerProvider.getIfAvailable()).thenReturn(null); + + stirling.software.proprietary.security.model.User user = + new stirling.software.proprietary.security.model.User(); + user.setUsername("test-user"); + user.setOauthGrandfathered(false); + + boolean result = service.isSamlEligible(user); + + assertEquals(false, result, "SAML should be blocked when LicenseKeyChecker is unavailable"); + } } diff --git a/build.gradle b/build.gradle index b85c2d4138..64908ee4ac 100644 --- a/build.gradle +++ b/build.gradle @@ -57,7 +57,7 @@ repositories { allprojects { group = 'stirling.software' - version = '2.0.2' + version = '2.1.2' configurations.configureEach { exclude group: 'commons-logging', module: 'commons-logging' diff --git a/devGuide/HowToAddNewLanguage.md b/devGuide/HowToAddNewLanguage.md index 6a9ed17f29..861772576d 100644 --- a/devGuide/HowToAddNewLanguage.md +++ b/devGuide/HowToAddNewLanguage.md @@ -8,36 +8,33 @@ Fork Stirling-PDF and create a new branch out of `main`. -Then add a reference to the language in the navbar by adding a new language entry to the dropdown: +## Frontend Translation Files (TOML Format) -- Edit the file: [languages.html](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/app/core/src/main/resources/templates/fragments/languages.html) +### Add Language Directory and Translation File +1. Create a new language directory in `frontend/public/locales/` + - Use hyphenated format: `pl-PL` (not underscore) -For example, to add Polish, you would add: +2. Copy the reference translation file: + - Source: `frontend/public/locales/en-GB/translation.toml` + - Destination: `frontend/public/locales/pl-PL/translation.toml` -```html -

-``` +3. Translate all entries in the TOML file + - Keep the TOML structure intact + - Preserve all placeholders like `{n}`, `{total}`, `{filename}`, `{{variable}}` + - See `scripts/translations/README.md` for translation tools and workflows -The `data-bs-language-code` is the code used to reference the file in the next step. +4. Update the language selector in the frontend to include your new language -### Add Language Property File - -Start by copying the existing English property file: - -- [messages_en_GB.properties](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/app/core/src/main/resources/messages_en_GB.properties) - -Copy and rename it to `messages_{your data-bs-language-code here}.properties`. In the Polish example, you would set the name to `messages_pl_PL.properties`. - -Then simply translate all property entries within that file and make a Pull Request (PR) into `main` for others to use! - -If you do not have a Java IDE, I am happy to verify that the changes work once you raise the PR (but I won't be able to verify the translations themselves). +Then make a Pull Request (PR) into `main` for others to use! ## Handling Untranslatable Strings -Sometimes, certain strings in the properties file may not require translation because they are the same in the target language or are universal (like names of protocols, certain terminologies, etc.). To ensure accurate statistics for language progress, these strings should be added to the `ignore_translation.toml` file located in the `scripts` directory. This will exclude them from the translation progress calculations. +Sometimes, certain strings may not require translation because they are the same in the target language or are universal (like names of protocols, certain terminologies, etc.). To ensure accurate statistics for language progress, these strings should be added to the `ignore_translation.toml` file located in the `scripts` directory. This will exclude them from the translation progress calculations. -For example, if the English string `error=Error` does not need translation in Polish, add it to the `ignore_translation.toml` under the Polish section: +For example, if the English string `error` does not need translation in Polish, add it to the `ignore_translation.toml` under the Polish section: + +**Note**: Use underscores in `ignore_translation.toml` even though frontend uses hyphens (e.g., `pl_PL` not `pl-PL`) ```toml [pl_PL] @@ -50,27 +47,27 @@ ignore = [ ## Add New Translation Tags > [!IMPORTANT] -> If you add any new translation tags, they must first be added to the `messages_en_GB.properties` file. This ensures consistency across all language files. +> If you add any new translation tags, they must first be added to the `en-GB/translation.toml` file. This ensures consistency across all language files. -- New translation tags **must be added** to the `messages_en_GB.properties` file to maintain a reference for other languages. -- After adding the new tags to `messages_en_GB.properties`, add and translate them in the respective language file (e.g., `messages_pl_PL.properties`). +- New translation tags **must be added** to `frontend/public/locales/en-GB/translation.toml` to maintain a reference for other languages. +- After adding the new tags to `en-GB/translation.toml`, add and translate them in the respective language file (e.g., `pl-PL/translation.toml`). +- Use the scripts in `scripts/translations/` to validate and manage translations (see `scripts/translations/README.md`) Make sure to place the entry under the correct language section. This helps maintain the accuracy of translation progress statistics and ensures that the translation tool or scripts do not misinterpret the completion rate. -### Use this code to perform a local check +### Validation Commands -#### Windows command - -```powershell -python .github/scripts/check_language_properties.py --reference-file app\core\src\main\resources\messages_en_GB.properties --branch "" --files app\core\src\main\resources\messages_pl_PL.properties - -python .github/scripts/check_language_properties.py --reference-file app\core\src\main\resources\messages_en_GB.properties --branch "" --check-file app\core\src\main\resources\messages_pl_PL.properties -``` - -#### Linux command +Use the translation scripts in `scripts/translations/` directory: ```bash -python3 .github/scripts/check_language_properties.py --reference-file app/core/src/main/resources/messages_en_GB.properties --branch "" --files app/core/src/main/resources/messages_pl_PL.properties +# Analyze translation progress +python3 scripts/translations/translation_analyzer.py --language pl-PL -python3 .github/scripts/check_language_properties.py --reference-file app/core/src/main/resources/messages_en_GB.properties --branch "" --check-file app/core/src/main/resources/messages_pl_PL.properties +# Validate TOML structure +python3 scripts/translations/validate_json_structure.py --language pl-PL + +# Validate placeholders +python3 scripts/translations/validate_placeholders.py --language pl-PL ``` + +See `scripts/translations/README.md` for complete documentation. diff --git a/docker/frontend/nginx.conf b/docker/frontend/nginx.conf index 3be5ec9005..ef74321efa 100644 --- a/docker/frontend/nginx.conf +++ b/docker/frontend/nginx.conf @@ -103,8 +103,8 @@ http { add_header Cache-Control "public, immutable"; } - # Cache static assets - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + # Cache static assets (but not API endpoints) + location ~* ^(?!/api/).*\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; } diff --git a/docker/unified/nginx.conf b/docker/unified/nginx.conf index 1e47b8619a..cbd5fee813 100644 --- a/docker/unified/nginx.conf +++ b/docker/unified/nginx.conf @@ -106,8 +106,8 @@ http { add_header Cache-Control "public, immutable"; } - # Cache static assets - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + # Cache static assets (but not API endpoints) + location ~* ^(?!/api/).*\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 6e1f885e65..8d90d96588 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -456,6 +456,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -499,6 +500,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -579,6 +581,7 @@ "resolved": "https://registry.npmjs.org/@embedpdf/core/-/core-1.5.0.tgz", "integrity": "sha512-Yrh9XoVaT8cUgzgqpJ7hx5wg6BqQrCFirqqlSwVb+Ly9oNn4fZbR9GycIWmzJOU5XBnaOJjXfQSaDyoNP0woNA==", "license": "MIT", + "peer": true, "dependencies": { "@embedpdf/engines": "1.5.0", "@embedpdf/models": "1.5.0" @@ -678,6 +681,7 @@ "resolved": "https://registry.npmjs.org/@embedpdf/plugin-history/-/plugin-history-1.5.0.tgz", "integrity": "sha512-p7PTNNaIr4gH3jLwX+eLJe1DeUXgi21kVGN6SRx/pocH8esg4jqoOeD/YiRRZoZnPOiy0jBXVhkPkwSmY7a2hQ==", "license": "MIT", + "peer": true, "dependencies": { "@embedpdf/models": "1.5.0" }, @@ -694,6 +698,7 @@ "resolved": "https://registry.npmjs.org/@embedpdf/plugin-interaction-manager/-/plugin-interaction-manager-1.5.0.tgz", "integrity": "sha512-ckHgTfvkW6c5Ta7Mc+Dl9C2foVnvEpqEJ84wyBnqrU0OWbe/jsiPhyKBVeartMGqNI/kVfaQTXupyrKhekAVmg==", "license": "MIT", + "peer": true, "dependencies": { "@embedpdf/models": "1.5.0" }, @@ -711,6 +716,7 @@ "resolved": "https://registry.npmjs.org/@embedpdf/plugin-loader/-/plugin-loader-1.5.0.tgz", "integrity": "sha512-P4YpIZfaW69etYIjphyaL4cGl2pB14h3OdTE0tRQ2pZYZHFLTvlt4q9B3PVSdhlSrHK5nob7jfLGon2U7xCslg==", "license": "MIT", + "peer": true, "dependencies": { "@embedpdf/models": "1.5.0" }, @@ -764,6 +770,7 @@ "resolved": "https://registry.npmjs.org/@embedpdf/plugin-render/-/plugin-render-1.5.0.tgz", "integrity": "sha512-ywwSj0ByrlkvrJIHKRzqxARkOZriki8VJUC+T4MV8fGyF4CzvCRJyKlPktahFz+VxhoodqTh7lBCib68dH+GvA==", "license": "MIT", + "peer": true, "dependencies": { "@embedpdf/models": "1.5.0" }, @@ -798,6 +805,7 @@ "resolved": "https://registry.npmjs.org/@embedpdf/plugin-scroll/-/plugin-scroll-1.5.0.tgz", "integrity": "sha512-RNmTZCZ8X1mA8cw9M7TMDuhO9GtkOalGha2bBL3En3D1IlDRS7PzNNMSMV7eqT7OQICSTltlpJ8p8Qi5esvL/Q==", "license": "MIT", + "peer": true, "dependencies": { "@embedpdf/models": "1.5.0" }, @@ -834,6 +842,7 @@ "resolved": "https://registry.npmjs.org/@embedpdf/plugin-selection/-/plugin-selection-1.5.0.tgz", "integrity": "sha512-zrxLBAZQoPswDuf9q9DrYaQc6B0Ysc2U1hueTjNH/4+ydfl0BFXZkKR63C2e3YmWtXvKjkoIj0GyPzsiBORLUw==", "license": "MIT", + "peer": true, "dependencies": { "@embedpdf/models": "1.5.0" }, @@ -909,6 +918,7 @@ "resolved": "https://registry.npmjs.org/@embedpdf/plugin-viewport/-/plugin-viewport-1.5.0.tgz", "integrity": "sha512-G8GDyYRhfehw72+r4qKkydnA5+AU8qH67g01Y12b0DzI0VIzymh/05Z4dK8DsY3jyWPXJfw2hlg5+KDHaMBHgQ==", "license": "MIT", + "peer": true, "dependencies": { "@embedpdf/models": "1.5.0" }, @@ -1064,6 +1074,7 @@ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -1107,6 +1118,7 @@ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -2137,6 +2149,7 @@ "resolved": "https://registry.npmjs.org/@mantine/core/-/core-8.3.6.tgz", "integrity": "sha512-paTl+0x+O/QtgMtqVJaG8maD8sfiOdgPmLOyG485FmeGZ1L3KMdEkhxZtmdGlDFsLXhmMGQ57ducT90bvhXX5A==", "license": "MIT", + "peer": true, "dependencies": { "@floating-ui/react": "^0.27.16", "clsx": "^2.1.1", @@ -2187,6 +2200,7 @@ "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-8.3.6.tgz", "integrity": "sha512-liHfaWXHAkLjJy+Bkr29UsCwAoDQ/a64WrM67lksx8F0qqyjR5RQH8zVlhuOjdpQnwtlUkE/YiTvbJiPcoI0bw==", "license": "MIT", + "peer": true, "peerDependencies": { "react": "^18.x || ^19.x" } @@ -2254,6 +2268,7 @@ "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.5.tgz", "integrity": "sha512-8VVxFmp1GIm9PpmnQoCoYo0UWHoOrdA57tDL62vkpzEgvb/d71Wsbv4FRg7r1Gyx7PuSo0tflH34cdl/NvfHNQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.28.4", "@mui/core-downloads-tracker": "^7.3.5", @@ -3186,6 +3201,7 @@ "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-7.9.0.tgz", "integrity": "sha512-ggs5k+/0FUJcIgNY08aZTqpBTtbExkJMYMLSMwyucrhtWexVOEY1KJmhBsxf+E/Q15f5rbwBpj+t0t2AW2oCsQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=12.16" } @@ -3304,7 +3320,6 @@ "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.6.tgz", "integrity": "sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ==", "license": "MIT", - "peer": true, "peerDependencies": { "acorn": "^8.9.0" } @@ -4081,6 +4096,7 @@ "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -4409,6 +4425,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz", "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -4419,6 +4436,7 @@ "integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -4488,6 +4506,7 @@ "integrity": "sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.3", "@typescript-eslint/types": "8.46.3", @@ -5201,7 +5220,6 @@ "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.24.tgz", "integrity": "sha512-BM8kBhtlkkbnyl4q+HiF5R5BL0ycDPfihowulm02q3WYp2vxgPcJuZO866qa/0u3idbMntKEtVNuAUp5bw4teg==", "license": "MIT", - "peer": true, "dependencies": { "@vue/shared": "3.5.24" } @@ -5211,7 +5229,6 @@ "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.24.tgz", "integrity": "sha512-RYP/byyKDgNIqfX/gNb2PB55dJmM97jc9wyF3jK7QUInYKypK2exmZMNwnjueWwGceEkP6NChd3D2ZVEp9undQ==", "license": "MIT", - "peer": true, "dependencies": { "@vue/reactivity": "3.5.24", "@vue/shared": "3.5.24" @@ -5222,7 +5239,6 @@ "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.24.tgz", "integrity": "sha512-Z8ANhr/i0XIluonHVjbUkjvn+CyrxbXRIxR7wn7+X7xlcb7dJsfITZbkVOeJZdP8VZwfrWRsWdShH6pngMxRjw==", "license": "MIT", - "peer": true, "dependencies": { "@vue/reactivity": "3.5.24", "@vue/runtime-core": "3.5.24", @@ -5235,7 +5251,6 @@ "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.24.tgz", "integrity": "sha512-Yh2j2Y4G/0/4z/xJ1Bad4mxaAk++C2v4kaa8oSYTMJBJ00/ndPuxCnWeot0/7/qafQFLh5pr6xeV6SdMcE/G1w==", "license": "MIT", - "peer": true, "dependencies": { "@vue/compiler-ssr": "3.5.24", "@vue/shared": "3.5.24" @@ -5262,6 +5277,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5669,7 +5685,6 @@ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", "license": "Apache-2.0", - "peer": true, "engines": { "node": ">= 0.4" } @@ -5946,6 +5961,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", @@ -6993,7 +7009,8 @@ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1521046.tgz", "integrity": "sha512-vhE6eymDQSKWUXwwA37NtTTVEzjtGVfDr3pRbsWEQ5onH/Snp2c+2xZHWJJawG/0hCCJLRGt4xVtEVUVILol4w==", "dev": true, - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/dezalgo": { "version": "1.0.4", @@ -7388,6 +7405,7 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -7558,6 +7576,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -7724,8 +7743,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/espree": { "version": "10.4.0", @@ -7790,7 +7808,6 @@ "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.1.2.tgz", "integrity": "sha512-DgvlIQeowRNyvLPWW4PT7Gu13WznY288Du086E751mwwbsgr29ytBiYeLzAGIo0qk3Ujob0SDk8TiSaM5WQzNg==", "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } @@ -8881,6 +8898,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.27.6" }, @@ -9357,7 +9375,6 @@ "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "^1.0.6" } @@ -9678,6 +9695,7 @@ "integrity": "sha512-Pcfm3eZ+eO4JdZCXthW9tCDT3nF4K+9dmeZ+5X39n+Kqz0DDIABRP5CAEOHRFZk8RGuC2efksTJxrjp8EXCunQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@acemir/cssom": "^0.9.19", "@asamuzakjp/dom-selector": "^6.7.3", @@ -10264,8 +10282,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/locate-path": { "version": "6.0.0", @@ -11411,6 +11428,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -11690,6 +11708,7 @@ "resolved": "https://registry.npmjs.org/preact/-/preact-10.27.2.tgz", "integrity": "sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg==", "license": "MIT", + "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" @@ -12072,6 +12091,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -12081,6 +12101,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -13592,7 +13613,6 @@ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", "license": "Apache-2.0", - "peer": true, "engines": { "node": ">= 0.4" } @@ -13801,6 +13821,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -14102,6 +14123,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -14183,6 +14205,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "napi-postinstall": "^0.3.0" }, @@ -14387,6 +14410,7 @@ "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -14538,6 +14562,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -14551,6 +14576,7 @@ "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", @@ -15162,8 +15188,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/zod": { "version": "3.25.76", diff --git a/frontend/public/Login/authentik.svg b/frontend/public/Login/authentik.svg new file mode 100644 index 0000000000..26dc0189ef --- /dev/null +++ b/frontend/public/Login/authentik.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/public/Login/cloudron.svg b/frontend/public/Login/cloudron.svg new file mode 100644 index 0000000000..a4b50c4217 --- /dev/null +++ b/frontend/public/Login/cloudron.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/public/Login/keycloak.svg b/frontend/public/Login/keycloak.svg new file mode 100644 index 0000000000..30628c7b2d --- /dev/null +++ b/frontend/public/Login/keycloak.svg @@ -0,0 +1 @@ + diff --git a/frontend/public/Login/oidc.svg b/frontend/public/Login/oidc.svg new file mode 100644 index 0000000000..440b54487c --- /dev/null +++ b/frontend/public/Login/oidc.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/public/locales/ar-AR/translation.toml b/frontend/public/locales/ar-AR/translation.toml index 0942b37b5a..55a20edc26 100644 --- a/frontend/public/locales/ar-AR/translation.toml +++ b/frontend/public/locales/ar-AR/translation.toml @@ -163,6 +163,11 @@ unfavorite = "؄زالة من المفضلة" fullscreen = "Ų§Ł„ŲŖŲØŲÆŁŠŁ„ ؄لى وضع ملؔ الؓاؓة" sidebar = "Ų§Ł„ŲŖŲØŲÆŁŠŁ„ ؄لى وضع Ų§Ł„Ų“Ų±ŁŠŲ· Ų§Ł„Ų¬Ų§Ł†ŲØŁŠ" +[backendStartup] +notFoundTitle = "لم ŁŠŲŖŁ… Ų§Ł„Ų¹Ų«ŁˆŲ± على الخادم Ų§Ł„Ų®Ł„ŁŁŠ" +retry = "Ų„Ų¹Ų§ŲÆŲ© Ų§Ł„Ł…Ų­Ų§ŁˆŁ„Ų©" +unreachable = "لا ŁŠŁ…ŁƒŁ† Ł„Ł„ŲŖŲ·ŲØŁŠŁ‚ Ų­Ų§Ł„ŁŠŲ§Ł‹ الاتصال بالخادم Ų§Ł„Ų®Ł„ŁŁŠ. تحقق من حالة الخادم ŁˆŲ§Ł„Ų§ŲŖŲµŲ§Ł„ ŲØŲ§Ł„Ų“ŲØŁƒŲ©ŲŒ Ų«Ł… Ų­Ų§ŁˆŁ„ Ł…Ų±Ų© أخرى." + [zipWarning] title = "ملف ZIP كبير" message = "هذا الملف ZIP يحتوي على {{count}} ملفات. هل تريد الاستخراج على أي Ų­Ų§Ł„ŲŸ" @@ -912,6 +917,9 @@ desc = "ابنِ تدفّقات عمل Ł…ŲŖŲ¹ŲÆŲÆŲ© Ų§Ł„Ų®Ų·ŁˆŲ§ŲŖ بسلسلة desc = "تراكب ملف PDF ŁŁˆŁ‚ Ų¢Ų®Ų±" title = "تراكب ملفات PDF" +[home.pdfTextEditor] +title = "Ł…Ų­Ų±Ų± نص PDF" +desc = "حرّر Ų§Ł„Ł†ŲµŁˆŲµ ŁˆŲ§Ł„ŲµŁˆŲ± Ų§Ł„Ł…ŁˆŲ¬ŁˆŲÆŲ© داخل ملفات PDF" [home.addText] tags = "نص,ŲŖŲ¹Ł„ŁŠŁ‚,ŲŖŲ³Ł…ŁŠŲ©" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "ŲŖŁˆŁ‚ŁŠŲ¹ Ł…Ų±Ų³ŁˆŁ…" defaultImageLabel = "ŲŖŁˆŁ‚ŁŠŲ¹ Ł…Ų±ŁŁˆŲ¹" defaultTextLabel = "ŲŖŁˆŁ‚ŁŠŲ¹ Ł…ŁƒŲŖŁˆŲØ" saveButton = "حفظ Ų§Ł„ŲŖŁˆŁ‚ŁŠŲ¹" +savePersonal = "حفظ ؓخصي" +saveShared = "حفظ Ł…Ų“ŲŖŲ±Łƒ" saveUnavailable = "أنؓئ ŲŖŁˆŁ‚ŁŠŲ¹Ų§Ł‹ Ų£ŁˆŁ„Ų§Ł‹ لحفظه." noChanges = "Ų§Ł„ŲŖŁˆŁ‚ŁŠŲ¹ Ų§Ł„Ų­Ų§Ł„ŁŠ Ł…Ų­ŁŁˆŲø بالفعل." +tempStorageTitle = "ŲŖŲ®Ų²ŁŠŁ† مؤقت في المتصفح" +tempStorageDescription = "ŁŠŲŖŁ… ŲŖŲ®Ų²ŁŠŁ† Ų§Ł„ŲŖŁˆŲ§Ł‚ŁŠŲ¹ في Ł…ŲŖŲµŁŲ­Łƒ فقط. Ų³ŲŖŁŁŁ‚ŲÆ Ų„Ų°Ų§ حذفت ŲØŁŠŲ§Ł†Ų§ŲŖ المتصفح أو بدّلت المتصفح." +personalHeading = "ŲŖŁˆŲ§Ł‚ŁŠŲ¹ ؓخصية" +sharedHeading = "ŲŖŁˆŲ§Ł‚ŁŠŲ¹ Ł…Ų“ŲŖŲ±ŁƒŲ©" +personalDescription = "أنت فقط من ŁŠŁ…ŁƒŁ†Ł‡ رؤية هذه Ų§Ł„ŲŖŁˆŲ§Ł‚ŁŠŲ¹." +sharedDescription = "ŁŠŁ…ŁƒŁ† Ł„Ų¬Ł…ŁŠŲ¹ Ų§Ł„Ł…Ų³ŲŖŲ®ŲÆŁ…ŁŠŁ† رؤية هذه Ų§Ł„ŲŖŁˆŲ§Ł‚ŁŠŲ¹ ŁˆŲ§Ų³ŲŖŲ®ŲÆŲ§Ł…Ł‡Ų§." [sign.saved.type] canvas = "رسم" @@ -3020,6 +3036,91 @@ title = "Ų§Ł„Ų­ŲµŁˆŁ„ على Ł…Ų¹Ł„ŁˆŁ…Ų§ŲŖ عن PDF" header = "Ų§Ł„Ų­ŲµŁˆŁ„ على Ł…Ų¹Ł„ŁˆŁ…Ų§ŲŖ عن PDF" submit = "Ų§Ł„Ų­ŲµŁˆŁ„ على Ų§Ł„Ł…Ų¹Ł„ŁˆŁ…Ų§ŲŖ" downloadJson = "ŲŖŲ­Ł…ŁŠŁ„ JSON" +processing = "Ų¬Ų§Ų±Ł Ų§Ų³ŲŖŲ®Ų±Ų§Ų¬ Ų§Ł„Ł…Ų¹Ł„ŁˆŁ…Ų§ŲŖ..." +results = "النتائج" +noResults = "ؓغّل الأداة ل؄نؓاؔ ŲŖŁ‚Ų±ŁŠŲ±." +downloads = "Ų§Ł„ŲŖŁ†Ų²ŁŠŁ„Ų§ŲŖ" +noneDetected = "لم ŁŠŲŖŁ… اكتؓاف أي ؓيؔ" +indexTitle = "الفهرس" + +[getPdfInfo.report] +entryLabel = "ملخص Ų§Ł„Ł…Ų¹Ł„ŁˆŁ…Ų§ŲŖ Ų§Ł„ŁƒŲ§Ł…Ł„" +shortTitle = "Ł…Ų¹Ł„ŁˆŁ…Ų§ŲŖ PDF" + +[getPdfInfo.sections] +metadata = "Ų§Ł„ŲØŁŠŲ§Ł†Ų§ŲŖ Ų§Ł„ŁˆŲµŁŁŠŲ©" +formFields = "Ų­Ł‚ŁˆŁ„ النماذج" +basicInfo = "Ł…Ų¹Ł„ŁˆŁ…Ų§ŲŖ أساسية" +documentInfo = "Ł…Ų¹Ł„ŁˆŁ…Ų§ŲŖ المستند" +compliance = "الامتثال" +encryption = "Ų§Ł„ŲŖŲ“ŁŁŠŲ±" +permissions = "Ų§Ł„Ų£Ų°ŁˆŁ†Ų§ŲŖ" +other = "أخرى" +perPageInfo = "Ł…Ų¹Ł„ŁˆŁ…Ų§ŲŖ Ł„ŁƒŁ„ صفحة" +tableOfContents = "Ų¬ŲÆŁˆŁ„ Ų§Ł„Ł…Ų­ŲŖŁˆŁŠŲ§ŲŖ" + +[getPdfInfo.other] +attachments = "المرفقات" +embeddedFiles = "ملفات مضمنة" +javaScript = "JavaScript" +layers = "الطبقات" +structureTree = "Ų“Ų¬Ų±Ų© Ų§Ł„ŲØŁ†ŁŠŲ©" +xmp = "ŲØŁŠŲ§Ł†Ų§ŲŖ XMP Ų§Ł„ŁˆŲµŁŁŠŲ©" + +[getPdfInfo.perPage] +size = "الحجم" +annotations = "Ų§Ł„ŲŖŲ¹Ł„ŁŠŁ‚Ų§ŲŖ Ų§Ł„ŲŖŁˆŲ¶ŁŠŲ­ŁŠŲ©" +images = "Ų§Ł„ŲµŁˆŲ±" +links = "Ų§Ł„Ų±ŁˆŲ§ŲØŲ·" +fonts = "Ų§Ł„Ų®Ų·ŁˆŲ·" +xobjects = "Ų¹ŲÆŲÆ ŁƒŲ§Ų¦Ł†Ų§ŲŖ XObject" +multimedia = "وسائط Ł…ŲŖŲ¹ŲÆŲÆŲ©" + +[getPdfInfo.summary] +pages = "الصفحات" +fileSize = "حجم الملف" +pdfVersion = "Ų„ŲµŲÆŲ§Ų± PDF" +language = "اللغة" +title = "ملخص PDF" +author = "المؤلف" +created = "ŲŖŁ… ال؄نؓاؔ" +modified = "ŲŖŁ… Ų§Ł„ŲŖŲ¹ŲÆŁŠŁ„" +permsAll = "Ų¬Ł…ŁŠŲ¹ Ų§Ł„Ų£Ų°ŁˆŁ†Ų§ŲŖ Ł…Ų³Ł…ŁˆŲ­ بها" +permsRestricted = "{{count}} Ł‚ŁŠŁˆŲÆ" +permsMixed = "ŲØŲ¹Ų¶ Ų§Ł„Ų£Ų°ŁˆŁ†Ų§ŲŖ Ł…Ł‚ŁŠŁ‘ŲÆŲ©" +hasCompliance = "ŁŠŲŖŲ¶Ł…Ł† Ł…Ų¹Ų§ŁŠŁŠŲ± Ų§Ł…ŲŖŲ«Ų§Ł„" +noCompliance = "لا توجد Ł…Ų¹Ų§ŁŠŁŠŲ± Ų§Ł…ŲŖŲ«Ų§Ł„" +basic = "Ł…Ų¹Ł„ŁˆŁ…Ų§ŲŖ أساسية" +documentInfo = "Ł…Ų¹Ł„ŁˆŁ…Ų§ŲŖ المستند" +securityTitle = "حالة الأمان" +technical = "ŲŖŁ‚Ł†ŁŠ" +overviewTitle = "نظرة Ų¹Ų§Ł…Ų© على PDF" + +[getPdfInfo.summary.security] +encrypted = "PDF مؓفّر - توجد Ų­Ł…Ų§ŁŠŲ© ŲØŁƒŁ„Ł…Ų© Ł…Ų±ŁˆŲ±" +unencrypted = "PDF غير Ł…ŁŲ“ŁŁ‘Ų± - لا توجد Ų­Ł…Ų§ŁŠŲ© ŲØŁƒŁ„Ł…Ų© Ł…Ų±ŁˆŲ±" + +[getPdfInfo.summary.tech] +images = "Ų§Ł„ŲµŁˆŲ±" +fonts = "Ų§Ł„Ų®Ų·ŁˆŲ·" +formFields = "Ų­Ł‚ŁˆŁ„ النماذج" +embeddedFiles = "ملفات مضمنة" +javaScript = "JavaScript" +layers = "الطبقات" +bookmarks = "ال؄ؓارات Ų§Ł„Ł…Ų±Ų¬Ų¹ŁŠŲ©" +multimedia = "وسائط Ł…ŲŖŲ¹ŲÆŲÆŲ©" + +[getPdfInfo.summary.overview] +untitled = "مستند بلا Ų¹Ł†ŁˆŲ§Ł†" +unknown = "مؤلف غير Ł…Ų¹Ų±ŁˆŁ" +text = "هذا ملف PDF يحتوي على {{pages}} صفحة ŲØŲ¹Ł†ŁˆŲ§Ł† {{title}} ŁˆŲŖŁ… ؄نؓاؤه بواسطة {{author}} (Ų„ŲµŲÆŲ§Ų± PDF {{version}})." + +[getPdfInfo.error] +partial = "تعذّر معالجة ŲØŲ¹Ų¶ الملفات." +unexpected = "Ų­ŲÆŲ« Ų®Ų·Ų£ غير Ł…ŲŖŁˆŁ‚Ų¹ أثناؔ الاستخراج." + +[getPdfInfo.status] +complete = "Ų§ŁƒŲŖŁ…Ł„ الاستخراج" [extractPage] tags = "Ų§Ų³ŲŖŲ®Ų±Ų§Ų¬" @@ -3438,6 +3539,9 @@ signinTitle = "الرجاؔ ŲŖŲ³Ų¬ŁŠŁ„ Ų§Ł„ŲÆŲ®ŁˆŁ„" ssoSignIn = "ŲŖŲ³Ų¬ŁŠŁ„ Ų§Ł„ŲÆŲ®ŁˆŁ„ Ų¹ŲØŲ± ŲŖŲ³Ų¬ŁŠŁ„ Ų§Ł„ŲÆŲ®ŁˆŁ„ Ų§Ł„Ų£Ų­Ų§ŲÆŁŠ" oAuth2AutoCreateDisabled = "ŲŖŁ… ŲŖŲ¹Ų·ŁŠŁ„ ال؄نؓاؔ Ų§Ł„ŲŖŁ„Ł‚Ų§Ų¦ŁŠ لمستخدم OAuth2" oAuth2AdminBlockedUser = "ŲŖŁ… Ų­ŲøŲ± ŲŖŲ³Ų¬ŁŠŁ„ أو ŲŖŲ³Ų¬ŁŠŁ„ ŲÆŲ®ŁˆŁ„ Ų§Ł„Ł…Ų³ŲŖŲ®ŲÆŁ…ŁŠŁ† غير Ų§Ł„Ł…Ų³Ų¬Ł„ŁŠŁ† Ų­Ų§Ł„ŁŠŁ‹Ų§. ŁŠŲ±Ų¬Ł‰ الاتصال ŲØŲ§Ł„Ł…Ų³Ų¤ŁˆŁ„." +oAuth2RequiresLicense = "ŁŠŲŖŲ·Ł„ŲØ ŲŖŲ³Ų¬ŁŠŁ„ Ų§Ł„ŲÆŲ®ŁˆŁ„ Ų¹ŲØŲ± OAuth/SSO ŲŖŲ±Ų®ŁŠŲµŲ§Ł‹ Ł…ŲÆŁŁˆŲ¹Ų§Ł‹ (Server أو Enterprise). ŁŠŲ±Ų¬Ł‰ الاتصال ŲØŲ§Ł„Ł…Ų³Ų¤ŁˆŁ„ Ł„ŲŖŲ±Ł‚ŁŠŲ© ŲØŲ§Ł‚ŲŖŁƒ." +saml2RequiresLicense = "ŁŠŲŖŲ·Ł„ŲØ ŲŖŲ³Ų¬ŁŠŁ„ Ų§Ł„ŲÆŲ®ŁˆŁ„ Ų¹ŲØŲ± SAML ŲŖŲ±Ų®ŁŠŲµŲ§Ł‹ Ł…ŲÆŁŁˆŲ¹Ų§Ł‹ (Server أو Enterprise). ŁŠŲ±Ų¬Ł‰ الاتصال ŲØŲ§Ł„Ł…Ų³Ų¤ŁˆŁ„ Ł„ŲŖŲ±Ł‚ŁŠŲ© ŲØŲ§Ł‚ŲŖŁƒ." +maxUsersReached = "ŲŖŁ… Ų§Ł„ŁˆŲµŁˆŁ„ ؄لى الحد الأقصى لعدد Ų§Ł„Ł…Ų³ŲŖŲ®ŲÆŁ…ŁŠŁ† ضمن ترخيصك Ų§Ł„Ų­Ų§Ł„ŁŠ. ŁŠŲ±Ų¬Ł‰ الاتصال ŲØŲ§Ł„Ł…Ų³Ų¤ŁˆŁ„ Ł„ŲŖŲ±Ł‚ŁŠŲ© ŲØŲ§Ł‚ŲŖŁƒ أو ؄ضافة مقاعد ؄ضافية." oauth2RequestNotFound = "لم ŁŠŲŖŁ… Ų§Ł„Ų¹Ų«ŁˆŲ± على طلب Ų§Ł„ŲŖŁŁˆŁŠŲ¶" oauth2InvalidUserInfoResponse = "Ų§Ų³ŲŖŲ¬Ų§ŲØŲ© Ł…Ų¹Ł„ŁˆŁ…Ų§ŲŖ المستخدم غير صالحة" oauth2invalidRequest = "طلب غير صالح" @@ -3771,7 +3875,7 @@ version = "ال؄صدار Ų§Ł„Ų­Ų§Ł„ŁŠ" title = "ŲŖŁˆŲ«ŁŠŁ‚ API" header = "ŲŖŁˆŲ«ŁŠŁ‚ API" desc = "Ų¹Ų±Ų¶ واختبار نقاط Ł†Ł‡Ų§ŁŠŲ© Stirling PDF API" -tags = "api,documentation,swagger,endpoints,development" +tags = "api,ŲŖŁˆŲ«ŁŠŁ‚,swagger,نقاط Ų§Ł„Ł†Ł‡Ų§ŁŠŲ©,تطوير" [cookieBanner.popUp] title = "كيف نستخدم ملفات تعريف الارتباط" @@ -3809,8 +3913,8 @@ title = "Ų§Ł„ŲŖŲ­Ł„ŁŠŁ„Ų§ŲŖ" description = "تساعدنا هذه الملفات على فهم كيفية Ų§Ų³ŲŖŲ®ŲÆŲ§Ł… Ų£ŲÆŁˆŲ§ŲŖŁ†Ų§ŲŒ كي Ł†Ų±ŁƒŁ‘Ų² على بناؔ Ų§Ł„Ł…ŁŠŲ²Ų§ŲŖ Ų§Ł„Ų£ŁƒŲ«Ų± Ł‚ŁŠŁ…Ų© لمجتمعنا. ŁƒŁ† Ł…Ų·Ł…Ų¦Ł†Ł‹Ų§ā€”ā€Stirling PDF لا ŁŠŁ…ŁƒŁ†Ł‡ ŁˆŁ„Ł† يتتبع Ł…Ų­ŲŖŁˆŁ‰ المستندات Ų§Ł„ŲŖŁŠ تعمل Ų¹Ł„ŁŠŁ‡Ų§." [cookieBanner.services] -posthog = "PostHog Analytics" -scarf = "Scarf Pixel" +posthog = "ŲŖŲ­Ł„ŁŠŁ„Ų§ŲŖ PostHog" +scarf = "Scarf ŲØŁƒŲ³Ł„" [removeMetadata] submit = "؄زالة Ų§Ł„ŲØŁŠŲ§Ł†Ų§ŲŖ Ų§Ł„ŁˆŲµŁŁŠŲ©" @@ -3846,14 +3950,17 @@ fitToWidth = "ملاؔمة للعرض" actualSize = "الحجم Ų§Ł„ŁŲ¹Ł„ŁŠ" [viewer] +cannotPreviewFile = "لا ŁŠŁ…ŁƒŁ† Ł…Ų¹Ų§ŁŠŁ†Ų© الملف" +dualPageView = "Ų¹Ų±Ų¶ ŲµŁŲ­ŲŖŁŠŁ†" firstPage = "الصفحة Ų§Ł„Ų£ŁˆŁ„Ł‰" lastPage = "الصفحة Ų§Ł„Ų£Ų®ŁŠŲ±Ų©" -previousPage = "الصفحة السابقة" nextPage = "الصفحة Ų§Ł„ŲŖŲ§Ł„ŁŠŲ©" +onlyPdfSupported = "Ų¹Ų§Ų±Ų¶ الملفات ŁŠŲÆŲ¹Ł… ملفات PDF فقط. يبدو أن هذا الملف ŲØŲŖŁ†Ų³ŁŠŁ‚ مختلف." +previousPage = "الصفحة السابقة" +singlePageView = "Ų¹Ų±Ų¶ صفحة واحدة" +unknownFile = "ملف غير Ł…Ų¹Ų±ŁˆŁ" zoomIn = "تكبير" zoomOut = "تصغير" -singlePageView = "Ų¹Ų±Ų¶ صفحة واحدة" -dualPageView = "Ų¹Ų±Ų¶ ŲµŁŲ­ŲŖŁŠŁ†" [rightRail] closeSelected = "؄غلاق الصفحات المحددة" @@ -3877,6 +3984,7 @@ toggleSidebar = "ŲŖŲØŲÆŁŠŁ„ Ų§Ł„Ų“Ų±ŁŠŲ· Ų§Ł„Ų¬Ų§Ł†ŲØŁŠ" exportSelected = "تصدير الصفحات المحددة" toggleAnnotations = "ŲŖŲØŲÆŁŠŁ„ ŲøŁ‡ŁˆŲ± Ų§Ł„ŲŖŲ¹Ł„ŁŠŁ‚Ų§ŲŖ Ų§Ł„ŲŖŁˆŲ¶ŁŠŲ­ŁŠŲ©" annotationMode = "ŲŖŲØŲÆŁŠŁ„ وضع Ų§Ł„ŲŖŲ¹Ł„ŁŠŁ‚Ų§ŲŖ" +print = "Ų·ŲØŲ§Ų¹Ų© PDF" draw = "رسم" save = "حفظ" saveChanges = "حفظ Ų§Ł„ŲŖŲŗŁŠŁŠŲ±Ų§ŲŖ" @@ -4494,6 +4602,7 @@ description = "Ų¹Ł†ŁˆŲ§Ł† URL أو اسم الملف الخاص ŲØŁ€ Impressum ( title = "الممتاز ŁˆŲ§Ł„Ł…Ų¤Ų³Ų³ŁŠ" description = "ŲŖŁ‡ŁŠŲ¦Ų© مفتاح Ų§Ł„ŲŖŲ±Ų®ŁŠŲµ Ł„Ł„Ł…Ų²Ų§ŁŠŲ§ الممتازة أو Ų§Ł„Ł…Ų¤Ų³Ų³ŁŠŲ©." license = "ŲŖŁ‡ŁŠŲ¦Ų© Ų§Ł„ŲŖŲ±Ų®ŁŠŲµ" +noInput = "ŁŠŲ±Ų¬Ł‰ ŲŖŁ‚ŲÆŁŠŁ… مفتاح ترخيص أو ملف" [admin.settings.premium.licenseKey] toggle = "هل Ł„ŲÆŁŠŁƒ مفتاح ترخيص أو ملف Ų“Ł‡Ų§ŲÆŲ©ŲŸ" @@ -4511,6 +4620,25 @@ line1 = "لا ŁŠŁ…ŁƒŁ† التراجع عن Ų§Ų³ŲŖŲØŲÆŲ§Ł„ مفتاح الترخ line2 = "Ų³ŁŠŁŁŁ‚ŲÆ ترخيصك السابق Ł†Ł‡Ų§Ų¦ŁŠŲ§Ł‹ Ł…Ų§ لم ŲŖŁƒŁ† قد احتفظت بنسخة احتياطية منه في Ł…ŁƒŲ§Ł† Ų¢Ų®Ų±." line3 = "مهم: احتفظ ŲØŁ…ŁŲ§ŲŖŁŠŲ­ Ų§Ł„ŲŖŲ±Ų®ŁŠŲµ Ų®Ų§ŲµŲ© ŁˆŲ¢Ł…Ł†Ų©. لا ŲŖŲ“Ų§Ų±ŁƒŁ‡Ų§ علناً Ų£ŲØŲÆŲ§Ł‹." +[admin.settings.premium.inputMethod] +text = "مفتاح Ų§Ł„ŲŖŲ±Ų®ŁŠŲµ" +file = "ملف الؓهادة" + +[admin.settings.premium.file] +label = "ملف ؓهادة Ų§Ł„ŲŖŲ±Ų®ŁŠŲµ" +description = "قم ŲØŲŖŲ­Ł…ŁŠŁ„ ملف Ų§Ł„ŲŖŲ±Ų®ŁŠŲµ .lic أو .cert من Ų¹Ł…Ł„ŁŠŲ§ŲŖ الؓراؔ ŲÆŁˆŁ† Ų§ŲŖŲµŲ§Ł„" +choose = "Ų§Ų®ŲŖŲ± ملف Ų§Ł„ŲŖŲ±Ų®ŁŠŲµ" +selected = "المحدد: {{filename}} ({{size}})" +successMessage = "ŲŖŁ… ŲŖŲ­Ł…ŁŠŁ„ ملف Ų§Ł„ŲŖŲ±Ų®ŁŠŲµ ŁˆŲŖŁŲ¹ŁŠŁ„Ł‡ بنجاح. لا ŁŠŁ„Ų²Ł… Ų„Ų¹Ų§ŲÆŲ© Ų§Ł„ŲŖŲ“ŲŗŁŠŁ„." + +[admin.settings.premium.currentLicense] +title = "Ų§Ł„ŲŖŲ±Ų®ŁŠŲµ النؓط" +file = "المصدر: ملف Ų§Ł„ŲŖŲ±Ų®ŁŠŲµ ({{path}})" +key = "المصدر: مفتاح Ų§Ł„ŲŖŲ±Ų®ŁŠŲµ" +type = "Ų§Ł„Ł†ŁˆŲ¹: {{type}}" +noInput = "ŁŠŲ±Ų¬Ł‰ ŲŖŁ‚ŲÆŁŠŁ… مفتاح ترخيص أو ŲŖŲ­Ł…ŁŠŁ„ ملف ؓهادة" +success = "نجاح" + [admin.settings.premium.enabled] label = "ŲŖŁ…ŁƒŁŠŁ† Ų§Ł„Ł…ŁŠŲ²Ų§ŲŖ الممتازة" description = "ŲŖŁ…ŁƒŁŠŁ† التحقق من مفتاح Ų§Ł„ŲŖŲ±Ų®ŁŠŲµ Ł„Ł…ŁŠŲ²Ų§ŲŖ Pro/المؤسسة" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} Ł…Ų­ŲÆŲÆ" download = "ŲŖŁ†Ų²ŁŠŁ„" delete = "حذف" unsupported = "غير Ł…ŲÆŲ¹ŁˆŁ…" +active = "نؓط" addToUpload = "؄ضافة ؄لى الرفع" +closeFile = "؄غلاق الملف" deleteAll = "حذف Ų§Ł„ŁƒŁ„" loadingFiles = "Ų¬Ų§Ų±Ł ŲŖŲ­Ł…ŁŠŁ„ الملفات..." noFiles = "لا توجد ملفات Ł…ŲŖŲ§Ų­Ų©" @@ -5132,7 +5262,7 @@ upgrade = "Ų§Ł„ŲŖŲ±Ł‚ŁŠŲ© الآن →" freeTitle = "ترخيص الخادم" overLimitTitle = "Ł…Ų·Ł„ŁˆŲØ ترخيص Ų®Ų§ŲÆŁ…" overLimitBody = "ŲŖŲ±Ų®ŁŠŲµŁ†Ų§ ŁŠŲ³Ł…Ų­ حتى {{freeTierLimit}} Ł…Ų³ŲŖŲ®ŲÆŁ…ŁŠŁ† مجاناً Ł„ŁƒŁ„ Ų®Ų§ŲÆŁ…. Ł„ŲÆŁŠŁƒ {{overLimitUserCopy}} Ł…Ų³ŲŖŲ®ŲÆŁ…ŁŠ Stirling. للمتابعة ŲÆŁˆŁ† Ų§Ł†Ł‚Ų·Ų§Ų¹ŲŒ Ų§Ų±Ł‚ŁŽ ؄لى Ų®Ų·Ų© Ų®Ų§ŲÆŁ… Stirling - مقاعد غير Ł…Ų­ŲÆŁˆŲÆŲ©ŲŒ تحرير Ł†ŲµŁˆŲµ PDF، ŁˆŲŖŲ­ŁƒŁ… ؄داري ŁƒŲ§Ł…Ł„ مقابل $99/Ų®Ų§ŲÆŁ…/Ų“Ł‡Ų±ŁŠŲ§Ł‹." -freeBody = "ترخيص Open-Core Ł„ŲÆŁŠŁ†Ų§ ŁŠŲ³Ł…Ų­ حتى {{freeTierLimit}} Ł…Ų³ŲŖŲ®ŲÆŁ…ŁŠŁ† مجاناً Ł„ŁƒŁ„ Ų®Ų§ŲÆŁ…. Ł„Ł„ŲŖŁˆŲ³Ų¹ بسلاسة ŁˆŲ§Ł„Ų­ŲµŁˆŁ„ على ŁˆŲµŁˆŁ„ Ł…ŲØŁƒŲ± ؄لى Ų£ŲÆŲ§Ų© تحرير Ł†ŲµŁˆŲµ PDF Ų§Ł„Ų¬ŲÆŁŠŲÆŲ©ŲŒ Ł†ŁˆŲµŁŠ ŲØŲ®Ų·Ų© Ų®Ų§ŲÆŁ… Stirling - تحرير ŁƒŲ§Ł…Ł„ ŁˆŁ…Ł‚Ų§Ų¹ŲÆ غير Ł…Ų­ŲÆŁˆŲÆŲ© مقابل $99/Ų®Ų§ŲÆŁ…/Ų“Ł‡Ų±ŁŠŲ§Ł‹." +freeBody = "يتيح ŲŖŲ±Ų®ŁŠŲµŁ†Ų§ Open-Core Ł…Ų§ ŁŠŲµŁ„ ؄لى {{freeTierLimit}} مستخدمًا مجانًا Ł„ŁƒŁ„ Ų®Ų§ŲÆŁ…. Ł„Ł„ŲŖŁˆŲ³Ų¹ ŲÆŁˆŁ† Ų§Ł†Ł‚Ų·Ų§Ų¹ŲŒ Ł†ŁˆŲµŁŠ ŲØŲ®Ų·Ų© Stirling Server - مقاعد غير Ł…Ų­ŲÆŁˆŲÆŲ© ŁˆŲÆŲ¹Ł… SSO مقابل $99/server/mo." [onboarding.desktopInstall] title = "ŲŖŁ†Ų²ŁŠŁ„" @@ -5237,6 +5367,31 @@ error = "فؓل تحديث حالة المستخدم" success = "ŲŖŁ… حذف المستخدم بنجاح" error = "فؓل حذف المستخدم" +[workspace.people.changePassword] +action = "تغيير ŁƒŁ„Ł…Ų© Ų§Ł„Ł…Ų±ŁˆŲ±" +title = "تغيير ŁƒŁ„Ł…Ų© Ų§Ł„Ł…Ų±ŁˆŲ±" +subtitle = "تحديث ŁƒŁ„Ł…Ų© Ų§Ł„Ł…Ų±ŁˆŲ± لـ" +newPassword = "ŁƒŁ„Ł…Ų© Ł…Ų±ŁˆŲ± جديدة" +confirmPassword = "تأكيد ŁƒŁ„Ł…Ų© Ų§Ł„Ł…Ų±ŁˆŲ±" +placeholder = "أدخل ŁƒŁ„Ł…Ų© Ł…Ų±ŁˆŲ± جديدة" +confirmPlaceholder = "Ų£Ų¹ŲÆ Ų„ŲÆŲ®Ų§Ł„ ŁƒŁ„Ł…Ų© Ų§Ł„Ł…Ų±ŁˆŲ± Ų§Ł„Ų¬ŲÆŁŠŲÆŲ©" +passwordRequired = "ŁŠŲ±Ų¬Ł‰ Ų„ŲÆŲ®Ų§Ł„ ŁƒŁ„Ł…Ų© Ł…Ų±ŁˆŲ± جديدة" +passwordMismatch = "ŁƒŁ„Ł…ŲŖŲ§ Ų§Ł„Ł…Ų±ŁˆŲ± غير Ł…ŲŖŲ·Ų§ŲØŁ‚ŲŖŁŠŁ†" +generateRandom = "؄نؓاؔ ŁƒŁ„Ł…Ų© Ł…Ų±ŁˆŲ± آمنة" +generatedPreview = "ŁƒŁ„Ł…Ų© Ų§Ł„Ł…Ų±ŁˆŲ± Ų§Ł„Ł…ŁŁ†Ų“Ų£Ų©:" +copyTooltip = "نسخ ؄لى الحافظة" +copiedToClipboard = "ŲŖŁ… نسخ ŁƒŁ„Ł…Ų© Ų§Ł„Ł…Ų±ŁˆŲ± ؄لى الحافظة" +copyFailed = "فؓل نسخ ŁƒŁ„Ł…Ų© Ų§Ł„Ł…Ų±ŁˆŲ±" +sendEmail = "Ų„Ų±Ų³Ų§Ł„ بريد Ų„Ł„ŁƒŲŖŲ±ŁˆŁ†ŁŠ للمستخدم Ų­ŁˆŁ„ هذا Ų§Ł„ŲŖŲŗŁŠŁŠŲ±" +includePassword = "ŲŖŲ¶Ł…ŁŠŁ† ŁƒŁ„Ł…Ų© Ų§Ł„Ł…Ų±ŁˆŲ± Ų§Ł„Ų¬ŲÆŁŠŲÆŲ© في Ų§Ł„ŲØŲ±ŁŠŲÆ Ų§Ł„Ų„Ł„ŁƒŲŖŲ±ŁˆŁ†ŁŠ" +forcePasswordChange = "؄لزام المستخدم بتغيير ŁƒŁ„Ł…Ų© Ų§Ł„Ł…Ų±ŁˆŲ± عند ŲŖŲ³Ų¬ŁŠŁ„ Ų§Ł„ŲÆŲ®ŁˆŁ„ Ų§Ł„ŲŖŲ§Ł„ŁŠ" +emailUnavailable = "بريد هذا المستخدم Ų§Ł„Ų„Ł„ŁƒŲŖŲ±ŁˆŁ†ŁŠ غير صالح. ŲŖŁ… ŲŖŲ¹Ų·ŁŠŁ„ ال؄ؓعارات." +smtpDisabled = "تتطلب Ų„Ų“Ų¹Ų§Ų±Ų§ŲŖ Ų§Ł„ŲØŲ±ŁŠŲÆ Ų§Ł„Ų„Ł„ŁƒŲŖŲ±ŁˆŁ†ŁŠ ŲŖŁŲ¹ŁŠŁ„ SMTP في ال؄عدادات." +notifyOnly = "Ų³ŁŠŲŖŁ… Ų„Ų±Ų³Ų§Ł„ بريد Ų„Ł„ŁƒŲŖŲ±ŁˆŁ†ŁŠ ŲØŲÆŁˆŁ† ŁƒŁ„Ł…Ų© Ų§Ł„Ł…Ų±ŁˆŲ± ل؄بلاغ المستخدم بأن المؓرف قد ŲŗŁŠŁ‘Ų±Ł‡Ų§." +submit = "تحديث ŁƒŁ„Ł…Ų© Ų§Ł„Ł…Ų±ŁˆŲ±" +success = "ŲŖŁ… تحديث ŁƒŁ„Ł…Ų© Ų§Ł„Ł…Ų±ŁˆŲ± بنجاح" +error = "فؓل تحديث ŁƒŁ„Ł…Ų© Ų§Ł„Ł…Ų±ŁˆŲ±" + [workspace.people.emailInvite] tab = "دعوة Ų¹ŲØŲ± Ų§Ł„ŲØŲ±ŁŠŲÆ Ų§Ł„Ų„Ł„ŁƒŲŖŲ±ŁˆŁ†ŁŠ" description = "اكتب أو الصق Ų¹Ł†Ų§ŁˆŁŠŁ† Ų§Ł„ŲØŲ±ŁŠŲÆ Ų§Ł„Ų„Ł„ŁƒŲŖŲ±ŁˆŁ†ŁŠ أدناه Ł…ŁŲµŁˆŁ„Ų© ŲØŁŁˆŲ§ŲµŁ„. Ų³ŁŠŲŖŁ„Ł‚Ł‰ Ų§Ł„Ł…Ų³ŲŖŲ®ŲÆŁ…ŁˆŁ† ŲØŁŠŲ§Ł†Ų§ŲŖ Ų§Ų¹ŲŖŁ…Ų§ŲÆ ŲŖŲ³Ų¬ŁŠŁ„ Ų§Ł„ŲÆŲ®ŁˆŁ„ Ų¹ŲØŲ± Ų§Ł„ŲØŲ±ŁŠŲÆ Ų§Ł„Ų„Ł„ŁƒŲŖŲ±ŁˆŁ†ŁŠ." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Ł…Ų·Ł„ŁˆŲØ Ų¹Ł†ŁˆŲ§Ł† بريد Ų„Ł„ŁƒŲŖŲ±ŁˆŁ†ŁŠ واحد على الأقل" submit = "Ų„Ų±Ų³Ų§Ł„ Ų§Ł„ŲÆŲ¹ŁˆŲ§ŲŖ" success = "ŲŖŁ…ŲŖ دعوة المستخدم/Ų§Ł„Ł…Ų³ŲŖŲ®ŲÆŁ…ŁŠŁ† بنجاح" -partialSuccess = "فؓلت ŲØŲ¹Ų¶ Ų§Ł„ŲÆŲ¹ŁˆŲ§ŲŖ" +partialFailure = "فؓل ŲØŲ¹Ų¶ Ų§Ł„ŲÆŲ¹ŁˆŲ§ŲŖ" allFailed = "فؓلت دعوة Ų§Ł„Ł…Ų³ŲŖŲ®ŲÆŁ…ŁŠŁ†" error = "فؓل Ų„Ų±Ų³Ų§Ł„ Ų§Ł„ŲÆŲ¹ŁˆŲ§ŲŖ" @@ -5770,6 +5925,7 @@ subtitle = "سجّل Ų§Ł„ŲÆŲ®ŁˆŁ„ ŲØŲ­Ų³Ų§ŲØ Stirling الخاص بك" [setup.selfhosted] title = "سجّل Ų§Ł„ŲÆŲ®ŁˆŁ„ ؄لى الخادم" subtitle = "أدخل ŲØŁŠŲ§Ł†Ų§ŲŖ Ų§Ų¹ŲŖŁ…Ų§ŲÆ الخادم" +link = "أو الاتصال ŲØŲ­Ų³Ų§ŲØ Ł…ŁŲ³ŲŖŲ¶Ų§Ł Ų°Ų§ŲŖŁŠŁ‹Ų§" [setup.server] title = "الاتصال بالخادم" @@ -5788,6 +5944,14 @@ description = "أدخل Ų¹Ł†ŁˆŲ§Ł† URL Ų§Ł„ŁƒŲ§Ł…Ł„ لخادم Stirling PDF Ų§Ł„ emptyUrl = "ŁŠŲ±Ų¬Ł‰ Ų„ŲÆŲ®Ų§Ł„ Ų¹Ł†ŁˆŲ§Ł† URL للخادم" unreachable = "تعذّر الاتصال بالخادم" testFailed = "فؓل Ų§Ų®ŲŖŲØŲ§Ų± الاتصال" +configFetch = "فؓل في جلب Ų„Ų¹ŲÆŲ§ŲÆŲ§ŲŖ الخادم. ŁŠŲ±Ų¬Ł‰ التحقق من Ų¹Ł†ŁˆŲ§Ł† URL ŁˆŲ§Ł„Ł…Ų­Ų§ŁˆŁ„Ų© Ł…Ų±Ų© أخرى." + +[setup.server.error.securityDisabled] +title = "ŲŖŲ³Ų¬ŁŠŁ„ Ų§Ł„ŲÆŲ®ŁˆŁ„ غير مفعّل" +body = "لا يحتوي هذا الخادم على ŲŖŲ³Ų¬ŁŠŁ„ ŲÆŲ®ŁˆŁ„ مفعّل. للاتصال بهذا Ų§Ł„Ų®Ų§ŲÆŁ…ŲŒ يجب ŲŖŁ…ŁƒŁŠŁ† المصادقة:" +step1 = "Ų¹ŁŠŁ‘Ł† DOCKER_ENABLE_SECURITY=true في بيئتك" +step2 = "أو Ų¹ŁŠŁ‘Ł† security.enableLogin=true في settings.yml" +step3 = "Ų£Ų¹ŲÆ ŲŖŲ“ŲŗŁŠŁ„ الخادم" [setup.login] title = "ŲŖŲ³Ų¬ŁŠŁ„ Ų§Ł„ŲÆŲ®ŁˆŁ„" @@ -5797,6 +5961,13 @@ submit = "ŲŖŲ³Ų¬ŁŠŁ„ Ų§Ł„ŲÆŲ®ŁˆŁ„" signInWith = "ŲŖŲ³Ų¬ŁŠŁ„ Ų§Ł„ŲÆŲ®ŁˆŁ„ ŲØŲ§Ų³ŲŖŲ®ŲÆŲ§Ł…" oauthPending = "Ų¬Ų§Ų±Ł فتح المتصفح للمصادقة..." orContinueWith = "أو المتابعة ŲØŲ§Ł„ŲØŲ±ŁŠŲÆ Ų§Ł„Ų„Ł„ŁƒŲŖŲ±ŁˆŁ†ŁŠ" +serverRequirement = "ملاحظة: يجب أن ŁŠŁƒŁˆŁ† ŲŖŲ³Ų¬ŁŠŁ„ Ų§Ł„ŲÆŲ®ŁˆŁ„ مفعّلاً على الخادم." +showInstructions = "كيفية Ų§Ł„ŲŖŁ…ŁƒŁŠŁ†ŲŸ" +hideInstructions = "؄خفاؔ ال؄رؓادات" +instructions = "Ł„ŲŖŁ…ŁƒŁŠŁ† ŲŖŲ³Ų¬ŁŠŁ„ Ų§Ł„ŲÆŲ®ŁˆŁ„ على Ų®Ų§ŲÆŁ… Stirling PDF الخاص بك:" +instructionsEnvVar = "Ų¹ŁŠŁ‘Ł† Ł…ŲŖŲŗŁŠŁ‘Ų± Ų§Ł„ŲØŁŠŲ¦Ų©:" +instructionsOrYml = "أو في settings.yml:" +instructionsRestart = "Ų«Ł… Ų£Ų¹ŲÆ ŲŖŲ“ŲŗŁŠŁ„ الخادم لتصبح Ų§Ł„ŲŖŲŗŁŠŁŠŲ±Ų§ŲŖ نافذة." [setup.login.username] label = "اسم المستخدم" @@ -5853,6 +6024,7 @@ earlyAccess = "ŁˆŲµŁˆŁ„ Ł…ŲØŁƒŲ±" reset = "Ų„Ų¹Ų§ŲÆŲ© ŲŖŲ¹ŁŠŁŠŁ† Ų§Ł„ŲŖŲŗŁŠŁŠŲ±Ų§ŲŖ" downloadJson = "ŲŖŁ†Ų²ŁŠŁ„ JSON" generatePdf = "ŲŖŁˆŁ„ŁŠŲÆ PDF" +saveChanges = "حفظ Ų§Ł„ŲŖŲŗŁŠŁŠŲ±Ų§ŲŖ" [pdfTextEditor.options.autoScaleText] title = "Ų¶ŲØŲ· النص ŲŖŁ„Ł‚Ų§Ų¦ŁŠŲ§Ł‹ Ł„ŁŠŲŖŁ†Ų§Ų³ŲØ Ł…Ų¹ Ų§Ł„ŲµŁ†Ų§ŲÆŁŠŁ‚" @@ -5890,6 +6062,8 @@ alpha = "هذا العارض بنسخة ألفا ŁˆŁ„Ų§ ŁŠŲ²Ų§Ł„ ŁŠŲŖŲ·ŁˆŲ±ā€”Ł‚ [pdfTextEditor.empty] title = "لم ŁŠŲŖŁ… ŲŖŲ­Ł…ŁŠŁ„ مستند" subtitle = "حمّل ملف PDF أو JSON لبدؔ تحرير Ł…Ų­ŲŖŁˆŁ‰ النص." +dropzone = "Ų§Ų³Ų­ŲØ ŁˆŲ£ŁŁ„ŲŖ ملف PDF أو JSON Ł‡Ł†Ų§ŲŒ أو انقر للاستعراض" +dropzoneWithFiles = "Ų­ŲÆŲÆ ملفًا من علامة تبويب Ų§Ł„Ł…Ł„ŁŲ§ŲŖŲŒ أو Ų§Ų³Ų­ŲØ ŁˆŲ£ŁŁ„ŲŖ ملف PDF أو JSON Ł‡Ł†Ų§ŲŒ أو انقر للاستعراض" [pdfTextEditor.welcomeBanner] title = "Ł…Ų±Ų­ŲØŲ§Ł‹ بك في Ł…Ų­Ų±Ų± Ł†ŲµŁˆŲµ PDF (ŁˆŲµŁˆŁ„ Ł…ŲØŁƒŲ±)" diff --git a/frontend/public/locales/az-AZ/translation.toml b/frontend/public/locales/az-AZ/translation.toml index d4c20aea64..5512e9e55f 100644 --- a/frontend/public/locales/az-AZ/translation.toml +++ b/frontend/public/locales/az-AZ/translation.toml @@ -163,6 +163,11 @@ unfavorite = "SeƧilmişlərdən Ƨıxar" fullscreen = "Tam ekran rejiminə keƧ" sidebar = "Yan panel rejiminə keƧ" +[backendStartup] +notFoundTitle = "Backend tapılmadı" +retry = "Yenidən cəhd et" +unreachable = "Tətbiq hazırda backend-ə qoşula bilmir. Backend-in vəziyyətini və şəbəkə bağlantısını yoxlayın, sonra yenidən cəhd edin." + [zipWarning] title = "Bƶyük ZIP faylı" message = "Bu ZIP {{count}} fayl ehtiva edir. Yenə də Ƨıxarılsın?" @@ -912,6 +917,9 @@ desc = "PDF əməliyyatlarını zəncirləyərək Ƨoxaddımlı iş axınları q desc = "Bir PDF-i digərinin üstünə qoyur" title = "Üst-Üstə Qoy" +[home.pdfTextEditor] +title = "PDF Mətn Redaktoru" +desc = "PDF-lərin iƧindəki mƶvcud mətn və şəkilləri redaktə edin" [home.addText] tags = "mətn,şərh,etiket" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Ƈəkilmiş imza" defaultImageLabel = "Yüklənmiş imza" defaultTextLabel = "Yazılmış imza" saveButton = "İmzanı saxla" +savePersonal = "Şəxsi yadda saxla" +saveShared = "Paylaşılanı yadda saxla" saveUnavailable = "Saxlamaq üçün əvvəlcə imza yaradın." noChanges = "Cari imza artıq saxlanıb." +tempStorageTitle = "Müvəqqəti brauzer yaddaşı" +tempStorageDescription = "İmzalar yalnız brauzerinizdə saxlanılır. Brauzer məlumatlarını təmizləsəniz və ya brauzer dəyişsəniz, itəcək." +personalHeading = "Şəxsi imzalar" +sharedHeading = "Paylaşılan imzalar" +personalDescription = "Bu imzaları yalnız siz gƶrə bilirsiniz." +sharedDescription = "Bütün istifadəƧilər bu imzaları gƶrə və istifadə edə bilərlər." [sign.saved.type] canvas = "Rəsm" @@ -3020,6 +3036,91 @@ title = "PDF Barəsində Məlumat ʏldə Et" header = "PDF Barəsində Məlumat ʏldə Et" submit = "Məlumat ʏldə Et" downloadJson = "JSON yüklə" +processing = "Məlumat Ƨıxarılır..." +results = "Nəticələr" +noResults = "Hesabat yaratmaq üçün aləti işə salın." +downloads = "Yükləmələr" +noneDetected = "HeƧ nə aşkar edilmədi" +indexTitle = "İndeks" + +[getPdfInfo.report] +entryLabel = "Tam məlumat xülasəsi" +shortTitle = "PDF Məlumatı" + +[getPdfInfo.sections] +metadata = "Metaməlumat" +formFields = "Forma sahələri" +basicInfo = "ʏsas Məlumat" +documentInfo = "Sənəd Məlumatı" +compliance = "Uyğunluq" +encryption = "Şifrələmə" +permissions = "İcazələr" +other = "Digər" +perPageInfo = "Hər səhifə üzrə məlumat" +tableOfContents = "Mündəricat" + +[getPdfInfo.other] +attachments = "ʏlavələr" +embeddedFiles = "Gƶmülü fayllar" +javaScript = "JavaScript" +layers = "Qatlar" +structureTree = "Struktur ağacı" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Ɩlçü" +annotations = "Annotasiyalar" +images = "Şəkillər" +links = "KeƧidlər" +fonts = "Şriftlər" +xobjects = "XObject sayları" +multimedia = "Multimedia" + +[getPdfInfo.summary] +pages = "Səhifələr" +fileSize = "Fayl Ɩlçüsü" +pdfVersion = "PDF Versiyası" +language = "Dil" +title = "PDF Xülasəsi" +author = "Müəllif" +created = "Yaradılıb" +modified = "Dəyişdirilib" +permsAll = "Bütün icazələr verilib" +permsRestricted = "{{count}} məhdudiyyət" +permsMixed = "Bəzi icazələr məhdudlaşdırılıb" +hasCompliance = "Uyğunluq standartları mƶvcuddur" +noCompliance = "Uyğunluq standartları yoxdur" +basic = "ʏsas Məlumat" +documentInfo = "Sənəd Məlumatı" +securityTitle = "Təhlükəsizlik Vəziyyəti" +technical = "Texniki" +overviewTitle = "PDF İcmalı" + +[getPdfInfo.summary.security] +encrypted = "Şifrələnmiş PDF - Parol ilə qorunur" +unencrypted = "Şifrələnməmiş PDF - Parol qorunması yoxdur" + +[getPdfInfo.summary.tech] +images = "Şəkillər" +fonts = "Şriftlər" +formFields = "Forma sahələri" +embeddedFiles = "Gƶmülü fayllar" +javaScript = "JavaScript" +layers = "Qatlar" +bookmarks = "ʏlfəcinlər" +multimedia = "Multimedia" + +[getPdfInfo.summary.overview] +untitled = "adsız sənəd" +unknown = "Naməlum müəllif" +text = "Bu, {{author}} tərəfindən yaradılmış, {{title}} adlı, {{pages}} səhifəlik PDF-dir (PDF versiyası {{version}})." + +[getPdfInfo.error] +partial = "Bəzi faylları emal etmək mümkün olmadı." +unexpected = "Ƈıxarılma zamanı gƶzlənilməz xəta baş verdi." + +[getPdfInfo.status] +complete = "Ƈıxarılma tamamlandı" [extractPage] tags = "Ƨıxar" @@ -3438,6 +3539,9 @@ signinTitle = "Zəhmət olmasa, daxil olun" ssoSignIn = "Single Sign-on vasitəsilə daxil olun" oAuth2AutoCreateDisabled = "OAUTH2 Auto-Create İstifadəƧisi Deaktivləşdirilmişdir" oAuth2AdminBlockedUser = "Qeydiyyatdan keƧməmiş istifadəƧilərin qeydiyyatı və daxil olması hal-hazırda bloklanmışdır. Zəhmət olmasa, administratorla əlaqə saxlayın." +oAuth2RequiresLicense = "OAuth/SSO ilə giriş üçün ƶdənişli lisenziya (Server və ya Enterprise) tələb olunur. Planınızı yüksəltmək üçün administratorla əlaqə saxlayın." +saml2RequiresLicense = "SAML ilə giriş üçün ƶdənişli lisenziya (Server və ya Enterprise) tələb olunur. Planınızı yüksəltmək üçün administratorla əlaqə saxlayın." +maxUsersReached = "Mƶvcud lisenziyanız üçün maksimum istifadəƧi sayına Ƨatılıb. Planınızı yüksəltmək və ya əlavə yerlər əlavə etmək üçün administratorla əlaqə saxlayın." oauth2RequestNotFound = "Təsdiqlənmə sorğusu tapılmadı" oauth2InvalidUserInfoResponse = "Yanlış İstifadəƧi Məlumatı Cavabı" oauth2invalidRequest = "Etibarsız Sorğu" @@ -3846,14 +3950,17 @@ fitToWidth = "Eninə sığdır" actualSize = "Həqiqi ƶlçü" [viewer] +cannotPreviewFile = "Faylın ƶnizlənməsi mümkün deyil" +dualPageView = "İki Səhifə Gƶrünüşü" firstPage = "Birinci səhifə" lastPage = "Son səhifə" -previousPage = "ʏvvəlki səhifə" nextPage = "Nƶvbəti səhifə" +onlyPdfSupported = "Gƶrüntüləyici yalnız PDF fayllarını dəstəkləyir. Bu fayl fərqli formatda gƶrünür." +previousPage = "ʏvvəlki səhifə" +singlePageView = "Tək Səhifə Gƶrünüşü" +unknownFile = "Naməlum fayl" zoomIn = "Bƶyüt" zoomOut = "KiƧilt" -singlePageView = "Tək Səhifə Gƶrünüşü" -dualPageView = "İki Səhifə Gƶrünüşü" [rightRail] closeSelected = "SeƧilmiş faylları bağla" @@ -3877,6 +3984,7 @@ toggleSidebar = "Yan paneli aƧ/bağla" exportSelected = "SeƧilmiş səhifələri ixrac et" toggleAnnotations = "Annotasiyaların gƶrünməsini dəyiş" annotationMode = "Annotasiya rejimini dəyiş" +print = "PDF-i Ƨap et" draw = "Rəsm Ƨək" save = "Yadda saxla" saveChanges = "Dəyişiklikləri yadda saxla" @@ -4407,7 +4515,7 @@ description = "Daha geniş sistem müvəqqəti qovluğunu təmizləyib-təmizlə label = "Proses İcraedicisi Limitləri" description = "Hər icraedici üçün sessiya limitlərini və taym-outları konfiqurasiya edin" libreOffice = "LibreOffice" -pdfToHtml = "PDF to HTML" +pdfToHtml = "PDF-dən HTML-ə" qpdf = "QPDF" tesseract = "Tesseract OCR" pythonOpenCv = "Python OpenCV" @@ -4494,6 +4602,7 @@ description = "Impressum üçün URL və ya fayl adı (bəzi yurisdiksiyalarda t title = "Premium və Enterprise" description = "Premium və ya enterprise lisenziya aƧarınızı konfiqurasiya edin." license = "Lisenziya Konfiqurasiyası" +noInput = "Zəhmət olmasa lisenziya aƧarı və ya fayl təqdim edin" [admin.settings.premium.licenseKey] toggle = "Lisenziya aƧarınız və ya sertifikat faylınız var?" @@ -4511,6 +4620,25 @@ line1 = "Cari lisenziya aƧarının üzərinə yazmaq geri alına bilməz." line2 = "Ehtiyat nüsxəsi yoxdursa, əvvəlki lisenziyanız birdəfəlik itəcək." line3 = "Vacibdir: Lisenziya aƧarlarını məxfi və təhlükəsiz saxlayın. HeƧ vaxt onları ictimai paylaşmayın." +[admin.settings.premium.inputMethod] +text = "Lisenziya aƧarı" +file = "Sertifikat faylı" + +[admin.settings.premium.file] +label = "Lisenziya sertifikat faylı" +description = "Oflayn alışdan əldə etdiyiniz .lic və ya .cert lisenziya faylını yükləyin" +choose = "Lisenziya faylını seƧin" +selected = "SeƧildi: {{filename}} ({{size}})" +successMessage = "Lisenziya faylı uğurla yüklənib və aktivləşdirilib. Yenidən başlatmağa ehtiyac yoxdur." + +[admin.settings.premium.currentLicense] +title = "Aktiv lisenziya" +file = "Mənbə: Lisenziya faylı ({{path}})" +key = "Mənbə: Lisenziya aƧarı" +type = "Nƶv: {{type}}" +noInput = "Zəhmət olmasa lisenziya aƧarı verin və ya sertifikat faylı yükləyin" +success = "Uğurlu" + [admin.settings.premium.enabled] label = "Premium Xüsusiyyətlərini aktiv et" description = "Pro/enterprise xüsusiyyətləri üçün lisenziya aƧarı yoxlamalarını aktiv et" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} seƧildi" download = "Endir" delete = "Sil" unsupported = "Dəstəklənmir" +active = "Aktiv" addToUpload = "Yükləməyə əlavə et" +closeFile = "Faylı bağla" deleteAll = "Hamısını sil" loadingFiles = "Fayllar yüklənir..." noFiles = "Fayl mƶvcud deyil" @@ -5132,7 +5262,7 @@ upgrade = "İndi yüksəlt →" freeTitle = "Server lisenziyası" overLimitTitle = "Server lisenziyası tələb olunur" overLimitBody = "Lisenziyalaşmamız hər server üçün pulsuz olaraq maksimum {{freeTierLimit}} istifadəƧiyə icazə verir. Sizdə {{overLimitUserCopy}} Stirling istifadəƧisi var. Fasiləsiz davam etmək üçün Stirling Server planına yüksəldin - limitsiz yerlər, PDF mətn redaktəsi və tam admin nəzarəti cəmi $99/server/ay." -freeBody = "Bizim Open-Core lisenziyası hər server üçün pulsuz olaraq maksimum {{freeTierLimit}} istifadəƧiyə icazə verir. Fasiləsiz miqyaslanmaq və yeni PDF mətn redaktəsi alətimizə erkən Ƨıxış əldə etmək üçün Stirling Server planını tƶvsiyə edirik — tam redaktə və limitsiz yerlər $99/server/ay." +freeBody = "Bizim Open-Core lisenziyalaşdırmamız hər server üçün pulsuz olaraq ən Ƨox {{freeTierLimit}} istifadəƧiyə icazə verir. Fasiləsiz miqyaslama üçün Stirling Server planını tƶvsiyə edirik - limitsiz yerlər və SSO dəstəyi $99/server/ay." [onboarding.desktopInstall] title = "Yüklə" @@ -5237,6 +5367,31 @@ error = "İstifadəƧi statusunu yeniləmək alınmadı" success = "İstifadəƧi uğurla silindi" error = "İstifadəƧini silmək alınmadı" +[workspace.people.changePassword] +action = "Parolu dəyiş" +title = "Parolu dəyiş" +subtitle = "Aşağıdakı istifadəƧi üçün parolu yeniləyin" +newPassword = "Yeni parol" +confirmPassword = "Parolu təsdiq edin" +placeholder = "Yeni parolu daxil edin" +confirmPlaceholder = "Yeni parolu yenidən daxil edin" +passwordRequired = "Zəhmət olmasa yeni parolu daxil edin" +passwordMismatch = "Parollar uyğun gəlmir" +generateRandom = "Təhlükəsiz parol yaradın" +generatedPreview = "Yaradılmış parol:" +copyTooltip = "Buferə kopyala" +copiedToClipboard = "Parol buferə kopyalandı" +copyFailed = "Parolu kopyalamaq alınmadı" +sendEmail = "Bu dəyişiklik barədə istifadəƧiyə e-poƧt gƶndərin" +includePassword = "E-poƧta yeni parolu daxil edin" +forcePasswordChange = "Nƶvbəti girişdə istifadəƧini parolu dəyişməyə məcbur et" +emailUnavailable = "Bu istifadəƧinin e-poƧtu etibarlı e-poƧt ünvanı deyil. Bildirişlər sƶndürülüb." +smtpDisabled = "E-poƧt bildirişləri üçün parametrlərdə SMTP aktiv olmalıdır." +notifyOnly = "Parol olmadan e-poƧt gƶndəriləcək; istifadəƧiyə adminin onu dəyişdiyi bildiriləcək." +submit = "Parolu yenilə" +success = "Parol uğurla yeniləndi" +error = "Parolu yeniləmək alınmadı" + [workspace.people.emailInvite] tab = "E-poƧt Dəvəti" description = "Aşağıya vergüllə ayrılmış e-poƧtları yazın və ya yapışdırın. İstifadəƧilərə giriş məlumatları e-poƧtla gƶndəriləcək." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "ʏn azı bir e-poƧt ünvanı tələb olunur" submit = "Dəvətnamələri gƶndər" success = "istifadəƧi(lər) uğurla dəvət olundu" -partialSuccess = "Bəzi dəvətnamələr alınmadı" +partialFailure = "Bəzi dəvətlər uğursuz oldu" allFailed = "İstifadəƧiləri dəvət etmək alınmadı" error = "Dəvətnamələri gƶndərmək alınmadı" @@ -5288,8 +5443,8 @@ emailDisabled = "E-poƧt dəvətləri üçün ayarlarda SMTP konfiqurasiyası v [workspace.people.license] users = "istifadəƧi" availableSlots = "Mƶvcud yerlər" -grandfathered = "Grandfathered" -grandfatheredShort = "{{count}} grandfathered" +grandfathered = "ʏvvəlki şərtlərlə" +grandfatheredShort = "{{count}} əvvəlki şərtlərlə" fromLicense = "lisenziyadan" slotsAvailable = "{{count}} istifadəƧi yeri mƶvcuddur" noSlotsAvailable = "Mƶvcud yer yoxdur" @@ -5770,6 +5925,7 @@ subtitle = "Stirling hesabınızla daxil olun" [setup.selfhosted] title = "Serverə daxil olun" subtitle = "Server məlumatlarınızı daxil edin" +link = "və ya self-hosted hesaba qoşulun" [setup.server] title = "Serverə qoşulun" @@ -5788,6 +5944,14 @@ description = "Ɩz Stirling PDF serverinizin tam URL ünvanını daxil edin" emptyUrl = "Zəhmət olmasa server URL-i daxil edin" unreachable = "Serverə qoşulmaq mümkün olmadı" testFailed = "Bağlantı testi uğursuz oldu" +configFetch = "Server konfiqurasiyasını əldə etmək mümkün olmadı. URL-i yoxlayın və yenidən cəhd edin." + +[setup.server.error.securityDisabled] +title = "Giriş aktiv deyil" +body = "Bu serverdə giriş aktiv deyil. Bu serverə qoşulmaq üçün autentifikasiya aktiv edilməlidir:" +step1 = "Mühitinizdə DOCKER_ENABLE_SECURITY=true təyin edin" +step2 = "Yaxud settings.yml faylında security.enableLogin=true təyin edin" +step3 = "Serveri yenidən başladın" [setup.login] title = "Daxil ol" @@ -5797,6 +5961,13 @@ submit = "Daxil ol" signInWith = "Bununla daxil ol" oauthPending = "Təsdiqləmə üçün brauzer aƧılır..." orContinueWith = "Və ya e-poƧt ilə davam edin" +serverRequirement = "Qeyd: Serverdə giriş funksiyası aktiv olmalıdır." +showInstructions = "Necə aktivləşdirmək olar?" +hideInstructions = "Təlimatları gizlət" +instructions = "Stirling PDF serverinizdə girişi aktivləşdirmək üçün:" +instructionsEnvVar = "Mühit dəyişənini təyin edin:" +instructionsOrYml = "Və ya settings.yml faylında:" +instructionsRestart = "Dəyişikliklərin qüvvəyə minməsi üçün serveri yenidən başladın." [setup.login.username] label = "İstifadəƧi adı" @@ -5853,6 +6024,7 @@ earlyAccess = "Erkən Giriş" reset = "Dəyişiklikləri sıfırla" downloadJson = "JSON-u endir" generatePdf = "PDF yarat" +saveChanges = "Dəyişiklikləri yadda saxla" [pdfTextEditor.options.autoScaleText] title = "Mətni avtomatik miqyasla" @@ -5890,6 +6062,8 @@ alpha = "Bu alfa gƶrüntüləyici hələ inkişaf edir—bəzi şriftlər, rən [pdfTextEditor.empty] title = "Sənəd yüklənməyib" subtitle = "Mətn məzmununu redaktə etməyə başlamaq üçün PDF və ya JSON faylı yükləyin." +dropzone = "Buraya PDF və ya JSON faylını sürükləyib buraxın və ya baxmaq üçün klikləyin" +dropzoneWithFiles = "Fayllar vərəqindən bir fayl seƧin və ya buraya PDF və ya JSON faylını sürükləyib buraxın, yaxud baxmaq üçün klikləyin" [pdfTextEditor.welcomeBanner] title = "PDF Text Editor-ə xoş gəldiniz (Erkən Giriş)" diff --git a/frontend/public/locales/bg-BG/translation.toml b/frontend/public/locales/bg-BG/translation.toml index 2d33576bfb..b25a7f246d 100644 --- a/frontend/public/locales/bg-BG/translation.toml +++ b/frontend/public/locales/bg-BG/translation.toml @@ -163,6 +163,11 @@ unfavorite = "ŠŸŃ€ŠµŠ¼Š°Ń…Š²Š°Š½Šµ от Š»ŃŽŠ±ŠøŠ¼Šø" fullscreen = "ŠŸŃ€ŠµŠ²ŠŗŠ»ŃŽŃ‡Š²Š°Š½Šµ към режим на Ń†ŃŠ» екран" sidebar = "ŠŸŃ€ŠµŠ²ŠŗŠ»ŃŽŃ‡Š²Š°Š½Šµ към режим със странична лента" +[backendStartup] +notFoundTitle = "Š‘ŠµŠŗŠµŠ½Š“ŃŠŃ‚ не е намерен" +retry = "ŠžŠæŠøŃ‚Š°Š¹ отново" +unreachable = "ŠŸŃ€ŠøŠ»Š¾Š¶ŠµŠ½ŠøŠµŃ‚Š¾ в момента не може Га се ŃŠ²ŃŠŃ€Š¶Šµ с бекенГа. ŠŸŃ€Š¾Š²ŠµŃ€ŠµŃ‚Šµ ŃŃŠŃŃ‚Š¾ŃŠ½ŠøŠµŃ‚Š¾ на бекенГа Šø мрежовата ŃŠ²ŃŠŃ€Š·Š°Š½Š¾ŃŃ‚, слеГ което опитайте отново." + [zipWarning] title = "Š“Š¾Š»ŃŠ¼ ZIP файл" message = "Този ZIP ŃŃŠŠ“ŃŠŃ€Š¶Š° {{count}} файла. Да се извлече Š²ŃŠŠæŃ€ŠµŠŗŠø това?" @@ -912,6 +917,9 @@ desc = "Š”ŃŠŠ·Š“Š°Š²Š°Š¹Ń‚Šµ Š¼Š½Š¾Š³Š¾ŃŃ‚ŃŠŠæŠŗŠ¾Š²Šø работни проц desc = "ŠŠ°ŃŠ»Š°Š³Š²Š° PDF файлове Š²ŃŠŃ€Ń…Ńƒ Š“Ń€ŃƒŠ³ PDF" title = "ŠŠ°ŃŠ»Š°Š³Š²Š°Š½Šµ PDF-Šø" +[home.pdfTextEditor] +title = "РеГактор на текст в PDF" +desc = "РеГактирайте ŃŃŠŃ‰ŠµŃŃ‚Š²ŃƒŠ²Š°Ń‰ текст Šø ŠøŠ·Š¾Š±Ń€Š°Š¶ŠµŠ½ŠøŃ в PDF файлове" [home.addText] tags = "текст,Š°Š½Š¾Ń‚Š°Ń†ŠøŃ,етикет" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "ŠŠ°Ń€ŠøŃŃƒŠ²Š°Š½ поГпис" defaultImageLabel = "ŠšŠ°Ń‡ŠµŠ½ поГпис" defaultTextLabel = "Š’ŃŠŠ²ŠµŠ“ŠµŠ½ поГпис" saveButton = "Запази поГписа" +savePersonal = "Запази като личен" +saveShared = "Запази като споГелен" saveUnavailable = "ŠŸŃŠŃ€Š²Š¾ ŃŃŠŠ·Š“Š°Š¹Ń‚Šµ поГпис, за Га го запазите." noChanges = "Š¢ŠµŠŗŃƒŃ‰ŠøŃŃ‚ поГпис вече е запазен." +tempStorageTitle = "Временно ŃŃŠŃ…Ń€Š°Š½ŠµŠ½ŠøŠµ в Š±Ń€Š°ŃƒŠ·ŃŠŃ€Š°" +tempStorageDescription = "ŠŸŠ¾Š“ŠæŠøŃŠøŃ‚Šµ се ŃŃŠŃ…Ń€Š°Š½ŃŠ²Š°Ń‚ само във Š²Š°ŃˆŠøŃ Š±Ń€Š°ŃƒŠ·ŃŠŃ€. Ще Š±ŃŠŠ“ат загубени, ако изчистите Ганните на Š±Ń€Š°ŃƒŠ·ŃŠŃ€Š° или смените Š±Ń€Š°ŃƒŠ·ŃŠŃ€." +personalHeading = "Лични поГписи" +sharedHeading = "ДпоГелени поГписи" +personalDescription = "Дамо вие можете Га вижГате тези поГписи." +sharedDescription = "Всички потребители могат Га вижГат Šø използват тези поГписи." [sign.saved.type] canvas = "Рисунка" @@ -3020,6 +3036,91 @@ title = "Вземете ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃ за PDF" header = "Вземете ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃ за PDF" submit = "Вземете ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃ" downloadJson = "Š˜Š·Ń‚ŠµŠ³Š»ŠµŃ‚Šµ JSON" +processing = "Š˜Š·Š²Š»ŠøŃ‡Š°Š½Šµ на ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃ..." +results = "Š ŠµŠ·ŃƒŠ»Ń‚Š°Ń‚Šø" +noResults = "Дтартирайте ŠøŠ½ŃŃ‚Ń€ŃƒŠ¼ŠµŠ½Ń‚Š°, за Га генерирате отчет." +downloads = "Š˜Š·Ń‚ŠµŠ³Š»ŃŠ½ŠøŃ" +noneDetected = "ŠŠøŃ‰Š¾ не е открито" +indexTitle = "ИнГекс" + +[getPdfInfo.report] +entryLabel = "Пълно Ń€ŠµŠ·ŃŽŠ¼Šµ на ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃŃ‚Š°" +shortTitle = "Š˜Š½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃ за PDF" + +[getPdfInfo.sections] +metadata = "ŠœŠµŃ‚Š°Š“Š°Š½Š½Šø" +formFields = "ŠŸŠ¾Š»ŠµŃ‚Š° на Ń„Š¾Ń€Š¼ŃƒŠ»ŃŃ€Š°" +basicInfo = "ŠžŃŠ½Š¾Š²Š½Š° ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃ" +documentInfo = "Š˜Š½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃ за Š“Š¾ŠŗŃƒŠ¼ŠµŠ½Ń‚Š°" +compliance = "Š”ŃŠŠ¾Ń‚Š²ŠµŃ‚ŃŃ‚Š²ŠøŠµ" +encryption = "Шифриране" +permissions = "Š Š°Š·Ń€ŠµŃˆŠµŠ½ŠøŃ" +other = "Š”Ń€ŃƒŠ³Š¾" +perPageInfo = "Š˜Š½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃ по страници" +tableOfContents = "Š”ŃŠŠ“ŃŠŃ€Š¶Š°Š½ŠøŠµ" + +[getPdfInfo.other] +attachments = "ŠŸŃ€ŠøŠŗŠ°Ń‡ŠµŠ½Šø файлове" +embeddedFiles = "ВграГени файлове" +javaScript = "JavaScript" +layers = "Длоеве" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Размер" +annotations = "Анотации" +images = "Š˜Š·Š¾Š±Ń€Š°Š¶ŠµŠ½ŠøŃ" +links = "Š’Ń€ŃŠŠ·ŠŗŠø" +fonts = "Шрифтове" +xobjects = "Брой XObject" +multimedia = "ŠœŃƒŠ»Ń‚ŠøŠ¼ŠµŠ“ŠøŃ" + +[getPdfInfo.summary] +pages = "Дтраници" +fileSize = "Размер на файла" +pdfVersion = "Š’ŠµŃ€ŃŠøŃ на PDF" +language = "Език" +title = "ŠžŠ±Š¾Š±Ń‰ŠµŠ½ŠøŠµ на PDF" +author = "Автор" +created = "ДъзГаГен" +modified = "ŠŸŃ€Š¾Š¼ŠµŠ½ŠµŠ½" +permsAll = "Всички Ń€Š°Š·Ń€ŠµŃˆŠµŠ½ŠøŃ са позволени" +permsRestricted = "{{count}} Š¾Š³Ń€Š°Š½ŠøŃ‡ŠµŠ½ŠøŃ" +permsMixed = "ŠŃŠŗŠ¾Šø Ń€Š°Š·Ń€ŠµŃˆŠµŠ½ŠøŃ са ограничени" +hasCompliance = "Има станГарти за ŃŃŠŠ¾Ń‚Š²ŠµŃ‚ŃŃ‚Š²ŠøŠµ" +noCompliance = "ŠŃŠ¼Š° станГарти за ŃŃŠŠ¾Ń‚Š²ŠµŃ‚ŃŃ‚Š²ŠøŠµ" +basic = "ŠžŃŠ½Š¾Š²Š½Š° ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃ" +documentInfo = "Š˜Š½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃ за Š“Š¾ŠŗŃƒŠ¼ŠµŠ½Ń‚Š°" +securityTitle = "Š”ŃŠŃŃ‚Š¾ŃŠ½ŠøŠµ на ŃŠøŠ³ŃƒŃ€Š½Š¾ŃŃ‚Ń‚Š°" +technical = "Технически" +overviewTitle = "ŠŸŃ€ŠµŠ³Š»ŠµŠ“ на PDF" + +[getPdfInfo.summary.security] +encrypted = "Шифриран PDF - налична защита с парола" +unencrypted = "ŠŠµŃˆŠøŃ„Ń€ŠøŃ€Š°Š½ PDF - Š½ŃŠ¼Š° защита с парола" + +[getPdfInfo.summary.tech] +images = "Š˜Š·Š¾Š±Ń€Š°Š¶ŠµŠ½ŠøŃ" +fonts = "Шрифтове" +formFields = "ŠŸŠ¾Š»ŠµŃ‚Š° на Ń„Š¾Ń€Š¼ŃƒŠ»ŃŃ€Š°" +embeddedFiles = "ВграГени файлове" +javaScript = "JavaScript" +layers = "Длоеве" +bookmarks = "ŠžŃ‚Š¼ŠµŃ‚ŠŗŠø" +multimedia = "ŠœŃƒŠ»Ń‚ŠøŠ¼ŠµŠ“ŠøŃ" + +[getPdfInfo.summary.overview] +untitled = "неозаглавен Š“Š¾ŠŗŃƒŠ¼ŠµŠ½Ń‚" +unknown = "ŠŠµŠøŠ·Š²ŠµŃŃ‚ŠµŠ½ автор" +text = "Това е {{pages}}-страничен PDF със заглавие {{title}}, съзГаГен от {{author}} (Š²ŠµŃ€ŃŠøŃ на PDF {{version}})." + +[getPdfInfo.error] +partial = "ŠŃŠŗŠ¾Šø файлове не можаха Га Š±ŃŠŠ“ат обработени." +unexpected = "ŠŠµŠ¾Ń‡Š°ŠŗŠ²Š°Š½Š° Š³Ń€ŠµŃˆŠŗŠ° по време на извличане." + +[getPdfInfo.status] +complete = "Š˜Š·Š²Š»ŠøŃ‡Š°Š½ŠµŃ‚Š¾ е Š·Š°Š²ŃŠŃ€ŃˆŠµŠ½Š¾" [extractPage] tags = "извличане" @@ -3438,6 +3539,9 @@ signinTitle = "ŠœŠ¾Š»Ń Š²ŠæŠøŃˆŠµŃ‚Šµ се" ssoSignIn = "Влизане чрез еГнократно влизане" oAuth2AutoCreateDisabled = "OAUTH2 Автоматично съзГаване на потребител е Геактивирано" oAuth2AdminBlockedUser = "Š ŠµŠ³ŠøŃŃ‚Ń€Š°Ń†ŠøŃŃ‚Š° или влизането на нерегистрирани потребители в момента е блокирано. ŠœŠ¾Š»Ń, ŃŠ²ŃŠŃ€Š¶ŠµŃ‚Šµ се с аГминистратора." +oAuth2RequiresLicense = "ВхоГ с OAuth/SSO изисква платен лиценз (Server или Enterprise). ŠœŠ¾Š»Ń, ŃŠ²ŃŠŃ€Š¶ŠµŃ‚Šµ се с аГминистратора, за Га наГстроите плана си." +saml2RequiresLicense = "ВхоГ със SAML изисква платен лиценз (Server или Enterprise). ŠœŠ¾Š»Ń, ŃŠ²ŃŠŃ€Š¶ŠµŃ‚Šµ се с аГминистратора, за Га наГстроите плана си." +maxUsersReached = "Достигнат е Š¼Š°ŠŗŃŠøŠ¼Š°Š»Š½ŠøŃŃ‚ брой потребители за Ń‚ŠµŠŗŃƒŃ‰ŠøŃ ви лиценз. ŠœŠ¾Š»Ń, ŃŠ²ŃŠŃ€Š¶ŠµŃ‚Šµ се с аГминистратора, за Га наГстроите плана си или Га Гобавите още места." oauth2RequestNotFound = "Š—Š°ŃŠ²ŠŗŠ°Ń‚Š° за Š¾Ń‚Š¾Ń€ŠøŠ·Š°Ń†ŠøŃ не е намерена" oauth2InvalidUserInfoResponse = "ŠŠµŠ²Š°Š»ŠøŠ“Š½Š° ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃ за ŠæŠ¾Ń‚Ń€ŠµŠ±ŠøŃ‚ŠµŠ»Ń" oauth2invalidRequest = "ŠŠµŠ²Š°Š»ŠøŠ“Š½Š° Š·Š°ŃŠ²ŠŗŠ°" @@ -3846,14 +3950,17 @@ fitToWidth = "ŠŸŠ¾Š±ŠøŃ€Š°Š½Šµ по ŃˆŠøŃ€ŠøŠ½Š°" actualSize = "Действителен размер" [viewer] +cannotPreviewFile = "ŠŠµ може Га се Š²ŠøŠ·ŃƒŠ°Š»ŠøŠ·ŠøŃ€Š° Ń„Š°Š¹Š»ŃŠŃ‚" +dualPageView = "ИзглеГ: Гве страници" firstPage = "ŠŸŃŠŃ€Š²Š° страница" lastPage = "ПослеГна страница" -previousPage = "ŠŸŃ€ŠµŠ“ŠøŃˆŠ½Š° страница" nextPage = "ДлеГваща страница" +onlyPdfSupported = "ŠŸŃ€ŠµŠ³Š»ŠµŠ“Š°Ń‡ŃŠŃ‚ ŠæŠ¾Š“Š“ŃŠŃ€Š¶Š° само PDF файлове. Този файл изглежГа е в Š“Ń€ŃƒŠ³ формат." +previousPage = "ŠŸŃ€ŠµŠ“ŠøŃˆŠ½Š° страница" +singlePageView = "ИзглеГ: еГна страница" +unknownFile = "ŠŠµŠæŠ¾Š·Š½Š°Ń‚ файл" zoomIn = "Увеличи" zoomOut = "ŠŠ°Š¼Š°Š»Šø" -singlePageView = "ИзглеГ: еГна страница" -dualPageView = "ИзглеГ: Гве страници" [rightRail] closeSelected = "Затвори избраните файлове" @@ -3877,6 +3984,7 @@ toggleSidebar = "Показване/скриване на страничната exportSelected = "Експорт на избраните страници" toggleAnnotations = "Показване/скриване на анотациите" annotationMode = "ŠŸŃ€ŠµŠ²ŠŗŠ»ŃŽŃ‡Šø режим на анотации" +print = "ŠŸŠµŃ‡Š°Ń‚ на PDF" draw = "Рисуване" save = "Запази" saveChanges = "Запази промените" @@ -4494,6 +4602,7 @@ description = "URL или име на файл към ŠøŠ¼ŠæŃ€ŠµŃŃƒŠ¼ (заГъ title = "ŠŸŃ€ŠµŠ¼ŠøŃƒŠ¼ Šø Enterprise" description = "ŠšŠ¾Š½Ń„ŠøŠ³ŃƒŃ€ŠøŃ€Š°Š¹Ń‚Šµ Š²Š°ŃˆŠøŃ ŠæŃ€ŠµŠ¼ŠøŃƒŠ¼ или enterprise лицензионен ŠŗŠ»ŃŽŃ‡." license = "ŠšŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†ŠøŃ на лиценз" +noInput = "ŠœŠ¾Š»Ń, преГоставете лицензен ŠŗŠ»ŃŽŃ‡ или файл" [admin.settings.premium.licenseKey] toggle = "Š˜Š¼Š°Ń‚Šµ лицензен ŠŗŠ»ŃŽŃ‡ или сертификат?" @@ -4511,6 +4620,25 @@ line1 = "ŠŸŃ€ŠµŠ·Š°ŠæŠøŃŠ²Š°Š½ŠµŃ‚Š¾ на Ń‚ŠµŠŗŃƒŃ‰ŠøŃ лицензен ŠŗŠ» line2 = "ŠŸŃ€ŠµŠ“ŠøŃˆŠ½ŠøŃŃ‚ лиценз ще бъГе окончателно загубен, освен ако не сте го архивирали Š“Ń€ŃƒŠ³Š°Š“Šµ." line3 = "Важно: ŠŸŠ°Š·ŠµŃ‚Šµ лицензните ŠŗŠ»ŃŽŃ‡Š¾Š²Šµ поверителни Šø ŃŠøŠ³ŃƒŃ€Š½Šø. ŠŠøŠŗŠ¾Š³Š° не ги ŃŠæŠ¾Š“ŠµŠ»ŃŠ¹Ń‚Šµ ŠæŃƒŠ±Š»ŠøŃ‡Š½Š¾." +[admin.settings.premium.inputMethod] +text = "Лицензен ŠŗŠ»ŃŽŃ‡" +file = "Файл със сертификат" + +[admin.settings.premium.file] +label = "Файл с лицензен сертификат" +description = "ŠšŠ°Ń‡ŠµŃ‚Šµ Š²Š°ŃˆŠøŃ .lic или .cert лицензен файл от офлайн покупки" +choose = "Š˜Š·Š±ŠµŃ€ŠµŃ‚Šµ лицензен файл" +selected = "Š˜Š·Š±Ń€Š°Š½Š¾: {{filename}} ({{size}})" +successMessage = "Š›ŠøŃ†ŠµŠ½Š·Š½ŠøŃŃ‚ файл беше качен Šø активиран успешно. ŠŠµ е необхоГимо рестартиране." + +[admin.settings.premium.currentLicense] +title = "Активен лиценз" +file = "Š˜Š·Ń‚Š¾Ń‡Š½ŠøŠŗ: Лицензен файл ({{path}})" +key = "Š˜Š·Ń‚Š¾Ń‡Š½ŠøŠŗ: Лицензен ŠŗŠ»ŃŽŃ‡" +type = "Тип: {{type}}" +noInput = "ŠœŠ¾Š»Ń, преГоставете лицензен ŠŗŠ»ŃŽŃ‡ или качете файл със сертификат" +success = "Успешно" + [admin.settings.premium.enabled] label = "Активирай ŠæŃ€ŠµŠ¼ŠøŃƒŠ¼ Ń„ŃƒŠ½ŠŗŃ†ŠøŠø" description = "Активира проверки на Š»ŠøŃ†ŠµŠ½Š·ŠøŠ¾Š½Š½ŠøŃ ŠŗŠ»ŃŽŃ‡ за pro/enterprise Ń„ŃƒŠ½ŠŗŃ†ŠøŠø" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} избрани" download = "Š˜Š·Ń‚ŠµŠ³Š»Šø" delete = "Š˜Š·Ń‚Ń€ŠøŠ¹" unsupported = "ŠŠµŠæŠ¾Š“Š“ŃŠŃ€Š¶Š°Š½Š¾" +active = "Активен" addToUpload = "Добави към качването" +closeFile = "Затвори файла" deleteAll = "Š˜Š·Ń‚Ń€ŠøŠ¹ всички" loadingFiles = "ЗарежГане на файлове..." noFiles = "ŠŃŠ¼Š° налични файлове" @@ -5132,7 +5262,7 @@ upgrade = "ŠŠ°Š“Š³Ń€Š°Š“ŠµŃ‚Šµ сега →" freeTitle = "Лиценз за ŃŃŠŃ€Š²ŃŠŃ€" overLimitTitle = "ŠŠµŠ¾Š±Ń…Š¾Š“ŠøŠ¼ е лиценз за ŃŃŠŃ€Š²ŃŠŃ€" overLimitBody = "ŠŠ°ŃˆŠøŃŃ‚ лиценз ŠæŠ¾Š·Š²Š¾Š»ŃŠ²Š° Го {{freeTierLimit}} безплатни потребители на ŃŃŠŃ€Š²ŃŠŃ€. Š˜Š¼Š°Ń‚Šµ {{overLimitUserCopy}} потребители на Stirling. За Га ŠæŃ€Š¾Š“ŃŠŠ»Š¶ŠøŃ‚Šµ без ŠæŃ€ŠµŠŗŃŠŃŠ²Š°Š½ŠøŃ, наГграГете Го плана Stirling Server – неограничени места, Ń€ŠµŠ“Š°ŠŗŃ†ŠøŃ на PDF текст Šø пълен аГмин контрол за $99/ŃŃŠŃ€Š²ŃŠŃ€/месец." -freeBody = "ŠŠ°ŃˆŠøŃŃ‚ Open-Core лиценз ŠæŠ¾Š·Š²Š¾Š»ŃŠ²Š° Го {{freeTierLimit}} безплатни потребители на ŃŃŠŃ€Š²ŃŠŃ€. За Га мащабирате без ŠæŃ€ŠµŠŗŃŠŃŠ²Š°Š½ŠøŃ Šø Га ŠæŠ¾Š»ŃƒŃ‡ŠøŃ‚Šµ ранен Š“Š¾ŃŃ‚ŃŠŠæ Го Š½Š°ŃˆŠøŃ нов ŠøŠ½ŃŃ‚Ń€ŃƒŠ¼ŠµŠ½Ń‚ за Ń€ŠµŠ“Š°ŠŗŃ†ŠøŃ на PDF текст, ŠæŃ€ŠµŠæŠ¾Ń€ŃŠŃ‡Š²Š°Š¼Šµ плана Stirling Server – пълно реГактиране Šø неограничени места за $99/ŃŃŠŃ€Š²ŃŠŃ€/месец." +freeBody = "ŠŠ°ŃˆŠøŃŃ‚ лицензен моГел Open-Core ŠæŠ¾Š·Š²Š¾Š»ŃŠ²Š° Го {{freeTierLimit}} потребители безплатно на ŃŃŠŃ€Š²ŃŠŃ€. За Š±ŠµŠ·ŠæŃ€ŠµŠæŃŃ‚ствено мащабиране ŠæŃ€ŠµŠæŠ¾Ń€ŃŠŃ‡Š²Š°Š¼Šµ плана Stirling Server - неограничени места Šø ŠæŠ¾Š“Š“Ń€ŃŠŠ¶ŠŗŠ° на SSO за $99/server/mo." [onboarding.desktopInstall] title = "Š˜Š·Ń‚ŠµŠ³Š»ŃŠ½Šµ" @@ -5237,6 +5367,31 @@ error = "ŠŠµŃƒŃŠæŠµŃˆŠ½Š¾ Š¾Š±Š½Š¾Š²ŃŠ²Š°Š½Šµ на ŃŃ‚Š°Ń‚ŃƒŃ на потр success = "ŠŸŠ¾Ń‚Ń€ŠµŠ±ŠøŃ‚ŠµŠ»ŃŃ‚ е изтрит успешно" error = "ŠŠµŃƒŃŠæŠµŃˆŠ½Š¾ изтриване на потребител" +[workspace.people.changePassword] +action = "ŠŸŃ€Š¾Š¼ŃŠ½Š° на парола" +title = "ŠŸŃ€Š¾Š¼ŃŠ½Š° на парола" +subtitle = "ŠŠŗŃ‚ŃƒŠ°Š»ŠøŠ·ŠøŃ€Š°Š¹Ń‚Šµ паролата за" +newPassword = "ŠŠ¾Š²Š° парола" +confirmPassword = "ŠŸŠ¾Ń‚Š²ŃŠŃ€Š“ŠµŃ‚Šµ паролата" +placeholder = "Š’ŃŠŠ²ŠµŠ“ŠµŃ‚Šµ нова парола" +confirmPlaceholder = "Š’ŃŠŠ²ŠµŠ“ŠµŃ‚Šµ отново новата парола" +passwordRequired = "ŠœŠ¾Š»Ń, Š²ŃŠŠ²ŠµŠ“ŠµŃ‚Šµ нова парола" +passwordMismatch = "ŠŸŠ°Ń€Š¾Š»ŠøŃ‚Šµ не ŃŃŠŠ²ŠæŠ°Š“Š°Ń‚" +generateRandom = "Генерирайте ŃŠøŠ³ŃƒŃ€Š½Š° парола" +generatedPreview = "Генерирана парола:" +copyTooltip = "ŠšŠ¾ŠæŠøŃ€Š°Š½Šµ в клипборГа" +copiedToClipboard = "ŠŸŠ°Ń€Š¾Š»Š°Ń‚Š° е копирана в клипборГа" +copyFailed = "ŠŠµŃƒŃŠæŠµŃˆŠ½Š¾ копиране на паролата" +sendEmail = "Š˜Š·ŠæŃ€Š°Ń‚ŠµŃ‚Šµ имейл на ŠæŠ¾Ń‚Ń€ŠµŠ±ŠøŃ‚ŠµŠ»Ń за тази ŠæŃ€Š¾Š¼ŃŠ½Š°" +includePassword = "Š’ŠŗŠ»ŃŽŃ‡ŠµŃ‚Šµ новата парола в имейла" +forcePasswordChange = "ŠŸŃ€ŠøŠ½ŃƒŠ“ŠµŃ‚Šµ ŠæŠ¾Ń‚Ń€ŠµŠ±ŠøŃ‚ŠµŠ»Ń Га смени паролата при слеГващо влизане" +emailUnavailable = "Š˜Š¼ŠµŠ¹Š»ŃŠŃ‚ на този потребител не е валиГен аГрес. Š˜Š·Š²ŠµŃŃ‚ŠøŃŃ‚Š° са ŠøŠ·ŠŗŠ»ŃŽŃ‡ŠµŠ½Šø." +smtpDisabled = "Имейл ŠøŠ·Š²ŠµŃŃ‚ŠøŃŃ‚Š° изискват SMTP Га е активиран в настройките." +notifyOnly = "Ще бъГе изпратен имейл без паролата, за Га увеГоми ŠæŠ¾Ń‚Ń€ŠµŠ±ŠøŃ‚ŠµŠ»Ń, че аГминистратор я е променил." +submit = "ŠŠŗŃ‚ŃƒŠ°Š»ŠøŠ·ŠøŃ€Š°Š½Šµ на паролата" +success = "ŠŸŠ°Ń€Š¾Š»Š°Ń‚Š° е Š°ŠŗŃ‚ŃƒŠ°Š»ŠøŠ·ŠøŃ€Š°Š½Š° успешно" +error = "ŠŠµŃƒŃŠæŠµŃˆŠ½Š¾ Š°ŠŗŃ‚ŃƒŠ°Š»ŠøŠ·ŠøŃ€Š°Š½Šµ на паролата" + [workspace.people.emailInvite] tab = "Покана по имейл" description = "Š’ŃŠŠ²ŠµŠ“ŠµŃ‚Šµ или поставете имейли по-Голу, разГелени със запетаи. ŠŸŠ¾Ń‚Ń€ŠµŠ±ŠøŃ‚ŠµŠ»ŠøŃ‚Šµ ще ŠæŠ¾Š»ŃƒŃ‡Š°Ń‚ Ганни за вхоГ по имейл." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Изисква се поне еГин имейл аГрес" submit = "Š˜Š·ŠæŃ€Š°Ń‚Šø покани" success = "ŠŸŠ¾Ń‚Ń€ŠµŠ±ŠøŃ‚ŠµŠ»(Šø) поканени успешно" -partialSuccess = "ŠŃŠŗŠ¾Šø покани не ŃƒŃŠæŃŃ…Š°" +partialFailure = "ŠŃŠŗŠ¾Šø покани Š±ŃŃ…а неуспешни" allFailed = "ŠŠµŃƒŃŠæŠµŃˆŠ½Š¾ канене на потребители" error = "ŠŠµŃƒŃŠæŠµŃˆŠ½Š¾ изпращане на покани" @@ -5770,6 +5925,7 @@ subtitle = "Š’ŠæŠøŃˆŠµŃ‚Šµ се с Š²Š°ŃˆŠøŃ Stirling Š°ŠŗŠ°ŃƒŠ½Ń‚" [setup.selfhosted] title = "Š’ŠæŠøŃˆŠµŃ‚Šµ се в ŃŃŠŃ€Š²ŃŠŃ€Š°" subtitle = "Š’ŃŠŠ²ŠµŠ“ŠµŃ‚Šµ своите Ганни за ŃŃŠŃ€Š²ŃŠŃ€Š°" +link = "или се ŃŠ²ŃŠŃ€Š¶ŠµŃ‚Šµ със ŃŠ°Š¼Š¾ŃŃ‚Š¾ŃŃ‚ŠµŠ»Š½Š¾ хостван Š°ŠŗŠ°ŃƒŠ½Ń‚" [setup.server] title = "Š”Š²ŃŠŃ€Š·Š²Š°Š½Šµ към ŃŃŠŃ€Š²ŃŠŃ€" @@ -5788,6 +5944,14 @@ description = "Š’ŃŠŠ²ŠµŠ“ŠµŃ‚Šµ ŠæŃŠŠ»Š½ŠøŃ URL на Š²Š°ŃˆŠøŃ самосто emptyUrl = "ŠœŠ¾Š»Ń, Š²ŃŠŠ²ŠµŠ“ŠµŃ‚Šµ URL на ŃŃŠŃ€Š²ŃŠŃ€" unreachable = "ŠŠµŃƒŃŠæŠµŃˆŠ½Š° Š²Ń€ŃŠŠ·ŠŗŠ° със ŃŃŠŃ€Š²ŃŠŃ€Š°" testFailed = "Š¢ŠµŃŃ‚ŃŠŃ‚ на Š²Ń€ŃŠŠ·ŠŗŠ°Ń‚а е неуспешен" +configFetch = "ŠŠµŃƒŃŠæŠµŃˆŠ½Š¾ извличане на ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†ŠøŃŃ‚Š° на ŃŃŠŃ€Š²ŃŠŃ€Š°. ŠœŠ¾Š»Ń, проверете URL аГреса Šø опитайте отново." + +[setup.server.error.securityDisabled] +title = "Š’Ń…Š¾Š“ŃŠŃ‚ не е активиран" +body = "ŠŠ° този ŃŃŠŃ€Š²ŃŠŃ€ не е активиран вхоГ. За Га се ŃŠ²ŃŠŃ€Š¶ŠµŃ‚Šµ, Ń‚Ń€ŃŠ±Š²Š° Га активирате ŃƒŠ“Š¾ŃŃ‚Š¾Š²ŠµŃ€ŃŠ²Š°Š½Šµ:" +step1 = "ЗаГайте DOCKER_ENABLE_SECURITY=true във Š²Š°ŃˆŠ°Ń‚а среГа" +step2 = "Или заГайте security.enableLogin=true в settings.yml" +step3 = "Рестартирайте ŃŃŠŃ€Š²ŃŠŃ€Š°" [setup.login] title = "Вписване" @@ -5797,6 +5961,13 @@ submit = "ВхоГ" signInWith = "Вписване с" oauthPending = "ŠžŃ‚Š²Š°Ń€ŃŠ½Šµ на Š±Ń€Š°ŃƒŠ·ŃŠŃ€ за ŃƒŠ“Š¾ŃŃ‚Š¾Š²ŠµŃ€ŃŠ²Š°Š½Šµ..." orContinueWith = "Или ŠæŃ€Š¾Š“ŃŠŠ»Š¶ŠµŃ‚Šµ с имейл" +serverRequirement = "Забележка: Š”ŃŠŃ€Š²ŃŠŃ€ŃŠŃ‚ Ń‚Ń€ŃŠ±Š²Š° Га има активиран вхоГ." +showInstructions = "Как Га се активира?" +hideInstructions = "Дкрий ŠøŠ½ŃŃ‚Ń€ŃƒŠŗŃ†ŠøŠøŃ‚Šµ" +instructions = "За Га активирате вхоГ на Š²Š°ŃˆŠøŃ Stirling PDF ŃŃŠŃ€Š²ŃŠŃ€:" +instructionsEnvVar = "ЗаГайте променливата на среГата:" +instructionsOrYml = "Или в settings.yml:" +instructionsRestart = "ДлеГ това рестартирайте ŃŃŠŃ€Š²ŃŠŃ€Š°, за Га Š²Š»ŃŠ·Š°Ń‚ промените в сила." [setup.login.username] label = "ŠŸŠ¾Ń‚Ń€ŠµŠ±ŠøŃ‚ŠµŠ»ŃŠŗŠ¾ име" @@ -5853,6 +6024,7 @@ earlyAccess = "Ранен Š“Š¾ŃŃ‚ŃŠŠæ" reset = "ŠžŃ‚Š¼ŠµŠ½Šø промените" downloadJson = "Š˜Š·Ń‚ŠµŠ³Š»Šø JSON" generatePdf = "Генерирай PDF" +saveChanges = "Запази промените" [pdfTextEditor.options.autoScaleText] title = "Авто-мащабиране на текст за напасване в полетата" @@ -5890,6 +6062,8 @@ alpha = "Този алфа Š²ŠøŠ·ŃƒŠ°Š»ŠøŠ·Š°Ń‚ор все още се разв [pdfTextEditor.empty] title = "ŠŃŠ¼Š° зареГен Š“Š¾ŠŗŃƒŠ¼ŠµŠ½Ń‚" subtitle = "ЗареГете PDF или JSON файл, за Га започнете Га реГактирате текстовото ŃŃŠŠ“ŃŠŃ€Š¶Š°Š½ŠøŠµ." +dropzone = "ŠŸŠ»ŃŠŠ·Š½ŠµŃ‚Šµ Šø ŠæŃƒŃŠ½ŠµŃ‚Šµ Ń‚ŃƒŠŗ PDF или JSON файл или щракнете, за Га преглеГате" +dropzoneWithFiles = "Š˜Š·Š±ŠµŃ€ŠµŃ‚Šµ файл от разГела Файлове или ŠæŠ»ŃŠŠ·Š½ŠµŃ‚Šµ Šø ŠæŃƒŃŠ½ŠµŃ‚Šµ Ń‚ŃƒŠŗ PDF или JSON файл, или щракнете, за Га преглеГате" [pdfTextEditor.welcomeBanner] title = "Добре Гошли в PDF Text Editor (ранен Š“Š¾ŃŃ‚ŃŠŠæ)" diff --git a/frontend/public/locales/ca-CA/translation.toml b/frontend/public/locales/ca-CA/translation.toml index 2d8bc23e33..00370a7d75 100644 --- a/frontend/public/locales/ca-CA/translation.toml +++ b/frontend/public/locales/ca-CA/translation.toml @@ -163,6 +163,11 @@ unfavorite = "Elimina dels preferits" fullscreen = "Canvia al mode de pantalla completa" sidebar = "Canvia al mode de barra lateral" +[backendStartup] +notFoundTitle = "Backend no trobat" +retry = "Torneu-ho a intentar" +unreachable = "L'aplicació no pot connectar-se al backend ara mateix. Verifiqueu l'estat del backend i la connectivitat de xarxa i torneu-ho a intentar." + [zipWarning] title = "Fitxer ZIP gran" message = "Aquest ZIP contĆ© {{count}} fitxers. Vols extreure'l igualment?" @@ -347,7 +352,7 @@ teams = "Equips" title = "Configuració" systemSettings = "Configuració del sistema" features = "Funcions" -endpoints = "Endpoints" +endpoints = "Punts finals" database = "Base de dades" advanced = "AvanƧat" @@ -556,7 +561,7 @@ totalEndpoints = "Total d'endpoints" totalVisits = "Total de visites" showing = "Mostrant" selectedVisits = "Visites seleccionades" -endpoint = "Endpoint" +endpoint = "Punt final" visits = "Visites" percentage = "Percentatge" loading = "Carregant..." @@ -912,6 +917,9 @@ desc = "Construeix fluxos de treball multietapa enllaƧant accions PDF. Ideal pe desc = "Superposa PDFs sobre un altre PDF" title = "Superposar PDFs" +[home.pdfTextEditor] +title = "Editor de text PDF" +desc = "Edita el text i les imatges existents dins dels PDF" [home.addText] tags = "text,anotació,etiqueta" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Signatura dibuixada" defaultImageLabel = "Signatura pujada" defaultTextLabel = "Signatura teclejada" saveButton = "Desa la signatura" +savePersonal = "Desa com a personal" +saveShared = "Desa com a compartida" saveUnavailable = "Crea una signatura primer per poder-la desar." noChanges = "La signatura actual ja estĆ  desada." +tempStorageTitle = "Emmagatzematge temporal del navegador" +tempStorageDescription = "Les signatures nomĆ©s s'emmagatzemen al vostre navegador. Es perdran si netegeu les dades del navegador o canvieu de navegador." +personalHeading = "Signatures personals" +sharedHeading = "Signatures compartides" +personalDescription = "NomĆ©s vosaltres podeu veure aquestes signatures." +sharedDescription = "Tots els usuaris poden veure i utilitzar aquestes signatures." [sign.saved.type] canvas = "Dibuix" @@ -3020,6 +3036,91 @@ title = "Obteniu Informació del PDF" header = "Obteniu Informació del PDF" submit = "Obteniu Informació" downloadJson = "Descarrega JSON" +processing = "Extraient informació..." +results = "Resultats" +noResults = "Executeu l'eina per generar un informe." +downloads = "DescĆ rregues" +noneDetected = "No se n'ha detectat cap" +indexTitle = "ƍndex" + +[getPdfInfo.report] +entryLabel = "Resum d'informació complet" +shortTitle = "Informació del PDF" + +[getPdfInfo.sections] +metadata = "Metadades" +formFields = "Camps de formulari" +basicInfo = "Informació bĆ sica" +documentInfo = "Informació del document" +compliance = "Conformitat" +encryption = "Xifratge" +permissions = "Permisos" +other = "Altres" +perPageInfo = "Informació per pĆ gina" +tableOfContents = "Taula de continguts" + +[getPdfInfo.other] +attachments = "Fitxers adjunts" +embeddedFiles = "Fitxers incrustats" +javaScript = "JavaScript" +layers = "Capes" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Mida" +annotations = "Anotacions" +images = "Imatges" +links = "EnllaƧos" +fonts = "Tipus de lletra" +xobjects = "Recompte d'XObject" +multimedia = "MultimĆØdia" + +[getPdfInfo.summary] +pages = "PĆ gines" +fileSize = "Mida del fitxer" +pdfVersion = "Versió del PDF" +language = "Idioma" +title = "Resum del PDF" +author = "Autor" +created = "Creat" +modified = "Modificat" +permsAll = "Tots els permisos permesos" +permsRestricted = "{{count}} restriccions" +permsMixed = "Alguns permisos restringits" +hasCompliance = "TĆ© estĆ ndards de conformitat" +noCompliance = "Sense estĆ ndards de conformitat" +basic = "Informació bĆ sica" +documentInfo = "Informació del document" +securityTitle = "Estat de seguretat" +technical = "TĆØcnic" +overviewTitle = "Visió general del PDF" + +[getPdfInfo.summary.security] +encrypted = "PDF xifrat - Protecció amb contrasenya present" +unencrypted = "PDF no xifrat - Sense protecció amb contrasenya" + +[getPdfInfo.summary.tech] +images = "Imatges" +fonts = "Tipus de lletra" +formFields = "Camps de formulari" +embeddedFiles = "Fitxers incrustats" +javaScript = "JavaScript" +layers = "Capes" +bookmarks = "Marcadors" +multimedia = "MultimĆØdia" + +[getPdfInfo.summary.overview] +untitled = "un document sense tĆ­tol" +unknown = "Autor desconegut" +text = "Aquest Ć©s un PDF de {{pages}} pĆ gines titulat {{title}} creat per {{author}} (versió del PDF {{version}})." + +[getPdfInfo.error] +partial = "Alguns fitxers no s'han pogut processar." +unexpected = "Error inesperat durant l'extracció." + +[getPdfInfo.status] +complete = "Extracció completada" [extractPage] tags = "extreure" @@ -3438,6 +3539,9 @@ signinTitle = "Autenticat" ssoSignIn = "Inicia sessió mitjanƧant inici de sessió Ćŗnic" oAuth2AutoCreateDisabled = "La creació automĆ tica d'usuaris OAUTH2 estĆ  desactivada" oAuth2AdminBlockedUser = "El registre o inici de sessió d'usuaris no registrats estĆ  actualment bloquejat. Si us plau, contacta amb l'administrador." +oAuth2RequiresLicense = "L'inici de sessió OAuth/SSO requereix una llicĆØncia de pagament (Server o Enterprise). Poseu-vos en contacte amb l'administrador per actualitzar el vostre pla." +saml2RequiresLicense = "L'inici de sessió SAML requereix una llicĆØncia de pagament (Server o Enterprise). Poseu-vos en contacte amb l'administrador per actualitzar el vostre pla." +maxUsersReached = "S'ha assolit el nombre mĆ xim d'usuaris de la vostra llicĆØncia actual. Poseu-vos en contacte amb l'administrador per actualitzar el vostre pla o afegir mĆ©s places." oauth2RequestNotFound = "SolĀ·licitud d'autorització no trobada" oauth2InvalidUserInfoResponse = "Resposta d'informació d'usuari no vĆ lida" oauth2invalidRequest = "SolĀ·licitud no vĆ lida" @@ -3846,14 +3950,17 @@ fitToWidth = "Ajusta a l'amplada" actualSize = "Mida real" [viewer] +cannotPreviewFile = "No es pot previsualitzar el fitxer" +dualPageView = "Vista de dues pĆ gines" firstPage = "Primera pĆ gina" lastPage = "Última pĆ gina" -previousPage = "PĆ gina anterior" nextPage = "PĆ gina següent" +onlyPdfSupported = "El visualitzador nomĆ©s admet fitxers PDF. Aquest fitxer sembla ser d'un format diferent." +previousPage = "PĆ gina anterior" +singlePageView = "Vista d'una sola pĆ gina" +unknownFile = "Fitxer desconegut" zoomIn = "Amplia" zoomOut = "Redueix" -singlePageView = "Vista d'una sola pĆ gina" -dualPageView = "Vista de dues pĆ gines" [rightRail] closeSelected = "Tanca els fitxers seleccionats" @@ -3877,6 +3984,7 @@ toggleSidebar = "Mostra/oculta la barra lateral" exportSelected = "Exporta les pĆ gines seleccionades" toggleAnnotations = "Mostra/oculta les anotacions" annotationMode = "Activa/desactiva el mode d'anotació" +print = "Imprimeix el PDF" draw = "Dibuixa" save = "Desa" saveChanges = "Desa els canvis" @@ -3925,7 +4033,7 @@ files = "Fitxers" activity = "Registre" help = "Ajuda" account = "Compte" -config = "Config" +config = "Configuració" settings = "Ajustos" adminSettings = "Ajustos admin" allTools = "All Tools" @@ -4343,7 +4451,7 @@ features = "Banderes de funcions" processing = "Processament" [admin.settings.advanced.endpoints] -label = "Endpoints" +label = "Punts finals" manage = "Gestiona els endpoints de l'API" description = "La gestió d'endpoints es configura via YAML. Consulteu la documentació per a detalls sobre com habilitar/deshabilitar endpoints especĆ­fics." @@ -4494,6 +4602,7 @@ description = "URL o nom de fitxer de l'impressum (requerit en algunes jurisdicc title = "Premium i Enterprise" description = "Configureu la clau de llicĆØncia Premium o Enterprise." license = "Configuració de llicĆØncia" +noInput = "Proporcioneu una clau de llicĆØncia o un fitxer" [admin.settings.premium.licenseKey] toggle = "Tens una clau de llicĆØncia o un fitxer de certificat?" @@ -4511,6 +4620,25 @@ line1 = "Sobreescriure la clau de llicĆØncia actual no es pot desfer." line2 = "La llicĆØncia anterior es perdrĆ  permanentment si no en tens una còpia de seguretat." line3 = "Important: mantĆ©n les claus de llicĆØncia privades i segures. No les comparteixis mai pĆŗblicament." +[admin.settings.premium.inputMethod] +text = "Clau de llicĆØncia" +file = "Fitxer de certificat" + +[admin.settings.premium.file] +label = "Fitxer de certificat de llicĆØncia" +description = "Pugeu el vostre fitxer de llicĆØncia .lic o .cert de compres fora de lĆ­nia" +choose = "Trieu el fitxer de llicĆØncia" +selected = "Seleccionat: {{filename}} ({{size}})" +successMessage = "Fitxer de llicĆØncia pujat i activat correctament. No cal reiniciar." + +[admin.settings.premium.currentLicense] +title = "LlicĆØncia activa" +file = "Origen: Fitxer de llicĆØncia ({{path}})" +key = "Origen: Clau de llicĆØncia" +type = "Tipus: {{type}}" +noInput = "Proporcioneu una clau de llicĆØncia o pugeu un fitxer de certificat" +success = "ƈxit" + [admin.settings.premium.enabled] label = "Habilita les funcions Premium" description = "Habilita les comprovacions de clau per a funcions pro/enterprise" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} seleccionats" download = "Descarrega" delete = "Esborra" unsupported = "No compatible" +active = "Actiu" addToUpload = "Afegeix a la pujada" +closeFile = "Tanca el fitxer" deleteAll = "Suprimeix-ho tot" loadingFiles = "Carregant fitxers..." noFiles = "No hi ha fitxers disponibles" @@ -5132,7 +5262,7 @@ upgrade = "Actualitza ara →" freeTitle = "LlicĆØncia del servidor" overLimitTitle = "Cal una llicĆØncia de servidor" overLimitBody = "La nostra llicĆØncia permet fins a {{freeTierLimit}} usuaris gratuĆÆts per servidor. Tens {{overLimitUserCopy}} usuaris de Stirling. Per continuar sense interrupcions, actualitza al pla Stirling Server: seients ilĀ·limitats, edició de text de PDF i control d'administració complet per 99 $/servidor/mes." -freeBody = "La nostra llicĆØncia Open-Core permet fins a {{freeTierLimit}} usuaris gratuĆÆts per servidor. Per escalar sense interrupcions i obtenir accĆ©s anticipat a la nova eina d'edició de text PDF, recomanem el pla Stirling Server: edició completa i seients ilĀ·limitats per 99 $/servidor/mes." +freeBody = "La nostra llicĆØncia Open-Core permet fins a {{freeTierLimit}} usuaris gratuĆÆts per servidor. Per escalar sense interrupcions, recomanem el pla Stirling Server - places ilĀ·limitades i suport SSO per $99/servidor/mes." [onboarding.desktopInstall] title = "Baixa" @@ -5237,6 +5367,31 @@ error = "No s’ha pogut actualitzar l’estat de l’usuari" success = "Usuari suprimit correctament" error = "No s’ha pogut suprimir l’usuari" +[workspace.people.changePassword] +action = "Canvieu la contrasenya" +title = "Canvi de contrasenya" +subtitle = "Actualitza la contrasenya de" +newPassword = "Contrasenya nova" +confirmPassword = "Confirma la contrasenya" +placeholder = "IntroduĆÆu una contrasenya nova" +confirmPlaceholder = "Torneu a introduir la contrasenya nova" +passwordRequired = "IntroduĆÆu una contrasenya nova" +passwordMismatch = "Les contrasenyes no coincideixen" +generateRandom = "Genereu una contrasenya segura" +generatedPreview = "Contrasenya generada:" +copyTooltip = "Copieu al portapapers" +copiedToClipboard = "Contrasenya copiada al portapapers" +copyFailed = "No s'ha pogut copiar la contrasenya" +sendEmail = "Envieu un correu a l'usuari sobre aquest canvi" +includePassword = "Incloeu la contrasenya nova al correu" +forcePasswordChange = "Obligueu l'usuari a canviar la contrasenya en el pròxim inici de sessió" +emailUnavailable = "El correu d'aquest usuari no Ć©s una adreƧa de correu vĆ lida. Les notificacions estan desactivades." +smtpDisabled = "Les notificacions per correu electrònic requereixen habilitar SMTP als parĆ metres." +notifyOnly = "S'enviarĆ  un correu sense la contrasenya, informant l'usuari que un administrador l'ha canviada." +submit = "Actualitzeu la contrasenya" +success = "La contrasenya s'ha actualitzat correctament" +error = "No s'ha pogut actualitzar la contrasenya" + [workspace.people.emailInvite] tab = "Invitació per correu" description = "Escriviu o enganxeu correus a continuació, separats per comes. Els usuaris rebran credencials d’inici de sessió per correu electrònic." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Cal almenys una adreƧa de correu" submit = "Envia invitacions" success = "usuari(s) convidat(s) correctament" -partialSuccess = "Algunes invitacions han fallat" +partialFailure = "Algunes invitacions han fallat" allFailed = "No s’ha pogut convidar els usuaris" error = "No s’han pogut enviar les invitacions" @@ -5709,7 +5864,7 @@ title = "GrĆ fic d'Ćŗs dels endpoints" [usage.table] title = "EstadĆ­stiques detallades" -endpoint = "Endpoint" +endpoint = "Punt final" visits = "Visites" percentage = "Percentatge" noData = "No hi ha dades disponibles" @@ -5770,6 +5925,7 @@ subtitle = "Inicia sessió amb el teu compte de Stirling" [setup.selfhosted] title = "Inicia sessió al servidor" subtitle = "Introdueix les credencials del servidor" +link = "o connecteu-vos a un compte autoallotjat" [setup.server] title = "Connecta't al servidor" @@ -5788,6 +5944,14 @@ description = "Introdueix la URL completa del teu servidor autoallotjat de Stirl emptyUrl = "Introdueix una URL de servidor" unreachable = "No s'ha pogut connectar amb el servidor" testFailed = "Ha fallat la prova de connexió" +configFetch = "No s'ha pogut obtenir la configuració del servidor. Comproveu l'URL i torneu-ho a provar." + +[setup.server.error.securityDisabled] +title = "Inici de sessió no habilitat" +body = "Aquest servidor no tĆ© l'inici de sessió habilitat. Per connectar-hi, heu d'habilitar l'autenticació:" +step1 = "Establiu DOCKER_ENABLE_SECURITY=true al vostre entorn" +step2 = "O establiu security.enableLogin=true a settings.yml" +step3 = "Reinicieu el servidor" [setup.login] title = "Inicia sessió" @@ -5797,6 +5961,13 @@ submit = "Inicia sessió" signInWith = "Inicia sessió amb" oauthPending = "Obrint el navegador per autenticar-te..." orContinueWith = "O continua amb el correu electrònic" +serverRequirement = "Nota: el servidor ha de tenir l'inici de sessió habilitat." +showInstructions = "Com s'habilita?" +hideInstructions = "Amagueu les instruccions" +instructions = "Per habilitar l'inici de sessió al vostre servidor de Stirling PDF:" +instructionsEnvVar = "Establiu la variable d'entorn:" +instructionsOrYml = "O a settings.yml:" +instructionsRestart = "A continuació, reinicieu el servidor perquĆØ els canvis tinguin efecte." [setup.login.username] label = "Nom d'usuari" @@ -5853,6 +6024,7 @@ earlyAccess = "AccĆ©s anticipat" reset = "Restableix els canvis" downloadJson = "Descarrega JSON" generatePdf = "Genera PDF" +saveChanges = "Deseu els canvis" [pdfTextEditor.options.autoScaleText] title = "Autoajusta el text a les caixes" @@ -5890,6 +6062,8 @@ alpha = "Aquest visor alfa encara evoluciona—certs tipus de lletra, colors, ef [pdfTextEditor.empty] title = "No s'ha carregat cap document" subtitle = "Carrega un fitxer PDF o JSON per comenƧar a editar el contingut de text." +dropzone = "Arrossegueu i deixeu anar un fitxer PDF o JSON aquĆ­, o feu clic per explorar" +dropzoneWithFiles = "Seleccioneu un fitxer de la pestanya Fitxers o arrossegueu i deixeu anar aquĆ­ un fitxer PDF o JSON, o feu clic per explorar" [pdfTextEditor.welcomeBanner] title = "Benvingut a l'Editor de text PDF (accĆ©s anticipat)" diff --git a/frontend/public/locales/cs-CZ/translation.toml b/frontend/public/locales/cs-CZ/translation.toml index 0f80dd14b0..54d1d87289 100644 --- a/frontend/public/locales/cs-CZ/translation.toml +++ b/frontend/public/locales/cs-CZ/translation.toml @@ -163,6 +163,11 @@ unfavorite = "Odebrat z oblĆ­bených" fullscreen = "Přepnout na režim na celou obrazovku" sidebar = "Přepnout na režim postrannĆ­ho panelu" +[backendStartup] +notFoundTitle = "Backend nebyl nalezen" +retry = "Zkusit znovu" +unreachable = "Aplikace se nynĆ­ nemůže připojit k backendu. Ověřte stav backendu a sĆ­Å„ovĆ© připojenĆ­ a potĆ© to zkuste znovu." + [zipWarning] title = "Velký soubor ZIP" message = "Tento ZIP obsahuje {{count}} souborÅÆ. Přesto rozbalit?" @@ -347,7 +352,7 @@ teams = "Týmy" title = "Konfigurace" systemSettings = "SystĆ©movĆ” nastavenĆ­" features = "Funkce" -endpoints = "Endpoints" +endpoints = "KoncovĆ© body" database = "DatabĆ”ze" advanced = "PokročilĆ©" @@ -912,6 +917,9 @@ desc = "VytvÔřejte vĆ­cekrokovĆ© workflow řetězenĆ­m akcĆ­ PDF. IdeĆ”lnĆ­ pr desc = "Překryje PDF nad jiným PDF" title = "Překrýt PDF" +[home.pdfTextEditor] +title = "Editor textu PDF" +desc = "Upravujte existujĆ­cĆ­ text a obrĆ”zky v PDF" [home.addText] tags = "text,anotace,Å”tĆ­tek" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Nakreslený podpis" defaultImageLabel = "Nahraný podpis" defaultTextLabel = "Napsaný podpis" saveButton = "Uložit podpis" +savePersonal = "Uložit osobnĆ­" +saveShared = "Uložit sdĆ­lenĆ©" saveUnavailable = "Nejprve vytvořte podpis, abyste jej mohli uložit." noChanges = "AktuĆ”lnĆ­ podpis je již uložen." +tempStorageTitle = "DočasnĆ© ĆŗložiÅ”tě prohlížeče" +tempStorageDescription = "Podpisy jsou uloženy pouze ve vaÅ”em prohlížeči. Při vymazĆ”nĆ­ dat prohlížeče nebo při přepnutĆ­ na jiný prohlížeč budou ztraceny." +personalHeading = "OsobnĆ­ podpisy" +sharedHeading = "SdĆ­lenĆ© podpisy" +personalDescription = "Tyto podpisy vidĆ­te pouze vy." +sharedDescription = "VÅ”ichni uživatelĆ© mohou tyto podpisy vidět a používat." [sign.saved.type] canvas = "Kresba" @@ -3020,6 +3036,91 @@ title = "ZĆ­skat informace o PDF" header = "ZĆ­skat informace o PDF" submit = "ZĆ­skat informace" downloadJson = "StĆ”hnout JSON" +processing = "ProbĆ­hĆ” extrahovĆ”nĆ­ informacĆ­..." +results = "Výsledky" +noResults = "SpusÅ„te nĆ”stroj pro vygenerovĆ”nĆ­ zprĆ”vy." +downloads = "StaženĆ­" +noneDetected = "Nic nebylo zjiÅ”těno" +indexTitle = "Rejstřík" + +[getPdfInfo.report] +entryLabel = "ÚplnĆ© shrnutĆ­ informacĆ­" +shortTitle = "Informace o PDF" + +[getPdfInfo.sections] +metadata = "Metadata" +formFields = "FormulÔřovĆ” pole" +basicInfo = "ZĆ”kladnĆ­ informace" +documentInfo = "Informace o dokumentu" +compliance = "Shoda" +encryption = "Å ifrovĆ”nĆ­" +permissions = "OprĆ”vněnĆ­" +other = "OstatnĆ­" +perPageInfo = "Informace po strĆ”nkĆ”ch" +tableOfContents = "Obsah" + +[getPdfInfo.other] +attachments = "Přílohy" +embeddedFiles = "VloženĆ© soubory" +javaScript = "JavaScript" +layers = "Vrstvy" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Velikost" +annotations = "Anotace" +images = "ObrĆ”zky" +links = "Odkazy" +fonts = "PĆ­sma" +xobjects = "Počty XObjectÅÆ" +multimedia = "MultimĆ©dia" + +[getPdfInfo.summary] +pages = "StrĆ”nky" +fileSize = "Velikost souboru" +pdfVersion = "Verze PDF" +language = "Jazyk" +title = "Souhrn PDF" +author = "Autor" +created = "Vytvořeno" +modified = "Upraveno" +permsAll = "VÅ”echna oprĆ”vněnĆ­ povolena" +permsRestricted = "{{count}} omezenĆ­" +permsMixed = "NěkterĆ” oprĆ”vněnĆ­ jsou omezena" +hasCompliance = "Obsahuje standardy shody" +noCompliance = "ŽÔdnĆ© standardy shody" +basic = "ZĆ”kladnĆ­ informace" +documentInfo = "Informace o dokumentu" +securityTitle = "Stav zabezpečenĆ­" +technical = "TechnickĆ©" +overviewTitle = "Přehled PDF" + +[getPdfInfo.summary.security] +encrypted = "Å ifrovanĆ© PDF – chrĆ”něno heslem" +unencrypted = "NeÅ”ifrovanĆ© PDF – bez ochrany heslem" + +[getPdfInfo.summary.tech] +images = "ObrĆ”zky" +fonts = "PĆ­sma" +formFields = "FormulÔřovĆ” pole" +embeddedFiles = "VloženĆ© soubory" +javaScript = "JavaScript" +layers = "Vrstvy" +bookmarks = "ZĆ”ložky" +multimedia = "MultimĆ©dia" + +[getPdfInfo.summary.overview] +untitled = "nepojmenovaný dokument" +unknown = "NeznĆ”mý autor" +text = "Toto je PDF o {{pages}} strĆ”nkĆ”ch s nĆ”zvem {{title}} od autora {{author}} (verze PDF {{version}})." + +[getPdfInfo.error] +partial = "NěkterĆ© soubory se nepodařilo zpracovat." +unexpected = "Během extrahovĆ”nĆ­ doÅ”lo k neočekĆ”vanĆ© chybě." + +[getPdfInfo.status] +complete = "ExtrahovĆ”nĆ­ dokončeno" [extractPage] tags = "extrahovat" @@ -3438,6 +3539,9 @@ signinTitle = "ProsĆ­m přihlaste se" ssoSignIn = "PřihlĆ”sit se přes Single Sign-on" oAuth2AutoCreateDisabled = "AutomatickĆ© vytvÔřenĆ­ OAUTH2 uživatelÅÆ je zakĆ”zĆ”no" oAuth2AdminBlockedUser = "Registrace nebo přihlÔŔenĆ­ neregistrovaných uživatelÅÆ je momentĆ”lně blokovĆ”no. Kontaktujte prosĆ­m sprĆ”vce." +oAuth2RequiresLicense = "PřihlÔŔenĆ­ pomocĆ­ OAuth/SSO vyžaduje placenou licenci (Server nebo Enterprise). Kontaktujte prosĆ­m administrĆ”tora kvÅÆli upgradu vaÅ”eho plĆ”nu." +saml2RequiresLicense = "PřihlÔŔenĆ­ pomocĆ­ SAML vyžaduje placenou licenci (Server nebo Enterprise). Kontaktujte prosĆ­m administrĆ”tora kvÅÆli upgradu vaÅ”eho plĆ”nu." +maxUsersReached = "Byl dosažen maximĆ”lnĆ­ počet uživatelÅÆ pro vaÅ”i aktuĆ”lnĆ­ licenci. Kontaktujte prosĆ­m administrĆ”tora kvÅÆli upgradu vaÅ”eho plĆ”nu nebo přidĆ”nĆ­ dalŔích mĆ­st." oauth2RequestNotFound = "Požadavek na autorizaci nebyl nalezen" oauth2InvalidUserInfoResponse = "NeplatnĆ” odpověď s informacemi o uživateli" oauth2invalidRequest = "Neplatný požadavek" @@ -3846,14 +3950,17 @@ fitToWidth = "PřizpÅÆsobit Ŕířce" actualSize = "SkutečnĆ” velikost" [viewer] +cannotPreviewFile = "Nelze zobrazit nĆ”hled souboru" +dualPageView = "ZobrazenĆ­ dvou strĆ”nek" firstPage = "PrvnĆ­ strĆ”nka" lastPage = "PoslednĆ­ strĆ”nka" -previousPage = "PředchozĆ­ strĆ”nka" nextPage = "DalŔí strĆ”nka" +onlyPdfSupported = "Prohlížeč podporuje pouze soubory PDF. Tento soubor mĆ” zřejmě jiný formĆ”t." +previousPage = "PředchozĆ­ strĆ”nka" +singlePageView = "ZobrazenĆ­ jednĆ© strĆ”nky" +unknownFile = "NeznĆ”mý soubor" zoomIn = "Přiblížit" zoomOut = "OddĆ”lit" -singlePageView = "ZobrazenĆ­ jednĆ© strĆ”nky" -dualPageView = "ZobrazenĆ­ dvou strĆ”nek" [rightRail] closeSelected = "Zavřít vybranĆ© soubory" @@ -3877,6 +3984,7 @@ toggleSidebar = "Přepnout postrannĆ­ panel" exportSelected = "Exportovat vybranĆ© strĆ”nky" toggleAnnotations = "Přepnout viditelnost anotacĆ­" annotationMode = "Přepnout režim anotacĆ­" +print = "Tisk PDF" draw = "Kreslit" save = "Uložit" saveChanges = "Uložit změny" @@ -4153,7 +4261,7 @@ description = "Sledovat akce uživatelÅÆ a systĆ©movĆ© udĆ”losti pro compliance [admin.settings.security.audit.level] label = "Úroveň auditu" -description = "0=OFF, 1=BASIC, 2=STANDARD, 3=VERBOSE" +description = "0=VYPNUTO, 1=ZƁKLADNƍ, 2=STANDARDNƍ, 3=PODROBNƝ" [admin.settings.security.audit.retentionDays] label = "Doba uchovĆ”nĆ­ auditÅÆ (dny)" @@ -4494,6 +4602,7 @@ description = "URL nebo nĆ”zev souboru k Impressu (vyžadovĆ”no v některých ju title = "Premium a Enterprise" description = "Nakonfigurujte svÅÆj prĆ©miový nebo enterprise licenčnĆ­ klƭč." license = "Konfigurace licence" +noInput = "Zadejte licenčnĆ­ klƭč nebo soubor" [admin.settings.premium.licenseKey] toggle = "MĆ”te licenčnĆ­ klƭč nebo certifikačnĆ­ soubor?" @@ -4511,6 +4620,25 @@ line1 = "PřepsĆ”nĆ­ aktuĆ”lnĆ­ho licenčnĆ­ho klƭče nelze vrĆ”tit zpět." line2 = "PředchozĆ­ licence bude trvale ztracena, pokud ji nemĆ”te zĆ”lohovanou jinde." line3 = "DÅÆležitĆ©: UchovĆ”vejte licenčnĆ­ klƭče v soukromĆ­ a v bezpečƭ. Nikdy je nesdĆ­lejte veřejně." +[admin.settings.premium.inputMethod] +text = "LicenčnĆ­ klƭč" +file = "Soubor certifikĆ”tu" + +[admin.settings.premium.file] +label = "Soubor licenčnĆ­ho certifikĆ”tu" +description = "Nahrajte svÅÆj licenčnĆ­ soubor .lic nebo .cert z offline nĆ”kupu" +choose = "Vybrat licenčnĆ­ soubor" +selected = "VybrĆ”no: {{filename}} ({{size}})" +successMessage = "LicenčnĆ­ soubor byl ĆŗspěŔně nahrĆ”n a aktivovĆ”n. Restart nenĆ­ vyžadovĆ”n." + +[admin.settings.premium.currentLicense] +title = "AktivnĆ­ licence" +file = "Zdroj: LicenčnĆ­ soubor ({{path}})" +key = "Zdroj: LicenčnĆ­ klƭč" +type = "Typ: {{type}}" +noInput = "Zadejte licenčnĆ­ klƭč nebo nahrajte soubor certifikĆ”tu" +success = "Úspěch" + [admin.settings.premium.enabled] label = "Povolit prĆ©miovĆ© funkce" description = "Povolit kontrolu licenčnĆ­ho klƭče pro pro/enterprise funkce" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} vybrĆ”no" download = "StĆ”hnout" delete = "Smazat" unsupported = "NepodporovĆ”no" +active = "AktivnĆ­" addToUpload = "Přidat k nahrĆ”nĆ­" +closeFile = "Zavřít soubor" deleteAll = "Smazat vÅ”e" loadingFiles = "NačƭtĆ”nĆ­ souborÅÆ..." noFiles = "Nejsou k dispozici žÔdnĆ© soubory" @@ -5132,7 +5262,7 @@ upgrade = "Upgradovat nynĆ­ →" freeTitle = "ServerovĆ” licence" overLimitTitle = "VyžadovĆ”na serverovĆ” licence" overLimitBody = "NaÅ”e licencovĆ”nĆ­ umožňuje až {{freeTierLimit}} uživatelÅÆ zdarma na server. MĆ”te {{overLimitUserCopy}} uživatelÅÆ Stirling. Pro nepřeruÅ”enĆ© používĆ”nĆ­ přejděte na plĆ”n Stirling Server – neomezený počet mĆ­st, Ćŗpravy textu PDF a plnĆ” sprĆ”va za 99 $/server/měsĆ­c." -freeBody = "NaÅ”e licencovĆ”nĆ­ Open-Core umožňuje až {{freeTierLimit}} uživatelÅÆ zdarma na server. Pro nepřeruÅ”ený rÅÆst a přednostnĆ­ přístup k naÅ”emu novĆ©mu nĆ”stroji pro Ćŗpravu textu PDF doporučujeme plĆ”n Stirling Server – plnĆ© Ćŗpravy a neomezený počet mĆ­st za 99 $/server/měsĆ­c." +freeBody = "NaÅ”e licencovĆ”nĆ­ Open-Core umožňuje až {{freeTierLimit}} uživatelÅÆ zdarma na server. Pro nepřeruÅ”ovanĆ© Å”kĆ”lovĆ”nĆ­ doporučujeme plĆ”n Stirling Server - neomezený počet mĆ­st a podpora SSO za $99/server/měs." [onboarding.desktopInstall] title = "StĆ”hnout" @@ -5237,6 +5367,31 @@ error = "Nepodařilo se aktualizovat stav uživatele" success = "Uživatel ĆŗspěŔně smazĆ”n" error = "Nepodařilo se smazat uživatele" +[workspace.people.changePassword] +action = "Změnit heslo" +title = "Změna hesla" +subtitle = "Aktualizovat heslo pro" +newPassword = "NovĆ© heslo" +confirmPassword = "PotvrzenĆ­ hesla" +placeholder = "Zadejte novĆ© heslo" +confirmPlaceholder = "Zadejte novĆ© heslo znovu" +passwordRequired = "Zadejte prosĆ­m novĆ© heslo" +passwordMismatch = "Hesla se neshodujĆ­" +generateRandom = "Vygenerovat bezpečnĆ© heslo" +generatedPreview = "VygenerovanĆ© heslo:" +copyTooltip = "ZkopĆ­rovat do schrĆ”nky" +copiedToClipboard = "Heslo zkopĆ­rovĆ”no do schrĆ”nky" +copyFailed = "Heslo se nepodařilo zkopĆ­rovat" +sendEmail = "Odeslat uživateli e-mail o tĆ©to změně" +includePassword = "Zahrnout novĆ© heslo do e-mailu" +forcePasswordChange = "Vynutit změnu hesla při příŔtĆ­m přihlÔŔenĆ­" +emailUnavailable = "E-mailovĆ” adresa tohoto uživatele nenĆ­ platnĆ”. OznĆ”menĆ­ jsou deaktivovĆ”na." +smtpDisabled = "E-mailovĆ” oznĆ”menĆ­ vyžadujĆ­, aby bylo v nastavenĆ­ povoleno SMTP." +notifyOnly = "Bude odeslĆ”n e-mail bez hesla, který uživateli oznĆ”mĆ­, že ho změnil administrĆ”tor." +submit = "Aktualizovat heslo" +success = "Heslo bylo ĆŗspěŔně aktualizovĆ”no" +error = "Heslo se nepodařilo aktualizovat" + [workspace.people.emailInvite] tab = "PozvĆ”nka e‑mailem" description = "Níže napiÅ”te nebo vložte e‑maily oddělenĆ© ÄĆ”rkami. UživatelĆ© obdrží přihlaÅ”ovacĆ­ Ćŗdaje e‑mailem." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "uzivatel1@priklad.cz, uzivatel2@priklad.cz" emailsRequired = "Je vyžadovĆ”na alespoň jedna e‑mailovĆ” adresa" submit = "Odeslat pozvĆ”nky" success = "uživatel(Ć©) ĆŗspěŔně pozvĆ”ni" -partialSuccess = "NěkterĆ© pozvĆ”nky se nepodařilo odeslat" +partialFailure = "NěkterĆ” pozvĆ”nĆ­ selhala" allFailed = "Nepodařilo se pozvat uživatele" error = "Nepodařilo se odeslat pozvĆ”nky" @@ -5709,7 +5864,7 @@ title = "Graf využitĆ­ endpointÅÆ" [usage.table] title = "PodrobnĆ© statistiky" -endpoint = "Endpoint" +endpoint = "Koncový bod" visits = "NĆ”vÅ”těvy" percentage = "Procenta" noData = "ŽÔdnĆ” data nejsou k dispozici" @@ -5770,6 +5925,7 @@ subtitle = "Přihlaste se svým ĆŗÄtem Stirling" [setup.selfhosted] title = "PřihlĆ”sit se k serveru" subtitle = "Zadejte přihlaÅ”ovacĆ­ Ćŗdaje k vaÅ”emu serveru" +link = "nebo se připojte k ĆŗÄtu s vlastnĆ­m hostovĆ”nĆ­m" [setup.server] title = "Připojit k serveru" @@ -5788,6 +5944,14 @@ description = "Zadejte Ćŗplnou URL vaÅ”eho samohostovanĆ©ho serveru Stirling PDF emptyUrl = "Zadejte URL serveru" unreachable = "Nelze se připojit k serveru" testFailed = "Test připojenĆ­ selhal" +configFetch = "Nepodařilo se načƭst konfiguraci serveru. Zkontrolujte prosĆ­m URL a zkuste to znovu." + +[setup.server.error.securityDisabled] +title = "PřihlÔŔenĆ­ nenĆ­ povoleno" +body = "Na tomto serveru nenĆ­ povoleno přihlaÅ”ovĆ”nĆ­. Pokud se chcete připojit, musĆ­te povolit ověřovĆ”nĆ­:" +step1 = "Nastavte DOCKER_ENABLE_SECURITY=true ve svĆ©m prostředĆ­" +step2 = "Nebo nastavte security.enableLogin=true v souboru settings.yml" +step3 = "Restartujte server" [setup.login] title = "PřihlÔŔenĆ­" @@ -5797,6 +5961,13 @@ submit = "PřihlĆ”sit se" signInWith = "PřihlĆ”sit se pomocĆ­" oauthPending = "OtevĆ­rĆ”m prohlížeč pro ověřenĆ­..." orContinueWith = "Nebo pokračovat e-mailem" +serverRequirement = "PoznĆ”mka: Na serveru musĆ­ být povoleno přihlÔŔenĆ­." +showInstructions = "Jak povolit?" +hideInstructions = "Skrýt pokyny" +instructions = "Chcete-li povolit přihlÔŔenĆ­ na vaÅ”em serveru Stirling PDF:" +instructionsEnvVar = "Nastavte proměnnou prostředĆ­:" +instructionsOrYml = "Nebo v settings.yml:" +instructionsRestart = "PotĆ© restartujte server, aby se změny projevily." [setup.login.username] label = "UživatelskĆ© jmĆ©no" @@ -5840,7 +6011,7 @@ paragraph = "OdstavcovĆ” strĆ”nka" sparse = "Řídký text" [pdfTextEditor.groupingMode] -auto = "Auto" +auto = "Automaticky" paragraph = "Odstavec" singleLine = "Jeden řÔdek" @@ -5853,6 +6024,7 @@ earlyAccess = "Předběžný přístup" reset = "Obnovit změny" downloadJson = "StĆ”hnout JSON" generatePdf = "Vytvořit PDF" +saveChanges = "Uložit změny" [pdfTextEditor.options.autoScaleText] title = "Automaticky přizpÅÆsobit text rĆ”mečkÅÆm" @@ -5890,6 +6062,8 @@ alpha = "Tento alfa prohlížeč se stĆ”le vyvĆ­jĆ­ — některĆ© fonty, barvy, [pdfTextEditor.empty] title = "NenĆ­ načten žÔdný dokument" subtitle = "Načtěte soubor PDF nebo JSON a začněte upravovat text." +dropzone = "Sem přetĆ”hněte soubor PDF nebo JSON, případně kliknutĆ­m vyberte" +dropzoneWithFiles = "Vyberte soubor na kartě Soubory, nebo sem přetĆ”hněte soubor PDF či JSON, případně kliknutĆ­m vyberte" [pdfTextEditor.welcomeBanner] title = "VĆ­tejte v editoru textu PDF (předběžný přístup)" @@ -5932,13 +6106,13 @@ warnings = "VarovĆ”nĆ­" suggestions = "PoznĆ”mky" currentPageFonts = "Fonty na tĆ©to strĆ”nce" allFonts = "VÅ”echny fonty" -fallback = "fallback" +fallback = "nĆ”hradnĆ­" missing = "chybĆ­" perfectMessage = "VÅ”echny fonty lze reprodukovat dokonale." warningMessage = "NěkterĆ© fonty se nemusĆ­ vykreslit sprĆ”vně." infoMessage = "K dispozici jsou informace o reprodukci fontÅÆ." -perfect = "perfect" -subset = "subset" +perfect = "dokonalĆ©" +subset = "podmnožina" [pdfTextEditor.errors] invalidJson = "Nelze přečƭst soubor JSON. Ujistěte se, že byl vytvořen nĆ”strojem PDF to JSON." diff --git a/frontend/public/locales/da-DK/translation.toml b/frontend/public/locales/da-DK/translation.toml index bc44aa479a..88e688291d 100644 --- a/frontend/public/locales/da-DK/translation.toml +++ b/frontend/public/locales/da-DK/translation.toml @@ -163,6 +163,11 @@ unfavorite = "Fjern fra favoritter" fullscreen = "Skift til fuldskƦrmstilstand" sidebar = "Skift til sidepanel-tilstand" +[backendStartup] +notFoundTitle = "Backend ikke fundet" +retry = "PrĆøv igen" +unreachable = "Programmet kan i Ćøjeblikket ikke forbinde til backend. Kontroller backend-status og netvƦrksforbindelse, og prĆøv igen." + [zipWarning] title = "Stor ZIP-fil" message = "Denne ZIP indeholder {{count}} filer. Udpak alligevel?" @@ -347,7 +352,7 @@ teams = "Teams" title = "Konfiguration" systemSettings = "Systemindstillinger" features = "Funktioner" -endpoints = "Endpoints" +endpoints = "Slutpunkter" database = "Database" advanced = "Avanceret" @@ -359,7 +364,7 @@ connections = "Forbindelser" [settings.licensingAnalytics] title = "Licensering & Analytics" plan = "Plan" -audit = "Audit" +audit = "Revision" usageAnalytics = "Brugsanalyse" [settings.policiesPrivacy] @@ -556,13 +561,13 @@ totalEndpoints = "Endpoints i alt" totalVisits = "BesĆøg i alt" showing = "Viser" selectedVisits = "Valgte besĆøg" -endpoint = "Endpoint" +endpoint = "Slutpunkt" visits = "BesĆøg" percentage = "Procent" loading = "Laster..." failedToLoad = "Kunne ikke indlƦse endpoint-data. PrĆøv at opdatere." home = "Hjem" -login = "Login" +login = "Log ind" top = "Top" numberOfVisits = "Antal besĆøg" visitsTooltip = "BesĆøg: {0} ({1}% af totalen)" @@ -912,6 +917,9 @@ desc = "Byg flertrins-workflows ved at kƦde PDF-handlinger sammen. Ideelt til t desc = "Overlejrer PDF'er oven pĆ„ en anden PDF" title = "Overlejr PDF'er" +[home.pdfTextEditor] +title = "PDF-teksteditor" +desc = "Rediger eksisterende tekst og billeder i PDF'er" [home.addText] tags = "tekst,annotering,etiket" @@ -1173,7 +1181,7 @@ selectFilesPlaceholder = "VƦlg filer i hovedvisningen for at komme i gang" settings = "Indstillinger" conversionCompleted = "Konvertering fuldfĆørt" results = "Resultater" -defaultFilename = "converted_file" +defaultFilename = "konverteret_fil" conversionResults = "Konverteringsresultater" convertFrom = "KonvertĆ©r fra" convertTo = "KonvertĆ©r til" @@ -1213,9 +1221,9 @@ pdfaDigitalSignatureWarning = "PDF'en indeholder en digital signatur. Dette vil fileFormat = "Filformat" wordDoc = "Word-dokument" wordDocExt = "Word-dokument (.docx)" -odtExt = "OpenDocument Text (.odt)" +odtExt = "OpenDocument-tekst (.odt)" pptExt = "PowerPoint (.pptx)" -odpExt = "OpenDocument Presentation (.odp)" +odpExt = "OpenDocument-prƦsentation (.odp)" txtExt = "Almindelig tekst (.txt)" rtfExt = "Rich Text Format (.rtf)" selectedFiles = "Valgte filer" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Tegnet signatur" defaultImageLabel = "Uploadet signatur" defaultTextLabel = "Indtastet signatur" saveButton = "Gem signatur" +savePersonal = "Gem personlig" +saveShared = "Gem delt" saveUnavailable = "Opret fĆørst en signatur for at gemme den." noChanges = "NuvƦrende signatur er allerede gemt." +tempStorageTitle = "Midlertidig browserlagring" +tempStorageDescription = "Signaturer gemmes kun i din browser. De gĆ„r tabt, hvis du rydder browserdata eller skifter browser." +personalHeading = "Personlige signaturer" +sharedHeading = "Delte signaturer" +personalDescription = "Kun du kan se disse signaturer." +sharedDescription = "Alle brugere kan se og bruge disse signaturer." [sign.saved.type] canvas = "Tegning" @@ -3020,6 +3036,91 @@ title = "FĆ„ Info om PDF" header = "FĆ„ Info om PDF" submit = "FĆ„ Info" downloadJson = "Download JSON" +processing = "UdtrƦkker oplysninger..." +results = "Resultater" +noResults = "KĆør vƦrktĆøjet for at generere en rapport." +downloads = "Downloads" +noneDetected = "Ingen registreret" +indexTitle = "Indeks" + +[getPdfInfo.report] +entryLabel = "Fuldt informationsresumĆ©" +shortTitle = "PDF-oplysninger" + +[getPdfInfo.sections] +metadata = "Metadata" +formFields = "Formularfelter" +basicInfo = "GrundlƦggende info" +documentInfo = "Dokumentinfo" +compliance = "Overensstemmelse" +encryption = "Kryptering" +permissions = "Tilladelser" +other = "Andet" +perPageInfo = "Info pr. side" +tableOfContents = "Indholdsfortegnelse" + +[getPdfInfo.other] +attachments = "VedhƦftninger" +embeddedFiles = "Indlejrede filer" +javaScript = "JavaScript" +layers = "Lag" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "StĆørrelse" +annotations = "AnmƦrkninger" +images = "Billeder" +links = "Links" +fonts = "Skrifttyper" +xobjects = "Antal XObjects" +multimedia = "Multimedie" + +[getPdfInfo.summary] +pages = "Sider" +fileSize = "FilstĆørrelse" +pdfVersion = "PDF-version" +language = "Sprog" +title = "PDF-resumĆ©" +author = "Forfatter" +created = "Oprettet" +modified = "Ɔndret" +permsAll = "Alle tilladelser tilladt" +permsRestricted = "{{count}} begrƦnsninger" +permsMixed = "Nogle tilladelser er begrƦnsede" +hasCompliance = "Har overensstemmelsesstandarder" +noCompliance = "Ingen overensstemmelsesstandarder" +basic = "GrundlƦggende oplysninger" +documentInfo = "Dokumentoplysninger" +securityTitle = "Sikkerhedsstatus" +technical = "Teknisk" +overviewTitle = "PDF-oversigt" + +[getPdfInfo.summary.security] +encrypted = "Krypteret PDF - med adgangskodebeskyttelse" +unencrypted = "Ukrypteret PDF - ingen adgangskodebeskyttelse" + +[getPdfInfo.summary.tech] +images = "Billeder" +fonts = "Skrifttyper" +formFields = "Formularfelter" +embeddedFiles = "Indlejrede filer" +javaScript = "JavaScript" +layers = "Lag" +bookmarks = "BogmƦrker" +multimedia = "Multimedie" + +[getPdfInfo.summary.overview] +untitled = "et dokument uden titel" +unknown = "Ukendt forfatter" +text = "Dette er en PDF pĆ„ {{pages}} sider med titlen {{title}}, oprettet af {{author}} (PDF-version {{version}})." + +[getPdfInfo.error] +partial = "Nogle filer kunne ikke behandles." +unexpected = "Uventet fejl under udtrƦkning." + +[getPdfInfo.status] +complete = "UdtrƦkning fuldfĆørt" [extractPage] tags = "udtrƦk" @@ -3438,6 +3539,9 @@ signinTitle = "Log venligst ind" ssoSignIn = "Log ind via Single Sign-on" oAuth2AutoCreateDisabled = "OAUTH2 Auto-Opret Bruger Deaktiveret" oAuth2AdminBlockedUser = "Registrering eller login af ikke-registrerede brugere er i Ćøjeblikket blokeret. Kontakt venligst administratoren." +oAuth2RequiresLicense = "OAuth/SSO-login krƦver en betalt licens (Server eller Enterprise). Kontakt administratoren for at opgradere din plan." +saml2RequiresLicense = "SAML-login krƦver en betalt licens (Server eller Enterprise). Kontakt administratoren for at opgradere din plan." +maxUsersReached = "Maksimalt antal brugere er nĆ„et for din nuvƦrende licens. Kontakt administratoren for at opgradere din plan eller tilfĆøje flere pladser." oauth2RequestNotFound = "Autorisationsanmodning ikke fundet" oauth2InvalidUserInfoResponse = "Ugyldigt Brugerinfo Svar" oauth2invalidRequest = "Ugyldig Anmodning" @@ -3771,7 +3875,7 @@ version = "NuvƦrende udgivelse" title = "API-dokumentation" header = "API-dokumentation" desc = "Se og test Stirling PDF API-endpoints" -tags = "api,documentation,swagger,endpoints,development" +tags = "api,dokumentation,swagger,endepunkter,udvikling" [cookieBanner.popUp] title = "SĆ„dan bruger vi cookies" @@ -3846,14 +3950,17 @@ fitToWidth = "Tilpas til bredde" actualSize = "Faktisk stĆørrelse" [viewer] +cannotPreviewFile = "Kan ikke forhĆ„ndsvise fil" +dualPageView = "To-siders visning" firstPage = "FĆørste side" lastPage = "Sidste side" -previousPage = "Forrige side" nextPage = "NƦste side" +onlyPdfSupported = "Visningen understĆøtter kun PDF-filer. Denne fil ser ud til at vƦre et andet format." +previousPage = "Forrige side" +singlePageView = "Enkelt-sides visning" +unknownFile = "Ukendt fil" zoomIn = "Zoom ind" zoomOut = "Zoom ud" -singlePageView = "Enkelt-sides visning" -dualPageView = "To-siders visning" [rightRail] closeSelected = "Luk valgte filer" @@ -3877,6 +3984,7 @@ toggleSidebar = "Skift sidepanel" exportSelected = "Eksporter valgte sider" toggleAnnotations = "Skift visning af annoteringer" annotationMode = "Skift annoteringstilstand" +print = "Udskriv PDF" draw = "Tegn" save = "Gem" saveChanges = "Gem Ʀndringer" @@ -4235,11 +4343,11 @@ label = "Issuer-URL" description = "OAuth2-udbyderens issuer-URL" [admin.settings.connections.oauth2.clientId] -label = "Client ID" +label = "Klient-ID" description = "OAuth2 Client ID fra din udbyder" [admin.settings.connections.oauth2.clientSecret] -label = "Client Secret" +label = "Klienthemmelighed" description = "OAuth2 Client Secret fra din udbyder" [admin.settings.connections.oauth2.useAsUsername] @@ -4343,7 +4451,7 @@ features = "Funktionsflag" processing = "Behandling" [admin.settings.advanced.endpoints] -label = "Endpoints" +label = "Slutpunkter" manage = "Administrer API-endpoints" description = "Endpointstyring konfigureres via YAML. Se dokumentationen for detaljer om aktivering/deaktivering af specifikke endpoints." @@ -4494,6 +4602,7 @@ description = "URL eller filnavn til impressum (pĆ„krƦvet i nogle jurisdiktione title = "Premium og Enterprise" description = "Konfigurer din premium- eller enterprise-licensnĆøgle." license = "Licenskonfiguration" +noInput = "Angiv en licensnĆøgle eller fil" [admin.settings.premium.licenseKey] toggle = "Har du en licensnĆøgle eller en certifikatfil?" @@ -4511,6 +4620,25 @@ line1 = "Overskrivning af din nuvƦrende licensnĆøgle kan ikke fortrydes." line2 = "Din tidligere licens gĆ„r permanent tabt, medmindre du har sikkerhedskopieret den andetsteds." line3 = "Vigtigt: Hold licensnĆøgler private og sikre. Del dem aldrig offentligt." +[admin.settings.premium.inputMethod] +text = "LicensnĆøgle" +file = "Certifikatfil" + +[admin.settings.premium.file] +label = "Licenscertifikatfil" +description = "Upload din .lic- eller .cert-licensfil fra offlinekĆøb" +choose = "VƦlg licensfil" +selected = "Valgt: {{filename}} ({{size}})" +successMessage = "Licensfil uploadet og aktiveret. Genstart er ikke pĆ„krƦvet." + +[admin.settings.premium.currentLicense] +title = "Aktiv licens" +file = "Kilde: Licensfil ({{path}})" +key = "Kilde: LicensnĆøgle" +type = "Type: {{type}}" +noInput = "Angiv en licensnĆøgle eller upload en certifikatfil" +success = "Succes" + [admin.settings.premium.enabled] label = "AktivĆ©r premium-funktioner" description = "AktivĆ©r licensnĆøgletjek for pro-/enterprise-funktioner" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} valgt" download = "Download" delete = "Slet" unsupported = "Ikke understĆøttet" +active = "Aktiv" addToUpload = "FĆøj til upload" +closeFile = "Luk fil" deleteAll = "Slet alle" loadingFiles = "IndlƦser filer..." noFiles = "Ingen filer tilgƦngelige" @@ -5132,7 +5262,7 @@ upgrade = "Opgrader nu →" freeTitle = "Serverlicens" overLimitTitle = "Serverlicens pĆ„krƦvet" overLimitBody = "Vores licens tillader op til {{freeTierLimit}} brugere gratis pr. server. Du har {{overLimitUserCopy}} Stirling-brugere. For at fortsƦtte uden afbrydelser skal du opgradere til Stirling Server-abonnementet – ubegrƦnsede pladser, PDF-tekstredigering og fuld admin-kontrol for $99/server/md." -freeBody = "Vores Open-Core-licens tillader op til {{freeTierLimit}} brugere gratis pr. server. For at skalere uden afbrydelser og fĆ„ tidlig adgang til vores nye PDF-tekstredigeringsvƦrktĆøj anbefaler vi Stirling Server-planen – fuld redigering og ubegrƦnsede pladser for $99/server/md." +freeBody = "Vores Open-Core-licens tillader op til {{freeTierLimit}} brugere gratis pr. server. For at skalere uden afbrydelser anbefaler vi Stirling Server-planen – ubegrƦnsede pladser og SSO-understĆøttelse for $99/server/md." [onboarding.desktopInstall] title = "Download" @@ -5237,6 +5367,31 @@ error = "Kunne ikke opdatere brugerstatus" success = "Bruger slettet" error = "Kunne ikke slette bruger" +[workspace.people.changePassword] +action = "Skift adgangskode" +title = "Skift adgangskode" +subtitle = "Opdater adgangskoden for" +newPassword = "Ny adgangskode" +confirmPassword = "BekrƦft adgangskode" +placeholder = "Indtast en ny adgangskode" +confirmPlaceholder = "Indtast den nye adgangskode igen" +passwordRequired = "Angiv en ny adgangskode" +passwordMismatch = "Adgangskoderne matcher ikke" +generateRandom = "GenerĆ©r sikker adgangskode" +generatedPreview = "Genereret adgangskode:" +copyTooltip = "KopiĆ©r til udklipsholder" +copiedToClipboard = "Adgangskode kopieret til udklipsholderen" +copyFailed = "Kunne ikke kopiere adgangskoden" +sendEmail = "Send en e-mail til brugeren om denne Ʀndring" +includePassword = "Medtag den nye adgangskode i e-mailen" +forcePasswordChange = "Tving brugeren til at Ʀndre adgangskode ved nƦste login" +emailUnavailable = "Denne brugers e-mail er ikke en gyldig e-mailadresse. Meddelelser er deaktiveret." +smtpDisabled = "E-mailmeddelelser krƦver, at SMTP er aktiveret i indstillingerne." +notifyOnly = "Der sendes en e-mail uden adgangskoden, som informerer brugeren om, at en administrator har Ʀndret den." +submit = "Opdater adgangskode" +success = "Adgangskoden blev opdateret" +error = "Kunne ikke opdatere adgangskoden" + [workspace.people.emailInvite] tab = "E-mailinvitation" description = "Skriv eller indsƦt e-mails nedenfor, adskilt af kommaer. Brugere modtager loginoplysninger via e-mail." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Mindst Ć©n e-mailadresse er pĆ„krƦvet" submit = "Send invitationer" success = "Bruger(e) inviteret" -partialSuccess = "Nogle invitationer mislykkedes" +partialFailure = "Nogle invitationer mislykkedes" allFailed = "Kunne ikke invitere brugere" error = "Kunne ikke sende invitationer" @@ -5288,8 +5443,8 @@ emailDisabled = "E-mailinvitationer krƦver SMTP-konfiguration og mail.enableInv [workspace.people.license] users = "brugere" availableSlots = "TilgƦngelige pladser" -grandfathered = "Grandfathered" -grandfatheredShort = "{{count}} grandfathered" +grandfathered = "PĆ„ gamle vilkĆ„r" +grandfatheredShort = "{{count}} pĆ„ gamle vilkĆ„r" fromLicense = "fra licens" slotsAvailable = "{{count}} ledig(e) brugerplads(er)" noSlotsAvailable = "Ingen pladser tilgƦngelige" @@ -5709,7 +5864,7 @@ title = "Diagram over endpoint-brug" [usage.table] title = "Detaljeret statistik" -endpoint = "Endpoint" +endpoint = "Slutpunkt" visits = "BesĆøg" percentage = "Procent" noData = "Ingen data tilgƦngelige" @@ -5752,7 +5907,7 @@ label = "VƦlg server" description = "Selvhostet server" [setup.step3] -label = "Login" +label = "Log ind" description = "Indtast loginoplysninger" [setup.mode.saas] @@ -5770,6 +5925,7 @@ subtitle = "Log ind med din Stirling-konto" [setup.selfhosted] title = "Log ind pĆ„ server" subtitle = "Indtast dine server-loginoplysninger" +link = "eller opret forbindelse til en selvhostet konto" [setup.server] title = "Forbind til server" @@ -5788,6 +5944,14 @@ description = "Indtast den fulde URL til din selvhostede Stirling PDF-server" emptyUrl = "Indtast en server-URL" unreachable = "Kunne ikke forbinde til server" testFailed = "Forbindelsestest mislykkedes" +configFetch = "Kunne ikke hente serverkonfiguration. KontrollĆ©r URL'en, og prĆøv igen." + +[setup.server.error.securityDisabled] +title = "Login ikke aktiveret" +body = "Denne server har ikke login aktiveret. For at oprette forbindelse til denne server skal du aktivere godkendelse:" +step1 = "SƦt DOCKER_ENABLE_SECURITY=true i dit miljĆø" +step2 = "Eller sƦt security.enableLogin=true i settings.yml" +step3 = "Genstart serveren" [setup.login] title = "Log ind" @@ -5797,6 +5961,13 @@ submit = "Log ind" signInWith = "Log ind med" oauthPending = "ƅbner browser for godkendelse..." orContinueWith = "Eller fortsƦt med email" +serverRequirement = "BemƦrk: Serveren skal have login aktiveret." +showInstructions = "Hvordan aktiveres det?" +hideInstructions = "Skjul instruktioner" +instructions = "SĆ„dan aktiverer du login pĆ„ din Stirling PDF-server:" +instructionsEnvVar = "SƦt miljĆøvariablen:" +instructionsOrYml = "Eller i settings.yml:" +instructionsRestart = "Genstart derefter serveren, sĆ„ Ʀndringerne trƦder i kraft." [setup.login.username] label = "Brugernavn" @@ -5853,6 +6024,7 @@ earlyAccess = "Tidlig adgang" reset = "Nulstil Ʀndringer" downloadJson = "Download JSON" generatePdf = "Generer PDF" +saveChanges = "Gem Ʀndringer" [pdfTextEditor.options.autoScaleText] title = "AutoskalĆ©r tekst, sĆ„ den passer i bokse" @@ -5890,6 +6062,8 @@ alpha = "Denne alpha-fremviser er stadig under udvikling—visse skrifttyper, fa [pdfTextEditor.empty] title = "Intet dokument indlƦst" subtitle = "IndlƦs en PDF- eller JSON-fil for at begynde at redigere tekstindhold." +dropzone = "TrƦk og slip en PDF- eller JSON-fil her, eller klik for at gennemse" +dropzoneWithFiles = "VƦlg en fil fra fanen Filer, eller trƦk og slip en PDF- eller JSON-fil her, eller klik for at gennemse" [pdfTextEditor.welcomeBanner] title = "Velkommen til PDF-teksteditor (Early Access)" diff --git a/frontend/public/locales/de-DE/translation.toml b/frontend/public/locales/de-DE/translation.toml index c8c8e6f71f..274de764ea 100644 --- a/frontend/public/locales/de-DE/translation.toml +++ b/frontend/public/locales/de-DE/translation.toml @@ -163,6 +163,11 @@ unfavorite = "Aus Favoriten entfernen" fullscreen = "In den Vollbildmodus wechseln" sidebar = "In den Seitenleistenmodus wechseln" +[backendStartup] +notFoundTitle = "Backend nicht gefunden" +retry = "Erneut versuchen" +unreachable = "Die Anwendung kann derzeit keine Verbindung zum Backend herstellen. Überprüfen Sie den Backend-Status und die Netzwerkverbindung und versuchen Sie es dann erneut." + [zipWarning] title = "Große ZIP-Datei" message = "Dieses ZIP enthƤlt {{count}} Dateien. Trotzdem extrahieren?" @@ -347,7 +352,7 @@ teams = "Teams" title = "Konfiguration" systemSettings = "Systemeinstellungen" features = "Funktionen" -endpoints = "Endpoints" +endpoints = "Endpunkte" database = "Datenbank" advanced = "Erweitert" @@ -383,7 +388,7 @@ logout = "Abmelden" [settings.connection.mode] saas = "Stirling Cloud" -selfhosted = "Self-Hosted" +selfhosted = "Selbst gehostet" [settings.general] title = "Allgemein" @@ -612,7 +617,7 @@ desc = "Anzeigen, Kommentieren, Text oder Bilder hinzufügen" brandAlt = "Stirling PDF-Logo" openFiles = "Dateien ƶffnen" swipeHint = "Zum Wechseln der Ansicht nach links oder rechts wischen" -tools = "Tools" +tools = "Werkzeuge" toolsSlide = "Bereich für Toolauswahl" viewSwitcher = "Ansicht des Arbeitsbereichs wechseln" workbenchSlide = "Arbeitsbereichs-Panel" @@ -912,9 +917,12 @@ desc = "Mehrstufige ArbeitsablƤufe durch Verkettung von PDF-Aktionen erstellen. desc = "Ein PDF über ein anderes legen" title = "PDFs überlagern" +[home.pdfTextEditor] +title = "PDF-Texteditor" +desc = "Vorhandenen Text und Bilder in PDFs bearbeiten" [home.addText] -tags = "text,annotation,label" +tags = "text,anmerkung,beschriftung" title = "Text hinzufügen" desc = "Beliebigen Text überall in Ihrem PDF hinzufügen" @@ -1213,7 +1221,7 @@ pdfaDigitalSignatureWarning = "Das PDF enthƤlt eine digitale Signatur. Sie wird fileFormat = "Dateiformat" wordDoc = "Word-Dokument" wordDocExt = "Word-Dokument (.docx)" -odtExt = "OpenDocument Text (.odt)" +odtExt = "OpenDocument-Text (.odt)" pptExt = "PowerPoint (.pptx)" odpExt = "OpenDocument PrƤsentation (.odp)" txtExt = "Einfacher Text (.txt)" @@ -2259,12 +2267,20 @@ defaultCanvasLabel = "Gezeichnete Unterschrift" defaultImageLabel = "Hochgeladene Unterschrift" defaultTextLabel = "Getippte Unterschrift" saveButton = "Unterschrift speichern" +savePersonal = "Persƶnlich speichern" +saveShared = "Geteilt speichern" saveUnavailable = "Erstellen Sie zuerst eine Unterschrift, um sie zu speichern." noChanges = "Die aktuelle Unterschrift ist bereits gespeichert." +tempStorageTitle = "TemporƤrer Browser-Speicher" +tempStorageDescription = "Signaturen werden nur in Ihrem Browser gespeichert. Sie gehen verloren, wenn Sie Browserdaten lƶschen oder den Browser wechseln." +personalHeading = "Persƶnliche Signaturen" +sharedHeading = "Geteilte Signaturen" +personalDescription = "Nur Sie kƶnnen diese Signaturen sehen." +sharedDescription = "Alle Benutzer kƶnnen diese Signaturen sehen und verwenden." [sign.saved.type] canvas = "Zeichnung" -image = "Upload" +image = "Hochladen" text = "Text" [sign.saved.status] @@ -3020,6 +3036,91 @@ title = "Alle Informationen anzeigen" header = "Alle Informationen anzeigen" submit = "Informationen anzeigen" downloadJson = "Als JSON herunterladen" +processing = "Informationen werden extrahiert..." +results = "Ergebnisse" +noResults = "Führen Sie das Tool aus, um einen Bericht zu erstellen." +downloads = "Downloads" +noneDetected = "Keine erkannt" +indexTitle = "Index" + +[getPdfInfo.report] +entryLabel = "VollstƤndige Informationsübersicht" +shortTitle = "PDF-Informationen" + +[getPdfInfo.sections] +metadata = "Metadaten" +formFields = "Formularfelder" +basicInfo = "Grundlegende Informationen" +documentInfo = "Dokumentinformationen" +compliance = "Compliance" +encryption = "Verschlüsselung" +permissions = "Berechtigungen" +other = "Sonstiges" +perPageInfo = "Informationen pro Seite" +tableOfContents = "Inhaltsverzeichnis" + +[getPdfInfo.other] +attachments = "AnhƤnge" +embeddedFiles = "Eingebettete Dateien" +javaScript = "JavaScript" +layers = "Ebenen" +structureTree = "Strukturbaum" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Größe" +annotations = "Anmerkungen" +images = "Bilder" +links = "Links" +fonts = "Schriftarten" +xobjects = "XObject-Anzahl" +multimedia = "Multimedia" + +[getPdfInfo.summary] +pages = "Seiten" +fileSize = "Dateigröße" +pdfVersion = "PDF-Version" +language = "Sprache" +title = "PDF-Zusammenfassung" +author = "Autor" +created = "Erstellt" +modified = "GeƤndert" +permsAll = "Alle Berechtigungen erlaubt" +permsRestricted = "{{count}} EinschrƤnkungen" +permsMixed = "Einige Berechtigungen eingeschrƤnkt" +hasCompliance = "Entspricht Compliance-Standards" +noCompliance = "Keine Compliance-Standards" +basic = "Grundlegende Informationen" +documentInfo = "Dokumentinformationen" +securityTitle = "Sicherheitsstatus" +technical = "Technisch" +overviewTitle = "PDF-Übersicht" + +[getPdfInfo.summary.security] +encrypted = "Verschlüsseltes PDF - Passwortschutz vorhanden" +unencrypted = "Unverschlüsseltes PDF - Kein Passwortschutz" + +[getPdfInfo.summary.tech] +images = "Bilder" +fonts = "Schriftarten" +formFields = "Formularfelder" +embeddedFiles = "Eingebettete Dateien" +javaScript = "JavaScript" +layers = "Ebenen" +bookmarks = "Lesezeichen" +multimedia = "Multimedia" + +[getPdfInfo.summary.overview] +untitled = "ein unbenanntes Dokument" +unknown = "Unbekannter Autor" +text = "Dies ist ein {{pages}}-seitiges PDF mit dem Titel {{title}}, erstellt von {{author}} (PDF-Version {{version}})." + +[getPdfInfo.error] +partial = "Einige Dateien konnten nicht verarbeitet werden." +unexpected = "Unerwarteter Fehler wƤhrend der Extraktion." + +[getPdfInfo.status] +complete = "Extraktion abgeschlossen" [extractPage] tags = "extrahieren,seite" @@ -3438,6 +3539,9 @@ signinTitle = "Bitte melden Sie sich an." ssoSignIn = "Anmeldung per Single Sign-On" oAuth2AutoCreateDisabled = "OAUTH2 Benutzer automatisch erstellen deaktiviert" oAuth2AdminBlockedUser = "Die Registrierung bzw. das anmelden von nicht registrierten Benutzern ist derzeit gesperrt. Bitte wenden Sie sich an den Administrator." +oAuth2RequiresLicense = "OAuth/SSO-Anmeldung erfordert eine kostenpflichtige Lizenz (Server oder Enterprise). Bitte wenden Sie sich an den Administrator, um Ihren Plan zu aktualisieren." +saml2RequiresLicense = "SAML-Anmeldung erfordert eine kostenpflichtige Lizenz (Server oder Enterprise). Bitte wenden Sie sich an den Administrator, um Ihren Plan zu aktualisieren." +maxUsersReached = "Die maximale Benutzeranzahl für Ihre aktuelle Lizenz wurde erreicht. Bitte wenden Sie sich an den Administrator, um Ihren Plan zu aktualisieren oder weitere BenutzerplƤtze hinzuzufügen." oauth2RequestNotFound = "Autorisierungsanfrage nicht gefunden" oauth2InvalidUserInfoResponse = "Ungültige Benutzerinformationsantwort" oauth2invalidRequest = "ungültige Anfrage" @@ -3846,14 +3950,17 @@ fitToWidth = "An Breite anpassen" actualSize = "Originalgröße" [viewer] +cannotPreviewFile = "Datei kann nicht in der Vorschau angezeigt werden" +dualPageView = "Doppelseitenansicht" firstPage = "Erste Seite" lastPage = "Letzte Seite" -previousPage = "Vorherige Seite" nextPage = "NƤchste Seite" +onlyPdfSupported = "Der Viewer unterstützt nur PDF-Dateien. Diese Datei scheint ein anderes Format zu haben." +previousPage = "Vorherige Seite" +singlePageView = "Einzelseitenansicht" +unknownFile = "Unbekannte Datei" zoomIn = "Vergrößern" zoomOut = "Verkleinern" -singlePageView = "Einzelseitenansicht" -dualPageView = "Doppelseitenansicht" [rightRail] closeSelected = "AusgewƤhlte Dateien schließen" @@ -3877,6 +3984,7 @@ toggleSidebar = "Seitenleiste umschalten" exportSelected = "AusgewƤhlte Seiten exportieren" toggleAnnotations = "Anmerkungen ein-/ausblenden" annotationMode = "Anmerkungsmodus umschalten" +print = "PDF drucken" draw = "Zeichnen" save = "Speichern" saveChanges = "Ƅnderungen speichern" @@ -3928,7 +4036,7 @@ account = "Konto" config = "Konfig" settings = "Optionen" adminSettings = "Admin Optionen" -allTools = "Tools" +allTools = "Werkzeuge" reader = "Reader" [quickAccess.helpMenu] @@ -4494,6 +4602,7 @@ description = "URL oder Dateiname zum Impressum (in einigen Rechtsordnungen erfo title = "Premium & Enterprise" description = "Ihren Premium- oder Enterprise-Lizenzschlüssel konfigurieren." license = "Lizenzkonfiguration" +noInput = "Bitte geben Sie einen Lizenzschlüssel oder eine Datei an" [admin.settings.premium.licenseKey] toggle = "Lizenzschlüssel oder Zertifikatsdatei vorhanden?" @@ -4511,6 +4620,25 @@ line1 = "Das Überschreiben Ihres aktuellen Lizenzschlüssels kann nicht rückg line2 = "Ihre vorherige Lizenz geht dauerhaft verloren, sofern Sie sie nicht anderweitig gesichert haben." line3 = "Wichtig: Halten Sie Lizenzschlüssel privat und sicher. Geben Sie sie niemals ƶffentlich weiter." +[admin.settings.premium.inputMethod] +text = "Lizenzschlüssel" +file = "Zertifikatsdatei" + +[admin.settings.premium.file] +label = "Lizenz-Zertifikatsdatei" +description = "Laden Sie Ihre .lic- oder .cert-Lizenzdatei aus Offline-KƤufen hoch" +choose = "Lizenzdatei auswƤhlen" +selected = "AusgewƤhlt: {{filename}} ({{size}})" +successMessage = "Lizenzdatei erfolgreich hochgeladen und aktiviert. Kein Neustart erforderlich." + +[admin.settings.premium.currentLicense] +title = "Aktive Lizenz" +file = "Quelle: Lizenzdatei ({{path}})" +key = "Quelle: Lizenzschlüssel" +type = "Typ: {{type}}" +noInput = "Bitte geben Sie einen Lizenzschlüssel an oder laden Sie eine Zertifikatdatei hoch" +success = "Erfolg" + [admin.settings.premium.enabled] label = "Premium-Funktionen aktivieren" description = "Lizenzschlüssel-Prüfungen für Pro-/Enterprise-Funktionen aktivieren" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} ausgewƤhlt" download = "Herunterladen" delete = "Lƶschen" unsupported = "Nicht unterstützt" +active = "Aktiv" addToUpload = "Zum Upload hinzufügen" +closeFile = "Datei schließen" deleteAll = "Alle lƶschen" loadingFiles = "Dateien werden geladen..." noFiles = "Keine Dateien verfügbar" @@ -5132,7 +5262,7 @@ upgrade = "Jetzt upgraden →" freeTitle = "Server-Lizenz" overLimitTitle = "Server-Lizenz erforderlich" overLimitBody = "Unsere Lizenz erlaubt bis zu {{freeTierLimit}} Nutzer pro Server kostenlos. Sie haben {{overLimitUserCopy}} Stirling-Nutzer. Um ohne Unterbrechung fortzufahren, upgraden Sie auf den Stirling-Server-Plan – unbegrenzte PlƤtze, PDF-Textbearbeitung und volle Admin-Kontrolle für $99/Server/Monat." -freeBody = "Unsere Open-Core-Lizenz erlaubt bis zu {{freeTierLimit}} Nutzer pro Server kostenlos. Für unterbrechungsfreies Skalieren und frühen Zugriff auf unser neues PDF-Textbearbeitungs-Tool empfehlen wir den Stirling-Server-Plan – volle Bearbeitung und unbegrenzte PlƤtze für $99/Server/Monat." +freeBody = "Unsere Open-Core-Lizenz erlaubt bis zu {{freeTierLimit}} Nutzern pro Server kostenlos. Um unterbrechungsfrei zu skalieren, empfehlen wir den Stirling Server-Plan - unbegrenzte PlƤtze und SSO-Unterstützung für $99/Server/Monat." [onboarding.desktopInstall] title = "Download" @@ -5237,6 +5367,31 @@ error = "Benutzerstatus konnte nicht aktualisiert werden" success = "Benutzer erfolgreich gelƶscht" error = "Benutzer konnte nicht gelƶscht werden" +[workspace.people.changePassword] +action = "Passwort Ƥndern" +title = "Passwort Ƥndern" +subtitle = "Passwort aktualisieren für" +newPassword = "Neues Passwort" +confirmPassword = "Passwort bestƤtigen" +placeholder = "Neues Passwort eingeben" +confirmPlaceholder = "Neues Passwort erneut eingeben" +passwordRequired = "Bitte geben Sie ein neues Passwort ein" +passwordMismatch = "Passwƶrter stimmen nicht überein" +generateRandom = "Sicheres Passwort generieren" +generatedPreview = "Generiertes Passwort:" +copyTooltip = "In Zwischenablage kopieren" +copiedToClipboard = "Passwort in die Zwischenablage kopiert" +copyFailed = "Kopieren des Passworts fehlgeschlagen" +sendEmail = "Den Benutzer per E-Mail über diese Ƅnderung informieren" +includePassword = "Neues Passwort in die E-Mail aufnehmen" +forcePasswordChange = "Benutzer zwingen, das Passwort bei der nƤchsten Anmeldung zu Ƥndern" +emailUnavailable = "Die E-Mail-Adresse dieses Benutzers ist keine gültige E-Mail-Adresse. Benachrichtigungen sind deaktiviert." +smtpDisabled = "E-Mail-Benachrichtigungen erfordern, dass SMTP in den Einstellungen aktiviert ist." +notifyOnly = "Es wird eine E-Mail ohne das Passwort gesendet, die den Benutzer darüber informiert, dass ein Admin es geƤndert hat." +submit = "Passwort aktualisieren" +success = "Passwort erfolgreich aktualisiert" +error = "Aktualisieren des Passworts fehlgeschlagen" + [workspace.people.emailInvite] tab = "E-Mail-Einladung" description = "Geben Sie unten E-Mails ein oder fügen Sie sie ein, getrennt durch Kommas. Benutzer erhalten Anmeldedaten per E-Mail." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Mindestens eine E-Mail-Adresse ist erforderlich" submit = "Einladungen senden" success = "Benutzer erfolgreich eingeladen" -partialSuccess = "Einige Einladungen sind fehlgeschlagen" +partialFailure = "Einige Einladungen sind fehlgeschlagen" allFailed = "Benutzer konnten nicht eingeladen werden" error = "Einladungen konnten nicht gesendet werden" @@ -5752,7 +5907,7 @@ label = "Server auswƤhlen" description = "Self-Hosted-Server" [setup.step3] -label = "Login" +label = "Anmeldung" description = "Anmeldedaten eingeben" [setup.mode.saas] @@ -5770,6 +5925,7 @@ subtitle = "Mit Ihrem Stirling-Konto anmelden" [setup.selfhosted] title = "Am Server anmelden" subtitle = "Geben Sie Ihre Server-Anmeldedaten ein" +link = "oder mit einem selbstgehosteten Konto verbinden" [setup.server] title = "Mit Server verbinden" @@ -5788,15 +5944,30 @@ description = "Geben Sie die vollstƤndige URL Ihres selbst gehosteten Stirling emptyUrl = "Bitte eine Server-URL eingeben" unreachable = "Verbindung zum Server konnte nicht hergestellt werden" testFailed = "Verbindungstest fehlgeschlagen" +configFetch = "Serverkonfiguration konnte nicht abgerufen werden. Bitte überprüfen Sie die URL und versuchen Sie es erneut." + +[setup.server.error.securityDisabled] +title = "Anmeldung nicht aktiviert" +body = "Auf diesem Server ist die Anmeldung nicht aktiviert. Um eine Verbindung zu diesem Server herzustellen, müssen Sie die Authentifizierung aktivieren:" +step1 = "Setzen Sie DOCKER_ENABLE_SECURITY=true in Ihrer Umgebung" +step2 = "Oder setzen Sie security.enableLogin=true in der settings.yml" +step3 = "Starten Sie den Server neu" [setup.login] title = "Anmelden" subtitle = "Geben Sie Ihre Anmeldedaten ein, um fortzufahren" connectingTo = "Verbinden mit:" -submit = "Login" +submit = "Anmelden" signInWith = "Anmelden mit" oauthPending = "Browser zur Authentifizierung wird geƶffnet..." orContinueWith = "Oder mit E-Mail fortfahren" +serverRequirement = "Hinweis: Auf dem Server muss die Anmeldung aktiviert sein." +showInstructions = "Wie aktivieren?" +hideInstructions = "Anleitung ausblenden" +instructions = "So aktivieren Sie die Anmeldung auf Ihrem Stirling PDF-Server:" +instructionsEnvVar = "Setzen Sie die Umgebungsvariable:" +instructionsOrYml = "Oder in der settings.yml:" +instructionsRestart = "Starten Sie anschließend Ihren Server neu, damit die Ƅnderungen wirksam werden." [setup.login.username] label = "Benutzername" @@ -5847,12 +6018,13 @@ singleLine = "Einzeilig" [pdfTextEditor.badges] unsaved = "Bearbeitet" modified = "Bearbeitet" -earlyAccess = "Early Access" +earlyAccess = "Früher Zugriff" [pdfTextEditor.actions] reset = "Ƅnderungen zurücksetzen" downloadJson = "JSON herunterladen" generatePdf = "PDF generieren" +saveChanges = "Ƅnderungen speichern" [pdfTextEditor.options.autoScaleText] title = "Text automatisch in Rahmen einpassen" @@ -5890,6 +6062,8 @@ alpha = "Dieser Alpha-Viewer entwickelt sich noch weiter – bestimmte Schriften [pdfTextEditor.empty] title = "Kein Dokument geladen" subtitle = "Laden Sie eine PDF- oder JSON-Datei, um mit der Textbearbeitung zu beginnen." +dropzone = "Ziehen Sie eine PDF- oder JSON-Datei hierher, oder klicken Sie zum Durchsuchen" +dropzoneWithFiles = "WƤhlen Sie eine Datei auf der Registerkarte Dateien aus oder ziehen Sie eine PDF- oder JSON-Datei hierher, oder klicken Sie zum Durchsuchen" [pdfTextEditor.welcomeBanner] title = "Willkommen beim PDF-Texteditor (Early Access)" diff --git a/frontend/public/locales/el-GR/translation.toml b/frontend/public/locales/el-GR/translation.toml index 2656466274..5af71838c6 100644 --- a/frontend/public/locales/el-GR/translation.toml +++ b/frontend/public/locales/el-GR/translation.toml @@ -163,6 +163,11 @@ unfavorite = "Ī‘Ļ†Ī±ĪÆĻĪµĻƒĪ· Ī±Ļ€ĻŒ τα Αγαπημένα" fullscreen = "ĪœĪµĻ„Ī¬Ī²Ī±ĻƒĪ· σε λειτουργία πλήρους ĪæĪøĻŒĪ½Ī·Ļ‚" sidebar = "ĪœĪµĻ„Ī¬Ī²Ī±ĻƒĪ· σε λειτουργία πλευρικής γραμμής" +[backendStartup] +notFoundTitle = "Το backend Γεν βρέθηκε" +retry = "Ī•Ļ€Ī±Ī½Ī¬Ī»Ī·ĻˆĪ·" +unreachable = "Ī— εφαρμογή Γεν μπορεί προς το Ļ€Ī±ĻĻŒĪ½ να ĻƒĻ…Ī½Ī“ĪµĪøĪµĪÆ με το backend. Ελέγξτε την ĪŗĪ±Ļ„Ī¬ĻƒĻ„Ī±ĻƒĪ· του backend και τη ĻƒĻ…Ī½Ī“ĪµĻƒĪ¹Ī¼ĻŒĻ„Ī·Ļ„Ī± Ī“Ī¹ĪŗĻ„ĻĪæĻ…, μετά Ī“ĪæĪŗĪ¹Ī¼Ī¬ĻƒĻ„Īµ ξανά." + [zipWarning] title = "Μεγάλο αρχείο ZIP" message = "Ī‘Ļ…Ļ„ĻŒ το ZIP περιέχει {{count}} αρχεία. ĪĪ± γίνει Ī±Ļ€ĪæĻƒĻ…Ī¼Ļ€ĪÆĪµĻƒĪ· ĪæĻĻ„Ļ‰Ļ‚ Ī® άλλως;" @@ -287,7 +292,7 @@ help = "Βοήθεια Pipeline" scanHelp = "Βοήθεια ĻƒĪ¬ĻĻ‰ĻƒĪ·Ļ‚ φακέλων" deletePrompt = "Ī•ĪÆĻƒĻ„Īµ βέβαιοι ĻŒĻ„Ī¹ θέλετε να Ī“Ī¹Ī±Ī³ĻĪ¬ĻˆĪµĻ„Īµ το pipeline;" tags = "Ī±Ļ…Ļ„ĪæĪ¼Ī±Ļ„ĪæĻ€ĪæĪÆĪ·ĻƒĪ·,ακολουθία,Ļ€ĻĪæĪ³ĻĪ±Ī¼Ī¼Ī±Ļ„Ī¹ĻƒĪ¼Ī­Ī½Īæ,ĪµĻ€ĪµĪ¾ĪµĻĪ³Ī±ĻƒĪÆĪ±-παρτίΓας" -title = "Pipeline" +title = "Δοή" [pipelineOptions] header = "Ī”Ī¹Ī±Ī¼ĻŒĻĻ†Ļ‰ĻƒĪ· Pipeline" @@ -296,7 +301,7 @@ saveSettings = "Ī‘Ļ€ĪæĪøĪ®ĪŗĪµĻ…ĻƒĪ· ĻĻ…ĪøĪ¼ĪÆĻƒĪµĻ‰Ī½ λειτουργίας" pipelineNamePrompt = "Ī•Ī¹ĻƒĪ¬Ī³ĪµĻ„Īµ όνομα pipeline ĪµĪ“ĻŽ" selectOperation = "Επιλογή λειτουργίας" addOperationButton = "Προσθήκη λειτουργίας" -pipelineHeader = "Pipeline:" +pipelineHeader = "Δοή:" saveButton = "Ī›Ī®ĻˆĪ·" validateButton = "Ī•Ļ€Ī¹ĪŗĻĻĻ‰ĻƒĪ·" @@ -347,7 +352,7 @@ teams = "ĪŸĪ¼Ī¬Ī“ĪµĻ‚" title = "Ī”Ī¹Ī±Ī¼ĻŒĻĻ†Ļ‰ĻƒĪ·" systemSettings = "Ī”Ļ…ĪøĪ¼ĪÆĻƒĪµĪ¹Ļ‚ ĻƒĻ…ĻƒĻ„Ī®Ī¼Ī±Ļ„ĪæĻ‚" features = "Ī”Ļ…Ī½Ī±Ļ„ĻŒĻ„Ī·Ļ„ĪµĻ‚" -endpoints = "Endpoints" +endpoints = "Σημεία Ļ„ĪµĻĪ¼Ī±Ļ„Ī¹ĻƒĪ¼ĪæĻ" database = "Ī’Ī¬ĻƒĪ· ΓεΓομένων" advanced = "Προχωρημένα" @@ -369,7 +374,7 @@ privacy = "Ī‘Ļ€ĻŒĻĻĪ·Ļ„Īæ" [settings.developer] title = "Ī ĻĪæĪ³ĻĪ±Ī¼Ī¼Ī±Ļ„Ī¹ĻƒĻ„Ī®Ļ‚" -apiKeys = "API Keys" +apiKeys = "ΚλειΓιά API" [settings.tooltips] enableLoginFirst = "Ī•Ī½ĪµĻĪ³ĪæĻ€ĪæĪ¹Ī®ĻƒĻ„Īµ Ļ€ĻĻŽĻ„Ī± τη λειτουργία ĻƒĻĪ½Ī“ĪµĻƒĪ·Ļ‚" @@ -383,7 +388,7 @@ logout = "Ī‘Ļ€ĪæĻƒĻĪ½Ī“ĪµĻƒĪ·" [settings.connection.mode] saas = "Stirling Cloud" -selfhosted = "Self-Hosted" +selfhosted = "Αυτο-Ļ†Ī¹Ī»ĪæĪ¾ĪµĪ½ĪæĻĪ¼ĪµĪ½Īæ" [settings.general] title = "Γενικά" @@ -912,6 +917,9 @@ desc = "Ī”Ī·Ī¼Ī¹ĪæĻ…ĻĪ³Ī®ĻƒĻ„Īµ ροές Ļ€ĪæĪ»Ī»ĻŽĪ½ βημάτων ĻƒĻ…Ī½Ī“Ī­ desc = "Ī•Ļ€Ī¹ĪŗĪ¬Ī»Ļ…ĻˆĪ· PDF πάνω σε άλλο PDF" title = "Ī•Ļ€Ī¹ĪŗĪ¬Ī»Ļ…ĻˆĪ· PDF" +[home.pdfTextEditor] +title = "Ī•Ļ€ĪµĪ¾ĪµĻĪ³Ī±ĻƒĻ„Ī®Ļ‚ κειμένου PDF" +desc = "Ī•Ļ€ĪµĪ¾ĪµĻĪ³Ī±ĻƒĻ„ĪµĪÆĻ„Īµ υπάρχον κείμενο και ĪµĪ¹ĪŗĻŒĪ½ĪµĻ‚ μέσα σε αρχεία PDF" [home.addText] tags = "κείμενο,ĻƒĻ‡ĪæĪ»Ī¹Ī±ĻƒĪ¼ĻŒĻ‚,ετικέτα" @@ -1173,7 +1181,7 @@ selectFilesPlaceholder = "Επιλέξτε αρχεία ĻƒĻ„Ī·Ī½ ĪŗĻĻĪ¹Ī± πρ settings = "Ī”Ļ…ĪøĪ¼ĪÆĻƒĪµĪ¹Ļ‚" conversionCompleted = "Ī— μετατροπή ĪæĪ»ĪæĪŗĪ»Ī·ĻĻŽĪøĪ·ĪŗĪµ" results = "Ī‘Ļ€ĪæĻ„ĪµĪ»Ī­ĻƒĪ¼Ī±Ļ„Ī±" -defaultFilename = "converted_file" +defaultFilename = "μετατραπμένο_αρχείο" conversionResults = "Ī‘Ļ€ĪæĻ„ĪµĪ»Ī­ĻƒĪ¼Ī±Ļ„Ī± μετατροπής" convertFrom = "ĪœĪµĻ„Ī±Ļ„ĻĪæĻ€Ī® Ī±Ļ€ĻŒ" convertTo = "ĪœĪµĻ„Ī±Ļ„ĻĪæĻ€Ī® σε" @@ -1360,7 +1368,7 @@ title = "Προσθήκη υΓατογραφήματος" desc = "Ī ĻĪæĻƒĪøĪ­ĻƒĻ„Īµ υΓατογραφήματα κειμένου Ī® ĪµĪ¹ĪŗĻŒĪ½Ī±Ļ‚ σε αρχεία PDF" completed = "Το υΓατογράφημα Ļ€ĻĪæĻƒĻ„Ī­ĪøĪ·ĪŗĪµ" submit = "Προσθήκη υΓατογραφήματος" -filenamePrefix = "watermarked" +filenamePrefix = "υΓατογραφημένο" [watermark.error] failed = "Ī Ī±ĻĪæĻ…ĻƒĪ¹Ī¬ĻƒĻ„Ī·ĪŗĪµ ĻƒĻ†Ī¬Ī»Ī¼Ī± κατά την Ļ€ĻĪæĻƒĪøĪ®ĪŗĪ· υΓατογραφήματος ĻƒĻ„Īæ PDF." @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Ī£Ļ‡ĪµĪ“Ī¹Ī±ĻƒĪ¼Ī­Ī½Ī· υπογραφή" defaultImageLabel = "Ī‘Ī½ĪµĪ²Ī±ĻƒĪ¼Ī­Ī½Ī· υπογραφή" defaultTextLabel = "Πληκτρολογημένη υπογραφή" saveButton = "Ī‘Ļ€ĪæĪøĪ®ĪŗĪµĻ…ĻƒĪ· υπογραφής" +savePersonal = "Ī‘Ļ€ĪæĪøĪ®ĪŗĪµĻ…ĻƒĪ· ως Ī ĻĪæĻƒĻ‰Ļ€Ī¹ĪŗĪ®" +saveShared = "Ī‘Ļ€ĪæĪøĪ®ĪŗĪµĻ…ĻƒĪ· ως ĪšĪæĪ¹Ī½ĻŒĻ‡ĻĪ·ĻƒĻ„Ī·" saveUnavailable = "Ī”Ī·Ī¼Ī¹ĪæĻ…ĻĪ³Ī®ĻƒĻ„Īµ Ļ€ĻĻŽĻ„Ī± μια υπογραφή για να την Ī±Ļ€ĪæĪøĪ·ĪŗĪµĻĻƒĪµĻ„Īµ." noChanges = "Ī— Ļ„ĻĪ­Ļ‡ĪæĻ…ĻƒĪ± υπογραφή είναι ήΓη αποθηκευμένη." +tempStorageTitle = "Ī ĻĪæĻƒĻ‰ĻĪ¹Ī½Ī® Ī±Ļ€ĪæĪøĪ®ĪŗĪµĻ…ĻƒĪ· ĻƒĻ„ĪæĪ½ περιηγητή" +tempStorageDescription = "Οι υπογραφές Ī±Ļ€ĪæĪøĪ·ĪŗĪµĻĪæĪ½Ļ„Ī±Ī¹ μόνο ĻƒĻ„ĪæĪ½ περιηγητή ĻƒĪ±Ļ‚. Θα Ļ‡Ī±ĪøĪæĻĪ½ αν ĪŗĪ±ĪøĪ±ĻĪÆĻƒĪµĻ„Īµ τα ΓεΓομένα του περιηγητή Ī® αλλάξετε περιηγητή." +personalHeading = "Ī ĻĪæĻƒĻ‰Ļ€Ī¹ĪŗĪ­Ļ‚ υπογραφές" +sharedHeading = "ĪšĪæĪ¹Ī½ĻŒĻ‡ĻĪ·ĻƒĻ„ĪµĻ‚ υπογραφές" +personalDescription = "Μόνο ĪµĻƒĪµĪÆĻ‚ μπορείτε να Γείτε αυτές τις υπογραφές." +sharedDescription = "Όλοι οι Ļ‡ĻĪ®ĻƒĻ„ĪµĻ‚ Ī¼Ļ€ĪæĻĪæĻĪ½ να βλέπουν και να Ļ‡ĻĪ·ĻƒĪ¹Ī¼ĪæĻ€ĪæĪ¹ĪæĻĪ½ αυτές τις υπογραφές." [sign.saved.type] canvas = "ΣχέΓιο" @@ -2701,7 +2717,7 @@ header = "Ī‘Ļ†Ī±ĪÆĻĪµĻƒĪ· της ĻˆĪ·Ļ†Ī¹Ī±ĪŗĪ®Ļ‚ υπογραφής Ī±Ļ€ĻŒ Ļ„ selectPDF = "Επιλέξτε ένα αρχείο PDF:" submit = "Ī‘Ļ†Ī±ĪÆĻĪµĻƒĪ· υπογραφής" description = "Ī‘Ļ…Ļ„ĻŒ το εργαλείο θα Ī±Ļ†Ī±Ī¹ĻĪ­ĻƒĪµĪ¹ τις υπογραφές ĻˆĪ·Ļ†Ī¹Ī±ĪŗĪæĻ Ļ€Ī¹ĻƒĻ„ĪæĻ€ĪæĪ¹Ī·Ļ„Ī¹ĪŗĪæĻ Ī±Ļ€ĻŒ το PDF ĻƒĪ±Ļ‚." -filenamePrefix = "unsigned" +filenamePrefix = "Ī±Ī½Ļ…Ļ€ĻŒĪ³ĻĪ±Ļ†Īæ" [removeCertSign.files] placeholder = "Επιλέξτε ένα αρχείο PDF ĻƒĻ„Ī·Ī½ ĪŗĻĻĪ¹Ī± προβολή για να Ī¾ĪµĪŗĪ¹Ī½Ī®ĻƒĪµĻ„Īµ" @@ -3020,6 +3036,91 @@ title = "Ī›Ī®ĻˆĪ· Ļ€Ī»Ī·ĻĪæĻ†ĪæĻĪ¹ĻŽĪ½ PDF" header = "Ī›Ī®ĻˆĪ· Ļ€Ī»Ī·ĻĪæĻ†ĪæĻĪ¹ĻŽĪ½ PDF" submit = "Ī›Ī®ĻˆĪ· Ļ€Ī»Ī·ĻĪæĻ†ĪæĻĪ¹ĻŽĪ½" downloadJson = "Ī›Ī®ĻˆĪ· JSON" +processing = "Εξαγωγή Ļ€Ī»Ī·ĻĪæĻ†ĪæĻĪ¹ĻŽĪ½..." +results = "Ī‘Ļ€ĪæĻ„ĪµĪ»Ī­ĻƒĪ¼Ī±Ļ„Ī±" +noResults = "Ī•ĪŗĻ„ĪµĪ»Ī­ĻƒĻ„Īµ το εργαλείο για να Ī“Ī·Ī¼Ī¹ĪæĻ…ĻĪ³Ī®ĻƒĪµĻ„Īµ αναφορά." +downloads = "Ī›Ī®ĻˆĪµĪ¹Ļ‚" +noneDetected = "Δεν ĪµĪ½Ļ„ĪæĻ€ĪÆĻƒĻ„Ī·ĪŗĪµ κανένα" +indexTitle = "Ευρετήριο" + +[getPdfInfo.report] +entryLabel = "Πλήρης ĻƒĻĪ½ĪæĻˆĪ· Ļ€Ī»Ī·ĻĪæĻ†ĪæĻĪ¹ĻŽĪ½" +shortTitle = "Πληροφορίες PDF" + +[getPdfInfo.sections] +metadata = "ĪœĪµĻ„Ī±Ī“ĪµĪ“ĪæĪ¼Ī­Ī½Ī±" +formFields = "ΠεΓία Ļ†ĻŒĻĪ¼Ī±Ļ‚" +basicInfo = "Ī’Ī±ĻƒĪ¹ĪŗĪ­Ļ‚ πληροφορίες" +documentInfo = "Πληροφορίες εγγράφου" +compliance = "Ī£Ļ…Ī¼Ī¼ĻŒĻĻ†Ļ‰ĻƒĪ·" +encryption = "ĪšĻĻ…Ļ€Ļ„ĪæĪ³ĻĪ¬Ļ†Ī·ĻƒĪ·" +permissions = "Ī”Ī¹ĪŗĪ±Ī¹ĻŽĪ¼Ī±Ļ„Ī±" +other = "Άλλα" +perPageInfo = "Πληροφορίες ανά σελίΓα" +tableOfContents = "Πίνακας περιεχομένων" + +[getPdfInfo.other] +attachments = "Συνημμένα" +embeddedFiles = "Ī•Ī½ĻƒĻ‰Ī¼Ī±Ļ„Ļ‰Ī¼Ī­Ī½Ī± αρχεία" +javaScript = "JavaScript" +layers = "ΕπίπεΓα" +structureTree = "Δέντρο Γομής" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "ĪœĪ­Ī³ĪµĪøĪæĻ‚" +annotations = "Ī•Ļ€Ī¹ĻƒĪ·Ī¼ĪµĪ¹ĻŽĻƒĪµĪ¹Ļ‚" +images = "Ī•Ī¹ĪŗĻŒĪ½ĪµĻ‚" +links = "Ī£ĻĪ½Ī“ĪµĻƒĪ¼ĪæĪ¹" +fonts = "Ī“ĻĪ±Ī¼Ī¼Ī±Ļ„ĪæĻƒĪµĪ¹ĻĪ­Ļ‚" +xobjects = "Πλήθος XObject" +multimedia = "Ī ĪæĪ»Ļ…Ī¼Ī­ĻƒĪ±" + +[getPdfInfo.summary] +pages = "ΣελίΓες" +fileSize = "ĪœĪ­Ī³ĪµĪøĪæĻ‚ αρχείου" +pdfVersion = "ΈκΓοση PDF" +language = "Ī“Ī»ĻŽĻƒĻƒĪ±" +title = "Ī£ĻĪ½ĪæĻˆĪ· PDF" +author = "Συγγραφέας" +created = "Δημιουργήθηκε" +modified = "Τροποποιήθηκε" +permsAll = "Όλα τα Ī“Ī¹ĪŗĪ±Ī¹ĻŽĪ¼Ī±Ļ„Ī± επιτρέπονται" +permsRestricted = "{{count}} Ļ€ĪµĻĪ¹ĪæĻĪ¹ĻƒĪ¼ĪæĪÆ" +permsMixed = "Ορισμένα Ī“Ī¹ĪŗĪ±Ī¹ĻŽĪ¼Ī±Ļ„Ī± είναι Ļ€ĪµĻĪ¹ĪæĻĪ¹ĻƒĪ¼Ī­Ī½Ī±" +hasCompliance = "Διαθέτει Ļ€ĻĻŒĻ„Ļ…Ļ€Ī± ĻƒĻ…Ī¼Ī¼ĻŒĻĻ†Ļ‰ĻƒĪ·Ļ‚" +noCompliance = "Χωρίς Ļ€ĻĻŒĻ„Ļ…Ļ€Ī± ĻƒĻ…Ī¼Ī¼ĻŒĻĻ†Ļ‰ĻƒĪ·Ļ‚" +basic = "Ī’Ī±ĻƒĪ¹ĪŗĪ­Ļ‚ πληροφορίες" +documentInfo = "Πληροφορίες εγγράφου" +securityTitle = "ĪšĪ±Ļ„Ī¬ĻƒĻ„Ī±ĻƒĪ· Ī±ĻƒĻ†Ī¬Ī»ĪµĪ¹Ī±Ļ‚" +technical = "Τεχνικά" +overviewTitle = "Ī•Ļ€Ī¹ĻƒĪŗĻŒĻ€Ī·ĻƒĪ· PDF" + +[getPdfInfo.summary.security] +encrypted = "ĪšĻĻ…Ļ€Ļ„ĪæĪ³ĻĪ±Ļ†Ī·Ī¼Ī­Ī½Īæ PDF - ΄πάρχει Ļ€ĻĪæĻƒĻ„Ī±ĻƒĪÆĪ± με ĪŗĻ‰Ī“Ī¹ĪŗĻŒ Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚" +unencrypted = "Μη κρυπτογραφημένο PDF - Χωρίς Ļ€ĻĪæĻƒĻ„Ī±ĻƒĪÆĪ± με ĪŗĻ‰Ī“Ī¹ĪŗĻŒ Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚" + +[getPdfInfo.summary.tech] +images = "Ī•Ī¹ĪŗĻŒĪ½ĪµĻ‚" +fonts = "Ī“ĻĪ±Ī¼Ī¼Ī±Ļ„ĪæĻƒĪµĪ¹ĻĪ­Ļ‚" +formFields = "ΠεΓία Ļ†ĻŒĻĪ¼Ī±Ļ‚" +embeddedFiles = "Ī•Ī½ĻƒĻ‰Ī¼Ī±Ļ„Ļ‰Ī¼Ī­Ī½Ī± αρχεία" +javaScript = "JavaScript" +layers = "ΕπίπεΓα" +bookmarks = "ΣελιΓοΓείκτες" +multimedia = "Ī ĪæĪ»Ļ…Ī¼Ī­ĻƒĪ±" + +[getPdfInfo.summary.overview] +untitled = "ένα έγγραφο χωρίς τίτλο" +unknown = "Ī†Ī³Ī½Ļ‰ĻƒĻ„ĪæĻ‚ ĻƒĻ…Ī³Ī³ĻĪ±Ļ†Ī­Ī±Ļ‚" +text = "Ī ĻĻŒĪŗĪµĪ¹Ļ„Ī±Ī¹ για ένα PDF {{pages}} ĻƒĪµĪ»ĪÆĪ“Ļ‰Ī½ με τίτλο {{title}} που Γημιουργήθηκε Ī±Ļ€ĻŒ τον/την {{author}} (έκΓοση PDF {{version}})." + +[getPdfInfo.error] +partial = "Δεν ήταν Γυνατή Ī· ĪµĻ€ĪµĪ¾ĪµĻĪ³Ī±ĻƒĪÆĪ± ĪæĻĪ¹ĻƒĪ¼Ī­Ī½Ļ‰Ī½ αρχείων." +unexpected = "Μη αναμενόμενο ĻƒĻ†Ī¬Ī»Ī¼Ī± κατά την εξαγωγή." + +[getPdfInfo.status] +complete = "Ī— εξαγωγή ĪæĪ»ĪæĪŗĪ»Ī·ĻĻŽĪøĪ·ĪŗĪµ" [extractPage] tags = "εξαγωγή" @@ -3438,6 +3539,9 @@ signinTitle = "Ī Ī±ĻĪ±ĪŗĪ±Ī»ĻŽ ĻƒĻ…Ī½Ī“ĪµĪøĪµĪÆĻ„Īµ" ssoSignIn = "Ī£ĻĪ½Ī“ĪµĻƒĪ· Ī¼Ī­ĻƒĻ‰ Single Sign-on" oAuth2AutoCreateDisabled = "Ī— Ī±Ļ…Ļ„ĻŒĪ¼Ī±Ļ„Ī· Γημιουργία Ļ‡ĻĪ®ĻƒĻ„Ī· OAUTH2 είναι απενεργοποιημένη" oAuth2AdminBlockedUser = "Ī— εγγραφή Ī® ĻƒĻĪ½Ī“ĪµĻƒĪ· μη εγγεγραμμένων Ļ‡ĻĪ·ĻƒĻ„ĻŽĪ½ είναι προς το Ļ€Ī±ĻĻŒĪ½ Ī±Ļ€ĪæĪŗĪ»ĪµĪ¹ĻƒĪ¼Ī­Ī½Ī·. Ī Ī±ĻĪ±ĪŗĪ±Ī»ĻŽ ĪµĻ€Ī¹ĪŗĪæĪ¹Ī½Ļ‰Ī½Ī®ĻƒĻ„Īµ με τον Ī“Ī¹Ī±Ļ‡ĪµĪ¹ĻĪ¹ĻƒĻ„Ī®." +oAuth2RequiresLicense = "Ī— ĻƒĻĪ½Ī“ĪµĻƒĪ· Ī¼Ī­ĻƒĻ‰ OAuth/SSO απαιτεί επί πληρωμή άΓεια (Server Ī® Enterprise). Ī Ī±ĻĪ±ĪŗĪ±Ī»ĪæĻĪ¼Īµ ĪµĻ€Ī¹ĪŗĪæĪ¹Ī½Ļ‰Ī½Ī®ĻƒĻ„Īµ με τον Ī“Ī¹Ī±Ļ‡ĪµĪ¹ĻĪ¹ĻƒĻ„Ī® για να Ī±Ī½Ī±Ī²Ī±ĪøĪ¼ĪÆĻƒĪµĻ„Īµ το πλάνο ĻƒĪ±Ļ‚." +saml2RequiresLicense = "Ī— ĻƒĻĪ½Ī“ĪµĻƒĪ· Ī¼Ī­ĻƒĻ‰ SAML απαιτεί επί πληρωμή άΓεια (Server Ī® Enterprise). Ī Ī±ĻĪ±ĪŗĪ±Ī»ĪæĻĪ¼Īµ ĪµĻ€Ī¹ĪŗĪæĪ¹Ī½Ļ‰Ī½Ī®ĻƒĻ„Īµ με τον Ī“Ī¹Ī±Ļ‡ĪµĪ¹ĻĪ¹ĻƒĻ„Ī® για να Ī±Ī½Ī±Ī²Ī±ĪøĪ¼ĪÆĻƒĪµĻ„Īµ το πλάνο ĻƒĪ±Ļ‚." +maxUsersReached = "ĪˆĻ‡ĪµĪ¹ επιτευχθεί Īæ Ī¼Ī­Ī³Ī¹ĻƒĻ„ĪæĻ‚ Ī±ĻĪ¹ĪøĪ¼ĻŒĻ‚ Ļ‡ĻĪ·ĻƒĻ„ĻŽĪ½ για την Ļ„ĻĪ­Ļ‡ĪæĻ…ĻƒĪ± άΓειά ĻƒĪ±Ļ‚. Ī Ī±ĻĪ±ĪŗĪ±Ī»ĪæĻĪ¼Īµ ĪµĻ€Ī¹ĪŗĪæĪ¹Ī½Ļ‰Ī½Ī®ĻƒĻ„Īµ με τον Ī“Ī¹Ī±Ļ‡ĪµĪ¹ĻĪ¹ĻƒĻ„Ī® για να Ī±Ī½Ī±Ī²Ī±ĪøĪ¼ĪÆĻƒĪµĻ„Īµ το πλάνο ĻƒĪ±Ļ‚ Ī® να Ļ€ĻĪæĻƒĪøĪ­ĻƒĪµĻ„Īµ Ļ€ĪµĻĪ¹ĻƒĻƒĻŒĻ„ĪµĻĪµĻ‚ ĪøĪ­ĻƒĪµĪ¹Ļ‚." oauth2RequestNotFound = "Το αίτημα ĪµĪ¾ĪæĻ…ĻƒĪ¹ĪæĪ“ĻŒĻ„Ī·ĻƒĪ·Ļ‚ Γεν βρέθηκε" oauth2InvalidUserInfoResponse = "Μη έγκυρη Ī±Ļ€ĻŒĪŗĻĪ¹ĻƒĪ· Ļ€Ī»Ī·ĻĪæĻ†ĪæĻĪ¹ĻŽĪ½ Ļ‡ĻĪ®ĻƒĻ„Ī·" oauth2invalidRequest = "Μη έγκυρο αίτημα" @@ -3533,7 +3637,7 @@ title = "PDF σε μία σελίΓα" header = "PDF σε μία σελίΓα" submit = "ĪœĪµĻ„Ī±Ļ„ĻĪæĻ€Ī® σε μία σελίΓα" description = "Ī‘Ļ…Ļ„ĻŒ το εργαλείο θα ĻƒĻ…Ī³Ļ‡Ļ‰Ī½ĪµĻĻƒĪµĪ¹ ĻŒĪ»ĪµĻ‚ τις ĻƒĪµĪ»ĪÆĪ“ĪµĻ‚ του PDF ĻƒĪ±Ļ‚ σε μία μεγάλη ενιαία σελίΓα. Το πλάτος θα παραμείνει ίΓιο με των Ī±ĻĻ‡Ī¹ĪŗĻŽĪ½ ĻƒĪµĪ»ĪÆĪ“Ļ‰Ī½, αλλά το ĻĻˆĪæĻ‚ θα είναι το άθροισμα ĻŒĪ»Ļ‰Ī½ των Ļ…ĻˆĻŽĪ½." -filenamePrefix = "single_page" +filenamePrefix = "μονοσέλιΓο" [pdfToSinglePage.files] placeholder = "Επιλέξτε ένα αρχείο PDF ĻƒĻ„Ī·Ī½ ĪŗĻĻĪ¹Ī± προβολή για να Ī¾ĪµĪŗĪ¹Ī½Ī®ĻƒĪµĻ„Īµ" @@ -3771,7 +3875,7 @@ version = "Ī¤ĻĪ­Ļ‡ĪæĻ…ĻƒĪ± έκΓοση" title = "Ī¤ĪµĪŗĪ¼Ī·ĻĪÆĻ‰ĻƒĪ· API" header = "Ī¤ĪµĪŗĪ¼Ī·ĻĪÆĻ‰ĻƒĪ· API" desc = "Προβάλετε και Ī“ĪæĪŗĪ¹Ī¼Ī¬ĻƒĻ„Īµ τα endpoints του Stirling PDF API" -tags = "api,documentation,swagger,endpoints,development" +tags = "api,Ļ„ĪµĪŗĪ¼Ī·ĻĪÆĻ‰ĻƒĪ·,swagger,τελικά σημεία,ανάπτυξη" [cookieBanner.popUp] title = "Ī ĻŽĻ‚ Ļ‡ĻĪ·ĻƒĪ¹Ī¼ĪæĻ€ĪæĪ¹ĪæĻĪ¼Īµ τα cookies" @@ -3846,14 +3950,17 @@ fitToWidth = "Προσαρμογή ĻƒĻ„Īæ πλάτος" actualSize = "Ī ĻĪ±Ī³Ī¼Ī±Ļ„Ī¹ĪŗĻŒ μέγεθος" [viewer] +cannotPreviewFile = "Δεν είναι Γυνατή Ī· Ļ€ĻĪæĪµĻ€Ī¹ĻƒĪŗĻŒĻ€Ī·ĻƒĪ· του αρχείου" +dualPageView = "Προβολή Γιπλής ĻƒĪµĪ»ĪÆĪ“Ī±Ļ‚" firstPage = "Ī ĻĻŽĻ„Ī· σελίΓα" lastPage = "Τελευταία σελίΓα" -previousPage = "Ī ĻĪæĪ·Ī³ĪæĻĪ¼ĪµĪ½Ī· σελίΓα" nextPage = "Ī•Ļ€ĻŒĪ¼ĪµĪ½Ī· σελίΓα" +onlyPdfSupported = "Ο προβολέας Ļ…Ļ€ĪæĻƒĻ„Ī·ĻĪÆĪ¶ĪµĪ¹ μόνο αρχεία PDF. Ī‘Ļ…Ļ„ĻŒ το αρχείο φαίνεται να είναι Γιαφορετικής μορφής." +previousPage = "Ī ĻĪæĪ·Ī³ĪæĻĪ¼ĪµĪ½Ī· σελίΓα" +singlePageView = "Προβολή μίας ĻƒĪµĪ»ĪÆĪ“Ī±Ļ‚" +unknownFile = "Ī†Ī³Ī½Ļ‰ĻƒĻ„Īæ αρχείο" zoomIn = "ĪœĪµĪ³Ī­ĪøĻ…Ī½ĻƒĪ·" zoomOut = "Ī£Ī¼ĪÆĪŗĻĻ…Ī½ĻƒĪ·" -singlePageView = "Προβολή μίας ĻƒĪµĪ»ĪÆĪ“Ī±Ļ‚" -dualPageView = "Προβολή Γιπλής ĻƒĪµĪ»ĪÆĪ“Ī±Ļ‚" [rightRail] closeSelected = "Κλείσιμο επιλεγμένων αρχείων" @@ -3877,6 +3984,7 @@ toggleSidebar = "Εναλλαγή πλευρικής γραμμής" exportSelected = "Εξαγωγή επιλεγμένων ĻƒĪµĪ»ĪÆĪ“Ļ‰Ī½" toggleAnnotations = "Εναλλαγή ĪæĻĪ±Ļ„ĻŒĻ„Ī·Ļ„Ī±Ļ‚ ĻƒĻ‡ĪæĪ»Ī¹Ī±ĻƒĪ¼ĻŽĪ½" annotationMode = "Εναλλαγή λειτουργίας ĻƒĻ‡ĪæĪ»Ī¹Ī±ĻƒĪ¼ĪæĻ" +print = "Ī•ĪŗĻ„ĻĻ€Ļ‰ĻƒĪ· PDF" draw = "Ī£Ļ‡ĪµĪ“ĪÆĪ±ĻƒĪ·" save = "Ī‘Ļ€ĪæĪøĪ®ĪŗĪµĻ…ĻƒĪ·" saveChanges = "Ī‘Ļ€ĪæĪøĪ®ĪŗĪµĻ…ĻƒĪ· Ī±Ī»Ī»Ī±Ī³ĻŽĪ½" @@ -4235,11 +4343,11 @@ label = "URL ĪµĪŗĪ“ĻŒĻ„Ī·" description = "Το URL ĪµĪŗĪ“ĻŒĻ„Ī· του Ļ€Ī±ĻĻŒĻ‡ĪæĻ… OAuth2" [admin.settings.connections.oauth2.clientId] -label = "Client ID" +label = "Ī‘Ī½Ī±Ī³Ī½Ļ‰ĻĪ¹ĻƒĻ„Ī¹ĪŗĻŒ πελάτη (Client ID)" description = "Το Client ID OAuth2 Ī±Ļ€ĻŒ τον Ļ€Ī¬ĻĪæĻ‡ĻŒ ĻƒĪ±Ļ‚" [admin.settings.connections.oauth2.clientSecret] -label = "Client Secret" +label = "ĪœĻ…ĻƒĻ„Ī¹ĪŗĻŒ πελάτη (Client Secret)" description = "Το Client Secret OAuth2 Ī±Ļ€ĻŒ τον Ļ€Ī¬ĻĪæĻ‡ĻŒ ĻƒĪ±Ļ‚" [admin.settings.connections.oauth2.useAsUsername] @@ -4459,7 +4567,7 @@ label = "Ī•Ī½ĪµĻĪ³ĪæĻ€ĪæĪÆĪ·ĻƒĪ· Ļ€ĻĪæĻƒĪŗĪ»Ī®ĻƒĪµĻ‰Ī½ Ī¼Ī­ĻƒĻ‰ email" description = "ĪĪ± επιτρέπεται ĻƒĻ„ĪæĻ…Ļ‚ Ī“Ī¹Ī±Ļ‡ĪµĪ¹ĻĪ¹ĻƒĻ„Ī­Ļ‚ να Ļ€ĻĪæĻƒĪŗĪ±Ī»ĪæĻĪ½ Ļ‡ĻĪ®ĻƒĻ„ĪµĻ‚ Ī¼Ī­ĻƒĻ‰ email με Ī±Ļ…Ļ„ĻŒĪ¼Ī±Ļ„Ī± Ļ€Ī±ĻĪ±Ī³ĻŒĪ¼ĪµĪ½ĪæĻ…Ļ‚ ĪŗĻ‰Ī“Ī¹ĪŗĪæĻĻ‚" [admin.settings.mail.frontendUrl] -label = "Frontend URL" +label = "URL front-end" description = "Ī’Ī±ĻƒĪ¹ĪŗĻŒ URL για το frontend (Ļ€.χ. https://pdf.example.com). Ī§ĻĪ·ĻƒĪ¹Ī¼ĪæĻ€ĪæĪ¹ĪµĪÆĻ„Ī±Ī¹ για τη Γημιουργία ĻƒĻ…Ī½Ī“Ī­ĻƒĪ¼Ļ‰Ī½ Ļ€ĻĻŒĻƒĪŗĪ»Ī·ĻƒĪ·Ļ‚ ĻƒĻ„Ī± email. Ī‘Ļ†Ī®ĻƒĻ„Īµ κενό για Ļ‡ĻĪ®ĻƒĪ· του backend URL." [admin.settings.legal] @@ -4494,6 +4602,7 @@ description = "URL Ī® όνομα αρχείου για το impressum (απαι title = "Premium & Enterprise" description = "Ī”Ļ…ĪøĪ¼ĪÆĻƒĻ„Īµ το κλειΓί άΓειας premium Ī® enterprise." license = "Ī”Ī¹Ī±Ī¼ĻŒĻĻ†Ļ‰ĻƒĪ· άΓειας" +noInput = "Ī Ī±ĻĪ±ĪŗĪ±Ī»ĻŽ Ī“ĻŽĻƒĻ„Īµ ένα κλειΓί άΓειας Ī® αρχείο" [admin.settings.premium.licenseKey] toggle = "ĪˆĻ‡ĪµĻ„Īµ κλειΓί άΓειας Ī® αρχείο Ļ€Ī¹ĻƒĻ„ĪæĻ€ĪæĪ¹Ī·Ļ„Ī¹ĪŗĪæĻ;" @@ -4511,6 +4620,25 @@ line1 = "Ī— Ī±Ī½Ļ„Ī¹ĪŗĪ±Ļ„Ī¬ĻƒĻ„Ī±ĻƒĪ· του τρέχοντος κλειΓιο line2 = "Ī— Ļ€ĻĪæĪ·Ī³ĪæĻĪ¼ĪµĪ½Ī· άΓεια θα χαθεί ĪæĻĪ¹ĻƒĻ„Ī¹ĪŗĪ¬ ĪµĪŗĻ„ĻŒĻ‚ αν την έχετε Ī±Ļ€ĪæĪøĪ·ĪŗĪµĻĻƒĪµĪ¹ Ī±Ī»Ī»ĪæĻ." line3 = "Ī£Ī·Ī¼Ī±Ī½Ļ„Ī¹ĪŗĻŒ: ĪšĻĪ±Ļ„Ī®ĻƒĻ„Īµ τα κλειΓιά άΓειας ιΓιωτικά και Ī±ĻƒĻ†Ī±Ī»Ī®. Μην τα κοινοποιείτε Γημόσια." +[admin.settings.premium.inputMethod] +text = "ΚλειΓί άΓειας" +file = "Αρχείο Ļ€Ī¹ĻƒĻ„ĪæĻ€ĪæĪ¹Ī·Ļ„Ī¹ĪŗĪæĻ" + +[admin.settings.premium.file] +label = "Αρχείο Ļ€Ī¹ĻƒĻ„ĪæĻ€ĪæĪ¹Ī·Ļ„Ī¹ĪŗĪæĻ άΓειας" +description = "ĪœĪµĻ„Ī±Ļ†ĪæĻĻ„ĻŽĻƒĻ„Īµ το αρχείο άΓειας .lic Ī® .cert Ī±Ļ€ĻŒ αγορές ĪµĪŗĻ„ĻŒĻ‚ ĻƒĻĪ½Ī“ĪµĻƒĪ·Ļ‚" +choose = "Επιλέξτε αρχείο άΓειας" +selected = "Επιλεγμένο: {{filename}} ({{size}})" +successMessage = "Το αρχείο άΓειας Ī¼ĪµĻ„Ī±Ļ†ĪæĻĻ„ĻŽĪøĪ·ĪŗĪµ και ενεργοποιήθηκε με επιτυχία. Δεν απαιτείται ĪµĻ€Ī±Ī½ĪµĪŗĪŗĪÆĪ½Ī·ĻƒĪ·." + +[admin.settings.premium.currentLicense] +title = "Ενεργή άΓεια" +file = "Πηγή: Αρχείο άΓειας ({{path}})" +key = "Πηγή: ΚλειΓί άΓειας" +type = "Ī¤ĻĻ€ĪæĻ‚: {{type}}" +noInput = "Ī Ī±ĻĪ±ĪŗĪ±Ī»ĻŽ Ī“ĻŽĻƒĻ„Īµ ένα κλειΓί άΓειας Ī® Ī¼ĪµĻ„Ī±Ļ†ĪæĻĻ„ĻŽĻƒĻ„Īµ ένα αρχείο Ļ€Ī¹ĻƒĻ„ĪæĻ€ĪæĪ¹Ī·Ļ„Ī¹ĪŗĪæĻ" +success = "Επιτυχία" + [admin.settings.premium.enabled] label = "Ī•Ī½ĪµĻĪ³ĪæĻ€ĪæĪÆĪ·ĻƒĪ· Ī»ĪµĪ¹Ļ„ĪæĻ…ĻĪ³Ī¹ĻŽĪ½ premium" description = "Ī•Ī½ĪµĻĪ³ĪæĻ€ĪæĪÆĪ·ĻƒĪ· ελέγχων ĪŗĪ»ĪµĪ¹Ī“Ī¹ĪæĻ άΓειας για λειτουργίες pro/enterprise" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} επιλεγμένα" download = "Ī›Ī®ĻˆĪ·" delete = "Διαγραφή" unsupported = "Μη Ļ…Ļ€ĪæĻƒĻ„Ī·ĻĪ¹Ī¶ĻŒĪ¼ĪµĪ½Īæ" +active = "Ī•Ī½ĪµĻĪ³ĻŒ" addToUpload = "Προσθήκη ĻƒĻ„Ī· Ī¼ĪµĻ„Ī±Ļ†ĻŒĻĻ„Ļ‰ĻƒĪ·" +closeFile = "Κλείσιμο αρχείου" deleteAll = "Διαγραφή ĻŒĪ»Ļ‰Ī½" loadingFiles = "Ī¦ĻŒĻĻ„Ļ‰ĻƒĪ· αρχείων..." noFiles = "Δεν υπάρχουν Γιαθέσιμα αρχεία" @@ -5132,7 +5262,7 @@ upgrade = "Ī‘Ī½Ī±Ī²Ī¬ĪøĪ¼Ī¹ĻƒĪ· Ļ„ĻŽĻĪ± →" freeTitle = "ΆΓεια Ī“Ī¹Ī±ĪŗĪæĪ¼Ī¹ĻƒĻ„Ī®" overLimitTitle = "Απαιτείται άΓεια Ī“Ī¹Ī±ĪŗĪæĪ¼Ī¹ĻƒĻ„Ī®" overLimitBody = "Ī— Ī±Ī“ĪµĪ¹ĪæĪ“ĻŒĻ„Ī·ĻƒĪ· μας επιτρέπει έως {{freeTierLimit}} Ļ‡ĻĪ®ĻƒĻ„ĪµĻ‚ Γωρεάν ανά Ī“Ī¹Ī±ĪŗĪæĪ¼Ī¹ĻƒĻ„Ī®. ĪˆĻ‡ĪµĻ„Īµ {{overLimitUserCopy}} Ļ‡ĻĪ®ĻƒĻ„ĪµĻ‚ Stirling. Για να ĻƒĻ…Ī½ĪµĻ‡ĪÆĻƒĪµĻ„Īµ χωρίς Γιακοπές, Ī±Ī½Ī±Ī²Ī±ĪøĪ¼ĪÆĻƒĻ„Īµ ĻƒĻ„Īæ πλάνο Stirling Server - Ī±Ļ€ĪµĻĪ¹ĻŒĻĪ¹ĻƒĻ„ĪµĻ‚ ĪøĪ­ĻƒĪµĪ¹Ļ‚, ĪµĻ€ĪµĪ¾ĪµĻĪ³Ī±ĻƒĪÆĪ± κειμένου PDF και πλήρης έλεγχος Ī“Ī¹Ī±Ļ‡ĪµĪ¹ĻĪ¹ĻƒĻ„Ī® για $99/server/μήνα." -freeBody = "Ī— Ī±Ī“ĪµĪ¹ĪæĪ“ĻŒĻ„Ī·ĻƒĪ· Open-Core μας επιτρέπει έως {{freeTierLimit}} Ļ‡ĻĪ®ĻƒĻ„ĪµĻ‚ Γωρεάν ανά Ī“Ī¹Ī±ĪŗĪæĪ¼Ī¹ĻƒĻ„Ī®. Για Ī±Ļ€ĻĻŒĻƒĪŗĪæĻ€Ļ„Ī· ĪŗĪ»Ī¹Ī¼Ī¬ĪŗĻ‰ĻƒĪ· και έγκαιρη Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ· ĻƒĻ„Īæ νέο εργαλείο ĪµĻ€ĪµĪ¾ĪµĻĪ³Ī±ĻƒĪÆĪ±Ļ‚ κειμένου PDF, προτείνουμε το πλάνο Stirling Server - πλήρης ĪµĻ€ĪµĪ¾ĪµĻĪ³Ī±ĻƒĪÆĪ± και Ī±Ļ€ĪµĻĪ¹ĻŒĻĪ¹ĻƒĻ„ĪµĻ‚ ĪøĪ­ĻƒĪµĪ¹Ļ‚ για $99/server/μήνα." +freeBody = "Οι άΓειες Ļ‡ĻĪ®ĻƒĪ·Ļ‚ Open-Core επιτρέπουν έως και {{freeTierLimit}} Ļ‡ĻĪ®ĻƒĻ„ĪµĻ‚ Γωρεάν ανά Ī“Ī¹Ī±ĪŗĪæĪ¼Ī¹ĻƒĻ„Ī®. Για Ī±Ļ€ĻĻŒĻƒĪŗĪæĻ€Ļ„Ī· ĪŗĪ»Ī¹Ī¼Ī¬ĪŗĻ‰ĻƒĪ·, προτείνουμε το πλάνο Stirling Server - Ī±Ļ€ĪµĻĪ¹ĻŒĻĪ¹ĻƒĻ„ĪµĻ‚ ĪøĪ­ĻƒĪµĪ¹Ļ‚ και Ļ…Ļ€ĪæĻƒĻ„Ī®ĻĪ¹Ī¾Ī· SSO με $99/Ī“Ī¹Ī±ĪŗĪæĪ¼Ī¹ĻƒĻ„Ī®/μήνα." [onboarding.desktopInstall] title = "Ī›Ī®ĻˆĪ·" @@ -5237,6 +5367,31 @@ error = "Αποτυχία ĪµĪ½Ī·Ī¼Ī­ĻĻ‰ĻƒĪ·Ļ‚ ĪŗĪ±Ļ„Ī¬ĻƒĻ„Ī±ĻƒĪ·Ļ‚ Ļ‡ĻĪ®ĻƒĻ„Ī· success = "Ο Ļ‡ĻĪ®ĻƒĻ„Ī·Ļ‚ Γιαγράφηκε με επιτυχία" error = "Αποτυχία Γιαγραφής Ļ‡ĻĪ®ĻƒĻ„Ī·" +[workspace.people.changePassword] +action = "Αλλαγή ĪŗĻ‰Ī“Ī¹ĪŗĪæĻ Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚" +title = "Αλλαγή ĪŗĻ‰Ī“Ī¹ĪŗĪæĻ Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚" +subtitle = "Ī•Ī½Ī·Ī¼Ī­ĻĻ‰ĻƒĪ· ĪŗĻ‰Ī“Ī¹ĪŗĪæĻ Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚ για" +newPassword = "ĪĪ­ĪæĻ‚ ĪŗĻ‰Ī“Ī¹ĪŗĻŒĻ‚ Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚" +confirmPassword = "Ī•Ļ€Ī¹Ī²ĪµĪ²Ī±ĪÆĻ‰ĻƒĪ· ĪŗĻ‰Ī“Ī¹ĪŗĪæĻ Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚" +placeholder = "Ī•Ī¹ĻƒĪ±Ī³Ī¬Ī³ĪµĻ„Īµ νέο ĪŗĻ‰Ī“Ī¹ĪŗĻŒ Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚" +confirmPlaceholder = "Ī•Ī¹ĻƒĪ±Ī³Ī¬Ī³ĪµĻ„Īµ ξανά τον νέο ĪŗĻ‰Ī“Ī¹ĪŗĻŒ Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚" +passwordRequired = "Ī Ī±ĻĪ±ĪŗĪ±Ī»ĪæĻĪ¼Īµ ĪµĪ¹ĻƒĪ±Ī³Ī¬Ī³ĪµĻ„Īµ νέο ĪŗĻ‰Ī“Ī¹ĪŗĻŒ Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚" +passwordMismatch = "Οι κωΓικοί Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚ Γεν ταιριάζουν" +generateRandom = "Δημιουργία Ī±ĻƒĻ†Ī±Ī»ĪæĻĻ‚ ĪŗĻ‰Ī“Ī¹ĪŗĪæĻ Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚" +generatedPreview = "Δημιουργημένος ĪŗĻ‰Ī“Ī¹ĪŗĻŒĻ‚ Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚:" +copyTooltip = "Αντιγραφή ĻƒĻ„Īæ Ļ€ĻĻŒĻ‡ĪµĪ¹ĻĪæ" +copiedToClipboard = "Ο ĪŗĻ‰Ī“Ī¹ĪŗĻŒĻ‚ Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚ αντιγράφηκε ĻƒĻ„Īæ Ļ€ĻĻŒĻ‡ĪµĪ¹ĻĪæ" +copyFailed = "Αποτυχία αντιγραφής ĪŗĻ‰Ī“Ī¹ĪŗĪæĻ Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚" +sendEmail = "Ī‘Ļ€ĪæĻƒĻ„ĪæĪ»Ī® email ĻƒĻ„ĪæĪ½ Ļ‡ĻĪ®ĻƒĻ„Ī· για αυτήν την αλλαγή" +includePassword = "ĪĪ± ĻƒĻ…Ī¼Ļ€ĪµĻĪ¹Ī»Ī·Ļ†ĪøĪµĪÆ Īæ νέος ĪŗĻ‰Ī“Ī¹ĪŗĻŒĻ‚ Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚ ĻƒĻ„Īæ email" +forcePasswordChange = "΄ποχρεωτική αλλαγή ĪŗĻ‰Ī“Ī¹ĪŗĪæĻ Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚ κατά την ĪµĻ€ĻŒĪ¼ĪµĪ½Ī· ĻƒĻĪ½Ī“ĪµĻƒĪ·" +emailUnavailable = "Το email Ī±Ļ…Ļ„ĪæĻ του Ļ‡ĻĪ®ĻƒĻ„Ī· Γεν είναι έγκυρη Ī“Ī¹ĪµĻĪøĻ…Ī½ĻƒĪ· email. Οι ĪµĪ¹Ī“ĪæĻ€ĪæĪ¹Ī®ĻƒĪµĪ¹Ļ‚ είναι απενεργοποιημένες." +smtpDisabled = "Οι ĪµĪ¹Ī“ĪæĻ€ĪæĪ¹Ī®ĻƒĪµĪ¹Ļ‚ Ī¼Ī­ĻƒĻ‰ email Ī±Ļ€Ī±Ī¹Ļ„ĪæĻĪ½ την ĪµĪ½ĪµĻĪ³ĪæĻ€ĪæĪÆĪ·ĻƒĪ· του SMTP ĻƒĻ„Ī¹Ļ‚ ĻĻ…ĪøĪ¼ĪÆĻƒĪµĪ¹Ļ‚." +notifyOnly = "Θα ĻƒĻ„Ī±Ī»ĪµĪÆ email χωρίς τον ĪŗĻ‰Ī“Ī¹ĪŗĻŒ Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚, ĪµĪ½Ī·Ī¼ĪµĻĻŽĪ½ĪæĪ½Ļ„Ī±Ļ‚ τον Ļ‡ĻĪ®ĻƒĻ„Ī· ĻŒĻ„Ī¹ ένας Ī“Ī¹Ī±Ļ‡ĪµĪ¹ĻĪ¹ĻƒĻ„Ī®Ļ‚ τον άλλαξε." +submit = "Ī•Ī½Ī·Ī¼Ī­ĻĻ‰ĻƒĪ· ĪŗĻ‰Ī“Ī¹ĪŗĪæĻ Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚" +success = "Ο ĪŗĻ‰Ī“Ī¹ĪŗĻŒĻ‚ Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚ ĪµĪ½Ī·Ī¼ĪµĻĻŽĪøĪ·ĪŗĪµ με επιτυχία" +error = "Αποτυχία ĪµĪ½Ī·Ī¼Ī­ĻĻ‰ĻƒĪ·Ļ‚ ĪŗĻ‰Ī“Ī¹ĪŗĪæĻ Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·Ļ‚" + [workspace.people.emailInvite] tab = "Πρόσκληση Ī¼Ī­ĻƒĻ‰ Email" description = "Ī Ī»Ī·ĪŗĻ„ĻĪæĪ»ĪæĪ³Ī®ĻƒĻ„Īµ Ī® ĪµĻ€Ī¹ĪŗĪæĪ»Ī»Ī®ĻƒĻ„Īµ emails παρακάτω, Ļ‡Ļ‰ĻĪ¹ĻƒĪ¼Ī­Ī½Ī± με κόμμα. Οι Ļ‡ĻĪ®ĻƒĻ„ĪµĻ‚ θα λάβουν ĻƒĻ„ĪæĪ¹Ļ‡ĪµĪÆĪ± ĻƒĻĪ½Ī“ĪµĻƒĪ·Ļ‚ Ī¼Ī­ĻƒĻ‰ email." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Απαιτείται Ļ„ĪæĻ…Ī»Ī¬Ļ‡Ī¹ĻƒĻ„ĪæĪ½ μία Ī“Ī¹ĪµĻĪøĻ…Ī½ĻƒĪ· email" submit = "Ī‘Ļ€ĪæĻƒĻ„ĪæĪ»Ī® Ļ€ĻĪæĻƒĪŗĪ»Ī®ĻƒĪµĻ‰Ī½" success = "ĻƒĻ„Ī¬Ī»ĪøĪ·ĪŗĪ±Ī½ Ļ€ĻĪæĻƒĪŗĪ»Ī®ĻƒĪµĪ¹Ļ‚ με επιτυχία" -partialSuccess = "ĪšĪ¬Ļ€ĪæĪ¹ĪµĻ‚ Ļ€ĻĪæĻƒĪŗĪ»Ī®ĻƒĪµĪ¹Ļ‚ απέτυχαν" +partialFailure = "ĪŸĻĪ¹ĻƒĪ¼Ī­Ī½ĪµĻ‚ Ļ€ĻĪæĻƒĪŗĪ»Ī®ĻƒĪµĪ¹Ļ‚ απέτυχαν" allFailed = "Αποτυχία Ļ€ĻĻŒĻƒĪŗĪ»Ī·ĻƒĪ·Ļ‚ Ļ‡ĻĪ·ĻƒĻ„ĻŽĪ½" error = "Αποτυχία Ī±Ļ€ĪæĻƒĻ„ĪæĪ»Ī®Ļ‚ Ļ€ĻĪæĻƒĪŗĪ»Ī®ĻƒĪµĻ‰Ī½" @@ -5709,7 +5864,7 @@ title = "Διάγραμμα Ļ‡ĻĪ®ĻƒĪ·Ļ‚ Endpoints" [usage.table] title = "Αναλυτικά ĻƒĻ„Ī±Ļ„Ī¹ĻƒĻ„Ī¹ĪŗĪ¬" -endpoint = "Endpoint" +endpoint = "Σημείο Ļ„ĪµĻĪ¼Ī±Ļ„Ī¹ĻƒĪ¼ĪæĻ" visits = "Ī•Ļ€Ī¹ĻƒĪŗĪ­ĻˆĪµĪ¹Ļ‚" percentage = "Ī ĪæĻƒĪæĻƒĻ„ĻŒ" noData = "Δεν υπάρχουν Γιαθέσιμα ΓεΓομένα" @@ -5770,6 +5925,7 @@ subtitle = "ΣυνΓεθείτε με τον λογαριασμό Stirling" [setup.selfhosted] title = "Ī£ĻĪ½Ī“ĪµĻƒĪ· ĻƒĻ„ĪæĪ½ Ī“Ī¹Ī±ĪŗĪæĪ¼Ī¹ĻƒĻ„Ī®" subtitle = "Ī•Ī¹ĻƒĪ±Ī³Ī¬Ī³ĪµĻ„Īµ τα Ī“Ī¹Ī±Ļ€Ī¹ĻƒĻ„ĪµĻ…Ļ„Ī®ĻĪ¹Ī± του Ī“Ī¹Ī±ĪŗĪæĪ¼Ī¹ĻƒĻ„Ī® ĻƒĪ±Ļ‚" +link = "Ī® ĻƒĻ…Ī½Ī“ĪµĪøĪµĪÆĻ„Īµ σε έναν self-hosted λογαριασμό" [setup.server] title = "Ī£ĻĪ½Ī“ĪµĻƒĪ· σε Ī“Ī¹Ī±ĪŗĪæĪ¼Ī¹ĻƒĻ„Ī®" @@ -5788,6 +5944,14 @@ description = "Ī•Ī¹ĻƒĪ±Ī³Ī¬Ī³ĪµĻ„Īµ το πλήρες URL του self-hosted Ī“ emptyUrl = "Ī•Ī¹ĻƒĪ±Ī³Ī¬Ī³ĪµĻ„Īµ URL Ī“Ī¹Ī±ĪŗĪæĪ¼Ī¹ĻƒĻ„Ī®" unreachable = "ΑΓυναμία ĻƒĻĪ½Ī“ĪµĻƒĪ·Ļ‚ με τον Ī“Ī¹Ī±ĪŗĪæĪ¼Ī¹ĻƒĻ„Ī®" testFailed = "Αποτυχία ελέγχου ĻƒĻĪ½Ī“ĪµĻƒĪ·Ļ‚" +configFetch = "Αποτυχία Ī±Ī½Ī¬ĪŗĻ„Ī·ĻƒĪ·Ļ‚ της Ī“Ī¹Ī±Ī¼ĻŒĻĻ†Ļ‰ĻƒĪ·Ļ‚ του Ī“Ī¹Ī±ĪŗĪæĪ¼Ī¹ĻƒĻ„Ī®. Ελέγξτε το URL και Ī“ĪæĪŗĪ¹Ī¼Ī¬ĻƒĻ„Īµ ξανά." + +[setup.server.error.securityDisabled] +title = "Ī— ĻƒĻĪ½Ī“ĪµĻƒĪ· Γεν είναι ενεργοποιημένη" +body = "Σε Ī±Ļ…Ļ„ĻŒĪ½ τον Ī“Ī¹Ī±ĪŗĪæĪ¼Ī¹ĻƒĻ„Ī® Γεν είναι ενεργοποιημένη Ī· ĻƒĻĪ½Ī“ĪµĻƒĪ·. Για να ĻƒĻ…Ī½Ī“ĪµĪøĪµĪÆĻ„Īµ σε Ī±Ļ…Ļ„ĻŒĪ½ τον Ī“Ī¹Ī±ĪŗĪæĪ¼Ī¹ĻƒĻ„Ī®, πρέπει να ĪµĪ½ĪµĻĪ³ĪæĻ€ĪæĪ¹Ī®ĻƒĪµĻ„Īµ τον έλεγχο Ļ„Ī±Ļ…Ļ„ĻŒĻ„Ī·Ļ„Ī±Ļ‚:" +step1 = "ĪŸĻĪÆĻƒĻ„Īµ το DOCKER_ENABLE_SECURITY=true ĻƒĻ„Īæ περιβάλλον ĻƒĪ±Ļ‚" +step2 = "Ή ĪæĻĪÆĻƒĻ„Īµ security.enableLogin=true ĻƒĻ„Īæ settings.yml" +step3 = "Ī•Ļ€Ī±Ī½ĪµĪŗĪŗĪ¹Ī½Ī®ĻƒĻ„Īµ τον Ī“Ī¹Ī±ĪŗĪæĪ¼Ī¹ĻƒĻ„Ī®" [setup.login] title = "Ī£ĻĪ½Ī“ĪµĻƒĪ·" @@ -5797,6 +5961,13 @@ submit = "Ī£ĻĪ½Ī“ĪµĻƒĪ·" signInWith = "Ī£ĻĪ½Ī“ĪµĻƒĪ· με" oauthPending = "Άνοιγμα προγράμματος Ļ€ĪµĻĪ¹Ī®Ī³Ī·ĻƒĪ·Ļ‚ για έλεγχο Ļ„Ī±Ļ…Ļ„ĻŒĻ„Ī·Ļ„Ī±Ļ‚..." orContinueWith = "Ή ĻƒĻ…Ī½ĪµĻ‡ĪÆĻƒĻ„Īµ με email" +serverRequirement = "Ī£Ī·Ī¼ĪµĪÆĻ‰ĻƒĪ·: Ο Ī“Ī¹Ī±ĪŗĪæĪ¼Ī¹ĻƒĻ„Ī®Ļ‚ πρέπει να έχει ενεργοποιημένη τη ĻƒĻĪ½Ī“ĪµĻƒĪ·." +showInstructions = "Ī ĻŽĻ‚ ενεργοποιείται;" +hideInstructions = "Ī‘Ļ€ĻŒĪŗĻĻ…ĻˆĪ· ĪæĪ“Ī·Ī³Ī¹ĻŽĪ½" +instructions = "Για να ĪµĪ½ĪµĻĪ³ĪæĻ€ĪæĪ¹Ī®ĻƒĪµĻ„Īµ τη ĻƒĻĪ½Ī“ĪµĻƒĪ· ĻƒĻ„ĪæĪ½ Ī“Ī¹Ī±ĪŗĪæĪ¼Ī¹ĻƒĻ„Ī® Stirling PDF:" +instructionsEnvVar = "ĪŸĻĪÆĻƒĻ„Īµ τη μεταβλητή περιβάλλοντος:" +instructionsOrYml = "Ή ĻƒĻ„Īæ settings.yml:" +instructionsRestart = "Στη ĻƒĻ…Ī½Ī­Ļ‡ĪµĪ¹Ī±, ĪµĻ€Ī±Ī½ĪµĪŗĪŗĪ¹Ī½Ī®ĻƒĻ„Īµ τον Ī“Ī¹Ī±ĪŗĪæĪ¼Ī¹ĻƒĻ„Ī® ĻƒĪ±Ļ‚ για να ĪµĻ†Ī±ĻĪ¼ĪæĻƒĻ„ĪæĻĪ½ οι αλλαγές." [setup.login.username] label = "Όνομα Ļ‡ĻĪ®ĻƒĻ„Ī·" @@ -5853,6 +6024,7 @@ earlyAccess = "Ī ĻĻŒĻ‰ĻĪ· Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·" reset = "Επαναφορά Ī±Ī»Ī»Ī±Ī³ĻŽĪ½" downloadJson = "Ī›Ī®ĻˆĪ· JSON" generatePdf = "Δημιουργία PDF" +saveChanges = "Ī‘Ļ€ĪæĪøĪ®ĪŗĪµĻ…ĻƒĪ· Ī±Ī»Ī»Ī±Ī³ĻŽĪ½" [pdfTextEditor.options.autoScaleText] title = "Ī‘Ļ…Ļ„ĻŒĪ¼Ī±Ļ„Ī· Ļ€ĻĪæĻƒĪ±ĻĪ¼ĪæĪ³Ī® κειμένου ĻƒĻ„Ī± Ļ€Ī»Ī±ĪÆĻƒĪ¹Ī±" @@ -5890,6 +6062,8 @@ alpha = "Ī‘Ļ…Ļ„ĻŒĻ‚ Īæ προβολέας άλφα ĪµĪ¾ĪµĪ»ĪÆĻƒĻƒĪµĻ„Ī±Ī¹ ακό [pdfTextEditor.empty] title = "Δεν Ļ†ĪæĻĻ„ĻŽĪøĪ·ĪŗĪµ έγγραφο" subtitle = "Ī¦ĪæĻĻ„ĻŽĻƒĻ„Īµ ένα αρχείο PDF Ī® JSON για να Ī¾ĪµĪŗĪ¹Ī½Ī®ĻƒĪµĻ„Īµ την ĪµĻ€ĪµĪ¾ĪµĻĪ³Ī±ĻƒĪÆĪ± κειμένου." +dropzone = "Ī£ĻĻĪµĻ„Īµ και Ī±Ļ€ĪæĪøĪ­ĻƒĻ„Īµ ĪµĪ“ĻŽ ένα αρχείο PDF Ī® JSON, Ī® κάντε κλικ για Ļ€ĪµĻĪ¹Ī®Ī³Ī·ĻƒĪ·" +dropzoneWithFiles = "Επιλέξτε ένα αρχείο Ī±Ļ€ĻŒ την καρτέλα Αρχεία, Ī® ĻƒĻĻĪµĻ„Īµ και Ī±Ļ€ĪæĪøĪ­ĻƒĻ„Īµ ĪµĪ“ĻŽ ένα αρχείο PDF Ī® JSON, Ī® κάντε κλικ για Ļ€ĪµĻĪ¹Ī®Ī³Ī·ĻƒĪ·" [pdfTextEditor.welcomeBanner] title = "ĪšĪ±Ī»ĻŽĻ‚ ĪæĻĪÆĻƒĪ±Ļ„Īµ ĻƒĻ„Īæ PDF Text Editor (Ī ĻĻŽĪ¹Ī¼Ī· Ļ€ĻĻŒĻƒĪ²Ī±ĻƒĪ·)" diff --git a/frontend/public/locales/en-GB/translation.toml b/frontend/public/locales/en-GB/translation.toml index a425ad3d50..9d9278922e 100644 --- a/frontend/public/locales/en-GB/translation.toml +++ b/frontend/public/locales/en-GB/translation.toml @@ -3036,6 +3036,91 @@ title = "Get Info on PDF" header = "Get Info on PDF" submit = "Get Info" downloadJson = "Download JSON" +processing = "Extracting information..." +results = "Results" +noResults = "Run the tool to generate a report." +downloads = "Downloads" +noneDetected = "None detected" +indexTitle = "Index" + +[getPdfInfo.report] +entryLabel = "Full information summary" +shortTitle = "PDF Information" + +[getPdfInfo.sections] +metadata = "Metadata" +formFields = "Form Fields" +basicInfo = "Basic Info" +documentInfo = "Document Info" +compliance = "Compliance" +encryption = "Encryption" +permissions = "Permissions" +other = "Other" +perPageInfo = "Per Page Info" +tableOfContents = "Table of Contents" + +[getPdfInfo.other] +attachments = "Attachments" +embeddedFiles = "Embedded Files" +javaScript = "JavaScript" +layers = "Layers" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Size" +annotations = "Annotations" +images = "Images" +links = "Links" +fonts = "Fonts" +xobjects = "XObject Counts" +multimedia = "Multimedia" + +[getPdfInfo.summary] +pages = "Pages" +fileSize = "File Size" +pdfVersion = "PDF Version" +language = "Language" +title = "PDF Summary" +author = "Author" +created = "Created" +modified = "Modified" +permsAll = "All Permissions Allowed" +permsRestricted = "{{count}} restrictions" +permsMixed = "Some permissions restricted" +hasCompliance = "Has compliance standards" +noCompliance = "No Compliance Standards" +basic = "Basic Information" +documentInfo = "Document Information" +securityTitle = "Security Status" +technical = "Technical" +overviewTitle = "PDF Overview" + +[getPdfInfo.summary.security] +encrypted = "Encrypted PDF - Password protection present" +unencrypted = "Unencrypted PDF - No password protection" + +[getPdfInfo.summary.tech] +images = "Images" +fonts = "Fonts" +formFields = "Form Fields" +embeddedFiles = "Embedded Files" +javaScript = "JavaScript" +layers = "Layers" +bookmarks = "Bookmarks" +multimedia = "Multimedia" + +[getPdfInfo.summary.overview] +untitled = "an untitled document" +unknown = "Unknown Author" +text = "This is a {{pages}}-page PDF titled {{title}} created by {{author}} (PDF version {{version}})." + +[getPdfInfo.error] +partial = "Some files could not be processed." +unexpected = "Unexpected error during extraction." + +[getPdfInfo.status] +complete = "Extraction complete" [extractPage] tags = "extract" @@ -3454,8 +3539,8 @@ signinTitle = "Please sign in" ssoSignIn = "Login via Single Sign-on" oAuth2AutoCreateDisabled = "OAUTH2 Auto-Create User Disabled" oAuth2AdminBlockedUser = "Registration or logging in of non-registered users is currently blocked. Please contact the administrator." -oAuth2RequiresLicense = "OAuth/SSO login requires a paid license (Server or Enterprise). Please contact the administrator to upgrade your plan." -saml2RequiresLicense = "SAML login requires a paid license (Server or Enterprise). Please contact the administrator to upgrade your plan." +oAuth2RequiresLicense = "OAuth/SSO login requires a Server or Enterprise license. Please contact the administrator to upgrade your plan." +saml2RequiresLicense = "SAML login requires an Enterprise license. Please contact the administrator to upgrade your plan." maxUsersReached = "Maximum number of users reached for your current license. Please contact the administrator to upgrade your plan or add more seats." oauth2RequestNotFound = "Authorization request not found" oauth2InvalidUserInfoResponse = "Invalid User Info Response" @@ -5178,7 +5263,7 @@ upgrade = "Upgrade now →" freeTitle = "Server License" overLimitTitle = "Server License Needed" overLimitBody = "Our licensing permits up to {{freeTierLimit}} users for free per server. You have {{overLimitUserCopy}} Stirling users. To continue uninterrupted, upgrade to the Stirling Server plan - unlimited seats, PDF text editing, and full admin control for $99/server/mo." -freeBody = "Our Open-Core licensing permits up to {{freeTierLimit}} users for free per server. To scale uninterrupted and get early access to our new PDF text editing tool, we recommend the Stirling Server plan - full editing and unlimited seats for $99/server/mo." +freeBody = "Our Open-Core licensing permits up to {{freeTierLimit}} users for free per server. To scale uninterrupted, we recommend the Stirling Server plan - unlimited seats and SSO support for $99/server/mo." [onboarding.desktopInstall] title = "Download" @@ -5283,6 +5368,31 @@ error = "Failed to update user status" success = "User deleted successfully" error = "Failed to delete user" +[workspace.people.changePassword] +action = "Change password" +title = "Change password" +subtitle = "Update the password for" +newPassword = "New password" +confirmPassword = "Confirm password" +placeholder = "Enter a new password" +confirmPlaceholder = "Re-enter the new password" +passwordRequired = "Please enter a new password" +passwordMismatch = "Passwords do not match" +generateRandom = "Generate secure password" +generatedPreview = "Generated password:" +copyTooltip = "Copy to clipboard" +copiedToClipboard = "Password copied to clipboard" +copyFailed = "Failed to copy password" +sendEmail = "Email the user about this change" +includePassword = "Include the new password in the email" +forcePasswordChange = "Force user to change password on next login" +emailUnavailable = "This user's email is not a valid email address. Notifications are disabled." +smtpDisabled = "Email notifications require SMTP to be enabled in settings." +notifyOnly = "An email will be sent without the password, letting the user know an admin changed it." +submit = "Update password" +success = "Password updated successfully" +error = "Failed to update password" + [workspace.people.emailInvite] tab = "Email Invite" description = "Type or paste in emails below, separated by commas. Users will receive login credentials via email." @@ -5816,6 +5926,7 @@ subtitle = "Sign in with your Stirling account" [setup.selfhosted] title = "Sign in to Server" subtitle = "Enter your server credentials" +link = "or connect to a self-hosted account" [setup.server] title = "Connect to Server" @@ -5834,6 +5945,14 @@ description = "Enter the full URL of your self-hosted Stirling PDF server" emptyUrl = "Please enter a server URL" unreachable = "Could not connect to server" testFailed = "Connection test failed" +configFetch = "Failed to fetch server configuration. Please check the URL and try again." + +[setup.server.error.securityDisabled] +title = "Login Not Enabled" +body = "This server does not have login enabled. To connect to this server, you must enable authentication:" +step1 = "Set DOCKER_ENABLE_SECURITY=true in your environment" +step2 = "Or set security.enableLogin=true in settings.yml" +step3 = "Restart the server" [setup.login] title = "Sign In" @@ -5906,6 +6025,7 @@ earlyAccess = "Early Access" reset = "Reset Changes" downloadJson = "Download JSON" generatePdf = "Generate PDF" +saveChanges = "Save Changes" [pdfTextEditor.options.autoScaleText] title = "Auto-scale text to fit boxes" @@ -5943,6 +6063,8 @@ alpha = "This alpha viewer is still evolving—certain fonts, colours, transpare [pdfTextEditor.empty] title = "No document loaded" subtitle = "Load a PDF or JSON file to begin editing text content." +dropzone = "Drag and drop a PDF or JSON file here, or click to browse" +dropzoneWithFiles = "Select a file from the Files tab, or drag and drop a PDF or JSON file here, or click to browse" [pdfTextEditor.welcomeBanner] title = "Welcome to PDF Text Editor (Early Access)" diff --git a/frontend/public/locales/es-ES/translation.toml b/frontend/public/locales/es-ES/translation.toml index 7890318ff2..a3faa4df13 100644 --- a/frontend/public/locales/es-ES/translation.toml +++ b/frontend/public/locales/es-ES/translation.toml @@ -163,6 +163,11 @@ unfavorite = "Quitar de favoritos" fullscreen = "Cambiar a modo pantalla completa" sidebar = "Cambiar a modo barra lateral" +[backendStartup] +notFoundTitle = "Backend no encontrado" +retry = "Reintentar" +unreachable = "La aplicación no puede conectarse actualmente al backend. Verifique el estado del backend y la conectividad de red, luego intĆ©ntelo de nuevo." + [zipWarning] title = "Archivo ZIP grande" message = "Este ZIP contiene {{count}} archivos. ĀæExtraer de todos modos?" @@ -347,7 +352,7 @@ teams = "Equipos" title = "Configuración" systemSettings = "Ajustes del sistema" features = "Funciones" -endpoints = "Endpoints" +endpoints = "Puntos de conexión" database = "Base de datos" advanced = "Avanzado" @@ -912,6 +917,9 @@ desc = "Crear flujos de trabajo de mĆŗltiples pasos encadenando acciones de PDF. desc = "Superponer PDFs encima de otro PDF" title = "Superponer PDFs" +[home.pdfTextEditor] +title = "Editor de texto de PDF" +desc = "Edita texto e imĆ”genes existentes dentro de archivos PDF" [home.addText] tags = "texto,anotación,etiqueta" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Firma dibujada" defaultImageLabel = "Firma subida" defaultTextLabel = "Firma escrita" saveButton = "Guardar firma" +savePersonal = "Guardar personal" +saveShared = "Guardar compartida" saveUnavailable = "Cree primero una firma para guardarla." noChanges = "La firma actual ya estĆ” guardada." +tempStorageTitle = "Almacenamiento temporal del navegador" +tempStorageDescription = "Las firmas se almacenan solo en tu navegador. Se perderĆ”n si borras los datos del navegador o cambias de navegador." +personalHeading = "Firmas personales" +sharedHeading = "Firmas compartidas" +personalDescription = "Solo tĆŗ puedes ver estas firmas." +sharedDescription = "Todos los usuarios pueden ver y usar estas firmas." [sign.saved.type] canvas = "Dibujo" @@ -3020,6 +3036,91 @@ title = "Obtener Información del PDF" header = "Obtener Información del PDF" submit = "Obtener Información" downloadJson = "Descargar JSON" +processing = "Extrayendo información..." +results = "Resultados" +noResults = "Ejecute la herramienta para generar un informe." +downloads = "Descargas" +noneDetected = "Ninguno detectado" +indexTitle = "ƍndice" + +[getPdfInfo.report] +entryLabel = "Resumen completo de información" +shortTitle = "Información del PDF" + +[getPdfInfo.sections] +metadata = "Metadatos" +formFields = "Campos de formulario" +basicInfo = "Información bĆ”sica" +documentInfo = "Información del documento" +compliance = "Conformidad" +encryption = "Cifrado" +permissions = "Permisos" +other = "Otros" +perPageInfo = "Información por pĆ”gina" +tableOfContents = "Tabla de contenidos" + +[getPdfInfo.other] +attachments = "Adjuntos" +embeddedFiles = "Archivos incrustados" +javaScript = "JavaScript" +layers = "Capas" +structureTree = "Ɓrbol de estructura" +xmp = "Metadatos XMP" + +[getPdfInfo.perPage] +size = "TamaƱo" +annotations = "Anotaciones" +images = "ImĆ”genes" +links = "Enlaces" +fonts = "Fuentes" +xobjects = "Recuento de XObject" +multimedia = "Multimedia" + +[getPdfInfo.summary] +pages = "PĆ”ginas" +fileSize = "TamaƱo del archivo" +pdfVersion = "Versión de PDF" +language = "Idioma" +title = "Resumen del PDF" +author = "Autor" +created = "Creado" +modified = "Modificado" +permsAll = "Todos los permisos permitidos" +permsRestricted = "{{count}} restricciones" +permsMixed = "Algunos permisos restringidos" +hasCompliance = "Cumple con estĆ”ndares" +noCompliance = "Sin estĆ”ndares de conformidad" +basic = "Información bĆ”sica" +documentInfo = "Información del documento" +securityTitle = "Estado de seguridad" +technical = "TĆ©cnico" +overviewTitle = "Vista general del PDF" + +[getPdfInfo.summary.security] +encrypted = "PDF cifrado: protección con contraseƱa presente" +unencrypted = "PDF sin cifrar: sin protección con contraseƱa" + +[getPdfInfo.summary.tech] +images = "ImĆ”genes" +fonts = "Fuentes" +formFields = "Campos de formulario" +embeddedFiles = "Archivos incrustados" +javaScript = "JavaScript" +layers = "Capas" +bookmarks = "Marcadores" +multimedia = "Multimedia" + +[getPdfInfo.summary.overview] +untitled = "un documento sin tĆ­tulo" +unknown = "Autor desconocido" +text = "Este es un PDF de {{pages}} pĆ”ginas titulado {{title}} creado por {{author}} (versión de PDF {{version}})." + +[getPdfInfo.error] +partial = "Algunos archivos no se pudieron procesar." +unexpected = "Error inesperado durante la extracción." + +[getPdfInfo.status] +complete = "Extracción completada" [extractPage] tags = "extraer" @@ -3438,6 +3539,9 @@ signinTitle = "Por favor, inicie sesión" ssoSignIn = "Iniciar sesión a travĆ©s del inicio de sesión Ćŗnico" oAuth2AutoCreateDisabled = "Usuario de creación automĆ”tica de OAUTH2 DESACTIVADO" oAuth2AdminBlockedUser = "El registro o inicio de sesión de usuarios no registrados estĆ” actualmente bloqueado. Por favor, póngase en contacto con el administrador." +oAuth2RequiresLicense = "El inicio de sesión OAuth/SSO requiere una licencia de pago (Server o Enterprise). Póngase en contacto con el administrador para actualizar su plan." +saml2RequiresLicense = "El inicio de sesión SAML requiere una licencia de pago (Server o Enterprise). Póngase en contacto con el administrador para actualizar su plan." +maxUsersReached = "Se alcanzó el nĆŗmero mĆ”ximo de usuarios para su licencia actual. Póngase en contacto con el administrador para actualizar su plan o aƱadir mĆ”s plazas." oauth2RequestNotFound = "Solicitud de autorización no encontrada" oauth2InvalidUserInfoResponse = "Respuesta de información de usuario no vĆ”lida" oauth2invalidRequest = "Solicitud no vĆ”lida" @@ -3846,14 +3950,17 @@ fitToWidth = "Ajustar al Ancho" actualSize = "TamaƱo Real" [viewer] +cannotPreviewFile = "No se puede previsualizar el archivo" +dualPageView = "Vista de PĆ”gina Doble" firstPage = "Primera PĆ”gina" lastPage = "Última PĆ”gina" -previousPage = "PĆ”gina Anterior" nextPage = "PĆ”gina Siguiente" +onlyPdfSupported = "El visor solo admite archivos PDF. Este archivo parece ser de un formato diferente." +previousPage = "PĆ”gina Anterior" +singlePageView = "Vista de PĆ”gina Única" +unknownFile = "Archivo desconocido" zoomIn = "Acercar" zoomOut = "Alejar" -singlePageView = "Vista de PĆ”gina Única" -dualPageView = "Vista de PĆ”gina Doble" [rightRail] closeSelected = "Cerrar Archivos Seleccionados" @@ -3877,6 +3984,7 @@ toggleSidebar = "Alternar Barra Lateral" exportSelected = "Exportar pĆ”ginas seleccionadas" toggleAnnotations = "Mostrar/ocultar anotaciones" annotationMode = "Cambiar modo de anotaciones" +print = "Imprimir PDF" draw = "Dibujar" save = "Guardar" saveChanges = "Guardar cambios" @@ -4494,6 +4602,7 @@ description = "URL o nombre de archivo del impressum (requerido en algunas juris title = "Premium y Enterprise" description = "Configura tu clave de licencia premium o enterprise." license = "Configuración de licencia" +noInput = "Proporciona una clave o archivo de licencia" [admin.settings.premium.licenseKey] toggle = "ĀæTiene una clave de licencia o un archivo de certificado?" @@ -4511,6 +4620,25 @@ line1 = "Sobrescribir su clave de licencia actual no se puede deshacer." line2 = "Su licencia anterior se perderĆ” de forma permanente a menos que la haya respaldado en otro lugar." line3 = "Importante: mantenga las claves de licencia privadas y seguras. Nunca las comparta pĆŗblicamente." +[admin.settings.premium.inputMethod] +text = "Clave de licencia" +file = "Archivo de certificado" + +[admin.settings.premium.file] +label = "Archivo de certificado de licencia" +description = "Sube tu archivo de licencia .lic o .cert de compras sin conexión" +choose = "Elegir archivo de licencia" +selected = "Seleccionado: {{filename}} ({{size}})" +successMessage = "Archivo de licencia subido y activado correctamente. No es necesario reiniciar." + +[admin.settings.premium.currentLicense] +title = "Licencia activa" +file = "Origen: Archivo de licencia ({{path}})" +key = "Origen: Clave de licencia" +type = "Tipo: {{type}}" +noInput = "Proporciona una clave de licencia o sube un archivo de certificado" +success = "Ɖxito" + [admin.settings.premium.enabled] label = "Habilitar funciones Premium" description = "Habilitar la verificación de la clave de licencia para funciones pro/enterprise" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} seleccionados" download = "Descargar" delete = "Borrar" unsupported = "No Soportado" +active = "Activo" addToUpload = "AƱadir a la subida" +closeFile = "Cerrar archivo" deleteAll = "Eliminar todo" loadingFiles = "Cargando archivos..." noFiles = "No hay archivos disponibles" @@ -5132,7 +5262,7 @@ upgrade = "Actualizar ahora →" freeTitle = "Licencia del servidor" overLimitTitle = "Se necesita licencia de servidor" overLimitBody = "Nuestra licencia permite hasta {{freeTierLimit}} usuarios gratis por servidor. Tiene {{overLimitUserCopy}} usuarios de Stirling. Para continuar sin interrupciones, actualice al plan Stirling Server: plazas ilimitadas, edición de texto PDF y control total de administración por 99 $/servidor/mes." -freeBody = "Nuestra licencia Open-Core permite hasta {{freeTierLimit}} usuarios gratis por servidor. Para escalar sin interrupciones y obtener acceso anticipado a nuestra nueva herramienta de edición de texto PDF, recomendamos el plan Stirling Server: edición completa y plazas ilimitadas por 99 $/servidor/mes." +freeBody = "Nuestra licencia Open-Core permite hasta {{freeTierLimit}} usuarios gratis por servidor. Para escalar sin interrupciones, recomendamos el plan Stirling Server - plazas ilimitadas y soporte SSO por $99/servidor/mes." [onboarding.desktopInstall] title = "Descargar" @@ -5204,7 +5334,7 @@ user = "Usuario" [workspace.people.addMember] title = "AƱadir miembro" username = "Nombre de usuario (correo)" -usernamePlaceholder = "user@example.com" +usernamePlaceholder = "usuario@ejemplo.com" password = "ContraseƱa" passwordPlaceholder = "Introduce la contraseƱa" role = "Rol" @@ -5237,15 +5367,40 @@ error = "No se pudo actualizar el estado del usuario" success = "Usuario eliminado correctamente" error = "No se pudo eliminar el usuario" +[workspace.people.changePassword] +action = "Cambiar contraseƱa" +title = "Cambiar contraseƱa" +subtitle = "Actualizar la contraseƱa de" +newPassword = "Nueva contraseƱa" +confirmPassword = "Confirmar contraseƱa" +placeholder = "Introduzca una nueva contraseƱa" +confirmPlaceholder = "Vuelva a introducir la nueva contraseƱa" +passwordRequired = "Introduzca una nueva contraseƱa" +passwordMismatch = "Las contraseƱas no coinciden" +generateRandom = "Generar contraseƱa segura" +generatedPreview = "ContraseƱa generada:" +copyTooltip = "Copiar al portapapeles" +copiedToClipboard = "ContraseƱa copiada al portapapeles" +copyFailed = "Error al copiar la contraseƱa" +sendEmail = "Enviar un correo al usuario sobre este cambio" +includePassword = "Incluir la nueva contraseƱa en el correo" +forcePasswordChange = "Obligar al usuario a cambiar la contraseƱa en el próximo inicio de sesión" +emailUnavailable = "El correo de este usuario no es una dirección vĆ”lida. Las notificaciones estĆ”n desactivadas." +smtpDisabled = "Las notificaciones por correo requieren que SMTP estĆ© habilitado en la configuración." +notifyOnly = "Se enviarĆ” un correo sin la contraseƱa, informando al usuario de que un administrador la cambió." +submit = "Actualizar contraseƱa" +success = "ContraseƱa actualizada correctamente" +error = "No se pudo actualizar la contraseƱa" + [workspace.people.emailInvite] tab = "Invitación por correo electrónico" description = "Escribe o pega correos a continuación, separados por comas. Los usuarios recibirĆ”n credenciales de inicio de sesión por correo electrónico." emails = "Direcciones de correo electrónico" -emailsPlaceholder = "user1@example.com, user2@example.com" +emailsPlaceholder = "usuario1@ejemplo.com, usuario2@ejemplo.com" emailsRequired = "Se requiere al menos una dirección de correo electrónico" submit = "Enviar invitaciones" success = "usuario(s) invitado(s) correctamente" -partialSuccess = "Algunas invitaciones fallaron" +partialFailure = "Algunas invitaciones fallaron" allFailed = "No se pudo invitar a los usuarios" error = "No se pudieron enviar las invitaciones" @@ -5541,7 +5696,7 @@ emailInvalid = "Introduzca una dirección de correo vĆ”lida" title = "Introduzca su correo electrónico" description = "Lo usaremos para enviar su clave de licencia y recibos." emailLabel = "Dirección de correo electrónico" -emailPlaceholder = "your@email.com" +emailPlaceholder = "su@email.com" continue = "Continuar" modalTitle = "Comenzar - {{planName}}" @@ -5770,6 +5925,7 @@ subtitle = "Inicie sesión con su cuenta de Stirling" [setup.selfhosted] title = "Inicie sesión en el servidor" subtitle = "Introduzca las credenciales de su servidor" +link = "o conectarse a una cuenta autoalojada" [setup.server] title = "Conectar con el servidor" @@ -5788,6 +5944,14 @@ description = "Introduzca la URL completa de su servidor autoalojado de Stirling emptyUrl = "Introduzca una URL de servidor" unreachable = "No se pudo conectar con el servidor" testFailed = "Falló la prueba de conexión" +configFetch = "No se pudo obtener la configuración del servidor. Compruebe la URL e intĆ©ntelo de nuevo." + +[setup.server.error.securityDisabled] +title = "Inicio de sesión no habilitado" +body = "Este servidor no tiene habilitado el inicio de sesión. Para conectarse a este servidor, debe habilitar la autenticación:" +step1 = "Establezca DOCKER_ENABLE_SECURITY=true en su entorno" +step2 = "O establezca security.enableLogin=true en settings.yml" +step3 = "Reinicie el servidor" [setup.login] title = "Iniciar sesión" @@ -5797,13 +5961,20 @@ submit = "Iniciar sesión" signInWith = "Iniciar sesión con" oauthPending = "Abriendo el navegador para autenticación..." orContinueWith = "O continuar con email" +serverRequirement = "Nota: el servidor debe tener el inicio de sesión habilitado." +showInstructions = "ĀæCómo habilitarlo?" +hideInstructions = "Ocultar instrucciones" +instructions = "Para habilitar el inicio de sesión en su servidor de Stirling PDF:" +instructionsEnvVar = "Establezca la variable de entorno:" +instructionsOrYml = "O en settings.yml:" +instructionsRestart = "Luego reinicie su servidor para que los cambios surtan efecto." [setup.login.username] label = "Nombre de usuario" placeholder = "Introduzca su nombre de usuario" [setup.login.email] -label = "Email" +label = "Correo electrónico" placeholder = "Introduzca su email" [setup.login.password] @@ -5840,7 +6011,7 @@ paragraph = "PĆ”gina de pĆ”rrafos" sparse = "Texto disperso" [pdfTextEditor.groupingMode] -auto = "Auto" +auto = "AutomĆ”tico" paragraph = "PĆ”rrafo" singleLine = "LĆ­nea Ćŗnica" @@ -5853,6 +6024,7 @@ earlyAccess = "Acceso anticipado" reset = "Restablecer cambios" downloadJson = "Descargar JSON" generatePdf = "Generar PDF" +saveChanges = "Guardar cambios" [pdfTextEditor.options.autoScaleText] title = "Escalar texto automĆ”ticamente para ajustar a las cajas" @@ -5890,6 +6062,8 @@ alpha = "Este visor alfa sigue evolucionando: ciertas fuentes, colores, efectos [pdfTextEditor.empty] title = "NingĆŗn documento cargado" subtitle = "Carga un archivo PDF o JSON para empezar a editar el contenido de texto." +dropzone = "Arrastre y suelte un archivo PDF o JSON aquĆ­, o haga clic para explorar" +dropzoneWithFiles = "Seleccione un archivo de la pestaƱa Archivos, o arrastre y suelte un archivo PDF o JSON aquĆ­, o haga clic para explorar" [pdfTextEditor.welcomeBanner] title = "Bienvenido a PDF Text Editor (Acceso anticipado)" diff --git a/frontend/public/locales/eu-ES/translation.toml b/frontend/public/locales/eu-ES/translation.toml index 89a7dddd55..154689c174 100644 --- a/frontend/public/locales/eu-ES/translation.toml +++ b/frontend/public/locales/eu-ES/translation.toml @@ -99,7 +99,7 @@ visitGithub = "Bisitatu Github biltegia" donate = "Dohaintza egin" color = "Color" sponsor = "Babestu" -info = "Info" +info = "Informazioa" pro = "Pro" page = "Orrialdea" pages = "Orrialdeak" @@ -131,7 +131,7 @@ unsupported = "Ez da onartzen" [toolPanel] placeholder = "Aukeratu tresna bat hasteko" -alpha = "Alpha" +alpha = "Alfa" premiumFeature = "Premium ezaugarria:" comingSoon = "Laster eskuragarri:" @@ -163,6 +163,11 @@ unfavorite = "Kendu gogokoetatik" fullscreen = "Aldatu pantaila osoko modura" sidebar = "Aldatu alboko barra modura" +[backendStartup] +notFoundTitle = "Backend-a ez da aurkitu" +retry = "Saiatu berriro" +unreachable = "Aplikazioak une honetan ezin du backend-arekin konektatu. Egiaztatu backend-aren egoera eta sare-konexioa, eta saiatu berriro." + [zipWarning] title = "ZIP fitxategi handia" message = "ZIP honek {{count}} fitxategi ditu. Erauzi hala ere?" @@ -274,7 +279,7 @@ iAgreeToThe = "Onartzen ditut honako hauek guztiak" terms = "Baldintzak eta erabilera-baldintzak" accessibility = "Irisgarritasuna" cookie = "Cookie politika" -impressum = "Impressum" +impressum = "Lege oharra" showCookieBanner = "Cookie-hobespenak" [pipeline] @@ -296,7 +301,7 @@ saveSettings = "Gorde eragiketa-ezarpenak" pipelineNamePrompt = "Sartu hemen pipeline izena" selectOperation = "Aukeratu eragiketa" addOperationButton = "Gehitu eragiketa" -pipelineHeader = "Pipeline:" +pipelineHeader = "Pipelinea:" saveButton = "Distira" validateButton = "Balidatu" @@ -347,7 +352,7 @@ teams = "Taldeak" title = "Konfigurazioa" systemSettings = "Sistemaren ezarpenak" features = "Eginbideak" -endpoints = "Endpoints" +endpoints = "Amaiera-puntuak" database = "Datu-basea" advanced = "Aurreratua" @@ -364,7 +369,7 @@ usageAnalytics = "Erabilera-analitika" [settings.policiesPrivacy] title = "Politikak eta Pribatutasuna" -legal = "Legal" +legal = "Lege" privacy = "Pribatutasuna" [settings.developer] @@ -513,7 +518,7 @@ syncToAccount = "Sync Kontua <- Nabigatzailea" [adminUserSettings] title = "Erabiltzailearen Ezarpenen Kontrolak" header = "Admin Erabiltzailearen Ezarpenen Kontrolak" -admin = "Admin" +admin = "Administratzailea" user = "Erabiltzaile" addUser = "Erabiltzaile berria" deleteUser = "Ezabatu erabiltzailea" @@ -912,6 +917,9 @@ desc = "Eraiki hainbat pausotako workflowak PDF ekintzak kateatuz. Egokia zeregi desc = "Overlays PDFs on-top of another PDF" title = "Gainjarri PDFak" +[home.pdfTextEditor] +title = "PDF testu editorea" +desc = "Editatu PDFetako lehendik dauden testuak eta irudiak" [home.addText] tags = "testua,anotazioa,etiketa" @@ -1217,7 +1225,7 @@ odtExt = "OpenDocument testua (.odt)" pptExt = "PowerPoint (.pptx)" odpExt = "OpenDocument aurkezpena (.odp)" txtExt = "Testu laua (.txt)" -rtfExt = "Rich Text Format (.rtf)" +rtfExt = "Testu aberatsaren formatua (.rtf)" selectedFiles = "Hautatutako fitxategiak" noFileSelected = "Ez da fitxategirik hautatu. Erabili fitxategi-panela fitxategiak gehitzeko." convertFiles = "Bihurtu fitxategiak" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Marrazketa sinadura" defaultImageLabel = "Igotako sinadura" defaultTextLabel = "Idatzitako sinadura" saveButton = "Gorde sinadura" +savePersonal = "Gorde pertsonala" +saveShared = "Gorde partekatua" saveUnavailable = "Lehenik sortu sinadura bat gordetzeko." noChanges = "Uneko sinadura dagoeneko gorde da." +tempStorageTitle = "Aldi baterako nabigatzaileko biltegiratzea" +tempStorageDescription = "Sinadurak zure nabigatzailean bakarrik gordetzen dira. Nabigatzailearen datuak ezabatzen badituzu edo nabigatzailea aldatzen baduzu, galdu egingo dira." +personalHeading = "Sinadura pertsonalak" +sharedHeading = "Partekatutako sinadurak" +personalDescription = "Zuk bakarrik ikus ditzakezu sinadura hauek." +sharedDescription = "Erabiltzaile guztiek ikus eta erabil ditzakete sinadura hauek." [sign.saved.type] canvas = "Marrazkia" @@ -3020,6 +3036,91 @@ title = "Lortu informazioa PDFn" header = "Lortu informazioa PDFn" submit = "Lortu informazioa" downloadJson = "Deskargatu JSON" +processing = "Informazioa erauzten..." +results = "Emaitzak" +noResults = "Exekutatu tresna txosten bat sortzeko." +downloads = "Deskargak" +noneDetected = "Ez da ezer detektatu" +indexTitle = "Indizea" + +[getPdfInfo.report] +entryLabel = "Informazio osoaren laburpena" +shortTitle = "PDFren informazioa" + +[getPdfInfo.sections] +metadata = "Metadatuak" +formFields = "Inprimaki-eremuak" +basicInfo = "Oinarrizko informazioa" +documentInfo = "Dokumentuaren informazioa" +compliance = "Arauen betetzea" +encryption = "Zifratzea" +permissions = "Baimenak" +other = "Bestelakoak" +perPageInfo = "Orrialdeko informazioa" +tableOfContents = "Aurkibidea" + +[getPdfInfo.other] +attachments = "Eranskinak" +embeddedFiles = "Txertatutako fitxategiak" +javaScript = "JavaScript" +layers = "Geruzak" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Tamaina" +annotations = "Anotazioak" +images = "Irudiak" +links = "Estekak" +fonts = "Letra-tipoak" +xobjects = "XObject kopuruak" +multimedia = "Multimedia" + +[getPdfInfo.summary] +pages = "Orriak" +fileSize = "Fitxategi-tamaina" +pdfVersion = "PDF bertsioa" +language = "Hizkuntza" +title = "PDFren laburpena" +author = "Egilea" +created = "Sortua" +modified = "Aldatua" +permsAll = "Baimen guztiak baimenduta" +permsRestricted = "{{count}} murrizketa" +permsMixed = "Zenbait baimen murriztuta" +hasCompliance = "Betetze-estandarrak ditu" +noCompliance = "Ez dago betetze-estandarrik" +basic = "Oinarrizko informazioa" +documentInfo = "Dokumentuaren informazioa" +securityTitle = "Segurtasun-egoera" +technical = "Teknikoa" +overviewTitle = "PDFren ikuspegi orokorra" + +[getPdfInfo.summary.security] +encrypted = "Zifratutako PDFa - Pasahitz-babesa dago" +unencrypted = "Zifratu gabeko PDFa - Ez dago pasahitz-babesik" + +[getPdfInfo.summary.tech] +images = "Irudiak" +fonts = "Letra-tipoak" +formFields = "Inprimaki-eremuak" +embeddedFiles = "Txertatutako fitxategiak" +javaScript = "JavaScript" +layers = "Geruzak" +bookmarks = "Laster-markak" +multimedia = "Multimedia" + +[getPdfInfo.summary.overview] +untitled = "izenbururik gabeko dokumentu bat" +unknown = "Egile ezezaguna" +text = "Hau {{pages}} orrialdeko PDF bat da; izenburua: {{title}}, egilea: {{author}} (PDF bertsioa: {{version}})." + +[getPdfInfo.error] +partial = "Fitxategi batzuk ezin izan dira prozesatu." +unexpected = "Ustekabeko errorea erauzketan zehar." + +[getPdfInfo.status] +complete = "Erau zketa amaituta" [extractPage] tags = "erauzi" @@ -3438,6 +3539,9 @@ signinTitle = "Mesedez, hasi saioa" ssoSignIn = "Hasi saioa Saioa hasteko modu bakarraren bidez" oAuth2AutoCreateDisabled = "OAUTH2 Sortu automatikoki erabiltzailea desgaituta dago" oAuth2AdminBlockedUser = "Erregistratu gabeko erabiltzaileen erregistroa edo saio-hasiera une honetan blokeatuta dago. Jarri harremanetan administratzailearekin." +oAuth2RequiresLicense = "OAuth/SSO bidezko saio-hasierak lizentzia ordaindua behar du (Server edo Enterprise). Mesedez, jarri harremanetan administratzailearekin plana eguneratzeko." +saml2RequiresLicense = "SAML bidezko saio-hasierak lizentzia ordaindua behar du (Server edo Enterprise). Mesedez, jarri harremanetan administratzailearekin plana eguneratzeko." +maxUsersReached = "Zure uneko lizentziarekin erabiltzaile kopuru maximoa gainditu da. Mesedez, jarri harremanetan administratzailearekin plana eguneratzeko edo eserleku gehiago gehitzeko." oauth2RequestNotFound = "Baimen-eskaera ez da aurkitu" oauth2InvalidUserInfoResponse = "Erabiltzaile-informazioaren erantzun baliogabea" oauth2invalidRequest = "Eskaera baliogabea" @@ -3533,7 +3637,7 @@ title = "PDF Orrialde bakarrera" header = "PDF Orrialde bakarrera" submit = "Orrialde bakarrera bihurtu" description = "Tresna honek zure PDFko orri guztiak orri handi bakarrean batuko ditu. Zabalera bera izango du jatorrizko orrienarekin, baina altuera orri guztien altueren batura izango da." -filenamePrefix = "single_page" +filenamePrefix = "orrialde_bakarra" [pdfToSinglePage.files] placeholder = "Hautatu PDF fitxategi bat ikuspegi nagusian hasteko" @@ -3846,14 +3950,17 @@ fitToWidth = "Zabalera egokitu" actualSize = "Benetako tamaina" [viewer] +cannotPreviewFile = "Ezin da fitxategia aurreikusi" +dualPageView = "Orri biko ikuspegia" firstPage = "Lehen orria" lastPage = "Azken orria" -previousPage = "Aurreko orria" nextPage = "Hurrengo orria" +onlyPdfSupported = "Ikustaileak PDF fitxategiak bakarrik onartzen ditu. Fitxategi honek beste formatu batekoa dirudi." +previousPage = "Aurreko orria" +singlePageView = "Orri bakarreko ikuspegia" +unknownFile = "Fitxategi ezezaguna" zoomIn = "Zoom handitu" zoomOut = "Zoom txikitu" -singlePageView = "Orri bakarreko ikuspegia" -dualPageView = "Orri biko ikuspegia" [rightRail] closeSelected = "Itxi hautatutako fitxategiak" @@ -3877,6 +3984,7 @@ toggleSidebar = "Alboko barra txandakatu" exportSelected = "Esportatu hautatutako orriak" toggleAnnotations = "Oharpenen ikusgarritasuna txandakatu" annotationMode = "Oharpen modua txandakatu" +print = "Inprimatu PDFa" draw = "Marraztu" save = "Gorde" saveChanges = "Aldaketak gorde" @@ -4407,7 +4515,7 @@ description = "Sistema zabalagoko aldi baterako direktorioa garbitu ala ez (kont label = "Prozesu-exekutorearen mugak" description = "Konfiguratu saio-mugak eta denbora-mugak prozesu-exekutore bakoitzerako" libreOffice = "LibreOffice" -pdfToHtml = "PDF to HTML" +pdfToHtml = "PDFtik HTMLra" qpdf = "QPDF" tesseract = "Tesseract OCR" pythonOpenCv = "Python OpenCV" @@ -4487,13 +4595,14 @@ label = "Cookieen politika" description = "Cookieen politikara doan URLa edo fitxategi-izena" [admin.settings.legal.impressum] -label = "Impressum" +label = "Lege oharra" description = "Impressum-era doan URLa edo fitxategi-izena (beharrezkoa jurisdikzio batzuetan)" [admin.settings.premium] title = "Premium eta Enterprise" description = "Konfiguratu zure premium edo enterprise lizentzia-gakoa." license = "Lizentziaren konfigurazioa" +noInput = "Eman lizentzia-gakoa edo fitxategia, mesedez" [admin.settings.premium.licenseKey] toggle = "Lizentzia-gakoa edo ziurtagiri-fitxategia duzu?" @@ -4511,6 +4620,25 @@ line1 = "Uneko lizentzia-gakoa gainidaztea ezin da desegin." line2 = "Aurreko lizentzia betiko galduko da beste nonbait babestu ezean." line3 = "Garrantzitsua: Mantendu lizentzia-gakoak pribatu eta seguru. Ez partekatu publikoki inoiz." +[admin.settings.premium.inputMethod] +text = "Lizentzia-gakoa" +file = "Ziurtagiri-fitxategia" + +[admin.settings.premium.file] +label = "Lizentzia-ziurtagiriaren fitxategia" +description = "Igo zure .lic edo .cert lizentzia-fitxategia lineaz kanpoko erosketetatik" +choose = "Aukeratu lizentzia-fitxategia" +selected = "Hautatuta: {{filename}} ({{size}})" +successMessage = "Lizentzia-fitxategia behar bezala igo eta aktibatu da. Ez da berrabiaraztea beharrezkoa." + +[admin.settings.premium.currentLicense] +title = "Lizentzia aktiboa" +file = "Iturburua: Lizentzia-fitxategia ({{path}})" +key = "Iturburua: Lizentzia-gakoa" +type = "Mota: {{type}}" +noInput = "Eman lizentzia-gakoa edo igo ziurtagiri-fitxategi bat, mesedez" +success = "Arrakasta" + [admin.settings.premium.enabled] label = "Premium eginbideak gaitu" description = "Gaitu lizentzia-gakoen egiaztapenak pro/enterprise eginbideetarako" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} hautatuta" download = "Distira" delete = "ezabatu" unsupported = "Ez da onartzen" +active = "Aktibo" addToUpload = "Gehitu igoerara" +closeFile = "Itxi fitxategia" deleteAll = "Ezabatu denak" loadingFiles = "Fitxategiak kargatzen..." noFiles = "Ez dago fitxategirik eskuragarri" @@ -5132,7 +5262,7 @@ upgrade = "Eguneratu orain →" freeTitle = "Zerbitzari-lizentzia" overLimitTitle = "Beharrezkoa da zerbitzari-lizentzia" overLimitBody = "Gure lizentziak baimentzen ditu {{freeTierLimit}} erabiltzaile doan zerbitzari bakoitzeko. {{overLimitUserCopy}} Stirling erabiltzaile dituzu. Jarraitzeko etenik gabe, eguneratu Stirling Server planera - eserleku mugagabeak, PDF testu-edizioa, eta admin kontrol osoa $99/zerbitzari/hilean." -freeBody = "Gure Open-Core lizentziak {{freeTierLimit}} erabiltzaile arte baimentzen ditu doan zerbitzari bakoitzeko. Etenik gabe eskalatzeko eta gure PDF testu-edizio tresna berrirako sarbide goiztiarra lortzeko, gomendatzen dugu Stirling Server plana - edizio osoa eta eserleku mugagabeak $99/zerbitzari/hilean." +freeBody = "Gure Open-Core lizentziak zerbitzari bakoitzeko doan gehienez {{freeTierLimit}} erabiltzaile baimentzen ditu. Etenik gabe eskalatzeko, Stirling Server plana gomendatzen dugu - eserleku mugagabeak eta SSO euskarria $99/server/mo." [onboarding.desktopInstall] title = "Deskargatu" @@ -5178,7 +5308,7 @@ active = "Aktibo" disabled = "Desgaituta" activeSession = "Saio aktiboa" member = "Kidea" -admin = "Admin" +admin = "Administratzailea" editRole = "Rola editatu" enable = "Gaitu" disable = "Desgaitu" @@ -5237,6 +5367,31 @@ error = "Ezin izan da erabiltzailearen egoera eguneratu" success = "Erabiltzailea ongi ezabatu da" error = "Ezin izan da erabiltzailea ezabatu" +[workspace.people.changePassword] +action = "Pasahitza aldatu" +title = "Pasahitza aldatu" +subtitle = "Honetarako pasahitza eguneratu" +newPassword = "Pasahitz berria" +confirmPassword = "Berretsi pasahitza" +placeholder = "Sartu pasahitz berria" +confirmPlaceholder = "Sartu berriro pasahitz berria" +passwordRequired = "Sartu pasahitz berria" +passwordMismatch = "Pasahitzak ez datoz bat" +generateRandom = "Sortu pasahitz segurua" +generatedPreview = "Sortutako pasahitza:" +copyTooltip = "Kopiatu arbelera" +copiedToClipboard = "Pasahitza arbelera kopiatu da" +copyFailed = "Pasahitza kopiatzeak huts egin du" +sendEmail = "Bidali mezu elektronikoa erabiltzaileari aldaketa honi buruz" +includePassword = "Sartu pasahitz berria mezu elektronikoan" +forcePasswordChange = "Behartu erabiltzailea hurrengo saio-hasieran pasahitza aldatzera" +emailUnavailable = "Erabiltzaile honen helbide elektronikoa ez da baliozkoa. Jakinarazpenak desgaituta daude." +smtpDisabled = "Posta elektroniko bidezko jakinarazpenek SMTP gaituta egotea eskatzen dute ezarpenetan." +notifyOnly = "Pasahitzik gabe bidaliko da mezu elektronikoa; erabiltzaileari jakinaraziko zaio administratzaile batek aldatu duela." +submit = "Eguneratu pasahitza" +success = "Pasahitza ongi eguneratu da" +error = "Pasahitza eguneratzeak huts egin du" + [workspace.people.emailInvite] tab = "E-posta bidezko gonbidapena" description = "Idatzi edo itsatsi behean helbide elektronikoak, komaz bereizita. Erabiltzaileek saio-hasierako kredentzialak e-postaz jasoko dituzte." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Gutxienez helbide elektroniko bat behar da" submit = "Bidali gonbidapenak" success = "erabiltzaile(a)(k) ongi gonbidatu dira" -partialSuccess = "Gonbidapen batzuek huts egin dute" +partialFailure = "Gonbidapen batzuk huts egin dute" allFailed = "Ezin izan da erabiltzaileak gonbidatu" error = "Ezin izan dira gonbidapenak bidali" @@ -5709,7 +5864,7 @@ title = "Endpoints erabileraren diagrama" [usage.table] title = "Estatistika xeheak" -endpoint = "Endpoint" +endpoint = "Amaiera-puntua" visits = "Bisitak" percentage = "Ehunekoa" noData = "Ez dago daturik eskuragarri" @@ -5770,6 +5925,7 @@ subtitle = "Hasi saioa zure Stirling kontuarekin" [setup.selfhosted] title = "Hasi saioa zerbitzarian" subtitle = "Sartu zure zerbitzariaren kredentzialak" +link = "edo konektatu autoostatutako kontu batera" [setup.server] title = "Konektatu zerbitzarira" @@ -5788,6 +5944,14 @@ description = "Sartu zure auto-ostatuko Stirling PDF zerbitzariaren URLa osoa" emptyUrl = "Sartu zerbitzari baten URLa" unreachable = "Ezin izan da zerbitzarira konektatu" testFailed = "Konexio proba huts egin du" +configFetch = "Ezin izan da zerbitzariaren konfigurazioa eskuratu. Egiaztatu URLa eta saiatu berriro." + +[setup.server.error.securityDisabled] +title = "Saio-hasiera ez dago gaituta" +body = "Zerbitzari honek ez du saio-hasiera gaituta. Zerbitzari honekin konektatzeko, autentifikazioa gaitu behar duzu:" +step1 = "Ezarri DOCKER_ENABLE_SECURITY=true zure ingurunean" +step2 = "Edo ezarri security.enableLogin=true settings.yml fitxategian" +step3 = "Berrabiarazi zerbitzaria" [setup.login] title = "Hasi saioa" @@ -5797,6 +5961,13 @@ submit = "Hasi saioa" signInWith = "Hasi saioa honekin" oauthPending = "Nabigatzailea irekitzen autentifikaziorako..." orContinueWith = "Edo jarraitu emailarekin" +serverRequirement = "Oharra: zerbitzariak saioa hastea gaituta eduki behar du." +showInstructions = "Nola gaitu?" +hideInstructions = "Ezkutatu argibideak" +instructions = "Saioa hastea gaitzeko zure Stirling PDF zerbitzarian:" +instructionsEnvVar = "Ezarri ingurune-aldagaia:" +instructionsOrYml = "Edo settings.yml fitxategian:" +instructionsRestart = "Ondoren, berrabiarazi zerbitzaria aldaketak indarrean sartzeko." [setup.login.username] label = "Erabiltzaile-izena" @@ -5840,7 +6011,7 @@ paragraph = "Paragrafo orria" sparse = "Testu sakabanatua" [pdfTextEditor.groupingMode] -auto = "Auto" +auto = "Automatikoa" paragraph = "Paragrafoa" singleLine = "Lerro bakarra" @@ -5853,6 +6024,7 @@ earlyAccess = "Sarbide goiztiarra" reset = "Aldaketak berrezarri" downloadJson = "JSON deskargatu" generatePdf = "PDF sortu" +saveChanges = "Gorde aldaketak" [pdfTextEditor.options.autoScaleText] title = "Testua automatikoki eskalatu kutxetara egokitzeko" @@ -5890,6 +6062,8 @@ alpha = "Ikusle alfa hau oraindik eboluzioan dago—zenbait letra-tipo, kolore, [pdfTextEditor.empty] title = "Ez da dokumenturik kargatu" subtitle = "Kargatu PDF edo JSON fitxategi bat testu-edukia editatzen hasteko." +dropzone = "Arrastatu eta jaregin PDF edo JSON fitxategi bat hemen, edo egin klik arakatzeko" +dropzoneWithFiles = "Hautatu fitxategi bat Fitxategiak fitxatik, edo arrastatu eta jaregin PDF edo JSON fitxategi bat hemen, edo egin klik arakatzeko" [pdfTextEditor.welcomeBanner] title = "Ongi etorri PDF Text Editor-era (Sarbide goiztiarra)" diff --git a/frontend/public/locales/fa-IR/translation.toml b/frontend/public/locales/fa-IR/translation.toml index f2cb9bd911..5e363a22a7 100644 --- a/frontend/public/locales/fa-IR/translation.toml +++ b/frontend/public/locales/fa-IR/translation.toml @@ -163,6 +163,11 @@ unfavorite = "حذف Ų§Ų² Ų¹Ł„Ų§Ł‚Ł‡ā€ŒŁ…Ł†ŲÆŪŒā€ŒŁ‡Ų§" fullscreen = "تغییر به حالت ŲŖŁ…Ų§Ł…ā€ŒŲµŁŲ­Ł‡" sidebar = "تغییر به حالت Ł†ŁˆŲ§Ų± Ś©Ł†Ų§Ų±ŪŒ" +[backendStartup] +notFoundTitle = "ŲØŚ©ā€ŒŲ§Ł†ŲÆ یافت نؓد" +retry = "تلاؓ Ł…Ų¬ŲÆŲÆ" +unreachable = "برنامه ŲÆŲ± Ų­Ų§Ł„ Ų­Ų§Ų¶Ų± Ł†Ł…ŪŒā€ŒŲŖŁˆŲ§Ł†ŲÆ به ŲØŚ©ā€ŒŲ§Ł†ŲÆ متصل ؓود. وضعیت ŲØŚ©ā€ŒŲ§Ł†ŲÆ و Ų§ŲŖŲµŲ§Ł„ ؓبکه Ų±Ų§ بررسی کرده و سپس ŲÆŁˆŲØŲ§Ų±Ł‡ تلاؓ Ś©Ł†ŪŒŲÆ." + [zipWarning] title = "ŁŲ§ŪŒŁ„ ZIP بزرگ" message = "Ų§ŪŒŁ† ZIP Ų“Ų§Ł…Ł„ {{count}} ŁŲ§ŪŒŁ„ Ų§Ų³ŲŖ. ŲØŲ§ Ų§ŪŒŁ† Ų­Ų§Ł„ Ų§Ų³ŲŖŲ®Ų±Ų§Ų¬ ؓود؟" @@ -912,6 +917,9 @@ desc = "Ų³Ų§Ų®ŲŖ ŚÆŲ±ŲÆŲ“ā€ŒŚ©Ų§Ų±Ł‡Ų§ŪŒ Ś†Ł†ŲÆŁ…Ų±Ų­Ł„Ł‡ā€ŒŲ§ŪŒ ŲØŲ§ Ų²Ł†Ų¬ŪŒŲ± desc = "PDFā€ŒŁ‡Ų§ Ų±Ų§ ŲØŲ± روی PDF دیگری Ł‡Ł…ā€ŒŁ¾ŁˆŲ“Ų§Ł†ŪŒ Ł…ŪŒā€ŒŚ©Ł†ŲÆ" title = "Ł‡Ł…ā€ŒŁ¾ŁˆŲ“Ų§Ł†ŪŒ PDFā€ŒŁ‡Ų§" +[home.pdfTextEditor] +title = "ویرایؓگر متن PDF" +desc = "ویرایؓ متن و تصاویر Ł…ŁˆŲ¬ŁˆŲÆ ŲÆŲ± PDFها" [home.addText] tags = "متن,Ų­Ų§Ų“ŪŒŁ‡ā€ŒŁ†ŁˆŪŒŲ³ŪŒ,برچسب" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Ų§Ł…Ų¶Ų§ŪŒ ŲŖŲ±Ų³ŪŒŁ…ŪŒ" defaultImageLabel = "Ų§Ł…Ų¶Ų§ŪŒ ŲØŲ§Ų±ŚÆŲ°Ų§Ų±ŪŒā€ŒŲ“ŲÆŁ‡" defaultTextLabel = "Ų§Ł…Ų¶Ų§ŪŒ تایپی" saveButton = "Ų°Ų®ŪŒŲ±Ł‡ Ų§Ł…Ų¶Ų§" +savePersonal = "Ų°Ų®ŪŒŲ±Ł‡ ؓخصی" +saveShared = "Ų°Ų®ŪŒŲ±Ł‡ اؓتراکی" saveUnavailable = "برای Ų°Ų®ŪŒŲ±Ł‡ŲŒ Ų§ŲØŲŖŲÆŲ§ Ų§Ł…Ų¶Ų§ŪŒŪŒ بسازید." noChanges = "Ų§Ł…Ų¶Ų§ŪŒ ŁŲ¹Ł„ŪŒ قبلاً Ų°Ų®ŪŒŲ±Ł‡ ؓده Ų§Ų³ŲŖ." +tempStorageTitle = "Ų°Ų®ŪŒŲ±Ł‡ā€ŒŲ³Ų§Ų²ŪŒ Ł…ŁˆŁ‚ŲŖ ŲÆŲ± Ł…Ų±ŁˆŲ±ŚÆŲ±" +tempStorageDescription = "امضاها فقط ŲÆŲ± Ł…Ų±ŁˆŲ±ŚÆŲ± Ų“Ł…Ų§ Ų°Ų®ŪŒŲ±Ł‡ Ł…ŪŒā€ŒŲ“ŁˆŁ†ŲÆ. ŲÆŲ± صورت Ł¾Ų§Ś©ā€ŒŚ©Ų±ŲÆŁ† ŲÆŲ§ŲÆŁ‡ā€ŒŁ‡Ų§ŪŒ Ł…Ų±ŁˆŲ±ŚÆŲ± یا تعویض Ł…Ų±ŁˆŲ±ŚÆŲ±ŲŒ Ų§Ų² ŲØŪŒŁ† Ł…ŪŒā€ŒŲ±ŁˆŁ†ŲÆ." +personalHeading = "Ų§Ł…Ų¶Ų§Ł‡Ų§ŪŒ ؓخصی" +sharedHeading = "Ų§Ł…Ų¶Ų§Ł‡Ų§ŪŒ اؓتراکی" +personalDescription = "تنها Ų“Ł…Ų§ Ł…ŪŒā€ŒŲŖŁˆŲ§Ł†ŪŒŲÆ Ų§ŪŒŁ† امضاها Ų±Ų§ ŲØŲØŪŒŁ†ŪŒŲÆ." +sharedDescription = "همه کاربران Ł…ŪŒā€ŒŲŖŁˆŲ§Ł†Ł†ŲÆ Ų§ŪŒŁ† امضاها Ų±Ų§ ŲØŲØŪŒŁ†Ł†ŲÆ و استفاده کنند." [sign.saved.type] canvas = "ŲŖŲ±Ų³ŪŒŁ…ŪŒ" @@ -3020,6 +3036,91 @@ title = "اطلاعات PDF Ų±Ų§ دریافت Ś©Ł†ŪŒŲÆ" header = "اطلاعات PDF Ų±Ų§ دریافت Ś©Ł†ŪŒŲÆ" submit = "دریافت اطلاعات" downloadJson = "ŲÆŲ§Ł†Ł„ŁˆŲÆ JSON" +processing = "ŲÆŲ± Ų­Ų§Ł„ Ų§Ų³ŲŖŲ®Ų±Ų§Ų¬ اطلاعات..." +results = "Ł†ŲŖŲ§ŪŒŲ¬" +noResults = "برای ایجاد گزارؓ، Ų§ŲØŲ²Ų§Ų± Ų±Ų§ Ų§Ų¬Ų±Ų§ Ś©Ł†ŪŒŲÆ." +downloads = "ŲÆŲ§Ł†Ł„ŁˆŲÆŁ‡Ų§" +noneDetected = "Ł‡ŪŒŚ† Ł…ŁˆŲ±ŲÆŪŒ Ų“Ł†Ų§Ų³Ų§ŪŒŪŒ نؓد" +indexTitle = "Ł†Ł…Ų§ŪŒŁ‡" + +[getPdfInfo.report] +entryLabel = "خلاصهٔ کامل اطلاعات" +shortTitle = "اطلاعات PDF" + +[getPdfInfo.sections] +metadata = "فراداده" +formFields = "ŁŪŒŁ„ŲÆŁ‡Ų§ŪŒ فرم" +basicInfo = "اطلاعات Ł¾Ų§ŪŒŁ‡" +documentInfo = "اطلاعات سند" +compliance = "انطباق" +encryption = "Ų±Ł…Ų²ŚÆŲ°Ų§Ų±ŪŒ" +permissions = "Ł…Ų¬ŁˆŲ²Ł‡Ų§" +other = "سایر" +perPageInfo = "اطلاعات هر صفحه" +tableOfContents = "فهرست مطالب" + +[getPdfInfo.other] +attachments = "Ł¾ŪŒŁˆŲ³ŲŖā€ŒŁ‡Ų§" +embeddedFiles = "ŁŲ§ŪŒŁ„ā€ŒŁ‡Ų§ŪŒ Ų¬Ų§Ų³Ų§Ų²ŪŒā€ŒŲ“ŲÆŁ‡" +javaScript = "JavaScript" +layers = "Ł„Ų§ŪŒŁ‡ā€ŒŁ‡Ų§" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "اندازه" +annotations = "Ų­Ų§Ų“ŪŒŁ‡ā€ŒŁ†ŁˆŪŒŲ³ŪŒā€ŒŁ‡Ų§" +images = "تصاویر" +links = "Ł¾ŪŒŁˆŁ†ŲÆŁ‡Ų§" +fonts = "ŁŁˆŁ†ŲŖā€ŒŁ‡Ų§" +xobjects = "ŲŖŲ¹ŲÆŲ§ŲÆ XObject" +multimedia = "Ś†Ł†ŲÆŲ±Ų³Ų§Ł†Ł‡ā€ŒŲ§ŪŒ" + +[getPdfInfo.summary] +pages = "صفحات" +fileSize = "حجم ŁŲ§ŪŒŁ„" +pdfVersion = "نسخهٔ PDF" +language = "زبان" +title = "خلاصهٔ PDF" +author = "Ł†ŁˆŪŒŲ³Ł†ŲÆŁ‡" +created = "ایجاد ؓده" +modified = "ویرایؓ ؓده" +permsAll = "همهٔ Ł…Ų¬ŁˆŲ²Ł‡Ų§ Ł…Ų¬Ų§Ų² هستند" +permsRestricted = "{{count}} Ł…Ų­ŲÆŁˆŲÆŪŒŲŖ" +permsMixed = "برخی Ł…Ų¬ŁˆŲ²Ł‡Ų§ Ł…Ų­ŲÆŁˆŲÆ Ų“ŲÆŁ‡ā€ŒŲ§Ł†ŲÆ" +hasCompliance = "دارای Ų§Ų³ŲŖŲ§Ł†ŲÆŲ§Ų±ŲÆŁ‡Ų§ŪŒ انطباق" +noCompliance = "ŲØŲÆŁˆŁ† Ų§Ų³ŲŖŲ§Ł†ŲÆŲ§Ų±ŲÆŁ‡Ų§ŪŒ انطباق" +basic = "اطلاعات Ł¾Ų§ŪŒŁ‡" +documentInfo = "اطلاعات سند" +securityTitle = "وضعیت Ų§Ł…Ł†ŪŒŲŖŪŒ" +technical = "ŁŁ†ŪŒ" +overviewTitle = "Ł†Ł…Ų§ŪŒ Ś©Ł„ŪŒ PDF" + +[getPdfInfo.summary.security] +encrypted = "PDF Ų±Ł…Ų²ŚÆŲ°Ų§Ų±ŪŒā€ŒŲ“ŲÆŁ‡ - دارای محافظت ŲØŲ§ ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡" +unencrypted = "PDF Ų±Ł…Ų²ŚÆŲ°Ų§Ų±ŪŒā€ŒŁ†Ų“ŲÆŁ‡ - ŲØŲÆŁˆŁ† محافظت ŲØŲ§ ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡" + +[getPdfInfo.summary.tech] +images = "تصاویر" +fonts = "ŁŁˆŁ†ŲŖā€ŒŁ‡Ų§" +formFields = "ŁŪŒŁ„ŲÆŁ‡Ų§ŪŒ فرم" +embeddedFiles = "ŁŲ§ŪŒŁ„ā€ŒŁ‡Ų§ŪŒ Ų¬Ų§Ų³Ų§Ų²ŪŒā€ŒŲ“ŲÆŁ‡" +javaScript = "JavaScript" +layers = "Ł„Ų§ŪŒŁ‡ā€ŒŁ‡Ų§" +bookmarks = "Ł†Ų“Ų§Ł†Ś©ā€ŒŁ‡Ų§" +multimedia = "Ś†Ł†ŲÆŲ±Ų³Ų§Ł†Ł‡ā€ŒŲ§ŪŒ" + +[getPdfInfo.summary.overview] +untitled = "یک سند ŲØŲÆŁˆŁ† Ų¹Ł†ŁˆŲ§Ł†" +unknown = "Ł†ŁˆŪŒŲ³Ł†ŲÆŁ‡Ł” نامؓخص" +text = "Ų§ŪŒŁ† یک PDF {{pages}} ŲµŁŲ­Ł‡ā€ŒŲ§ŪŒ ŲØŲ§ Ų¹Ł†ŁˆŲ§Ł† {{title}} Ų§Ų³ŲŖ که توسط {{author}} ایجاد ؓده Ų§Ų³ŲŖ (نسخهٔ PDF {{version}})." + +[getPdfInfo.error] +partial = "برخی ŁŲ§ŪŒŁ„ā€ŒŁ‡Ų§ قابل پردازؓ Ł†ŲØŁˆŲÆŁ†ŲÆ." +unexpected = "خطای ŲŗŪŒŲ±Ł…Ł†ŲŖŲøŲ±Ł‡ هنگام Ų§Ų³ŲŖŲ®Ų±Ų§Ų¬." + +[getPdfInfo.status] +complete = "Ų§Ų³ŲŖŲ®Ų±Ų§Ų¬ کامل Ų“ŲÆ" [extractPage] tags = "Ų§Ų³ŲŖŲ®Ų±Ų§Ų¬" @@ -3438,6 +3539,9 @@ signinTitle = "لطفاً وارد ؓوید" ssoSignIn = "ورود Ų§Ų² Ų·Ų±ŪŒŁ‚ Single Sign-on" oAuth2AutoCreateDisabled = "ایجاد خودکار کاربر ŲØŲ§ OAUTH2 ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ Ų§Ų³ŲŖ" oAuth2AdminBlockedUser = "Ų«ŲØŲŖā€ŒŁ†Ų§Ł… یا ورود کاربران Ų«ŲØŲŖā€ŒŁ†Ų“ŲÆŁ‡ ŲÆŲ± Ų­Ų§Ł„ Ų­Ų§Ų¶Ų± Ł…Ų³ŲÆŁˆŲÆ Ų§Ų³ŲŖ. لطفاً ŲØŲ§ Ł…ŲÆŪŒŲ± ŲŖŁ…Ų§Ų³ بگیرید." +oAuth2RequiresLicense = "ورود ŲØŲ§ OAuth/SSO به Ł„Ų§ŪŒŲ³Ł†Ų³ Ł¾ŁˆŁ„ŪŒ (Server یا Enterprise) Ł†ŪŒŲ§Ų² ŲÆŲ§Ų±ŲÆ. لطفاً برای Ų§Ų±ŲŖŁ‚Ų§ŪŒ Ų·Ų±Ų­ خود ŲØŲ§ Ł…ŲÆŪŒŲ± ŲŖŁ…Ų§Ų³ بگیرید." +saml2RequiresLicense = "ورود ŲØŲ§ SAML به Ł„Ų§ŪŒŲ³Ł†Ų³ Ł¾ŁˆŁ„ŪŒ (Server یا Enterprise) Ł†ŪŒŲ§Ų² ŲÆŲ§Ų±ŲÆ. لطفاً برای Ų§Ų±ŲŖŁ‚Ų§ŪŒ Ų·Ų±Ų­ خود ŲØŲ§ Ł…ŲÆŪŒŲ± ŲŖŁ…Ų§Ų³ بگیرید." +maxUsersReached = "حداکثر ŲŖŲ¹ŲÆŲ§ŲÆ کاربران برای Ł„Ų§ŪŒŲ³Ł†Ų³ Ś©Ł†ŁˆŁ†ŪŒ Ų“Ł…Ų§ به Ų­ŲÆ نصاب Ų±Ų³ŪŒŲÆŁ‡ Ų§Ų³ŲŖ. لطفاً برای Ų§Ų±ŲŖŁ‚Ų§ŪŒ Ų·Ų±Ų­ یا Ų§ŁŲ²ŁˆŲÆŁ† کاربران بیؓتر ŲØŲ§ Ł…ŲÆŪŒŲ± ŲŖŁ…Ų§Ų³ بگیرید." oauth2RequestNotFound = "درخواست Ų§Ų­Ų±Ų§Ų² Ł‡ŁˆŪŒŲŖ پیدا نؓد" oauth2InvalidUserInfoResponse = "پاسخ اطلاعات کاربری نامعتبر Ų§Ų³ŲŖ" oauth2invalidRequest = "درخواست نامعتبر" @@ -3846,14 +3950,17 @@ fitToWidth = "تناسب ŲØŲ§ Ų¹Ų±Ų¶" actualSize = "اندازه ŁˆŲ§Ł‚Ų¹ŪŒ" [viewer] +cannotPreviewFile = "امکان Ł¾ŪŒŲ“ā€ŒŁ†Ł…Ų§ŪŒŲ“ ŁŲ§ŪŒŁ„ Ł†ŪŒŲ³ŲŖ" +dualPageView = "Ł†Ł…Ų§ŪŒ ŲÆŁˆŲµŁŲ­Ł‡ā€ŒŲ§ŪŒ" firstPage = "صفحه نخست" lastPage = "صفحه Ų¢Ų®Ų±" -previousPage = "صفحه قبل" nextPage = "صفحه ŲØŲ¹ŲÆ" +onlyPdfSupported = "Ł†Ł…Ų§ŪŒŲ“ŚÆŲ± فقط ŁŲ§ŪŒŁ„ā€ŒŁ‡Ų§ŪŒ PDF Ų±Ų§ Ł¾Ų“ŲŖŪŒŲØŲ§Ł†ŪŒ Ł…ŪŒā€ŒŚ©Ł†ŲÆ. به نظر Ł…ŪŒā€ŒŲ±Ų³ŲÆ Ų§ŪŒŁ† ŁŲ§ŪŒŁ„ قالب Ł…ŲŖŁŲ§ŁˆŲŖŪŒ ŲÆŲ§Ų±ŲÆ." +previousPage = "صفحه قبل" +singlePageView = "Ł†Ł…Ų§ŪŒ ŲŖŚ©ā€ŒŲµŁŲ­Ł‡ā€ŒŲ§ŪŒ" +unknownFile = "ŁŲ§ŪŒŁ„ ناؓناخته" zoomIn = "ŲØŲ²Ų±ŚÆā€ŒŁ†Ł…Ų§ŪŒŪŒ" zoomOut = "Ś©ŁˆŚ†Ś©ā€ŒŁ†Ł…Ų§ŪŒŪŒ" -singlePageView = "Ł†Ł…Ų§ŪŒ ŲŖŚ©ā€ŒŲµŁŲ­Ł‡ā€ŒŲ§ŪŒ" -dualPageView = "Ł†Ł…Ų§ŪŒ ŲÆŁˆŲµŁŲ­Ł‡ā€ŒŲ§ŪŒ" [rightRail] closeSelected = "بستن ŁŲ§ŪŒŁ„ā€ŒŁ‡Ų§ŪŒ Ų§Ł†ŲŖŲ®Ų§ŲØā€ŒŲ“ŲÆŁ‡" @@ -3877,6 +3984,7 @@ toggleSidebar = "تغییر وضعیت Ł†ŁˆŲ§Ų± Ś©Ł†Ų§Ų±ŪŒ" exportSelected = "ŲØŲ±ŁˆŁ†ā€ŒŲØŲ±ŪŒ صفحات Ų§Ł†ŲŖŲ®Ų§ŲØā€ŒŲ“ŲÆŁ‡" toggleAnnotations = "تغییر وضعیت Ł†Ł…Ų§ŪŒŲ“ Ų­Ų§Ų“ŪŒŁ‡ā€ŒŁ†ŁˆŪŒŲ³ŪŒā€ŒŁ‡Ų§" annotationMode = "تغییر حالت Ų­Ų§Ų“ŪŒŁ‡ā€ŒŁ†ŁˆŪŒŲ³ŪŒ" +print = "چاپ PDF" draw = "رسم" save = "Ų°Ų®ŪŒŲ±Ł‡" saveChanges = "Ų°Ų®ŪŒŲ±Ł‡ تغییرات" @@ -4235,11 +4343,11 @@ label = "URL صادرکننده" description = "Issuer URL Ų§Ų±Ų§Ų¦Ł‡ā€ŒŲÆŁ‡Ł†ŲÆŁ‡ OAuth2" [admin.settings.connections.oauth2.clientId] -label = "Client ID" +label = "ؓناسهٔ Ś©Ł„Ų§ŪŒŁ†ŲŖ" description = "Client ID Ł…Ų±ŲØŁˆŲ· به OAuth2 Ų§Ų² Ų§Ų±Ų§Ų¦Ł‡ā€ŒŲÆŁ‡Ł†ŲÆŁ‡ Ų“Ł…Ų§" [admin.settings.connections.oauth2.clientSecret] -label = "Client Secret" +label = "Ų±Ų§Ų² Ś©Ł„Ų§ŪŒŁ†ŲŖ" description = "Client Secret Ł…Ų±ŲØŁˆŲ· به OAuth2 Ų§Ų² Ų§Ų±Ų§Ų¦Ł‡ā€ŒŲÆŁ‡Ł†ŲÆŁ‡ Ų“Ł…Ų§" [admin.settings.connections.oauth2.useAsUsername] @@ -4270,7 +4378,7 @@ label = "Ų§Ų±Ų§Ų¦Ł‡ā€ŒŲÆŁ‡Ł†ŲÆŁ‡" description = "نام Ų§Ų±Ų§Ų¦Ł‡ā€ŒŲÆŁ‡Ł†ŲÆŁ‡ SAML2" [admin.settings.connections.saml2.registrationId] -label = "Registration ID" +label = "ؓناسهٔ Ų«ŲØŲŖā€ŒŁ†Ų§Ł…" description = "ؓناسه Ų«ŲØŲŖā€ŒŁ†Ų§Ł… SAML2" [admin.settings.connections.saml2.autoCreateUser] @@ -4487,13 +4595,14 @@ label = "Ų®Ų·ā€ŒŁ…Ų“ŪŒ کوکی" description = "URL یا نام ŁŲ§ŪŒŁ„ برای Ų®Ų·ā€ŒŁ…Ų“ŪŒ کوکی" [admin.settings.legal.impressum] -label = "Impressum" +label = "اطلاعات Ų­Ł‚ŁˆŁ‚ŪŒ" description = "URL یا نام ŁŲ§ŪŒŁ„ برای impressum (ŲÆŲ± برخی Ų­ŁˆŲ²Ł‡ā€ŒŁ‡Ų§ŪŒ Ł‚Ų¶Ų§ŪŒŪŒ Ų§Ł„Ų²Ų§Ł…ŪŒ Ų§Ų³ŲŖ)" [admin.settings.premium] title = "Ł¾Ų±Ł…ŪŒŁˆŁ… و Ų³Ų§Ų²Ł…Ų§Ł†ŪŒ" description = "Ś©Ł„ŪŒŲÆ Ł„Ų§ŪŒŲ³Ł†Ų³ Ł¾Ų±Ł…ŪŒŁˆŁ… یا Ų³Ų§Ų²Ł…Ų§Ł†ŪŒ خود Ų±Ų§ Ł¾ŪŒŚ©Ų±ŲØŁ†ŲÆŪŒ Ś©Ł†ŪŒŲÆ." license = "Ł¾ŪŒŚ©Ų±ŲØŁ†ŲÆŪŒ Ł„Ų§ŪŒŲ³Ł†Ų³" +noInput = "لطفاً Ś©Ł„ŪŒŲÆ یا ŁŲ§ŪŒŁ„ Ł…Ų¬ŁˆŲ² Ų±Ų§ ارائه Ś©Ł†ŪŒŲÆ" [admin.settings.premium.licenseKey] toggle = "Ś©Ł„ŪŒŲÆ Ł„Ų§ŪŒŲ³Ł†Ų³ یا ŁŲ§ŪŒŁ„ ŚÆŁˆŲ§Ł‡ŪŒ دارید؟" @@ -4511,6 +4620,25 @@ line1 = "ŲØŲ§Ų²Ł†ŁˆŪŒŲ³ŪŒ Ś©Ł„ŪŒŲÆ Ł„Ų§ŪŒŲ³Ł†Ų³ ŁŲ¹Ł„ŪŒ قابل بازگؓت line2 = "Ł…ŚÆŲ± آنکه ŲÆŲ± جایی Ł¾Ų“ŲŖŪŒŲØŲ§Ł† گرفته باؓید، Ł„Ų§ŪŒŲ³Ł†Ų³ Ł‚ŲØŁ„ŪŒ ŲØŁ‡ā€ŒŲ·ŁˆŲ± ŲÆŲ§Ų¦Ł…ŪŒ Ų§Ų² ŲÆŲ³ŲŖ Ł…ŪŒā€ŒŲ±ŁˆŲÆ." line3 = "مهم: Ś©Ł„ŪŒŲÆŁ‡Ų§ŪŒ Ł„Ų§ŪŒŲ³Ł†Ų³ Ų±Ų§ خصوصی و امن نگه دارید. هرگز Ų¢Ł†ā€ŒŁ‡Ų§ Ų±Ų§ Ų¹Ł…ŁˆŁ…ŪŒ ŲØŁ‡ā€ŒŲ§Ų“ŲŖŲ±Ų§Ś© Ł†ŚÆŲ°Ų§Ų±ŪŒŲÆ." +[admin.settings.premium.inputMethod] +text = "Ś©Ł„ŪŒŲÆ Ł…Ų¬ŁˆŲ²" +file = "ŁŲ§ŪŒŁ„ ŚÆŁˆŲ§Ł‡ŪŒ" + +[admin.settings.premium.file] +label = "ŁŲ§ŪŒŁ„ ŚÆŁˆŲ§Ł‡ŪŒ Ł…Ų¬ŁˆŲ²" +description = "ŁŲ§ŪŒŁ„ Ł…Ų¬ŁˆŲ² .lic یا .cert Ł…Ų±ŲØŁˆŲ· به Ų®Ų±ŪŒŲÆŁ‡Ų§ŪŒ Ų¢ŁŁ„Ų§ŪŒŁ† خود Ų±Ų§ بارگذاری Ś©Ł†ŪŒŲÆ" +choose = "انتخاب ŁŲ§ŪŒŁ„ Ł…Ų¬ŁˆŲ²" +selected = "Ų§Ł†ŲŖŲ®Ų§ŲØā€ŒŲ“ŲÆŁ‡: {{filename}} ({{size}})" +successMessage = "ŁŲ§ŪŒŁ„ Ł…Ų¬ŁˆŲ² ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ بارگذاری و فعال Ų“ŲÆ. Ł†ŪŒŲ§Ų²ŪŒ به Ų±Ų§Ł‡ā€ŒŲ§Ł†ŲÆŲ§Ų²ŪŒ Ł…Ų¬ŲÆŲÆ Ł†ŪŒŲ³ŲŖ." + +[admin.settings.premium.currentLicense] +title = "Ł…Ų¬ŁˆŲ² فعال" +file = "منبع: ŁŲ§ŪŒŁ„ Ł…Ų¬ŁˆŲ² ({{path}})" +key = "منبع: Ś©Ł„ŪŒŲÆ Ł…Ų¬ŁˆŲ²" +type = "Ł†ŁˆŲ¹: {{type}}" +noInput = "لطفاً Ś©Ł„ŪŒŲÆ Ł…Ų¬ŁˆŲ² ارائه Ś©Ł†ŪŒŲÆ یا ŁŲ§ŪŒŁ„ ŚÆŁˆŲ§Ł‡ŪŒ Ų±Ų§ بارگذاری Ś©Ł†ŪŒŲÆ" +success = "Ł…ŁˆŁŁ‚" + [admin.settings.premium.enabled] label = "ŁŲ¹Ų§Ł„ā€ŒŲ³Ų§Ų²ŪŒ Ł‚Ų§ŲØŁ„ŪŒŲŖā€ŒŁ‡Ų§ŪŒ Ł¾Ų±Ł…ŪŒŁˆŁ…" description = "ŁŲ¹Ų§Ł„ā€ŒŲ³Ų§Ų²ŪŒ بررسی Ś©Ł„ŪŒŲÆ Ł„Ų§ŪŒŲ³Ł†Ų³ برای Ł‚Ų§ŲØŁ„ŪŒŲŖā€ŒŁ‡Ų§ŪŒ Ų­Ų±ŁŁ‡ā€ŒŲ§ŪŒ/Ų³Ų§Ų²Ł…Ų§Ł†ŪŒ" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} Ł…ŁˆŲ±ŲÆ Ų§Ł†ŲŖŲ®Ų§ŲØā€ŒŲ“ŲÆŁ‡" download = "ŲÆŲ§Ł†Ł„ŁˆŲÆ" delete = "حذف" unsupported = "Ł¾Ų“ŲŖŪŒŲØŲ§Ł†ŪŒā€ŒŁ†Ų“ŲÆŁ‡" +active = "فعال" addToUpload = "Ų§ŁŲ²ŁˆŲÆŁ† به بارگذاری" +closeFile = "بستن ŁŲ§ŪŒŁ„" deleteAll = "حذف همه" loadingFiles = "ŲÆŲ± Ų­Ų§Ł„ بارگذاری ŁŲ§ŪŒŁ„ā€ŒŁ‡Ų§..." noFiles = "ŁŲ§ŪŒŁ„ŪŒ Ł…ŁˆŲ¬ŁˆŲÆ Ł†ŪŒŲ³ŲŖ" @@ -5132,7 +5262,7 @@ upgrade = "Ł‡Ł…ŪŒŁ† حالا ارتقا بده →" freeTitle = "Ł„Ų§ŪŒŲ³Ł†Ų³ سرور" overLimitTitle = "Ł†ŪŒŲ§Ų² به Ł„Ų§ŪŒŲ³Ł†Ų³ سرور" overLimitBody = "Ł…Ų¬ŁˆŲ² Ł…Ų§ ŲŖŲ§ {{freeTierLimit}} کاربر Ų±Ų§ŪŒŚÆŲ§Ł† ŲØŁ‡ā€ŒŲ§Ų²Ų§ŪŒ هر سرور Ų±Ų§ Ł…Ų¬Ų§Ų² Ł…ŪŒā€ŒŲÆŲ§Ł†ŲÆ. Ų“Ł…Ų§ {{overLimitUserCopy}} کاربر Stirling دارید. برای ادامه ŲØŲÆŁˆŁ† ŁˆŁ‚ŁŁ‡ŲŒ به پلن Stirling Server ارتقا ŲÆŁ‡ŪŒŲÆ - ŲµŁ†ŲÆŁ„ŪŒ Ł†Ų§Ł…Ų­ŲÆŁˆŲÆŲŒ ویرایؓ متن PDF و کنترل کامل Ų§ŲÆŁ…ŪŒŁ† ŲØŲ§ 99$ ŲØŁ‡ā€ŒŲ§Ų²Ų§ŪŒ هر سرور ŲÆŲ± ماه." -freeBody = "Ł…Ų¬ŁˆŲ² Open-Core Ł…Ų§ ŲŖŲ§ {{freeTierLimit}} کاربر Ų±Ų§ŪŒŚÆŲ§Ł† ŲØŁ‡ā€ŒŲ§Ų²Ų§ŪŒ هر سرور Ų±Ų§ Ł…Ų¬Ų§Ų² Ł…ŪŒā€ŒŲÆŲ§Ł†ŲÆ. برای Ł…Ł‚ŪŒŲ§Ų³ā€ŒŁ¾Ų°ŪŒŲ±ŪŒ ŲØŲÆŁˆŁ† ŁˆŁ‚ŁŁ‡ و دسترسی Ų²ŁˆŲÆŁ‡Ł†ŚÆŲ§Ł… به Ų§ŲØŲ²Ų§Ų± ویرایؓ متن PDF Ų¬ŲÆŪŒŲÆŁ…Ų§Ł†ŲŒ پلن Stirling Server Ų±Ų§ Ł¾ŪŒŲ“Ł†Ł‡Ų§ŲÆ Ł…ŪŒā€ŒŚ©Ł†ŪŒŁ… - ویرایؓ کامل و ŲµŁ†ŲÆŁ„ŪŒ Ł†Ų§Ł…Ų­ŲÆŁˆŲÆ ŲØŲ§ 99$ ŲØŁ‡ā€ŒŲ§Ų²Ų§ŪŒ هر سرور ŲÆŲ± ماه." +freeBody = "Ł…Ų¬ŁˆŲ² Open-Core Ł…Ų§ ŲØŁ‡ā€ŒŲ§Ų²Ų§ŪŒ هر سرور اجازهٔ استفادهٔ Ų±Ų§ŪŒŚÆŲ§Ł† برای حداکثر {{freeTierLimit}} کاربر Ų±Ų§ Ł…ŪŒā€ŒŲÆŁ‡ŲÆ. برای Ł…Ł‚ŪŒŲ§Ų³ā€ŒŲÆŁ‡ŪŒ ŲØŲÆŁˆŁ† ŁˆŁ‚ŁŁ‡ŲŒ Ų·Ų±Ų­ Stirling Server Ų±Ų§ ŲŖŁˆŲµŪŒŁ‡ Ł…ŪŒā€ŒŚ©Ł†ŪŒŁ… - ŲŖŲ¹ŲÆŲ§ŲÆ کاربران Ł†Ų§Ł…Ų­ŲÆŁˆŲÆ و Ł¾Ų“ŲŖŪŒŲØŲ§Ł†ŪŒ Ų§Ų² SSO ŲØŲ§ $99/سرور/ماه." [onboarding.desktopInstall] title = "ŲÆŲ§Ł†Ł„ŁˆŲÆ" @@ -5237,6 +5367,31 @@ error = "ŲØŁ‡ā€ŒŲ±ŁˆŲ²Ų±Ų³Ų§Ł†ŪŒ وضعیت کاربر Ł†Ų§Ł…ŁˆŁŁ‚ بود" success = "کاربر ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ حذف Ų“ŲÆ" error = "حذف کاربر Ł†Ų§Ł…ŁˆŁŁ‚ بود" +[workspace.people.changePassword] +action = "تغییر ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡" +title = "تغییر ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡" +subtitle = "ŲØŁ‡ā€ŒŲ±ŁˆŲ²Ų±Ų³Ų§Ł†ŪŒ ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡ برای" +newPassword = "ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡Ł” جدید" +confirmPassword = "تأیید ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡" +placeholder = "ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡Ł” جدید وارد Ś©Ł†ŪŒŲÆ" +confirmPlaceholder = "ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡Ł” جدید Ų±Ų§ ŲÆŁˆŲØŲ§Ų±Ł‡ وارد Ś©Ł†ŪŒŲÆ" +passwordRequired = "لطفاً یک ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡Ł” جدید وارد Ś©Ł†ŪŒŲÆ" +passwordMismatch = "ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡ā€ŒŁ‡Ų§ ŪŒŚ©Ų³Ų§Ł† Ł†ŪŒŲ³ŲŖŁ†ŲÆ" +generateRandom = "ایجاد ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡Ł” امن" +generatedPreview = "ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡Ł” ŲŖŁˆŁ„ŪŒŲÆŲ“ŲÆŁ‡:" +copyTooltip = "کپی ŲÆŲ± Ś©Ł„ŪŒŁ¾ā€ŒŲØŁˆŲ±ŲÆ" +copiedToClipboard = "ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡ ŲÆŲ± Ś©Ł„ŪŒŁ¾ā€ŒŲØŁˆŲ±ŲÆ کپی Ų“ŲÆ" +copyFailed = "کپی ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡ Ł†Ų§Ł…ŁˆŁŁ‚ بود" +sendEmail = "به کاربر دربارهٔ Ų§ŪŒŁ† تغییر Ų§ŪŒŁ…ŪŒŁ„ Ų§Ų±Ų³Ų§Ł„ Ś©Ł†ŪŒŲÆ" +includePassword = "ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡Ł” جدید Ų±Ų§ ŲÆŲ± Ų§ŪŒŁ…ŪŒŁ„ ŲÆŲ±Ų¬ Ś©Ł†ŪŒŲÆ" +forcePasswordChange = "کاربر Ų±Ų§ ملزم Ś©Ł†ŪŒŲÆ ŲÆŲ± ورود بعدی ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡ Ų±Ų§ تغییر دهد" +emailUnavailable = "Ų§ŪŒŁ…ŪŒŁ„ Ų§ŪŒŁ† کاربر Ł…Ų¹ŲŖŲØŲ± Ł†ŪŒŲ³ŲŖ. Ų§Ų¹Ł„Ų§Ł†ā€ŒŁ‡Ų§ ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ Ų“ŲÆŁ‡ā€ŒŲ§Ł†ŲÆ." +smtpDisabled = "برای Ų§Ų¹Ł„Ų§Ł†ā€ŒŁ‡Ų§ŪŒ Ų§ŪŒŁ…ŪŒŁ„ باید SMTP ŲÆŲ± ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ فعال ŲØŲ§Ų“ŲÆ." +notifyOnly = "Ų§ŪŒŁ…ŪŒŁ„ŪŒ ŲØŲÆŁˆŁ† ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡ Ų§Ų±Ų³Ų§Ł„ Ų®ŁˆŲ§Ł‡ŲÆ Ų“ŲÆ ŲŖŲ§ به کاربر اطلاع دهد که یک Ł…ŲÆŪŒŲ± آن Ų±Ų§ تغییر داده Ų§Ų³ŲŖ." +submit = "ŲØŁ‡ā€ŒŲ±ŁˆŲ²Ų±Ų³Ų§Ł†ŪŒ ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡" +success = "ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡ ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ ŲØŁ‡ā€ŒŲ±ŁˆŲ²Ų±Ų³Ų§Ł†ŪŒ Ų“ŲÆ" +error = "ŲØŁ‡ā€ŒŲ±ŁˆŲ²Ų±Ų³Ų§Ł†ŪŒ ŚÆŲ°Ų±ŁˆŲ§Ś˜Ł‡ Ł†Ų§Ł…ŁˆŁŁ‚ بود" + [workspace.people.emailInvite] tab = "دعوت Ų§ŪŒŁ…ŪŒŁ„ŪŒ" description = "ŲÆŲ± زیر Ų§ŪŒŁ…ŪŒŁ„ā€ŒŁ‡Ų§ Ų±Ų§ تایپ یا ŲØŚ†Ų³ŲØŲ§Ł†ŪŒŲÆ و ŲØŲ§ کاما Ų¬ŲÆŲ§ Ś©Ł†ŪŒŲÆ. به کاربران اطلاعات ورود Ų§Ų² Ų·Ų±ŪŒŁ‚ Ų§ŪŒŁ…ŪŒŁ„ Ų§Ų±Ų³Ų§Ł„ Ł…ŪŒā€ŒŲ“ŁˆŲÆ." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "حداقل یک Ų¢ŲÆŲ±Ų³ Ų§ŪŒŁ…ŪŒŁ„ Ų§Ł„Ų²Ų§Ł…ŪŒ Ų§Ų³ŲŖ" submit = "Ų§Ų±Ų³Ų§Ł„ ŲÆŲ¹ŁˆŲŖā€ŒŁ†Ų§Ł…Ł‡ā€ŒŁ‡Ų§" success = "کاربر(ان) ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ دعوت Ų“ŲÆ(ند)" -partialSuccess = "برخی ŲÆŲ¹ŁˆŲŖā€ŒŁ‡Ų§ Ł†Ų§Ł…ŁˆŁŁ‚ بود" +partialFailure = "برخی ŲÆŲ¹ŁˆŲŖā€ŒŁ‡Ų§ Ł†Ų§Ł…ŁˆŁŁ‚ ŲØŁˆŲÆŁ†ŲÆ" allFailed = "دعوت کاربران Ł†Ų§Ł…ŁˆŁŁ‚ بود" error = "Ų§Ų±Ų³Ų§Ł„ ŲÆŲ¹ŁˆŲŖā€ŒŁ†Ų§Ł…Ł‡ā€ŒŁ‡Ų§ Ł†Ų§Ł…ŁˆŁŁ‚ بود" @@ -5770,6 +5925,7 @@ subtitle = "ŲØŲ§ Ų­Ų³Ų§ŲØ Stirling خود وارد ؓوید" [setup.selfhosted] title = "ورود به سرور" subtitle = "اطلاعات کاربری سرور خود Ų±Ų§ وارد Ś©Ł†ŪŒŲÆ" +link = "یا به یک Ų­Ų³Ų§ŲØ Ų®ŁˆŲÆŁ…ŪŒŲ²ŲØŲ§Ł† متصل ؓوید" [setup.server] title = "Ų§ŲŖŲµŲ§Ł„ به سرور" @@ -5788,6 +5944,14 @@ description = "URL کامل سرور Ų®ŁˆŲÆŁ…ŪŒŲ²ŲØŲ§Ł† Stirling PDF خود Ų±Ų§ emptyUrl = "لطفاً URL سرور Ų±Ų§ وارد Ś©Ł†ŪŒŲÆ" unreachable = "Ų§ŲŖŲµŲ§Ł„ به سرور ممکن نؓد" testFailed = "Ų¢Ų²Ł…ŁˆŁ† Ų§ŲŖŲµŲ§Ł„ Ł†Ų§Ł…ŁˆŁŁ‚ بود" +configFetch = "بازیابی Ł¾ŪŒŚ©Ų±ŲØŁ†ŲÆŪŒ سرور Ł†Ų§Ł…ŁˆŁŁ‚ بود. لطفاً URL Ų±Ų§ بررسی Ś©Ł†ŪŒŲÆ و ŲÆŁˆŲØŲ§Ų±Ł‡ تلاؓ Ś©Ł†ŪŒŲÆ." + +[setup.server.error.securityDisabled] +title = "ورود فعال Ł†ŪŒŲ³ŲŖ" +body = "ورود ŲÆŲ± Ų§ŪŒŁ† سرور فعال نؓده Ų§Ų³ŲŖ. برای Ų§ŲŖŲµŲ§Ł„ به Ų§ŪŒŁ† سرور باید Ų§Ų­Ų±Ų§Ų² Ł‡ŁˆŪŒŲŖ Ų±Ų§ فعال Ś©Ł†ŪŒŲÆ:" +step1 = "DOCKER_ENABLE_SECURITY=true Ų±Ų§ ŲÆŲ± Ł…Ų­ŪŒŲ· خود ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ" +step2 = "یا security.enableLogin=true Ų±Ų§ ŲÆŲ± settings.yml ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ" +step3 = "سرور Ų±Ų§ Ų±Ų§Ł‡ā€ŒŲ§Ł†ŲÆŲ§Ų²ŪŒ Ł…Ų¬ŲÆŲÆ Ś©Ł†ŪŒŲÆ" [setup.login] title = "ورود" @@ -5797,6 +5961,13 @@ submit = "ورود" signInWith = "ورود ŲØŲ§" oauthPending = "ŲÆŲ± Ų­Ų§Ł„ ŲØŲ§Ų² کردن Ł…Ų±ŁˆŲ±ŚÆŲ± برای Ų§Ų­Ų±Ų§Ų² Ł‡ŁˆŪŒŲŖ..." orContinueWith = "یا ŲØŲ§ Ų§ŪŒŁ…ŪŒŁ„ ادامه ŲÆŁ‡ŪŒŲÆ" +serverRequirement = "ŲŖŁˆŲ¬Ł‡: سرور باید ورود Ų±Ų§ فعال کرده ŲØŲ§Ų“ŲÆ." +showInstructions = "Ł†Ų­ŁˆŁ‡ ŁŲ¹Ų§Ł„ā€ŒŲ³Ų§Ų²ŪŒŲŸ" +hideInstructions = "Ł…Ų®ŁŪŒ کردن ŲÆŲ³ŲŖŁˆŲ±Ų§Ł„Ų¹Ł…Ł„ā€ŒŁ‡Ų§" +instructions = "برای ŁŲ¹Ų§Ł„ā€ŒŲ³Ų§Ų²ŪŒ ورود ŲÆŲ± سرور Stirling PDF خود:" +instructionsEnvVar = "Ł…ŲŖŲŗŪŒŲ± Ł…Ų­ŪŒŲ·ŪŒ Ų±Ų§ ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ:" +instructionsOrYml = "یا ŲÆŲ± settings.yml:" +instructionsRestart = "سپس سرور خود Ų±Ų§ Ų±Ų§Ł‡ā€ŒŲ§Ł†ŲÆŲ§Ų²ŪŒ Ł…Ų¬ŲÆŲÆ Ś©Ł†ŪŒŲÆ ŲŖŲ§ تغییرات اعمال Ų“ŁˆŁ†ŲÆ." [setup.login.username] label = "نام کاربری" @@ -5853,6 +6024,7 @@ earlyAccess = "دسترسی Ų²ŁˆŲÆŁ‡Ł†ŚÆŲ§Ł…" reset = "ŲØŲ§Ų²Ł†Ų“Ų§Ł†ŪŒ تغییرات" downloadJson = "ŲÆŲ§Ł†Ł„ŁˆŲÆ JSON" generatePdf = "ŲŖŁˆŁ„ŪŒŲÆ PDF" +saveChanges = "Ų°Ų®ŪŒŲ±Ł‡Ł” تغییرات" [pdfTextEditor.options.autoScaleText] title = "Ł…Ł‚ŪŒŲ§Ų³ خودکار متن برای Ų¬Ų§ ؓدن ŲÆŲ± ŲØŲ§Ś©Ų³ā€ŒŁ‡Ų§" @@ -5890,6 +6062,8 @@ alpha = "Ų§ŪŒŁ† Ł†Ł…Ų§ŪŒŲ“ŚÆŲ± آلفا Ł‡Ł†ŁˆŲ² ŲÆŲ± Ų­Ų§Ł„ تکامل Ų§Ų³ŲŖ [pdfTextEditor.empty] title = "Ł‡ŪŒŚ† Ų³Ł†ŲÆŪŒ بارگذاری نؓده Ų§Ų³ŲŖ" subtitle = "برای ؓروع ویرایؓ Ł…ŲŖŁ†ŲŒ یک ŁŲ§ŪŒŁ„ PDF یا JSON بارگذاری Ś©Ł†ŪŒŲÆ." +dropzone = "یک ŁŲ§ŪŒŁ„ PDF یا JSON Ų±Ų§ Ų§ŪŒŁ†Ų¬Ų§ بکؓید و رها Ś©Ł†ŪŒŲÆŲŒ یا برای Ł…Ų±ŁˆŲ± Ś©Ł„ŪŒŚ© Ś©Ł†ŪŒŲÆ" +dropzoneWithFiles = "یک ŁŲ§ŪŒŁ„ Ų±Ų§ Ų§Ų² برگهٔ ŁŲ§ŪŒŁ„ā€ŒŁ‡Ų§ انتخاب Ś©Ł†ŪŒŲÆŲŒ یا یک ŁŲ§ŪŒŁ„ PDF یا JSON Ų±Ų§ Ų§ŪŒŁ†Ų¬Ų§ بکؓید و رها Ś©Ł†ŪŒŲÆŲŒ یا برای Ł…Ų±ŁˆŲ± Ś©Ł„ŪŒŚ© Ś©Ł†ŪŒŲÆ" [pdfTextEditor.welcomeBanner] title = "به ویرایؓگر متن PDF (دسترسی Ų²ŁˆŲÆŁ‡Ł†ŚÆŲ§Ł…) خوؓ Ų¢Ł…ŲÆŪŒŲÆ" diff --git a/frontend/public/locales/fr-FR/translation.toml b/frontend/public/locales/fr-FR/translation.toml index 2bd5da5c51..37d11a274e 100644 --- a/frontend/public/locales/fr-FR/translation.toml +++ b/frontend/public/locales/fr-FR/translation.toml @@ -163,6 +163,11 @@ unfavorite = "Retirer des favoris" fullscreen = "Passer en mode plein Ć©cran" sidebar = "Passer en mode barre latĆ©rale" +[backendStartup] +notFoundTitle = "Backend introuvable" +retry = "RĆ©essayer" +unreachable = "L’application ne peut actuellement pas se connecter au backend. VĆ©rifiez l’état du backend et la connectivitĆ© rĆ©seau, puis rĆ©essayez." + [zipWarning] title = "Fichier ZIP volumineux" message = "Ce ZIP contient {{count}} fichiers. Extraire quand mĆŖmeĀ ?" @@ -347,7 +352,7 @@ teams = "Ɖquipes" title = "Configuration" systemSettings = "ParamĆØtres systĆØme" features = "FonctionnalitĆ©s" -endpoints = "Endpoints" +endpoints = "Points de terminaison" database = "Base de donnĆ©es" advanced = "AvancĆ©" @@ -358,7 +363,7 @@ connections = "Connexions" [settings.licensingAnalytics] title = "Licences et analyses" -plan = "Plan" +plan = "Forfait" audit = "Audit" usageAnalytics = "Analyses d'utilisation" @@ -912,6 +917,9 @@ desc = "CrĆ©ez des workflows multi-Ć©tapes en enchaĆ®nant des actions PDF. IdĆ©a desc = "Superposer un PDF sur un autre" title = "Superposer des PDF" +[home.pdfTextEditor] +title = "Ɖditeur de texte PDF" +desc = "Modifier le texte et les images existants dans les PDF" [home.addText] tags = "texte,annotation,Ć©tiquette" @@ -1217,7 +1225,7 @@ odtExt = "Texte OpenDocument (.odt)" pptExt = "PowerPoint (.pptx)" odpExt = "PrĆ©sentation OpenDocument (.odp)" txtExt = "Texte brut (.txt)" -rtfExt = "Rich Text Format (.rtf)" +rtfExt = "Format de texte enrichi (.rtf)" selectedFiles = "Fichiers sĆ©lectionnĆ©s" noFileSelected = "Aucun fichier sĆ©lectionnĆ©. Utilisez le panneau de fichiers pour ajouter des fichiers." convertFiles = "Convertir les fichiers" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Signature dessinĆ©e" defaultImageLabel = "Signature tĆ©lĆ©versĆ©e" defaultTextLabel = "Signature saisie" saveButton = "Enregistrer la signature" +savePersonal = "Enregistrer en personnel" +saveShared = "Enregistrer en partagĆ©" saveUnavailable = "CrĆ©ez d’abord une signature pour l’enregistrer." noChanges = "La signature actuelle est dĆ©jĆ  enregistrĆ©e." +tempStorageTitle = "Stockage temporaire du navigateur" +tempStorageDescription = "Les signatures sont stockĆ©es uniquement dans votre navigateur. Elles seront perdues si vous effacez les donnĆ©es du navigateur ou si vous changez de navigateur." +personalHeading = "Signatures personnelles" +sharedHeading = "Signatures partagĆ©es" +personalDescription = "Vous seul pouvez voir ces signatures." +sharedDescription = "Tous les utilisateurs peuvent voir et utiliser ces signatures." [sign.saved.type] canvas = "Dessin" @@ -3020,6 +3036,91 @@ title = "RĆ©cupĆ©rer les informations" header = "RĆ©cupĆ©rer les informations" submit = "RĆ©cupĆ©rer les informations" downloadJson = "TĆ©lĆ©charger le JSON" +processing = "Extraction des informations..." +results = "RĆ©sultats" +noResults = "ExĆ©cutez l'outil pour gĆ©nĆ©rer un rapport." +downloads = "TĆ©lĆ©chargements" +noneDetected = "Aucun dĆ©tectĆ©" +indexTitle = "Index" + +[getPdfInfo.report] +entryLabel = "RĆ©sumĆ© complet des informations" +shortTitle = "Informations PDF" + +[getPdfInfo.sections] +metadata = "MĆ©tadonnĆ©es" +formFields = "Champs de formulaire" +basicInfo = "Informations de base" +documentInfo = "Informations sur le document" +compliance = "ConformitĆ©" +encryption = "Chiffrement" +permissions = "Autorisations" +other = "Autre" +perPageInfo = "Informations par page" +tableOfContents = "Table des matiĆØres" + +[getPdfInfo.other] +attachments = "PiĆØces jointes" +embeddedFiles = "Fichiers intĆ©grĆ©s" +javaScript = "JavaScript" +layers = "Calques" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Taille" +annotations = "Annotations" +images = "Images" +links = "Liens" +fonts = "Polices" +xobjects = "Nombre d'objets XObject" +multimedia = "MultimĆ©dia" + +[getPdfInfo.summary] +pages = "Pages" +fileSize = "Taille du fichier" +pdfVersion = "Version PDF" +language = "Langue" +title = "RĆ©sumĆ© PDF" +author = "Auteur" +created = "CrƩƩ" +modified = "ModifiĆ©" +permsAll = "Toutes les autorisations accordĆ©es" +permsRestricted = "{{count}} restrictions" +permsMixed = "Certaines autorisations sont restreintes" +hasCompliance = "Respecte des normes de conformitĆ©" +noCompliance = "Aucune norme de conformitĆ©" +basic = "Informations de base" +documentInfo = "Informations sur le document" +securityTitle = "Ɖtat de sĆ©curitĆ©" +technical = "Technique" +overviewTitle = "AperƧu du PDF" + +[getPdfInfo.summary.security] +encrypted = "PDF chiffrĆ© - Protection par mot de passe prĆ©sente" +unencrypted = "PDF non chiffrĆ© - Aucune protection par mot de passe" + +[getPdfInfo.summary.tech] +images = "Images" +fonts = "Polices" +formFields = "Champs de formulaire" +embeddedFiles = "Fichiers intĆ©grĆ©s" +javaScript = "JavaScript" +layers = "Calques" +bookmarks = "Signets" +multimedia = "MultimĆ©dia" + +[getPdfInfo.summary.overview] +untitled = "un document sans titre" +unknown = "Auteur inconnu" +text = "Ceci est un PDF de {{pages}} pages intitulĆ© {{title}} crƩƩ par {{author}} (version PDF {{version}})." + +[getPdfInfo.error] +partial = "Certains fichiers n'ont pas pu ĆŖtre traitĆ©s." +unexpected = "Erreur inattendue lors de l'extraction." + +[getPdfInfo.status] +complete = "Extraction terminĆ©e" [extractPage] tags = "extraire,extract" @@ -3438,6 +3539,9 @@ signinTitle = "Veuillez vous connecter" ssoSignIn = "Se connecter via l'authentification unique" oAuth2AutoCreateDisabled = "OAUTH2 CrĆ©ation automatique d'utilisateur dĆ©sactivĆ©e" oAuth2AdminBlockedUser = "La crĆ©ation ou l'authentification d'utilisateurs non enregistrĆ©s est actuellement bloquĆ©e. Veuillez contacter l'administrateur." +oAuth2RequiresLicense = "La connexion OAuth/SSO nĆ©cessite une licence payante (Server ou Enterprise). Veuillez contacter l’administrateur pour mettre Ć  niveau votre plan." +saml2RequiresLicense = "La connexion SAML nĆ©cessite une licence payante (Server ou Enterprise). Veuillez contacter l’administrateur pour mettre Ć  niveau votre plan." +maxUsersReached = "Nombre maximal d’utilisateurs atteint pour votre licence actuelle. Veuillez contacter l’administrateur pour mettre Ć  niveau votre plan ou ajouter des places." oauth2RequestNotFound = "Demande d'autorisation introuvable" oauth2InvalidUserInfoResponse = "RĆ©ponse contenant les informations de l'utilisateur est invalide" oauth2invalidRequest = "RequĆŖte invalide" @@ -3846,14 +3950,17 @@ fitToWidth = "Ajuster Ć  la largeur" actualSize = "Taille rĆ©elle" [viewer] +cannotPreviewFile = "Impossible d’afficher un aperƧu du fichier" +dualPageView = "Vue double page" firstPage = "PremiĆØre page" lastPage = "DerniĆØre page" -previousPage = "Page prĆ©cĆ©dente" nextPage = "Page suivante" +onlyPdfSupported = "Le visualiseur prend uniquement en charge les fichiers PDF. Ce fichier semble ĆŖtre d’un autre format." +previousPage = "Page prĆ©cĆ©dente" +singlePageView = "Vue page unique" +unknownFile = "Fichier inconnu" zoomIn = "Zoom avant" zoomOut = "Zoom arriĆØre" -singlePageView = "Vue page unique" -dualPageView = "Vue double page" [rightRail] closeSelected = "Fermer les fichiers sĆ©lectionnĆ©s" @@ -3877,6 +3984,7 @@ toggleSidebar = "Afficher/masquer la barre latĆ©rale" exportSelected = "Exporter les pages sĆ©lectionnĆ©es" toggleAnnotations = "Afficher/masquer les annotations" annotationMode = "Basculer en mode annotation" +print = "Imprimer le PDF" draw = "Dessiner" save = "Enregistrer" saveChanges = "Enregistrer les modifications" @@ -4153,7 +4261,7 @@ description = "Suivre les actions des utilisateurs et les Ć©vĆ©nements systĆØme [admin.settings.security.audit.level] label = "Niveau d’audit" -description = "0=OFF, 1=BASIC, 2=STANDARD, 3=VERBOSE" +description = "0=DƉSACTIVƉ, 1=BASIQUE, 2=STANDARD, 3=VERBEUX" [admin.settings.security.audit.retentionDays] label = "RĆ©tention des journaux (jours)" @@ -4491,9 +4599,10 @@ label = "Mentions lĆ©gales" description = "URL ou nom de fichier de l’impressum (obligatoire dans certaines juridictions)" [admin.settings.premium] -title = "Premium & Enterprise" +title = "Premium et Entreprise" description = "Configurer votre clĆ© de licence Premium ou Enterprise." license = "Configuration de la licence" +noInput = "Veuillez fournir une clĆ© de licence ou un fichier" [admin.settings.premium.licenseKey] toggle = "Vous avez une clĆ© de licence ou un fichier de certificat ?" @@ -4511,6 +4620,25 @@ line1 = "Ɖcraser votre clĆ© de licence actuelle est irrĆ©versible." line2 = "Votre licence prĆ©cĆ©dente sera dĆ©finitivement perdue, sauf si vous l’avez sauvegardĆ©e ailleurs." line3 = "Important : gardez vos clĆ©s de licence privĆ©es et sĆ©curisĆ©es. Ne les partagez jamais publiquement." +[admin.settings.premium.inputMethod] +text = "ClĆ© de licence" +file = "Fichier de certificat" + +[admin.settings.premium.file] +label = "Fichier de certificat de licence" +description = "TĆ©lĆ©versez votre fichier de licence .lic ou .cert issu d’achats hors ligne" +choose = "Choisir le fichier de licence" +selected = "SĆ©lectionnĆ©: {{filename}} ({{size}})" +successMessage = "Fichier de licence tĆ©lĆ©versĆ© et activĆ© avec succĆØs. Aucun redĆ©marrage requis." + +[admin.settings.premium.currentLicense] +title = "Licence active" +file = "Source: Fichier de licence ({{path}})" +key = "Source: ClĆ© de licence" +type = "Type : {{type}}" +noInput = "Veuillez fournir une clĆ© de licence ou tĆ©lĆ©verser un fichier de certificat" +success = "SuccĆØs" + [admin.settings.premium.enabled] label = "Activer les fonctionnalitĆ©s Premium" description = "Activer la vĆ©rification de la clĆ© de licence pour les fonctionnalitĆ©s Pro/Enterprise" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} sĆ©lectionnĆ©(s)" download = "TĆ©lĆ©charger" delete = "Supprimer" unsupported = "Non pris en charge" +active = "Actif" addToUpload = "Ajouter au tĆ©lĆ©versement" +closeFile = "Fermer le fichier" deleteAll = "Tout supprimer" loadingFiles = "Chargement des fichiers..." noFiles = "Aucun fichier disponible" @@ -5132,7 +5262,7 @@ upgrade = "Mettre Ć  niveau maintenant →" freeTitle = "Licence serveur" overLimitTitle = "Licence serveur requise" overLimitBody = "Notre licence autorise jusqu’à {{freeTierLimit}} utilisateurs gratuits par serveur. Vous avez {{overLimitUserCopy}} utilisateurs Stirling. Pour continuer sans interruption, passez au plan Stirling Server — places illimitĆ©es, Ć©dition de texte PDF et contrĆ“le d’administration complet pour 99Ā $/serveur/mois." -freeBody = "Notre licence Open-Core autorise jusqu’à {{freeTierLimit}} utilisateurs gratuits par serveur. Pour Ć©voluer sans interruption et accĆ©der en avant-premiĆØre Ć  notre nouvel outil d’édition de texte PDF, nous recommandons le plan Stirling Server — Ć©dition complĆØte et places illimitĆ©es pour 99Ā $/serveur/mois." +freeBody = "Notre rĆ©gime de licence Open-Core autorise jusqu'Ć  {{freeTierLimit}} utilisateurs gratuitement par serveur. Pour Ć©voluer sans interruption, nous recommandons le forfait Stirling Server - places illimitĆ©es et prise en charge du SSO pour 99 $/serveur/mois." [onboarding.desktopInstall] title = "TĆ©lĆ©charger" @@ -5178,7 +5308,7 @@ active = "Actif" disabled = "DĆ©sactivĆ©" activeSession = "Session active" member = "Membre" -admin = "Admin" +admin = "Administrateur" editRole = "Modifier le rĆ“le" enable = "Activer" disable = "DĆ©sactiver" @@ -5237,6 +5367,31 @@ error = "Ɖchec de la mise Ć  jour du statut de l’utilisateur" success = "Utilisateur supprimĆ© avec succĆØs" error = "Ɖchec de la suppression de l’utilisateur" +[workspace.people.changePassword] +action = "Changer le mot de passe" +title = "Changer le mot de passe" +subtitle = "Mettre Ć  jour le mot de passe de" +newPassword = "Nouveau mot de passe" +confirmPassword = "Confirmer le mot de passe" +placeholder = "Saisissez un nouveau mot de passe" +confirmPlaceholder = "Saisissez Ć  nouveau le nouveau mot de passe" +passwordRequired = "Veuillez saisir un nouveau mot de passe" +passwordMismatch = "Les mots de passe ne correspondent pas" +generateRandom = "GĆ©nĆ©rer un mot de passe sĆ©curisĆ©" +generatedPreview = "Mot de passe gĆ©nĆ©rĆ© :" +copyTooltip = "Copier dans le presse-papiers" +copiedToClipboard = "Mot de passe copiĆ© dans le presse-papiers" +copyFailed = "Ɖchec de la copie du mot de passe" +sendEmail = "Envoyer un e-mail Ć  l'utilisateur Ć  propos de ce changement" +includePassword = "Inclure le nouveau mot de passe dans l'e-mail" +forcePasswordChange = "Forcer l'utilisateur Ć  changer son mot de passe Ć  la prochaine connexion" +emailUnavailable = "L'e-mail de cet utilisateur n'est pas une adresse valide. Les notifications sont dĆ©sactivĆ©es." +smtpDisabled = "Les notifications par e-mail nĆ©cessitent l'activation de SMTP dans les paramĆØtres." +notifyOnly = "Un e-mail sera envoyĆ© sans le mot de passe, informant l'utilisateur qu'un administrateur l'a modifiĆ©." +submit = "Mettre Ć  jour le mot de passe" +success = "Mot de passe mis Ć  jour avec succĆØs" +error = "Ɖchec de la mise Ć  jour du mot de passe" + [workspace.people.emailInvite] tab = "Invitation par e-mail" description = "Saisissez ou collez des adresses e-mail ci-dessous, sĆ©parĆ©es par des virgules. Les utilisateurs recevront leurs identifiants de connexion par e-mail." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Au moins une adresse e-mail est requise" submit = "Envoyer les invitations" success = "Utilisateur(s) invitĆ©(s) avec succĆØs" -partialSuccess = "Certaines invitations ont Ć©chouĆ©" +partialFailure = "Certaines invitations ont Ć©chouĆ©" allFailed = "Ɖchec de l’invitation des utilisateurs" error = "Ɖchec de l’envoi des invitations" @@ -5709,7 +5864,7 @@ title = "Graphique d’utilisation des endpoints" [usage.table] title = "Statistiques dĆ©taillĆ©es" -endpoint = "Endpoint" +endpoint = "Point de terminaison" visits = "Visites" percentage = "Pourcentage" noData = "Aucune donnĆ©e disponible" @@ -5770,6 +5925,7 @@ subtitle = "Connectez-vous avec votre compte Stirling" [setup.selfhosted] title = "Se connecter au serveur" subtitle = "Saisissez les identifiants du serveur" +link = "ou connectez-vous Ć  un compte auto-hĆ©bergĆ©" [setup.server] title = "Connexion au serveur" @@ -5788,6 +5944,14 @@ description = "Saisissez l’URL complĆØte de votre serveur Stirling PDF auto‑ emptyUrl = "Veuillez saisir une URL de serveur" unreachable = "Connexion au serveur impossible" testFailed = "Ɖchec du test de connexion" +configFetch = "Ɖchec de la rĆ©cupĆ©ration de la configuration du serveur. Veuillez vĆ©rifier l'URL et rĆ©essayer." + +[setup.server.error.securityDisabled] +title = "Connexion non activĆ©e" +body = "La connexion n'est pas activĆ©e sur ce serveur. Pour vous y connecter, vous devez activer l'authentification :" +step1 = "DĆ©finissez DOCKER_ENABLE_SECURITY=true dans votre environnement" +step2 = "Ou dĆ©finissez security.enableLogin=true dans settings.yml" +step3 = "RedĆ©marrez le serveur" [setup.login] title = "Se connecter" @@ -5797,13 +5961,20 @@ submit = "Se connecter" signInWith = "Se connecter avec" oauthPending = "Ouverture du navigateur pour l'authentification..." orContinueWith = "Ou continuer avec l’email" +serverRequirement = "Remarque : le serveur doit avoir la connexion activĆ©e." +showInstructions = "Comment l’activer ?" +hideInstructions = "Masquer les instructions" +instructions = "Pour activer la connexion sur votre serveur Stirling PDF :" +instructionsEnvVar = "DĆ©finissez la variable d’environnement :" +instructionsOrYml = "Ou dans settings.yml :" +instructionsRestart = "RedĆ©marrez ensuite votre serveur pour que les modifications prennent effet." [setup.login.username] label = "Nom d’utilisateur" placeholder = "Entrez votre nom d’utilisateur" [setup.login.email] -label = "Email" +label = "E-mail" placeholder = "Saisissez votre email" [setup.login.password] @@ -5840,7 +6011,7 @@ paragraph = "Page de paragraphe" sparse = "Texte clairsemĆ©" [pdfTextEditor.groupingMode] -auto = "Auto" +auto = "Automatique" paragraph = "Paragraphe" singleLine = "Ligne unique" @@ -5853,6 +6024,7 @@ earlyAccess = "AccĆØs anticipĆ©" reset = "RĆ©initialiser les modifications" downloadJson = "TĆ©lĆ©charger le JSON" generatePdf = "GĆ©nĆ©rer le PDF" +saveChanges = "Enregistrer les modifications" [pdfTextEditor.options.autoScaleText] title = "Ajuster automatiquement le texte aux cadres" @@ -5890,6 +6062,8 @@ alpha = "Ce visualiseur alpha Ć©volue encore — certaines polices, couleurs, ef [pdfTextEditor.empty] title = "Aucun document chargĆ©" subtitle = "Chargez un fichier PDF ou JSON pour commencer Ć  modifier le texte." +dropzone = "Glissez-dĆ©posez un fichier PDF ou JSON ici, ou cliquez pour parcourir" +dropzoneWithFiles = "SĆ©lectionnez un fichier depuis l'onglet Fichiers, ou glissez-dĆ©posez un fichier PDF ou JSON ici, ou cliquez pour parcourir" [pdfTextEditor.welcomeBanner] title = "Bienvenue dans l'Ć©diteur de texte PDF (accĆØs anticipĆ©)" diff --git a/frontend/public/locales/ga-IE/translation.toml b/frontend/public/locales/ga-IE/translation.toml index d34fbfba57..868f4203a1 100644 --- a/frontend/public/locales/ga-IE/translation.toml +++ b/frontend/public/locales/ga-IE/translation.toml @@ -163,6 +163,11 @@ unfavorite = "Bain den CheanĆ”in" fullscreen = "Athraigh go mód lĆ”nscĆ”ileĆ”in" sidebar = "Athraigh go mód barra taoibh" +[backendStartup] +notFoundTitle = "NĆ­or aimsĆ­odh an cĆŗlchóras" +retry = "Atriail" +unreachable = "NĆ­ fĆ©idir leis an bhfeidhmchlĆ”r ceangal leis an gcĆŗlchóras faoi lĆ”thair. Deimhnigh stĆ”das an chĆŗlchórais agus nascacht an lĆ­onra, ansin bain triail eile as." + [zipWarning] title = "Comhad ZIP Mór" message = "TĆ” {{count}} comhad sa ZIP seo. An mbaineann tĆŗ amach mar sin fĆ©in?" @@ -912,6 +917,9 @@ desc = "Tóg sreafaĆ­ oibre ilchĆ©ime trĆ­ ghnĆ­omhartha PDF a nascadh le chĆ©il desc = "Forleagain PDF ar bharr PDF eile" title = "Forleagan PDF" +[home.pdfTextEditor] +title = "Eagarthóir TĆ©acs PDF" +desc = "Cuir tĆ©acs agus Ć­omhĆ”nna atĆ” ann cheana in eagar laistigh de PDFanna" [home.addText] tags = "tĆ©acs,anótĆ”il,lipĆ©ad" @@ -1635,7 +1643,7 @@ subtitle = "ƍoslódĆ”il an comhad próiseĆ”ilte nó cealaigh an oibrĆ­ocht thĆ­ [removePages] tags = "Bain leathanaigh, scrios leathanaigh" title = "Bain" -filenamePrefix = "pages_removed" +filenamePrefix = "leathanaigh_bainte" submit = "Bain" [removePages.pageNumbers] @@ -1837,7 +1845,7 @@ title = "Bain LĆ©amh-AmhĆ”in ó RĆ©imsĆ­ Foirme" header = "DĆ­ghlasĆ”il Foirmeacha PDF" submit = "Remove" description = "Bainfidh an uirlis seo srianta lĆ©amh-amĆ”in ó rĆ©imsĆ­ foirme PDF, rud a fhĆ”gann go mbeidh siad in-eagarthóireachta agus inlĆ­onta." -filenamePrefix = "unlocked_forms" +filenamePrefix = "foirmeacha_dĆ­ghlasĆ”ilte" [unlockPDFForms.files] placeholder = "Roghnaigh comhad PDF sa phrĆ­omh-amharc chun tosĆŗ" @@ -1851,7 +1859,7 @@ title = "TorthaĆ­ DĆ­ghlasĆ”la Foirmeacha" [changeMetadata] header = "Athraigh MeiteashonraĆ­" submit = "AthrĆŗ" -filenamePrefix = "metadata" +filenamePrefix = "meiteashonraĆ­" [changeMetadata.settings] title = "Socruithe MeiteashonraĆ­" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "SĆ­niĆŗ lĆ­nĆ­ochta" defaultImageLabel = "SĆ­niĆŗ uaslódĆ”ilte" defaultTextLabel = "SĆ­niĆŗ clóscrĆ­ofa" saveButton = "SĆ”bhĆ”il sĆ­niĆŗ" +savePersonal = "SĆ”bhĆ”il Pearsanta" +saveShared = "SĆ”bhĆ”il Comhroinnte" saveUnavailable = "Cruthaigh sĆ­niĆŗ ar dtĆŗs chun Ć© a shĆ”bhĆ”il." noChanges = "TĆ” an sĆ­niĆŗ reatha sĆ”bhĆ”ilte cheana." +tempStorageTitle = "StórĆ”il shealadach an bhrabhsĆ”laĆ­" +tempStorageDescription = "StórĆ”iltear na sĆ­nithe i do bhrabhsĆ”laĆ­ amhĆ”in. Caillfear iad mĆ” ghlanann tĆŗ sonraĆ­ an bhrabhsĆ”laĆ­ nó mĆ” athraĆ­onn tĆŗ brabhsĆ”laithe." +personalHeading = "SĆ­nithe Pearsanta" +sharedHeading = "SĆ­nithe Comhroinnte" +personalDescription = "NĆ­ fĆ©idir ach leatsa na sĆ­nithe seo a fheiceĆ”il." +sharedDescription = "Is fĆ©idir le gach ĆŗsĆ”ideoir na sĆ­nithe seo a fheiceĆ”il agus a ĆŗsĆ”id." [sign.saved.type] canvas = "LĆ­nĆ­ocht" @@ -2318,7 +2334,7 @@ title = "Comhcheangail" header = "PDF cothromĆŗ" flattenOnlyForms = "Flatten foirmeacha amhĆ”in" submit = "Comhcheangail" -filenamePrefix = "flattened" +filenamePrefix = "maolaithe" [flatten.files] placeholder = "Roghnaigh comhad PDF sa phrĆ­omh-amharc chun tosĆŗ" @@ -2366,7 +2382,7 @@ title = "DeisiĆŗchĆ”n" header = "PDF a dheisiĆŗ" submit = "DeisiĆŗchĆ”n" description = "DĆ©anfaidh an uirlis seo iarracht comhaid PDF truaillithe nó damĆ”iste a dheisiĆŗ. NĆ­l aon socruithe breise ag teastĆ”il." -filenamePrefix = "repaired" +filenamePrefix = "deisithe" [repair.files] placeholder = "Roghnaigh comhad PDF sa phrĆ­omh-amharc chun tosĆŗ" @@ -2567,7 +2583,7 @@ stopButton = "Stop comparĆ”id" [certSign] tags = "fĆ­ordheimhnigh, PEM, P12, oifigiĆŗil, criptigh" title = "SĆ­niĆŗ Teastais" -filenamePrefix = "signed" +filenamePrefix = "sĆ­nĆ­the" chooseCertificate = "Roghnaigh Comhad Teastais" chooseJksFile = "Roghnaigh Comhad JKS" chooseP12File = "Roghnaigh Comhad PKCS12" @@ -2701,7 +2717,7 @@ header = "Bain an deimhniĆŗ digiteach ó PDF" selectPDF = "Roghnaigh comhad PDF:" submit = "Bain SĆ­niĆŗ" description = "Bainfidh an uirlis seo sĆ­nithe teastais dhigiteacha de do dhoicimĆ©ad PDF." -filenamePrefix = "unsigned" +filenamePrefix = "neamhshĆ­nithe" [removeCertSign.files] placeholder = "Roghnaigh comhad PDF sa phrĆ­omh-amharc chun tosĆŗ" @@ -3020,6 +3036,91 @@ title = "Faigh eolas ar PDF" header = "Faigh eolas ar PDF" submit = "Faigh Eolas" downloadJson = "ƍosluchtaigh ceol JSON" +processing = "Ag eastóscadh faisnĆ©ise..." +results = "TorthaĆ­" +noResults = "Rith an uirlis chun tuairisc a ghiniĆŗint." +downloads = "ƍoslódĆ”lacha" +noneDetected = "NĆ­or braitheadh aon cheann" +indexTitle = "InnĆ©acs" + +[getPdfInfo.report] +entryLabel = "Achoimre iomlĆ”n eolais" +shortTitle = "Eolas PDF" + +[getPdfInfo.sections] +metadata = "MeiteashonraĆ­" +formFields = "RĆ©imsĆ­ Foirme" +basicInfo = "Buneolas" +documentInfo = "Eolas faoin DoicimĆ©ad" +compliance = "ComhlĆ­onadh" +encryption = "CriptiĆŗ" +permissions = "Ceadanna" +other = "Eile" +perPageInfo = "Eolas in aghaidh an leathanaigh" +tableOfContents = "ClĆ”r Ɓbhair" + +[getPdfInfo.other] +attachments = "IatĆ”in" +embeddedFiles = "Comhaid Leabaithe" +javaScript = "JavaScript" +layers = "Sraitheanna" +structureTree = "Crann StruchtĆŗir" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "MĆ©id" +annotations = "AnótĆ”lacha" +images = "ƍomhĆ”nna" +links = "Naisc" +fonts = "Clónna" +xobjects = "LĆ­on XObjectanna" +multimedia = "IlmheĆ”in" + +[getPdfInfo.summary] +pages = "Leathanaigh" +fileSize = "MĆ©id Comhaid" +pdfVersion = "Leagan PDF" +language = "Teanga" +title = "Achoimre PDF" +author = "Údar" +created = "Cruthaithe" +modified = "Athraithe" +permsAll = "Gach cead ceadaithe" +permsRestricted = "{{count}} srianta" +permsMixed = "TĆ” roinnt ceadanna srianta" +hasCompliance = "TĆ” caighdeĆ”in chomhlĆ­onta ann" +noCompliance = "Gan chaighdeĆ”in chomhlĆ­onta" +basic = "Buneolas" +documentInfo = "Eolas faoin DoicimĆ©ad" +securityTitle = "StĆ”das SlĆ”ndĆ”la" +technical = "TeicniĆŗil" +overviewTitle = "ForbhreathnĆŗ PDF" + +[getPdfInfo.summary.security] +encrypted = "PDF criptithe - cosaint le focal faire" +unencrypted = "PDF neamchriptithe - gan chosaint le focal faire" + +[getPdfInfo.summary.tech] +images = "ƍomhĆ”nna" +fonts = "Clónna" +formFields = "RĆ©imsĆ­ Foirme" +embeddedFiles = "Comhaid Leabaithe" +javaScript = "JavaScript" +layers = "Sraitheanna" +bookmarks = "Leabharmharcanna" +multimedia = "IlmheĆ”in" + +[getPdfInfo.summary.overview] +untitled = "doicimĆ©ad gan teideal" +unknown = "Údar Anaithnid" +text = "Is PDF {{pages}} leathanach dar teideal {{title}} Ć©, a chruthaigh {{author}} (leagan PDF {{version}})." + +[getPdfInfo.error] +partial = "NĆ­orbh fhĆ©idir roinnt comhad a phróiseĆ”il." +unexpected = "EarrĆ”id gan choinne le linn eastósctha." + +[getPdfInfo.status] +complete = "Eastóscadh crĆ­ochnaithe" [extractPage] tags = "sliocht" @@ -3438,6 +3539,9 @@ signinTitle = "SĆ­nigh isteach le do thoil" ssoSignIn = "LogĆ”il isteach trĆ­ ChlĆ”rĆŗ Aonair" oAuth2AutoCreateDisabled = "OAUTH2 Uath-Chruthaigh ÚsĆ”ideoir faoi MhĆ­chumas" oAuth2AdminBlockedUser = "TĆ” bac faoi lĆ”thair ar chlĆ”rĆŗ nó logĆ”il isteach ĆŗsĆ”ideoirĆ­ neamhchlĆ”raithe. DĆ©an teagmhĆ”il leis an riarthóir le do thoil." +oAuth2RequiresLicense = "TeastaĆ­onn ceadĆŗnas Ć­octha (Server nó Enterprise) chun logĆ”il isteach le OAuth/SSO. DĆ©an teagmhĆ”il leis an riarthóir chun do phlean a uasghrĆ”dĆŗ." +saml2RequiresLicense = "TeastaĆ­onn ceadĆŗnas Ć­octha (Server nó Enterprise) chun logĆ”il isteach le SAML. DĆ©an teagmhĆ”il leis an riarthóir chun do phlean a uasghrĆ”dĆŗ." +maxUsersReached = "Sroicheadh an lĆ­on uasta ĆŗsĆ”ideoirĆ­ do do cheadĆŗnas reatha. DĆ©an teagmhĆ”il leis an riarthóir chun do phlean a uasghrĆ”dĆŗ nó suĆ­ochĆ”in bhreise a chur leis." oauth2RequestNotFound = "NĆ­or aimsĆ­odh iarratas Ćŗdaraithe" oauth2InvalidUserInfoResponse = "Freagra NeamhbhailĆ­ FaisnĆ©ise ÚsĆ”ideora" oauth2invalidRequest = "Iarratas NeamhbhailĆ­" @@ -3533,7 +3637,7 @@ title = "PDF go leathanach amhĆ”in" header = "PDF go leathanach amhĆ”in" submit = "Tiontaigh go Leathanach Aonair" description = "Cuirfidh an uirlis seo gach leathanach de do PDF le chĆ©ile in aon leathanach mór amhĆ”in. Fanfaidh an leithead mar an gcĆ©anna leis na leathanaigh bhunaidh, ach beidh an airde cothrom le suim airde na leathanach go lĆ©ir." -filenamePrefix = "single_page" +filenamePrefix = "leathanach_aonair" [pdfToSinglePage.files] placeholder = "Roghnaigh comhad PDF sa phrĆ­omh-amharc chun tosĆŗ" @@ -3846,14 +3950,17 @@ fitToWidth = "OiriĆŗnaigh don Leithead" actualSize = "FĆ­ormhĆ©id" [viewer] +cannotPreviewFile = "NĆ­ fĆ©idir an comhad a rĆ©amhamharc." +dualPageView = "Amharc DhĆ” Leathanach" firstPage = "An ChĆ©ad Leathanach" lastPage = "An Leathanach Deireanach" -previousPage = "Leathanach Roimhe Seo" nextPage = "Leathanach Ar Aghaidh" +onlyPdfSupported = "NĆ­ thacaĆ­onn an t-amharcĆ”n ach le comhaid PDF. Is cosĆŗil gur formĆ”id eile Ć© an comhad seo." +previousPage = "Leathanach Roimhe Seo" +singlePageView = "Amharc Leathanach Aonair" +unknownFile = "Comhad anaithnid" zoomIn = "SĆŗmĆ”il Isteach" zoomOut = "SĆŗmĆ”il Amach" -singlePageView = "Amharc Leathanach Aonair" -dualPageView = "Amharc DhĆ” Leathanach" [rightRail] closeSelected = "DĆŗn na Comhaid Roghnaithe" @@ -3877,6 +3984,7 @@ toggleSidebar = "Athraigh an Barra Taoibh" exportSelected = "EaspórtĆ”il na Leathanaigh Roghnaithe" toggleAnnotations = "Athraigh Infheictheacht AnótĆ”lacha" annotationMode = "Athraigh Mód AnótĆ”la" +print = "PriontĆ”il PDF" draw = "Tarraing" save = "SĆ”bhĆ”il" saveChanges = "SĆ”bhĆ”il Athruithe" @@ -4494,6 +4602,7 @@ description = "URL nó ainm comhaid don impressum (riachtanach i roinnt dlĆ­nsĆ­ title = "PrĆ©imh & Fiontar" description = "Cumraigh do eochair cheadĆŗnais prĆ©imhe nó fiontair." license = "CumraĆ­ocht CeadĆŗnais" +noInput = "Tabhair eochair nó comhad ceadĆŗnais, le do thoil" [admin.settings.premium.licenseKey] toggle = "An bhfuil eochair cheadĆŗnais nó comhad teastais agat?" @@ -4511,6 +4620,25 @@ line1 = "NĆ­ fĆ©idir forshcrĆ­obh ar do eochair cheadĆŗnais reatha a chealĆŗ." line2 = "Caillefar do cheadĆŗnas roimhe seo go buan mura bhfuil cĆŗltaca de in Ć”it eile agat." line3 = "TĆ”bhachtach: Coinnigh eochracha ceadĆŗnais prĆ­obhĆ”ideach agus slĆ”n. NĆ” roinn go poiblĆ­ riamh." +[admin.settings.premium.inputMethod] +text = "Eochair CeadĆŗnais" +file = "Comhad Teastais" + +[admin.settings.premium.file] +label = "Comhad Teastais CeadĆŗnais" +description = "UaslódĆ”il do chomhad ceadĆŗnais .lic nó .cert ó cheannachĆ”in as lĆ­ne" +choose = "Roghnaigh Comhad CeadĆŗnais" +selected = "Roghnaithe: {{filename}} ({{size}})" +successMessage = "D’éirigh le huaslódĆ”il agus gnĆ­omhachtĆŗ an chomhaid cheadĆŗnais. NĆ­l atosĆŗ ag teastĆ”il." + +[admin.settings.premium.currentLicense] +title = "CeadĆŗnas GnĆ­omhach" +file = "Foinse: Comhad ceadĆŗnais ({{path}})" +key = "Foinse: Eochair ceadĆŗnais" +type = "CineĆ”l: {{type}}" +noInput = "Tabhair eochair ceadĆŗnais nó uaslódĆ”il comhad teastais, le do thoil" +success = "Rath" + [admin.settings.premium.enabled] label = "Cumasaigh GnĆ©ithe PrĆ©imhe" description = "Cumasaigh seiceĆ”lacha eochrach ceadĆŗnais do ghnĆ©ithe pro/fiontair" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} roghnaithe" download = "ƍosluchtaigh" delete = "Scrios" unsupported = "Gan tacaĆ­ocht" +active = "GnĆ­omhach" addToUpload = "Cuir leis an UaslódĆ”il" +closeFile = "DĆŗn an comhad" deleteAll = "Scrios Uile" loadingFiles = "Comhaid Ć” LuchtĆŗ..." noFiles = "NĆ­l comhaid ar fĆ”il" @@ -5132,7 +5262,7 @@ upgrade = "UasghrĆ”daigh anois →" freeTitle = "CeadĆŗnas FreastalaĆ­" overLimitTitle = "CeadĆŗnas FreastalaĆ­ de dhĆ­th" overLimitBody = "CeadaĆ­onn Ć”r gceadĆŗnĆŗ suas le {{freeTierLimit}} ĆŗsĆ”ideoir in aisce in aghaidh freastalaĆ­. TĆ” {{overLimitUserCopy}} ĆŗsĆ”ideoir Stirling agat. Chun leanĆŗint gan bhriseadh, uasghrĆ”daigh go plean FreastalaĆ­ Stirling - suĆ­ochĆ”in neamhtheoranta, eagarthóireacht tĆ©acs PDF, agus lĆ”nrialĆŗ riarachĆ”in ar $99/freastalaĆ­/mĆ­." -freeBody = "CeadaĆ­onn Ć”r gceadĆŗnĆŗ Open-Core suas le {{freeTierLimit}} ĆŗsĆ”ideoir in aisce in aghaidh freastalaĆ­. Chun mĆ©adĆŗ gan bhriseadh agus rochtain luath a fhĆ”il ar Ć”r uirlis eagarthóireachta tĆ©acs PDF nua, molaimid Plean FreastalaĆ­ Stirling - eagarthóireacht iomlĆ”n agus suĆ­ochĆ”in neamhtheoranta ar $99/freastalaĆ­/mĆ­." +freeBody = "CeadaĆ­onn Ć”r gceadĆŗnĆŗ Open-Core suas le {{freeTierLimit}} ĆŗsĆ”ideoirĆ­ saor in aisce in aghaidh an fhreastalaĆ­. Chun scĆ”lĆŗ gan bhriseadh, molaimid an plean Stirling Server - suĆ­ochĆ”in neamhtheoranta agus tacaĆ­ocht SSO ar $99/server/mo." [onboarding.desktopInstall] title = "ƍoslódĆ”il" @@ -5237,6 +5367,31 @@ error = "Theip ar stĆ”das ĆŗsĆ”ideora a nuashonrĆŗ" success = "Scriosadh an t-ĆŗsĆ”ideoir go rathĆŗil" error = "Theip ar an ĆŗsĆ”ideoir a scriosadh" +[workspace.people.changePassword] +action = "Athraigh an focal faire" +title = "Athraigh an focal faire" +subtitle = "Nuashonraigh an focal faire do" +newPassword = "Focal faire nua" +confirmPassword = "Deimhnigh an focal faire" +placeholder = "Cuir focal faire nua isteach" +confirmPlaceholder = "Cuir an focal faire nua isteach arĆ­s" +passwordRequired = "Cuir focal faire nua isteach le do thoil" +passwordMismatch = "NĆ­ hionann na focail faire" +generateRandom = "Gin focal faire slĆ”n" +generatedPreview = "Focal faire ginte:" +copyTooltip = "CóipeĆ”il chuig an ngearrthaisce" +copiedToClipboard = "CóipeĆ”ladh an focal faire chuig an ngearrthaisce" +copyFailed = "NĆ­or Ć©irigh le cóipeĆ”il an fhocail fhaire" +sendEmail = "Seol rĆ­omhphost chuig an ĆŗsĆ”ideoir faoin athrĆŗ seo" +includePassword = "Cuir an focal faire nua san rĆ­omhphost" +forcePasswordChange = "Cuir iallach ar an ĆŗsĆ”ideoir an focal faire a athrĆŗ ag an gcĆ©ad logĆ”il isteach eile" +emailUnavailable = "NĆ­l seoladh rĆ­omhphoist bailĆ­ ag an ĆŗsĆ”ideoir seo. TĆ” fógraĆ­ dĆ­chumasaithe." +smtpDisabled = "TeastaĆ­onn SMTP cumasaithe sna socruithe le haghaidh fógraĆ­ rĆ­omhphoist." +notifyOnly = "Seolfar rĆ­omhphost gan an focal faire, ag cur in iĆŗl don ĆŗsĆ”ideoir gur d’athraigh riarthóir Ć©." +submit = "Nuashonraigh an focal faire" +success = "NuashonraĆ­odh an focal faire go rathĆŗil" +error = "NĆ­or Ć©irigh le focal faire a nuashonrĆŗ" + [workspace.people.emailInvite] tab = "Cuireadh RĆ­omhphoist" description = "ClóscrĆ­obh nó greamaigh seoltaĆ­ rĆ­omhphoist thĆ­os, scartha le camóga. Gheobhaidh ĆŗsĆ”ideoirĆ­ dintiĆŗir logĆ”la isteach trĆ­ r-phost." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "TĆ” ar a laghad seoladh rĆ­omhphoist amhĆ”in de dhĆ­th" submit = "Seol CuirĆ­" success = "Tugadh cuireadh d’úsĆ”ideoir(Ć­) go rathĆŗil" -partialSuccess = "Theip ar chuid de na cuirĆ­" +partialFailure = "NĆ­or Ć©irigh le roinnt cuirĆ­" allFailed = "Theip ar ĆŗsĆ”ideoirĆ­ a thabhairt isteach" error = "Theip ar churĆ­ a sheoladh" @@ -5288,8 +5443,8 @@ emailDisabled = "TeastaĆ­onn cumraĆ­ocht SMTP agus mail.enableInvites=true sna s [workspace.people.license] users = "ĆŗsĆ”ideoirĆ­" availableSlots = "Ɓiteanna Ar FĆ”il" -grandfathered = "Grandfathered" -grandfatheredShort = "{{count}} grandfathered" +grandfathered = "Ceadaithe roimhe seo" +grandfatheredShort = "{{count}} ceadaithe roimhe seo" fromLicense = "ón gceadĆŗnas" slotsAvailable = "{{count}} Ć”it(Ć­) ĆŗsĆ”ideora ar fĆ”il" noSlotsAvailable = "NĆ­l aon Ć”iteanna ar fĆ”il" @@ -5770,6 +5925,7 @@ subtitle = "SĆ­nigh isteach le do chuntas Stirling" [setup.selfhosted] title = "SĆ­nigh isteach chuig an bhFreastalaĆ­" subtitle = "Cuir isteach dintiĆŗir do fhreastalaĆ­" +link = "nó ceangail le cuntas fĆ©inóstĆ”ilte" [setup.server] title = "Ceangail leis an bhFreastalaĆ­" @@ -5788,6 +5944,14 @@ description = "Cuir isteach URL iomlĆ”n do fhreastalaĆ­ Stirling PDF fĆ©in-óst emptyUrl = "Cuir isteach URL freastalaĆ­ le do thoil" unreachable = "NĆ­orbh fhĆ©idir ceangal leis an bhfreastalaĆ­" testFailed = "Theip ar thĆ”stĆ”il an cheangail" +configFetch = "NĆ­or Ć©irigh le cumraĆ­ocht an fhreastalaĆ­ a fhĆ”il. SeiceĆ”il an URL agus bain triail eile as." + +[setup.server.error.securityDisabled] +title = "NĆ­l an LogĆ”il Isteach Cumasaithe" +body = "NĆ­l logĆ”il isteach cumasaithe ar an bhfreastalaĆ­ seo. Chun ceangal leis an bhfreastalaĆ­ seo, nĆ­ mór duit fĆ­ordheimhniĆŗ a chumasĆŗ:" +step1 = "Socraigh DOCKER_ENABLE_SECURITY=true i do thimpeallacht" +step2 = "Nó socraigh security.enableLogin=true i settings.yml" +step3 = "Atosaigh an freastalaĆ­" [setup.login] title = "SĆ­nigh Isteach" @@ -5797,6 +5961,13 @@ submit = "LogĆ”il Isteach" signInWith = "SĆ­nigh isteach le" oauthPending = "BrabhsĆ”laĆ­ Ć” oscailt le haghaidh fĆ­ordheimhnithe..." orContinueWith = "Nó lean ar aghaidh le rĆ­omhphost" +serverRequirement = "Nóta: NĆ­ mór an cumas logĆ”la isteach a bheith cumasaithe ar an bhfreastalaĆ­." +showInstructions = "Conas Ć© a chumasĆŗ?" +hideInstructions = "Folaigh na treoracha" +instructions = "Chun logĆ”il isteach a chumasĆŗ ar do fhreastalaĆ­ Stirling PDF:" +instructionsEnvVar = "Socraigh an athróg chomhshaoil:" +instructionsOrYml = "Nó i settings.yml:" +instructionsRestart = "Ansin atosaigh do fhreastalaĆ­ chun go mbeidh na hathruithe i bhfeidhm." [setup.login.username] label = "Ainm ÚsĆ”ideora" @@ -5853,6 +6024,7 @@ earlyAccess = "Rochtain Luath" reset = "Athshocraigh Athruithe" downloadJson = "ƍoslódĆ”il JSON" generatePdf = "Gin PDF" +saveChanges = "SĆ”bhĆ”il Athruithe" [pdfTextEditor.options.autoScaleText] title = "ScĆ”laigh tĆ©acs go huathoibrĆ­och chun boscaĆ­ a fheistiĆŗ" @@ -5890,6 +6062,8 @@ alpha = "TĆ” an t-amharcóir alfa seo fós ag forbairt — d’fhĆ©adfadh clónn [pdfTextEditor.empty] title = "NĆ­l aon chĆ”ipĆ©is luchtaithe" subtitle = "Luchtaigh comhad PDF nó JSON chun eagarthóireacht ar Ć”bhar tĆ©acs a thosĆŗ." +dropzone = "Tarraing agus scaoil comhad PDF nó JSON anseo, nó cliceĆ”il chun brabhsĆ”il" +dropzoneWithFiles = "Roghnaigh comhad ón gcluaisĆ­n Comhaid, nó tarraing agus scaoil comhad PDF nó JSON anseo, nó cliceĆ”il chun brabhsĆ”il" [pdfTextEditor.welcomeBanner] title = "FĆ”ilte go dtĆ­ Eagarthóir TĆ©acs PDF (Rochtain Luath)" diff --git a/frontend/public/locales/hi-IN/translation.toml b/frontend/public/locales/hi-IN/translation.toml index f0fcf78333..c636e7eacb 100644 --- a/frontend/public/locales/hi-IN/translation.toml +++ b/frontend/public/locales/hi-IN/translation.toml @@ -131,7 +131,7 @@ unsupported = "ą¤…ą¤øą¤®ą¤°ą„ą¤„ą¤æą¤¤" [toolPanel] placeholder = "ą¤¶ą„ą¤°ą„‚ ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤•ą„‹ą¤ˆ ą¤Ÿą„‚ą¤² ą¤šą„ą¤Øą„‡ą¤‚" -alpha = "Alpha" +alpha = "ą¤…ą¤²ą„ą¤«ą¤¾" premiumFeature = "ą¤Ŗą„ą¤°ą„€ą¤®ą¤æą¤Æą¤® ą¤«ą„€ą¤šą¤°:" comingSoon = "ą¤œą¤²ą„ą¤¦ आ रहा ą¤¹ą„ˆ:" @@ -163,6 +163,11 @@ unfavorite = "ą¤Ŗą¤øą¤‚ą¤¦ą„€ą¤¦ą¤¾ ą¤øą„‡ ą¤¹ą¤Ÿą¤¾ą¤ą¤‚" fullscreen = "ą¤«ą„ą¤²ą¤øą„ą¤•ą„ą¤°ą„€ą¤Ø ą¤®ą„‹ą¤” पर ą¤øą„ą¤µą¤æą¤š ą¤•ą¤°ą„‡ą¤‚" sidebar = "साइऔबार ą¤®ą„‹ą¤” पर ą¤øą„ą¤µą¤æą¤š ą¤•ą¤°ą„‡ą¤‚" +[backendStartup] +notFoundTitle = "ą¤¬ą„ˆą¤•ą¤ą¤‚ą¤” ą¤Øą¤¹ą„€ą¤‚ मिला" +retry = "ą¤Ŗą„ą¤Øą¤ƒ ą¤Ŗą„ą¤°ą¤Æą¤¾ą¤ø ą¤•ą¤°ą„‡ą¤‚" +unreachable = "ą¤ą¤Ŗą„ą¤²ą¤æą¤•ą„‡ą¤¶ą¤Ø फिलहाल ą¤¬ą„ˆą¤•ą¤ą¤‚ą¤” ą¤øą„‡ ą¤•ą¤Øą„‡ą¤•ą„ą¤Ÿ ą¤Øą¤¹ą„€ą¤‚ ą¤¹ą„‹ पा रहा ą¤¹ą„ˆą„¤ ą¤•ą„ƒą¤Ŗą¤Æą¤¾ ą¤¬ą„ˆą¤•ą¤ą¤‚ą¤” ą¤•ą„€ ą¤øą„ą¤„ą¤æą¤¤ą¤æ और ą¤Øą„‡ą¤Ÿą¤µą¤°ą„ą¤• ą¤•ą¤Øą„‡ą¤•ą„ą¤Ÿą¤æą¤µą¤æą¤Ÿą„€ ą¤œą¤¾ą¤‚ą¤šą„‡ą¤‚, फिर ą¤Ŗą„ą¤Øą¤ƒ ą¤Ŗą„ą¤°ą¤Æą¤¾ą¤ø ą¤•ą¤°ą„‡ą¤‚ą„¤" + [zipWarning] title = "ą¤¬ą¤”ą¤¼ą„€ ZIP फ़ाइल" message = "इस ZIP ą¤®ą„‡ą¤‚ {{count}} ą¤«ą¤¼ą¤¾ą¤‡ą¤²ą„‡ą¤‚ ą¤¹ą„ˆą¤‚ą„¤ फिर ą¤­ą„€ ą¤Øą¤æą¤•ą¤¾ą¤²ą„‡ą¤‚?" @@ -369,7 +374,7 @@ privacy = "ą¤—ą„‹ą¤Ŗą¤Øą„€ą¤Æą¤¤ą¤¾" [settings.developer] title = "ą¤”ą„‡ą¤µą¤²ą¤Ŗą¤°" -apiKeys = "API Keys" +apiKeys = "API ą¤•ą„ą¤‚ą¤œą¤æą¤Æą¤¾ą¤" [settings.tooltips] enableLoginFirst = "ą¤Ŗą¤¹ą¤²ą„‡ ą¤²ą„‰ą¤—ą¤æą¤Ø ą¤®ą„‹ą¤” ą¤øą¤•ą„ą¤·ą¤® ą¤•ą¤°ą„‡ą¤‚" @@ -829,7 +834,7 @@ title = "PDF ą¤¹ą¤øą„ą¤¤ą¤¾ą¤•ą„ą¤·ą¤° ą¤øą¤¤ą„ą¤Æą¤¾ą¤Ŗą¤æą¤¤ ą¤•ą¤°ą„‡ą¤‚" desc = "PDF ą¤¦ą¤øą„ą¤¤ą¤¾ą¤µą„‡ą¤œą„‹ą¤‚ ą¤®ą„‡ą¤‚ औिजिटल ą¤¹ą¤øą„ą¤¤ą¤¾ą¤•ą„ą¤·ą¤° और ą¤Ŗą„ą¤°ą¤®ą¤¾ą¤£ą¤Ŗą¤¤ą„ą¤°ą„‹ą¤‚ ą¤•ą„‹ ą¤øą¤¤ą„ą¤Æą¤¾ą¤Ŗą¤æą¤¤ ą¤•ą¤°ą„‡ą¤‚" [home.swagger] -tags = "API,documentation,test" +tags = "API,ą¤¦ą¤øą„ą¤¤ą¤¾ą¤µą„‡ą¤œą¤¼ą„€ą¤•ą¤°ą¤£,ą¤Ŗą¤°ą„€ą¤•ą„ą¤·ą¤£" title = "API ą¤¦ą¤øą„ą¤¤ą¤¾ą¤µą„‡ą¤œą¤¼ą„€ą¤•ą¤°ą¤£" desc = "API ą¤¦ą¤øą„ą¤¤ą¤¾ą¤µą„‡ą¤œą¤¼ ą¤¦ą„‡ą¤–ą„‡ą¤‚ और ą¤ą¤‚ą¤”ą¤Ŗą„‰ą¤‡ą¤‚ą¤Ÿ ą¤Ÿą„‡ą¤øą„ą¤Ÿ ą¤•ą¤°ą„‡ą¤‚" @@ -878,7 +883,7 @@ title = "रंग ą¤¬ą¤¦ą¤²ą„‡ą¤‚/ą¤‰ą¤²ą¤Ÿą„‡ą¤‚" desc = "PDF ą¤¦ą¤øą„ą¤¤ą¤¾ą¤µą„‡ą¤œą¤¼ą„‹ą¤‚ ą¤®ą„‡ą¤‚ ą¤°ą¤‚ą¤—ą„‹ą¤‚ ą¤•ą„‹ ą¤Ŗą„ą¤°ą¤¤ą¤æą¤øą„ą¤„ą¤¾ą¤Ŗą¤æą¤¤ या ą¤‰ą¤²ą¤Ÿą„‡ą¤‚" [home.devApi] -tags = "API,development,documentation" +tags = "API,विकास,ą¤¦ą¤øą„ą¤¤ą¤¾ą¤µą„‡ą¤œą¤¼ą„€ą¤•ą¤°ą¤£" title = "API" desc = "API ą¤¦ą¤øą„ą¤¤ą¤¾ą¤µą„‡ą¤œą¤¼ ą¤•ą„‡ ą¤²ą¤æą¤ लिंक" @@ -912,9 +917,12 @@ desc = "PDF ą¤•ą„ą¤°ą¤æą¤Æą¤¾ą¤“ą¤‚ ą¤•ą„‹ ą¤œą„‹ą¤”ą¤¼ą¤•ą¤° ą¤¬ą¤¹ą„-चर desc = "PDF ą¤•ą„‹ ą¤¦ą„‚ą¤øą¤°ą„€ PDF ą¤•ą„‡ ऊपर ą¤“ą¤µą¤°ą¤²ą„‡ ą¤•ą¤°ą„‡ą¤‚" title = "PDF ą¤“ą¤µą¤°ą¤²ą„‡ ą¤•ą¤°ą„‡ą¤‚" +[home.pdfTextEditor] +title = "PDF ą¤Ÿą„‡ą¤•ą„ą¤øą„ą¤Ÿ ą¤ą¤”ą¤æą¤Ÿą¤°" +desc = "PDF ą¤«ą¤¼ą¤¾ą¤‡ą¤²ą„‹ą¤‚ ą¤•ą„‡ ą¤­ą„€ą¤¤ą¤° ą¤®ą„Œą¤œą„‚ą¤¦ą¤¾ ą¤Ÿą„‡ą¤•ą„ą¤øą„ą¤Ÿ और ą¤‡ą¤®ą„‡ą¤œ संपादित ą¤•ą¤°ą„‡ą¤‚" [home.addText] -tags = "text,annotation,label" +tags = "पाठ,ą¤Ÿą¤æą¤Ŗą„ą¤Ŗą¤£ą„€,ą¤²ą„‡ą¤¬ą¤²" title = "ą¤Ÿą„‡ą¤•ą„ą¤øą„ą¤Ÿ ą¤œą„‹ą¤”ą¤¼ą„‡ą¤‚" desc = "ą¤…ą¤Ŗą¤Øą„‡ PDF ą¤®ą„‡ą¤‚ ą¤•ą¤¹ą„€ą¤‚ ą¤­ą„€ ą¤•ą¤øą„ą¤Ÿą¤® ą¤Ÿą„‡ą¤•ą„ą¤øą„ą¤Ÿ ą¤œą„‹ą¤”ą¤¼ą„‡ą¤‚" @@ -1173,7 +1181,7 @@ selectFilesPlaceholder = "ą¤¶ą„ą¤°ą„‚ ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤®ą„ą¤– settings = "ą¤øą„‡ą¤Ÿą¤æą¤‚ą¤—ą„ą¤ø" conversionCompleted = "ą¤°ą„‚ą¤Ŗą¤¾ą¤‚ą¤¤ą¤°ą¤£ ą¤Ŗą„‚ą¤°ą¤¾ ą¤¹ą„ą¤†" results = "परिणाम" -defaultFilename = "converted_file" +defaultFilename = "ą¤Ŗą¤°ą¤æą¤µą¤°ą„ą¤¤ą¤æą¤¤_फ़ाइल" conversionResults = "ą¤°ą„‚ą¤Ŗą¤¾ą¤‚ą¤¤ą¤°ą¤£ परिणाम" convertFrom = "ą¤øą„‡ ą¤°ą„‚ą¤Ŗą¤¾ą¤‚ą¤¤ą¤°ą¤æą¤¤ ą¤•ą¤°ą„‡ą¤‚" convertTo = "ą¤®ą„‡ą¤‚ ą¤°ą„‚ą¤Ŗą¤¾ą¤‚ą¤¤ą¤°ą¤æą¤¤ ą¤•ą¤°ą„‡ą¤‚" @@ -1213,11 +1221,11 @@ pdfaDigitalSignatureWarning = "PDF ą¤®ą„‡ą¤‚ ą¤ą¤• औिजिटल ą¤¹ą¤øą„ fileFormat = "फ़ाइल ą¤«ą¤¼ą„‰ą¤°ą„ą¤®ą„‡ą¤Ÿ" wordDoc = "Word ą¤¦ą¤øą„ą¤¤ą¤¾ą¤µą„‡ą¤œą¤¼" wordDocExt = "Word ą¤¦ą¤øą„ą¤¤ą¤¾ą¤µą„‡ą¤œą¤¼ (.docx)" -odtExt = "OpenDocument Text (.odt)" +odtExt = "OpenDocument ą¤Ÿą„‡ą¤•ą„ą¤øą„ą¤Ÿ (.odt)" pptExt = "PowerPoint (.pptx)" -odpExt = "OpenDocument Presentation (.odp)" -txtExt = "Plain Text (.txt)" -rtfExt = "Rich Text Format (.rtf)" +odpExt = "OpenDocument ą¤Ŗą„ą¤°ą¤øą„ą¤¤ą„ą¤¤ą¤æ (.odp)" +txtExt = "सादा पाठ (.txt)" +rtfExt = "रिच ą¤Ÿą„‡ą¤•ą„ą¤øą„ą¤Ÿ ą¤«ą¤¼ą„‰ą¤°ą„ą¤®ą„‡ą¤Ÿ (.rtf)" selectedFiles = "चयनित ą¤«ą¤¼ą¤¾ą¤‡ą¤²ą„‡ą¤‚" noFileSelected = "ą¤•ą„‹ą¤ˆ फ़ाइल चयनित ą¤Øą¤¹ą„€ą¤‚ą„¤ ą¤«ą¤¼ą¤¾ą¤‡ą¤²ą„‡ą¤‚ ą¤œą„‹ą¤”ą¤¼ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ फ़ाइल ą¤Ŗą„ˆą¤Øą¤² का ą¤‰ą¤Ŗą¤Æą„‹ą¤— ą¤•ą¤°ą„‡ą¤‚ą„¤" convertFiles = "ą¤«ą¤¼ą¤¾ą¤‡ą¤²ą„‡ą¤‚ ą¤°ą„‚ą¤Ŗą¤¾ą¤‚ą¤¤ą¤°ą¤æą¤¤ ą¤•ą¤°ą„‡ą¤‚" @@ -1360,7 +1368,7 @@ title = "ą¤µą„‰ą¤Ÿą¤°ą¤®ą¤¾ą¤°ą„ą¤• ą¤œą„‹ą¤”ą¤¼ą„‡ą¤‚" desc = "PDF ą¤«ą¤¼ą¤¾ą¤‡ą¤²ą„‹ą¤‚ ą¤®ą„‡ą¤‚ ą¤Ÿą„‡ą¤•ą„ą¤øą„ą¤Ÿ या ą¤‡ą¤®ą„‡ą¤œ ą¤µą„‰ą¤Ÿą¤°ą¤®ą¤¾ą¤°ą„ą¤• ą¤œą„‹ą¤”ą¤¼ą„‡ą¤‚" completed = "ą¤µą„‰ą¤Ÿą¤°ą¤®ą¤¾ą¤°ą„ą¤• ą¤œą„‹ą¤”ą¤¼ą¤¾ गया" submit = "ą¤µą„‰ą¤Ÿą¤°ą¤®ą¤¾ą¤°ą„ą¤• ą¤œą„‹ą¤”ą¤¼ą„‡ą¤‚" -filenamePrefix = "watermarked" +filenamePrefix = "ą¤µą„‰ą¤Ÿą¤°ą¤®ą¤¾ą¤°ą„ą¤•_ą¤Æą„ą¤•ą„ą¤¤" [watermark.error] failed = "PDF ą¤®ą„‡ą¤‚ ą¤µą„‰ą¤Ÿą¤°ą¤®ą¤¾ą¤°ą„ą¤• ą¤œą„‹ą¤”ą¤¼ą¤¤ą„‡ समय ą¤ą¤• ą¤¤ą„ą¤°ą„ą¤Ÿą¤æ ą¤¹ą„ą¤ˆą„¤" @@ -1635,7 +1643,7 @@ subtitle = "ą¤Ŗą„ą¤°ą„‹ą¤øą„‡ą¤øą„ą¤” फ़ाइल ą¤”ą¤¾ą¤‰ą¤Øą¤²ą„‹ą¤” [removePages] tags = "ą¤Ŗą„ƒą¤·ą„ą¤  ą¤Øą¤æą¤•ą¤¾ą¤²ą„‡ą¤‚,ą¤Ŗą„ƒą¤·ą„ą¤  ą¤¹ą¤Ÿą¤¾ą¤ą¤‚" title = "ą¤Øą¤æą¤•ą¤¾ą¤²ą„‡ą¤‚" -filenamePrefix = "pages_removed" +filenamePrefix = "ą¤Ŗą„ƒą¤·ą„ą¤ _ą¤¹ą¤Ÿą¤¾ą¤_ą¤—ą¤" submit = "ą¤Øą¤æą¤•ą¤¾ą¤²ą„‡ą¤‚" [removePages.pageNumbers] @@ -1832,12 +1840,12 @@ title = "ą¤‰ą¤Øą„ą¤Øą¤¤" tags = "ą¤•ą¤®ą„ą¤Ŗą„ą¤°ą„‡ą¤ø,ą¤›ą„‹ą¤Ÿą¤¾,ą¤›ą„‹ą¤Ÿą¤¾" [unlockPDFForms] -tags = "remove,delete,form,field,readonly" +tags = "ą¤¹ą¤Ÿą¤¾ą¤ą¤‚,ą¤®ą¤æą¤Ÿą¤¾ą¤ą¤‚,ą¤«ą„‰ą¤°ą„ą¤®,ą¤«ą¤¼ą„€ą¤²ą„ą¤”,ą¤°ą„€ą¤”-ą¤“ą¤Øą¤²ą„€" title = "ą¤«ą„‰ą¤°ą„ą¤® ą¤«ą¤¼ą„€ą¤²ą„ą¤” ą¤øą„‡ Read-Only ą¤¹ą¤Ÿą¤¾ą¤ą¤‚" header = "PDF ą¤«ą„‰ą¤°ą„ą¤® ą¤…ą¤Øą¤²ą„‰ą¤• ą¤•ą¤°ą„‡ą¤‚" submit = "Remove" description = "यह ą¤Ÿą„‚ą¤² PDF ą¤«ą„‰ą¤°ą„ą¤® ą¤«ą¤¼ą„€ą¤²ą„ą¤” ą¤øą„‡ Read-Only ą¤Ŗą„ą¤°ą¤¤ą¤æą¤¬ą¤‚ą¤§ ą¤¹ą¤Ÿą¤¾ą¤ą¤—ą¤¾, ą¤œą¤æą¤øą¤øą„‡ ą¤µą„‡ संपादन ą¤Æą„‹ą¤—ą„ą¤Æ और ą¤­ą¤°ą¤Øą„‡ ą¤Æą„‹ą¤—ą„ą¤Æ ą¤¬ą¤Øą„‡ą¤‚ą¤—ą„‡ą„¤" -filenamePrefix = "unlocked_forms" +filenamePrefix = "ą¤…ą¤Øą¤²ą„‰ą¤•_ą¤«ą¤¼ą„‰ą¤°ą„ą¤®" [unlockPDFForms.files] placeholder = "ą¤¶ą„ą¤°ą„‚ ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤®ą„ą¤–ą„ą¤Æ ą¤¦ą„ƒą¤¶ą„ą¤Æ ą¤®ą„‡ą¤‚ ą¤ą¤• PDF फ़ाइल ą¤šą„ą¤Øą„‡ą¤‚" @@ -1851,7 +1859,7 @@ title = "ą¤…ą¤Øą¤²ą„‰ą¤• ą¤•ą¤æą¤ ą¤—ą¤ ą¤«ą„‰ą¤°ą„ą¤® ą¤•ą„‡ परिणा [changeMetadata] header = "ą¤®ą„‡ą¤Ÿą¤¾ą¤”ą„‡ą¤Ÿą¤¾ ą¤¬ą¤¦ą¤²ą„‡ą¤‚" submit = "ą¤¬ą¤¦ą¤²ą„‡ą¤‚" -filenamePrefix = "metadata" +filenamePrefix = "ą¤®ą„‡ą¤Ÿą¤¾ą¤”ą„‡ą¤Ÿą¤¾" [changeMetadata.settings] title = "ą¤®ą„‡ą¤Ÿą¤¾ą¤”ą„‡ą¤Ÿą¤¾ ą¤øą„‡ą¤Ÿą¤æą¤‚ą¤—ą„ą¤ø" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "ą¤”ą„ą¤°ą„‰ą¤‡ą¤‚ą¤— ą¤¹ą¤øą„ą¤¤ą¤¾ą¤•ą„ą¤·ą¤°" defaultImageLabel = "ą¤…ą¤Ŗą¤²ą„‹ą¤” किया गया ą¤¹ą¤øą„ą¤¤ą¤¾ą¤•ą„ą¤·ą¤°" defaultTextLabel = "ą¤Ÿą¤¾ą¤‡ą¤Ŗ किया ą¤¹ą„ą¤† ą¤¹ą¤øą„ą¤¤ą¤¾ą¤•ą„ą¤·ą¤°" saveButton = "ą¤¹ą¤øą„ą¤¤ą¤¾ą¤•ą„ą¤·ą¤° ą¤øą¤¹ą„‡ą¤œą„‡ą¤‚" +savePersonal = "ą¤µą„ą¤Æą¤•ą„ą¤¤ą¤æą¤—ą¤¤ ą¤øą¤¹ą„‡ą¤œą„‡ą¤‚" +saveShared = "ą¤øą¤¾ą¤ą¤¾ ą¤øą¤¹ą„‡ą¤œą„‡ą¤‚" saveUnavailable = "ą¤øą„‡ą¤µ ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤Ŗą¤¹ą¤²ą„‡ ą¤ą¤• ą¤¹ą¤øą„ą¤¤ą¤¾ą¤•ą„ą¤·ą¤° ą¤¬ą¤Øą¤¾ą¤ą¤ą„¤" noChanges = "ą¤µą¤°ą„ą¤¤ą¤®ą¤¾ą¤Ø ą¤¹ą¤øą„ą¤¤ą¤¾ą¤•ą„ą¤·ą¤° ą¤Ŗą¤¹ą¤²ą„‡ ą¤øą„‡ ą¤øą¤¹ą„‡ą¤œą¤¾ गया ą¤¹ą„ˆą„¤" +tempStorageTitle = "ą¤…ą¤øą„ą¤„ą¤¾ą¤Æą„€ ą¤¬ą„ą¤°ą¤¾ą¤‰ą¤œą¤¼ą¤° ą¤øą¤‚ą¤—ą„ą¤°ą¤¹ą¤£" +tempStorageDescription = "ą¤¹ą¤øą„ą¤¤ą¤¾ą¤•ą„ą¤·ą¤° ą¤•ą„‡ą¤µą¤² ą¤†ą¤Ŗą¤•ą„‡ ą¤¬ą„ą¤°ą¤¾ą¤‰ą¤œą¤¼ą¤° ą¤®ą„‡ą¤‚ ą¤øą¤‚ą¤—ą„ą¤°ą¤¹ą„€ą¤¤ ą¤¹ą„‹ą¤¤ą„‡ ą¤¹ą„ˆą¤‚ą„¤ ą¤¬ą„ą¤°ą¤¾ą¤‰ą¤œą¤¼ą¤° ą¤”ą„‡ą¤Ÿą¤¾ साफ़ ą¤•ą¤°ą¤Øą„‡ या ą¤¬ą„ą¤°ą¤¾ą¤‰ą¤œą¤¼ą¤° ą¤¬ą¤¦ą¤²ą¤Øą„‡ पर ą¤µą„‡ ą¤–ą„‹ ą¤œą¤¾ą¤ą¤‚ą¤—ą„‡ą„¤" +personalHeading = "ą¤µą„ą¤Æą¤•ą„ą¤¤ą¤æą¤—ą¤¤ ą¤¹ą¤øą„ą¤¤ą¤¾ą¤•ą„ą¤·ą¤°" +sharedHeading = "ą¤øą¤¾ą¤ą¤¾ ą¤¹ą¤øą„ą¤¤ą¤¾ą¤•ą„ą¤·ą¤°" +personalDescription = "इन ą¤¹ą¤øą„ą¤¤ą¤¾ą¤•ą„ą¤·ą¤°ą„‹ą¤‚ ą¤•ą„‹ ą¤•ą„‡ą¤µą¤² आप ą¤¦ą„‡ą¤– ą¤øą¤•ą¤¤ą„‡ ą¤¹ą„ˆą¤‚ą„¤" +sharedDescription = "ą¤øą¤­ą„€ ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ इन ą¤¹ą¤øą„ą¤¤ą¤¾ą¤•ą„ą¤·ą¤°ą„‹ą¤‚ ą¤•ą„‹ ą¤¦ą„‡ą¤– और ą¤‰ą¤Ŗą¤Æą„‹ą¤— कर ą¤øą¤•ą¤¤ą„‡ ą¤¹ą„ˆą¤‚ą„¤" [sign.saved.type] canvas = "ą¤”ą„ą¤°ą„‰ą¤‡ą¤‚ą¤—" @@ -2318,7 +2334,7 @@ title = "समतल ą¤•ą¤°ą„‡ą¤‚" header = "PDF समतल ą¤•ą¤°ą„‡ą¤‚" flattenOnlyForms = "ą¤•ą„‡ą¤µą¤² ą¤«ą¤¼ą„‰ą¤°ą„ą¤® समतल ą¤•ą¤°ą„‡ą¤‚" submit = "समतल ą¤•ą¤°ą„‡ą¤‚" -filenamePrefix = "flattened" +filenamePrefix = "ą¤øą¤®ą¤¤ą¤²ą„€ą¤•ą„ƒą¤¤" [flatten.files] placeholder = "ą¤¶ą„ą¤°ą„‚ ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤®ą„ą¤–ą„ą¤Æ ą¤¦ą„ƒą¤¶ą„ą¤Æ ą¤®ą„‡ą¤‚ ą¤ą¤• PDF फ़ाइल ą¤šą„ą¤Øą„‡ą¤‚" @@ -2366,7 +2382,7 @@ title = "ą¤®ą¤°ą¤®ą„ą¤®ą¤¤" header = "PDF ą¤®ą¤°ą¤®ą„ą¤®ą¤¤" submit = "ą¤®ą¤°ą¤®ą„ą¤®ą¤¤" description = "यह ą¤Ÿą„‚ą¤² ą¤­ą„ą¤°ą¤·ą„ą¤Ÿ या ą¤•ą„ą¤·ą¤¤ą¤æą¤—ą„ą¤°ą¤øą„ą¤¤ PDF ą¤«ą¤¼ą¤¾ą¤‡ą¤²ą„‹ą¤‚ ą¤•ą„€ ą¤®ą¤°ą¤®ą„ą¤®ą¤¤ ą¤•ą¤°ą¤Øą„‡ का ą¤Ŗą„ą¤°ą¤Æą¤¾ą¤ø ą¤•ą¤°ą„‡ą¤—ą¤¾ą„¤ ą¤•ą„‹ą¤ˆ ą¤…ą¤¤ą¤æą¤°ą¤æą¤•ą„ą¤¤ ą¤øą„‡ą¤Ÿą¤æą¤‚ą¤—ą„ą¤ø ą¤†ą¤µą¤¶ą„ą¤Æą¤• ą¤Øą¤¹ą„€ą¤‚ ą¤¹ą„ˆą¤‚ą„¤" -filenamePrefix = "repaired" +filenamePrefix = "ą¤®ą¤°ą¤®ą„ą¤®ą¤¤_किया" [repair.files] placeholder = "ą¤¶ą„ą¤°ą„‚ ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤®ą„ą¤–ą„ą¤Æ ą¤¦ą„ƒą¤¶ą„ą¤Æ ą¤®ą„‡ą¤‚ ą¤ą¤• PDF फ़ाइल ą¤šą„ą¤Øą„‡ą¤‚" @@ -2567,7 +2583,7 @@ stopButton = "ą¤¤ą„ą¤²ą¤Øą¤¾ ą¤°ą„‹ą¤•ą„‡ą¤‚" [certSign] tags = "ą¤Ŗą„ą¤°ą¤®ą¤¾ą¤£ą„€ą¤•ą¤°ą¤£,PEM,P12,आधिकारिक,ą¤ą¤Øą„ą¤•ą„ą¤°ą¤æą¤Ŗą„ą¤Ÿ" title = "ą¤Ŗą„ą¤°ą¤®ą¤¾ą¤£ą¤Ŗą¤¤ą„ą¤° ą¤¹ą¤øą„ą¤¤ą¤¾ą¤•ą„ą¤·ą¤°" -filenamePrefix = "signed" +filenamePrefix = "ą¤¹ą¤øą„ą¤¤ą¤¾ą¤•ą„ą¤·ą¤°ą¤æą¤¤" chooseCertificate = "ą¤Ŗą„ą¤°ą¤®ą¤¾ą¤£ą¤Ŗą¤¤ą„ą¤° फ़ाइल ą¤šą„ą¤Øą„‡ą¤‚" chooseJksFile = "JKS फ़ाइल ą¤šą„ą¤Øą„‡ą¤‚" chooseP12File = "PKCS12 फ़ाइल ą¤šą„ą¤Øą„‡ą¤‚" @@ -2701,7 +2717,7 @@ header = "PDF ą¤øą„‡ औिजिटल ą¤Ŗą„ą¤°ą¤®ą¤¾ą¤£ą¤Ŗą¤¤ą„ą¤° हटा selectPDF = "PDF फ़ाइल ą¤šą„ą¤Øą„‡ą¤‚:" submit = "ą¤¹ą¤øą„ą¤¤ą¤¾ą¤•ą„ą¤·ą¤° ą¤¹ą¤Ÿą¤¾ą¤ą¤‚" description = "यह ą¤Ÿą„‚ą¤² ą¤†ą¤Ŗą¤•ą„‡ PDF ą¤¦ą¤øą„ą¤¤ą¤¾ą¤µą„‡ą¤œą¤¼ ą¤øą„‡ औिजिटल ą¤Ŗą„ą¤°ą¤®ą¤¾ą¤£ą¤Ŗą¤¤ą„ą¤° ą¤¹ą¤øą„ą¤¤ą¤¾ą¤•ą„ą¤·ą¤° ą¤¹ą¤Ÿą¤¾ą¤ą¤—ą¤¾ą„¤" -filenamePrefix = "unsigned" +filenamePrefix = "ą¤…ą¤Øą¤¹ą¤øą„ą¤¤ą¤¾ą¤•ą„ą¤·ą¤°ą¤æą¤¤" [removeCertSign.files] placeholder = "ą¤¶ą„ą¤°ą„‚ ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤®ą„ą¤–ą„ą¤Æ ą¤¦ą„ƒą¤¶ą„ą¤Æ ą¤®ą„‡ą¤‚ ą¤ą¤• PDF फ़ाइल ą¤šą„ą¤Øą„‡ą¤‚" @@ -2731,7 +2747,7 @@ submit = "जमा ą¤•ą¤°ą„‡ą¤‚" failed = "ą¤®ą¤²ą„ą¤Ÿą„€-ą¤Ŗą„ƒą¤·ą„ą¤  ą¤²ą„‡ą¤†ą¤‰ą¤Ÿ ą¤¬ą¤Øą¤¾ą¤¤ą„‡ समय ą¤¤ą„ą¤°ą„ą¤Ÿą¤æ ą¤¹ą„ą¤ˆą„¤" [bookletImposition] -tags = "booklet,imposition,printing,binding,folding,signature" +tags = "ą¤¬ą„ą¤•ą¤²ą„‡ą¤Ÿ,ą¤‡ą¤®ą„ą¤Ŗą„‹ą¤œą¤¼ą¤æą¤¶ą¤Ø,ą¤Ŗą„ą¤°ą¤æą¤‚ą¤Ÿą¤æą¤‚ą¤—,बाइंऔिंग,ą¤«ą„‹ą¤²ą„ą¤”ą¤æą¤‚ą¤—,ą¤øą¤æą¤—ą„ą¤Øą„‡ą¤šą¤°" title = "ą¤¬ą„ą¤•ą¤²ą„‡ą¤Ÿ ą¤‡ą¤®ą„ą¤Ŗą„‹ą¤œą¤¼ą¤æą¤¶ą¤Ø" header = "ą¤¬ą„ą¤•ą¤²ą„‡ą¤Ÿ ą¤‡ą¤®ą„ą¤Ŗą„‹ą¤œą¤¼ą¤æą¤¶ą¤Ø" submit = "ą¤¬ą„ą¤•ą¤²ą„‡ą¤Ÿ ą¤¬ą¤Øą¤¾ą¤ą¤" @@ -2830,7 +2846,7 @@ scaleFactor = "ą¤ą¤• ą¤Ŗą„ƒą¤·ą„ą¤  का ą¤œą¤¼ą„‚ą¤® ą¤øą„ą¤¤ą¤° (ą¤•ą„ submit = "जमा ą¤•ą¤°ą„‡ą¤‚" [adjustPageScale] -tags = "resize,modify,dimension,adapt" +tags = "आकार ą¤¬ą¤¦ą¤²ą„‡ą¤‚,ą¤øą¤‚ą¤¶ą„‹ą¤§ą¤æą¤¤ ą¤•ą¤°ą„‡ą¤‚,आयाम,ą¤…ą¤Øą„ą¤•ą„‚ą¤²ą¤æą¤¤ ą¤•ą¤°ą„‡ą¤‚" title = "ą¤Ŗą„ƒą¤·ą„ą¤  ą¤øą„ą¤•ą„‡ą¤² ą¤øą¤®ą¤¾ą¤Æą„‹ą¤œą¤æą¤¤ ą¤•ą¤°ą„‡ą¤‚" header = "ą¤Ŗą„ƒą¤·ą„ą¤  ą¤øą„ą¤•ą„‡ą¤² ą¤øą¤®ą¤¾ą¤Æą„‹ą¤œą¤æą¤¤ ą¤•ą¤°ą„‡ą¤‚" submit = "ą¤Ŗą„ƒą¤·ą„ą¤  ą¤øą„ą¤•ą„‡ą¤² ą¤øą¤®ą¤¾ą¤Æą„‹ą¤œą¤æą¤¤ ą¤•ą¤°ą„‡ą¤‚" @@ -3020,6 +3036,91 @@ title = "PDF ą¤•ą„€ ą¤œą¤¾ą¤Øą¤•ą¤¾ą¤°ą„€ ą¤Ŗą„ą¤°ą¤¾ą¤Ŗą„ą¤¤ ą¤•ą¤°ą„‡ą¤‚" header = "PDF ą¤•ą„€ ą¤œą¤¾ą¤Øą¤•ą¤¾ą¤°ą„€ ą¤Ŗą„ą¤°ą¤¾ą¤Ŗą„ą¤¤ ą¤•ą¤°ą„‡ą¤‚" submit = "ą¤œą¤¾ą¤Øą¤•ą¤¾ą¤°ą„€ ą¤Ŗą„ą¤°ą¤¾ą¤Ŗą„ą¤¤ ą¤•ą¤°ą„‡ą¤‚" downloadJson = "JSON ą¤”ą¤¾ą¤‰ą¤Øą¤²ą„‹ą¤” ą¤•ą¤°ą„‡ą¤‚" +processing = "ą¤œą¤¾ą¤Øą¤•ą¤¾ą¤°ą„€ ą¤Øą¤æą¤•ą¤¾ą¤²ą„€ जा ą¤°ą¤¹ą„€ ą¤¹ą„ˆ..." +results = "परिणाम" +noResults = "ą¤°ą¤æą¤Ŗą„‹ą¤°ą„ą¤Ÿ ą¤¬ą¤Øą¤¾ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤Ÿą„‚ą¤² ą¤šą¤²ą¤¾ą¤ą¤ą„¤" +downloads = "ą¤”ą¤¾ą¤‰ą¤Øą¤²ą„‹ą¤”" +noneDetected = "ą¤•ą„ą¤› ą¤­ą„€ पता ą¤Øą¤¹ą„€ą¤‚ चला" +indexTitle = "ą¤…ą¤Øą„ą¤•ą„ą¤°ą¤®ą¤£ą¤æą¤•ą¤¾" + +[getPdfInfo.report] +entryLabel = "ą¤Ŗą„‚ą¤°ą„€ ą¤œą¤¾ą¤Øą¤•ą¤¾ą¤°ą„€ का सारांश" +shortTitle = "PDF ą¤œą¤¾ą¤Øą¤•ą¤¾ą¤°ą„€" + +[getPdfInfo.sections] +metadata = "ą¤®ą„‡ą¤Ÿą¤¾ą¤”ą„‡ą¤Ÿą¤¾" +formFields = "ą¤«ą¤¼ą„‰ą¤°ą„ą¤® ą¤«ą¤¼ą„€ą¤²ą„ą¤”ą„ą¤ø" +basicInfo = "ą¤®ą„‚ą¤² ą¤œą¤¾ą¤Øą¤•ą¤¾ą¤°ą„€" +documentInfo = "ą¤¦ą¤øą„ą¤¤ą¤¾ą¤µą„‡ą¤œą¤¼ ą¤œą¤¾ą¤Øą¤•ą¤¾ą¤°ą„€" +compliance = "ą¤…ą¤Øą„ą¤Ŗą¤¾ą¤²ą¤Ø" +encryption = "ą¤ą¤Øą„ą¤•ą„ą¤°ą¤æą¤Ŗą„ą¤¶ą¤Ø" +permissions = "ą¤…ą¤Øą„ą¤®ą¤¤ą¤æą¤Æą¤¾ą¤" +other = "ą¤…ą¤Øą„ą¤Æ" +perPageInfo = "ą¤Ŗą„ą¤°ą¤¤ą¤æ ą¤Ŗą„‡ą¤œ ą¤œą¤¾ą¤Øą¤•ą¤¾ą¤°ą„€" +tableOfContents = "विषय ą¤øą„‚ą¤šą„€" + +[getPdfInfo.other] +attachments = "ą¤øą¤‚ą¤²ą¤—ą„ą¤Øą¤•" +embeddedFiles = "ą¤ą¤®ą„ą¤¬ą„‡ą¤”ą„‡ą¤” ą¤«ą¤¼ą¤¾ą¤‡ą¤²ą„‡ą¤‚" +javaScript = "JavaScript" +layers = "ą¤²ą„‡ą¤Æą¤°ą„ą¤ø" +structureTree = "ą¤øą„ą¤Ÿą„ą¤°ą¤•ą„ą¤šą¤° ą¤Ÿą„ą¤°ą„€" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "आकार" +annotations = "ą¤Ÿą¤æą¤Ŗą„ą¤Ŗą¤£ą¤æą¤Æą¤¾ą¤" +images = "छवियाँ" +links = "लिंक" +fonts = "ą¤«ą¤¼ą„‰ą¤Øą„ą¤Ÿą„ą¤ø" +xobjects = "XObject ą¤•ą„€ ą¤øą¤‚ą¤–ą„ą¤Æą¤¾" +multimedia = "ą¤®ą¤²ą„ą¤Ÿą„€ą¤®ą„€ą¤”ą¤æą¤Æą¤¾" + +[getPdfInfo.summary] +pages = "ą¤Ŗą„ƒą¤·ą„ą¤ " +fileSize = "फ़ाइल आकार" +pdfVersion = "PDF ą¤øą¤‚ą¤øą„ą¤•ą¤°ą¤£" +language = "भाषा" +title = "PDF सारांश" +author = "ą¤²ą„‡ą¤–ą¤•" +created = "ą¤Øą¤æą¤°ą„ą¤®ą¤æą¤¤" +modified = "ą¤øą¤‚ą¤¶ą„‹ą¤§ą¤æą¤¤" +permsAll = "ą¤øą¤­ą„€ ą¤…ą¤Øą„ą¤®ą¤¤ą¤æą¤Æą¤¾ą¤ ą¤øą„ą¤µą„€ą¤•ą„ƒą¤¤" +permsRestricted = "{{count}} ą¤Ŗą„ą¤°ą¤¤ą¤æą¤¬ą¤‚ą¤§" +permsMixed = "ą¤•ą„ą¤› ą¤…ą¤Øą„ą¤®ą¤¤ą¤æą¤Æą¤¾ą¤ ą¤Ŗą„ą¤°ą¤¤ą¤æą¤¬ą¤‚ą¤§ą¤æą¤¤" +hasCompliance = "ą¤…ą¤Øą„ą¤Ŗą¤¾ą¤²ą¤Ø मानक ą¤®ą„Œą¤œą„‚ą¤¦ ą¤¹ą„ˆą¤‚" +noCompliance = "ą¤•ą„‹ą¤ˆ ą¤…ą¤Øą„ą¤Ŗą¤¾ą¤²ą¤Ø मानक ą¤Øą¤¹ą„€ą¤‚" +basic = "ą¤®ą„‚ą¤² ą¤œą¤¾ą¤Øą¤•ą¤¾ą¤°ą„€" +documentInfo = "ą¤¦ą¤øą„ą¤¤ą¤¾ą¤µą„‡ą¤œą¤¼ ą¤œą¤¾ą¤Øą¤•ą¤¾ą¤°ą„€" +securityTitle = "ą¤øą„ą¤°ą¤•ą„ą¤·ą¤¾ ą¤øą„ą¤„ą¤æą¤¤ą¤æ" +technical = "ą¤¤ą¤•ą¤Øą„€ą¤•ą„€" +overviewTitle = "PDF ą¤…ą¤µą¤²ą„‹ą¤•ą¤Ø" + +[getPdfInfo.summary.security] +encrypted = "ą¤ą¤Øą„ą¤•ą„ą¤°ą¤æą¤Ŗą„ą¤Ÿą„‡ą¤” PDF - ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤” ą¤øą„ą¤°ą¤•ą„ą¤·ą¤¾ ą¤®ą„Œą¤œą„‚ą¤¦ ą¤¹ą„ˆ" +unencrypted = "बिना ą¤ą¤Øą„ą¤•ą„ą¤°ą¤æą¤Ŗą„ą¤¶ą¤Ø वाला PDF - ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤” ą¤øą„ą¤°ą¤•ą„ą¤·ą¤¾ ą¤Øą¤¹ą„€ą¤‚ ą¤¹ą„ˆ" + +[getPdfInfo.summary.tech] +images = "छवियाँ" +fonts = "ą¤«ą¤¼ą„‰ą¤Øą„ą¤Ÿą„ą¤ø" +formFields = "ą¤«ą¤¼ą„‰ą¤°ą„ą¤® ą¤«ą¤¼ą„€ą¤²ą„ą¤”ą„ą¤ø" +embeddedFiles = "ą¤ą¤®ą„ą¤¬ą„‡ą¤”ą„‡ą¤” ą¤«ą¤¼ą¤¾ą¤‡ą¤²ą„‡ą¤‚" +javaScript = "JavaScript" +layers = "ą¤²ą„‡ą¤Æą¤°ą„ą¤ø" +bookmarks = "ą¤¬ą„ą¤•ą¤®ą¤¾ą¤°ą„ą¤•" +multimedia = "ą¤®ą¤²ą„ą¤Ÿą„€ą¤®ą„€ą¤”ą¤æą¤Æą¤¾" + +[getPdfInfo.summary.overview] +untitled = "ą¤ą¤• बिना ą¤¶ą„€ą¤°ą„ą¤·ą¤• वाला ą¤¦ą¤øą„ą¤¤ą¤¾ą¤µą„‡ą¤œą¤¼" +unknown = "ą¤…ą¤œą„ą¤žą¤¾ą¤¤ ą¤²ą„‡ą¤–ą¤•" +text = "यह {{pages}}-ą¤Ŗą„ƒą¤·ą„ą¤  वाला PDF ą¤¹ą„ˆ ą¤œą¤æą¤øą¤•ą¤¾ ą¤¶ą„€ą¤°ą„ą¤·ą¤• {{title}} ą¤¹ą„ˆ, ą¤œą¤æą¤øą„‡ {{author}} ą¤Øą„‡ बनाया ą¤¹ą„ˆ (PDF ą¤øą¤‚ą¤øą„ą¤•ą¤°ą¤£ {{version}})." + +[getPdfInfo.error] +partial = "ą¤•ą„ą¤› ą¤«ą¤¼ą¤¾ą¤‡ą¤²ą„‹ą¤‚ का ą¤Ŗą„ą¤°ą¤øą¤‚ą¤øą„ą¤•ą¤°ą¤£ ą¤Øą¤¹ą„€ą¤‚ ą¤¹ą„‹ ą¤øą¤•ą¤¾ą„¤" +unexpected = "ą¤Øą¤æą¤•ą¤¾ą¤²ą¤¤ą„‡ समय ą¤…ą¤Ŗą„ą¤°ą¤¤ą„ą¤Æą¤¾ą¤¶ą¤æą¤¤ ą¤¤ą„ą¤°ą„ą¤Ÿą¤æ ą¤¹ą„ą¤ˆą„¤" + +[getPdfInfo.status] +complete = "ą¤ą¤•ą„ą¤øą¤Ÿą„ą¤°ą„ˆą¤•ą„ą¤¶ą¤Ø ą¤Ŗą„‚ą¤°ą„ą¤£" [extractPage] tags = "ą¤Øą¤æą¤•ą¤¾ą¤²ą„‡ą¤‚" @@ -3380,7 +3481,7 @@ certHint = "ą¤•ą¤øą„ą¤Ÿą¤® ą¤Ÿą„ą¤°ą¤øą„ą¤Ÿ ą¤øą„ą¤°ą„‹ą¤¤ ą¤•ą„‡ विर title = "ą¤øą¤¤ą„ą¤Æą¤¾ą¤Ŗą¤Ø ą¤øą„‡ą¤Ÿą¤æą¤‚ą¤—ą„ą¤ø" [replaceColor] -tags = "Replace Colour,Page operations,Back end,server side" +tags = "रंग ą¤¬ą¤¦ą¤²ą„‡ą¤‚,ą¤Ŗą„ƒą¤·ą„ą¤  ą¤øą¤‚ą¤šą¤¾ą¤²ą¤Ø,Back end,server side" [replaceColor.labels] settings = "ą¤øą„‡ą¤Ÿą¤æą¤‚ą¤—ą„ą¤ø" @@ -3438,6 +3539,9 @@ signinTitle = "ą¤•ą„ƒą¤Ŗą¤Æą¤¾ साइन इन ą¤•ą¤°ą„‡ą¤‚" ssoSignIn = "सिंगल साइन-ऑन ą¤•ą„‡ ą¤®ą¤¾ą¤§ą„ą¤Æą¤® ą¤øą„‡ ą¤²ą„‰ą¤—ą¤æą¤Ø ą¤•ą¤°ą„‡ą¤‚" oAuth2AutoCreateDisabled = "OAUTH2 ą¤øą„ą¤µą¤¤ą¤ƒ ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ ą¤Øą¤æą¤°ą„ą¤®ą¤¾ą¤£ ą¤…ą¤•ą„ą¤·ą¤® ą¤¹ą„ˆ" oAuth2AdminBlockedUser = "ą¤—ą„ˆą¤°-ą¤Ŗą¤‚ą¤œą„€ą¤•ą„ƒą¤¤ ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ą¤“ą¤‚ का ą¤Ŗą¤‚ą¤œą„€ą¤•ą¤°ą¤£ या ą¤²ą„‰ą¤—ą¤æą¤Ø ą¤µą¤°ą„ą¤¤ą¤®ą¤¾ą¤Ø ą¤®ą„‡ą¤‚ ą¤…ą¤µą¤°ą„ą¤¦ą„ą¤§ ą¤¹ą„ˆą„¤ ą¤•ą„ƒą¤Ŗą¤Æą¤¾ ą¤µą„ą¤Æą¤µą¤øą„ą¤„ą¤¾ą¤Ŗą¤• ą¤øą„‡ ą¤øą¤‚ą¤Ŗą¤°ą„ą¤• ą¤•ą¤°ą„‡ą¤‚ą„¤" +oAuth2RequiresLicense = "OAuth/SSO ą¤²ą„‰ą¤—ą¤æą¤Ø ą¤•ą„‡ ą¤²ą¤æą¤ ą¤Ŗą„‡ą¤” ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø (Server या Enterprise) ą¤†ą¤µą¤¶ą„ą¤Æą¤• ą¤¹ą„ˆą„¤ ą¤•ą„ƒą¤Ŗą¤Æą¤¾ अपना ą¤Ŗą„ą¤²ą¤¾ą¤Ø ą¤…ą¤Ŗą¤—ą„ą¤°ą„‡ą¤” ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤µą„ą¤Æą¤µą¤øą„ą¤„ą¤¾ą¤Ŗą¤• ą¤øą„‡ ą¤øą¤‚ą¤Ŗą¤°ą„ą¤• ą¤•ą¤°ą„‡ą¤‚ą„¤" +saml2RequiresLicense = "SAML ą¤²ą„‰ą¤—ą¤æą¤Ø ą¤•ą„‡ ą¤²ą¤æą¤ ą¤Ŗą„‡ą¤” ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø (Server या Enterprise) ą¤†ą¤µą¤¶ą„ą¤Æą¤• ą¤¹ą„ˆą„¤ ą¤•ą„ƒą¤Ŗą¤Æą¤¾ अपना ą¤Ŗą„ą¤²ą¤¾ą¤Ø ą¤…ą¤Ŗą¤—ą„ą¤°ą„‡ą¤” ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤µą„ą¤Æą¤µą¤øą„ą¤„ą¤¾ą¤Ŗą¤• ą¤øą„‡ ą¤øą¤‚ą¤Ŗą¤°ą„ą¤• ą¤•ą¤°ą„‡ą¤‚ą„¤" +maxUsersReached = "ą¤†ą¤Ŗą¤•ą„‡ ą¤µą¤°ą„ą¤¤ą¤®ą¤¾ą¤Ø ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø ą¤•ą„‡ ą¤²ą¤æą¤ ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ą¤“ą¤‚ ą¤•ą„€ अधिकतम ą¤øą„€ą¤®ą¤¾ ą¤Ŗą„‚ą¤°ą„€ ą¤¹ą„‹ ą¤šą„ą¤•ą„€ ą¤¹ą„ˆą„¤ ą¤•ą„ƒą¤Ŗą¤Æą¤¾ अपना ą¤Ŗą„ą¤²ą¤¾ą¤Ø ą¤…ą¤Ŗą¤—ą„ą¤°ą„‡ą¤” ą¤•ą¤°ą¤Øą„‡ या अधिक ą¤øą„€ą¤Ÿą„‡ą¤‚ ą¤œą„‹ą¤”ą¤¼ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤µą„ą¤Æą¤µą¤øą„ą¤„ą¤¾ą¤Ŗą¤• ą¤øą„‡ ą¤øą¤‚ą¤Ŗą¤°ą„ą¤• ą¤•ą¤°ą„‡ą¤‚ą„¤" oauth2RequestNotFound = "ą¤Ŗą„ą¤°ą¤¾ą¤§ą¤æą¤•ą¤°ą¤£ ą¤…ą¤Øą„ą¤°ą„‹ą¤§ ą¤Øą¤¹ą„€ą¤‚ मिला" oauth2InvalidUserInfoResponse = "ą¤…ą¤®ą¤¾ą¤Øą„ą¤Æ ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ ą¤œą¤¾ą¤Øą¤•ą¤¾ą¤°ą„€ ą¤Ŗą„ą¤°ą¤¤ą¤æą¤•ą„ą¤°ą¤æą¤Æą¤¾" oauth2invalidRequest = "ą¤…ą¤®ą¤¾ą¤Øą„ą¤Æ ą¤…ą¤Øą„ą¤°ą„‹ą¤§" @@ -3533,7 +3637,7 @@ title = "PDF ą¤•ą„‹ ą¤ą¤•ą¤² ą¤Ŗą„ƒą¤·ą„ą¤  ą¤®ą„‡ą¤‚" header = "PDF ą¤•ą„‹ ą¤ą¤•ą¤² ą¤Ŗą„ƒą¤·ą„ą¤  ą¤®ą„‡ą¤‚" submit = "ą¤ą¤•ą¤² ą¤Ŗą„ƒą¤·ą„ą¤  ą¤®ą„‡ą¤‚ ą¤¬ą¤¦ą¤²ą„‡ą¤‚" description = "यह ą¤Ÿą„‚ą¤² ą¤†ą¤Ŗą¤•ą„‡ PDF ą¤•ą„‡ ą¤øą¤­ą„€ ą¤Ŗą„ƒą¤·ą„ą¤ ą„‹ą¤‚ ą¤•ą„‹ ą¤ą¤• ą¤¬ą¤”ą¤¼ą„‡ ą¤ą¤•ą¤² ą¤Ŗą„ƒą¤·ą„ą¤  ą¤®ą„‡ą¤‚ मिला ą¤¦ą„‡ą¤—ą¤¾ą„¤ ą¤šą„Œą¤”ą¤¼ą¤¾ą¤ˆ ą¤®ą„‚ą¤² ą¤Ŗą„ƒą¤·ą„ą¤ ą„‹ą¤‚ ą¤œą„ˆą¤øą„€ ą¤¹ą„€ ą¤°ą¤¹ą„‡ą¤—ą„€, पर ऊँचाई ą¤øą¤­ą„€ ą¤Ŗą„ƒą¤·ą„ą¤  ą¤Šą¤ą¤šą¤¾ą¤‡ą¤Æą„‹ą¤‚ का ą¤Æą„‹ą¤— ą¤¹ą„‹ą¤—ą„€ą„¤" -filenamePrefix = "single_page" +filenamePrefix = "ą¤ą¤•ą¤²_ą¤Ŗą„ƒą¤·ą„ą¤ " [pdfToSinglePage.files] placeholder = "ą¤¶ą„ą¤°ą„‚ ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤®ą„ą¤–ą„ą¤Æ ą¤¦ą„ƒą¤¶ą„ą¤Æ ą¤®ą„‡ą¤‚ ą¤ą¤• PDF फ़ाइल ą¤šą„ą¤Øą„‡ą¤‚" @@ -3771,7 +3875,7 @@ version = "ą¤µą¤°ą„ą¤¤ą¤®ą¤¾ą¤Ø ą¤°ą¤æą¤²ą„€ą¤œą¤¼" title = "API ą¤¦ą¤øą„ą¤¤ą¤¾ą¤µą„‡ą¤œą¤¼ą„€ą¤•ą¤°ą¤£" header = "API ą¤¦ą¤øą„ą¤¤ą¤¾ą¤µą„‡ą¤œą¤¼ą„€ą¤•ą¤°ą¤£" desc = "Stirling PDF API ą¤ą¤‚ą¤”ą¤Ŗą„‰ą¤‡ą¤‚ą¤Ÿą„ą¤ø ą¤¦ą„‡ą¤–ą„‡ą¤‚ और ą¤Ŗą¤°ą„€ą¤•ą„ą¤·ą¤£ ą¤•ą¤°ą„‡ą¤‚" -tags = "api,documentation,swagger,endpoints,development" +tags = "api,ą¤¦ą¤øą„ą¤¤ą¤¾ą¤µą„‡ą¤œą¤¼ą„€ą¤•ą¤°ą¤£,swagger,endpoints,विकास" [cookieBanner.popUp] title = "हम ą¤•ą„ą¤•ą„€ą¤œą¤¼ का ą¤‰ą¤Ŗą¤Æą„‹ą¤— ą¤•ą„ˆą¤øą„‡ ą¤•ą¤°ą¤¤ą„‡ ą¤¹ą„ˆą¤‚" @@ -3846,14 +3950,17 @@ fitToWidth = "ą¤šą„Œą¤”ą¤¼ą¤¾ą¤ˆ ą¤•ą„‡ ą¤…ą¤Øą„ą¤øą¤¾ą¤° फिट ą¤•ą¤°ą„‡ actualSize = "ą¤µą¤¾ą¤øą„ą¤¤ą¤µą¤æą¤• आकार" [viewer] +cannotPreviewFile = "फ़ाइल का ą¤Ŗą„‚ą¤°ą„ą¤µą¤¾ą¤µą¤²ą„‹ą¤•ą¤Ø ą¤Øą¤¹ą„€ą¤‚ किया जा सकता" +dualPageView = "ą¤¦ą„‹ą¤¹ą¤°ą¤¾ ą¤Ŗą„ƒą¤·ą„ą¤  ą¤¦ą„ƒą¤¶ą„ą¤Æ" firstPage = "पहला ą¤Ŗą„ƒą¤·ą„ą¤ " lastPage = "अंतिम ą¤Ŗą„ƒą¤·ą„ą¤ " -previousPage = "पिछला ą¤Ŗą„ƒą¤·ą„ą¤ " nextPage = "अगला ą¤Ŗą„ƒą¤·ą„ą¤ " +onlyPdfSupported = "ą¤µą„ą¤Æą„‚ą¤…ą¤° ą¤•ą„‡ą¤µą¤² PDF ą¤«ą¤¼ą¤¾ą¤‡ą¤²ą„‹ą¤‚ का ą¤øą¤®ą¤°ą„ą¤„ą¤Ø करता ą¤¹ą„ˆą„¤ यह फ़ाइल ą¤•ą¤æą¤øą„€ ą¤­ą¤æą¤Øą„ą¤Ø ą¤«ą¤¼ą„‰ą¤°ą„ą¤®ą„‡ą¤Ÿ ą¤®ą„‡ą¤‚ ą¤Ŗą„ą¤°ą¤¤ą„€ą¤¤ ą¤¹ą„‹ą¤¤ą„€ ą¤¹ą„ˆą„¤" +previousPage = "पिछला ą¤Ŗą„ƒą¤·ą„ą¤ " +singlePageView = "ą¤ą¤•ą¤² ą¤Ŗą„ƒą¤·ą„ą¤  ą¤¦ą„ƒą¤¶ą„ą¤Æ" +unknownFile = "ą¤…ą¤œą„ą¤žą¤¾ą¤¤ फ़ाइल" zoomIn = "ą¤œą¤¼ą„‚ą¤® इन" zoomOut = "ą¤œą¤¼ą„‚ą¤® ą¤†ą¤‰ą¤Ÿ" -singlePageView = "ą¤ą¤•ą¤² ą¤Ŗą„ƒą¤·ą„ą¤  ą¤¦ą„ƒą¤¶ą„ą¤Æ" -dualPageView = "ą¤¦ą„‹ą¤¹ą¤°ą¤¾ ą¤Ŗą„ƒą¤·ą„ą¤  ą¤¦ą„ƒą¤¶ą„ą¤Æ" [rightRail] closeSelected = "चयनित ą¤«ą¤¼ą¤¾ą¤‡ą¤²ą„‡ą¤‚ बंद ą¤•ą¤°ą„‡ą¤‚" @@ -3877,6 +3984,7 @@ toggleSidebar = "साइऔबार ą¤Ÿą„‰ą¤—ą¤² ą¤•ą¤°ą„‡ą¤‚" exportSelected = "चयनित ą¤Ŗą„ƒą¤·ą„ą¤  ą¤Øą¤æą¤°ą„ą¤Æą¤¾ą¤¤ ą¤•ą¤°ą„‡ą¤‚" toggleAnnotations = "ą¤ą¤Øą„‹ą¤Ÿą„‡ą¤¶ą¤Ø ą¤¦ą„ƒą¤¶ą„ą¤Æą¤¤ą¤¾ ą¤Ÿą„‰ą¤—ą¤² ą¤•ą¤°ą„‡ą¤‚" annotationMode = "ą¤ą¤Øą„‹ą¤Ÿą„‡ą¤¶ą¤Ø ą¤®ą„‹ą¤” ą¤Ÿą„‰ą¤—ą¤² ą¤•ą¤°ą„‡ą¤‚" +print = "PDF ą¤Ŗą„ą¤°ą¤æą¤‚ą¤Ÿ ą¤•ą¤°ą„‡ą¤‚" draw = "ą¤”ą„ą¤°ą„‰" save = "ą¤øą¤¹ą„‡ą¤œą„‡ą¤‚" saveChanges = "ą¤Ŗą¤°ą¤æą¤µą¤°ą„ą¤¤ą¤Øą„‹ą¤‚ ą¤•ą„‹ ą¤øą¤¹ą„‡ą¤œą„‡ą¤‚" @@ -4231,15 +4339,15 @@ label = "ą¤Ŗą„ą¤°ą¤¦ą¤¾ą¤¤ą¤¾" description = "ą¤Ŗą„ą¤°ą¤®ą¤¾ą¤£ą„€ą¤•ą¤°ą¤£ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤‰ą¤Ŗą¤Æą„‹ą¤— किया ą¤œą¤¾ą¤Øą„‡ वाला OAuth2 ą¤Ŗą„ą¤°ą¤¦ą¤¾ą¤¤ą¤¾" [admin.settings.connections.oauth2.issuer] -label = "Issuer URL" +label = "ą¤œą¤¾ą¤°ą„€ą¤•ą¤°ą„ą¤¤ą¤¾ URL" description = "OAuth2 ą¤Ŗą„ą¤°ą¤¦ą¤¾ą¤¤ą¤¾ का Issuer URL" [admin.settings.connections.oauth2.clientId] -label = "Client ID" +label = "ą¤•ą„ą¤²ą¤¾ą¤‡ą¤‚ą¤Ÿ ID" description = "ą¤†ą¤Ŗą¤•ą„‡ ą¤Ŗą„ą¤°ą¤¦ą¤¾ą¤¤ą¤¾ ą¤øą„‡ ą¤Ŗą„ą¤°ą¤¾ą¤Ŗą„ą¤¤ OAuth2 Client ID" [admin.settings.connections.oauth2.clientSecret] -label = "Client Secret" +label = "ą¤•ą„ą¤²ą¤¾ą¤‡ą¤‚ą¤Ÿ ą¤øą„€ą¤•ą„ą¤°ą„‡ą¤Ÿ" description = "ą¤†ą¤Ŗą¤•ą„‡ ą¤Ŗą„ą¤°ą¤¦ą¤¾ą¤¤ą¤¾ ą¤øą„‡ ą¤Ŗą„ą¤°ą¤¾ą¤Ŗą„ą¤¤ OAuth2 Client Secret" [admin.settings.connections.oauth2.useAsUsername] @@ -4407,7 +4515,7 @@ description = "ą¤µą¤æą¤øą„ą¤¤ą„ƒą¤¤ ą¤øą¤æą¤øą„ą¤Ÿą¤® ą¤Ÿą„‡ą¤®ą„ą¤Ŗ औा label = "ą¤Ŗą„ą¤°ą„‹ą¤øą„‡ą¤ø ą¤ą¤•ą„ą¤øą„€ą¤•ą„ą¤Æą„‚ą¤Ÿą¤° ą¤øą„€ą¤®ą¤¾ą¤ą¤" description = "ą¤Ŗą„ą¤°ą¤¤ą„ą¤Æą„‡ą¤• ą¤Ŗą„ą¤°ą„‹ą¤øą„‡ą¤ø ą¤ą¤•ą„ą¤øą„€ą¤•ą„ą¤Æą„‚ą¤Ÿą¤° ą¤•ą„‡ ą¤²ą¤æą¤ ą¤øą„‡ą¤¶ą¤Ø ą¤øą„€ą¤®ą¤¾ą¤ą¤ और ą¤Ÿą¤¾ą¤‡ą¤®ą¤†ą¤‰ą¤Ÿ ą¤•ą„‰ą¤Øą„ą¤«ą¤¼ą¤æą¤—ą¤° ą¤•ą¤°ą„‡ą¤‚" libreOffice = "LibreOffice" -pdfToHtml = "PDF to HTML" +pdfToHtml = "PDF ą¤øą„‡ HTML" qpdf = "QPDF" tesseract = "Tesseract OCR" pythonOpenCv = "Python OpenCV" @@ -4494,6 +4602,7 @@ description = "Impressum का URL या फ़ाइल नाम (ą¤•ą„ą¤› title = "ą¤Ŗą„ą¤°ą„€ą¤®ą¤æą¤Æą¤® और ą¤ą¤‚ą¤Ÿą¤°ą¤Ŗą„ą¤°ą¤¾ą¤‡ą¤œą¤¼" description = "ą¤…ą¤Ŗą¤Øą„€ ą¤Ŗą„ą¤°ą„€ą¤®ą¤æą¤Æą¤® या ą¤ą¤‚ą¤Ÿą¤°ą¤Ŗą„ą¤°ą¤¾ą¤‡ą¤œą¤¼ ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø ą¤•ą„ą¤‚ą¤œą„€ ą¤•ą„‰ą¤Øą„ą¤«ą¤¼ą¤æą¤—ą¤° ą¤•ą¤°ą„‡ą¤‚ą„¤" license = "ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø ą¤•ą„‰ą¤Øą„ą¤«ą¤¼ą¤æą¤—ą¤°ą„‡ą¤¶ą¤Ø" +noInput = "ą¤•ą„ƒą¤Ŗą¤Æą¤¾ ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø ą¤•ą„ą¤‚ą¤œą„€ या फ़ाइल ą¤Ŗą„ą¤°ą¤¦ą¤¾ą¤Ø ą¤•ą¤°ą„‡ą¤‚" [admin.settings.premium.licenseKey] toggle = "ą¤•ą„ą¤Æą¤¾ ą¤†ą¤Ŗą¤•ą„‡ पास ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø ą¤•ą„€ या ą¤øą¤°ą„ą¤Ÿą¤æą¤«ą¤æą¤•ą„‡ą¤Ÿ फ़ाइल ą¤¹ą„ˆ?" @@ -4511,6 +4620,25 @@ line1 = "ą¤µą¤°ą„ą¤¤ą¤®ą¤¾ą¤Ø ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø ą¤•ą„€ ą¤•ą„‹ ओवरर line2 = "यदि ą¤†ą¤Ŗą¤Øą„‡ ą¤•ą¤¹ą„€ą¤‚ और ą¤¬ą„ˆą¤•ą¤…ą¤Ŗ ą¤Øą¤¹ą„€ą¤‚ रखा ą¤¹ą„ˆ ą¤¤ą„‹ आपका पिछला ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø ą¤øą„ą¤„ą¤¾ą¤Æą„€ ą¤°ą„‚ą¤Ŗ ą¤øą„‡ ą¤–ą„‹ ą¤œą¤¾ą¤ą¤—ą¤¾ą„¤" line3 = "ą¤®ą¤¹ą¤¤ą„ą¤µą¤Ŗą„‚ą¤°ą„ą¤£: ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø ą¤•ą„€ ą¤•ą„‹ ą¤Øą¤æą¤œą„€ और ą¤øą„ą¤°ą¤•ą„ą¤·ą¤æą¤¤ ą¤°ą¤–ą„‡ą¤‚ą„¤ ą¤‡ą¤Øą„ą¤¹ą„‡ą¤‚ ą¤•ą¤­ą„€ ą¤øą¤¾ą¤°ą„ą¤µą¤œą¤Øą¤æą¤• ą¤°ą„‚ą¤Ŗ ą¤øą„‡ ą¤øą¤¾ą¤ą¤¾ न ą¤•ą¤°ą„‡ą¤‚ą„¤" +[admin.settings.premium.inputMethod] +text = "ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø ą¤•ą„ą¤‚ą¤œą„€" +file = "ą¤Ŗą„ą¤°ą¤®ą¤¾ą¤£ą¤Ŗą¤¤ą„ą¤° फ़ाइल" + +[admin.settings.premium.file] +label = "ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø ą¤Ŗą„ą¤°ą¤®ą¤¾ą¤£ą¤Ŗą¤¤ą„ą¤° फ़ाइल" +description = "ऑफ़लाइन ą¤–ą¤°ą„€ą¤¦ ą¤øą„‡ ą¤…ą¤Ŗą¤Øą„€ .lic या .cert ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø फ़ाइल ą¤…ą¤Ŗą¤²ą„‹ą¤” ą¤•ą¤°ą„‡ą¤‚" +choose = "ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø फ़ाइल ą¤šą„ą¤Øą„‡ą¤‚" +selected = "चयनित: {{filename}} ({{size}})" +successMessage = "ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø फ़ाइल ą¤øą¤«ą¤²ą¤¤ą¤¾ą¤Ŗą„‚ą¤°ą„ą¤µą¤• ą¤…ą¤Ŗą¤²ą„‹ą¤” और ą¤øą¤•ą„ą¤°ą¤æą¤Æ ą¤•ą„€ ą¤—ą¤ˆą„¤ ą¤Ŗą„ą¤Øą¤ƒ आरंभ ą¤•ą„€ ą¤†ą¤µą¤¶ą„ą¤Æą¤•ą¤¤ą¤¾ ą¤Øą¤¹ą„€ą¤‚ą„¤" + +[admin.settings.premium.currentLicense] +title = "ą¤øą¤•ą„ą¤°ą¤æą¤Æ ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø" +file = "ą¤øą„ą¤°ą„‹ą¤¤: ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø फ़ाइल ({{path}})" +key = "ą¤øą„ą¤°ą„‹ą¤¤: ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø ą¤•ą„ą¤‚ą¤œą„€" +type = "ą¤Ŗą„ą¤°ą¤•ą¤¾ą¤°: {{type}}" +noInput = "ą¤•ą„ƒą¤Ŗą¤Æą¤¾ ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø ą¤•ą„ą¤‚ą¤œą„€ ą¤Ŗą„ą¤°ą¤¦ą¤¾ą¤Ø ą¤•ą¤°ą„‡ą¤‚ या ą¤ą¤• ą¤Ŗą„ą¤°ą¤®ą¤¾ą¤£ą¤Ŗą¤¤ą„ą¤° फ़ाइल ą¤…ą¤Ŗą¤²ą„‹ą¤” ą¤•ą¤°ą„‡ą¤‚" +success = "सफलता" + [admin.settings.premium.enabled] label = "ą¤Ŗą„ą¤°ą„€ą¤®ą¤æą¤Æą¤® ą¤«ą¤¼ą„€ą¤šą¤°ą„ą¤ø ą¤øą¤•ą„ą¤°ą¤æą¤Æ ą¤•ą¤°ą„‡ą¤‚" description = "ą¤Ŗą„ą¤°ą„‹/ą¤ą¤‚ą¤Ÿą¤°ą¤Ŗą„ą¤°ą¤¾ą¤‡ą¤œą¤¼ ą¤«ą¤¼ą„€ą¤šą¤°ą„ą¤ø ą¤•ą„‡ ą¤²ą¤æą¤ ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø ą¤•ą„ą¤‚ą¤œą„€ जाँच ą¤øą¤•ą„ą¤·ą¤® ą¤•ą¤°ą„‡ą¤‚" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} चयनित" download = "ą¤”ą¤¾ą¤‰ą¤Øą¤²ą„‹ą¤” ą¤•ą¤°ą„‡ą¤‚" delete = "ą¤¹ą¤Ÿą¤¾ą¤ą¤‚" unsupported = "ą¤…ą¤øą¤®ą¤°ą„ą¤„ą¤æą¤¤" +active = "ą¤øą¤•ą„ą¤°ą¤æą¤Æ" addToUpload = "ą¤…ą¤Ŗą¤²ą„‹ą¤” ą¤®ą„‡ą¤‚ ą¤œą„‹ą¤”ą¤¼ą„‡ą¤‚" +closeFile = "फ़ाइल बंद ą¤•ą¤°ą„‡ą¤‚" deleteAll = "सब ą¤¹ą¤Ÿą¤¾ą¤ą¤" loadingFiles = "ą¤«ą¤¼ą¤¾ą¤‡ą¤²ą„‡ą¤‚ ą¤²ą„‹ą¤” ą¤¹ą„‹ ą¤°ą¤¹ą„€ ą¤¹ą„ˆą¤‚..." noFiles = "ą¤•ą„‹ą¤ˆ फ़ाइल ą¤‰ą¤Ŗą¤²ą¤¬ą„ą¤§ ą¤Øą¤¹ą„€ą¤‚" @@ -5132,7 +5262,7 @@ upgrade = "ą¤…ą¤­ą„€ ą¤…ą¤Ŗą¤—ą„ą¤°ą„‡ą¤” ą¤•ą¤°ą„‡ą¤‚ →" freeTitle = "ą¤øą¤°ą„ą¤µą¤° ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø" overLimitTitle = "ą¤øą¤°ą„ą¤µą¤° ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤ø ą¤†ą¤µą¤¶ą„ą¤Æą¤•" overLimitBody = "हमारा ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤øą¤æą¤‚ą¤— ą¤Ŗą„ą¤°ą¤¤ą¤æ ą¤øą¤°ą„ą¤µą¤° अधिकतम {{freeTierLimit}} ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ą¤“ą¤‚ ą¤•ą„‹ ą¤®ą„ą¤«ą„ą¤¤ ą¤…ą¤Øą„ą¤®ą¤¤ą¤æ ą¤¦ą„‡ą¤¤ą¤¾ ą¤¹ą„ˆą„¤ ą¤†ą¤Ŗą¤•ą„‡ पास {{overLimitUserCopy}} Stirling ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ ą¤¹ą„ˆą¤‚ą„¤ बिना बाधा ą¤•ą„‡ ą¤œą¤¾ą¤°ą„€ ą¤°ą¤–ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤, Stirling Server ą¤Ŗą„ą¤²ą¤¾ą¤Ø ą¤®ą„‡ą¤‚ ą¤…ą¤Ŗą¤—ą„ą¤°ą„‡ą¤” ą¤•ą¤°ą„‡ą¤‚ - ą¤…ą¤Øą¤²ą¤æą¤®ą¤æą¤Ÿą„‡ą¤” ą¤øą„€ą¤Ÿą„ą¤ø, PDF ą¤Ÿą„‡ą¤•ą„ą¤øą„ą¤Ÿ ą¤ą¤”ą¤æą¤Ÿą¤æą¤‚ą¤—, और ą¤Ŗą„‚ą¤°ą„ą¤£ ą¤ą¤”ą¤®ą¤æą¤Ø ą¤Øą¤æą¤Æą¤‚ą¤¤ą„ą¤°ą¤£ $99/server/mo ą¤®ą„‡ą¤‚ą„¤" -freeBody = "हमारा Open-Core ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤øą¤æą¤‚ą¤— ą¤Ŗą„ą¤°ą¤¤ą¤æ ą¤øą¤°ą„ą¤µą¤° अधिकतम {{freeTierLimit}} ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ą¤“ą¤‚ ą¤•ą„‹ ą¤®ą„ą¤«ą„ą¤¤ ą¤…ą¤Øą„ą¤®ą¤¤ą¤æ ą¤¦ą„‡ą¤¤ą¤¾ ą¤¹ą„ˆą„¤ बिना बाधा ą¤øą„ą¤•ą„‡ą¤² ą¤•ą¤°ą¤Øą„‡ और ą¤¹ą¤®ą¤¾ą¤°ą„‡ ą¤Øą¤ PDF ą¤Ÿą„‡ą¤•ą„ą¤øą„ą¤Ÿ ą¤ą¤”ą¤æą¤Ÿą¤æą¤‚ą¤— ą¤Ÿą„‚ą¤² ą¤•ą„€ ą¤Ŗą„ą¤°ą¤¾ą¤°ą¤‚ą¤­ą¤æą¤• ą¤Ŗą¤¹ą„ą¤ą¤š ą¤Ŗą¤¾ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ हम Stirling Server ą¤Ŗą„ą¤²ą¤¾ą¤Ø ą¤•ą„€ सलाह ą¤¦ą„‡ą¤¤ą„‡ ą¤¹ą„ˆą¤‚ - ą¤Ŗą„‚ą¤°ą„ą¤£ ą¤ą¤”ą¤æą¤Ÿą¤æą¤‚ą¤— और ą¤…ą¤Øą¤²ą¤æą¤®ą¤æą¤Ÿą„‡ą¤” ą¤øą„€ą¤Ÿą„ą¤ø $99/server/mo ą¤®ą„‡ą¤‚ą„¤" +freeBody = "हमारा Open-Core ą¤²ą¤¾ą¤‡ą¤øą„‡ą¤‚ą¤øą¤æą¤‚ą¤— ą¤Ŗą„ą¤°ą¤¤ą¤æ ą¤øą¤°ą„ą¤µą¤° अधिकतम {{freeTierLimit}} ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ą¤“ą¤‚ ą¤•ą„‹ ą¤Øą¤æą¤ƒą¤¶ą„ą¤²ą„ą¤• ą¤…ą¤Øą„ą¤®ą¤¤ą¤æ ą¤¦ą„‡ą¤¤ą¤¾ ą¤¹ą„ˆą„¤ बिना ą¤°ą„ą¤•ą¤¾ą¤µą¤Ÿ ą¤øą„ą¤•ą„‡ą¤² ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤, हम Stirling Server ą¤Ŗą„ą¤²ą¤¾ą¤Ø ą¤•ą„€ ą¤…ą¤Øą„ą¤¶ą¤‚ą¤øą¤¾ ą¤•ą¤°ą¤¤ą„‡ ą¤¹ą„ˆą¤‚ - ą¤…ą¤øą„€ą¤®ą¤æą¤¤ ą¤øą„€ą¤Ÿą„‡ą¤‚ और SSO ą¤øą¤®ą¤°ą„ą¤„ą¤Ø $99/ą¤øą¤°ą„ą¤µą¤°/माह ą¤Ŗą¤°ą„¤" [onboarding.desktopInstall] title = "ą¤”ą¤¾ą¤‰ą¤Øą¤²ą„‹ą¤”" @@ -5237,6 +5367,31 @@ error = "ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ ą¤øą„ą¤„ą¤æą¤¤ą¤æ ą¤…ą¤Ŗą¤”ą„‡ą¤Ÿ क success = "ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ ą¤øą¤«ą¤²ą¤¤ą¤¾ą¤Ŗą„‚ą¤°ą„ą¤µą¤• हटाया गया" error = "ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ ą¤¹ą¤Ÿą¤¾ą¤Øą„‡ ą¤®ą„‡ą¤‚ विफल" +[workspace.people.changePassword] +action = "ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤” ą¤¬ą¤¦ą¤²ą„‡ą¤‚" +title = "ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤” ą¤¬ą¤¦ą¤²ą„‡ą¤‚" +subtitle = "ą¤•ą„‡ ą¤²ą¤æą¤ ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤” ą¤…ą¤Ŗą¤”ą„‡ą¤Ÿ ą¤•ą¤°ą„‡ą¤‚" +newPassword = "नया ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤”" +confirmPassword = "ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤” ą¤•ą„€ ą¤Ŗą„ą¤·ą„ą¤Ÿą¤æ ą¤•ą¤°ą„‡ą¤‚" +placeholder = "नया ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤” ą¤¦ą¤°ą„ą¤œ ą¤•ą¤°ą„‡ą¤‚" +confirmPlaceholder = "नया ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤” फिर ą¤øą„‡ ą¤¦ą¤°ą„ą¤œ ą¤•ą¤°ą„‡ą¤‚" +passwordRequired = "ą¤•ą„ƒą¤Ŗą¤Æą¤¾ नया ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤” ą¤¦ą¤°ą„ą¤œ ą¤•ą¤°ą„‡ą¤‚" +passwordMismatch = "ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤” ą¤®ą„‡ą¤² ą¤Øą¤¹ą„€ą¤‚ ą¤–ą¤¾ą¤¤ą„‡" +generateRandom = "ą¤øą„ą¤°ą¤•ą„ą¤·ą¤æą¤¤ ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤” ą¤œą¤Øą¤°ą„‡ą¤Ÿ ą¤•ą¤°ą„‡ą¤‚" +generatedPreview = "ą¤œą¤Øą¤°ą„‡ą¤Ÿ किया गया ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤”:" +copyTooltip = "ą¤•ą„ą¤²ą¤æą¤Ŗą¤¬ą„‹ą¤°ą„ą¤” पर ą¤•ą„‰ą¤Ŗą„€ ą¤•ą¤°ą„‡ą¤‚" +copiedToClipboard = "ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤” ą¤•ą„ą¤²ą¤æą¤Ŗą¤¬ą„‹ą¤°ą„ą¤” पर ą¤•ą„‰ą¤Ŗą„€ किया गया" +copyFailed = "ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤” ą¤•ą„‰ą¤Ŗą„€ ą¤•ą¤°ą¤Øą„‡ ą¤®ą„‡ą¤‚ विफल" +sendEmail = "इस बदलाव ą¤•ą„‡ ą¤¬ą¤¾ą¤°ą„‡ ą¤®ą„‡ą¤‚ ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ ą¤•ą„‹ ą¤ˆą¤®ą„‡ą¤² ą¤•ą¤°ą„‡ą¤‚" +includePassword = "ą¤ˆą¤®ą„‡ą¤² ą¤®ą„‡ą¤‚ नया ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤” शामिल ą¤•ą¤°ą„‡ą¤‚" +forcePasswordChange = "ą¤…ą¤—ą¤²ą„‡ ą¤²ą„‰ą¤—ą¤æą¤Ø पर ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ ą¤•ą„‹ ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤” ą¤¬ą¤¦ą¤²ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤¬ą¤¾ą¤§ą„ą¤Æ ą¤•ą¤°ą„‡ą¤‚" +emailUnavailable = "इस ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ का ą¤ˆą¤®ą„‡ą¤² ą¤ą¤• ą¤®ą¤¾ą¤Øą„ą¤Æ ą¤ˆą¤®ą„‡ą¤² पता ą¤Øą¤¹ą„€ą¤‚ ą¤¹ą„ˆą„¤ ą¤øą„‚ą¤šą¤Øą¤¾ą¤ą¤ ą¤…ą¤•ą„ą¤·ą¤® ą¤¹ą„ˆą¤‚ą„¤" +smtpDisabled = "ą¤ˆą¤®ą„‡ą¤² ą¤øą„‚ą¤šą¤Øą¤¾ą¤“ą¤‚ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤øą„‡ą¤Ÿą¤æą¤‚ą¤—ą„ą¤ø ą¤®ą„‡ą¤‚ SMTP ą¤øą¤•ą„ą¤·ą¤® ą¤¹ą„‹ą¤Øą¤¾ ą¤†ą¤µą¤¶ą„ą¤Æą¤• ą¤¹ą„ˆą„¤" +notifyOnly = "ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤” ą¤•ą„‡ बिना ą¤ą¤• ą¤ˆą¤®ą„‡ą¤² ą¤­ą„‡ą¤œą¤¾ ą¤œą¤¾ą¤ą¤—ą¤¾, ą¤œą¤æą¤øą¤øą„‡ ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ ą¤•ą„‹ पता ą¤šą¤²ą„‡ą¤—ą¤¾ कि ą¤•ą¤æą¤øą„€ ą¤µą„ą¤Æą¤µą¤øą„ą¤„ą¤¾ą¤Ŗą¤• ą¤Øą„‡ ą¤‡ą¤øą„‡ बदला ą¤¹ą„ˆą„¤" +submit = "ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤” ą¤…ą¤Ŗą¤”ą„‡ą¤Ÿ ą¤•ą¤°ą„‡ą¤‚" +success = "ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤” ą¤øą¤«ą¤²ą¤¤ą¤¾ą¤Ŗą„‚ą¤°ą„ą¤µą¤• ą¤…ą¤Ŗą¤”ą„‡ą¤Ÿ किया गया" +error = "ą¤Ŗą¤¾ą¤øą¤µą¤°ą„ą¤” ą¤…ą¤Ŗą¤”ą„‡ą¤Ÿ ą¤•ą¤°ą¤Øą„‡ ą¤®ą„‡ą¤‚ विफल" + [workspace.people.emailInvite] tab = "ą¤ˆą¤®ą„‡ą¤² ą¤†ą¤®ą¤‚ą¤¤ą„ą¤°ą¤£" description = "ą¤Øą„€ą¤šą„‡ ą¤ˆą¤®ą„‡ą¤² ą¤Ÿą¤¾ą¤‡ą¤Ŗ या ą¤Ŗą„‡ą¤øą„ą¤Ÿ ą¤•ą¤°ą„‡ą¤‚, ą¤…ą¤²ą„ą¤Ŗą¤µą¤æą¤°ą¤¾ą¤® ą¤øą„‡ अलग ą¤•ą¤°ą„‡ą¤‚ą„¤ ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ą¤“ą¤‚ ą¤•ą„‹ ą¤ˆą¤®ą„‡ą¤² ą¤•ą„‡ ą¤®ą¤¾ą¤§ą„ą¤Æą¤® ą¤øą„‡ ą¤²ą„‰ą¤—ą¤æą¤Ø ą¤•ą„ą¤°ą„‡ą¤”ą„‡ą¤‚ą¤¶ą¤æą¤Æą¤² ą¤®ą¤æą¤²ą„‡ą¤‚ą¤—ą„‡ą„¤" @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "कम ą¤øą„‡ कम ą¤ą¤• ą¤ˆą¤®ą„‡ą¤² पता ą¤†ą¤µą¤¶ą„ą¤Æą¤• ą¤¹ą„ˆ" submit = "ą¤†ą¤®ą¤‚ą¤¤ą„ą¤°ą¤£ ą¤­ą„‡ą¤œą„‡ą¤‚" success = "ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾(ओं) ą¤•ą„‹ ą¤øą¤«ą¤²ą¤¤ą¤¾ą¤Ŗą„‚ą¤°ą„ą¤µą¤• ą¤†ą¤®ą¤‚ą¤¤ą„ą¤°ą¤æą¤¤ किया गया" -partialSuccess = "ą¤•ą„ą¤› ą¤†ą¤®ą¤‚ą¤¤ą„ą¤°ą¤£ विफल ą¤°ą¤¹ą„‡" +partialFailure = "ą¤•ą„ą¤› ą¤Øą¤æą¤®ą¤‚ą¤¤ą„ą¤°ą¤£ विफल ą¤¹ą„ą¤" allFailed = "ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ą¤“ą¤‚ ą¤•ą„‹ ą¤†ą¤®ą¤‚ą¤¤ą„ą¤°ą¤æą¤¤ ą¤•ą¤°ą¤Øą„‡ ą¤®ą„‡ą¤‚ विफल" error = "ą¤†ą¤®ą¤‚ą¤¤ą„ą¤°ą¤£ ą¤­ą„‡ą¤œą¤Øą„‡ ą¤®ą„‡ą¤‚ विफल" @@ -5770,6 +5925,7 @@ subtitle = "ą¤…ą¤Ŗą¤Øą„‡ Stirling ą¤–ą¤¾ą¤¤ą„‡ ą¤øą„‡ साइन इन कर [setup.selfhosted] title = "ą¤øą¤°ą„ą¤µą¤° ą¤®ą„‡ą¤‚ साइन इन" subtitle = "ą¤…ą¤Ŗą¤Øą„‡ ą¤øą¤°ą„ą¤µą¤° ą¤•ą„ą¤°ą„‡ą¤”ą„‡ą¤‚ą¤¶ą¤æą¤Æą¤²ą„ą¤ø ą¤¦ą¤°ą„ą¤œ ą¤•ą¤°ą„‡ą¤‚" +link = "या ą¤•ą¤æą¤øą„€ ą¤øą„ą¤µ-ą¤¹ą„‹ą¤øą„ą¤Ÿą„‡ą¤” ą¤–ą¤¾ą¤¤ą„‡ ą¤øą„‡ ą¤•ą¤Øą„‡ą¤•ą„ą¤Ÿ ą¤•ą¤°ą„‡ą¤‚" [setup.server] title = "ą¤øą¤°ą„ą¤µą¤° ą¤øą„‡ ą¤•ą¤Øą„‡ą¤•ą„ą¤Ÿ ą¤•ą¤°ą„‡ą¤‚" @@ -5788,6 +5944,14 @@ description = "ą¤…ą¤Ŗą¤Øą„‡ ą¤øą„‡ą¤²ą„ą¤«-ą¤¹ą„‹ą¤øą„ą¤Ÿą„‡ą¤” Stirling PDF emptyUrl = "ą¤•ą„ƒą¤Ŗą¤Æą¤¾ ą¤øą¤°ą„ą¤µą¤° URL ą¤¦ą¤°ą„ą¤œ ą¤•ą¤°ą„‡ą¤‚" unreachable = "ą¤øą¤°ą„ą¤µą¤° ą¤øą„‡ ą¤•ą¤Øą„‡ą¤•ą„ą¤Ÿ ą¤Øą¤¹ą„€ą¤‚ ą¤¹ą„‹ सका" testFailed = "ą¤•ą¤Øą„‡ą¤•ą„ą¤¶ą¤Ø ą¤Ŗą¤°ą„€ą¤•ą„ą¤·ą¤£ विफल" +configFetch = "ą¤øą¤°ą„ą¤µą¤° ą¤•ą„‰ą¤Øą„ą¤«ą¤¼ą¤æą¤—ą¤°ą„‡ą¤¶ą¤Ø ą¤Ŗą„ą¤°ą¤¾ą¤Ŗą„ą¤¤ ą¤•ą¤°ą¤Øą„‡ ą¤®ą„‡ą¤‚ ą¤µą¤æą¤«ą¤²ą„¤ ą¤•ą„ƒą¤Ŗą¤Æą¤¾ URL ą¤œą¤¾ą¤ą¤šą„‡ą¤‚ और फिर ą¤øą„‡ ą¤Ŗą„ą¤°ą¤Æą¤¾ą¤ø ą¤•ą¤°ą„‡ą¤‚ą„¤" + +[setup.server.error.securityDisabled] +title = "ą¤²ą„‰ą¤—ą¤æą¤Ø ą¤øą¤•ą„ą¤·ą¤® ą¤Øą¤¹ą„€ą¤‚ ą¤¹ą„ˆ" +body = "इस ą¤øą¤°ą„ą¤µą¤° पर ą¤²ą„‰ą¤—ą¤æą¤Ø ą¤øą¤•ą„ą¤·ą¤® ą¤Øą¤¹ą„€ą¤‚ ą¤¹ą„ˆą„¤ इस ą¤øą¤°ą„ą¤µą¤° ą¤øą„‡ ą¤•ą¤Øą„‡ą¤•ą„ą¤Ÿ ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤, ą¤†ą¤Ŗą¤•ą„‹ ą¤Ŗą„ą¤°ą¤®ą¤¾ą¤£ą„€ą¤•ą¤°ą¤£ ą¤øą¤•ą„ą¤·ą¤® करना ą¤¹ą„‹ą¤—ą¤¾:" +step1 = "ą¤…ą¤Ŗą¤Øą„‡ ą¤ą¤Øą¤µą¤¾ą¤Æą¤°ą¤Øą¤®ą„‡ą¤‚ą¤Ÿ ą¤®ą„‡ą¤‚ DOCKER_ENABLE_SECURITY=true ą¤øą„‡ą¤Ÿ ą¤•ą¤°ą„‡ą¤‚" +step2 = "या settings.yml ą¤®ą„‡ą¤‚ security.enableLogin=true ą¤øą„‡ą¤Ÿ ą¤•ą¤°ą„‡ą¤‚" +step3 = "ą¤øą¤°ą„ą¤µą¤° ą¤•ą„‹ ą¤Ŗą„ą¤Øą¤ƒ ą¤Ŗą„ą¤°ą¤¾ą¤°ą¤‚ą¤­ ą¤•ą¤°ą„‡ą¤‚" [setup.login] title = "साइन इन" @@ -5797,6 +5961,13 @@ submit = "ą¤²ą„‰ą¤—ą¤æą¤Ø" signInWith = "ą¤‡ą¤øą¤•ą„‡ साऄ साइन इन ą¤•ą¤°ą„‡ą¤‚" oauthPending = "ą¤Ŗą„ą¤°ą¤®ą¤¾ą¤£ą„€ą¤•ą¤°ą¤£ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤¬ą„ą¤°ą¤¾ą¤‰ą¤œą¤¼ą¤° ą¤–ą„ą¤² रहा ą¤¹ą„ˆ..." orContinueWith = "या ą¤ˆą¤®ą„‡ą¤² ą¤•ą„‡ साऄ ą¤œą¤¾ą¤°ą„€ ą¤°ą¤–ą„‡ą¤‚" +serverRequirement = "ą¤§ą„ą¤Æą¤¾ą¤Ø ą¤¦ą„‡ą¤‚: ą¤øą¤°ą„ą¤µą¤° पर ą¤²ą„‰ą¤—ą¤æą¤Ø ą¤øą¤•ą„ą¤·ą¤® ą¤¹ą„‹ą¤Øą¤¾ ą¤šą¤¾ą¤¹ą¤æą¤ą„¤" +showInstructions = "ą¤•ą„ˆą¤øą„‡ ą¤øą¤•ą„ą¤·ą¤® ą¤•ą¤°ą„‡ą¤‚?" +hideInstructions = "ą¤Øą¤æą¤°ą„ą¤¦ą„‡ą¤¶ ą¤›ą¤æą¤Ŗą¤¾ą¤ą¤" +instructions = "ą¤…ą¤Ŗą¤Øą„‡ Stirling PDF ą¤øą¤°ą„ą¤µą¤° पर ą¤²ą„‰ą¤—ą¤æą¤Ø ą¤øą¤•ą„ą¤·ą¤® ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤:" +instructionsEnvVar = "ą¤ą¤Øą„ą¤µą¤¾ą¤Æą¤°ą¤Øą¤®ą„‡ą¤‚ą¤Ÿ ą¤µą„‡ą¤°ą¤æą¤ą¤¬ą¤² ą¤øą„‡ą¤Ÿ ą¤•ą¤°ą„‡ą¤‚:" +instructionsOrYml = "या settings.yml ą¤®ą„‡ą¤‚:" +instructionsRestart = "ą¤‡ą¤øą¤•ą„‡ बाद बदलाव ą¤Ŗą„ą¤°ą¤­ą¤¾ą¤µą„€ ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ अपना ą¤øą¤°ą„ą¤µą¤° ą¤Ŗą„ą¤Øą¤ƒ ą¤Ŗą„ą¤°ą¤¾ą¤°ą¤‚ą¤­ ą¤•ą¤°ą„‡ą¤‚ą„¤" [setup.login.username] label = "ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ नाम" @@ -5853,6 +6024,7 @@ earlyAccess = "ą¤…ą¤°ą„ą¤²ą„€ ą¤ą¤•ą„ą¤øą„‡ą¤ø" reset = "ą¤Ŗą¤°ą¤æą¤µą¤°ą„ą¤¤ą¤Ø ą¤°ą„€ą¤øą„‡ą¤Ÿ ą¤•ą¤°ą„‡ą¤‚" downloadJson = "JSON ą¤”ą¤¾ą¤‰ą¤Øą¤²ą„‹ą¤” ą¤•ą¤°ą„‡ą¤‚" generatePdf = "PDF ą¤œą¤Øą¤°ą„‡ą¤Ÿ ą¤•ą¤°ą„‡ą¤‚" +saveChanges = "ą¤Ŗą¤°ą¤æą¤µą¤°ą„ą¤¤ą¤Ø ą¤øą¤¹ą„‡ą¤œą„‡ą¤‚" [pdfTextEditor.options.autoScaleText] title = "ą¤Ÿą„‡ą¤•ą„ą¤øą„ą¤Ÿ ą¤•ą„‹ ą¤¬ą„‰ą¤•ą„ą¤ø ą¤®ą„‡ą¤‚ फिट ą¤•ą¤°ą¤Øą„‡ ą¤¹ą„‡ą¤¤ą„ ą¤‘ą¤Ÿą„‹-ą¤øą„ą¤•ą„‡ą¤²" @@ -5890,6 +6062,8 @@ alpha = "यह alpha ą¤µą„ą¤Æą„‚ą¤…ą¤° ą¤…ą¤­ą„€ विकसित ą¤¹ą„‹ र [pdfTextEditor.empty] title = "ą¤•ą„‹ą¤ˆ ą¤¦ą¤øą„ą¤¤ą¤¾ą¤µą„‡ą¤œą¤¼ ą¤²ą„‹ą¤” ą¤Øą¤¹ą„€ą¤‚" subtitle = "ą¤Ÿą„‡ą¤•ą„ą¤øą„ą¤Ÿ ą¤øą¤¾ą¤®ą¤—ą„ą¤°ą„€ संपादित ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ PDF या JSON फ़ाइल ą¤²ą„‹ą¤” ą¤•ą¤°ą„‡ą¤‚." +dropzone = "यहाँ PDF या JSON फ़ाइल ą¤–ą„€ą¤‚ą¤šą¤•ą¤° ą¤›ą„‹ą¤”ą¤¼ą„‡ą¤‚, या ą¤¬ą„ą¤°ą¤¾ą¤‰ą¤œą¤¼ ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤•ą„ą¤²ą¤æą¤• ą¤•ą¤°ą„‡ą¤‚" +dropzoneWithFiles = "ą¤«ą¤¼ą¤¾ą¤‡ą¤²ą„‡ą¤‚ ą¤Ÿą„ˆą¤¬ ą¤øą„‡ ą¤•ą„‹ą¤ˆ फ़ाइल ą¤šą„ą¤Øą„‡ą¤‚, या यहाँ PDF या JSON फ़ाइल ą¤–ą„€ą¤‚ą¤šą¤•ą¤° ą¤›ą„‹ą¤”ą¤¼ą„‡ą¤‚, या ą¤¬ą„ą¤°ą¤¾ą¤‰ą¤œą¤¼ ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤•ą„ą¤²ą¤æą¤• ą¤•ą¤°ą„‡ą¤‚" [pdfTextEditor.welcomeBanner] title = "PDF Text Editor ą¤®ą„‡ą¤‚ आपका ą¤øą„ą¤µą¤¾ą¤—ą¤¤ ą¤¹ą„ˆ (Early Access)" @@ -5932,7 +6106,7 @@ warnings = "ą¤šą„‡ą¤¤ą¤¾ą¤µą¤Øą¤æą¤Æą¤¾ą¤" suggestions = "ą¤Øą„‹ą¤Ÿą„ą¤ø" currentPageFonts = "इस ą¤Ŗą„‡ą¤œ ą¤•ą„‡ ą¤«ą¤¼ą„‰ą¤Øą„ą¤Ÿą„ą¤ø" allFonts = "ą¤øą¤­ą„€ ą¤«ą¤¼ą„‰ą¤Øą„ą¤Ÿą„ą¤ø" -fallback = "fallback" +fallback = "ą¤«ą„‰ą¤²ą¤¬ą„ˆą¤•" missing = "गायब" perfectMessage = "ą¤øą¤­ą„€ ą¤«ą¤¼ą„‰ą¤Øą„ą¤Ÿą„ą¤ø ą¤•ą„‹ ą¤Ŗą„‚ą¤°ą„€ तरह ą¤Ŗą„ą¤Øą¤°ą„ą¤¤ą„ą¤Ŗą¤¾ą¤¦ą¤æą¤¤ किया जा सकता ą¤¹ą„ˆ." warningMessage = "ą¤•ą„ą¤› ą¤«ą¤¼ą„‰ą¤Øą„ą¤Ÿą„ą¤ø ą¤øą¤¹ą„€ ą¤øą„‡ ą¤°ą„‡ą¤‚ą¤”ą¤° ą¤Øą¤¹ą„€ą¤‚ ą¤¹ą„‹ ą¤øą¤•ą¤¤ą„‡ ą¤¹ą„ˆą¤‚." @@ -5953,7 +6127,7 @@ insufficientPermissions = "ą¤†ą¤Ŗą¤•ą„‡ पास यह ą¤•ą„ą¤°ą¤æą¤Æą¤¾ क [addText] title = "ą¤Ÿą„‡ą¤•ą„ą¤øą„ą¤Ÿ ą¤œą„‹ą¤”ą¤¼ą„‡ą¤‚" header = "PDFs ą¤®ą„‡ą¤‚ ą¤Ÿą„‡ą¤•ą„ą¤øą„ą¤Ÿ ą¤œą„‹ą¤”ą¤¼ą„‡ą¤‚" -tags = "text,annotation,label" +tags = "पाठ,ą¤Ÿą¤æą¤Ŗą„ą¤Ŗą¤£ą„€,ą¤²ą„‡ą¤¬ą¤²" applySignatures = "ą¤Ÿą„‡ą¤•ą„ą¤øą„ą¤Ÿ ą¤²ą¤¾ą¤—ą„‚ ą¤•ą¤°ą„‡ą¤‚" [addText.text] diff --git a/frontend/public/locales/hr-HR/translation.toml b/frontend/public/locales/hr-HR/translation.toml index c19e67180f..a523fc1b7c 100644 --- a/frontend/public/locales/hr-HR/translation.toml +++ b/frontend/public/locales/hr-HR/translation.toml @@ -163,6 +163,11 @@ unfavorite = "Ukloni iz omiljenih" fullscreen = "Prebaci na način cijelog zaslona" sidebar = "Prebaci na način bočne trake" +[backendStartup] +notFoundTitle = "Backend nije pronađen" +retry = "PokuÅ”aj ponovno" +unreachable = "Aplikacija se trenutačno ne može povezati s backendom. Provjerite status backenda i mrežnu povezanost, zatim pokuÅ”ajte ponovno." + [zipWarning] title = "Velika ZIP datoteka" message = "Ovaj ZIP sadrži {{count}} datoteka. Ipak izdvojiti?" @@ -912,6 +917,9 @@ desc = "Izgradite viÅ”ekoračne tijekove rada povezivanjem PDF radnji. Idealno z desc = "Preklapa PDF-ove na drugi PDF" title = "Preklapanje PDF-ova" +[home.pdfTextEditor] +title = "Uređivač teksta PDF-a" +desc = "Uređujte postojeći tekst i slike unutar PDF-ova" [home.addText] tags = "tekst,anotacija,oznaka" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Crtani potpis" defaultImageLabel = "Učitani potpis" defaultTextLabel = "Upisani potpis" saveButton = "Spremi potpis" +savePersonal = "Spremi osobno" +saveShared = "Spremi dijeljeno" saveUnavailable = "Najprije izradite potpis da biste ga spremili." noChanges = "Trenutačni potpis je već spremljen." +tempStorageTitle = "Privremena pohrana u pregledniku" +tempStorageDescription = "Potpisi se pohranjuju samo u vaÅ”em pregledniku. Izgubit će se ako očistite podatke preglednika ili promijenite preglednik." +personalHeading = "Osobni potpisi" +sharedHeading = "Dijeljeni potpisi" +personalDescription = "Samo vi možete vidjeti ove potpise." +sharedDescription = "Svi korisnici mogu vidjeti i koristiti ove potpise." [sign.saved.type] canvas = "Crtanje" @@ -3020,6 +3036,91 @@ title = "Informacije o PDF-u" header = "Informacije o PDF-u" submit = "Informacije" downloadJson = "Preuzmite JSON" +processing = "Izdvajanje informacija..." +results = "Rezultati" +noResults = "Pokrenite alat za generiranje izvjeŔća." +downloads = "Preuzimanja" +noneDetected = "NiÅ”ta nije otkriveno" +indexTitle = "Indeks" + +[getPdfInfo.report] +entryLabel = "Potpuni sažetak informacija" +shortTitle = "Informacije o PDF-u" + +[getPdfInfo.sections] +metadata = "Metapodaci" +formFields = "Polja obrasca" +basicInfo = "Osnovne informacije" +documentInfo = "Informacije o dokumentu" +compliance = "Sukladnost" +encryption = "Å ifriranje" +permissions = "Dozvole" +other = "Ostalo" +perPageInfo = "Informacije po stranici" +tableOfContents = "Sadržaj" + +[getPdfInfo.other] +attachments = "Privici" +embeddedFiles = "Ugrađene datoteke" +javaScript = "JavaScript" +layers = "Slojevi" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Veličina" +annotations = "BiljeÅ”ke" +images = "Slike" +links = "Poveznice" +fonts = "Fontovi" +xobjects = "Broj XObjecta" +multimedia = "Multimedija" + +[getPdfInfo.summary] +pages = "Stranice" +fileSize = "Veličina datoteke" +pdfVersion = "PDF verzija" +language = "Jezik" +title = "Sažetak PDF-a" +author = "Autor" +created = "Stvoreno" +modified = "Izmijenjeno" +permsAll = "Sve dozvole dopuÅ”tene" +permsRestricted = "{{count}} ograničenja" +permsMixed = "Neke dozvole su ograničene" +hasCompliance = "Ima standarde sukladnosti" +noCompliance = "Nema standarda sukladnosti" +basic = "Osnovne informacije" +documentInfo = "Informacije o dokumentu" +securityTitle = "Status sigurnosti" +technical = "Tehničko" +overviewTitle = "Pregled PDF-a" + +[getPdfInfo.summary.security] +encrypted = "Å ifrirani PDF - prisutna zaÅ”tita lozinkom" +unencrypted = "NeÅ”ifrirani PDF - bez zaÅ”tite lozinkom" + +[getPdfInfo.summary.tech] +images = "Slike" +fonts = "Fontovi" +formFields = "Polja obrasca" +embeddedFiles = "Ugrađene datoteke" +javaScript = "JavaScript" +layers = "Slojevi" +bookmarks = "Oznake" +multimedia = "Multimedija" + +[getPdfInfo.summary.overview] +untitled = "neimenovani dokument" +unknown = "Nepoznat autor" +text = "Ovo je PDF od {{pages}} stranica pod nazivom {{title}}, čiji je autor {{author}} (PDF verzija {{version}})." + +[getPdfInfo.error] +partial = "Neke datoteke nije bilo moguće obraditi." +unexpected = "Neočekivana pogreÅ”ka tijekom izdvajanja." + +[getPdfInfo.status] +complete = "Izdvajanje dovrÅ”eno" [extractPage] tags = "izdvajanje" @@ -3438,6 +3539,9 @@ signinTitle = "Molimo vas da se prijavite" ssoSignIn = "Prijavite se putem jedinstvene prijave" oAuth2AutoCreateDisabled = "OAUTH2 automatsko kreiranje korisnika je onemogućeno" oAuth2AdminBlockedUser = "Registracija ili prijava nekadreguiranih korisnika trenutno su blokirane. Molimo Vas da kontaktirate administratora." +oAuth2RequiresLicense = "Prijava putem OAuth/SSO zahtijeva plaćenu licencu (Server ili Enterprise). Obratite se administratoru radi nadogradnje vaÅ”eg plana." +saml2RequiresLicense = "Prijava putem SAML zahtijeva plaćenu licencu (Server ili Enterprise). Obratite se administratoru radi nadogradnje vaÅ”eg plana." +maxUsersReached = "Dosegnut je maksimalan broj korisnika za vaÅ”u trenutačnu licencu. Obratite se administratoru radi nadogradnje plana ili dodavanja dodatnih mjesta." oauth2RequestNotFound = "Zahtjev za autorizaciju nije pronađen" oauth2InvalidUserInfoResponse = "Nevažeće informacije o korisniku" oauth2invalidRequest = "Neispravan zahtjev" @@ -3846,14 +3950,17 @@ fitToWidth = "Prilagodi Å”irini" actualSize = "Stvarna veličina" [viewer] +cannotPreviewFile = "Nije moguće pregledati datoteku" +dualPageView = "Prikaz dviju stranica" firstPage = "Prva stranica" lastPage = "Zadnja stranica" -previousPage = "Prethodna stranica" nextPage = "Sljedeća stranica" +onlyPdfSupported = "Preglednik podržava samo PDF datoteke. Čini se da je ova datoteka u drugačijem formatu." +previousPage = "Prethodna stranica" +singlePageView = "Prikaz jedne stranice" +unknownFile = "Nepoznata datoteka" zoomIn = "Povećaj" zoomOut = "Umanji" -singlePageView = "Prikaz jedne stranice" -dualPageView = "Prikaz dviju stranica" [rightRail] closeSelected = "Zatvori odabrane datoteke" @@ -3877,6 +3984,7 @@ toggleSidebar = "Prebaci bočnu traku" exportSelected = "Izvezi odabrane stranice" toggleAnnotations = "Prebaci vidljivost biljeÅ”ki" annotationMode = "Prebaci način biljeÅ”ki" +print = "Ispis PDF-a" draw = "Crtaj" save = "Spremi" saveChanges = "Spremi promjene" @@ -4494,6 +4602,7 @@ description = "URL ili naziv datoteke za impresum (obvezno u nekim nadležnostim title = "Premium i Enterprise" description = "Konfigurirajte svoj premium ili enterprise licencni ključ." license = "Konfiguracija licence" +noInput = "Molimo navedite licencni ključ ili datoteku" [admin.settings.premium.licenseKey] toggle = "Imate licencni ključ ili datoteku certifikata?" @@ -4511,6 +4620,25 @@ line1 = "Prepisivanje vaÅ”eg trenutačnog licencnog ključa ne može se poniÅ”ti line2 = "VaÅ”a će prethodna licenca trajno biti izgubljena osim ako je niste sigurnosno kopirali drugdje." line3 = "Važno: Licencne ključeve držite privatnima i sigurnima. Nikada ih javno ne dijelite." +[admin.settings.premium.inputMethod] +text = "Licencni ključ" +file = "Datoteka certifikata" + +[admin.settings.premium.file] +label = "Datoteka certifikata licence" +description = "Učitajte svoju .lic ili .cert licencnu datoteku iz izvanmrežnih kupnji" +choose = "Odaberite licencnu datoteku" +selected = "Odabrano: {{filename}} ({{size}})" +successMessage = "Licencna datoteka je uspjeÅ”no učitana i aktivirana. Nije potrebno ponovno pokretanje." + +[admin.settings.premium.currentLicense] +title = "Aktivna licenca" +file = "Izvor: Licencna datoteka ({{path}})" +key = "Izvor: Licencni ključ" +type = "Vrsta: {{type}}" +noInput = "Navedite licencni ključ ili učitajte datoteku certifikata" +success = "Uspjeh" + [admin.settings.premium.enabled] label = "Omogući premium značajke" description = "Omogući provjere licencnog ključa za pro/enterprise značajke" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} odabrano" download = "Preuzmi datoteku" delete = "IzbriÅ”i" unsupported = "Nepodržano" +active = "Aktivno" addToUpload = "Dodaj za otpremu" +closeFile = "Zatvori datoteku" deleteAll = "IzbriÅ”i sve" loadingFiles = "Učitavanje datoteka..." noFiles = "Nema dostupnih datoteka" @@ -5132,7 +5262,7 @@ upgrade = "Nadogradi odmah →" freeTitle = "Poslužiteljska licenca" overLimitTitle = "Potrebna poslužiteljska licenca" overLimitBody = "NaÅ”e licenciranje dopuÅ”ta do {{freeTierLimit}} korisnika besplatno po poslužitelju. Imate {{overLimitUserCopy}} Stirling korisnika. Za nesmetan nastavak, nadogradite na Stirling Server plan - neograničena mjesta, uređivanje teksta u PDF-u i puna admin kontrola za $99/server/mo." -freeBody = "NaÅ”e Open-Core licenciranje dopuÅ”ta do {{freeTierLimit}} korisnika besplatno po poslužitelju. Za nesmetano skaliranje i rani pristup naÅ”em novom alatu za uređivanje teksta u PDF-u, preporučujemo Stirling Server plan - potpuno uređivanje i neograničena mjesta za $99/server/mo." +freeBody = "NaÅ”e licenciranje Open-Core omogućuje do {{freeTierLimit}} korisnika besplatno po poslužitelju. Za neometano skaliranje preporučujemo Stirling Server plan - neograničena mjesta i podrÅ”ka za SSO za $99/poslužitelj/mj." [onboarding.desktopInstall] title = "Preuzimanje" @@ -5237,6 +5367,31 @@ error = "Nije uspjelo ažuriranje statusa korisnika" success = "Korisnik je uspjeÅ”no izbrisan" error = "Nije uspjelo brisanje korisnika" +[workspace.people.changePassword] +action = "Promijeni lozinku" +title = "Promijeni lozinku" +subtitle = "Ažuriraj lozinku za" +newPassword = "Nova lozinka" +confirmPassword = "Potvrdi lozinku" +placeholder = "Unesite novu lozinku" +confirmPlaceholder = "Ponovno unesite novu lozinku" +passwordRequired = "Unesite novu lozinku" +passwordMismatch = "Lozinke se ne podudaraju" +generateRandom = "Generiraj sigurnu lozinku" +generatedPreview = "Generirana lozinka:" +copyTooltip = "Kopiraj u međuspremnik" +copiedToClipboard = "Lozinka kopirana u međuspremnik" +copyFailed = "Nije uspjelo kopiranje lozinke" +sendEmail = "PoÅ”alji korisniku e-poruku o ovoj promjeni" +includePassword = "Uključi novu lozinku u e-poruku" +forcePasswordChange = "Prisili korisnika da promijeni lozinku pri sljedećoj prijavi" +emailUnavailable = "E-adresa ovog korisnika nije valjana. Obavijesti su onemogućene." +smtpDisabled = "Obavijesti e-poÅ”tom zahtijevaju da SMTP bude omogućen u postavkama." +notifyOnly = "Poslat će se e-poruka bez lozinke kako bi se korisniku javilo da ju je administrator promijenio." +submit = "Ažuriraj lozinku" +success = "Lozinka je uspjeÅ”no ažurirana" +error = "Nije uspjelo ažuriranje lozinke" + [workspace.people.emailInvite] tab = "Poziv e-poÅ”tom" description = "Utipkajte ili zalijepite e-adrese dolje, odvojene zarezima. Korisnici će putem e-poÅ”te dobiti pristupne podatke." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Potreban je barem jedan e-mail" submit = "PoÅ”alji pozive" success = "Korisnici su uspjeÅ”no pozvani" -partialSuccess = "Neki pozivi nisu uspjeli" +partialFailure = "Neke pozivnice nisu uspjele" allFailed = "Pozivanje korisnika nije uspjelo" error = "Slanje poziva nije uspjelo" @@ -5770,6 +5925,7 @@ subtitle = "Prijavite se svojim Stirling računom" [setup.selfhosted] title = "Prijavite se na poslužitelj" subtitle = "Unesite vjerodajnice poslužitelja" +link = "ili se povežite sa samohostiranim računom" [setup.server] title = "Povežite se s poslužiteljem" @@ -5788,6 +5944,14 @@ description = "Unesite puni URL svog self-hosted Stirling PDF poslužitelja" emptyUrl = "Unesite URL poslužitelja" unreachable = "Nije moguće povezati se s poslužiteljem" testFailed = "Test veze nije uspio" +configFetch = "Neuspjelo dohvaćanje konfiguracije poslužitelja. Provjerite URL i pokuÅ”ajte ponovno." + +[setup.server.error.securityDisabled] +title = "Prijava nije omogućena" +body = "Na ovom poslužitelju prijava nije omogućena. Da biste se povezali s ovim poslužiteljem, morate omogućiti autentikaciju:" +step1 = "Postavite DOCKER_ENABLE_SECURITY=true u svom okruženju" +step2 = "Ili postavite security.enableLogin=true u settings.yml" +step3 = "Ponovno pokrenite poslužitelj" [setup.login] title = "Prijava" @@ -5797,6 +5961,13 @@ submit = "Prijava" signInWith = "Prijavite se pomoću" oauthPending = "Otvaranje preglednika za autentikaciju..." orContinueWith = "Ili nastavite s e-poÅ”tom" +serverRequirement = "Napomena: Poslužitelj mora imati omogućenu prijavu." +showInstructions = "Kako omogućiti?" +hideInstructions = "Sakrij upute" +instructions = "Da biste omogućili prijavu na svom Stirling PDF poslužitelju:" +instructionsEnvVar = "Postavite varijablu okruženja:" +instructionsOrYml = "Ili u settings.yml:" +instructionsRestart = "Zatim ponovno pokrenite poslužitelj kako bi promjene stupile na snagu." [setup.login.username] label = "Korisničko ime" @@ -5840,7 +6011,7 @@ paragraph = "Stranica s odlomcima" sparse = "Rijedak tekst" [pdfTextEditor.groupingMode] -auto = "Auto" +auto = "Automatski" paragraph = "Odlomak" singleLine = "Jedan redak" @@ -5853,6 +6024,7 @@ earlyAccess = "Rani pristup" reset = "PoniÅ”ti promjene" downloadJson = "Preuzmi JSON" generatePdf = "Generiraj PDF" +saveChanges = "Spremi promjene" [pdfTextEditor.options.autoScaleText] title = "Automatski skaliraj tekst kako bi stao u okvire" @@ -5890,6 +6062,8 @@ alpha = "Ovaj alfa preglednik joÅ” se razvija — određeni fontovi, boje, efekt [pdfTextEditor.empty] title = "Nijedan dokument nije učitan" subtitle = "Učitajte PDF ili JSON datoteku kako biste započeli uređivanje teksta." +dropzone = "Ovdje povucite i ispustite PDF ili JSON datoteku ili kliknite za pregledavanje" +dropzoneWithFiles = "Odaberite datoteku s kartice Datoteke ili ovdje povucite i ispustite PDF ili JSON datoteku, ili kliknite za pregledavanje" [pdfTextEditor.welcomeBanner] title = "DobrodoÅ”li u PDF uređivač teksta (rani pristup)" diff --git a/frontend/public/locales/hu-HU/translation.toml b/frontend/public/locales/hu-HU/translation.toml index 514db6f07c..d68e3a0bba 100644 --- a/frontend/public/locales/hu-HU/translation.toml +++ b/frontend/public/locales/hu-HU/translation.toml @@ -163,6 +163,11 @@ unfavorite = "EltĆ”volĆ­tĆ”s a kedvencekből" fullscreen = "VĆ”ltĆ”s teljes kĆ©pernyős módra" sidebar = "VĆ”ltĆ”s oldalsĆ”v módra" +[backendStartup] +notFoundTitle = "Backend nem talĆ”lható" +retry = "PróbĆ”lja Ćŗjra" +unreachable = "Az alkalmazĆ”s jelenleg nem tud csatlakozni a Backendhez. Ellenőrizze a Backend Ć”llapotĆ”t Ć©s a hĆ”lózati kapcsolatot, majd próbĆ”lja Ćŗjra." + [zipWarning] title = "Nagy ZIP fĆ”jl" message = "Ez a ZIP {{count}} fĆ”jlt tartalmaz. MĆ©gis kibontja?" @@ -912,6 +917,9 @@ desc = "TƶbblĆ©pĆ©ses munkafolyamatok ƶsszeĆ”llĆ­tĆ”sa PDF műveletek ƶsszef desc = "PDF-ek egymĆ”sra helyezĆ©se egy mĆ”sik PDF-en" title = "PDF-ek egymĆ”sra helyezĆ©se" +[home.pdfTextEditor] +title = "PDF szƶvegszerkesztő" +desc = "MeglĆ©vő szƶveg Ć©s kĆ©pek szerkesztĆ©se a PDF-ekben" [home.addText] tags = "szƶveg, megjegyzĆ©s, cĆ­mke" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Rajzolt alƔƭrĆ”s" defaultImageLabel = "Feltƶltƶtt alƔƭrĆ”s" defaultTextLabel = "GĆ©pelt alƔƭrĆ”s" saveButton = "AlƔƭrĆ”s mentĆ©se" +savePersonal = "MentĆ©s szemĆ©lyeskĆ©nt" +saveShared = "MentĆ©s megosztottkĆ©nt" saveUnavailable = "Előbb hozzon lĆ©tre egy alƔƭrĆ”st a mentĆ©shez." noChanges = "Az aktuĆ”lis alƔƭrĆ”s mĆ”r mentve van." +tempStorageTitle = "Ideiglenes bƶngĆ©szőbeli tĆ”rolĆ”s" +tempStorageDescription = "Az alƔƭrĆ”sok csak a bƶngĆ©szőben tĆ”rolódnak. Elvesznek, ha tƶrli a bƶngĆ©szőadatokat vagy bƶngĆ©szőt vĆ”lt." +personalHeading = "SzemĆ©lyes alƔƭrĆ”sok" +sharedHeading = "Megosztott alƔƭrĆ”sok" +personalDescription = "Csak Ɩn lĆ”thatja ezeket az alƔƭrĆ”sokat." +sharedDescription = "Minden felhasznĆ”ló lĆ”thatja Ć©s hasznĆ”lhatja ezeket az alƔƭrĆ”sokat." [sign.saved.type] canvas = "Rajz" @@ -3020,6 +3036,91 @@ title = "PDF informĆ”ciók lekĆ©rĆ©se" header = "PDF informĆ”ciók lekĆ©rĆ©se" submit = "InformĆ”ciók lekĆ©rĆ©se" downloadJson = "JSON letƶltĆ©se" +processing = "InformĆ”ciók kinyerĆ©se..." +results = "EredmĆ©nyek" +noResults = "Futtassa az eszkƶzt a jelentĆ©s lĆ©trehozĆ”sĆ”hoz." +downloads = "LetƶltĆ©sek" +noneDetected = "Nem talĆ”lható" +indexTitle = "Index" + +[getPdfInfo.report] +entryLabel = "Teljes informĆ”ciós ƶsszefoglaló" +shortTitle = "PDF-informĆ”ciók" + +[getPdfInfo.sections] +metadata = "Metaadatok" +formFields = "Űrlapmezők" +basicInfo = "AlapinformĆ”ciók" +documentInfo = "DokumentuminformĆ”ciók" +compliance = "MegfelelősĆ©g" +encryption = "TitkosĆ­tĆ”s" +permissions = "EngedĆ©lyek" +other = "EgyĆ©b" +perPageInfo = "OldalankĆ©nti informĆ”ciók" +tableOfContents = "TartalomjegyzĆ©k" + +[getPdfInfo.other] +attachments = "MellĆ©kletek" +embeddedFiles = "BeĆ”gyazott fĆ”jlok" +javaScript = "JavaScript" +layers = "RĆ©tegek" +structureTree = "StruktĆŗrafa" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "MĆ©ret" +annotations = "MegjegyzĆ©sek" +images = "KĆ©pek" +links = "HivatkozĆ”sok" +fonts = "BetűtĆ­pusok" +xobjects = "XObject-ek szĆ”ma" +multimedia = "MultimĆ©dia" + +[getPdfInfo.summary] +pages = "Oldalak" +fileSize = "FĆ”jlmĆ©ret" +pdfVersion = "PDF-verzió" +language = "Nyelv" +title = "PDF-ƶsszefoglaló" +author = "Szerző" +created = "LĆ©trehozva" +modified = "MódosĆ­tva" +permsAll = "Minden jogosultsĆ”g engedĆ©lyezve" +permsRestricted = "{{count}} korlĆ”tozĆ”s" +permsMixed = "NĆ©hĆ”ny jogosultsĆ”g korlĆ”tozott" +hasCompliance = "MegfelelősĆ©gi szabvĆ”nyokkal rendelkezik" +noCompliance = "Nincsenek megfelelősĆ©gi szabvĆ”nyok" +basic = "AlapinformĆ”ciók" +documentInfo = "DokumentuminformĆ”ciók" +securityTitle = "BiztonsĆ”gi Ć”llapot" +technical = "Technikai" +overviewTitle = "PDF-Ć”ttekintĆ©s" + +[getPdfInfo.summary.security] +encrypted = "TitkosĆ­tott PDF – jelszóvĆ©delemmel" +unencrypted = "TitkosĆ­tatlan PDF – nincs jelszóvĆ©delem" + +[getPdfInfo.summary.tech] +images = "KĆ©pek" +fonts = "BetűtĆ­pusok" +formFields = "Űrlapmezők" +embeddedFiles = "BeĆ”gyazott fĆ”jlok" +javaScript = "JavaScript" +layers = "RĆ©tegek" +bookmarks = "Kƶnyvjelzők" +multimedia = "MultimĆ©dia" + +[getPdfInfo.summary.overview] +untitled = "egy cĆ­m nĆ©lküli dokumentum" +unknown = "Ismeretlen szerző" +text = "Ez egy {{pages}} oldalas, {{title}} cĆ­mű PDF, amelyet {{author}} kĆ©szĆ­tett (PDF-verzió: {{version}})." + +[getPdfInfo.error] +partial = "NĆ©hĆ”ny fĆ”jlt nem sikerült feldolgozni." +unexpected = "VĆ”ratlan hiba a kinyerĆ©s sorĆ”n." + +[getPdfInfo.status] +complete = "KinyerĆ©s befejezve" [extractPage] tags = "kinyerĆ©s" @@ -3438,6 +3539,9 @@ signinTitle = "KĆ©rjük, jelentkezzen be" ssoSignIn = "BejelentkezĆ©s egyszeri bejelentkezĆ©ssel" oAuth2AutoCreateDisabled = "OAuth2 automatikus felhasznĆ”lólĆ©trehozĆ”s letiltva" oAuth2AdminBlockedUser = "A nem regisztrĆ”lt felhasznĆ”lók regisztrĆ”ciója vagy bejelentkezĆ©se jelenleg le van tiltva. KĆ©rjük, forduljon a rendszergazdĆ”hoz." +oAuth2RequiresLicense = "Az OAuth/SSO bejelentkezĆ©s fizetős licencet igĆ©nyel (Server vagy Enterprise). KĆ©rjük, lĆ©pjen kapcsolatba az adminisztrĆ”torral a csomag frissĆ­tĆ©sĆ©hez." +saml2RequiresLicense = "A SAML bejelentkezĆ©s fizetős licencet igĆ©nyel (Server vagy Enterprise). KĆ©rjük, lĆ©pjen kapcsolatba az adminisztrĆ”torral a csomag frissĆ­tĆ©sĆ©hez." +maxUsersReached = "ElĆ©rte az aktuĆ”lis licenchez tartozó felhasznĆ”lók maximĆ”lis szĆ”mĆ”t. KĆ©rjük, lĆ©pjen kapcsolatba az adminisztrĆ”torral a csomag frissĆ­tĆ©sĆ©hez vagy tovĆ”bbi felhasznĆ”lói helyek hozzĆ”adĆ”sĆ”hoz." oauth2RequestNotFound = "A hitelesĆ­tĆ©si kĆ©rĆ©s nem talĆ”lható" oauth2InvalidUserInfoResponse = "ƉrvĆ©nytelen felhasznĆ”lói informĆ”ció vĆ”lasz" oauth2invalidRequest = "ƉrvĆ©nytelen kĆ©rĆ©s" @@ -3846,14 +3950,17 @@ fitToWidth = "SzĆ©lessĆ©ghez igazĆ­tĆ”s" actualSize = "TĆ©nyleges mĆ©ret" [viewer] +cannotPreviewFile = "A fĆ”jl előnĆ©zete nem lehetsĆ©ges" +dualPageView = "KĆ©toldalas nĆ©zet" firstPage = "Első oldal" lastPage = "Utolsó oldal" -previousPage = "Előző oldal" nextPage = "Kƶvetkező oldal" +onlyPdfSupported = "A megjelenĆ­tő csak PDF fĆ”jlokat tĆ”mogat. Úgy tűnik, ez a fĆ”jl mĆ”s formĆ”tumĆŗ." +previousPage = "Előző oldal" +singlePageView = "Egyoldalas nĆ©zet" +unknownFile = "Ismeretlen fĆ”jl" zoomIn = "NagyĆ­tĆ”s" zoomOut = "KicsinyĆ­tĆ©s" -singlePageView = "Egyoldalas nĆ©zet" -dualPageView = "KĆ©toldalas nĆ©zet" [rightRail] closeSelected = "Kijelƶlt fĆ”jlok bezĆ”rĆ”sa" @@ -3877,6 +3984,7 @@ toggleSidebar = "OldalsĆ”v ki/be" exportSelected = "Kijelƶlt oldalak exportĆ”lĆ”sa" toggleAnnotations = "Jegyzetek lĆ”thatósĆ”gĆ”nak vĆ”ltĆ”sa" annotationMode = "JegyzetelĆ©si mód vĆ”ltĆ”sa" +print = "PDF nyomtatĆ”sa" draw = "RajzolĆ”s" save = "MentĆ©s" saveChanges = "VĆ”ltoztatĆ”sok mentĆ©se" @@ -4494,6 +4602,7 @@ description = "URL vagy fĆ”jlnĆ©v az impresszumhoz (egyes joghatósĆ”gokban kƶt title = "PrĆ©mium Ć©s VĆ”llalati" description = "PrĆ©mium vagy vĆ”llalati licenckulcs konfigurĆ”lĆ”sa." license = "LicenckonfigurĆ”ció" +noInput = "KĆ©rjük, adjon meg egy licenckulcsot vagy fĆ”jlt" [admin.settings.premium.licenseKey] toggle = "Van licenckulcsa vagy tanĆŗsĆ­tvĆ”nyfĆ”jlja?" @@ -4511,6 +4620,25 @@ line1 = "A jelenlegi licenckulcs felülĆ­rĆ”sa nem vonható vissza." line2 = "A korĆ”bbi licenc vĆ©gleg elveszik, hacsak nem kĆ©szĆ­tett róla mĆ”shol biztonsĆ”gi mĆ”solatot." line3 = "Fontos: Tartsa a licenckulcsokat bizalmasan Ć©s biztonsĆ”gban. Soha ne ossza meg nyilvĆ”nosan." +[admin.settings.premium.inputMethod] +text = "Licenckulcs" +file = "TanĆŗsĆ­tvĆ”nyfĆ”jl" + +[admin.settings.premium.file] +label = "Licenc tanĆŗsĆ­tvĆ”nyfĆ”jl" +description = "Tƶltse fel az offline vĆ”sĆ”rlĆ”sból szĆ”rmazó .lic vagy .cert licencfĆ”jlt" +choose = "LicencfĆ”jl kivĆ”lasztĆ”sa" +selected = "KivĆ”lasztva: {{filename}} ({{size}})" +successMessage = "A licencfĆ”jl feltƶltĆ©se Ć©s aktivĆ”lĆ”sa sikeres. Nincs szüksĆ©g ĆŗjraindĆ­tĆ”sra." + +[admin.settings.premium.currentLicense] +title = "AktĆ­v licenc" +file = "ForrĆ”s: licencfĆ”jl ({{path}})" +key = "ForrĆ”s: licenckulcs" +type = "TĆ­pus: {{type}}" +noInput = "Adjon meg egy licenckulcsot, vagy tƶltsƶn fel tanĆŗsĆ­tvĆ”nyfĆ”jlt" +success = "Siker" + [admin.settings.premium.enabled] label = "PrĆ©mium funkciók engedĆ©lyezĆ©se" description = "Licenckulcs-ellenőrzĆ©sek engedĆ©lyezĆ©se a pro/vĆ”llalati funkciókhoz" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} kivĆ”lasztva" download = "LetƶltĆ©s" delete = "TƶrlĆ©s" unsupported = "Nem tĆ”mogatott" +active = "AktĆ­v" addToUpload = "HozzĆ”adĆ”s a feltƶltĆ©shez" +closeFile = "FĆ”jl bezĆ”rĆ”sa" deleteAll = "Ɩsszes tƶrlĆ©se" loadingFiles = "FĆ”jlok betƶltĆ©se..." noFiles = "Nem Ć”llnak rendelkezĆ©sre fĆ”jlok" @@ -5132,7 +5262,7 @@ upgrade = "FrissĆ­tĆ©s most →" freeTitle = "Szerverlicenc" overLimitTitle = "Szerverlicenc szüksĆ©ges" overLimitBody = "LicencelĆ©sünk szerverenkĆ©nt legfeljebb {{freeTierLimit}} felhasznĆ”lót enged ingyen. Ɩnnek {{overLimitUserCopy}} Stirling felhasznĆ”lója van. A zavartalan hasznĆ”lathoz vĆ”ltson a Stirling Server csomagra – korlĆ”tlan hely, PDF szƶvegszerkesztĆ©s Ć©s teljes adminisztrĆ”tori vezĆ©rlĆ©s $99/szerver/hó Ć”ron." -freeBody = "Az Open-Core licencelĆ©sünk szerverenkĆ©nt legfeljebb {{freeTierLimit}} felhasznĆ”lót enged ingyen. A zavartalan bővülĆ©shez Ć©s az Ćŗj PDF szƶvegszerkesztő eszkƶz korai elĆ©rĆ©sĆ©hez a Stirling Server csomagot ajĆ”nljuk – teljes szerkesztĆ©s Ć©s korlĆ”tlan hely $99/szerver/hó Ć”ron." +freeBody = "A Open-Core licencünk szerverenkĆ©nt legfeljebb {{freeTierLimit}} felhasznĆ”lót engedĆ©lyez ingyenesen. A zƶkkenőmentes skĆ”lĆ”zĆ”shoz a Stirling Server csomagot ajĆ”nljuk - korlĆ”tlan felhasznĆ”ló Ć©s SSO tĆ”mogatĆ”s $99/szerver/hó." [onboarding.desktopInstall] title = "LetƶltĆ©s" @@ -5237,6 +5367,31 @@ error = "Nem sikerült frissĆ­teni a felhasznĆ”lói Ć”llapotot" success = "FelhasznĆ”ló sikeresen tƶrƶlve" error = "Nem sikerült tƶrƶlni a felhasznĆ”lót" +[workspace.people.changePassword] +action = "Jelszó módosĆ­tĆ”sa" +title = "Jelszó módosĆ­tĆ”sa" +subtitle = "Jelszó frissĆ­tĆ©se ehhez:" +newPassword = "Új jelszó" +confirmPassword = "Jelszó megerősĆ­tĆ©se" +placeholder = "Adjon meg egy Ćŗj jelszót" +confirmPlaceholder = "Adja meg Ćŗjra az Ćŗj jelszót" +passwordRequired = "KĆ©rjük, adjon meg egy Ćŗj jelszót" +passwordMismatch = "A jelszavak nem egyeznek" +generateRandom = "BiztonsĆ”gos jelszó generĆ”lĆ”sa" +generatedPreview = "GenerĆ”lt jelszó:" +copyTooltip = "MĆ”solĆ”s a vĆ”gólapra" +copiedToClipboard = "A jelszó a vĆ”gólapra mĆ”solva" +copyFailed = "A jelszó mĆ”solĆ”sa nem sikerült" +sendEmail = "E-mail küldĆ©se a felhasznĆ”lónak a vĆ”ltozĆ”sról" +includePassword = "Az Ćŗj jelszó szerepeljen az e-mailben" +forcePasswordChange = "A felhasznĆ”ló kĆ©nyszerĆ­tĆ©se a jelszó megvĆ”ltoztatĆ”sĆ”ra a kƶvetkező bejelentkezĆ©skor" +emailUnavailable = "Ennek a felhasznĆ”lónak az e-mail cĆ­me Ć©rvĆ©nytelen. Az Ć©rtesĆ­tĆ©sek le vannak tiltva." +smtpDisabled = "Az e-mail Ć©rtesĆ­tĆ©sekhez az SMTP engedĆ©lyezĆ©se szüksĆ©ges a beĆ”llĆ­tĆ”sokban." +notifyOnly = "E-mailt küldünk jelszó nĆ©lkül, amelyben Ć©rtesĆ­tjük a felhasznĆ”lót, hogy egy admin módosĆ­totta a jelszót." +submit = "Jelszó frissĆ­tĆ©se" +success = "A jelszó sikeresen frissĆ­tve" +error = "A jelszó frissĆ­tĆ©se nem sikerült" + [workspace.people.emailInvite] tab = "E-mail meghĆ­vó" description = "ƍrja be vagy illessze be alĆ”bb az e-mail cĆ­meket, vesszővel elvĆ”lasztva. A felhasznĆ”lók e-mailben kapjĆ”k meg a bejelentkezĆ©si adatokat." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "LegalĆ”bb egy e-mail cĆ­m megadĆ”sa szüksĆ©ges" submit = "MeghĆ­vók küldĆ©se" success = "felhasznĆ”ló sikeresen meghĆ­vva" -partialSuccess = "NĆ©hĆ”ny meghĆ­vó sikertelen volt" +partialFailure = "NĆ©hĆ”ny meghĆ­vĆ”s sikertelen volt" allFailed = "Nem sikerült meghĆ­vni a felhasznĆ”lókat" error = "Nem sikerült elküldeni a meghĆ­vókat" @@ -5770,6 +5925,7 @@ subtitle = "Jelentkezzen be Stirling-fiókjĆ”val" [setup.selfhosted] title = "BejelentkezĆ©s a szerverre" subtitle = "Adja meg a szerver hitelesĆ­tő adatait" +link = "vagy csatlakozzon egy sajĆ”t üzemeltetĆ©sű fiókhoz" [setup.server] title = "CsatlakozĆ”s a szerverhez" @@ -5788,6 +5944,14 @@ description = "Adja meg az ƶnhostolt Stirling PDF szerver teljes URL-jĆ©t" emptyUrl = "Adjon meg egy szerver URL-t" unreachable = "Nem sikerült kapcsolódni a szerverhez" testFailed = "A kapcsolat tesztje sikertelen" +configFetch = "Nem sikerült letƶlteni a szerver konfigurĆ”ciójĆ”t. Ellenőrizze az URL-t, Ć©s próbĆ”lja meg Ćŗjra." + +[setup.server.error.securityDisabled] +title = "A bejelentkezĆ©s nincs engedĆ©lyezve" +body = "Ezen a szerveren a bejelentkezĆ©s nincs engedĆ©lyezve. A csatlakozĆ”shoz engedĆ©lyeznie kell a hitelesĆ­tĆ©st:" +step1 = "ƁllĆ­tsa be a DOCKER_ENABLE_SECURITY=true Ć©rtĆ©ket a kƶrnyezetĆ©ben" +step2 = "Vagy Ć”llĆ­tsa be a security.enableLogin=true Ć©rtĆ©ket a settings.yml fĆ”jlban" +step3 = "IndĆ­tsa Ćŗjra a szervert" [setup.login] title = "BejelentkezĆ©s" @@ -5797,6 +5961,13 @@ submit = "BejelentkezĆ©s" signInWith = "BejelentkezĆ©s ezzel" oauthPending = "BƶngĆ©sző megnyitĆ”sa hitelesĆ­tĆ©shez..." orContinueWith = "Vagy folytassa e-maillel" +serverRequirement = "MegjegyzĆ©s: A szerveren engedĆ©lyezni kell a bejelentkezĆ©st." +showInstructions = "Hogyan engedĆ©lyezhető?" +hideInstructions = "UtasĆ­tĆ”sok elrejtĆ©se" +instructions = "A bejelentkezĆ©s engedĆ©lyezĆ©sĆ©hez a Stirling PDF szerverĆ©n:" +instructionsEnvVar = "ƁllĆ­tsa be a kƶrnyezeti vĆ”ltozót:" +instructionsOrYml = "Vagy a settings.yml-ben:" +instructionsRestart = "EzutĆ”n indĆ­tsa Ćŗjra a szervert, hogy a módosĆ­tĆ”sok Ć©letbe lĆ©pjenek." [setup.login.username] label = "FelhasznĆ”lónĆ©v" @@ -5853,6 +6024,7 @@ earlyAccess = "Korai hozzĆ”fĆ©rĆ©s" reset = "MódosĆ­tĆ”sok visszaĆ”llĆ­tĆ”sa" downloadJson = "JSON letƶltĆ©se" generatePdf = "PDF generĆ”lĆ”sa" +saveChanges = "VĆ”ltoztatĆ”sok mentĆ©se" [pdfTextEditor.options.autoScaleText] title = "Szƶveg automatikus mĆ©retezĆ©se a dobozokhoz" @@ -5890,6 +6062,8 @@ alpha = "Ez az alfa nĆ©ző mĆ©g fejlődik—bizonyos betűtĆ­pusok, szĆ­nek, Ć”t [pdfTextEditor.empty] title = "Nincs dokumentum betƶltve" subtitle = "Tƶltsƶn be egy PDF- vagy JSON-fĆ”jlt a szƶvegtartalom szerkesztĆ©sĆ©nek megkezdĆ©sĆ©hez." +dropzone = "HĆŗzzon ide egy PDF vagy JSON fĆ”jlt, vagy kattintson a tallózĆ”shoz" +dropzoneWithFiles = "VĆ”lasszon fĆ”jlt a FĆ”jlok fülƶn, vagy hĆŗzzon ide egy PDF vagy JSON fĆ”jlt, illetve kattintson a tallózĆ”shoz" [pdfTextEditor.welcomeBanner] title = "Üdvƶzƶljük a PDF Text Editorben (korai hozzĆ”fĆ©rĆ©s)" diff --git a/frontend/public/locales/id-ID/translation.toml b/frontend/public/locales/id-ID/translation.toml index 010266296b..77758a4928 100644 --- a/frontend/public/locales/id-ID/translation.toml +++ b/frontend/public/locales/id-ID/translation.toml @@ -163,6 +163,11 @@ unfavorite = "Hapus dari Favorit" fullscreen = "Beralih ke mode layar penuh" sidebar = "Beralih ke mode bilah sisi" +[backendStartup] +notFoundTitle = "Backend tidak ditemukan" +retry = "Coba lagi" +unreachable = "Aplikasi saat ini tidak dapat terhubung ke backend. Periksa status backend dan konektivitas jaringan, lalu coba lagi." + [zipWarning] title = "File ZIP Besar" message = "ZIP ini berisi {{count}} file. Tetap ekstrak?" @@ -190,7 +195,7 @@ title = "Pengaturan Dibuka" message = "Silakan pilih Stirling PDF di pengaturan sistem Anda" [defaultApp.error] -title = "Error" +title = "Kesalahan" message = "Gagal menyetel penangan PDF default" [language] @@ -348,7 +353,7 @@ title = "Konfigurasi" systemSettings = "Pengaturan Sistem" features = "Fitur" endpoints = "Endpoint" -database = "Database" +database = "Basis Data" advanced = "Lanjutan" [settings.securityAuth] @@ -383,7 +388,7 @@ logout = "Keluar" [settings.connection.mode] saas = "Stirling Cloud" -selfhosted = "Self-Hosted" +selfhosted = "Dihost Sendiri" [settings.general] title = "Umum" @@ -544,8 +549,8 @@ usage = "Lihat Penggunaan" [endpointStatistics] title = "Statistik Endpoint" header = "Statistik Endpoint" -top10 = "Top 10" -top20 = "Top 20" +top10 = "10 Teratas" +top20 = "20 Teratas" all = "Semua" refresh = "Muat Ulang" dataTypeLabel = "Tipe Data:" @@ -912,6 +917,9 @@ desc = "Bangun alur kerja multi-langkah dengan merangkai tindakan PDF. Ideal unt desc = "Menumpuk PDF di atas PDF lain" title = "Tumpuk PDF" +[home.pdfTextEditor] +title = "Editor Teks PDF" +desc = "Edit teks dan gambar yang ada di dalam PDF" [home.addText] tags = "teks,anotasi,label" @@ -1173,7 +1181,7 @@ selectFilesPlaceholder = "Pilih file di tampilan utama untuk memulai" settings = "Pengaturan" conversionCompleted = "Konversi selesai" results = "Hasil" -defaultFilename = "converted_file" +defaultFilename = "file_terkonversi" conversionResults = "Hasil Konversi" convertFrom = "Konversi dari" convertTo = "Konversi ke" @@ -1360,7 +1368,7 @@ title = "Tambahkan Watermark" desc = "Tambahkan tanda air teks atau gambar ke file PDF" completed = "Tanda air ditambahkan" submit = "Tambahkan Watermark" -filenamePrefix = "watermarked" +filenamePrefix = "bertanda_air" [watermark.error] failed = "Terjadi kesalahan saat menambahkan tanda air ke PDF." @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Tanda tangan gambar" defaultImageLabel = "Tanda tangan terunggah" defaultTextLabel = "Tanda tangan ketik" saveButton = "Simpan tanda tangan" +savePersonal = "Simpan Pribadi" +saveShared = "Simpan Bersama" saveUnavailable = "Buat tanda tangan terlebih dahulu untuk menyimpannya." noChanges = "Tanda tangan saat ini sudah disimpan." +tempStorageTitle = "Penyimpanan browser sementara" +tempStorageDescription = "Tanda tangan disimpan hanya di browser Anda. Data akan hilang jika Anda membersihkan data browser atau berpindah browser." +personalHeading = "Tanda Tangan Pribadi" +sharedHeading = "Tanda Tangan Bersama" +personalDescription = "Hanya Anda yang dapat melihat tanda tangan ini." +sharedDescription = "Semua pengguna dapat melihat dan menggunakan tanda tangan ini." [sign.saved.type] canvas = "Gambar" @@ -2701,7 +2717,7 @@ header = "Hapus sertifikat digital dari PDF" selectPDF = "Pilih file PDF:" submit = "Hapus Tanda Tangan" description = "Alat ini akan menghapus tanda tangan sertifikat digital dari dokumen PDF Anda." -filenamePrefix = "unsigned" +filenamePrefix = "tanpa_tanda_tangan" [removeCertSign.files] placeholder = "Pilih file PDF di tampilan utama untuk memulai" @@ -3020,6 +3036,91 @@ title = "Dapatkan Info tentang PDF" header = "Dapatkan Info tentang PDF" submit = "Dapatkan Info" downloadJson = "Unduh JSON" +processing = "Mengekstrak informasi..." +results = "Hasil" +noResults = "Jalankan alat untuk menghasilkan laporan." +downloads = "Unduhan" +noneDetected = "Tidak ada yang terdeteksi" +indexTitle = "Indeks" + +[getPdfInfo.report] +entryLabel = "Ringkasan informasi lengkap" +shortTitle = "Informasi PDF" + +[getPdfInfo.sections] +metadata = "Metadata" +formFields = "Bidang Formulir" +basicInfo = "Info Dasar" +documentInfo = "Info Dokumen" +compliance = "Kepatuhan" +encryption = "Enkripsi" +permissions = "Izin" +other = "Lainnya" +perPageInfo = "Info per Halaman" +tableOfContents = "Daftar Isi" + +[getPdfInfo.other] +attachments = "Lampiran" +embeddedFiles = "File Tertanam" +javaScript = "JavaScript" +layers = "Lapisan" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Ukuran" +annotations = "Anotasi" +images = "Gambar" +links = "Tautan" +fonts = "Font" +xobjects = "Jumlah XObject" +multimedia = "Multimedia" + +[getPdfInfo.summary] +pages = "Halaman" +fileSize = "Ukuran File" +pdfVersion = "Versi PDF" +language = "Bahasa" +title = "Ringkasan PDF" +author = "Penulis" +created = "Dibuat" +modified = "Diubah" +permsAll = "Semua izin diperbolehkan" +permsRestricted = "{{count}} pembatasan" +permsMixed = "Beberapa izin dibatasi" +hasCompliance = "Memiliki standar kepatuhan" +noCompliance = "Tidak ada standar kepatuhan" +basic = "Informasi Dasar" +documentInfo = "Informasi Dokumen" +securityTitle = "Status Keamanan" +technical = "Teknis" +overviewTitle = "Gambaran Umum PDF" + +[getPdfInfo.summary.security] +encrypted = "PDF terenkripsi - Perlindungan kata sandi aktif" +unencrypted = "PDF tidak terenkripsi - Tanpa perlindungan kata sandi" + +[getPdfInfo.summary.tech] +images = "Gambar" +fonts = "Font" +formFields = "Bidang Formulir" +embeddedFiles = "File Tertanam" +javaScript = "JavaScript" +layers = "Lapisan" +bookmarks = "Bookmark" +multimedia = "Multimedia" + +[getPdfInfo.summary.overview] +untitled = "dokumen tanpa judul" +unknown = "Penulis tidak diketahui" +text = "Ini adalah PDF {{pages}} halaman berjudul {{title}} yang dibuat oleh {{author}} (versi PDF {{version}})." + +[getPdfInfo.error] +partial = "Beberapa file tidak dapat diproses." +unexpected = "Kesalahan tak terduga saat ekstraksi." + +[getPdfInfo.status] +complete = "Ekstraksi selesai" [extractPage] tags = "ekstrak" @@ -3438,6 +3539,9 @@ signinTitle = "Silakan masuk" ssoSignIn = "Masuk melalui Single Sign - on" oAuth2AutoCreateDisabled = "OAUTH2 Buat Otomatis Pengguna Dinonaktifkan" oAuth2AdminBlockedUser = "Registrasi atau login pengguna yang tidak terdaftar saat ini diblokir. Silakan hubungi administrator." +oAuth2RequiresLicense = "Login OAuth/SSO memerlukan lisensi berbayar (Server atau Enterprise). Silakan hubungi administrator untuk meningkatkan paket Anda." +saml2RequiresLicense = "Login SAML memerlukan lisensi berbayar (Server atau Enterprise). Silakan hubungi administrator untuk meningkatkan paket Anda." +maxUsersReached = "Jumlah pengguna maksimum untuk lisensi Anda saat ini telah tercapai. Silakan hubungi administrator untuk meningkatkan paket Anda atau menambah seat." oauth2RequestNotFound = "Permintaan otorisasi tidak ditemukan" oauth2InvalidUserInfoResponse = "Respons Info Pengguna Tidak Valid" oauth2invalidRequest = "Permintaan Tidak Valid" @@ -3533,7 +3637,7 @@ title = "PDF Ke Halaman Tunggal" header = "PDF Ke Halaman Tunggal" submit = "Konversi ke Halaman Tunggal" description = "Alat ini akan menggabungkan semua halaman PDF Anda menjadi satu halaman besar. Lebarnya akan tetap sama dengan halaman asli, tetapi tingginya merupakan penjumlahan dari semua tinggi halaman." -filenamePrefix = "single_page" +filenamePrefix = "halaman_tunggal" [pdfToSinglePage.files] placeholder = "Pilih file PDF di tampilan utama untuk memulai" @@ -3846,14 +3950,17 @@ fitToWidth = "Sesuaikan ke Lebar" actualSize = "Ukuran Asli" [viewer] +cannotPreviewFile = "Tidak dapat menampilkan pratinjau file" +dualPageView = "Tampilan Dua Halaman" firstPage = "Halaman Pertama" lastPage = "Halaman Terakhir" -previousPage = "Halaman Sebelumnya" nextPage = "Halaman Berikutnya" +onlyPdfSupported = "Penampil hanya mendukung file PDF. File ini tampaknya memiliki format yang berbeda." +previousPage = "Halaman Sebelumnya" +singlePageView = "Tampilan Satu Halaman" +unknownFile = "File tidak dikenal" zoomIn = "Perbesar" zoomOut = "Perkecil" -singlePageView = "Tampilan Satu Halaman" -dualPageView = "Tampilan Dua Halaman" [rightRail] closeSelected = "Tutup File Terpilih" @@ -3877,6 +3984,7 @@ toggleSidebar = "Alihkan Sidebar" exportSelected = "Ekspor Halaman Terpilih" toggleAnnotations = "Alihkan Visibilitas Anotasi" annotationMode = "Alihkan Mode Anotasi" +print = "Cetak PDF" draw = "Gambar" save = "Simpan" saveChanges = "Simpan Perubahan" @@ -4282,7 +4390,7 @@ label = "Blokir Pendaftaran" description = "Cegah pendaftaran pengguna baru melalui SAML2" [admin.settings.database] -title = "Database" +title = "Basis Data" description = "Konfigurasikan pengaturan koneksi database kustom untuk penerapan enterprise." configuration = "Konfigurasi Database" @@ -4494,6 +4602,7 @@ description = "URL atau nama file untuk impressum (diperlukan di beberapa yurisd title = "Premium & Enterprise" description = "Konfigurasikan kunci lisensi premium atau enterprise Anda." license = "Konfigurasi Lisensi" +noInput = "Harap berikan kunci atau file lisensi" [admin.settings.premium.licenseKey] toggle = "Punya kunci lisensi atau file sertifikat?" @@ -4511,6 +4620,25 @@ line1 = "Menimpa kunci lisensi Anda saat ini tidak dapat dibatalkan." line2 = "Lisensi sebelumnya akan hilang permanen kecuali Anda mencadangkannya di tempat lain." line3 = "Penting: Jaga kunci lisensi tetap privat dan aman. Jangan pernah membagikannya secara publik." +[admin.settings.premium.inputMethod] +text = "Kunci Lisensi" +file = "File Sertifikat" + +[admin.settings.premium.file] +label = "File Sertifikat Lisensi" +description = "Unggah file lisensi .lic atau .cert Anda dari pembelian offline" +choose = "Pilih File Lisensi" +selected = "Dipilih: {{filename}} ({{size}})" +successMessage = "File lisensi berhasil diunggah dan diaktifkan. Tidak perlu restart." + +[admin.settings.premium.currentLicense] +title = "Lisensi Aktif" +file = "Sumber: File lisensi ({{path}})" +key = "Sumber: Kunci lisensi" +type = "Tipe: {{type}}" +noInput = "Harap berikan kunci lisensi atau unggah file sertifikat" +success = "Berhasil" + [admin.settings.premium.enabled] label = "Aktifkan Fitur Premium" description = "Aktifkan pemeriksaan kunci lisensi untuk fitur pro/enterprise" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} dipilih" download = "Unduh" delete = "Hapus" unsupported = "Tidak didukung" +active = "Aktif" addToUpload = "Tambahkan ke Unggahan" +closeFile = "Tutup File" deleteAll = "Hapus Semua" loadingFiles = "Memuat file..." noFiles = "Tidak ada file tersedia" @@ -4938,13 +5068,13 @@ done = "Selesai" loading = "Memuat..." back = "Kembali" continue = "Lanjut" -error = "Error" +error = "Kesalahan" [config.overview] title = "Konfigurasi Aplikasi" description = "Pengaturan dan detail konfigurasi aplikasi saat ini." loading = "Memuat konfigurasi..." -error = "Error" +error = "Kesalahan" warning = "Peringatan Konfigurasi" [config.overview.sections] @@ -5132,7 +5262,7 @@ upgrade = "Upgrade sekarang →" freeTitle = "Lisensi Server" overLimitTitle = "Perlu Lisensi Server" overLimitBody = "Lisensi kami mengizinkan hingga {{freeTierLimit}} pengguna gratis per server. Anda memiliki {{overLimitUserCopy}} pengguna Stirling. Untuk terus berjalan tanpa gangguan, upgrade ke paket Stirling Server - kursi tanpa batas, pengeditan teks PDF, dan kontrol admin penuh seharga $99/server/bulan." -freeBody = "Lisensi Open-Core kami mengizinkan hingga {{freeTierLimit}} pengguna gratis per server. Untuk skala tanpa hambatan dan mendapatkan akses awal ke alat pengeditan teks PDF baru kami, kami sarankan paket Stirling Server - pengeditan penuh dan kursi tanpa batas seharga $99/server/bulan." +freeBody = "Lisensi Open-Core kami mengizinkan hingga {{freeTierLimit}} pengguna gratis per server. Untuk meningkatkan skala tanpa gangguan, kami merekomendasikan paket Stirling Server - pengguna tanpa batas dan dukungan SSO seharga $99/server/mo." [onboarding.desktopInstall] title = "Unduh" @@ -5237,6 +5367,31 @@ error = "Gagal memperbarui status pengguna" success = "Pengguna berhasil dihapus" error = "Gagal menghapus pengguna" +[workspace.people.changePassword] +action = "Ubah kata sandi" +title = "Ubah kata sandi" +subtitle = "Perbarui kata sandi untuk" +newPassword = "Kata sandi baru" +confirmPassword = "Konfirmasi kata sandi" +placeholder = "Masukkan kata sandi baru" +confirmPlaceholder = "Masukkan ulang kata sandi baru" +passwordRequired = "Silakan masukkan kata sandi baru" +passwordMismatch = "Kata sandi tidak cocok" +generateRandom = "Buat kata sandi aman" +generatedPreview = "Kata sandi yang dibuat:" +copyTooltip = "Salin ke papan klip" +copiedToClipboard = "Kata sandi disalin ke papan klip" +copyFailed = "Gagal menyalin kata sandi" +sendEmail = "Kirim email kepada pengguna tentang perubahan ini" +includePassword = "Sertakan kata sandi baru dalam email" +forcePasswordChange = "Paksa pengguna mengganti kata sandi saat login berikutnya" +emailUnavailable = "Email pengguna ini bukan alamat email yang valid. Notifikasi dinonaktifkan." +smtpDisabled = "Notifikasi email memerlukan SMTP diaktifkan di pengaturan." +notifyOnly = "Email akan dikirim tanpa kata sandi, memberi tahu pengguna bahwa admin telah mengubahnya." +submit = "Perbarui kata sandi" +success = "Kata sandi berhasil diperbarui" +error = "Gagal memperbarui kata sandi" + [workspace.people.emailInvite] tab = "Undangan Email" description = "Ketik atau tempel email di bawah, dipisahkan dengan koma. Pengguna akan menerima kredensial login melalui email." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Setidaknya satu alamat email diperlukan" submit = "Kirim Undangan" success = "pengguna berhasil diundang" -partialSuccess = "Beberapa undangan gagal" +partialFailure = "Beberapa undangan gagal" allFailed = "Gagal mengundang pengguna" error = "Gagal mengirim undangan" @@ -5388,7 +5543,7 @@ hideComparison = "Sembunyikan Perbandingan Fitur" featureComparison = "Perbandingan Fitur" from = "Mulai" perMonth = "/bulan" -perSeat = "/seat" +perSeat = "/pengguna" withServer = "+ Paket Server" licensedSeats = "Berlisensi: {{count}} seat" includedInCurrent = "Termasuk dalam Paket Anda" @@ -5549,7 +5704,7 @@ modalTitle = "Mulai - {{planName}}" title = "Pilih Periode Penagihan" savingsNote = "Hemat {{percent}}% dengan penagihan tahunan" basePrice = "Harga Dasar" -seatPrice = "Per Seat" +seatPrice = "Per Pengguna" totalForSeats = "Total ({{count}} seat)" selectMonthly = "Pilih Bulanan" selectYearly = "Pilih Tahunan" @@ -5752,7 +5907,7 @@ label = "Pilih Server" description = "Server self-hosted" [setup.step3] -label = "Login" +label = "Masuk" description = "Masukkan kredensial" [setup.mode.saas] @@ -5770,6 +5925,7 @@ subtitle = "Masuk dengan akun Stirling Anda" [setup.selfhosted] title = "Masuk ke Server" subtitle = "Masukkan kredensial server Anda" +link = "atau hubungkan ke akun self-hosted" [setup.server] title = "Sambungkan ke Server" @@ -5788,15 +5944,30 @@ description = "Masukkan URL lengkap server Stirling PDF self-hosted Anda" emptyUrl = "Masukkan URL server" unreachable = "Tidak dapat terhubung ke server" testFailed = "Tes koneksi gagal" +configFetch = "Gagal mengambil konfigurasi server. Periksa URL dan coba lagi." + +[setup.server.error.securityDisabled] +title = "Login Tidak Diaktifkan" +body = "Server ini tidak mengaktifkan login. Untuk terhubung ke server ini, Anda harus mengaktifkan autentikasi:" +step1 = "Setel DOCKER_ENABLE_SECURITY=true di lingkungan Anda" +step2 = "Atau setel security.enableLogin=true di settings.yml" +step3 = "Mulai ulang server" [setup.login] title = "Masuk" subtitle = "Masukkan kredensial Anda untuk melanjutkan" connectingTo = "Menghubungkan ke:" -submit = "Login" +submit = "Masuk" signInWith = "Masuk dengan" oauthPending = "Membuka browser untuk autentikasi..." orContinueWith = "Atau lanjut dengan email" +serverRequirement = "Catatan: Server harus mengaktifkan login." +showInstructions = "Bagaimana cara mengaktifkannya?" +hideInstructions = "Sembunyikan instruksi" +instructions = "Untuk mengaktifkan login pada server Stirling PDF Anda:" +instructionsEnvVar = "Setel variabel lingkungan:" +instructionsOrYml = "Atau di settings.yml:" +instructionsRestart = "Kemudian mulai ulang server Anda agar perubahan diterapkan." [setup.login.username] label = "Nama pengguna" @@ -5853,6 +6024,7 @@ earlyAccess = "Akses Awal" reset = "Reset Perubahan" downloadJson = "Unduh JSON" generatePdf = "Buat PDF" +saveChanges = "Simpan Perubahan" [pdfTextEditor.options.autoScaleText] title = "Sesuaikan teks otomatis ke kotak" @@ -5890,6 +6062,8 @@ alpha = "Penampil alpha ini masih berkembang—beberapa font, warna, efek transp [pdfTextEditor.empty] title = "Tidak ada dokumen dimuat" subtitle = "Muat file PDF atau JSON untuk mulai mengedit konten teks." +dropzone = "Seret dan letakkan file PDF atau JSON di sini, atau klik untuk memilih" +dropzoneWithFiles = "Pilih file dari tab File, atau seret dan letakkan file PDF atau JSON di sini, atau klik untuk memilih" [pdfTextEditor.welcomeBanner] title = "Selamat datang di PDF Text Editor (Akses Awal)" diff --git a/frontend/public/locales/it-IT/translation.toml b/frontend/public/locales/it-IT/translation.toml index ef9d94d83a..20796ceb06 100644 --- a/frontend/public/locales/it-IT/translation.toml +++ b/frontend/public/locales/it-IT/translation.toml @@ -163,6 +163,11 @@ unfavorite = "Rimuovi dai preferiti" fullscreen = "Passa alla modalitĆ  a schermo intero" sidebar = "Passa alla modalitĆ  barra laterale" +[backendStartup] +notFoundTitle = "Backend non trovato" +retry = "Riprova" +unreachable = "L'applicazione al momento non riesce a connettersi al backend. Verificare lo stato del backend e la connettivitĆ  di rete, quindi riprovare." + [zipWarning] title = "File ZIP di grandi dimensioni" message = "Questo ZIP contiene {{count}} file. Estrarre comunque?" @@ -339,7 +344,7 @@ popular = "Popolare" title = "Preferenze" [settings.workspace] -title = "Workspace" +title = "Area di lavoro" people = "Persone" teams = "Team" @@ -912,6 +917,9 @@ desc = "Crea flussi multi‑step concatenando azioni PDF. Ideale per attivitĆ  r desc = "Sovrapponi un PDF sopra un altro" title = "Sovrapponi PDF" +[home.pdfTextEditor] +title = "Editor di testo PDF" +desc = "Modifica testo e immagini esistenti nei PDF" [home.addText] tags = "testo,annotazione,etichetta" @@ -1400,7 +1408,7 @@ arabic = "Arabo" japanese = "Giapponese" korean = "Coreano" chinese = "Cinese" -thai = "Thai" +thai = "Tailandese" [watermark.steps] type = "Tipo di filigrana" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Firma disegnata" defaultImageLabel = "Firma caricata" defaultTextLabel = "Firma digitata" saveButton = "Salva firma" +savePersonal = "Salva come personale" +saveShared = "Salva come condivisa" saveUnavailable = "Crea prima una firma per salvarla." noChanges = "La firma corrente ĆØ giĆ  salvata." +tempStorageTitle = "Archiviazione temporanea del browser" +tempStorageDescription = "Le firme sono archiviate solo nel tuo browser. Verranno perse se cancelli i dati del browser o cambi browser." +personalHeading = "Firme personali" +sharedHeading = "Firme condivise" +personalDescription = "Solo tu puoi vedere queste firme." +sharedDescription = "Tutti gli utenti possono vedere e usare queste firme." [sign.saved.type] canvas = "Disegno" @@ -2841,8 +2857,8 @@ label = "Fattore di scala" [adjustPageScale.pageSize] label = "Dimensione pagina di destinazione" keep = "Mantieni dimensioni originali" -letter = "Letter" -legal = "Legal" +letter = "Lettera" +legal = "Legale" [adjustPageScale.error] failed = "Si ĆØ verificato un errore durante la regolazione della scala della pagina." @@ -3020,6 +3036,91 @@ title = "Ottieni informazioni in PDF" header = "Ottieni informazioni in PDF" submit = "Ottieni informazioni" downloadJson = "Scarica JSON" +processing = "Estrazione delle informazioni in corso..." +results = "Risultati" +noResults = "Esegui lo strumento per generare un report." +downloads = "Download" +noneDetected = "Nessuno rilevato" +indexTitle = "Indice" + +[getPdfInfo.report] +entryLabel = "Riepilogo completo delle informazioni" +shortTitle = "Informazioni PDF" + +[getPdfInfo.sections] +metadata = "Metadati" +formFields = "Campi del modulo" +basicInfo = "Informazioni di base" +documentInfo = "Informazioni sul documento" +compliance = "ConformitĆ " +encryption = "Crittografia" +permissions = "Autorizzazioni" +other = "Altro" +perPageInfo = "Info per pagina" +tableOfContents = "Sommario" + +[getPdfInfo.other] +attachments = "Allegati" +embeddedFiles = "File incorporati" +javaScript = "JavaScript" +layers = "Livelli" +structureTree = "Albero della struttura" +xmp = "Metadati XMP" + +[getPdfInfo.perPage] +size = "Dimensioni" +annotations = "Annotazioni" +images = "Immagini" +links = "Link" +fonts = "Font" +xobjects = "Conteggi XObject" +multimedia = "Multimedia" + +[getPdfInfo.summary] +pages = "Pagine" +fileSize = "Dimensione file" +pdfVersion = "Versione PDF" +language = "Lingua" +title = "Riepilogo PDF" +author = "Autore" +created = "Creato" +modified = "Modificato" +permsAll = "Tutte le autorizzazioni consentite" +permsRestricted = "{{count}} restrizioni" +permsMixed = "Alcune autorizzazioni limitate" +hasCompliance = "Conforme agli standard" +noCompliance = "Nessuno standard di conformitĆ " +basic = "Informazioni di base" +documentInfo = "Informazioni sul documento" +securityTitle = "Stato della sicurezza" +technical = "Tecnico" +overviewTitle = "Panoramica PDF" + +[getPdfInfo.summary.security] +encrypted = "PDF crittografato - Protezione tramite password presente" +unencrypted = "PDF non crittografato - Nessuna protezione tramite password" + +[getPdfInfo.summary.tech] +images = "Immagini" +fonts = "Font" +formFields = "Campi del modulo" +embeddedFiles = "File incorporati" +javaScript = "JavaScript" +layers = "Livelli" +bookmarks = "Segnalibri" +multimedia = "Multimedia" + +[getPdfInfo.summary.overview] +untitled = "un documento senza titolo" +unknown = "Autore sconosciuto" +text = "Questo ĆØ un PDF di {{pages}} pagine intitolato {{title}} creato da {{author}} (versione PDF {{version}})." + +[getPdfInfo.error] +partial = "Non ĆØ stato possibile elaborare alcuni file." +unexpected = "Errore imprevisto durante l'estrazione." + +[getPdfInfo.status] +complete = "Estrazione completata" [extractPage] tags = "estrarre" @@ -3438,6 +3539,9 @@ signinTitle = "Per favore accedi" ssoSignIn = "Accedi tramite Single Sign-on" oAuth2AutoCreateDisabled = "Creazione automatica utente OAUTH2 DISABILITATA" oAuth2AdminBlockedUser = "La registrazione o l'accesso degli utenti non registrati ĆØ attualmente bloccata. Si prega di contattare l'amministratore." +oAuth2RequiresLicense = "L'accesso OAuth/SSO richiede una licenza a pagamento (Server o Enterprise). Contatta l'amministratore per aggiornare il tuo piano." +saml2RequiresLicense = "L'accesso SAML richiede una licenza a pagamento (Server o Enterprise). Contatta l'amministratore per aggiornare il tuo piano." +maxUsersReached = "Numero massimo di utenti raggiunto per la licenza corrente. Contatta l'amministratore per aggiornare il piano o aggiungere altri posti." oauth2RequestNotFound = "Richiesta di autorizzazione non trovata" oauth2InvalidUserInfoResponse = "Risposta relativa alle informazioni utente non valida" oauth2invalidRequest = "Richiesta non valida" @@ -3846,14 +3950,17 @@ fitToWidth = "Adatta alla larghezza" actualSize = "Dimensione reale" [viewer] +cannotPreviewFile = "Impossibile visualizzare l'anteprima del file" +dualPageView = "Vista doppia pagina" firstPage = "Prima pagina" lastPage = "Ultima pagina" -previousPage = "Pagina precedente" nextPage = "Pagina successiva" +onlyPdfSupported = "Il visualizzatore supporta solo file PDF. Questo file sembra essere in un formato diverso." +previousPage = "Pagina precedente" +singlePageView = "Vista pagina singola" +unknownFile = "File sconosciuto" zoomIn = "Ingrandisci" zoomOut = "Riduci" -singlePageView = "Vista pagina singola" -dualPageView = "Vista doppia pagina" [rightRail] closeSelected = "Chiudi file selezionati" @@ -3877,6 +3984,7 @@ toggleSidebar = "Mostra/Nascondi barra laterale" exportSelected = "Esporta pagine selezionate" toggleAnnotations = "Attiva/disattiva visibilitĆ  annotazioni" annotationMode = "Attiva/disattiva modalitĆ  annotazione" +print = "Stampa PDF" draw = "Disegna" save = "Salva" saveChanges = "Salva modifiche" @@ -3925,7 +4033,7 @@ files = "File" activity = "AttivitĆ " help = "Guida" account = "Account" -config = "Config" +config = "Configurazione" settings = "Opzioni" adminSettings = "Opzioni Admin" allTools = "Funzioni" @@ -4153,7 +4261,7 @@ description = "Traccia azioni degli utenti ed eventi di sistema per conformitĆ  [admin.settings.security.audit.level] label = "Livello audit" -description = "0=OFF, 1=BASIC, 2=STANDARD, 3=VERBOSE" +description = "0=SPENTO, 1=BASE, 2=STANDARD, 3=DETTAGLIATO" [admin.settings.security.audit.retentionDays] label = "Conservazione audit (giorni)" @@ -4487,13 +4595,14 @@ label = "Informativa sui cookie" description = "URL o nome file della cookie policy" [admin.settings.legal.impressum] -label = "Impressum" +label = "Note legali" description = "URL o nome file dell'Impressum (richiesto in alcune giurisdizioni)" [admin.settings.premium] title = "Premium e Enterprise" description = "Configura la tua chiave di licenza premium o enterprise." license = "Configurazione licenza" +noInput = "Fornisci una chiave o un file di licenza" [admin.settings.premium.licenseKey] toggle = "Hai una chiave di licenza o un file di certificato?" @@ -4511,6 +4620,25 @@ line1 = "La sovrascrittura della licenza attuale non può essere annullata." line2 = "La tua licenza precedente andrĆ  persa in modo permanente a meno che tu non l'abbia salvata altrove." line3 = "Importante: mantieni le chiavi di licenza private e sicure. Non condividerle mai pubblicamente." +[admin.settings.premium.inputMethod] +text = "Chiave di licenza" +file = "File del certificato" + +[admin.settings.premium.file] +label = "File del certificato di licenza" +description = "Carica il file di licenza .lic o .cert degli acquisti offline" +choose = "Scegli file di licenza" +selected = "Selezionato: {{filename}} ({{size}})" +successMessage = "File di licenza caricato e attivato con successo. Non ĆØ richiesto il riavvio." + +[admin.settings.premium.currentLicense] +title = "Licenza attiva" +file = "Origine: File di licenza ({{path}})" +key = "Origine: Chiave di licenza" +type = "Tipo: {{type}}" +noInput = "Fornisci una chiave di licenza o carica un file di certificato" +success = "Successo" + [admin.settings.premium.enabled] label = "Abilita funzionalitĆ  Premium" description = "Abilita i controlli della chiave di licenza per funzionalitĆ  pro/enterprise" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} selezionati" download = "Salva" delete = "Elimina" unsupported = "Non supportato" +active = "Attivo" addToUpload = "Aggiungi al caricamento" +closeFile = "Chiudi file" deleteAll = "Elimina tutto" loadingFiles = "Caricamento file..." noFiles = "Nessun file disponibile" @@ -5132,7 +5262,7 @@ upgrade = "Esegui upgrade ora →" freeTitle = "Licenza server" overLimitTitle = "Licenza server necessaria" overLimitBody = "La nostra licenza consente fino a {{freeTierLimit}} utenti gratuiti per server. Hai {{overLimitUserCopy}} utenti Stirling. Per continuare senza interruzioni, esegui l'upgrade al piano Stirling Server - posti illimitati, modifica del testo PDF e pieno controllo admin a $99/server/mese." -freeBody = "La nostra licenza Open-Core consente fino a {{freeTierLimit}} utenti gratuiti per server. Per scalare senza interruzioni e ottenere accesso anticipato al nuovo strumento di modifica testo PDF, consigliamo il piano Stirling Server - modifica completa e posti illimitati a $99/server/mese." +freeBody = "La nostra licenza Open-Core consente fino a {{freeTierLimit}} utenti gratuiti per server. Per scalare senza interruzioni, consigliamo il piano Stirling Server - posti illimitati e supporto SSO a $99/server/mese." [onboarding.desktopInstall] title = "Download" @@ -5194,7 +5324,7 @@ subtitle = "Digita o incolla le email qui sotto, separate da virgole. La tua are [workspace.people.actions] label = "Azioni" -upgrade = "Upgrade" +upgrade = "Aggiorna" [workspace.people.roleDescriptions] admin = "Può gestire impostazioni e invitare membri, con pieno accesso amministrativo." @@ -5237,6 +5367,31 @@ error = "Impossibile aggiornare lo stato utente" success = "Utente eliminato con successo" error = "Impossibile eliminare l'utente" +[workspace.people.changePassword] +action = "Cambia password" +title = "Cambia password" +subtitle = "Aggiorna la password per" +newPassword = "Nuova password" +confirmPassword = "Conferma password" +placeholder = "Inserisci una nuova password" +confirmPlaceholder = "Reinserisci la nuova password" +passwordRequired = "Inserisci una nuova password" +passwordMismatch = "Le password non coincidono" +generateRandom = "Genera password sicura" +generatedPreview = "Password generata:" +copyTooltip = "Copia negli appunti" +copiedToClipboard = "Password copiata negli appunti" +copyFailed = "Impossibile copiare la password" +sendEmail = "Invia un'email all'utente riguardo a questa modifica" +includePassword = "Includi la nuova password nell'email" +forcePasswordChange = "Obbliga l'utente a cambiare password al prossimo accesso" +emailUnavailable = "L'email di questo utente non ĆØ un indirizzo valido. Le notifiche sono disabilitate." +smtpDisabled = "Le notifiche email richiedono che SMTP sia abilitato nelle impostazioni." +notifyOnly = "VerrĆ  inviata un'email senza la password, informando l'utente che un amministratore l'ha modificata." +submit = "Aggiorna password" +success = "Password aggiornata correttamente" +error = "Impossibile aggiornare la password" + [workspace.people.emailInvite] tab = "Invito via email" description = "Digita o incolla le email qui sotto, separate da virgole. Gli utenti riceveranno le credenziali di accesso via email." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "ƈ richiesto almeno un indirizzo email" submit = "Invia inviti" success = "utente/i invitato/i con successo" -partialSuccess = "Alcuni inviti non sono riusciti" +partialFailure = "Alcuni inviti non sono riusciti" allFailed = "Impossibile invitare gli utenti" error = "Invio inviti non riuscito" @@ -5752,7 +5907,7 @@ label = "Seleziona server" description = "Server self-hosted" [setup.step3] -label = "Login" +label = "Accesso" description = "Inserisci credenziali" [setup.mode.saas] @@ -5770,6 +5925,7 @@ subtitle = "Accedi con il tuo account Stirling" [setup.selfhosted] title = "Accedi al server" subtitle = "Inserisci le credenziali del server" +link = "oppure connettiti a un account self-hosted" [setup.server] title = "Connetti al server" @@ -5788,15 +5944,30 @@ description = "Inserisci l'URL completo del tuo server Stirling PDF self-hosted" emptyUrl = "Inserisci un URL del server" unreachable = "Impossibile connettersi al server" testFailed = "Test di connessione non riuscito" +configFetch = "Impossibile recuperare la configurazione del server. Controlla l'URL e riprova." + +[setup.server.error.securityDisabled] +title = "Accesso non abilitato" +body = "L'accesso non ĆØ abilitato su questo server. Per connetterti, devi abilitare l'autenticazione:" +step1 = "Imposta DOCKER_ENABLE_SECURITY=true nel tuo ambiente" +step2 = "Oppure imposta security.enableLogin=true in settings.yml" +step3 = "Riavvia il server" [setup.login] title = "Accedi" subtitle = "Inserisci le credenziali per continuare" connectingTo = "Connessione a:" -submit = "Login" +submit = "Accedi" signInWith = "Accedi con" oauthPending = "Apertura del browser per l'autenticazione..." orContinueWith = "Oppure continua con email" +serverRequirement = "Nota: il server deve avere il login abilitato." +showInstructions = "Come abilitarlo?" +hideInstructions = "Nascondi istruzioni" +instructions = "Per abilitare il login sul tuo server Stirling PDF:" +instructionsEnvVar = "Imposta la variabile d'ambiente:" +instructionsOrYml = "Oppure in settings.yml:" +instructionsRestart = "Quindi riavvia il server affinchĆ© le modifiche abbiano effetto." [setup.login.username] label = "Nome utente" @@ -5840,7 +6011,7 @@ paragraph = "Pagina a paragrafi" sparse = "Testo sparso" [pdfTextEditor.groupingMode] -auto = "Auto" +auto = "Automatico" paragraph = "Paragrafo" singleLine = "Riga singola" @@ -5853,6 +6024,7 @@ earlyAccess = "Accesso anticipato" reset = "Reimposta modifiche" downloadJson = "Scarica JSON" generatePdf = "Genera PDF" +saveChanges = "Salva modifiche" [pdfTextEditor.options.autoScaleText] title = "Ridimensiona automaticamente il testo alle caselle" @@ -5890,6 +6062,8 @@ alpha = "Questo visualizzatore alpha ĆØ in evoluzione: alcuni font, colori, effe [pdfTextEditor.empty] title = "Nessun documento caricato" subtitle = "Carica un file PDF o JSON per iniziare a modificare il testo." +dropzone = "Trascina qui un file PDF o JSON oppure fai clic per sfogliare" +dropzoneWithFiles = "Seleziona un file dalla scheda File oppure trascina qui un file PDF o JSON, o fai clic per sfogliare" [pdfTextEditor.welcomeBanner] title = "Benvenuto in PDF Text Editor (Accesso anticipato)" diff --git a/frontend/public/locales/ja-JP/translation.toml b/frontend/public/locales/ja-JP/translation.toml index 90b5eeee48..dda2f9b813 100644 --- a/frontend/public/locales/ja-JP/translation.toml +++ b/frontend/public/locales/ja-JP/translation.toml @@ -163,6 +163,11 @@ unfavorite = "ćŠę°—ć«å…„ć‚Šć‹ć‚‰å‰Šé™¤" fullscreen = "ćƒ•ćƒ«ć‚¹ć‚ÆćƒŖćƒ¼ćƒ³ćƒ¢ćƒ¼ćƒ‰ć«åˆ‡ć‚Šę›æćˆ" sidebar = "ć‚µć‚¤ćƒ‰ćƒćƒ¼ćƒ¢ćƒ¼ćƒ‰ć«åˆ‡ć‚Šę›æćˆ" +[backendStartup] +notFoundTitle = "ćƒćƒƒć‚Æć‚Øćƒ³ćƒ‰ćŒč¦‹ć¤ć‹ć‚Šć¾ć›ć‚“" +retry = "å†č©¦č”Œ" +unreachable = "ē¾åœØć€ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ćÆćƒćƒƒć‚Æć‚Øćƒ³ćƒ‰ć«ęŽ„ē¶šć§ćć¾ć›ć‚“ć€‚ćƒćƒƒć‚Æć‚Øćƒ³ćƒ‰ć®ēØ¼åƒēŠ¶ę³ćØćƒćƒƒćƒˆćƒÆćƒ¼ć‚ÆęŽ„ē¶šć‚’ē¢ŗčŖć—ć€å†åŗ¦ćŠč©¦ć—ćć ć•ć„ć€‚" + [zipWarning] title = "å¤§ććŖ ZIP ćƒ•ć‚”ć‚¤ćƒ«" message = "恓恮ZIPには{{count}}å€‹ć®ćƒ•ć‚”ć‚¤ćƒ«ćŒå«ć¾ć‚Œć¦ć„ć¾ć™ć€‚å±•é–‹ć—ć¾ć™ć‹ļ¼Ÿ" @@ -912,6 +917,9 @@ desc = "PDF ć‚¢ć‚Æć‚·ćƒ§ćƒ³ć‚’é€£ēµć—ć¦č¤‡ę•°ć‚¹ćƒ†ćƒƒćƒ—ć®ćƒÆćƒ¼ć‚Æćƒ•ćƒ­ desc = "1恤恮PDFć‚’åˆ„ć®PDFć®äøŠć«é‡ć­ć¾ć™" title = "PDFć‚’é‡ć­åˆć‚ć›" +[home.pdfTextEditor] +title = "PDFćƒ†ć‚­ć‚¹ćƒˆć‚Øćƒ‡ć‚£ć‚æ" +desc = "PDFå†…ć®ę—¢å­˜ć®ćƒ†ć‚­ć‚¹ćƒˆćØē”»åƒć‚’ē·Øé›†" [home.addText] tags = "ćƒ†ć‚­ć‚¹ćƒˆ,ę³Øé‡ˆ,ćƒ©ćƒ™ćƒ«" @@ -1173,7 +1181,7 @@ selectFilesPlaceholder = "é–‹å§‹ć™ć‚‹ć«ćÆćƒ”ć‚¤ćƒ³ćƒ“ćƒ„ćƒ¼ć§ćƒ•ć‚”ć‚¤ćƒ«ć‚’ settings = "設定" conversionCompleted = "å¤‰ę›ćŒå®Œäŗ†ć—ć¾ć—ćŸ" results = "ēµęžœ" -defaultFilename = "converted_file" +defaultFilename = "å¤‰ę›ęøˆćæćƒ•ć‚”ć‚¤ćƒ«" conversionResults = "å¤‰ę›ēµęžœ" convertFrom = "å¤‰ę›å…ƒ" convertTo = "å¤‰ę›å…ˆ" @@ -1360,7 +1368,7 @@ title = "é€ć‹ć—ć®čæ½åŠ " desc = "PDF ćƒ•ć‚”ć‚¤ćƒ«ć«ćƒ†ć‚­ć‚¹ćƒˆć¾ćŸćÆē”»åƒć®é€ć‹ć—ć‚’čæ½åŠ " completed = "é€ć‹ć—ć‚’čæ½åŠ ć—ć¾ć—ćŸ" submit = "é€ć‹ć—ć‚’čæ½åŠ " -filenamePrefix = "watermarked" +filenamePrefix = "é€ć‹ć—å…„ć‚Š" [watermark.error] failed = "PDF ćøć®é€ć‹ć—čæ½åŠ äø­ć«ć‚Øćƒ©ćƒ¼ćŒē™ŗē”Ÿć—ć¾ć—ćŸć€‚" @@ -1635,7 +1643,7 @@ subtitle = "å‡¦ē†ęøˆćæćƒ•ć‚”ć‚¤ćƒ«ć‚’ćƒ€ć‚¦ćƒ³ćƒ­ćƒ¼ćƒ‰ć™ć‚‹ć‹ć€äø‹ć§ę“ [removePages] tags = "ćƒšćƒ¼ć‚øć‚’å‰Šé™¤,ćƒšćƒ¼ć‚øå‰Šé™¤" title = "削除" -filenamePrefix = "pages_removed" +filenamePrefix = "ćƒšćƒ¼ć‚øå‰Šé™¤ęøˆćæ" submit = "削除" [removePages.pageNumbers] @@ -1837,7 +1845,7 @@ title = "ćƒ•ć‚©ćƒ¼ćƒ ćƒ•ć‚£ćƒ¼ćƒ«ćƒ‰ć‹ć‚‰čŖ­ćæå–ć‚Šå°‚ē”Øć‚’å‰Šé™¤" header = "PDFćƒ•ć‚©ćƒ¼ćƒ ć®ćƒ­ćƒƒć‚Æć‚’č§£é™¤" submit = "Remove" description = "ć“ć®ćƒ„ćƒ¼ćƒ«ćÆ PDF ćƒ•ć‚©ćƒ¼ćƒ ćƒ•ć‚£ćƒ¼ćƒ«ćƒ‰ć®čŖ­ćæå–ć‚Šå°‚ē”Øåˆ¶é™ć‚’č§£é™¤ć—ć€ē·Øé›†ćƒ»å…„åŠ›åÆčƒ½ć«ć—ć¾ć™ć€‚" -filenamePrefix = "unlocked_forms" +filenamePrefix = "ćƒ•ć‚©ćƒ¼ćƒ ć®ćƒ­ćƒƒć‚Æč§£é™¤ęøˆćæ" [unlockPDFForms.files] placeholder = "ćƒ”ć‚¤ćƒ³ćƒ“ćƒ„ćƒ¼ć§ PDF ćƒ•ć‚”ć‚¤ćƒ«ć‚’éøęŠžć—ć¦é–‹å§‹ć—ć¦ćć ć•ć„" @@ -1851,7 +1859,7 @@ title = "ćƒ•ć‚©ćƒ¼ćƒ ć®ćƒ­ćƒƒć‚Æč§£é™¤ēµęžœ" [changeMetadata] header = "ćƒ”ć‚æćƒ‡ćƒ¼ć‚æć®å¤‰ę›“" submit = "変曓" -filenamePrefix = "metadata" +filenamePrefix = "ćƒ”ć‚æćƒ‡ćƒ¼ć‚æ" [changeMetadata.settings] title = "ćƒ”ć‚æćƒ‡ćƒ¼ć‚æčØ­å®š" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "ę‰‹ę›øćē½²å" defaultImageLabel = "ć‚¢ćƒƒćƒ—ćƒ­ćƒ¼ćƒ‰ć—ćŸē½²å" defaultTextLabel = "å…„åŠ›ć—ćŸē½²å" saveButton = "ē½²åć‚’äæå­˜" +savePersonal = "å€‹äŗŗē”ØćØć—ć¦äæå­˜" +saveShared = "å…±ęœ‰ē”ØćØć—ć¦äæå­˜" saveUnavailable = "ć¾ćšē½²åć‚’ä½œęˆć—ć¦ć‹ć‚‰äæå­˜ć—ć¦ćć ć•ć„ć€‚" noChanges = "ē¾åœØć®ē½²åćÆć™ć§ć«äæå­˜ęøˆćæć§ć™ć€‚" +tempStorageTitle = "ćƒ–ćƒ©ć‚¦ć‚¶ćƒ¼ć®äø€ę™‚ć‚¹ćƒˆćƒ¬ćƒ¼ć‚ø" +tempStorageDescription = "ē½²åćÆćƒ–ćƒ©ć‚¦ć‚¶ćƒ¼å†…ć®ćæć«äæå­˜ć•ć‚Œć¾ć™ć€‚ćƒ–ćƒ©ć‚¦ć‚¶ćƒ¼ć®ćƒ‡ćƒ¼ć‚æć‚’ę¶ˆåŽ»ć™ć‚‹ć‹ć€åˆ„ć®ćƒ–ćƒ©ć‚¦ć‚¶ćƒ¼ć«åˆ‡ć‚Šę›æćˆć‚‹ćØå¤±ć‚ć‚Œć¾ć™ć€‚" +personalHeading = "å€‹äŗŗē”Øē½²å" +sharedHeading = "å…±ęœ‰ē½²å" +personalDescription = "ć“ć‚Œć‚‰ć®ē½²åćÆć‚ćŖćŸć ć‘ćŒč”Øē¤ŗć§ćć¾ć™ć€‚" +sharedDescription = "ć™ć¹ć¦ć®ćƒ¦ćƒ¼ć‚¶ćƒ¼ćŒć“ć‚Œć‚‰ć®ē½²åć‚’č”Øē¤ŗć—ć¦ä½æē”Øć§ćć¾ć™ć€‚" [sign.saved.type] canvas = "ęē”»" @@ -2318,7 +2334,7 @@ title = "平坦化" header = "PDFć‚’å¹³å¦åŒ–ć™ć‚‹" flattenOnlyForms = "ćƒ•ć‚©ćƒ¼ćƒ ć®ćæć‚’å¹³å¦ć«ć™ć‚‹" submit = "平坦化" -filenamePrefix = "flattened" +filenamePrefix = "ćƒ•ćƒ©ćƒƒćƒˆåŒ–ęøˆćæ" [flatten.files] placeholder = "é–‹å§‹ć™ć‚‹ć«ćÆćƒ”ć‚¤ćƒ³ćƒ“ćƒ„ćƒ¼ć§ PDF ćƒ•ć‚”ć‚¤ćƒ«ć‚’éøęŠžć—ć¦ćć ć•ć„" @@ -2366,7 +2382,7 @@ title = "修復" header = "PDFを修復" submit = "修復" description = "ć“ć®ćƒ„ćƒ¼ćƒ«ćÆē “ęć¾ćŸćÆęå‚·ć—ćŸ PDF ćƒ•ć‚”ć‚¤ćƒ«ć®äæ®å¾©ć‚’č©¦ćæć¾ć™ć€‚čæ½åŠ ć®čØ­å®šćÆäøč¦ć§ć™ć€‚" -filenamePrefix = "repaired" +filenamePrefix = "修復済み" [repair.files] placeholder = "é–‹å§‹ć™ć‚‹ć«ćÆćƒ”ć‚¤ćƒ³ē”»é¢ć§ PDF ćƒ•ć‚”ć‚¤ćƒ«ć‚’éøęŠžć—ć¦ćć ć•ć„" @@ -2567,7 +2583,7 @@ stopButton = "ęÆ”č¼ƒć‚’åœę­¢" [certSign] tags = "authenticate,PEM,P12,official,encrypt" title = "čØ¼ę˜Žę›øć«ć‚ˆć‚‹ē½²å" -filenamePrefix = "signed" +filenamePrefix = "ē½²åęøˆćæ" chooseCertificate = "čØ¼ę˜Žę›øćƒ•ć‚”ć‚¤ćƒ«ć‚’éøęŠž" chooseJksFile = "JKS ćƒ•ć‚”ć‚¤ćƒ«ć‚’éøęŠž" chooseP12File = "PKCS12 ćƒ•ć‚”ć‚¤ćƒ«ć‚’éøęŠž" @@ -2701,7 +2717,7 @@ header = "PDFć‹ć‚‰é›»å­čØ¼ę˜Žę›øć‚’å‰Šé™¤ć™ć‚‹" selectPDF = "PDFćƒ•ć‚”ć‚¤ćƒ«ć®éøęŠž:" submit = "ē½²åć®å‰Šé™¤" description = "ć“ć®ćƒ„ćƒ¼ćƒ«ćÆ PDF ę–‡ę›øć‹ć‚‰ćƒ‡ć‚øć‚æćƒ«čØ¼ę˜Žę›øē½²åć‚’å‰Šé™¤ć—ć¾ć™ć€‚" -filenamePrefix = "unsigned" +filenamePrefix = "ē½²åćŖć—" [removeCertSign.files] placeholder = "é–‹å§‹ć™ć‚‹ć«ćÆćƒ”ć‚¤ćƒ³ē”»é¢ć§ PDF ćƒ•ć‚”ć‚¤ćƒ«ć‚’éøęŠžć—ć¦ćć ć•ć„" @@ -3020,6 +3036,91 @@ title = "PDFć®ęƒ…å ±ć‚’å…„ę‰‹" header = "PDFć®ęƒ…å ±ć‚’å…„ę‰‹" submit = "ęƒ…å ±ć‚’å…„ę‰‹" downloadJson = "JSONć§ćƒ€ć‚¦ćƒ³ćƒ­ćƒ¼ćƒ‰" +processing = "ęƒ…å ±ć‚’ęŠ½å‡ŗć—ć¦ć„ć¾ć™..." +results = "ēµęžœ" +noResults = "ćƒ¬ćƒćƒ¼ćƒˆć‚’ē”Ÿęˆć™ć‚‹ć«ćÆćƒ„ćƒ¼ćƒ«ć‚’å®Ÿč”Œć—ć¦ćć ć•ć„ć€‚" +downloads = "ćƒ€ć‚¦ćƒ³ćƒ­ćƒ¼ćƒ‰" +noneDetected = "ę¤œå‡ŗćŖć—" +indexTitle = "ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹" + +[getPdfInfo.report] +entryLabel = "å…Øęƒ…å ±ć®ę¦‚č¦" +shortTitle = "PDFęƒ…å ±" + +[getPdfInfo.sections] +metadata = "ćƒ”ć‚æćƒ‡ćƒ¼ć‚æ" +formFields = "ćƒ•ć‚©ćƒ¼ćƒ ćƒ•ć‚£ćƒ¼ćƒ«ćƒ‰" +basicInfo = "åŸŗęœ¬ęƒ…å ±" +documentInfo = "ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆęƒ…å ±" +compliance = "ęŗ–ę‹ " +encryption = "ęš—å·åŒ–" +permissions = "権限" +other = "ćć®ä»–" +perPageInfo = "ćƒšćƒ¼ć‚øć”ćØć®ęƒ…å ±" +tableOfContents = "目欔" + +[getPdfInfo.other] +attachments = "ę·»ä»˜ćƒ•ć‚”ć‚¤ćƒ«" +embeddedFiles = "åŸ‹ć‚č¾¼ćæćƒ•ć‚”ć‚¤ćƒ«" +javaScript = "JavaScript" +layers = "ćƒ¬ć‚¤ćƒ¤ćƒ¼" +structureTree = "ę§‹é€ ćƒ„ćƒŖćƒ¼" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "サイズ" +annotations = "ę³Øé‡ˆ" +images = "ē”»åƒ" +links = "ćƒŖćƒ³ć‚Æ" +fonts = "ćƒ•ć‚©ćƒ³ćƒˆ" +xobjects = "XObject 恮ꕰ" +multimedia = "ćƒžćƒ«ćƒćƒ”ćƒ‡ć‚£ć‚¢" + +[getPdfInfo.summary] +pages = "ćƒšćƒ¼ć‚øę•°" +fileSize = "ćƒ•ć‚”ć‚¤ćƒ«ć‚µć‚¤ć‚ŗ" +pdfVersion = "PDF ćƒćƒ¼ć‚øćƒ§ćƒ³" +language = "čØ€čŖž" +title = "PDF ꦂ要" +author = "ä½œęˆč€…" +created = "ä½œęˆę—„" +modified = "ꛓꖰꗄ" +permsAll = "ć™ć¹ć¦ć®ęØ©é™ćŒčØ±åÆć•ć‚Œć¦ć„ć¾ć™" +permsRestricted = "{{count}} ä»¶ć®åˆ¶é™" +permsMixed = "äø€éƒØć®ęØ©é™ćŒåˆ¶é™ć•ć‚Œć¦ć„ć¾ć™" +hasCompliance = "ęŗ–ę‹ č¦ę ¼ć‚ć‚Š" +noCompliance = "ęŗ–ę‹ č¦ę ¼ćŖć—" +basic = "åŸŗęœ¬ęƒ…å ±" +documentInfo = "ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆęƒ…å ±" +securityTitle = "ć‚»ć‚­ćƒ„ćƒŖćƒ†ć‚£ēŠ¶ę…‹" +technical = "ęŠ€č”“ęƒ…å ±" +overviewTitle = "PDF ꦂ要" + +[getPdfInfo.summary.security] +encrypted = "ęš—å·åŒ–ć•ć‚ŒćŸ PDF - ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰äæč­·ć‚ć‚Š" +unencrypted = "ęš—å·åŒ–ć•ć‚Œć¦ć„ćŖć„ PDF - ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰äæč­·ćŖć—" + +[getPdfInfo.summary.tech] +images = "ē”»åƒ" +fonts = "ćƒ•ć‚©ćƒ³ćƒˆ" +formFields = "ćƒ•ć‚©ćƒ¼ćƒ ćƒ•ć‚£ćƒ¼ćƒ«ćƒ‰" +embeddedFiles = "åŸ‹ć‚č¾¼ćæćƒ•ć‚”ć‚¤ćƒ«" +javaScript = "JavaScript" +layers = "ćƒ¬ć‚¤ćƒ¤ćƒ¼" +bookmarks = "ćƒ–ćƒƒć‚Æćƒžćƒ¼ć‚Æ" +multimedia = "ćƒžćƒ«ćƒćƒ”ćƒ‡ć‚£ć‚¢" + +[getPdfInfo.summary.overview] +untitled = "ē„”é”Œć®ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆ" +unknown = "äøę˜ŽćŖä½œęˆč€…" +text = "ć“ć‚ŒćÆ {{author}} ć«ć‚ˆć£ć¦ä½œęˆć•ć‚ŒćŸć€ć‚æć‚¤ćƒˆćƒ« {{title}} 恮 {{pages}} ćƒšćƒ¼ć‚øć® PDF ć§ć™ļ¼ˆPDF ćƒćƒ¼ć‚øćƒ§ćƒ³ {{version}})。" + +[getPdfInfo.error] +partial = "äø€éƒØć®ćƒ•ć‚”ć‚¤ćƒ«ć‚’å‡¦ē†ć§ćć¾ć›ć‚“ć§ć—ćŸć€‚" +unexpected = "ęŠ½å‡ŗäø­ć«äŗˆęœŸć—ćŖć„ć‚Øćƒ©ćƒ¼ćŒē™ŗē”Ÿć—ć¾ć—ćŸć€‚" + +[getPdfInfo.status] +complete = "ęŠ½å‡ŗćŒå®Œäŗ†ć—ć¾ć—ćŸ" [extractPage] tags = "ęŠ½å‡ŗ" @@ -3438,6 +3539,9 @@ signinTitle = "ć‚µć‚¤ćƒ³ć‚¤ćƒ³ć—ć¦ćć ć•ć„" ssoSignIn = "ć‚·ćƒ³ć‚°ćƒ«ć‚µć‚¤ćƒ³ć‚Ŗćƒ³ć§ćƒ­ć‚°ć‚¤ćƒ³" oAuth2AutoCreateDisabled = "OAuth 2č‡Ŗå‹•ä½œęˆćƒ¦ćƒ¼ć‚¶ćƒ¼ćŒē„”åŠ¹" oAuth2AdminBlockedUser = "ē¾åœØć€ęœŖē™»éŒ²ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®ē™»éŒ²ć¾ćŸćÆćƒ­ć‚°ć‚¤ćƒ³ćÆćƒ–ćƒ­ćƒƒć‚Æć•ć‚Œć¦ć„ć¾ć™ć€‚ē®”ē†č€…ć«ćŠå•ć„åˆć‚ć›ćć ć•ć„ć€‚" +oAuth2RequiresLicense = "OAuth/SSO ćƒ­ć‚°ć‚¤ćƒ³ć«ćÆęœ‰ę–™ćƒ©ć‚¤ć‚»ćƒ³ć‚¹ļ¼ˆServer または Enterpriseļ¼‰ćŒåæ…č¦ć§ć™ć€‚ćƒ—ćƒ©ćƒ³ć®ć‚¢ćƒƒćƒ—ć‚°ćƒ¬ćƒ¼ćƒ‰ć«ć¤ć„ć¦ćÆē®”ē†č€…ć«ćŠå•ć„åˆć‚ć›ćć ć•ć„ć€‚" +saml2RequiresLicense = "SAML ćƒ­ć‚°ć‚¤ćƒ³ć«ćÆęœ‰ę–™ćƒ©ć‚¤ć‚»ćƒ³ć‚¹ļ¼ˆServer または Enterpriseļ¼‰ćŒåæ…č¦ć§ć™ć€‚ćƒ—ćƒ©ćƒ³ć®ć‚¢ćƒƒćƒ—ć‚°ćƒ¬ćƒ¼ćƒ‰ć«ć¤ć„ć¦ćÆē®”ē†č€…ć«ćŠå•ć„åˆć‚ć›ćć ć•ć„ć€‚" +maxUsersReached = "ē¾åœØć®ćƒ©ć‚¤ć‚»ćƒ³ć‚¹ć®ćƒ¦ćƒ¼ć‚¶ćƒ¼ę•°äøŠé™ć«é”ć—ć¾ć—ćŸć€‚ćƒ—ćƒ©ćƒ³ć®ć‚¢ćƒƒćƒ—ć‚°ćƒ¬ćƒ¼ćƒ‰ć¾ćŸćÆć‚·ćƒ¼ćƒˆę•°ć®čæ½åŠ ć«ć¤ć„ć¦ć€ē®”ē†č€…ć«ćŠå•ć„åˆć‚ć›ćć ć•ć„ć€‚" oauth2RequestNotFound = "čŖčØ¼ćƒŖć‚Æć‚Øć‚¹ćƒˆćŒč¦‹ć¤ć‹ć‚Šć¾ć›ć‚“" oauth2InvalidUserInfoResponse = "ē„”åŠ¹ćŖćƒ¦ćƒ¼ć‚¶ćƒ¼ęƒ…å ±ć®åæœē­”" oauth2invalidRequest = "ē„”åŠ¹ćŖćƒŖć‚Æć‚Øć‚¹ćƒˆ" @@ -3533,7 +3637,7 @@ title = "PDFć‚’å˜äø€ćƒšćƒ¼ć‚øć«å¤‰ę›" header = "PDFć‚’å˜äø€ćƒšćƒ¼ć‚øć«å¤‰ę›" submit = "å˜äø€ćƒšćƒ¼ć‚øć«å¤‰ę›" description = "ć“ć®ćƒ„ćƒ¼ćƒ«ćÆ PDF ć®å…Øćƒšćƒ¼ć‚øć‚’ 1 ć¤ć®å¤§ććŖå˜äø€ćƒšćƒ¼ć‚øć«ēµåˆć—ć¾ć™ć€‚å¹…ćÆå…ƒć®ćƒšćƒ¼ć‚øćØåŒć˜ć§ć€é«˜ć•ćÆå…Øćƒšćƒ¼ć‚øć®é«˜ć•ć®åˆčØˆć«ćŖć‚Šć¾ć™ć€‚" -filenamePrefix = "single_page" +filenamePrefix = "å˜äø€ćƒšćƒ¼ć‚ø" [pdfToSinglePage.files] placeholder = "é–‹å§‹ć™ć‚‹ć«ćÆćƒ”ć‚¤ćƒ³ē”»é¢ć§ PDF ćƒ•ć‚”ć‚¤ćƒ«ć‚’éøęŠžć—ć¦ćć ć•ć„" @@ -3846,14 +3950,17 @@ fitToWidth = "å¹…ć«åˆć‚ć›ć‚‹" actualSize = "åŽŸåÆø" [viewer] +cannotPreviewFile = "ćƒ•ć‚”ć‚¤ćƒ«ć‚’ćƒ—ćƒ¬ćƒ“ćƒ„ćƒ¼ć§ćć¾ć›ć‚“" +dualPageView = "č¦‹é–‹ćč”Øē¤ŗ" firstPage = "ęœ€åˆć®ćƒšćƒ¼ć‚ø" lastPage = "ęœ€å¾Œć®ćƒšćƒ¼ć‚ø" -previousPage = "å‰ć®ćƒšćƒ¼ć‚ø" nextPage = "ę¬”ć®ćƒšćƒ¼ć‚ø" +onlyPdfSupported = "ć“ć®ćƒ“ćƒ„ćƒ¼ć‚¢ćÆ PDF ćƒ•ć‚”ć‚¤ćƒ«ć®ćæć‚’ć‚µćƒćƒ¼ćƒˆć—ć¦ć„ć¾ć™ć€‚ć“ć®ćƒ•ć‚”ć‚¤ćƒ«ćÆåˆ„ć®å½¢å¼ć®ć‚ˆć†ć§ć™ć€‚" +previousPage = "å‰ć®ćƒšćƒ¼ć‚ø" +singlePageView = "å˜äø€ćƒšćƒ¼ć‚øč”Øē¤ŗ" +unknownFile = "äøę˜ŽćŖćƒ•ć‚”ć‚¤ćƒ«" zoomIn = "拔大" zoomOut = "ēø®å°" -singlePageView = "å˜äø€ćƒšćƒ¼ć‚øč”Øē¤ŗ" -dualPageView = "č¦‹é–‹ćč”Øē¤ŗ" [rightRail] closeSelected = "éøęŠžć—ćŸćƒ•ć‚”ć‚¤ćƒ«ć‚’é–‰ć˜ć‚‹" @@ -3877,6 +3984,7 @@ toggleSidebar = "ć‚µć‚¤ćƒ‰ćƒćƒ¼ć‚’åˆ‡ć‚Šę›æćˆ" exportSelected = "éøęŠžć—ćŸćƒšćƒ¼ć‚øć‚’ę›øćå‡ŗć—" toggleAnnotations = "ę³Øé‡ˆć®č”Øē¤ŗć‚’åˆ‡ć‚Šę›æćˆ" annotationMode = "ę³Øé‡ˆćƒ¢ćƒ¼ćƒ‰ć‚’åˆ‡ć‚Šę›æćˆ" +print = "PDFć‚’å°åˆ·" draw = "ęē”»" save = "äæå­˜" saveChanges = "å¤‰ę›“ć‚’äæå­˜" @@ -4231,15 +4339,15 @@ label = "惗惭惐悤惀" description = "čŖčØ¼ć«ä½æē”Øć™ć‚‹ OAuth2 惗惭惐悤惀" [admin.settings.connections.oauth2.issuer] -label = "Issuer URL" +label = "ē™ŗč”Œč€… URL" description = "OAuth2 ćƒ—ćƒ­ćƒć‚¤ćƒ€ć® Issuer URL" [admin.settings.connections.oauth2.clientId] -label = "Client ID" +label = "ć‚Æćƒ©ć‚¤ć‚¢ćƒ³ćƒˆ ID" description = "ćƒ—ćƒ­ćƒć‚¤ćƒ€ć‹ć‚‰ē™ŗč”Œć•ć‚ŒćŸ OAuth2 恮 Client ID" [admin.settings.connections.oauth2.clientSecret] -label = "Client Secret" +label = "ć‚Æćƒ©ć‚¤ć‚¢ćƒ³ćƒˆ ć‚·ćƒ¼ć‚Æćƒ¬ćƒƒćƒˆ" description = "ćƒ—ćƒ­ćƒć‚¤ćƒ€ć‹ć‚‰ē™ŗč”Œć•ć‚ŒćŸ OAuth2 恮 Client Secret" [admin.settings.connections.oauth2.useAsUsername] @@ -4270,7 +4378,7 @@ label = "惗惭惐悤惀" description = "SAML2 ćƒ—ćƒ­ćƒć‚¤ćƒ€å" [admin.settings.connections.saml2.registrationId] -label = "Registration ID" +label = "ē™»éŒ² ID" description = "SAML2 ć®ē™»éŒ²č­˜åˆ„å­" [admin.settings.connections.saml2.autoCreateUser] @@ -4407,7 +4515,7 @@ description = "ć‚ˆć‚ŠåŗƒēÆ„ćŖć‚·ć‚¹ćƒ†ćƒ äø€ę™‚ćƒ‡ć‚£ćƒ¬ć‚ÆćƒˆćƒŖć‚’ć‚ÆćƒŖćƒ¼ label = "ćƒ—ćƒ­ć‚»ć‚¹å®Ÿč”Œåˆ¶é™" description = "å„ćƒ—ćƒ­ć‚»ć‚¹å®Ÿč”Œå™Øć®ć‚»ćƒƒć‚·ćƒ§ćƒ³äøŠé™ćØć‚æć‚¤ćƒ ć‚¢ć‚¦ćƒˆć‚’čØ­å®š" libreOffice = "LibreOffice" -pdfToHtml = "PDF to HTML" +pdfToHtml = "PDF 悒 HTML 恫" qpdf = "QPDF" tesseract = "Tesseract OCR" pythonOpenCv = "Python OpenCV" @@ -4494,6 +4602,7 @@ description = "ć‚¤ćƒ³ćƒ—ćƒŖćƒ³ćƒˆćøć® URL ć¾ćŸćÆćƒ•ć‚”ć‚¤ćƒ«åļ¼ˆåœ°åŸŸć« title = "ćƒ—ćƒ¬ćƒŸć‚¢ćƒ ćØć‚Øćƒ³ć‚æćƒ¼ćƒ—ćƒ©ć‚¤ć‚ŗ" description = "ćƒ—ćƒ¬ćƒŸć‚¢ćƒ ć¾ćŸćÆć‚Øćƒ³ć‚æćƒ¼ćƒ—ćƒ©ć‚¤ć‚ŗć®ćƒ©ć‚¤ć‚»ćƒ³ć‚¹ć‚­ćƒ¼ć‚’ę§‹ęˆć—ć¾ć™ć€‚" license = "ćƒ©ć‚¤ć‚»ćƒ³ć‚¹čØ­å®š" +noInput = "ćƒ©ć‚¤ć‚»ćƒ³ć‚¹ć‚­ćƒ¼ć¾ćŸćÆćƒ•ć‚”ć‚¤ćƒ«ć‚’å…„åŠ›ć—ć¦ćć ć•ć„" [admin.settings.premium.licenseKey] toggle = "ćƒ©ć‚¤ć‚»ćƒ³ć‚¹ć‚­ćƒ¼ć¾ćŸćÆčØ¼ę˜Žę›øćƒ•ć‚”ć‚¤ćƒ«ć‚’ćŠęŒć”ć§ć™ć‹ļ¼Ÿ" @@ -4511,6 +4620,25 @@ line1 = "ē¾åœØć®ćƒ©ć‚¤ć‚»ćƒ³ć‚¹ć‚­ćƒ¼ć‚’äøŠę›øćć™ć‚‹ćØå…ƒć«ęˆ»ć›ć¾ć› line2 = "åˆ„é€”ćƒćƒƒć‚Æć‚¢ćƒƒćƒ—ć—ć¦ć„ćŖć„é™ć‚Šć€ä»„å‰ć®ćƒ©ć‚¤ć‚»ćƒ³ć‚¹ćÆę°øä¹…ć«å¤±ć‚ć‚Œć¾ć™ć€‚" line3 = "é‡č¦: ćƒ©ć‚¤ć‚»ćƒ³ć‚¹ć‚­ćƒ¼ćÆē§˜åÆ†ć«å®‰å…Øć«äæē®”ć—ć¦ćć ć•ć„ć€‚å…¬é–‹ć§å…±ęœ‰ć—ćŖć„ć§ćć ć•ć„ć€‚" +[admin.settings.premium.inputMethod] +text = "ćƒ©ć‚¤ć‚»ćƒ³ć‚¹ć‚­ćƒ¼" +file = "čØ¼ę˜Žę›øćƒ•ć‚”ć‚¤ćƒ«" + +[admin.settings.premium.file] +label = "ćƒ©ć‚¤ć‚»ćƒ³ć‚¹čØ¼ę˜Žę›øćƒ•ć‚”ć‚¤ćƒ«" +description = "ć‚Ŗćƒ•ćƒ©ć‚¤ćƒ³č³¼å…„ć® .lic または .cert ćƒ©ć‚¤ć‚»ćƒ³ć‚¹ćƒ•ć‚”ć‚¤ćƒ«ć‚’ć‚¢ćƒƒćƒ—ćƒ­ćƒ¼ćƒ‰ć—ć¦ćć ć•ć„" +choose = "ćƒ©ć‚¤ć‚»ćƒ³ć‚¹ćƒ•ć‚”ć‚¤ćƒ«ć‚’éøęŠž" +selected = "éøęŠžęøˆćæ: {{filename}} ({{size}})" +successMessage = "ćƒ©ć‚¤ć‚»ćƒ³ć‚¹ćƒ•ć‚”ć‚¤ćƒ«ć‚’ć‚¢ćƒƒćƒ—ćƒ­ćƒ¼ćƒ‰ć—ć¦ęœ‰åŠ¹åŒ–ć—ć¾ć—ćŸć€‚å†čµ·å‹•ćÆäøč¦ć§ć™ć€‚" + +[admin.settings.premium.currentLicense] +title = "ęœ‰åŠ¹ćŖćƒ©ć‚¤ć‚»ćƒ³ć‚¹" +file = "ć‚½ćƒ¼ć‚¹: ćƒ©ć‚¤ć‚»ćƒ³ć‚¹ćƒ•ć‚”ć‚¤ćƒ« ({{path}})" +key = "ć‚½ćƒ¼ć‚¹: ćƒ©ć‚¤ć‚»ćƒ³ć‚¹ć‚­ćƒ¼" +type = "ēØ®é”ž: {{type}}" +noInput = "ćƒ©ć‚¤ć‚»ćƒ³ć‚¹ć‚­ćƒ¼ć‚’å…„åŠ›ć™ć‚‹ć‹ć€čØ¼ę˜Žę›øćƒ•ć‚”ć‚¤ćƒ«ć‚’ć‚¢ćƒƒćƒ—ćƒ­ćƒ¼ćƒ‰ć—ć¦ćć ć•ć„" +success = "成功" + [admin.settings.premium.enabled] label = "ćƒ—ćƒ¬ćƒŸć‚¢ćƒ ę©Ÿčƒ½ć‚’ęœ‰åŠ¹åŒ–" description = "Pro/Enterprise ę©Ÿčƒ½ć®ćƒ©ć‚¤ć‚»ćƒ³ć‚¹ć‚­ćƒ¼ę¤œčØ¼ć‚’ęœ‰åŠ¹åŒ–" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} ä»¶éøęŠž" download = "ćƒ€ć‚¦ćƒ³ćƒ­ćƒ¼ćƒ‰" delete = "削除" unsupported = "未対応" +active = "ć‚¢ć‚Æćƒ†ć‚£ćƒ–" addToUpload = "ć‚¢ćƒƒćƒ—ćƒ­ćƒ¼ćƒ‰ć«čæ½åŠ " +closeFile = "ćƒ•ć‚”ć‚¤ćƒ«ć‚’é–‰ć˜ć‚‹" deleteAll = "ć™ć¹ć¦å‰Šé™¤" loadingFiles = "ćƒ•ć‚”ć‚¤ćƒ«ć‚’čŖ­ćæč¾¼ćæäø­..." noFiles = "ćƒ•ć‚”ć‚¤ćƒ«ćÆć‚ć‚Šć¾ć›ć‚“" @@ -4669,7 +4799,7 @@ title = "ć‚µćƒ‹ć‚æć‚¤ć‚ŗ" desc = "PDF ćƒ•ć‚”ć‚¤ćƒ«ć‹ć‚‰ę½œåœØēš„ć«ęœ‰å®³ćŖč¦ē“ ć‚’å‰Šé™¤ć—ć¾ć™ć€‚" submit = "PDFć‚’ć‚µćƒ‹ć‚æć‚¤ć‚ŗ" completed = "ć‚µćƒ‹ć‚æć‚¤ć‚ŗćŒę­£åøøć«å®Œäŗ†ć—ć¾ć—ćŸ" -filenamePrefix = "sanitised" +filenamePrefix = "ć‚µćƒ‹ć‚æć‚¤ć‚ŗęøˆćæ" sanitizationResults = "ć‚µćƒ‹ć‚æć‚¤ć‚ŗēµęžœ" [sanitize.error] @@ -4717,7 +4847,7 @@ title = "ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć®čæ½åŠ " desc = "ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć§ PDF ę–‡ę›øć‚’ęš—å·åŒ–ć—ć¾ć™ć€‚" completed = "ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰äæč­·ć‚’é©ē”Øć—ć¾ć—ćŸ" submit = "ęš—å·åŒ–" -filenamePrefix = "encrypted" +filenamePrefix = "ęš—å·åŒ–ęøˆćæ" [addPassword.error] failed = "PDF ć®ęš—å·åŒ–äø­ć«ć‚Øćƒ©ćƒ¼ćŒē™ŗē”Ÿć—ć¾ć—ćŸć€‚" @@ -4812,7 +4942,7 @@ text = "ć“ć‚Œć‚‰ć®ęØ©é™ć‚’å¤‰ę›“äøåÆć«ć™ć‚‹ć«ćÆć€ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰čæ½åŠ  title = "ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć®å‰Šé™¤" desc = "PDFć‹ć‚‰ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć®å‰Šé™¤ć—ć¾ć™ć€‚" tags = "ć‚»ć‚­ćƒ„ć‚¢,復号,ć‚»ć‚­ćƒ„ćƒŖćƒ†ć‚£,ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰č§£é™¤,ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰å‰Šé™¤" -filenamePrefix = "decrypted" +filenamePrefix = "å¾©å·ęøˆćæ" submit = "削除" [removePassword.password] @@ -5132,7 +5262,7 @@ upgrade = "ä»Šć™ćć‚¢ćƒƒćƒ—ć‚°ćƒ¬ćƒ¼ćƒ‰ →" freeTitle = "ć‚µćƒ¼ćƒćƒ¼ćƒ©ć‚¤ć‚»ćƒ³ć‚¹" overLimitTitle = "ć‚µćƒ¼ćƒćƒ¼ćƒ©ć‚¤ć‚»ćƒ³ć‚¹ćŒåæ…č¦ć§ć™" overLimitBody = "å½“ē¤¾ć®ćƒ©ć‚¤ć‚»ćƒ³ć‚¹ć§ćÆć€ć‚µćƒ¼ćƒćƒ¼ć”ćØć« {{freeTierLimit}} ćƒ¦ćƒ¼ć‚¶ćƒ¼ć¾ć§ē„”ę–™ć§ć™ć€‚ē¾åœØ {{overLimitUserCopy}} 恮 Stirling ćƒ¦ćƒ¼ć‚¶ćƒ¼ćŒć„ć¾ć™ć€‚äø­ę–­ćŖćåˆ©ē”Øć‚’ē¶šć‘ć‚‹ć«ćÆć€Stirling Server ćƒ—ćƒ©ćƒ³ć«ć‚¢ćƒƒćƒ—ć‚°ćƒ¬ćƒ¼ćƒ‰ć—ć¦ćć ć•ć„ - ē„”åˆ¶é™åø­ę•°ć€PDF ćƒ†ć‚­ć‚¹ćƒˆē·Øé›†ć€å®Œå…ØćŖē®”ē†ę©Ÿčƒ½ćŒ $99/ć‚µćƒ¼ćƒćƒ¼/月 恧恙怂" -freeBody = "当社の ć‚Ŗćƒ¼ćƒ—ćƒ³ć‚³ć‚¢ ćƒ©ć‚¤ć‚»ćƒ³ć‚¹ć§ćÆć€ć‚µćƒ¼ćƒćƒ¼ć”ćØć«ęœ€å¤§ {{freeTierLimit}} ćƒ¦ćƒ¼ć‚¶ćƒ¼ć¾ć§ē„”ę–™ć§ć™ć€‚äø­ę–­ćŖćę‹”å¼µć—ć€ę–°ć—ć„ PDF ćƒ†ć‚­ć‚¹ćƒˆē·Øé›†ćƒ„ćƒ¼ćƒ« ć«ę—©ęœŸć‚¢ć‚Æć‚»ć‚¹ć™ć‚‹ć«ćÆć€Stirling Server ćƒ—ćƒ©ćƒ³ć‚’ćŠå‹§ć‚ć—ć¾ć™ć€‚å®Œå…Øē·Øé›†ćØ ē„”åˆ¶é™åø­ę•° が $99/ć‚µćƒ¼ćƒćƒ¼/月 恧恙怂" +freeBody = "当社のOpen-Corećƒ©ć‚¤ć‚»ćƒ³ć‚¹ć§ćÆć€ć‚µćƒ¼ćƒćƒ¼ć”ćØć«ęœ€å¤§{{freeTierLimit}}ćƒ¦ćƒ¼ć‚¶ćƒ¼ć¾ć§ē„”ę–™ć§ć”åˆ©ē”Øć„ćŸć ć‘ć¾ć™ć€‚äø­ę–­ćŖćć‚¹ć‚±ćƒ¼ćƒ«ć™ć‚‹ć«ćÆć€Stirling Server ćƒ—ćƒ©ćƒ³ć‚’ćŠć™ć™ć‚ć—ć¾ć™ - ē„”åˆ¶é™ć®åø­ę•°ćØSSO ć‚µćƒćƒ¼ćƒˆć§ $99/ć‚µćƒ¼ćƒćƒ¼/꜈怂" [onboarding.desktopInstall] title = "ćƒ€ć‚¦ćƒ³ćƒ­ćƒ¼ćƒ‰" @@ -5237,6 +5367,31 @@ error = "ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®ć‚¹ćƒ†ćƒ¼ć‚æć‚¹ć®ę›“ę–°ć«å¤±ę•—ć—ć¾ć—ćŸ" success = "ćƒ¦ćƒ¼ć‚¶ćƒ¼ć‚’å‰Šé™¤ć—ć¾ć—ćŸ" error = "ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®å‰Šé™¤ć«å¤±ę•—ć—ć¾ć—ćŸ" +[workspace.people.changePassword] +action = "ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć‚’å¤‰ę›“" +title = "ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć‚’å¤‰ę›“" +subtitle = "ć®ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć‚’ę›“ę–°" +newPassword = "ę–°ć—ć„ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰" +confirmPassword = "ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć®ē¢ŗčŖ" +placeholder = "ę–°ć—ć„ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć‚’å…„åŠ›" +confirmPlaceholder = "ę–°ć—ć„ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć‚’å†å…„åŠ›" +passwordRequired = "ę–°ć—ć„ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć‚’å…„åŠ›ć—ć¦ćć ć•ć„" +passwordMismatch = "ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćŒäø€č‡“ć—ć¾ć›ć‚“" +generateRandom = "å®‰å…ØćŖćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć‚’ē”Ÿęˆ" +generatedPreview = "ē”Ÿęˆć•ć‚ŒćŸćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰:" +copyTooltip = "ć‚ÆćƒŖćƒƒćƒ—ćƒœćƒ¼ćƒ‰ć«ć‚³ćƒ”ćƒ¼" +copiedToClipboard = "ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć‚’ć‚ÆćƒŖćƒƒćƒ—ćƒœćƒ¼ćƒ‰ć«ć‚³ćƒ”ćƒ¼ć—ć¾ć—ćŸ" +copyFailed = "ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć®ć‚³ćƒ”ćƒ¼ć«å¤±ę•—ć—ć¾ć—ćŸ" +sendEmail = "ć“ć®å¤‰ę›“ć«ć¤ć„ć¦ćƒ¦ćƒ¼ć‚¶ćƒ¼ć«ćƒ”ćƒ¼ćƒ«ć™ć‚‹" +includePassword = "ćƒ”ćƒ¼ćƒ«ć«ę–°ć—ć„ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć‚’å«ć‚ć‚‹" +forcePasswordChange = "ę¬”å›žćƒ­ć‚°ć‚¤ćƒ³ę™‚ć«ćƒ¦ćƒ¼ć‚¶ćƒ¼ć«ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰å¤‰ę›“ć‚’å¼·åˆ¶ć™ć‚‹" +emailUnavailable = "ć“ć®ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®ćƒ”ćƒ¼ćƒ«ćÆęœ‰åŠ¹ćŖćƒ”ćƒ¼ćƒ«ć‚¢ćƒ‰ćƒ¬ć‚¹ć§ćÆć‚ć‚Šć¾ć›ć‚“ć€‚é€šēŸ„ćÆē„”åŠ¹ć§ć™ć€‚" +smtpDisabled = "ćƒ”ćƒ¼ćƒ«é€šēŸ„ć«ćÆčØ­å®šć§ SMTP ć‚’ęœ‰åŠ¹ć«ć™ć‚‹åæ…č¦ćŒć‚ć‚Šć¾ć™ć€‚" +notifyOnly = "ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć‚’å«ć‚ćšć«ćƒ”ćƒ¼ćƒ«ć‚’é€äæ”ć—ć€ē®”ē†č€…ćŒå¤‰ę›“ć—ćŸć“ćØć‚’ćƒ¦ćƒ¼ć‚¶ćƒ¼ć«ēŸ„ć‚‰ć›ć¾ć™ć€‚" +submit = "ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć‚’ę›“ę–°" +success = "ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć‚’ę›“ę–°ć—ć¾ć—ćŸ" +error = "ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć®ę›“ę–°ć«å¤±ę•—ć—ć¾ć—ćŸ" + [workspace.people.emailInvite] tab = "ćƒ”ćƒ¼ćƒ«ę‹›å¾…" description = "äø‹ć«ćƒ”ćƒ¼ćƒ«ć‚¢ćƒ‰ćƒ¬ć‚¹ć‚’ć‚«ćƒ³ćƒžåŒŗåˆ‡ć‚Šć§å…„åŠ›ć¾ćŸćÆč²¼ć‚Šä»˜ć‘ć¦ćć ć•ć„ć€‚ćƒ¦ćƒ¼ć‚¶ćƒ¼ć«ćÆćƒ”ćƒ¼ćƒ«ć§ćƒ­ć‚°ć‚¤ćƒ³ęƒ…å ±ćŒå±Šćć¾ć™ć€‚" @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "å°‘ćŖććØć‚‚1ä»¶ć®ćƒ”ćƒ¼ćƒ«ć‚¢ćƒ‰ćƒ¬ć‚¹ćŒåæ…č¦ć§ć™" submit = "招待を送俔" success = "ćƒ¦ćƒ¼ć‚¶ćƒ¼ć‚’ę‹›å¾…ć—ć¾ć—ćŸ" -partialSuccess = "äø€éƒØć®ę‹›å¾…ć«å¤±ę•—ć—ć¾ć—ćŸ" +partialFailure = "äø€éƒØć®ę‹›å¾…ć«å¤±ę•—ć—ć¾ć—ćŸ" allFailed = "ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®ę‹›å¾…ć«å¤±ę•—ć—ć¾ć—ćŸ" error = "ę‹›å¾…ć®é€äæ”ć«å¤±ę•—ć—ć¾ć—ćŸ" @@ -5770,6 +5925,7 @@ subtitle = "Stirling ć‚¢ć‚«ć‚¦ćƒ³ćƒˆć§ć‚µć‚¤ćƒ³ć‚¤ćƒ³" [setup.selfhosted] title = "ć‚µćƒ¼ćƒćƒ¼ć«ć‚µć‚¤ćƒ³ć‚¤ćƒ³" subtitle = "ć‚µćƒ¼ćƒćƒ¼ć®čŖčØ¼ęƒ…å ±ć‚’å…„åŠ›" +link = "ć¾ćŸćÆć‚»ćƒ«ćƒ•ćƒ›ć‚¹ćƒˆåž‹ć‚¢ć‚«ć‚¦ćƒ³ćƒˆć«ęŽ„ē¶š" [setup.server] title = "ć‚µćƒ¼ćƒćƒ¼ć«ęŽ„ē¶š" @@ -5788,6 +5944,14 @@ description = "ć‚»ćƒ«ćƒ•ćƒ›ć‚¹ćƒˆć® Stirling PDF ć‚µćƒ¼ćƒćƒ¼ć®å®Œå…ØćŖ URL emptyUrl = "ć‚µćƒ¼ćƒćƒ¼ URL ć‚’å…„åŠ›ć—ć¦ćć ć•ć„" unreachable = "ć‚µćƒ¼ćƒćƒ¼ć«ęŽ„ē¶šć§ćć¾ć›ć‚“ć§ć—ćŸ" testFailed = "ęŽ„ē¶šćƒ†ć‚¹ćƒˆć«å¤±ę•—ć—ć¾ć—ćŸ" +configFetch = "ć‚µćƒ¼ćƒćƒ¼ę§‹ęˆć®å–å¾—ć«å¤±ę•—ć—ć¾ć—ćŸć€‚URL ć‚’ē¢ŗčŖć—ć¦ć€ć‚‚ć†äø€åŗ¦ćŠč©¦ć—ćć ć•ć„ć€‚" + +[setup.server.error.securityDisabled] +title = "ćƒ­ć‚°ć‚¤ćƒ³ćŒęœ‰åŠ¹ć«ćŖć£ć¦ć„ć¾ć›ć‚“" +body = "ć“ć®ć‚µćƒ¼ćƒćƒ¼ć§ćÆćƒ­ć‚°ć‚¤ćƒ³ćŒęœ‰åŠ¹ć«ćŖć£ć¦ć„ć¾ć›ć‚“ć€‚ć“ć®ć‚µćƒ¼ćƒćƒ¼ć«ęŽ„ē¶šć™ć‚‹ć«ćÆć€čŖčØ¼ć‚’ęœ‰åŠ¹ć«ć™ć‚‹åæ…č¦ćŒć‚ć‚Šć¾ć™:" +step1 = "ē’°å¢ƒć§ DOCKER_ENABLE_SECURITY=true ć‚’čØ­å®šć™ć‚‹" +step2 = "または settings.yml 恧 security.enableLogin=true ć‚’čØ­å®šć™ć‚‹" +step3 = "ć‚µćƒ¼ćƒćƒ¼ć‚’å†čµ·å‹•ć™ć‚‹" [setup.login] title = "ć‚µć‚¤ćƒ³ć‚¤ćƒ³" @@ -5797,13 +5961,20 @@ submit = "ćƒ­ć‚°ć‚¤ćƒ³" signInWith = "ć§ć‚µć‚¤ćƒ³ć‚¤ćƒ³" oauthPending = "čŖčØ¼ć®ćŸć‚ćƒ–ćƒ©ć‚¦ć‚¶ćƒ¼ć‚’é–‹ć„ć¦ć„ć¾ć™..." orContinueWith = "またはピールで続蔌" +serverRequirement = "注: ć‚µćƒ¼ćƒćƒ¼ć§ćƒ­ć‚°ć‚¤ćƒ³ć‚’ęœ‰åŠ¹ć«ć™ć‚‹åæ…č¦ćŒć‚ć‚Šć¾ć™ć€‚" +showInstructions = "ęœ‰åŠ¹åŒ–ć™ć‚‹ć«ćÆļ¼Ÿ" +hideInstructions = "ę‰‹é †ć‚’éžč”Øē¤ŗ" +instructions = "Stirling PDF ć‚µćƒ¼ćƒćƒ¼ć§ćƒ­ć‚°ć‚¤ćƒ³ć‚’ęœ‰åŠ¹ć«ć™ć‚‹ć«ćÆ:" +instructionsEnvVar = "ē’°å¢ƒå¤‰ę•°ć‚’čØ­å®š:" +instructionsOrYml = "または settings.yml 恧:" +instructionsRestart = "ćć®å¾Œć€ć‚µćƒ¼ćƒćƒ¼ć‚’å†čµ·å‹•ć—ć¦å¤‰ę›“ć‚’åę˜ ć•ć›ć¦ćć ć•ć„ć€‚" [setup.login.username] label = "ćƒ¦ćƒ¼ć‚¶ćƒ¼å" placeholder = "ćƒ¦ćƒ¼ć‚¶ćƒ¼åć‚’å…„åŠ›" [setup.login.email] -label = "Email" +label = "ćƒ”ćƒ¼ćƒ«ć‚¢ćƒ‰ćƒ¬ć‚¹" placeholder = "ćƒ”ćƒ¼ćƒ«ć‚¢ćƒ‰ćƒ¬ć‚¹ć‚’å…„åŠ›" [setup.login.password] @@ -5853,6 +6024,7 @@ earlyAccess = "ę—©ęœŸć‚¢ć‚Æć‚»ć‚¹" reset = "å¤‰ę›“ć‚’ćƒŖć‚»ćƒƒćƒˆ" downloadJson = "JSON ć‚’ćƒ€ć‚¦ćƒ³ćƒ­ćƒ¼ćƒ‰" generatePdf = "PDF ć‚’ē”Ÿęˆ" +saveChanges = "å¤‰ę›“ć‚’äæå­˜" [pdfTextEditor.options.autoScaleText] title = "ćƒœćƒƒć‚Æć‚¹ć«åŽć¾ć‚‹ć‚ˆć†ćƒ†ć‚­ć‚¹ćƒˆć‚’č‡Ŗå‹•ć‚¹ć‚±ćƒ¼ćƒ«" @@ -5890,6 +6062,8 @@ alpha = "ć“ć®ć‚¢ćƒ«ćƒ•ć‚”ē‰ˆćƒ“ćƒ„ćƒ¼ć‚¢ćÆé–‹ē™ŗé€”äøŠć§ć™ć€‚äø€éƒØć®ćƒ• [pdfTextEditor.empty] title = "ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆćŒčŖ­ćæč¾¼ć¾ć‚Œć¦ć„ć¾ć›ć‚“" subtitle = "ćƒ†ć‚­ć‚¹ćƒˆē·Øé›†ć‚’é–‹å§‹ć™ć‚‹ć«ćÆPDFまたはJSONćƒ•ć‚”ć‚¤ćƒ«ć‚’čŖ­ćæč¾¼ć‚“ć§ćć ć•ć„ć€‚" +dropzone = "恓恓恫 PDF または JSON ćƒ•ć‚”ć‚¤ćƒ«ć‚’ćƒ‰ćƒ©ćƒƒć‚°ļ¼†ćƒ‰ćƒ­ćƒƒćƒ—ć™ć‚‹ć‹ć€ć‚ÆćƒŖćƒƒć‚Æć—ć¦å‚ē…§" +dropzoneWithFiles = "ćƒ•ć‚”ć‚¤ćƒ«ć‚æćƒ–ć‹ć‚‰ćƒ•ć‚”ć‚¤ćƒ«ć‚’éøęŠžć™ć‚‹ć‹ć€ć“ć“ć« PDF または JSON ćƒ•ć‚”ć‚¤ćƒ«ć‚’ćƒ‰ćƒ©ćƒƒć‚°ļ¼†ćƒ‰ćƒ­ćƒƒćƒ—ć™ć‚‹ć‹ć€ć‚ÆćƒŖćƒƒć‚Æć—ć¦å‚ē…§" [pdfTextEditor.welcomeBanner] title = "PDF Text Editorļ¼ˆę—©ęœŸć‚¢ć‚Æć‚»ć‚¹ļ¼‰ćøć‚ˆć†ć“ć" diff --git a/frontend/public/locales/ko-KR/translation.toml b/frontend/public/locales/ko-KR/translation.toml index f63fd82c62..63c1018c98 100644 --- a/frontend/public/locales/ko-KR/translation.toml +++ b/frontend/public/locales/ko-KR/translation.toml @@ -163,6 +163,11 @@ unfavorite = "ģ¦ź²Øģ°¾źø°ģ—ģ„œ 제거" fullscreen = "전첓 화멓 ėŖØė“œė”œ ģ „ķ™˜" sidebar = "ģ‚¬ģ“ė“œė°” ėŖØė“œė”œ ģ „ķ™˜" +[backendStartup] +notFoundTitle = "ė°±ģ—”ė“œė„¼ ģ°¾ģ„ 수 ģ—†ģŒ" +retry = "ģž¬ģ‹œė„" +unreachable = "ķ˜„ģž¬ ģ• ķ”Œė¦¬ģ¼€ģ“ģ…˜ģ“ ė°±ģ—”ė“œģ— ģ—°ź²°ķ•  수 ģ—†ģŠµė‹ˆė‹¤. ė°±ģ—”ė“œ ģƒķƒœģ™€ ė„¤ķŠøģ›Œķ¬ ģ—°ź²°ģ„ ķ™•ģøķ•œ 후 ė‹¤ģ‹œ ģ‹œė„ķ•˜ģ„øģš”." + [zipWarning] title = "큰 ZIP ķŒŒģ¼" message = "ģ“ ZIPģ—ėŠ” {{count}}ź°œģ˜ ķŒŒģ¼ģ“ ķ¬ķ•Øė˜ģ–“ ģžˆģŠµė‹ˆė‹¤. ź·øėž˜ė„ ģ••ģ¶•ģ„ ķ•“ģ œķ•˜ģ‹œź² ģŠµė‹ˆź¹Œ?" @@ -912,6 +917,9 @@ desc = "PDF ģž‘ģ—…ģ„ ģ—°ź²°ķ•˜ģ—¬ 다단계 ģ›Œķ¬ķ”Œė”œė„¼ źµ¬ģ„±ķ•˜ģ„øģš”. desc = "PDF넼 다넸 PDF ģœ„ģ— ģ˜¤ė²„ė ˆģ“" title = "PDF ģ˜¤ė²„ė ˆģ“" +[home.pdfTextEditor] +title = "PDF ķ…ģŠ¤ķŠø ķŽøģ§‘źø°" +desc = "PDF ė‚“ė¶€ģ˜ 기씓 ķ…ģŠ¤ķŠøģ™€ ģ“ėÆøģ§€ė„¼ ķŽøģ§‘ķ•©ė‹ˆė‹¤" [home.addText] tags = "ķ…ģŠ¤ķŠø,ģ£¼ģ„,ė ˆģ“ėø”" @@ -1901,8 +1909,8 @@ placeholder = "ģˆ˜ģ • ė‚ ģ§œ" [changeMetadata.trapped] label = "ķŠøėž˜ķ•‘ 상태" unknown = "ģ•Œ 수 ģ—†ģŒ" -true = "True" -false = "False" +true = "ģ°ø" +false = "ź±°ģ§“" [changeMetadata.advanced] title = "ź³ źø‰ ģ˜µģ…˜" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "그린 ģ„œėŖ…" defaultImageLabel = "ģ—…ė”œė“œėœ ģ„œėŖ…" defaultTextLabel = "ģž…ė „ķ•œ ģ„œėŖ…" saveButton = "ģ„œėŖ… ģ €ģž„" +savePersonal = "ź°œģøģš©ģœ¼ė”œ ģ €ģž„" +saveShared = "공유용으딜 ģ €ģž„" saveUnavailable = "먼저 ģ„œėŖ…ģ„ ė§Œė“  후 ģ €ģž„ķ•˜ģ„øģš”." noChanges = "ķ˜„ģž¬ ģ„œėŖ…ģ“ ģ“ėÆø ģ €ģž„ė˜ģ–“ ģžˆģŠµė‹ˆė‹¤." +tempStorageTitle = "ģž„ģ‹œ ėøŒė¼ģš°ģ € ģ €ģž„ģ†Œ" +tempStorageDescription = "ģ„œėŖ…ģ€ ėøŒė¼ģš°ģ €ģ—ė§Œ ģ €ģž„ė©ė‹ˆė‹¤. ėøŒė¼ģš°ģ € ė°ģ“ķ„°ė„¼ ģ‚­ģ œķ•˜ź±°ė‚˜ ėøŒė¼ģš°ģ €ė„¼ ė³€ź²½ķ•˜ė©“ ģ‚¬ė¼ģ§‘ė‹ˆė‹¤." +personalHeading = "ź°œģø ģ„œėŖ…" +sharedHeading = "공유 ģ„œėŖ…" +personalDescription = "ģ“ ģ„œėŖ…ģ€ ė³øģøė§Œ ė³¼ 수 ģžˆģŠµė‹ˆė‹¤." +sharedDescription = "ėŖØė“  ģ‚¬ģš©ģžź°€ ģ“ ģ„œėŖ…ģ„ 볓고 ģ‚¬ģš©ķ•  수 ģžˆģŠµė‹ˆė‹¤." [sign.saved.type] canvas = "그리기" @@ -3020,6 +3036,91 @@ title = "PDF 정볓 ź°€ģ øģ˜¤źø°" header = "PDF 정볓 ź°€ģ øģ˜¤źø°" submit = "정볓 ź°€ģ øģ˜¤źø°" downloadJson = "JSON ė‹¤ģš“ė”œė“œ" +processing = "정볓넼 ģ¶”ģ¶œķ•˜ėŠ” 중..." +results = "ź²°ź³¼" +noResults = "ė³“ź³ ģ„œė„¼ ģƒģ„±ķ•˜ė ¤ė©“ ė„źµ¬ė„¼ ģ‹¤ķ–‰ķ•˜ģ„øģš”." +downloads = "ė‹¤ģš“ė”œė“œ" +noneDetected = "ź°ģ§€ė˜ģ§€ ģ•ŠģŒ" +indexTitle = "ģƒ‰ģø" + +[getPdfInfo.report] +entryLabel = "전첓 정볓 ģš”ģ•½" +shortTitle = "PDF 정볓" + +[getPdfInfo.sections] +metadata = "ė©”ķƒ€ė°ģ“ķ„°" +formFields = "ģ–‘ģ‹ ķ•„ė“œ" +basicInfo = "źø°ė³ø 정볓" +documentInfo = "ė¬øģ„œ 정볓" +compliance = "규격 ģ¤€ģˆ˜" +encryption = "ģ•”ķ˜øķ™”" +permissions = "ź¶Œķ•œ" +other = "źø°ķƒ€" +perPageInfo = "ķŽ˜ģ“ģ§€ė³„ 정볓" +tableOfContents = "ėŖ©ģ°Ø" + +[getPdfInfo.other] +attachments = "첨부 ķŒŒģ¼" +embeddedFiles = "ė‚“ģž„ ķŒŒģ¼" +javaScript = "JavaScript" +layers = "ė ˆģ“ģ–“" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "크기" +annotations = "ģ£¼ģ„" +images = "ģ“ėÆøģ§€" +links = "링크" +fonts = "글꼓" +xobjects = "XObject 개수" +multimedia = "멀티미디얓" + +[getPdfInfo.summary] +pages = "ķŽ˜ģ“ģ§€ 수" +fileSize = "ķŒŒģ¼ 크기" +pdfVersion = "PDF 버전" +language = "ģ–øģ–“" +title = "PDF ģš”ģ•½" +author = "ģž‘ģ„±ģž" +created = "ģƒģ„±ėØ" +modified = "ģˆ˜ģ •ėØ" +permsAll = "ėŖØė“  ź¶Œķ•œ ķ—ˆģš©" +permsRestricted = "{{count}}ź°œģ˜ ģ œķ•œ" +permsMixed = "ģ¼ė¶€ ź¶Œķ•œģ“ ģ œķ•œėØ" +hasCompliance = "규격 ģ¤€ģˆ˜ 기준 ģžˆģŒ" +noCompliance = "규격 ģ¤€ģˆ˜ 기준 ģ—†ģŒ" +basic = "źø°ė³ø 정볓" +documentInfo = "ė¬øģ„œ 정볓" +securityTitle = "ė³“ģ•ˆ 상태" +technical = "기술 정볓" +overviewTitle = "PDF ź°œģš”" + +[getPdfInfo.summary.security] +encrypted = "ģ•”ķ˜øķ™”ėœ PDF - ģ•”ķ˜ø 볓호 적용" +unencrypted = "ģ•”ķ˜øķ™”ė˜ģ§€ ģ•Šģ€ PDF - ģ•”ķ˜ø 볓호 ģ—†ģŒ" + +[getPdfInfo.summary.tech] +images = "ģ“ėÆøģ§€" +fonts = "글꼓" +formFields = "ģ–‘ģ‹ ķ•„ė“œ" +embeddedFiles = "ė‚“ģž„ ķŒŒģ¼" +javaScript = "JavaScript" +layers = "ė ˆģ“ģ–“" +bookmarks = "북마크" +multimedia = "멀티미디얓" + +[getPdfInfo.summary.overview] +untitled = "제목 ģ—†ėŠ” ė¬øģ„œ" +unknown = "ģ•Œ 수 ģ—†ėŠ” ģž‘ģ„±ģž" +text = "ģ“ ė¬øģ„œėŠ” {{pages}}ķŽ˜ģ“ģ§€ ė¶„ėŸ‰ģ˜ PDF딜, ģ œėŖ©ģ€ {{title}}ģ“ė©° ģž‘ģ„±ģžėŠ” {{author}}ģž…ė‹ˆė‹¤(PDF 버전 {{version}})." + +[getPdfInfo.error] +partial = "ģ¼ė¶€ ķŒŒģ¼ģ„ ģ²˜ė¦¬ķ•˜ģ§€ ėŖ»ķ–ˆģŠµė‹ˆė‹¤." +unexpected = "ģ¶”ģ¶œ 중 예기치 ģ•Šģ€ ģ˜¤ė„˜ź°€ ė°œģƒķ–ˆģŠµė‹ˆė‹¤." + +[getPdfInfo.status] +complete = "ģ¶”ģ¶œ ģ™„ė£Œ" [extractPage] tags = "ģ¶”ģ¶œ" @@ -3438,6 +3539,9 @@ signinTitle = "ė”œź·øģøķ•“ ģ£¼ģ„øģš”" ssoSignIn = "ė‹Øģ¼ ė”œź·øģøģœ¼ė”œ ė”œź·øģø" oAuth2AutoCreateDisabled = "OAuth2 ģ‚¬ģš©ģž ģžė™ ģƒģ„±ģ“ ė¹„ķ™œģ„±ķ™”ė˜ģ—ˆģŠµė‹ˆė‹¤" oAuth2AdminBlockedUser = "ķ˜„ģž¬ ėÆøė“±ė” ģ‚¬ģš©ģžģ˜ ė“±ė” ė˜ėŠ” ė”œź·øģøģ“ ģ°Øė‹Øė˜ģ–“ ģžˆģŠµė‹ˆė‹¤. ź“€ė¦¬ģžģ—ź²Œ ė¬øģ˜ķ•˜ģ„øģš”." +oAuth2RequiresLicense = "OAuth/SSO ė”œź·øģøģ€ 유료 ė¼ģ“ģ„ ģŠ¤(ģ„œė²„ ė˜ėŠ” ģ—”ķ„°ķ”„ė¼ģ“ģ¦ˆ)ź°€ ķ•„ģš”ķ•©ė‹ˆė‹¤. ķ”Œėžœ ģ—…ź·øė ˆģ“ė“œė„¼ ģœ„ķ•“ ź“€ė¦¬ģžģ—ź²Œ ė¬øģ˜ķ•˜ģ„øģš”." +saml2RequiresLicense = "SAML ė”œź·øģøģ€ 유료 ė¼ģ“ģ„ ģŠ¤(ģ„œė²„ ė˜ėŠ” ģ—”ķ„°ķ”„ė¼ģ“ģ¦ˆ)ź°€ ķ•„ģš”ķ•©ė‹ˆė‹¤. ķ”Œėžœ ģ—…ź·øė ˆģ“ė“œė„¼ ģœ„ķ•“ ź“€ė¦¬ģžģ—ź²Œ ė¬øģ˜ķ•˜ģ„øģš”." +maxUsersReached = "ķ˜„ģž¬ ė¼ģ“ģ„ ģŠ¤ģ—ģ„œ ķ—ˆģš©ėœ ģµœėŒ€ ģ‚¬ģš©ģž ģˆ˜ģ— ė„ė‹¬ķ–ˆģŠµė‹ˆė‹¤. ķ”Œėžœ ģ—…ź·øė ˆģ“ė“œ ė˜ėŠ” ģ‹œķŠø 추가넼 ģœ„ķ•“ ź“€ė¦¬ģžģ—ź²Œ ė¬øģ˜ķ•˜ģ„øģš”." oauth2RequestNotFound = "ģøģ¦ ģš”ģ²­ģ„ ģ°¾ģ„ 수 ģ—†ģŠµė‹ˆė‹¤" oauth2InvalidUserInfoResponse = "ģž˜ėŖ»ėœ ģ‚¬ģš©ģž 정볓 ģ‘ė‹µ" oauth2invalidRequest = "ģž˜ėŖ»ėœ ģš”ģ²­" @@ -3846,14 +3950,17 @@ fitToWidth = "ė„ˆė¹„ģ— ė§žģ¶”źø°" actualSize = "ģ‹¤ģ œ 크기" [viewer] +cannotPreviewFile = "ķŒŒģ¼ģ„ 미리볓기할 수 ģ—†ģŠµė‹ˆė‹¤" +dualPageView = "두 ķŽ˜ģ“ģ§€ 볓기" firstPage = "첫 ķŽ˜ģ“ģ§€" lastPage = "ė§ˆģ§€ė§‰ ķŽ˜ģ“ģ§€" -previousPage = "ģ“ģ „ ķŽ˜ģ“ģ§€" nextPage = "ė‹¤ģŒ ķŽ˜ģ“ģ§€" +onlyPdfSupported = "ė·°ģ–“ėŠ” PDF ķŒŒģ¼ė§Œ ģ§€ģ›ķ•©ė‹ˆė‹¤. ģ“ ķŒŒģ¼ģ€ 다넸 ķ˜•ģ‹ģø 것으딜 ė³“ģž…ė‹ˆė‹¤." +previousPage = "ģ“ģ „ ķŽ˜ģ“ģ§€" +singlePageView = "ė‹Øģ¼ ķŽ˜ģ“ģ§€ 볓기" +unknownFile = "ģ•Œ 수 ģ—†ėŠ” ķŒŒģ¼" zoomIn = "ķ™•ėŒ€" zoomOut = "ģ¶•ģ†Œ" -singlePageView = "ė‹Øģ¼ ķŽ˜ģ“ģ§€ 볓기" -dualPageView = "두 ķŽ˜ģ“ģ§€ 볓기" [rightRail] closeSelected = "ģ„ ķƒķ•œ ķŒŒģ¼ ė‹«źø°" @@ -3877,6 +3984,7 @@ toggleSidebar = "ģ‚¬ģ“ė“œė°” ģ „ķ™˜" exportSelected = "ģ„ ķƒķ•œ ķŽ˜ģ“ģ§€ 낓볓낓기" toggleAnnotations = "ģ£¼ģ„ ź°€ģ‹œģ„± ģ „ķ™˜" annotationMode = "ģ£¼ģ„ ėŖØė“œ ģ „ķ™˜" +print = "PDF ģøģ‡„" draw = "그리기" save = "ģ €ģž„" saveChanges = "변경 ė‚“ģš© ģ €ģž„" @@ -4153,7 +4261,7 @@ description = "ģ»“ķ”Œė¼ģ“ģ–øģŠ¤ ė° ė³“ģ•ˆ ėŖØė‹ˆķ„°ė§ģ„ ģœ„ķ•“ ģ‚¬ģš©ģž [admin.settings.security.audit.level] label = "감사 ģˆ˜ģ¤€" -description = "0=OFF, 1=BASIC, 2=STANDARD, 3=VERBOSE" +description = "0=끄기, 1=źø°ė³ø, 2=ķ‘œģ¤€, 3=ģƒģ„ø" [admin.settings.security.audit.retentionDays] label = "감사 딜그 볓씓 źø°ź°„(ģ¼)" @@ -4407,7 +4515,7 @@ description = "ė” ė„“ģ€ ģ‹œģŠ¤ķ…œ ģž„ģ‹œ 디렉터리넼 정리할지 여부( label = "ķ”„ė”œģ„øģŠ¤ 실행기 ģ œķ•œ" description = "각 ķ”„ė”œģ„øģŠ¤ ģ‹¤ķ–‰źø°ģ˜ ģ„øģ…˜ ģ œķ•œ ė° ģ‹œź°„ ģ œķ•œģ„ źµ¬ģ„±ķ•©ė‹ˆė‹¤" libreOffice = "LibreOffice" -pdfToHtml = "PDF to HTML" +pdfToHtml = "PDF넼 HTML딜" qpdf = "QPDF" tesseract = "Tesseract OCR" pythonOpenCv = "Python OpenCV" @@ -4487,13 +4595,14 @@ label = "쿠키 ģ •ģ±…" description = "쿠키 ģ •ģ±…ģ˜ URL ė˜ėŠ” ķŒŒģ¼ ģ“ė¦„" [admin.settings.legal.impressum] -label = "Impressum" +label = "법적 ź³ ģ§€" description = "Impressumģ˜ URL ė˜ėŠ” ķŒŒģ¼ ģ“ė¦„(ģ¼ė¶€ ź“€ķ• ź¶Œģ—ģ„œ ķ•„ģˆ˜)" [admin.settings.premium] title = "프리미엄 ė° ģ—”ķ„°ķ”„ė¼ģ“ģ¦ˆ" description = "프리미엄 ė˜ėŠ” ģ—”ķ„°ķ”„ė¼ģ“ģ¦ˆ ė¼ģ“ģ„ ģŠ¤ 키넼 źµ¬ģ„±ķ•©ė‹ˆė‹¤." license = "ė¼ģ“ģ„ ģŠ¤ 구성" +noInput = "ė¼ģ“ģ„ ģŠ¤ 키 ė˜ėŠ” ķŒŒģ¼ģ„ ģž…ė „ķ•“ ģ£¼ģ„øģš”" [admin.settings.premium.licenseKey] toggle = "ė¼ģ“ģ„ ģŠ¤ ķ‚¤ė‚˜ ģøģ¦ģ„œ ķŒŒģ¼ģ“ ģžˆė‚˜ģš”?" @@ -4511,6 +4620,25 @@ line1 = "ķ˜„ģž¬ ė¼ģ“ģ„ ģŠ¤ 키넼 ė®ģ–“ģ“°ė©“ ė˜ėŒė¦“ 수 ģ—†ģŠµė‹ˆė‹¤." line2 = "다넸 곳에 ė°±ģ—…ķ•˜ģ§€ ģ•Šģ•˜ė‹¤ė©“ ģ“ģ „ ė¼ģ“ģ„ ģŠ¤ėŠ” 영구적으딜 ģ†ģ‹¤ė©ė‹ˆė‹¤." line3 = "ģ¤‘ģš”: ė¼ģ“ģ„ ģŠ¤ ķ‚¤ėŠ” ź°œģøģ ģœ¼ė”œ ģ•ˆģ „ķ•˜ź²Œ ė³“ź“€ķ•˜ģ„øģš”. 공개적으딜 ź³µģœ ķ•˜ģ§€ ė§ˆģ„øģš”." +[admin.settings.premium.inputMethod] +text = "ė¼ģ“ģ„ ģŠ¤ 키" +file = "ģøģ¦ģ„œ ķŒŒģ¼" + +[admin.settings.premium.file] +label = "ė¼ģ“ģ„ ģŠ¤ ģøģ¦ģ„œ ķŒŒģ¼" +description = "ģ˜¤ķ”„ė¼ģø 구매 ģ‹œ ė°›ģ€ .lic ė˜ėŠ” .cert ė¼ģ“ģ„ ģŠ¤ ķŒŒģ¼ģ„ ģ—…ė”œė“œķ•˜ģ„øģš”" +choose = "ė¼ģ“ģ„ ģŠ¤ ķŒŒģ¼ ģ„ ķƒ" +selected = "ģ„ ķƒėØ: {{filename}} ({{size}})" +successMessage = "ė¼ģ“ģ„ ģŠ¤ ķŒŒģ¼ģ“ ģ—…ė”œė“œė˜ģ–“ ģ„±ź³µģ ģœ¼ė”œ ķ™œģ„±ķ™”ė˜ģ—ˆģŠµė‹ˆė‹¤. ģž¬ģ‹œģž‘ģ€ ķ•„ģš”ķ•˜ģ§€ ģ•ŠģŠµė‹ˆė‹¤." + +[admin.settings.premium.currentLicense] +title = "ķ™œģ„± ė¼ģ“ģ„ ģŠ¤" +file = "ģ†ŒģŠ¤: ė¼ģ“ģ„ ģŠ¤ ķŒŒģ¼ ({{path}})" +key = "ģ†ŒģŠ¤: ė¼ģ“ģ„ ģŠ¤ 키" +type = "ģœ ķ˜•: {{type}}" +noInput = "ė¼ģ“ģ„ ģŠ¤ 키넼 ģž…ė „ķ•˜ź±°ė‚˜ ģøģ¦ģ„œ ķŒŒģ¼ģ„ ģ—…ė”œė“œķ•“ ģ£¼ģ„øģš”" +success = "성공" + [admin.settings.premium.enabled] label = "프리미엄 기늄 ķ™œģ„±ķ™”" description = "ķ”„ė”œ/ģ—”ķ„°ķ”„ė¼ģ“ģ¦ˆ źø°ėŠ„ģ— ėŒ€ķ•œ ė¼ģ“ģ„ ģŠ¤ 키 ķ™•ģø ķ™œģ„±ķ™”" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}}개 ģ„ ķƒėØ" download = "ė‹¤ģš“ė”œė“œ" delete = "ģ‚­ģ œ" unsupported = "ģ§€ģ›ė˜ģ§€ ģ•ŠģŒ" +active = "ķ™œģ„±" addToUpload = "ģ—…ė”œė“œģ— 추가" +closeFile = "ķŒŒģ¼ ė‹«źø°" deleteAll = "모두 ģ‚­ģ œ" loadingFiles = "ķŒŒģ¼ ė¶ˆėŸ¬ģ˜¤ėŠ” 중..." noFiles = "ģ‚¬ģš© ź°€ėŠ„ķ•œ ķŒŒģ¼ģ“ ģ—†ģŠµė‹ˆė‹¤" @@ -5132,7 +5262,7 @@ upgrade = "ģ§€źøˆ ģ—…ź·øė ˆģ“ė“œ →" freeTitle = "ģ„œė²„ ė¼ģ“ģ„ ģŠ¤" overLimitTitle = "ģ„œė²„ ė¼ģ“ģ„ ģŠ¤ ķ•„ģš”" overLimitBody = "ė‹¹ģ‚¬ģ˜ ė¼ģ“ģ„ ģŠ¤ėŠ” ģ„œė²„ė‹¹ 묓료딜 ģµœėŒ€ {{freeTierLimit}}ėŖ…ģ˜ ģ‚¬ģš©ģžė„¼ ķ—ˆģš©ķ•©ė‹ˆė‹¤. ķ˜„ģž¬ {{overLimitUserCopy}}ėŖ…ģ˜ Stirling ģ‚¬ģš©ģžź°€ ģžˆģŠµė‹ˆė‹¤. 중단 ģ—†ģ“ ź³„ģ† ģ‚¬ģš©ķ•˜ė ¤ė©“ Stirling Server ķ”Œėžœģœ¼ė”œ ģ—…ź·øė ˆģ“ė“œķ•˜ģ„øģš” - ė¬“ģ œķ•œ ģ¢Œģ„, PDF ķ…ģŠ¤ķŠø ķŽøģ§‘, 전첓 ź“€ė¦¬ģž ģ œģ–“ 제공, $99/ģ„œė²„/ģ›”." -freeBody = "ė‹¹ģ‚¬ģ˜ Open-Core ė¼ģ“ģ„ ģŠ¤ėŠ” ģ„œė²„ė‹¹ ģµœėŒ€ {{freeTierLimit}}ėŖ…ģ˜ ģ‚¬ģš©ģžė„¼ 묓료딜 ķ—ˆģš©ķ•©ė‹ˆė‹¤. 중단 ģ—†ģ“ ķ™•ģž„ķ•˜ź³  새딜욓 PDF ķ…ģŠ¤ķŠø ķŽøģ§‘ ė„źµ¬ģ— ģ”°źø° ģ•”ģ„øģŠ¤ķ•˜ė ¤ė©“ Stirling Server ķ”Œėžœģ„ ź¶Œģž„ķ•©ė‹ˆė‹¤ - 전첓 ķŽøģ§‘ź³¼ ė¬“ģ œķ•œ ģ¢Œģ„ģ„ $99/ģ„œė²„/월에 ģ œź³µķ•©ė‹ˆė‹¤." +freeBody = "ė‹¹ģ‚¬ģ˜ Open-Core ė¼ģ“ģ„ ģŠ¤ėŠ” ģ„œė²„ė‹¹ ģµœėŒ€ {{freeTierLimit}}ėŖ…ģ˜ ģ‚¬ģš©ģžė„¼ 묓료딜 ķ—ˆģš©ķ•©ė‹ˆė‹¤. 중단 ģ—†ģ“ ķ™•ģž„ķ•˜ė ¤ė©“ Stirling Server ķ”Œėžœģ„ ź¶Œģž„ķ•©ė‹ˆė‹¤ - ė¬“ģ œķ•œ ģ¢Œģ„ ė° SSO 지원, $99/ģ„œė²„/ģ›”." [onboarding.desktopInstall] title = "ė‹¤ģš“ė”œė“œ" @@ -5237,6 +5367,31 @@ error = "ģ‚¬ģš©ģž 상태 ģ—…ė°ģ“ķŠøģ— ģ‹¤ķŒØķ–ˆģŠµė‹ˆė‹¤" success = "ģ‚¬ģš©ģžė„¼ ģ„±ź³µģ ģœ¼ė”œ ģ‚­ģ œķ–ˆģŠµė‹ˆė‹¤" error = "ģ‚¬ģš©ģž ģ‚­ģ œģ— ģ‹¤ķŒØķ–ˆģŠµė‹ˆė‹¤" +[workspace.people.changePassword] +action = "ė¹„ė°€ė²ˆķ˜ø 변경" +title = "ė¹„ė°€ė²ˆķ˜ø 변경" +subtitle = "ė‹¤ģŒ ģ‚¬ģš©ģžģ˜ ė¹„ė°€ė²ˆķ˜ø ģ—…ė°ģ“ķŠø" +newPassword = "새 ė¹„ė°€ė²ˆķ˜ø" +confirmPassword = "ė¹„ė°€ė²ˆķ˜ø ķ™•ģø" +placeholder = "새 ė¹„ė°€ė²ˆķ˜øė„¼ ģž…ė „ķ•˜ģ„øģš”" +confirmPlaceholder = "새 ė¹„ė°€ė²ˆķ˜øė„¼ ė‹¤ģ‹œ ģž…ė „ķ•˜ģ„øģš”" +passwordRequired = "새 ė¹„ė°€ė²ˆķ˜øė„¼ ģž…ė „ķ•“ ģ£¼ģ„øģš”" +passwordMismatch = "ė¹„ė°€ė²ˆķ˜øź°€ ģ¼ģ¹˜ķ•˜ģ§€ ģ•ŠģŠµė‹ˆė‹¤" +generateRandom = "ģ•ˆģ „ķ•œ ė¹„ė°€ė²ˆķ˜ø ģƒģ„±" +generatedPreview = "ģƒģ„±ėœ ė¹„ė°€ė²ˆķ˜ø:" +copyTooltip = "ķ“ė¦½ė³“ė“œģ— 복사" +copiedToClipboard = "ė¹„ė°€ė²ˆķ˜øė„¼ ķ“ė¦½ė³“ė“œģ— ė³µģ‚¬ķ–ˆģŠµė‹ˆė‹¤" +copyFailed = "ė¹„ė°€ė²ˆķ˜ø 복사 ģ‹¤ķŒØ" +sendEmail = "ģ“ 변경 ģ‚¬ķ•­ģ„ ģ‚¬ģš©ģžģ—ź²Œ ģ“ė©”ģ¼ė”œ ģ•Œė¦¬źø°" +includePassword = "ģ“ė©”ģ¼ģ— 새 ė¹„ė°€ė²ˆķ˜ø ķ¬ķ•Ø" +forcePasswordChange = "ė‹¤ģŒ ė”œź·øģø ģ‹œ ė¹„ė°€ė²ˆķ˜ø 변경 ź°•ģ œ" +emailUnavailable = "ģ“ ģ‚¬ģš©ģžģ˜ ģ“ė©”ģ¼ģ“ ģ˜¬ė°”ė„ø ģ£¼ģ†Œź°€ ģ•„ė‹ˆėÆ€ė”œ ģ•Œė¦¼ģ“ ė¹„ķ™œģ„±ķ™”ė˜ģ—ˆģŠµė‹ˆė‹¤." +smtpDisabled = "ģ“ė©”ģ¼ ģ•Œė¦¼ģ„ ģ‚¬ģš©ķ•˜ė ¤ė©“ ģ„¤ģ •ģ—ģ„œ SMTP넼 ķ™œģ„±ķ™”ķ•“ģ•¼ ķ•©ė‹ˆė‹¤." +notifyOnly = "ė¹„ė°€ė²ˆķ˜ø ģ—†ģ“ ģ“ė©”ģ¼ģ“ ė°œģ†”ė˜ė©°, ź“€ė¦¬ģžź°€ ė³€ź²½ķ–ˆģŒģ„ ģ‚¬ģš©ģžģ—ź²Œ ģ•Œė ¤ ģ¤ė‹ˆė‹¤." +submit = "ė¹„ė°€ė²ˆķ˜ø ģ—…ė°ģ“ķŠø" +success = "ė¹„ė°€ė²ˆķ˜øź°€ ģ„±ź³µģ ģœ¼ė”œ ģ—…ė°ģ“ķŠøė˜ģ—ˆģŠµė‹ˆė‹¤" +error = "ė¹„ė°€ė²ˆķ˜ø ģ—…ė°ģ“ķŠøģ— ģ‹¤ķŒØķ–ˆģŠµė‹ˆė‹¤" + [workspace.people.emailInvite] tab = "ģ“ė©”ģ¼ ģ“ˆėŒ€" description = "ģ•„ėž˜ģ— ģ“ė©”ģ¼ģ„ ģ‰¼ķ‘œė”œ 구분핓 ģž…ė „ķ•˜ź±°ė‚˜ ė¶™ģ—¬ė„£ģœ¼ģ„øģš”. ģ‚¬ģš©ģžėŠ” ģ“ė©”ģ¼ė”œ ė”œź·øģø ģžź²© ģ¦ėŖ…ģ„ ė°›ģŠµė‹ˆė‹¤." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "ģ“ė©”ģ¼ ģ£¼ģ†Œė„¼ ģµœģ†Œ ķ•œ 개 ģ“ģƒ ģž…ė „ķ•“ģ•¼ ķ•©ė‹ˆė‹¤" submit = "ģ“ˆėŒ€ģž„ 볓낓기" success = "ģ‚¬ģš©ģž ģ“ˆėŒ€ź°€ ģ™„ė£Œė˜ģ—ˆģŠµė‹ˆė‹¤" -partialSuccess = "ģ¼ė¶€ ģ“ˆėŒ€ź°€ ģ‹¤ķŒØķ–ˆģŠµė‹ˆė‹¤" +partialFailure = "ģ¼ė¶€ ģ“ˆėŒ€ź°€ ģ‹¤ķŒØķ–ˆģŠµė‹ˆė‹¤" allFailed = "ģ‚¬ģš©ģž ģ“ˆėŒ€ģ— ģ‹¤ķŒØķ–ˆģŠµė‹ˆė‹¤" error = "ģ“ˆėŒ€ģž„ 전솔에 ģ‹¤ķŒØķ–ˆģŠµė‹ˆė‹¤" @@ -5770,6 +5925,7 @@ subtitle = "Stirling ź³„ģ •ģœ¼ė”œ ė”œź·øģø" [setup.selfhosted] title = "ģ„œė²„ģ— ė”œź·øģø" subtitle = "ģ„œė²„ ģžź²© ģ¦ėŖ…ģ„ ģž…ė „ķ•˜ģ„øģš”" +link = "ė˜ėŠ” 셀프 ķ˜øģŠ¤ķŒ… 계정에 ģ—°ź²°" [setup.server] title = "ģ„œė²„ģ— ģ—°ź²°" @@ -5788,6 +5944,14 @@ description = "ģžź°€ ķ˜øģŠ¤ķŒ… Stirling PDF ģ„œė²„ģ˜ 전첓 URLģ„ ģž…ė „ķ•˜ emptyUrl = "ģ„œė²„ URLģ„ ģž…ė „ķ•˜ģ„øģš”" unreachable = "ģ„œė²„ģ— ģ—°ź²°ķ•  수 ģ—†ģŠµė‹ˆė‹¤" testFailed = "ģ—°ź²° ķ…ŒģŠ¤ķŠøģ— ģ‹¤ķŒØķ–ˆģŠµė‹ˆė‹¤" +configFetch = "ģ„œė²„ źµ¬ģ„±ģ„ ź°€ģ øģ˜¤ģ§€ ėŖ»ķ–ˆģŠµė‹ˆė‹¤. URLģ„ ķ™•ģøķ•˜ź³  ė‹¤ģ‹œ ģ‹œė„ķ•˜ģ„øģš”." + +[setup.server.error.securityDisabled] +title = "ė”œź·øģøģ“ ķ™œģ„±ķ™”ė˜ģ–“ ģžˆģ§€ ģ•ŠģŒ" +body = "ģ“ ģ„œė²„ģ—ėŠ” ė”œź·øģøģ“ ķ™œģ„±ķ™”ė˜ģ–“ ģžˆģ§€ ģ•ŠģŠµė‹ˆė‹¤. ģ“ ģ„œė²„ģ— ģ—°ź²°ķ•˜ė ¤ė©“ ģøģ¦ģ„ ķ™œģ„±ķ™”ķ•“ģ•¼ ķ•©ė‹ˆė‹¤:" +step1 = "ķ™˜ź²½ģ—ģ„œ DOCKER_ENABLE_SECURITY=true넼 ģ„¤ģ •ķ•˜ģ„øģš”" +step2 = "ė˜ėŠ” settings.ymlģ—ģ„œ security.enableLogin=true넼 ģ„¤ģ •ķ•˜ģ„øģš”" +step3 = "ģ„œė²„ė„¼ ģž¬ģ‹œģž‘ķ•˜ģ„øģš”" [setup.login] title = "ė”œź·øģø" @@ -5797,6 +5961,13 @@ submit = "ė”œź·øģø" signInWith = "ė‹¤ģŒģœ¼ė”œ ė”œź·øģø" oauthPending = "ģøģ¦ģ„ ģœ„ķ•“ ėøŒė¼ģš°ģ €ė„¼ ģ—¬ėŠ” 중..." orContinueWith = "ė˜ėŠ” ģ“ė©”ģ¼ė”œ ź³„ģ†" +serverRequirement = "ģ°øź³ : ģ„œė²„ģ—ģ„œ ė”œź·øģø źø°ėŠ„ģ“ ķ™œģ„±ķ™”ė˜ģ–“ ģžˆģ–“ģ•¼ ķ•©ė‹ˆė‹¤." +showInstructions = "ķ™œģ„±ķ™” 방법" +hideInstructions = "지침 숨기기" +instructions = "Stirling PDF ģ„œė²„ģ—ģ„œ ė”œź·øģø źø°ėŠ„ģ„ ķ™œģ„±ķ™”ķ•˜ė ¤ė©“:" +instructionsEnvVar = "ė‹¤ģŒ ķ™˜ź²½ ė³€ģˆ˜ė„¼ ģ„¤ģ •ķ•˜ģ„øģš”:" +instructionsOrYml = "ė˜ėŠ” settings.ymlģ—ģ„œ:" +instructionsRestart = "그런 ė‹¤ģŒ 변경 ģ‚¬ķ•­ģ„ ģ ģš©ķ•˜ė ¤ė©“ ģ„œė²„ė„¼ ģž¬ģ‹œģž‘ķ•˜ģ„øģš”." [setup.login.username] label = "ģ‚¬ģš©ģž ģ“ė¦„" @@ -5853,6 +6024,7 @@ earlyAccess = "얼리 ģ•”ģ„øģŠ¤" reset = "변경 사항 ģ“ˆźø°ķ™”" downloadJson = "JSON ė‹¤ģš“ė”œė“œ" generatePdf = "PDF ģƒģ„±" +saveChanges = "변경 사항 ģ €ģž„" [pdfTextEditor.options.autoScaleText] title = "ķ…ģŠ¤ķŠø ģžė™ 크기 ģ”°ģ •" @@ -5890,6 +6062,8 @@ alpha = "ģ“ ģ•ŒķŒŒ ė·°ģ–“ėŠ” 아직 ė°œģ „ ģ¤‘ģž…ė‹ˆė‹¤ā€”ģ¼ė¶€ 글꼓, ģƒ‰ģƒ [pdfTextEditor.empty] title = "ė”œė“œėœ ė¬øģ„œ ģ—†ģŒ" subtitle = "ķ…ģŠ¤ķŠø ķŽøģ§‘ģ„ ģ‹œģž‘ķ•˜ė ¤ė©“ PDF ė˜ėŠ” JSON ķŒŒģ¼ģ„ ė”œė“œķ•˜ģ„øģš”." +dropzone = "여기에 PDF ė˜ėŠ” JSON ķŒŒģ¼ģ„ ėŒģ–“ė‹¤ ė†“ź±°ė‚˜ ķ“ė¦­ķ•˜ģ—¬ ģ°¾ģ•„ė³“ģ„øģš”" +dropzoneWithFiles = "ķŒŒģ¼ ķƒ­ģ—ģ„œ ķŒŒģ¼ģ„ ģ„ ķƒķ•˜ź±°ė‚˜, 여기에 PDF ė˜ėŠ” JSON ķŒŒģ¼ģ„ ėŒģ–“ė‹¤ ė†“ź±°ė‚˜ ķ“ė¦­ķ•˜ģ—¬ ģ°¾ģ•„ė³“ģ„øģš”" [pdfTextEditor.welcomeBanner] title = "PDF ķ…ģŠ¤ķŠø ķŽøģ§‘źø°(얼리 ģ•”ģ„øģŠ¤)에 ģ˜¤ģ‹  ź²ƒģ„ ķ™˜ģ˜ķ•©ė‹ˆė‹¤" diff --git a/frontend/public/locales/ml-ML/translation.toml b/frontend/public/locales/ml-ML/translation.toml index b7f97c3c22..2da56e0fcf 100644 --- a/frontend/public/locales/ml-ML/translation.toml +++ b/frontend/public/locales/ml-ML/translation.toml @@ -131,7 +131,7 @@ unsupported = "ą“Ŗą“æą“Øąµą“¤ąµą“£ą“Æą“æą“²ąµą“²" [toolPanel] placeholder = "ą“¤ąµą“Ÿą“™ąµą“™ą“¾ąµ» ą“’ą“°ąµ ą“Ÿąµ‚ąµ¾ ą“¤ą“æą“°ą“žąµą“žąµ†ą“Ÿąµą“•ąµą“•ąµą“•" -alpha = "Alpha" +alpha = "ą“†ąµ½ą“«" premiumFeature = "ą“Ŗąµą“°ąµ€ą“®ą“æą“Æą“‚ ą“«ąµ€ą“šąµą“šąµ¼:" comingSoon = "ą“µą“°ąµą“Øąµą“Øąµ:" @@ -163,6 +163,11 @@ unfavorite = "ą“Ŗąµą“°ą“æą“Æą“Ŗąµą“Ŗąµ†ą“Ÿąµą“Ÿą“µą“Æą“æąµ½ ą“Øą“æą“Øąµą“Ø fullscreen = "ą“«ąµąµ¾ą“øąµą“•ąµą“°ąµ€ąµ» ą“®ąµ‹ą“”ą“æą“²ąµ‡ą“•ąµą“•ąµ ą“®ą“¾ą“±ąµą“±ąµą“•" sidebar = "ą“øąµˆą“”ąµą“¬ą“¾ąµ¼ ą“®ąµ‹ą“”ą“æą“²ąµ‡ą“•ąµą“•ąµ ą“®ą“¾ą“±ąµą“±ąµą“•" +[backendStartup] +notFoundTitle = "ą“¬ą“¾ą“•ąµą“•ąµā€Œą“Žąµ»ą“”ąµ ą“•ą“£ąµą“Ÿąµ†ą“¤ąµą“¤ą“¾ą“Øą“¾ą“Æą“æą“²ąµą“²" +retry = "ą“µąµ€ą“£ąµą“Ÿąµą“‚ ą“¶ąµą“°ą“®ą“æą“•ąµą“•ąµą“•" +unreachable = "ą“ˆ ą“†ą“Ŗąµą“Ŗąµą“²ą“æą“•ąµą“•ąµ‡ą“·ąµ» ą“Øą“æą“²ą“µą“æąµ½ ą“¬ą“¾ą“•ąµą“•ąµā€Œą“Žąµ»ą“”ąµą“®ą“¾ą“Æą“æ ą“•ą“£ą“•ąµą“±ąµą“±ąµ ą“šąµ†ą“Æąµą“Æą“¾ąµ» ą“•ą““ą“æą“Æąµą“Øąµą“Øą“æą“²ąµą“². ą“¬ą“¾ą“•ąµą“•ąµā€Œą“Žąµ»ą“”ą“æą“Øąµą“±ąµ† ą“Øą“æą“²ą“Æąµą“‚ ą“Øąµ†ą“±ąµą“±ąµā€Œą“µąµ¼ą“•ąµą“•ąµ ą“•ą“£ą“•ąµą“±ąµą“±ą“æą“µą“æą“±ąµą“±ą“æą“Æąµą“‚ ą“Ŗą“°ą“æą“¶ąµ‹ą“§ą“æą“šąµą“šąµ ą“µąµ€ą“£ąµą“Ÿąµą“‚ ą“¶ąµą“°ą“®ą“æą“•ąµą“•ąµą“•." + [zipWarning] title = "ą“µą“²ą“æą“Æ ZIP ą“«ą“Æąµ½" message = "ą“ˆ ZIP-ąµ½ {{count}} ą“«ą“Æą“²ąµą“•ąµ¾ ą“‰ą“£ąµą“Ÿąµ. ą“Žą“™ąµą“•ą“æą“²ąµą“‚ ą“Žą“•ąµą“øąµą“Ÿąµą“°ą“¾ą“•ąµą“±ąµą“±ąµ ą“šąµ†ą“Æąµą“Æą“Ÿąµą“Ÿąµ‡?" @@ -369,7 +374,7 @@ privacy = "ą“øąµą“µą“•ą“¾ą“°ąµą“Æą“¤" [settings.developer] title = "ą“”ąµ†ą“µą“²ą“Ŗąµą“Ŗąµ¼" -apiKeys = "API Keys" +apiKeys = "API ą“•ąµ€ą“•ąµ¾" [settings.tooltips] enableLoginFirst = "ą“†ą“¦ąµą“Æą“‚ ą“²ąµ‹ą“—ą“æąµ» ą“®ąµ‹ą“”ąµ ą“øą“œąµ€ą“µą“®ą“¾ą“•ąµą“•ąµą“•" @@ -709,7 +714,7 @@ title = "ą“Ŗą“°ą“¤ąµą“¤ąµą“•" desc = "ą“’ą“°ąµ PDF-ąµ½ ą“Øą“æą“Øąµą“Øąµ ą“Žą“²ąµą“²ą“¾ ą“‡ą“Øąµą“±ą“±ą“¾ą“•ąµą“Ÿąµ€ą“µąµ ą“˜ą“Ÿą“•ą“™ąµą“™ą“³ąµą“‚ ą“«ąµ‹ą“®ąµą“•ą“³ąµą“‚ ą“Øąµ€ą“•ąµą“•ą“‚ ą“šąµ†ą“Æąµą“Æąµą“•" [home.certSign] -tags = "authenticate,PEM,P12,official,encrypt,sign,certificate,PKCS12,JKS,server,manual,auto" +tags = "ą“Ŗąµą“°ą“¾ą“®ą“¾ą“£ąµ€ą“•ą“°ą“£ą“‚,PEM,P12,ą“”ą“¦ąµą“Æąµ‹ą“—ą“æą“•ą“‚,ą“Žąµ»ą“•ąµą“°ą“æą“Ŗąµą“±ąµą“±ąµ,ą“øąµˆąµ»,ą“øąµ¼ą“Ÿąµą“Ÿą“æą“«ą“æą“•ąµą“•ą“±ąµą“±ąµ,PKCS12,JKS,ą“øąµ†ąµ¼ą“µąµ¼,ą“®ą“¾ą“Øąµą“µąµ½,ą““ą“Ÿąµą“Ÿąµ‹" title = "ą“øąµ¼ą“Ÿąµą“Ÿą“æą“«ą“æą“•ąµą“•ą“±ąµą“±ąµ ą“‰ą“Ŗą“Æąµ‹ą“—ą“æą“šąµą“šąµ ą“’ą“Ŗąµą“Ŗą“æą“Ÿąµą“•" desc = "ą“’ą“°ąµ ą“øąµ¼ą“Ÿąµą“Ÿą“æą“«ą“æą“•ąµą“•ą“±ąµą“±ąµ/ą“•ąµ€ (PEM/P12) ą“‰ą“Ŗą“Æąµ‹ą“—ą“æą“šąµą“šąµ ą“’ą“°ąµ PDF ą“’ą“Ŗąµą“Ŗą“æą“Ÿąµą“Øąµą“Øąµ" @@ -794,7 +799,7 @@ title = "ą“’ą“°ąµŠą“±ąµą“± ą“µą“²ą“æą“Æ ą“Ŗąµ‡ą“œąµ" desc = "ą“Žą“²ąµą“²ą“¾ PDF ą“Ŗąµ‡ą“œąµą“•ą“³ąµą“‚ ą“’ą“°ąµŠą“±ąµą“± ą“µą“²ą“æą“Æ ą“Ŗąµ‡ą“œą“æą“²ąµ‡ą“•ąµą“•ąµ ą“²ą“Æą“æą“Ŗąµą“Ŗą“æą“•ąµą“•ąµą“Øąµą“Øąµ" [home.showJS] -tags = "javascript,code,script" +tags = "javascript,ą“•ąµ‹ą“”ąµ,ą“øąµą“•ąµą“°ą“æą“Ŗąµą“±ąµą“±ąµ" title = "ą“œą“¾ą“µą“¾ą“øąµą“•ąµą“°ą“æą“Ŗąµą“±ąµą“±ąµ ą“•ą“¾ą“£ą“æą“•ąµą“•ąµą“•" desc = "ą“’ą“°ąµ PDF-ąµ½ ą“•ąµą“¤ąµą“¤ą“æą“µą“šąµą“š ą“ą“¤ąµ†ą“™ąµą“•ą“æą“²ąµą“‚ JS ą“¤ą“æą“°ą“Æąµą“•ą“Æąµą“‚ ą“Ŗąµą“°ą“¦ąµ¼ą“¶ą“æą“Ŗąµą“Ŗą“æą“•ąµą“•ąµą“•ą“Æąµą“‚ ą“šąµ†ą“Æąµą“Æąµą“Øąµą“Øąµ" @@ -912,9 +917,12 @@ desc = "PDF ą“Ŗąµą“°ą“µąµ¼ą“¤ąµą“¤ą“Øą“™ąµą“™ąµ¾ ą“¬ą“Øąµą“§ą“æą“Ŗąµą“Ŗą“æ desc = "ą“®ą“±ąµą“±ąµŠą“°ąµ PDF-ą“Øąµ ą“®ąµą“•ą“³ą“æąµ½ PDF-ą“•ąµ¾ ą““ą“µąµ¼ą“²ąµ‡ ą“šąµ†ą“Æąµą“Æąµą“Øąµą“Øąµ" title = "PDF-ą“•ąµ¾ ą““ą“µąµ¼ą“²ąµ‡ ą“šąµ†ą“Æąµą“Æąµą“•" +[home.pdfTextEditor] +title = "PDF ą“Ÿąµ†ą“•ąµą“øąµą“±ąµą“±ąµ ą“Žą“”ą“æą“±ąµą“±ąµ¼" +desc = "PDFą“•ą“³ą“æą“²ąµ† ą“Øą“æą“²ą“µą“æą“²ąµą“³ąµą“³ ą“Ÿąµ†ą“•ąµą“øąµą“±ąµą“±ąµą“‚ ą“šą“æą“¤ąµą“°ą“™ąµą“™ą“³ąµą“‚ ą“¤ą“æą“°ąµą“¤ąµą“¤ąµą“•" [home.addText] -tags = "text,annotation,label" +tags = "ą“Ÿąµ†ą“•ąµą“øąµą“±ąµą“±ąµ,ą“…ą“Øąµ‹ą“Ÿąµą“Ÿąµ‡ą“·ąµ»,ą“²ąµ‡ą“¬ąµ½" title = "ą“Ÿąµ†ą“•ąµą“øąµą“±ąµą“±ąµ ą“šąµ‡ąµ¼ą“•ąµą“•ąµą“•" desc = "ą“Øą“æą“™ąµą“™ą“³ąµą“Ÿąµ† PDF-ąµ½ ą“Žą“µą“æą“Ÿąµ†ą“Æą“æą“²ąµą“‚ ą“•ą“øąµą“±ąµą“±ą“‚ ą“Ÿąµ†ą“•ąµą“øąµą“±ąµą“±ąµ ą“šąµ‡ąµ¼ą“•ąµą“•ąµą“•" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "ą“µą“°ą“šąµą“š ą“’ą“Ŗąµą“Ŗąµ" defaultImageLabel = "ą“…ą“Ŗąµā€Œą“²ąµ‹ą“”ąµ ą“šąµ†ą“Æąµą“¤ ą“’ą“Ŗąµą“Ŗąµ" defaultTextLabel = "ą“Ÿąµˆą“Ŗąµą“Ŗąµ ą“šąµ†ą“Æąµą“¤ ą“’ą“Ŗąµą“Ŗąµ" saveButton = "ą“’ą“Ŗąµą“Ŗąµ ą“øąµ‡ą“µąµ ą“šąµ†ą“Æąµą“Æąµą“•" +savePersonal = "ą“µąµą“Æą“•ąµą“¤ą“æą“Ŗą“°ą“®ą“¾ą“Æą“æ ą“øą“‚ą“°ą“•ąµą“·ą“æą“•ąµą“•ąµą“•" +saveShared = "ą“Ŗą“™ąµą“•ą“æą“Ÿąµą“Ÿą“¤ą“¾ą“Æą“æ ą“øą“‚ą“°ą“•ąµą“·ą“æą“•ąµą“•ąµą“•" saveUnavailable = "ą“øąµ‡ą“µąµ ą“šąµ†ą“Æąµą“Æą“¾ąµ» ą“†ą“¦ąµą“Æą“‚ ą“’ą“°ąµ ą“’ą“Ŗąµą“Ŗąµ ą“øąµƒą“·ąµą“Ÿą“æą“•ąµą“•ąµą“•." noChanges = "ą“Øą“æą“²ą“µą“æą“²ąµ† ą“’ą“Ŗąµą“Ŗąµ ą“‡ą“¤ą“æą“Øą“•ą“‚ ą“øąµ‡ą“µąµ ą“šąµ†ą“Æąµą“¤ą“æą“Ÿąµą“Ÿąµą“£ąµą“Ÿąµ." +tempStorageTitle = "ą“¤ą“¾ąµ½ą“•ąµą“•ą“¾ą“²ą“æą“• ą“¬ąµą“°ąµ—ą“øąµ¼ ą“øąµą“±ąµą“±ąµ‹ą“±ąµ‡ą“œąµ" +tempStorageDescription = "ą“’ą“Ŗąµą“Ŗąµą“•ąµ¾ ą“Øą“æą“™ąµą“™ą“³ąµą“Ÿąµ† ą“¬ąµą“°ąµ—ą“øą“±ą“æąµ½ ą“®ą“¾ą“¤ąµą“°ą“‚ ą“øą“‚ą“­ą“°ą“æą“•ąµą“•ą“Ŗąµą“Ŗąµ†ą“Ÿąµą“‚. ą“¬ąµą“°ąµ—ą“øąµ¼ ą“”ą“¾ą“±ąµą“± ą“Øąµ€ą“•ąµą“•ą“‚ ą“šąµ†ą“Æąµą“¤ą“¾ąµ½ ą“…ą“²ąµą“²ąµ†ą“™ąµą“•ą“æąµ½ ą“¬ąµą“°ąµ—ą“øąµ¼ ą“®ą“¾ą“±ąµą“±ą“æą“Æą“¾ąµ½ ą“…ą“µ ą“Øą“·ąµą“Ÿą“Ŗąµą“Ŗąµ†ą“Ÿąµą“‚." +personalHeading = "ą“µąµą“Æą“•ąµą“¤ą“æą“—ą“¤ ą“’ą“Ŗąµą“Ŗąµą“•ąµ¾" +sharedHeading = "ą“Ŗą“™ąµą“•ą“æą“Ÿąµą“Ÿ ą“’ą“Ŗąµą“Ŗąµą“•ąµ¾" +personalDescription = "ą“ˆ ą“’ą“Ŗąµą“Ŗąµą“•ąµ¾ ą“Øą“æą“™ąµą“™ą“³ąµą“•ąµą“•ąµ ą“®ą“¾ą“¤ąµą“°ą“®ąµ‡ ą“•ą“¾ą“£ą“¾ą“Øą“¾ą“•ąµ‚." +sharedDescription = "ą“Žą“²ąµą“²ą“¾ ą“‰ą“Ŗą“Æąµ‹ą“•ąµą“¤ą“¾ą“•ąµą“•ą“³ąµą“‚ ą“ˆ ą“’ą“Ŗąµą“Ŗąµą“•ąµ¾ ą“•ą“¾ą“£ąµą“•ą“Æąµą“‚ ą“‰ą“Ŗą“Æąµ‹ą“—ą“æą“•ąµą“•ąµą“•ą“Æąµą“‚ ą“šąµ†ą“Æąµą“Æą“¾ą“‚." [sign.saved.type] canvas = "ą“”ąµą“°ąµ‹ą“Æą“æą“‚ą“—ąµ" @@ -2731,7 +2747,7 @@ submit = "ą“øą“®ąµ¼ą“Ŗąµą“Ŗą“æą“•ąµą“•ąµą“•" failed = "ą“®ąµ¾ą“Ÿąµą“Ÿą“æ-ą“Ŗąµ‡ą“œąµ ą“²ąµ‡ą“”ą“Ÿąµą“Ÿąµ ą“øąµƒą“·ąµą“Ÿą“æą“•ąµą“•ąµą“®ąµą“Ŗąµ‹ąµ¾ ą“Ŗą“æą“¶ą“•ąµ ą“øą“‚ą“­ą“µą“æą“šąµą“šąµ." [bookletImposition] -tags = "booklet,imposition,printing,binding,folding,signature" +tags = "ą“¬ąµą“•ąµą“•ąµā€Œą“²ąµ†ą“±ąµą“±ąµ,ą“‡ą“‚ą“Ŗąµ‹ą“øą“æą“·ąµ»,ą“Ŗąµą“°ą“æą“Øąµą“±ą“æą“‚ą“—ąµ,ą“¬ąµˆąµ»ą“”ą“æą“‚ą“—ąµ,ą“®ą“Ÿą“•ąµą“•ąµ½,ą“øą“æą“—ąµą“Øąµ‡ą“šąµą“šąµ¼" title = "ą“¬ąµą“•ąµą“•ąµą“²ąµ†ą“±ąµą“±ąµ ą“‡ą“‚ą“Ŗąµ‹ą“øą“æą“·ąµ»" header = "ą“¬ąµą“•ąµą“•ąµą“²ąµ†ą“±ąµą“±ąµ ą“‡ą“‚ą“Ŗąµ‹ą“øą“æą“·ąµ»" submit = "ą“¬ąµą“•ąµą“•ąµą“²ąµ†ą“±ąµą“±ąµ ą“øąµƒą“·ąµą“Ÿą“æą“•ąµą“•ąµą“•" @@ -2830,7 +2846,7 @@ scaleFactor = "ą“’ą“°ąµ ą“Ŗąµ‡ą“œą“æą“Øąµą“±ąµ† ą“øąµ‚ą“‚ ą“Øą“æą“² (ą“•ąµą“° submit = "ą“øą“®ąµ¼ą“Ŗąµą“Ŗą“æą“•ąµą“•ąµą“•" [adjustPageScale] -tags = "resize,modify,dimension,adapt" +tags = "ą“µą“²ą“æą“Ŗąµą“Ŗą“®ą“¾ą“±ąµą“±ą“‚,ą“­ąµ‡ą“¦ą“—ą“¤ą“æ,ą“Ŗą“°ą“æą“®ą“¾ą“£ą“‚,ą“…ą“Øąµą“øąµƒą“¤ą“®ą“¾ą“•ąµą“•ąµ½" title = "ą“Ŗąµ‡ą“œąµ ą“øąµą“•ąµ†ą“Æą“æąµ½ ą“•ąµą“°ą“®ą“Ŗąµą“Ŗąµ†ą“Ÿąµą“¤ąµą“¤ąµą“•" header = "ą“Ŗąµ‡ą“œąµ ą“øąµą“•ąµ†ą“Æą“æąµ½ ą“•ąµą“°ą“®ą“Ŗąµą“Ŗąµ†ą“Ÿąµą“¤ąµą“¤ąµą“•" submit = "ą“Ŗąµ‡ą“œąµ ą“øąµą“•ąµ†ą“Æą“æąµ½ ą“•ąµą“°ą“®ą“Ŗąµą“Ŗąµ†ą“Ÿąµą“¤ąµą“¤ąµą“•" @@ -2841,8 +2857,8 @@ label = "ą“øąµą“•ąµ†ą“Æą“æąµ½ ą“«ą“¾ą“•ąµą“Ÿąµ¼" [adjustPageScale.pageSize] label = "ą“Ÿą“¾ąµ¼ą“—ą“±ąµą“±ąµ ą“Ŗąµ‡ą“œąµ ą“µą“²ą“æą“Ŗąµą“Ŗą“‚" keep = "ą“…ą“øąµ½ ą“µą“²ą“æą“Ŗąµą“Ŗą“‚ ą“Øą“æą“²ą“Øą“æąµ¼ą“¤ąµą“¤ąµą“•" -letter = "Letter" -legal = "Legal" +letter = "ą“²ą“±ąµą“±ąµ¼" +legal = "ą“²ąµ€ą“—ąµ½" [adjustPageScale.error] failed = "ą“Ŗąµ‡ą“œąµ ą“øąµą“•ąµ†ą“Æą“æąµ½ ą“•ąµą“°ą“®ą“Ŗąµą“Ŗąµ†ą“Ÿąµą“¤ąµą“¤ąµą“Øąµą“Øą“¤ą“æą“Øą“æą“Ÿąµ† ą“Ŗą“æą“¶ą“•ąµ ą“øą“‚ą“­ą“µą“æą“šąµą“šąµ." @@ -3020,6 +3036,91 @@ title = "PDF-ą“Øąµ†ą“•ąµą“•ąµą“±ą“æą“šąµą“šąµą“³ąµą“³ ą“µą“æą“µą“°ą“™ąµą“™ header = "PDF-ą“Øąµ†ą“•ąµą“•ąµą“±ą“æą“šąµą“šąµą“³ąµą“³ ą“µą“æą“µą“°ą“™ąµą“™ąµ¾ ą“Øąµ‡ą“Ÿąµą“•" submit = "ą“µą“æą“µą“°ą“™ąµą“™ąµ¾ ą“Øąµ‡ą“Ÿąµą“•" downloadJson = "JSON ą“”ąµ—ąµŗą“²ąµ‹ą“”ąµ ą“šąµ†ą“Æąµą“Æąµą“•" +processing = "ą“µą“æą“µą“°ą“™ąµą“™ąµ¾ ą“Ŗąµą“±ą“¤ąµą“¤ąµ†ą“Ÿąµą“•ąµą“•ąµą“Øąµą“Øąµ..." +results = "ą“«ą“²ą“™ąµą“™ąµ¾" +noResults = "ą“’ą“°ąµ ą“±ą“æą“Ŗąµą“Ŗąµ‹ąµ¼ą“Ÿąµą“Ÿąµ ą“øąµƒą“·ąµą“Ÿą“æą“•ąµą“•ą“¾ąµ» ą“‰ą“Ŗą“•ą“°ą“£ą“‚ ą“Ŗąµą“°ą“µąµ¼ą“¤ąµą“¤ą“æą“Ŗąµą“Ŗą“æą“•ąµą“•ąµą“•." +downloads = "ą“”ąµ—ąµŗą“²ąµ‹ą“”ąµą“•ąµ¾" +noneDetected = "ą“’ą“Øąµą“Øąµą“‚ ą“•ą“£ąµą“Ÿąµ†ą“¤ąµą“¤ą“æą“Æą“æą“²ąµą“²" +indexTitle = "ą“øąµ‚ą“šą“æą“•" + +[getPdfInfo.report] +entryLabel = "ą“Ŗąµ‚ąµ¼ą“£ąµą“£ ą“µą“æą“µą“° ą“øą“‚ą“—ąµą“°ą“¹ą“‚" +shortTitle = "PDF ą“µą“æą“µą“°ą“‚" + +[getPdfInfo.sections] +metadata = "ą“®ąµ†ą“±ąµą“±ą“¾ą“”ą“¾ą“±ąµą“±" +formFields = "ą“«ąµ‹ą“‚ ą“«ąµ€ąµ½ą“”ąµą“•ąµ¾" +basicInfo = "ą“…ą“Ÿą“æą“øąµą“„ą“¾ą“Ø ą“µą“æą“µą“°ą“‚" +documentInfo = "ą“Ŗąµą“°ą“®ą“¾ą“£ ą“µą“æą“µą“°ą“‚" +compliance = "ą“…ą“Øąµą“øą“°ą“£ą“‚" +encryption = "ą“Žąµ»ą“•ąµą“°ą“æą“Ŗąµą“·ąµ»" +permissions = "ą“…ą“Øąµą“®ą“¤ą“æą“•ąµ¾" +other = "ą“®ą“±ąµą“±ąµ" +perPageInfo = "ą““ą“°ąµ‹ ą“Ŗąµ‡ą“œą“æą“²ąµ† ą“µą“æą“µą“°ą“‚" +tableOfContents = "ą“µą“æą“·ą“Æą“øąµ‚ą“šą“æą“•" + +[getPdfInfo.other] +attachments = "ą“…ą“±ąµą“±ą“¾ą“šąµą“šąµą“®ąµ†ą“Øąµą“±ąµą“•ąµ¾" +embeddedFiles = "ą“Žą“‚ą“¬ąµ†ą“”ąµ ą“šąµ†ą“Æąµą“¤ ą“«ą“Æą“²ąµą“•ąµ¾" +javaScript = "JavaScript" +layers = "ą“²ąµ†ą“Æą“±ąµą“•ąµ¾" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "ą“µą“²ąµą“Ŗąµą“Ŗą“‚" +annotations = "ą“…ą“Øąµ‹ą“Ÿąµą“Ÿąµ‡ą“·ą“Øąµą“•ąµ¾" +images = "ą“šą“æą“¤ąµą“°ą“™ąµą“™ąµ¾" +links = "ą“²ą“æą“™ąµą“•ąµą“•ąµ¾" +fonts = "ą“«ąµ‹ą“£ąµą“Ÿąµą“•ąµ¾" +xobjects = "XObject ą“Žą“£ąµą“£ą“™ąµą“™ąµ¾" +multimedia = "ą“®ąµ¾ą“Ÿąµą“Ÿą“æą“®ąµ€ą“”ą“æą“Æ" + +[getPdfInfo.summary] +pages = "ą“Ŗąµ‡ą“œąµą“•ąµ¾" +fileSize = "ą“«ą“Æąµ½ ą“µą“²ąµą“Ŗąµą“Ŗą“‚" +pdfVersion = "PDF ą“Ŗą“¤ą“æą“Ŗąµą“Ŗąµ" +language = "ą“­ą“¾ą“·" +title = "PDF ą“øą“‚ą“—ąµą“°ą“¹ą“‚" +author = "ą“°ą“šą“Æą“æą“¤ą“¾ą“µąµ" +created = "ą“øąµƒą“·ąµą“Ÿą“æą“šąµą“šą“¤ąµ" +modified = "ą“¤ą“æą“°ąµą“¤ąµą“¤ą“æą“Æą“¤ąµ" +permsAll = "ą“Žą“²ąµą“²ą“¾ ą“…ą“Øąµą“®ą“¤ą“æą“•ą“³ąµą“‚ ą“…ą“Øąµą“µą“¦ą“æą“šąµą“šą“æą“Ÿąµą“Ÿąµą“£ąµą“Ÿąµ" +permsRestricted = "{{count}} ą“Øą“æą“Æą“Øąµą“¤ąµą“°ą“£ą“™ąµą“™ąµ¾" +permsMixed = "ą“šą“æą“² ą“…ą“Øąµą“®ą“¤ą“æą“•ąµ¾ ą“Øą“æą“Æą“Øąµą“¤ąµą“°ą“æą“šąµą“šą“æą“°ą“æą“•ąµą“•ąµą“Øąµą“Øąµ" +hasCompliance = "ą“…ą“Øąµą“øą“°ą“£ ą“®ą“¾ą“Øą“¦ą“£ąµą“”ą“™ąµą“™ąµ¾ ą“‰ą“£ąµą“Ÿąµ" +noCompliance = "ą“…ą“Øąµą“øą“°ą“£ ą“®ą“¾ą“Øą“¦ą“£ąµą“”ą“™ąµą“™ą“³ą“æą“²ąµą“²" +basic = "ą“…ą“Ÿą“æą“øąµą“„ą“¾ą“Ø ą“µą“æą“µą“°ą“‚" +documentInfo = "ą“Ŗąµą“°ą“®ą“¾ą“£ ą“µą“æą“µą“°ą“‚" +securityTitle = "ą“øąµą“°ą“•ąµą“·ą“¾ ą“Øą“æą“²" +technical = "ą“øą“¾ą“™ąµą“•ąµ‡ą“¤ą“æą“•" +overviewTitle = "PDF ą“…ą“µą“²ąµ‹ą“•ą“Øą“‚" + +[getPdfInfo.summary.security] +encrypted = "ą“Žąµ»ą“•ąµą“°ą“æą“Ŗąµą“±ąµą“±ąµ ą“šąµ†ą“Æąµą“¤ PDF - ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµ ą“øą“‚ą“°ą“•ąµą“·ą“£ą“‚ ą“‰ą“£ąµą“Ÿąµ" +unencrypted = "ą“Žąµ»ą“•ąµą“°ą“æą“Ŗąµą“±ąµą“±ąµ ą“šąµ†ą“Æąµą“Æą“¾ą“¤ąµą“¤ PDF - ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµ ą“øą“‚ą“°ą“•ąµą“·ą“£ą“®ą“æą“²ąµą“²" + +[getPdfInfo.summary.tech] +images = "ą“šą“æą“¤ąµą“°ą“™ąµą“™ąµ¾" +fonts = "ą“«ąµ‹ą“£ąµą“Ÿąµą“•ąµ¾" +formFields = "ą“«ąµ‹ą“‚ ą“«ąµ€ąµ½ą“”ąµą“•ąµ¾" +embeddedFiles = "ą“Žą“‚ą“¬ąµ†ą“”ąµ ą“šąµ†ą“Æąµą“¤ ą“«ą“Æą“²ąµą“•ąµ¾" +javaScript = "JavaScript" +layers = "ą“²ąµ†ą“Æą“±ąµą“•ąµ¾" +bookmarks = "ą“¬ąµą“•ąµą“•ąµā€Œą“®ą“¾ąµ¼ą“•ąµą“•ąµą“•ąµ¾" +multimedia = "ą“®ąµ¾ą“Ÿąµą“Ÿą“æą“®ąµ€ą“”ą“æą“Æ" + +[getPdfInfo.summary.overview] +untitled = "ą“’ą“°ąµ ą“¶ąµ€ąµ¼ą“·ą“•ą“®ą“æą“²ąµą“²ą“¾ą“¤ąµą“¤ ą“Ŗąµą“°ą“®ą“¾ą“£ą“‚" +unknown = "ą“…ą“œąµą“žą“¾ą“¤ ą“°ą“šą“Æą“æą“¤ą“¾ą“µąµ" +text = "ą“‡ą“¤ąµ {{author}} ą“øąµƒą“·ąµą“Ÿą“æą“šąµą“š, {{title}} ą“Žą“Øąµą“Ø ą“¶ąµ€ąµ¼ą“·ą“•ą“®ąµą“³ąµą“³ {{pages}}-ą“Ŗąµ‡ą“œąµ PDF ą“†ą“£ąµ (PDF ą“Ŗą“¤ą“æą“Ŗąµą“Ŗąµ {{version}})." + +[getPdfInfo.error] +partial = "ą“šą“æą“² ą“«ą“Æą“²ąµą“•ąµ¾ ą“Ŗąµą“°ąµ‹ą“øą“øąµą“øąµ ą“šąµ†ą“Æąµą“Æą“¾ąµ» ą“•ą““ą“æą“žąµą“žą“æą“²ąµą“²." +unexpected = "ą“Ŗąµą“±ą“¤ąµą“¤ąµ†ą“Ÿąµą“•ąµą“•ąµą“Øąµą“Øą“¤ą“æą“Øą“æą“Ÿąµ† ą“…ą“Ŗąµą“°ą“¤ąµ€ą“•ąµą“·ą“æą“¤ ą“Ŗą“æą“¶ą“•ąµ." + +[getPdfInfo.status] +complete = "ą“Ŗąµą“±ą“¤ąµą“¤ąµ†ą“Ÿąµą“•ąµą“•ąµ½ ą“Ŗąµ‚ąµ¼ą“¤ąµą“¤ą“æą“Æą“¾ą“Æą“æ" [extractPage] tags = "ą“µąµ‡ąµ¼ą“¤ą“æą“°ą“æą“šąµą“šąµ†ą“Ÿąµą“•ąµą“•ąµą“•" @@ -3380,7 +3481,7 @@ certHint = "ą“•ą“øąµą“±ąµą“±ą“‚ ą“Ÿąµą“°ą“øąµą“±ąµą“±ąµ ą“øąµ‹ą““ąµā€Œą“ø title = "ą“øąµą“„ą“æą“°ąµ€ą“•ą“°ą“£ ą“øąµ†ą“±ąµą“±ą“æą“™ąµą“™ąµą“•ąµ¾" [replaceColor] -tags = "Replace Colour,Page operations,Back end,server side" +tags = "ą“Øą“æą“±ą“‚ ą“Ŗą“•ą“°ąµą“•,ą“Ŗąµ‡ą“œąµ ą“Ŗąµą“°ą“µąµ¼ą“¤ąµą“¤ą“Øą“™ąµą“™ąµ¾,ą“¬ą“¾ą“•ąµą“•ąµā€Œą“Žąµ»ą“”ąµ,ą“øąµ†ąµ¼ą“µąµ¼-ą“øąµˆą“”ąµ" [replaceColor.labels] settings = "ą“øąµ†ą“±ąµą“±ą“æą“™ąµą“™ąµą“•ąµ¾" @@ -3438,6 +3539,9 @@ signinTitle = "ą“¦ą“Æą“µą“¾ą“Æą“æ ą“øąµˆąµ» ą“‡ąµ» ą“šąµ†ą“Æąµą“Æąµą“•" ssoSignIn = "ą“øą“æą“‚ą“—ą“æąµ¾ ą“øąµˆąµ»-ą““ąµŗ ą“µą““ą“æ ą“²ąµ‹ą“—ą“æąµ» ą“šąµ†ą“Æąµą“Æąµą“•" oAuth2AutoCreateDisabled = "OAUTH2 ą““ą“Ÿąµą“Ÿąµ‹-ą“•ąµą“°ą“æą“Æąµ‡ą“±ąµą“±ąµ ą“Æąµ‚ą“øąµ¼ ą“Ŗąµą“°ą“µąµ¼ą“¤ąµą“¤ą“Øą“°ą“¹ą“æą“¤ą“®ą“¾ą“•ąµą“•ą“æ" oAuth2AdminBlockedUser = "ą“°ą“œą“æą“øąµą“±ąµą“±ąµ¼ ą“šąµ†ą“Æąµą“Æą“¾ą“¤ąµą“¤ ą“‰ą“Ŗą“Æąµ‹ą“•ąµą“¤ą“¾ą“•ąµą“•ą“³ąµą“Ÿąµ† ą“°ą“œą“æą“øąµą“Ÿąµą“°ąµ‡ą“·ą“Øąµ‹ ą“²ąµ‹ą“—ą“æąµ» ą“šąµ†ą“Æąµą“Æąµą“Øąµą“Øą“¤ąµ‹ ą“Øą“æą“²ą“µą“æąµ½ ą“¤ą“Ÿą“žąµą“žą“æą“°ą“æą“•ąµą“•ąµą“Øąµą“Øąµ. ą“¦ą“Æą“µą“¾ą“Æą“æ ą“…ą“”ąµą“®ą“æą“Øą“æą“øąµą“Ÿąµą“°ąµ‡ą“±ąµą“±ą“±ąµą“®ą“¾ą“Æą“æ ą“¬ą“Øąµą“§ą“Ŗąµą“Ŗąµ†ą“Ÿąµą“•." +oAuth2RequiresLicense = "OAuth/SSO ą“²ąµ‹ą“—ą“æą“Øą“æą“Øąµ ą“’ą“°ąµ ą“Ŗąµ†ą“Æąµą“”ąµ ą“²ąµˆą“øąµ»ą“øąµ (Server ą“…ą“²ąµą“²ąµ†ą“™ąµą“•ą“æąµ½ Enterprise) ą“†ą“µą“¶ąµą“Æą“®ą“¾ą“£ąµ. ą“Øą“æą“™ąµą“™ą“³ąµą“Ÿąµ† ą“Ŗąµą“²ą“¾ąµ» ą“…ą“Ŗąµā€Œą“—ąµą“°ąµ‡ą“”ąµ ą“šąµ†ą“Æąµą“Æą“¾ąµ» ą“…ą“”ąµą“®ą“æą“Øą“æą“øąµą“Ÿąµą“°ąµ‡ą“±ąµą“±ą“±ąµą“®ą“¾ą“Æą“æ ą“¬ą“Øąµą“§ą“Ŗąµą“Ŗąµ†ą“Ÿąµą“•." +saml2RequiresLicense = "SAML ą“²ąµ‹ą“—ą“æą“Øą“æą“Øąµ ą“’ą“°ąµ ą“Ŗąµ†ą“Æąµą“”ąµ ą“²ąµˆą“øąµ»ą“øąµ (Server ą“…ą“²ąµą“²ąµ†ą“™ąµą“•ą“æąµ½ Enterprise) ą“†ą“µą“¶ąµą“Æą“®ą“¾ą“£ąµ. ą“Øą“æą“™ąµą“™ą“³ąµą“Ÿąµ† ą“Ŗąµą“²ą“¾ąµ» ą“…ą“Ŗąµā€Œą“—ąµą“°ąµ‡ą“”ąµ ą“šąµ†ą“Æąµą“Æą“¾ąµ» ą“…ą“”ąµą“®ą“æą“Øą“æą“øąµą“Ÿąµą“°ąµ‡ą“±ąµą“±ą“±ąµą“®ą“¾ą“Æą“æ ą“¬ą“Øąµą“§ą“Ŗąµą“Ŗąµ†ą“Ÿąµą“•." +maxUsersReached = "ą“Øą“æą“²ą“µą“æą“²ąµą“³ąµą“³ ą“²ąµˆą“øąµ»ą“øą“æą“²ąµ† ą“Ŗą“°ą“®ą“¾ą“µą“§ą“æ ą“‰ą“Ŗą“Æąµ‹ą“•ąµą“¤ą“¾ą“•ąµą“•ąµ¾ ą“Žą“¤ąµą“¤ą“æą“šąµą“šąµ‡ąµ¼ą“Øąµą“Øąµ. ą“Øą“æą“™ąµą“™ą“³ąµą“Ÿąµ† ą“Ŗąµą“²ą“¾ąµ» ą“…ą“Ŗąµā€Œą“—ąµą“°ąµ‡ą“”ąµ ą“šąµ†ą“Æąµą“Æąµą“•ą“Æąµ‹ ą“•ąµ‚ą“Ÿąµą“¤ąµ½ ą“øąµ€ą“±ąµą“±ąµą“•ąµ¾ ą“šąµ‡ąµ¼ą“•ąµą“•ąµą“•ą“Æąµ‹ ą“šąµ†ą“Æąµą“Æą“¾ąµ» ą“…ą“”ąµą“®ą“æą“Øą“æą“øąµą“Ÿąµą“°ąµ‡ą“±ąµą“±ą“±ąµą“®ą“¾ą“Æą“æ ą“¬ą“Øąµą“§ą“Ŗąµą“Ŗąµ†ą“Ÿąµą“•." oauth2RequestNotFound = "ą“…ą“‚ą“—ąµ€ą“•ą“¾ą“° ą“…ą“­ąµą“Æąµ¼ą“¤ąµą“„ą“Ø ą“•ą“£ąµą“Ÿąµ†ą“¤ąµą“¤ą“æą“Æą“æą“²ąµą“²" oauth2InvalidUserInfoResponse = "ą“…ą“øą“¾ą“§ąµą“µą“¾ą“Æ ą“‰ą“Ŗą“Æąµ‹ą“•ąµą“¤ąµƒ ą“µą“æą“µą“° ą“Ŗąµą“°ą“¤ą“æą“•ą“°ą“£ą“‚" oauth2invalidRequest = "ą“…ą“øą“¾ą“§ąµą“µą“¾ą“Æ ą“…ą“­ąµą“Æąµ¼ą“¤ąµą“„ą“Ø" @@ -3771,7 +3875,7 @@ version = "ą“Øą“æą“²ą“µą“æą“²ąµ† ą“±ą“æą“²ąµ€ą“øąµ" title = "API ą“”ąµ‹ą“•ąµą“Æąµą“®ąµ†ą“Øąµą“±ąµ‡ą“·ąµ»" header = "API ą“”ąµ‹ą“•ąµą“Æąµą“®ąµ†ą“Øąµą“±ąµ‡ą“·ąµ»" desc = "Stirling PDF API ą“Žąµ»ą“”ąµą“Ŗąµ‹ą“Æą“æą“Øąµą“±ąµą“•ąµ¾ ą“•ą“¾ą“£ąµą“•ą“Æąµą“‚ ą“Ŗą“°ą“æą“¶ąµ‹ą“§ą“æą“•ąµą“•ąµą“•ą“Æąµą“‚ ą“šąµ†ą“Æąµą“Æąµą“•" -tags = "api,documentation,swagger,endpoints,development" +tags = "api,ą“”ąµ‹ą“•ąµą“Æąµą“®ąµ†ą“Øąµą“±ąµ‡ą“·ąµ»,swagger,ą“Žąµ»ą“”ąµą“Ŗąµ‹ą“Æą“æą“Øąµą“±ąµą“•ąµ¾,ą“µą“æą“•ą“øą“Øą“‚" [cookieBanner.popUp] title = "ą“žą“™ąµą“™ąµ¾ ą“•ąµą“•ąµą“•ą“æą“•ąµ¾ ą“Žą“™ąµą“™ą“Øąµ† ą“‰ą“Ŗą“Æąµ‹ą“—ą“æą“•ąµą“•ąµą“Øąµą“Øąµ" @@ -3846,14 +3950,17 @@ fitToWidth = "ą“µąµ€ą“¤ą“æą“•ąµą“•ąµ ą“’ą“¤ąµą“¤ą“¾ą“•ąµą“•ąµą“•" actualSize = "ą“Æą“„ą“¾ąµ¼ą“¤ąµą“„ ą“µą“²ą“æą“Ŗąµą“Ŗą“‚" [viewer] +cannotPreviewFile = "ą“«ą“Æąµ½ ą“Ŗąµą“°ą“æą“µąµą“Æąµ‚ ą“šąµ†ą“Æąµą“Æą“¾ąµ» ą“•ą““ą“æą“Æą“æą“²ąµą“²" +dualPageView = "ą“°ą“£ąµą“Ÿąµą“Ŗąµ‡ą“œąµ ą“¦ąµƒą“¶ąµą“Æą“‚" firstPage = "ą“†ą“¦ąµą“Æ ą“Ŗąµ‡ą“œąµ" lastPage = "ą“…ą“µą“øą“¾ą“Ø ą“Ŗąµ‡ą“œąµ" -previousPage = "ą“®ąµąµ»ą“Ŗą“¤ąµą“¤ąµ† ą“Ŗąµ‡ą“œąµ" nextPage = "ą“…ą“Ÿąµą“¤ąµą“¤ ą“Ŗąµ‡ą“œąµ" +onlyPdfSupported = "ą“µąµą“Æąµ‚ą“µą“±ą“æą“Øąµ PDF ą“«ą“Æą“²ąµą“•ąµ¾ ą“®ą“¾ą“¤ąµą“°ą“‚ ą“Ŗą“æą“Øąµą“¤ąµą“£ą“Æąµą“•ąµą“•ą“¾ą“‚. ą“ˆ ą“«ą“Æąµ½ ą“µąµ‡ą“±ąµ† ą“’ą“°ąµ ą“«ąµ‹ąµ¼ą“®ą“¾ą“±ąµą“±ą“¾ą“£ąµ†ą“Øąµą“Øąµ ą“¤ąµ‹ą“Øąµą“Øąµą“Øąµą“Øąµ." +previousPage = "ą“®ąµąµ»ą“Ŗą“¤ąµą“¤ąµ† ą“Ŗąµ‡ą“œąµ" +singlePageView = "ą“’ą“±ąµą“± ą“Ŗąµ‡ą“œąµ ą“¦ąµƒą“¶ąµą“Æą“‚" +unknownFile = "ą“…ą“Ŗą“°ą“æą“šą“æą“¤ą“®ą“¾ą“Æ ą“«ą“Æąµ½" zoomIn = "ą“øąµ‚ą“‚ ą“‡ąµ»" zoomOut = "ą“øąµ‚ą“‚ ą“”ą“Ÿąµą“Ÿąµ" -singlePageView = "ą“’ą“±ąµą“± ą“Ŗąµ‡ą“œąµ ą“¦ąµƒą“¶ąµą“Æą“‚" -dualPageView = "ą“°ą“£ąµą“Ÿąµą“Ŗąµ‡ą“œąµ ą“¦ąµƒą“¶ąµą“Æą“‚" [rightRail] closeSelected = "ą“¤ą“æą“°ą“žąµą“žąµ†ą“Ÿąµą“¤ąµą“¤ ą“«ą“Æą“²ąµą“•ąµ¾ ą“…ą“Ÿą“Æąµā€Œą“•ąµą“•ąµą“•" @@ -3877,6 +3984,7 @@ toggleSidebar = "ą“øąµˆą“”ąµą“¬ą“¾ąµ¼ ą“®ą“¾ą“±ąµą“±ąµą“•" exportSelected = "ą“¤ą“æą“°ą“žąµą“žąµ†ą“Ÿąµą“¤ąµą“¤ ą“Ŗąµ‡ą“œąµą“•ąµ¾ ą“Žą“•ąµą“øąµą“Ŗąµ‹ąµ¼ą“Ÿąµą“Ÿąµ ą“šąµ†ą“Æąµą“Æąµą“•" toggleAnnotations = "ą“…ą“Øąµ‹ą“Ÿąµą“Ÿąµ‡ą“·ąµ» ą“¦ąµƒą“¶ąµą“Æą“®ą“¾ą“Øą“‚ ą“®ą“¾ą“±ąµą“±ąµą“•" annotationMode = "ą“…ą“Øąµ‹ą“Ÿąµą“Ÿąµ‡ą“·ąµ» ą“®ąµ‹ą“”ąµ ą“®ą“¾ą“±ąµą“±ąµą“•" +print = "PDF ą“…ą“šąµą“šą“Ÿą“æą“•ąµą“•ąµą“•" draw = "ą“µą“°ą“Æąµą“•ąµą“•ąµą“•" save = "ą“øą“‚ą“°ą“•ąµą“·ą“æą“•ąµą“•ąµą“•" saveChanges = "ą“®ą“¾ą“±ąµą“±ą“™ąµą“™ąµ¾ ą“øą“‚ą“°ą“•ąµą“·ą“æą“•ąµą“•ąµą“•" @@ -4231,7 +4339,7 @@ label = "ą“Ŗąµą“°ąµŠą“µąµˆą“”ąµ¼" description = "ą““ą“¤ą“Øąµą“±ą“æą“•ąµą“•ąµ‡ą“·ą“Øą“¾ą“Æą“æ ą“‰ą“Ŗą“Æąµ‹ą“—ą“æą“•ąµą“•ąµą“Øąµą“Ø OAuth2 ą“Ŗąµą“°ąµŠą“µąµˆą“”ąµ¼" [admin.settings.connections.oauth2.issuer] -label = "Issuer URL" +label = "ą“‡ą“·ąµą“Æąµ‚ą“µąµ¼ URL" description = "OAuth2 ą“Ŗąµą“°ąµŠą“µąµˆą“”ą“±ą“æą“Øąµą“±ąµ† issuer URL" [admin.settings.connections.oauth2.clientId] @@ -4255,7 +4363,7 @@ label = "ą“°ą“œą“æą“øąµā€Œą“Ÿąµą“°ąµ‡ą“·ąµ» ą“¤ą“Ÿą“Æąµą“•" description = "OAuth2 ą“µą““ą“æ ą“Ŗąµą“¤ą“æą“Æ ą“‰ą“Ŗą“Æąµ‹ą“•ąµą“¤ąµƒ ą“°ą“œą“æą“øąµā€Œą“Ÿąµą“°ąµ‡ą“·ąµ» ą“¤ą“Ÿą“Æąµą“•" [admin.settings.connections.oauth2.scopes] -label = "OAuth2 Scopes" +label = "OAuth2 ą“øąµą“•ąµ‹ą“Ŗąµą“Ŗąµą“•ąµ¾" description = "OAuth2 ą“øąµą“•ąµ‹ą“Ŗąµą“Ŗąµą“•ą“³ąµą“Ÿąµ† ą“•ąµ‹ą“® ą“‰ą“Ŗą“Æąµ‹ą“—ą“æą“šąµą“šąµ ą“µąµ‡ąµ¼ą“¤ą“æą“°ą“æą“šąµą“š ą“Ŗą“Ÿąµą“Ÿą“æą“• (ą“‰ą“¦ą“¾., openid, profile, email)" [admin.settings.connections.saml2] @@ -4494,6 +4602,7 @@ description = "Impressum-ą“²ąµ‡ą“•ąµą“•ąµ URL ą“…ą“²ąµą“²ąµ†ą“™ąµą“•ą“æąµ½ title = "ą“Ŗąµą“°ąµ€ą“®ą“æą“Æą“‚ & ą“Žą“Øąµą“±ąµ¼ą“Ŗąµą“°ąµˆą“øąµ" description = "ą“Øą“æą“™ąµą“™ą“³ąµą“Ÿąµ† ą“Ŗąµą“°ąµ€ą“®ą“æą“Æą“‚ ą“…ą“²ąµą“²ąµ†ą“™ąµą“•ą“æąµ½ ą“Žą“Øąµą“±ąµ¼ą“Ŗąµą“°ąµˆą“øąµ ą“²ąµˆą“øąµ»ą“øąµ ą“•ąµ€ ą“•ąµą“°ą“®ąµ€ą“•ą“°ą“æą“•ąµą“•ąµą“•." license = "ą“²ąµˆą“øąµ»ą“øąµ ą“•ąµ‹ąµŗą“«ą“æą“—ą“±ąµ‡ą“·ąµ»" +noInput = "ą“¦ą“Æą“µą“¾ą“Æą“æ ą“’ą“°ąµ ą“²ąµˆą“øą“Øąµą“±ąµā€Œą“øąµ ą“•ąµ€ ą“…ą“²ąµą“²ąµ†ą“™ąµą“•ą“æąµ½ ą“«ą“Æąµ½ ą“Øąµ½ą“•ąµą“•" [admin.settings.premium.licenseKey] toggle = "ą“²ąµˆą“øąµ»ą“øąµ ą“•ąµ€ ą“…ą“²ąµą“²ąµ†ą“™ąµą“•ą“æąµ½ ą“øąµ¼ą“Ÿąµą“Ÿą“æą“«ą“æą“•ąµą“•ą“±ąµą“±ąµ ą“«ą“Æąµ½ ą“‰ą“£ąµą“Ÿąµ‹?" @@ -4511,6 +4620,25 @@ line1 = "ą“Øą“æą“²ą“µą“æą“²ąµ† ą“²ąµˆą“øąµ»ą“øąµ ą“•ąµ€ ą““ą“µąµ¼ą“±ąµˆą“±ąµ line2 = "ą“Øą“æą“™ąµą“™ąµ¾ ą“®ą“±ąµą“±ąµ†ą“µą“æą“Ÿąµ†ą“Æąµ†ą“™ąµą“•ą“æą“²ąµą“‚ ą“¬ą“¾ą“•ąµą“•ą“Ŗąµą“Ŗąµ ą“Žą“Ÿąµą“¤ąµą“¤ą“æą“Ÿąµą“Ÿą“æą“²ąµą“²ąµ†ą“™ąµą“•ą“æąµ½ ą“Øą“æą“™ąµą“™ą“³ąµą“Ÿąµ† ą“Ŗą““ą“Æ ą“²ąµˆą“øąµ»ą“øąµ ą“øąµą“„ą“æą“°ą“®ą“¾ą“Æą“æ ą“Øą“·ąµą“Ÿą“Ŗąµą“Ŗąµ†ą“Ÿąµą“‚." line3 = "ą“Ŗąµą“°ą“§ą“¾ą“Øą“Ŗąµą“Ŗąµ†ą“Ÿąµą“Ÿą“¤ąµ: ą“²ąµˆą“øąµ»ą“øąµ ą“•ąµ€ą“•ąµ¾ ą“øąµą“µą“•ą“¾ą“°ąµą“Æą“µąµą“‚ ą“øąµą“°ą“•ąµą“·ą“æą“¤ą“µąµą“®ą“¾ą“•ąµą“•ą“æ ą“øąµ‚ą“•ąµą“·ą“æą“•ąµą“•ąµą“•. ą“’ą“°ą“æą“•ąµą“•ą“²ąµą“‚ ą“…ą“µ ą“ŖąµŠą“¤ąµ ą“µąµ‡ą“¦ą“æą“Æą“æąµ½ ą“Ŗą“™ąµą“•ąµą“µąµ†ą“•ąµą“•ą“°ąµą“¤ąµ." +[admin.settings.premium.inputMethod] +text = "ą“²ąµˆą“øąµ»ą“øąµ ą“•ąµ€" +file = "ą“øąµ¼ą“Ÿąµą“Ÿą“æą“«ą“æą“•ąµą“•ą“±ąµą“±ąµ ą“«ą“Æąµ½" + +[admin.settings.premium.file] +label = "ą“²ąµˆą“øąµ»ą“øąµ ą“øąµ¼ą“Ÿąµą“Ÿą“æą“«ą“æą“•ąµą“•ą“±ąµą“±ąµ ą“«ą“Æąµ½" +description = "ą“Øą“æą“™ąµą“™ą“³ąµą“Ÿąµ† ą““ą“«ąµą“²ąµˆąµ» ą“µą“¾ą“™ąµą“™ą“²ąµą“•ą“³ą“æąµ½ ą“Øą“æą“Øąµą“Øąµą“³ąµą“³ .lic ą“…ą“²ąµą“²ąµ†ą“™ąµą“•ą“æąµ½ .cert ą“²ąµˆą“øąµ»ą“øąµ ą“«ą“Æąµ½ ą“…ą“Ŗąµā€Œą“²ąµ‹ą“”ąµ ą“šąµ†ą“Æąµą“Æąµą“•" +choose = "ą“²ąµˆą“øąµ»ą“øąµ ą“«ą“Æąµ½ ą“¤ą“æą“°ą“žąµą“žąµ†ą“Ÿąµą“•ąµą“•ąµą“•" +selected = "ą“¤ą“æą“°ą“žąµą“žąµ†ą“Ÿąµą“•ąµą“•ą“æą“Æą“¤ąµ: {{filename}} ({{size}})" +successMessage = "ą“²ąµˆą“øąµ»ą“øąµ ą“«ą“Æąµ½ ą“…ą“Ŗąµā€Œą“²ąµ‹ą“”ąµ ą“šąµ†ą“Æąµą“¤ąµ ą“µą“æą“œą“Æą“•ą“°ą“®ą“¾ą“Æą“æ ą“øą“œąµ€ą“µą“®ą“¾ą“•ąµą“•ą“æą“Æą“æą“°ą“æą“•ąµą“•ąµą“Øąµą“Øąµ. ą“±ąµ€ą“øąµą“±ąµą“±ą“¾ąµ¼ą“Ÿąµą“Ÿąµ ą“†ą“µą“¶ąµą“Æą“®ą“æą“²ąµą“²." + +[admin.settings.premium.currentLicense] +title = "ą“øą“œąµ€ą“µ ą“²ąµˆą“øąµ»ą“øąµ" +file = "ą“‰ą“±ą“µą“æą“Ÿą“‚: ą“²ąµˆą“øąµ»ą“øąµ ą“«ą“Æąµ½ ({{path}})" +key = "ą“‰ą“±ą“µą“æą“Ÿą“‚: ą“²ąµˆą“øąµ»ą“øąµ ą“•ąµ€" +type = "ą“¤ą“°ą“‚: {{type}}" +noInput = "ą“¦ą“Æą“µą“¾ą“Æą“æ ą“’ą“°ąµ ą“²ąµˆą“øąµ»ą“øąµ ą“•ąµ€ ą“Øąµ½ą“•ąµą“•ą“Æąµ‹ ą“’ą“°ąµ ą“øąµ¼ą“Ÿąµą“Ÿą“æą“«ą“æą“•ąµą“•ą“±ąµą“±ąµ ą“«ą“Æąµ½ ą“…ą“Ŗąµā€Œą“²ąµ‹ą“”ąµ ą“šąµ†ą“Æąµą“Æąµą“•ą“Æąµ‹ ą“šąµ†ą“Æąµą“Æąµą“•" +success = "ą“µą“æą“œą“Æą“‚" + [admin.settings.premium.enabled] label = "ą“Ŗą±ą°°ą±€ą“®ą“æą“Æą“‚ ą“øą“µą“æą“¶ąµ‡ą“·ą“¤ą“•ąµ¾ ą“Ŗąµą“°ą“¾ą“Ŗąµą“¤ą“®ą“¾ą“•ąµą“•ąµą“•" description = "ą“Ŗąµą“°ąµ‹/ą“Žą“Øąµą“±ąµ¼ą“Ŗąµą“°ąµˆą“øąµ ą“øą“µą“æą“¶ąµ‡ą“·ą“¤ą“•ąµ¾ą“•ąµą“•ą“¾ą“Æą“æ ą“²ąµˆą“øąµ»ą“øąµ ą“•ąµ€ ą“Ŗą“°ą“æą“¶ąµ‹ą“§ą“Øą“•ąµ¾ ą“Ŗąµą“°ą“¾ą“Ŗąµą“¤ą“®ą“¾ą“•ąµą“•ąµą“•" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} ą“¤ą“æą“°ą“žąµą“žąµ†ą“Ÿąµą“•ąµą“•ą“Ŗąµą“Ŗąµ†ą“Ÿ download = "ą“”ąµ—ąµŗą“²ąµ‹ą“”ąµ" delete = "ą“‡ą“²ąµą“²ą“¾ą“¤ą“¾ą“•ąµą“•ąµą“•" unsupported = "ą“Ŗą“æą“Øąµą“¤ąµą“£ą“Æą“æą“²ąµą“²" +active = "ą“øą“œąµ€ą“µą“‚" addToUpload = "ą“…ą“Ŗąµā€Œą“²ąµ‹ą“”ą“æą“²ąµ‡ą“•ąµą“•ąµ ą“šąµ‡ąµ¼ą“•ąµą“•ąµą“•" +closeFile = "ą“«ą“Æąµ½ ą“…ą“Ÿą“Æąµą“•ąµą“•ąµą“•" deleteAll = "ą“Žą“²ąµą“²ą“¾ą“‚ ą“‡ą“²ąµą“²ą“¾ą“¤ą“¾ą“•ąµą“•ąµą“•" loadingFiles = "ą“«ą“Æą“²ąµą“•ąµ¾ ą“²ąµ‹ą“”ąµą“šąµ†ą“Æąµą“Æąµą“Øąµą“Øąµ..." noFiles = "ą“«ą“Æą“²ąµą“•ą“³ąµŠą“Øąµą“Øąµą“‚ ą“²ą“­ąµą“Æą“®ą“²ąµą“²" @@ -5132,7 +5262,7 @@ upgrade = "ą“‡ą“Ŗąµą“Ŗąµ‹ąµ¾ ą“…ą“Ŗąµā€Œą“—ąµą“°ąµ‡ą“”ąµ ą“šąµ†ą“Æąµą“Æąµ freeTitle = "ą“øąµ†ąµ¼ą“µąµ¼ ą“²ąµˆą“øąµ»ą“øąµ" overLimitTitle = "ą“øąµ†ąµ¼ą“µąµ¼ ą“²ąµˆą“øąµ»ą“øąµ ą“†ą“µą“¶ąµą“Æą“®ą“¾ą“£ąµ" overLimitBody = "ą“žą“™ąµą“™ą“³ąµą“Ÿąµ† ą“²ąµˆą“øąµ»ą“øą“æą“‚ą“—ąµ ą““ą“°ąµ‹ ą“øąµ†ąµ¼ą“µąµ¼ą“•ąµą“•ąµą“‚ ą“Ŗą“°ą“®ą“¾ą“µą“§ą“æ {{freeTierLimit}} ą“‰ą“Ŗą“Æąµ‹ą“•ąµą“¤ą“¾ą“•ąµą“•ą“³ąµ† ą“øąµ—ą“œą“Øąµą“Æą“®ą“¾ą“Æą“æ ą“…ą“Øąµą“µą“¦ą“æą“•ąµą“•ąµą“Øąµą“Øąµ. ą“Øą“æą“™ąµą“™ą“³ąµą“•ąµą“•ąµ {{overLimitUserCopy}} Stirling ą“‰ą“Ŗą“Æąµ‹ą“•ąµą“¤ą“¾ą“•ąµą“•ą“³ąµą“£ąµą“Ÿąµ. ą“¤ą“Ÿą“øąµą“øą“®ą“æą“²ąµą“²ą“¾ą“¤ąµ† ą“¤ąµą“Ÿą“°ą“¾ąµ», Stirling Server ą“Ŗąµą“²ą“¾ą“Øą“æą“²ąµ‡ą“•ąµą“•ąµ ą“…ą“Ŗąµā€Œą“—ąµą“°ąµ‡ą“”ąµ ą“šąµ†ą“Æąµą“Æąµą“• - unlimited seats, PDF text editing, ą“Ŗąµ‚ąµ¼ą“£ąµą“£ ą“…ą“”ąµą“®ą“æąµ» ą“Øą“æą“Æą“Øąµą“¤ąµą“°ą“£ą“‚, $99/server/mo." -freeBody = "ą“žą“™ąµą“™ą“³ąµą“Ÿąµ† Open-Core ą“²ąµˆą“øąµ»ą“øą“æą“‚ą“—ąµ ą““ą“°ąµ‹ ą“øąµ†ąµ¼ą“µąµ¼ą“•ąµą“•ąµą“‚ ą“Ŗą“°ą“®ą“¾ą“µą“§ą“æ {{freeTierLimit}} ą“‰ą“Ŗą“Æąµ‹ą“•ąµą“¤ą“¾ą“•ąµą“•ą“³ąµ† ą“øąµ—ą“œą“Øąµą“Æą“®ą“¾ą“Æą“æ ą“…ą“Øąµą“µą“¦ą“æą“•ąµą“•ąµą“Øąµą“Øąµ. ą“¤ą“Ÿą“øąµą“øą“®ą“æą“²ąµą“²ą“¾ą“¤ąµ† ą“øąµą“•ąµ†ą“Æą“æąµ½ ą“šąµ†ą“Æąµą“Æą“¾ą“Øąµą“‚ ą“Ŗąµą“¤ą“æą“Æ PDF text editing tool ą“Øąµ ą“®ąµąµ»ą“•ą“¾ą“² ą“†ą“•ąµą“øą“øąµ ą“Øąµ‡ą“Ÿą“¾ą“Øąµą“‚, Stirling Server ą“Ŗąµą“²ą“¾ąµ» ą“žą“™ąµą“™ąµ¾ ą“¶ąµą“Ŗą“¾ąµ¼ą“¶ ą“šąµ†ą“Æąµą“Æąµą“Øąµą“Øąµ - ą“Ŗąµ‚ąµ¼ą“£ąµą“£ ą“Žą“”ą“æą“±ąµą“±ą“æą“‚ą“—ąµą“‚ unlimited seats ą“‰ą“‚ $99/server/mo." +freeBody = "ą“žą“™ąµą“™ą“³ąµą“Ÿąµ† Open-Core ą“²ąµˆą“øąµ»ą“øą“æą“‚ą“—ąµ ą““ą“°ąµ‹ ą“øąµ†ąµ¼ą“µąµ¼ą“•ąµą“•ąµą“‚ ą“Ŗą“°ą“®ą“¾ą“µą“§ą“æ {{freeTierLimit}} ą“‰ą“Ŗą“Æąµ‹ą“•ąµą“¤ą“¾ą“•ąµą“•ą“³ąµ† ą“øąµ—ą“œą“Øąµą“Æą“®ą“¾ą“Æą“æ ą“…ą“Øąµą“µą“¦ą“æą“•ąµą“•ąµą“Øąµą“Øąµ. ą“¤ą“Ÿą“øąµą“øą“®ą“æą“²ąµą“²ą“¾ą“¤ąµ† ą“øąµā€Œą“•ąµ†ą“Æą“æąµ½ ą“šąµ†ą“Æąµą“Æą“¾ąµ», ą“žą“™ąµą“™ąµ¾ Stirling Server ą“Ŗąµą“²ą“¾ąµ» ą“¶ąµą“Ŗą“¾ąµ¼ą“¶ ą“šąµ†ą“Æąµą“Æąµą“Øąµą“Øąµ - ą“Ŗą“°ą“æą“®ą“æą“¤ą“æą“Æą“æą“²ąµą“²ą“¾ą“¤ąµą“¤ ą“øąµ€ą“±ąµą“±ąµą“•ąµ¾ą“Æąµą“‚ SSO ą“Ŗą“æą“Øąµą“¤ąµą“£ą“Æąµą“‚ for $99/server/mo." [onboarding.desktopInstall] title = "ą“”ąµ—ąµŗą“²ąµ‹ą“”ąµ" @@ -5237,6 +5367,31 @@ error = "ą“‰ą“Ŗą“Æąµ‹ą“•ąµą“¤ą“¾ą“µą“æą“Øąµą“±ąµ† ą“Øą“æą“² ą“…ą“Ŗąµā€Œą“”ąµ‡ success = "ą“‰ą“Ŗą“Æąµ‹ą“•ąµą“¤ą“¾ą“µą“æą“Øąµ† ą“µą“æą“œą“Æą“•ą“°ą“®ą“¾ą“Æą“æ ą“‡ą“²ąµą“²ą“¾ą“¤ą“¾ą“•ąµą“•ą“æ" error = "ą“‰ą“Ŗą“Æąµ‹ą“•ąµą“¤ą“¾ą“µą“æą“Øąµ† ą“‡ą“²ąµą“²ą“¾ą“¤ą“¾ą“•ąµą“•ąµ½ ą“Ŗą“°ą“¾ą“œą“Æą“Ŗąµą“Ŗąµ†ą“Ÿąµą“Ÿąµ" +[workspace.people.changePassword] +action = "ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµ ą“®ą“¾ą“±ąµą“±ąµą“•" +title = "ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµ ą“®ą“¾ą“±ąµą“±ąµą“•" +subtitle = "ą“‡ą“¤ą“æą“Øą“¾ą“Æąµą“³ąµą“³ ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµ ą“Ŗąµą“¤ąµą“•ąµą“•ąµą“•" +newPassword = "ą“Ŗąµą“¤ą“æą“Æ ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµ" +confirmPassword = "ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµ ą“¶ą“°ą“æą“µąµ†ą“•ąµą“•ąµą“•" +placeholder = "ą“’ą“°ąµ ą“Ŗąµą“¤ą“æą“Æ ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµ ą“Øąµ½ą“•ąµą“•" +confirmPlaceholder = "ą“Ŗąµą“¤ą“æą“Æ ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµ ą“µąµ€ą“£ąµą“Ÿąµą“‚ ą“Øąµ½ą“•ąµą“•" +passwordRequired = "ą“¦ą“Æą“µą“¾ą“Æą“æ ą“’ą“°ąµ ą“Ŗąµą“¤ą“æą“Æ ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµ ą“Øąµ½ą“•ąµą“•" +passwordMismatch = "ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµą“•ąµ¾ ą“ŖąµŠą“°ąµą“¤ąµą“¤ą“Ŗąµą“Ŗąµ†ą“Ÿąµą“Øąµą“Øą“æą“²ąµą“²" +generateRandom = "ą“øąµą“°ą“•ąµą“·ą“æą“¤ ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµ ą“øąµƒą“·ąµą“Ÿą“æą“•ąµą“•ąµą“•" +generatedPreview = "ą“øąµƒą“·ąµą“Ÿą“æą“šąµą“š ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµ:" +copyTooltip = "ą“•ąµą“²ą“æą“Ŗąµą“Ŗąµą“¬ąµ‹ąµ¼ą“”ą“æą“²ąµ‡ą“Æąµą“•ąµą“•ąµ ą“Ŗą“•ąµ¼ą“¤ąµą“¤ąµą“•" +copiedToClipboard = "ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµ ą“•ąµą“²ą“æą“Ŗąµą“Ŗąµą“¬ąµ‹ąµ¼ą“”ą“æą“²ąµ‡ą“Æąµą“•ąµą“•ąµ ą“Ŗą“•ąµ¼ą“¤ąµą“¤ą“æ" +copyFailed = "ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµ ą“Ŗą“•ąµ¼ą“¤ąµą“¤ąµ½ ą“Ŗą“°ą“¾ą“œą“Æą“Ŗąµą“Ŗąµ†ą“Ÿąµą“Ÿąµ" +sendEmail = "ą“ˆ ą“®ą“¾ą“±ąµą“±ą“¤ąµą“¤ąµ†ą“•ąµą“•ąµą“±ą“æą“šąµą“šąµ ą“‰ą“Ŗą“Æąµ‹ą“•ąµą“¤ą“¾ą“µą“æą“Øąµ ą“‡ą“®ąµ†ą“Æą“æąµ½ ą“…ą“Æą“Æąµą“•ąµą“•ąµą“•" +includePassword = "ą“Ŗąµą“¤ą“æą“Æ ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµ ą“‡ą“®ąµ†ą“Æą“æą“²ą“æąµ½ ą“‰ąµ¾ą“Ŗąµą“Ŗąµ†ą“Ÿąµą“¤ąµą“¤ąµą“•" +forcePasswordChange = "ą“…ą“Ÿąµą“¤ąµą“¤ ą“²ąµ‹ą“—ą“æą“Øą“æąµ½ ą“‰ą“Ŗą“Æąµ‹ą“•ąµą“¤ą“¾ą“µą“æą“Øąµ† ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµ ą“®ą“¾ą“±ąµą“±ą“¾ąµ» ą“Øą“æąµ¼ą“¬ą“Øąµą“§ą“æą“•ąµą“•ąµą“•" +emailUnavailable = "ą“ˆ ą“‰ą“Ŗą“Æąµ‹ą“•ąµą“¤ą“¾ą“µą“æą“Øąµą“±ąµ† ą“‡ą“®ąµ†ą“Æą“æąµ½ ą“’ą“°ąµ ą“øą“¾ą“§ąµą“µą“¾ą“Æ ą“‡ą“®ąµ†ą“Æą“æąµ½ ą“µą“æą“²ą“¾ą“øą“®ą“²ąµą“². ą“…ą“±ą“æą“Æą“æą“Ŗąµą“Ŗąµą“•ąµ¾ ą“Ŗąµą“°ą“µąµ¼ą“¤ąµą“¤ą“Øą“°ą“¹ą“æą“¤ą“®ą“¾ą“£ąµ." +smtpDisabled = "ą“‡ą“®ąµ†ą“Æą“æąµ½ ą“…ą“±ą“æą“Æą“æą“Ŗąµą“Ŗąµą“•ąµ¾ą“•ąµą“•ą“¾ą“Æą“æ ą“•ąµą“°ą“®ąµ€ą“•ą“°ą“£ą“™ąµą“™ą“³ą“æąµ½ SMTP ą“øą“œąµ€ą“µą“®ą“¾ą“•ąµą“•ą“£ą“‚." +notifyOnly = "ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµ ą“‡ą“²ąµą“²ą“¾ą“¤ąµ† ą“’ą“°ąµ ą“‡ą“®ąµ†ą“Æą“æąµ½ ą“…ą“Æą“Æąµą“•ąµą“•ą“Ŗąµą“Ŗąµ†ą“Ÿąµą“‚; ą“…ą“”ąµą“®ą“æąµ» ą“…ą“¤ąµ ą“®ą“¾ą“±ąµą“±ą“æą“Æą“¤ą“¾ą“Æą“æ ą“‰ą“Ŗą“Æąµ‹ą“•ąµą“¤ą“¾ą“µą“æą“Øąµ† ą“…ą“±ą“æą“Æą“æą“•ąµą“•ąµą“‚." +submit = "ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµ ą“Ŗąµą“¤ąµą“•ąµą“•ąµą“•" +success = "ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµ ą“µą“æą“œą“Æą“•ą“°ą“®ą“¾ą“Æą“æ ą“Ŗąµą“¤ąµą“•ąµą“•ą“æ" +error = "ą“Ŗą“¾ą“øąµā€Œą“µąµ‡ą“”ąµ ą“Ŗąµą“¤ąµą“•ąµą“•ąµ½ ą“Ŗą“°ą“¾ą“œą“Æą“Ŗąµą“Ŗąµ†ą“Ÿąµą“Ÿąµ" + [workspace.people.emailInvite] tab = "ą“‡ą“®ąµ†ą“Æą“æąµ½ ą“•ąµą“·ą“£ą“‚" description = "ą“¤ą“¾ą““ąµ† ą“‡ą“®ąµ†ą“Æą“æą“²ąµą“•ąµ¾ ą“•ąµ‹ą“® ą“‰ą“Ŗą“Æąµ‹ą“—ą“æą“šąµą“šąµ ą“µąµ‡ąµ¼ą“¤ą“æą“°ą“æą“šąµą“šąµ ą“Ÿąµˆą“Ŗąµą“Ŗąµ ą“šąµ†ą“Æąµą“Æąµą“•ą“Æąµ‹ ą“Ŗą“¤ą“æą“•ąµą“•ąµą“•ą“Æąµ‹ ą“šąµ†ą“Æąµą“Æąµą“•. ą“‰ą“Ŗą“Æąµ‹ą“•ąµą“¤ą“¾ą“•ąµą“•ąµ¾ą“•ąµą“•ąµ ą“²ąµ‹ą“—ą“æąµ» ą“µą“æą“µą“°ą“™ąµą“™ąµ¾ ą“‡ą“®ąµ†ą“Æą“æą“²ą“æą“²ąµ‚ą“Ÿąµ† ą“²ą“­ą“æą“•ąµą“•ąµą“‚." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "ą“•ąµą“±ą“žąµą“žą“¤ąµ ą“’ą“°ąµ ą“‡ą“®ąµ†ą“Æą“æąµ½ ą“µą“æą“²ą“¾ą“øą“®ąµ†ą“™ąµą“•ą“æą“²ąµą“‚ ą“†ą“µą“¶ąµą“Æą“®ą“¾ą“£ąµ" submit = "ą“•ąµą“·ą“£ą“™ąµą“™ąµ¾ ą“…ą“Æą“Æąµą“•ąµą“•ąµą“•" success = "ą“‰ą“Ŗą“Æąµ‹ą“•ąµą“¤ą“¾ą“•ąµą“•ą“³ąµ† ą“µą“æą“œą“Æą“•ą“°ą“®ą“¾ą“Æą“æ ą“•ąµą“·ą“£ą“æą“šąµą“šąµ" -partialSuccess = "ą“šą“æą“² ą“•ąµą“·ą“£ą“™ąµą“™ąµ¾ ą“Ŗą“°ą“¾ą“œą“Æą“Ŗąµą“Ŗąµ†ą“Ÿąµą“Ÿąµ" +partialFailure = "ą“šą“æą“² ą“•ąµą“·ą“£ą“™ąµą“™ąµ¾ ą“Ŗą“°ą“¾ą“œą“Æą“Ŗąµą“Ŗąµ†ą“Ÿąµą“Ÿąµ" allFailed = "ą“‰ą“Ŗą“Æąµ‹ą“•ąµą“¤ą“¾ą“•ąµą“•ą“³ąµ† ą“•ąµą“·ą“£ą“æą“•ąµą“•ąµ½ ą“Ŗą“°ą“¾ą“œą“Æą“Ŗąµą“Ŗąµ†ą“Ÿąµą“Ÿąµ" error = "ą“•ąµą“·ą“£ą“™ąµą“™ąµ¾ ą“…ą“Æą“Æąµą“•ąµą“•ąµ½ ą“Ŗą“°ą“¾ą“œą“Æą“Ŗąµą“Ŗąµ†ą“Ÿąµą“Ÿąµ" @@ -5770,6 +5925,7 @@ subtitle = "ą“Øą“æą“™ąµą“™ą“³ąµą“Ÿąµ† Stirling ą“…ą“•ąµą“•ąµ—ą“£ąµą“Ÿą“æą“² [setup.selfhosted] title = "ą“øąµ†ąµ¼ą“µą“±ą“æąµ½ ą“øąµˆąµ» ą“‡ąµ» ą“šąµ†ą“Æąµą“Æąµą“•" subtitle = "ą“Øą“æą“™ąµą“™ą“³ąµą“Ÿąµ† ą“øąµ†ąµ¼ą“µąµ¼ ą“•ąµą“°ąµ†ą“”ąµ»ą“·ąµą“Æą“²ąµą“•ąµ¾ ą“Øąµ½ą“•ąµą“•" +link = "ą“…ą“²ąµą“²ąµ†ą“™ąµą“•ą“æąµ½ ą“øąµą“µą“Æą“‚-ą“¹ąµ‹ą“øąµą“±ąµą“±ąµą“šąµ†ą“Æąµą“¤ ą“…ą“•ąµą“•ąµ—ą“£ąµą“Ÿąµą“®ą“¾ą“Æą“æ ą“¬ą“Øąµą“§ą“æą“Ŗąµą“Ŗą“æą“•ąµą“•ąµą“•" [setup.server] title = "ą“øąµ†ąµ¼ą“µą“±ą“æą“²ąµ‡ą“•ąµą“•ąµ ą“•ą“£ą“•ąµą“±ąµą“±ąµą“šąµ†ą“Æąµą“Æąµą“•" @@ -5788,6 +5944,14 @@ description = "ą“Øą“æą“™ąµą“™ą“³ąµą“Ÿąµ† ą“øąµ†ąµ½ą“«ąµ-ą“¹ąµ‹ą“øąµą“±ąµą“± emptyUrl = "ą“¦ą“Æą“µą“¾ą“Æą“æ ą“’ą“°ąµ ą“øąµ†ąµ¼ą“µąµ¼ URL ą“Øąµ½ą“•ąµą“•" unreachable = "ą“øąµ†ąµ¼ą“µą“±ąµą“®ą“¾ą“Æą“æ ą“¬ą“Øąµą“§ą“Ŗąµą“Ŗąµ†ą“Ÿą“¾ąµ» ą“•ą““ą“æą“žąµą“žą“æą“²ąµą“²" testFailed = "ą“•ą“£ą“•ąµą“·ąµ» ą“Ÿąµ†ą“øąµą“±ąµą“±ąµ ą“Ŗą“°ą“¾ą“œą“Æą“Ŗąµą“Ŗąµ†ą“Ÿąµą“Ÿąµ" +configFetch = "ą“øąµ¼ą“µąµ¼ ą“•ąµ‹ąµŗą“«ą“æą“—ą“±ąµ‡ą“·ąµ» ą“²ą“­ąµą“Æą“®ą“¾ą“•ąµą“•ąµ½ ą“Ŗą“°ą“¾ą“œą“Æą“Ŗąµą“Ŗąµ†ą“Ÿąµą“Ÿąµ. ą“¦ą“Æą“µą“¾ą“Æą“æ URL ą“Ŗą“°ą“æą“¶ąµ‹ą“§ą“æą“šąµą“šąµ ą“µąµ€ą“£ąµą“Ÿąµą“‚ ą“¶ąµą“°ą“®ą“æą“•ąµą“•ąµą“•." + +[setup.server.error.securityDisabled] +title = "ą“²ąµ‹ą“—ą“æąµ» ą“øą“œąµ€ą“µą“®ą“¾ą“•ąµą“•ą“æą“Æą“æą“Ÿąµą“Ÿą“æą“²ąµą“²" +body = "ą“ˆ ą“øąµ¼ą“µą“±ą“æąµ½ ą“²ąµ‹ą“—ą“æąµ» ą“øą“œąµ€ą“µą“®ą“¾ą“•ąµą“•ą“æą“Æą“æą“Ÿąµą“Ÿą“æą“²ąµą“². ą“ˆ ą“øąµ¼ą“µą“±ąµą“®ą“¾ą“Æą“æ ą“¬ą“Øąµą“§ą“Ŗąµą“Ŗąµ†ą“Ÿą“¾ąµ», ą““ą“¤ą“Øąµą“±ą“æą“•ąµą“•ąµ‡ą“·ąµ» ą“øą“œąµ€ą“µą“®ą“¾ą“•ąµą“•ą“£ą“‚:" +step1 = "ą“Øą“æą“™ąµą“™ą“³ąµą“Ÿąµ† ą“Ŗą“°ą“æą“øąµą“„ą“æą“¤ą“æą“Æą“æąµ½ DOCKER_ENABLE_SECURITY=true ą“†ą“Æą“æ ą“•ąµą“°ą“®ąµ€ą“•ą“°ą“æą“•ąµą“•ąµą“•" +step2 = "ą“…ą“²ąµą“²ąµ†ą“™ąµą“•ą“æąµ½ settings.yml ąµ½ security.enableLogin=true ą“†ą“Æą“æ ą“•ąµą“°ą“®ąµ€ą“•ą“°ą“æą“•ąµą“•ąµą“•" +step3 = "ą“øąµ¼ą“µąµ¼ ą“Ŗąµą“Øą“°ą“¾ą“°ą“‚ą“­ą“æą“•ąµą“•ąµą“•" [setup.login] title = "ą“øąµˆąµ» ą“‡ąµ»" @@ -5797,6 +5961,13 @@ submit = "ą“²ąµ‹ą“—ą“æąµ»" signInWith = "ą“‡ą“¤ąµą“Ŗą“Æąµ‹ą“—ą“æą“šąµą“šąµ ą“øąµˆąµ» ą“‡ąµ» ą“šąµ†ą“Æąµą“Æąµą“•" oauthPending = "ą““ą“¤ą“Øąµą“±ą“æą“•ąµą“•ąµ‡ą“·ą“Øą“¾ą“Æą“æ ą“¬ąµą“°ąµ—ą“øąµ¼ ą“¤ąµą“±ą“•ąµą“•ąµą“Øąµą“Øąµ..." orContinueWith = "ą“…ą“²ąµą“²ąµ†ą“™ąµą“•ą“æąµ½ ą“‡ą“®ąµ†ą“Æą“æą“²ąµ‹ą“Ÿąµ† ą“¤ąµą“Ÿą“°ąµą“•" +serverRequirement = "ą“¶ąµą“°ą“¦ąµą“§ą“æą“•ąµą“•ąµą“•: ą“øąµ†ąµ¼ą“µą“±ą“æąµ½ ą“²ąµ‹ą“—ą“æąµ» ą“Ŗąµą“°ą“µąµ¼ą“¤ąµą“¤ą“Øą“•ąµą“·ą“®ą“®ą“¾ą“•ąµą“•ą“æą“Æą“æą“°ą“æą“•ąµą“•ą“£ą“‚." +showInstructions = "ą“Žą“™ąµą“™ą“Øąµ† ą“Ŗąµą“°ą“µąµ¼ą“¤ąµą“¤ą“Øą“•ąµą“·ą“®ą“®ą“¾ą“•ąµą“•ą“¾ą“‚?" +hideInstructions = "ą“Øą“æąµ¼ą“¦ąµą“¦ąµ‡ą“¶ą“™ąµą“™ąµ¾ ą“®ą“±ą“Æąµą“•ąµą“•ąµą“•" +instructions = "ą“Øą“æą“™ąµą“™ą“³ąµą“Ÿąµ† Stirling PDF ą“øąµ†ąµ¼ą“µą“±ą“æąµ½ ą“²ąµ‹ą“—ą“æąµ» ą“Ŗąµą“°ą“µąµ¼ą“¤ąµą“¤ą“Øą“•ąµą“·ą“®ą“®ą“¾ą“•ąµą“•ą“¾ąµ»:" +instructionsEnvVar = "Environment variable ą“øą“œąµą“œą“®ą“¾ą“•ąµą“•ąµą“•:" +instructionsOrYml = "ą“…ą“²ąµą“²ąµ†ą“™ąµą“•ą“æąµ½ settings.yml-ąµ½:" +instructionsRestart = "ą“¤ąµą“Ÿąµ¼ą“Øąµą“Øąµ ą“®ą“¾ą“±ąµą“±ą“™ąµą“™ąµ¾ ą“Ŗąµą“°ą“¾ą“¬ą“²ąµą“Æą“¤ąµą“¤ą“æąµ½ ą“µą“°ą“¾ąµ» ą“Øą“æą“™ąµą“™ą“³ąµą“Ÿąµ† ą“øąµ†ąµ¼ą“µąµ¼ ą“±ąµ€ą“øąµą“±ąµą“±ą“¾ąµ¼ą“Ÿąµą“Ÿąµ ą“šąµ†ą“Æąµą“Æąµą“•." [setup.login.username] label = "ą“Æąµ‚ą“øąµ¼ą“Øąµ†ą“Æą“æą“‚" @@ -5847,12 +6018,13 @@ singleLine = "ą“øą“æą“‚ą“—ą“æąµ¾ ą“²ąµˆąµ»" [pdfTextEditor.badges] unsaved = "ą“Žą“”ą“æą“±ąµą“±ąµ ą“šąµ†ą“Æąµą“¤ąµ" modified = "ą“Žą“”ą“æą“±ąµą“±ąµ ą“šąµ†ą“Æąµą“¤ąµ" -earlyAccess = "Early Access" +earlyAccess = "ą“Žąµ¼ą“²ą“æ ą“†ą“•ąµā€Œą“øą“øąµ" [pdfTextEditor.actions] reset = "ą“®ą“¾ą“±ąµą“±ą“™ąµą“™ąµ¾ ą“±ąµ€ą“øąµ†ą“±ąµą“±ąµ ą“šąµ†ą“Æąµą“Æąµą“•" downloadJson = "JSON ą“”ąµ—ąµŗą“²ąµ‹ą“”ąµ ą“šąµ†ą“Æąµą“Æąµą“•" generatePdf = "PDF ą“øąµƒą“·ąµą“Ÿą“æą“•ąµą“•ąµą“•" +saveChanges = "ą“®ą“¾ą“±ąµą“±ą“™ąµą“™ąµ¾ ą“øą“‚ą“°ą“•ąµą“·ą“æą“•ąµą“•ąµą“•" [pdfTextEditor.options.autoScaleText] title = "ą“¬ąµ‹ą“•ąµā€Œą“øą“æąµ½ ą“’ą“¤ąµą“™ąµą“™ą“¾ąµ» ą“Ÿąµ†ą“•ąµą“øąµą“±ąµą“±ąµ ą“øąµą“µą“Æą“‚ ą“øąµą“•ąµ†ą“Æą“æąµ½ ą“šąµ†ą“Æąµą“Æąµą“•" @@ -5890,6 +6062,8 @@ alpha = "ą“ˆ ą“†ąµ½ą“« ą“µąµ€ą“µąµ¼ ą“‡ą“Øą“æą“Æąµą“‚ ą“µą“æą“•ą“øą“Øą“¤ąµą“¤ [pdfTextEditor.empty] title = "ą“Ŗąµą“°ą“®ą“¾ą“£ą“‚ ą“²ąµ‹ą“”ąµ ą“šąµ†ą“Æąµą“¤ą“æą“Ÿąµą“Ÿą“æą“²ąµą“²" subtitle = "ą“Ÿąµ†ą“•ąµą“øąµą“±ąµą“±ąµ ą“Žą“”ą“æą“±ąµą“±ąµ ą“†ą“°ą“‚ą“­ą“æą“•ąµą“•ą“¾ąµ» PDF ą“…ą“²ąµą“²ąµ†ą“™ąµą“•ą“æąµ½ JSON ą“«ą“Æąµ½ ą“²ąµ‹ą“”ąµ ą“šąµ†ą“Æąµą“Æąµą“•." +dropzone = "ą“’ą“°ąµ PDF ą“…ą“²ąµą“²ąµ†ą“™ąµą“•ą“æąµ½ JSON ą“«ą“Æąµ½ ą“‡ą“µą“æą“Ÿąµ† ą“µą“²ą“æą“šąµą“šą“æą“Ÿąµą“•, ą“…ą“²ąµą“²ąµ†ą“™ąµą“•ą“æąµ½ ą“¬ąµą“°ąµ—ą“øąµ ą“šąµ†ą“Æąµą“Æą“¾ąµ» ą“•ąµą“²ą“æą“•ąµą“•ąµ ą“šąµ†ą“Æąµą“Æąµą“•" +dropzoneWithFiles = "Files ą“Ÿą“¾ą“¬ą“æąµ½ ą“Øą“æą“Øąµą“Øąµ ą“’ą“°ąµ ą“«ą“Æąµ½ ą“¤ą“æą“°ą“žąµą“žąµ†ą“Ÿąµą“•ąµą“•ąµą“•, ą“…ą“²ąµą“²ąµ†ą“™ąµą“•ą“æąµ½ ą“’ą“°ąµ PDF ą“…ą“²ąµą“²ąµ†ą“™ąµą“•ą“æąµ½ JSON ą“«ą“Æąµ½ ą“‡ą“µą“æą“Ÿąµ† ą“µą“²ą“æą“šąµą“šą“æą“Ÿąµą“•, ą“…ą“²ąµą“²ąµ†ą“™ąµą“•ą“æąµ½ ą“¬ąµą“°ąµ—ą“øąµ ą“šąµ†ą“Æąµą“Æą“¾ąµ» ą“•ąµą“²ą“æą“•ąµą“•ąµ ą“šąµ†ą“Æąµą“Æąµą“•" [pdfTextEditor.welcomeBanner] title = "PDF Text Editor-ą“²ąµ‡ą“•ąµą“•ąµ ą“øąµą“µą“¾ą“—ą“¤ą“‚ (Early Access)" diff --git a/frontend/public/locales/nl-NL/translation.toml b/frontend/public/locales/nl-NL/translation.toml index 321acd4753..b9599e1278 100644 --- a/frontend/public/locales/nl-NL/translation.toml +++ b/frontend/public/locales/nl-NL/translation.toml @@ -131,7 +131,7 @@ unsupported = "Niet ondersteund" [toolPanel] placeholder = "Kies een tool om te beginnen" -alpha = "Alpha" +alpha = "Alfa" premiumFeature = "Premiumfunctie:" comingSoon = "Binnenkort beschikbaar:" @@ -163,6 +163,11 @@ unfavorite = "Uit favorieten verwijderen" fullscreen = "Overschakelen naar volledig scherm" sidebar = "Overschakelen naar zijbalkmodus" +[backendStartup] +notFoundTitle = "Backend niet gevonden" +retry = "Opnieuw proberen" +unreachable = "De applicatie kan momenteel geen verbinding maken met de backend. Controleer de status van de backend en de netwerkverbinding en probeer het vervolgens opnieuw." + [zipWarning] title = "Groot ZIP-bestand" message = "Dit ZIP-bestand bevat {{count}} bestanden. Toch uitpakken?" @@ -347,7 +352,7 @@ teams = "Teams" title = "Configuratie" systemSettings = "Systeeminstellingen" features = "Functies" -endpoints = "Endpoints" +endpoints = "Eindpunten" database = "Database" advanced = "Geavanceerd" @@ -556,7 +561,7 @@ totalEndpoints = "Totaal aantal endpoints" totalVisits = "Totaal aantal bezoeken" showing = "Weergeven" selectedVisits = "Geselecteerde bezoeken" -endpoint = "Endpoint" +endpoint = "Eindpunt" visits = "Bezoeken" percentage = "Percentage" loading = "Laden..." @@ -912,6 +917,9 @@ desc = "Bouw workflows met meerdere stappen door PDF-acties te koppelen. Ideaal desc = "Plaatst PDF's over een andere PDF heen" title = "PDF's overlappen" +[home.pdfTextEditor] +title = "PDF-teksteditor" +desc = "Bewerk bestaande tekst en afbeeldingen in PDF's" [home.addText] tags = "tekst,annotatie,label" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Getekende handtekening" defaultImageLabel = "Geüploade handtekening" defaultTextLabel = "Getypte handtekening" saveButton = "Handtekening opslaan" +savePersonal = "Als persoonlijk opslaan" +saveShared = "Als gedeeld opslaan" saveUnavailable = "Maak eerst een handtekening om deze op te slaan." noChanges = "De huidige handtekening is al opgeslagen." +tempStorageTitle = "Tijdelijke browseropslag" +tempStorageDescription = "Handtekeningen worden alleen in je browser opgeslagen. Ze gaan verloren als je je browsergegevens wist of van browser wisselt." +personalHeading = "Persoonlijke handtekeningen" +sharedHeading = "Gedeelde handtekeningen" +personalDescription = "Alleen jij kunt deze handtekeningen zien." +sharedDescription = "Alle gebruikers kunnen deze handtekeningen zien en gebruiken." [sign.saved.type] canvas = "Tekening" @@ -3020,6 +3036,91 @@ title = "Informatie over PDF ophalen" header = "Informatie over PDF ophalen" submit = "Haal informatie op" downloadJson = "JSON downloaden" +processing = "Informatie wordt geĆ«xtraheerd..." +results = "Resultaten" +noResults = "Voer de tool uit om een rapport te genereren." +downloads = "Downloads" +noneDetected = "Niets gedetecteerd" +indexTitle = "Index" + +[getPdfInfo.report] +entryLabel = "Volledig informatieoverzicht" +shortTitle = "PDF-informatie" + +[getPdfInfo.sections] +metadata = "Metadata" +formFields = "Formuliervelden" +basicInfo = "Basisinformatie" +documentInfo = "Documentinformatie" +compliance = "Compliance" +encryption = "Versleuteling" +permissions = "Machtigingen" +other = "Overig" +perPageInfo = "Informatie per pagina" +tableOfContents = "Inhoudsopgave" + +[getPdfInfo.other] +attachments = "Bijlagen" +embeddedFiles = "Ingesloten bestanden" +javaScript = "JavaScript" +layers = "Lagen" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Grootte" +annotations = "Annotaties" +images = "Afbeeldingen" +links = "Koppelingen" +fonts = "Lettertypen" +xobjects = "Aantal XObjects" +multimedia = "Multimedia" + +[getPdfInfo.summary] +pages = "Pagina's" +fileSize = "Bestandsgrootte" +pdfVersion = "PDF-versie" +language = "Taal" +title = "PDF-samenvatting" +author = "Auteur" +created = "Gemaakt" +modified = "Gewijzigd" +permsAll = "Alle machtigingen toegestaan" +permsRestricted = "{{count}} beperkingen" +permsMixed = "Sommige machtigingen beperkt" +hasCompliance = "Voldoet aan compliancestandaarden" +noCompliance = "Geen compliancestandaarden" +basic = "Basisinformatie" +documentInfo = "Documentinformatie" +securityTitle = "Beveiligingsstatus" +technical = "Technisch" +overviewTitle = "PDF-overzicht" + +[getPdfInfo.summary.security] +encrypted = "Versleutelde PDF - Wachtwoordbeveiliging aanwezig" +unencrypted = "Onversleutelde PDF - Geen wachtwoordbeveiliging" + +[getPdfInfo.summary.tech] +images = "Afbeeldingen" +fonts = "Lettertypen" +formFields = "Formuliervelden" +embeddedFiles = "Ingesloten bestanden" +javaScript = "JavaScript" +layers = "Lagen" +bookmarks = "Bladwijzers" +multimedia = "Multimedia" + +[getPdfInfo.summary.overview] +untitled = "een document zonder titel" +unknown = "Onbekende auteur" +text = "Dit is een PDF van {{pages}} pagina's met de titel {{title}}, gemaakt door {{author}} (PDF-versie {{version}})." + +[getPdfInfo.error] +partial = "Sommige bestanden konden niet worden verwerkt." +unexpected = "Onverwachte fout tijdens het extraheren." + +[getPdfInfo.status] +complete = "Extractie voltooid" [extractPage] tags = "extraheren" @@ -3438,6 +3539,9 @@ signinTitle = "Gelieve in te loggen" ssoSignIn = "Inloggen via Single Sign-on" oAuth2AutoCreateDisabled = "OAUTH2 Automatisch aanmaken gebruiker uitgeschakeld" oAuth2AdminBlockedUser = "Registratie of inloggen van niet-registreerde gebruikers is helaas momenteel geblokkeerd. Neem contact op met de beheerder." +oAuth2RequiresLicense = "OAuth/SSO-inloggen vereist een betaalde licentie (Server of Enterprise). Neem contact op met de beheerder om uw abonnement te upgraden." +saml2RequiresLicense = "SAML-inloggen vereist een betaalde licentie (Server of Enterprise). Neem contact op met de beheerder om uw abonnement te upgraden." +maxUsersReached = "Het maximumaantal gebruikers voor uw huidige licentie is bereikt. Neem contact op met de beheerder om uw abonnement te upgraden of extra plaatsen toe te voegen." oauth2RequestNotFound = "Autorisatieverzoek niet gevonden" oauth2InvalidUserInfoResponse = "Ongeldige reactie op gebruikersinfo" oauth2invalidRequest = "Ongeldig verzoek" @@ -3805,7 +3909,7 @@ description = "These cookies are essential for the website to function properly. 2 = "Altijd ingeschakeld" [cookieBanner.preferencesModal.analytics] -title = "Analytics" +title = "Analyse" description = "Deze cookies helpen ons te begrijpen hoe onze tools worden gebruikt, zodat we ons kunnen richten op het bouwen van de functies die onze community het meest waardeert. Wees gerust—Stirling PDF kan niet en zal nooit de inhoud van de documenten waarmee je werkt volgen." [cookieBanner.services] @@ -3846,14 +3950,17 @@ fitToWidth = "Passend op breedte" actualSize = "Werkelijke grootte" [viewer] +cannotPreviewFile = "Kan voorbeeld van bestand niet weergeven" +dualPageView = "Dubbele paginaweergave" firstPage = "Eerste pagina" lastPage = "Laatste pagina" -previousPage = "Vorige pagina" nextPage = "Volgende pagina" +onlyPdfSupported = "De viewer ondersteunt alleen PDF-bestanden. Dit bestand lijkt een ander formaat te hebben." +previousPage = "Vorige pagina" +singlePageView = "Enkele paginaweergave" +unknownFile = "Onbekend bestand" zoomIn = "Inzoomen" zoomOut = "Uitzoomen" -singlePageView = "Enkele paginaweergave" -dualPageView = "Dubbele paginaweergave" [rightRail] closeSelected = "Geselecteerde bestanden sluiten" @@ -3877,6 +3984,7 @@ toggleSidebar = "Zijbalk tonen/verbergen" exportSelected = "Geselecteerde pagina's exporteren" toggleAnnotations = "Annotaties tonen/verbergen" annotationMode = "Annotatiemodus schakelen" +print = "PDF afdrukken" draw = "Tekenen" save = "Opslaan" saveChanges = "Wijzigingen opslaan" @@ -4343,7 +4451,7 @@ features = "Feature-flags" processing = "Verwerking" [admin.settings.advanced.endpoints] -label = "Endpoints" +label = "Eindpunten" manage = "API-endpoints beheren" description = "Endpointbeheer wordt geconfigureerd via YAML. Zie de documentatie voor details over het in-/uitschakelen van specifieke endpoints." @@ -4494,6 +4602,7 @@ description = "URL of bestandsnaam van het impressum (in sommige jurisdicties ve title = "Premium & Enterprise" description = "Configureer je premium- of enterprise-licentiesleutel." license = "Licentieconfiguratie" +noInput = "Geef een licentiesleutel of bestand op" [admin.settings.premium.licenseKey] toggle = "Heb je een licentiesleutel of certificaatbestand?" @@ -4511,6 +4620,25 @@ line1 = "Het overschrijven van je huidige licentiesleutel kan niet ongedaan word line2 = "Je vorige licentie gaat permanent verloren, tenzij je er elders een back-up van hebt." line3 = "Belangrijk: houd licentiesleutels privĆ© en veilig. Deel ze nooit openbaar." +[admin.settings.premium.inputMethod] +text = "Licentiesleutel" +file = "Certificaatbestand" + +[admin.settings.premium.file] +label = "Licentiecertificaatbestand" +description = "Upload je .lic- of .cert-licentiebestand van offline aankopen" +choose = "Kies licentiebestand" +selected = "Geselecteerd: {{filename}} ({{size}})" +successMessage = "Licentiebestand succesvol geüpload en geactiveerd. Herstarten niet vereist." + +[admin.settings.premium.currentLicense] +title = "Actieve licentie" +file = "Bron: licentiebestand ({{path}})" +key = "Bron: licentiesleutel" +type = "Type: {{type}}" +noInput = "Geef een licentiesleutel op of upload een certificaatbestand" +success = "Succes" + [admin.settings.premium.enabled] label = "Premiumfuncties inschakelen" description = "Licentiesleutelcontrole inschakelen voor pro-/enterprise-functies" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} geselecteerd" download = "Downloaden" delete = "Verwijderen" unsupported = "Niet ondersteund" +active = "Actief" addToUpload = "Aan upload toevoegen" +closeFile = "Bestand sluiten" deleteAll = "Alles verwijderen" loadingFiles = "Bestanden laden..." noFiles = "Geen bestanden beschikbaar" @@ -5132,7 +5262,7 @@ upgrade = "Nu upgraden →" freeTitle = "Serverlicentie" overLimitTitle = "Serverlicentie vereist" overLimitBody = "Onze licentie staat tot {{freeTierLimit}} gebruikers gratis per server toe. Je hebt {{overLimitUserCopy}} Stirling-gebruikers. Om zonder onderbreking door te gaan, upgrade naar het Stirling Server-plan - onbeperkte plaatsen, PDF-tekstbewerking en volledige admincontrole voor $99/server/maand." -freeBody = "Onze Open-Core-licentie staat tot {{freeTierLimit}} gebruikers gratis per server toe. Om ononderbroken te schalen en vroege toegang te krijgen tot onze nieuwe PDF-tekstbewerkingstool, raden we het Stirling Server-plan aan - volledige bewerking en onbeperkte plaatsen voor $99/server/maand." +freeBody = "Onze Open-Core-licentie staat tot {{freeTierLimit}} gebruikers per server gratis toe. Om ononderbroken op te schalen, raden we het Stirling Server-abonnement aan - onbeperkte plaatsen en SSO-ondersteuning voor $99/server/maand." [onboarding.desktopInstall] title = "Downloaden" @@ -5237,6 +5367,31 @@ error = "Bijwerken van gebruikersstatus is mislukt" success = "Gebruiker succesvol verwijderd" error = "Gebruiker verwijderen is mislukt" +[workspace.people.changePassword] +action = "Wachtwoord wijzigen" +title = "Wachtwoord wijzigen" +subtitle = "Wachtwoord bijwerken voor" +newPassword = "Nieuw wachtwoord" +confirmPassword = "Wachtwoord bevestigen" +placeholder = "Voer een nieuw wachtwoord in" +confirmPlaceholder = "Voer het nieuwe wachtwoord opnieuw in" +passwordRequired = "Voer een nieuw wachtwoord in" +passwordMismatch = "Wachtwoorden komen niet overeen" +generateRandom = "Beveiligd wachtwoord genereren" +generatedPreview = "Gegenereerd wachtwoord:" +copyTooltip = "KopiĆ«ren naar klembord" +copiedToClipboard = "Wachtwoord gekopieerd naar klembord" +copyFailed = "KopiĆ«ren van wachtwoord mislukt" +sendEmail = "Gebruiker per e-mail informeren over deze wijziging" +includePassword = "Nieuw wachtwoord in de e-mail opnemen" +forcePasswordChange = "Gebruiker dwingen het wachtwoord bij de volgende aanmelding te wijzigen" +emailUnavailable = "Het e-mailadres van deze gebruiker is ongeldig. Meldingen zijn uitgeschakeld." +smtpDisabled = "E-mailmeldingen vereisen dat SMTP is ingeschakeld in de instellingen." +notifyOnly = "Er wordt een e-mail verzonden zonder het wachtwoord, om de gebruiker te laten weten dat een beheerder het heeft gewijzigd." +submit = "Wachtwoord bijwerken" +success = "Wachtwoord succesvol bijgewerkt" +error = "Bijwerken van wachtwoord mislukt" + [workspace.people.emailInvite] tab = "E-mailuitnodiging" description = "Typ of plak hieronder e-mailadressen, gescheiden door komma's. Gebruikers ontvangen inloggegevens via e-mail." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Er is minstens ƩƩn e-mailadres vereist" submit = "Uitnodigingen verzenden" success = "Gebruiker(s) succesvol uitgenodigd" -partialSuccess = "Sommige uitnodigingen zijn mislukt" +partialFailure = "Sommige uitnodigingen zijn mislukt" allFailed = "Uitnodigen van gebruikers is mislukt" error = "Uitnodigingen verzenden is mislukt" @@ -5709,7 +5864,7 @@ title = "Grafiek van endpointgebruik" [usage.table] title = "Gedetailleerde statistieken" -endpoint = "Endpoint" +endpoint = "Eindpunt" visits = "Bezoeken" percentage = "Percentage" noData = "Geen gegevens beschikbaar" @@ -5770,6 +5925,7 @@ subtitle = "Log in met je Stirling-account" [setup.selfhosted] title = "Inloggen bij server" subtitle = "Vul je servergegevens in" +link = "of maak verbinding met een zelfgehost account" [setup.server] title = "Verbinden met server" @@ -5788,6 +5944,14 @@ description = "Voer de volledige URL van je self-hosted Stirling PDF-server in" emptyUrl = "Voer een server-URL in" unreachable = "Kan geen verbinding maken met server" testFailed = "Verbindingstest mislukt" +configFetch = "Ophalen van serverconfiguratie mislukt. Controleer de URL en probeer het opnieuw." + +[setup.server.error.securityDisabled] +title = "Inloggen niet ingeschakeld" +body = "Op deze server is inloggen niet ingeschakeld. Om verbinding te maken met deze server moet u authenticatie inschakelen:" +step1 = "Stel DOCKER_ENABLE_SECURITY=true in in uw omgeving" +step2 = "Of stel security.enableLogin=true in in settings.yml" +step3 = "Start de server opnieuw" [setup.login] title = "Inloggen" @@ -5797,6 +5961,13 @@ submit = "Inloggen" signInWith = "Inloggen met" oauthPending = "Browser wordt geopend voor authenticatie..." orContinueWith = "Of ga verder met e-mail" +serverRequirement = "Let op: op de server moet inloggen zijn ingeschakeld." +showInstructions = "Hoe inschakelen?" +hideInstructions = "Instructies verbergen" +instructions = "Om inloggen op uw Stirling PDF-server in te schakelen:" +instructionsEnvVar = "Stel de omgevingsvariabele in:" +instructionsOrYml = "Of in settings.yml:" +instructionsRestart = "Start vervolgens uw server opnieuw zodat de wijzigingen van kracht worden." [setup.login.username] label = "Gebruikersnaam" @@ -5853,6 +6024,7 @@ earlyAccess = "Vroege toegang" reset = "Wijzigingen resetten" downloadJson = "JSON downloaden" generatePdf = "PDF genereren" +saveChanges = "Wijzigingen opslaan" [pdfTextEditor.options.autoScaleText] title = "Tekst automatisch schalen zodat deze in vakken past" @@ -5890,6 +6062,8 @@ alpha = "Deze alpha-viewer is nog in ontwikkeling—bepaalde lettertypen, kleure [pdfTextEditor.empty] title = "Geen document geladen" subtitle = "Laad een PDF- of JSON-bestand om tekst te bewerken." +dropzone = "Sleep hier een PDF- of JSON-bestand naartoe, of klik om te bladeren" +dropzoneWithFiles = "Selecteer een bestand op het tabblad Bestanden, of sleep hier een PDF- of JSON-bestand naartoe, of klik om te bladeren" [pdfTextEditor.welcomeBanner] title = "Welkom bij PDF-teksteditor (Early Access)" diff --git a/frontend/public/locales/no-NB/translation.toml b/frontend/public/locales/no-NB/translation.toml index 21b873f015..c60238acf4 100644 --- a/frontend/public/locales/no-NB/translation.toml +++ b/frontend/public/locales/no-NB/translation.toml @@ -131,7 +131,7 @@ unsupported = "Ikke stĆøttet" [toolPanel] placeholder = "Velg et verktĆøy for Ć„ komme i gang" -alpha = "Alpha" +alpha = "Alfa" premiumFeature = "Premium-funksjon:" comingSoon = "Kommer snart:" @@ -163,6 +163,11 @@ unfavorite = "Fjern fra favoritter" fullscreen = "Bytt til fullskjerm-modus" sidebar = "Bytt til sidepanel-modus" +[backendStartup] +notFoundTitle = "Backend ikke funnet" +retry = "PrĆøv igjen" +unreachable = "Programmet kan for Ćøyeblikket ikke koble til backend. Kontroller backend-status og nettverkstilkobling, og prĆøv igjen." + [zipWarning] title = "Stor ZIP-fil" message = "Denne ZIP-en inneholder {{count}} filer. Pakk ut likevel?" @@ -912,6 +917,9 @@ desc = "Bygg flertrinns arbeidsflyter ved Ć„ lenke sammen PDF-handlinger. Ideelt desc = "Legger PDF-er over hverandre" title = "Overlay PDF-er" +[home.pdfTextEditor] +title = "PDF-teksteditor" +desc = "Rediger eksisterende tekst og bilder i PDF-filer" [home.addText] tags = "tekst,merknad,etikett" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Tegnet signatur" defaultImageLabel = "Opplastet signatur" defaultTextLabel = "Tekstsignatur" saveButton = "Lagre signatur" +savePersonal = "Lagre personlig" +saveShared = "Lagre delt" saveUnavailable = "Opprett en signatur fĆørst for Ć„ lagre den." noChanges = "Gjeldende signatur er allerede lagret." +tempStorageTitle = "Midlertidig nettleserlagring" +tempStorageDescription = "Signaturer lagres bare i nettleseren din. De gĆ„r tapt hvis du sletter nettleserdata eller bytter nettleser." +personalHeading = "Personlige signaturer" +sharedHeading = "Delte signaturer" +personalDescription = "Bare du kan se disse signaturene." +sharedDescription = "Alle brukere kan se og bruke disse signaturene." [sign.saved.type] canvas = "Tegning" @@ -3020,6 +3036,91 @@ title = "FĆ„ Info om PDF" header = "FĆ„ Info om PDF" submit = "FĆ„ Info" downloadJson = "Last ned JSON" +processing = "Henter ut informasjon..." +results = "Resultater" +noResults = "KjĆør verktĆøyet for Ć„ generere en rapport." +downloads = "Nedlastinger" +noneDetected = "Ingen funnet" +indexTitle = "Indeks" + +[getPdfInfo.report] +entryLabel = "Fullstendig informasjonsoppsummering" +shortTitle = "PDF-informasjon" + +[getPdfInfo.sections] +metadata = "Metadata" +formFields = "Skjemafelt" +basicInfo = "Grunnleggende info" +documentInfo = "Dokumentinformasjon" +compliance = "Samsvar" +encryption = "Kryptering" +permissions = "Tillatelser" +other = "Annet" +perPageInfo = "Informasjon per side" +tableOfContents = "Innholdsfortegnelse" + +[getPdfInfo.other] +attachments = "Vedlegg" +embeddedFiles = "Innebygde filer" +javaScript = "JavaScript" +layers = "Lag" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "StĆørrelse" +annotations = "Merknader" +images = "Bilder" +links = "Lenker" +fonts = "Skrifter" +xobjects = "XObject-antall" +multimedia = "Multimedia" + +[getPdfInfo.summary] +pages = "Sider" +fileSize = "FilstĆørrelse" +pdfVersion = "PDF-versjon" +language = "SprĆ„k" +title = "PDF-sammendrag" +author = "Forfatter" +created = "Opprettet" +modified = "Endret" +permsAll = "Alle tillatelser tillatt" +permsRestricted = "{{count}} begrensninger" +permsMixed = "Noen tillatelser begrenset" +hasCompliance = "Har samsvarsstandarder" +noCompliance = "Ingen samsvarsstandarder" +basic = "Grunnleggende informasjon" +documentInfo = "Dokumentinformasjon" +securityTitle = "Sikkerhetsstatus" +technical = "Teknisk" +overviewTitle = "PDF-oversikt" + +[getPdfInfo.summary.security] +encrypted = "Kryptert PDF - passordbeskyttelse er aktiv" +unencrypted = "Ukryptert PDF - ingen passordbeskyttelse" + +[getPdfInfo.summary.tech] +images = "Bilder" +fonts = "Skrifter" +formFields = "Skjemafelt" +embeddedFiles = "Innebygde filer" +javaScript = "JavaScript" +layers = "Lag" +bookmarks = "Bokmerker" +multimedia = "Multimedia" + +[getPdfInfo.summary.overview] +untitled = "et navnlĆøst dokument" +unknown = "Ukjent forfatter" +text = "Dette er en PDF pĆ„ {{pages}} sider med tittelen {{title}}, opprettet av {{author}} (PDF-versjon {{version}})." + +[getPdfInfo.error] +partial = "Noen filer kunne ikke behandles." +unexpected = "Uventet feil under uttrekk." + +[getPdfInfo.status] +complete = "Uttrekk fullfĆørt" [extractPage] tags = "ekstrahere" @@ -3438,6 +3539,9 @@ signinTitle = "Vennligst logg inn" ssoSignIn = "Logg inn via Enkel PĆ„logging" oAuth2AutoCreateDisabled = "OAUTH2 Auto-Opretting av bruker deaktivert" oAuth2AdminBlockedUser = "Registrering eller pĆ„logging for ikke-registrerte brukere er for Ćøyeblikket blokkert. Vennligst kontakt administrator" +oAuth2RequiresLicense = "OAuth/SSO-pĆ„logging krever en betalt lisens (Server eller Enterprise). Kontakt administratoren for Ć„ oppgradere planen din." +saml2RequiresLicense = "SAML-pĆ„logging krever en betalt lisens (Server eller Enterprise). Kontakt administratoren for Ć„ oppgradere planen din." +maxUsersReached = "Maksimalt antall brukere er nĆ„dd for din nĆ„vƦrende lisens. Kontakt administratoren for Ć„ oppgradere planen din eller legge til flere brukerplasser." oauth2RequestNotFound = "AutentiseringsforespĆørsel ikke funnet" oauth2InvalidUserInfoResponse = "Ugyldig brukerinforespons" oauth2invalidRequest = "Ugyldig forespĆørsel" @@ -3846,14 +3950,17 @@ fitToWidth = "Tilpass til bredde" actualSize = "Faktisk stĆørrelse" [viewer] +cannotPreviewFile = "Kan ikke forhĆ„ndsvise fil" +dualPageView = "Dobbelsidevisning" firstPage = "FĆørste side" lastPage = "Siste side" -previousPage = "Forrige side" nextPage = "Neste side" +onlyPdfSupported = "Visningsprogrammet stĆøtter bare PDF-filer. Denne filen ser ut til Ć„ ha et annet format." +previousPage = "Forrige side" +singlePageView = "Enkeltsidevisning" +unknownFile = "Ukjent fil" zoomIn = "Zoom inn" zoomOut = "Zoom ut" -singlePageView = "Enkeltsidevisning" -dualPageView = "Dobbelsidevisning" [rightRail] closeSelected = "Lukk valgte filer" @@ -3877,6 +3984,7 @@ toggleSidebar = "Vis/skjul sidepanel" exportSelected = "Eksporter valgte sider" toggleAnnotations = "Vis/skjul merknader" annotationMode = "Veksle merknadsmodus" +print = "Skriv ut PDF" draw = "Tegn" save = "Lagre" saveChanges = "Lagre endringer" @@ -4153,7 +4261,7 @@ description = "Spor brukerhandlinger og systemhendelser for etterlevelse og sikk [admin.settings.security.audit.level] label = "RevisjonsnivĆ„" -description = "0=OFF, 1=BASIC, 2=STANDARD, 3=VERBOSE" +description = "0=AV, 1=GRUNNLEGGENDE, 2=STANDARD, 3=DETALJERT" [admin.settings.security.audit.retentionDays] label = "Bevaring av revisjon (dager)" @@ -4494,6 +4602,7 @@ description = "URL eller filnavn til impressum (pĆ„krevd i noen jurisdiksjoner)" title = "Premium og Enterprise" description = "Konfigurer din premium- eller enterprise-lisensnĆøkkel." license = "Lisenskonfigurasjon" +noInput = "Oppgi en lisensnĆøkkel eller fil" [admin.settings.premium.licenseKey] toggle = "Har du en lisensnĆøkkel eller sertifikatfil?" @@ -4511,6 +4620,25 @@ line1 = "ƅ overskrive gjeldende lisensnĆøkkel kan ikke angres." line2 = "Den forrige lisensen vil gĆ„ tapt permanent med mindre du har sikkerhetskopiert den et annet sted." line3 = "Viktig: Hold lisensnĆøkler private og sikre. Del dem aldri offentlig." +[admin.settings.premium.inputMethod] +text = "LisensnĆøkkel" +file = "Sertifikatfil" + +[admin.settings.premium.file] +label = "Lisenssertifikatfil" +description = "Last opp .lic- eller .cert-lisensfilen din fra offline-kjĆøp" +choose = "Velg lisensfil" +selected = "Valgt: {{filename}} ({{size}})" +successMessage = "Lisensfilen ble lastet opp og aktivert. Omstart er ikke nĆødvendig." + +[admin.settings.premium.currentLicense] +title = "Aktiv lisens" +file = "Kilde: Lisensfil ({{path}})" +key = "Kilde: LisensnĆøkkel" +type = "Type: {{type}}" +noInput = "Oppgi en lisensnĆøkkel eller last opp en sertifikatfil" +success = "Vellykket" + [admin.settings.premium.enabled] label = "Aktiver premiumfunksjoner" description = "Aktiver lisensnĆøkkelkontroller for pro-/enterprise-funksjoner" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} valgt" download = "Last ned" delete = "Slett" unsupported = "Ikke stĆøttet" +active = "Aktiv" addToUpload = "Legg til i opplasting" +closeFile = "Lukk fil" deleteAll = "Slett alt" loadingFiles = "Laster filer..." noFiles = "Ingen filer tilgjengelig" @@ -5059,7 +5189,7 @@ title = "Erstatt-Inverter-Farge" [replace-color.options] fill = "Fyllfarge" -gradient = "Gradient" +gradient = "Fargeovergang" [replace-color.selectText] 1 = "Erstatt eller Inverter farge alternativer" @@ -5132,7 +5262,7 @@ upgrade = "Oppgrader nĆ„ →" freeTitle = "Serverlisens" overLimitTitle = "Serverlisens kreves" overLimitBody = "Lisensieringen vĆ„r tillater opptil {{freeTierLimit}} brukere gratis per server. Du har {{overLimitUserCopy}} Stirling-brukere. For Ć„ fortsette uten avbrudd, oppgrader til Stirling Server-planen – ubegrensede plasser, PDF-tekstredigering og full admin-kontroll for $99/server/mnd." -freeBody = "VĆ„r Open-Core-lisensiering tillater opptil {{freeTierLimit}} brukere gratis per server. For Ć„ skalere uten avbrudd og fĆ„ tidlig tilgang til vĆ„rt nye PDF-tekstredigeringsverktĆøy, anbefaler vi Stirling Server-planen – full redigering og ubegrensede plasser for $99/server/mnd." +freeBody = "VĆ„r Open-Core-lisensiering tillater opptil {{freeTierLimit}} brukere gratis per server. For Ć„ skalere uten avbrudd anbefaler vi Stirling Server-planen - ubegrensede plasser og SSO-stĆøtte for $99/server/mnd." [onboarding.desktopInstall] title = "Last ned" @@ -5237,6 +5367,31 @@ error = "Kunne ikke oppdatere brukerstatus" success = "Bruker slettet" error = "Kunne ikke slette bruker" +[workspace.people.changePassword] +action = "Endre passord" +title = "Endre passord" +subtitle = "Oppdater passordet for" +newPassword = "Nytt passord" +confirmPassword = "Bekreft passord" +placeholder = "Angi et nytt passord" +confirmPlaceholder = "Skriv inn det nye passordet pĆ„ nytt" +passwordRequired = "Angi et nytt passord" +passwordMismatch = "Passordene samsvarer ikke" +generateRandom = "Generer sikkert passord" +generatedPreview = "Generert passord:" +copyTooltip = "Kopier til utklippstavle" +copiedToClipboard = "Passord kopiert til utklippstavle" +copyFailed = "Kunne ikke kopiere passord" +sendEmail = "Send e-post til brukeren om denne endringen" +includePassword = "Inkluder det nye passordet i e-posten" +forcePasswordChange = "Tving brukeren til Ć„ endre passord ved neste innlogging" +emailUnavailable = "Denne brukerens e-post er ikke en gyldig e-postadresse. Varsler er deaktivert." +smtpDisabled = "E-postvarsler krever at SMTP er aktivert i innstillingene." +notifyOnly = "Det sendes en e-post uten passordet som informerer brukeren om at en admin har endret det." +submit = "Oppdater passord" +success = "Passord oppdatert" +error = "Kunne ikke oppdatere passord" + [workspace.people.emailInvite] tab = "E-postinvitasjon" description = "Skriv eller lim inn e-poster nedenfor, separert med komma. Brukere vil motta innloggingsdetaljer via e-post." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Minst Ć©n e-postadresse er pĆ„krevd" submit = "Send invitasjoner" success = "bruker(e) invitert" -partialSuccess = "Noen invitasjoner mislyktes" +partialFailure = "Noen invitasjoner mislyktes" allFailed = "Kunne ikke invitere brukere" error = "Kunne ikke sende invitasjoner" @@ -5770,6 +5925,7 @@ subtitle = "Logg inn med Stirling-kontoen din" [setup.selfhosted] title = "Logg inn pĆ„ server" subtitle = "Oppgi serverlegitimasjonen din" +link = "eller koble til en selvhostet konto" [setup.server] title = "Koble til server" @@ -5788,6 +5944,14 @@ description = "Skriv inn full URL til din selvhostede Stirling PDF-server" emptyUrl = "Skriv inn en server-URL" unreachable = "Kunne ikke koble til server" testFailed = "Tilkoblingstest mislyktes" +configFetch = "Kunne ikke hente serverkonfigurasjon. Kontroller URL-en og prĆøv igjen." + +[setup.server.error.securityDisabled] +title = "Innlogging ikke aktivert" +body = "Denne serveren har ikke innlogging aktivert. For Ć„ koble til denne serveren mĆ„ du aktivere autentisering:" +step1 = "Angi DOCKER_ENABLE_SECURITY=true i miljĆøet ditt" +step2 = "Eller angi security.enableLogin=true i settings.yml" +step3 = "Start serveren pĆ„ nytt" [setup.login] title = "Logg inn" @@ -5797,6 +5961,13 @@ submit = "Logg inn" signInWith = "Logg inn med" oauthPending = "ƅpner nettleser for autentisering..." orContinueWith = "Eller fortsett med e-post" +serverRequirement = "Merk: Serveren mĆ„ ha pĆ„logging aktivert." +showInstructions = "Hvordan aktivere?" +hideInstructions = "Skjul instruksjoner" +instructions = "Slik aktiverer du pĆ„logging pĆ„ din Stirling PDF-server:" +instructionsEnvVar = "Sett miljĆøvariabelen:" +instructionsOrYml = "Eller i settings.yml:" +instructionsRestart = "Start deretter serveren pĆ„ nytt for at endringene skal tre i kraft." [setup.login.username] label = "Brukernavn" @@ -5840,7 +6011,7 @@ paragraph = "Avsnittsside" sparse = "Sparsom tekst" [pdfTextEditor.groupingMode] -auto = "Auto" +auto = "Automatisk" paragraph = "Avsnitt" singleLine = "Ɖn linje" @@ -5853,6 +6024,7 @@ earlyAccess = "Tidlig tilgang" reset = "Tilbakestill endringer" downloadJson = "Last ned JSON" generatePdf = "Generer PDF" +saveChanges = "Lagre endringer" [pdfTextEditor.options.autoScaleText] title = "Autoskalere tekst til Ć„ passe i bokser" @@ -5890,6 +6062,8 @@ alpha = "Denne alfa-visningen er fortsatt under utvikling—visse skrifttyper, f [pdfTextEditor.empty] title = "Ingen dokument lastet inn" subtitle = "Last inn en PDF- eller JSON-fil for Ć„ begynne Ć„ redigere tekstinnhold." +dropzone = "Dra og slipp en PDF- eller JSON-fil her, eller klikk for Ć„ bla gjennom" +dropzoneWithFiles = "Velg en fil fra fanen Filer, eller dra og slipp en PDF- eller JSON-fil her, eller klikk for Ć„ bla gjennom" [pdfTextEditor.welcomeBanner] title = "Velkommen til PDF Text Editor (Tidlig tilgang)" diff --git a/frontend/public/locales/pl-PL/translation.toml b/frontend/public/locales/pl-PL/translation.toml index bbde870220..c066d13840 100644 --- a/frontend/public/locales/pl-PL/translation.toml +++ b/frontend/public/locales/pl-PL/translation.toml @@ -131,7 +131,7 @@ unsupported = "Nieobsługiwane" [toolPanel] placeholder = "Wybierz narzędzie, aby zacząć" -alpha = "Alpha" +alpha = "Alfa" premiumFeature = "Funkcja premium:" comingSoon = "Wkrótce:" @@ -163,6 +163,11 @@ unfavorite = "Usuń z ulubionych" fullscreen = "Przełącz na tryb pełnoekranowy" sidebar = "Przełącz na tryb paska bocznego" +[backendStartup] +notFoundTitle = "Nie znaleziono backendu" +retry = "Spróbuj ponownie" +unreachable = "Aplikacja nie może obecnie połączyć się z backendem. SprawdÅŗ stan backendu i łączność sieciową, a następnie spróbuj ponownie." + [zipWarning] title = "Duży plik ZIP" message = "Ten ZIP zawiera {{count}} plików. Mimo to rozpakować?" @@ -563,7 +568,7 @@ loading = "Ładowanie..." failedToLoad = "Nie udało się załadować danych punktów końcowych. Spróbuj odświeżyć." home = "Strona główna" login = "Logowanie" -top = "Top" +top = "Najlepsze" numberOfVisits = "Liczba wizyt" visitsTooltip = "Wizyty: {0} ({1}% całości)" retry = "Spróbuj ponownie" @@ -912,6 +917,9 @@ desc = "Buduj wieloetapowe przepływy, łącząc akcje PDF. Idealne do powtarzaj desc = "Nakłada dokumenty PDF na siebie" title = "Nałóż PDFa" +[home.pdfTextEditor] +title = "Edytor tekstu PDF" +desc = "Edytuj istniejący tekst i obrazy w plikach PDF" [home.addText] tags = "tekst,adnotacja,etykieta" @@ -1217,7 +1225,7 @@ odtExt = "Tekst OpenDocument (.odt)" pptExt = "PowerPoint (.pptx)" odpExt = "Prezentacja OpenDocument (.odp)" txtExt = "Tekst niesformatowany (.txt)" -rtfExt = "Rich Text Format (.rtf)" +rtfExt = "Format RTF (.rtf)" selectedFiles = "Wybrane pliki" noFileSelected = "Nie wybrano pliku. Użyj panelu plików, aby dodać pliki." convertFiles = "Konwertuj pliki" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Podpis rysowany" defaultImageLabel = "Przesłany podpis" defaultTextLabel = "Podpis wpisany" saveButton = "Zapisz podpis" +savePersonal = "Zapisz osobiste" +saveShared = "Zapisz udostępnione" saveUnavailable = "Najpierw utwórz podpis, aby go zapisać." noChanges = "Bieżący podpis jest już zapisany." +tempStorageTitle = "Tymczasowe przechowywanie w przeglądarce" +tempStorageDescription = "Podpisy są przechowywane tylko w Twojej przeglądarce. Zostaną utracone po wyczyszczeniu danych przeglądarki lub zmianie przeglądarki." +personalHeading = "Osobiste podpisy" +sharedHeading = "Udostępnione podpisy" +personalDescription = "Tylko Ty widzisz te podpisy." +sharedDescription = "Wszyscy użytkownicy mogą widzieć i używać tych podpisów." [sign.saved.type] canvas = "Rysunek" @@ -3020,6 +3036,91 @@ title = "Pobierz informacje o pliku PDF" header = "Pobierz informacje o pliku PDF" submit = "Pobierz informacje" downloadJson = "Pobierz JSON z zawartością" +processing = "Wyodrębnianie informacji..." +results = "Wyniki" +noResults = "Uruchom narzędzie, aby wygenerować raport." +downloads = "Pobrania" +noneDetected = "Nic nie wykryto" +indexTitle = "Indeks" + +[getPdfInfo.report] +entryLabel = "Pełne podsumowanie informacji" +shortTitle = "Informacje o PDF" + +[getPdfInfo.sections] +metadata = "Metadane" +formFields = "Pola formularza" +basicInfo = "Informacje podstawowe" +documentInfo = "Informacje o dokumencie" +compliance = "Zgodność" +encryption = "Szyfrowanie" +permissions = "Uprawnienia" +other = "Inne" +perPageInfo = "Informacje dla każdej strony" +tableOfContents = "Spis treści" + +[getPdfInfo.other] +attachments = "Załączniki" +embeddedFiles = "Osadzone pliki" +javaScript = "JavaScript" +layers = "Warstwy" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Rozmiar" +annotations = "Adnotacje" +images = "Obrazy" +links = "Linki" +fonts = "Czcionki" +xobjects = "Liczba XObject" +multimedia = "Multimedia" + +[getPdfInfo.summary] +pages = "Strony" +fileSize = "Rozmiar pliku" +pdfVersion = "Wersja PDF" +language = "Język" +title = "Podsumowanie PDF" +author = "Autor" +created = "Utworzono" +modified = "Zmodyfikowano" +permsAll = "Wszystkie uprawnienia dozwolone" +permsRestricted = "{{count}} ograniczeń" +permsMixed = "Niektóre uprawnienia ograniczone" +hasCompliance = "Spełnia standardy zgodności" +noCompliance = "Brak standardów zgodności" +basic = "Informacje podstawowe" +documentInfo = "Informacje o dokumencie" +securityTitle = "Stan zabezpieczeń" +technical = "Techniczne" +overviewTitle = "Przegląd PDF" + +[getPdfInfo.summary.security] +encrypted = "Zaszyfrowany PDF - obecne zabezpieczenie hasłem" +unencrypted = "Niezaszyfrowany PDF - brak zabezpieczenia hasłem" + +[getPdfInfo.summary.tech] +images = "Obrazy" +fonts = "Czcionki" +formFields = "Pola formularza" +embeddedFiles = "Osadzone pliki" +javaScript = "JavaScript" +layers = "Warstwy" +bookmarks = "Zakładki" +multimedia = "Multimedia" + +[getPdfInfo.summary.overview] +untitled = "niezatytułowany dokument" +unknown = "Nieznany autor" +text = "To jest {{pages}}-stronicowy plik PDF zatytułowany {{title}}, utworzony przez {{author}} (wersja PDF {{version}})." + +[getPdfInfo.error] +partial = "Niektórych plików nie udało się przetworzyć." +unexpected = "Nieoczekiwany błąd podczas wyodrębniania." + +[getPdfInfo.status] +complete = "Zakończono wyodrębnianie" [extractPage] tags = "wydobycie,separacja,wyciaganie" @@ -3438,6 +3539,9 @@ signinTitle = "Zaloguj się" ssoSignIn = "Zaloguj się za pomocą logowania jednokrotnego" oAuth2AutoCreateDisabled = "Wyłączono automatyczne tworzenie użytkownika OAUTH2" oAuth2AdminBlockedUser = "Rejestracja lub logowanie niezarejestrowanych użytkowników jest obecnie zablokowane. Prosimy o kontakt z administratorem." +oAuth2RequiresLicense = "Logowanie OAuth/SSO wymaga płatnej licencji (Server lub Enterprise). Skontaktuj się z administratorem, aby uaktualnić swój plan." +saml2RequiresLicense = "Logowanie SAML wymaga płatnej licencji (Server lub Enterprise). Skontaktuj się z administratorem, aby uaktualnić swój plan." +maxUsersReached = "Osiągnięto maksymalną liczbę użytkowników dla Twojej obecnej licencji. Skontaktuj się z administratorem, aby uaktualnić plan lub dodać więcej miejsc." oauth2RequestNotFound = "Błąd logowania OAuth2" oauth2InvalidUserInfoResponse = "Niewłaściwe dane logowania" oauth2invalidRequest = "Nieprawidłowe żądanie" @@ -3533,7 +3637,7 @@ title = "PDF do pojedyńczej strony" header = "PDF do pojedyńczej strony" submit = "Zapisz dokument jako PDF z jedną stroną" description = "To narzędzie scali wszystkie strony Twojego PDF w jedną dużą stronę. Szerokość pozostanie taka jak w oryginalnych stronach, a wysokość będzie sumą wysokości wszystkich stron." -filenamePrefix = "single_page" +filenamePrefix = "pojedyncza_strona" [pdfToSinglePage.files] placeholder = "Wybierz plik PDF w widoku głównym, aby rozpocząć" @@ -3846,14 +3950,17 @@ fitToWidth = "Dopasuj do szerokości" actualSize = "Rzeczywisty rozmiar" [viewer] +cannotPreviewFile = "Nie można wyświetlić podglądu pliku" +dualPageView = "Widok dwóch stron" firstPage = "Pierwsza strona" lastPage = "Ostatnia strona" -previousPage = "Poprzednia strona" nextPage = "Następna strona" +onlyPdfSupported = "Przeglądarka obsługuje tylko pliki PDF. Ten plik wydaje się mieć inny format." +previousPage = "Poprzednia strona" +singlePageView = "Widok pojedynczej strony" +unknownFile = "Nieznany plik" zoomIn = "Powiększ" zoomOut = "Pomniejsz" -singlePageView = "Widok pojedynczej strony" -dualPageView = "Widok dwóch stron" [rightRail] closeSelected = "Zamknij wybrane pliki" @@ -3877,6 +3984,7 @@ toggleSidebar = "Przełącz panel boczny" exportSelected = "Eksportuj wybrane strony" toggleAnnotations = "Przełącz widoczność adnotacji" annotationMode = "Przełącz tryb adnotacji" +print = "Drukuj PDF" draw = "Rysuj" save = "Zapisz" saveChanges = "Zapisz zmiany" @@ -4153,7 +4261,7 @@ description = "ŚledÅŗ działania użytkowników i zdarzenia systemowe na potrze [admin.settings.security.audit.level] label = "Poziom audytu" -description = "0=OFF, 1=BASIC, 2=STANDARD, 3=VERBOSE" +description = "0=WYŁ., 1=PODSTAWOWY, 2=STANDARDOWY, 3=SZCZEGÓŁOWY" [admin.settings.security.audit.retentionDays] label = "Przechowywanie audytu (dni)" @@ -4494,6 +4602,7 @@ description = "URL lub nazwa pliku do impressum (wymagane w niektórych jurysdyk title = "Premium i Enterprise" description = "Skonfiguruj swój klucz licencyjny premium lub enterprise." license = "Konfiguracja licencji" +noInput = "Podaj klucz licencyjny lub plik" [admin.settings.premium.licenseKey] toggle = "Masz klucz licencyjny lub plik certyfikatu?" @@ -4511,6 +4620,25 @@ line1 = "Nadpisania bieżącego klucza licencyjnego nie można cofnąć." line2 = "Poprzednia licencja zostanie trwale utracona, jeśli nie masz jej kopii zapasowej." line3 = "Ważne: przechowuj klucze licencyjne prywatnie i bezpiecznie. Nigdy nie udostępniaj ich publicznie." +[admin.settings.premium.inputMethod] +text = "Klucz licencyjny" +file = "Plik certyfikatu" + +[admin.settings.premium.file] +label = "Plik certyfikatu licencji" +description = "Prześlij swój plik licencji .lic lub .cert z zakupów offline" +choose = "Wybierz plik licencji" +selected = "Wybrano: {{filename}} ({{size}})" +successMessage = "Plik licencji przesłano i pomyślnie aktywowano. Ponowne uruchomienie nie jest wymagane." + +[admin.settings.premium.currentLicense] +title = "Aktywna licencja" +file = "Źródło: plik licencji ({{path}})" +key = "Źródło: klucz licencyjny" +type = "Typ: {{type}}" +noInput = "Podaj klucz licencyjny lub prześlij plik certyfikatu" +success = "Sukces" + [admin.settings.premium.enabled] label = "Włącz funkcje premium" description = "Włącz weryfikację klucza licencyjnego dla funkcji pro/enterprise" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} wybrane" download = "Pobierz" delete = "usuń" unsupported = "Nieobsługiwane" +active = "Aktywny" addToUpload = "Dodaj do przesyłania" +closeFile = "Zamknij plik" deleteAll = "Usuń wszystko" loadingFiles = "Ładowanie plików..." noFiles = "Brak dostępnych plików" @@ -5132,7 +5262,7 @@ upgrade = "Ulepsz teraz →" freeTitle = "Licencja serwera" overLimitTitle = "Wymagana licencja serwera" overLimitBody = "Nasza licencja pozwala na maks. {{freeTierLimit}} użytkowników bez opłat na serwer. Masz {{overLimitUserCopy}} użytkowników Stirling. Aby kontynuować bez przerw, przejdÅŗ na plan Stirling Server – nielimitowane miejsca, edycja tekstu PDF i pełna kontrola administracyjna za 99 USD/serwer/mies." -freeBody = "Nasza licencja Open-Core pozwala na maks. {{freeTierLimit}} użytkowników bez opłat na serwer. Aby skalować bez przerw i uzyskać wczesny dostęp do nowego narzędzia edycji tekstu PDF, polecamy plan Stirling Server – pełna edycja i nielimitowane miejsca za 99 USD/serwer/mies." +freeBody = "Nasza licencja Open-Core pozwala na maksymalnie {{freeTierLimit}} użytkowników bezpłatnie na serwer. Aby skalować bez zakłóceń, zalecamy plan Stirling Server - nielimitowana liczba miejsc i obsługa SSO za $99/serwer/mies." [onboarding.desktopInstall] title = "Pobierz" @@ -5237,6 +5367,31 @@ error = "Nie udało się zaktualizować statusu użytkownika" success = "Użytkownik usunięty pomyślnie" error = "Nie udało się usunąć użytkownika" +[workspace.people.changePassword] +action = "Zmień hasło" +title = "Zmień hasło" +subtitle = "Zaktualizuj hasło dla" +newPassword = "Nowe hasło" +confirmPassword = "PotwierdÅŗ hasło" +placeholder = "WprowadÅŗ nowe hasło" +confirmPlaceholder = "WprowadÅŗ ponownie nowe hasło" +passwordRequired = "WprowadÅŗ nowe hasło" +passwordMismatch = "Hasła nie są zgodne" +generateRandom = "Wygeneruj bezpieczne hasło" +generatedPreview = "Wygenerowane hasło:" +copyTooltip = "Kopiuj do schowka" +copiedToClipboard = "Hasło skopiowano do schowka" +copyFailed = "Nie udało się skopiować hasła" +sendEmail = "Wyślij użytkownikowi e-mail o tej zmianie" +includePassword = "Dołącz nowe hasło do e-maila" +forcePasswordChange = "Wymuś zmianę hasła przy następnym logowaniu" +emailUnavailable = "E-mail tego użytkownika nie jest prawidłowym adresem. Powiadomienia są wyłączone." +smtpDisabled = "Powiadomienia e-mail wymagają włączenia SMTP w ustawieniach." +notifyOnly = "Zostanie wysłany e-mail bez hasła, informujący użytkownika, że administrator je zmienił." +submit = "Zaktualizuj hasło" +success = "Hasło zaktualizowano pomyślnie" +error = "Nie udało się zaktualizować hasła" + [workspace.people.emailInvite] tab = "Zaproszenie e‑mail" description = "Wpisz lub wklej e‑maile poniżej, rozdzielone przecinkami. Użytkownicy otrzymają dane logowania e‑mailem." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Wymagany jest co najmniej jeden adres e‑mail" submit = "Wyślij zaproszenia" success = "Pomyślnie zaproszono użytkowników" -partialSuccess = "Niektóre zaproszenia nie powiodły się" +partialFailure = "Niektóre zaproszenia nie powiodły się" allFailed = "Nie udało się zaprosić użytkowników" error = "Nie udało się wysłać zaproszeń" @@ -5541,7 +5696,7 @@ emailInvalid = "Wpisz poprawny adres e‑mail" title = "Podaj e‑mail" description = "Użyjemy go do wysłania klucza licencyjnego i rachunków." emailLabel = "Adres e‑mail" -emailPlaceholder = "your@email.com" +emailPlaceholder = "twoj@email.com" continue = "Kontynuuj" modalTitle = "Zaczynamy – {{planName}}" @@ -5770,6 +5925,7 @@ subtitle = "Zaloguj się na konto Stirling" [setup.selfhosted] title = "Zaloguj się do serwera" subtitle = "WprowadÅŗ dane logowania do serwera" +link = "lub połącz się z kontem hostowanym samodzielnie" [setup.server] title = "Połącz z serwerem" @@ -5788,6 +5944,14 @@ description = "Wpisz pełny URL własnego serwera Stirling PDF" emptyUrl = "Wpisz URL serwera" unreachable = "Nie można połączyć z serwerem" testFailed = "Test połączenia nie powiódł się" +configFetch = "Nie udało się pobrać konfiguracji serwera. SprawdÅŗ URL i spróbuj ponownie." + +[setup.server.error.securityDisabled] +title = "Logowanie nie jest włączone" +body = "Na tym serwerze logowanie nie jest włączone. Aby się połączyć, musisz włączyć uwierzytelnianie:" +step1 = "Ustaw w środowisku DOCKER_ENABLE_SECURITY=true" +step2 = "Lub ustaw security.enableLogin=true w pliku settings.yml" +step3 = "Uruchom ponownie serwer" [setup.login] title = "Zaloguj się" @@ -5797,6 +5961,13 @@ submit = "Zaloguj" signInWith = "Zaloguj przez" oauthPending = "Otwieranie przeglądarki do uwierzytelnienia..." orContinueWith = "Lub kontynuuj e‑mailem" +serverRequirement = "Uwaga: Na serwerze musi być włączone logowanie." +showInstructions = "Jak włączyć?" +hideInstructions = "Ukryj instrukcje" +instructions = "Aby włączyć logowanie na swoim serwerze Stirling PDF:" +instructionsEnvVar = "Ustaw zmienną środowiskową:" +instructionsOrYml = "Lub w settings.yml:" +instructionsRestart = "Następnie uruchom ponownie serwer, aby zmiany zaczęły obowiązywać." [setup.login.username] label = "Nazwa użytkownika" @@ -5840,7 +6011,7 @@ paragraph = "Strona akapitowa" sparse = "Rzadki tekst" [pdfTextEditor.groupingMode] -auto = "Auto" +auto = "Automatycznie" paragraph = "Akapit" singleLine = "Pojedyncza linia" @@ -5853,6 +6024,7 @@ earlyAccess = "Wczesny dostęp" reset = "Resetuj zmiany" downloadJson = "Pobierz JSON" generatePdf = "Generuj PDF" +saveChanges = "Zapisz zmiany" [pdfTextEditor.options.autoScaleText] title = "Automatycznie skaluj tekst do pól" @@ -5890,6 +6062,8 @@ alpha = "Ten podgląd alfa jest wciąż rozwijany — niektóre czcionki, kolory [pdfTextEditor.empty] title = "Nie wczytano dokumentu" subtitle = "Wczytaj plik PDF lub JSON, aby rozpocząć edycję treści tekstowych." +dropzone = "Przeciągnij i upuść tutaj plik PDF lub JSON albo kliknij, aby przeglądać" +dropzoneWithFiles = "Wybierz plik z karty Pliki, przeciągnij i upuść tutaj plik PDF lub JSON albo kliknij, aby przeglądać" [pdfTextEditor.welcomeBanner] title = "Witamy w edytorze tekstu PDF (wczesny dostęp)" diff --git a/frontend/public/locales/pt-BR/translation.toml b/frontend/public/locales/pt-BR/translation.toml index 76dff8f2d7..f146113a28 100644 --- a/frontend/public/locales/pt-BR/translation.toml +++ b/frontend/public/locales/pt-BR/translation.toml @@ -131,7 +131,7 @@ unsupported = "NĆ£o suportado" [toolPanel] placeholder = "Escolha uma ferramenta para comeƧar" -alpha = "Alpha" +alpha = "Alfa" premiumFeature = "Recurso premium:" comingSoon = "Em breve:" @@ -163,6 +163,11 @@ unfavorite = "Remover dos favoritos" fullscreen = "Alternar para modo tela cheia" sidebar = "Alternar para modo barra lateral" +[backendStartup] +notFoundTitle = "Backend nĆ£o encontrado" +retry = "Tentar novamente" +unreachable = "No momento, o aplicativo nĆ£o consegue se conectar ao backend. Verifique o status do backend e a conectividade de rede e tente novamente." + [zipWarning] title = "Arquivo ZIP grande" message = "Este ZIP contĆ©m {{count}} arquivos. Extrair mesmo assim?" @@ -912,6 +917,9 @@ desc = "Crie fluxos de trabalho de vĆ”rias etapas encadeando aƧƵes de PDF. Ide desc = "Sobrepor um PDF sobre outro" title = "Sobrepor PDFs" +[home.pdfTextEditor] +title = "Editor de Texto em PDF" +desc = "Edite texto e imagens existentes em PDFs" [home.addText] tags = "texto,anotação,rótulo" @@ -1217,7 +1225,7 @@ odtExt = "Texto OpenDocument (.odt)" pptExt = "PowerPoint (.pptx)" odpExt = "Apresentação OpenDocument (.odp)" txtExt = "Texto simples (.txt)" -rtfExt = "Rich Text Format (.rtf)" +rtfExt = "Formato Rich Text (.rtf)" selectedFiles = "Arquivos selecionados" noFileSelected = "Nenhum arquivo selecionado. Use o painel de arquivos para adicionar arquivos." convertFiles = "Converter arquivos" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Assinatura desenhada" defaultImageLabel = "Assinatura enviada" defaultTextLabel = "Assinatura digitada" saveButton = "Salvar assinatura" +savePersonal = "Salvar pessoal" +saveShared = "Salvar compartilhado" saveUnavailable = "Crie uma assinatura primeiro para salvĆ”-la." noChanges = "A assinatura atual jĆ” estĆ” salva." +tempStorageTitle = "Armazenamento temporĆ”rio do navegador" +tempStorageDescription = "As assinaturas sĆ£o armazenadas apenas no seu navegador. Elas serĆ£o perdidas se vocĆŖ limpar os dados do navegador ou trocar de navegador." +personalHeading = "Assinaturas pessoais" +sharedHeading = "Assinaturas compartilhadas" +personalDescription = "Somente vocĆŖ pode ver essas assinaturas." +sharedDescription = "Todos os usuĆ”rios podem ver e usar essas assinaturas." [sign.saved.type] canvas = "Desenho" @@ -3020,6 +3036,91 @@ title = "Obter InformaƧƵes do PDF" header = "Obter InformaƧƵes do PDF" submit = "Obter InformaƧƵes" downloadJson = "Baixar JSON" +processing = "Extraindo informaƧƵes..." +results = "Resultados" +noResults = "Execute a ferramenta para gerar um relatório." +downloads = "Downloads" +noneDetected = "Nenhum detectado" +indexTitle = "ƍndice" + +[getPdfInfo.report] +entryLabel = "Resumo completo das informaƧƵes" +shortTitle = "InformaƧƵes do PDF" + +[getPdfInfo.sections] +metadata = "Metadados" +formFields = "Campos de formulĆ”rio" +basicInfo = "InformaƧƵes bĆ”sicas" +documentInfo = "InformaƧƵes do documento" +compliance = "Conformidade" +encryption = "Criptografia" +permissions = "PermissƵes" +other = "Outros" +perPageInfo = "InformaƧƵes por pĆ”gina" +tableOfContents = "SumĆ”rio" + +[getPdfInfo.other] +attachments = "Anexos" +embeddedFiles = "Arquivos incorporados" +javaScript = "JavaScript" +layers = "Camadas" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Tamanho" +annotations = "AnotaƧƵes" +images = "Imagens" +links = "Links" +fonts = "Fontes" +xobjects = "Contagem de XObjects" +multimedia = "MultimĆ­dia" + +[getPdfInfo.summary] +pages = "PĆ”ginas" +fileSize = "Tamanho do arquivo" +pdfVersion = "VersĆ£o do PDF" +language = "Idioma" +title = "Resumo do PDF" +author = "Autor" +created = "Criado" +modified = "Modificado" +permsAll = "Todas as permissƵes permitidas" +permsRestricted = "{{count}} restriƧƵes" +permsMixed = "Algumas permissƵes restritas" +hasCompliance = "Possui padrƵes de conformidade" +noCompliance = "Sem padrƵes de conformidade" +basic = "InformaƧƵes bĆ”sicas" +documentInfo = "InformaƧƵes do documento" +securityTitle = "Status de seguranƧa" +technical = "TĆ©cnico" +overviewTitle = "VisĆ£o geral do PDF" + +[getPdfInfo.summary.security] +encrypted = "PDF criptografado - Proteção por senha ativada" +unencrypted = "PDF nĆ£o criptografado - Sem proteção por senha" + +[getPdfInfo.summary.tech] +images = "Imagens" +fonts = "Fontes" +formFields = "Campos de formulĆ”rio" +embeddedFiles = "Arquivos incorporados" +javaScript = "JavaScript" +layers = "Camadas" +bookmarks = "Marcadores" +multimedia = "MultimĆ­dia" + +[getPdfInfo.summary.overview] +untitled = "um documento sem tĆ­tulo" +unknown = "Autor desconhecido" +text = "Este Ć© um PDF de {{pages}} pĆ”ginas intitulado {{title}} criado por {{author}} (versĆ£o do PDF {{version}})." + +[getPdfInfo.error] +partial = "Alguns arquivos nĆ£o puderam ser processados." +unexpected = "Erro inesperado durante a extração." + +[getPdfInfo.status] +complete = "Extração concluĆ­da" [extractPage] tags = "extrair" @@ -3438,6 +3539,9 @@ signinTitle = "Por favor, inicie a sessĆ£o" ssoSignIn = "Iniciar sessĆ£o atravĆ©s de login Ćŗnico (SSO)" oAuth2AutoCreateDisabled = "Auto-Criar UsuĆ”rio OAUTH2 Desativado" oAuth2AdminBlockedUser = "O registro ou login de usuĆ”rios nĆ£o registrados estĆ” atualmente bloqueado. Entre em contato com o administrador." +oAuth2RequiresLicense = "O login via OAuth/SSO requer uma licenƧa paga (Server ou Enterprise). Entre em contato com o administrador para atualizar seu plano." +saml2RequiresLicense = "O login via SAML requer uma licenƧa paga (Server ou Enterprise). Entre em contato com o administrador para atualizar seu plano." +maxUsersReached = "NĆŗmero mĆ”ximo de usuĆ”rios atingido para sua licenƧa atual. Entre em contato com o administrador para atualizar seu plano ou adicionar mais assentos." oauth2RequestNotFound = "Solicitação de autorização nĆ£o encontrada" oauth2InvalidUserInfoResponse = "Resposta de informação de usuĆ”rio invĆ”lida" oauth2invalidRequest = "Requisição InvĆ”lida" @@ -3846,14 +3950,17 @@ fitToWidth = "Ajustar Ć  largura" actualSize = "Tamanho real" [viewer] +cannotPreviewFile = "NĆ£o Ć© possĆ­vel visualizar o arquivo" +dualPageView = "Visualização de duas pĆ”ginas" firstPage = "Primeira pĆ”gina" lastPage = "Última pĆ”gina" -previousPage = "PĆ”gina anterior" nextPage = "Próxima pĆ”gina" +onlyPdfSupported = "O visualizador oferece suporte apenas a arquivos PDF. Este arquivo parece estar em um formato diferente." +previousPage = "PĆ”gina anterior" +singlePageView = "Visualização de pĆ”gina Ćŗnica" +unknownFile = "Arquivo desconhecido" zoomIn = "Ampliar" zoomOut = "Reduzir" -singlePageView = "Visualização de pĆ”gina Ćŗnica" -dualPageView = "Visualização de duas pĆ”ginas" [rightRail] closeSelected = "Fechar arquivos selecionados" @@ -3877,6 +3984,7 @@ toggleSidebar = "Alternar barra lateral" exportSelected = "Exportar pĆ”ginas selecionadas" toggleAnnotations = "Alternar visibilidade das anotaƧƵes" annotationMode = "Alternar modo de anotação" +print = "Imprimir PDF" draw = "Desenhar" save = "Salvar" saveChanges = "Salvar alteraƧƵes" @@ -3925,7 +4033,7 @@ files = "Arquivos" activity = "Ativ." help = "Ajuda" account = "Conta" -config = "Config" +config = "ConfiguraƧƵes" settings = "Ajustes" adminSettings = "Ajustes admin" allTools = "Ferram." @@ -4153,7 +4261,7 @@ description = "Rastrear aƧƵes do usuĆ”rio e eventos do sistema para conformida [admin.settings.security.audit.level] label = "NĆ­vel de auditoria" -description = "0=OFF, 1=BASIC, 2=STANDARD, 3=VERBOSE" +description = "0=DESLIGADO, 1=BƁSICO, 2=PADRƃO, 3=DETALHADO" [admin.settings.security.audit.retentionDays] label = "Retenção de auditoria (dias)" @@ -4235,11 +4343,11 @@ label = "URL do emissor" description = "A URL do emissor do provedor OAuth2" [admin.settings.connections.oauth2.clientId] -label = "Client ID" +label = "ID do cliente" description = "O Client ID do OAuth2 do seu provedor" [admin.settings.connections.oauth2.clientSecret] -label = "Client Secret" +label = "Segredo do cliente" description = "O Client Secret do OAuth2 do seu provedor" [admin.settings.connections.oauth2.useAsUsername] @@ -4494,6 +4602,7 @@ description = "URL ou nome de arquivo do impressum (exigido em algumas jurisdiƧ title = "Premium e Enterprise" description = "Configurar sua chave de licenƧa premium ou enterprise." license = "Configuração de licenƧa" +noInput = "ForneƧa uma chave ou arquivo de licenƧa" [admin.settings.premium.licenseKey] toggle = "Tem uma chave de licenƧa ou arquivo de certificado?" @@ -4511,6 +4620,25 @@ line1 = "Substituir sua chave de licenƧa atual nĆ£o pode ser desfeito." line2 = "Sua licenƧa anterior serĆ” perdida permanentemente, a menos que vocĆŖ tenha um backup em outro lugar." line3 = "Importante: mantenha chaves de licenƧa privadas e seguras. Nunca as compartilhe publicamente." +[admin.settings.premium.inputMethod] +text = "Chave de licenƧa" +file = "Arquivo de certificado" + +[admin.settings.premium.file] +label = "Arquivo de certificado de licenƧa" +description = "FaƧa upload do seu arquivo de licenƧa .lic ou .cert de compras offline" +choose = "Escolher arquivo de licenƧa" +selected = "Selecionado: {{filename}} ({{size}})" +successMessage = "Arquivo de licenƧa enviado e ativado com sucesso. NĆ£o Ć© necessĆ”rio reiniciar." + +[admin.settings.premium.currentLicense] +title = "LicenƧa ativa" +file = "Origem: arquivo de licenƧa ({{path}})" +key = "Origem: chave de licenƧa" +type = "Tipo: {{type}}" +noInput = "ForneƧa uma chave de licenƧa ou envie um arquivo de certificado" +success = "Sucesso" + [admin.settings.premium.enabled] label = "Habilitar recursos Premium" description = "Habilitar verificação de chave de licenƧa para recursos pro/enterprise" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} selecionado(s)" download = "Baixar (JSON)" delete = "Apagar" unsupported = "NĆ£o suportado" +active = "Ativo" addToUpload = "Adicionar ao upload" +closeFile = "Fechar arquivo" deleteAll = "Excluir tudo" loadingFiles = "Carregando arquivos..." noFiles = "Nenhum arquivo disponĆ­vel" @@ -5132,7 +5262,7 @@ upgrade = "Fazer upgrade agora →" freeTitle = "LicenƧa do servidor" overLimitTitle = "NecessĆ”ria licenƧa do servidor" overLimitBody = "Nossa licenƧa permite atĆ© {{freeTierLimit}} usuĆ”rios grĆ”tis por servidor. VocĆŖ tem {{overLimitUserCopy}} usuĆ”rios do Stirling. Para continuar sem interrupƧƵes, faƧa upgrade para o plano Stirling Server - assentos ilimitados, edição de texto em PDF e controle total de admin por US$ 99/servidor/mĆŖs." -freeBody = "Nossa licenƧa Open-Core permite atĆ© {{freeTierLimit}} usuĆ”rios grĆ”tis por servidor. Para escalar sem interrupƧƵes e ter acesso antecipado Ć  nova ferramenta de edição de texto em PDF, recomendamos o plano Stirling Server - edição completa e assentos ilimitados por US$ 99/servidor/mĆŖs." +freeBody = "Nossa licenƧa Open-Core permite atĆ© {{freeTierLimit}} usuĆ”rios gratuitos por servidor. Para escalar sem interrupƧƵes, recomendamos o plano Stirling Server - assentos ilimitados e suporte a SSO por US$ 99/servidor/mĆŖs." [onboarding.desktopInstall] title = "Download" @@ -5178,7 +5308,7 @@ active = "Ativo" disabled = "Desativado" activeSession = "SessĆ£o ativa" member = "Membro" -admin = "Admin" +admin = "Administrador" editRole = "Editar função" enable = "Ativar" disable = "Desativar" @@ -5237,6 +5367,31 @@ error = "Falha ao atualizar o status do usuĆ”rio" success = "UsuĆ”rio excluĆ­do com sucesso" error = "Falha ao excluir usuĆ”rio" +[workspace.people.changePassword] +action = "Alterar senha" +title = "Alterar senha" +subtitle = "Atualizar a senha de" +newPassword = "Nova senha" +confirmPassword = "Confirmar senha" +placeholder = "Insira uma nova senha" +confirmPlaceholder = "Digite novamente a nova senha" +passwordRequired = "Por favor, insira uma nova senha" +passwordMismatch = "As senhas nĆ£o coincidem" +generateRandom = "Gerar senha segura" +generatedPreview = "Senha gerada:" +copyTooltip = "Copiar para a Ć”rea de transferĆŖncia" +copiedToClipboard = "Senha copiada para a Ć”rea de transferĆŖncia" +copyFailed = "Falha ao copiar a senha" +sendEmail = "Enviar email ao usuĆ”rio sobre esta alteração" +includePassword = "Incluir a nova senha no email" +forcePasswordChange = "Exigir que o usuĆ”rio altere a senha no próximo login" +emailUnavailable = "O email deste usuĆ”rio nĆ£o Ć© um endereƧo vĆ”lido. As notificaƧƵes estĆ£o desativadas." +smtpDisabled = "As notificaƧƵes por email exigem que o SMTP esteja habilitado nas configuraƧƵes." +notifyOnly = "Um email serĆ” enviado sem a senha, informando ao usuĆ”rio que um administrador a alterou." +submit = "Atualizar senha" +success = "Senha atualizada com sucesso" +error = "Falha ao atualizar a senha" + [workspace.people.emailInvite] tab = "Convite por email" description = "Digite ou cole emails abaixo, separados por vĆ­rgulas. Os usuĆ”rios receberĆ£o credenciais de login por email." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Ao menos um endereƧo de email Ć© obrigatório" submit = "Enviar convites" success = "usuĆ”rio(s) convidado(s) com sucesso" -partialSuccess = "Alguns convites falharam" +partialFailure = "Alguns convites falharam" allFailed = "Falha ao convidar usuĆ”rios" error = "Falha ao enviar convites" @@ -5541,7 +5696,7 @@ emailInvalid = "Digite um endereƧo de e-mail vĆ”lido" title = "Informe seu e-mail" description = "Usaremos isso para enviar sua chave de licenƧa e recibos." emailLabel = "EndereƧo de e-mail" -emailPlaceholder = "your@email.com" +emailPlaceholder = "seu@email.com" continue = "Continuar" modalTitle = "ComeƧar - {{planName}}" @@ -5770,6 +5925,7 @@ subtitle = "Entre com sua conta do Stirling" [setup.selfhosted] title = "Entrar no servidor" subtitle = "Informe suas credenciais do servidor" +link = "ou conecte-se a uma conta auto-hospedada" [setup.server] title = "Conectar ao servidor" @@ -5788,6 +5944,14 @@ description = "Informe a URL completa do seu servidor self-hosted Stirling PDF" emptyUrl = "Informe uma URL de servidor" unreachable = "NĆ£o foi possĆ­vel conectar ao servidor" testFailed = "Falha no teste de conexĆ£o" +configFetch = "Falha ao buscar a configuração do servidor. Verifique a URL e tente novamente." + +[setup.server.error.securityDisabled] +title = "Login nĆ£o habilitado" +body = "Este servidor nĆ£o tem o login habilitado. Para conectar-se a este servidor, vocĆŖ deve habilitar a autenticação:" +step1 = "Defina DOCKER_ENABLE_SECURITY=true no seu ambiente" +step2 = "Ou defina security.enableLogin=true em settings.yml" +step3 = "Reinicie o servidor" [setup.login] title = "Entrar" @@ -5797,13 +5961,20 @@ submit = "Login" signInWith = "Entrar com" oauthPending = "Abrindo o navegador para autenticação..." orContinueWith = "Ou continue com e-mail" +serverRequirement = "Observação: o servidor deve ter o login ativado." +showInstructions = "Como ativar?" +hideInstructions = "Ocultar instruƧƵes" +instructions = "Para ativar o login no seu servidor Stirling PDF:" +instructionsEnvVar = "Defina a variĆ”vel de ambiente:" +instructionsOrYml = "Ou em settings.yml:" +instructionsRestart = "Em seguida, reinicie o servidor para que as alteraƧƵes entrem em vigor." [setup.login.username] label = "UsuĆ”rio" placeholder = "Digite seu usuĆ”rio" [setup.login.email] -label = "Email" +label = "E-mail" placeholder = "Digite seu e-mail" [setup.login.password] @@ -5840,7 +6011,7 @@ paragraph = "PĆ”gina de parĆ”grafos" sparse = "Texto esparso" [pdfTextEditor.groupingMode] -auto = "Auto" +auto = "AutomĆ”tico" paragraph = "ParĆ”grafo" singleLine = "Linha Ćŗnica" @@ -5853,6 +6024,7 @@ earlyAccess = "Acesso antecipado" reset = "Reverter alteraƧƵes" downloadJson = "Baixar JSON" generatePdf = "Gerar PDF" +saveChanges = "Salvar alteraƧƵes" [pdfTextEditor.options.autoScaleText] title = "Dimensionar texto automaticamente para caber nas caixas" @@ -5890,6 +6062,8 @@ alpha = "Este visualizador alpha ainda estĆ” evoluindo—certas fontes, cores, e [pdfTextEditor.empty] title = "Nenhum documento carregado" subtitle = "Carregue um PDF ou JSON para comeƧar a editar texto." +dropzone = "Arraste e solte um arquivo PDF ou JSON aqui, ou clique para procurar" +dropzoneWithFiles = "Selecione um arquivo na aba Arquivos ou arraste e solte um arquivo PDF ou JSON aqui, ou clique para procurar" [pdfTextEditor.welcomeBanner] title = "Bem-vindo ao Editor de Texto PDF (Acesso antecipado)" diff --git a/frontend/public/locales/pt-PT/translation.toml b/frontend/public/locales/pt-PT/translation.toml index e530345e6b..5e89a96125 100644 --- a/frontend/public/locales/pt-PT/translation.toml +++ b/frontend/public/locales/pt-PT/translation.toml @@ -131,7 +131,7 @@ unsupported = "NĆ£o suportado" [toolPanel] placeholder = "Escolha uma ferramenta para comeƧar" -alpha = "Alpha" +alpha = "Alfa" premiumFeature = "Funcionalidade premium:" comingSoon = "Em breve:" @@ -163,6 +163,11 @@ unfavorite = "Remover dos favoritos" fullscreen = "Mudar para modo de ecrĆ£ inteiro" sidebar = "Mudar para modo de barra lateral" +[backendStartup] +notFoundTitle = "Backend nĆ£o encontrado" +retry = "Tentar novamente" +unreachable = "A aplicação nĆ£o consegue ligar-se ao backend neste momento. Verifique o estado do backend e a conectividade de rede e tente novamente." + [zipWarning] title = "Ficheiro ZIP grande" message = "Este ZIP contĆ©m {{count}} ficheiros. Extrair mesmo assim?" @@ -364,12 +369,12 @@ usageAnalytics = "AnĆ”lise de utilização" [settings.policiesPrivacy] title = "PolĆ­ticas e Privacidade" -legal = "Legal" +legal = "JurĆ­dico" privacy = "Privacidade" [settings.developer] title = "Programador" -apiKeys = "API Keys" +apiKeys = "Chaves de API" [settings.tooltips] enableLoginFirst = "Ative primeiro o modo de login" @@ -912,6 +917,9 @@ desc = "Crie fluxos de trabalho de vĆ”rios passos encadeando aƧƵes de PDF. Ide desc = "SobrepƵe PDFs em cima de outro PDF" title = "Sobrepor PDFs" +[home.pdfTextEditor] +title = "Editor de texto de PDF" +desc = "Edite texto e imagens existentes dentro de PDFs" [home.addText] tags = "texto,anotação,etiqueta" @@ -1217,7 +1225,7 @@ odtExt = "Texto OpenDocument (.odt)" pptExt = "PowerPoint (.pptx)" odpExt = "Apresentação OpenDocument (.odp)" txtExt = "Texto simples (.txt)" -rtfExt = "Rich Text Format (.rtf)" +rtfExt = "Formato de Texto Enriquecido (.rtf)" selectedFiles = "Ficheiros selecionados" noFileSelected = "Nenhum ficheiro selecionado. Use o painel de ficheiros para adicionar ficheiros." convertFiles = "Converter ficheiros" @@ -1360,7 +1368,7 @@ title = "Adicionar Marca de Ɓgua" desc = "Adicionar marcas de Ć”gua de texto ou imagem a ficheiros PDF" completed = "Marca de Ć”gua adicionada" submit = "Adicionar Marca de Ɓgua" -filenamePrefix = "watermarked" +filenamePrefix = "com-marca-de-Ć”gua" [watermark.error] failed = "Ocorreu um erro ao adicionar a marca de Ć”gua ao PDF." @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Assinatura desenhada" defaultImageLabel = "Assinatura carregada" defaultTextLabel = "Assinatura digitada" saveButton = "Guardar assinatura" +savePersonal = "Guardar como pessoal" +saveShared = "Guardar como partilhada" saveUnavailable = "Crie primeiro uma assinatura para a guardar." noChanges = "A assinatura atual jĆ” estĆ” guardada." +tempStorageTitle = "Armazenamento temporĆ”rio do navegador" +tempStorageDescription = "As assinaturas sĆ£o armazenadas apenas no seu navegador. SerĆ£o perdidas se limpar os dados do navegador ou mudar de navegador." +personalHeading = "Assinaturas pessoais" +sharedHeading = "Assinaturas partilhadas" +personalDescription = "Apenas vocĆŖ pode ver estas assinaturas." +sharedDescription = "Todos os utilizadores podem ver e usar estas assinaturas." [sign.saved.type] canvas = "Desenho" @@ -2841,7 +2857,7 @@ label = "Fator de escala" [adjustPageScale.pageSize] label = "Tamanho da pĆ”gina de destino" keep = "Manter tamanho original" -letter = "Letter" +letter = "Carta" legal = "Legal" [adjustPageScale.error] @@ -3020,6 +3036,91 @@ title = "Obter Informação do PDF" header = "Obter Informação do PDF" submit = "Obter Informação" downloadJson = "Transferir JSON" +processing = "A extrair informaƧƵes..." +results = "Resultados" +noResults = "Execute a ferramenta para gerar um relatório." +downloads = "TransferĆŖncias" +noneDetected = "Nenhum detetado" +indexTitle = "ƍndice" + +[getPdfInfo.report] +entryLabel = "Resumo completo das informaƧƵes" +shortTitle = "InformaƧƵes do PDF" + +[getPdfInfo.sections] +metadata = "Metadados" +formFields = "Campos do formulĆ”rio" +basicInfo = "InformaƧƵes bĆ”sicas" +documentInfo = "InformaƧƵes do documento" +compliance = "Conformidade" +encryption = "Encriptação" +permissions = "PermissƵes" +other = "Outros" +perPageInfo = "InformaƧƵes por pĆ”gina" +tableOfContents = "ƍndice" + +[getPdfInfo.other] +attachments = "Anexos" +embeddedFiles = "Ficheiros incorporados" +javaScript = "JavaScript" +layers = "Camadas" +structureTree = "Ɓrvore de estrutura" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Tamanho" +annotations = "AnotaƧƵes" +images = "Imagens" +links = "LigaƧƵes" +fonts = "Tipos de letra" +xobjects = "Contagens de XObject" +multimedia = "MultimĆ©dia" + +[getPdfInfo.summary] +pages = "PĆ”ginas" +fileSize = "Tamanho do ficheiro" +pdfVersion = "VersĆ£o do PDF" +language = "Idioma" +title = "Resumo do PDF" +author = "Autor" +created = "Criado" +modified = "Modificado" +permsAll = "Todas as permissƵes autorizadas" +permsRestricted = "{{count}} restriƧƵes" +permsMixed = "Algumas permissƵes restritas" +hasCompliance = "Tem normas de conformidade" +noCompliance = "Sem normas de conformidade" +basic = "InformaƧƵes bĆ”sicas" +documentInfo = "InformaƧƵes do documento" +securityTitle = "Estado de seguranƧa" +technical = "TĆ©cnico" +overviewTitle = "VisĆ£o geral do PDF" + +[getPdfInfo.summary.security] +encrypted = "PDF encriptado - Proteção por palavra-passe presente" +unencrypted = "PDF nĆ£o encriptado - Sem proteção por palavra-passe" + +[getPdfInfo.summary.tech] +images = "Imagens" +fonts = "Tipos de letra" +formFields = "Campos do formulĆ”rio" +embeddedFiles = "Ficheiros incorporados" +javaScript = "JavaScript" +layers = "Camadas" +bookmarks = "Marcadores" +multimedia = "MultimĆ©dia" + +[getPdfInfo.summary.overview] +untitled = "um documento sem tĆ­tulo" +unknown = "Autor desconhecido" +text = "Este Ć© um PDF de {{pages}} pĆ”ginas intitulado {{title}}, criado por {{author}} (versĆ£o do PDF {{version}})." + +[getPdfInfo.error] +partial = "Alguns ficheiros nĆ£o puderam ser processados." +unexpected = "Erro inesperado durante a extração." + +[getPdfInfo.status] +complete = "Extração concluĆ­da" [extractPage] tags = "extrair" @@ -3438,6 +3539,9 @@ signinTitle = "Por favor inicie sessĆ£o" ssoSignIn = "Iniciar sessĆ£o via Single Sign-On" oAuth2AutoCreateDisabled = "Criação AutomĆ”tica de Utilizador OAUTH2 Desativada" oAuth2AdminBlockedUser = "O registo ou login de utilizadores nĆ£o registados estĆ” atualmente bloqueado. Por favor contacte o administrador." +oAuth2RequiresLicense = "O inĆ­cio de sessĆ£o via OAuth/SSO requer uma licenƧa paga (Server ou Enterprise). Contacte o administrador para atualizar o seu plano." +saml2RequiresLicense = "O inĆ­cio de sessĆ£o SAML requer uma licenƧa paga (Server ou Enterprise). Contacte o administrador para atualizar o seu plano." +maxUsersReached = "Foi atingido o nĆŗmero mĆ”ximo de utilizadores da sua licenƧa atual. Contacte o administrador para atualizar o seu plano ou adicionar mais lugares." oauth2RequestNotFound = "Pedido de autorização nĆ£o encontrado" oauth2InvalidUserInfoResponse = "Resposta de Informação de Utilizador InvĆ”lida" oauth2invalidRequest = "Pedido InvĆ”lido" @@ -3846,14 +3950,17 @@ fitToWidth = "Ajustar Ć  largura" actualSize = "Tamanho real" [viewer] +cannotPreviewFile = "NĆ£o Ć© possĆ­vel prĆ©-visualizar o ficheiro" +dualPageView = "Vista de duas pĆ”ginas" firstPage = "Primeira pĆ”gina" lastPage = "Última pĆ”gina" -previousPage = "PĆ”gina anterior" nextPage = "PĆ”gina seguinte" +onlyPdfSupported = "O visualizador só suporta ficheiros PDF. Este ficheiro parece ter um formato diferente." +previousPage = "PĆ”gina anterior" +singlePageView = "Vista de pĆ”gina Ćŗnica" +unknownFile = "Ficheiro desconhecido" zoomIn = "Ampliar" zoomOut = "Reduzir" -singlePageView = "Vista de pĆ”gina Ćŗnica" -dualPageView = "Vista de duas pĆ”ginas" [rightRail] closeSelected = "Fechar ficheiros selecionados" @@ -3877,6 +3984,7 @@ toggleSidebar = "Alternar barra lateral" exportSelected = "Exportar pĆ”ginas selecionadas" toggleAnnotations = "Alternar visibilidade das anotaƧƵes" annotationMode = "Alternar modo de anotação" +print = "Imprimir PDF" draw = "Desenhar" save = "Guardar" saveChanges = "Guardar alteraƧƵes" @@ -4494,6 +4602,7 @@ description = "URL ou nome de ficheiro para o impressum (obrigatório em algumas title = "Premium e Enterprise" description = "Configurar a sua chave de licenƧa premium ou enterprise." license = "Configuração de licenƧa" +noInput = "ForneƧa uma chave ou ficheiro de licenƧa" [admin.settings.premium.licenseKey] toggle = "Tem uma chave de licenƧa ou ficheiro de certificado?" @@ -4511,6 +4620,25 @@ line1 = "Sobrescrever a sua chave de licenƧa atual nĆ£o pode ser anulado." line2 = "A sua licenƧa anterior serĆ” perdida permanentemente, a menos que a tenha guardado noutro local." line3 = "Importante: mantenha as chaves de licenƧa privadas e seguras. Nunca as partilhe publicamente." +[admin.settings.premium.inputMethod] +text = "Chave de licenƧa" +file = "Ficheiro de certificado" + +[admin.settings.premium.file] +label = "Ficheiro de certificado de licenƧa" +description = "Carregue o seu ficheiro de licenƧa .lic ou .cert de compras offline" +choose = "Escolher ficheiro de licenƧa" +selected = "Selecionado: {{filename}} ({{size}})" +successMessage = "Ficheiro de licenƧa carregado e ativado com sucesso. NĆ£o Ć© necessĆ”rio reiniciar." + +[admin.settings.premium.currentLicense] +title = "LicenƧa ativa" +file = "Origem: ficheiro de licenƧa ({{path}})" +key = "Origem: chave de licenƧa" +type = "Tipo: {{type}}" +noInput = "ForneƧa uma chave de licenƧa ou carregue um ficheiro de certificado" +success = "Sucesso" + [admin.settings.premium.enabled] label = "Ativar funcionalidades premium" description = "Ativar verificaƧƵes de chave de licenƧa para funcionalidades pro/enterprise" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} selecionado(s)" download = "Transferir" delete = "Eliminar" unsupported = "NĆ£o suportado" +active = "Ativo" addToUpload = "Adicionar ao carregamento" +closeFile = "Fechar ficheiro" deleteAll = "Eliminar tudo" loadingFiles = "A carregar ficheiros..." noFiles = "NĆ£o hĆ” ficheiros disponĆ­veis" @@ -5132,7 +5262,7 @@ upgrade = "Atualizar agora →" freeTitle = "LicenƧa do servidor" overLimitTitle = "Ɖ necessĆ”ria licenƧa de servidor" overLimitBody = "A nossa licenƧa permite atĆ© {{freeTierLimit}} utilizadores gratuitos por servidor. Tem {{overLimitUserCopy}} utilizadores Stirling. Para continuar sem interrupƧƵes, atualize para o plano Stirling Server - lugares ilimitados, edição de texto em PDF e controlo total de administração por $99/servidor/mĆŖs." -freeBody = "A nossa licenƧa Open-Core permite atĆ© {{freeTierLimit}} utilizadores gratuitos por servidor. Para escalar sem interrupƧƵes e obter acesso antecipado Ć  nossa nova ferramenta de edição de texto PDF, recomendamos o plano Stirling Server - edição completa e lugares ilimitados por $99/servidor/mĆŖs." +freeBody = "O nosso licenciamento Open-Core permite atĆ© {{freeTierLimit}} utilizadores gratuitos por servidor. Para escalar sem interrupƧƵes, recomendamos o plano Stirling Server - lugares ilimitados e suporte SSO por $99/servidor/mĆŖs." [onboarding.desktopInstall] title = "Transferir" @@ -5237,6 +5367,31 @@ error = "Falha ao atualizar o estado do utilizador" success = "Utilizador eliminado com sucesso" error = "Falha ao eliminar utilizador" +[workspace.people.changePassword] +action = "Alterar palavra-passe" +title = "Alterar palavra-passe" +subtitle = "Atualizar a palavra-passe de" +newPassword = "Nova palavra-passe" +confirmPassword = "Confirmar palavra-passe" +placeholder = "Introduza uma nova palavra-passe" +confirmPlaceholder = "Introduza novamente a nova palavra-passe" +passwordRequired = "Por favor, introduza uma nova palavra-passe" +passwordMismatch = "As palavras-passe nĆ£o coincidem" +generateRandom = "Gerar palavra-passe segura" +generatedPreview = "Palavra-passe gerada:" +copyTooltip = "Copiar para a Ć”rea de transferĆŖncia" +copiedToClipboard = "Palavra-passe copiada para a Ć”rea de transferĆŖncia" +copyFailed = "Falha ao copiar a palavra-passe" +sendEmail = "Enviar email ao utilizador sobre esta alteração" +includePassword = "Incluir a nova palavra-passe no email" +forcePasswordChange = "Obrigar o utilizador a alterar a palavra-passe no próximo inĆ­cio de sessĆ£o" +emailUnavailable = "O email deste utilizador nĆ£o Ć© um endereƧo vĆ”lido. As notificaƧƵes estĆ£o desativadas." +smtpDisabled = "As notificaƧƵes por email requerem que o SMTP esteja ativado nas definiƧƵes." +notifyOnly = "SerĆ” enviado um email sem a palavra-passe, informando o utilizador de que um administrador a alterou." +submit = "Guardar palavra-passe" +success = "Palavra-passe atualizada com sucesso" +error = "Falha ao atualizar a palavra-passe" + [workspace.people.emailInvite] tab = "Convite por email" description = "Escreva ou cole emails abaixo, separados por vĆ­rgulas. Os utilizadores receberĆ£o credenciais de login por email." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Ɖ necessĆ”rio pelo menos um endereƧo de email" submit = "Enviar convites" success = "utilizador(es) convidado(s) com sucesso" -partialSuccess = "Alguns convites falharam" +partialFailure = "Alguns convites falharam" allFailed = "Falha ao convidar utilizadores" error = "Falha ao enviar convites" @@ -5282,7 +5437,7 @@ submit = "Gerar link de convite" [workspace.people.inviteMode] username = "Nome de utilizador" email = "Email" -link = "Link" +link = "Ligação" emailDisabled = "Convites por email requerem configuração de SMTP e mail.enableInvites=true nas definiƧƵes" [workspace.people.license] @@ -5770,6 +5925,7 @@ subtitle = "Inicie sessĆ£o com a sua conta Stirling" [setup.selfhosted] title = "Iniciar sessĆ£o no servidor" subtitle = "Introduza as credenciais do seu servidor" +link = "ou ligue-se a uma conta autoalojada" [setup.server] title = "Ligar ao servidor" @@ -5788,6 +5944,14 @@ description = "Introduza o URL completo do seu servidor Stirling PDF autoalojado emptyUrl = "Introduza um URL de servidor" unreachable = "NĆ£o foi possĆ­vel ligar ao servidor" testFailed = "Falha no teste de ligação" +configFetch = "Falha ao obter a configuração do servidor. Verifique o URL e tente novamente." + +[setup.server.error.securityDisabled] +title = "InĆ­cio de sessĆ£o nĆ£o ativado" +body = "Este servidor nĆ£o tem o inĆ­cio de sessĆ£o ativado. Para se ligar a este servidor, tem de ativar a autenticação:" +step1 = "Defina DOCKER_ENABLE_SECURITY=true no seu ambiente" +step2 = "Ou defina security.enableLogin=true no settings.yml" +step3 = "Reinicie o servidor" [setup.login] title = "Iniciar sessĆ£o" @@ -5797,6 +5961,13 @@ submit = "Iniciar sessĆ£o" signInWith = "Iniciar sessĆ£o com" oauthPending = "A abrir o navegador para autenticação..." orContinueWith = "Ou continuar com email" +serverRequirement = "Nota: O servidor deve ter o inĆ­cio de sessĆ£o ativado." +showInstructions = "Como ativar?" +hideInstructions = "Ocultar instruƧƵes" +instructions = "Para ativar o inĆ­cio de sessĆ£o no seu servidor Stirling PDF:" +instructionsEnvVar = "Defina a variĆ”vel de ambiente:" +instructionsOrYml = "Ou em settings.yml:" +instructionsRestart = "Em seguida, reinicie o servidor para que as alteraƧƵes tenham efeito." [setup.login.username] label = "Nome de utilizador" @@ -5840,7 +6011,7 @@ paragraph = "PĆ”gina de parĆ”grafos" sparse = "Texto disperso" [pdfTextEditor.groupingMode] -auto = "Auto" +auto = "AutomĆ”tico" paragraph = "ParĆ”grafo" singleLine = "Linha Ćŗnica" @@ -5853,6 +6024,7 @@ earlyAccess = "Acesso antecipado" reset = "Repor alteraƧƵes" downloadJson = "Transferir JSON" generatePdf = "Gerar PDF" +saveChanges = "Guardar alteraƧƵes" [pdfTextEditor.options.autoScaleText] title = "Dimensionar texto automaticamente para caber nas caixas" @@ -5890,6 +6062,8 @@ alpha = "Este visualizador alpha ainda estĆ” a evoluir—certas fontes, cores, e [pdfTextEditor.empty] title = "Nenhum documento carregado" subtitle = "Carregue um ficheiro PDF ou JSON para comeƧar a editar o conteĆŗdo de texto." +dropzone = "Arraste e largue aqui um ficheiro PDF ou JSON, ou clique para procurar" +dropzoneWithFiles = "Selecione um ficheiro no separador Ficheiros, ou arraste e largue aqui um ficheiro PDF ou JSON, ou clique para procurar" [pdfTextEditor.welcomeBanner] title = "Bem-vindo ao PDF Text Editor (Acesso Antecipado)" @@ -5932,7 +6106,7 @@ warnings = "Avisos" suggestions = "Notas" currentPageFonts = "Fontes nesta pĆ”gina" allFonts = "Todas as fontes" -fallback = "fallback" +fallback = "alternativa" missing = "em falta" perfectMessage = "Todas as fontes podem ser reproduzidas na perfeição." warningMessage = "Algumas fontes podem nĆ£o ser renderizadas corretamente." diff --git a/frontend/public/locales/ro-RO/translation.toml b/frontend/public/locales/ro-RO/translation.toml index 57c2ce4812..927a10c5aa 100644 --- a/frontend/public/locales/ro-RO/translation.toml +++ b/frontend/public/locales/ro-RO/translation.toml @@ -163,6 +163,11 @@ unfavorite = "Eliminați din favorite" fullscreen = "Comutați la modul ecran complet" sidebar = "Comutați la modul bară laterală" +[backendStartup] +notFoundTitle = "Backend negăsit" +retry = "ReĆ®ncearcă" +unreachable = "Aplicația nu se poate conecta Ć®n prezent la backend. Verificați starea backend-ului și conexiunea la rețea, apoi Ć®ncercați din nou." + [zipWarning] title = "Fișier ZIP mare" message = "Acest ZIP conține {{count}} fișiere. Extrageți oricum?" @@ -912,6 +917,9 @@ desc = "Construiți fluxuri cu mai mulți pași legĆ¢nd acțiuni PDF. Ideal pent desc = "Suprapune PDF-uri peste alt PDF" title = "Suprapune PDF-uri" +[home.pdfTextEditor] +title = "Editor de text PDF" +desc = "Editați textul și imaginile existente Ć®n PDF-uri" [home.addText] tags = "text,anotare,etichetă" @@ -1213,11 +1221,11 @@ pdfaDigitalSignatureWarning = "PDF-ul conține o semnătură digitală. Aceasta fileFormat = "Format fișier" wordDoc = "Document Word" wordDocExt = "Document Word (.docx)" -odtExt = "OpenDocument Text (.odt)" +odtExt = "Text OpenDocument (.odt)" pptExt = "PowerPoint (.pptx)" -odpExt = "OpenDocument Presentation (.odp)" +odpExt = "Prezentare OpenDocument (.odp)" txtExt = "Text simplu (.txt)" -rtfExt = "Rich Text Format (.rtf)" +rtfExt = "Format Rich Text (.rtf)" selectedFiles = "Fișiere selectate" noFileSelected = "Niciun fișier selectat. Folosiți panoul de fișiere pentru a adăuga fișiere." convertFiles = "Convertiți fișiere" @@ -1395,7 +1403,7 @@ height = "Spațiere pe Ć®nălțime" width = "Spațiere pe lățime" [watermark.alphabet] -roman = "Roman/Latin" +roman = "Romano/Latin" arabic = "Arabă" japanese = "Japoneză" korean = "Coreeană" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Semnătură desenată" defaultImageLabel = "Semnătură Ć®ncărcată" defaultTextLabel = "Semnătură tastată" saveButton = "Salvează semnătura" +savePersonal = "Salvați ca personal" +saveShared = "Salvați ca partajat" saveUnavailable = "Creează mai Ć®ntĆ¢i o semnătură pentru a o salva." noChanges = "Semnătura curentă este deja salvată." +tempStorageTitle = "Stocare temporară Ć®n browser" +tempStorageDescription = "Semnăturile sunt stocate doar Ć®n browserul dvs. Vor fi pierdute dacă ștergeți datele browserului sau schimbați browserul." +personalHeading = "Semnături personale" +sharedHeading = "Semnături partajate" +personalDescription = "Doar dvs. puteți vedea aceste semnături." +sharedDescription = "Toți utilizatorii pot vedea și utiliza aceste semnături." [sign.saved.type] canvas = "Desen" @@ -3020,6 +3036,91 @@ title = "Obține Informații despre PDF" header = "Obține Informații despre PDF" submit = "Obține Informații" downloadJson = "Descarcă JSON" +processing = "Se extrag informațiile..." +results = "Rezultate" +noResults = "Rulați instrumentul pentru a genera un raport." +downloads = "Descărcări" +noneDetected = "Nu s-a detectat nimic" +indexTitle = "Index" + +[getPdfInfo.report] +entryLabel = "Rezumat complet al informațiilor" +shortTitle = "Informații PDF" + +[getPdfInfo.sections] +metadata = "Metadate" +formFields = "CĆ¢mpuri de formular" +basicInfo = "Informații de bază" +documentInfo = "Informații despre document" +compliance = "Conformitate" +encryption = "Criptare" +permissions = "Permisiuni" +other = "Altele" +perPageInfo = "Informații pe pagină" +tableOfContents = "Cuprins" + +[getPdfInfo.other] +attachments = "Atașamente" +embeddedFiles = "Fișiere Ć®ncorporate" +javaScript = "JavaScript" +layers = "Straturi" +structureTree = "Arbore de structură" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Dimensiune" +annotations = "Adnotări" +images = "Imagini" +links = "Linkuri" +fonts = "Fonturi" +xobjects = "Număr de XObject-uri" +multimedia = "Multimedia" + +[getPdfInfo.summary] +pages = "Pagini" +fileSize = "Dimensiune fișier" +pdfVersion = "Versiune PDF" +language = "Limbă" +title = "Rezumat PDF" +author = "Autor" +created = "Creat" +modified = "Modificat" +permsAll = "Toate permisiunile sunt acordate" +permsRestricted = "{{count}} restricții" +permsMixed = "Unele permisiuni sunt restricționate" +hasCompliance = "Are standarde de conformitate" +noCompliance = "Fără standarde de conformitate" +basic = "Informații de bază" +documentInfo = "Informații despre document" +securityTitle = "Starea securității" +technical = "Tehnic" +overviewTitle = "Prezentare generală PDF" + +[getPdfInfo.summary.security] +encrypted = "PDF criptat - Protejat cu parolă" +unencrypted = "PDF necriptat - Fără protecție prin parolă" + +[getPdfInfo.summary.tech] +images = "Imagini" +fonts = "Fonturi" +formFields = "CĆ¢mpuri de formular" +embeddedFiles = "Fișiere Ć®ncorporate" +javaScript = "JavaScript" +layers = "Straturi" +bookmarks = "Marcaje" +multimedia = "Multimedia" + +[getPdfInfo.summary.overview] +untitled = "un document fără titlu" +unknown = "Autor necunoscut" +text = "Acesta este un PDF de {{pages}} pagini, intitulat {{title}}, creat de {{author}} (versiune PDF {{version}})." + +[getPdfInfo.error] +partial = "Unele fișiere nu au putut fi procesate." +unexpected = "Eroare neașteptată Ć®n timpul extragerii." + +[getPdfInfo.status] +complete = "Extragere finalizată" [extractPage] tags = "extrage" @@ -3438,6 +3539,9 @@ signinTitle = "Te rugăm să te autentifici" ssoSignIn = "Conectare prin conectare unică" oAuth2AutoCreateDisabled = "OAUTH2 Creare automată utilizator dezactivată" oAuth2AdminBlockedUser = "Ǝnregistrarea sau conectarea utilizatorilor neĆ®nregistrați este Ć®n prezent blocată. Te rugăm să contactezi administratorul." +oAuth2RequiresLicense = "Autentificarea OAuth/SSO necesită o licență plătită (Server sau Enterprise). Contactați administratorul pentru a vă actualiza planul." +saml2RequiresLicense = "Autentificarea SAML necesită o licență plătită (Server sau Enterprise). Contactați administratorul pentru a vă actualiza planul." +maxUsersReached = "Numărul maxim de utilizatori a fost atins pentru licența curentă. Contactați administratorul pentru a vă actualiza planul sau pentru a adăuga mai multe locuri." oauth2RequestNotFound = "Cererea de autorizare nu a fost găsită" oauth2InvalidUserInfoResponse = "Răspuns Invalid la Informațiile Utilizatorului" oauth2invalidRequest = "Cerere Invalidă" @@ -3846,14 +3950,17 @@ fitToWidth = "Potriviți la lățime" actualSize = "Dimensiune reală" [viewer] +cannotPreviewFile = "Nu se poate previzualiza fișierul" +dualPageView = "Vizualizare cu două pagini" firstPage = "Prima pagină" lastPage = "Ultima pagină" -previousPage = "Pagina anterioară" nextPage = "Pagina următoare" +onlyPdfSupported = "Vizualizatorul acceptă doar fișiere PDF. Acest fișier pare a fi Ć®ntr-un format diferit." +previousPage = "Pagina anterioară" +singlePageView = "Vizualizare cu o singură pagină" +unknownFile = "Fișier necunoscut" zoomIn = "Măriți" zoomOut = "Micșorați" -singlePageView = "Vizualizare cu o singură pagină" -dualPageView = "Vizualizare cu două pagini" [rightRail] closeSelected = "Ǝnchideți fișierele selectate" @@ -3877,6 +3984,7 @@ toggleSidebar = "Comutați bara laterală" exportSelected = "Exportați paginile selectate" toggleAnnotations = "Comutați vizibilitatea adnotărilor" annotationMode = "Comutați modul de adnotare" +print = "Imprimați PDF" draw = "Desenați" save = "Salvați" saveChanges = "Salvați modificările" @@ -3925,7 +4033,7 @@ files = "Fișiere" activity = "Jurnal" help = "Ajutor" account = "Cont" -config = "Config" +config = "Configurare" settings = "Setări" adminSettings = "Setări admin" allTools = "All Tools" @@ -4153,7 +4261,7 @@ description = "Urmărește acțiunile utilizatorilor și evenimentele de sistem [admin.settings.security.audit.level] label = "Nivel audit" -description = "0=OFF, 1=BASIC, 2=STANDARD, 3=VERBOSE" +description = "0=OPRIT, 1=DE BAZĂ, 2=STANDARD, 3=DETALIAT" [admin.settings.security.audit.retentionDays] label = "Păstrare audit (zile)" @@ -4494,6 +4602,7 @@ description = "URL sau nume de fișier către impressum (necesar Ć®n unele juris title = "Premium și Enterprise" description = "Configurați cheia de licență premium sau enterprise." license = "Configurare licență" +noInput = "Vă rugăm să furnizați o cheie sau un fișier de licență" [admin.settings.premium.licenseKey] toggle = "Ai o cheie de licență sau un fișier certificat?" @@ -4511,6 +4620,25 @@ line1 = "Suprascrierea cheii de licență curente nu poate fi anulată." line2 = "Licența anterioară va fi pierdută definitiv dacă nu ai o copie de rezervă." line3 = "Important: Păstrează cheile de licență private și Ć®n siguranță. Nu le distribui public." +[admin.settings.premium.inputMethod] +text = "Cheie de licență" +file = "Fișier de certificat" + +[admin.settings.premium.file] +label = "Fișier certificat de licență" +description = "Ǝncărcați fișierul de licență .lic sau .cert din achizițiile offline" +choose = "Alegeți fișierul de licență" +selected = "Selectat: {{filename}} ({{size}})" +successMessage = "Fișierul de licență a fost Ć®ncărcat și activat cu succes. Nu este necesară repornirea." + +[admin.settings.premium.currentLicense] +title = "Licență activă" +file = "Sursă: Fișier de licență ({{path}})" +key = "Sursă: Cheie de licență" +type = "Tip: {{type}}" +noInput = "Vă rugăm să furnizați o cheie de licență sau să Ć®ncărcați un fișier de certificat" +success = "Succes" + [admin.settings.premium.enabled] label = "Activează funcțiile Premium" description = "Activează verificările cheii de licență pentru funcțiile pro/enterprise" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} selectate" download = "Descarcă" delete = "Șterge" unsupported = "Nesuportat" +active = "Activ" addToUpload = "Adăugați la Ć®ncărcare" +closeFile = "Ǝnchide fișierul" deleteAll = "Ștergeți tot" loadingFiles = "Se Ć®ncarcă fișierele..." noFiles = "Nu există fișiere disponibile" @@ -5132,7 +5262,7 @@ upgrade = "Fă upgrade acum →" freeTitle = "Licență server" overLimitTitle = "Necesită licență de server" overLimitBody = "Politica noastră de licențiere permite pĆ¢nă la {{freeTierLimit}} utilizatori gratuit per server. Ai {{overLimitUserCopy}} utilizatori Stirling. Pentru a continua fără Ć®ntreruperi, fă upgrade la planul Stirling Server - locuri nelimitate, editare text PDF și control complet de admin pentru $99/server/lună." -freeBody = "Licențierea noastră Open-Core permite pĆ¢nă la {{freeTierLimit}} utilizatori gratuit per server. Pentru a scala fără Ć®ntreruperi și a primi acces timpuriu la noul nostru instrument de editare text PDF, recomandăm planul Stirling Server - editare completă și locuri nelimitate pentru $99/server/lună." +freeBody = "Licențierea noastră Open-Core permite pĆ¢nă la {{freeTierLimit}} utilizatori gratuit per server. Pentru scalare fără Ć®ntreruperi, recomandăm planul Stirling Server - locuri nelimitate și suport SSO pentru $99/server/lună." [onboarding.desktopInstall] title = "Descărcare" @@ -5237,6 +5367,31 @@ error = "Actualizarea stării utilizatorului a eșuat" success = "Utilizator șters cu succes" error = "Ștergerea utilizatorului a eșuat" +[workspace.people.changePassword] +action = "Schimbă parola" +title = "Schimbă parola" +subtitle = "Actualizați parola pentru" +newPassword = "Parolă nouă" +confirmPassword = "Confirmă parola" +placeholder = "Introduceți o parolă nouă" +confirmPlaceholder = "Reintroduceți parola nouă" +passwordRequired = "Introduceți o parolă nouă" +passwordMismatch = "Parolele nu coincid" +generateRandom = "Generează o parolă sigură" +generatedPreview = "Parolă generată:" +copyTooltip = "Copiază Ć®n clipboard" +copiedToClipboard = "Parola a fost copiată Ć®n clipboard" +copyFailed = "Copierea parolei a eșuat" +sendEmail = "Trimite un email utilizatorului despre această modificare" +includePassword = "Include parola nouă Ć®n email" +forcePasswordChange = "Forțează utilizatorul să schimbe parola la următoarea autentificare" +emailUnavailable = "Emailul acestui utilizator nu este o adresă de email validă. Notificările sunt dezactivate." +smtpDisabled = "Notificările prin email necesită activarea SMTP Ć®n setări." +notifyOnly = "Se va trimite un email fără parolă, informĆ¢nd utilizatorul că un administrator a schimbat-o." +submit = "Actualizează parola" +success = "Parola a fost actualizată cu succes" +error = "Actualizarea parolei a eșuat" + [workspace.people.emailInvite] tab = "Invitație prin email" description = "Tastați sau lipiți emailuri mai jos, separate prin virgule. Utilizatorii vor primi datele de conectare prin email." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Este necesară cel puțin o adresă de email" submit = "Trimiteți invitații" success = "utilizator(i) invitați cu succes" -partialSuccess = "Unele invitații au eșuat" +partialFailure = "Unele invitații au eșuat" allFailed = "Invitarea utilizatorilor a eșuat" error = "Trimiterea invitațiilor a eșuat" @@ -5380,7 +5535,7 @@ submit = "Schimbă echipa" currency = "Monedă" popular = "Popular" current = "Plan curent" -upgrade = "Upgrade" +upgrade = "Actualizează" contact = "Contactează-ne" customPricing = "Personalizat" showComparison = "Compară toate funcțiile" @@ -5770,6 +5925,7 @@ subtitle = "Autentifică-te cu contul tău Stirling" [setup.selfhosted] title = "Autentifică-te pe server" subtitle = "Introdu acreditările serverului tău" +link = "sau conectați-vă la un cont self-hosted" [setup.server] title = "Conectează-te la server" @@ -5788,6 +5944,14 @@ description = "Introdu URL-ul complet al serverului tău Stirling PDF găzduit l emptyUrl = "Te rugăm să introduci un URL de server" unreachable = "Nu s-a putut conecta la server" testFailed = "Testul de conexiune a eșuat" +configFetch = "Nu s-a putut prelua configurația serverului. Verificați URL-ul și Ć®ncercați din nou." + +[setup.server.error.securityDisabled] +title = "Autentificarea nu este activată" +body = "Acest server nu are autentificarea activată. Pentru a vă conecta la acest server, trebuie să activați autentificarea:" +step1 = "Setați DOCKER_ENABLE_SECURITY=true Ć®n mediul dvs." +step2 = "Sau setați security.enableLogin=true Ć®n settings.yml" +step3 = "Reporniți serverul" [setup.login] title = "Autentificare" @@ -5797,6 +5961,13 @@ submit = "Autentificare" signInWith = "Autentifică-te cu" oauthPending = "Se deschide browserul pentru autentificare..." orContinueWith = "Sau continuă cu email" +serverRequirement = "Notă: Serverul trebuie să aibă autentificarea activată." +showInstructions = "Cum se activează?" +hideInstructions = "Ascunde instrucțiunile" +instructions = "Pentru a activa autentificarea pe serverul dvs. Stirling PDF:" +instructionsEnvVar = "Setați variabila de mediu:" +instructionsOrYml = "Sau Ć®n settings.yml:" +instructionsRestart = "Apoi reporniți serverul pentru ca modificările să intre Ć®n vigoare." [setup.login.username] label = "Utilizator" @@ -5853,6 +6024,7 @@ earlyAccess = "Acces timpuriu" reset = "Resetați modificările" downloadJson = "Descărcați JSON" generatePdf = "Generați PDF" +saveChanges = "Salvează modificările" [pdfTextEditor.options.autoScaleText] title = "Scalare automată a textului pentru a se potrivi Ć®n casete" @@ -5890,6 +6062,8 @@ alpha = "Acest vizualizator alpha este Ć®ncă Ć®n dezvoltare — anumite fonturi [pdfTextEditor.empty] title = "Niciun document Ć®ncărcat" subtitle = "Ǝncărcați un fișier PDF sau JSON pentru a Ć®ncepe editarea conținutului text." +dropzone = "Glisați și fixați aici un fișier PDF sau JSON, sau faceți clic pentru a răsfoi" +dropzoneWithFiles = "Selectați un fișier din fila Fișiere sau glisați și fixați aici un fișier PDF sau JSON, sau faceți clic pentru a răsfoi" [pdfTextEditor.welcomeBanner] title = "Bine ați venit la Editor text PDF (Acces timpuriu)" @@ -5932,7 +6106,7 @@ warnings = "Avertizări" suggestions = "Note" currentPageFonts = "Fonturi pe această pagină" allFonts = "Toate fonturile" -fallback = "fallback" +fallback = "rezervă" missing = "lipsește" perfectMessage = "Toate fonturile pot fi redate perfect." warningMessage = "Unele fonturi pot să nu fie redate corect." diff --git a/frontend/public/locales/ru-RU/translation.toml b/frontend/public/locales/ru-RU/translation.toml index 359bb57832..600c7dfc0c 100644 --- a/frontend/public/locales/ru-RU/translation.toml +++ b/frontend/public/locales/ru-RU/translation.toml @@ -163,6 +163,11 @@ unfavorite = "Š£Š“Š°Š»ŠøŃ‚ŃŒ ŠøŠ· избранного" fullscreen = "ŠŸŠµŃ€ŠµŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒŃŃ в ŠæŠ¾Š»Š½Š¾ŃŠŗŃ€Š°Š½Š½Ń‹Š¹ режим" sidebar = "ŠŸŠµŃ€ŠµŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒŃŃ в режим боковой панели" +[backendStartup] +notFoundTitle = "Š”ŠµŃ€Š²ŠµŃ€Š½Š°Ń Ń‡Š°ŃŃ‚ŃŒ не найГена" +retry = "ŠŸŠ¾Š²Ń‚Š¾Ń€ŠøŃ‚ŃŒ" +unreachable = "ŠŸŃ€ŠøŠ»Š¾Š¶ŠµŠ½ŠøŠµ сейчас не может ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒŃŃ Šŗ серверной части. ŠŸŃ€Š¾Š²ŠµŃ€ŃŒŃ‚Šµ ŃŠ¾ŃŃ‚Š¾ŃŠ½ŠøŠµ серверной части Šø ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ŠøŠµ Šŗ сети, затем повторите ŠæŠ¾ŠæŃ‹Ń‚ŠŗŃƒ." + [zipWarning] title = "Š‘Š¾Š»ŃŒŃˆŠ¾Š¹ ZIP-файл" message = "Этот ZIP соГержит {{count}} файлов. Все равно ŠøŠ·Š²Š»ŠµŃ‡ŃŒ?" @@ -912,6 +917,9 @@ desc = "ДозГавайте Š¼Š½Š¾Š³Š¾ŃˆŠ°Š³Š¾Š²Ń‹Šµ процессы, ŃŠ²ŃŠ· desc = "ŠŠ°Š»Š¾Š¶ŠøŃ‚ŃŒ оГин PDF поверх Š“Ń€ŃƒŠ³Š¾Š³Š¾" title = "ŠŠ°Š»Š¾Š¶ŠµŠ½ŠøŠµ PDF" +[home.pdfTextEditor] +title = "РеГактор текста PDF" +desc = "Š ŠµŠ“Š°ŠŗŃ‚ŠøŃ€ŃƒŠ¹Ń‚Šµ ŃŃƒŃ‰ŠµŃŃ‚Š²ŃƒŃŽŃ‰ŠøŠ¹ текст Šø ŠøŠ·Š¾Š±Ń€Š°Š¶ŠµŠ½ŠøŃ Š²Š½ŃƒŃ‚Ń€Šø PDF-файлов" [home.addText] tags = "текст,Š°Š½Š½Š¾Ń‚Š°Ń†ŠøŃ,ŃŃ€Š»Ń‹Šŗ" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Š ŠøŃŠ¾Š²Š°Š½Š½Š°Ń поГпись" defaultImageLabel = "Š—Š°Š³Ń€ŃƒŠ¶ŠµŠ½Š½Š°Ń поГпись" defaultTextLabel = "Š’Š²ŠµŠ“Ń‘Š½Š½Š°Ń поГпись" saveButton = "Š”Š¾Ń…Ń€Š°Š½ŠøŃ‚ŃŒ поГпись" +savePersonal = "Š”Š¾Ń…Ń€Š°Š½ŠøŃ‚ŃŒ как Š»ŠøŃ‡Š½ŃƒŃŽ" +saveShared = "Š”Š¾Ń…Ń€Š°Š½ŠøŃ‚ŃŒ как Š¾Š±Ń‰ŃƒŃŽ" saveUnavailable = "Дначала созГайте поГпись, чтобы ŃŠ¾Ń…Ń€Š°Š½ŠøŃ‚ŃŒ её." noChanges = "Š¢ŠµŠŗŃƒŃ‰Š°Ń поГпись уже сохранена." +tempStorageTitle = "Временное хранилище Š±Ń€Š°ŃƒŠ·ŠµŃ€Š°" +tempStorageDescription = "ПоГписи ŃŠ¾Ń…Ń€Š°Š½ŃŃŽŃ‚ŃŃ Ń‚Š¾Š»ŃŒŠŗŠ¾ в вашем Š±Ń€Š°ŃƒŠ·ŠµŃ€Šµ. ŠžŠ½Šø Š±ŃƒŠ“ŃƒŃ‚ ŠæŠ¾Ń‚ŠµŃ€ŃŠ½Ń‹, если вы очистите Ганные Š±Ń€Š°ŃƒŠ·ŠµŃ€Š° или смените Š±Ń€Š°ŃƒŠ·ŠµŃ€." +personalHeading = "Личные поГписи" +sharedHeading = "ŠžŠ±Ń‰ŠøŠµ поГписи" +personalDescription = "Эти поГписи виГны Ń‚Š¾Š»ŃŒŠŗŠ¾ вам." +sharedDescription = "Все ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Šø Š¼Š¾Š³ŃƒŃ‚ Š²ŠøŠ“ŠµŃ‚ŃŒ Šø ŠøŃŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŃŒ ŃŃ‚Šø поГписи." [sign.saved.type] canvas = "Рисунок" @@ -3020,6 +3036,91 @@ title = "ŠŸŠ¾Š»ŃƒŃ‡ŠøŃ‚ŃŒ ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃŽ о PDF" header = "ŠŸŠ¾Š»ŃƒŃ‡ŠøŃ‚ŃŒ ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃŽ о PDF" submit = "ŠŸŠ¾Š»ŃƒŃ‡ŠøŃ‚ŃŒ ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃŽ" downloadJson = "Š”ŠŗŠ°Ń‡Š°Ń‚ŃŒ JSON" +processing = "Š˜Š·Š²Š»ŠµŃ‡ŠµŠ½ŠøŠµ информации..." +results = "Š ŠµŠ·ŃƒŠ»ŃŒŃ‚Š°Ń‚Ń‹" +noResults = "Š—Š°ŠæŃƒŃŃ‚ŠøŃ‚Šµ ŠøŠ½ŃŃ‚Ń€ŃƒŠ¼ŠµŠ½Ń‚, чтобы ŃŠ¾Š·Š“Š°Ń‚ŃŒ отчет." +downloads = "Š—Š°Š³Ń€ŃƒŠ·ŠŗŠø" +noneDetected = "ŠŠøŃ‡ŠµŠ³Š¾ не Š¾Š±Š½Š°Ń€ŃƒŠ¶ŠµŠ½Š¾" +indexTitle = "ИнГекс" + +[getPdfInfo.report] +entryLabel = "ŠŸŠ¾Š»Š½Š°Ń своГка информации" +shortTitle = "Š˜Š½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃ о PDF" + +[getPdfInfo.sections] +metadata = "ŠœŠµŃ‚Š°Š“Š°Š½Š½Ń‹Šµ" +formFields = "ŠŸŠ¾Š»Ń формы" +basicInfo = "ŠžŃŠ½Š¾Š²Š½Š°Ń ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃ" +documentInfo = "Š”Š²ŠµŠ“ŠµŠ½ŠøŃ о Š“Š¾ŠŗŃƒŠ¼ŠµŠ½Ń‚Šµ" +compliance = "Доответствие" +encryption = "Шифрование" +permissions = "Š Š°Š·Ń€ŠµŃˆŠµŠ½ŠøŃ" +other = "Š”Ń€ŃƒŠ³Š¾Šµ" +perPageInfo = "Š˜Š½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃ по страницам" +tableOfContents = "ŠžŠ³Š»Š°Š²Š»ŠµŠ½ŠøŠµ" + +[getPdfInfo.other] +attachments = "Š’Š»Š¾Š¶ŠµŠ½ŠøŃ" +embeddedFiles = "Встроенные файлы" +javaScript = "JavaScript" +layers = "Длои" +structureTree = "Дерево ŃŃ‚Ń€ŃƒŠŗŃ‚ŃƒŃ€Ń‹" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Размер" +annotations = "Аннотации" +images = "Š˜Š·Š¾Š±Ń€Š°Š¶ŠµŠ½ŠøŃ" +links = "Дсылки" +fonts = "Шрифты" +xobjects = "ŠšŠ¾Š»ŠøŃ‡ŠµŃŃ‚Š²Š¾ XObject" +multimedia = "ŠœŃƒŠ»ŃŒŃ‚ŠøŠ¼ŠµŠ“ŠøŠ°" + +[getPdfInfo.summary] +pages = "Дтраницы" +fileSize = "Размер файла" +pdfVersion = "Š’ŠµŃ€ŃŠøŃ PDF" +language = "Язык" +title = "ДвоГка по PDF" +author = "Автор" +created = "ДозГано" +modified = "Изменено" +permsAll = "Все Ń€Š°Š·Ń€ŠµŃˆŠµŠ½ŠøŃ Š“Š¾ŃŃ‚ŃƒŠæŠ½Ń‹" +permsRestricted = "{{count}} ограничений" +permsMixed = "ŠŠµŠŗŠ¾Ń‚Š¾Ń€Ń‹Šµ Ń€Š°Š·Ń€ŠµŃˆŠµŠ½ŠøŃ ограничены" +hasCompliance = "Š•ŃŃ‚ŃŒ станГарты ŃŠ¾Š¾Ń‚Š²ŠµŃ‚ŃŃ‚Š²ŠøŃ" +noCompliance = "ŠŠµŃ‚ станГартов ŃŠ¾Š¾Ń‚Š²ŠµŃ‚ŃŃ‚Š²ŠøŃ" +basic = "ŠžŃŠ½Š¾Š²Š½Š°Ń ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃ" +documentInfo = "Š”Š²ŠµŠ“ŠµŠ½ŠøŃ о Š“Š¾ŠŗŃƒŠ¼ŠµŠ½Ń‚Šµ" +securityTitle = "Š”Ń‚Š°Ń‚ŃƒŃ безопасности" +technical = "Технические ŃŠ²ŠµŠ“ŠµŠ½ŠøŃ" +overviewTitle = "ŠžŠ±Š·Š¾Ń€ PDF" + +[getPdfInfo.summary.security] +encrypted = "Š—Š°ŃˆŠøŃ„Ń€Š¾Š²Š°Š½Š½Ń‹Š¹ PDF — ŠøŃŠæŠ¾Š»ŃŒŠ·ŃƒŠµŃ‚ŃŃ защита паролем" +unencrypted = "ŠŠµŃˆŠøŃ„Ń€Š¾Š²Š°Š½Š½Ń‹Š¹ PDF — защита паролем Š¾Ń‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚" + +[getPdfInfo.summary.tech] +images = "Š˜Š·Š¾Š±Ń€Š°Š¶ŠµŠ½ŠøŃ" +fonts = "Шрифты" +formFields = "ŠŸŠ¾Š»Ń формы" +embeddedFiles = "Встроенные файлы" +javaScript = "JavaScript" +layers = "Длои" +bookmarks = "ЗаклаГки" +multimedia = "ŠœŃƒŠ»ŃŒŃ‚ŠøŠ¼ŠµŠ“ŠøŠ°" + +[getPdfInfo.summary.overview] +untitled = "Š“Š¾ŠŗŃƒŠ¼ŠµŠ½Ń‚ без Š½Š°Š·Š²Š°Š½ŠøŃ" +unknown = "ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Ń‹Š¹ автор" +text = "Это PDF ŠøŠ· {{pages}} страниц поГ названием {{title}}, созГан {{author}} (Š²ŠµŃ€ŃŠøŃ PDF {{version}})." + +[getPdfInfo.error] +partial = "ŠŠµŠŗŠ¾Ń‚Š¾Ń€Ń‹Šµ файлы не уГалось Š¾Š±Ń€Š°Š±Š¾Ń‚Š°Ń‚ŃŒ." +unexpected = "ŠŠµŠæŃ€ŠµŠ“Š²ŠøŠ“ŠµŠ½Š½Š°Ń ошибка при извлечении." + +[getPdfInfo.status] +complete = "Š˜Š·Š²Š»ŠµŃ‡ŠµŠ½ŠøŠµ Š·Š°Š²ŠµŃ€ŃˆŠµŠ½Š¾" [extractPage] tags = "извлечение" @@ -3438,6 +3539,9 @@ signinTitle = "ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, войГите" ssoSignIn = "ВхоГ через еГиный вхоГ" oAuth2AutoCreateDisabled = "Автоматическое созГание ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»ŠµŠ¹ OAuth2 Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾" oAuth2AdminBlockedUser = "Š ŠµŠ³ŠøŃŃ‚Ń€Š°Ń†ŠøŃ или вхоГ незарегистрированных ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»ŠµŠ¹ в Š½Š°ŃŃ‚Š¾ŃŃ‰ŠµŠµ Š²Ń€ŠµŠ¼Ń заблокированы. ŠžŠ±Ń€Š°Ń‚ŠøŃ‚ŠµŃŃŒ Šŗ Š°Š“Š¼ŠøŠ½ŠøŃŃ‚Ń€Š°Ń‚Š¾Ń€Ńƒ." +oAuth2RequiresLicense = "ВхоГ через OAuth/SSO Ń‚Ń€ŠµŠ±ŃƒŠµŃ‚ ŠæŠ»Š°Ń‚Š½ŃƒŃŽ Š»ŠøŃ†ŠµŠ½Š·ŠøŃŽ (Server или Enterprise). ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŃŠ²ŃŠ¶ŠøŃ‚ŠµŃŃŒ с аГминистратором, чтобы Š¾Š±Š½Š¾Š²ŠøŃ‚ŃŒ ваш план." +saml2RequiresLicense = "ВхоГ через SAML Ń‚Ń€ŠµŠ±ŃƒŠµŃ‚ ŠæŠ»Š°Ń‚Š½ŃƒŃŽ Š»ŠøŃ†ŠµŠ½Š·ŠøŃŽ (Server или Enterprise). ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŃŠ²ŃŠ¶ŠøŃ‚ŠµŃŃŒ с аГминистратором, чтобы Š¾Š±Š½Š¾Š²ŠøŃ‚ŃŒ ваш план." +maxUsersReached = "Š”Š¾ŃŃ‚ŠøŠ³Š½ŃƒŃ‚Š¾ максимальное количество ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»ŠµŠ¹ Š“Š»Ń вашей Ń‚ŠµŠŗŃƒŃ‰ŠµŠ¹ лицензии. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŃŠ²ŃŠ¶ŠøŃ‚ŠµŃŃŒ с аГминистратором, чтобы Š¾Š±Š½Š¾Š²ŠøŃ‚ŃŒ ваш план или Š“Š¾Š±Š°Š²ŠøŃ‚ŃŒ места." oauth2RequestNotFound = "Запрос авторизации не найГен" oauth2InvalidUserInfoResponse = "ŠŠµŠ“ŠµŠ¹ŃŃ‚Š²ŠøŃ‚ŠµŠ»ŃŒŠ½Ń‹Š¹ ответ с информацией о ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Šµ" oauth2invalidRequest = "ŠŠµŠ“ŠµŠ¹ŃŃ‚Š²ŠøŃ‚ŠµŠ»ŃŒŠ½Ń‹Š¹ запрос" @@ -3846,14 +3950,17 @@ fitToWidth = "По ŃˆŠøŃ€ŠøŠ½Šµ" actualSize = "Фактический размер" [viewer] +cannotPreviewFile = "ŠŠµ ŃƒŠ“Š°Ń‘Ń‚ŃŃ ŠæŃ€Š¾ŃŠ¼Š¾Ń‚Ń€ŠµŃ‚ŃŒ файл" +dualPageView = "Š”Š²ŃƒŃ…ŃŃ‚Ń€Š°Š½ŠøŃ‡Š½Ń‹Š¹ виГ" firstPage = "ŠŸŠµŃ€Š²Š°Ń страница" lastPage = "ŠŸŠ¾ŃŠ»ŠµŠ“Š½ŃŃ страница" -previousPage = "ŠŸŃ€ŠµŠ“Ń‹Š“ŃƒŃ‰Š°Ń страница" nextPage = "Š”Š»ŠµŠ“ŃƒŃŽŃ‰Š°Ń страница" +onlyPdfSupported = "ŠŸŃ€Š¾ŃŠ¼Š¾Ń‚Ń€Ń‰ŠøŠŗ поГГерживает Ń‚Š¾Š»ŃŒŠŗŠ¾ PDF-файлы. ŠŸŠ¾Ń…Š¾Š¶Šµ, ŃŃ‚Š¾Ń‚ файл Š“Ń€ŃƒŠ³Š¾Š³Š¾ формата." +previousPage = "ŠŸŃ€ŠµŠ“Ń‹Š“ŃƒŃ‰Š°Ń страница" +singlePageView = "ŠžŠ“Š½Š¾ŃŃ‚Ń€Š°Š½ŠøŃ‡Š½Ń‹Š¹ виГ" +unknownFile = "ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Ń‹Š¹ файл" zoomIn = "Š£Š²ŠµŠ»ŠøŃ‡ŠøŃ‚ŃŒ" zoomOut = "Š£Š¼ŠµŠ½ŃŒŃˆŠøŃ‚ŃŒ" -singlePageView = "ŠžŠ“Š½Š¾ŃŃ‚Ń€Š°Š½ŠøŃ‡Š½Ń‹Š¹ виГ" -dualPageView = "Š”Š²ŃƒŃ…ŃŃ‚Ń€Š°Š½ŠøŃ‡Š½Ń‹Š¹ виГ" [rightRail] closeSelected = "Š—Š°ŠŗŃ€Ń‹Ń‚ŃŒ выбранные файлы" @@ -3877,6 +3984,7 @@ toggleSidebar = "ŠŸŠ¾ŠŗŠ°Š·Š°Ń‚ŃŒ/ŃŠŗŃ€Ń‹Ń‚ŃŒ Š±Š¾ŠŗŠ¾Š²ŃƒŃŽ панель" exportSelected = "Š­ŠŗŃŠæŠ¾Ń€Ń‚ŠøŃ€Š¾Š²Š°Ń‚ŃŒ выбранные страницы" toggleAnnotations = "ŠŸŠ¾ŠŗŠ°Š·Š°Ń‚ŃŒ/ŃŠŗŃ€Ń‹Ń‚ŃŒ аннотации" annotationMode = "ŠŸŠµŃ€ŠµŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒ режим аннотаций" +print = "ŠŸŠµŃ‡Š°Ń‚ŃŒ PDF" draw = "Š ŠøŃŠ¾Š²Š°Ń‚ŃŒ" save = "Š”Š¾Ń…Ń€Š°Š½ŠøŃ‚ŃŒ" saveChanges = "Š”Š¾Ń…Ń€Š°Š½ŠøŃ‚ŃŒ ŠøŠ·Š¼ŠµŠ½ŠµŠ½ŠøŃ" @@ -4487,13 +4595,14 @@ label = "ŠŸŠ¾Š»ŠøŃ‚ŠøŠŗŠ° cookie" description = "URL или ŠøŠ¼Ń файла Š“Š»Ń политики cookie" [admin.settings.legal.impressum] -label = "Impressum" +label = "Š®Ń€ŠøŠ“ŠøŃ‡ŠµŃŠŗŠ°Ń ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŃ" description = "URL или ŠøŠ¼Ń файла Š“Š»Ń impressum (Ń‚Ń€ŠµŠ±ŃƒŠµŃ‚ŃŃ в некоторых ŃŽŃ€ŠøŃŠ“ŠøŠŗŃ†ŠøŃŃ…)" [admin.settings.premium] title = "ŠŸŃ€ŠµŠ¼ŠøŃƒŠ¼ Šø Enterprise" description = "ŠŠ°ŃŃ‚Ń€Š¾Š¹Ń‚Šµ ŠŗŠ»ŃŽŃ‡ лицензии ŠæŃ€ŠµŠ¼ŠøŃƒŠ¼ или enterprise." license = "ŠšŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†ŠøŃ лицензии" +noInput = "Укажите лицензионный ŠŗŠ»ŃŽŃ‡ или файл" [admin.settings.premium.licenseKey] toggle = "Š•ŃŃ‚ŃŒ лицензионный ŠŗŠ»ŃŽŃ‡ или файл сертификата?" @@ -4511,6 +4620,25 @@ line1 = "ŠŸŠµŃ€ŠµŠ·Š°ŠæŠøŃŃŒ Ń‚ŠµŠŗŃƒŃ‰ŠµŠ³Š¾ лицензионного ŠŗŠ»ŃŽ line2 = "ŠŸŃ€ŠµŠ“Ń‹Š“ŃƒŃ‰Š°Ń Š»ŠøŃ†ŠµŠ½Š·ŠøŃ Š±ŃƒŠ“ŠµŃ‚ безвозвратно ŃƒŃ‚ŠµŃ€ŃŠ½Š°, если вы не сГелали Ń€ŠµŠ·ŠµŃ€Š²Š½ŃƒŃŽ ŠŗŠ¾ŠæŠøŃŽ гГе‑то ещё." line3 = "Важно: храните лицензионные ŠŗŠ»ŃŽŃ‡Šø в секрете Šø безопасности. ŠŠøŠŗŠ¾Š³Š“Š° не ŠæŃƒŠ±Š»ŠøŠŗŃƒŠ¹Ń‚е ŠøŃ…." +[admin.settings.premium.inputMethod] +text = "Лицензионный ŠŗŠ»ŃŽŃ‡" +file = "Файл сертификата" + +[admin.settings.premium.file] +label = "Файл лицензионного сертификата" +description = "Š—Š°Š³Ń€ŃƒŠ·ŠøŃ‚Šµ файл лицензии .lic или .cert ŠøŠ· офлайн-покупки" +choose = "Выберите файл лицензии" +selected = "Выбрано: {{filename}} ({{size}})" +successMessage = "Файл лицензии успешно Š·Š°Š³Ń€ŃƒŠ¶ŠµŠ½ Šø активирован. ŠŸŠµŃ€ŠµŠ·Š°ŠæŃƒŃŠŗ не Ń‚Ń€ŠµŠ±ŃƒŠµŃ‚ŃŃ." + +[admin.settings.premium.currentLicense] +title = "ŠŠŗŃ‚ŠøŠ²Š½Š°Ń Š»ŠøŃ†ŠµŠ½Š·ŠøŃ" +file = "Š˜ŃŃ‚Š¾Ń‡Š½ŠøŠŗ: файл лицензии ({{path}})" +key = "Š˜ŃŃ‚Š¾Ń‡Š½ŠøŠŗ: лицензионный ŠŗŠ»ŃŽŃ‡" +type = "Тип: {{type}}" +noInput = "Укажите лицензионный ŠŗŠ»ŃŽŃ‡ или Š·Š°Š³Ń€ŃƒŠ·ŠøŃ‚е файл сертификата" +success = "Успешно" + [admin.settings.premium.enabled] label = "Š’ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒ ŠæŃ€ŠµŠ¼ŠøŃƒŠ¼-Ń„ŃƒŠ½ŠŗŃ†ŠøŠø" description = "Š’ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒ ŠæŃ€Š¾Š²ŠµŃ€ŠŗŃƒ лицензии Š“Š»Ń pro/enterprise Ń„ŃƒŠ½ŠŗŃ†ŠøŠ¹" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} выбрано" download = "Š”ŠŗŠ°Ń‡Š°Ń‚ŃŒ" delete = "Š£Š“Š°Š»ŠøŃ‚ŃŒ" unsupported = "ŠŠµ ŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŃ‚ŃŃ" +active = "Активный" addToUpload = "Š”Š¾Š±Š°Š²ŠøŃ‚ŃŒ Šŗ Š·Š°Š³Ń€ŃƒŠ·ŠŗŠµ" +closeFile = "Š—Š°ŠŗŃ€Ń‹Ń‚ŃŒ файл" deleteAll = "Š£Š“Š°Š»ŠøŃ‚ŃŒ все" loadingFiles = "Š—Š°Š³Ń€ŃƒŠ·ŠŗŠ° файлов..." noFiles = "ŠŠµŃ‚ Š“Š¾ŃŃ‚ŃƒŠæŠ½Ń‹Ń… файлов" @@ -4964,7 +5094,7 @@ description = "ŠŸŃ€ŠøŠ²ŃŠ¶ŠøŃ‚Šµ Š°ŠŗŠŗŠ°ŃƒŠ½Ń‚, чтобы ŃŠ¾Ń…Ń€Š°Š½ŠøŃ‚ŃŒ socialLogin = "ŠžŠ±Š½Š¾Š²ŠøŃ‚ŃŒ через ŃŠ¾Ń†ŃŠµŃ‚ŃŒ" linkWith = "ŠŸŃ€ŠøŠ²ŃŠ·Š°Ń‚ŃŒ Šŗ" emailPassword = "или ввеГите email Šø ŠæŠ°Ń€Š¾Š»ŃŒ" -email = "Email" +email = "Š­Š». почта" emailPlaceholder = "ВвеГите ваш email" password = "ŠŸŠ°Ń€Š¾Š»ŃŒ (Š½ŠµŠ¾Š±ŃŠ·Š°Ń‚ŠµŠ»ŃŒŠ½Š¾)" passwordPlaceholder = "ЗаГайте ŠæŠ°Ń€Š¾Š»ŃŒ" @@ -5132,7 +5262,7 @@ upgrade = "ŠžŠ±Š½Š¾Š²ŠøŃ‚ŃŒ сейчас →" freeTitle = "Š”ŠµŃ€Š²ŠµŃ€Š½Š°Ń Š»ŠøŃ†ŠµŠ½Š·ŠøŃ" overLimitTitle = "Š¢Ń€ŠµŠ±ŃƒŠµŃ‚ŃŃ ŃŠµŃ€Š²ŠµŃ€Š½Š°Ń Š»ŠøŃ†ŠµŠ½Š·ŠøŃ" overLimitBody = "ŠŠ°ŃˆŠ° Š»ŠøŃ†ŠµŠ½Š·ŠøŃ Š“Š¾ŠæŃƒŃŠŗŠ°ŠµŃ‚ Го {{freeTierLimit}} ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»ŠµŠ¹ бесплатно на сервер. Š£ вас {{overLimitUserCopy}} ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»ŠµŠ¹ Stirling. Чтобы ŠæŃ€Š¾Š“Š¾Š»Š¶ŠøŃ‚ŃŒ без перебоев, перейГите на тариф Stirling Server — неограниченные места, реГактирование текста в PDF Šø полный Š°Š“Š¼ŠøŠ½ā€‘ŠŗŠ¾Š½Ń‚Ń€Š¾Š»ŃŒ за $99/server/mo." -freeBody = "ŠŠ°ŃˆŠ° Š»ŠøŃ†ŠµŠ½Š·ŠøŃ Open-Core Š“Š¾ŠæŃƒŃŠŗŠ°ŠµŃ‚ Го {{freeTierLimit}} ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»ŠµŠ¹ бесплатно на сервер. Чтобы Š¼Š°ŃŃˆŃ‚Š°Š±ŠøŃ€Š¾Š²Š°Ń‚ŃŒŃŃ без ограничений Šø Ń€Š°Š½ŃŒŃˆŠµ ŠæŠ¾Š»ŃƒŃ‡ŠøŃ‚ŃŒ Š“Š¾ŃŃ‚ŃƒŠæ Šŗ новому ŠøŠ½ŃŃ‚Ń€ŃƒŠ¼ŠµŠ½Ń‚Ńƒ Ń€ŠµŠ“Š°ŠŗŃ‚ŠøŃ€Š¾Š²Š°Š½ŠøŃ текста в PDF, Ń€ŠµŠŗŠ¾Š¼ŠµŠ½Š“ŃƒŠµŠ¼ тариф Stirling Server — полный реГактор Šø неограниченные места за $99/server/mo." +freeBody = "ŠŠ°ŃˆŠ° Š»ŠøŃ†ŠµŠ½Š·ŠøŃ Open-Core ŠæŠ¾Š·Š²Š¾Š»ŃŠµŃ‚ бесплатно ŠøŃŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŃŒ Го {{freeTierLimit}} ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»ŠµŠ¹ на сервер. Š”Š»Ń бесшовного Š¼Š°ŃŃˆŃ‚Š°Š±ŠøŃ€Š¾Š²Š°Š½ŠøŃ мы Ń€ŠµŠŗŠ¾Š¼ŠµŠ½Š“ŃƒŠµŠ¼ план Stirling Server - неограниченное число мест Šø поГГержка SSO за $99/сервер/мес." [onboarding.desktopInstall] title = "Š”ŠŗŠ°Ń‡Š°Ń‚ŃŒ" @@ -5237,6 +5367,31 @@ error = "ŠŠµ уГалось Š¾Š±Š½Š¾Š²ŠøŃ‚ŃŒ ŃŃ‚Š°Ń‚ŃƒŃ ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚Šµ success = "ŠŸŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»ŃŒ успешно уГален" error = "ŠŠµ уГалось ŃƒŠ“Š°Š»ŠøŃ‚ŃŒ ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Ń" +[workspace.people.changePassword] +action = "Š˜Š·Š¼ŠµŠ½ŠøŃ‚ŃŒ ŠæŠ°Ń€Š¾Š»ŃŒ" +title = "Š˜Š·Š¼ŠµŠ½ŠøŃ‚ŃŒ ŠæŠ°Ń€Š¾Š»ŃŒ" +subtitle = "ŠžŠ±Š½Š¾Š²ŠøŃ‚ŃŒ ŠæŠ°Ń€Š¾Š»ŃŒ Š“Š»Ń" +newPassword = "ŠŠ¾Š²Ń‹Š¹ ŠæŠ°Ń€Š¾Š»ŃŒ" +confirmPassword = "ŠŸŠ¾Š“Ń‚Š²ŠµŃ€Š“ŠøŃ‚Šµ ŠæŠ°Ń€Š¾Š»ŃŒ" +placeholder = "ВвеГите новый ŠæŠ°Ń€Š¾Š»ŃŒ" +confirmPlaceholder = "ŠŸŠ¾Š²Ń‚Š¾Ń€Š½Š¾ ввеГите новый ŠæŠ°Ń€Š¾Š»ŃŒ" +passwordRequired = "ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ввеГите новый ŠæŠ°Ń€Š¾Š»ŃŒ" +passwordMismatch = "ŠŸŠ°Ń€Š¾Š»Šø не ŃŠ¾Š²ŠæŠ°Š“Š°ŃŽŃ‚" +generateRandom = "Š”Š³ŠµŠ½ŠµŃ€ŠøŃ€Š¾Š²Š°Ń‚ŃŒ безопасный ŠæŠ°Ń€Š¾Š»ŃŒ" +generatedPreview = "Дгенерированный ŠæŠ°Ń€Š¾Š»ŃŒ:" +copyTooltip = "ŠšŠ¾ŠæŠøŃ€Š¾Š²Š°Ń‚ŃŒ в Š±ŃƒŃ„ер обмена" +copiedToClipboard = "ŠŸŠ°Ń€Š¾Š»ŃŒ скопирован в Š±ŃƒŃ„ер обмена" +copyFailed = "ŠŠµ уГалось ŃŠŗŠ¾ŠæŠøŃ€Š¾Š²Š°Ń‚ŃŒ ŠæŠ°Ń€Š¾Š»ŃŒ" +sendEmail = "ŠžŃ‚ŠæŃ€Š°Š²ŠøŃ‚ŃŒ ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»ŃŽ письмо об ŃŃ‚Š¾Š¼ изменении" +includePassword = "Š’ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒ новый ŠæŠ°Ń€Š¾Š»ŃŒ в письмо" +forcePasswordChange = "ŠŸŠ¾Ń‚Ń€ŠµŠ±Š¾Š²Š°Ń‚ŃŒ смену ŠæŠ°Ń€Š¾Š»Ń при ŃŠ»ŠµŠ“ŃƒŃŽŃ‰ŠµŠ¼ вхоГе" +emailUnavailable = "АГрес ŃŠ»ŠµŠŗŃ‚Ń€Š¾Š½Š½Š¾Š¹ почты ŃŃ‚Š¾Š³Š¾ ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Ń неГействителен. Š£Š²ŠµŠ“Š¾Š¼Š»ŠµŠ½ŠøŃ Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½Ń‹." +smtpDisabled = "Š”Š»Ń увеГомлений по ŃŠ»ŠµŠŗŃ‚Ń€Š¾Š½Š½Š¾Š¹ почте необхоГимо Š²ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒ SMTP в настройках." +notifyOnly = "Š‘ŃƒŠ“ŠµŃ‚ отправлено письмо без ŠæŠ°Ń€Š¾Š»Ń с увеГомлением, что аГминистратор его изменил." +submit = "ŠžŠ±Š½Š¾Š²ŠøŃ‚ŃŒ ŠæŠ°Ń€Š¾Š»ŃŒ" +success = "ŠŸŠ°Ń€Š¾Š»ŃŒ успешно обновлён" +error = "ŠŠµ уГалось Š¾Š±Š½Š¾Š²ŠøŃ‚ŃŒ ŠæŠ°Ń€Š¾Š»ŃŒ" + [workspace.people.emailInvite] tab = "ŠŸŃ€ŠøŠ³Š»Š°ŃˆŠµŠ½ŠøŠµ по email" description = "ВвеГите или Š²ŃŃ‚Š°Š²ŃŒŃ‚Šµ ниже аГреса email, Ń€Š°Š·Š“ŠµŠ»ŃŃ ŠøŃ… Š·Š°ŠæŃŃ‚Ń‹Š¼Šø. ŠŸŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Šø ŠæŠ¾Š»ŃƒŃ‡Š°Ń‚ ŃƒŃ‡ŠµŃ‚Š½Ń‹Šµ Ганные Š“Š»Ń вхоГа по email." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Š¢Ń€ŠµŠ±ŃƒŠµŃ‚ŃŃ Ń…Š¾Ń‚Ń бы оГин аГрес email" submit = "ŠžŃ‚ŠæŃ€Š°Š²ŠøŃ‚ŃŒ ŠæŃ€ŠøŠ³Š»Š°ŃˆŠµŠ½ŠøŃ" success = "ŠŸŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Šø успешно ŠæŃ€ŠøŠ³Š»Š°ŃˆŠµŠ½Ń‹" -partialSuccess = "ŠŠµŠŗŠ¾Ń‚Š¾Ń€Ń‹Šµ ŠæŃ€ŠøŠ³Š»Š°ŃˆŠµŠ½ŠøŃ не отправлены" +partialFailure = "ŠŠµŠŗŠ¾Ń‚Š¾Ń€Ń‹Šµ ŠæŃ€ŠøŠ³Š»Š°ŃˆŠµŠ½ŠøŃ не уГалось Š¾Ń‚ŠæŃ€Š°Š²ŠøŃ‚ŃŒ" allFailed = "ŠŠµ уГалось ŠæŃ€ŠøŠ³Š»Š°ŃŠøŃ‚ŃŒ ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»ŠµŠ¹" error = "ŠŠµ уГалось Š¾Ń‚ŠæŃ€Š°Š²ŠøŃ‚ŃŒ ŠæŃ€ŠøŠ³Š»Š°ŃˆŠµŠ½ŠøŃ" @@ -5770,6 +5925,7 @@ subtitle = "ВойГите в Š°ŠŗŠŗŠ°ŃƒŠ½Ń‚ Stirling" [setup.selfhosted] title = "ВхоГ на сервер" subtitle = "ВвеГите ŃƒŃ‡Ń‘Ń‚Š½Ń‹Šµ Ганные сервера" +link = "или ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŠµŃŃŒ Šŗ ŃŠ°Š¼Š¾Ń…Š¾ŃŃ‚ŠøŃ€ŃƒŠµŠ¼Š¾Š¹ ŃƒŃ‡Ń‘Ń‚Š½Š¾Š¹ записи" [setup.server] title = "ŠŸŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ŠøŠµ Šŗ ŃŠµŃ€Š²ŠµŃ€Ńƒ" @@ -5788,6 +5944,14 @@ description = "ВвеГите полный URL вашего self-hosted серв emptyUrl = "ВвеГите URL сервера" unreachable = "ŠŠµ уГалось ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒŃŃ Šŗ ŃŠµŃ€Š²ŠµŃ€Ńƒ" testFailed = "Тест ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ŠøŃ не пройГен" +configFetch = "ŠŠµ уГалось ŠæŠ¾Š»ŃƒŃ‡ŠøŃ‚ŃŒ ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†ŠøŃŽ сервера. ŠŸŃ€Š¾Š²ŠµŃ€ŃŒŃ‚Šµ URL Šø ŠæŠ¾ŠæŃ€Š¾Š±ŃƒŠ¹Ń‚Šµ ещё раз." + +[setup.server.error.securityDisabled] +title = "ВхоГ не Š²ŠŗŠ»ŃŽŃ‡Ń‘н" +body = "ŠŠ° ŃŃ‚Š¾Š¼ сервере вхоГ не Š²ŠŗŠ»ŃŽŃ‡Ń‘н. Чтобы ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒŃŃ Šŗ ŃŃ‚Š¾Š¼Ńƒ ŃŠµŃ€Š²ŠµŃ€Ńƒ, необхоГимо Š²ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒ Š°ŃƒŃ‚ŠµŠ½Ń‚ŠøŃ„ŠøŠŗŠ°Ń†ŠøŃŽ:" +step1 = "Установите DOCKER_ENABLE_SECURITY=true в вашей среГе" +step2 = "Или ŃƒŃŃ‚Š°Š½Š¾Š²ŠøŃ‚Šµ security.enableLogin=true в settings.yml" +step3 = "ŠŸŠµŃ€ŠµŠ·Š°ŠæŃƒŃŃ‚ŠøŃ‚Šµ сервер" [setup.login] title = "ВхоГ" @@ -5797,13 +5961,20 @@ submit = "Войти" signInWith = "Войти через" oauthPending = "ŠžŃ‚ŠŗŃ€Ń‹Š²Š°ŠµŠ¼ Š±Ń€Š°ŃƒŠ·ŠµŃ€ Š“Š»Ń Š°ŃƒŃ‚ŠµŠ½Ń‚ŠøŃ„ŠøŠŗŠ°Ń†ŠøŠø..." orContinueWith = "Или ŠæŃ€Š¾Š“Š¾Š»Š¶ŠøŃ‚ŃŒ по email" +serverRequirement = "ŠŸŃ€ŠøŠ¼ŠµŃ‡Š°Š½ŠøŠµ: на сервере Голжен Š±Ń‹Ń‚ŃŒ Š²ŠŗŠ»ŃŽŃ‡Ń‘Š½ вхоГ в ŃŠøŃŃ‚ŠµŠ¼Ńƒ." +showInstructions = "Как Š²ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒ?" +hideInstructions = "Š”ŠŗŃ€Ń‹Ń‚ŃŒ ŠøŠ½ŃŃ‚Ń€ŃƒŠŗŃ†ŠøŠø" +instructions = "Чтобы Š²ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŃŒ вхоГ в ŃŠøŃŃ‚ŠµŠ¼Ńƒ на вашем сервере Stirling PDF:" +instructionsEnvVar = "Установите ŠæŠµŃ€ŠµŠ¼ŠµŠ½Š½ŃƒŃŽ Š¾ŠŗŃ€ŃƒŠ¶ŠµŠ½ŠøŃ:" +instructionsOrYml = "Или в settings.yml:" +instructionsRestart = "Затем ŠæŠµŃ€ŠµŠ·Š°ŠæŃƒŃŃ‚ŠøŃ‚Šµ сервер, чтобы ŠøŠ·Š¼ŠµŠ½ŠµŠ½ŠøŃ Š²ŃŃ‚ŃƒŠæŠøŠ»Šø в силу." [setup.login.username] label = "Š˜Š¼Ń ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Ń" placeholder = "ВвеГите ŠøŠ¼Ń ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Ń" [setup.login.email] -label = "Email" +label = "Š­Š». почта" placeholder = "ВвеГите email" [setup.login.password] @@ -5853,6 +6024,7 @@ earlyAccess = "Ранний Š“Š¾ŃŃ‚ŃƒŠæ" reset = "Š”Š±Ń€Š¾ŃŠøŃ‚ŃŒ ŠøŠ·Š¼ŠµŠ½ŠµŠ½ŠøŃ" downloadJson = "Š”ŠŗŠ°Ń‡Š°Ń‚ŃŒ JSON" generatePdf = "Š”Ń„Š¾Ń€Š¼ŠøŃ€Š¾Š²Š°Ń‚ŃŒ PDF" +saveChanges = "Š”Š¾Ń…Ń€Š°Š½ŠøŃ‚ŃŒ ŠøŠ·Š¼ŠµŠ½ŠµŠ½ŠøŃ" [pdfTextEditor.options.autoScaleText] title = "Автоматически ŠæŠ¾Š“Š³Š¾Š½ŃŃ‚ŃŒ текст по рамке" @@ -5890,6 +6062,8 @@ alpha = "Этот Š°Š»ŃŒŃ„Š°ā€‘ŠæŃ€Š¾ŃŠ¼Š¾Ń‚Ń€Ń‰ŠøŠŗ ещё развивает [pdfTextEditor.empty] title = "Š”Š¾ŠŗŃƒŠ¼ŠµŠ½Ń‚ не Š·Š°Š³Ń€ŃƒŠ¶ŠµŠ½" subtitle = "Š—Š°Š³Ń€ŃƒŠ·ŠøŃ‚Šµ файл PDF или JSON, чтобы Š½Š°Ń‡Š°Ń‚ŃŒ реГактирование текста." +dropzone = "ŠŸŠµŃ€ŠµŃ‚Š°Ń‰ŠøŃ‚Šµ ŃŃŽŠ“Š° файл PDF или JSON, или нажмите, чтобы Š²Ń‹Š±Ń€Š°Ń‚ŃŒ" +dropzoneWithFiles = "Выберите файл на вклаГке «Файлы» или перетащите ŃŃŽŠ“Š° файл PDF или JSON, или нажмите, чтобы Š²Ń‹Š±Ń€Š°Ń‚ŃŒ" [pdfTextEditor.welcomeBanner] title = "Добро ŠæŠ¾Š¶Š°Š»Š¾Š²Š°Ń‚ŃŒ в реГактор текста PDF (ранний Š“Š¾ŃŃ‚ŃƒŠæ)" diff --git a/frontend/public/locales/sk-SK/translation.toml b/frontend/public/locales/sk-SK/translation.toml index 0b6633ff23..64d3da204e 100644 --- a/frontend/public/locales/sk-SK/translation.toml +++ b/frontend/public/locales/sk-SK/translation.toml @@ -99,7 +99,7 @@ visitGithub = "NavÅ”tĆ­viÅ„ GitHub repozitĆ”r" donate = "DarovaÅ„" color = "Farba" sponsor = "SponzorovaÅ„" -info = "Info" +info = "InformĆ”cie" pro = "Pro" page = "Strana" pages = "Strany" @@ -163,6 +163,11 @@ unfavorite = "OdstrĆ”niÅ„ z obľúbených" fullscreen = "PrepnĆŗÅ„ na režim celej obrazovky" sidebar = "PrepnĆŗÅ„ na režim bočnĆ©ho panela" +[backendStartup] +notFoundTitle = "Backend sa nenaÅ”iel" +retry = "SkĆŗsiÅ„ znova" +unreachable = "AplikĆ”cia sa momentĆ”lne nedokÔže pripojiÅ„ k backendu. Overte stav backendu a sieÅ„ovĆ© pripojenie, potom to skĆŗste znova." + [zipWarning] title = "Veľký ZIP sĆŗbor" message = "Tento ZIP obsahuje {{count}} sĆŗborov. Aj tak rozbaliÅ„?" @@ -274,7 +279,7 @@ iAgreeToThe = "SĆŗhlasĆ­m so vÅ”etkými" terms = "Podmienkami používania" accessibility = "PrĆ­stupnosÅ„" cookie = "ZĆ”sady používania sĆŗborov cookie" -impressum = "Impressum" +impressum = "Impresum" showCookieBanner = "Predvoľby sĆŗborov cookie" [pipeline] @@ -513,7 +518,7 @@ syncToAccount = "SynchronizovaÅ„ ĆŗÄet <- Prehliadač" [adminUserSettings] title = "Nastavenia kontroly používateľov" header = "Admin nastavenia kontroly používateľov" -admin = "Admin" +admin = "AdministrĆ”tor" user = "Používateľ" addUser = "PridaÅ„ novĆ©ho používateľa" deleteUser = "OdstrĆ”niÅ„ používateľa" @@ -912,6 +917,9 @@ desc = "StavaÅ„ viacstupňovĆ© pracovnĆ© postupy spĆ”janĆ­m akciĆ­ PDF. IdeĆ”lne desc = "Prekrýva PDF sĆŗbory na iný PDF" title = "Prekrývanie PDF" +[home.pdfTextEditor] +title = "Editor textu PDF" +desc = "Upravujte existujĆŗci text a obrĆ”zky v PDF" [home.addText] tags = "text,anotĆ”cia,Å”tĆ­tok" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Kreslený podpis" defaultImageLabel = "Nahraný podpis" defaultTextLabel = "NapĆ­saný podpis" saveButton = "UložiÅ„ podpis" +savePersonal = "UložiÅ„ osobnĆ©" +saveShared = "UložiÅ„ zdieľanĆ©" saveUnavailable = "Najprv vytvorte podpis, aby ste ho mohli uložiÅ„." noChanges = "AktuĆ”lny podpis je už uložený." +tempStorageTitle = "DočasnĆ© Ćŗložisko prehliadača" +tempStorageDescription = "Podpisy sĆŗ uloženĆ© iba vo vaÅ”om prehliadači. Pri vymazanĆ­ Ćŗdajov prehliadača alebo pri zmene prehliadača sa stratia." +personalHeading = "OsobnĆ© podpisy" +sharedHeading = "ZdieľanĆ© podpisy" +personalDescription = "Tieto podpisy vidĆ­te iba vy." +sharedDescription = "VÅ”etci používatelia mÓžu tieto podpisy vidieÅ„ a používaÅ„." [sign.saved.type] canvas = "Kresba" @@ -3020,6 +3036,91 @@ title = "ZĆ­skaÅ„ informĆ”cie o PDF" header = "ZĆ­skaÅ„ informĆ”cie o PDF" submit = "ZĆ­skaÅ„ info" downloadJson = "StiahnuÅ„ JSON" +processing = "ExtrahujĆŗ sa informĆ”cie..." +results = "Výsledky" +noResults = "Spustite nĆ”stroj na vygenerovanie prehľadu." +downloads = "SÅ„ahovania" +noneDetected = "Nič nebolo zistenĆ©" +indexTitle = "Index" + +[getPdfInfo.report] +entryLabel = "ÚplnĆ© zhrnutie informĆ”ciĆ­" +shortTitle = "InformĆ”cie o PDF" + +[getPdfInfo.sections] +metadata = "MetadĆ”ta" +formFields = "Polia formulĆ”ra" +basicInfo = "ZĆ”kladnĆ© informĆ”cie" +documentInfo = "InformĆ”cie o dokumente" +compliance = "SĆŗlad" +encryption = "Å ifrovanie" +permissions = "OprĆ”vnenia" +other = "InĆ©" +perPageInfo = "InformĆ”cie pre strany" +tableOfContents = "Obsah" + +[getPdfInfo.other] +attachments = "PrĆ­lohy" +embeddedFiles = "VloženĆ© sĆŗbory" +javaScript = "JavaScript" +layers = "Vrstvy" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "VeľkosÅ„" +annotations = "AnotĆ”cie" +images = "ObrĆ”zky" +links = "Odkazy" +fonts = "PĆ­sma" +xobjects = "Počty XObjectov" +multimedia = "MultimĆ©diĆ”" + +[getPdfInfo.summary] +pages = "Strany" +fileSize = "VeľkosÅ„ sĆŗboru" +pdfVersion = "Verzia PDF" +language = "Jazyk" +title = "SĆŗhrn PDF" +author = "Autor" +created = "VytvorenĆ©" +modified = "UpravenĆ©" +permsAll = "VÅ”etky oprĆ”vnenia povolenĆ©" +permsRestricted = "{{count}} obmedzenĆ­" +permsMixed = "NiektorĆ© oprĆ”vnenia obmedzenĆ©" +hasCompliance = "Spĺňa Å”tandardy sĆŗladu" +noCompliance = "Žiadne Å”tandardy sĆŗladu" +basic = "ZĆ”kladnĆ© informĆ”cie" +documentInfo = "InformĆ”cie o dokumente" +securityTitle = "Stav zabezpečenia" +technical = "TechnickĆ©" +overviewTitle = "Prehľad PDF" + +[getPdfInfo.summary.security] +encrypted = "Å ifrovanĆ© PDF - prĆ­tomnĆ” ochrana heslom" +unencrypted = "NeÅ”ifrovanĆ© PDF - Bez ochrany heslom" + +[getPdfInfo.summary.tech] +images = "ObrĆ”zky" +fonts = "PĆ­sma" +formFields = "Polia formulĆ”ra" +embeddedFiles = "VloženĆ© sĆŗbory" +javaScript = "JavaScript" +layers = "Vrstvy" +bookmarks = "ZĆ”ložky" +multimedia = "MultimĆ©diĆ”" + +[getPdfInfo.summary.overview] +untitled = "nepomenovaný dokument" +unknown = "NeznĆ”my autor" +text = "Toto je {{pages}}-stranovĆ© PDF s nĆ”zvom {{title}} od autora {{author}} (verzia PDF {{version}})." + +[getPdfInfo.error] +partial = "NiektorĆ© sĆŗbory sa nepodarilo spracovaÅ„." +unexpected = "Počas extrahovania doÅ”lo k neočakĆ”vanej chybe." + +[getPdfInfo.status] +complete = "Extrahovanie dokončenĆ©" [extractPage] tags = "extrahovaÅ„" @@ -3438,6 +3539,9 @@ signinTitle = "ProsĆ­m, prihlĆ”ste sa" ssoSignIn = "PrihlĆ”siÅ„ sa cez Single Sign-on" oAuth2AutoCreateDisabled = "VytvĆ”ranie používateľa cez OAUTH2 je zakĆ”zanĆ©" oAuth2AdminBlockedUser = "RegistrĆ”cia alebo prihlasovanie neregistrovaných používateľov je momentĆ”lne blokovanĆ©. Kontaktujte administrĆ”tora." +oAuth2RequiresLicense = "PrihlĆ”senie cez OAuth/SSO vyžaduje platenĆŗ licenciu (Server alebo Enterprise). ObrÔńte sa na administrĆ”tora, aby aktualizoval vÔŔ plĆ”n." +saml2RequiresLicense = "PrihlĆ”senie cez SAML vyžaduje platenĆŗ licenciu (Server alebo Enterprise). ObrÔńte sa na administrĆ”tora, aby aktualizoval vÔŔ plĆ”n." +maxUsersReached = "Bol dosiahnutý maximĆ”lny počet používateľov pre vaÅ”u aktuĆ”lnu licenciu. ObrÔńte sa na administrĆ”tora, aby aktualizoval vÔŔ plĆ”n alebo pridal ďalÅ”ie miesta." oauth2RequestNotFound = "Požiadavka na autorizĆ”ciu sa nenaÅ”la" oauth2InvalidUserInfoResponse = "NeplatnĆ” odpoveď User Info" oauth2invalidRequest = "NeplatnĆ” požiadavka" @@ -3533,7 +3637,7 @@ title = "PDF na jednu strĆ”nku" header = "PDF na jednu strĆ”nku" submit = "KonvertovaÅ„ na jednu strĆ”nku" description = "Tento nĆ”stroj zlĆŗÄi vÅ”etky strany vÔŔho PDF do jednej veľkej strĆ”nky. Å Ć­rka zostane rovnakĆ” ako pri pĆ“vodných stranĆ”ch, výŔka bude sĆŗÄtom výŔok vÅ”etkých strĆ”n." -filenamePrefix = "single_page" +filenamePrefix = "jedna_strana" [pdfToSinglePage.files] placeholder = "Vyberte sĆŗbor PDF v hlavnom zobrazenĆ­, aby ste začali" @@ -3771,7 +3875,7 @@ version = "AktuĆ”lne vydanie" title = "DokumentĆ”cia API" header = "DokumentĆ”cia API" desc = "ZobraziÅ„ a testovaÅ„ API endpointy Stirling PDF" -tags = "api,documentation,swagger,endpoints,development" +tags = "api,dokumentĆ”cia,swagger,endpointy,vývoj" [cookieBanner.popUp] title = "Ako používame sĆŗbory cookie" @@ -3846,14 +3950,17 @@ fitToWidth = "PrispĆ“sobiÅ„ Ŕírke" actualSize = "SkutočnĆ” veľkosÅ„" [viewer] +cannotPreviewFile = "NedĆ” sa zobraziÅ„ nĆ”hľad sĆŗboru" +dualPageView = "DvojstranovĆ© zobrazenie" firstPage = "PrvĆ” strana" lastPage = "PoslednĆ” strana" -previousPage = "PredchĆ”dzajĆŗca strana" nextPage = "NasledujĆŗca strana" +onlyPdfSupported = "Prehliadač podporuje iba sĆŗbory PDF. Tento sĆŗbor sa zdĆ” byÅ„ inĆ©ho formĆ”tu." +previousPage = "PredchĆ”dzajĆŗca strana" +singlePageView = "Zobrazenie jednej strany" +unknownFile = "NeznĆ”my sĆŗbor" zoomIn = "PriblížiÅ„" zoomOut = "OddialiÅ„" -singlePageView = "Zobrazenie jednej strany" -dualPageView = "DvojstranovĆ© zobrazenie" [rightRail] closeSelected = "ZavrieÅ„ vybranĆ© sĆŗbory" @@ -3877,6 +3984,7 @@ toggleSidebar = "PrepnĆŗÅ„ bočný panel" exportSelected = "ExportovaÅ„ vybranĆ© strany" toggleAnnotations = "PrepnĆŗÅ„ zobrazenie anotĆ”ciĆ­" annotationMode = "PrepnĆŗÅ„ režim anotĆ”ciĆ­" +print = "VytlačiÅ„ PDF" draw = "KresliÅ„" save = "UložiÅ„" saveChanges = "UložiÅ„ zmeny" @@ -4487,13 +4595,14 @@ label = "ZĆ”sady používania sĆŗborov cookie" description = "URL alebo nĆ”zov sĆŗboru so zĆ”sadami používania sĆŗborov cookie" [admin.settings.legal.impressum] -label = "Impressum" +label = "Impresum" description = "URL alebo nĆ”zov sĆŗboru k Impressu (požadovanĆ© v niektorých jurisdikciĆ”ch)" [admin.settings.premium] title = "Premium a Enterprise" description = "Nakonfigurujte svoj Premium alebo Enterprise licenčný kÄ¾ĆŗÄ." license = "KonfigurĆ”cia licencie" +noInput = "Zadajte licenčný kÄ¾ĆŗÄ alebo sĆŗbor" [admin.settings.premium.licenseKey] toggle = "MĆ”te licenčný kÄ¾ĆŗÄ alebo sĆŗbor certifikĆ”tu?" @@ -4511,6 +4620,25 @@ line1 = "PrepĆ­sanie aktuĆ”lneho licenčnĆ©ho kÄ¾ĆŗÄa nemožno vrĆ”tiÅ„ späń. line2 = "VaÅ”a predchĆ”dzajĆŗca licencia bude natrvalo stratenĆ”, pokiaľ ju nemĆ”te zĆ”lohovanĆŗ inde." line3 = "DĆ“ležitĆ©: LicenčnĆ© kÄ¾ĆŗÄe uchovĆ”vajte sĆŗkromnĆ© a v bezpečƭ. Nikdy ich nezdieľajte verejne." +[admin.settings.premium.inputMethod] +text = "Licenčný kÄ¾ĆŗÄ" +file = "SĆŗbor certifikĆ”tu" + +[admin.settings.premium.file] +label = "SĆŗbor licenčnĆ©ho certifikĆ”tu" +description = "Nahrajte svoj licenčný sĆŗbor .lic alebo .cert z offline nĆ”kupu" +choose = "VybraÅ„ licenčný sĆŗbor" +selected = "VybranĆ©: {{filename}} ({{size}})" +successMessage = "Licenčný sĆŗbor bol ĆŗspeÅ”ne nahraný a aktivovaný. ReÅ”tart nie je potrebný." + +[admin.settings.premium.currentLicense] +title = "AktĆ­vna licencia" +file = "Zdroj: Licenčný sĆŗbor ({{path}})" +key = "Zdroj: Licenčný kÄ¾ĆŗÄ" +type = "Typ: {{type}}" +noInput = "Zadajte licenčný kÄ¾ĆŗÄ alebo nahrajte sĆŗbor certifikĆ”tu" +success = "Úspech" + [admin.settings.premium.enabled] label = "PovoliÅ„ Premium funkcie" description = "PovoliÅ„ kontrolu licenčnĆ©ho kÄ¾ĆŗÄa pre pro/enterprise funkcie" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} vybraných" download = "StiahnuÅ„" delete = "VymazaÅ„" unsupported = "NepodporovanĆ©" +active = "AktĆ­vne" addToUpload = "PridaÅ„ na nahratie" +closeFile = "ZatvoriÅ„ sĆŗbor" deleteAll = "OdstrĆ”niÅ„ vÅ”etko" loadingFiles = "NačƭtavajĆŗ sa sĆŗbory..." noFiles = "Nie sĆŗ dostupnĆ© žiadne sĆŗbory" @@ -5132,7 +5262,7 @@ upgrade = "UpgradovaÅ„ teraz →" freeTitle = "ServerovĆ” licencia" overLimitTitle = "PotrebnĆ” serverovĆ” licencia" overLimitBody = "NaÅ”e licencovanie povoľuje až {{freeTierLimit}} používateľov zdarma na server. MĆ”te {{overLimitUserCopy}} používateľov Stirling. Ak chcete pokračovaÅ„ bez preruÅ”enia, prejdite na plĆ”n Stirling Server - neobmedzenĆ© miesta, Ćŗpravy textu PDF a plnĆ” sprĆ”va pre $99/server/mo." -freeBody = "NaÅ”e licencovanie Open-Core povoľuje až {{freeTierLimit}} používateľov zdarma na server. Ak chcete Å”kĆ”lovaÅ„ bez preruÅ”enia a zĆ­skaÅ„ skorý prĆ­stup k nÔŔmu novĆ©mu nĆ”stroju na Ćŗpravu textu PDF, odporĆŗÄame plĆ”n Stirling Server - plnĆ© Ćŗpravy a neobmedzenĆ© miesta za $99/server/mo." +freeBody = "NaÅ”e licencovanie Open-Core umožňuje až {{freeTierLimit}} používateľov zadarmo na server. Na plynulĆ© Å”kĆ”lovanie odporĆŗÄame plĆ”n Stirling Server - neobmedzený počet používateľov a podporu SSO za $99/server/mes." [onboarding.desktopInstall] title = "StiahnuÅ„" @@ -5178,7 +5308,7 @@ active = "AktĆ­vny" disabled = "ZakĆ”zaný" activeSession = "AktĆ­vna relĆ”cia" member = "Člen" -admin = "Admin" +admin = "AdministrĆ”tor" editRole = "UpraviÅ„ rolu" enable = "PovoliÅ„" disable = "ZakĆ”zaÅ„" @@ -5237,6 +5367,31 @@ error = "Nepodarilo sa aktualizovaÅ„ stav používateľa" success = "Používateľ bol ĆŗspeÅ”ne odstrĆ”nený" error = "Nepodarilo sa odstrĆ”niÅ„ používateľa" +[workspace.people.changePassword] +action = "ZmeniÅ„ heslo" +title = "ZmeniÅ„ heslo" +subtitle = "AktualizovaÅ„ heslo pre" +newPassword = "NovĆ© heslo" +confirmPassword = "PotvrdiÅ„ heslo" +placeholder = "Zadajte novĆ© heslo" +confirmPlaceholder = "Zadajte novĆ© heslo eÅ”te raz" +passwordRequired = "Zadajte novĆ© heslo" +passwordMismatch = "HeslĆ” sa nezhodujĆŗ" +generateRandom = "VygenerovaÅ„ bezpečnĆ© heslo" +generatedPreview = "VygenerovanĆ© heslo:" +copyTooltip = "KopĆ­rovaÅ„ do schrĆ”nky" +copiedToClipboard = "Heslo skopĆ­rovanĆ© do schrĆ”nky" +copyFailed = "Heslo sa nepodarilo skopĆ­rovaÅ„" +sendEmail = "OdoslaÅ„ používateľovi e-mail o tejto zmene" +includePassword = "ZahrnĆŗÅ„ novĆ© heslo do e-mailu" +forcePasswordChange = "VynĆŗtiÅ„ zmenu hesla pri najbližŔom prihlĆ”senĆ­" +emailUnavailable = "E-mailovĆ” adresa tohto používateľa nie je platnĆ”. Upozornenia sĆŗ vypnutĆ©." +smtpDisabled = "E-mailovĆ© upozornenia vyžadujĆŗ, aby bolo v nastaveniach povolenĆ© SMTP." +notifyOnly = "OdoÅ”le sa e-mail bez hesla, ktorý používateľa informuje, že ho zmenil administrĆ”tor." +submit = "AktualizovaÅ„ heslo" +success = "Heslo bolo ĆŗspeÅ”ne aktualizovanĆ©" +error = "Heslo sa nepodarilo aktualizovaÅ„" + [workspace.people.emailInvite] tab = "PozvĆ”nka e-mailom" description = "NižŔie napĆ­Å”te alebo vložte e-mailovĆ© adresy oddelenĆ© čiarkami. Používatelia dostanĆŗ prihlasovacie Ćŗdaje e-mailom." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Vyžaduje sa aspoň jedna e-mailovĆ” adresa" submit = "OdoslaÅ„ pozvĆ”nky" success = "Používatelia boli ĆŗspeÅ”ne pozvanĆ­" -partialSuccess = "NiektorĆ© pozvĆ”nky zlyhali" +partialFailure = "NiektorĆ© pozvĆ”nky zlyhali" allFailed = "Nepodarilo sa pozvaÅ„ používateľov" error = "Nepodarilo sa odoslaÅ„ pozvĆ”nky" @@ -5770,6 +5925,7 @@ subtitle = "PrihlĆ”ste sa pomocou svojho ĆŗÄtu Stirling" [setup.selfhosted] title = "PrihlĆ”siÅ„ sa na server" subtitle = "Zadajte prihlasovacie Ćŗdaje k serveru" +link = "alebo sa pripojte k self-hosted ĆŗÄtu" [setup.server] title = "PripojiÅ„ sa na server" @@ -5788,6 +5944,14 @@ description = "Zadajte ĆŗplnĆŗ URL svojho self-hostovanĆ©ho servera Stirling PDF emptyUrl = "Zadajte URL servera" unreachable = "NedĆ” sa pripojiÅ„ k serveru" testFailed = "Test pripojenia zlyhal" +configFetch = "Nepodarilo sa načƭtaÅ„ konfigurĆ”ciu servera. Skontrolujte URL a skĆŗste to znova." + +[setup.server.error.securityDisabled] +title = "PrihlĆ”senie nie je povolenĆ©" +body = "Na tomto serveri nie je povolenĆ© prihlĆ”senie. Ak sa chcete pripojiÅ„ k tomuto serveru, musĆ­te povoliÅ„ overenie:" +step1 = "Nastavte DOCKER_ENABLE_SECURITY=true vo svojom prostredĆ­" +step2 = "Alebo nastavte security.enableLogin=true v sĆŗbore settings.yml" +step3 = "ReÅ”tartujte server" [setup.login] title = "PrihlĆ”siÅ„ sa" @@ -5797,13 +5961,20 @@ submit = "PrihlĆ”siÅ„ sa" signInWith = "PrihlĆ”siÅ„ sa cez" oauthPending = "OtvĆ”ra sa prehliadač na overenie..." orContinueWith = "Alebo pokračujte emailom" +serverRequirement = "PoznĆ”mka: Na serveri musĆ­ byÅ„ povolenĆ© prihlĆ”senie." +showInstructions = "Ako povoliÅ„?" +hideInstructions = "SkryÅ„ pokyny" +instructions = "Na povolenie prihlĆ”senia na vaÅ”om serveri Stirling PDF:" +instructionsEnvVar = "Nastavte premennĆŗ prostredia:" +instructionsOrYml = "Alebo v sĆŗbore settings.yml:" +instructionsRestart = "Potom reÅ”tartujte server, aby sa zmeny prejavili." [setup.login.username] label = "PoužívateľskĆ© meno" placeholder = "Zadajte používateľskĆ© meno" [setup.login.email] -label = "Email" +label = "E-mail" placeholder = "Zadajte svoj email" [setup.login.password] @@ -5840,7 +6011,7 @@ paragraph = "Strana s odsekmi" sparse = "Riedky text" [pdfTextEditor.groupingMode] -auto = "Auto" +auto = "Automaticky" paragraph = "Odsek" singleLine = "Jeden riadok" @@ -5853,6 +6024,7 @@ earlyAccess = "Skorý prĆ­stup" reset = "ResetovaÅ„ zmeny" downloadJson = "StiahnuÅ„ JSON" generatePdf = "VygenerovaÅ„ PDF" +saveChanges = "UložiÅ„ zmeny" [pdfTextEditor.options.autoScaleText] title = "Automaticky prispĆ“sobiÅ„ text do boxov" @@ -5890,6 +6062,8 @@ alpha = "Tento alfa prehliadač sa stĆ”le vyvĆ­ja—niektorĆ© pĆ­sma, farby, efe [pdfTextEditor.empty] title = "Žiadny dokument nie je načƭtaný" subtitle = "Načƭtajte sĆŗbor PDF alebo JSON a začnite upravovaÅ„ textový obsah." +dropzone = "Pretiahnite sem sĆŗbor PDF alebo JSON, alebo kliknite pre prehľadanie" +dropzoneWithFiles = "Vyberte sĆŗbor na karte SĆŗbory, alebo sem presuňte sĆŗbor PDF alebo JSON, prĆ­padne kliknite pre prehľadanie" [pdfTextEditor.welcomeBanner] title = "Vitajte v PDF Text Editore (Early Access)" diff --git a/frontend/public/locales/sl-SI/translation.toml b/frontend/public/locales/sl-SI/translation.toml index 36497b6a9c..4d80264fb0 100644 --- a/frontend/public/locales/sl-SI/translation.toml +++ b/frontend/public/locales/sl-SI/translation.toml @@ -163,6 +163,11 @@ unfavorite = "Odstrani iz priljubljenih" fullscreen = "Preklopi na celozaslonski način" sidebar = "Preklopi na način stranske vrstice" +[backendStartup] +notFoundTitle = "Zaledje ni najdeno" +retry = "Poskusi znova" +unreachable = "Aplikacija se trenutno ne more povezati z zaledjem. Preverite stanje zaledja in omrežno povezavo, nato poskusite znova." + [zipWarning] title = "Velika datoteka ZIP" message = "Ta ZIP vsebuje {{count}} datotek. Vseeno razpakiram?" @@ -912,6 +917,9 @@ desc = "Sestavite večkorakovne poteke z veriženjem dejanj PDF. Idealno za pona desc = "Prekriva PDF-je na vrhu drugega PDF-ja" title = "Prekrivanje PDF-jev" +[home.pdfTextEditor] +title = "Urejevalnik besedila PDF" +desc = "Urejajte obstoječe besedilo in slike v PDF-jih" [home.addText] tags = "besedilo,pripomba,oznaka" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Narisan podpis" defaultImageLabel = "Naložen podpis" defaultTextLabel = "Vpisan podpis" saveButton = "Shrani podpis" +savePersonal = "Shrani osebno" +saveShared = "Shrani deljeno" saveUnavailable = "Najprej ustvarite podpis, da ga lahko shranite." noChanges = "Trenutni podpis je že shranjen." +tempStorageTitle = "Začasno shranjevanje v brskalniku" +tempStorageDescription = "Podpisi so shranjeni samo v vaÅ”em brskalniku. Izgubite jih, če počistite podatke brskalnika ali zamenjate brskalnik." +personalHeading = "Osebni podpisi" +sharedHeading = "Deljeni podpisi" +personalDescription = "Te podpise vidite samo vi." +sharedDescription = "Vsi uporabniki lahko te podpise vidijo in uporabljajo." [sign.saved.type] canvas = "Risba" @@ -3020,6 +3036,91 @@ title = "Pridobite informacije o PDF-ju" header = "Pridobite informacije o PDF-ju" submit = "Pridobi informacije" downloadJson = "Prenesite JSON" +processing = "Pridobivanje informacij..." +results = "Rezultati" +noResults = "Za ustvarjanje poročila zaženite orodje." +downloads = "Prenosi" +noneDetected = "Ni zaznanih" +indexTitle = "Kazalo" + +[getPdfInfo.report] +entryLabel = "Celoviti povzetek informacij" +shortTitle = "Informacije o PDF" + +[getPdfInfo.sections] +metadata = "Metapodatki" +formFields = "Polja obrazca" +basicInfo = "Osnovne informacije" +documentInfo = "Informacije o dokumentu" +compliance = "Skladnost" +encryption = "Å ifriranje" +permissions = "Dovoljenja" +other = "Drugo" +perPageInfo = "Informacije po strani" +tableOfContents = "Kazalo vsebine" + +[getPdfInfo.other] +attachments = "Priloge" +embeddedFiles = "Vdelane datoteke" +javaScript = "JavaScript" +layers = "Plasti" +structureTree = "Drevo strukture" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Velikost" +annotations = "Opombe" +images = "Slike" +links = "Povezave" +fonts = "Pisave" +xobjects = "Å tevilo XObject" +multimedia = "Večpredstavnost" + +[getPdfInfo.summary] +pages = "Strani" +fileSize = "Velikost datoteke" +pdfVersion = "Različica PDF" +language = "Jezik" +title = "Povzetek PDF" +author = "Avtor" +created = "Ustvarjeno" +modified = "Spremenjeno" +permsAll = "Vsa dovoljenja so dovoljena" +permsRestricted = "{{count}} omejitev" +permsMixed = "Nekatera dovoljenja omejena" +hasCompliance = "Ima standarde skladnosti" +noCompliance = "Brez standardov skladnosti" +basic = "Osnovne informacije" +documentInfo = "Informacije o dokumentu" +securityTitle = "Varnostno stanje" +technical = "Tehnično" +overviewTitle = "Pregled PDF" + +[getPdfInfo.summary.security] +encrypted = "Å ifriran PDF - prisotna zaŔčita z geslom" +unencrypted = "NeÅ”ifriran PDF - brez zaŔčite z geslom" + +[getPdfInfo.summary.tech] +images = "Slike" +fonts = "Pisave" +formFields = "Polja obrazca" +embeddedFiles = "Vdelane datoteke" +javaScript = "JavaScript" +layers = "Plasti" +bookmarks = "Zaznamki" +multimedia = "Večpredstavnost" + +[getPdfInfo.summary.overview] +untitled = "neimenovan dokument" +unknown = "Neznani avtor" +text = "To je {{pages}}-stranski PDF z naslovom {{title}}, ki ga je ustvaril {{author}} (različica PDF {{version}})." + +[getPdfInfo.error] +partial = "Nekaterih datotek ni bilo mogoče obdelati." +unexpected = "Nepričakovana napaka med pridobivanjem." + +[getPdfInfo.status] +complete = "Pridobivanje zaključeno" [extractPage] tags = "izvleček" @@ -3438,6 +3539,9 @@ signinTitle = "Prosim prijavite se" ssoSignIn = "Prijava prek enotne prijave" oAuth2AutoCreateDisabled = "OAUTH2 Samodejno ustvarjanje uporabnika onemogočeno" oAuth2AdminBlockedUser = "Registracija ali prijava neregistriranih uporabnikov je trenutno blokirana. Prosimo kontaktirajte skrbnika." +oAuth2RequiresLicense = "Prijava prek OAuth/SSO zahteva plačljivo licenco (Server ali Enterprise). Obrnite se na skrbnika, da nadgradi vaÅ” načrt." +saml2RequiresLicense = "Prijava prek SAML zahteva plačljivo licenco (Server ali Enterprise). Obrnite se na skrbnika, da nadgradi vaÅ” načrt." +maxUsersReached = "Doseženo je največje Å”tevilo uporabnikov za vaÅ”o trenutno licenco. Obrnite se na skrbnika, da nadgradi vaÅ” načrt ali doda več mest." oauth2RequestNotFound = "Zahteva za avtorizacijo ni bila najdena" oauth2InvalidUserInfoResponse = "Neveljaven odgovor z informacijami o uporabniku" oauth2invalidRequest = "Neveljavna zahteva" @@ -3846,14 +3950,17 @@ fitToWidth = "Prilagodi Å”irini" actualSize = "Dejanska velikost" [viewer] +cannotPreviewFile = "Predogled datoteke ni mogoč" +dualPageView = "Dvo-stranski pogled" firstPage = "Prva stran" lastPage = "Zadnja stran" -previousPage = "PrejÅ”nja stran" nextPage = "Naslednja stran" +onlyPdfSupported = "Pregledovalnik podpira samo PDF datoteke. Ta datoteka je videti v drugačnem formatu." +previousPage = "PrejÅ”nja stran" +singlePageView = "Enostranski pogled" +unknownFile = "Neznana datoteka" zoomIn = "Povečaj" zoomOut = "PomanjÅ”aj" -singlePageView = "Enostranski pogled" -dualPageView = "Dvo-stranski pogled" [rightRail] closeSelected = "Zapri izbrane datoteke" @@ -3877,6 +3984,7 @@ toggleSidebar = "Preklopi stransko vrstico" exportSelected = "Izvozi izbrane strani" toggleAnnotations = "Preklopi vidnost opomb" annotationMode = "Preklopi način opomb" +print = "Natisni PDF" draw = "RiÅ”i" save = "Shrani" saveChanges = "Shrani spremembe" @@ -4494,6 +4602,7 @@ description = "URL ali ime datoteke do impressuma (zahtevano v nekaterih jurisdi title = "Premium in Enterprise" description = "Konfigurirajte svoj ključ licence Premium ali Enterprise." license = "Konfiguracija licence" +noInput = "Navedite licenčni ključ ali datoteko" [admin.settings.premium.licenseKey] toggle = "Imate licenčni ključ ali potrdilno datoteko?" @@ -4511,6 +4620,25 @@ line1 = "Prepis trenutnega licenčnega ključa ni mogoče razveljaviti." line2 = "PrejÅ”nja licenca bo trajno izgubljena, razen če ste jo varnostno kopirali drugje." line3 = "Pomembno: Licenčne ključe hranite zasebno in varno. Nikoli jih ne delite javno." +[admin.settings.premium.inputMethod] +text = "Licenčni ključ" +file = "Datoteka potrdila" + +[admin.settings.premium.file] +label = "Datoteka licenčnega potrdila" +description = "Naložite svojo licenčno datoteko .lic ali .cert iz nakupov brez povezave" +choose = "Izberite licenčno datoteko" +selected = "Izbrano: {{filename}} ({{size}})" +successMessage = "Licenčna datoteka je bila uspeÅ”no naložena in aktivirana. Ponovni zagon ni potreben." + +[admin.settings.premium.currentLicense] +title = "Aktivna licenca" +file = "Vir: licenčna datoteka ({{path}})" +key = "Vir: licenčni ključ" +type = "Vrsta: {{type}}" +noInput = "Navedite licenčni ključ ali naložite datoteko potrdila" +success = "UspeÅ”no" + [admin.settings.premium.enabled] label = "Omogoči funkcije Premium" description = "Omogoči preverjanje licenčnega ključa za funkcije pro/enterprise" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} izbranih" download = "Prenos" delete = "IzbriÅ”i" unsupported = "Nepodprto" +active = "Aktivno" addToUpload = "Dodaj k nalaganju" +closeFile = "Zapri datoteko" deleteAll = "IzbriÅ”i vse" loadingFiles = "Nalaganje datotek..." noFiles = "Ni razpoložljivih datotek" @@ -5132,7 +5262,7 @@ upgrade = "Nadgradi zdaj →" freeTitle = "Licenca strežnika" overLimitTitle = "Potrebna licenca strežnika" overLimitBody = "NaÅ”e licenciranje brezplačno omogoča do {{freeTierLimit}} uporabnikov na strežnik. Imate {{overLimitUserCopy}} uporabnikov Stirling. Za nemoteno uporabo nadgradite na načrt Stirling Server – neomejena mesta, urejanje besedila PDF in popoln skrbniÅ”ki nadzor za $99/strežnik/mesec." -freeBody = "NaÅ”e licenciranje Open-Core brezplačno omogoča do {{freeTierLimit}} uporabnikov na strežnik. Za nemoteno rast in zgodnji dostop do naÅ”ega novega orodja za urejanje besedila PDF priporočamo načrt Stirling Server – polno urejanje in neomejena mesta za $99/strežnik/mesec." +freeBody = "NaÅ”e licenciranje Open-Core omogoča do {{freeTierLimit}} uporabnikov brezplačno na strežnik. Za nemoteno skaliranje priporočamo načrt Stirling Server - neomejena mesta in podpora za SSO za $99/strežnik/mesec." [onboarding.desktopInstall] title = "Prenesi" @@ -5237,6 +5367,31 @@ error = "Stanja uporabnika ni bilo mogoče posodobiti" success = "Uporabnik uspeÅ”no izbrisan" error = "Uporabnika ni bilo mogoče izbrisati" +[workspace.people.changePassword] +action = "Spremeni geslo" +title = "Spremeni geslo" +subtitle = "Posodobite geslo za" +newPassword = "Novo geslo" +confirmPassword = "Potrdi geslo" +placeholder = "Vnesite novo geslo" +confirmPlaceholder = "Znova vnesite novo geslo" +passwordRequired = "Prosimo, vnesite novo geslo" +passwordMismatch = "Gesli se ne ujemata" +generateRandom = "Ustvari varno geslo" +generatedPreview = "Ustvarjeno geslo:" +copyTooltip = "Kopiraj v odložiŔče" +copiedToClipboard = "Geslo je kopirano v odložiŔče" +copyFailed = "Kopiranje gesla ni uspelo" +sendEmail = "Uporabniku poÅ”lji e-poÅ”to o tej spremembi" +includePassword = "V e-poÅ”to vključi novo geslo" +forcePasswordChange = "Prisili uporabnika, da ob naslednji prijavi spremeni geslo" +emailUnavailable = "E-poÅ”tni naslov tega uporabnika ni veljaven. Obvestila so onemogočena." +smtpDisabled = "Za e-poÅ”tna obvestila mora biti v nastavitvah omogočen SMTP." +notifyOnly = "Poslano bo e-poÅ”tno sporočilo brez gesla, ki bo uporabnika obvestilo, da ga je skrbnik spremenil." +submit = "Posodobi geslo" +success = "Geslo je bilo uspeÅ”no posodobljeno" +error = "Posodobitev gesla ni uspela" + [workspace.people.emailInvite] tab = "E-poÅ”tno povabilo" description = "Spodaj vnesite ali prilepite e-poÅ”tne naslove, ločene z vejicami. Uporabniki bodo prejeli prijavne podatke po e-poÅ”ti." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Zahtevan je vsaj en e-poÅ”tni naslov" submit = "PoÅ”lji povabila" success = "uporabnik(i) uspeÅ”no povabljen(i)" -partialSuccess = "Nekatera povabila niso uspela" +partialFailure = "Nekatera povabila niso uspela" allFailed = "Uporabnikov ni bilo mogoče povabiti" error = "PoÅ”iljanje povabil ni uspelo" @@ -5770,6 +5925,7 @@ subtitle = "Prijavite se s svojim računom Stirling" [setup.selfhosted] title = "Prijavite se v strežnik" subtitle = "Vnesite poverilnice strežnika" +link = "ali se povežite z računom na lastnem strežniku" [setup.server] title = "Poveži se s strežnikom" @@ -5788,6 +5944,14 @@ description = "Vnesite celoten URL svojega samogostovanega strežnika Stirling P emptyUrl = "Vnesite URL strežnika" unreachable = "Povezava s strežnikom ni uspela" testFailed = "Preizkus povezave ni uspel" +configFetch = "Pridobitev konfiguracije strežnika ni uspela. Preverite URL in poskusite znova." + +[setup.server.error.securityDisabled] +title = "Prijava ni omogočena" +body = "Na tem strežniku prijava ni omogočena. Za povezavo s tem strežnikom morate omogočiti overjanje:" +step1 = "V svojem okolju nastavite DOCKER_ENABLE_SECURITY=true" +step2 = "Ali nastavite security.enableLogin=true v settings.yml" +step3 = "Znova zaženite strežnik" [setup.login] title = "Prijava" @@ -5797,6 +5961,13 @@ submit = "Prijava" signInWith = "Prijavite se z" oauthPending = "Odpiranje brskalnika za overjanje..." orContinueWith = "Ali nadaljujte z e-poÅ”to" +serverRequirement = "Opomba: Strežnik mora imeti omogočeno prijavo." +showInstructions = "Kako omogočiti?" +hideInstructions = "Skrij navodila" +instructions = "Za omogočanje prijave na vaÅ”em strežniku Stirling PDF:" +instructionsEnvVar = "Nastavite okoljsko spremenljivko:" +instructionsOrYml = "Ali v settings.yml:" +instructionsRestart = "Nato znova zaženite strežnik, da spremembe začnejo veljati." [setup.login.username] label = "UporabniÅ”ko ime" @@ -5853,6 +6024,7 @@ earlyAccess = "Zgodnji dostop" reset = "Ponastavi spremembe" downloadJson = "Prenesi JSON" generatePdf = "Ustvari PDF" +saveChanges = "Shrani spremembe" [pdfTextEditor.options.autoScaleText] title = "Samodejno prilagodi besedilo okvirjem" @@ -5890,6 +6062,8 @@ alpha = "Ta alfa pregledovalnik se Å”e razvija — nekatere pisave, barve, učin [pdfTextEditor.empty] title = "Ni naloženega dokumenta" subtitle = "Naložite datoteko PDF ali JSON, da začnete urejati besedilo." +dropzone = "Sem povlecite in spustite datoteko PDF ali JSON ali kliknite za brskanje" +dropzoneWithFiles = "Izberite datoteko na zavihku Datoteke ali sem povlecite in spustite datoteko PDF ali JSON oziroma kliknite za brskanje" [pdfTextEditor.welcomeBanner] title = "DobrodoÅ”li v PDF Text Editor (zgodnji dostop)" diff --git a/frontend/public/locales/sr-LATN-RS/translation.toml b/frontend/public/locales/sr-LATN-RS/translation.toml index a241131237..9404bc0011 100644 --- a/frontend/public/locales/sr-LATN-RS/translation.toml +++ b/frontend/public/locales/sr-LATN-RS/translation.toml @@ -163,6 +163,11 @@ unfavorite = "Ukloni iz omiljenog" fullscreen = "Prebaci na režim celog ekrana" sidebar = "Prebaci na režim bočne trake" +[backendStartup] +notFoundTitle = "Bekend nije pronađen" +retry = "PokuÅ”aj ponovo" +unreachable = "Aplikacija trenutno ne može da se poveže sa bekendom. Proverite status bekenda i mrežnu povezanost, pa pokuÅ”ajte ponovo." + [zipWarning] title = "Velika ZIP datoteka" message = "Ovaj ZIP sadrži {{count}} datoteka. Ipak raspakovati?" @@ -912,6 +917,9 @@ desc = "Gradite viÅ”ekorake tokove rada povezivanjem PDF akcija. Idealno za pona desc = "Preklapa PDF-ove jedan preko drugog" title = "Preklapanje PDF-ova" +[home.pdfTextEditor] +title = "PDF uređivač teksta" +desc = "Uređujte postojeći tekst i slike unutar PDF-ova" [home.addText] tags = "tekst,anotacija,oznaka" @@ -1213,11 +1221,11 @@ pdfaDigitalSignatureWarning = "PDF sadrži digitalni potpis. Biće uklonjen u sl fileFormat = "Format datoteke" wordDoc = "Word dokument" wordDocExt = "Word dokument (.docx)" -odtExt = "OpenDocument Text (.odt)" +odtExt = "OpenDocument tekst (.odt)" pptExt = "PowerPoint (.pptx)" -odpExt = "OpenDocument Presentation (.odp)" +odpExt = "OpenDocument prezentacija (.odp)" txtExt = "Običan tekst (.txt)" -rtfExt = "Rich Text Format (.rtf)" +rtfExt = "Format obogaćenog teksta (.rtf)" selectedFiles = "Izabrane datoteke" noFileSelected = "Nije izabrana nijedna datoteka. Koristite panel datoteka da dodate datoteke." convertFiles = "Konvertuj datoteke" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Crtani potpis" defaultImageLabel = "Otpremljeni potpis" defaultTextLabel = "Ukucani potpis" saveButton = "Sačuvaj potpis" +savePersonal = "Sačuvaj lično" +saveShared = "Sačuvaj deljeno" saveUnavailable = "Prvo napravite potpis da biste ga sačuvali." noChanges = "Trenutni potpis je već sačuvan." +tempStorageTitle = "Privremeno skladiÅ”te pregledača" +tempStorageDescription = "Potpisi se čuvaju samo u vaÅ”em pregledaču. Biće izgubljeni ako obriÅ”ete podatke pregledača ili promenite pregledač." +personalHeading = "Lični potpisi" +sharedHeading = "Deljeni potpisi" +personalDescription = "Samo vi možete da vidite ove potpise." +sharedDescription = "Svi korisnici mogu da vide i koriste ove potpise." [sign.saved.type] canvas = "Crtanje" @@ -2318,7 +2334,7 @@ title = "Ravnanje" header = "Ravnanje PDF fajlova" flattenOnlyForms = "Izravnaj samo forme" submit = "Ravnanje" -filenamePrefix = "flattened" +filenamePrefix = "spljoÅ”teno" [flatten.files] placeholder = "Izaberite PDF datoteku u glavnom prikazu da biste započeli" @@ -3020,6 +3036,91 @@ title = "Informacije o PDF-u" header = "Informacije o PDF-u" submit = "Informacije" downloadJson = "Preuzmi JSON" +processing = "Izdvajanje informacija..." +results = "Rezultati" +noResults = "Pokrenite alat da generiÅ”ete izveÅ”taj." +downloads = "Preuzimanja" +noneDetected = "Nije otkriveno niÅ”ta" +indexTitle = "Indeks" + +[getPdfInfo.report] +entryLabel = "Potpuni sažetak informacija" +shortTitle = "Informacije o PDF-u" + +[getPdfInfo.sections] +metadata = "Metapodaci" +formFields = "Polja obrasca" +basicInfo = "Osnovne informacije" +documentInfo = "Informacije o dokumentu" +compliance = "Usklađenost" +encryption = "Å ifrovanje" +permissions = "Dozvole" +other = "Ostalo" +perPageInfo = "Informacije po stranici" +tableOfContents = "Sadržaj" + +[getPdfInfo.other] +attachments = "Prilozi" +embeddedFiles = "Ugrađene datoteke" +javaScript = "JavaScript" +layers = "Slojevi" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Veličina" +annotations = "Anotacije" +images = "Slike" +links = "Linkovi" +fonts = "Fontovi" +xobjects = "Broj XObject-ova" +multimedia = "Multimedija" + +[getPdfInfo.summary] +pages = "Stranice" +fileSize = "Veličina datoteke" +pdfVersion = "PDF verzija" +language = "Jezik" +title = "PDF sažetak" +author = "Autor" +created = "Kreirano" +modified = "Izmenjeno" +permsAll = "Sve dozvole su omogućene" +permsRestricted = "{{count}} ograničenja" +permsMixed = "Neke dozvole su ograničene" +hasCompliance = "Ima standarde usklađenosti" +noCompliance = "Nema standarda usklađenosti" +basic = "Osnovne informacije" +documentInfo = "Informacije o dokumentu" +securityTitle = "Status bezbednosti" +technical = "Tehničko" +overviewTitle = "Pregled PDF-a" + +[getPdfInfo.summary.security] +encrypted = "Å ifrovan PDF - prisutna zaÅ”tita lozinkom" +unencrypted = "NeÅ”ifrovan PDF - bez zaÅ”tite lozinkom" + +[getPdfInfo.summary.tech] +images = "Slike" +fonts = "Fontovi" +formFields = "Polja obrasca" +embeddedFiles = "Ugrađene datoteke" +javaScript = "JavaScript" +layers = "Slojevi" +bookmarks = "Obeleživači" +multimedia = "Multimedija" + +[getPdfInfo.summary.overview] +untitled = "neimenovani dokument" +unknown = "Nepoznat autor" +text = "Ovo je PDF od {{pages}} stranica pod nazivom {{title}} koji je kreirao {{author}} (PDF verzija {{version}})." + +[getPdfInfo.error] +partial = "Neke datoteke nije bilo moguće obraditi." +unexpected = "Neočekivana greÅ”ka tokom izdvajanja." + +[getPdfInfo.status] +complete = "Izdvajanje zavrÅ”eno" [extractPage] tags = "izdvajanje" @@ -3438,6 +3539,9 @@ signinTitle = "Molimo vas da se prijavite" ssoSignIn = "Prijavite se putem jedinstvene prijave" oAuth2AutoCreateDisabled = "OAUTH2 automatsko kreiranje korisnika je onemogućeno" oAuth2AdminBlockedUser = "Registracija ili prijava neregistrovanog korisnika je trenutno onemogućeno. Kontaktirajte administratora." +oAuth2RequiresLicense = "Prijava putem OAuth/SSO zahteva plaćenu licencu (Server ili Enterprise). Kontaktirajte administratora da unapredi vaÅ” plan." +saml2RequiresLicense = "Prijava putem SAML zahteva plaćenu licencu (Server ili Enterprise). Kontaktirajte administratora da unapredi vaÅ” plan." +maxUsersReached = "Dostignut je maksimalni broj korisnika za vaÅ”u trenutnu licencu. Kontaktirajte administratora da unapredi vaÅ” plan ili doda viÅ”e mesta." oauth2RequestNotFound = "Zahtev za autorizaciju nije pronađen" oauth2InvalidUserInfoResponse = "Neispravan odgovor sa korisničkim informacijama" oauth2invalidRequest = "Neispravan zahtev" @@ -3771,7 +3875,7 @@ version = "Aktuelno izdanje" title = "API dokumentacija" header = "API dokumentacija" desc = "Pregledajte i testirajte Stirling PDF API endpoints" -tags = "api,documentation,swagger,endpoints,development" +tags = "api,dokumentacija,swagger,krajnje tačke,razvoj" [cookieBanner.popUp] title = "Kako koristimo kolačiće" @@ -3846,14 +3950,17 @@ fitToWidth = "Uklopi po Å”irini" actualSize = "Stvarna veličina" [viewer] +cannotPreviewFile = "Nije moguće pregledati datoteku" +dualPageView = "Prikaz dve stranice" firstPage = "Prva stranica" lastPage = "Poslednja stranica" -previousPage = "Prethodna stranica" nextPage = "Sledeća stranica" +onlyPdfSupported = "Prikazivač podržava samo PDF datoteke. Izgleda da je ova datoteka drugačijeg formata." +previousPage = "Prethodna stranica" +singlePageView = "Prikaz jedne stranice" +unknownFile = "Nepoznata datoteka" zoomIn = "Uvećaj" zoomOut = "Umanji" -singlePageView = "Prikaz jedne stranice" -dualPageView = "Prikaz dve stranice" [rightRail] closeSelected = "Zatvori izabrane fajlove" @@ -3877,6 +3984,7 @@ toggleSidebar = "Uključi/isključi bočnu traku" exportSelected = "Izvezi izabrane stranice" toggleAnnotations = "Uključi/isključi vidljivost anotacija" annotationMode = "Uključi/isključi režim anotacija" +print = "Å tampaj PDF" draw = "Crtaj" save = "Sačuvaj" saveChanges = "Sačuvaj izmene" @@ -4494,6 +4602,7 @@ description = "URL ili naziv datoteke do impresuma (obavezno u nekim jurisdikcij title = "Premium i Enterprise" description = "Podesite svoj premium ili enterprise licencni ključ." license = "Konfiguracija licence" +noInput = "Navedite licencni ključ ili fajl" [admin.settings.premium.licenseKey] toggle = "Imate licencni ključ ili datoteku sertifikata?" @@ -4511,6 +4620,25 @@ line1 = "Prepisivanje vaÅ”eg trenutnog licencnog ključa ne može se opozvati." line2 = "Prethodna licenca će biti trajno izgubljena osim ako je niste sačuvali na drugom mestu." line3 = "Važno: Čuvajte licencne ključeve privatnim i bezbednim. Nikada ih javno ne delite." +[admin.settings.premium.inputMethod] +text = "Licencni ključ" +file = "Fajl sertifikata" + +[admin.settings.premium.file] +label = "Fajl licencnog sertifikata" +description = "Otpremite svoj .lic ili .cert licencni fajl iz offline kupovina" +choose = "Izaberite licencni fajl" +selected = "Izabrano: {{filename}} ({{size}})" +successMessage = "Licencni fajl je uspeÅ”no otpremljen i aktiviran. Restart nije potreban." + +[admin.settings.premium.currentLicense] +title = "Aktivna licenca" +file = "Izvor: licencni fajl ({{path}})" +key = "Izvor: licencni ključ" +type = "Tip: {{type}}" +noInput = "Navedite licencni ključ ili otpremite fajl sertifikata" +success = "Uspeh" + [admin.settings.premium.enabled] label = "Omogući premium funkcije" description = "Omogući provere licencnog ključa za pro/enterprise funkcije" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} izabrano" download = "Preuzmi" delete = "ObriÅ”i" unsupported = "Nepodržano" +active = "Aktivno" addToUpload = "Dodaj za otpremanje" +closeFile = "Zatvori datoteku" deleteAll = "ObriÅ”i sve" loadingFiles = "Učitavanje datoteka..." noFiles = "Nema dostupnih datoteka" @@ -5132,7 +5262,7 @@ upgrade = "Nadogradite sada →" freeTitle = "Serverska licenca" overLimitTitle = "Potrebna serverska licenca" overLimitBody = "NaÅ”e licenciranje dozvoljava do {{freeTierLimit}} korisnika besplatno po serveru. Imate {{overLimitUserCopy}} Stirling korisnika. Da nastavite bez prekida, pređite na Stirling Server plan - neograničena mesta, uređivanje PDF teksta i puna admin kontrola za $99/server/mes." -freeBody = "NaÅ”e Open-Core licenciranje dozvoljava do {{freeTierLimit}} korisnika besplatno po serveru. Da se bez prekida skalirate i dobijete rani pristup naÅ”em novom alatu za uređivanje PDF teksta, preporučujemo Stirling Server plan - puno uređivanje i neograničena mesta za $99/server/mes." +freeBody = "NaÅ”e licenciranje Open-Core dozvoljava do {{freeTierLimit}} korisnika besplatno po serveru. Za neometano skaliranje, preporučujemo plan Stirling Server - neograničena mesta i SSO podrÅ”ka za $99/server/mo." [onboarding.desktopInstall] title = "Preuzmi" @@ -5237,6 +5367,31 @@ error = "Ažuriranje statusa korisnika nije uspelo" success = "Korisnik uspeÅ”no obrisan" error = "Brisanje korisnika nije uspelo" +[workspace.people.changePassword] +action = "Promenite lozinku" +title = "Promenite lozinku" +subtitle = "Ažurirajte lozinku za" +newPassword = "Nova lozinka" +confirmPassword = "Potvrdite lozinku" +placeholder = "Unesite novu lozinku" +confirmPlaceholder = "Ponovo unesite novu lozinku" +passwordRequired = "Unesite novu lozinku" +passwordMismatch = "Lozinke se ne poklapaju" +generateRandom = "GeneriÅ”ite bezbednu lozinku" +generatedPreview = "Generisana lozinka:" +copyTooltip = "Kopirajte u privremenu memoriju" +copiedToClipboard = "Lozinka je kopirana u privremenu memoriju" +copyFailed = "Nije uspelo kopiranje lozinke" +sendEmail = "PoÅ”aljite korisniku email o ovoj promeni" +includePassword = "Uključite novu lozinku u email" +forcePasswordChange = "Naterajte korisnika da promeni lozinku pri sledećoj prijavi" +emailUnavailable = "Email ovog korisnika nije važeća email adresa. ObaveÅ”tenja su onemogućena." +smtpDisabled = "Email obaveÅ”tenja zahtevaju da SMTP bude omogućen u podeÅ”avanjima." +notifyOnly = "Biće poslat email bez lozinke, kako bi korisnik znao da je administrator promenio lozinku." +submit = "Ažurirajte lozinku" +success = "Lozinka je uspeÅ”no ažurirana" +error = "Ažuriranje lozinke nije uspelo" + [workspace.people.emailInvite] tab = "Poziv emailom" description = "Ukucajte ili nalepite email adrese ispod, odvojene zarezima. Korisnici će dobiti pristupne podatke putem emaila." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Potrebna je bar jedna email adresa" submit = "PoÅ”alji pozive" success = "Korisnik(ci) uspeÅ”no pozvan(i)" -partialSuccess = "Neki pozivi nisu uspeli" +partialFailure = "Neki pozivi nisu uspeli" allFailed = "Pozivanje korisnika nije uspelo" error = "Slanje poziva nije uspelo" @@ -5770,6 +5925,7 @@ subtitle = "Prijavite se svojim Stirling nalogom" [setup.selfhosted] title = "Prijava na server" subtitle = "Unesite kredencijale servera" +link = "ili se povežite na samohostovani nalog" [setup.server] title = "Poveži se na server" @@ -5788,6 +5944,14 @@ description = "Unesite puni URL vaÅ”eg samohostovanog Stirling PDF servera" emptyUrl = "Unesite URL servera" unreachable = "Nije moguće povezati se sa serverom" testFailed = "Test veze nije uspeo" +configFetch = "Nije uspelo preuzimanje konfiguracije servera. Proverite URL i pokuÅ”ajte ponovo." + +[setup.server.error.securityDisabled] +title = "Prijavljivanje nije omogućeno" +body = "Na ovom serveru prijavljivanje nije omogućeno. Da biste se povezali na ovaj server, morate omogućiti autentikaciju:" +step1 = "Postavite DOCKER_ENABLE_SECURITY=true u svom okruženju" +step2 = "Ili postavite security.enableLogin=true u settings.yml" +step3 = "Restartujte server" [setup.login] title = "Prijava" @@ -5797,6 +5961,13 @@ submit = "Prijavi se" signInWith = "Prijavite se sa" oauthPending = "Otvaranje pregledača za autentikaciju..." orContinueWith = "Ili nastavite uz email" +serverRequirement = "Napomena: Server mora imati omogućenu prijavu." +showInstructions = "Kako omogućiti?" +hideInstructions = "Sakrij uputstva" +instructions = "Da biste omogućili prijavu na svom Stirling PDF serveru:" +instructionsEnvVar = "Podesite promenljivu okruženja:" +instructionsOrYml = "Ili u settings.yml:" +instructionsRestart = "Zatim restartujte server da bi izmene stupile na snagu." [setup.login.username] label = "Korisničko ime" @@ -5853,6 +6024,7 @@ earlyAccess = "Rani pristup" reset = "PoniÅ”ti izmene" downloadJson = "Preuzmi JSON" generatePdf = "GeneriÅ”i PDF" +saveChanges = "Sačuvajte izmene" [pdfTextEditor.options.autoScaleText] title = "Automatski prilagodi tekst okvirima" @@ -5890,6 +6062,8 @@ alpha = "Ovaj alfa pregledač je i dalje u razvoju—određeni fontovi, boje, ef [pdfTextEditor.empty] title = "Nijedan dokument nije učitan" subtitle = "Učitajte PDF ili JSON datoteku da biste počeli sa uređivanjem teksta." +dropzone = "Prevucite i otpustite PDF ili JSON datoteku ovde ili kliknite da izaberete" +dropzoneWithFiles = "Izaberite datoteku sa kartice Datoteke, ili prevucite i otpustite PDF ili JSON datoteku ovde, ili kliknite da izaberete" [pdfTextEditor.welcomeBanner] title = "DobrodoÅ”li u Uređivač teksta za PDF (rani pristup)" diff --git a/frontend/public/locales/sv-SE/translation.toml b/frontend/public/locales/sv-SE/translation.toml index 3cf3913ca6..ac154f1418 100644 --- a/frontend/public/locales/sv-SE/translation.toml +++ b/frontend/public/locales/sv-SE/translation.toml @@ -163,6 +163,11 @@ unfavorite = "Ta bort frĆ„n favoriter" fullscreen = "Byt till helskƤrmslƤge" sidebar = "Byt till sidopanellƤge" +[backendStartup] +notFoundTitle = "Backend hittades inte" +retry = "Fƶrsƶk igen" +unreachable = "Applikationen kan fƶr nƤrvarande inte ansluta till backend. Kontrollera backendens status och nƤtverksanslutningen och fƶrsƶk sedan igen." + [zipWarning] title = "Stor ZIP-fil" message = "Denna ZIP innehĆ„ller {{count}} filer. Extrahera ƤndĆ„?" @@ -912,6 +917,9 @@ desc = "Skapa flerstegade arbetsflƶden genom att kedja ihop PDF‑ÄtgƤrder. P desc = "Ɩverlagrar PDF:er ovanpĆ„ en annan PDF" title = "Ɩverlagra PDF:er" +[home.pdfTextEditor] +title = "PDF-textredigerare" +desc = "Redigera befintlig text och bilder i PDF-filer" [home.addText] tags = "text,kommentar,etikett" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Ritad signatur" defaultImageLabel = "Uppladdad signatur" defaultTextLabel = "Skriven signatur" saveButton = "Spara signatur" +savePersonal = "Spara som personlig" +saveShared = "Spara som delad" saveUnavailable = "Skapa en signatur fƶrst fƶr att kunna spara." noChanges = "Aktuell signatur Ƥr redan sparad." +tempStorageTitle = "TillfƤllig lagring i webblƤsaren" +tempStorageDescription = "Signaturer lagras endast i din webblƤsare. De gĆ„r fƶrlorade om du rensar webblƤsardata eller byter webblƤsare." +personalHeading = "Personliga signaturer" +sharedHeading = "Delade signaturer" +personalDescription = "Endast du kan se dessa signaturer." +sharedDescription = "Alla anvƤndare kan se och anvƤnda dessa signaturer." [sign.saved.type] canvas = "Ritning" @@ -2281,7 +2297,7 @@ placeDesc = "Placera signaturen pĆ„ din PDF" [sign.type] title = "Signaturtyp" draw = "Rita" -canvas = "Canvas" +canvas = "Rityta" image = "Bild" text = "Text" saved = "Sparad" @@ -3020,6 +3036,91 @@ title = "HƤmta information om PDF" header = "HƤmta information om PDF" submit = "HƤmta information" downloadJson = "Ladda ner JSON" +processing = "Extraherar information..." +results = "Resultat" +noResults = "Kƶr verktyget fƶr att skapa en rapport." +downloads = "Nedladdningar" +noneDetected = "Inget upptƤckt" +indexTitle = "Index" + +[getPdfInfo.report] +entryLabel = "FullstƤndig informationssammanfattning" +shortTitle = "PDF-information" + +[getPdfInfo.sections] +metadata = "Metadata" +formFields = "FormulƤrfƤlt" +basicInfo = "GrundlƤggande info" +documentInfo = "Dokumentinformation" +compliance = "Efterlevnad" +encryption = "Kryptering" +permissions = "Behƶrigheter" +other = "Ɩvrigt" +perPageInfo = "Information per sida" +tableOfContents = "InnehĆ„llsfƶrteckning" + +[getPdfInfo.other] +attachments = "Bilagor" +embeddedFiles = "InbƤddade filer" +javaScript = "JavaScript" +layers = "Lager" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Storlek" +annotations = "Anteckningar" +images = "Bilder" +links = "LƤnkar" +fonts = "Typsnitt" +xobjects = "Antal XObject" +multimedia = "Multimedia" + +[getPdfInfo.summary] +pages = "Sidor" +fileSize = "Filstorlek" +pdfVersion = "PDF-version" +language = "SprĆ„k" +title = "PDF-sammanfattning" +author = "Fƶrfattare" +created = "Skapad" +modified = "Ƅndrad" +permsAll = "Alla behƶrigheter tillĆ„tna" +permsRestricted = "{{count}} begrƤnsningar" +permsMixed = "Vissa behƶrigheter begrƤnsade" +hasCompliance = "Har efterlevnadsstandarder" +noCompliance = "Inga efterlevnadsstandarder" +basic = "GrundlƤggande information" +documentInfo = "Dokumentinformation" +securityTitle = "SƤkerhetsstatus" +technical = "Tekniskt" +overviewTitle = "PDF-ƶversikt" + +[getPdfInfo.summary.security] +encrypted = "Krypterad PDF - Lƶsenordsskydd finns" +unencrypted = "Okrypterad PDF - Inget lƶsenordsskydd" + +[getPdfInfo.summary.tech] +images = "Bilder" +fonts = "Typsnitt" +formFields = "FormulƤrfƤlt" +embeddedFiles = "InbƤddade filer" +javaScript = "JavaScript" +layers = "Lager" +bookmarks = "BokmƤrken" +multimedia = "Multimedia" + +[getPdfInfo.summary.overview] +untitled = "ett namnlƶst dokument" +unknown = "OkƤnd fƶrfattare" +text = "Detta Ƥr en PDF pĆ„ {{pages}} sidor med titeln {{title}}, skapad av {{author}} (PDF-version {{version}})." + +[getPdfInfo.error] +partial = "Vissa filer kunde inte bearbetas." +unexpected = "OvƤntat fel under extrahering." + +[getPdfInfo.status] +complete = "Extrahering klar" [extractPage] tags = "extrahera" @@ -3438,6 +3539,9 @@ signinTitle = "VƤnligen logga in" ssoSignIn = "Logga in via enkel inloggning" oAuth2AutoCreateDisabled = "OAUTH2 Auto-skapa anvƤndare inaktiverad" oAuth2AdminBlockedUser = "Registrering eller inloggning av icke-registrerade anvƤndare Ƥr fƶr nƤrvarande blockerad. Kontakta administratƶren." +oAuth2RequiresLicense = "OAuth/SSO-inloggning krƤver en betald licens (Server eller Enterprise). Kontakta administratƶren fƶr att uppgradera din plan." +saml2RequiresLicense = "SAML-inloggning krƤver en betald licens (Server eller Enterprise). Kontakta administratƶren fƶr att uppgradera din plan." +maxUsersReached = "Maximalt antal anvƤndare har uppnĆ„tts fƶr din nuvarande licens. Kontakta administratƶren fƶr att uppgradera din plan eller lƤgga till fler anvƤndarplatser." oauth2RequestNotFound = "AuktoriseringsbegƤran hittades inte" oauth2InvalidUserInfoResponse = "Ogiltigt svar pĆ„ anvƤndarinformation" oauth2invalidRequest = "Ogiltig begƤran" @@ -3846,14 +3950,17 @@ fitToWidth = "Anpassa till bredd" actualSize = "Faktisk storlek" [viewer] +cannotPreviewFile = "Kan inte fƶrhandsgranska filen" +dualPageView = "Dubbelsidig vy" firstPage = "Fƶrsta sidan" lastPage = "Sista sidan" -previousPage = "FƶregĆ„ende sida" nextPage = "NƤsta sida" +onlyPdfSupported = "Visaren stƶder endast PDF-filer. Den hƤr filen verkar vara i ett annat format." +previousPage = "FƶregĆ„ende sida" +singlePageView = "Ensidig vy" +unknownFile = "OkƤnd fil" zoomIn = "Zooma in" zoomOut = "Zooma ut" -singlePageView = "Ensidig vy" -dualPageView = "Dubbelsidig vy" [rightRail] closeSelected = "StƤng markerade filer" @@ -3877,6 +3984,7 @@ toggleSidebar = "VƤxla sidofƤlt" exportSelected = "Exportera markerade sidor" toggleAnnotations = "VƤxla synlighet fƶr anteckningar" annotationMode = "VƤxla anteckningslƤge" +print = "Skriv ut PDF" draw = "Rita" save = "Spara" saveChanges = "Spara Ƥndringar" @@ -4494,6 +4602,7 @@ description = "URL eller filnamn till impressum (krƤvs i vissa jurisdiktioner)" title = "Premium och Enterprise" description = "Konfigurera din premium- eller enterprise-licensnyckel." license = "Licenskonfiguration" +noInput = "Ange en licensnyckel eller fil" [admin.settings.premium.licenseKey] toggle = "Har du en licensnyckel eller certifikatfil?" @@ -4511,6 +4620,25 @@ line1 = "Att skriva ƶver din nuvarande licensnyckel kan inte Ć„ngras." line2 = "Din tidigare licens gĆ„r fƶrlorad permanent om du inte har sƤkerhetskopierat den nĆ„gon annanstans." line3 = "Viktigt: HĆ„ll licensnycklar privata och sƤkra. Dela dem aldrig offentligt." +[admin.settings.premium.inputMethod] +text = "Licensnyckel" +file = "Certifikatfil" + +[admin.settings.premium.file] +label = "Licenscertifikatfil" +description = "Ladda upp din .lic- eller .cert-licensfil frĆ„n offlinekƶp" +choose = "VƤlj licensfil" +selected = "Vald: {{filename}} ({{size}})" +successMessage = "Licensfilen har laddats upp och aktiverats. Ingen omstart krƤvs." + +[admin.settings.premium.currentLicense] +title = "Aktiv licens" +file = "KƤlla: Licensfil ({{path}})" +key = "KƤlla: Licensnyckel" +type = "Typ: {{type}}" +noInput = "Ange en licensnyckel eller ladda upp en certifikatfil" +success = "Lyckat" + [admin.settings.premium.enabled] label = "Aktivera premiumfunktioner" description = "Aktivera licensnyckelkontroller fƶr pro-/enterprise-funktioner" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} markerade" download = "Ladda ner" delete = "Radera" unsupported = "Stƶds inte" +active = "Aktiv" addToUpload = "LƤgg till i uppladdning" +closeFile = "StƤng fil" deleteAll = "Ta bort alla" loadingFiles = "LƤser in filer..." noFiles = "Inga filer tillgƤngliga" @@ -5132,7 +5262,7 @@ upgrade = "Uppgradera nu →" freeTitle = "Serverlicens" overLimitTitle = "Serverlicens krƤvs" overLimitBody = "VĆ„r licensiering tillĆ„ter upp till {{freeTierLimit}} anvƤndare gratis per server. Du har {{overLimitUserCopy}} Stirling-anvƤndare. Fƶr att fortsƤtta utan avbrott, uppgradera till Stirling Server-planen - obegrƤnsade platser, PDF-textredigering och full adminkontroll fƶr $99/server/mĆ„n." -freeBody = "VĆ„r Open-Core-licens tillĆ„ter upp till {{freeTierLimit}} anvƤndare gratis per server. Fƶr att skala utan avbrott och fĆ„ tidig Ć„tkomst till vĆ„rt nya PDF-textredigeringsverktyg rekommenderar vi Stirling Server-planen - full redigering och obegrƤnsade platser fƶr $99/server/mĆ„n." +freeBody = "VĆ„r Open-Core-licens tillĆ„ter upp till {{freeTierLimit}} anvƤndare gratis per server. Fƶr att skala utan avbrott rekommenderar vi Stirling Server plan - obegrƤnsat antal platser och SSO-stƶd fƶr $99/server/mĆ„nad." [onboarding.desktopInstall] title = "Ladda ner" @@ -5237,6 +5367,31 @@ error = "Misslyckades med att uppdatera anvƤndarstatus" success = "AnvƤndare borttagen" error = "Misslyckades med att ta bort anvƤndare" +[workspace.people.changePassword] +action = "Byt lƶsenord" +title = "Byt lƶsenord" +subtitle = "Uppdatera lƶsenordet fƶr" +newPassword = "Nytt lƶsenord" +confirmPassword = "BekrƤfta lƶsenord" +placeholder = "Ange ett nytt lƶsenord" +confirmPlaceholder = "Ange det nya lƶsenordet igen" +passwordRequired = "Ange ett nytt lƶsenord" +passwordMismatch = "Lƶsenorden matchar inte" +generateRandom = "Generera sƤkert lƶsenord" +generatedPreview = "Genererat lƶsenord:" +copyTooltip = "Kopiera till urklipp" +copiedToClipboard = "Lƶsenord kopierat till urklipp" +copyFailed = "Det gick inte att kopiera lƶsenordet" +sendEmail = "Skicka e-post till anvƤndaren om denna Ƥndring" +includePassword = "Inkludera det nya lƶsenordet i e-postmeddelandet" +forcePasswordChange = "Tvinga anvƤndaren att byta lƶsenord vid nƤsta inloggning" +emailUnavailable = "Den hƤr anvƤndarens e-postadress Ƥr inte giltig. Aviseringar Ƥr inaktiverade." +smtpDisabled = "E-postaviseringar krƤver att SMTP Ƥr aktiverat i instƤllningarna." +notifyOnly = "Ett e-postmeddelande skickas utan lƶsenordet och informerar anvƤndaren om att en admin har Ƥndrat det." +submit = "Uppdatera lƶsenord" +success = "Lƶsenordet har uppdaterats" +error = "Det gick inte att uppdatera lƶsenordet" + [workspace.people.emailInvite] tab = "E-postinbjudan" description = "Skriv eller klistra in e-postadresser nedan, separerade med kommatecken. AnvƤndare fĆ„r inloggningsuppgifter via e-post." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "Minst en e-postadress krƤvs" submit = "Skicka inbjudningar" success = "AnvƤndare inbjudna" -partialSuccess = "Vissa inbjudningar misslyckades" +partialFailure = "Vissa inbjudningar misslyckades" allFailed = "Misslyckades med att bjuda in anvƤndare" error = "Misslyckades med att skicka inbjudningar" @@ -5770,6 +5925,7 @@ subtitle = "Logga in med ditt Stirling-konto" [setup.selfhosted] title = "Logga in pĆ„ server" subtitle = "Ange dina serveruppgifter" +link = "eller anslut till ett sjƤlvhostat konto" [setup.server] title = "Anslut till server" @@ -5788,6 +5944,14 @@ description = "Ange den fullstƤndiga URL:en till din sjƤlvhostade Stirling PDF emptyUrl = "Ange en server-URL" unreachable = "Kunde inte ansluta till servern" testFailed = "Anslutningstest misslyckades" +configFetch = "Det gick inte att hƤmta serverkonfigurationen. Kontrollera URL:en och fƶrsƶk igen." + +[setup.server.error.securityDisabled] +title = "Inloggning inte aktiverad" +body = "Den hƤr servern har inte inloggning aktiverad. Fƶr att ansluta till den hƤr servern mĆ„ste du aktivera autentisering:" +step1 = "StƤll in DOCKER_ENABLE_SECURITY=true i din miljƶ" +step2 = "Eller stƤll in security.enableLogin=true i settings.yml" +step3 = "Starta om servern" [setup.login] title = "Logga in" @@ -5797,6 +5961,13 @@ submit = "Logga in" signInWith = "Logga in med" oauthPending = "Ɩppnar webblƤsaren fƶr autentisering..." orContinueWith = "Eller fortsƤtt med e-post" +serverRequirement = "Observera: Servern mĆ„ste ha inloggning aktiverad." +showInstructions = "Hur aktiverar man?" +hideInstructions = "Dƶlj instruktioner" +instructions = "Fƶr att aktivera inloggning pĆ„ din Stirling PDF-server:" +instructionsEnvVar = "StƤll in miljƶvariabeln:" +instructionsOrYml = "Eller i settings.yml:" +instructionsRestart = "Starta sedan om servern fƶr att Ƥndringarna ska bƶrja gƤlla." [setup.login.username] label = "AnvƤndarnamn" @@ -5853,6 +6024,7 @@ earlyAccess = "Tidig Ć„tkomst" reset = "ƅterstƤll Ƥndringar" downloadJson = "Ladda ner JSON" generatePdf = "Skapa PDF" +saveChanges = "Spara Ƥndringar" [pdfTextEditor.options.autoScaleText] title = "Skala text automatiskt fƶr att passa rutor" @@ -5890,6 +6062,8 @@ alpha = "Denna alfa-visare utvecklas fortfarande—vissa typsnitt, fƤrger, tran [pdfTextEditor.empty] title = "Inget dokument inlƤst" subtitle = "Ladda en PDF- eller JSON-fil fƶr att bƶrja redigera textinnehĆ„ll." +dropzone = "Dra och slƤpp en PDF- eller JSON-fil hƤr, eller klicka fƶr att blƤddra" +dropzoneWithFiles = "VƤlj en fil frĆ„n fliken Filer, eller dra och slƤpp en PDF- eller JSON-fil hƤr, eller klicka fƶr att blƤddra" [pdfTextEditor.welcomeBanner] title = "VƤlkommen till PDF Text Editor (Tidig Ć„tkomst)" diff --git a/frontend/public/locales/th-TH/translation.toml b/frontend/public/locales/th-TH/translation.toml index bfd0077b27..eea7ee0d68 100644 --- a/frontend/public/locales/th-TH/translation.toml +++ b/frontend/public/locales/th-TH/translation.toml @@ -163,6 +163,11 @@ unfavorite = "ąø™ąø³ąø­ąø­ąøąøˆąø²ąøąø£ąø²ąø¢ąøąø²ąø£ą¹‚ąø›ąø£ąø”" fullscreen = "ąøŖąø„ąø±ąøšą¹€ąø›ą¹‡ąø™ą¹‚ąø«ąø”ąø”ą¹€ąø•ą¹‡ąø”ąø«ąø™ą¹‰ąø²ąøˆąø­" sidebar = "ąøŖąø„ąø±ąøšą¹€ąø›ą¹‡ąø™ą¹‚ąø«ąø”ąø”ą¹ąø–ąøšąø”ą¹‰ąø²ąø™ąø‚ą¹‰ąø²ąø‡" +[backendStartup] +notFoundTitle = "ą¹„ąø”ą¹ˆąøžąøš Backend" +retry = "คองอีกครั้ง" +unreachable = "ąø‚ąø“ąø°ąø™ąøµą¹‰ą¹ąø­ąø›ąøžąø„ąø“ą¹€ąø„ąøŠąø±ąø™ą¹„ąø”ą¹ˆąøŖąø²ąø”ąø²ąø£ąø–ą¹€ąøŠąø·ą¹ˆąø­ąø”ąø•ą¹ˆąø­ąøąø±ąøš Backend ได้ ą¹‚ąø›ąø£ąø”ąø•ąø£ąø§ąøˆąøŖąø­ąøšąøŖąø–ąø²ąø™ąø°ąø‚ąø­ąø‡ Backend ą¹ąø„ąø°ąøąø²ąø£ą¹€ąøŠąø·ą¹ˆąø­ąø”ąø•ą¹ˆąø­ą¹€ąø„ąø£ąø·ąø­ąø‚ą¹ˆąø²ąø¢ ąøˆąø²ąøąø™ąø±ą¹‰ąø™ąø„ąø­ąø‡ąø­ąøµąøąø„ąø£ąø±ą¹‰ąø‡" + [zipWarning] title = "ą¹„ąøŸąø„ą¹Œ ZIP ąø‚ąø™ąø²ąø”ą¹ƒąø«ąøą¹ˆ" message = "ZIP นี้ดี {{count}} ą¹„ąøŸąø„ą¹Œ ąø•ą¹‰ąø­ąø‡ąøąø²ąø£ą¹ąø•ąøą¹„ąøŸąø„ą¹Œąø•ą¹ˆąø­ąø«ąø£ąø·ąø­ą¹„ąø”ą¹ˆ?" @@ -347,7 +352,7 @@ teams = "ทีด" title = "ąøąø²ąø£ąøąø³ąø«ąø™ąø”ąø„ą¹ˆąø²" systemSettings = "ąøąø²ąø£ąø•ąø±ą¹‰ąø‡ąø„ą¹ˆąø²ąø£ąø°ąøšąøš" features = "ąøŸąøµą¹€ąøˆąø­ąø£ą¹Œ" -endpoints = "Endpoints" +endpoints = "ปคายทาง" database = "ฐานข้อดูค" advanced = "ขั้นสูง" @@ -369,7 +374,7 @@ privacy = "ąø„ąø§ąø²ąø”ą¹€ąø›ą¹‡ąø™ąøŖą¹ˆąø§ąø™ąø•ąø±ąø§" [settings.developer] title = "ąø™ąø±ąøąøžąø±ąø’ąø™ąø²" -apiKeys = "API Keys" +apiKeys = "ąø„ąøµąø¢ą¹Œ API" [settings.tooltips] enableLoginFirst = "ą¹€ąø›ąø“ąø”ą¹ƒąøŠą¹‰ąø‡ąø²ąø™ą¹‚ąø«ąø”ąø”ą¹€ąø‚ą¹‰ąø²ąøŖąø¹ą¹ˆąø£ąø°ąøšąøšąøą¹ˆąø­ąø™" @@ -556,7 +561,7 @@ totalEndpoints = "ąøˆąø³ąø™ąø§ąø™ Endpoint ทั้งหดด" totalVisits = "ąøˆąø³ąø™ąø§ąø™ąøąø²ąø£ą¹€ąø‚ą¹‰ąø²ąøŠąø”ąø—ąø±ą¹‰ąø‡ąø«ąø”ąø”" showing = "กำคังแสดง" selectedVisits = "ąøąø²ąø£ą¹€ąø‚ą¹‰ąø²ąøŠąø”ąø—ąøµą¹ˆą¹€ąø„ąø·ąø­ąø" -endpoint = "Endpoint" +endpoint = "ปคายทาง" visits = "ąøąø²ąø£ą¹€ąø‚ą¹‰ąø²ąøŠąø”" percentage = "ą¹€ąø›ąø­ąø£ą¹Œą¹€ąø‹ą¹‡ąø™ąø•ą¹Œ" loading = "กำคังโหคด..." @@ -794,7 +799,7 @@ title = "รวดเป็น หน้าเดียว" desc = "รวดหน้าทั้งหดดของ PDF ą¹€ąø›ą¹‡ąø™ąø«ąø™ą¹‰ąø²ą¹€ąø”ąøµąø¢ąø§ąø‚ąø™ąø²ąø”ą¹ƒąø«ąøą¹ˆ" [home.showJS] -tags = "javascript,code,script" +tags = "javascript,โค้ด,ąøŖąø„ąø£ąø“ąø›ąø•ą¹Œ" title = "แสดง Javascript" desc = "ค้นหาแคะแสดง Javascript ąø—ąøµą¹ˆąøąø±ąø‡ą¹ƒąø™ PDF" @@ -829,7 +834,7 @@ title = "ąø•ąø£ąø§ąøˆąøŖąø­ąøšąø„ąø²ąø¢ą¹€ąø‹ą¹‡ąø™ PDF" desc = "ąø•ąø£ąø§ąøˆąøŖąø­ąøšąø„ąø²ąø¢ą¹€ąø‹ą¹‡ąø™ąø”ąø“ąøˆąø“ąø—ąø±ąø„ą¹ąø„ąø°ą¹ƒąøšąø£ąø±ąøšąø£ąø­ąø‡ą¹ƒąø™ą¹€ąø­ąøąøŖąø²ąø£ PDF" [home.swagger] -tags = "API,documentation,test" +tags = "API,ą¹€ąø­ąøąøŖąø²ąø£ąø›ąø£ąø°ąøąø­ąøš,ąø—ąø”ąøŖąø­ąøš" title = "เอกสาร API" desc = "ดูเอกสาร API ą¹ąø„ąø°ąø—ąø”ąøŖąø­ąøšą¹€ąø­ą¹‡ąø™ąø”ą¹Œąøžąø­ąø¢ąø•ą¹Œ" @@ -878,7 +883,7 @@ title = "ą¹ąø—ąø™ąø—ąøµą¹ˆą¹ąø„ąø°ąøąø„ąø±ąøšąøŖąøµ" desc = "ą¹ąø—ąø™ąø—ąøµą¹ˆąø«ąø£ąø·ąø­ąøąø„ąø±ąøšąøŖąøµą¹ƒąø™ą¹€ąø­ąøąøŖąø²ąø£ PDF" [home.devApi] -tags = "API,development,documentation" +tags = "API,ąøąø²ąø£ąøžąø±ąø’ąø™ąø²,ą¹€ąø­ąøąøŖąø²ąø£ąø›ąø£ąø°ąøąø­ąøš" title = "API" desc = "ąø„ąø“ąø‡ąøą¹Œą¹„ąø›ąø¢ąø±ąø‡ą¹€ąø­ąøąøŖąø²ąø£ API" @@ -912,9 +917,12 @@ desc = "ąøŖąø£ą¹‰ąø²ąø‡ą¹€ąø§ąø“ąø£ą¹Œąøą¹‚ąøŸąø„ąø§ą¹Œąø«ąø„ąø²ąø¢ąø‚ąø±ą¹‰ąø™ desc = "ąø‹ą¹‰ąø­ąø™ąø—ąø±ąøš PDF ąøšąø™ PDF ąø­ąøµąøą¹„ąøŸąø„ą¹Œąø«ąø™ąø¶ą¹ˆąø‡" title = "ąø‹ą¹‰ąø­ąø™ąø—ąø±ąøš PDF" +[home.pdfTextEditor] +title = "ตัวแก้ไขข้อควาด PDF" +desc = "ą¹ąøą¹‰ą¹„ąø‚ąø‚ą¹‰ąø­ąø„ąø§ąø²ąø”ą¹ąø„ąø°ąø£ąø¹ąø›ąø ąø²ąøžąø—ąøµą¹ˆąø”ąøµąø­ąø¢ąø¹ą¹ˆąø ąø²ąø¢ą¹ƒąø™ą¹„ąøŸąø„ą¹Œ PDF" [home.addText] -tags = "text,annotation,label" +tags = "ข้อควาด,ąø„ąø³ąø­ąø˜ąø“ąøšąø²ąø¢ąø›ąø£ąø°ąøąø­ąøš,ąø›ą¹‰ąø²ąø¢ąøąø³ąøąø±ąøš" title = "ą¹€ąøžąø“ą¹ˆąø”ąø‚ą¹‰ąø­ąø„ąø§ąø²ąø”" desc = "ą¹€ąøžąø“ą¹ˆąø”ąø‚ą¹‰ąø­ąø„ąø§ąø²ąø”ąø—ąøµą¹ˆąøąø³ąø«ąø™ąø”ą¹€ąø­ąø‡ąø—ąøµą¹ˆą¹ƒąø”ąøą¹‡ą¹„ąø”ą¹‰ą¹ƒąø™ PDF ของคุณ" @@ -1213,9 +1221,9 @@ pdfaDigitalSignatureWarning = "PDF ąø”ąøµąø„ąø²ąø¢ą¹€ąø‹ą¹‡ąø™ąø”ąø“ąøˆąø“ąø—ąø± fileFormat = "ąø£ąø¹ąø›ą¹ąøšąøšą¹„ąøŸąø„ą¹Œ" wordDoc = "เอกสาร Word" wordDocExt = "เอกสาร Word (.docx)" -odtExt = "OpenDocument Text (.odt)" +odtExt = "ข้อควาด OpenDocument (.odt)" pptExt = "PowerPoint (.pptx)" -odpExt = "OpenDocument Presentation (.odp)" +odpExt = "งานนำเสนอ OpenDocument (.odp)" txtExt = "ข้อควาดค้วน (.txt)" rtfExt = "Rich Text Format (.rtf)" selectedFiles = "ą¹„ąøŸąø„ą¹Œąø—ąøµą¹ˆą¹€ąø„ąø·ąø­ąø" @@ -1832,7 +1840,7 @@ title = "ขั้นสูง" tags = "ย่อ, เค็ก, ąøˆąø“ą¹‹ąø§" [unlockPDFForms] -tags = "remove,delete,form,field,readonly" +tags = "เอาออก,คบ,ฟอร์ด,ąøŸąø“ąø„ąø”ą¹Œ,ąø­ą¹ˆąø²ąø™ąø­ąø¢ą¹ˆąø²ąø‡ą¹€ąø”ąøµąø¢ąø§" title = "ąø„ąøšąøŖąø–ąø²ąø™ąø°ąø­ą¹ˆąø²ąø™ąø­ąø¢ą¹ˆąø²ąø‡ą¹€ąø”ąøµąø¢ąø§ąø­ąø­ąøąøˆąø²ąøąøŠą¹ˆąø­ąø‡ąøŸąø­ąø£ą¹Œąø”" header = "ąø›ąø„ąø”ąø„ą¹‡ąø­ąøąøŸąø­ąø£ą¹Œąø” PDF" submit = "Remove" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "ąø„ąø²ąø¢ą¹€ąø‹ą¹‡ąø™ą¹ąøšąøšąø§ąø²ąø”" defaultImageLabel = "ąø„ąø²ąø¢ą¹€ąø‹ą¹‡ąø™ąø—ąøµą¹ˆąø­ąø±ąø›ą¹‚ąø«ąø„ąø”" defaultTextLabel = "ąø„ąø²ąø¢ą¹€ąø‹ą¹‡ąø™ą¹ąøšąøšąøžąø“ąø”ąøžą¹Œ" saveButton = "ąøšąø±ąø™ąø—ąø¶ąøąø„ąø²ąø¢ą¹€ąø‹ą¹‡ąø™" +savePersonal = "ąøšąø±ąø™ąø—ąø¶ąøąøŖą¹ˆąø§ąø™ąø•ąø±ąø§" +saveShared = "ąøšąø±ąø™ąø—ąø¶ąøą¹ąøšąøšą¹ƒąøŠą¹‰ąø£ą¹ˆąø§ąø”ąøąø±ąø™" saveUnavailable = "ąøŖąø£ą¹‰ąø²ąø‡ąø„ąø²ąø¢ą¹€ąø‹ą¹‡ąø™ąøą¹ˆąø­ąø™ą¹€ąøžąø·ą¹ˆąø­ąøšąø±ąø™ąø—ąø¶ąø" noChanges = "ąø„ąø²ąø¢ą¹€ąø‹ą¹‡ąø™ąø›ąø±ąøˆąøˆąøøąøšąø±ąø™ąø–ąø¹ąøąøšąø±ąø™ąø—ąø¶ąøą¹„ąø§ą¹‰ą¹ąø„ą¹‰ąø§" +tempStorageTitle = "ąøžąø·ą¹‰ąø™ąø—ąøµą¹ˆąøˆąø±ąø”ą¹€ąøą¹‡ąøšąøŠąø±ą¹ˆąø§ąø„ąø£ąø²ąø§ąøšąø™ą¹€ąøšąø£ąø²ąø§ą¹Œą¹€ąø‹ąø­ąø£ą¹Œ" +tempStorageDescription = "ąø„ąø²ąø¢ą¹€ąø‹ą¹‡ąø™ąøˆąø°ąø–ąø¹ąøąøˆąø±ąø”ą¹€ąøą¹‡ąøšą¹„ąø§ą¹‰ą¹ƒąø™ą¹€ąøšąø£ąø²ąø§ą¹Œą¹€ąø‹ąø­ąø£ą¹Œąø‚ąø­ąø‡ąø„ąøøąø“ą¹€ąø—ą¹ˆąø²ąø™ąø±ą¹‰ąø™ ą¹ąø„ąø°ąøˆąø°ąø«ąø²ąø¢ą¹„ąø›ąø«ąø²ąøąø„ąøøąø“ąø„ą¹‰ąø²ąø‡ąø‚ą¹‰ąø­ąø”ąø¹ąø„ą¹€ąøšąø£ąø²ąø§ą¹Œą¹€ąø‹ąø­ąø£ą¹Œąø«ąø£ąø·ąø­ąøŖąø„ąø±ąøšą¹„ąø›ą¹ƒąøŠą¹‰ą¹€ąøšąø£ąø²ąø§ą¹Œą¹€ąø‹ąø­ąø£ą¹Œąø­ąø·ą¹ˆąø™" +personalHeading = "ąø„ąø²ąø¢ą¹€ąø‹ą¹‡ąø™ąøŖą¹ˆąø§ąø™ąø•ąø±ąø§" +sharedHeading = "ąø„ąø²ąø¢ą¹€ąø‹ą¹‡ąø™ąø—ąøµą¹ˆą¹ƒąøŠą¹‰ąø£ą¹ˆąø§ąø”ąøąø±ąø™" +personalDescription = "ąø”ąøµą¹€ąøžąøµąø¢ąø‡ąø„ąøøąø“ą¹€ąø—ą¹ˆąø²ąø™ąø±ą¹‰ąø™ąø—ąøµą¹ˆąø”ąø­ąø‡ą¹€ąø«ą¹‡ąø™ąø„ąø²ąø¢ą¹€ąø‹ą¹‡ąø™ą¹€ąø«ąø„ą¹ˆąø²ąø™ąøµą¹‰" +sharedDescription = "ąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ąø—ąøøąøąø„ąø™ąøŖąø²ąø”ąø²ąø£ąø–ą¹€ąø«ą¹‡ąø™ą¹ąø„ąø°ą¹ƒąøŠą¹‰ąø„ąø²ąø¢ą¹€ąø‹ą¹‡ąø™ą¹€ąø«ąø„ą¹ˆąø²ąø™ąøµą¹‰ą¹„ąø”ą¹‰" [sign.saved.type] canvas = "การวาด" @@ -2731,7 +2747,7 @@ submit = "ąøŖą¹ˆąø‡" failed = "ą¹€ąøąø“ąø”ąø‚ą¹‰ąø­ąøœąø“ąø”ąøžąø„ąø²ąø”ąø‚ąø“ąø°ąøŖąø£ą¹‰ąø²ąø‡ą¹€ąø„ąø¢ą¹Œą¹€ąø­ąø²ąø•ą¹Œąø«ąø„ąø²ąø¢ąø«ąø™ą¹‰ąø²" [bookletImposition] -tags = "booklet,imposition,printing,binding,folding,signature" +tags = "ąø«ąø™ąø±ąø‡ąøŖąø·ąø­ą¹€ąø¢ą¹‡ąøšą¹€ąø„ą¹ˆąø”,ąøąø²ąø£ąøˆąø±ąø”ąø«ąø™ą¹‰ąø²,ąøąø²ąø£ąøžąø“ąø”ąøžą¹Œ,ąøąø²ąø£ą¹€ąø‚ą¹‰ąø²ą¹€ąø„ą¹ˆąø”,ąøąø²ąø£ąøžąø±ąøš,ąøŠąøøąø”ąøžąø“ąø”ąøžą¹Œ" title = "ąøąø²ąø£ąøˆąø±ąø”ąø«ąø™ą¹‰ąø²ąøŖąø”ąøøąø”" header = "ąøąø²ąø£ąøˆąø±ąø”ąø«ąø™ą¹‰ąø²ąøŖąø”ąøøąø”" submit = "สร้างสดุด" @@ -2830,7 +2846,7 @@ scaleFactor = "ąø£ąø°ąø”ąø±ąøšąøąø²ąø£ąø‹ąø¹ąø” (ąø„ąø£ąø­ąøšąø•ąø±ąø”) ąø‚ąø­ submit = "ąøŖą¹ˆąø‡" [adjustPageScale] -tags = "resize,modify,dimension,adapt" +tags = "ąø›ąø£ąø±ąøšąø‚ąø™ąø²ąø”,แก้ไข,ขนาด,ąø›ąø£ąø±ąøšą¹ƒąø«ą¹‰ą¹€ąø«ąø”ąø²ąø°ąøŖąø”" title = "ąø›ąø£ąø±ąøšąøŖą¹€ąøąø„ąø«ąø™ą¹‰ąø²" header = "ąø›ąø£ąø±ąøšąøŖą¹€ąøąø„ąø«ąø™ą¹‰ąø²" submit = "ąø›ąø£ąø±ąøšąøŖą¹€ąøąø„ąø«ąø™ą¹‰ąø²" @@ -3020,6 +3036,91 @@ title = "ąø£ąø±ąøšąø‚ą¹‰ąø­ąø”ąø¹ąø„ą¹€ąøąøµą¹ˆąø¢ąø§ąøąø±ąøš PDF" header = "ąø£ąø±ąøšąø‚ą¹‰ąø­ąø”ąø¹ąø„ą¹€ąøąøµą¹ˆąø¢ąø§ąøąø±ąøš PDF" submit = "ąø£ąø±ąøšąø‚ą¹‰ąø­ąø”ąø¹ąø„" downloadJson = "ąø”ąø²ąø§ąø™ą¹Œą¹‚ąø«ąø„ąø” JSON" +processing = "กำคังแยกข้อดูค..." +results = "ąøœąø„ąø„ąø±ąøžąø˜ą¹Œ" +noResults = "ą¹€ąø£ąøµąø¢ąøą¹ƒąøŠą¹‰ą¹€ąø„ąø£ąø·ą¹ˆąø­ąø‡ąø”ąø·ąø­ąø™ąøµą¹‰ą¹€ąøžąø·ą¹ˆąø­ąøŖąø£ą¹‰ąø²ąø‡ąø£ąø²ąø¢ąø‡ąø²ąø™" +downloads = "ąø”ąø²ąø§ąø™ą¹Œą¹‚ąø«ąø„ąø”" +noneDetected = "ą¹„ąø”ą¹ˆąøžąøš" +indexTitle = "ąø”ąø±ąøŠąø™ąøµ" + +[getPdfInfo.report] +entryLabel = "สรุปข้อดูคทั้งหดด" +shortTitle = "ข้อดูค PDF" + +[getPdfInfo.sections] +metadata = "ข้อดูคเดตา" +formFields = "ą¹€ąø‚ąø•ąø‚ą¹‰ąø­ąø”ąø¹ąø„ąøŸąø­ąø£ą¹Œąø”" +basicInfo = "ąø‚ą¹‰ąø­ąø”ąø¹ąø„ąøžąø·ą¹‰ąø™ąøąø²ąø™" +documentInfo = "ข้อดูคเอกสาร" +compliance = "ąøąø²ąø£ąø›ąøąø“ąøšąø±ąø•ąø“ąø•ąø²ąø”ąø”ąø²ąø•ąø£ąøąø²ąø™" +encryption = "การเข้ารหัส" +permissions = "ąøŖąø“ąø—ąø˜ąø“ą¹Œ" +other = "ąø­ąø·ą¹ˆąø™ą¹†" +perPageInfo = "ข้อดูครายหน้า" +tableOfContents = "ąøŖąø²ąø£ąøšąø±ąø" + +[getPdfInfo.other] +attachments = "ą¹„ąøŸąø„ą¹Œą¹ąø™ąøš" +embeddedFiles = "ą¹„ąøŸąø„ą¹Œąøąø±ąø‡" +javaScript = "JavaScript" +layers = "ą¹€ąø„ą¹€ąø¢ąø­ąø£ą¹Œ" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "ขนาด" +annotations = "ąø„ąø³ąø­ąø˜ąø“ąøšąø²ąø¢ąø›ąø£ąø°ąøąø­ąøš" +images = "ąø£ąø¹ąø›ąø ąø²ąøž" +links = "ąø„ąø“ąø‡ąøą¹Œ" +fonts = "ąøŸąø­ąø™ąø•ą¹Œ" +xobjects = "ąøˆąø³ąø™ąø§ąø™ XObject" +multimedia = "ดัคตณดีเดีย" + +[getPdfInfo.summary] +pages = "หน้า" +fileSize = "ąø‚ąø™ąø²ąø”ą¹„ąøŸąø„ą¹Œ" +pdfVersion = "ą¹€ąø§ąø­ąø£ą¹ŒąøŠąø±ąø™ PDF" +language = "ภาษา" +title = "สรุป PDF" +author = "ąøœąø¹ą¹‰ą¹€ąø‚ąøµąø¢ąø™" +created = "ąøŖąø£ą¹‰ąø²ąø‡ą¹€ąø”ąø·ą¹ˆąø­" +modified = "ą¹ąøą¹‰ą¹„ąø‚ą¹€ąø”ąø·ą¹ˆąø­" +permsAll = "ąø­ąø™ąøøąøąø²ąø•ąøŖąø“ąø—ąø˜ąø“ą¹Œąø—ąø±ą¹‰ąø‡ąø«ąø”ąø”" +permsRestricted = "ąø‚ą¹‰ąø­ąøˆąø³ąøąø±ąø” {{count}} รายการ" +permsMixed = "ąø”ąøµąøąø²ąø£ąøˆąø³ąøąø±ąø”ąøŖąø“ąø—ąø˜ąø“ą¹Œąøšąø²ąø‡ąø­ąø¢ą¹ˆąø²ąø‡" +hasCompliance = "ąø”ąøµąø”ąø²ąø•ąø£ąøąø²ąø™ąøąø²ąø£ąø›ąøąø“ąøšąø±ąø•ąø“ąø•ąø²ąø”" +noCompliance = "ą¹„ąø”ą¹ˆąø”ąøµąø”ąø²ąø•ąø£ąøąø²ąø™ąøąø²ąø£ąø›ąøąø“ąøšąø±ąø•ąø“ąø•ąø²ąø”" +basic = "ąø‚ą¹‰ąø­ąø”ąø¹ąø„ąøžąø·ą¹‰ąø™ąøąø²ąø™" +documentInfo = "ข้อดูคเอกสาร" +securityTitle = "สถานะควาดปคอดภัย" +technical = "ą¹€ąøŠąø“ąø‡ą¹€ąø—ąø„ąø™ąø“ąø„" +overviewTitle = "ąø ąø²ąøžąø£ąø§ąø”ąø‚ąø­ąø‡ PDF" + +[getPdfInfo.summary.security] +encrypted = "PDF เข้ารหัส - ąø”ąøµąøąø²ąø£ąø›ą¹‰ąø­ąø‡ąøąø±ąø™ąø”ą¹‰ąø§ąø¢ąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™" +unencrypted = "PDF ą¹„ąø”ą¹ˆą¹€ąø‚ą¹‰ąø²ąø£ąø«ąø±ąøŖ - ą¹„ąø”ą¹ˆąø”ąøµąøąø²ąø£ąø›ą¹‰ąø­ąø‡ąøąø±ąø™ąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™" + +[getPdfInfo.summary.tech] +images = "ąø£ąø¹ąø›ąø ąø²ąøž" +fonts = "ąøŸąø­ąø™ąø•ą¹Œ" +formFields = "ą¹€ąø‚ąø•ąø‚ą¹‰ąø­ąø”ąø¹ąø„ąøŸąø­ąø£ą¹Œąø”" +embeddedFiles = "ą¹„ąøŸąø„ą¹Œąøąø±ąø‡" +javaScript = "JavaScript" +layers = "ą¹€ąø„ą¹€ąø¢ąø­ąø£ą¹Œ" +bookmarks = "ąø—ąøµą¹ˆąø„ąø±ą¹ˆąø™ąø«ąø™ą¹‰ąø²" +multimedia = "ดัคตณดีเดีย" + +[getPdfInfo.summary.overview] +untitled = "ą¹€ąø­ąøąøŖąø²ąø£ąø—ąøµą¹ˆą¹„ąø”ą¹ˆąø”ąøµąøŠąø·ą¹ˆąø­" +unknown = "ąøœąø¹ą¹‰ą¹€ąø‚ąøµąø¢ąø™ą¹„ąø”ą¹ˆąø—ąø£ąø²ąøš" +text = "ąø™ąøµą¹ˆąø„ąø·ąø­ą¹„ąøŸąø„ą¹Œ PDF ąøˆąø³ąø™ąø§ąø™ {{pages}} หน้า ชื่อ {{title}} สร้างโดย {{author}} (ą¹€ąø§ąø­ąø£ą¹ŒąøŠąø±ąø™ PDF {{version}})." + +[getPdfInfo.error] +partial = "ą¹„ąø”ą¹ˆąøŖąø²ąø”ąø²ąø£ąø–ąø›ąø£ąø°ąø”ąø§ąø„ąøœąø„ą¹„ąøŸąø„ą¹Œąøšąø²ąø‡ą¹„ąøŸąø„ą¹Œą¹„ąø”ą¹‰" +unexpected = "ą¹€ąøąø“ąø”ąø‚ą¹‰ąø­ąøœąø“ąø”ąøžąø„ąø²ąø”ąø—ąøµą¹ˆą¹„ąø”ą¹ˆąø„ąø²ąø”ąø„ąø“ąø”ąø£ąø°ąø«ąø§ą¹ˆąø²ąø‡ąøąø²ąø£ą¹ąø¢ąøąø‚ą¹‰ąø­ąø”ąø¹ąø„" + +[getPdfInfo.status] +complete = "ąøąø²ąø£ą¹ąø¢ąøąø‚ą¹‰ąø­ąø”ąø¹ąø„ą¹€ąøŖąø£ą¹‡ąøˆąøŖąø“ą¹‰ąø™" [extractPage] tags = "แยก" @@ -3438,6 +3539,9 @@ signinTitle = "ąøąø£ąøøąø“ąø²ąø„ąø‡ąøŠąø·ą¹ˆąø­ą¹€ąø‚ą¹‰ąø²ą¹ƒąøŠą¹‰" ssoSignIn = "ą¹€ąø‚ą¹‰ąø²ąøŖąø¹ą¹ˆąø£ąø°ąøšąøšąø”ą¹‰ąø§ąø¢ Single Sign-on" oAuth2AutoCreateDisabled = "ąøąø²ąø£ąøŖąø£ą¹‰ąø²ąø‡ąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ OAuth2 ąø­ąø±ąø•ą¹‚ąø™ąø”ąø±ąø•ąø“ąø–ąø¹ąøąø›ąø“ąø”ą¹ƒąøŠą¹‰ąø‡ąø²ąø™" oAuth2AdminBlockedUser = "ąø‚ąø“ąø°ąø™ąøµą¹‰ąø”ąøµąøąø²ąø£ąøšąø„ą¹‡ąø­ąøąøąø²ąø£ąø„ąø‡ąø—ąø°ą¹€ąøšąøµąø¢ąø™ąø«ąø£ąø·ąø­ąøąø²ąø£ą¹€ąø‚ą¹‰ąø²ąøŖąø¹ą¹ˆąø£ąø°ąøšąøšąø‚ąø­ąø‡ąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ąø—ąøµą¹ˆą¹„ąø”ą¹ˆą¹„ąø”ą¹‰ąø„ąø‡ąø—ąø°ą¹€ąøšąøµąø¢ąø™ ą¹‚ąø›ąø£ąø”ąø•ąø“ąø”ąø•ą¹ˆąø­ąøœąø¹ą¹‰ąø”ąø¹ą¹ąø„ąø£ąø°ąøšąøš" +oAuth2RequiresLicense = "ąøąø²ąø£ą¹€ąø‚ą¹‰ąø²ąøŖąø¹ą¹ˆąø£ąø°ąøšąøšąø”ą¹‰ąø§ąø¢ OAuth/SSO ąø•ą¹‰ąø­ąø‡ąø”ąøµą¹„ąø„ą¹€ąø‹ąø™ąøŖą¹Œą¹ąøšąøšąøŠąø³ąø£ąø°ą¹€ąø‡ąø“ąø™ (Server หรือ Enterprise) ą¹‚ąø›ąø£ąø”ąø•ąø“ąø”ąø•ą¹ˆąø­ąøœąø¹ą¹‰ąø”ąø¹ą¹ąø„ąø£ąø°ąøšąøšą¹€ąøžąø·ą¹ˆąø­ąø­ąø±ąø›ą¹€ąøąø£ąø”ą¹ąøœąø™ąø‚ąø­ąø‡ąø„ąøøąø“" +saml2RequiresLicense = "ąøąø²ąø£ą¹€ąø‚ą¹‰ąø²ąøŖąø¹ą¹ˆąø£ąø°ąøšąøšąø”ą¹‰ąø§ąø¢ SAML ąø•ą¹‰ąø­ąø‡ąø”ąøµą¹„ąø„ą¹€ąø‹ąø™ąøŖą¹Œą¹ąøšąøšąøŠąø³ąø£ąø°ą¹€ąø‡ąø“ąø™ (Server หรือ Enterprise) ą¹‚ąø›ąø£ąø”ąø•ąø“ąø”ąø•ą¹ˆąø­ąøœąø¹ą¹‰ąø”ąø¹ą¹ąø„ąø£ąø°ąøšąøšą¹€ąøžąø·ą¹ˆąø­ąø­ąø±ąø›ą¹€ąøąø£ąø”ą¹ąøœąø™ąø‚ąø­ąø‡ąø„ąøøąø“" +maxUsersReached = "ąøˆąø³ąø™ąø§ąø™ąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ąø–ąø¶ąø‡ąø‚ąøµąø”ąøŖąø¹ąø‡ąøŖąøøąø”ąøŖąø³ąø«ąø£ąø±ąøšą¹„ąø„ą¹€ąø‹ąø™ąøŖą¹Œąø›ąø±ąøˆąøˆąøøąøšąø±ąø™ąø‚ąø­ąø‡ąø„ąøøąø“ ą¹‚ąø›ąø£ąø”ąø•ąø“ąø”ąø•ą¹ˆąø­ąøœąø¹ą¹‰ąø”ąø¹ą¹ąø„ąø£ąø°ąøšąøšą¹€ąøžąø·ą¹ˆąø­ąø­ąø±ąø›ą¹€ąøąø£ąø”ą¹ąøœąø™ąø«ąø£ąø·ąø­ą¹€ąøžąø“ą¹ˆąø”ąøˆąø³ąø™ąø§ąø™ąø—ąøµą¹ˆąø™ąø±ą¹ˆąø‡" oauth2RequestNotFound = "ą¹„ąø”ą¹ˆąøžąøšąø„ąø³ąø‚ąø­ąøąø²ąø£ąø­ąø™ąøøąøąø²ąø•" oauth2InvalidUserInfoResponse = "ąøąø²ąø£ąø•ąø­ąøšąøąø„ąø±ąøšąø‚ą¹‰ąø­ąø”ąø¹ąø„ąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ą¹„ąø”ą¹ˆąø–ąø¹ąøąø•ą¹‰ąø­ąø‡" oauth2invalidRequest = "ąø„ąø³ąø‚ąø­ą¹„ąø”ą¹ˆąø–ąø¹ąøąø•ą¹‰ąø­ąø‡" @@ -3771,7 +3875,7 @@ version = "ąø£ąøøą¹ˆąø™ąø›ąø±ąøˆąøˆąøøąøšąø±ąø™" title = "เอกสาร API" header = "เอกสาร API" desc = "ąø”ąø¹ą¹ąø„ąø°ąø—ąø”ąøŖąø­ąøšąøˆąøøąø”ąø›ąø„ąø²ąø¢ąø—ąø²ąø‡ API ąø‚ąø­ąø‡ Stirling PDF" -tags = "api,documentation,swagger,endpoints,development" +tags = "api,ą¹€ąø­ąøąøŖąø²ąø£ąø›ąø£ąø°ąøąø­ąøš,swagger,ปคายทาง,ąøąø²ąø£ąøžąø±ąø’ąø™ąø²" [cookieBanner.popUp] title = "ą¹€ąø£ąø²ą¹ƒąøŠą¹‰ąø„ąøøąøąøąøµą¹‰ąø­ąø¢ą¹ˆąø²ąø‡ą¹„ąø£" @@ -3846,14 +3950,17 @@ fitToWidth = "ąøžąø­ąø”ąøµąøąø±ąøšąø„ąø§ąø²ąø”ąøąø§ą¹‰ąø²ąø‡" actualSize = "ąø‚ąø™ąø²ąø”ąøˆąø£ąø“ąø‡" [viewer] +cannotPreviewFile = "ą¹„ąø”ą¹ˆąøŖąø²ąø”ąø²ąø£ąø–ą¹ąøŖąø”ąø‡ąø•ąø±ąø§ąø­ąø¢ą¹ˆąø²ąø‡ą¹„ąøŸąø„ą¹Œą¹„ąø”ą¹‰" +dualPageView = "ดุดดองสองหน้า" firstPage = "หน้าแรก" lastPage = "หน้าสุดท้าย" -previousPage = "ąø«ąø™ą¹‰ąø²ąøą¹ˆąø­ąø™ąø«ąø™ą¹‰ąø²" nextPage = "หน้าถัดไป" +onlyPdfSupported = "ąø•ąø±ąø§ą¹ąøŖąø”ąø‡ąøœąø„ąø£ąø­ąø‡ąø£ąø±ąøšą¹€ąø‰ąøžąø²ąø°ą¹„ąøŸąø„ą¹Œ PDF ą¹„ąøŸąø„ą¹Œąø™ąøµą¹‰ąø”ąø¹ą¹€ąø«ąø”ąø·ąø­ąø™ąøˆąø°ą¹€ąø›ą¹‡ąø™ąø£ąø¹ąø›ą¹ąøšąøšąø­ąø·ą¹ˆąø™" +previousPage = "ąø«ąø™ą¹‰ąø²ąøą¹ˆąø­ąø™ąø«ąø™ą¹‰ąø²" +singlePageView = "ดุดดองหน้าเดียว" +unknownFile = "ą¹„ąøŸąø„ą¹Œą¹„ąø”ą¹ˆąø£ąø¹ą¹‰ąøˆąø±ąø" zoomIn = "ซูดเข้า" zoomOut = "ซูดออก" -singlePageView = "ดุดดองหน้าเดียว" -dualPageView = "ดุดดองสองหน้า" [rightRail] closeSelected = "ąø›ąø“ąø”ą¹„ąøŸąø„ą¹Œąø—ąøµą¹ˆą¹€ąø„ąø·ąø­ąø" @@ -3877,6 +3984,7 @@ toggleSidebar = "ąøŖąø„ąø±ąøšą¹ąø–ąøšąø‚ą¹‰ąø²ąø‡" exportSelected = "ąøŖą¹ˆąø‡ąø­ąø­ąøąø«ąø™ą¹‰ąø²ąø—ąøµą¹ˆą¹€ąø„ąø·ąø­ąø" toggleAnnotations = "ąøŖąø„ąø±ąøšąøąø²ąø£ą¹ąøŖąø”ąø‡ąø„ąø³ąø­ąø˜ąø“ąøšąø²ąø¢ąø›ąø£ąø°ąøąø­ąøš" annotationMode = "ąøŖąø„ąø±ąøšą¹‚ąø«ąø”ąø”ąø„ąø³ąø­ąø˜ąø“ąøšąø²ąø¢ąø›ąø£ąø°ąøąø­ąøš" +print = "ąøžąø“ąø”ąøžą¹Œ PDF" draw = "วาด" save = "ąøšąø±ąø™ąø—ąø¶ąø" saveChanges = "ąøšąø±ąø™ąø—ąø¶ąøąøąø²ąø£ą¹€ąø›ąø„ąøµą¹ˆąø¢ąø™ą¹ąø›ąø„ąø‡" @@ -4270,7 +4378,7 @@ label = "ąøœąø¹ą¹‰ą¹ƒąø«ą¹‰ąøšąø£ąø“ąøąø²ąø£" description = "ąøŠąø·ą¹ˆąø­ąøœąø¹ą¹‰ą¹ƒąø«ą¹‰ąøšąø£ąø“ąøąø²ąø£ SAML2" [admin.settings.connections.saml2.registrationId] -label = "Registration ID" +label = "ąø£ąø«ąø±ąøŖąøąø²ąø£ąø„ąø‡ąø—ąø°ą¹€ąøšąøµąø¢ąø™" description = "ąø•ąø±ąø§ąø£ąø°ąøšąøøąøąø²ąø£ąø„ąø‡ąø—ąø°ą¹€ąøšąøµąø¢ąø™ąø‚ąø­ąø‡ SAML2" [admin.settings.connections.saml2.autoCreateUser] @@ -4343,7 +4451,7 @@ features = "ą¹ąøŸąø„ąøąøŸąøµą¹€ąøˆąø­ąø£ą¹Œ" processing = "ąøąø²ąø£ąø›ąø£ąø°ąø”ąø§ąø„ąøœąø„" [admin.settings.advanced.endpoints] -label = "Endpoints" +label = "ปคายทาง" manage = "ąøˆąø±ąø”ąøąø²ąø£ API Endpoints" description = "ąøąø²ąø£ąøˆąø±ąø”ąøąø²ąø£ Endpoint ąøąø³ąø«ąø™ąø”ąø„ą¹ˆąø²ąøœą¹ˆąø²ąø™ YAML ąø”ąø¹ą¹€ąø­ąøąøŖąø²ąø£ąø›ąø£ąø°ąøąø­ąøšąøŖąø³ąø«ąø£ąø±ąøšąø£ąø²ąø¢ąø„ąø°ą¹€ąø­ąøµąø¢ąø”ąøąø²ąø£ą¹€ąø›ąø“ąø”/ąø›ąø“ąø”ą¹ƒąøŠą¹‰ąø‡ąø²ąø™ Endpoint ą¹€ąø‰ąøžąø²ąø°" @@ -4407,7 +4515,7 @@ description = "ąøˆąø°ąø—ąø³ąø„ąø§ąø²ąø”ąøŖąø°ąø­ąø²ąø”ą¹„ąø”ą¹€ąø£ąøąø—ąø­ąø£ label = "ąø‚ą¹‰ąø­ąøˆąø³ąøąø±ąø”ąø‚ąø­ąø‡ąø•ąø±ąø§ąø›ąø£ąø°ąø”ąø§ąø„ąøœąø„ąøąø£ąø°ąøšąø§ąø™ąøąø²ąø£" description = "ąøąø³ąø«ąø™ąø”ąø‚ąøµąø”ąøˆąø³ąøąø±ąø”ą¹€ąø‹ąøŖąøŠąø±ąø™ą¹ąø„ąø°ąø£ąø°ąø¢ąø°ąø«ąø”ąø”ą¹€ąø§ąø„ąø²ąøŖąø³ąø«ąø£ąø±ąøšąø•ąø±ąø§ąø›ąø£ąø°ąø”ąø§ąø„ąøœąø„ą¹ąø•ą¹ˆąø„ąø°ąø•ąø±ąø§" libreOffice = "LibreOffice" -pdfToHtml = "PDF to HTML" +pdfToHtml = "PDF เป็น HTML" qpdf = "QPDF" tesseract = "Tesseract OCR" pythonOpenCv = "Python OpenCV" @@ -4494,6 +4602,7 @@ description = "URL ąø«ąø£ąø·ąø­ąøŠąø·ą¹ˆąø­ą¹„ąøŸąø„ą¹Œą¹„ąø›ąø¢ąø±ąø‡ Impressum title = "ąøžąø£ąøµą¹€ąø”ąøµąø¢ąø”ą¹ąø„ąø°ą¹€ąø­ąø™ą¹€ąø•ąø­ąø£ą¹Œą¹„ąøžąø£ąøŖą¹Œ" description = "ąøąø³ąø«ąø™ąø”ąø„ą¹ˆąø²ąø„ąøµąø¢ą¹Œą¹„ąø„ą¹€ąø‹ąø™ąøŖą¹Œąøžąø£ąøµą¹€ąø”ąøµąø¢ąø”ąø«ąø£ąø·ąø­ą¹€ąø­ąø™ą¹€ąø•ąø­ąø£ą¹Œą¹„ąøžąø£ąøŖą¹Œąø‚ąø­ąø‡ąø„ąøøąø“" license = "ąøąø²ąø£ąøąø³ąø«ąø™ąø”ąø„ą¹ˆąø²ą¹„ąø„ą¹€ąø‹ąø™ąøŖą¹Œ" +noInput = "ą¹‚ąø›ąø£ąø”ąø£ąø°ąøšąøøąø„ąøµąø¢ą¹Œąø«ąø£ąø·ąø­ą¹„ąøŸąø„ą¹Œą¹„ąø„ą¹€ąø‹ąø™ąøŖą¹Œ" [admin.settings.premium.licenseKey] toggle = "ดี license key ąø«ąø£ąø·ąø­ą¹„ąøŸąø„ą¹Œ certificate ไหด?" @@ -4511,6 +4620,25 @@ line1 = "ąøąø²ąø£ą¹€ąø‚ąøµąø¢ąø™ąø—ąø±ąøš license key ąø›ąø±ąøˆąøˆąøøąøšąø±ąø™ line2 = "ą¹„ąø„ą¹€ąø‹ąø™ąøŖą¹Œąøą¹ˆąø­ąø™ąø«ąø™ą¹‰ąø²ąø‚ąø­ąø‡ąø„ąøøąø“ąøˆąø°ąøŖąø¹ąøąø«ąø²ąø¢ąø–ąø²ąø§ąø£ ą¹€ąø§ą¹‰ąø™ą¹ąø•ą¹ˆąø„ąøøąø“ą¹„ąø”ą¹‰ąøŖąø³ąø£ąø­ąø‡ą¹„ąø§ą¹‰ąø—ąøµą¹ˆąø­ąø·ą¹ˆąø™" line3 = "ąøŖąø³ąø„ąø±ąø: ą¹€ąøą¹‡ąøš license keys ą¹ƒąø«ą¹‰ą¹€ąø›ą¹‡ąø™ąøŖą¹ˆąø§ąø™ąø•ąø±ąø§ą¹ąø„ąø°ąø›ąø„ąø­ąø”ąø ąø±ąø¢ ąø«ą¹‰ąø²ąø”ą¹ąøŠąø£ą¹ŒąøŖąø²ąø˜ąø²ąø£ąø“ąø°" +[admin.settings.premium.inputMethod] +text = "ąø„ąøµąø¢ą¹Œą¹„ąø„ą¹€ąø‹ąø™ąøŖą¹Œ" +file = "ą¹„ąøŸąø„ą¹Œą¹ƒąøšąø£ąø±ąøšąø£ąø­ąø‡" + +[admin.settings.premium.file] +label = "ą¹„ąøŸąø„ą¹Œą¹ƒąøšąø£ąø±ąøšąø£ąø­ąø‡ą¹„ąø„ą¹€ąø‹ąø™ąøŖą¹Œ" +description = "ąø­ąø±ąø›ą¹‚ąø«ąø„ąø”ą¹„ąøŸąø„ą¹Œą¹„ąø„ą¹€ąø‹ąø™ąøŖą¹Œ .lic หรือ .cert ąøˆąø²ąøąøąø²ąø£ąøŖąø±ą¹ˆąø‡ąø‹ąø·ą¹‰ąø­ą¹ąøšąøšąø­ąø­ąøŸą¹„ąø„ąø™ą¹Œąø‚ąø­ąø‡ąø„ąøøąø“" +choose = "ą¹€ąø„ąø·ąø­ąøą¹„ąøŸąø„ą¹Œą¹„ąø„ą¹€ąø‹ąø™ąøŖą¹Œ" +selected = "ąø—ąøµą¹ˆą¹€ąø„ąø·ąø­ąø: {{filename}} ({{size}})" +successMessage = "ąø­ąø±ąø›ą¹‚ąø«ąø„ąø”ą¹ąø„ąø°ą¹€ąø›ąø“ąø”ą¹ƒąøŠą¹‰ąø‡ąø²ąø™ą¹„ąøŸąø„ą¹Œą¹„ąø„ą¹€ąø‹ąø™ąøŖą¹ŒąøŖąø³ą¹€ąø£ą¹‡ąøˆ ą¹„ąø”ą¹ˆąøˆąø³ą¹€ąø›ą¹‡ąø™ąø•ą¹‰ąø­ąø‡ą¹€ąø£ąø“ą¹ˆąø”ąø£ąø°ąøšąøšą¹ƒąø«ąø”ą¹ˆ" + +[admin.settings.premium.currentLicense] +title = "ą¹„ąø„ą¹€ąø‹ąø™ąøŖą¹Œąø—ąøµą¹ˆą¹ƒąøŠą¹‰ąø‡ąø²ąø™ąø­ąø¢ąø¹ą¹ˆ" +file = "ą¹ąø«ąø„ą¹ˆąø‡ąø—ąøµą¹ˆąø”ąø²: ą¹„ąøŸąø„ą¹Œą¹„ąø„ą¹€ąø‹ąø™ąøŖą¹Œ ({{path}})" +key = "ą¹ąø«ąø„ą¹ˆąø‡ąø—ąøµą¹ˆąø”ąø²: ąø„ąøµąø¢ą¹Œą¹„ąø„ą¹€ąø‹ąø™ąøŖą¹Œ" +type = "ประเภท: {{type}}" +noInput = "ą¹‚ąø›ąø£ąø”ąø£ąø°ąøšąøøąø„ąøµąø¢ą¹Œą¹„ąø„ą¹€ąø‹ąø™ąøŖą¹Œąø«ąø£ąø·ąø­ąø­ąø±ąø›ą¹‚ąø«ąø„ąø”ą¹„ąøŸąø„ą¹Œą¹ƒąøšąø£ąø±ąøšąø£ąø­ąø‡" +success = "ąøŖąø³ą¹€ąø£ą¹‡ąøˆ" + [admin.settings.premium.enabled] label = "ą¹€ąø›ąø“ąø”ą¹ƒąøŠą¹‰ąø‡ąø²ąø™ąøŸąøµą¹€ąøˆąø­ąø£ą¹Œąøžąø£ąøµą¹€ąø”ąøµąø¢ąø”" description = "ą¹€ąø›ąø“ąø”ąøąø²ąø£ąø•ąø£ąø§ąøˆąøŖąø­ąøšąø„ąøµąø¢ą¹Œą¹„ąø„ą¹€ąø‹ąø™ąøŖą¹ŒąøŖąø³ąø«ąø£ąø±ąøšąøŸąøµą¹€ąøˆąø­ąø£ą¹Œą¹ąøšąøš Pro/Enterprise" @@ -4544,7 +4672,7 @@ label = "ąøŖąø£ą¹‰ąø²ąø‡ą¹ƒąø«ąø”ą¹ˆą¹€ąø”ąø·ą¹ˆąø­ą¹€ąø£ąø“ą¹ˆąø”ąø•ą¹‰ąø™" description = "ąøŖąø£ą¹‰ąø²ąø‡ą¹ƒąøšąø£ąø±ąøšąø£ąø­ąø‡ą¹ƒąø«ąø”ą¹ˆąø—ąøøąøąø„ąø£ąø±ą¹‰ąø‡ąø—ąøµą¹ˆą¹ąø­ąø›ąøžąø„ąø“ą¹€ąø„ąøŠąø±ąø™ą¹€ąø£ąø“ą¹ˆąø”ąø•ą¹‰ąø™" [admin.settings.endpoints] -title = "API Endpoints" +title = "ปคายทาง API" description = "ąø„ąø§ąøšąø„ąøøąø”ąø§ą¹ˆąø² API Endpoints แคะกคุ่ด Endpoint ą¹ƒąø”ąø—ąøµą¹ˆą¹ƒąøŠą¹‰ąø‡ąø²ąø™ą¹„ąø”ą¹‰" management = "ąøąø²ąø£ąøˆąø±ąø”ąøąø²ąø£ Endpoint" note = "หดายเหตุ: ąøąø²ąø£ąø›ąø“ąø”ąøąø²ąø£ą¹ƒąøŠą¹‰ąø‡ąø²ąø™ endpoints ąøˆąø°ąøˆąø³ąøąø±ąø”ąøąø²ąø£ą¹€ąø‚ą¹‰ąø²ąø–ąø¶ąø‡ API ą¹ąø•ą¹ˆąøˆąø°ą¹„ąø”ą¹ˆąø„ąøšąøŖą¹ˆąø§ąø™ąø•ąø“ąø”ąø•ą¹ˆąø­ąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ ąø•ą¹‰ąø­ąø‡ąø£ąøµąøŖąø•ąø²ąø£ą¹Œąø—ąøˆąø¶ąø‡ąøˆąø°ąø”ąøµąøœąø„" @@ -4644,7 +4772,9 @@ selectedCount = "เคือกแค้ว {{count}} รายการ" download = "ąø”ąø²ąø§ąø™ą¹Œą¹‚ąø«ąø„ąø”" delete = "คบ" unsupported = "ą¹„ąø”ą¹ˆąø£ąø­ąø‡ąø£ąø±ąøš" +active = "ą¹ƒąøŠą¹‰ąø‡ąø²ąø™ąø­ąø¢ąø¹ą¹ˆ" addToUpload = "ą¹€ąøžąø“ą¹ˆąø”ą¹„ąø›ąø¢ąø±ąø‡ąø­ąø±ąø›ą¹‚ąø«ąø„ąø”" +closeFile = "ąø›ąø“ąø”ą¹„ąøŸąø„ą¹Œ" deleteAll = "ąø„ąøšąø—ąø±ą¹‰ąø‡ąø«ąø”ąø”" loadingFiles = "ąøąø³ąø„ąø±ąø‡ą¹‚ąø«ąø„ąø”ą¹„ąøŸąø„ą¹Œ..." noFiles = "ą¹„ąø”ą¹ˆąø”ąøµą¹„ąøŸąø„ą¹Œ" @@ -4989,7 +5119,7 @@ chartAriaLabel = "ąøąø²ąø£ą¹ƒąøŠą¹‰ą¹€ąø„ąø£ąø”ąø“ąø•: ą¹ƒąøŠą¹‰ą¹ąøšąøšąø£ąø§ nextReset = "รีเซ็ตครั้งถัดไป" lastApiUse = "ąøąø²ąø£ą¹ƒąøŠą¹‰ąø‡ąø²ąø™ API ąø„ą¹ˆąø²ąøŖąøøąø”" overlayMessage = "ąøŖąø£ą¹‰ąø²ąø‡ąø„ąøµąø¢ą¹Œą¹€ąøžąø·ą¹ˆąø­ąø”ąø¹ą¹€ąø„ąø£ąø”ąø“ąø•ą¹ąø„ąø°ą¹€ąø„ąø£ąø”ąø“ąø•ąø—ąøµą¹ˆąø”ąøµąø­ąø¢ąø¹ą¹ˆ" -label = "API Key" +label = "ąø„ąøµąø¢ą¹Œ API" guestInfo = "ąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ą¹ąøšąøšą¹ąø‚ąøąøˆąø°ą¹„ąø”ą¹ˆą¹„ąø”ą¹‰ąø£ąø±ąøš API key ąøŖąø£ą¹‰ąø²ąø‡ąøšąø±ąøąøŠąøµą¹€ąøžąø·ą¹ˆąø­ąø£ąø±ąøš API key ąøŖąø³ąø«ąø£ąø±ąøšą¹ƒąøŠą¹‰ąø‡ąø²ąø™ą¹ƒąø™ą¹ąø­ąø›ąøžąø„ąø“ą¹€ąø„ąøŠąø±ąø™ąø‚ąø­ąø‡ąø„ąøøąø“" goToAccount = "ą¹„ąø›ąø—ąøµą¹ˆąøšąø±ąøąøŠąøµ" generateError = "ą¹„ąø”ą¹ˆąøŖąø²ąø”ąø²ąø£ąø–ąøŖąø£ą¹‰ąø²ąø‡ API key ของคุณได้" @@ -5132,7 +5262,7 @@ upgrade = "อัปเกรดเคย →" freeTitle = "ą¹„ąø„ą¹€ąø‹ąø™ąøŖą¹Œą¹€ąø‹ąø“ąø£ą¹ŒąøŸą¹€ąø§ąø­ąø£ą¹Œ" overLimitTitle = "ąø•ą¹‰ąø­ąø‡ą¹ƒąøŠą¹‰ą¹„ąø„ą¹€ąø‹ąø™ąøŖą¹Œą¹€ąø‹ąø“ąø£ą¹ŒąøŸą¹€ąø§ąø­ąø£ą¹Œ" overLimitBody = "ąøŖąø“ąø—ąø˜ąø“ą¹Œąøąø²ąø£ą¹ƒąøŠą¹‰ąø‡ąø²ąø™ąø‚ąø­ąø‡ą¹€ąø£ąø²ąø£ąø­ąø‡ąø£ąø±ąøšąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ą¹„ąø”ą¹‰ąøŸąø£ąøµąøŖąø¹ąø‡ąøŖąøøąø” {{freeTierLimit}} ąø„ąø™ąø•ą¹ˆąø­ą¹€ąø‹ąø“ąø£ą¹ŒąøŸą¹€ąø§ąø­ąø£ą¹Œ ąø‚ąø“ąø°ąø™ąøµą¹‰ąø„ąøøąø“ąø”ąøµąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ Stirling {{overLimitUserCopy}} ąø„ąø™ ą¹€ąøžąø·ą¹ˆąø­ą¹ƒąøŠą¹‰ąø‡ąø²ąø™ąø•ą¹ˆąø­ą¹€ąø™ąø·ą¹ˆąø­ąø‡ ą¹‚ąø›ąø£ąø”ąø­ąø±ąø›ą¹€ąøąø£ąø”ą¹€ąø›ą¹‡ąø™ą¹ąøžą¹‡ąøą¹€ąøąøˆ Stirling Server - ąø—ąøµą¹ˆąø™ąø±ą¹ˆąø‡ą¹„ąø”ą¹ˆąøˆąø³ąøąø±ąø” แก้ไขข้อควาด PDF ą¹ąø„ąø°ąø„ąø§ąøšąø„ąøøąø”ą¹ąø­ąø”ąø”ąø“ąø™ą¹€ąø•ą¹‡ąø”ąø£ąø¹ąø›ą¹ąøšąøš ราคา $99/ąø•ą¹ˆąø­ą¹€ąø‹ąø“ąø£ą¹ŒąøŸą¹€ąø§ąø­ąø£ą¹Œ/เดือน" -freeBody = "ą¹„ąø„ą¹€ąø‹ąø™ąøŖą¹Œą¹ąøšąøš Open-Core ąø‚ąø­ąø‡ą¹€ąø£ąø²ąø£ąø­ąø‡ąø£ąø±ąøšąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ą¹„ąø”ą¹‰ąøŸąø£ąøµąøŖąø¹ąø‡ąøŖąøøąø” {{freeTierLimit}} ąø„ąø™ąø•ą¹ˆąø­ą¹€ąø‹ąø“ąø£ą¹ŒąøŸą¹€ąø§ąø­ąø£ą¹Œ ą¹€ąøžąø·ą¹ˆąø­ąø‚ąø¢ąø²ąø¢ąøąø²ąø£ą¹ƒąøŠą¹‰ąø‡ąø²ąø™ą¹„ąø”ą¹‰ąø•ą¹ˆąø­ą¹€ąø™ąø·ą¹ˆąø­ąø‡ą¹ąø„ąø°ą¹€ąø‚ą¹‰ąø²ąø–ąø¶ąø‡ ą¹€ąø„ąø£ąø·ą¹ˆąø­ąø‡ąø”ąø·ąø­ą¹ąøą¹‰ą¹„ąø‚ąø‚ą¹‰ąø­ąø„ąø§ąø²ąø” PDF ąø„ą¹ˆąø§ąø‡ąø«ąø™ą¹‰ąø² ą¹€ąø£ąø²ą¹ąø™ąø°ąø™ąø³ą¹ąøžą¹‡ąøą¹€ąøąøˆ Stirling Server - ą¹ąøą¹‰ą¹„ąø‚ą¹„ąø”ą¹‰ą¹€ąø•ą¹‡ąø”ąø£ąø¹ąø›ą¹ąøšąøšą¹ąø„ąø° ąø—ąøµą¹ˆąø™ąø±ą¹ˆąø‡ą¹„ąø”ą¹ˆąøˆąø³ąøąø±ąø” ราคา $99/ąø•ą¹ˆąø­ą¹€ąø‹ąø“ąø£ą¹ŒąøŸą¹€ąø§ąø­ąø£ą¹Œ/เดือน" +freeBody = "ąøŖąø±ąøąøąø²ąø­ąø™ąøøąøąø²ąø•ą¹ąøšąøš Open-Core ąø‚ąø­ąø‡ą¹€ąø£ąø²ąø­ąø™ąøøąøąø²ąø•ą¹ƒąø«ą¹‰ą¹ƒąøŠą¹‰ąø‡ąø²ąø™ąøŸąø£ąøµą¹„ąø”ą¹‰ąøŖąø¹ąø‡ąøŖąøøąø” {{freeTierLimit}} ąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ąø•ą¹ˆąø­ą¹€ąø‹ąø“ąø£ą¹ŒąøŸą¹€ąø§ąø­ąø£ą¹Œąø«ąø™ąø¶ą¹ˆąø‡ą¹€ąø„ąø£ąø·ą¹ˆąø­ąø‡ ą¹€ąøžąø·ą¹ˆąø­ąø‚ąø¢ąø²ąø¢ąøąø²ąø£ą¹ƒąøŠą¹‰ąø‡ąø²ąø™ąø­ąø¢ą¹ˆąø²ąø‡ąø•ą¹ˆąø­ą¹€ąø™ąø·ą¹ˆąø­ąø‡ ą¹€ąø£ąø²ąø‚ąø­ą¹ąø™ąø°ąø™ąø³ą¹ąøœąø™ Stirling Server - ąø—ąøµą¹ˆąø™ąø±ą¹ˆąø‡ą¹„ąø”ą¹ˆąøˆąø³ąøąø±ąø” แคะ ąø£ąø­ąø‡ąø£ąø±ąøš SSO ą¹ƒąø™ąø£ąø²ąø„ąø² $99/ą¹€ąø‹ąø“ąø£ą¹ŒąøŸą¹€ąø§ąø­ąø£ą¹Œ/เดือน" [onboarding.desktopInstall] title = "ąø”ąø²ąø§ąø™ą¹Œą¹‚ąø«ąø„ąø”" @@ -5237,6 +5367,31 @@ error = "ąø­ąø±ąø›ą¹€ąø”ąø•ąøŖąø–ąø²ąø™ąø°ąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ą¹„ąø”ą¹ˆąøŖąø³ą¹€ success = "ąø„ąøšąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ą¹€ąø£ąøµąø¢ąøšąø£ą¹‰ąø­ąø¢ą¹ąø„ą¹‰ąø§" error = "ąø„ąøšąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ą¹„ąø”ą¹ˆąøŖąø³ą¹€ąø£ą¹‡ąøˆ" +[workspace.people.changePassword] +action = "ą¹€ąø›ąø„ąøµą¹ˆąø¢ąø™ąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™" +title = "ą¹€ąø›ąø„ąøµą¹ˆąø¢ąø™ąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™" +subtitle = "ąø­ąø±ąø›ą¹€ąø”ąø•ąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™ąøŖąø³ąø«ąø£ąø±ąøš" +newPassword = "ąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™ą¹ƒąø«ąø”ą¹ˆ" +confirmPassword = "ąø¢ąø·ąø™ąø¢ąø±ąø™ąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™" +placeholder = "ąø›ą¹‰ąø­ąø™ąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™ą¹ƒąø«ąø”ą¹ˆ" +confirmPlaceholder = "ąø›ą¹‰ąø­ąø™ąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™ą¹ƒąø«ąø”ą¹ˆąø­ąøµąøąø„ąø£ąø±ą¹‰ąø‡" +passwordRequired = "ą¹‚ąø›ąø£ąø”ąø›ą¹‰ąø­ąø™ąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™ą¹ƒąø«ąø”ą¹ˆ" +passwordMismatch = "ąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™ą¹„ąø”ą¹ˆąø•ąø£ąø‡ąøąø±ąø™" +generateRandom = "ąøŖąø£ą¹‰ąø²ąø‡ąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™ąø—ąøµą¹ˆąø›ąø„ąø­ąø”ąø ąø±ąø¢" +generatedPreview = "ąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™ąø—ąøµą¹ˆąøŖąø£ą¹‰ąø²ąø‡ąø‚ąø¶ą¹‰ąø™:" +copyTooltip = "ąø„ąø±ąø”ąø„ąø­ąøą¹„ąø›ąø¢ąø±ąø‡ąø„ąø„ąø“ąø›ąøšąø­ąø£ą¹Œąø”" +copiedToClipboard = "ąø„ąø±ąø”ąø„ąø­ąøąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™ą¹„ąø›ąø¢ąø±ąø‡ąø„ąø„ąø“ąø›ąøšąø­ąø£ą¹Œąø”ą¹ąø„ą¹‰ąø§" +copyFailed = "ąø„ąø±ąø”ąø„ąø­ąøąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™ą¹„ąø”ą¹ˆąøŖąø³ą¹€ąø£ą¹‡ąøˆ" +sendEmail = "ąøŖą¹ˆąø‡ąø­ąøµą¹€ąø”ąø„ą¹ąøˆą¹‰ąø‡ąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ą¹€ąøąøµą¹ˆąø¢ąø§ąøąø±ąøšąøąø²ąø£ą¹€ąø›ąø„ąøµą¹ˆąø¢ąø™ą¹ąø›ąø„ąø‡ąø™ąøµą¹‰" +includePassword = "ąø£ąø§ąø”ąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™ą¹ƒąø«ąø”ą¹ˆą¹ƒąø™ąø­ąøµą¹€ąø”ąø„" +forcePasswordChange = "ąøšąø±ąø‡ąø„ąø±ąøšą¹ƒąø«ą¹‰ąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ą¹€ąø›ąø„ąøµą¹ˆąø¢ąø™ąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™ą¹ƒąø™ąøąø²ąø£ą¹€ąø‚ą¹‰ąø²ąøŖąø¹ą¹ˆąø£ąø°ąøšąøšąø„ąø£ąø±ą¹‰ąø‡ąø–ąø±ąø”ą¹„ąø›" +emailUnavailable = "ąø­ąøµą¹€ąø”ąø„ąø‚ąø­ąø‡ąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ąø™ąøµą¹‰ą¹„ąø”ą¹ˆą¹ƒąøŠą¹ˆąø—ąøµą¹ˆąø­ąø¢ąø¹ą¹ˆąø­ąøµą¹€ąø”ąø„ąø—ąøµą¹ˆąø–ąø¹ąøąø•ą¹‰ąø­ąø‡ ąøąø²ąø£ą¹ąøˆą¹‰ąø‡ą¹€ąø•ąø·ąø­ąø™ąø–ąø¹ąøąø›ąø“ąø”ą¹ƒąøŠą¹‰ąø‡ąø²ąø™" +smtpDisabled = "ąøąø²ąø£ą¹ąøˆą¹‰ąø‡ą¹€ąø•ąø·ąø­ąø™ąø—ąø²ąø‡ąø­ąøµą¹€ąø”ąø„ąø•ą¹‰ąø­ąø‡ą¹€ąø›ąø“ąø”ą¹ƒąøŠą¹‰ąø‡ąø²ąø™ SMTP ą¹ƒąø™ąøąø²ąø£ąø•ąø±ą¹‰ąø‡ąø„ą¹ˆąø²" +notifyOnly = "ąøˆąø°ąø”ąøµąøąø²ąø£ąøŖą¹ˆąø‡ąø­ąøµą¹€ąø”ąø„ą¹‚ąø”ąø¢ą¹„ąø”ą¹ˆąø£ąø§ąø”ąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™ ą¹€ąøžąø·ą¹ˆąø­ą¹ąøˆą¹‰ąø‡ą¹ƒąø«ą¹‰ąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ąø—ąø£ąø²ąøšąø§ą¹ˆąø²ąøœąø¹ą¹‰ąø”ąø¹ą¹ąø„ąø£ąø°ąøšąøšą¹„ąø”ą¹‰ą¹€ąø›ąø„ąøµą¹ˆąø¢ąø™ąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™ą¹ąø„ą¹‰ąø§" +submit = "ąø­ąø±ąø›ą¹€ąø”ąø•ąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™" +success = "ąø­ąø±ąø›ą¹€ąø”ąø•ąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™ąøŖąø³ą¹€ąø£ą¹‡ąøˆ" +error = "ąø­ąø±ąø›ą¹€ąø”ąø•ąø£ąø«ąø±ąøŖąøœą¹ˆąø²ąø™ą¹„ąø”ą¹ˆąøŖąø³ą¹€ąø£ą¹‡ąøˆ" + [workspace.people.emailInvite] tab = "ą¹€ąøŠąø“ąøąø—ąø²ąø‡ąø­ąøµą¹€ąø”ąø„" description = "ąøžąø“ąø”ąøžą¹Œąø«ąø£ąø·ąø­ąø§ąø²ąø‡ąø­ąøµą¹€ąø”ąø„ąø”ą¹‰ąø²ąø™ąø„ą¹ˆąø²ąø‡ ąø„ąø±ą¹ˆąø™ąø”ą¹‰ąø§ąø¢ą¹€ąø„ąø£ąø·ą¹ˆąø­ąø‡ąø«ąø”ąø²ąø¢ąøˆąøøąø„ąø ąø²ąø„ ąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ąøˆąø°ą¹„ąø”ą¹‰ąø£ąø±ąøšąø‚ą¹‰ąø­ąø”ąø¹ąø„ą¹€ąø‚ą¹‰ąø²ąøŖąø¹ą¹ˆąø£ąø°ąøšąøšąø—ąø²ąø‡ąø­ąøµą¹€ąø”ąø„" @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "ąø•ą¹‰ąø­ąø‡ąø”ąøµąø­ąø¢ą¹ˆąø²ąø‡ąø™ą¹‰ąø­ąø¢ąø«ąø™ąø¶ą¹ˆąø‡ąø—ąøµą¹ˆąø­ąø¢ąø¹ą¹ˆąø­ąøµą¹€ąø”ąø„" submit = "ąøŖą¹ˆąø‡ąø„ąø³ą¹€ąøŠąø“ąø" success = "ą¹€ąøŠąø“ąøąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ąøŖąø³ą¹€ąø£ą¹‡ąøˆ" -partialSuccess = "ąøšąø²ąø‡ąø„ąø³ą¹€ąøŠąø“ąøąø„ą¹‰ąø”ą¹€ąø«ąø„ąø§" +partialFailure = "ąø„ąø³ą¹€ąøŠąø“ąøąøšąø²ąø‡ąø£ąø²ąø¢ąøąø²ąø£ą¹„ąø”ą¹ˆąøŖąø³ą¹€ąø£ą¹‡ąøˆ" allFailed = "ą¹€ąøŠąø“ąøąøœąø¹ą¹‰ą¹ƒąøŠą¹‰ą¹„ąø”ą¹ˆąøŖąø³ą¹€ąø£ą¹‡ąøˆ" error = "ąøŖą¹ˆąø‡ąø„ąø³ą¹€ąøŠąø“ąøą¹„ąø”ą¹ˆąøŖąø³ą¹€ąø£ą¹‡ąøˆ" @@ -5709,7 +5864,7 @@ title = "ą¹ąøœąø™ąø ąø¹ąø”ąø“ąøąø²ąø£ą¹ƒąøŠą¹‰ąø‡ąø²ąø™ Endpoint" [usage.table] title = "ąøŖąø–ąø“ąø•ąø“ą¹ąøšąøšąø„ąø°ą¹€ąø­ąøµąø¢ąø”" -endpoint = "Endpoint" +endpoint = "ปคายทาง" visits = "ąøąø²ąø£ą¹€ąø‚ą¹‰ąø²ąøŠąø”" percentage = "ą¹€ąø›ąø­ąø£ą¹Œą¹€ąø‹ą¹‡ąø™ąø•ą¹Œ" noData = "ą¹„ąø”ą¹ˆąø”ąøµąø‚ą¹‰ąø­ąø”ąø¹ąø„" @@ -5770,6 +5925,7 @@ subtitle = "ąø„ąø‡ąøŠąø·ą¹ˆąø­ą¹€ąø‚ą¹‰ąø²ą¹ƒąøŠą¹‰ąø”ą¹‰ąø§ąø¢ąøšąø±ąøąøŠąøµ S [setup.selfhosted] title = "ąø„ąø‡ąøŠąø·ą¹ˆąø­ą¹€ąø‚ą¹‰ąø²ą¹ƒąøŠą¹‰ą¹€ąø‹ąø“ąø£ą¹ŒąøŸą¹€ąø§ąø­ąø£ą¹Œ" subtitle = "ąø›ą¹‰ąø­ąø™ąø‚ą¹‰ąø­ąø”ąø¹ąø„ąø£ąø±ąøšąø£ąø­ąø‡ąø‚ąø­ąø‡ą¹€ąø‹ąø“ąø£ą¹ŒąøŸą¹€ąø§ąø­ąø£ą¹Œąø‚ąø­ąø‡ąø„ąøøąø“" +link = "ąø«ąø£ąø·ąø­ą¹€ąøŠąø·ą¹ˆąø­ąø”ąø•ą¹ˆąø­ąøąø±ąøšąøšąø±ąøąøŠąøµą¹ąøšąøš self-hosted" [setup.server] title = "ą¹€ąøŠąø·ą¹ˆąø­ąø”ąø•ą¹ˆąø­ąøąø±ąøšą¹€ąø‹ąø“ąø£ą¹ŒąøŸą¹€ąø§ąø­ąø£ą¹Œ" @@ -5788,6 +5944,14 @@ description = "ป้อน URL ą¹ąøšąøšą¹€ąø•ą¹‡ąø”ąø‚ąø­ąø‡ą¹€ąø‹ąø“ąø£ą¹ŒąøŸ emptyUrl = "โปรดป้อน URL ą¹€ąø‹ąø“ąø£ą¹ŒąøŸą¹€ąø§ąø­ąø£ą¹Œ" unreachable = "ą¹„ąø”ą¹ˆąøŖąø²ąø”ąø²ąø£ąø–ą¹€ąøŠąø·ą¹ˆąø­ąø”ąø•ą¹ˆąø­ąøąø±ąøšą¹€ąø‹ąø“ąø£ą¹ŒąøŸą¹€ąø§ąø­ąø£ą¹Œ" testFailed = "ąøąø²ąø£ąø—ąø”ąøŖąø­ąøšąøąø²ąø£ą¹€ąøŠąø·ą¹ˆąø­ąø”ąø•ą¹ˆąø­ąø„ą¹‰ąø”ą¹€ąø«ąø„ąø§" +configFetch = "ą¹„ąø”ą¹ˆąøŖąø²ąø”ąø²ąø£ąø–ąø”ąø¶ąø‡ąøąø²ąø£ąøąø³ąø«ąø™ąø”ąø„ą¹ˆąø²ąø‚ąø­ąø‡ą¹€ąø‹ąø“ąø£ą¹ŒąøŸą¹€ąø§ąø­ąø£ą¹Œą¹„ąø”ą¹‰ ą¹‚ąø›ąø£ąø”ąø•ąø£ąø§ąøˆąøŖąø­ąøš URL แค้วคองอีกครั้ง" + +[setup.server.error.securityDisabled] +title = "ą¹„ąø”ą¹ˆą¹„ąø”ą¹‰ą¹€ąø›ąø“ąø”ą¹ƒąøŠą¹‰ąø‡ąø²ąø™ąøąø²ąø£ą¹€ąø‚ą¹‰ąø²ąøŖąø¹ą¹ˆąø£ąø°ąøšąøš" +body = "ą¹€ąø‹ąø“ąø£ą¹ŒąøŸą¹€ąø§ąø­ąø£ą¹Œąø™ąøµą¹‰ą¹„ąø”ą¹ˆą¹„ąø”ą¹‰ą¹€ąø›ąø“ąø”ą¹ƒąøŠą¹‰ąø‡ąø²ąø™ąøąø²ąø£ą¹€ąø‚ą¹‰ąø²ąøŖąø¹ą¹ˆąø£ąø°ąøšąøš ą¹€ąøžąø·ą¹ˆąø­ą¹€ąøŠąø·ą¹ˆąø­ąø”ąø•ą¹ˆąø­ąøąø±ąøšą¹€ąø‹ąø“ąø£ą¹ŒąøŸą¹€ąø§ąø­ąø£ą¹Œąø™ąøµą¹‰ ąø„ąøøąø“ąø•ą¹‰ąø­ąø‡ą¹€ąø›ąø“ąø”ą¹ƒąøŠą¹‰ąø‡ąø²ąø™ąøąø²ąø£ąø¢ąø·ąø™ąø¢ąø±ąø™ąø•ąø±ąø§ąø•ąø™:" +step1 = "ąøąø³ąø«ąø™ąø”ąø„ą¹ˆąø² DOCKER_ENABLE_SECURITY=true ą¹ƒąø™ąøŖąø ąø²ąøžą¹ąø§ąø”ąø„ą¹‰ąø­ąø”ąø‚ąø­ąø‡ąø„ąøøąø“" +step2 = "ąø«ąø£ąø·ąø­ąøąø³ąø«ąø™ąø”ąø„ą¹ˆąø² security.enableLogin=true ą¹ƒąø™ settings.yml" +step3 = "ą¹€ąø£ąø“ą¹ˆąø”ą¹€ąø‹ąø“ąø£ą¹ŒąøŸą¹€ąø§ąø­ąø£ą¹Œą¹ƒąø«ąø”ą¹ˆ" [setup.login] title = "ą¹€ąø‚ą¹‰ąø²ąøŖąø¹ą¹ˆąø£ąø°ąøšąøš" @@ -5797,6 +5961,13 @@ submit = "ą¹€ąø‚ą¹‰ąø²ąøŖąø¹ą¹ˆąø£ąø°ąøšąøš" signInWith = "ąø„ąø‡ąøŠąø·ą¹ˆąø­ą¹€ąø‚ą¹‰ąø²ą¹ƒąøŠą¹‰ąø”ą¹‰ąø§ąø¢" oauthPending = "ąøąø³ąø„ąø±ąø‡ą¹€ąø›ąø“ąø”ą¹€ąøšąø£ąø²ąø§ą¹Œą¹€ąø‹ąø­ąø£ą¹Œą¹€ąøžąø·ą¹ˆąø­ąø¢ąø·ąø™ąø¢ąø±ąø™ąø•ąø±ąø§ąø•ąø™..." orContinueWith = "ąø«ąø£ąø·ąø­ąø”ąø³ą¹€ąø™ąø“ąø™ąøąø²ąø£ąø•ą¹ˆąø­ąø”ą¹‰ąø§ąø¢ąø­ąøµą¹€ąø”ąø„" +serverRequirement = "หดายเหตุ: ą¹€ąø‹ąø“ąø£ą¹ŒąøŸą¹€ąø§ąø­ąø£ą¹Œąø•ą¹‰ąø­ąø‡ą¹€ąø›ąø“ąø”ą¹ƒąøŠą¹‰ąø‡ąø²ąø™ąøąø²ąø£ą¹€ąø‚ą¹‰ąø²ąøŖąø¹ą¹ˆąø£ąø°ąøšąøš" +showInstructions = "ą¹€ąø›ąø“ąø”ą¹ƒąøŠą¹‰ąø‡ąø²ąø™ąø­ąø¢ą¹ˆąø²ąø‡ą¹„ąø£?" +hideInstructions = "ąø‹ą¹ˆąø­ąø™ąø„ąø³ą¹ąø™ąø°ąø™ąø³" +instructions = "ąø§ąø“ąø˜ąøµą¹€ąø›ąø“ąø”ą¹ƒąøŠą¹‰ąø‡ąø²ąø™ąøąø²ąø£ą¹€ąø‚ą¹‰ąø²ąøŖąø¹ą¹ˆąø£ąø°ąøšąøšąøšąø™ą¹€ąø‹ąø“ąø£ą¹ŒąøŸą¹€ąø§ąø­ąø£ą¹Œ Stirling PDF ของคุณ:" +instructionsEnvVar = "ąø•ąø±ą¹‰ąø‡ąø„ą¹ˆąø²ąø•ąø±ąø§ą¹ąø›ąø£ąøŖąø ąø²ąøžą¹ąø§ąø”ąø„ą¹‰ąø­ąø”:" +instructionsOrYml = "ąø«ąø£ąø·ąø­ą¹ƒąø™ settings.yml:" +instructionsRestart = "ąøˆąø²ąøąø™ąø±ą¹‰ąø™ąø£ąøµąøŖąø•ąø²ąø£ą¹Œąø—ą¹€ąø‹ąø“ąø£ą¹ŒąøŸą¹€ąø§ąø­ąø£ą¹Œąø‚ąø­ąø‡ąø„ąøøąø“ą¹€ąøžąø·ą¹ˆąø­ą¹ƒąø«ą¹‰ąøąø²ąø£ą¹€ąø›ąø„ąøµą¹ˆąø¢ąø™ą¹ąø›ąø„ąø‡ąø”ąøµąøœąø„" [setup.login.username] label = "ąøŠąø·ą¹ˆąø­ąøœąø¹ą¹‰ą¹ƒąøŠą¹‰" @@ -5853,6 +6024,7 @@ earlyAccess = "ą¹€ąø‚ą¹‰ąø²ąø–ąø¶ąø‡ąø„ą¹ˆąø§ąø‡ąø«ąø™ą¹‰ąø²" reset = "ąø£ąøµą¹€ąø‹ą¹‡ąø•ąøąø²ąø£ą¹€ąø›ąø„ąøµą¹ˆąø¢ąø™ą¹ąø›ąø„ąø‡" downloadJson = "ąø”ąø²ąø§ąø™ą¹Œą¹‚ąø«ąø„ąø” JSON" generatePdf = "สร้าง PDF" +saveChanges = "ąøšąø±ąø™ąø—ąø¶ąøąøąø²ąø£ą¹€ąø›ąø„ąøµą¹ˆąø¢ąø™ą¹ąø›ąø„ąø‡" [pdfTextEditor.options.autoScaleText] title = "ąø›ąø£ąø±ąøšąø‚ąø™ąø²ąø”ąø‚ą¹‰ąø­ąø„ąø§ąø²ąø”ąø­ąø±ąø•ą¹‚ąø™ąø”ąø±ąø•ąø“ą¹ƒąø«ą¹‰ąøžąø­ąø”ąøµąøąø„ą¹ˆąø­ąø‡" @@ -5890,6 +6062,8 @@ alpha = "ąø•ąø±ąø§ąø”ąø¹ą¹ąøšąøš alpha ąø™ąøµą¹‰ąø¢ąø±ąø‡ąøžąø±ąø’ąø™ąø²ąø­ąø¢ [pdfTextEditor.empty] title = "ąø¢ąø±ąø‡ą¹„ąø”ą¹ˆą¹„ąø”ą¹‰ą¹‚ąø«ąø„ąø”ą¹€ąø­ąøąøŖąø²ąø£" subtitle = "ą¹‚ąø«ąø„ąø”ą¹„ąøŸąø„ą¹Œ PDF หรือ JSON ą¹€ąøžąø·ą¹ˆąø­ą¹€ąø£ąø“ą¹ˆąø”ą¹ąøą¹‰ą¹„ąø‚ąø‚ą¹‰ąø­ąø„ąø§ąø²ąø”" +dropzone = "ąø„ąø²ąøą¹ąø„ąø°ąø§ąø²ąø‡ą¹„ąøŸąø„ą¹Œ PDF หรือ JSON ąø—ąøµą¹ˆąø™ąøµą¹ˆ ąø«ąø£ąø·ąø­ąø„ąø„ąø“ąøą¹€ąøžąø·ą¹ˆąø­ą¹€ąø£ąøµąø¢ąøąø”ąø¹" +dropzoneWithFiles = "ą¹€ąø„ąø·ąø­ąøą¹„ąøŸąø„ą¹Œąøˆąø²ąøą¹ąø—ą¹‡ąøšą¹„ąøŸąø„ą¹Œ ąø«ąø£ąø·ąø­ąø„ąø²ąøą¹ąø„ąø°ąø§ąø²ąø‡ą¹„ąøŸąø„ą¹Œ PDF หรือ JSON ąø—ąøµą¹ˆąø™ąøµą¹ˆ ąø«ąø£ąø·ąø­ąø„ąø„ąø“ąøą¹€ąøžąø·ą¹ˆąø­ą¹€ąø£ąøµąø¢ąøąø”ąø¹" [pdfTextEditor.welcomeBanner] title = "ąø¢ąø“ąø™ąø”ąøµąø•ą¹‰ąø­ąø™ąø£ąø±ąøšąøŖąø¹ą¹ˆ PDF Text Editor (Early Access)" @@ -5932,13 +6106,13 @@ warnings = "คำเตือน" suggestions = "หดายเหตุ" currentPageFonts = "ąøŸąø­ąø™ąø•ą¹Œąøšąø™ąø«ąø™ą¹‰ąø²ąø™ąøµą¹‰" allFonts = "ąøŸąø­ąø™ąø•ą¹Œąø—ąø±ą¹‰ąø‡ąø«ąø”ąø”" -fallback = "fallback" +fallback = "สำรอง" missing = "หายไป" perfectMessage = "ąøŖąø²ąø”ąø²ąø£ąø–ąø—ąø³ąø‹ą¹‰ąø³ąøŸąø­ąø™ąø•ą¹Œąø—ąø±ą¹‰ąø‡ąø«ąø”ąø”ą¹„ąø”ą¹‰ąø­ąø¢ą¹ˆąø²ąø‡ąøŖąø”ąøšąø¹ąø£ąø“ą¹Œą¹ąøšąøš" warningMessage = "ąøšąø²ąø‡ąøŸąø­ąø™ąø•ą¹Œąø­ąø²ąøˆą¹ąøŖąø”ąø‡ąøœąø„ą¹„ąø”ą¹ˆąø–ąø¹ąøąø•ą¹‰ąø­ąø‡" infoMessage = "ąø”ąøµąø‚ą¹‰ąø­ąø”ąø¹ąø„ąøąø²ąø£ąø—ąø³ąø‹ą¹‰ąø³ąøŸąø­ąø™ąø•ą¹Œ" perfect = "ąøŖąø”ąøšąø¹ąø£ąø“ą¹Œą¹ąøšąøš" -subset = "subset" +subset = "ąø‹ąø±ąøšą¹€ąø‹ą¹‡ąø•" [pdfTextEditor.errors] invalidJson = "ą¹„ąø”ą¹ˆąøŖąø²ąø”ąø²ąø£ąø–ąø­ą¹ˆąø²ąø™ą¹„ąøŸąø„ą¹Œ JSON ą¹‚ąø›ąø£ąø”ąø•ąø£ąø§ąøˆąøŖąø­ąøšąø§ą¹ˆąø²ą¹„ąøŸąø„ą¹Œąø–ąø¹ąøąøŖąø£ą¹‰ąø²ąø‡ą¹‚ąø”ąø¢ą¹€ąø„ąø£ąø·ą¹ˆąø­ąø‡ąø”ąø·ąø­ PDF to JSON" @@ -5953,7 +6127,7 @@ insufficientPermissions = "ąø„ąøøąø“ą¹„ąø”ą¹ˆąø”ąøµąøŖąø“ąø—ąø˜ąø“ą¹Œą¹ƒąø™ąø [addText] title = "ą¹€ąøžąø“ą¹ˆąø”ąø‚ą¹‰ąø­ąø„ąø§ąø²ąø”" header = "ą¹€ąøžąø“ą¹ˆąø”ąø‚ą¹‰ąø­ąø„ąø§ąø²ąø”ąø„ąø‡ą¹ƒąø™ PDF" -tags = "text,annotation,label" +tags = "ข้อควาด,ąø„ąø³ąø­ąø˜ąø“ąøšąø²ąø¢ąø›ąø£ąø°ąøąø­ąøš,ąø›ą¹‰ąø²ąø¢ąøąø³ąøąø±ąøš" applySignatures = "ą¹ƒąøŠą¹‰ąø‚ą¹‰ąø­ąø„ąø§ąø²ąø”" [addText.text] diff --git a/frontend/public/locales/tr-TR/translation.toml b/frontend/public/locales/tr-TR/translation.toml index a5bee2af75..43d9eb0cd2 100644 --- a/frontend/public/locales/tr-TR/translation.toml +++ b/frontend/public/locales/tr-TR/translation.toml @@ -163,6 +163,11 @@ unfavorite = "Favorilerden kaldır" fullscreen = "Tam ekran moduna geƧ" sidebar = "Kenar Ƨubuğu moduna geƧ" +[backendStartup] +notFoundTitle = "Arka uƧ bulunamadı" +retry = "Yeniden dene" +unreachable = "Uygulama şu anda arka uca bağlanamıyor. Lütfen arka uƧ durumunu ve ağ bağlantısını doğrulayın, ardından tekrar deneyin." + [zipWarning] title = "Büyük ZIP Dosyası" message = "Bu ZIP {{count}} dosya iƧeriyor. Yine de Ƨıkartılsın mı?" @@ -912,6 +917,9 @@ desc = "PDF eylemlerini birbirine bağlayarak Ƨok adımlı iş akışları olu desc = "PDF'leri başka bir PDF'nin üzerine bindirir" title = "PDF'leri Bindirme" +[home.pdfTextEditor] +title = "PDF Metin Düzenleyici" +desc = "PDF'lerin iƧindeki mevcut metinleri ve gƶrselleri düzenleyin" [home.addText] tags = "metin,ek aƧıklama,etiket" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Ƈizim imzası" defaultImageLabel = "Yüklenen imza" defaultTextLabel = "Yazılmış imza" saveButton = "İmzayı kaydet" +savePersonal = "Kişisel Olarak Kaydet" +saveShared = "Paylaşılan Olarak Kaydet" saveUnavailable = "Kaydetmek iƧin ƶnce bir imza oluşturun." noChanges = "GeƧerli imza zaten kaydedildi." +tempStorageTitle = "GeƧici tarayıcı depolaması" +tempStorageDescription = "İmzalar yalnızca tarayıcınızda saklanır. Tarayıcı verilerini temizlerseniz veya tarayıcı değiştirirseniz kaybolurlar." +personalHeading = "Kişisel İmzalar" +sharedHeading = "Paylaşılan İmzalar" +personalDescription = "Bu imzaları yalnızca siz gƶrebilirsiniz." +sharedDescription = "Tüm kullanıcılar bu imzaları gƶrebilir ve kullanabilir." [sign.saved.type] canvas = "Ƈizim" @@ -3020,6 +3036,91 @@ title = "PDF Hakkında Bilgi Al" header = "PDF Hakkında Bilgi Al" submit = "Bilgi Al" downloadJson = "JSON İndir" +processing = "Bilgi Ƨıkarılıyor..." +results = "SonuƧlar" +noResults = "Bir rapor oluşturmak iƧin aracı Ƨalıştırın." +downloads = "İndirmeler" +noneDetected = "HiƧbiri tespit edilmedi" +indexTitle = "Dizin" + +[getPdfInfo.report] +entryLabel = "Tam bilgi ƶzeti" +shortTitle = "PDF Bilgileri" + +[getPdfInfo.sections] +metadata = "Meta veriler" +formFields = "Form Alanları" +basicInfo = "Temel Bilgiler" +documentInfo = "Belge Bilgileri" +compliance = "Uyumluluk" +encryption = "Şifreleme" +permissions = "İzinler" +other = "Diğer" +perPageInfo = "Sayfa Başına Bilgi" +tableOfContents = "İƧindekiler" + +[getPdfInfo.other] +attachments = "Ekler" +embeddedFiles = "Gƶmülü Dosyalar" +javaScript = "JavaScript" +layers = "Katmanlar" +structureTree = "Yapı Ağacı" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Boyut" +annotations = "AƧıklamalar" +images = "Gƶrüntüler" +links = "Bağlantılar" +fonts = "Yazı Tipleri" +xobjects = "XObject Sayıları" +multimedia = "Multimedya" + +[getPdfInfo.summary] +pages = "Sayfalar" +fileSize = "Dosya Boyutu" +pdfVersion = "PDF Sürümü" +language = "Dil" +title = "PDF Ɩzeti" +author = "Yazar" +created = "Oluşturulma" +modified = "Değiştirilme" +permsAll = "Tüm izinlere izin verildi" +permsRestricted = "{{count}} kısıtlama" +permsMixed = "Bazı izinler kısıtlandı" +hasCompliance = "Uyumluluk standartlarına sahip" +noCompliance = "Uyumluluk standardı yok" +basic = "Temel Bilgiler" +documentInfo = "Belge Bilgileri" +securityTitle = "Güvenlik Durumu" +technical = "Teknik" +overviewTitle = "PDF Genel Bakış" + +[getPdfInfo.summary.security] +encrypted = "Şifrelenmiş PDF - Parola koruması var" +unencrypted = "Şifrelenmemiş PDF - Parola koruması yok" + +[getPdfInfo.summary.tech] +images = "Gƶrüntüler" +fonts = "Yazı Tipleri" +formFields = "Form Alanları" +embeddedFiles = "Gƶmülü Dosyalar" +javaScript = "JavaScript" +layers = "Katmanlar" +bookmarks = "Yer İmleri" +multimedia = "Multimedya" + +[getPdfInfo.summary.overview] +untitled = "başlıksız bir belge" +unknown = "Bilinmeyen Yazar" +text = "Bu, {{author}} tarafından oluşturulan, {{title}} başlıklı {{pages}} sayfalık bir PDF'dir (PDF sürümü {{version}})." + +[getPdfInfo.error] +partial = "Bazı dosyalar işlenemedi." +unexpected = "Ƈıkarma sırasında beklenmeyen bir hata oluştu." + +[getPdfInfo.status] +complete = "Ƈıkarma tamamlandı" [extractPage] tags = "Ƨıkar" @@ -3438,6 +3539,9 @@ signinTitle = "Lütfen giriş yapınız." ssoSignIn = "Tek Oturum AƧma ile Giriş Yap" oAuth2AutoCreateDisabled = "OAUTH2 Otomatik Oluşturma Kullanıcı Devre Dışı Bırakıldı" oAuth2AdminBlockedUser = "Kayıtlı olmayan kullanıcıların kayıt veya giriş yapması şu anda engellenmiştir. Lütfen yƶneticiyle iletişime geƧin." +oAuth2RequiresLicense = "OAuth/SSO ile oturum aƧma ücretli bir lisans (Server veya Enterprise) gerektirir. Planınızı yükseltmek iƧin lütfen yƶneticiyle iletişime geƧin." +saml2RequiresLicense = "SAML ile oturum aƧma ücretli bir lisans (Server veya Enterprise) gerektirir. Planınızı yükseltmek iƧin lütfen yƶneticiyle iletişime geƧin." +maxUsersReached = "Mevcut lisansınız iƧin azami kullanıcı sayısına ulaşıldı. Planınızı yükseltmek veya daha fazla koltuk eklemek iƧin lütfen yƶneticiyle iletişime geƧin." oauth2RequestNotFound = "Yetkilendirme isteği bulunamadı" oauth2InvalidUserInfoResponse = "GeƧersiz Kullanıcı Bilgisi Yanıtı" oauth2invalidRequest = "GeƧersiz İstek" @@ -3846,14 +3950,17 @@ fitToWidth = "Genişliğe Sığdır" actualSize = "GerƧek Boyut" [viewer] +cannotPreviewFile = "Dosya ƶnizlenemiyor" +dualPageView = "Ƈift Sayfa Gƶrünümü" firstPage = "İlk Sayfa" lastPage = "Son Sayfa" -previousPage = "Ɩnceki Sayfa" nextPage = "Sonraki Sayfa" +onlyPdfSupported = "Gƶrüntüleyici yalnızca PDF dosyalarını destekler. Bu dosya farklı bir biƧimde gƶrünüyor." +previousPage = "Ɩnceki Sayfa" +singlePageView = "Tek Sayfa Gƶrünümü" +unknownFile = "Bilinmeyen dosya" zoomIn = "Yakınlaştır" zoomOut = "Uzaklaştır" -singlePageView = "Tek Sayfa Gƶrünümü" -dualPageView = "Ƈift Sayfa Gƶrünümü" [rightRail] closeSelected = "SeƧilen Dosyaları Kapat" @@ -3877,6 +3984,7 @@ toggleSidebar = "Kenar Ƈubuğunu AƧ/Kapat" exportSelected = "SeƧilen Sayfaları Dışa Aktar" toggleAnnotations = "AƧıklamaların Gƶrünürlüğünü Değiştir" annotationMode = "AƧıklama Modunu Değiştir" +print = "PDF'yi Yazdır" draw = "Ƈiz" save = "Kaydet" saveChanges = "Değişiklikleri Kaydet" @@ -4494,6 +4602,7 @@ description = "Impressum iƧin URL veya dosya adı (bazı yargı bƶlgelerinde g title = "Premium ve Kurumsal" description = "Premium veya kurumsal lisans anahtarınızı yapılandırın." license = "Lisans Yapılandırması" +noInput = "Lütfen bir lisans anahtarı veya dosyası sağlayın" [admin.settings.premium.licenseKey] toggle = "Lisans anahtarınız veya sertifika dosyanız mı var?" @@ -4511,6 +4620,25 @@ line1 = "Mevcut lisans anahtarınızın üzerine yazma işlemi geri alınamaz." line2 = "Başka yerde yedeğiniz yoksa ƶnceki lisansınız kalıcı olarak kaybolacaktır." line3 = "Ɩnemli: Lisans anahtarlarını gizli ve güvenli tutun. Asla herkese aƧık şekilde paylaşmayın." +[admin.settings.premium.inputMethod] +text = "Lisans Anahtarı" +file = "Sertifika Dosyası" + +[admin.settings.premium.file] +label = "Lisans Sertifika Dosyası" +description = "Ƈevrimdışı satın alımlardan aldığınız .lic veya .cert lisans dosyanızı yükleyin" +choose = "Lisans Dosyası SeƧ" +selected = "SeƧildi: {{filename}} ({{size}})" +successMessage = "Lisans dosyası başarıyla yüklendi ve etkinleştirildi. Yeniden başlatma gerekmez." + +[admin.settings.premium.currentLicense] +title = "Etkin Lisans" +file = "Kaynak: Lisans dosyası ({{path}})" +key = "Kaynak: Lisans anahtarı" +type = "Tür: {{type}}" +noInput = "Lütfen bir lisans anahtarı girin veya bir sertifika dosyası yükleyin" +success = "Başarılı" + [admin.settings.premium.enabled] label = "Premium Ɩzellikleri Etkinleştir" description = "Pro/kurumsal ƶzellikler iƧin lisans anahtarı kontrollerini etkinleştir" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} seƧildi" download = "İndir" delete = "Sil" unsupported = "Desteklenmiyor" +active = "Aktif" addToUpload = "Yüklemeye Ekle" +closeFile = "Dosyayı Kapat" deleteAll = "Tümünü Sil" loadingFiles = "Dosyalar yükleniyor..." noFiles = "Kullanılabilir dosya yok" @@ -5132,7 +5262,7 @@ upgrade = "Şimdi yükselt →" freeTitle = "Sunucu Lisansı" overLimitTitle = "Sunucu Lisansı Gerekli" overLimitBody = "Lisansımız, sunucu başına ücretsiz olarak en fazla {{freeTierLimit}} kullanıcıya izin verir. {{overLimitUserCopy}} Stirling kullanıcınız var. Kesintisiz devam etmek iƧin Stirling Server planına yükseltin - sınırsız koltuk, PDF metin düzenleme ve tam yƶnetici kontrolü $99/server/ay." -freeBody = "Open-Core lisansımız, sunucu başına ücretsiz olarak en fazla {{freeTierLimit}} kullanıcıya izin verir. Kesintisiz ƶlƧeklemek ve yeni PDF metin düzenleme aracımıza erken erişim almak iƧin Stirling Server planını ƶneririz - tam düzenleme ve sınırsız koltuk $99/server/ay." +freeBody = "Open-Core lisanslamamız, sunucu başına en fazla {{freeTierLimit}} kullanıcıya ücretsiz izin verir. Kesintisiz ƶlƧeklendirme iƧin Stirling Server planını ƶneririz - sınırsız kullanıcı ve SSO desteği iƧin $99/sunucu/ay." [onboarding.desktopInstall] title = "İndir" @@ -5237,6 +5367,31 @@ error = "Kullanıcı durumu güncellenemedi" success = "Kullanıcı başarıyla silindi" error = "Kullanıcı silinemedi" +[workspace.people.changePassword] +action = "Parolayı değiştir" +title = "Parolayı değiştir" +subtitle = "Şunun iƧin parolayı güncelle" +newPassword = "Yeni parola" +confirmPassword = "Parolayı onayla" +placeholder = "Yeni bir parola girin" +confirmPlaceholder = "Yeni parolayı tekrar girin" +passwordRequired = "Lütfen yeni bir parola girin" +passwordMismatch = "Parolalar eşleşmiyor" +generateRandom = "Güvenli parola oluştur" +generatedPreview = "Oluşturulan parola:" +copyTooltip = "Panoya kopyala" +copiedToClipboard = "Parola panoya kopyalandı" +copyFailed = "Parola kopyalanamadı" +sendEmail = "Kullanıcıya bu değişiklik hakkında e-posta gƶnder" +includePassword = "E-postaya yeni parolayı dahil et" +forcePasswordChange = "Kullanıcının bir sonraki girişte parolasını değiştirmesini zorunlu kıl" +emailUnavailable = "Bu kullanıcının e-posta adresi geƧerli değil. Bildirimler devre dışı." +smtpDisabled = "E-posta bildirimleri iƧin ayarlarda SMTP'nin etkinleştirilmesi gerekir." +notifyOnly = "Parola olmadan bir e-posta gƶnderilecek ve kullanıcıya bir yƶneticinin parolayı değiştirdiği bildirilecek." +submit = "Parolayı güncelle" +success = "Parola başarıyla güncellendi" +error = "Parola güncellenemedi" + [workspace.people.emailInvite] tab = "E-posta Daveti" description = "Aşağıya e-postaları virgülle ayırarak yazın veya yapıştırın. Kullanıcılar giriş bilgilerini e-posta ile alacaktır." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "En az bir e-posta adresi gereklidir" submit = "Davetleri Gƶnder" success = "kullanıcı(lar) başarıyla davet edildi" -partialSuccess = "Bazı davetler başarısız oldu" +partialFailure = "Bazı davetler başarısız oldu" allFailed = "Kullanıcılar davet edilemedi" error = "Davetler gƶnderilemedi" @@ -5770,6 +5925,7 @@ subtitle = "Stirling hesabınızla oturum aƧın" [setup.selfhosted] title = "Sunucuda Oturum AƧın" subtitle = "Sunucu kimlik bilgilerinizi girin" +link = "veya kendi barındırdığınız bir hesaba bağlanın" [setup.server] title = "Sunucuya Bağlan" @@ -5788,6 +5944,14 @@ description = "Ɩz barındırılan Stirling PDF sunucunuzun tam URL'sini girin" emptyUrl = "Lütfen bir sunucu URL'si girin" unreachable = "Sunucuya bağlanılamadı" testFailed = "Bağlantı testi başarısız" +configFetch = "Sunucu yapılandırması alınamadı. Lütfen URL'yi kontrol edip tekrar deneyin." + +[setup.server.error.securityDisabled] +title = "Giriş Etkin Değil" +body = "Bu sunucuda giriş etkin değil. Bu sunucuya bağlanmak iƧin kimlik doğrulamayı etkinleştirmelisiniz:" +step1 = "Ortamınızda DOCKER_ENABLE_SECURITY=true olarak ayarlayın" +step2 = "Ya da settings.yml iƧinde security.enableLogin=true olarak ayarlayın" +step3 = "Sunucuyu yeniden başlatın" [setup.login] title = "Oturum AƧ" @@ -5797,6 +5961,13 @@ submit = "Oturum AƧ" signInWith = "Şununla oturum aƧ" oauthPending = "Kimlik doğrulama iƧin tarayıcı aƧılıyor..." orContinueWith = "Veya e-postayla devam edin" +serverRequirement = "Not: Sunucuda oturum aƧma etkin olmalıdır." +showInstructions = "Nasıl etkinleştirilir?" +hideInstructions = "Yƶnergeleri gizle" +instructions = "Stirling PDF sunucunuzda oturum aƧmayı etkinleştirmek iƧin:" +instructionsEnvVar = "Ortam değişkenini ayarlayın:" +instructionsOrYml = "Veya settings.yml iƧinde:" +instructionsRestart = "Ardından değişikliklerin etkili olması iƧin sunucunuzu yeniden başlatın." [setup.login.username] label = "Kullanıcı adı" @@ -5853,6 +6024,7 @@ earlyAccess = "Erken Erişim" reset = "Değişiklikleri Sıfırla" downloadJson = "JSON'u İndir" generatePdf = "PDF Oluştur" +saveChanges = "Değişiklikleri Kaydet" [pdfTextEditor.options.autoScaleText] title = "Metni kutulara otomatik sığdır" @@ -5890,6 +6062,8 @@ alpha = "Bu alfa gƶrüntüleyici hĆ¢lĆ¢ gelişiyor—bazı yazı tipleri, renkl [pdfTextEditor.empty] title = "Belge yüklenmedi" subtitle = "Metin iƧeriğini düzenlemeye başlamak iƧin bir PDF veya JSON dosyası yükleyin." +dropzone = "Buraya bir PDF veya JSON dosyası sürükleyip bırakın veya gƶz atmak iƧin tıklayın" +dropzoneWithFiles = "Dosyalar sekmesinden bir dosya seƧin veya buraya bir PDF veya JSON dosyası sürükleyip bırakın veya gƶz atmak iƧin tıklayın" [pdfTextEditor.welcomeBanner] title = "PDF Metin Düzenleyiciye Hoş Geldiniz (Erken Erişim)" diff --git a/frontend/public/locales/uk-UA/translation.toml b/frontend/public/locales/uk-UA/translation.toml index dbb957a6a7..7a21b1e656 100644 --- a/frontend/public/locales/uk-UA/translation.toml +++ b/frontend/public/locales/uk-UA/translation.toml @@ -163,6 +163,11 @@ unfavorite = "ВиГалити Š· вибраного" fullscreen = "ŠŸŠµŃ€ŠµŠŗŠ»ŃŽŃ‡ŠøŃ‚ŠøŃŃ на повноекранний режим" sidebar = "ŠŸŠµŃ€ŠµŠŗŠ»ŃŽŃ‡ŠøŃ‚ŠøŃŃ на режим бічної панелі" +[backendStartup] +notFoundTitle = "Š”ŠµŃ€Š²ŠµŃ€Š½Ńƒ Ń‡Š°ŃŃ‚ŠøŠ½Ńƒ не знайГено" +retry = "ŠŸŠ¾Š²Ń‚Š¾Ń€ŠøŃ‚Šø" +unreachable = "Š—Š°ŃŃ‚Š¾ŃŃƒŠ½Š¾Šŗ наразі не може ŠæŃ–Š“ā€™Ń”Š“Š½Š°Ń‚ŠøŃŃ Го серверної частини. ŠŸŠµŃ€ŠµŠ²Ń–Ń€Ń‚Šµ стан серверної частини та мережеве Š·ā€™Ń”Š“Š½Š°Š½Š½Ń, потім ŃŠæŃ€Š¾Š±ŃƒŠ¹Ń‚Šµ ще раз." + [zipWarning] title = "Великий ZIP-файл" message = "Цей ZIP Š¼Ń–ŃŃ‚ŠøŃ‚ŃŒ {{count}} файлів. Š Š¾Š·ŠæŠ°ŠŗŃƒŠ²Š°Ń‚Šø попри це?" @@ -912,6 +917,9 @@ desc = "Š”Ń‚Š²Š¾Ń€ŃŽŠ¹Ń‚Šµ багатокрокові робочі процес desc = "ŠŠ°ŠŗŠ»Š°Š“ŠµŠ½Š½Ń оГного PDF поверх Ń–Š½ŃˆŠ¾Š³Š¾ PDF" title = "ŠŠ°ŠŗŠ»Š°Š“ŠµŠ½Š½Ń PDF" +[home.pdfTextEditor] +title = "РеГактор Ń‚ŠµŠŗŃŃ‚Ńƒ PDF" +desc = "Š ŠµŠ“Š°Š³ŃƒŠ¹Ń‚Šµ Š½Š°ŃŠ²Š½ŠøŠ¹ текст і Š·Š¾Š±Ń€Š°Š¶ŠµŠ½Š½Ń у PDF" [home.addText] tags = "текст,Š°Š½Š¾Ń‚Š°Ń†Ń–Ń,мітка" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "ŠŠ°Š¼Š°Š»ŃŒŠ¾Š²Š°Š½ŠøŠ¹ піГпис" defaultImageLabel = "Завантажений піГпис" defaultTextLabel = "ŠŠ°Š±Ń€Š°Š½ŠøŠ¹ піГпис" saveButton = "Зберегти піГпис" +savePersonal = "Зберегти ŃŠŗ особистий" +saveShared = "Зберегти ŃŠŗ ŃŠæŃ–Š»ŃŒŠ½ŠøŠ¹" saveUnavailable = "Š”ŠæŠ¾Ń‡Š°Ń‚ŠŗŃƒ ŃŃ‚Š²Š¾Ń€Ń–Ń‚ŃŒ піГпис, щоб зберегти його." noChanges = "ŠŸŠ¾Ń‚Š¾Ń‡Š½ŠøŠ¹ піГпис вже збережено." +tempStorageTitle = "Тимчасове сховище Š±Ń€Š°ŃƒŠ·ŠµŃ€Š°" +tempStorageDescription = "ŠŸŃ–Š“ŠæŠøŃŠø Š·Š±ŠµŃ€Ń–Š³Š°ŃŽŃ‚ŃŒŃŃ лише у вашому Š±Ń€Š°ŃƒŠ·ŠµŃ€Ń–. Вони Š±ŃƒŠ“ŃƒŃ‚ŃŒ втрачені, ŃŠŗŃ‰Š¾ ви очистите Гані Š±Ń€Š°ŃƒŠ·ŠµŃ€Š° або зміните Š±Ń€Š°ŃƒŠ·ŠµŃ€." +personalHeading = "ŠžŃŠ¾Š±ŠøŃŃ‚Ń– піГписи" +sharedHeading = "Š”ŠæŃ–Š»ŃŒŠ½Ń– піГписи" +personalDescription = "Š›ŠøŃˆŠµ ви можете бачити ці піГписи." +sharedDescription = "Усі ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Ń– Š¼Š¾Š¶ŃƒŃ‚ŃŒ бачити та Š²ŠøŠŗŠ¾Ń€ŠøŃŃ‚Š¾Š²ŃƒŠ²Š°Ń‚Šø ці піГписи." [sign.saved.type] canvas = "ŠœŠ°Š»ŃŽŠ²Š°Š½Š½Ń" @@ -3020,6 +3036,91 @@ title = "ŠžŃ‚Ń€ŠøŠ¼Š°Ń‚Šø Ń–Š½Ń„Š¾Ń€Š¼Š°Ń†Ń–ŃŽ в PDF" header = "ŠžŃ‚Ń€ŠøŠ¼Š°Ń‚Šø Ń–Š½Ń„Š¾Ń€Š¼Š°Ń†Ń–ŃŽ в PDF" submit = "ŠžŃ‚Ń€ŠøŠ¼Š°Ń‚Šø Ń–Š½Ń„Š¾Ń€Š¼Š°Ń†Ń–ŃŽ" downloadJson = "Завантажити JSON" +processing = "Š’ŠøŠ»ŃƒŃ‡ŠµŠ½Š½Ń інформації..." +results = "Š ŠµŠ·ŃƒŠ»ŃŒŃ‚Š°Ń‚Šø" +noResults = "Š—Š°ŠæŃƒŃŃ‚Ń–Ń‚ŃŒ Ń–Š½ŃŃ‚Ń€ŃƒŠ¼ŠµŠ½Ń‚, щоб ŃŃ„Š¾Ń€Š¼ŃƒŠ²Š°Ń‚Šø звіт." +downloads = "Š—Š°Š²Š°Š½Ń‚Š°Š¶ŠµŠ½Š½Ń" +noneDetected = "ŠŃ–Ń‡Š¾Š³Š¾ не Š²ŠøŃŠ²Š»ŠµŠ½Š¾" +indexTitle = "ŠŸŠ¾ŠŗŠ°Š¶Ń‡ŠøŠŗ" + +[getPdfInfo.report] +entryLabel = "Повне Š·Š²ŠµŠ“ŠµŠ½Š½Ń інформації" +shortTitle = "Š†Š½Ń„Š¾Ń€Š¼Š°Ń†Ń–Ń про PDF" + +[getPdfInfo.sections] +metadata = "ŠœŠµŃ‚Š°Š“Š°Š½Ń–" +formFields = "ŠŸŠ¾Š»Ń форми" +basicInfo = "Базова Ń–Š½Ń„Š¾Ń€Š¼Š°Ń†Ń–Ń" +documentInfo = "Š†Š½Ń„Š¾Ń€Š¼Š°Ń†Ń–Ń про Š“Š¾ŠŗŃƒŠ¼ŠµŠ½Ń‚" +compliance = "Š’Ń–Š“ŠæŠ¾Š²Ń–Š“Š½Ń–ŃŃ‚ŃŒ" +encryption = "ŠØŠøŃ„Ń€ŃƒŠ²Š°Š½Š½Ń" +permissions = "Дозволи" +other = "Š†Š½ŃˆŠµ" +perPageInfo = "Š†Š½Ń„Š¾Ń€Š¼Š°Ń†Ń–Ń по сторінках" +tableOfContents = "Зміст" + +[getPdfInfo.other] +attachments = "Š’ŠŗŠ»Š°Š“ŠµŠ½Š½Ń" +embeddedFiles = "Š’Š±ŃƒŠ“Š¾Š²Š°Š½Ń– файли" +javaScript = "JavaScript" +layers = "Шари" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "Розмір" +annotations = "Анотації" +images = "Š—Š¾Š±Ń€Š°Š¶ŠµŠ½Š½Ń" +links = "ŠŸŠ¾ŃŠøŠ»Š°Š½Š½Ń" +fonts = "Шрифти" +xobjects = "ŠšŃ–Š»ŃŒŠŗŃ–ŃŃ‚ŃŒ XObject" +multimedia = "ŠœŃƒŠ»ŃŒŃ‚ŠøŠ¼ŠµŠ“Ń–Š°" + +[getPdfInfo.summary] +pages = "Дторінки" +fileSize = "Розмір Ń„Š°Š¹Š»Ńƒ" +pdfVersion = "Š’ŠµŃ€ŃŃ–Ń PDF" +language = "Мова" +title = "Š—Š²ŠµŠ“ŠµŠ½Š½Ń PDF" +author = "Автор" +created = "Дтворено" +modified = "Змінено" +permsAll = "Усі Гозволи наГано" +permsRestricted = "{{count}} обмежень" +permsMixed = "Š”ŠµŃŠŗŃ– Гозволи обмежено" +hasCompliance = "ВіГповіГає станГартам віГповіГності" +noCompliance = "ŠŠµŠ¼Š°Ń” станГартів віГповіГності" +basic = "ŠžŃŠ½Š¾Š²Š½Š° Ń–Š½Ń„Š¾Ń€Š¼Š°Ń†Ń–Ń" +documentInfo = "Š†Š½Ń„Š¾Ń€Š¼Š°Ń†Ń–Ń про Š“Š¾ŠŗŃƒŠ¼ŠµŠ½Ń‚" +securityTitle = "Дтан безпеки" +technical = "Технічна Ń–Š½Ń„Š¾Ń€Š¼Š°Ń†Ń–Ń" +overviewTitle = "ŠžŠ³Š»ŃŠ“ PDF" + +[getPdfInfo.summary.security] +encrypted = "PDF Š·Š°ŃˆŠøŃ„Ń€Š¾Š²Š°Š½Š¾ — встановлено захист паролем" +unencrypted = "PDF не Š·Š°ŃˆŠøŃ„ровано — немає Š·Š°Ń…ŠøŃŃ‚Ńƒ паролем" + +[getPdfInfo.summary.tech] +images = "Š—Š¾Š±Ń€Š°Š¶ŠµŠ½Š½Ń" +fonts = "Шрифти" +formFields = "ŠŸŠ¾Š»Ń форми" +embeddedFiles = "Š’Š±ŃƒŠ“Š¾Š²Š°Š½Ń– файли" +javaScript = "JavaScript" +layers = "Шари" +bookmarks = "ЗаклаГки" +multimedia = "ŠœŃƒŠ»ŃŒŃ‚ŠøŠ¼ŠµŠ“Ń–Š°" + +[getPdfInfo.summary.overview] +untitled = "Š“Š¾ŠŗŃƒŠ¼ŠµŠ½Ń‚ без назви" +unknown = "ŠŠµŠ²Ń–Š“Š¾Š¼ŠøŠ¹ автор" +text = "Це PDF на {{pages}} сторінок піГ Š½Š°Š·Š²Š¾ŃŽ {{title}}, створений {{author}} (Š²ŠµŃ€ŃŃ–Ń PDF {{version}})." + +[getPdfInfo.error] +partial = "Š”ŠµŃŠŗŃ– файли не Š²Š“Š°Š»Š¾ŃŃ обробити." +unexpected = "ŠŠµŠ¾Ń‡Ń–ŠŗŃƒŠ²Š°Š½Š° помилка піГ час Š²ŠøŠ»ŃƒŃ‡ŠµŠ½Š½Ń." + +[getPdfInfo.status] +complete = "Š’ŠøŠ»ŃƒŃ‡ŠµŠ½Š½Ń Š·Š°Š²ŠµŃ€ŃˆŠµŠ½Š¾" [extractPage] tags = "екстракт" @@ -3438,6 +3539,9 @@ signinTitle = "Š‘ŃƒŠ“ŃŒ ласка, ŃƒŠ²Ń–Š¹Š“Ń–Ń‚ŃŒ" ssoSignIn = "Увійти через єГиний вхіГ" oAuth2AutoCreateDisabled = "Автоматичне ŃŃ‚Š²Š¾Ń€ŠµŠ½Š½Ń ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Š° OAUTH2 Š’Š˜ŠœŠšŠŠ•ŠŠž" oAuth2AdminBlockedUser = "Š ŠµŃ”ŃŃ‚Ń€Š°Ń†Ń–Ń або вхіГ незареєстрованих ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Ń–Š² наразі заборонено. Š‘ŃƒŠ“ŃŒ ласка, зв'ŃŠ¶Ń–Ń‚ŃŒŃŃ Š· аГміністратором." +oAuth2RequiresLicense = "Š’Ń…Ń–Š“ через OAuth/SSO ŠæŠ¾Ń‚Ń€ŠµŠ±ŃƒŃ” платної ліцензії (Server або Enterprise). Š—Š²ŠµŃ€Š½Ń–Ń‚ŃŒŃŃ Го аГміністратора, щоб оновити ваш план." +saml2RequiresLicense = "Š’Ń…Ń–Š“ через SAML ŠæŠ¾Ń‚Ń€ŠµŠ±ŃƒŃ” платної ліцензії (Server або Enterprise). Š—Š²ŠµŃ€Š½Ń–Ń‚ŃŒŃŃ Го аГміністратора, щоб оновити ваш план." +maxUsersReached = "Š”Š¾ŃŃŠ³Š½ŃƒŃ‚Š¾ Š¼Š°ŠŗŃŠøŠ¼Š°Š»ŃŒŠ½Š¾Ń— ŠŗŃ–Š»ŃŒŠŗŠ¾ŃŃ‚Ń– ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Ń–Š² Š“Š»Ń Š²Š°ŃˆŠ¾Ń— поточної ліцензії. Š—Š²ŠµŃ€Š½Ń–Ń‚ŃŒŃŃ Го аГміністратора, щоб оновити план або ГоГати Š¼Ń–ŃŃ†Ń." oauth2RequestNotFound = "Запит на Š°Š²Ń‚Š¾Ń€ŠøŠ·Š°Ń†Ń–Ń не знайГено" oauth2InvalidUserInfoResponse = "ŠŠµŠ“Ń–Š¹ŃŠ½Š° Š²Ń–Š“ŠæŠ¾Š²Ń–Š“ŃŒ Š· Ń–Š½Ń„Š¾Ń€Š¼Š°Ń†Ń–Ń”ŃŽ ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Š°" oauth2invalidRequest = "ŠŠµŠ“Ń–Š¹ŃŠ½ŠøŠ¹ запит" @@ -3771,7 +3875,7 @@ version = "Š¢ŠµŠŗŃƒŃ‰ŠøŠ¹ релиз" title = "Š”Š¾ŠŗŃƒŠ¼ŠµŠ½Ń‚Š°Ń†Ń–Ń API" header = "Š”Š¾ŠŗŃƒŠ¼ŠµŠ½Ń‚Š°Ń†Ń–Ń API" desc = "ŠŸŠµŃ€ŠµŠ³Š»ŃŠ“Š°Š¹Ń‚Šµ та Ń‚ŠµŃŃ‚ŃƒŠ¹Ń‚Šµ кінцеві точки API Stirling PDF" -tags = "api,documentation,swagger,endpoints,development" +tags = "api,Š“Š¾ŠŗŃƒŠ¼ŠµŠ½Ń‚Š°Ń†Ń–Ń,swagger,кінцеві точки,розробка" [cookieBanner.popUp] title = "ŠÆŠŗ ми Š²ŠøŠŗŠ¾Ń€ŠøŃŃ‚Š¾Š²ŃƒŃ”Š¼Š¾ файли cookie" @@ -3846,14 +3950,17 @@ fitToWidth = "ŠŸŃ–Š“Ń–Š³Š½Š°Ń‚Šø за ŃˆŠøŃ€ŠøŠ½Š¾ŃŽ" actualSize = "Фактичний розмір" [viewer] +cannotPreviewFile = "ŠŠµ Š²Š“Š°Ń”Ń‚ŃŒŃŃ ŠæŠµŃ€ŠµŠ³Š»ŃŠ½ŃƒŃ‚Šø файл" +dualPageView = "ŠŸŠ°Ń€Š½ŠøŠ¹ ŠæŠµŃ€ŠµŠ³Š»ŃŠ“" firstPage = "ŠŸŠµŃ€ŃˆŠ° сторінка" lastPage = "ŠžŃŃ‚Š°Š½Š½Ń сторінка" -previousPage = "ŠŸŠ¾ŠæŠµŃ€ŠµŠ“Š½Ń сторінка" nextPage = "ŠŠ°ŃŃ‚ŃƒŠæŠ½Š° сторінка" +onlyPdfSupported = "ŠŸŠµŃ€ŠµŠ³Š»ŃŠ“Š°Ń‡ ŠæŃ–Š“Ń‚Ń€ŠøŠ¼ŃƒŃ” лише файли PDF. Дхоже, цей файл має Ń–Š½ŃˆŠøŠ¹ формат." +previousPage = "ŠŸŠ¾ŠæŠµŃ€ŠµŠ“Š½Ń сторінка" +singlePageView = "ŠžŠ“ŠøŠ½Š°Ń€Š½ŠøŠ¹ ŠæŠµŃ€ŠµŠ³Š»ŃŠ“" +unknownFile = "ŠŠµŠ²Ń–Š“Š¾Š¼ŠøŠ¹ файл" zoomIn = "Š—Š±Ń–Š»ŃŒŃˆŠøŃ‚Šø" zoomOut = "Š—Š¼ŠµŠ½ŃˆŠøŃ‚Šø" -singlePageView = "ŠžŠ“ŠøŠ½Š°Ń€Š½ŠøŠ¹ ŠæŠµŃ€ŠµŠ³Š»ŃŠ“" -dualPageView = "ŠŸŠ°Ń€Š½ŠøŠ¹ ŠæŠµŃ€ŠµŠ³Š»ŃŠ“" [rightRail] closeSelected = "Закрити вибрані файли" @@ -3877,6 +3984,7 @@ toggleSidebar = "ŠŸŠµŃ€ŠµŠ¼ŠŗŠ½ŃƒŃ‚Šø Š±Ń–Ń‡Š½Ńƒ панель" exportSelected = "Експорт вибраних сторінок" toggleAnnotations = "ŠŸŠµŃ€ŠµŠ¼ŠŗŠ½ŃƒŃ‚Šø Š²ŠøŠ“ŠøŠ¼Ń–ŃŃ‚ŃŒ анотацій" annotationMode = "ŠŸŠµŃ€ŠµŠ¼ŠŗŠ½ŃƒŃ‚Šø режим анотацій" +print = "ŠŠ°Š“Ń€ŃƒŠŗŃƒŠ²Š°Ń‚Šø PDF" draw = "ŠœŠ°Š»ŃŽŠ²Š°Ń‚Šø" save = "Зберегти" saveChanges = "Зберегти зміни" @@ -4153,7 +4261,7 @@ description = "Š’Ń–Š“ŃŃ‚ŠµŠ¶ŃƒŠ²Š°Ń‚Šø Гії ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Ń–Š² і си [admin.settings.security.audit.level] label = "Š Ń–Š²ŠµŠ½ŃŒ Š°ŃƒŠ“ŠøŃ‚Ńƒ" -description = "0=OFF, 1=BASIC, 2=STANDARD, 3=VERBOSE" +description = "0=Š’Š˜ŠœŠšŠŠ•ŠŠž, 1=Š‘ŠŠ—ŠžŠ’Š˜Š™, 2=Š”Š¢ŠŠŠ”ŠŠ Š¢, 3=Š”ŠžŠšŠ›ŠŠ”ŠŠ˜Š™" [admin.settings.security.audit.retentionDays] label = "Š—Š±ŠµŃ€Ń–Š³Š°Š½Š½Ń Š°ŃƒŠ“ŠøŃ‚Ńƒ (Гні)" @@ -4407,7 +4515,7 @@ description = "Чи очищати ŃˆŠøŃ€ŃˆŠøŠ¹ системний тимчас label = "ŠžŠ±Š¼ŠµŠ¶ŠµŠ½Š½Ń Š²ŠøŠŗŠ¾Š½Š°Š²Ń†Ń процесів" description = "ŠŠ°Š»Š°ŃˆŃ‚ŃƒŠ¹Ń‚Šµ ліміти сеансів і тайм-Š°ŃƒŃ‚Šø Š“Š»Ń кожного Š²ŠøŠŗŠ¾Š½Š°Š²Ń†Ń процесів" libreOffice = "LibreOffice" -pdfToHtml = "PDF to HTML" +pdfToHtml = "PDF у HTML" qpdf = "QPDF" tesseract = "Tesseract OCR" pythonOpenCv = "Python OpenCV" @@ -4494,6 +4602,7 @@ description = "URL або назва Ń„Š°Š¹Š»Ńƒ Го Ń–Š¼ŠæŃ€ŠµŃŃƒŠ¼Ńƒ (пот title = "ŠŸŃ€ŠµŠ¼Ń–ŃƒŠ¼ і Enterprise" description = "ŠŠ°Š»Š°ŃˆŃ‚ŃƒŠ¹Ń‚Šµ свій ŠæŃ€ŠµŠ¼Ń–ŃƒŠ¼ або корпоративний ліцензійний ŠŗŠ»ŃŽŃ‡." license = "ŠšŠ¾Š½Ń„Ń–Š³ŃƒŃ€Š°Ń†Ń–Ń ліцензії" +noInput = "ŠŠ°Š“Š°Š¹Ń‚Šµ ліцензійний ŠŗŠ»ŃŽŃ‡ або файл" [admin.settings.premium.licenseKey] toggle = "Š„ ліцензійний ŠŗŠ»ŃŽŃ‡ або файл сертифіката?" @@ -4511,6 +4620,25 @@ line1 = "ŠŸŠµŃ€ŠµŠ·Š°ŠæŠøŃ поточного ліцензійного ŠŗŠ»ŃŽŃ‡ line2 = "ŠŸŠ¾ŠæŠµŃ€ŠµŠ“Š½Ń Š»Ń–Ń†ŠµŠ½Š·Ń–Ń буГе втрачена назавжГи, ŃŠŗŃ‰Š¾ ви не маєте її резервної копії." line3 = "Важливо: зберігайте ліцензійні ŠŗŠ»ŃŽŃ‡Ń– приватними та безпечними. ŠŃ–ŠŗŠ¾Š»Šø не ŠæŃƒŠ±Š»Ń–ŠŗŃƒŠ¹Ń‚Šµ їх." +[admin.settings.premium.inputMethod] +text = "Ліцензійний ŠŗŠ»ŃŽŃ‡" +file = "Файл сертифіката" + +[admin.settings.premium.file] +label = "Файл ліцензійного сертифіката" +description = "Завантажте свій ліцензійний файл .lic або .cert Š· офлайн-покупок" +choose = "Š’ŠøŠ±ŠµŃ€Ń–Ń‚ŃŒ ліцензійний файл" +selected = "Вибрано: {{filename}} ({{size}})" +successMessage = "Ліцензійний файл ŃƒŃŠæŃ–ŃˆŠ½Š¾ завантажено й активовано. ŠŸŠµŃ€ŠµŠ·Š°ŠæŃƒŃŠŗ не потрібен." + +[admin.settings.premium.currentLicense] +title = "Активна Š»Ń–Ń†ŠµŠ½Š·Ń–Ń" +file = "Джерело: ліцензійний файл ({{path}})" +key = "Джерело: ліцензійний ŠŗŠ»ŃŽŃ‡" +type = "Тип: {{type}}" +noInput = "ŠŠ°Š“Š°Š¹Ń‚Šµ ліцензійний ŠŗŠ»ŃŽŃ‡ або завантажте файл сертифіката" +success = "Š£ŃŠæŃ–ŃˆŠ½Š¾" + [admin.settings.premium.enabled] label = "Š£Š²Ń–Š¼ŠŗŠ½ŃƒŃ‚Šø ŠæŃ€ŠµŠ¼Ń–ŃƒŠ¼-Ń„ŃƒŠ½ŠŗŃ†Ń–Ń—" description = "Š£Š²Ń–Š¼ŠŗŠ½ŃƒŃ‚Šø ŠæŠµŃ€ŠµŠ²Ń–Ń€ŠŗŃƒ ліцензійного ŠŗŠ»ŃŽŃ‡Š° Š“Š»Ń pro/enterprise Ń„ŃƒŠ½ŠŗŃ†Ń–Š¹" @@ -4644,7 +4772,9 @@ selectedCount = "Вибрано {{count}}" download = "Завантажити" delete = "ВиГалити" unsupported = "ŠŠµŠæŃ–Š“Ń‚Ń€ŠøŠ¼ŃƒŠ²Š°Š½ŠøŠ¹" +active = "Активний" addToUpload = "ДоГати Го Š·Š°Š²Š°Š½Ń‚Š°Š¶ŠµŠ½Š½Ń" +closeFile = "Закрити файл" deleteAll = "ВиГалити все" loadingFiles = "Š—Š°Š²Š°Š½Ń‚Š°Š¶ŠµŠ½Š½Ń файлів..." noFiles = "ŠŠµŠ¼Š°Ń” Š“Š¾ŃŃ‚ŃƒŠæŠ½ŠøŃ… файлів" @@ -5132,7 +5262,7 @@ upgrade = "ŠžŠ½Š¾Š²ŠøŃ‚Šø зараз →" freeTitle = "Š›Ń–Ń†ŠµŠ½Š·Ń–Ń сервера" overLimitTitle = "ŠŸŠ¾Ń‚Ń€Ń–Š±Š½Š° Š»Ń–Ń†ŠµŠ½Š·Ń–Ń сервера" overLimitBody = "ŠŠ°ŃˆŠ° Š»Ń–Ń†ŠµŠ½Š·Ń–Ń Š“Š¾Š·Š²Š¾Š»ŃŃ” Го {{freeTierLimit}} ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Ń–Š² Š±ŠµŠ·ŠŗŠ¾ŃˆŃ‚Š¾Š²Š½Š¾ на сервер. Š£ вас {{overLimitUserCopy}} ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Ń–Š² Stirling. Щоб ŠæŃ€Š°Ń†ŃŽŠ²Š°Ń‚Šø без перерв, ŠæŠµŃ€ŠµŠ¹Š“Ń–Ń‚ŃŒ на план Stirling Server — необмежена ŠŗŃ–Š»ŃŒŠŗŃ–ŃŃ‚ŃŒ Š¼Ń–ŃŃ†ŃŒ, Ń€ŠµŠ“Š°Š³ŃƒŠ²Š°Š½Š½Ń Ń‚ŠµŠŗŃŃ‚Ńƒ PDF та повний аГмін-ŠŗŠ¾Š½Ń‚Ń€Š¾Š»ŃŒ за $99/server/mo." -freeBody = "ŠŠ°ŃˆŠ° Open-Core Š»Ń–Ń†ŠµŠ½Š·Ń–Ń Š“Š¾Š·Š²Š¾Š»ŃŃ” Го {{freeTierLimit}} ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Ń–Š² Š±ŠµŠ·ŠŗŠ¾ŃˆŃ‚Š¾Š²Š½Š¾ на сервер. Щоб Š¼Š°ŃŃˆŃ‚Š°Š±ŃƒŠ²Š°Ń‚ŠøŃŃ безперервно та отримати ранній Š“Š¾ŃŃ‚ŃƒŠæ Го нового Ń–Š½ŃŃ‚Ń€ŃƒŠ¼ŠµŠ½Ń‚Š° Ń€ŠµŠ“Š°Š³ŃƒŠ²Š°Š½Š½Ń Ń‚ŠµŠŗŃŃ‚Ńƒ PDF, Ń€ŠµŠŗŠ¾Š¼ŠµŠ½Š“ŃƒŃ”Š¼Š¾ план Stirling Server — повне Ń€ŠµŠ“Š°Š³ŃƒŠ²Š°Š½Š½Ń та необмежена ŠŗŃ–Š»ŃŒŠŗŃ–ŃŃ‚ŃŒ Š¼Ń–ŃŃ†ŃŒ за $99/server/mo." +freeBody = "ŠŠ°ŃˆŠ° Š»Ń–Ń†ŠµŠ½Š·Ń–Ń Open-Core Š“Š¾Š·Š²Š¾Š»ŃŃ” Го {{freeTierLimit}} ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Ń–Š² Š±ŠµŠ·ŠŗŠ¾ŃˆŃ‚Š¾Š²Š½Š¾ на сервер. Щоб Š¼Š°ŃŃˆŃ‚Š°Š±ŃƒŠ²Š°Ń‚ŠøŃŃ без перерв, Ń€ŠµŠŗŠ¾Š¼ŠµŠ½Š“ŃƒŃ”Š¼Š¾ план Stirling Server — необмежена ŠŗŃ–Š»ŃŒŠŗŃ–ŃŃ‚ŃŒ Š¼Ń–ŃŃ†ŃŒ і піГтримка SSO за $99/сервер/міс." [onboarding.desktopInstall] title = "Завантажити" @@ -5237,6 +5367,31 @@ error = "ŠŠµ Š²Š“Š°Š»Š¾ŃŃ оновити ŃŃ‚Š°Ń‚ŃƒŃ ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Š°" success = "ŠšŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Š° ŃƒŃŠæŃ–ŃˆŠ½Š¾ виГалено" error = "ŠŠµ Š²Š“Š°Š»Š¾ŃŃ виГалити ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Š°" +[workspace.people.changePassword] +action = "Змінити ŠæŠ°Ń€Š¾Š»ŃŒ" +title = "Змінити ŠæŠ°Ń€Š¾Š»ŃŒ" +subtitle = "ŠžŠ½Š¾Š²ŠøŃ‚Šø ŠæŠ°Ń€Š¾Š»ŃŒ Š“Š»Ń" +newPassword = "ŠŠ¾Š²ŠøŠ¹ ŠæŠ°Ń€Š¾Š»ŃŒ" +confirmPassword = "ŠŸŃ–Š“Ń‚Š²ŠµŃ€Š“Š¶ŠµŠ½Š½Ń ŠæŠ°Ń€Š¾Š»Ń" +placeholder = "Š’Š²ŠµŠ“Ń–Ń‚ŃŒ новий ŠæŠ°Ń€Š¾Š»ŃŒ" +confirmPlaceholder = "ŠŸŠ¾Š²Ń‚Š¾Ń€Š½Š¾ Š²Š²ŠµŠ“Ń–Ń‚ŃŒ новий ŠæŠ°Ń€Š¾Š»ŃŒ" +passwordRequired = "Š‘ŃƒŠ“ŃŒ ласка, Š²Š²ŠµŠ“Ń–Ń‚ŃŒ новий ŠæŠ°Ń€Š¾Š»ŃŒ" +passwordMismatch = "ŠŸŠ°Ń€Š¾Š»Ń– не Š·Š±Ń–Š³Š°ŃŽŃ‚ŃŒŃŃ" +generateRandom = "Š—Š³ŠµŠ½ŠµŃ€ŃƒŠ²Š°Ń‚Šø наГійний ŠæŠ°Ń€Š¾Š»ŃŒ" +generatedPreview = "Згенерований ŠæŠ°Ń€Š¾Š»ŃŒ:" +copyTooltip = "ŠšŠ¾ŠæŃ–ŃŽŠ²Š°Ń‚Šø в Š±ŃƒŃ„ер Š¾Š±Š¼Ń–Š½Ńƒ" +copiedToClipboard = "ŠŸŠ°Ń€Š¾Š»ŃŒ скопійовано в Š±ŃƒŃ„ер Š¾Š±Š¼Ń–Š½Ńƒ" +copyFailed = "ŠŠµ Š²Š“Š°Š»Š¾ŃŃ ŃŠŗŠ¾ŠæŃ–ŃŽŠ²Š°Ń‚Šø ŠæŠ°Ń€Š¾Š»ŃŒ" +sendEmail = "ŠŠ°Š“Ń–ŃŠ»Š°Ń‚Šø ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Ńƒ електронний лист про цю Š·Š¼Ń–Š½Ńƒ" +includePassword = "ДоГати новий ŠæŠ°Ń€Š¾Š»ŃŒ Го листа" +forcePasswordChange = "ŠŸŃ€ŠøŠ¼ŃƒŃŠøŃ‚Šø ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Š° змінити ŠæŠ°Ń€Š¾Š»ŃŒ піГ час Š½Š°ŃŃ‚ŃƒŠæŠ½Š¾Š³Š¾ Š²Ń…Š¾Š“Ńƒ" +emailUnavailable = "Електронна аГреса Ń†ŃŒŠ¾Š³Š¾ ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Š° неГійсна. Š”ŠæŠ¾Š²Ń–Ń‰ŠµŠ½Š½Ń вимкнено." +smtpDisabled = "Š”Š»Ń ŃŠæŠ¾Š²Ń–Ń‰ŠµŠ½ŃŒ ŠµŠ»ŠµŠŗŃ‚Ń€Š¾Š½Š½Š¾ŃŽ ŠæŠ¾ŃˆŃ‚Š¾ŃŽ потрібно Š²Š²Ń–Š¼ŠŗŠ½ŃƒŃ‚Šø SMTP у Š½Š°Š»Š°ŃˆŃ‚ŃƒŠ²Š°Š½Š½ŃŃ…." +notifyOnly = "Š‘ŃƒŠ“Šµ наГіслано лист без ŠæŠ°Ń€Š¾Š»Ń, щоб повіГомити ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Š°, що аГміністратор його змінив." +submit = "ŠžŠ½Š¾Š²ŠøŃ‚Šø ŠæŠ°Ń€Š¾Š»ŃŒ" +success = "ŠŸŠ°Ń€Š¾Š»ŃŒ ŃƒŃŠæŃ–ŃˆŠ½Š¾ оновлено" +error = "ŠŠµ Š²Š“Š°Š»Š¾ŃŃ оновити ŠæŠ°Ń€Š¾Š»ŃŒ" + [workspace.people.emailInvite] tab = "Š—Š°ŠæŃ€Š¾ŃˆŠµŠ½Š½Ń ŠµŠ»ŠµŠŗŃ‚Ń€Š¾Š½Š½Š¾ŃŽ ŠæŠ¾ŃˆŃ‚Š¾ŃŽ" description = "Š’Š²ŠµŠ“Ń–Ń‚ŃŒ або вставте аГреси нижче, розГілені комами. ŠšŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Ń– Š¾Ń‚Ń€ŠøŠ¼Š°ŃŽŃ‚ŃŒ Гані Š“Š»Ń Š²Ń…Š¾Š“Ńƒ ŠµŠ»ŠµŠŗŃ‚Ń€Š¾Š½Š½Š¾ŃŽ ŠæŠ¾ŃˆŃ‚Š¾ŃŽ." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "ŠŸŠ¾Ń‚Ń€Ń–Š±Š½Š° Ń‰Š¾Š½Š°Š¹Š¼ŠµŠ½ŃˆŠµ оГна електронна аГреса" submit = "ŠŠ°Š“Ń–ŃŠ»Š°Ń‚Šø Š·Š°ŠæŃ€Š¾ŃˆŠµŠ½Š½Ń" success = "ŠšŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Š°(ів) ŃƒŃŠæŃ–ŃˆŠ½Š¾ Š·Š°ŠæŃ€Š¾ŃˆŠµŠ½Š¾" -partialSuccess = "Š”ŠµŃŠŗŃ– Š·Š°ŠæŃ€Š¾ŃˆŠµŠ½Š½Ń не Š²Š“Š°Š»Š¾ŃŃ наГіслати" +partialFailure = "Š”ŠµŃŠŗŃ– Š·Š°ŠæŃ€Š¾ŃˆŠµŠ½Š½Ń не Š²Š“Š°Š»Š¾ŃŃ наГіслати" allFailed = "ŠŠµ Š²Š“Š°Š»Š¾ŃŃ запросити ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Ń–Š²" error = "ŠŠµ Š²Š“Š°Š»Š¾ŃŃ наГіслати Š·Š°ŠæŃ€Š¾ŃˆŠµŠ½Š½Ń" @@ -5281,7 +5436,7 @@ submit = "Š—Š³ŠµŠ½ŠµŃ€ŃƒŠ²Š°Ń‚Šø ŠæŠ¾ŃŠøŠ»Š°Š½Š½Ń-Š·Š°ŠæŃ€Š¾ŃˆŠµŠ½Š½Ń" [workspace.people.inviteMode] username = "Š†Š¼ā€™Ń ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Š°" -email = "Email" +email = "Електронна ŠæŠ¾ŃˆŃ‚Š°" link = "ŠŸŠ¾ŃŠøŠ»Š°Š½Š½Ń" emailDisabled = "Š—Š°ŠæŃ€Š¾ŃˆŠµŠ½Š½Ń ŠµŠ»ŠµŠŗŃ‚Ń€Š¾Š½Š½Š¾ŃŽ ŠæŠ¾ŃˆŃ‚Š¾ŃŽ ŠæŠ¾Ń‚Ń€ŠµŠ±ŃƒŃŽŃ‚ŃŒ Š½Š°Š»Š°ŃˆŃ‚ŃƒŠ²Š°Š½Š½Ń SMTP і mail.enableInvites=true в Š½Š°Š»Š°ŃˆŃ‚ŃƒŠ²Š°Š½Š½ŃŃ…" @@ -5770,6 +5925,7 @@ subtitle = "Š£Š²Ń–Š¹Š“Ń–Ń‚ŃŒ через обліковий запис Stirling" [setup.selfhosted] title = "Š£Š²Ń–Š¹Š“Ń–Ń‚ŃŒ на сервер" subtitle = "Š’Š²ŠµŠ“Ń–Ń‚ŃŒ облікові Гані сервера" +link = "або ŠæŃ–Š“ŠŗŠ»ŃŽŃ‡Ń–Ń‚ŃŒŃŃ Го самохостингового облікового запису" [setup.server] title = "ŠŸŃ–Š“ā€™Ń”Š“Š½Š°Ń‚ŠøŃŃ Го сервера" @@ -5788,6 +5944,14 @@ description = "Š’Š²ŠµŠ“Ń–Ń‚ŃŒ повну URL-Š°Š“Ń€ŠµŃŃƒ вашого само emptyUrl = "Š’Š²ŠµŠ“Ń–Ń‚ŃŒ URL сервера" unreachable = "ŠŠµ Š²Š“Š°Š»Š¾ŃŃ ŠæŃ–Š“ā€™Ń”Š“Š½Š°Ń‚ŠøŃŃ Го сервера" testFailed = "Тест ŠæŃ–Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½Š½Ń не Š²Š“Š°Š²ŃŃ" +configFetch = "ŠŠµ Š²Š“Š°Š»Š¾ŃŃ отримати ŠŗŠ¾Š½Ń„Ń–Š³ŃƒŃ€Š°Ń†Ń–ŃŽ сервера. ŠŸŠµŃ€ŠµŠ²Ń–Ń€Ń‚Šµ URL і ŃŠæŃ€Š¾Š±ŃƒŠ¹Ń‚Šµ ще раз." + +[setup.server.error.securityDisabled] +title = "Š’Ń…Ń–Š“ не ввімкнено" +body = "ŠŠ° Ń†ŃŒŠ¾Š¼Ńƒ сервері не ввімкнено вхіГ. Щоб ŠæŃ–Š“ŠŗŠ»ŃŽŃ‡ŠøŃ‚ŠøŃŃ Го Ń†ŃŒŠ¾Š³Š¾ сервера, потрібно Š²Š²Ń–Š¼ŠŗŠ½ŃƒŃ‚Šø Š°Š²Ń‚ŠµŠ½Ń‚ŠøŃ„Ń–ŠŗŠ°Ń†Ń–ŃŽ:" +step1 = "Š’ŃŃ‚Š°Š½Š¾Š²Ń–Ń‚ŃŒ DOCKER_ENABLE_SECURITY=true у вашому сереГовищі" +step2 = "Або Š²ŃŃ‚Š°Š½Š¾Š²Ń–Ń‚ŃŒ security.enableLogin=true у settings.yml" +step3 = "ŠŸŠµŃ€ŠµŠ·Š°ŠæŃƒŃŃ‚Ń–Ń‚ŃŒ сервер" [setup.login] title = "Š’Ń…Ń–Š“" @@ -5797,13 +5961,20 @@ submit = "Увійти" signInWith = "Увійти через" oauthPending = "ВіГкриваємо Š±Ń€Š°ŃƒŠ·ŠµŃ€ Š“Š»Ń автентифікації..." orContinueWith = "Або проГовжити через email" +serverRequirement = "ŠŸŃ€ŠøŠ¼Ń–Ń‚ŠŗŠ°: на сервері має Š±ŃƒŃ‚Šø ŃƒŠ²Ń–Š¼ŠŗŠ½ŠµŠ½Š¾ вхіГ." +showInstructions = "ŠÆŠŗ ŃƒŠ²Ń–Š¼ŠŗŠ½ŃƒŃ‚Šø?" +hideInstructions = "ŠŸŃ€ŠøŃ…Š¾Š²Š°Ń‚Šø Ń–Š½ŃŃ‚Ń€ŃƒŠŗŃ†Ń–Ń—" +instructions = "Щоб ŃƒŠ²Ń–Š¼ŠŗŠ½ŃƒŃ‚Šø вхіГ на вашому сервері Stirling PDF:" +instructionsEnvVar = "ЗаГайте Š·Š¼Ń–Š½Š½Ńƒ сереГовища:" +instructionsOrYml = "Або у settings.yml:" +instructionsRestart = "ŠŸŠ¾Ń‚Ń–Š¼ ŠæŠµŃ€ŠµŠ·Š°ŠæŃƒŃŃ‚Ń–Ń‚ŃŒ сервер, щоб зміни набрали чинності." [setup.login.username] label = "Š†Š¼ā€™Ń ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Š°" placeholder = "Š’Š²ŠµŠ“Ń–Ń‚ŃŒ Ń–Š¼ā€™Ń ŠŗŠ¾Ń€ŠøŃŃ‚ŃƒŠ²Š°Ń‡Š°" [setup.login.email] -label = "Email" +label = "Електронна ŠæŠ¾ŃˆŃ‚Š°" placeholder = "Š’Š²ŠµŠ“Ń–Ń‚ŃŒ свій email" [setup.login.password] @@ -5853,6 +6024,7 @@ earlyAccess = "Ранній Š“Š¾ŃŃ‚ŃƒŠæ" reset = "Š”ŠŗŠøŠ½ŃƒŃ‚Šø зміни" downloadJson = "Завантажити JSON" generatePdf = "Š—Š³ŠµŠ½ŠµŃ€ŃƒŠ²Š°Ń‚Šø PDF" +saveChanges = "Зберегти зміни" [pdfTextEditor.options.autoScaleText] title = "АвтопіГгін Ń‚ŠµŠŗŃŃ‚Ńƒ піГ рамки" @@ -5890,6 +6062,8 @@ alpha = "Цей Š°Š»ŃŒŃ„а-ŠæŠµŃ€ŠµŠ³Š»ŃŠ“Š°Ń‡ ще Ń€Š¾Š·Š²ŠøŠ²Š°Ń”Ń‚ŃŒŃŃ [pdfTextEditor.empty] title = "Š”Š¾ŠŗŃƒŠ¼ŠµŠ½Ń‚ не завантажено" subtitle = "Завантажте файл PDF або JSON, щоб почати Ń€ŠµŠ“Š°Š³ŃƒŠ²Š°Ń‚Šø текстовий вміст." +dropzone = "ŠŸŠµŃ€ŠµŃ‚ŃŠ³Š½Ń–Ń‚ŃŒ і Š²Ń–Š“ŠæŃƒŃŃ‚Ń–Ń‚ŃŒ файл PDF або JSON ŃŃŽŠ“Šø, або Š½Š°Ń‚ŠøŃŠ½Ń–Ń‚ŃŒ, щоб вибрати" +dropzoneWithFiles = "Š’ŠøŠ±ŠµŃ€Ń–Ń‚ŃŒ файл на вклаГці Файли або ŠæŠµŃ€ŠµŃ‚ŃŠ³Š½Ń–Ń‚ŃŒ і Š²Ń–Š“ŠæŃƒŃŃ‚Ń–Ń‚ŃŒ файл PDF чи JSON ŃŃŽŠ“Šø, або Š½Š°Ń‚ŠøŃŠ½Ń–Ń‚ŃŒ, щоб вибрати" [pdfTextEditor.welcomeBanner] title = "Вітаємо у РеГакторі Ń‚ŠµŠŗŃŃ‚Ńƒ PDF (ранній Š“Š¾ŃŃ‚ŃƒŠæ)" diff --git a/frontend/public/locales/vi-VN/translation.toml b/frontend/public/locales/vi-VN/translation.toml index 464407a2ac..9b21be6ef5 100644 --- a/frontend/public/locales/vi-VN/translation.toml +++ b/frontend/public/locales/vi-VN/translation.toml @@ -163,6 +163,11 @@ unfavorite = "Xóa khį»i MỄc yĆŖu thĆ­ch" fullscreen = "Chuyển sang chįŗæ độ toĆ n mĆ n hƬnh" sidebar = "Chuyển sang chįŗæ độ thanh bĆŖn" +[backendStartup] +notFoundTitle = "KhĆ“ng tƬm thįŗ„y Backend" +retry = "Thį»­ lįŗ”i" +unreachable = "Ứng dỄng hiện khĆ“ng thể kįŗæt nối tį»›i Backend. HĆ£y kiểm tra trįŗ”ng thĆ”i Backend vĆ  kįŗæt nối mįŗ”ng, sau đó thį»­ lįŗ”i." + [zipWarning] title = "Tệp ZIP lį»›n" message = "ZIP nĆ y chứa {{count}} tệp. Vįŗ«n giįŗ£i nĆ©n?" @@ -296,7 +301,7 @@ saveSettings = "Lʰu cĆ i đặt thao tĆ”c" pipelineNamePrompt = "Nhįŗ­p tĆŖn pipeline tįŗ”i đây" selectOperation = "Chį»n thao tĆ”c" addOperationButton = "ThĆŖm thao tĆ”c" -pipelineHeader = "Pipeline:" +pipelineHeader = "Chuį»—i xį»­ lý:" saveButton = "Tįŗ£i xuống" validateButton = "XĆ”c thį»±c" @@ -347,7 +352,7 @@ teams = "Nhóm" title = "Cįŗ„u hƬnh" systemSettings = "CĆ i đặt hệ thống" features = "TĆ­nh năng" -endpoints = "Endpoints" +endpoints = "Điểm cuối" database = "CĘ” sở dữ liệu" advanced = "NĆ¢ng cao" @@ -359,7 +364,7 @@ connections = "Kįŗæt nối" [settings.licensingAnalytics] title = "Giįŗ„y phĆ©p & PhĆ¢n tĆ­ch" plan = "Gói" -audit = "Audit" +audit = "Kiểm toĆ”n" usageAnalytics = "PhĆ¢n tĆ­ch sį»­ dỄng" [settings.policiesPrivacy] @@ -369,7 +374,7 @@ privacy = "Quyền riĆŖng tʰ" [settings.developer] title = "NhĆ  phĆ”t triển" -apiKeys = "API Keys" +apiKeys = "Khóa API" [settings.tooltips] enableLoginFirst = "Bįŗ­t chįŗæ độ đăng nhįŗ­p trước" @@ -912,6 +917,9 @@ desc = "XĆ¢y dį»±ng quy trƬnh nhiều bước bįŗ±ng cĆ”ch xĆ¢u chuį»—i cĆ”c th desc = "Chồng lį»›p PDF lĆŖn trĆŖn PDF khĆ”c" title = "Chồng lį»›p PDF" +[home.pdfTextEditor] +title = "TrƬnh chỉnh sį»­a văn bįŗ£n PDF" +desc = "Chỉnh sį»­a văn bįŗ£n vĆ  hƬnh įŗ£nh hiện có bĆŖn trong PDF" [home.addText] tags = "văn bįŗ£n,chĆŗ thĆ­ch,nhĆ£n" @@ -1173,7 +1181,7 @@ selectFilesPlaceholder = "Chį»n tệp trong khung chĆ­nh Ä‘į»ƒ bįŗÆt đầu" settings = "CĆ i đặt" conversionCompleted = "HoĆ n tįŗ„t chuyển đổi" results = "Kįŗæt quįŗ£" -defaultFilename = "converted_file" +defaultFilename = "tep_da_chuyen_doi" conversionResults = "Kįŗæt quįŗ£ chuyển đổi" convertFrom = "Chuyển từ" convertTo = "Sang" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "Chữ ký vįŗ½" defaultImageLabel = "Chữ ký đã tįŗ£i lĆŖn" defaultTextLabel = "Chữ ký nhįŗ­p" saveButton = "Lʰu chữ ký" +savePersonal = "Lʰu cĆ” nhĆ¢n" +saveShared = "Lʰu dùng chung" saveUnavailable = "HĆ£y tįŗ”o chữ ký trước Ä‘į»ƒ lʰu." noChanges = "Chữ ký hiện tįŗ”i đã được lʰu." +tempStorageTitle = "Bį»™ nhį»› tįŗ”m cį»§a trƬnh duyệt" +tempStorageDescription = "Chữ ký chỉ được lʰu trong trƬnh duyệt cį»§a bįŗ”n. ChĆŗng sįŗ½ bị mįŗ„t nįŗæu bįŗ”n xóa dữ liệu trƬnh duyệt hoįŗ·c đổi sang trƬnh duyệt khĆ”c." +personalHeading = "Chữ ký cĆ” nhĆ¢n" +sharedHeading = "Chữ ký dùng chung" +personalDescription = "Chỉ mƬnh bįŗ”n có thể xem cĆ”c chữ ký nĆ y." +sharedDescription = "Tįŗ„t cįŗ£ ngĘ°į»i dùng đều có thể xem vĆ  sį»­ dỄng cĆ”c chữ ký nĆ y." [sign.saved.type] canvas = "Vįŗ½" @@ -2567,7 +2583,7 @@ stopButton = "Dừng so sĆ”nh" [certSign] tags = "xĆ”c thį»±c,PEM,P12,chĆ­nh thức,mĆ£ hóa" title = "Ký bįŗ±ng chứng chỉ" -filenamePrefix = "signed" +filenamePrefix = "da_ky" chooseCertificate = "Chį»n tệp chứng chỉ" chooseJksFile = "Chį»n tệp JKS" chooseP12File = "Chį»n tệp PKCS12" @@ -2701,7 +2717,7 @@ header = "Xóa chứng chỉ số khį»i PDF" selectPDF = "Chį»n mį»™t tệp PDF:" submit = "Xóa chữ ký" description = "CĆ“ng cỄ nĆ y sįŗ½ xóa chữ ký chứng chỉ số khį»i tĆ i liệu PDF cį»§a bįŗ”n." -filenamePrefix = "unsigned" +filenamePrefix = "chua_ky" [removeCertSign.files] placeholder = "Chį»n mį»™t tệp PDF trong mĆ n hƬnh chĆ­nh Ä‘į»ƒ bįŗÆt đầu" @@ -3020,6 +3036,91 @@ title = "Lįŗ„y thĆ“ng tin PDF" header = "Lįŗ„y thĆ“ng tin PDF" submit = "Lįŗ„y thĆ“ng tin" downloadJson = "Tįŗ£i xuống JSON" +processing = "Đang trĆ­ch xuįŗ„t thĆ“ng tin..." +results = "Kįŗæt quįŗ£" +noResults = "Chįŗ”y cĆ“ng cỄ Ä‘į»ƒ tįŗ”o bĆ”o cĆ”o." +downloads = "Tįŗ£i xuống" +noneDetected = "KhĆ“ng phĆ”t hiện" +indexTitle = "Chỉ mỄc" + +[getPdfInfo.report] +entryLabel = "Tóm tįŗÆt thĆ“ng tin đầy đủ" +shortTitle = "ThĆ“ng tin PDF" + +[getPdfInfo.sections] +metadata = "SiĆŖu dữ liệu" +formFields = "TrĘ°į»ng biểu mįŗ«u" +basicInfo = "ThĆ“ng tin cĘ” bįŗ£n" +documentInfo = "ThĆ“ng tin tĆ i liệu" +compliance = "TuĆ¢n thį»§" +encryption = "MĆ£ hóa" +permissions = "Quyền" +other = "KhĆ”c" +perPageInfo = "ThĆ“ng tin theo trang" +tableOfContents = "MỄc lỄc" + +[getPdfInfo.other] +attachments = "Tệp đƭnh kĆØm" +embeddedFiles = "Tệp nhĆŗng" +javaScript = "JavaScript" +layers = "Lį»›p" +structureTree = "StructureTree" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "KĆ­ch thước" +annotations = "ChĆŗ thĆ­ch" +images = "HƬnh įŗ£nh" +links = "LiĆŖn kįŗæt" +fonts = "PhĆ“ng chữ" +xobjects = "Số lượng XObject" +multimedia = "Đa phʰʔng tiện" + +[getPdfInfo.summary] +pages = "Trang" +fileSize = "KĆ­ch thước tệp" +pdfVersion = "PhiĆŖn bįŗ£n PDF" +language = "NgĆ“n ngữ" +title = "Tóm tįŗÆt PDF" +author = "TĆ”c giįŗ£" +created = "Đã tįŗ”o" +modified = "Đã sį»­a đổi" +permsAll = "Tįŗ„t cįŗ£ quyền được cho phĆ©p" +permsRestricted = "{{count}} giį»›i hįŗ”n" +permsMixed = "Mį»™t số quyền bị hįŗ”n chįŗæ" +hasCompliance = "Có tiĆŖu chuįŗ©n tuĆ¢n thį»§" +noCompliance = "KhĆ“ng có tiĆŖu chuįŗ©n tuĆ¢n thį»§" +basic = "ThĆ“ng tin cĘ” bįŗ£n" +documentInfo = "ThĆ“ng tin tĆ i liệu" +securityTitle = "Trįŗ”ng thĆ”i bįŗ£o mįŗ­t" +technical = "Kỹ thuįŗ­t" +overviewTitle = "Tổng quan PDF" + +[getPdfInfo.summary.security] +encrypted = "PDF đã mĆ£ hóa - Có bįŗ£o vệ bįŗ±ng mįŗ­t khįŗ©u" +unencrypted = "PDF khĆ“ng mĆ£ hóa - KhĆ“ng có bįŗ£o vệ bįŗ±ng mįŗ­t khįŗ©u" + +[getPdfInfo.summary.tech] +images = "HƬnh įŗ£nh" +fonts = "PhĆ“ng chữ" +formFields = "TrĘ°į»ng biểu mįŗ«u" +embeddedFiles = "Tệp nhĆŗng" +javaScript = "JavaScript" +layers = "Lį»›p" +bookmarks = "Dįŗ„u trang" +multimedia = "Đa phʰʔng tiện" + +[getPdfInfo.summary.overview] +untitled = "mį»™t tĆ i liệu khĆ“ng tiĆŖu đề" +unknown = "TĆ”c giįŗ£ khĆ“ng xĆ”c định" +text = "Đây lĆ  PDF {{pages}} trang có tiĆŖu đề {{title}} do {{author}} tįŗ”o (phiĆŖn bįŗ£n PDF {{version}})." + +[getPdfInfo.error] +partial = "Mį»™t số tệp khĆ“ng thể xį»­ lý." +unexpected = "Lį»—i khĆ“ng mong muốn trong quĆ” trƬnh trĆ­ch xuįŗ„t." + +[getPdfInfo.status] +complete = "HoĆ n tįŗ„t trĆ­ch xuįŗ„t" [extractPage] tags = "trĆ­ch xuįŗ„t" @@ -3438,6 +3539,9 @@ signinTitle = "Vui lòng đăng nhįŗ­p" ssoSignIn = "Đăng nhįŗ­p qua Single Sign-on" oAuth2AutoCreateDisabled = "Tį»± động tįŗ”o ngĘ°į»i dùng OAUTH2 bị vĆ“ hiệu hóa" oAuth2AdminBlockedUser = "Hiện đang chįŗ·n đăng ký hoįŗ·c đăng nhįŗ­p ngĘ°į»i dùng chʰa đăng ký. Vui lòng liĆŖn hệ quįŗ£n trị viĆŖn." +oAuth2RequiresLicense = "Đăng nhįŗ­p OAuth/SSO cįŗ§n giįŗ„y phĆ©p trįŗ£ phĆ­ (Server hoįŗ·c Enterprise). Vui lòng liĆŖn hệ quįŗ£n trị viĆŖn Ä‘į»ƒ nĆ¢ng cįŗ„p gói cį»§a bįŗ”n." +saml2RequiresLicense = "Đăng nhįŗ­p SAML cįŗ§n giįŗ„y phĆ©p trįŗ£ phĆ­ (Server hoįŗ·c Enterprise). Vui lòng liĆŖn hệ quįŗ£n trị viĆŖn Ä‘į»ƒ nĆ¢ng cįŗ„p gói cį»§a bįŗ”n." +maxUsersReached = "Đã đẔt số lượng ngĘ°į»i dùng tối đa cho giįŗ„y phĆ©p hiện tįŗ”i cį»§a bįŗ”n. Vui lòng liĆŖn hệ quįŗ£n trị viĆŖn Ä‘į»ƒ nĆ¢ng cįŗ„p gói hoįŗ·c thĆŖm suįŗ„t." oauth2RequestNotFound = "KhĆ“ng tƬm thįŗ„y yĆŖu cįŗ§u į»§y quyền" oauth2InvalidUserInfoResponse = "Phįŗ£n hồi thĆ“ng tin ngĘ°į»i dùng khĆ“ng hợp lệ" oauth2invalidRequest = "YĆŖu cįŗ§u khĆ“ng hợp lệ" @@ -3533,7 +3637,7 @@ title = "PDF thĆ nh mį»™t trang" header = "PDF thĆ nh mį»™t trang" submit = "Chuyển đổi thĆ nh mį»™t trang" description = "CĆ“ng cỄ nĆ y sįŗ½ gį»™p tįŗ„t cįŗ£ cĆ”c trang cį»§a PDF cį»§a bįŗ”n thĆ nh mį»™t trang đʔn lį»›n. Chiều rį»™ng giữ nguyĆŖn nhʰ cĆ”c trang gốc, còn chiều cao sįŗ½ bįŗ±ng tổng chiều cao cį»§a tįŗ„t cįŗ£ cĆ”c trang." -filenamePrefix = "single_page" +filenamePrefix = "mot_trang" [pdfToSinglePage.files] placeholder = "Chį»n mį»™t tệp PDF ở mĆ n hƬnh chĆ­nh Ä‘į»ƒ bįŗÆt đầu" @@ -3846,14 +3950,17 @@ fitToWidth = "Vừa chiều rį»™ng" actualSize = "KĆ­ch thước thįŗ­t" [viewer] +cannotPreviewFile = "KhĆ“ng thể xem trước tệp" +dualPageView = "Chįŗæ độ xem hai trang" firstPage = "Trang đầu" lastPage = "Trang cuối" -previousPage = "Trang trước" nextPage = "Trang tiįŗæp" +onlyPdfSupported = "TrƬnh xem chỉ hį»— trợ tệp PDF. Tệp nĆ y có vįŗ» lĆ  định dįŗ”ng khĆ”c." +previousPage = "Trang trước" +singlePageView = "Chįŗæ độ xem trang đʔn" +unknownFile = "Tệp khĆ“ng xĆ”c định" zoomIn = "Phóng to" zoomOut = "Thu nhį»" -singlePageView = "Chįŗæ độ xem trang đʔn" -dualPageView = "Chįŗæ độ xem hai trang" [rightRail] closeSelected = "Đóng cĆ”c tệp đã chį»n" @@ -3877,6 +3984,7 @@ toggleSidebar = "Chuyển đổi thanh bĆŖn" exportSelected = "Xuįŗ„t cĆ”c trang đã chį»n" toggleAnnotations = "Chuyển đổi hiển thị chĆŗ thĆ­ch" annotationMode = "Chuyển đổi chįŗæ độ chĆŗ thĆ­ch" +print = "In PDF" draw = "Vįŗ½" save = "Lʰu" saveChanges = "Lʰu thay đổi" @@ -4494,6 +4602,7 @@ description = "URL hoįŗ·c tĆŖn tệp cho impressum (bįŗÆt buį»™c ở mį»™t số title = "Premium & Enterprise" description = "Cįŗ„u hƬnh khóa giįŗ„y phĆ©p premium hoįŗ·c enterprise cį»§a bįŗ”n." license = "Cįŗ„u hƬnh giįŗ„y phĆ©p" +noInput = "Vui lòng cung cįŗ„p khóa giįŗ„y phĆ©p hoįŗ·c tệp" [admin.settings.premium.licenseKey] toggle = "Có license key hoįŗ·c tệp chứng chỉ?" @@ -4511,6 +4620,25 @@ line1 = "Ghi đè license key hiện tįŗ”i khĆ“ng thể hoĆ n tĆ”c." line2 = "License trước đó sįŗ½ bị mįŗ„t vÄ©nh viį»…n trừ khi bįŗ”n đã sao lʰu ở nĘ”i khĆ”c." line3 = "Quan trį»ng: Giữ license key riĆŖng tʰ vĆ  an toĆ n. KhĆ“ng bao giį» chia sįŗ» cĆ“ng khai." +[admin.settings.premium.inputMethod] +text = "Khóa giįŗ„y phĆ©p" +file = "Tệp chứng chỉ" + +[admin.settings.premium.file] +label = "Tệp chứng chỉ giįŗ„y phĆ©p" +description = "Tįŗ£i lĆŖn tệp giįŗ„y phĆ©p .lic hoįŗ·c .cert từ cĆ”c lįŗ§n mua ngoįŗ”i tuyįŗæn cį»§a bįŗ”n" +choose = "Chį»n tệp giįŗ„y phĆ©p" +selected = "Đã chį»n: {{filename}} ({{size}})" +successMessage = "Tệp giįŗ„y phĆ©p đã được tįŗ£i lĆŖn vĆ  kĆ­ch hoįŗ”t thĆ nh cĆ“ng. KhĆ“ng cįŗ§n khởi động lįŗ”i." + +[admin.settings.premium.currentLicense] +title = "Giįŗ„y phĆ©p đang hoįŗ”t động" +file = "Nguồn: Tệp giįŗ„y phĆ©p ({{path}})" +key = "Nguồn: Khóa giįŗ„y phĆ©p" +type = "Loįŗ”i: {{type}}" +noInput = "Vui lòng cung cįŗ„p khóa giįŗ„y phĆ©p hoįŗ·c tįŗ£i lĆŖn tệp chứng chỉ" +success = "ThĆ nh cĆ“ng" + [admin.settings.premium.enabled] label = "Bįŗ­t tĆ­nh năng Premium" description = "Bįŗ­t kiểm tra khóa giįŗ„y phĆ©p cho cĆ”c tĆ­nh năng pro/enterprise" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} đã chį»n" download = "Tįŗ£i xuống" delete = "Xóa" unsupported = "KhĆ“ng được hį»— trợ" +active = "Hoįŗ”t động" addToUpload = "ThĆŖm vĆ o tįŗ£i lĆŖn" +closeFile = "Đóng tệp" deleteAll = "Xóa tįŗ„t cįŗ£" loadingFiles = "Đang tįŗ£i tệp..." noFiles = "KhĆ“ng có tệp nĆ o" @@ -5132,7 +5262,7 @@ upgrade = "NĆ¢ng cįŗ„p ngay →" freeTitle = "Giįŗ„y phĆ©p Server" overLimitTitle = "Cįŗ§n giįŗ„y phĆ©p Server" overLimitBody = "Giįŗ„y phĆ©p cį»§a chĆŗng tĆ“i cho phĆ©p tối đa {{freeTierLimit}} ngĘ°į»i dùng miį»…n phĆ­ mį»—i server. Bįŗ”n có {{overLimitUserCopy}} ngĘ°į»i dùng Stirling. Để tiįŗæp tỄc khĆ“ng giĆ”n đoįŗ”n, hĆ£y nĆ¢ng cįŗ„p lĆŖn gói Stirling Server - số ghįŗæ khĆ“ng giį»›i hįŗ”n, chỉnh sį»­a văn bįŗ£n PDF vĆ  toĆ n quyền quįŗ£n trị vį»›i $99/server/thĆ”ng." -freeBody = "Giįŗ„y phĆ©p Open-Core cį»§a chĆŗng tĆ“i cho phĆ©p tối đa {{freeTierLimit}} ngĘ°į»i dùng miį»…n phĆ­ mį»—i server. Để mở rį»™ng khĆ“ng giĆ”n đoįŗ”n vĆ  truy cįŗ­p sį»›m cĆ“ng cỄ chỉnh sį»­a văn bįŗ£n PDF mį»›i, chĆŗng tĆ“i khuyįŗæn nghị gói Stirling Server - chỉnh sį»­a đầy đủ vĆ  số ghįŗæ khĆ“ng giį»›i hįŗ”n vį»›i $99/server/thĆ”ng." +freeBody = "Giįŗ„y phĆ©p Open-Core cį»§a chĆŗng tĆ“i cho phĆ©p tối đa {{freeTierLimit}} ngĘ°į»i dùng miį»…n phĆ­ cho mį»—i mĆ”y chį»§. Để mở rį»™ng quy mĆ“ liền mįŗ”ch, chĆŗng tĆ“i khuyįŗæn nghị gói Stirling Server - số lượng ngĘ°į»i dùng khĆ“ng giį»›i hįŗ”n vĆ  hį»— trợ SSO vį»›i giĆ” $99/mĆ”y chį»§/thĆ”ng." [onboarding.desktopInstall] title = "Tįŗ£i xuống" @@ -5237,6 +5367,31 @@ error = "Cįŗ­p nhįŗ­t trįŗ”ng thĆ”i ngĘ°į»i dùng thįŗ„t bįŗ”i" success = "Xóa ngĘ°į»i dùng thĆ nh cĆ“ng" error = "Xóa ngĘ°į»i dùng thįŗ„t bįŗ”i" +[workspace.people.changePassword] +action = "Đổi mįŗ­t khįŗ©u" +title = "Đổi mįŗ­t khįŗ©u" +subtitle = "Cįŗ­p nhįŗ­t mįŗ­t khįŗ©u cho" +newPassword = "Mįŗ­t khįŗ©u mį»›i" +confirmPassword = "XĆ”c nhįŗ­n mįŗ­t khįŗ©u" +placeholder = "Nhįŗ­p mįŗ­t khįŗ©u mį»›i" +confirmPlaceholder = "Nhįŗ­p lįŗ”i mįŗ­t khįŗ©u mį»›i" +passwordRequired = "Vui lòng nhįŗ­p mįŗ­t khįŗ©u mį»›i" +passwordMismatch = "Mįŗ­t khįŗ©u khĆ“ng khį»›p" +generateRandom = "Tįŗ”o mįŗ­t khįŗ©u an toĆ n" +generatedPreview = "Mįŗ­t khįŗ©u đã tįŗ”o:" +copyTooltip = "Sao chĆ©p vĆ o bį»™ nhį»› tįŗ”m" +copiedToClipboard = "Đã sao chĆ©p mįŗ­t khįŗ©u vĆ o bį»™ nhį»› tįŗ”m" +copyFailed = "KhĆ“ng thể sao chĆ©p mįŗ­t khįŗ©u" +sendEmail = "Gį»­i email cho ngĘ°į»i dùng về thay đổi nĆ y" +includePassword = "Bao gồm mįŗ­t khįŗ©u mį»›i trong email" +forcePasswordChange = "Buį»™c ngĘ°į»i dùng đổi mįŗ­t khįŗ©u ở lįŗ§n đăng nhįŗ­p tiįŗæp theo" +emailUnavailable = "Email cį»§a ngĘ°į»i dùng nĆ y khĆ“ng phįŗ£i lĆ  địa chỉ email hợp lệ. Đã tįŗÆt thĆ“ng bĆ”o." +smtpDisabled = "ThĆ“ng bĆ”o email yĆŖu cįŗ§u bįŗ­t SMTP trong cĆ i đặt." +notifyOnly = "Sįŗ½ gį»­i mį»™t email khĆ“ng kĆØm mįŗ­t khįŗ©u, cho ngĘ°į»i dùng biįŗæt quįŗ£n trị viĆŖn đã thay đổi mįŗ­t khįŗ©u." +submit = "Cįŗ­p nhįŗ­t mįŗ­t khįŗ©u" +success = "Cįŗ­p nhįŗ­t mįŗ­t khįŗ©u thĆ nh cĆ“ng" +error = "KhĆ“ng thể cįŗ­p nhįŗ­t mįŗ­t khįŗ©u" + [workspace.people.emailInvite] tab = "Mį»i qua Email" description = "Nhįŗ­p hoįŗ·c dĆ”n email bĆŖn dưới, phĆ¢n tĆ”ch bįŗ±ng dįŗ„u phįŗ©y. NgĘ°į»i dùng sįŗ½ nhįŗ­n thĆ“ng tin đăng nhįŗ­p qua email." @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "YĆŖu cįŗ§u Ć­t nhįŗ„t mį»™t địa chỉ email" submit = "Gį»­i lį»i mį»i" success = "Mį»i ngĘ°į»i dùng thĆ nh cĆ“ng" -partialSuccess = "Mį»™t số lį»i mį»i thįŗ„t bįŗ”i" +partialFailure = "Mį»™t số lį»i mį»i khĆ“ng thĆ nh cĆ“ng" allFailed = "Mį»i ngĘ°į»i dùng thįŗ„t bįŗ”i" error = "Gį»­i lį»i mį»i thįŗ„t bįŗ”i" @@ -5770,6 +5925,7 @@ subtitle = "Đăng nhįŗ­p bįŗ±ng tĆ i khoįŗ£n Stirling" [setup.selfhosted] title = "Đăng nhįŗ­p vĆ o server" subtitle = "Nhįŗ­p thĆ“ng tin đăng nhįŗ­p server" +link = "hoįŗ·c kįŗæt nối tį»›i tĆ i khoįŗ£n tį»± lʰu trữ" [setup.server] title = "Kįŗæt nối đến server" @@ -5788,6 +5944,14 @@ description = "Nhįŗ­p URL đầy đủ cį»§a server Stirling PDF tį»± lʰu trữ emptyUrl = "Vui lòng nhįŗ­p URL server" unreachable = "KhĆ“ng thể kįŗæt nối đến server" testFailed = "Kiểm tra kįŗæt nối thįŗ„t bįŗ”i" +configFetch = "KhĆ“ng thể lįŗ„y cįŗ„u hƬnh mĆ”y chį»§. Vui lòng kiểm tra URL vĆ  thį»­ lįŗ”i." + +[setup.server.error.securityDisabled] +title = "Chʰa bįŗ­t đăng nhįŗ­p" +body = "MĆ”y chį»§ nĆ y chʰa bįŗ­t chức năng đăng nhįŗ­p. Để kįŗæt nối tį»›i mĆ”y chį»§ nĆ y, bįŗ”n phįŗ£i bįŗ­t xĆ”c thį»±c:" +step1 = "Đặt DOCKER_ENABLE_SECURITY=true trong mĆ“i trĘ°į»ng cį»§a bįŗ”n" +step2 = "Hoįŗ·c đặt security.enableLogin=true trong settings.yml" +step3 = "Khởi động lįŗ”i mĆ”y chį»§" [setup.login] title = "Đăng nhįŗ­p" @@ -5797,6 +5961,13 @@ submit = "Đăng nhįŗ­p" signInWith = "Đăng nhįŗ­p vį»›i" oauthPending = "Đang mở trƬnh duyệt Ä‘į»ƒ xĆ”c thį»±c..." orContinueWith = "Hoįŗ·c tiįŗæp tỄc bįŗ±ng email" +serverRequirement = "Lʰu ý: MĆ”y chį»§ phįŗ£i bįŗ­t đăng nhįŗ­p." +showInstructions = "CĆ”ch bįŗ­t?" +hideInstructions = "įŗØn hướng dįŗ«n" +instructions = "Để bįŗ­t đăng nhįŗ­p trĆŖn mĆ”y chį»§ Stirling PDF cį»§a bįŗ”n:" +instructionsEnvVar = "Đặt biįŗæn mĆ“i trĘ°į»ng:" +instructionsOrYml = "Hoįŗ·c trong settings.yml:" +instructionsRestart = "Sau đó khởi động lįŗ”i mĆ”y chį»§ Ä‘į»ƒ cĆ”c thay đổi có hiệu lį»±c." [setup.login.username] label = "TĆŖn ngĘ°į»i dùng" @@ -5853,6 +6024,7 @@ earlyAccess = "Truy cįŗ­p sį»›m" reset = "Đặt lįŗ”i thay đổi" downloadJson = "Tįŗ£i JSON" generatePdf = "Tįŗ”o PDF" +saveChanges = "Lʰu thay đổi" [pdfTextEditor.options.autoScaleText] title = "Tį»± căn chỉnh văn bįŗ£n cho vừa hį»™p" @@ -5890,6 +6062,8 @@ alpha = "TrƬnh xem alpha nĆ y vįŗ«n đang phĆ”t triển—mį»™t số font, mĆ u [pdfTextEditor.empty] title = "Chʰa tįŗ£i tĆ i liệu" subtitle = "Tįŗ£i tệp PDF hoįŗ·c JSON Ä‘į»ƒ bįŗÆt đầu chỉnh sį»­a nį»™i dung văn bįŗ£n." +dropzone = "KĆ©o vĆ  thįŗ£ tệp PDF hoįŗ·c JSON vĆ o đây, hoįŗ·c nhįŗ„p Ä‘į»ƒ duyệt" +dropzoneWithFiles = "Chį»n mį»™t tệp từ tab Tệp, hoįŗ·c kĆ©o vĆ  thįŗ£ tệp PDF hoįŗ·c JSON vĆ o đây, hoįŗ·c nhįŗ„p Ä‘į»ƒ duyệt" [pdfTextEditor.welcomeBanner] title = "ChĆ o mừng đến vį»›i PDF Text Editor (Truy cįŗ­p sį»›m)" diff --git a/frontend/public/locales/zh-BO/translation.toml b/frontend/public/locales/zh-BO/translation.toml index efbc9cb914..8c05b4c5f1 100644 --- a/frontend/public/locales/zh-BO/translation.toml +++ b/frontend/public/locales/zh-BO/translation.toml @@ -163,6 +163,11 @@ unfavorite = "ä»Žę”¶č—äø­ē§»é™¤" fullscreen = "åˆ‡ę¢åˆ°å…Øå±ęØ”å¼" sidebar = "åˆ‡ę¢åˆ°ä¾§č¾¹ę ęØ”å¼" +[backendStartup] +notFoundTitle = "ęœŖę‰¾åˆ°åŽē«Æ" +retry = "é‡čÆ•" +unreachable = "åŗ”ē”Øē›®å‰ę— ę³•čæžęŽ„åˆ°åŽē«Æć€‚čÆ·ę£€ęŸ„åŽē«ÆēŠ¶ę€å’Œē½‘ē»œčæžęŽ„ļ¼Œē„¶åŽé‡čÆ•ć€‚" + [zipWarning] title = "大型 ZIP ꖇ件" message = "ę­¤ ZIP 包含 {{count}} äøŖę–‡ä»¶ć€‚ä»č¦č§£åŽ‹å—ļ¼Ÿ" @@ -912,6 +917,9 @@ desc = "é€ščæ‡äø²č” PDF åŠØä½œęž„å»ŗå¤šę­„å·„ä½œęµć€‚é€‚åˆé‡å¤ę€§ä»»åŠ”ć€‚" desc = "PDF ą½‚ą½žą½“ą¼‹ą½žą½²ą½‚ą¼‹ą½‚ą½²ą¼‹ą½¦ą¾Ÿą½ŗą½„ą¼‹ą½‘ą½“ą¼‹ PDF ą½–ą½¢ą¾©ą½ŗą½‚ą½¦ą¼‹ą½”ą¼" title = "PDF ą½¦ą¾Ÿą½ŗą½„ą¼‹ą½–ą½¢ą¾©ą½ŗą½‚ą½¦ą¼" +[home.pdfTextEditor] +title = "PDF ę–‡ęœ¬ē¼–č¾‘å™Ø" +desc = "编辑 PDF äø­ēš„ēŽ°ęœ‰ę–‡ęœ¬å’Œå›¾åƒ" [home.addText] tags = "ę–‡ęœ¬,ę³Øé‡Š,标签" @@ -1173,7 +1181,7 @@ selectFilesPlaceholder = "åœØäø»č§†å›¾äø­é€‰ę‹©ę–‡ä»¶ä»„å¼€å§‹" settings = "设置" conversionCompleted = "č½¬ę¢å®Œęˆ" results = "ē»“ęžœ" -defaultFilename = "converted_file" +defaultFilename = "å·²č½¬ę¢_ꖇ件" conversionResults = "č½¬ę¢ē»“ęžœ" convertFrom = "ä»Žä»„äø‹ę ¼å¼č½¬ę¢" convertTo = "č½¬ę¢äøŗ" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "ę‰‹å†™ē­¾å" defaultImageLabel = "å·²äøŠä¼ ēš„ē­¾å" defaultTextLabel = "é”®å…„ēš„ē­¾å" saveButton = "äæå­˜ē­¾å" +savePersonal = "äæå­˜äøŗäøŖäŗŗ" +saveShared = "äæå­˜äøŗå…±äŗ«" saveUnavailable = "čÆ·å…ˆåˆ›å»ŗē­¾åå†äæå­˜ć€‚" noChanges = "å½“å‰ē­¾åå·²äæå­˜ć€‚" +tempStorageTitle = "ęµč§ˆå™Øäø“ę—¶å­˜å‚Ø" +tempStorageDescription = "ē­¾åä»…å­˜å‚ØåœØę‚Øēš„ęµč§ˆå™Øäø­ć€‚č‹„ęø…é™¤ęµč§ˆå™Øę•°ę®ęˆ–ę›“ę¢ęµč§ˆå™Øļ¼Œčæ™äŗ›ē­¾åå°†ä¼šäø¢å¤±ć€‚" +personalHeading = "äøŖäŗŗē­¾å" +sharedHeading = "å…±äŗ«ē­¾å" +personalDescription = "åŖęœ‰ę‚ØåÆä»„ēœ‹åˆ°čæ™äŗ›ē­¾åć€‚" +sharedDescription = "ę‰€ęœ‰ē”Øęˆ·éƒ½åÆä»„ęŸ„ēœ‹å¹¶ä½æē”Øčæ™äŗ›ē­¾åć€‚" [sign.saved.type] canvas = "绘制" @@ -3020,6 +3036,91 @@ title = "PDF ą½”ą½²ą¼‹ą½‚ą½“ą½¦ą¼‹ą½šą½“ą½£ą¼‹ą½£ą½ŗą½“ą¼‹ą½”ą¼" header = "PDF ą½”ą½²ą¼‹ą½‚ą½“ą½¦ą¼‹ą½šą½“ą½£ą¼‹ą½£ą½ŗą½“ą¼‹ą½”ą¼" submit = "ą½‚ą½“ą½¦ą¼‹ą½šą½“ą½£ą¼‹ą½£ą½ŗą½“ą¼‹ą½”ą¼" downloadJson = "JSON ą½•ą½–ą¼‹ą½£ą½ŗą½“ą¼" +processing = "ę­£åœØęå–äæ”ęÆ..." +results = "ē»“ęžœ" +noResults = "čæč”Œę­¤å·„å…·ä»„ē”ŸęˆęŠ„å‘Šć€‚" +downloads = "äø‹č½½" +noneDetected = "ęœŖę£€ęµ‹åˆ°" +indexTitle = "瓢引" + +[getPdfInfo.report] +entryLabel = "å®Œę•“äæ”ęÆę‘˜č¦" +shortTitle = "PDF 俔息" + +[getPdfInfo.sections] +metadata = "å…ƒę•°ę®" +formFields = "č”Øå•å­—ę®µ" +basicInfo = "基本俔息" +documentInfo = "文攣俔息" +compliance = "åˆč§„ę€§" +encryption = "åŠ åÆ†" +permissions = "ꝃ限" +other = "其他" +perPageInfo = "ęÆé”µäæ”ęÆ" +tableOfContents = "目录" + +[getPdfInfo.other] +attachments = "附件" +embeddedFiles = "åµŒå…„ę–‡ä»¶" +javaScript = "JavaScript" +layers = "图层" +structureTree = "ē»“ęž„ę ‘" +xmp = "XMP å…ƒę•°ę®" + +[getPdfInfo.perPage] +size = "尺寸" +annotations = "ę³Øé‡Š" +images = "图像" +links = "é“¾ęŽ„" +fonts = "字体" +xobjects = "XObject ꕰ量" +multimedia = "å¤šåŖ’ä½“" + +[getPdfInfo.summary] +pages = "锵ꕰ" +fileSize = "ę–‡ä»¶å¤§å°" +pdfVersion = "PDF ē‰ˆęœ¬" +language = "语言" +title = "PDF ę‘˜č¦" +author = "ä½œč€…" +created = "åˆ›å»ŗę—¶é—“" +modified = "修改时闓" +permsAll = "å…č®øę‰€ęœ‰ęƒé™" +permsRestricted = "{{count}} 锹限制" +permsMixed = "éƒØåˆ†ęƒé™å—é™" +hasCompliance = "ē¬¦åˆåˆč§„ę ‡å‡†" +noCompliance = "ę— åˆč§„ę ‡å‡†" +basic = "基本俔息" +documentInfo = "文攣俔息" +securityTitle = "å®‰å…ØēŠ¶ę€" +technical = "ęŠ€ęœÆ" +overviewTitle = "PDF ę¦‚č§ˆ" + +[getPdfInfo.summary.security] +encrypted = "å·²åŠ åÆ†ēš„ PDF - å—åÆ†ē äæęŠ¤" +unencrypted = "ęœŖåŠ åÆ†ēš„ PDF - ę— åÆ†ē äæęŠ¤" + +[getPdfInfo.summary.tech] +images = "图像" +fonts = "字体" +formFields = "č”Øå•å­—ę®µ" +embeddedFiles = "åµŒå…„ę–‡ä»¶" +javaScript = "JavaScript" +layers = "图层" +bookmarks = "书签" +multimedia = "å¤šåŖ’ä½“" + +[getPdfInfo.summary.overview] +untitled = "ęœŖå‘½åę–‡ę”£" +unknown = "ä½œč€…ęœŖēŸ„" +text = "čæ™ę˜Æäø€äøŖ {{pages}} é”µēš„ PDFļ¼Œę ‡é¢˜äøŗ {{title}}ļ¼Œē”± {{author}} åˆ›å»ŗļ¼ˆPDF ē‰ˆęœ¬ {{version}})。" + +[getPdfInfo.error] +partial = "éƒØåˆ†ę–‡ä»¶ę— ę³•å¤„ē†ć€‚" +unexpected = "ęå–čæ‡ēØ‹äø­å‘ē”Ÿę„å¤–é”™čÆÆć€‚" + +[getPdfInfo.status] +complete = "ęå–å®Œęˆ" [extractPage] tags = "ą½•ą¾±ą½²ą½¢ą¼‹ą½ ą½‘ą½¼ą½“ą¼" @@ -3438,6 +3539,9 @@ signinTitle = "ą½“ą½„ą¼‹ą½ ą½›ą½“ą½£ą¼‹ą½‚ą½“ą½„ą¼‹ą½¢ą½¼ą½‚ą½¦ą¼" ssoSignIn = "ą½‚ą½…ą½²ą½‚ą¼‹ą½‚ą¾±ą½“ą½¢ą¼‹ą½“ą½„ą¼‹ą½ ą½›ą½“ą½£ą¼‹ą½–ą½¢ą¾’ą¾±ą½“ą½‘ą¼‹ą½“ą½¦ą¼‹ą½“ą½„ą¼‹ą½ ą½›ą½“ą½£ą¼" oAuth2AutoCreateDisabled = "OAUTH2 ą½¢ą½„ą¼‹ą½ ą½‚ą½“ą½£ą¼‹ą½¦ą¾¤ą¾±ą½¼ą½‘ą¼‹ą½˜ą½ą½“ą¼‹ą½‚ą½¦ą½¢ą¼‹ą½–ą½Ÿą½¼ą¼‹ą½–ą½€ą½‚ą¼‹ą½¦ą¾”ą½¼ą½˜ą¼‹ą½–ą¾±ą½¦ą¼‹ą½Ÿą½²ą½“ą¼" oAuth2AdminBlockedUser = "ą½‘ą¼‹ą½£ą¾Ÿą¼‹ą½ą½¼ą¼‹ą½ ą½‚ą½¼ą½‘ą¼‹ą½˜ą¼‹ą½–ą¾±ą½¦ą¼‹ą½”ą½ ą½²ą¼‹ą½¦ą¾¤ą¾±ą½¼ą½‘ą¼‹ą½˜ą½ą½“ą¼‹ą½‚ą¾±ą½²ą¼‹ą½ą½¼ą¼‹ą½ ą½‚ą½¼ą½‘ą¼‹ą½‘ą½„ą¼‹ą½“ą½„ą¼‹ą½ ą½›ą½“ą½£ą¼‹ą½–ą½€ą½‚ą¼‹ą½¦ą¾”ą½¼ą½˜ą¼‹ą½–ą¾±ą½¦ą¼‹ą½”ą½¼ą½‘ą¼ ą½‘ą½¼ą¼‹ą½‘ą½˜ą¼‹ą½”ą½¢ą¼‹ą½ ą½–ą¾²ą½ŗą½£ą¼‹ą½–ą¼‹ą½‚ą½“ą½„ą¼‹ą½¢ą½¼ą½‚ą½¦ą¼" +oAuth2RequiresLicense = "使用 OAuth/SSO ē™»å½•éœ€č¦ä»˜č“¹č®øåÆļ¼ˆServer ꈖ Enterpriseļ¼‰ć€‚čÆ·č”ē³»ē®”ē†å‘˜å‡ēŗ§ę‚Øēš„ę–¹ę”ˆć€‚" +saml2RequiresLicense = "使用 SAML ē™»å½•éœ€č¦ä»˜č“¹č®øåÆļ¼ˆServer ꈖ Enterpriseļ¼‰ć€‚čÆ·č”ē³»ē®”ē†å‘˜å‡ēŗ§ę‚Øēš„ę–¹ę”ˆć€‚" +maxUsersReached = "ę‚Øå½“å‰ēš„č®øåÆå·²č¾¾åˆ°ęœ€å¤§ē”Øęˆ·ę•°ć€‚čÆ·č”ē³»ē®”ē†å‘˜å‡ēŗ§ę‚Øēš„ę–¹ę”ˆęˆ–å¢žåŠ åø­ä½ć€‚" oauth2RequestNotFound = "ą½‘ą½–ą½„ą¼‹ą½¦ą¾¤ą¾²ą½¼ą½‘ą¼‹ą½¢ą½ŗą¼‹ą½žą½“ą¼‹ą½¢ą¾™ą½ŗą½‘ą¼‹ą½˜ą¼‹ą½–ą¾±ą½“ą½„ą¼‹ą¼" oauth2InvalidUserInfoResponse = "ą½¦ą¾¤ą¾±ą½¼ą½‘ą¼‹ą½˜ą½ą½“ą¼‹ą½‚ą¾±ą½²ą¼‹ą½‚ą½“ą½¦ą¼‹ą½šą½“ą½£ą¼‹ą½£ą½“ą¼‹ą½ ą½‘ą½ŗą½–ą½¦ą¼‹ą½“ą½¼ą½¢ą¼‹ą½ ą½ą¾²ą½“ą½£ą¼" oauth2invalidRequest = "ą½¢ą½ŗą¼‹ą½žą½“ą¼‹ą½“ą½¼ą½¢ą¼‹ą½ ą½ą¾²ą½“ą½£ą¼" @@ -3846,14 +3950,17 @@ fitToWidth = "适应宽度" actualSize = "实际大小" [viewer] +cannotPreviewFile = "ę— ę³•é¢„č§ˆę–‡ä»¶" +dualPageView = "åŒé”µč§†å›¾" firstPage = "第一锵" lastPage = "ęœ€åŽäø€é”µ" -previousPage = "äøŠäø€é”µ" nextPage = "下一锵" +onlyPdfSupported = "ę­¤ęŸ„ēœ‹å™Øä»…ę”ÆęŒ PDF ę–‡ä»¶ć€‚čÆ„ę–‡ä»¶ä¼¼ä¹Žäøŗå…¶ä»–ę ¼å¼ć€‚" +previousPage = "äøŠäø€é”µ" +singlePageView = "å•é”µč§†å›¾" +unknownFile = "ęœŖēŸ„ę–‡ä»¶" zoomIn = "放大" zoomOut = "ē¼©å°" -singlePageView = "å•é”µč§†å›¾" -dualPageView = "åŒé”µč§†å›¾" [rightRail] closeSelected = "关闭所选文件" @@ -3877,6 +3984,7 @@ toggleSidebar = "åˆ‡ę¢ä¾§č¾¹ę " exportSelected = "åÆ¼å‡ŗę‰€é€‰é”µé¢" toggleAnnotations = "åˆ‡ę¢ę³Øé‡ŠåÆč§ę€§" annotationMode = "åˆ‡ę¢ę³Øé‡ŠęØ”å¼" +print = "ę‰“å° PDF" draw = "绘制" save = "äæå­˜" saveChanges = "äæå­˜ę›“ę”¹" @@ -4494,6 +4602,7 @@ description = "Impressum ēš„ URL ęˆ–ę–‡ä»¶åļ¼ˆęŸäŗ›åøę³•ē®”č¾–åŒŗč¦ę±‚ļ¼‰" title = "é«˜ēŗ§äøŽä¼äøšē‰ˆ" description = "é…ē½®ä½ ēš„é«˜ēŗ§ęˆ–ä¼äøšč®øåÆčÆåÆ†é’„ć€‚" license = "č®øåÆčÆé…ē½®" +noInput = "čÆ·ęä¾›č®øåÆčÆåÆ†é’„ęˆ–ę–‡ä»¶" [admin.settings.premium.licenseKey] toggle = "ęœ‰č®øåÆčÆåÆ†é’„ęˆ–čÆä¹¦ę–‡ä»¶ļ¼Ÿ" @@ -4511,6 +4620,25 @@ line1 = "č¦†ē›–å½“å‰č®øåÆčÆåÆ†é’„åŽå°†ę— ę³•ę’¤é”€ć€‚" line2 = "é™¤éžå¦ęœ‰å¤‡ä»½ļ¼Œå¦åˆ™ä¹‹å‰ēš„č®øåÆčÆå°†č¢«ę°øä¹…äø¢å¤±ć€‚" line3 = "é‡č¦ļ¼ščÆ·å¦„å–„äæē®”č®øåÆčÆåÆ†é’„ļ¼Œåˆ‡å‹æå…¬å¼€åˆ†äŗ«ć€‚" +[admin.settings.premium.inputMethod] +text = "č®øåÆčÆåÆ†é’„" +file = "证书文件" + +[admin.settings.premium.file] +label = "č®øåÆčÆčÆä¹¦ę–‡ä»¶" +description = "äøŠä¼ ę‚Øēŗæäø‹č“­ä¹°ēš„ .lic ꈖ .cert č®øåÆčÆę–‡ä»¶" +choose = "é€‰ę‹©č®øåÆčÆę–‡ä»¶" +selected = "å·²é€‰ę‹©ļ¼š{{filename}}({{size}})" +successMessage = "č®øåÆčÆę–‡ä»¶äøŠä¼ å¹¶ęæ€ę“»ęˆåŠŸć€‚ę— éœ€é‡åÆć€‚" + +[admin.settings.premium.currentLicense] +title = "å·²ęæ€ę“»ēš„č®øåÆčÆ" +file = "ę„ęŗļ¼šč®øåÆčÆę–‡ä»¶ļ¼ˆ{{path}})" +key = "ę„ęŗļ¼šč®øåÆčÆåÆ†é’„" +type = "ē±»åž‹ļ¼š{{type}}" +noInput = "čÆ·ęä¾›č®øåÆčÆåÆ†é’„ęˆ–äøŠä¼ čÆä¹¦ę–‡ä»¶" +success = "成功" + [admin.settings.premium.enabled] label = "åÆē”Øé«˜ēŗ§åŠŸčƒ½" description = "äøŗäø“äøš/ä¼äøšåŠŸčƒ½åÆē”Øč®øåÆčÆåÆ†é’„ę£€ęŸ„" @@ -4644,7 +4772,9 @@ selectedCount = "已选 {{count}} äøŖ" download = "äø‹č½½" delete = "删除" unsupported = "äøę”ÆęŒ" +active = "已启用" addToUpload = "添加到上传" +closeFile = "关闭文件" deleteAll = "å…ØéƒØåˆ é™¤" loadingFiles = "ę­£åœØåŠ č½½ę–‡ä»¶..." noFiles = "Ꚃꗠꖇ件" @@ -5132,7 +5262,7 @@ upgrade = "ē«‹å³å‡ēŗ§ →" freeTitle = "ęœåŠ”å™Øč®øåÆčÆ" overLimitTitle = "éœ€č¦ęœåŠ”å™Øč®øåÆčÆ" overLimitBody = "ęˆ‘ä»¬ēš„č®øåÆęÆå°ęœåŠ”å™Øęœ€å¤šå…č®ø {{freeTierLimit}} åē”Øęˆ·å…č“¹ä½æē”Øć€‚ę‚Øå…±ęœ‰ {{overLimitUserCopy}} 名 Stirling ē”Øęˆ·ć€‚äøŗéæå…äø­ę–­ļ¼ŒčÆ·å‡ēŗ§åˆ° Stirling Server ę–¹ę”ˆ - ę— é™åø­ä½ć€PDF ę–‡ęœ¬ē¼–č¾‘ļ¼Œä»„åŠęÆå°ęœåŠ”å™Ø $99/月 ēš„å®Œę•“ē®”ē†å‘˜ęŽ§åˆ¶ć€‚" -freeBody = "ęˆ‘ä»¬ēš„ å¼€ęŗå†…ę øļ¼ˆOpen-Core) č®øåÆå…č®øęÆå°ęœåŠ”å™Øęœ€å¤š {{freeTierLimit}} åē”Øęˆ·å…č“¹ä½æē”Øć€‚äøŗé”ŗē•…ę‰©å±•å¹¶ęŠ¢å…ˆä½“éŖŒå…Øę–°ēš„ PDF ę–‡ęœ¬ē¼–č¾‘å·„å…·ļ¼Œęˆ‘ä»¬ęŽØč Stirling Server ę–¹ę”ˆ - å®Œę•“ē¼–č¾‘äøŽ ę— é™åø­ä½ļ¼Œ$99/ęœåŠ”å™Ø/꜈怂" +freeBody = "ęˆ‘ä»¬ēš„Open-Coreč®øåÆå…č®øęÆå°ęœåŠ”å™Øęœ€å¤š{{freeTierLimit}}åē”Øęˆ·å…č“¹ä½æē”Øć€‚äøŗå®žēŽ°äøäø­ę–­ēš„ę‰©å±•ļ¼Œęˆ‘ä»¬ęŽØč Stirling Server ę–¹ę”ˆ - äøé™åø­ä½å¹¶ęä¾›SSO ę”ÆęŒļ¼Œ$99/ęœåŠ”å™Ø/꜈怂" [onboarding.desktopInstall] title = "äø‹č½½" @@ -5237,6 +5367,31 @@ error = "ē”Øęˆ·ēŠ¶ę€ę›“ę–°å¤±č“„" success = "ē”Øęˆ·åˆ é™¤ęˆåŠŸ" error = "åˆ é™¤ē”Øęˆ·å¤±č“„" +[workspace.people.changePassword] +action = "曓改密码" +title = "曓改密码" +subtitle = "为其曓新密码" +newPassword = "新密码" +confirmPassword = "甮认密码" +placeholder = "输兄新密码" +confirmPlaceholder = "å†ę¬”č¾“å…„ę–°åÆ†ē " +passwordRequired = "请输兄新密码" +passwordMismatch = "äø¤ę¬”č¾“å…„ēš„åÆ†ē äøäø€č‡“" +generateRandom = "ē”Ÿęˆå®‰å…ØåÆ†ē " +generatedPreview = "å·²ē”Ÿęˆēš„åÆ†ē ļ¼š" +copyTooltip = "å¤åˆ¶åˆ°å‰Ŗč““ęæ" +copiedToClipboard = "åÆ†ē å·²å¤åˆ¶åˆ°å‰Ŗč““ęæ" +copyFailed = "å¤åˆ¶åÆ†ē å¤±č“„" +sendEmail = "é€ščæ‡ē”µå­é‚®ä»¶é€šēŸ„ē”Øęˆ·ę­¤ę›“ę”¹" +includePassword = "åœØé‚®ä»¶äø­åŒ…å«ę–°åÆ†ē " +forcePasswordChange = "å¼ŗåˆ¶ē”Øęˆ·äø‹ę¬”ē™»å½•ę—¶ę›“ę”¹åÆ†ē " +emailUnavailable = "čÆ„ē”Øęˆ·ēš„é‚®ē®±åœ°å€ę— ę•ˆļ¼Œå·²ē¦ē”Øé€šēŸ„ć€‚" +smtpDisabled = "é‚®ä»¶é€šēŸ„éœ€č¦åœØč®¾ē½®äø­åÆē”Ø SMTP怂" +notifyOnly = "å°†å‘é€äøåŒ…å«åÆ†ē ēš„é‚®ä»¶ļ¼Œå‘ŠēŸ„ē”Øęˆ·ē®”ē†å‘˜å·²ę›“ę”¹å…¶åÆ†ē ć€‚" +submit = "曓新密码" +success = "åÆ†ē ę›“ę–°ęˆåŠŸ" +error = "密码曓新失蓄" + [workspace.people.emailInvite] tab = "邮件邀请" description = "åœØäø‹ę–¹č¾“å…„ęˆ–ē²˜č““é‚®ē®±ļ¼Œä½æē”Øé€—å·åˆ†éš”ć€‚ē”Øęˆ·å°†é€ščæ‡é‚®ä»¶ę”¶åˆ°ē™»å½•å‡­ę®ć€‚" @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "č‡³å°‘éœ€č¦äø€äøŖé‚®ē®±åœ°å€" submit = "å‘é€é‚€čÆ·" success = "å·²ęˆåŠŸé‚€čÆ·ē”Øęˆ·" -partialSuccess = "éƒØåˆ†é‚€čÆ·å¤±č“„" +partialFailure = "éƒØåˆ†é‚€čÆ·å‘é€å¤±č“„" allFailed = "é‚€čÆ·ē”Øęˆ·å¤±č“„" error = "å‘é€é‚€čÆ·å¤±č“„" @@ -5770,6 +5925,7 @@ subtitle = "ä½æē”Øę‚Øēš„ Stirling č“¦ęˆ·ē™»å½•" [setup.selfhosted] title = "ē™»å½•ęœåŠ”å™Ø" subtitle = "č¾“å…„ę‚Øēš„ęœåŠ”å™Øå‡­ę®" +link = "ęˆ–čæžęŽ„åˆ°č‡Ŗę‰˜ē®”č“¦ęˆ·" [setup.server] title = "čæžęŽ„åˆ°ęœåŠ”å™Ø" @@ -5788,6 +5944,14 @@ description = "č¾“å…„č‡Ŗę‰˜ē®” Stirling PDF ęœåŠ”å™Øēš„å®Œę•“ URL" emptyUrl = "čÆ·č¾“å…„ęœåŠ”å™Ø URL" unreachable = "ę— ę³•čæžęŽ„åˆ°ęœåŠ”å™Ø" testFailed = "čæžęŽ„ęµ‹čÆ•å¤±č“„" +configFetch = "čŽ·å–ęœåŠ”å™Øé…ē½®å¤±č“„ć€‚čÆ·ę£€ęŸ„ URL åŽé‡čÆ•ć€‚" + +[setup.server.error.securityDisabled] +title = "ęœŖåÆē”Øē™»å½•" +body = "ę­¤ęœåŠ”å™ØęœŖåÆē”Øē™»å½•ć€‚č¦čæžęŽ„åˆ°čÆ„ęœåŠ”å™Øļ¼Œåæ…é”»åÆē”Øčŗ«ä»½éŖŒčÆļ¼š" +step1 = "åœØēŽÆå¢ƒäø­č®¾ē½® DOCKER_ENABLE_SECURITY=true" +step2 = "ęˆ–åœØ settings.yml 中设置 security.enableLogin=true" +step3 = "é‡åÆęœåŠ”å™Ø" [setup.login] title = "登录" @@ -5797,6 +5961,13 @@ submit = "登录" signInWith = "ē™»å½•ę–¹å¼" oauthPending = "ę­£åœØę‰“å¼€ęµč§ˆå™Øčæ›č”Œč®¤čÆ..." orContinueWith = "ęˆ–ä½æē”Øé‚®ē®±ē»§ē»­" +serverRequirement = "ę³Øę„ļ¼šęœåŠ”å™Øåæ…é”»åÆē”Øē™»å½•åŠŸčƒ½ć€‚" +showInstructions = "å¦‚ä½•åÆē”Øļ¼Ÿ" +hideInstructions = "éšč—čÆ“ę˜Ž" +instructions = "åœØę‚Øēš„ Stirling PDF ęœåŠ”å™ØäøŠåÆē”Øē™»å½•åŠŸčƒ½ļ¼š" +instructionsEnvVar = "č®¾ē½®ēŽÆå¢ƒå˜é‡ļ¼š" +instructionsOrYml = "ęˆ–åœØ settings.yml 中:" +instructionsRestart = "ē„¶åŽé‡åÆęœåŠ”å™Øä»„ä½æę›“ę”¹ē”Ÿę•ˆć€‚" [setup.login.username] label = "ē”Øęˆ·å" @@ -5853,6 +6024,7 @@ earlyAccess = "ęŠ¢å…ˆä½“éŖŒ" reset = "é‡ē½®ę›“ę”¹" downloadJson = "äø‹č½½ JSON" generatePdf = "ē”Ÿęˆ PDF" +saveChanges = "äæå­˜ę›“ę”¹" [pdfTextEditor.options.autoScaleText] title = "č‡ŖåŠØē¼©ę”¾ę–‡ęœ¬ä»„é€‚é…ę”†" @@ -5890,6 +6062,8 @@ alpha = "ę­¤ Alpha é¢„č§ˆå™Øä»åœØę¼”čæ›äø­ā€”ā€”ęŸäŗ›å­—ä½“ć€é¢œč‰²ć€é€ę˜Ž [pdfTextEditor.empty] title = "ęœŖåŠ č½½ę–‡ę”£" subtitle = "加载 PDF ꈖ JSON ę–‡ä»¶ä»„å¼€å§‹ē¼–č¾‘ę–‡ęœ¬å†…å®¹ć€‚" +dropzone = "将 PDF ꈖ JSON ę–‡ä»¶ę‹–ę”¾åˆ°ę­¤å¤„ļ¼Œęˆ–ē‚¹å‡»ęµč§ˆ" +dropzoneWithFiles = "ä»Žā€œę–‡ä»¶ā€é€‰é”¹å”é€‰ę‹©ę–‡ä»¶ļ¼Œęˆ–å°† PDF ꈖ JSON ę–‡ä»¶ę‹–ę”¾åˆ°ę­¤å¤„ļ¼Œęˆ–ē‚¹å‡»ęµč§ˆ" [pdfTextEditor.welcomeBanner] title = "ę¬¢čæŽä½æē”Ø PDF ę–‡ęœ¬ē¼–č¾‘å™Øļ¼ˆęŠ¢å…ˆä½“éŖŒļ¼‰" diff --git a/frontend/public/locales/zh-CN/translation.toml b/frontend/public/locales/zh-CN/translation.toml index 2d166272c5..ba345187f4 100644 --- a/frontend/public/locales/zh-CN/translation.toml +++ b/frontend/public/locales/zh-CN/translation.toml @@ -163,6 +163,11 @@ unfavorite = "ä»Žę”¶č—äø­ē§»é™¤" fullscreen = "åˆ‡ę¢åˆ°å…Øå±ęØ”å¼" sidebar = "åˆ‡ę¢åˆ°ä¾§č¾¹ę ęØ”å¼" +[backendStartup] +notFoundTitle = "ęœŖę‰¾åˆ°åŽē«Æ" +retry = "é‡čÆ•" +unreachable = "åŗ”ē”ØēØ‹åŗå½“å‰ę— ę³•čæžęŽ„åˆ°åŽē«Æć€‚čÆ·ę£€ęŸ„åŽē«ÆēŠ¶ę€å’Œē½‘ē»œčæžęŽ„ļ¼Œē„¶åŽé‡čÆ•ć€‚" + [zipWarning] title = "大型 ZIP ꖇ件" message = "ę­¤ ZIP 包含 {{count}} äøŖę–‡ä»¶ć€‚ä»č¦č§£åŽ‹ļ¼Ÿ" @@ -912,6 +917,9 @@ desc = "é€ščæ‡äø²č” PDF ę“ä½œęž„å»ŗå¤šę­„å·„ä½œęµć€‚é€‚åˆé‡å¤ę€§ä»»åŠ”ć€‚" desc = "将一个 PDF å åŠ åœØå¦äø€äøŖä¹‹äøŠ" title = "叠加 PDF" +[home.pdfTextEditor] +title = "PDF ę–‡ęœ¬ē¼–č¾‘å™Ø" +desc = "编辑 PDF äø­ēš„ēŽ°ęœ‰ę–‡ęœ¬å’Œå›¾åƒ" [home.addText] tags = "ę–‡ęœ¬,ę³Øé‡Š,标签" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "ę‰‹å†™ē­¾å" defaultImageLabel = "å·²äøŠä¼ ē­¾å" defaultTextLabel = "é”®å…„ē­¾å" saveButton = "äæå­˜ē­¾å" +savePersonal = "äæå­˜åˆ°äøŖäŗŗ" +saveShared = "äæå­˜åˆ°å…±äŗ«" saveUnavailable = "čÆ·å…ˆåˆ›å»ŗē­¾åå†äæå­˜ć€‚" noChanges = "å½“å‰ē­¾åå·²äæå­˜ć€‚" +tempStorageTitle = "äø“ę—¶ęµč§ˆå™Øå­˜å‚Ø" +tempStorageDescription = "ē­¾åä»…å­˜å‚ØåœØę‚Øēš„ęµč§ˆå™Øäø­ć€‚ęø…é™¤ęµč§ˆå™Øę•°ę®ęˆ–ę›“ę¢ęµč§ˆå™ØåŽå°†ä¼šäø¢å¤±ć€‚" +personalHeading = "äøŖäŗŗē­¾å" +sharedHeading = "å…±äŗ«ē­¾å" +personalDescription = "åŖęœ‰ę‚ØåÆä»„ēœ‹åˆ°čæ™äŗ›ē­¾åć€‚" +sharedDescription = "ę‰€ęœ‰ē”Øęˆ·éƒ½åÆä»„ęŸ„ēœ‹å¹¶ä½æē”Øčæ™äŗ›ē­¾åć€‚" [sign.saved.type] canvas = "手写" @@ -2574,7 +2590,7 @@ chooseP12File = "选ꋩ PKCS12 ꖇ件" choosePfxFile = "选ꋩ PFX ꖇ件" choosePrivateKey = "选择私钄文件" location = "ä½ē½®" -logoTitle = "Logo" +logoTitle = "徽标" name = "åē§°" noLogo = "ꗠ Logo" pageNumber = "锵码" @@ -3020,6 +3036,91 @@ title = "čŽ·å– PDF 俔息" header = "čŽ·å– PDF 俔息" submit = "čŽ·å–äæ”ęÆ" downloadJson = "äø‹č½½ JSON" +processing = "ę­£åœØęå–äæ”ęÆ..." +results = "ē»“ęžœ" +noResults = "čæč”ŒčÆ„å·„å…·ä»„ē”ŸęˆęŠ„å‘Šć€‚" +downloads = "äø‹č½½" +noneDetected = "ęœŖę£€ęµ‹åˆ°" +indexTitle = "瓢引" + +[getPdfInfo.report] +entryLabel = "å®Œę•“äæ”ęÆę‘˜č¦" +shortTitle = "PDF 俔息" + +[getPdfInfo.sections] +metadata = "å…ƒę•°ę®" +formFields = "č”Øå•å­—ę®µ" +basicInfo = "基本俔息" +documentInfo = "文攣俔息" +compliance = "åˆč§„ę€§" +encryption = "åŠ åÆ†" +permissions = "ꝃ限" +other = "其他" +perPageInfo = "ęÆé”µäæ”ęÆ" +tableOfContents = "目录" + +[getPdfInfo.other] +attachments = "附件" +embeddedFiles = "åµŒå…„ę–‡ä»¶" +javaScript = "JavaScript" +layers = "图层" +structureTree = "ē»“ęž„ę ‘" +xmp = "XMP å…ƒę•°ę®" + +[getPdfInfo.perPage] +size = "尺寸" +annotations = "ę³Øé‡Š" +images = "图像" +links = "é“¾ęŽ„" +fonts = "字体" +xobjects = "XObject ꕰ量" +multimedia = "å¤šåŖ’ä½“" + +[getPdfInfo.summary] +pages = "锵ꕰ" +fileSize = "ę–‡ä»¶å¤§å°" +pdfVersion = "PDF ē‰ˆęœ¬" +language = "语言" +title = "PDF ę‘˜č¦" +author = "ä½œč€…" +created = "åˆ›å»ŗę—¶é—“" +modified = "修改时闓" +permsAll = "å·²å…č®øę‰€ęœ‰ęƒé™" +permsRestricted = "{{count}} 锹限制" +permsMixed = "éƒØåˆ†ęƒé™å—é™" +hasCompliance = "ē¬¦åˆåˆč§„ę ‡å‡†" +noCompliance = "ę— åˆč§„ę ‡å‡†" +basic = "基本俔息" +documentInfo = "文攣俔息" +securityTitle = "å®‰å…ØēŠ¶ę€" +technical = "ęŠ€ęœÆ" +overviewTitle = "PDF ę¦‚č§ˆ" + +[getPdfInfo.summary.security] +encrypted = "å·²åŠ åÆ† PDF - å­˜åœØåÆ†ē äæęŠ¤" +unencrypted = "ęœŖåŠ åÆ† PDF - ę— åÆ†ē äæęŠ¤" + +[getPdfInfo.summary.tech] +images = "图像" +fonts = "字体" +formFields = "č”Øå•å­—ę®µ" +embeddedFiles = "åµŒå…„ę–‡ä»¶" +javaScript = "JavaScript" +layers = "图层" +bookmarks = "书签" +multimedia = "å¤šåŖ’ä½“" + +[getPdfInfo.summary.overview] +untitled = "ęœŖå‘½åę–‡ę”£" +unknown = "ęœŖēŸ„ä½œč€…" +text = "čæ™ę˜Æäø€äøŖ {{pages}} é”µēš„ PDFļ¼Œę ‡é¢˜äøŗ {{title}}ļ¼Œä½œč€…äøŗ {{author}}(PDF ē‰ˆęœ¬ {{version}})。" + +[getPdfInfo.error] +partial = "éƒØåˆ†ę–‡ä»¶ę— ę³•å¤„ē†ć€‚" +unexpected = "ęå–čæ‡ēØ‹äø­å‘ē”Ÿę„å¤–é”™čÆÆć€‚" + +[getPdfInfo.status] +complete = "ęå–å®Œęˆ" [extractPage] tags = "ęå–" @@ -3438,6 +3539,9 @@ signinTitle = "请登录" ssoSignIn = "é€ščæ‡å•ē‚¹ē™»å½•ē™»å½•" oAuth2AutoCreateDisabled = "OAuth2 č‡ŖåŠØåˆ›å»ŗē”Øęˆ·å·²ē¦ē”Ø" oAuth2AdminBlockedUser = "ē›®å‰å·²é˜»ę­¢ęœŖę³Øå†Œē”Øęˆ·ēš„ę³Øå†Œęˆ–ē™»å½•ć€‚čÆ·č”ē³»ē®”ē†å‘˜ć€‚" +oAuth2RequiresLicense = "OAuth/SSO ē™»å½•éœ€č¦ä»˜č“¹č®øåÆļ¼ˆServer ꈖ Enterpriseļ¼‰ć€‚čÆ·č”ē³»ē®”ē†å‘˜ä»„å‡ēŗ§ę‚Øēš„č®”åˆ’ć€‚" +saml2RequiresLicense = "SAML ē™»å½•éœ€č¦ä»˜č“¹č®øåÆļ¼ˆServer ꈖ Enterpriseļ¼‰ć€‚čÆ·č”ē³»ē®”ē†å‘˜ä»„å‡ēŗ§ę‚Øēš„č®”åˆ’ć€‚" +maxUsersReached = "ę‚Øå½“å‰ēš„č®øåÆå·²č¾¾åˆ°ē”Øęˆ·ę•°é‡äøŠé™ć€‚čÆ·č”ē³»ē®”ē†å‘˜ä»„å‡ēŗ§ę‚Øēš„č®”åˆ’ęˆ–å¢žåŠ åø­ä½ć€‚" oauth2RequestNotFound = "ę‰¾äøåˆ°éŖŒčÆčÆ·ę±‚" oauth2InvalidUserInfoResponse = "ę— ę•ˆēš„ē”Øęˆ·äæ”ęÆå“åŗ”" oauth2invalidRequest = "ę— ę•ˆčÆ·ę±‚" @@ -3846,14 +3950,17 @@ fitToWidth = "é€‚é…å®½åŗ¦" actualSize = "实际大小" [viewer] +cannotPreviewFile = "ę— ę³•é¢„č§ˆę–‡ä»¶" +dualPageView = "åŒé”µč§†å›¾" firstPage = "第一锵" lastPage = "ęœ€åŽäø€é”µ" -previousPage = "äøŠäø€é”µ" nextPage = "下一锵" +onlyPdfSupported = "čÆ„ęŸ„ēœ‹å™Øä»…ę”ÆęŒ PDF ę–‡ä»¶ć€‚ę­¤ę–‡ä»¶ä¼¼ä¹Žę˜Æå…¶ä»–ę ¼å¼ć€‚" +previousPage = "äøŠäø€é”µ" +singlePageView = "å•é”µč§†å›¾" +unknownFile = "ęœŖēŸ„ę–‡ä»¶" zoomIn = "放大" zoomOut = "ē¼©å°" -singlePageView = "å•é”µč§†å›¾" -dualPageView = "åŒé”µč§†å›¾" [rightRail] closeSelected = "关闭所选文件" @@ -3877,6 +3984,7 @@ toggleSidebar = "åˆ‡ę¢ä¾§č¾¹ę " exportSelected = "åÆ¼å‡ŗę‰€é€‰é”µé¢" toggleAnnotations = "åˆ‡ę¢ę³Øé‡ŠåÆč§ę€§" annotationMode = "åˆ‡ę¢ę³Øé‡ŠęØ”å¼" +print = "ę‰“å° PDF" draw = "绘制" save = "äæå­˜" saveChanges = "äæå­˜ę›“ę”¹" @@ -4494,6 +4602,7 @@ description = "Impressum ēš„ URL ęˆ–ę–‡ä»¶åļ¼ˆęŸäŗ›åøę³•č¾–åŒŗč¦ę±‚ļ¼‰" title = "é«˜ēŗ§ē‰ˆäøŽä¼äøšē‰ˆ" description = "é…ē½®ę‚Øēš„é«˜ēŗ§ē‰ˆęˆ–ä¼äøšē‰ˆč®øåÆčÆåÆ†é’„ć€‚" license = "č®øåÆčÆé…ē½®" +noInput = "čÆ·ęä¾›č®øåÆčÆåÆ†é’„ęˆ–ę–‡ä»¶" [admin.settings.premium.licenseKey] toggle = "ęœ‰č®øåÆčÆåÆ†é’„ęˆ–čÆä¹¦ę–‡ä»¶å—ļ¼Ÿ" @@ -4511,6 +4620,25 @@ line1 = "č¦†ē›–å½“å‰č®øåÆčÆåÆ†é’„åŽå°†ę— ę³•ę’¤é”€ć€‚" line2 = "é™¤éžå·²åœØå…¶ä»–ä½ē½®å¤‡ä»½ļ¼Œå¦åˆ™ę‚Øä¹‹å‰ēš„č®øåÆčÆå°†č¢«ę°øä¹…äø¢å¤±ć€‚" line3 = "é‡č¦ļ¼ščÆ·å¦„å–„äæē®”č®øåÆčÆåÆ†é’„ļ¼Œåˆ‡å‹æå…¬å¼€åˆ†äŗ«ć€‚" +[admin.settings.premium.inputMethod] +text = "č®øåÆčÆåÆ†é’„" +file = "证书文件" + +[admin.settings.premium.file] +label = "č®øåÆčÆčÆä¹¦ę–‡ä»¶" +description = "äøŠä¼ ę‚Øēŗæäø‹č“­ä¹°ēš„ .lic ꈖ .cert č®øåÆčÆę–‡ä»¶" +choose = "é€‰ę‹©č®øåÆčÆę–‡ä»¶" +selected = "å·²é€‰ę‹©ļ¼š{{filename}}({{size}})" +successMessage = "č®øåÆčÆę–‡ä»¶å·²ęˆåŠŸäøŠä¼ å¹¶ęæ€ę“»ć€‚ę— éœ€é‡åÆć€‚" + +[admin.settings.premium.currentLicense] +title = "å·²ęæ€ę“»ēš„č®øåÆčÆ" +file = "ę„ęŗļ¼šč®øåÆčÆę–‡ä»¶ļ¼ˆ{{path}})" +key = "ę„ęŗļ¼šč®øåÆčÆåÆ†é’„" +type = "ē±»åž‹ļ¼š{{type}}" +noInput = "čÆ·ęä¾›č®øåÆčÆåÆ†é’„ęˆ–äøŠä¼ čÆä¹¦ę–‡ä»¶" +success = "成功" + [admin.settings.premium.enabled] label = "åÆē”Øé«˜ēŗ§åŠŸčƒ½" description = "åÆē”ØåÆ¹äø“äøš/ä¼äøšåŠŸčƒ½ēš„č®øåÆčÆåÆ†é’„ę£€ęŸ„" @@ -4622,7 +4750,7 @@ searchFiles = "ęœē“¢ę–‡ä»¶ā€¦" recent = "ęœ€čæ‘" localFiles = "ęœ¬åœ°ę–‡ä»¶" googleDrive = "Google äŗ‘ē«Æē”¬ē›˜" -googleDriveShort = "Drive" +googleDriveShort = "äŗ‘ē«Æē”¬ē›˜" myFiles = "ęˆ‘ēš„ę–‡ä»¶" noRecentFiles = "ęœŖę‰¾åˆ°ęœ€čæ‘ę–‡ä»¶" googleDriveNotAvailable = "äøåÆä½æē”Ø Google äŗ‘ē«Æē”¬ē›˜é›†ęˆ" @@ -4644,7 +4772,9 @@ selectedCount = "已选 {{count}}" download = "äø‹č½½" delete = "删除" unsupported = "äøę”ÆęŒ" +active = "ꓻ跃" addToUpload = "ę·»åŠ č‡³äøŠä¼ " +closeFile = "关闭文件" deleteAll = "åˆ é™¤å…ØéƒØ" loadingFiles = "ę­£åœØåŠ č½½ę–‡ä»¶..." noFiles = "ę²”ęœ‰åÆē”Øēš„ę–‡ä»¶" @@ -5132,7 +5262,7 @@ upgrade = "ē«‹å³å‡ēŗ§ →" freeTitle = "ęœåŠ”å™Øč®øåÆčÆ" overLimitTitle = "éœ€č¦ęœåŠ”å™Øč®øåÆčÆ" overLimitBody = "ęˆ‘ä»¬ēš„č®øåÆå…č®øęÆå°ęœåŠ”å™Øęœ€å¤šå…č“¹ {{freeTierLimit}} åē”Øęˆ·ć€‚ę‚Øęœ‰ {{overLimitUserCopy}} 名 Stirling ē”Øęˆ·ć€‚äøŗäøé—“ę–­ä½æē”Øļ¼ŒčÆ·å‡ēŗ§č‡³ Stirling Server ę–¹ę”ˆ - ę— é™åø­ä½ć€PDF ę–‡ęœ¬ē¼–č¾‘ļ¼Œä»„åŠ $99/server/mo ēš„å®Œę•“ē®”ē†å‘˜ęŽ§åˆ¶ć€‚" -freeBody = "ęˆ‘ä»¬ēš„ Open-Core č®øåÆå…č®øęÆå°ęœåŠ”å™Øęœ€å¤šå…č“¹ {{freeTierLimit}} åē”Øęˆ·ć€‚äøŗę— ē¼ę‰©å±•å¹¶ęŠ¢å…ˆä½“éŖŒå…Øę–°ēš„ PDF ę–‡ęœ¬ē¼–č¾‘å·„å…·ļ¼ŒęŽØč Stirling Server ę–¹ę”ˆ - å®Œę•“ē¼–č¾‘äøŽ ę— é™åø­ä½ļ¼Œ$99/server/mo怂" +freeBody = "ęˆ‘ä»¬ēš„ Open-Core č®øåÆå…č®øęÆå°ęœåŠ”å™Øęœ€å¤š {{freeTierLimit}} åē”Øęˆ·å…č“¹ä½æē”Øć€‚äøŗå®žēŽ°äøé—“ę–­ę‰©å±•ļ¼Œęˆ‘ä»¬ęŽØč Stirling Server ę–¹ę”ˆ - ę— é™åø­ä½ 和 SSO ę”ÆęŒļ¼Œ$99/server/mo." [onboarding.desktopInstall] title = "äø‹č½½" @@ -5237,6 +5367,31 @@ error = "ę›“ę–°ē”Øęˆ·ēŠ¶ę€å¤±č“„" success = "ē”Øęˆ·åˆ é™¤ęˆåŠŸ" error = "åˆ é™¤ē”Øęˆ·å¤±č“„" +[workspace.people.changePassword] +action = "曓改密码" +title = "曓改密码" +subtitle = "äøŗä»„äø‹ē”Øęˆ·ę›“ę–°åÆ†ē " +newPassword = "新密码" +confirmPassword = "甮认密码" +placeholder = "输兄新密码" +confirmPlaceholder = "å†ę¬”č¾“å…„ę–°åÆ†ē " +passwordRequired = "请输兄新密码" +passwordMismatch = "äø¤ę¬”č¾“å…„ēš„åÆ†ē äøäø€č‡“" +generateRandom = "ē”Ÿęˆå®‰å…ØåÆ†ē " +generatedPreview = "ē”Ÿęˆēš„åÆ†ē ļ¼š" +copyTooltip = "å¤åˆ¶åˆ°å‰Ŗč““ęæ" +copiedToClipboard = "åÆ†ē å·²å¤åˆ¶åˆ°å‰Ŗč““ęæ" +copyFailed = "å¤åˆ¶åÆ†ē å¤±č“„" +sendEmail = "å‘ē”Øęˆ·å‘é€ęœ‰å…³ę­¤ę›“ę”¹ēš„é‚®ä»¶" +includePassword = "åœØé‚®ä»¶äø­åŒ…å«ę–°åÆ†ē " +forcePasswordChange = "å¼ŗåˆ¶ē”Øęˆ·äø‹ę¬”ē™»å½•ę—¶ę›“ę”¹åÆ†ē " +emailUnavailable = "čÆ„ē”Øęˆ·ēš„é‚®ē®±åœ°å€ę— ę•ˆć€‚é€šēŸ„å·²ē¦ē”Øć€‚" +smtpDisabled = "é‚®ä»¶é€šēŸ„éœ€č¦åœØč®¾ē½®äø­åÆē”Ø SMTP怂" +notifyOnly = "å°†å‘é€äøå«åÆ†ē ēš„é‚®ä»¶ļ¼Œå‘ŠēŸ„ē”Øęˆ·ē®”ē†å‘˜å·²ę›“ę”¹äŗ†åÆ†ē ć€‚" +submit = "曓新密码" +success = "åÆ†ē ę›“ę–°ęˆåŠŸ" +error = "曓新密码失蓄" + [workspace.people.emailInvite] tab = "电子邮件邀请" description = "åœØäø‹ę–¹č¾“å…„ęˆ–ē²˜č““ē”µå­é‚®ä»¶åœ°å€ļ¼Œē”Øé€—å·åˆ†éš”ć€‚ē”Øęˆ·å°†é€ščæ‡ē”µå­é‚®ä»¶ę”¶åˆ°ē™»å½•å‡­ę®ć€‚" @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "č‡³å°‘éœ€č¦äø€äøŖē”µå­é‚®ä»¶åœ°å€" submit = "å‘é€é‚€čÆ·" success = "å·²ęˆåŠŸé‚€čÆ·ē”Øęˆ·" -partialSuccess = "éƒØåˆ†é‚€čÆ·å¤±č“„" +partialFailure = "éƒØåˆ†é‚€čÆ·å¤±č“„" allFailed = "é‚€čÆ·ē”Øęˆ·å¤±č“„" error = "å‘é€é‚€čÆ·å¤±č“„" @@ -5770,6 +5925,7 @@ subtitle = "ä½æē”Øę‚Øēš„ Stirling č“¦ęˆ·ē™»å½•" [setup.selfhosted] title = "ē™»å½•åˆ°ęœåŠ”å™Ø" subtitle = "č¾“å…„ę‚Øēš„ęœåŠ”å™Øå‡­ę®" +link = "ęˆ–čæžęŽ„åˆ°č‡Ŗę‰˜ē®”č“¦ęˆ·" [setup.server] title = "čæžęŽ„åˆ°ęœåŠ”å™Ø" @@ -5788,6 +5944,14 @@ description = "č¾“å…„ę‚Øč‡Ŗę‰˜ē®” Stirling PDF ęœåŠ”å™Øēš„å®Œę•“ URL" emptyUrl = "čÆ·č¾“å…„ęœåŠ”å™Ø URL" unreachable = "ę— ę³•čæžęŽ„åˆ°ęœåŠ”å™Ø" testFailed = "čæžęŽ„ęµ‹čÆ•å¤±č“„" +configFetch = "čŽ·å–ęœåŠ”å™Øé…ē½®å¤±č“„ć€‚čÆ·ę£€ęŸ„ URL å¹¶é‡čÆ•ć€‚" + +[setup.server.error.securityDisabled] +title = "ęœŖåÆē”Øē™»å½•" +body = "ę­¤ęœåŠ”å™ØęœŖåÆē”Øē™»å½•ć€‚č¦čæžęŽ„åˆ°ę­¤ęœåŠ”å™Øļ¼Œåæ…é”»åÆē”Øčŗ«ä»½éŖŒčÆļ¼š" +step1 = "åœØēŽÆå¢ƒäø­č®¾ē½® DOCKER_ENABLE_SECURITY=true" +step2 = "ęˆ–åœØ settings.yml 中设置 security.enableLogin=true" +step3 = "é‡åÆęœåŠ”å™Ø" [setup.login] title = "登录" @@ -5797,6 +5961,13 @@ submit = "登录" signInWith = "ä½æē”Øä»„äø‹ę–¹å¼ē™»å½•" oauthPending = "ę­£åœØę‰“å¼€ęµč§ˆå™Øčæ›č”Œč®¤čÆ..." orContinueWith = "ęˆ–ä½æē”Ø Email ē»§ē»­" +serverRequirement = "ę³Øę„ļ¼šęœåŠ”å™Øåæ…é”»åÆē”Øē™»å½•åŠŸčƒ½ć€‚" +showInstructions = "å¦‚ä½•åÆē”Øļ¼Ÿ" +hideInstructions = "éšč—čÆ“ę˜Ž" +instructions = "č¦åœØę‚Øēš„ Stirling PDF ęœåŠ”å™ØäøŠåÆē”Øē™»å½•ļ¼š" +instructionsEnvVar = "č®¾ē½®ēŽÆå¢ƒå˜é‡ļ¼š" +instructionsOrYml = "ęˆ–åœØ settings.yml 中:" +instructionsRestart = "ē„¶åŽé‡åÆęœåŠ”å™Øä»„ä½æę›“ę”¹ē”Ÿę•ˆć€‚" [setup.login.username] label = "ē”Øęˆ·å" @@ -5853,6 +6024,7 @@ earlyAccess = "ęŠ¢å…ˆä½“éŖŒ" reset = "é‡ē½®ę›“ę”¹" downloadJson = "äø‹č½½ JSON" generatePdf = "ē”Ÿęˆ PDF" +saveChanges = "äæå­˜ę›“ę”¹" [pdfTextEditor.options.autoScaleText] title = "č‡ŖåŠØē¼©ę”¾ę–‡ęœ¬ä»„é€‚é…ę”†ä½“" @@ -5890,6 +6062,8 @@ alpha = "ę­¤ Alpha é¢„č§ˆå™Øä»åœØę¼”čæ›äø­ā€”ā€”ęŸäŗ›å­—ä½“ć€é¢œč‰²ć€é€ę˜Ž [pdfTextEditor.empty] title = "ęœŖåŠ č½½ę–‡ę”£" subtitle = "加载 PDF ꈖ JSON ę–‡ä»¶ä»„å¼€å§‹ē¼–č¾‘ę–‡ęœ¬å†…å®¹ć€‚" +dropzone = "将 PDF ꈖ JSON ę–‡ä»¶ę‹–ę”¾åˆ°ę­¤å¤„ļ¼Œęˆ–ē‚¹å‡»ęµč§ˆ" +dropzoneWithFiles = "ä»Žā€œę–‡ä»¶ā€é€‰é”¹å”é€‰ę‹©ę–‡ä»¶ļ¼Œęˆ–å°† PDF ꈖ JSON ę–‡ä»¶ę‹–ę”¾åˆ°ę­¤å¤„ļ¼Œęˆ–ē‚¹å‡»ęµč§ˆ" [pdfTextEditor.welcomeBanner] title = "ę¬¢čæŽä½æē”Ø PDF ę–‡ęœ¬ē¼–č¾‘å™Øļ¼ˆęŠ¢å…ˆä½“éŖŒļ¼‰" diff --git a/frontend/public/locales/zh-TW/translation.toml b/frontend/public/locales/zh-TW/translation.toml index 55b4e6b52b..a15298988e 100644 --- a/frontend/public/locales/zh-TW/translation.toml +++ b/frontend/public/locales/zh-TW/translation.toml @@ -163,6 +163,11 @@ unfavorite = "å¾žęˆ‘ēš„ęœ€ę„›ē§»é™¤" fullscreen = "åˆ‡ę›č‡³å…Øčž¢å¹•ęØ”å¼" sidebar = "åˆ‡ę›č‡³å“é‚Šę¬„ęØ”å¼" +[backendStartup] +notFoundTitle = "ę‰¾äøåˆ°å¾Œē«Æ" +retry = "é‡č©¦" +unreachable = "ę‡‰ē”ØēØ‹å¼ē›®å‰ē„”ę³•é€£ē·šč‡³å¾Œē«Æć€‚č«‹ē¢ŗčŖå¾Œē«Æē‹€ę…‹čˆ‡ē¶²č·Æé€£ē·šļ¼Œē„¶å¾Œå†č©¦äø€ę¬”ć€‚" + [zipWarning] title = "大型 ZIP ęŖ”ę”ˆ" message = "ę­¤ ZIP 包含 {{count}} å€‹ęŖ”ę”ˆć€‚ä»č¦č§£å£“ēø®å—Žļ¼Ÿ" @@ -912,6 +917,9 @@ desc = "將多個 PDF å‹•ä½œäø²ęŽ„ļ¼Œå»ŗē«‹å¤šę­„é©Ÿå·„ä½œęµēØ‹ć€‚é©åˆé‡č¤‡ desc = "將 PDF č¦†č“‹åœØå¦äø€å€‹ PDF 上" title = "覆蓋 PDF" +[home.pdfTextEditor] +title = "PDF 文字編輯器" +desc = "編輯 PDF äø­ę—¢ęœ‰ēš„ę–‡å­—čˆ‡åœ–ē‰‡" [home.addText] tags = "文字,註解,標籤" @@ -1173,7 +1181,7 @@ selectFilesPlaceholder = "åœØäø»č¦–åœ–éøå–ęŖ”ę”ˆä»„é–‹å§‹" settings = "設定" conversionCompleted = "č½‰ę›å®Œęˆ" results = "ēµęžœ" -defaultFilename = "converted_file" +defaultFilename = "å·²č½‰ę›_ęŖ”ę”ˆ" conversionResults = "č½‰ę›ēµęžœ" convertFrom = "ä¾†ęŗę ¼å¼" convertTo = "ē›®ęØ™ę ¼å¼" @@ -1360,7 +1368,7 @@ title = "ę–°å¢žęµ®ę°“å°" desc = "å°‡ę–‡å­—ęˆ–å½±åƒęµ®ę°“å°åŠ å…„ PDF ęŖ”ę”ˆ" completed = "å·²åŠ å…„ęµ®ę°“å°" submit = "ę–°å¢žęµ®ę°“å°" -filenamePrefix = "watermarked" +filenamePrefix = "已加_ęµ®ę°“å°" [watermark.error] failed = "為 PDF ę–°å¢žęµ®ę°“å°ę™‚ē™¼ē”ŸéŒÆčŖ¤ć€‚" @@ -2259,8 +2267,16 @@ defaultCanvasLabel = "ę‰‹ē¹Ŗē°½å" defaultImageLabel = "äøŠå‚³ēš„ē°½å" defaultTextLabel = "č¼øå…„ēš„ē°½å" saveButton = "å„²å­˜ē°½å" +savePersonal = "å„²å­˜ē‚ŗå€‹äŗŗ" +saveShared = "å„²å­˜ē‚ŗå…±ē”Ø" saveUnavailable = "č«‹å…ˆå»ŗē«‹ē°½åę‰čƒ½å„²å­˜ć€‚" noChanges = "ē›®å‰ē°½åå·²å„²å­˜ć€‚" +tempStorageTitle = "ē€č¦½å™Øęš«å­˜" +tempStorageDescription = "ē°½ååƒ…å„²å­˜åœØę‚Øēš„ē€č¦½å™Øäø­ć€‚č‹„ęø…é™¤ē€č¦½č³‡ę–™ęˆ–ę›“ę›ē€č¦½å™Øļ¼Œå°‡ęœƒéŗå¤±ć€‚" +personalHeading = "å€‹äŗŗē°½å" +sharedHeading = "å…±ē”Øē°½å" +personalDescription = "åŖęœ‰ę‚Øčƒ½ēœ‹åˆ°é€™äŗ›ē°½åć€‚" +sharedDescription = "ę‰€ęœ‰ä½æē”Øč€…éƒ½åÆä»„ęŸ„ēœ‹äø¦ä½æē”Øé€™äŗ›ē°½åć€‚" [sign.saved.type] canvas = "手繪" @@ -3020,6 +3036,91 @@ title = "取得 PDF č³‡čØŠ" header = "取得 PDF č³‡čØŠ" submit = "å–å¾—č³‡čØŠ" downloadJson = "下載 JSON" +processing = "ę­£åœØę“·å–č³‡čØŠ..." +results = "ēµęžœ" +noResults = "åŸ·č”Œę­¤å·„å…·ä»„ē”¢ē”Ÿå ±å‘Šć€‚" +downloads = "下載" +noneDetected = "未偵測到" +indexTitle = "瓢引" + +[getPdfInfo.report] +entryLabel = "å®Œę•“č³‡čØŠę‘˜č¦" +shortTitle = "PDF č³‡čØŠ" + +[getPdfInfo.sections] +metadata = "中繼資料" +formFields = "č”Øå–®ę¬„ä½" +basicInfo = "åŸŗęœ¬č³‡čØŠ" +documentInfo = "ę–‡ä»¶č³‡čØŠ" +compliance = "ē¬¦åˆę€§" +encryption = "åŠ åÆ†" +permissions = "ꬊ限" +other = "其他" +perPageInfo = "ęÆé č³‡čØŠ" +tableOfContents = "ē›®éŒ„" + +[getPdfInfo.other] +attachments = "附件" +embeddedFiles = "å…§åµŒęŖ”ę”ˆ" +javaScript = "JavaScript" +layers = "圖層" +structureTree = "結構樹" +xmp = "XMPMetadata" + +[getPdfInfo.perPage] +size = "尺寸" +annotations = "註解" +images = "影像" +links = "連結" +fonts = "字型" +xobjects = "XObject ę•øé‡" +multimedia = "å¤šåŖ’é«”" + +[getPdfInfo.summary] +pages = "頁數" +fileSize = "ęŖ”ę”ˆå¤§å°" +pdfVersion = "PDF ē‰ˆęœ¬" +language = "čŖžčØ€" +title = "PDF ę‘˜č¦" +author = "ä½œč€…" +created = "建立時間" +modified = "修改時間" +permsAll = "å…čØ±ę‰€ęœ‰ę¬Šé™" +permsRestricted = "{{count}} 個限制" +permsMixed = "éƒØåˆ†ę¬Šé™å—é™" +hasCompliance = "å…·ęœ‰ē¬¦åˆę€§ęØ™ęŗ–" +noCompliance = "ē„”ē¬¦åˆę€§ęØ™ęŗ–" +basic = "åŸŗęœ¬č³‡čØŠ" +documentInfo = "ę–‡ä»¶č³‡čØŠ" +securityTitle = "安全性狀態" +technical = "ęŠ€č”“č³‡čØŠ" +overviewTitle = "PDF 概覽" + +[getPdfInfo.summary.security] +encrypted = "å·²åŠ åÆ†ēš„ PDF - å…·ęœ‰åÆ†ē¢¼äæč­·" +unencrypted = "ęœŖåŠ åÆ†ēš„ PDF - ē„”åÆ†ē¢¼äæč­·" + +[getPdfInfo.summary.tech] +images = "影像" +fonts = "字型" +formFields = "č”Øå–®ę¬„ä½" +embeddedFiles = "å…§åµŒęŖ”ę”ˆ" +javaScript = "JavaScript" +layers = "圖層" +bookmarks = "書籤" +multimedia = "å¤šåŖ’é«”" + +[getPdfInfo.summary.overview] +untitled = "ęœŖå‘½åę–‡ä»¶" +unknown = "ä½œč€…äøč©³" +text = "é€™ę˜Æäø€ä»½ {{pages}} é ēš„ PDFļ¼ŒęØ™é”Œē‚ŗ {{title}}ļ¼Œä½œč€… {{author}}(PDF ē‰ˆęœ¬ {{version}})。" + +[getPdfInfo.error] +partial = "éƒØåˆ†ęŖ”ę”ˆē„”ę³•č™•ē†ć€‚" +unexpected = "ę“·å–éŽēØ‹ē™¼ē”Ÿéžé ęœŸéŒÆčŖ¤ć€‚" + +[getPdfInfo.status] +complete = "ę“·å–å®Œęˆ" [extractPage] tags = "ęå–" @@ -3438,6 +3539,9 @@ signinTitle = "請登兄" ssoSignIn = "透過 SSO 單一登兄" oAuth2AutoCreateDisabled = "OAuth 2.0 č‡Ŗå‹•å»ŗē«‹ä½æē”Øč€…åŠŸčƒ½å·²åœē”Ø" oAuth2AdminBlockedUser = "ē›®å‰äøå…čØ±ęœŖčØ»å†Šēš„ä½æē”Øč€…čØ»å†Šęˆ–ē™»å…„ć€‚č«‹čÆēµ”ē³»ēµ±ē®”ē†å“”ć€‚" +oAuth2RequiresLicense = "OAuth/SSO ē™»å…„éœ€č¦ä»˜č²»ęŽˆę¬Šļ¼ˆServer ꈖ Enterpriseļ¼‰ć€‚č«‹čÆēµ”ē®”ē†å“”å‡ē“šę‚Øēš„ę–¹ę”ˆć€‚" +saml2RequiresLicense = "SAML ē™»å…„éœ€č¦ä»˜č²»ęŽˆę¬Šļ¼ˆServer ꈖ Enterpriseļ¼‰ć€‚č«‹čÆēµ”ē®”ē†å“”å‡ē“šę‚Øēš„ę–¹ę”ˆć€‚" +maxUsersReached = "ę‚Øē›®å‰ēš„ęŽˆę¬Šå·²é”ä½æē”Øč€…äøŠé™ć€‚č«‹čÆēµ”ē®”ē†å“”ä»„å‡ē“šę–¹ę”ˆęˆ–ę–°å¢žåø­ę¬”ć€‚" oauth2RequestNotFound = "ę‰¾äøåˆ°é©—č­‰č«‹ę±‚" oauth2InvalidUserInfoResponse = "ä½æē”Øč€…č³‡čØŠå›žę‡‰ē„”ę•ˆ" oauth2invalidRequest = "č«‹ę±‚ē„”ę•ˆ" @@ -3533,7 +3637,7 @@ title = "PDF č½‰ē‚ŗå–®äø€é é¢" header = "PDF č½‰ē‚ŗå–®äø€é é¢" submit = "č½‰ę›ē‚ŗå–®äø€é é¢" description = "ę­¤å·„å…·ęœƒå°‡ PDF ēš„ę‰€ęœ‰é é¢åˆä½µęˆäø€å€‹å¤§åž‹å–®é ć€‚åÆ¬åŗ¦å°‡čˆ‡åŽŸé ē›øåŒļ¼Œä½†é«˜åŗ¦ęœƒę˜Æę‰€ęœ‰é é¢é«˜åŗ¦ä¹‹ēø½å’Œć€‚" -filenamePrefix = "single_page" +filenamePrefix = "單一_頁面" [pdfToSinglePage.files] placeholder = "åœØäø»ē•«é¢éøę“‡äø€å€‹ PDF 檔開始使用" @@ -3846,14 +3950,17 @@ fitToWidth = "適合寬度" actualSize = "åÆ¦éš›å¤§å°" [viewer] +cannotPreviewFile = "ē„”ę³•é č¦½ęŖ”ę”ˆ" +dualPageView = "雙頁檢視" firstPage = "第一頁" lastPage = "ęœ€å¾Œäø€é " -previousPage = "äøŠäø€é " nextPage = "下一頁" +onlyPdfSupported = "ę­¤ęŖ¢č¦–å™Øåƒ…ę”Æę“ PDF ęŖ”ę”ˆć€‚ę­¤ęŖ”ę”ˆä¼¼ä¹Žę˜Æå…¶ä»–ę ¼å¼ć€‚" +previousPage = "äøŠäø€é " +singlePageView = "單頁檢視" +unknownFile = "ęœŖēŸ„ęŖ”ę”ˆ" zoomIn = "放大" zoomOut = "ēø®å°" -singlePageView = "單頁檢視" -dualPageView = "雙頁檢視" [rightRail] closeSelected = "é—œé–‰å·²éøęŖ”ę”ˆ" @@ -3877,6 +3984,7 @@ toggleSidebar = "åˆ‡ę›å“é‚Šę¬„" exportSelected = "åŒÆå‡ŗéøå–ēš„é é¢" toggleAnnotations = "åˆ‡ę›čØ»č§£åÆč¦‹åŗ¦" annotationMode = "åˆ‡ę›čØ»č§£ęØ”å¼" +print = "列印 PDF" draw = "ē¹Ŗåœ–" save = "儲存" saveChanges = "å„²å­˜č®Šę›“" @@ -4494,6 +4602,7 @@ description = "Impressum ēš„ URL ęˆ–ęŖ”åļ¼ˆęŸäŗ›åøę³•ē®”č½„å€č¦ę±‚ęä¾› title = "Premium 與 Enterprise" description = "čØ­å®šę‚Øēš„ Premium ꈖ Enterprise ęŽˆę¬Šé‡‘é‘°ć€‚" license = "ę““å……ęŽˆę¬ŠčØ­å®š" +noInput = "č«‹ęä¾›ęŽˆę¬Šé‡‘é‘°ęˆ–ęŖ”ę”ˆ" [admin.settings.premium.licenseKey] toggle = "ęœ‰ęŽˆę¬Šé‡‘é‘°ęˆ–ę†‘č­‰ęŖ”å—Žļ¼Ÿ" @@ -4511,6 +4620,25 @@ line1 = "č¦†åÆ«ē›®å‰ēš„ęŽˆę¬Šé‡‘é‘°å¾Œå°‡ē„”ę³•å¾©åŽŸć€‚" line2 = "é™¤éžä½ å¦ęœ‰å‚™ä»½ļ¼Œå¦å‰‡å…ˆå‰ēš„ęŽˆę¬Šå°‡ę°øä¹…éŗå¤±ć€‚" line3 = "é‡č¦ļ¼šč«‹å¦„å–„äæē®”ęŽˆę¬Šé‡‘é‘°äø¦äæęŒē§åÆ†ļ¼Œåˆ‡å‹æå…¬é–‹åˆ†äŗ«ć€‚" +[admin.settings.premium.inputMethod] +text = "ęŽˆę¬Šé‡‘é‘°" +file = "ę†‘č­‰ęŖ”ę”ˆ" + +[admin.settings.premium.file] +label = "ęŽˆę¬Šę†‘č­‰ęŖ”ę”ˆ" +description = "äøŠå‚³ę‚Øé›¢ē·šč³¼č²·ēš„ .lic ꈖ .cert ęŽˆę¬ŠęŖ”ę”ˆ" +choose = "éøę“‡ęŽˆę¬ŠęŖ”ę”ˆ" +selected = "å·²éøå–ļ¼š{{filename}}({{size}})" +successMessage = "ęŽˆę¬ŠęŖ”ę”ˆå·²ęˆåŠŸäøŠå‚³äø¦å•Ÿē”Øļ¼Œē„”éœ€é‡ę–°å•Ÿå‹•ć€‚" + +[admin.settings.premium.currentLicense] +title = "ä½æē”Øäø­ēš„ęŽˆę¬Š" +file = "ä¾†ęŗļ¼šęŽˆę¬ŠęŖ”ę”ˆļ¼ˆ{{path}})" +key = "ä¾†ęŗļ¼šęŽˆę¬Šé‡‘é‘°" +type = "é”žåž‹ļ¼š{{type}}" +noInput = "č«‹ęä¾›ęŽˆę¬Šé‡‘é‘°ęˆ–äøŠå‚³ę†‘č­‰ęŖ”ę”ˆ" +success = "成功" + [admin.settings.premium.enabled] label = "å•Ÿē”Ø Premium 功能" description = "å•Ÿē”Øå°é€²éšŽ/ä¼ę„­åŠŸčƒ½ēš„ęŽˆę¬Šé‡‘é‘°ęŖ¢ęŸ„" @@ -4644,7 +4772,9 @@ selectedCount = "{{count}} 個已選" download = "下載" delete = "åˆŖé™¤" unsupported = "äøę”Æę“" +active = "å•Ÿē”Ø" addToUpload = "åŠ å…„äøŠå‚³" +closeFile = "é—œé–‰ęŖ”ę”ˆ" deleteAll = "å…ØéƒØåˆŖé™¤" loadingFiles = "ę­£åœØč¼‰å…„ęŖ”ę”ˆ..." noFiles = "ę²’ęœ‰åÆē”Øēš„ęŖ”ę”ˆ" @@ -5132,7 +5262,7 @@ upgrade = "ē«‹å³å‡ē“š →" freeTitle = "ä¼ŗęœå™ØęŽˆę¬Š" overLimitTitle = "éœ€č¦ä¼ŗęœå™ØęŽˆę¬Š" overLimitBody = "ęˆ‘å€‘ēš„ęŽˆę¬Šå…čØ±ęÆå°ä¼ŗęœå™Øęœ€å¤š {{freeTierLimit}} ä½ä½æē”Øč€…å…č²»ä½æē”Øć€‚ä½ ęœ‰ {{overLimitUserCopy}} 位 Stirling ä½æē”Øč€…ć€‚č‹„č¦äøäø­ę–·ä½æē”Øļ¼Œč«‹å‡ē“šč‡³ Stirling Server ę–¹ę”ˆ - äøé™åø­ę¬”ć€PDF ę–‡å­—ē·Øč¼Æļ¼Œä»„åŠå®Œę•“ē®”ē†ęŽ§åˆ¶ļ¼ŒęÆå°ä¼ŗęœå™Ø $99/꜈怂" -freeBody = "ęˆ‘å€‘ēš„ Open-Core ęŽˆę¬Šå…čØ±ęÆå°ä¼ŗęœå™Øęœ€å¤š {{freeTierLimit}} ä½ä½æē”Øč€…å…č²»ä½æē”Øć€‚č‹„č¦ē„”ēø«ę““å……äø¦ę¶å…ˆé«”é©—å…Øę–°ēš„ PDF ę–‡å­—ē·Øč¼Æå·„å…·ļ¼Œå»ŗč­°å‡ē“šč‡³ Stirling Server ę–¹ę”ˆ - å®Œę•“ē·Øč¼Æčˆ‡ äøé™åø­ę¬”ļ¼ŒęÆå°ä¼ŗęœå™Ø $99/꜈怂" +freeBody = "ęˆ‘å€‘ēš„ Open-Core ęŽˆę¬Šå…čØ±ęÆå°ä¼ŗęœå™Øęœ€å¤š {{freeTierLimit}} ä½ä½æē”Øč€…å…č²»ä½æē”Øć€‚č‹„č¦ē„”ēø«ę““å……ļ¼Œęˆ‘å€‘å»ŗč­°éøē”Ø Stirling Server ę–¹ę”ˆ - äøé™åø­ę¬” 與 SSO ę”Æę“ļ¼ŒęÆä¼ŗęœå™ØęÆęœˆ $99怂" [onboarding.desktopInstall] title = "下載" @@ -5237,6 +5367,31 @@ error = "曓新使用者狀態失敗" success = "å·²ęˆåŠŸåˆŖé™¤ä½æē”Øč€…" error = "åˆŖé™¤ä½æē”Øč€…å¤±ę•—" +[workspace.people.changePassword] +action = "č®Šę›“åÆ†ē¢¼" +title = "č®Šę›“åÆ†ē¢¼" +subtitle = "為使用者曓新密碼" +newPassword = "新密碼" +confirmPassword = "ē¢ŗčŖåÆ†ē¢¼" +placeholder = "輸兄新密碼" +confirmPlaceholder = "å†ę¬”č¼øå…„ę–°åÆ†ē¢¼" +passwordRequired = "請輸兄新密碼" +passwordMismatch = "åÆ†ē¢¼äøē›øē¬¦" +generateRandom = "ē”¢ē”Ÿå®‰å…ØåÆ†ē¢¼" +generatedPreview = "ē”¢ē”Ÿēš„åÆ†ē¢¼ļ¼š" +copyTooltip = "č¤‡č£½åˆ°å‰Ŗč²¼ē°æ" +copiedToClipboard = "å·²å°‡åÆ†ē¢¼č¤‡č£½åˆ°å‰Ŗč²¼ē°æ" +copyFailed = "焔法複製密碼" +sendEmail = "仄 Email é€šēŸ„ä½æē”Øč€…ę­¤č®Šę›“" +includePassword = "在 Email äø­åŒ…å«ę–°åÆ†ē¢¼" +forcePasswordChange = "å¼·åˆ¶ä½æē”Øč€…äø‹ę¬”ē™»å…„ę™‚č®Šę›“åÆ†ē¢¼" +emailUnavailable = "ę­¤ä½æē”Øč€…ēš„ Email äøę˜Æęœ‰ę•ˆēš„ Email ä½å€ć€‚å·²åœē”Øé€šēŸ„ć€‚" +smtpDisabled = "Email é€šēŸ„éœ€č¦åœØčØ­å®šäø­å•Ÿē”Ø SMTP怂" +notifyOnly = "å°‡å‚³é€äøå«åÆ†ē¢¼ēš„ Emailļ¼Œé€šēŸ„ä½æē”Øč€…ē®”ē†å“”å·²č®Šę›“å…¶åÆ†ē¢¼ć€‚" +submit = "曓新密碼" +success = "åÆ†ē¢¼å·²ęˆåŠŸę›“ę–°" +error = "曓新密碼失敗" + [workspace.people.emailInvite] tab = "é›»å­éƒµä»¶é‚€č«‹" description = "åœØäø‹ę–¹č¼øå…„ęˆ–č²¼äøŠé›»å­éƒµä»¶ļ¼Œä½æē”Øé€—č™Ÿåˆ†éš”ć€‚ä½æē”Øč€…å°‡é€éŽé›»å­éƒµä»¶ę”¶åˆ°ē™»å…„ę†‘č­‰ć€‚" @@ -5245,7 +5400,7 @@ emailsPlaceholder = "user1@example.com, user2@example.com" emailsRequired = "č‡³å°‘éœ€č¦äø€å€‹é›»å­éƒµä»¶åœ°å€" submit = "發送邀請" success = "å·²ęˆåŠŸé‚€č«‹ä½æē”Øč€…" -partialSuccess = "éƒØåˆ†é‚€č«‹å¤±ę•—" +partialFailure = "éƒØåˆ†é‚€č«‹å¤±ę•—" allFailed = "邀請使用者失敗" error = "發送邀請失敗" @@ -5770,6 +5925,7 @@ subtitle = "ä½æē”Øä½ ēš„ Stirling åø³ęˆ¶ē™»å…„" [setup.selfhosted] title = "ē™»å…„ä¼ŗęœå™Ø" subtitle = "č¼øå…„ä½ ēš„ä¼ŗęœå™ØčŖč­‰č³‡čØŠ" +link = "ęˆ–é€£ē·šåˆ°č‡Ŗč”ŒčØ—ē®”ēš„åø³ęˆ¶" [setup.server] title = "é€£ē·šč‡³ä¼ŗęœå™Ø" @@ -5788,6 +5944,14 @@ description = "輸兄你自託箔 Stirling PDF ä¼ŗęœå™Øēš„å®Œę•“ URL" emptyUrl = "č«‹č¼øå…„ä¼ŗęœå™Ø URL" unreachable = "ē„”ę³•é€£ē·šč‡³ä¼ŗęœå™Ø" testFailed = "é€£ē·šęø¬č©¦å¤±ę•—" +configFetch = "ē„”ę³•ę“·å–ä¼ŗęœå™Øēµ„ę…‹ć€‚č«‹ęŖ¢ęŸ„ URL å¾Œå†č©¦äø€ę¬”ć€‚" + +[setup.server.error.securityDisabled] +title = "ęœŖå•Ÿē”Øē™»å…„" +body = "ę­¤ä¼ŗęœå™ØęœŖå•Ÿē”Øē™»å…„ć€‚č‹„č¦é€£ē·šč‡³ę­¤ä¼ŗęœå™Øļ¼Œę‚Øåæ…é ˆå•Ÿē”Øé©—č­‰ļ¼š" +step1 = "åœØę‚Øēš„ē’°å¢ƒäø­čØ­å®š DOCKER_ENABLE_SECURITY=true" +step2 = "ęˆ–åœØ settings.yml äø­å°‡ security.enableLogin 設為 true" +step3 = "é‡ę–°å•Ÿå‹•ä¼ŗęœå™Ø" [setup.login] title = "登兄" @@ -5797,13 +5961,20 @@ submit = "登兄" signInWith = "仄此登兄" oauthPending = "ę­£åœØé–‹å•Ÿē€č¦½å™Øé€²č”Œé©—č­‰..." orContinueWith = "ęˆ–ę”¹ē”Ø Email 繼續" +serverRequirement = "ę³Øę„ļ¼šä¼ŗęœå™Øåæ…é ˆå•Ÿē”Øē™»å…„åŠŸčƒ½ć€‚" +showInstructions = "å¦‚ä½•å•Ÿē”Øļ¼Ÿ" +hideInstructions = "éš±č—čŖŖę˜Ž" +instructions = "åœØę‚Øēš„ Stirling PDF ä¼ŗęœå™ØäøŠå•Ÿē”Øē™»å…„åŠŸčƒ½ļ¼š" +instructionsEnvVar = "čØ­å®šē’°å¢ƒč®Šę•øļ¼š" +instructionsOrYml = "ęˆ–åœØ settings.yml 中:" +instructionsRestart = "ē„¶å¾Œé‡ę–°å•Ÿå‹•ä¼ŗęœå™Øä»„å„—ē”Øč®Šę›“ć€‚" [setup.login.username] label = "ä½æē”Øč€…åēØ±" placeholder = "č¼øå…„ä½ ēš„ä½æē”Øč€…åēØ±" [setup.login.email] -label = "Email" +label = "電子郵件" placeholder = "č¼øå…„ä½ ēš„ Email" [setup.login.password] @@ -5853,6 +6024,7 @@ earlyAccess = "ę¶å…ˆé«”é©—" reset = "é‡čØ­č®Šę›“" downloadJson = "下載 JSON" generatePdf = "ē”¢ē”Ÿ PDF" +saveChanges = "å„²å­˜č®Šę›“" [pdfTextEditor.options.autoScaleText] title = "č‡Ŗå‹•ēø®ę”¾ę–‡å­—ä»„ē¬¦åˆę–¹ę”†" @@ -5890,6 +6062,8 @@ alpha = "ę­¤ Alpha ęŖ¢č¦–å™Øä»åœØę¼”é€²äø­ā€”ā€”éƒØåˆ†å­—åž‹ć€é”č‰²ć€é€ę˜Ž [pdfTextEditor.empty] title = "å°šęœŖč¼‰å…„ę–‡ä»¶" subtitle = "載兄 PDF ꈖ JSON 檔仄開始編輯文字內容。" +dropzone = "將 PDF ꈖ JSON ęŖ”ę”ˆę‹–ę”¾åˆ°ę­¤č™•ļ¼Œęˆ–é»žę“Šä»„ē€č¦½" +dropzoneWithFiles = "å¾žć€ŒęŖ”ę”ˆć€åˆ†é éøå–ęŖ”ę”ˆļ¼Œęˆ–å°‡ PDF ꈖ JSON ęŖ”ę”ˆę‹–ę”¾åˆ°ę­¤č™•ļ¼Œęˆ–é»žę“Šä»„ē€č¦½" [pdfTextEditor.welcomeBanner] title = "ę­”čæŽä½æē”Ø PDF ę–‡å­—ē·Øč¼Æå™Øļ¼ˆę¶å…ˆé«”é©—ļ¼‰" diff --git a/frontend/src-tauri/Cargo.lock b/frontend/src-tauri/Cargo.lock index 9d2395e2de..9719752dc8 100644 --- a/frontend/src-tauri/Cargo.lock +++ b/frontend/src-tauri/Cargo.lock @@ -2152,7 +2152,11 @@ version = "3.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c" dependencies = [ + "byteorder", "log", + "security-framework 2.11.1", + "security-framework 3.5.1", + "windows-sys 0.60.2", "zeroize", ] @@ -2378,7 +2382,7 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "tempfile", ] @@ -3841,6 +3845,19 @@ dependencies = [ "security-framework-sys", ] +[[package]] +name = "security-framework" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +dependencies = [ + "bitflags 2.10.0", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + [[package]] name = "security-framework-sys" version = "2.15.0" diff --git a/frontend/src-tauri/Cargo.toml b/frontend/src-tauri/Cargo.toml index 6884bd1788..dc84ad8a23 100644 --- a/frontend/src-tauri/Cargo.toml +++ b/frontend/src-tauri/Cargo.toml @@ -32,7 +32,7 @@ tauri-plugin-http = "2.4.4" tauri-plugin-single-instance = "2.0.1" tauri-plugin-store = "2.1.0" tauri-plugin-opener = "2.0.0" -keyring = "3.6.1" +keyring = { version = "3.6.1", features = ["apple-native", "windows-native"] } tokio = { version = "1.0", features = ["time", "sync"] } reqwest = { version = "0.11", features = ["json"] } tiny_http = "0.12" diff --git a/frontend/src-tauri/src/commands/auth.rs b/frontend/src-tauri/src/commands/auth.rs index 30ec0d6c40..3e75b452ec 100644 --- a/frontend/src-tauri/src/commands/auth.rs +++ b/frontend/src-tauri/src/commands/auth.rs @@ -1,4 +1,4 @@ -use keyring::Entry; +use keyring::{Entry}; use serde::{Deserialize, Serialize}; use std::sync::{Arc, Mutex}; use tauri::AppHandle; @@ -21,53 +21,70 @@ pub struct UserInfo { } fn get_keyring_entry() -> Result { - Entry::new(KEYRING_SERVICE, KEYRING_TOKEN_KEY) - .map_err(|e| format!("Failed to access keyring: {}", e)) + log::debug!("Creating keyring entry with service='{}' username='{}'", KEYRING_SERVICE, KEYRING_TOKEN_KEY); + let entry = Entry::new(KEYRING_SERVICE, KEYRING_TOKEN_KEY) + .map_err(|e| { + log::error!("Failed to create keyring entry: {}", e); + format!("Failed to access keyring: {}", e) + })?; + log::debug!("Keyring entry created successfully"); + Ok(entry) } #[tauri::command] pub async fn save_auth_token(_app_handle: AppHandle, token: String) -> Result<(), String> { - log::info!("Saving auth token to keyring"); + if token.is_empty() { + log::warn!("Attempted to save empty auth token"); + return Err("Token cannot be empty".to_string()); + } let entry = get_keyring_entry()?; entry .set_password(&token) - .map_err(|e| format!("Failed to save token to keyring: {}", e))?; + .map_err(|e| { + log::error!("Failed to set password in keyring: {}", e); + format!("Failed to save token to keyring: {}", e) + })?; + + // Verify the save worked + match entry.get_password() { + Ok(retrieved_token) => { + if retrieved_token != token { + log::error!("Token verification failed: Retrieved token doesn't match"); + return Err("Token verification failed after save".to_string()); + } + } + Err(e) => { + log::error!("Token verification failed: {}", e); + return Err(format!("Token verification failed: {}", e)); + } + } - log::info!("Auth token saved successfully"); Ok(()) } #[tauri::command] pub async fn get_auth_token(_app_handle: AppHandle) -> Result, String> { - log::debug!("Retrieving auth token from keyring"); - let entry = get_keyring_entry()?; match entry.get_password() { Ok(token) => Ok(Some(token)), Err(keyring::Error::NoEntry) => Ok(None), - Err(e) => Err(format!("Failed to retrieve token: {}", e)), + Err(e) => { + log::error!("Failed to retrieve token from keyring: {}", e); + Err(format!("Failed to retrieve token: {}", e)) + }, } } #[tauri::command] pub async fn clear_auth_token(_app_handle: AppHandle) -> Result<(), String> { - log::info!("Clearing auth token from keyring"); - let entry = get_keyring_entry()?; // Delete the token - ignore error if it doesn't exist match entry.delete_credential() { - Ok(_) => { - log::info!("Auth token cleared successfully"); - Ok(()) - } - Err(keyring::Error::NoEntry) => { - log::info!("Auth token was already cleared"); - Ok(()) - } + Ok(_) | Err(keyring::Error::NoEntry) => Ok(()), Err(e) => Err(format!("Failed to clear token: {}", e)), } } @@ -78,8 +95,6 @@ pub async fn save_user_info( username: String, email: Option, ) -> Result<(), String> { - log::info!("Saving user info for: {}", username); - let user_info = UserInfo { username, email }; let store = app_handle @@ -96,7 +111,6 @@ pub async fn save_user_info( .save() .map_err(|e| format!("Failed to save store: {}", e))?; - log::info!("User info saved successfully"); Ok(()) } @@ -117,8 +131,6 @@ pub async fn get_user_info(app_handle: AppHandle) -> Result, St #[tauri::command] pub async fn clear_user_info(app_handle: AppHandle) -> Result<(), String> { - log::info!("Clearing user info"); - let store = app_handle .store(STORE_FILE) .map_err(|e| format!("Failed to access store: {}", e))?; @@ -129,7 +141,6 @@ pub async fn clear_user_info(app_handle: AppHandle) -> Result<(), String> { .save() .map_err(|e| format!("Failed to save store: {}", e))?; - log::info!("User info cleared successfully"); Ok(()) } @@ -186,12 +197,8 @@ pub async fn login( supabase_key: String, saas_server_url: String, ) -> Result { - log::info!("Login attempt for user: {} to server: {}", username, server_url); - // Detect if this is Supabase (SaaS) or Spring Boot (self-hosted) - // Compare against the configured SaaS server URL let is_supabase = server_url.trim_end_matches('/') == saas_server_url.trim_end_matches('/'); - log::info!("Authentication type: {}", if is_supabase { "Supabase (SaaS)" } else { "Spring Boot (Self-hosted)" }); // Create HTTP client let client = reqwest::Client::new(); @@ -248,8 +255,6 @@ pub async fn login( .or_else(|| email.clone()) .unwrap_or_else(|| username); - log::info!("Supabase login successful for user: {}", username); - Ok(LoginResponse { token: login_response.access_token, username, diff --git a/frontend/src/core/components/AppProviders.tsx b/frontend/src/core/components/AppProviders.tsx index 7e47d00e0d..5fa8d7cb91 100644 --- a/frontend/src/core/components/AppProviders.tsx +++ b/frontend/src/core/components/AppProviders.tsx @@ -20,6 +20,7 @@ import ErrorBoundary from "@app/components/shared/ErrorBoundary"; import { useScarfTracking } from "@app/hooks/useScarfTracking"; import { useAppInitialization } from "@app/hooks/useAppInitialization"; import { useLogoAssets } from '@app/hooks/useLogoAssets'; +import AppConfigLoader from '@app/components/shared/AppConfigLoader'; // Component to initialize scarf tracking (must be inside AppConfigProvider) function ScarfTrackingInitializer() { @@ -81,6 +82,7 @@ export function AppProviders({ children, appConfigRetryOptions, appConfigProvide {...appConfigProviderProps} > + diff --git a/frontend/src/core/components/layout/Workbench.tsx b/frontend/src/core/components/layout/Workbench.tsx index f6477c67aa..0fa31dd244 100644 --- a/frontend/src/core/components/layout/Workbench.tsx +++ b/frontend/src/core/components/layout/Workbench.tsx @@ -71,6 +71,20 @@ export default function Workbench() { }; const renderMainContent = () => { + // Check for custom workbench views first + if (!isBaseWorkbench(currentView)) { + const customView = customWorkbenchViews.find((view) => view.workbenchId === currentView && view.data != null); + if (customView) { + // PDF text editor handles its own empty state (shows dropzone when no document) + const handlesOwnEmptyState = currentView === 'custom:pdfTextEditor'; + if (handlesOwnEmptyState || activeFiles.length > 0) { + const CustomComponent = customView.component; + return ; + } + } + } + + // For base workbenches (or custom views that don't handle empty state), show landing page when no files if (activeFiles.length === 0) { return ( view.workbenchId === currentView && view.data != null); - - - if (customView) { - const CustomComponent = customView.component; - return ; - } - } return ; } }; diff --git a/frontend/src/core/components/onboarding/Onboarding.tsx b/frontend/src/core/components/onboarding/Onboarding.tsx index eb752ec6aa..e851ee60bf 100644 --- a/frontend/src/core/components/onboarding/Onboarding.tsx +++ b/frontend/src/core/components/onboarding/Onboarding.tsx @@ -6,6 +6,7 @@ import { isAuthRoute } from '@app/constants/routes'; import { dispatchTourState } from '@app/constants/events'; import { useOnboardingOrchestrator } from '@app/components/onboarding/orchestrator/useOnboardingOrchestrator'; import { markStepSeen } from '@app/components/onboarding/orchestrator/onboardingStorage'; +import { useBypassOnboarding } from '@app/components/onboarding/useBypassOnboarding'; import OnboardingTour, { type AdvanceArgs, type CloseArgs } from '@app/components/onboarding/OnboardingTour'; import OnboardingModalSlide from '@app/components/onboarding/OnboardingModalSlide'; import { @@ -29,6 +30,7 @@ export default function Onboarding() { const { t } = useTranslation(); const navigate = useNavigate(); const location = useLocation(); + const bypassOnboarding = useBypassOnboarding(); const { state, actions } = useOnboardingOrchestrator(); const serverExperience = useServerExperience(); const onAuthRoute = isAuthRoute(location.pathname); @@ -227,6 +229,10 @@ export default function Onboarding() { return modalSlides.findIndex((step) => step.id === currentStep.id); }, [activeFlow, currentStep]); + if (bypassOnboarding) { + return null; + } + if (onAuthRoute) { return null; } diff --git a/frontend/src/core/components/onboarding/orchestrator/useOnboardingOrchestrator.ts b/frontend/src/core/components/onboarding/orchestrator/useOnboardingOrchestrator.ts index 07888b7ce6..514b98e1c7 100644 --- a/frontend/src/core/components/onboarding/orchestrator/useOnboardingOrchestrator.ts +++ b/frontend/src/core/components/onboarding/orchestrator/useOnboardingOrchestrator.ts @@ -17,6 +17,7 @@ import { migrateFromLegacyPreferences, } from '@app/components/onboarding/orchestrator/onboardingStorage'; import { accountService } from '@app/services/accountService'; +import { useBypassOnboarding } from '@app/components/onboarding/useBypassOnboarding'; const AUTH_ROUTES = ['/login', '/signup', '/auth', '/invite']; const SESSION_TOUR_REQUESTED = 'onboarding::session::tour-requested'; @@ -142,6 +143,7 @@ export function useOnboardingOrchestrator( const serverExperience = useServerExperience(); const { config, loading: configLoading } = useAppConfig(); const location = useLocation(); + const bypassOnboarding = useBypassOnboarding(); const [runtimeState, setRuntimeState] = useState(() => getInitialRuntimeState(defaultState) @@ -193,7 +195,7 @@ export function useOnboardingOrchestrator( accountService.getAccountData(), accountService.getLoginPageData(), ]); - + setRuntimeState((prev) => ({ ...prev, requiresPasswordChange: accountData.changeCredsFlag, @@ -213,7 +215,8 @@ export function useOnboardingOrchestrator( const isOnAuthRoute = AUTH_ROUTES.some((route) => location.pathname.startsWith(route)); const loginEnabled = config?.enableLogin === true; const isUnauthenticatedWithLoginEnabled = loginEnabled && !hasAuthToken(); - const shouldBlockOnboarding = isOnAuthRoute || configLoading || isUnauthenticatedWithLoginEnabled; + const shouldBlockOnboarding = + bypassOnboarding || isOnAuthRoute || configLoading || isUnauthenticatedWithLoginEnabled; const conditionContext = useMemo(() => ({ ...serverExperience, @@ -223,8 +226,12 @@ export function useOnboardingOrchestrator( }), [serverExperience, runtimeState]); const activeFlow = useMemo(() => { + // If password change is required, ONLY show the first-login step + if (runtimeState.requiresPasswordChange) { + return ONBOARDING_STEPS.filter((step) => step.id === 'first-login'); + } return ONBOARDING_STEPS.filter((step) => step.condition(conditionContext)); - }, [conditionContext]); + }, [conditionContext, runtimeState.requiresPasswordChange]); // Wait for config AND admin status before calculating initial step const adminStatusResolved = !configLoading && ( @@ -235,23 +242,31 @@ export function useOnboardingOrchestrator( useEffect(() => { if (configLoading || !adminStatusResolved || activeFlow.length === 0) return; - + let firstUnseenIndex = -1; for (let i = 0; i < activeFlow.length; i++) { - if (!hasSeenStep(activeFlow[i].id)) { + // Special case: first-login step should always be considered "unseen" if requiresPasswordChange is true + const isFirstLoginStep = activeFlow[i].id === 'first-login'; + const shouldTreatAsUnseen = isFirstLoginStep ? runtimeState.requiresPasswordChange : !hasSeenStep(activeFlow[i].id); + + if (shouldTreatAsUnseen) { firstUnseenIndex = i; break; } } - - if (firstUnseenIndex === -1) { + + // Force reset index when password change is required (overrides initialIndexSet) + if (runtimeState.requiresPasswordChange && firstUnseenIndex === 0) { + setCurrentStepIndex(0); + initialIndexSet.current = true; + } else if (firstUnseenIndex === -1) { setCurrentStepIndex(activeFlow.length); initialIndexSet.current = true; } else if (!initialIndexSet.current) { setCurrentStepIndex(firstUnseenIndex); initialIndexSet.current = true; } - }, [activeFlow, configLoading, adminStatusResolved]); + }, [activeFlow, configLoading, adminStatusResolved, runtimeState.requiresPasswordChange]); const totalSteps = activeFlow.length; @@ -300,10 +315,13 @@ export function useOnboardingOrchestrator( if (!currentStep || isLoading) { return; } - if (hasSeenStep(currentStep.id)) { + // Special case: never auto-complete first-login step if requiresPasswordChange is true + const isFirstLoginStep = currentStep.id === 'first-login'; + + if (!isFirstLoginStep && hasSeenStep(currentStep.id)) { complete(); } - }, [currentStep, isLoading, complete]); + }, [currentStep, isLoading, complete, runtimeState.requiresPasswordChange]); const updateRuntimeState = useCallback((updates: Partial) => { persistRuntimeState(updates); diff --git a/frontend/src/core/components/onboarding/slides/ServerLicenseSlide.tsx b/frontend/src/core/components/onboarding/slides/ServerLicenseSlide.tsx index f118d2ac02..3da69103b5 100644 --- a/frontend/src/core/components/onboarding/slides/ServerLicenseSlide.tsx +++ b/frontend/src/core/components/onboarding/slides/ServerLicenseSlide.tsx @@ -39,7 +39,7 @@ export default function ServerLicenseSlide({ licenseNotice }: ServerLicenseSlide components={{ strong: , }} - defaults="Our Open-Core licensing permits up to {{freeTierLimit}} users for free per server. To scale uninterrupted and get early access to our new PDF text editing tool, we recommend the Stirling Server plan - full editing and unlimited seats for $99/server/mo." + defaults="Our Open-Core licensing permits up to {{freeTierLimit}} users for free per server. To scale uninterrupted, we recommend the Stirling Server plan - unlimited seats and SSO support for $99/server/mo." /> ); diff --git a/frontend/src/core/components/onboarding/useBypassOnboarding.ts b/frontend/src/core/components/onboarding/useBypassOnboarding.ts new file mode 100644 index 0000000000..8e7d9b14f0 --- /dev/null +++ b/frontend/src/core/components/onboarding/useBypassOnboarding.ts @@ -0,0 +1,70 @@ +import { useEffect, useMemo, useRef, useState } from 'react'; +import { useLocation } from 'react-router-dom'; +import { ONBOARDING_STEPS } from '@app/components/onboarding/orchestrator/onboardingConfig'; +import { markStepSeen } from '@app/components/onboarding/orchestrator/onboardingStorage'; + +const SESSION_KEY = 'onboarding::bypass-all'; +const PARAM_KEY = 'bypassOnboarding'; + +function isTruthy(value: string | null): boolean { + return value?.toLowerCase() === 'true'; +} + +function readStoredBypass(): boolean { + if (typeof window === 'undefined') return false; + try { + return sessionStorage.getItem(SESSION_KEY) === 'true'; + } catch { + return false; + } +} + +function setStoredBypass(enabled: boolean): void { + if (typeof window === 'undefined') return; + try { + if (enabled) { + sessionStorage.setItem(SESSION_KEY, 'true'); + } else { + sessionStorage.removeItem(SESSION_KEY); + } + } catch { + // Ignore storage errors to avoid blocking the bypass flow + } +} + +/** + * Detects the `bypassOnboarding` query parameter and stores it in session storage + * so that onboarding remains disabled while the app is open. Also marks all steps + * as seen to ensure any dependent UI elements remain hidden. + */ +export function useBypassOnboarding(): boolean { + const location = useLocation(); + const [bypassOnboarding, setBypassOnboarding] = useState(() => readStoredBypass()); + const stepsMarkedRef = useRef(false); + + const shouldBypassFromSearch = useMemo(() => { + try { + const params = new URLSearchParams(location.search); + return isTruthy(params.get(PARAM_KEY)); + } catch { + return false; + } + }, [location.search]); + + useEffect(() => { + const fromStorage = readStoredBypass(); + const nextBypass = shouldBypassFromSearch || fromStorage; + setBypassOnboarding(nextBypass); + if (nextBypass) { + setStoredBypass(true); + } + }, [shouldBypassFromSearch]); + + useEffect(() => { + if (!bypassOnboarding || stepsMarkedRef.current) return; + stepsMarkedRef.current = true; + ONBOARDING_STEPS.forEach((step) => markStepSeen(step.id)); + }, [bypassOnboarding]); + + return bypassOnboarding; +} diff --git a/frontend/src/core/components/shared/AllToolsNavButton.tsx b/frontend/src/core/components/shared/AllToolsNavButton.tsx index cc7a8777c1..efa9a2a5d3 100644 --- a/frontend/src/core/components/shared/AllToolsNavButton.tsx +++ b/frontend/src/core/components/shared/AllToolsNavButton.tsx @@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'; import { Tooltip } from '@app/components/shared/Tooltip'; import AppsIcon from '@mui/icons-material/AppsRounded'; import { useToolWorkflow } from '@app/contexts/ToolWorkflowContext'; +import { useNavigationState, useNavigationActions } from '@app/contexts/NavigationContext'; import { useSidebarNavigation } from '@app/hooks/useSidebarNavigation'; import { handleUnlessSpecialClick } from '@app/utils/clickHandlers'; @@ -20,21 +21,36 @@ const AllToolsNavButton: React.FC = ({ }) => { const { t } = useTranslation(); const { handleReaderToggle, handleBackToTools, selectedToolKey, leftPanelView } = useToolWorkflow(); + const { hasUnsavedChanges } = useNavigationState(); + const { actions: navigationActions } = useNavigationActions(); const { getHomeNavigation } = useSidebarNavigation(); - const handleClick = () => { + const performNavigation = () => { setActiveButton('tools'); // Preserve existing behavior used in QuickAccessBar header handleReaderToggle(); handleBackToTools(); }; + const handleClick = () => { + if (hasUnsavedChanges) { + navigationActions.requestNavigation(performNavigation); + return; + } + performNavigation(); + }; + // Do not highlight All Tools when a specific tool is open (indicator is shown) const isActive = activeButton === 'tools' && !selectedToolKey && leftPanelView === 'toolPicker'; const navProps = getHomeNavigation(); const handleNavClick = (e: React.MouseEvent) => { + if (hasUnsavedChanges) { + e.preventDefault(); + navigationActions.requestNavigation(performNavigation); + return; + } handleUnlessSpecialClick(e, handleClick); }; diff --git a/frontend/src/core/components/shared/AppConfigLoader.tsx b/frontend/src/core/components/shared/AppConfigLoader.tsx index 45cd7ad1bb..7da104a6f6 100644 --- a/frontend/src/core/components/shared/AppConfigLoader.tsx +++ b/frontend/src/core/components/shared/AppConfigLoader.tsx @@ -14,8 +14,9 @@ export default function AppConfigLoader() { useEffect(() => { if (!loading && config) { - // Update supported languages if config specifies a language filter - updateSupportedLanguages(config.languages); + // Update supported languages and apply default locale from server config + // Priority: localStorage > config.defaultLocale > browser detection > fallback + updateSupportedLanguages(config.languages, config.defaultLocale); } }, [config, loading]); diff --git a/frontend/src/core/components/shared/LanguageSelector.tsx b/frontend/src/core/components/shared/LanguageSelector.tsx index 87fd0db628..7eb35d1f73 100644 --- a/frontend/src/core/components/shared/LanguageSelector.tsx +++ b/frontend/src/core/components/shared/LanguageSelector.tsx @@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'; import { supportedLanguages } from '@app/i18n'; import LocalIcon from '@app/components/shared/LocalIcon'; import styles from '@app/components/shared/LanguageSelector.module.css'; -import { Z_INDEX_OVER_FULLSCREEN_SURFACE } from '@app/styles/zIndex'; +import { Z_INDEX_CONFIG_MODAL } from '@app/styles/zIndex'; // Types interface LanguageSelectorProps { @@ -163,13 +163,35 @@ const LanguageSelector: React.FC = ({ const [pendingLanguage, setPendingLanguage] = useState(null); const [rippleEffect, setRippleEffect] = useState(null); + // Get the filtered list of supported languages from i18n + // This respects server config (ui.languages) applied by AppConfigLoader + const allowedLanguages = (i18n.options.supportedLngs as string[] || []) + .filter(lang => lang !== 'cimode'); // Exclude i18next debug language + const languageOptions: LanguageOption[] = Object.entries(supportedLanguages) + .filter(([code]) => allowedLanguages.length === 0 || allowedLanguages.includes(code)) .sort(([, nameA], [, nameB]) => nameA.localeCompare(nameB)) .map(([code, name]) => ({ value: code, - label: name, + label: `${name} (${code})`, })); + // Hide the language selector if there's only one language option + // (no point showing a selector when there's nothing to select) + if (languageOptions.length <= 1) { + return null; + } + + // Calculate dropdown width and grid columns based on number of languages + // 2-4: 300px/2 cols, 5-9: 400px/3 cols, 10+: 600px/4 cols + const dropdownWidth = languageOptions.length <= 4 ? 300 + : languageOptions.length <= 9 ? 400 + : 600; + + const gridColumns = languageOptions.length <= 4 ? 2 + : languageOptions.length <= 9 ? 3 + : 4; + const handleLanguageChange = (value: string, event: React.MouseEvent) => { // Create ripple effect at click position (only for button mode) if (!compact) { @@ -219,10 +241,11 @@ const LanguageSelector: React.FC = ({ = ({ boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)', backgroundColor: 'light-dark(var(--mantine-color-white), var(--mantine-color-dark-6))', border: 'light-dark(1px solid var(--mantine-color-gray-3), 1px solid var(--mantine-color-dark-4))', - zIndex: Z_INDEX_OVER_FULLSCREEN_SURFACE, }} > -
- {languageOptions.map((option, index) => { - const enabledLanguages = [ - 'en-GB', 'zh-CN', 'zh-TW', 'ar-AR', 'fa-IR', 'tr-TR', 'uk-UA', 'zh-BO', 'sl-SI', - 'ru-RU', 'ja-JP', 'ko-KR', 'hu-HU', 'ga-IE', 'bg-BG', 'es-ES', 'hi-IN', 'hr-HR', - 'el-GR', 'ml-ML', 'pt-BR', 'pl-PL', 'pt-PT', 'sk-SK', 'sr-LATN-RS', 'no-NB', - 'th-TH', 'vi-VN', 'az-AZ', 'eu-ES', 'de-DE', 'sv-SE', 'it-IT', 'ca-CA', 'id-ID', - 'ro-RO', 'fr-FR', 'nl-NL', 'da-DK', 'cs-CZ' - ]; - const isDisabled = !enabledLanguages.includes(option.value); - - return ( +
+ {languageOptions.map((option, index) => ( = ({ rippleEffect={rippleEffect} pendingLanguage={pendingLanguage} compact={compact} - disabled={isDisabled} + disabled={false} /> - ); - })} + ))}
diff --git a/frontend/src/core/components/shared/NavigationWarningModal.tsx b/frontend/src/core/components/shared/NavigationWarningModal.tsx index faff074279..b8803f1760 100644 --- a/frontend/src/core/components/shared/NavigationWarningModal.tsx +++ b/frontend/src/core/components/shared/NavigationWarningModal.tsx @@ -12,7 +12,7 @@ interface NavigationWarningModalProps { const NavigationWarningModal = ({ onApplyAndContinue, onExportAndContinue }: NavigationWarningModalProps) => { const { t } = useTranslation(); - const { showNavigationWarning, hasUnsavedChanges, cancelNavigation, confirmNavigation, setHasUnsavedChanges } = + const { showNavigationWarning, hasUnsavedChanges, pendingNavigation, cancelNavigation, confirmNavigation, setHasUnsavedChanges } = useNavigationGuard(); const handleKeepWorking = () => { @@ -41,7 +41,9 @@ const NavigationWarningModal = ({ onApplyAndContinue, onExportAndContinue }: Nav }; const BUTTON_WIDTH = "10rem"; - if (!hasUnsavedChanges) { + // Only show modal if there are unsaved changes AND there's an actual pending navigation + // This prevents the modal from showing due to spurious state updates + if (!hasUnsavedChanges || !pendingNavigation) { return null; } diff --git a/frontend/src/core/components/shared/QuickAccessBar.tsx b/frontend/src/core/components/shared/QuickAccessBar.tsx index 029a6d567e..28efd60cbc 100644 --- a/frontend/src/core/components/shared/QuickAccessBar.tsx +++ b/frontend/src/core/components/shared/QuickAccessBar.tsx @@ -7,6 +7,7 @@ import { useRainbowThemeContext } from "@app/components/shared/RainbowThemeProvi import { useIsOverflowing } from '@app/hooks/useIsOverflowing'; import { useFilesModalContext } from '@app/contexts/FilesModalContext'; import { useToolWorkflow } from '@app/contexts/ToolWorkflowContext'; +import { useNavigationState, useNavigationActions } from '@app/contexts/NavigationContext'; import { useSidebarNavigation } from '@app/hooks/useSidebarNavigation'; import { handleUnlessSpecialClick } from '@app/utils/clickHandlers'; import { ButtonConfig } from '@app/types/sidebar'; @@ -32,6 +33,8 @@ const QuickAccessBar = forwardRef((_, ref) => { const { isRainbowMode } = useRainbowThemeContext(); const { openFilesModal, isFilesModalOpen } = useFilesModalContext(); const { handleReaderToggle, handleToolSelect, selectedToolKey, leftPanelView, toolRegistry, readerMode, resetTool } = useToolWorkflow(); + const { hasUnsavedChanges } = useNavigationState(); + const { actions: navigationActions } = useNavigationActions(); const { getToolNavigation } = useSidebarNavigation(); const { config } = useAppConfig(); const licenseAlert = useLicenseAlert(); @@ -58,7 +61,7 @@ const QuickAccessBar = forwardRef((_, ref) => { }; // Helper function to render navigation buttons with URL support - const renderNavButton = (config: ButtonConfig, index: number) => { + const renderNavButton = (config: ButtonConfig, index: number, shouldGuardNavigation = false) => { const isActive = isNavButtonActive(config, activeButton, isFilesModalOpen, configModalOpen, selectedToolKey, leftPanelView); // Check if this button has URL navigation support @@ -67,6 +70,14 @@ const QuickAccessBar = forwardRef((_, ref) => { : null; const handleClick = (e?: React.MouseEvent) => { + // If there are unsaved changes and this button should guard navigation, show warning modal + if (shouldGuardNavigation && hasUnsavedChanges) { + e?.preventDefault(); + navigationActions.requestNavigation(() => { + config.onClick(); + }); + return; + } if (navProps && e) { handleUnlessSpecialClick(e, config.onClick); } else { @@ -89,7 +100,7 @@ const QuickAccessBar = forwardRef((_, ref) => { onClick: (e: React.MouseEvent) => handleClick(e), 'aria-label': config.name } : { - onClick: () => handleClick(), + onClick: (e: React.MouseEvent) => handleClick(e), 'aria-label': config.name })} size={isActive ? 'lg' : 'md'} @@ -222,7 +233,7 @@ const QuickAccessBar = forwardRef((_, ref) => { {mainButtons.map((config, index) => ( - {renderNavButton(config, index)} + {renderNavButton(config, index, config.id === 'read' || config.id === 'automate')} ))} diff --git a/frontend/src/core/components/shared/SkeletonLoader.tsx b/frontend/src/core/components/shared/SkeletonLoader.tsx index 63c4bd22a3..a95949e8f2 100644 --- a/frontend/src/core/components/shared/SkeletonLoader.tsx +++ b/frontend/src/core/components/shared/SkeletonLoader.tsx @@ -2,23 +2,44 @@ import React from 'react'; import { Box, Group, Stack } from '@mantine/core'; interface SkeletonLoaderProps { - type: 'pageGrid' | 'fileGrid' | 'controls' | 'viewer'; + type: 'pageGrid' | 'fileGrid' | 'controls' | 'viewer' | 'block'; count?: number; animated?: boolean; + width?: number | string; + height?: number | string; + radius?: number | string; } -const SkeletonLoader: React.FC = ({ - type, - count = 8, - animated = true +const SkeletonLoader: React.FC = ({ + type, + count = 8, + animated = true, + width, + height, + radius = 8, }) => { const animationStyle = animated ? { animation: 'pulse 2s infinite' } : {}; + // Generic block skeleton for inline text/inputs/etc. + const renderBlock = () => ( + + ); + const renderPageGridSkeleton = () => ( -
{Array.from({ length: count }).map((_, i) => ( = ({ w="100%" h={240} bg="gray.1" - style={{ + style={{ borderRadius: '8px', ...animationStyle, animationDelay: animated ? `${i * 0.1}s` : undefined @@ -37,10 +58,10 @@ const SkeletonLoader: React.FC = ({ ); const renderFileGridSkeleton = () => ( -
{Array.from({ length: count }).map((_, i) => ( = ({ w="100%" h={280} bg="gray.1" - style={{ + style={{ borderRadius: '8px', ...animationStyle, animationDelay: animated ? `${i * 0.1}s` : undefined @@ -76,18 +97,20 @@ const SkeletonLoader: React.FC = ({ {/* Main content skeleton */} - ); switch (type) { + case 'block': + return renderBlock(); case 'pageGrid': return renderPageGridSkeleton(); case 'fileGrid': @@ -101,4 +124,4 @@ const SkeletonLoader: React.FC = ({ } }; -export default SkeletonLoader; \ No newline at end of file +export default SkeletonLoader; diff --git a/frontend/src/core/components/shared/config/configSections/providerDefinitions.ts b/frontend/src/core/components/shared/config/configSections/providerDefinitions.ts index f51f997c72..f9d4af7c4e 100644 --- a/frontend/src/core/components/shared/config/configSections/providerDefinitions.ts +++ b/frontend/src/core/components/shared/config/configSections/providerDefinitions.ts @@ -97,6 +97,7 @@ export const OAUTH2_PROVIDERS: Provider[] = [ icon: 'key-rounded', type: 'oauth2', scope: 'SSO', + businessTier: false, // Server tier - OAuth2/OIDC SSO fields: [ { key: 'issuer', @@ -141,6 +142,7 @@ export const GENERIC_OAUTH2_PROVIDER: Provider = { icon: 'link-rounded', type: 'oauth2', scope: 'SSO', + businessTier: false, // Server tier - OAuth2/OIDC SSO fields: [ { key: 'enabled', @@ -262,8 +264,8 @@ export const SAML2_PROVIDER: Provider = { name: 'SAML2', icon: 'verified-user-rounded', type: 'saml2', - scope: 'SSO', - businessTier: true, + scope: 'SSO (SAML)', + businessTier: true, // Enterprise tier - SAML only fields: [ { key: 'enabled', diff --git a/frontend/src/core/components/shared/quickAccessBar/ActiveToolButton.tsx b/frontend/src/core/components/shared/quickAccessBar/ActiveToolButton.tsx index 273c09d0e1..55b79b26ca 100644 --- a/frontend/src/core/components/shared/quickAccessBar/ActiveToolButton.tsx +++ b/frontend/src/core/components/shared/quickAccessBar/ActiveToolButton.tsx @@ -16,6 +16,7 @@ import React, { useEffect, useRef, useState } from 'react'; import { ActionIcon } from '@mantine/core'; import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded'; import { useToolWorkflow } from '@app/contexts/ToolWorkflowContext'; +import { useNavigationState, useNavigationActions } from '@app/contexts/NavigationContext'; import { useSidebarNavigation } from '@app/hooks/useSidebarNavigation'; import { handleUnlessSpecialClick } from '@app/utils/clickHandlers'; import FitText from '@app/components/shared/FitText'; @@ -31,6 +32,8 @@ const NAV_IDS = ['read', 'sign', 'automate']; const ActiveToolButton: React.FC = ({ setActiveButton, tooltipPosition = 'right' }) => { const { selectedTool, selectedToolKey, leftPanelView, handleBackToTools } = useToolWorkflow(); + const { hasUnsavedChanges } = useNavigationState(); + const { actions: navigationActions } = useNavigationActions(); const { getHomeNavigation } = useSidebarNavigation(); // Determine if the indicator should be visible (do not require selectedTool to be resolved yet) @@ -150,10 +153,16 @@ const ActiveToolButton: React.FC = ({ setActiveButton, to component="a" href={getHomeNavigation().href} onClick={(e: React.MouseEvent) => { - handleUnlessSpecialClick(e, () => { + const performNavigation = () => { setActiveButton('tools'); handleBackToTools(); - }); + }; + if (hasUnsavedChanges) { + e.preventDefault(); + navigationActions.requestNavigation(performNavigation); + return; + } + handleUnlessSpecialClick(e, performNavigation); }} size={'lg'} variant="subtle" diff --git a/frontend/src/core/components/tools/getPdfInfo/GetPdfInfoReportView.tsx b/frontend/src/core/components/tools/getPdfInfo/GetPdfInfoReportView.tsx new file mode 100644 index 0000000000..ec5d3805e3 --- /dev/null +++ b/frontend/src/core/components/tools/getPdfInfo/GetPdfInfoReportView.tsx @@ -0,0 +1,128 @@ +import React, { useEffect, useMemo, useRef } from 'react'; +import { Badge, Divider, Stack, Text } from '@mantine/core'; +import { useTranslation } from 'react-i18next'; +import type { + PdfInfoReportData, + PdfInfoReportEntry, + PdfInfoBackendData, + ParsedPdfSections, +} from '@app/types/getPdfInfo'; +import '@app/components/tools/validateSignature/reportView/styles.css'; +import SummarySection from '@app/components/tools/getPdfInfo/sections/SummarySection'; +import KeyValueSection from '@app/components/tools/getPdfInfo/sections/KeyValueSection'; +import TableOfContentsSection from '@app/components/tools/getPdfInfo/sections/TableOfContentsSection'; +import OtherSection from '@app/components/tools/getPdfInfo/sections/OtherSection'; +import PerPageSection from '@app/components/tools/getPdfInfo/sections/PerPageSection'; + + +/** Valid section anchor IDs for navigation */ +const VALID_ANCHORS = new Set([ + 'summary', 'metadata', 'formFields', 'basicInfo', 'documentInfo', + 'compliance', 'encryption', 'permissions', 'toc', 'other', 'perPage', +]); + +interface GetPdfInfoReportViewProps { + data: PdfInfoReportData & { scrollTo?: string | null }; +} + +const GetPdfInfoReportView: React.FC = ({ data }) => { + const { t } = useTranslation(); + const containerRef = useRef(null); + const entry: PdfInfoReportEntry | null = data.entries[0] ?? null; + + useEffect(() => { + if (!data.scrollTo || !VALID_ANCHORS.has(data.scrollTo)) return; + const anchor = data.scrollTo; + const container = containerRef.current; + const el = container?.querySelector(`#${anchor}`); + if (el && container) { + // Calculate scroll position with 4rem buffer from top + const bufferPx = parseFloat(getComputedStyle(document.documentElement).fontSize) * 4; + const elementTop = el.getBoundingClientRect().top; + const containerTop = container.getBoundingClientRect().top; + const currentScroll = container.scrollTop; + const targetScroll = currentScroll + (elementTop - containerTop) - bufferPx; + + container.scrollTo({ top: Math.max(0, targetScroll), behavior: 'smooth' }); + + // Flash highlight the section + el.classList.remove('section-flash-highlight'); + void el.offsetWidth; // Force reflow + el.classList.add('section-flash-highlight'); + setTimeout(() => el.classList.remove('section-flash-highlight'), 1500); + } + }, [data.scrollTo]); + + const sections = useMemo((): ParsedPdfSections => { + const raw: PdfInfoBackendData = entry?.data ?? {}; + return { + metadata: raw.Metadata ?? null, + formFields: raw.FormFields ?? raw['Form Fields'] ?? null, + basicInfo: raw.BasicInfo ?? raw['Basic Info'] ?? null, + documentInfo: raw.DocumentInfo ?? raw['Document Info'] ?? null, + compliance: raw.Compliancy ?? raw.Compliance ?? null, + encryption: raw.Encryption ?? null, + permissions: raw.Permissions ?? null, + toc: raw['Bookmarks/Outline/TOC'] ?? raw['Table of Contents'] ?? null, + other: raw.Other ?? null, + perPage: raw.PerPageInfo ?? raw['Per Page Info'] ?? null, + summaryData: raw.SummaryData ?? null, + }; + }, [entry]); + + if (!entry) { + return ( +
+ + No Data + Run the tool to generate the report. + +
+ ); + } + + return ( +
+ + +
+ + + + {entry.fileName} + - {t('getPdfInfo.summary.title', 'PDF Summary')} + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ ); +}; + +export default GetPdfInfoReportView; + + diff --git a/frontend/src/core/components/tools/getPdfInfo/GetPdfInfoResults.tsx b/frontend/src/core/components/tools/getPdfInfo/GetPdfInfoResults.tsx new file mode 100644 index 0000000000..5ee89db4aa --- /dev/null +++ b/frontend/src/core/components/tools/getPdfInfo/GetPdfInfoResults.tsx @@ -0,0 +1,79 @@ +import { useCallback, useMemo } from 'react'; +import { Alert, Button, Group, Loader, Stack, Text } from '@mantine/core'; +import { useTranslation } from 'react-i18next'; +import type { GetPdfInfoOperationHook } from '@app/hooks/tools/getPdfInfo/useGetPdfInfoOperation'; + +interface GetPdfInfoResultsProps { + operation: GetPdfInfoOperationHook; + isLoading: boolean; + errorMessage: string | null; +} + +const findFileByExtension = (files: File[], extension: string) => { + return files.find((file) => file.name.toLowerCase().endsWith(extension)); +}; + +const GetPdfInfoResults = ({ operation, isLoading, errorMessage }: GetPdfInfoResultsProps) => { + const { t } = useTranslation(); + + const jsonFile = useMemo(() => findFileByExtension(operation.files, '.json'), [operation.files]); + const selectedFile = useMemo(() => jsonFile ?? null, [jsonFile]); + const selectedDownloadLabel = useMemo(() => t('getPdfInfo.downloadJson', 'Download JSON'), [t]); + + const handleDownload = useCallback((file: File) => { + const blobUrl = URL.createObjectURL(file); + const link = document.createElement('a'); + link.href = blobUrl; + link.download = file.name; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(blobUrl); + }, []); + + if (isLoading && operation.results.length === 0) { + return ( + + + {t('getPdfInfo.processing', 'Extracting information...')} + + ); + } + + if (!isLoading && operation.results.length === 0) { + return ( + + {t('getPdfInfo.noResults', 'Run the tool to generate a report.')} + + ); + } + + return ( + + {/* No background post-processing once JSON is ready */} + {errorMessage && ( + + {errorMessage} + + )} + + + + {t('getPdfInfo.downloads', 'Downloads')} + + + + + ); +}; + +export default GetPdfInfoResults; + + diff --git a/frontend/src/core/components/tools/getPdfInfo/sections/KeyValueSection.tsx b/frontend/src/core/components/tools/getPdfInfo/sections/KeyValueSection.tsx new file mode 100644 index 0000000000..a98a475688 --- /dev/null +++ b/frontend/src/core/components/tools/getPdfInfo/sections/KeyValueSection.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import SectionBlock from '@app/components/tools/getPdfInfo/shared/SectionBlock'; +import KeyValueList from '@app/components/tools/getPdfInfo/shared/KeyValueList'; + +interface KeyValueSectionProps { + title: string; + anchorId: string; + obj?: Record | null; + emptyLabel?: string; +} + +const KeyValueSection: React.FC = ({ title, anchorId, obj, emptyLabel }) => { + return ( + + + + ); +}; + +export default KeyValueSection; + + diff --git a/frontend/src/core/components/tools/getPdfInfo/sections/OtherSection.tsx b/frontend/src/core/components/tools/getPdfInfo/sections/OtherSection.tsx new file mode 100644 index 0000000000..e7eb8d8b3e --- /dev/null +++ b/frontend/src/core/components/tools/getPdfInfo/sections/OtherSection.tsx @@ -0,0 +1,84 @@ +import React from 'react'; +import { Accordion, Stack, Text } from '@mantine/core'; +import { useTranslation } from 'react-i18next'; +import type { PdfOtherInfo } from '@app/types/getPdfInfo'; +import SectionBlock from '@app/components/tools/getPdfInfo/shared/SectionBlock'; +import ScrollableCodeBlock from '@app/components/tools/getPdfInfo/shared/ScrollableCodeBlock'; +import { pdfInfoAccordionStyles } from '@app/components/tools/getPdfInfo/shared/accordionStyles'; + +interface OtherSectionProps { + anchorId: string; + other?: PdfOtherInfo | null; +} + +const renderList = (arr: unknown[] | undefined, emptyText: string) => { + if (!arr || arr.length === 0) return {emptyText}; + return ( + + {arr.map((item, idx) => ( + + {typeof item === 'string' ? item : JSON.stringify(item)} + + ))} + + ); +}; + +const OtherSection: React.FC = ({ anchorId, other }) => { + const { t } = useTranslation(); + const noneDetected = t('getPdfInfo.noneDetected', 'None detected'); + + const structureTreeContent = Array.isArray(other?.StructureTree) && other.StructureTree.length > 0 + ? JSON.stringify(other.StructureTree, null, 2) + : null; + + return ( + + + + {t('getPdfInfo.other.attachments', 'Attachments')} + {renderList(other?.Attachments, noneDetected)} + + + {t('getPdfInfo.other.embeddedFiles', 'Embedded Files')} + {renderList(other?.EmbeddedFiles, noneDetected)} + + + {t('getPdfInfo.other.javaScript', 'JavaScript')} + {renderList(other?.JavaScript, noneDetected)} + + + {t('getPdfInfo.other.layers', 'Layers')} + {renderList(other?.Layers, noneDetected)} + + + + + {t('getPdfInfo.other.structureTree', 'StructureTree')} + + + + + + + + {t('getPdfInfo.other.xmp', 'XMPMetadata')} + + + + + + + + + ); +}; + +export default OtherSection; + + diff --git a/frontend/src/core/components/tools/getPdfInfo/sections/PerPageSection.tsx b/frontend/src/core/components/tools/getPdfInfo/sections/PerPageSection.tsx new file mode 100644 index 0000000000..4fd257cce6 --- /dev/null +++ b/frontend/src/core/components/tools/getPdfInfo/sections/PerPageSection.tsx @@ -0,0 +1,122 @@ +import React from 'react'; +import { Accordion, Stack, Text } from '@mantine/core'; +import { useTranslation } from 'react-i18next'; +import type { PdfPerPageInfo, PdfPageInfo, PdfFontInfo } from '@app/types/getPdfInfo'; +import SectionBlock from '@app/components/tools/getPdfInfo/shared/SectionBlock'; +import KeyValueList from '@app/components/tools/getPdfInfo/shared/KeyValueList'; +import { pdfInfoAccordionStyles } from '@app/components/tools/getPdfInfo/shared/accordionStyles'; + +interface PerPageSectionProps { + anchorId: string; + perPage?: PdfPerPageInfo | null; +} + +const renderList = (arr: unknown[] | undefined, emptyText: string) => { + if (!arr || arr.length === 0) return {emptyText}; + return ( + + {arr.map((item, idx) => ( + + {typeof item === 'string' ? item : JSON.stringify(item)} + + ))} + + ); +}; + +const renderFontsList = (fonts: PdfFontInfo[] | undefined, emptyText: string) => { + if (!fonts || fonts.length === 0) return {emptyText}; + return ( + + {fonts.map((font, idx) => ( + + {`${font.Name ?? 'Unknown'}${font.IsEmbedded ? ' (embedded)' : ''}`} + + ))} + + ); +}; + +const PerPageSection: React.FC = ({ anchorId, perPage }) => { + const { t } = useTranslation(); + const noneDetected = t('getPdfInfo.noneDetected', 'None detected'); + + const hasPages = perPage && Object.keys(perPage).length > 0; + + return ( + + {hasPages ? ( + + {Object.entries(perPage).map(([pageLabel, pageInfo]: [string, PdfPageInfo]) => ( + + + {pageLabel} + + +
+ + {pageInfo?.Size && ( + + {t('getPdfInfo.perPage.size', 'Size')} + + + )} + + {pageInfo?.Annotations && ( + + {t('getPdfInfo.perPage.annotations', 'Annotations')} + + + )} + + {t('getPdfInfo.perPage.images', 'Images')} + {renderList(pageInfo?.Images, noneDetected)} + + + {t('getPdfInfo.perPage.links', 'Links')} + {renderList(pageInfo?.Links, noneDetected)} + + + {t('getPdfInfo.perPage.fonts', 'Fonts')} + {renderFontsList(pageInfo?.Fonts, noneDetected)} + + {pageInfo?.XObjectCounts && ( + + {t('getPdfInfo.perPage.xobjects', 'XObject Counts')} + + + )} + + {t('getPdfInfo.perPage.multimedia', 'Multimedia')} + {renderList(pageInfo?.Multimedia, noneDetected)} + + +
+
+
+ ))} +
+ ) : ( + {noneDetected} + )} +
+ ); +}; + +export default PerPageSection; + + diff --git a/frontend/src/core/components/tools/getPdfInfo/sections/SummarySection.tsx b/frontend/src/core/components/tools/getPdfInfo/sections/SummarySection.tsx new file mode 100644 index 0000000000..680f4b64dd --- /dev/null +++ b/frontend/src/core/components/tools/getPdfInfo/sections/SummarySection.tsx @@ -0,0 +1,148 @@ +import React, { useMemo } from 'react'; +import { Stack, Text } from '@mantine/core'; +import { useTranslation } from 'react-i18next'; +import type { ParsedPdfSections, PdfFontInfo } from '@app/types/getPdfInfo'; +import SectionBlock from '@app/components/tools/getPdfInfo/shared/SectionBlock'; +import KeyValueList from '@app/components/tools/getPdfInfo/shared/KeyValueList'; + +interface SummarySectionProps { + sections: ParsedPdfSections; + hideSectionTitle?: boolean; +} + +const SummarySection: React.FC = ({ sections, hideSectionTitle = false }) => { + const { t } = useTranslation(); + + const summaryBlocks = useMemo(() => { + const basic = sections.basicInfo ?? {}; + const docInfo = sections.documentInfo ?? {}; + const metadata = sections.metadata ?? {}; + const encryption = sections.encryption ?? {}; + const permissions = sections.permissions ?? {}; + const summary = sections.summaryData ?? {}; + const other = sections.other ?? {}; + const perPage = sections.perPage ?? {}; + + const pages = basic['Number of pages']; + const fileSizeBytes = basic.FileSizeInBytes; + const pdfVersion = docInfo['PDF version']; + const language = basic.Language; + + const basicInformation: Record = { + [t('getPdfInfo.summary.pages', 'Pages')]: pages, + [t('getPdfInfo.summary.fileSize', 'File Size')]: typeof fileSizeBytes === 'number' ? `${(fileSizeBytes / 1024).toFixed(2)} KB` : fileSizeBytes, + [t('getPdfInfo.summary.pdfVersion', 'PDF Version')]: pdfVersion, + [t('getPdfInfo.summary.language', 'Language')]: language, + }; + + const documentInformation: Record = { + [t('getPdfInfo.summary.title', 'Title')]: metadata.Title, + [t('getPdfInfo.summary.author', 'Author')]: metadata.Author, + [t('getPdfInfo.summary.created', 'Created')]: metadata.CreationDate, + [t('getPdfInfo.summary.modified', 'Modified')]: metadata.ModificationDate, + }; + + const securityStatusText = encryption.IsEncrypted + ? t('getPdfInfo.summary.security.encrypted', 'Encrypted PDF - Password protection present') + : t('getPdfInfo.summary.security.unencrypted', 'Unencrypted PDF - No password protection'); + + const restrictedCount = summary.restrictedPermissionsCount ?? 0; + const permissionsAllAllowed = Object.values(permissions).every((v) => v === 'Allowed'); + const permSummary = permissionsAllAllowed + ? t('getPdfInfo.summary.permsAll', 'All Permissions Allowed') + : restrictedCount > 0 + ? t('getPdfInfo.summary.permsRestricted', '{{count}} restrictions', { count: restrictedCount }) + : t('getPdfInfo.summary.permsMixed', 'Some permissions restricted'); + + const complianceText = sections.compliance && Object.values(sections.compliance).some(Boolean) + ? t('getPdfInfo.summary.hasCompliance', 'Has compliance standards') + : t('getPdfInfo.summary.noCompliance', 'No Compliance Standards'); + + // Helper to get first page data + const firstPage = perPage['Page 1']; + const firstPageFonts: PdfFontInfo[] = firstPage?.Fonts ?? []; + + const technical: Record = { + [t('getPdfInfo.summary.tech.images', 'Images')]: (() => { + const total = basic.TotalImages; + if (typeof total === 'number') return total === 0 ? 'None' : `${total}`; + return 'None'; + })(), + [t('getPdfInfo.summary.tech.fonts', 'Fonts')]: (() => { + if (firstPageFonts.length === 0) return 'None'; + const embedded = firstPageFonts.filter((f) => f.IsEmbedded).length; + return `${firstPageFonts.length} (${embedded} embedded)`; + })(), + [t('getPdfInfo.summary.tech.formFields', 'Form Fields')]: sections.formFields && Object.keys(sections.formFields).length > 0 ? Object.keys(sections.formFields).length : 'None', + [t('getPdfInfo.summary.tech.embeddedFiles', 'Embedded Files')]: other.EmbeddedFiles?.length ?? 'None', + [t('getPdfInfo.summary.tech.javaScript', 'JavaScript')]: other.JavaScript?.length ?? 'None', + [t('getPdfInfo.summary.tech.layers', 'Layers')]: other.Layers?.length ?? 'None', + [t('getPdfInfo.summary.tech.bookmarks', 'Bookmarks')]: sections.toc?.length ?? 'None', + [t('getPdfInfo.summary.tech.multimedia', 'Multimedia')]: firstPage?.Multimedia?.length ?? 'None', + }; + + const overview = (() => { + const tTitle = metadata.Title ? `"${metadata.Title}"` : t('getPdfInfo.summary.overview.untitled', 'an untitled document'); + const author = metadata.Author || t('getPdfInfo.summary.overview.unknown', 'Unknown Author'); + const pagesCount = typeof pages === 'number' ? pages : '?'; + const version = pdfVersion ?? '?'; + return t('getPdfInfo.summary.overview.text', 'This is a {{pages}}-page PDF titled {{title}} created by {{author}} (PDF version {{version}}).', { + pages: pagesCount, + title: tTitle, + author, + version, + }); + })(); + + return { + basicInformation, + documentInformation, + securityStatusText, + permSummary, + complianceText, + technical, + overview, + }; + }, [sections, t]); + + const content = ( + + + {t('getPdfInfo.summary.basic', 'Basic Information')} + + + + {t('getPdfInfo.summary.documentInfo', 'Document Information')} + + + + {t('getPdfInfo.summary.securityTitle', 'Security Status')} + {summaryBlocks.securityStatusText} + {summaryBlocks.permSummary} + {summaryBlocks.complianceText} + + + {t('getPdfInfo.summary.technical', 'Technical')} + + + + {t('getPdfInfo.summary.overviewTitle', 'PDF Overview')} + {summaryBlocks.overview} + + + ); + + if (hideSectionTitle) { + return
{content}
; + } + + return ( + + {content} + + ); +}; + +export default SummarySection; + + diff --git a/frontend/src/core/components/tools/getPdfInfo/sections/TableOfContentsSection.tsx b/frontend/src/core/components/tools/getPdfInfo/sections/TableOfContentsSection.tsx new file mode 100644 index 0000000000..57e4ac2fed --- /dev/null +++ b/frontend/src/core/components/tools/getPdfInfo/sections/TableOfContentsSection.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { Stack, Text } from '@mantine/core'; +import { useTranslation } from 'react-i18next'; +import type { PdfTocEntry } from '@app/types/getPdfInfo'; +import SectionBlock from '@app/components/tools/getPdfInfo/shared/SectionBlock'; + +interface TableOfContentsSectionProps { + anchorId: string; + tocArray: PdfTocEntry[]; +} + +const TableOfContentsSection: React.FC = ({ anchorId, tocArray }) => { + const { t } = useTranslation(); + const noneDetected = t('getPdfInfo.noneDetected', 'None detected'); + + return ( + + {!tocArray || tocArray.length === 0 ? ( + {noneDetected} + ) : ( + + {tocArray.map((item, idx) => ( + + {typeof item === 'string' ? item : JSON.stringify(item)} + + ))} + + )} + + ); +}; + +export default TableOfContentsSection; + + diff --git a/frontend/src/core/components/tools/getPdfInfo/shared/KeyValueList.tsx b/frontend/src/core/components/tools/getPdfInfo/shared/KeyValueList.tsx new file mode 100644 index 0000000000..e0cd809cc4 --- /dev/null +++ b/frontend/src/core/components/tools/getPdfInfo/shared/KeyValueList.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { Group, Stack, Text } from '@mantine/core'; + +interface KeyValueListProps { + obj?: Record | null; + emptyLabel?: string; +} + +const KeyValueList: React.FC = ({ obj, emptyLabel }) => { + if (!obj || Object.keys(obj).length === 0) { + return {emptyLabel ?? 'None detected'}; + } + return ( + + {Object.entries(obj).map(([k, v]) => ( + + {k} + + {v == null ? '' : String(v)} + + + ))} + + ); +}; + +export default KeyValueList; + + diff --git a/frontend/src/core/components/tools/getPdfInfo/shared/ScrollableCodeBlock.tsx b/frontend/src/core/components/tools/getPdfInfo/shared/ScrollableCodeBlock.tsx new file mode 100644 index 0000000000..bf04264268 --- /dev/null +++ b/frontend/src/core/components/tools/getPdfInfo/shared/ScrollableCodeBlock.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { Code, Text } from '@mantine/core'; +import { useTranslation } from 'react-i18next'; + +interface ScrollableCodeBlockProps { + content: string | null | undefined; + maxHeight?: string; + emptyMessage?: string; +} + +/** + * A reusable scrollable code block component with consistent styling. + * Used for displaying large text content like XMP metadata or structure trees. + */ +const ScrollableCodeBlock: React.FC = ({ + content, + maxHeight = '400px', + emptyMessage, +}) => { + const { t } = useTranslation(); + + if (!content) { + return ( + + {emptyMessage ?? t('getPdfInfo.noneDetected', 'None detected')} + + ); + } + + return ( + + {content} + + ); +}; + +export default ScrollableCodeBlock; + diff --git a/frontend/src/core/components/tools/getPdfInfo/shared/SectionBlock.tsx b/frontend/src/core/components/tools/getPdfInfo/shared/SectionBlock.tsx new file mode 100644 index 0000000000..0faa993f60 --- /dev/null +++ b/frontend/src/core/components/tools/getPdfInfo/shared/SectionBlock.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { Stack, Text, Divider } from '@mantine/core'; + +interface SectionBlockProps { + title: string; + anchorId: string; + children: React.ReactNode; +} + +const SectionBlock: React.FC = ({ title, anchorId, children }) => { + return ( + + {title} + + {children} + + ); +}; + +export default SectionBlock; + + diff --git a/frontend/src/core/components/tools/getPdfInfo/shared/accordionStyles.ts b/frontend/src/core/components/tools/getPdfInfo/shared/accordionStyles.ts new file mode 100644 index 0000000000..e974c98450 --- /dev/null +++ b/frontend/src/core/components/tools/getPdfInfo/shared/accordionStyles.ts @@ -0,0 +1,14 @@ +import type { AccordionStylesNames } from '@mantine/core'; +import type { CSSProperties } from 'react'; + +type AccordionStyles = Partial>; + +export const pdfInfoAccordionStyles: AccordionStyles = { + item: { + backgroundColor: 'var(--accordion-item-bg)', + }, + control: { + backgroundColor: 'transparent', + }, +}; + diff --git a/frontend/src/core/components/tools/ocr/LanguagePicker.tsx b/frontend/src/core/components/tools/ocr/LanguagePicker.tsx index 784d22da5c..7e023e9ccd 100644 --- a/frontend/src/core/components/tools/ocr/LanguagePicker.tsx +++ b/frontend/src/core/components/tools/ocr/LanguagePicker.tsx @@ -134,7 +134,7 @@ const LanguagePicker: React.FC = ({ textDecoration: 'underline', textAlign: 'center' }} - onClick={() => window.open('https://docs.stirlingpdf.com/Advanced%20Configuration/OCR', '_blank')} + onClick={() => window.open('https://docs.stirlingpdf.com/Configuration/OCR', '_blank')} > {t('ocr.languagePicker.viewSetupGuide', 'View setup guide →')} @@ -158,4 +158,4 @@ const LanguagePicker: React.FC = ({ ); }; -export default LanguagePicker; \ No newline at end of file +export default LanguagePicker; diff --git a/frontend/src/proprietary/components/tools/pdfTextEditor/FontStatusPanel.tsx b/frontend/src/core/components/tools/pdfTextEditor/FontStatusPanel.tsx similarity index 100% rename from frontend/src/proprietary/components/tools/pdfTextEditor/FontStatusPanel.tsx rename to frontend/src/core/components/tools/pdfTextEditor/FontStatusPanel.tsx diff --git a/frontend/src/proprietary/components/tools/pdfTextEditor/PdfTextEditorView.tsx b/frontend/src/core/components/tools/pdfTextEditor/PdfTextEditorView.tsx similarity index 95% rename from frontend/src/proprietary/components/tools/pdfTextEditor/PdfTextEditorView.tsx rename to frontend/src/core/components/tools/pdfTextEditor/PdfTextEditorView.tsx index 3c9309e960..3c5b45e0e6 100644 --- a/frontend/src/proprietary/components/tools/pdfTextEditor/PdfTextEditorView.tsx +++ b/frontend/src/core/components/tools/pdfTextEditor/PdfTextEditorView.tsx @@ -21,6 +21,7 @@ import { Title, Tooltip, } from '@mantine/core'; +import { Dropzone } from '@mantine/dropzone'; import { useTranslation } from 'react-i18next'; import DescriptionIcon from '@mui/icons-material/DescriptionOutlined'; import FileDownloadIcon from '@mui/icons-material/FileDownloadOutlined'; @@ -32,9 +33,12 @@ import CloseIcon from '@mui/icons-material/Close'; import MergeTypeIcon from '@mui/icons-material/MergeType'; import CallSplitIcon from '@mui/icons-material/CallSplit'; import MoreVertIcon from '@mui/icons-material/MoreVert'; +import UploadFileIcon from '@mui/icons-material/UploadFileOutlined'; +import SaveIcon from '@mui/icons-material/SaveOutlined'; import { Rnd } from 'react-rnd'; -import NavigationWarningModal from '@core/components/shared/NavigationWarningModal'; +import NavigationWarningModal from '@app/components/shared/NavigationWarningModal'; +import { useFileContext } from '@app/contexts/FileContext'; import { PdfTextEditorViewData, PdfJsonFont, @@ -313,6 +317,7 @@ type GroupingMode = 'auto' | 'paragraph' | 'singleLine'; const PdfTextEditorView = ({ data }: PdfTextEditorViewProps) => { const { t } = useTranslation(); + const { activeFiles } = useFileContext(); const [activeGroupId, setActiveGroupId] = useState(null); const [editingGroupId, setEditingGroupId] = useState(null); const [activeImageId, setActiveImageId] = useState(null); @@ -329,6 +334,7 @@ const PdfTextEditorView = ({ data }: PdfTextEditorViewProps) => { const containerRef = useRef(null); const editorRefs = useRef>(new Map()); const caretOffsetsRef = useRef>(new Map()); + const composingGroupsRef = useRef>(new Set()); const lastSelectedGroupIdRef = useRef(null); const widthOverridesRef = useRef>(widthOverrides); const resizingRef = useRef<{ @@ -375,6 +381,7 @@ const PdfTextEditorView = ({ data }: PdfTextEditorViewProps) => { fileName, errorMessage, isGeneratingPdf, + isSavingToWorkbench, isConverting, conversionProgress, hasChanges, @@ -389,11 +396,12 @@ const PdfTextEditorView = ({ data }: PdfTextEditorViewProps) => { onReset, onDownloadJson, onGeneratePdf, - onGeneratePdfForNavigation, + onSaveToWorkbench, onForceSingleTextElementChange, onGroupingModeChange, onMergeGroups, onUngroupGroup, + onLoadFile, } = data; // Define derived variables immediately after props destructuring, before any hooks @@ -607,6 +615,18 @@ const PdfTextEditorView = ({ data }: PdfTextEditorViewProps) => { [editingGroupId, onGroupEdit], ); + const handleCompositionStart = useCallback((groupId: string) => { + composingGroupsRef.current.add(groupId); + }, []); + + const handleCompositionEnd = useCallback( + (element: HTMLElement, pageIndex: number, groupId: string) => { + composingGroupsRef.current.delete(groupId); + syncEditorValue(element, pageIndex, groupId); + }, + [syncEditorValue], + ); + const handleMergeSelection = useCallback(() => { if (!canMergeSelection) { return; @@ -1430,7 +1450,8 @@ const selectionToolbarPosition = useMemo(() => { height: '100%', display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) 320px', - alignItems: 'start', + gridTemplateRows: '1fr', + alignItems: hasDocument ? 'start' : 'stretch', gap: '1.5rem', }} > @@ -1486,6 +1507,17 @@ const selectionToolbarPosition = useMemo(() => { > {t('pdfTextEditor.actions.generatePdf', 'Generate PDF')} + {fileName && ( @@ -1639,17 +1671,45 @@ const selectionToolbarPosition = useMemo(() => { )} {!hasDocument && !isConverting && ( - - - - - {t('pdfTextEditor.empty.title', 'No document loaded')} - - - {t('pdfTextEditor.empty.subtitle', 'Load a PDF or JSON file to begin editing text content.')} - - - + + { + if (files.length > 0) { + onLoadFile(files[0]); + } + }} + accept={['application/pdf', 'application/json']} + maxFiles={1} + style={{ + width: '100%', + maxWidth: 480, + minHeight: 200, + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + border: '2px dashed var(--mantine-color-gray-4)', + borderRadius: 'var(--mantine-radius-lg)', + cursor: 'pointer', + transition: 'border-color 150ms ease, background-color 150ms ease', + }} + > + + + + {t('pdfTextEditor.empty.title', 'No document loaded')} + + + {activeFiles.length > 0 + ? t('pdfTextEditor.empty.dropzoneWithFiles', 'Select a file from the Files tab, or drag and drop a PDF or JSON file here, or click to browse') + : t('pdfTextEditor.empty.dropzone', 'Drag and drop a PDF or JSON file here, or click to browse')} + + + + )} {isConverting && ( @@ -1683,7 +1743,7 @@ const selectionToolbarPosition = useMemo(() => { )} - {hasDocument && ( + {hasDocument && !isConverting && ( { // Determine text wrapping behavior based on whether text has been changed const hasChanges = changed; const widthExtended = resolvedWidth - baseWidth > 0.5; - const enableWrap = isParagraphLayout || widthExtended || isEditing || hasChanges; + // Only enable wrapping if: + // 1. It's paragraph layout (multi-line groups should wrap) + // 2. Width was manually extended (user explicitly made space for wrapping) + // 3. Has changes AND was already wrapping (preserve existing wrap state) + // DO NOT enable wrapping just because isEditing - text should only wrap when it actually overflows + const wasWrapping = isParagraphLayout || widthExtended; + const enableWrap = wasWrapping || (hasChanges && wasWrapping); const whiteSpace = enableWrap ? 'pre-wrap' : 'pre'; const wordBreak = enableWrap ? 'break-word' : 'normal'; const overflowWrap = enableWrap ? 'break-word' : 'normal'; // For paragraph mode, allow height to grow to accommodate lines without wrapping // For single-line mode, maintain fixed height based on PDF bounds - const useFlexibleHeight = isEditing || enableWrap || (isParagraphLayout && lineCount > 1); + const useFlexibleHeight = enableWrap || (isParagraphLayout && lineCount > 1); // The renderGroupContainer wrapper adds 4px horizontal padding (2px left + 2px right) // We need to add this to the container width to compensate, so the inner content @@ -2226,6 +2292,10 @@ const selectionToolbarPosition = useMemo(() => { contentEditable suppressContentEditableWarning data-editor-group={group.id} + onCompositionStart={() => handleCompositionStart(group.id)} + onCompositionEnd={(event) => + handleCompositionEnd(event.currentTarget, group.pageIndex, group.id) + } onFocus={(event) => { const primaryFont = fontFamily.split(',')[0]?.replace(/['"]/g, '').trim(); if (primaryFont && typeof document !== 'undefined') { @@ -2247,6 +2317,7 @@ const selectionToolbarPosition = useMemo(() => { event.stopPropagation(); }} onBlur={(event) => { + composingGroupsRef.current.delete(group.id); syncEditorValue(event.currentTarget, group.pageIndex, group.id, { skipCaretRestore: true, }); @@ -2256,6 +2327,9 @@ const selectionToolbarPosition = useMemo(() => { setEditingGroupId(null); }} onInput={(event) => { + if (composingGroupsRef.current.has(group.id)) { + return; + } syncEditorValue(event.currentTarget, group.pageIndex, group.id); }} style={{ @@ -2438,7 +2512,7 @@ const selectionToolbarPosition = useMemo(() => { {/* Navigation Warning Modal */} ); diff --git a/frontend/src/core/components/tools/shared/createToolFlow.tsx b/frontend/src/core/components/tools/shared/createToolFlow.tsx index 5e42e73ab2..4ddaae67c4 100644 --- a/frontend/src/core/components/tools/shared/createToolFlow.tsx +++ b/frontend/src/core/components/tools/shared/createToolFlow.tsx @@ -87,6 +87,7 @@ export function createToolFlow(config: ToolFlowConfig steps.create(stepConfig.title, { isVisible: stepConfig.isVisible, + isCollapsed: stepConfig.isCollapsed, onCollapsedClick: stepConfig.onCollapsedClick, tooltip: stepConfig.tooltip }, stepConfig.content) diff --git a/frontend/src/core/components/tools/sign/SavedSignaturesSection.tsx b/frontend/src/core/components/tools/sign/SavedSignaturesSection.tsx index c2e8ca8f1d..95a8a7e77b 100644 --- a/frontend/src/core/components/tools/sign/SavedSignaturesSection.tsx +++ b/frontend/src/core/components/tools/sign/SavedSignaturesSection.tsx @@ -2,14 +2,16 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ActionIcon, Alert, Badge, Box, Card, Group, Stack, Text, TextInput, Tooltip } from '@mantine/core'; import { LocalIcon } from '@app/components/shared/LocalIcon'; -import { MAX_SAVED_SIGNATURES, SavedSignature, SavedSignatureType } from '@app/hooks/tools/sign/useSavedSignatures'; +import { SavedSignature, SavedSignatureType } from '@app/hooks/tools/sign/useSavedSignatures'; import type { StorageType } from '@app/services/signatureStorageService'; interface SavedSignaturesSectionProps { signatures: SavedSignature[]; disabled?: boolean; isAtCapacity: boolean; + maxLimit: number; storageType?: StorageType | null; + isAdmin?: boolean; onUseSignature: (signature: SavedSignature) => void; onDeleteSignature: (signature: SavedSignature) => void; onRenameSignature: (id: string, label: string) => void; @@ -26,7 +28,9 @@ export const SavedSignaturesSection = ({ signatures, disabled = false, isAtCapacity, + maxLimit, storageType: _storageType, + isAdmin = false, onUseSignature, onDeleteSignature, onRenameSignature, @@ -155,7 +159,7 @@ export const SavedSignaturesSection = ({ {translate( 'saved.emptyDescription', 'Draw, upload, or type a signature above, then use "Save to library" to keep up to {{max}} favourites ready to use.', - { max: MAX_SAVED_SIGNATURES } + { max: maxLimit } )} @@ -216,7 +220,7 @@ export const SavedSignaturesSection = ({ {translate('saved.limitDescription', 'Remove a saved signature before adding new ones (max {{max}}).', { - max: MAX_SAVED_SIGNATURES, + max: maxLimit, })} @@ -365,17 +369,19 @@ export const SavedSignaturesSection = ({ > - - onDeleteSignature(activeSharedSignature)} - disabled={disabled} - > - - - + {isAdmin && ( + + onDeleteSignature(activeSharedSignature)} + disabled={disabled} + > + + + + )} {renderPreview(activeSharedSignature)} diff --git a/frontend/src/core/components/tools/sign/SignSettings.tsx b/frontend/src/core/components/tools/sign/SignSettings.tsx index 6fb3238f8c..233b87373c 100644 --- a/frontend/src/core/components/tools/sign/SignSettings.tsx +++ b/frontend/src/core/components/tools/sign/SignSettings.tsx @@ -14,7 +14,7 @@ import { ImageUploader } from "@app/components/annotation/shared/ImageUploader"; import { TextInputWithFont } from "@app/components/annotation/shared/TextInputWithFont"; import { ColorPicker } from "@app/components/annotation/shared/ColorPicker"; import { LocalIcon } from "@app/components/shared/LocalIcon"; -import { useSavedSignatures, SavedSignature, SavedSignaturePayload, SavedSignatureType, MAX_SAVED_SIGNATURES, AddSignatureResult } from '@app/hooks/tools/sign/useSavedSignatures'; +import { useSavedSignatures, SavedSignature, SavedSignaturePayload, SavedSignatureType, AddSignatureResult } from '@app/hooks/tools/sign/useSavedSignatures'; import { SavedSignaturesSection } from '@app/components/tools/sign/SavedSignaturesSection'; import { buildSignaturePreview } from '@app/utils/signaturePreview'; @@ -96,11 +96,13 @@ const SignSettings = ({ const { savedSignatures, isAtCapacity: isSavedSignatureLimitReached, + maxLimit, addSignature, removeSignature, updateSignatureLabel, byTypeCounts, storageType, + isAdmin, } = useSavedSignatures(); const [signatureSource, setSignatureSource] = useState(() => { const paramSource = parameters.signatureType as SignatureSource; @@ -246,16 +248,19 @@ const SignSettings = ({ (signature: SavedSignature) => { setPlacementManuallyPaused(false); + // Use the data URL directly (already converted to base64 when loaded) + const dataUrlToUse = signature.dataUrl; + if (signature.type === 'canvas') { if (parameters.signatureType !== 'canvas') { onParameterChange('signatureType', 'canvas'); } - setCanvasSignatureData(signature.dataUrl); + setCanvasSignatureData(dataUrlToUse); } else if (signature.type === 'image') { if (parameters.signatureType !== 'image') { onParameterChange('signatureType', 'image'); } - setImageSignatureData(signature.dataUrl); + setImageSignatureData(dataUrlToUse); } else if (signature.type === 'text') { if (parameters.signatureType !== 'text') { onParameterChange('signatureType', 'text'); @@ -269,7 +274,7 @@ const SignSettings = ({ const savedKey = signature.type === 'text' ? buildTextSignatureKey(signature.signerName, signature.fontSize, signature.fontFamily, signature.textColor) - : signature.dataUrl; + : dataUrlToUse; setLastSavedKeyForType(signature.type, savedKey); const activate = () => onActivateSignaturePlacement?.(); @@ -326,8 +331,8 @@ const SignSettings = ({ } else if (isSaved) { tooltipMessage = translate('saved.noChanges', 'Current signature is already saved.'); } else if (isSavedSignatureLimitReached) { - tooltipMessage = translate('saved.limitDescription', 'Remove a saved signature before adding new ones (max {{max}}).', { - max: MAX_SAVED_SIGNATURES, + tooltipMessage = translate('saved.limitDescription', 'You have reached the maximum limit of {{max}} saved signatures. Remove a saved signature before adding new ones.', { + max: maxLimit, }); } @@ -791,7 +796,9 @@ const SignSettings = ({ signatures={savedSignatures} disabled={disabled} isAtCapacity={isSavedSignatureLimitReached} + maxLimit={maxLimit} storageType={storageType} + isAdmin={isAdmin} onUseSignature={handleUseSavedSignature} onDeleteSignature={handleDeleteSavedSignature} onRenameSignature={handleRenameSavedSignature} diff --git a/frontend/src/core/components/tools/validateSignature/reportView/styles.css b/frontend/src/core/components/tools/validateSignature/reportView/styles.css index b2d01f2242..27b1081d4c 100644 --- a/frontend/src/core/components/tools/validateSignature/reportView/styles.css +++ b/frontend/src/core/components/tools/validateSignature/reportView/styles.css @@ -44,15 +44,15 @@ .simulated-page { width: min(820px, 100%); min-height: 1040px; - background-color: rgb(var(--pdf-light-simulated-page-bg)) !important; - box-shadow: 0 12px 32px rgba(var(--pdf-light-simulated-page-text), 0.12) !important; + background-color: var(--bg-raised) !important; + box-shadow: 0 12px 32px var(--shadow-color) !important; border-radius: 12px !important; padding: 48px 56px !important; position: relative; overflow: hidden; display: flex; flex-direction: column; - color: rgb(var(--pdf-light-simulated-page-text)) !important; + color: var(--text-primary) !important; } /* Container for the interactive report view */ @@ -67,12 +67,12 @@ /* Keep field blocks stable colors across themes */ .field-value { - border: 1px solid rgb(var(--pdf-light-box-border)) !important; - background-color: rgb(var(--pdf-light-box-bg)) !important; + border: 1px solid var(--border-default) !important; + background-color: var(--bg-raised) !important; } .field-container { - color: rgb(var(--pdf-light-simulated-page-text)) !important; + color: var(--text-primary) !important; } /* Thumbnail preview styles */ @@ -103,3 +103,28 @@ color: rgb(var(--pdf-light-text-muted)); background: linear-gradient(145deg, var(--mantine-color-gray-1) 0%, var(--mantine-color-gray-0) 100%); } + +/* Flash highlight animation for section navigation */ +@keyframes section-flash { + 0% { + background-color: rgba(255, 235, 59, 0); + box-shadow: none; + } + 20% { + background-color: rgba(255, 235, 59, 0.35); + box-shadow: 0 0 20px rgba(255, 235, 59, 0.5); + } + 50% { + background-color: rgba(255, 235, 59, 0.25); + box-shadow: 0 0 15px rgba(255, 235, 59, 0.4); + } + 100% { + background-color: rgba(255, 235, 59, 0); + box-shadow: none; + } +} + +.section-flash-highlight { + animation: section-flash 1.5s ease-out; + border-radius: 8px; +} diff --git a/frontend/src/core/constants/links.ts b/frontend/src/core/constants/links.ts new file mode 100644 index 0000000000..294e993633 --- /dev/null +++ b/frontend/src/core/constants/links.ts @@ -0,0 +1 @@ +export const devApiLink = "/swagger-ui/index.html"; diff --git a/frontend/src/core/contexts/AppConfigContext.tsx b/frontend/src/core/contexts/AppConfigContext.tsx index 5ae0d30a67..2e42f95812 100644 --- a/frontend/src/core/contexts/AppConfigContext.tsx +++ b/frontend/src/core/contexts/AppConfigContext.tsx @@ -20,6 +20,7 @@ export interface AppConfig { serverPort?: number; appNameNavbar?: string; languages?: string[]; + defaultLocale?: string; logoStyle?: 'modern' | 'classic'; enableLogin?: boolean; enableEmailInvites?: boolean; diff --git a/frontend/src/core/contexts/NavigationContext.tsx b/frontend/src/core/contexts/NavigationContext.tsx index 500a6db5e1..c11649400a 100644 --- a/frontend/src/core/contexts/NavigationContext.tsx +++ b/frontend/src/core/contexts/NavigationContext.tsx @@ -1,4 +1,4 @@ -import React, { createContext, useContext, useReducer, useCallback } from 'react'; +import React, { createContext, useContext, useReducer, useCallback, useMemo } from 'react'; import { WorkbenchType, getDefaultWorkbench } from '@app/types/workbench'; import { ToolId, isValidToolId } from '@app/types/toolId'; import { useToolRegistry } from '@app/contexts/ToolRegistryContext'; @@ -110,8 +110,8 @@ export const NavigationProvider: React.FC<{ const { allTools: toolRegistry } = useToolRegistry(); const unsavedChangesCheckerRef = React.useRef<(() => boolean) | null>(null); - const actions: NavigationContextActions = { - setWorkbench: useCallback((workbench: WorkbenchType) => { + // Memoize individual callbacks + const setWorkbench = useCallback((workbench: WorkbenchType) => { // Check for unsaved changes using registered checker or state const hasUnsavedChanges = unsavedChangesCheckerRef.current?.() || state.hasUnsavedChanges; console.log('[NavigationContext] setWorkbench:', { @@ -152,13 +152,13 @@ export const NavigationProvider: React.FC<{ } else { dispatch({ type: 'SET_WORKBENCH', payload: { workbench } }); } - }, [state.workbench, state.hasUnsavedChanges]), + }, [state.workbench, state.hasUnsavedChanges]); - setSelectedTool: useCallback((toolId: ToolId | null) => { + const setSelectedTool = useCallback((toolId: ToolId | null) => { dispatch({ type: 'SET_SELECTED_TOOL', payload: { toolId } }); - }, []), + }, []); - setToolAndWorkbench: useCallback((toolId: ToolId | null, workbench: WorkbenchType) => { + const setToolAndWorkbench = useCallback((toolId: ToolId | null, workbench: WorkbenchType) => { // Check for unsaved changes using registered checker or state const hasUnsavedChanges = unsavedChangesCheckerRef.current?.() || state.hasUnsavedChanges; @@ -177,25 +177,25 @@ export const NavigationProvider: React.FC<{ } else { dispatch({ type: 'SET_TOOL_AND_WORKBENCH', payload: { toolId, workbench } }); } - }, [state.workbench, state.hasUnsavedChanges]), + }, [state.workbench, state.hasUnsavedChanges]); - setHasUnsavedChanges: useCallback((hasChanges: boolean) => { + const setHasUnsavedChanges = useCallback((hasChanges: boolean) => { dispatch({ type: 'SET_UNSAVED_CHANGES', payload: { hasChanges } }); - }, []), + }, []); - registerUnsavedChangesChecker: useCallback((checker: () => boolean) => { + const registerUnsavedChangesChecker = useCallback((checker: () => boolean) => { unsavedChangesCheckerRef.current = checker; - }, []), + }, []); - unregisterUnsavedChangesChecker: useCallback(() => { + const unregisterUnsavedChangesChecker = useCallback(() => { unsavedChangesCheckerRef.current = null; - }, []), + }, []); - showNavigationWarning: useCallback((show: boolean) => { + const showNavigationWarning = useCallback((show: boolean) => { dispatch({ type: 'SHOW_NAVIGATION_WARNING', payload: { show } }); - }, []), + }, []); - requestNavigation: useCallback((navigationFn: () => void) => { + const requestNavigation = useCallback((navigationFn: () => void) => { if (!state.hasUnsavedChanges) { navigationFn(); return; @@ -203,9 +203,9 @@ export const NavigationProvider: React.FC<{ dispatch({ type: 'SET_PENDING_NAVIGATION', payload: { navigationFn } }); dispatch({ type: 'SHOW_NAVIGATION_WARNING', payload: { show: true } }); - }, [state.hasUnsavedChanges]), + }, [state.hasUnsavedChanges]); - confirmNavigation: useCallback(() => { + const confirmNavigation = useCallback(() => { console.log('[NavigationContext] confirmNavigation called', { hasPendingNav: !!state.pendingNavigation, currentWorkbench: state.workbench, @@ -218,18 +218,18 @@ export const NavigationProvider: React.FC<{ dispatch({ type: 'SET_PENDING_NAVIGATION', payload: { navigationFn: null } }); dispatch({ type: 'SHOW_NAVIGATION_WARNING', payload: { show: false } }); console.log('[NavigationContext] confirmNavigation completed'); - }, [state.pendingNavigation, state.workbench, state.selectedTool]), + }, [state.pendingNavigation, state.workbench, state.selectedTool]); - cancelNavigation: useCallback(() => { + const cancelNavigation = useCallback(() => { dispatch({ type: 'SET_PENDING_NAVIGATION', payload: { navigationFn: null } }); dispatch({ type: 'SHOW_NAVIGATION_WARNING', payload: { show: false } }); - }, []), + }, []); - clearToolSelection: useCallback(() => { + const clearToolSelection = useCallback(() => { dispatch({ type: 'SET_TOOL_AND_WORKBENCH', payload: { toolId: null, workbench: getDefaultWorkbench() } }); - }, []), + }, []); - handleToolSelect: useCallback((toolId: string) => { + const handleToolSelect = useCallback((toolId: string) => { if (toolId === 'allTools') { dispatch({ type: 'SET_TOOL_AND_WORKBENCH', payload: { toolId: null, workbench: getDefaultWorkbench() } }); return; @@ -245,11 +245,40 @@ export const NavigationProvider: React.FC<{ const tool = isValidToolId(toolId)? toolRegistry[toolId] : null; const workbench = tool ? (tool.workbench || getDefaultWorkbench()) : getDefaultWorkbench(); - // Validate toolId and convert to ToolId type - const validToolId = isValidToolId(toolId) ? toolId : null; - dispatch({ type: 'SET_TOOL_AND_WORKBENCH', payload: { toolId: validToolId, workbench } }); - }, [toolRegistry]) - }; + // Validate toolId and convert to ToolId type + const validToolId = isValidToolId(toolId) ? toolId : null; + dispatch({ type: 'SET_TOOL_AND_WORKBENCH', payload: { toolId: validToolId, workbench } }); + }, [toolRegistry]); + + // Memoize the actions object to prevent unnecessary context updates + // This is critical to avoid infinite loops when effects depend on actions + const actions: NavigationContextActions = useMemo(() => ({ + setWorkbench, + setSelectedTool, + setToolAndWorkbench, + setHasUnsavedChanges, + registerUnsavedChangesChecker, + unregisterUnsavedChangesChecker, + showNavigationWarning, + requestNavigation, + confirmNavigation, + cancelNavigation, + clearToolSelection, + handleToolSelect, + }), [ + setWorkbench, + setSelectedTool, + setToolAndWorkbench, + setHasUnsavedChanges, + registerUnsavedChangesChecker, + unregisterUnsavedChangesChecker, + showNavigationWarning, + requestNavigation, + confirmNavigation, + cancelNavigation, + clearToolSelection, + handleToolSelect, + ]); const stateValue: NavigationContextStateValue = { workbench: state.workbench, @@ -259,9 +288,10 @@ export const NavigationProvider: React.FC<{ showNavigationWarning: state.showNavigationWarning }; - const actionsValue: NavigationContextActionsValue = { + // Also memoize the context value to prevent unnecessary re-renders + const actionsValue: NavigationContextActionsValue = useMemo(() => ({ actions - }; + }), [actions]); return ( diff --git a/frontend/src/core/contexts/ToolWorkflowContext.tsx b/frontend/src/core/contexts/ToolWorkflowContext.tsx index 9717b7f68f..b2c6958018 100644 --- a/frontend/src/core/contexts/ToolWorkflowContext.tsx +++ b/frontend/src/core/contexts/ToolWorkflowContext.tsx @@ -224,11 +224,15 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) { return; } + if (navigationState.pendingNavigation || navigationState.showNavigationWarning) { + return; + } + const currentCustomView = customWorkbenchViews.find(view => view.workbenchId === navigationState.workbench); if (!currentCustomView || currentCustomView.data == null) { actions.setWorkbench(getDefaultWorkbench()); } - }, [actions, customWorkbenchViews, navigationState.workbench]); + }, [actions, customWorkbenchViews, navigationState.workbench, navigationState.pendingNavigation, navigationState.showNavigationWarning]); // Persisted via PreferencesContext; no direct localStorage writes needed here diff --git a/frontend/src/core/contexts/TourOrchestrationContext.tsx b/frontend/src/core/contexts/TourOrchestrationContext.tsx index f8364ba5c3..a594e8322b 100644 --- a/frontend/src/core/contexts/TourOrchestrationContext.tsx +++ b/frontend/src/core/contexts/TourOrchestrationContext.tsx @@ -1,4 +1,5 @@ import React, { createContext, useContext, useCallback, useRef } from 'react'; +import { BASE_PATH } from '@app/constants/app'; import { useFileHandler } from '@app/hooks/useFileHandler'; import { useFilesModalContext } from '@app/contexts/FilesModalContext'; import { useNavigationActions } from '@app/contexts/NavigationContext'; @@ -110,7 +111,7 @@ export const TourOrchestrationProvider: React.FC<{ children: React.ReactNode }> const loadSampleFile = useCallback(async () => { try { - const response = await fetch('samples/Sample.pdf'); + const response = await fetch(`${BASE_PATH}/samples/Sample.pdf`); const blob = await response.blob(); const file = new File([blob], 'Sample.pdf', { type: 'application/pdf' }); diff --git a/frontend/src/core/data/useTranslatedToolRegistry.tsx b/frontend/src/core/data/useTranslatedToolRegistry.tsx index cda99dc40e..3208e4ef78 100644 --- a/frontend/src/core/data/useTranslatedToolRegistry.tsx +++ b/frontend/src/core/data/useTranslatedToolRegistry.tsx @@ -1,6 +1,7 @@ import { useMemo } from "react"; import LocalIcon from "@app/components/shared/LocalIcon"; import { useTranslation } from "react-i18next"; +import { devApiLink } from "@app/constants/links"; import SplitPdfPanel from "@app/tools/Split"; import CompressPdfPanel from "@app/tools/Compress"; import OCRPanel from "@app/tools/OCR"; @@ -27,6 +28,7 @@ import AdjustContrastSingleStepSettings from "@app/components/tools/adjustContra import { adjustContrastOperationConfig } from "@app/hooks/tools/adjustContrast/useAdjustContrastOperation"; import { getSynonyms } from "@app/utils/toolSynonyms"; import { useProprietaryToolRegistry } from "@app/data/useProprietaryToolRegistry"; +import GetPdfInfo from "@app/tools/GetPdfInfo"; import AddWatermark from "@app/tools/AddWatermark"; import AddStamp from "@app/tools/AddStamp"; import AddAttachments from "@app/tools/AddAttachments"; @@ -43,6 +45,7 @@ import CertSign from "@app/tools/CertSign"; import BookletImposition from "@app/tools/BookletImposition"; import Flatten from "@app/tools/Flatten"; import Rotate from "@app/tools/Rotate"; +import PdfTextEditor from "@app/tools/pdfTextEditor/PdfTextEditor"; import ChangeMetadata from "@app/tools/ChangeMetadata"; import Crop from "@app/tools/Crop"; import Sign from "@app/tools/Sign"; @@ -150,6 +153,23 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog { // Proprietary tools (if any) ...proprietaryTools, // Recommended Tools in order + pdfTextEditor: { + icon: , + name: t("home.pdfTextEditor.title", "PDF Text Editor"), + component: PdfTextEditor, + description: t( + "home.pdfTextEditor.desc", + "Review and edit text and images in PDFs with grouped text editing and PDF regeneration" + ), + categoryId: ToolCategoryId.RECOMMENDED_TOOLS, + subcategoryId: SubcategoryId.GENERAL, + maxFiles: 1, + endpoints: ["text-editor-pdf"], + synonyms: getSynonyms(t, "pdfTextEditor"), + supportsAutomate: false, + automationSettings: null, + versionStatus: "alpha", + }, multiTool: { icon: , name: t("home.multiTool.title", "Multi-Tool"), @@ -323,14 +343,15 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog { getPdfInfo: { icon: , name: t("home.getPdfInfo.title", "Get ALL Info on PDF"), - component: null, + component: GetPdfInfo, description: t("home.getPdfInfo.desc", "Grabs any and all information possible on PDFs"), categoryId: ToolCategoryId.STANDARD_TOOLS, subcategoryId: SubcategoryId.VERIFICATION, endpoints: ["get-info-on-pdf"], synonyms: getSynonyms(t, "getPdfInfo"), supportsAutomate: false, - automationSettings: null + automationSettings: null, + maxFiles: 1, }, validateSignature: { icon: , @@ -764,7 +785,7 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog { description: t("home.devApi.desc", "Link to API documentation"), categoryId: ToolCategoryId.ADVANCED_TOOLS, subcategoryId: SubcategoryId.DEVELOPER_TOOLS, - link: "https://stirlingpdf.io/swagger-ui/5.21.0/index.html", + link: devApiLink, synonyms: getSynonyms(t, "devApi"), supportsAutomate: false, automationSettings: null diff --git a/frontend/src/core/hooks/tools/adjustContrast/useAdjustContrastOperation.ts b/frontend/src/core/hooks/tools/adjustContrast/useAdjustContrastOperation.ts index 176ddd4b5f..6524a2585d 100644 --- a/frontend/src/core/hooks/tools/adjustContrast/useAdjustContrastOperation.ts +++ b/frontend/src/core/hooks/tools/adjustContrast/useAdjustContrastOperation.ts @@ -1,5 +1,5 @@ import { useTranslation } from 'react-i18next'; -import { ToolType, useToolOperation } from '@app/hooks/tools/shared/useToolOperation'; +import { ToolType, useToolOperation, CustomProcessorResult } from '@app/hooks/tools/shared/useToolOperation'; import { AdjustContrastParameters, defaultParameters } from '@app/hooks/tools/adjustContrast/useAdjustContrastParameters'; import { PDFDocument as PDFLibDocument } from 'pdf-lib'; import { applyAdjustmentsToCanvas } from '@app/components/tools/adjustContrast/utils'; @@ -46,7 +46,7 @@ async function buildAdjustedPdfForFile(file: File, params: AdjustContrastParamet return out; } -async function processPdfClientSide(params: AdjustContrastParameters, files: File[]): Promise { +async function processPdfClientSide(params: AdjustContrastParameters, files: File[]): Promise { // Limit concurrency to avoid exhausting memory/CPU while still getting speedups // Heuristic: use up to 4 workers on capable machines, otherwise 2-3 let CONCURRENCY_LIMIT = 2; @@ -72,7 +72,12 @@ async function processPdfClientSide(params: AdjustContrastParameters, files: Fil return results; }; - return mapWithConcurrency(files, CONCURRENCY_LIMIT, (file) => buildAdjustedPdfForFile(file, params)); + const processedFiles = await mapWithConcurrency(files, CONCURRENCY_LIMIT, (file) => buildAdjustedPdfForFile(file, params)); + + return { + files: processedFiles, + consumedAllInputs: false, + }; } export const adjustContrastOperationConfig = { diff --git a/frontend/src/core/hooks/tools/automate/useAutomateOperation.ts b/frontend/src/core/hooks/tools/automate/useAutomateOperation.ts index 004f289027..f6fbcacda7 100644 --- a/frontend/src/core/hooks/tools/automate/useAutomateOperation.ts +++ b/frontend/src/core/hooks/tools/automate/useAutomateOperation.ts @@ -36,7 +36,10 @@ export function useAutomateOperation() { ); console.log(`āœ… Automation completed, returning ${finalResults.length} files`); - return finalResults; + return { + files: finalResults, + consumedAllInputs: false, + }; }, [toolRegistry]); return useToolOperation({ diff --git a/frontend/src/core/hooks/tools/convert/useConvertOperation.ts b/frontend/src/core/hooks/tools/convert/useConvertOperation.ts index 9134c9db4a..9650ac0e2a 100644 --- a/frontend/src/core/hooks/tools/convert/useConvertOperation.ts +++ b/frontend/src/core/hooks/tools/convert/useConvertOperation.ts @@ -3,8 +3,8 @@ import apiClient from '@app/services/apiClient'; import { useTranslation } from 'react-i18next'; import { ConvertParameters, defaultParameters } from '@app/hooks/tools/convert/useConvertParameters'; import { createFileFromApiResponse } from '@app/utils/fileResponseUtils'; -import { useToolOperation, ToolType } from '@app/hooks/tools/shared/useToolOperation'; -import { getEndpointUrl, isImageFormat, isWebFormat } from '@app/utils/convertUtils'; +import { useToolOperation, ToolType, CustomProcessorResult } from '@app/hooks/tools/shared/useToolOperation'; +import { getEndpointUrl, isImageFormat, isWebFormat, isOfficeFormat } from '@app/utils/convertUtils'; // Static function that can be used by both the hook and automation executor export const shouldProcessFilesSeparately = ( @@ -21,6 +21,10 @@ export const shouldProcessFilesSeparately = ( (parameters.fromExtension === 'pdf' && parameters.toExtension === 'pdfa') || // PDF to text-like formats should be one output per input (parameters.fromExtension === 'pdf' && ['txt', 'rtf', 'csv'].includes(parameters.toExtension)) || + // PDF to office format conversions (each PDF should generate its own office file) + (parameters.fromExtension === 'pdf' && isOfficeFormat(parameters.toExtension)) || + // Office files to PDF conversions (each file should be processed separately via LibreOffice) + (isOfficeFormat(parameters.fromExtension) && parameters.toExtension === 'pdf') || // Web files to PDF conversions (each web file should generate its own PDF) ((isWebFormat(parameters.fromExtension) || parameters.fromExtension === 'web') && parameters.toExtension === 'pdf') || @@ -98,7 +102,7 @@ export const createFileFromResponse = ( export const convertProcessor = async ( parameters: ConvertParameters, selectedFiles: File[] -): Promise => { +): Promise => { const processedFiles: File[] = []; const endpoint = getEndpointUrl(parameters.fromExtension, parameters.toExtension); @@ -107,7 +111,9 @@ export const convertProcessor = async ( } // Convert-specific routing logic: decide batch vs individual processing - if (shouldProcessFilesSeparately(selectedFiles, parameters)) { + const isSeparateProcessing = shouldProcessFilesSeparately(selectedFiles, parameters); + + if (isSeparateProcessing) { // Individual processing for complex cases (PDF→image, smart detection, etc.) for (const file of selectedFiles) { try { @@ -134,7 +140,14 @@ export const convertProcessor = async ( processedFiles.push(convertedFile); } - return processedFiles; + // When batch processing multiple files into one output (e.g., 3 images → 1 PDF), + // mark all inputs as consumed even though there's only 1 output file + const isCombiningMultiple = !isSeparateProcessing && selectedFiles.length > 1; + + return { + files: processedFiles, + consumedAllInputs: isCombiningMultiple, + }; }; // Static configuration object @@ -151,7 +164,7 @@ export const useConvertOperation = () => { const customConvertProcessor = useCallback(async ( parameters: ConvertParameters, selectedFiles: File[] - ): Promise => { + ): Promise => { return convertProcessor(parameters, selectedFiles); }, []); diff --git a/frontend/src/core/hooks/tools/extractPages/useExtractPagesOperation.ts b/frontend/src/core/hooks/tools/extractPages/useExtractPagesOperation.ts index 086fd65cc2..687dde1700 100644 --- a/frontend/src/core/hooks/tools/extractPages/useExtractPagesOperation.ts +++ b/frontend/src/core/hooks/tools/extractPages/useExtractPagesOperation.ts @@ -1,6 +1,6 @@ import apiClient from '@app/services/apiClient'; import { useTranslation } from 'react-i18next'; -import { ToolType, useToolOperation } from '@app/hooks/tools/shared/useToolOperation'; +import { ToolType, useToolOperation, CustomProcessorResult } from '@app/hooks/tools/shared/useToolOperation'; import { createStandardErrorHandler } from '@app/utils/toolErrorHandler'; import { ExtractPagesParameters, defaultParameters } from '@app/hooks/tools/extractPages/useExtractPagesParameters'; import { pdfWorkerManager } from '@app/services/pdfWorkerManager'; @@ -23,7 +23,7 @@ async function resolveSelectionToCsv(expression: string, file: File): Promise => { + customProcessor: async (parameters: ExtractPagesParameters, files: File[]): Promise => { const outputs: File[] = []; for (const file of files) { @@ -43,7 +43,10 @@ export const extractPagesOperationConfig = { outputs.push(outFile); } - return outputs; + return { + files: outputs, + consumedAllInputs: false, + }; }, defaultParameters, } as const; diff --git a/frontend/src/core/hooks/tools/getPdfInfo/useGetPdfInfoOperation.ts b/frontend/src/core/hooks/tools/getPdfInfo/useGetPdfInfoOperation.ts new file mode 100644 index 0000000000..019968bcac --- /dev/null +++ b/frontend/src/core/hooks/tools/getPdfInfo/useGetPdfInfoOperation.ts @@ -0,0 +1,194 @@ +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import apiClient from '@app/services/apiClient'; +import { useFileContext } from '@app/contexts/file/fileHooks'; +import { ToolOperationHook } from '@app/hooks/tools/shared/useToolOperation'; +import type { StirlingFile } from '@app/types/fileContext'; +import { extractErrorMessage } from '@app/utils/toolErrorHandler'; +import { + PdfInfoReportEntry, + INFO_JSON_FILENAME, +} from '@app/types/getPdfInfo'; +import type { GetPdfInfoParameters } from '@app/hooks/tools/getPdfInfo/useGetPdfInfoParameters'; + +export interface GetPdfInfoOperationHook extends ToolOperationHook { + results: PdfInfoReportEntry[]; +} + +export const useGetPdfInfoOperation = (): GetPdfInfoOperationHook => { + const { t } = useTranslation(); + const { selectors } = useFileContext(); + const [isLoading, setIsLoading] = useState(false); + const [status, setStatus] = useState(''); + const [errorMessage, setErrorMessage] = useState(null); + const [files, setFiles] = useState([]); + const [downloadUrl, setDownloadUrl] = useState(null); + const [downloadFilename, setDownloadFilename] = useState(''); + const [results, setResults] = useState([]); + + const cancelRequested = useRef(false); + const previousUrl = useRef(null); + + const cleanupDownloadUrl = useCallback(() => { + if (previousUrl.current) { + URL.revokeObjectURL(previousUrl.current); + previousUrl.current = null; + } + }, []); + + const resetResults = useCallback(() => { + cancelRequested.current = false; + setResults([]); + setFiles([]); + cleanupDownloadUrl(); + setDownloadUrl(null); + setDownloadFilename(''); + setStatus(''); + setErrorMessage(null); + }, [cleanupDownloadUrl]); + + const clearError = useCallback(() => { + setErrorMessage(null); + }, []); + + const executeOperation = useCallback( + async (_params: GetPdfInfoParameters, selectedFiles: StirlingFile[]) => { + if (selectedFiles.length === 0) { + setErrorMessage(t('noFileSelected', 'No files selected')); + return; + } + + cancelRequested.current = false; + setIsLoading(true); + setStatus(t('getPdfInfo.processing', 'Extracting information...')); + setErrorMessage(null); + setResults([]); + setFiles([]); + cleanupDownloadUrl(); + setDownloadUrl(null); + setDownloadFilename(''); + + try { + const aggregated: PdfInfoReportEntry[] = []; + const generatedAt = Date.now(); + + for (const file of selectedFiles) { + if (cancelRequested.current) break; + + const formData = new FormData(); + formData.append('fileInput', file); + + try { + const response = await apiClient.post('/api/v1/security/get-info-on-pdf', formData, { + headers: { 'Content-Type': 'multipart/form-data' }, + }); + + const stub = selectors.getStirlingFileStub(file.fileId); + const entry: PdfInfoReportEntry = { + fileId: file.fileId, + fileName: file.name, + fileSize: file.size ?? null, + lastModified: file.lastModified ?? null, + thumbnailUrl: stub?.thumbnailUrl ?? null, + data: response.data ?? {}, + error: null, + summaryGeneratedAt: generatedAt, + }; + aggregated.push(entry); + } catch (error) { + const stub = selectors.getStirlingFileStub(file.fileId); + aggregated.push({ + fileId: file.fileId, + fileName: file.name, + fileSize: file.size ?? null, + lastModified: file.lastModified ?? null, + thumbnailUrl: stub?.thumbnailUrl ?? null, + data: {}, + error: extractErrorMessage(error), + summaryGeneratedAt: generatedAt, + }); + } + } + + if (!cancelRequested.current) { + setResults(aggregated); + if (aggregated.length > 0) { + // Build V1-compatible JSON: use backend payloads directly. + const payloads = aggregated + .filter((e) => !e.error) + .map((e) => e.data); + const content = payloads.length === 1 ? payloads[0] : payloads; + const json = JSON.stringify(content, null, 2); + const resultFile = new File([json], INFO_JSON_FILENAME, { type: 'application/json' }); + setFiles([resultFile]); + } + + const anyError = aggregated.some((item) => item.error); + if (anyError) { + setErrorMessage(t('getPdfInfo.error.partial', 'Some files could not be processed.')); + } + setStatus(t('getPdfInfo.status.complete', 'Extraction complete')); + } + } catch (e) { + console.error('[getPdfInfo] unexpected failure', e); + setErrorMessage(t('getPdfInfo.error.unexpected', 'Unexpected error during extraction.')); + } finally { + setIsLoading(false); + } + }, + [cleanupDownloadUrl, selectors, t] + ); + + const cancelOperation = useCallback(() => { + if (isLoading) { + cancelRequested.current = true; + setIsLoading(false); + setStatus(t('operationCancelled', 'Operation cancelled')); + } + }, [isLoading, t]); + + const undoOperation = useCallback(async () => { + resetResults(); + }, [resetResults]); + + useEffect(() => { + return () => { + cleanupDownloadUrl(); + }; + }, [cleanupDownloadUrl]); + + return useMemo( + () => ({ + files, + thumbnails: [], + isGeneratingThumbnails: false, + downloadUrl, + downloadFilename, + isLoading, + status, + errorMessage, + progress: null, + executeOperation, + resetResults, + clearError, + cancelOperation, + undoOperation, + results, + }), + [ + cancelOperation, + clearError, + downloadFilename, + downloadUrl, + errorMessage, + executeOperation, + files, + isLoading, + resetResults, + results, + status, + ] + ); +}; + + diff --git a/frontend/src/core/hooks/tools/getPdfInfo/useGetPdfInfoParameters.ts b/frontend/src/core/hooks/tools/getPdfInfo/useGetPdfInfoParameters.ts new file mode 100644 index 0000000000..488484809a --- /dev/null +++ b/frontend/src/core/hooks/tools/getPdfInfo/useGetPdfInfoParameters.ts @@ -0,0 +1,19 @@ +import { BaseParameters } from '@app/types/parameters'; +import { useBaseParameters, BaseParametersHook } from '@app/hooks/tools/shared/useBaseParameters'; + +export interface GetPdfInfoParameters extends BaseParameters { + // No parameters needed +} + +export const defaultParameters: GetPdfInfoParameters = {}; + +export type GetPdfInfoParametersHook = BaseParametersHook; + +export const useGetPdfInfoParameters = (): GetPdfInfoParametersHook => { + return useBaseParameters({ + defaultParameters, + endpointName: 'get-info-on-pdf', + }); +}; + + diff --git a/frontend/src/core/hooks/tools/removeAnnotations/useRemoveAnnotationsOperation.ts b/frontend/src/core/hooks/tools/removeAnnotations/useRemoveAnnotationsOperation.ts index 4078b2b073..e4b176c8d6 100644 --- a/frontend/src/core/hooks/tools/removeAnnotations/useRemoveAnnotationsOperation.ts +++ b/frontend/src/core/hooks/tools/removeAnnotations/useRemoveAnnotationsOperation.ts @@ -1,10 +1,10 @@ import { useTranslation } from 'react-i18next'; -import { useToolOperation, ToolType } from '@app/hooks/tools/shared/useToolOperation'; +import { useToolOperation, ToolType, CustomProcessorResult } from '@app/hooks/tools/shared/useToolOperation'; import { createStandardErrorHandler } from '@app/utils/toolErrorHandler'; import { RemoveAnnotationsParameters, defaultParameters } from '@app/hooks/tools/removeAnnotations/useRemoveAnnotationsParameters'; import { PDFDocument, PDFName, PDFRef, PDFDict } from 'pdf-lib'; // Client-side PDF processing using PDF-lib -const removeAnnotationsProcessor = async (_parameters: RemoveAnnotationsParameters, files: File[]): Promise => { +const removeAnnotationsProcessor = async (_parameters: RemoveAnnotationsParameters, files: File[]): Promise => { const processedFiles: File[] = []; for (const file of files) { @@ -75,7 +75,10 @@ const removeAnnotationsProcessor = async (_parameters: RemoveAnnotationsParamete } } - return processedFiles; + return { + files: processedFiles, + consumedAllInputs: false, + }; }; // Static configuration object diff --git a/frontend/src/core/hooks/tools/shared/useBaseTool.ts b/frontend/src/core/hooks/tools/shared/useBaseTool.ts index aad6556eaa..1b8db3bd1e 100644 --- a/frontend/src/core/hooks/tools/shared/useBaseTool.ts +++ b/frontend/src/core/hooks/tools/shared/useBaseTool.ts @@ -47,6 +47,10 @@ export function useBaseTool(''); + // Tool-specific hooks const params = useParams(); const operation = useOperation(); @@ -54,19 +58,45 @@ export function useBaseTool= minFiles; + const hasResults = operation.files.length > 0 || operation.downloadUrl !== null; + const settingsCollapsed = !hasFiles || hasResults; + // Reset results when parameters change useEffect(() => { operation.resetResults(); onPreviewFile?.(null); }, [params.parameters]); - // Reset results when selected files change + // When operation completes, flag the next selection change to skip reset + // (consumeFiles auto-selects outputs immediately after processing) useEffect(() => { - if (selectedFiles.length > 0) { - operation.resetResults(); - onPreviewFile?.(null); + if (hasResults) { + skipNextSelectionResetRef.current = true; } - }, [selectedFiles.length]); + }, [hasResults]); + + // Reset results when user manually changes file selection + useEffect(() => { + if (selectedFiles.length === 0) return; + + const currentSelection = selectedFiles.map(f => f.fileId).sort().join(','); + + if (currentSelection === previousSelectionRef.current) return; // No change + + // Skip reset if this is the auto-selection after operation completed + if (skipNextSelectionResetRef.current) { + skipNextSelectionResetRef.current = false; + previousSelectionRef.current = currentSelection; + return; + } + + // User manually selected different files - reset results + previousSelectionRef.current = currentSelection; + operation.resetResults(); + onPreviewFile?.(null); + }, [selectedFiles]); // Reset parameters when transitioning from 0 files to at least 1 file useEffect(() => { @@ -101,6 +131,7 @@ export function useBaseTool { + skipNextSelectionResetRef.current = false; operation.resetResults(); onPreviewFile?.(null); }, [operation, onPreviewFile]); @@ -110,11 +141,6 @@ export function useBaseTool= minFiles; - const hasResults = operation.files.length > 0 || operation.downloadUrl !== null; - const settingsCollapsed = !hasFiles || hasResults; - return { // File management selectedFiles, diff --git a/frontend/src/core/hooks/tools/shared/useToolApiCalls.ts b/frontend/src/core/hooks/tools/shared/useToolApiCalls.ts index a8e6a88a02..1ab8925f14 100644 --- a/frontend/src/core/hooks/tools/shared/useToolApiCalls.ts +++ b/frontend/src/core/hooks/tools/shared/useToolApiCalls.ts @@ -4,6 +4,7 @@ import apiClient from '@app/services/apiClient'; // Our configured instance import { processResponse, ResponseHandler } from '@app/utils/toolResponseProcessor'; import { isEmptyOutput } from '@app/services/errorUtils'; import type { ProcessingProgress } from '@app/hooks/tools/shared/useToolState'; +import type { StirlingFile, FileId } from '@app/types/fileContext'; export interface ApiCallsConfig { endpoint: string | ((params: TParams) => string); @@ -18,14 +19,14 @@ export const useToolApiCalls = () => { const processFiles = useCallback(async ( params: TParams, - validFiles: File[], + validFiles: StirlingFile[], config: ApiCallsConfig, onProgress: (progress: ProcessingProgress) => void, onStatus: (status: string) => void, - markFileError?: (fileId: string) => void, - ): Promise<{ outputFiles: File[]; successSourceIds: string[] }> => { + markFileError?: (fileId: FileId) => void, + ): Promise<{ outputFiles: File[]; successSourceIds: FileId[] }> => { const processedFiles: File[] = []; - const successSourceIds: string[] = []; + const successSourceIds: FileId[] = []; const failedFiles: string[] = []; const total = validFiles.length; @@ -35,7 +36,7 @@ export const useToolApiCalls = () => { for (let i = 0; i < validFiles.length; i++) { const file = validFiles[i]; - console.debug('[processFiles] Start', { index: i, total, name: file.name, fileId: (file as any).fileId }); + console.debug('[processFiles] Start', { index: i, total, name: file.name, fileId: file.fileId }); onProgress({ current: i + 1, total, currentFileName: file.name }); onStatus(`Processing ${file.name} (${i + 1}/${total})`); @@ -47,7 +48,7 @@ export const useToolApiCalls = () => { responseType: 'blob', cancelToken: cancelTokenRef.current?.token, }); - console.debug('[processFiles] Response OK', { name: file.name, status: (response as any)?.status }); + console.debug('[processFiles] Response OK', { name: file.name, status: response.status }); // Forward to shared response processor (uses tool-specific responseHandler if provided) const responseFiles = await processResponse( @@ -63,7 +64,7 @@ export const useToolApiCalls = () => { console.warn('[processFiles] Empty output treated as failure', { name: file.name }); failedFiles.push(file.name); try { - (markFileError as any)?.((file as any).fileId); + markFileError?.(file.fileId); } catch (e) { console.debug('markFileError', e); } @@ -71,7 +72,7 @@ export const useToolApiCalls = () => { } processedFiles.push(...responseFiles); // record source id as successful - successSourceIds.push((file as any).fileId); + successSourceIds.push(file.fileId); console.debug('[processFiles] Success', { name: file.name, produced: responseFiles.length }); } catch (error) { @@ -82,7 +83,7 @@ export const useToolApiCalls = () => { failedFiles.push(file.name); // mark errored file so UI can highlight try { - (markFileError as any)?.((file as any).fileId); + markFileError?.(file.fileId); } catch (e) { console.debug('markFileError', e); } diff --git a/frontend/src/core/hooks/tools/shared/useToolOperation.ts b/frontend/src/core/hooks/tools/shared/useToolOperation.ts index e1dccd1e70..4804032d0d 100644 --- a/frontend/src/core/hooks/tools/shared/useToolOperation.ts +++ b/frontend/src/core/hooks/tools/shared/useToolOperation.ts @@ -8,6 +8,7 @@ import { useToolResources } from '@app/hooks/tools/shared/useToolResources'; import { extractErrorMessage } from '@app/utils/toolErrorHandler'; import { StirlingFile, extractFiles, FileId, StirlingFileStub, createStirlingFile } from '@app/types/fileContext'; import { FILE_EVENTS } from '@app/services/errorUtils'; +import { getFilenameWithoutExtension } from '@app/utils/fileUtils'; import { ResponseHandler } from '@app/utils/toolResponseProcessor'; import { createChildStub, generateProcessedFileMetadata } from '@app/contexts/file/fileActions'; import { ToolOperation } from '@app/types/file'; @@ -23,6 +24,20 @@ export enum ToolType { custom, } +/** + * Result from custom processor with optional metadata about input consumption. + */ +export interface CustomProcessorResult { + /** Processed output files */ + files: File[]; + /** + * When true, marks all input files as successfully consumed regardless of output count. + * Use when operation combines N inputs into fewer outputs (e.g., 3 images → 1 PDF). + * When false/undefined, uses filename-based mapping to determine which inputs succeeded. + */ + consumedAllInputs?: boolean; +} + /** * Configuration for tool operations defining processing behavior and API integration. * @@ -98,8 +113,12 @@ export interface CustomToolOperationConfig extends BaseToolOperationCon * Custom processing logic that completely bypasses standard file processing. * This tool handles all API calls, response processing, and file creation. * Use for tools with complex routing logic or non-standard processing requirements. + * + * Returns CustomProcessorResult with: + * - files: Processed output files + * - consumedAllInputs: true if operation combines N inputs → fewer outputs */ - customProcessor: (params: TParams, files: File[]) => Promise; + customProcessor: (params: TParams, files: File[]) => Promise; } export type ToolOperationConfig = SingleFileToolOperationConfig | MultiFileToolOperationConfig | CustomToolOperationConfig; @@ -172,17 +191,17 @@ export const useToolOperation = ( } // Handle zero-byte inputs explicitly: mark as error and continue with others - const zeroByteFiles = selectedFiles.filter(file => (file as any)?.size === 0); + const zeroByteFiles = selectedFiles.filter(file => file.size === 0); if (zeroByteFiles.length > 0) { try { for (const f of zeroByteFiles) { - (fileActions.markFileError as any)((f as any).fileId); + fileActions.markFileError(f.fileId); } } catch (e) { console.log('markFileError', e); } } - const validFiles = selectedFiles.filter(file => (file as any)?.size > 0); + const validFiles: StirlingFile[] = selectedFiles.filter(file => file.size > 0); if (validFiles.length === 0) { actions.setError(t('noValidFiles', 'No valid files to process')); return; @@ -215,7 +234,7 @@ export const useToolOperation = ( try { let processedFiles: File[]; - let successSourceIds: string[] = []; + let successSourceIds: FileId[] = []; // Use original files directly (no PDF metadata injection - history stored in IndexedDB) const filesForAPI = extractFiles(validFiles); @@ -233,14 +252,14 @@ export const useToolOperation = ( console.debug('[useToolOperation] Multi-file start', { count: filesForAPI.length }); const result = await processFiles( params, - filesForAPI, + validFiles, apiCallsConfig, actions.setProgress, actions.setStatus, - fileActions.markFileError as any + fileActions.markFileError ); processedFiles = result.outputFiles; - successSourceIds = result.successSourceIds as any; + successSourceIds = result.successSourceIds; console.debug('[useToolOperation] Multi-file results', { outputFiles: processedFiles.length, successSources: result.successSourceIds.length }); break; } @@ -268,30 +287,40 @@ export const useToolOperation = ( processedFiles = await extractZipFiles(response.data); } // Assume all inputs succeeded together unless server provided an error earlier - successSourceIds = validFiles.map(f => (f as any).fileId) as any; + successSourceIds = validFiles.map(f => f.fileId); break; } case ToolType.custom: { actions.setStatus('Processing files...'); - processedFiles = await config.customProcessor(params, filesForAPI); - // Try to map outputs back to inputs by filename (before extension) - const inputBaseNames = new Map(); - for (const f of validFiles) { - const base = (f.name || '').replace(/\.[^.]+$/, '').toLowerCase(); - inputBaseNames.set(base, (f as any).fileId); - } - const mappedSuccess: string[] = []; - for (const out of processedFiles) { - const base = (out.name || '').replace(/\.[^.]+$/, '').toLowerCase(); - const id = inputBaseNames.get(base); - if (id) mappedSuccess.push(id); - } - // Fallback to naive alignment if names don't match - if (mappedSuccess.length === 0) { - successSourceIds = validFiles.slice(0, processedFiles.length).map(f => (f as any).fileId) as any; + const result = await config.customProcessor(params, filesForAPI); + + processedFiles = result.files; + const consumedAllInputs = result.consumedAllInputs || false; + + // If consumedAllInputs flag is set, mark all inputs as successful + // (used for operations that combine N inputs into fewer outputs) + if (consumedAllInputs) { + successSourceIds = validFiles.map(f => f.fileId); } else { - successSourceIds = mappedSuccess as any; + // Try to map outputs back to inputs by filename (before extension) + const inputBaseNames = new Map(); + for (const f of validFiles) { + const base = getFilenameWithoutExtension(f.name || ''); + inputBaseNames.set(base, f.fileId); + } + const mappedSuccess: FileId[] = []; + for (const out of processedFiles) { + const base = getFilenameWithoutExtension(out.name || ''); + const id = inputBaseNames.get(base); + if (id) mappedSuccess.push(id); + } + // Fallback to naive alignment if names don't match + if (mappedSuccess.length === 0) { + successSourceIds = validFiles.slice(0, processedFiles.length).map(f => f.fileId); + } else { + successSourceIds = mappedSuccess; + } } break; } @@ -299,16 +328,16 @@ export const useToolOperation = ( // Normalize error flags across tool types: mark failures, clear successes try { - const allInputIds = validFiles.map(f => (f as any).fileId) as unknown as string[]; - const okSet = new Set((successSourceIds as unknown as string[]) || []); + const allInputIds = validFiles.map(f => f.fileId); + const okSet = new Set(successSourceIds); // Clear errors on successes for (const okId of okSet) { - try { (fileActions.clearFileError as any)(okId); } catch (_e) { void _e; } + try { fileActions.clearFileError(okId); } catch (_e) { void _e; } } // Mark errors on inputs that didn't succeed for (const id of allInputIds) { if (!okSet.has(id)) { - try { (fileActions.markFileError as any)(id); } catch (_e) { void _e; } + try { fileActions.markFileError(id); } catch (_e) { void _e; } } } } catch (_e) { void _e; } @@ -316,12 +345,12 @@ export const useToolOperation = ( if (externalErrorFileIds.length > 0) { // If backend told us which sources failed, prefer that mapping successSourceIds = validFiles - .map(f => (f as any).fileId) - .filter(id => !externalErrorFileIds.includes(id)) as any; + .map(f => f.fileId) + .filter(id => !externalErrorFileIds.includes(id)); // Also mark failed IDs immediately try { for (const badId of externalErrorFileIds) { - (fileActions.markFileError as any)(badId); + fileActions.markFileError(badId as FileId); } } catch (_e) { void _e; } } @@ -370,7 +399,7 @@ export const useToolOperation = ( ); // Always create child stubs linking back to the successful source inputs const successInputStubs = successSourceIds - .map((id) => selectors.getStirlingFileStub(id as any)) + .map((id) => selectors.getStirlingFileStub(id)) .filter(Boolean) as StirlingFileStub[]; if (successInputStubs.length !== processedFiles.length) { @@ -396,7 +425,7 @@ export const useToolOperation = ( return createStirlingFile(file, childStub.id); }); // Build consumption arrays aligned to the successful source IDs - const toConsumeInputIds = successSourceIds.filter((id: string) => inputFileIds.includes(id as any)) as unknown as FileId[]; + const toConsumeInputIds = successSourceIds.filter((id) => inputFileIds.includes(id)); // Outputs and stubs are already ordered by success sequence console.debug('[useToolOperation] Consuming files', { inputCount: inputFileIds.length, toConsume: toConsumeInputIds.length }); const outputFileIds = await consumeFiles(toConsumeInputIds, outputStirlingFiles, outputStirlingFileStubs); @@ -413,25 +442,27 @@ export const useToolOperation = ( } catch (error: any) { // Centralized 422 handler: mark provided IDs in errorFileIds try { - const status = (error?.response?.status as number | undefined); - if (status === 422) { + const status = error?.response?.status; + if (typeof status === 'number' && status === 422) { const payload = error?.response?.data; - let parsed: any = payload; + let parsed: unknown = payload; if (typeof payload === 'string') { try { parsed = JSON.parse(payload); } catch { parsed = payload; } - } else if (payload && typeof (payload as any).text === 'function') { + } else if (payload && typeof (payload as Blob).text === 'function') { // Blob or Response-like object from axios when responseType='blob' const text = await (payload as Blob).text(); try { parsed = JSON.parse(text); } catch { parsed = text; } } - let ids: string[] | undefined = Array.isArray(parsed?.errorFileIds) ? parsed.errorFileIds : undefined; + let ids: string[] | undefined = Array.isArray((parsed as { errorFileIds?: unknown })?.errorFileIds) + ? (parsed as { errorFileIds: string[] }).errorFileIds + : undefined; if (!ids && typeof parsed === 'string') { const match = parsed.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g); if (match && match.length > 0) ids = Array.from(new Set(match)); } if (ids && ids.length > 0) { for (const badId of ids) { - try { (fileActions.markFileError as any)(badId); } catch (_e) { void _e; } + try { fileActions.markFileError(badId as FileId); } catch (_e) { void _e; } } actions.setStatus('Process failed due to invalid/corrupted file(s)'); // Avoid duplicating toast messaging here diff --git a/frontend/src/core/hooks/tools/sign/useSavedSignatures.ts b/frontend/src/core/hooks/tools/sign/useSavedSignatures.ts index 655038a3f2..b878dcd0de 100644 --- a/frontend/src/core/hooks/tools/sign/useSavedSignatures.ts +++ b/frontend/src/core/hooks/tools/sign/useSavedSignatures.ts @@ -1,7 +1,9 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { signatureStorageService, type StorageType } from '@app/services/signatureStorageService'; +import { useAppConfig } from '@app/contexts/AppConfigContext'; -export const MAX_SAVED_SIGNATURES = 10; +export const MAX_SAVED_SIGNATURES_BACKEND = 20; // Backend limit per user +export const MAX_SAVED_SIGNATURES_LOCALSTORAGE = 10; // LocalStorage limit export type SavedSignatureType = 'canvas' | 'image' | 'text'; export type SignatureScope = 'personal' | 'shared' | 'localStorage'; @@ -49,6 +51,8 @@ export const useSavedSignatures = () => { const [savedSignatures, setSavedSignatures] = useState([]); const [storageType, setStorageType] = useState(null); const [isLoading, setIsLoading] = useState(true); + const { config } = useAppConfig(); + const isAdmin = config?.isAdmin ?? false; // Load signatures and detect storage type on mount useEffect(() => { @@ -97,7 +101,9 @@ export const useSavedSignatures = () => { return () => window.removeEventListener('storage', syncFromStorage); }, [storageType]); - const isAtCapacity = savedSignatures.length >= MAX_SAVED_SIGNATURES; + // Different limits for backend vs localStorage + const maxLimit = storageType === 'backend' ? MAX_SAVED_SIGNATURES_BACKEND : MAX_SAVED_SIGNATURES_LOCALSTORAGE; + const isAtCapacity = savedSignatures.length >= maxLimit; const addSignature = useCallback( async (payload: SavedSignaturePayload, label?: string, scope?: SignatureScope): Promise => { @@ -108,7 +114,7 @@ export const useSavedSignatures = () => { return { success: false, reason: 'invalid' }; } - if (savedSignatures.length >= MAX_SAVED_SIGNATURES) { + if (isAtCapacity) { return { success: false, reason: 'limit' }; } @@ -146,17 +152,24 @@ export const useSavedSignatures = () => { const updateSignatureLabel = useCallback(async (id: string, nextLabel: string) => { try { await signatureStorageService.updateSignatureLabel(id, nextLabel); - setSavedSignatures(prev => - prev.map(entry => - entry.id === id - ? { ...entry, label: nextLabel.trim() || entry.label || 'Signature', updatedAt: Date.now() } - : entry - ) - ); + // Reload signatures to get updated data from backend + if (storageType === 'backend') { + const signatures = await signatureStorageService.loadSignatures(); + setSavedSignatures(signatures); + } else { + // For localStorage, update in place + setSavedSignatures(prev => + prev.map(entry => + entry.id === id + ? { ...entry, label: nextLabel.trim() || entry.label || 'Signature', updatedAt: Date.now() } + : entry + ) + ); + } } catch (error) { console.error('[useSavedSignatures] Failed to update signature label:', error); } - }, []); + }, [storageType]); const replaceSignature = useCallback( async (id: string, payload: SavedSignaturePayload) => { @@ -201,6 +214,7 @@ export const useSavedSignatures = () => { return { savedSignatures, isAtCapacity, + maxLimit, addSignature, removeSignature, updateSignatureLabel, @@ -209,6 +223,7 @@ export const useSavedSignatures = () => { byTypeCounts, storageType, isLoading, + isAdmin, }; }; diff --git a/frontend/src/core/hooks/useServerExperience.ts b/frontend/src/core/hooks/useServerExperience.ts index 8dd60069a7..28f62c1c57 100644 --- a/frontend/src/core/hooks/useServerExperience.ts +++ b/frontend/src/core/hooks/useServerExperience.ts @@ -65,7 +65,7 @@ export function useServerExperience(): ServerExperienceValue { const loginEnabled = config?.enableLogin !== false; const configIsAdmin = Boolean(config?.isAdmin); const effectiveIsAdmin = configIsAdmin || (!loginEnabled && selfReportedAdmin); - const hasPaidLicense = config?.license === 'PRO' || config?.license === 'ENTERPRISE'; + const hasPaidLicense = config?.license === 'SERVER' || config?.license === 'PRO' || config?.license === 'ENTERPRISE'; const setSelfReportedAdmin = useCallback((value: boolean) => { setSelfReportedAdminState(value); diff --git a/frontend/src/core/i18n.ts b/frontend/src/core/i18n.ts index e3f3f7bd2d..badc729497 100644 --- a/frontend/src/core/i18n.ts +++ b/frontend/src/core/i18n.ts @@ -105,33 +105,94 @@ i18n.on('languageChanged', (lng) => { document.documentElement.lang = lng; }); +export function normalizeLanguageCode(languageCode: string): string { + // Replace underscores with hyphens to align with i18next/translation file naming + const hyphenated = languageCode.replace(/_/g, '-'); + const [base, ...rest] = hyphenated.split('-'); + + if (rest.length === 0) { + return base.toLowerCase(); + } + + const normalizedParts = rest.map(part => (part.length <= 3 ? part.toUpperCase() : part)); + return [base.toLowerCase(), ...normalizedParts].join('-'); +} + +/** + * Convert language codes to underscore format (e.g., en-GB → en_GB) + * Used for backend API communication which expects underscore format + */ +export function toUnderscoreFormat(languageCode: string): string { + return languageCode.replace(/-/g, '_'); +} + +/** + * Convert array of language codes to underscore format + */ +export function toUnderscoreLanguages(languages: string[]): string[] { + return languages.map(toUnderscoreFormat); +} + /** * Updates the supported languages list dynamically based on config * If configLanguages is null/empty, all languages remain available - * Otherwise, only specified languages plus 'en-GB' fallback are enabled + * Otherwise, only the specified languages are enabled with the first valid + * option (preferring en-GB when present) used as the fallback language. + * + * @param configLanguages - Optional array of language codes from server config (ui.languages) + * @param defaultLocale - Optional default language for new users (system.defaultLocale) */ -export function updateSupportedLanguages(configLanguages?: string[] | null) { +export function updateSupportedLanguages(configLanguages?: string[] | null, defaultLocale?: string | null) { + // Normalize and validate default locale if provided + const normalizedDefault = defaultLocale ? normalizeLanguageCode(defaultLocale) : null; + const validDefault = normalizedDefault && normalizedDefault in supportedLanguages ? normalizedDefault : null; + if (!configLanguages || configLanguages.length === 0) { // No filter specified - keep all languages + // But still apply default locale if provided and user has no preference + if (validDefault) { + applyDefaultLocale(validDefault); + } return; } - // Ensure fallback language is always included - const languagesToSupport = new Set(['en-GB', ...configLanguages]); + const validLanguages = configLanguages + .map(normalizeLanguageCode) + .filter(lang => lang in supportedLanguages); - // Filter to only valid language codes that exist in our translations - const validLanguages = Array.from(languagesToSupport).filter( - lang => lang in supportedLanguages - ); + // If no valid languages were provided, keep existing configuration + if (validLanguages.length === 0) { + return; + } - if (validLanguages.length > 0) { - i18n.options.supportedLngs = validLanguages; + // Determine fallback: prefer validDefault if in the list, then en-GB, then first valid language + const fallback = validDefault && validLanguages.includes(validDefault) + ? validDefault + : validLanguages.includes('en-GB') + ? 'en-GB' + : validLanguages[0]; - // If current language is not in the new supported list, switch to fallback - const currentLang = i18n.language; - if (currentLang && !validLanguages.includes(currentLang)) { - i18n.changeLanguage('en-GB'); - } + i18n.options.supportedLngs = validLanguages; + i18n.options.fallbackLng = fallback; + + // If current language is not in the new supported list, switch to fallback + const currentLang = normalizeLanguageCode(i18n.language || ''); + if (currentLang && !validLanguages.includes(currentLang)) { + i18n.changeLanguage(fallback); + } else if (validDefault && !localStorage.getItem('i18nextLng')) { + // User has no saved preference - apply server default + i18n.changeLanguage(validDefault); + } +} + +/** + * Apply server default locale when user has no saved language preference + * This respects the priority: localStorage > defaultLocale > browser detection > fallback + */ +function applyDefaultLocale(defaultLocale: string) { + // Only apply if user has no saved preference + if (!localStorage.getItem('i18nextLng')) { + i18n.changeLanguage(defaultLocale); } } diff --git a/frontend/src/core/pages/HomePage.tsx b/frontend/src/core/pages/HomePage.tsx index b629e86a5c..c22a9624d9 100644 --- a/frontend/src/core/pages/HomePage.tsx +++ b/frontend/src/core/pages/HomePage.tsx @@ -59,17 +59,21 @@ export default function HomePage() { const prevFileCountRef = useRef(activeFiles.length); // Auto-switch to viewer when going from 0 to 1 file + // Skip this if PDF Text Editor is active - it handles its own empty state useEffect(() => { const prevCount = prevFileCountRef.current; const currentCount = activeFiles.length; if (prevCount === 0 && currentCount === 1) { - actions.setWorkbench('viewer'); - setActiveFileIndex(0); + // PDF Text Editor handles its own empty state with a dropzone + if (selectedToolKey !== 'pdfTextEditor') { + actions.setWorkbench('viewer'); + setActiveFileIndex(0); + } } prevFileCountRef.current = currentCount; - }, [activeFiles.length, actions, setActiveFileIndex]); + }, [activeFiles.length, actions, setActiveFileIndex, selectedToolKey]); const brandAltText = t("home.mobile.brandAlt", "Stirling PDF logo"); const brandIconSrc = useLogoPath(); diff --git a/frontend/src/core/services/signatureStorageService.ts b/frontend/src/core/services/signatureStorageService.ts index 365a0cbc60..63dabe9094 100644 --- a/frontend/src/core/services/signatureStorageService.ts +++ b/frontend/src/core/services/signatureStorageService.ts @@ -14,7 +14,6 @@ interface SignatureStorageCapabilities { class SignatureStorageService { private capabilities: SignatureStorageCapabilities | null = null; private detectionPromise: Promise | null = null; - private blobUrls: Set = new Set(); /** * Detect if backend supports signature storage API @@ -83,9 +82,6 @@ class SignatureStorageService { * Load all signatures */ async loadSignatures(): Promise { - // Clean up old blob URLs before loading new ones - this.cleanup(); - const capabilities = await this.detectCapabilities(); if (capabilities.supportsBackend) { @@ -130,9 +126,7 @@ class SignatureStorageService { const capabilities = await this.detectCapabilities(); if (capabilities.supportsBackend) { - // Backend only stores images - labels not supported for backend signatures - console.log('[SignatureStorage] Label updates not supported for backend signatures'); - return; + await this._updateLabelInBackend(id, label); } else { this._updateLabelInLocalStorage(id, label); } @@ -144,7 +138,7 @@ class SignatureStorageService { const response = await apiClient.get('/api/v1/proprietary/signatures'); const signatures = response.data; - // Fetch image data for each signature and convert to blob URLs + // Fetch image data for each signature and convert to data URLs const signaturePromises = signatures.map(async (sig) => { if (sig.dataUrl && sig.dataUrl.startsWith('/api/v1/general/signatures/')) { try { @@ -153,14 +147,20 @@ class SignatureStorageService { responseType: 'arraybuffer', }); - // Convert to blob URL + // Convert to data URL (base64) for both display and use const blob = new Blob([imageResponse.data], { type: imageResponse.headers['content-type'] || 'image/png', }); - const blobUrl = URL.createObjectURL(blob); - this.blobUrls.add(blobUrl); - return { ...sig, dataUrl: blobUrl }; + const dataUrl = await new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onloadend = () => resolve(reader.result as string); + reader.onerror = reject; + reader.readAsDataURL(blob); + }); + + // Use data URL for everything - more reliable than blob URLs + return { ...sig, dataUrl }; } catch (error) { console.error(`[SignatureStorage] Failed to load image for ${sig.id}:`, error); return sig; // Return original if image fetch fails @@ -184,6 +184,10 @@ class SignatureStorageService { await apiClient.delete(`/api/v1/proprietary/signatures/${id}`); } + private async _updateLabelInBackend(id: string, label: string): Promise { + await apiClient.post(`/api/v1/proprietary/signatures/${id}/label`, { label }); + } + // LocalStorage methods private readonly STORAGE_KEY = 'stirling:saved-signatures:v1'; @@ -267,16 +271,6 @@ class SignatureStorageService { return { migrated, failed }; } - - /** - * Clean up blob URLs to prevent memory leaks - */ - cleanup(): void { - this.blobUrls.forEach(url => { - URL.revokeObjectURL(url); - }); - this.blobUrls.clear(); - } } export const signatureStorageService = new SignatureStorageService(); diff --git a/frontend/src/core/setupTests.ts b/frontend/src/core/setupTests.ts index 3ebb22fdab..0e5e9737a2 100644 --- a/frontend/src/core/setupTests.ts +++ b/frontend/src/core/setupTests.ts @@ -1,6 +1,40 @@ import '@testing-library/jest-dom'; import { vi } from 'vitest'; +// Mock localStorage for tests +class LocalStorageMock implements Storage { + private store: Record = {}; + + get length(): number { + return Object.keys(this.store).length; + } + + clear(): void { + this.store = {}; + } + + getItem(key: string): string | null { + return this.store[key] ?? null; + } + + key(index: number): string | null { + return Object.keys(this.store)[index] ?? null; + } + + removeItem(key: string): void { + delete this.store[key]; + } + + setItem(key: string, value: string): void { + this.store[key] = value; + } +} + +Object.defineProperty(window, 'localStorage', { + value: new LocalStorageMock(), + writable: true, +}); + // Mock i18next for tests vi.mock('react-i18next', () => ({ useTranslation: () => ({ diff --git a/frontend/src/core/styles/theme.css b/frontend/src/core/styles/theme.css index 30991cf30a..c43b330b00 100644 --- a/frontend/src/core/styles/theme.css +++ b/frontend/src/core/styles/theme.css @@ -256,6 +256,7 @@ --header-selected-bg: #1E88E5; /* light mode selected header matches dark */ --header-selected-fg: #FFFFFF; --file-card-bg: #FFFFFF; /* file card background (light/dark paired) */ + --accordion-item-bg: #E8EAED; /* accordion item background - more distinguishable */ /* shadows */ --drop-shadow-color: rgba(0, 0, 0, 0.08); @@ -519,6 +520,7 @@ --header-selected-fg: #FFFFFF; /* file card background (dark) */ --file-card-bg: #1F2329; + --accordion-item-bg: #373D45; /* accordion item background - more distinguishable */ /* shadows */ --drop-shadow-color: rgba(255, 255, 255, 0.08); @@ -615,6 +617,41 @@ border-color: var(--landing-drop-inner-paper-border) !important; } +/* Plan section card borders - only override in dark mode */ +[data-mantine-color-scheme="dark"] .plan-card { + --paper-border-color: rgb(158, 158, 158) !important; +} + +[data-mantine-color-scheme="dark"] .plan-card [data-size="sm"] { + color: rgb(221, 221, 221) !important; +} + +/* Current plan badge - use light mode green in dark mode */ +[data-mantine-color-scheme="dark"] .current-plan-badge { + background-color: var(--color-green-300) !important; +} + +/* Plan section button colors */ +.plan-button:not(:disabled):not([data-disabled]) { + background-color: #0A8BFF !important; +} + +[data-mantine-color-scheme="dark"] .plan-button:not(:disabled):not([data-disabled]) { + background-color: #1C598E !important; +} + +/* Lighter grey for disabled plan buttons */ +.plan-button:disabled, +.plan-button[data-disabled] { + background-color: #7e7e7e !important; + color: white; +} + +[data-mantine-color-scheme="dark"] .plan-button:disabled, +[data-mantine-color-scheme="dark"] .plan-button[data-disabled] { + background-color: #6b7280 !important; +} + /* Smooth transitions for theme switching */ * { transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease; diff --git a/frontend/src/core/tools/Convert.tsx b/frontend/src/core/tools/Convert.tsx index 51353cad46..82852f36d1 100644 --- a/frontend/src/core/tools/Convert.tsx +++ b/frontend/src/core/tools/Convert.tsx @@ -23,6 +23,10 @@ const Convert = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => { const { enabled: endpointEnabled, loading: endpointLoading } = useEndpointEnabled(convertParams.getEndpointName()); + // Prevent reset immediately after operation completes (when consumeFiles auto-selects outputs) + const skipNextSelectionResetRef = useRef(false); + const previousSelectionRef = useRef(''); + const scrollToBottom = () => { if (scrollContainerRef.current) { scrollContainerRef.current.scrollTo({ @@ -33,24 +37,49 @@ const Convert = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => { }; const hasFiles = selectedFiles.length > 0; - const hasResults = convertOperation.downloadUrl !== null; + const hasResults = convertOperation.files.length > 0 || convertOperation.downloadUrl !== null; const settingsCollapsed = hasResults; + // When operation completes, flag the next selection change to skip reset useEffect(() => { + if (hasResults) { + skipNextSelectionResetRef.current = true; + } + }, [hasResults]); + + // Reset results when user manually changes file selection + useEffect(() => { + const currentSelection = selectedFiles.map(f => f.fileId).sort().join(','); + + if (currentSelection === previousSelectionRef.current) return; // No change + + // Skip reset if this is the auto-selection after operation completed + // Don't analyze file types - would change parameters and trigger another reset + if (skipNextSelectionResetRef.current) { + skipNextSelectionResetRef.current = false; + previousSelectionRef.current = currentSelection; + return; + } + + // User manually selected different files if (selectedFiles.length > 0) { + previousSelectionRef.current = currentSelection; convertParams.analyzeFileTypes(selectedFiles); + if (hasResults) { + convertOperation.resetResults(); + onPreviewFile?.(null); + } } else { - // Only reset when there are no active files at all - // If there are active files but no selected files, keep current format (user filtered by format) + previousSelectionRef.current = ''; if (activeFiles.length === 0) { convertParams.resetParameters(); } } - }, [selectedFiles, activeFiles, convertParams.analyzeFileTypes, convertParams.resetParameters]); + }, [selectedFiles]); useEffect(() => { - // Only clear results if we're not currently processing and parameters changed - if (!convertOperation.isLoading) { + // Reset when user changes conversion parameters (but not during operation) + if (!convertOperation.isLoading && !skipNextSelectionResetRef.current) { convertOperation.resetResults(); onPreviewFile?.(null); } @@ -87,6 +116,7 @@ const Convert = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => { }; const handleSettingsReset = () => { + skipNextSelectionResetRef.current = false; convertOperation.resetResults(); onPreviewFile?.(null); }; diff --git a/frontend/src/core/tools/GetPdfInfo.tsx b/frontend/src/core/tools/GetPdfInfo.tsx new file mode 100644 index 0000000000..aa35fe16bf --- /dev/null +++ b/frontend/src/core/tools/GetPdfInfo.tsx @@ -0,0 +1,188 @@ +import { useEffect, useMemo, useRef } from 'react'; +import { useTranslation } from 'react-i18next'; +import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf'; +import LinkIcon from '@mui/icons-material/Link'; +import { Stack, Group, Divider, Text, UnstyledButton } from '@mantine/core'; +import { createToolFlow } from '@app/components/tools/shared/createToolFlow'; +import { useBaseTool } from '@app/hooks/tools/shared/useBaseTool'; +import { BaseToolProps, ToolComponent } from '@app/types/tool'; +import { useGetPdfInfoParameters, defaultParameters } from '@app/hooks/tools/getPdfInfo/useGetPdfInfoParameters'; +import GetPdfInfoResults from '@app/components/tools/getPdfInfo/GetPdfInfoResults'; +import { useGetPdfInfoOperation, GetPdfInfoOperationHook } from '@app/hooks/tools/getPdfInfo/useGetPdfInfoOperation'; +import GetPdfInfoReportView from '@app/components/tools/getPdfInfo/GetPdfInfoReportView'; +import { useToolWorkflow } from '@app/contexts/ToolWorkflowContext'; +import { useNavigationActions, useNavigationState } from '@app/contexts/NavigationContext'; +import type { PdfInfoReportData } from '@app/types/getPdfInfo'; + +const CHAPTERS = [ + { id: 'summary', labelKey: 'getPdfInfo.summary.title', fallback: 'PDF Summary' }, + { id: 'metadata', labelKey: 'getPdfInfo.sections.metadata', fallback: 'Metadata' }, + { id: 'formFields', labelKey: 'getPdfInfo.sections.formFields', fallback: 'Form Fields' }, + { id: 'basicInfo', labelKey: 'getPdfInfo.sections.basicInfo', fallback: 'Basic Info' }, + { id: 'documentInfo', labelKey: 'getPdfInfo.sections.documentInfo', fallback: 'Document Info' }, + { id: 'compliance', labelKey: 'getPdfInfo.sections.compliance', fallback: 'Compliance' }, + { id: 'encryption', labelKey: 'getPdfInfo.sections.encryption', fallback: 'Encryption' }, + { id: 'permissions', labelKey: 'getPdfInfo.sections.permissions', fallback: 'Permissions' }, + { id: 'toc', labelKey: 'getPdfInfo.sections.tableOfContents', fallback: 'Table of Contents' }, + { id: 'other', labelKey: 'getPdfInfo.sections.other', fallback: 'Other' }, + { id: 'perPage', labelKey: 'getPdfInfo.sections.perPageInfo', fallback: 'Per Page Info' }, +]; + +const GetPdfInfo = (props: BaseToolProps) => { + const { t } = useTranslation(); + const { actions: navigationActions } = useNavigationActions(); + const navigationState = useNavigationState(); + const { + registerCustomWorkbenchView, + unregisterCustomWorkbenchView, + setCustomWorkbenchViewData, + clearCustomWorkbenchViewData, + } = useToolWorkflow(); + + const REPORT_VIEW_ID = 'getPdfInfoReport'; + const REPORT_WORKBENCH_ID = 'custom:getPdfInfoReport' as const; + const reportIcon = useMemo(() => , []); + + const base = useBaseTool( + 'getPdfInfo', + useGetPdfInfoParameters, + useGetPdfInfoOperation, + props + ); + + const operation = base.operation as GetPdfInfoOperationHook; + const hasResults = operation.results.length > 0; + const showResultsStep = hasResults || base.operation.isLoading || !!base.operation.errorMessage; + + useEffect(() => { + registerCustomWorkbenchView({ + id: REPORT_VIEW_ID, + workbenchId: REPORT_WORKBENCH_ID, + label: t('getPdfInfo.report.shortTitle', 'PDF Information'), + icon: reportIcon, + component: GetPdfInfoReportView, + }); + + return () => { + clearCustomWorkbenchViewData(REPORT_VIEW_ID); + unregisterCustomWorkbenchView(REPORT_VIEW_ID); + }; + }, [ + clearCustomWorkbenchViewData, + registerCustomWorkbenchView, + reportIcon, + t, + unregisterCustomWorkbenchView, + ]); + + const reportData = useMemo(() => { + if (operation.results.length === 0) return null; + const generatedAt = operation.results[0].summaryGeneratedAt ?? Date.now(); + return { + generatedAt, + entries: operation.results, + }; + }, [operation.results]); + + const lastReportGeneratedAtRef = useRef(null); + useEffect(() => { + if (reportData) { + setCustomWorkbenchViewData(REPORT_VIEW_ID, reportData); + const generatedAt = reportData.generatedAt ?? null; + const isNewReport = generatedAt && generatedAt !== lastReportGeneratedAtRef.current; + if (isNewReport) { + lastReportGeneratedAtRef.current = generatedAt; + if (navigationState.selectedTool === 'getPdfInfo' && navigationState.workbench !== REPORT_WORKBENCH_ID) { + navigationActions.setWorkbench(REPORT_WORKBENCH_ID); + } + } + } else { + clearCustomWorkbenchViewData(REPORT_VIEW_ID); + lastReportGeneratedAtRef.current = null; + } + }, [ + clearCustomWorkbenchViewData, + navigationActions, + navigationState.selectedTool, + navigationState.workbench, + reportData, + setCustomWorkbenchViewData, + ]); + + return createToolFlow({ + files: { + selectedFiles: base.selectedFiles, + isCollapsed: hasResults, + }, + steps: [ + { + title: t('getPdfInfo.indexTitle', 'Index'), + isVisible: Boolean(reportData), + isCollapsed: false, + content: ( + + {CHAPTERS.map((c, idx) => ( + + { + if (!reportData) return; + setCustomWorkbenchViewData(REPORT_VIEW_ID, { ...reportData, scrollTo: c.id }); + if (navigationState.workbench !== REPORT_WORKBENCH_ID) { + navigationActions.setWorkbench(REPORT_WORKBENCH_ID); + } + }} + style={{ width: '100%', textAlign: 'left', padding: '8px 4px' }} + > + + + + {t(c.labelKey, c.fallback)} + + + + {idx < CHAPTERS.length - 1 && } + + ))} + + ), + }, + { + title: t('getPdfInfo.results', 'Results'), + isVisible: showResultsStep, + isCollapsed: false, + content: ( + + ), + }, + ], + executeButton: { + text: t('getPdfInfo.submit', 'Generate'), + loadingText: t('loading', 'Loading...'), + onClick: base.handleExecute, + disabled: + !base.params.validateParameters() || + !base.hasFiles || + base.operation.isLoading || + !base.endpointEnabled, + isVisible: true, + }, + review: { + isVisible: false, + operation: base.operation, + title: t('getPdfInfo.results', 'Results'), + onUndo: base.handleUndo, + }, + }); +}; + +const GetPdfInfoTool = GetPdfInfo as ToolComponent; +GetPdfInfoTool.tool = () => useGetPdfInfoOperation; +GetPdfInfoTool.getDefaultParameters = () => ({ ...defaultParameters }); + +export default GetPdfInfoTool; + + diff --git a/frontend/src/proprietary/tools/pdfTextEditor/PdfTextEditor.tsx b/frontend/src/core/tools/pdfTextEditor/PdfTextEditor.tsx similarity index 82% rename from frontend/src/proprietary/tools/pdfTextEditor/PdfTextEditor.tsx rename to frontend/src/core/tools/pdfTextEditor/PdfTextEditor.tsx index 6337c23fd0..533dc644b3 100644 --- a/frontend/src/proprietary/tools/pdfTextEditor/PdfTextEditor.tsx +++ b/frontend/src/core/tools/pdfTextEditor/PdfTextEditor.tsx @@ -3,14 +3,16 @@ import { useTranslation } from 'react-i18next'; import DescriptionIcon from '@mui/icons-material/DescriptionOutlined'; import { useToolWorkflow } from '@app/contexts/ToolWorkflowContext'; -import { useFileSelection } from '@app/contexts/FileContext'; +import { useFileSelection, useFileManagement, useFileContext } from '@app/contexts/FileContext'; import { useNavigationActions, useNavigationState } from '@app/contexts/NavigationContext'; +import { createStirlingFilesAndStubs } from '@app/services/fileStubHelpers'; import { BaseToolProps, ToolComponent } from '@app/types/tool'; +import { getDefaultWorkbench } from '@app/types/workbench'; import { CONVERSION_ENDPOINTS } from '@app/constants/convertConstants'; import apiClient from '@app/services/apiClient'; import { downloadBlob, downloadTextAsFile } from '@app/utils/downloadUtils'; import { getFilenameFromHeaders } from '@app/utils/fileResponseUtils'; -import { pdfWorkerManager } from '@core/services/pdfWorkerManager'; +import { pdfWorkerManager } from '@app/services/pdfWorkerManager'; import { Util } from 'pdfjs-dist/legacy/build/pdf.mjs'; import { PdfJsonDocument, @@ -208,7 +210,8 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => { } = useToolWorkflow(); const { actions: navigationActions } = useNavigationActions(); const navigationState = useNavigationState(); - const { registerUnsavedChangesChecker, unregisterUnsavedChangesChecker } = navigationActions; + const { addFiles } = useFileManagement(); + const { consumeFiles, selectors } = useFileContext(); const [loadedDocument, setLoadedDocument] = useState(null); const [groupsByPage, setGroupsByPage] = useState([]); @@ -217,6 +220,8 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => { const [fileName, setFileName] = useState(''); const [errorMessage, setErrorMessage] = useState(null); const [isGeneratingPdf, setIsGeneratingPdf] = useState(false); + const [isSavingToWorkbench, setIsSavingToWorkbench] = useState(false); + const [shouldNavigateAfterSave, setShouldNavigateAfterSave] = useState(false); const [isConverting, setIsConverting] = useState(false); const [conversionProgress, setConversionProgress] = useState(null); const [forceSingleTextElement, setForceSingleTextElement] = useState(true); @@ -234,6 +239,7 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => { const originalGroupsRef = useRef([]); const imagesByPageRef = useRef([]); const autoLoadKeyRef = useRef(null); + const sourceFileIdRef = useRef(null); const loadRequestIdRef = useRef(0); const latestPdfRequestIdRef = useRef(null); const loadedDocumentRef = useRef(null); @@ -279,6 +285,23 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => { ); const hasChanges = useMemo(() => dirtyPages.some(Boolean), [dirtyPages]); const hasDocument = loadedDocument !== null; + + // Sync hasChanges to navigation context so navigation guards can block + useEffect(() => { + navigationActions.setHasUnsavedChanges(hasChanges); + return () => { + navigationActions.setHasUnsavedChanges(false); + }; + }, [hasChanges, navigationActions]); + + // Navigate to files view AFTER the unsaved changes state is properly cleared + useEffect(() => { + if (shouldNavigateAfterSave && !navigationState.hasUnsavedChanges) { + setShouldNavigateAfterSave(false); + navigationActions.setToolAndWorkbench(null, getDefaultWorkbench()); + } + }, [shouldNavigateAfterSave, navigationState.hasUnsavedChanges, navigationActions]); + const viewLabel = useMemo(() => t('pdfTextEditor.viewLabel', 'PDF Editor'), [t]); const { selectedFiles } = useFileSelection(); @@ -720,6 +743,21 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => { [groupingMode, resetToDocument, t], ); + // Wrapper for loading files from the dropzone - adds to workbench first + const handleLoadFileFromDropzone = useCallback( + async (file: File) => { + // Add the file to the workbench so it appears in the file list + const addedFiles = await addFiles([file]); + // Capture the file ID for save-to-workbench functionality + if (addedFiles.length > 0 && addedFiles[0].fileId) { + sourceFileIdRef.current = addedFiles[0].fileId; + } + // Then load it into the editor + void handleLoadFile(file); + }, + [addFiles, handleLoadFile], + ); + const handleSelectPage = useCallback((pageIndex: number) => { setSelectedPage(pageIndex); // Trigger lazy loading for images on the selected page @@ -1122,6 +1160,229 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => { t, ]); + // Save changes to workbench (replaces the original file with edited version) + const handleSaveToWorkbench = useCallback(async () => { + setIsSavingToWorkbench(true); + + try { + if (!sourceFileIdRef.current) { + console.warn('[PdfTextEditor] No source file ID available for save to workbench'); + // Fall back to generating PDF download if no source file + await handleGeneratePdf(true); + return; + } + + const parentStub = selectors.getStirlingFileStub(sourceFileIdRef.current as any); + if (!parentStub) { + console.warn('[PdfTextEditor] Could not find parent stub for save to workbench'); + await handleGeneratePdf(true); + return; + } + + const ensureImagesForPages = async (pageIndices: number[]) => { + const uniqueIndices = Array.from(new Set(pageIndices)).filter((index) => index >= 0); + if (uniqueIndices.length === 0) { + return; + } + + for (const index of uniqueIndices) { + if (!loadedImagePagesRef.current.has(index)) { + await loadImagesForPage(index); + } + } + + const maxWaitTime = 15000; + const pollInterval = 150; + const startWait = Date.now(); + while (Date.now() - startWait < maxWaitTime) { + const allLoaded = uniqueIndices.every( + (index) => + loadedImagePagesRef.current.has(index) && + imagesByPageRef.current[index] !== undefined, + ); + const anyLoading = uniqueIndices.some((index) => + loadingImagePagesRef.current.has(index), + ); + if (allLoaded && !anyLoading) { + return; + } + await new Promise((resolve) => setTimeout(resolve, pollInterval)); + } + + const missing = uniqueIndices.filter( + (index) => !loadedImagePagesRef.current.has(index), + ); + if (missing.length > 0) { + throw new Error( + `Failed to load images for pages ${missing.map((i) => i + 1).join(', ')}`, + ); + } + }; + + const currentDoc = loadedDocumentRef.current; + const totalPages = currentDoc?.pages?.length ?? 0; + const currentDirtyPages = getDirtyPages(groupsByPage, imagesByPage, originalGroupsRef.current, originalImagesRef.current); + const dirtyPageIndices = currentDirtyPages + .map((isDirty, index) => (isDirty ? index : -1)) + .filter((index) => index >= 0); + + let pdfBlob: Blob; + let downloadName: string; + + const canUseIncremental = + isLazyMode && + cachedJobId && + dirtyPageIndices.length > 0 && + dirtyPageIndices.length < totalPages; + + if (canUseIncremental) { + await ensureImagesForPages(dirtyPageIndices); + + try { + const payload = buildPayload(); + if (!payload) { + throw new Error('Failed to build payload'); + } + + const { document, filename } = payload; + const dirtyPageSet = new Set(dirtyPageIndices); + const partialPages = + document.pages?.filter((_, index) => dirtyPageSet.has(index)) ?? []; + + const partialDocument: PdfJsonDocument = { + metadata: document.metadata, + xmpMetadata: document.xmpMetadata, + fonts: document.fonts, + lazyImages: true, + pages: partialPages, + }; + + const baseName = sanitizeBaseName(filename).replace(/-edited$/u, ''); + const expectedName = `${baseName || 'document'}.pdf`; + const response = await apiClient.post( + `/api/v1/convert/pdf/text-editor/partial/${cachedJobId}?filename=${encodeURIComponent(expectedName)}`, + partialDocument, + { + responseType: 'blob', + }, + ); + + const contentDisposition = response.headers?.['content-disposition'] ?? ''; + const detectedName = getFilenameFromHeaders(contentDisposition); + downloadName = detectedName || expectedName; + pdfBlob = response.data; + } catch (incrementalError) { + console.warn( + '[handleSaveToWorkbench] Incremental export failed, falling back to full export', + incrementalError, + ); + // Fall through to full export + if (isLazyMode && totalPages > 0) { + const allPageIndices = Array.from({ length: totalPages }, (_, index) => index); + await ensureImagesForPages(allPageIndices); + } + + const payload = buildPayload(); + if (!payload) { + throw new Error('Failed to build payload'); + } + + const { document, filename } = payload; + const serialized = JSON.stringify(document); + const jsonFile = new File([serialized], filename, { type: 'application/json' }); + + const formData = new FormData(); + formData.append('fileInput', jsonFile); + const response = await apiClient.post(CONVERSION_ENDPOINTS['text-editor-pdf'], formData, { + responseType: 'blob', + }); + + const contentDisposition = response.headers?.['content-disposition'] ?? ''; + const detectedName = getFilenameFromHeaders(contentDisposition); + const baseName = sanitizeBaseName(filename).replace(/-edited$/u, ''); + downloadName = detectedName || `${baseName || 'document'}.pdf`; + pdfBlob = response.data; + } + } else { + if (isLazyMode && totalPages > 0) { + const allPageIndices = Array.from({ length: totalPages }, (_, index) => index); + await ensureImagesForPages(allPageIndices); + } + + const payload = buildPayload(); + if (!payload) { + throw new Error('Failed to build payload'); + } + + const { document, filename } = payload; + const serialized = JSON.stringify(document); + const jsonFile = new File([serialized], filename, { type: 'application/json' }); + + const formData = new FormData(); + formData.append('fileInput', jsonFile); + const response = await apiClient.post(CONVERSION_ENDPOINTS['text-editor-pdf'], formData, { + responseType: 'blob', + }); + + const contentDisposition = response.headers?.['content-disposition'] ?? ''; + const detectedName = getFilenameFromHeaders(contentDisposition); + const baseName = sanitizeBaseName(filename).replace(/-edited$/u, ''); + downloadName = detectedName || `${baseName || 'document'}.pdf`; + pdfBlob = response.data; + } + + // Create the new PDF file + const pdfFile = new File([pdfBlob], downloadName, { type: 'application/pdf' }); + + // Create StirlingFile and stub for the output + const { stirlingFiles, stubs } = await createStirlingFilesAndStubs( + [pdfFile], + parentStub, + 'pdfTextEditor', + ); + + // Replace the original file with the edited version + await consumeFiles([sourceFileIdRef.current as any], stirlingFiles, stubs); + + // Update the source file ID to point to the new file + sourceFileIdRef.current = stubs[0].id; + + // Clear the unsaved changes flag - this will trigger the useEffect to navigate + // once React has processed the state update + navigationActions.setHasUnsavedChanges(false); + setErrorMessage(null); + + // Set flag to trigger navigation after state update is processed + setShouldNavigateAfterSave(true); + } catch (error: any) { + console.error('Failed to save to workbench', error); + const message = + error?.response?.data || + error?.message || + t('pdfTextEditor.errors.pdfConversion', 'Unable to save changes to workbench.'); + const msgString = typeof message === 'string' ? message : String(message); + setErrorMessage(msgString); + if (onError) { + onError(msgString); + } + } finally { + setIsSavingToWorkbench(false); + } + }, [ + buildPayload, + cachedJobId, + consumeFiles, + groupsByPage, + handleGeneratePdf, + imagesByPage, + isLazyMode, + loadImagesForPage, + navigationActions, + onError, + selectors, + t, + ]); + const requestPagePreview = useCallback( async (pageIndex: number, scale: number) => { if (!hasVectorPreview || !pdfDocumentRef.current) { @@ -1260,6 +1521,7 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => { fileName, errorMessage, isGeneratingPdf, + isSavingToWorkbench, isConverting, conversionProgress, hasChanges, @@ -1278,15 +1540,19 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => { // Generate PDF without triggering tool completion await handleGeneratePdf(true); }, + onSaveToWorkbench: handleSaveToWorkbench, onForceSingleTextElementChange: setForceSingleTextElement, onGroupingModeChange: setGroupingMode, onMergeGroups: handleMergeGroups, onUngroupGroup: handleUngroupGroup, + onLoadFile: handleLoadFileFromDropzone, }), [ handleMergeGroups, handleUngroupGroup, handleImageTransform, + handleSaveToWorkbench, imagesByPage, + isSavingToWorkbench, pagePreviews, dirtyPages, errorMessage, @@ -1311,6 +1577,7 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => { groupingMode, requestPagePreview, setForceSingleTextElement, + handleLoadFileFromDropzone, ]); const latestViewDataRef = useRef(viewData); @@ -1326,6 +1593,7 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => { useEffect(() => { if (selectedFiles.length === 0) { autoLoadKeyRef.current = null; + sourceFileIdRef.current = null; return; } @@ -1344,6 +1612,8 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => { } autoLoadKeyRef.current = fileKey; + // Capture the source file ID for save-to-workbench functionality + sourceFileIdRef.current = (file as any).fileId ?? null; void handleLoadFile(file); }, [selectedFiles, navigationState.selectedTool, handleLoadFile]); @@ -1398,27 +1668,6 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => { // The workbench should be set when the tool is selected via proper channels // (tool registry, tool picker, etc.) - not forced here - // Keep hasChanges in a ref for the checker to access - const hasChangesRef = useRef(hasChanges); - useEffect(() => { - hasChangesRef.current = hasChanges; - console.log('[PdfTextEditor] hasChanges updated to:', hasChanges); - }, [hasChanges]); - - // Register unsaved changes checker for navigation guard - useEffect(() => { - const checker = () => { - console.log('[PdfTextEditor] Checking unsaved changes:', hasChangesRef.current); - return hasChangesRef.current; - }; - registerUnsavedChangesChecker(checker); - console.log('[PdfTextEditor] Registered unsaved changes checker'); - return () => { - console.log('[PdfTextEditor] Unregistered unsaved changes checker'); - unregisterUnsavedChangesChecker(); - }; - }, [registerUnsavedChangesChecker, unregisterUnsavedChangesChecker]); - const lastSentViewDataRef = useRef(null); useEffect(() => { diff --git a/frontend/src/proprietary/tools/pdfTextEditor/fontAnalysis.ts b/frontend/src/core/tools/pdfTextEditor/fontAnalysis.ts similarity index 100% rename from frontend/src/proprietary/tools/pdfTextEditor/fontAnalysis.ts rename to frontend/src/core/tools/pdfTextEditor/fontAnalysis.ts diff --git a/frontend/src/proprietary/tools/pdfTextEditor/pdfTextEditorTypes.ts b/frontend/src/core/tools/pdfTextEditor/pdfTextEditorTypes.ts similarity index 98% rename from frontend/src/proprietary/tools/pdfTextEditor/pdfTextEditorTypes.ts rename to frontend/src/core/tools/pdfTextEditor/pdfTextEditorTypes.ts index 8439bb4c18..3dd45a4657 100644 --- a/frontend/src/proprietary/tools/pdfTextEditor/pdfTextEditorTypes.ts +++ b/frontend/src/core/tools/pdfTextEditor/pdfTextEditorTypes.ts @@ -221,8 +221,11 @@ export interface PdfTextEditorViewData { onDownloadJson: () => void; onGeneratePdf: () => void; onGeneratePdfForNavigation: () => Promise; + onSaveToWorkbench: () => Promise; + isSavingToWorkbench: boolean; onForceSingleTextElementChange: (value: boolean) => void; onGroupingModeChange: (value: 'auto' | 'paragraph' | 'singleLine') => void; onMergeGroups: (pageIndex: number, groupIds: string[]) => boolean; onUngroupGroup: (pageIndex: number, groupId: string) => boolean; + onLoadFile: (file: File) => void; } diff --git a/frontend/src/proprietary/tools/pdfTextEditor/pdfTextEditorUtils.ts b/frontend/src/core/tools/pdfTextEditor/pdfTextEditorUtils.ts similarity index 100% rename from frontend/src/proprietary/tools/pdfTextEditor/pdfTextEditorUtils.ts rename to frontend/src/core/tools/pdfTextEditor/pdfTextEditorUtils.ts diff --git a/frontend/src/core/types/getPdfInfo.ts b/frontend/src/core/types/getPdfInfo.ts new file mode 100644 index 0000000000..f489cd99ae --- /dev/null +++ b/frontend/src/core/types/getPdfInfo.ts @@ -0,0 +1,273 @@ +/** Metadata section from PDF */ +export interface PdfMetadata { + Title?: string | null; + Author?: string | null; + Subject?: string | null; + Keywords?: string | null; + Creator?: string | null; + Producer?: string | null; + CreationDate?: string | null; + ModificationDate?: string | null; + [key: string]: unknown; +} + +/** Basic info section */ +export interface PdfBasicInfo { + FileSizeInBytes?: number; + WordCount?: number; + ParagraphCount?: number; + CharacterCount?: number; + Compression?: boolean; + CompressionType?: string; + Language?: string | null; + 'Number of pages'?: number; + TotalImages?: number; + [key: string]: unknown; +} + +/** Document info section */ +export interface PdfDocumentInfo { + 'PDF version'?: string; + Trapped?: string | null; + 'Page Mode'?: string; + [key: string]: unknown; +} + +/** Encryption section */ +export interface PdfEncryption { + IsEncrypted?: boolean; + EncryptionAlgorithm?: string; + KeyLength?: number; + [key: string]: unknown; +} + +/** Permissions section - values are "Allowed" or "Not Allowed" */ +export interface PdfPermissions { + 'Document Assembly'?: 'Allowed' | 'Not Allowed'; + 'Extracting Content'?: 'Allowed' | 'Not Allowed'; + 'Extracting for accessibility'?: 'Allowed' | 'Not Allowed'; + 'Form Filling'?: 'Allowed' | 'Not Allowed'; + 'Modifying'?: 'Allowed' | 'Not Allowed'; + 'Modifying annotations'?: 'Allowed' | 'Not Allowed'; + 'Printing'?: 'Allowed' | 'Not Allowed'; + [key: string]: 'Allowed' | 'Not Allowed' | undefined; +} + +/** Compliance section */ +export interface PdfCompliance { + 'IsPDF/ACompliant'?: boolean; + 'PDF/AConformanceLevel'?: string; + 'IsPDF/AValidated'?: boolean; + 'IsPDF/XCompliant'?: boolean; + 'IsPDF/ECompliant'?: boolean; + 'IsPDF/VTCompliant'?: boolean; + 'IsPDF/UACompliant'?: boolean; + 'IsPDF/BCompliant'?: boolean; + 'IsPDF/SECCompliant'?: boolean; + [key: string]: unknown; +} + +/** Font info within a page */ +export interface PdfFontInfo { + Name?: string; + IsEmbedded?: boolean; + Subtype?: string; + ItalicAngle?: number; + IsItalic?: boolean; + IsBold?: boolean; + IsFixedPitch?: boolean; + IsSerif?: boolean; + IsSymbolic?: boolean; + IsScript?: boolean; + IsNonsymbolic?: boolean; + FontFamily?: string; + FontWeight?: number; + Count?: number; +} + +/** Image info within a page */ +export interface PdfImageInfo { + Width?: number; + Height?: number; + Name?: string; + ColorSpace?: string; +} + +/** Link info within a page */ +export interface PdfLinkInfo { + URI?: string; +} + +/** Annotations info within a page */ +export interface PdfAnnotationsInfo { + AnnotationsCount?: number; + SubtypeCount?: number; + ContentsCount?: number; + [key: string]: unknown; +} + +/** Size/dimensions info within a page */ +export interface PdfSizeInfo { + 'Width (px)'?: string; + 'Height (px)'?: string; + 'Width (in)'?: string; + 'Height (in)'?: string; + 'Width (cm)'?: string; + 'Height (cm)'?: string; + 'Standard Page'?: string; + [key: string]: unknown; +} + +/** XObject counts within a page */ +export interface PdfXObjectCounts { + Image?: number; + Form?: number; + Other?: number; + [key: string]: unknown; +} + +/** ICC Profile info */ +export interface PdfICCProfile { + 'ICC Profile Length'?: number; +} + +/** Page-level information */ +export interface PdfPageInfo { + Size?: PdfSizeInfo; + Rotation?: number; + 'Page Orientation'?: string; + MediaBox?: string; + CropBox?: string; + BleedBox?: string; + TrimBox?: string; + ArtBox?: string; + 'Text Characters Count'?: number; + Annotations?: PdfAnnotationsInfo; + Images?: PdfImageInfo[]; + Links?: PdfLinkInfo[]; + Fonts?: PdfFontInfo[]; + 'Color Spaces & ICC Profiles'?: PdfICCProfile[]; + XObjectCounts?: PdfXObjectCounts; + Multimedia?: Record[]; +} + +/** Per-page info section (keyed by "Page 1", "Page 2", etc.) */ +export interface PdfPerPageInfo { + [pageLabel: string]: PdfPageInfo; +} + +/** Embedded file info */ +export interface PdfEmbeddedFileInfo { + Name?: string; + FileSize?: number; +} + +/** Attachment info */ +export interface PdfAttachmentInfo { + Name?: string; + Description?: string; +} + +/** JavaScript info */ +export interface PdfJavaScriptInfo { + 'JS Name'?: string; + 'JS Script Length'?: number; +} + +/** Layer info */ +export interface PdfLayerInfo { + Name?: string; +} + +/** Structure tree element */ +export interface PdfStructureTreeElement { + Type?: string; + Content?: string; + Children?: PdfStructureTreeElement[]; +} + +/** Other section with miscellaneous data */ +export interface PdfOtherInfo { + Attachments?: PdfAttachmentInfo[]; + EmbeddedFiles?: PdfEmbeddedFileInfo[]; + JavaScript?: PdfJavaScriptInfo[]; + Layers?: PdfLayerInfo[]; + StructureTree?: PdfStructureTreeElement[]; + 'Bookmarks/Outline/TOC'?: PdfTocEntry[]; + XMPMetadata?: string | null; +} + +/** Table of contents bookmark entry */ +export interface PdfTocEntry { + Title?: string; + [key: string]: unknown; +} + +/** Summary data section */ +export interface PdfSummaryData { + encrypted?: boolean; + restrictedPermissions?: string[]; + restrictedPermissionsCount?: number; + standardCompliance?: string; + standardPurpose?: string; + standardValidationPassed?: boolean; +} + +/** Form fields section */ +export type PdfFormFields = Record; + +/** Parsed sections with normalized keys for frontend use */ +export interface ParsedPdfSections { + metadata?: PdfMetadata | null; + formFields?: PdfFormFields | null; + basicInfo?: PdfBasicInfo | null; + documentInfo?: PdfDocumentInfo | null; + compliance?: PdfCompliance | null; + encryption?: PdfEncryption | null; + permissions?: PdfPermissions | null; + toc?: PdfTocEntry[] | null; + other?: PdfOtherInfo | null; + perPage?: PdfPerPageInfo | null; + summaryData?: PdfSummaryData | null; +} + +/** Raw backend response structure */ +export interface PdfInfoBackendData { + Metadata?: PdfMetadata; + FormFields?: PdfFormFields; + BasicInfo?: PdfBasicInfo; + DocumentInfo?: PdfDocumentInfo; + Compliancy?: PdfCompliance; + Encryption?: PdfEncryption; + Permissions?: PdfPermissions; + Other?: PdfOtherInfo; + PerPageInfo?: PdfPerPageInfo; + SummaryData?: PdfSummaryData; + // Legacy/alternative keys for backwards compatibility + 'Form Fields'?: PdfFormFields; + 'Basic Info'?: PdfBasicInfo; + 'Document Info'?: PdfDocumentInfo; + Compliance?: PdfCompliance; + 'Bookmarks/Outline/TOC'?: PdfTocEntry[]; + 'Table of Contents'?: PdfTocEntry[]; + 'Per Page Info'?: PdfPerPageInfo; +} + +export interface PdfInfoReportEntry { + fileId: string; + fileName: string; + fileSize: number | null; + lastModified: number | null; + thumbnailUrl?: string | null; + data: PdfInfoBackendData; + error: string | null; + summaryGeneratedAt?: number; +} + +export interface PdfInfoReportData { + generatedAt: number; + entries: PdfInfoReportEntry[]; +} + +export const INFO_JSON_FILENAME = 'response.json'; +export const INFO_PDF_FILENAME = 'pdf-information-report.pdf'; diff --git a/frontend/src/core/types/toolId.ts b/frontend/src/core/types/toolId.ts index 23b03a8716..752be58f43 100644 --- a/frontend/src/core/types/toolId.ts +++ b/frontend/src/core/types/toolId.ts @@ -54,6 +54,7 @@ export const CORE_REGULAR_TOOL_IDS = [ 'replaceColor', 'showJS', 'bookletImposition', + 'pdfTextEditor', ] as const; export const CORE_SUPER_TOOL_IDS = [ diff --git a/frontend/src/core/utils/automationExecutor.ts b/frontend/src/core/utils/automationExecutor.ts index 760a86d7bb..2651784e18 100644 --- a/frontend/src/core/utils/automationExecutor.ts +++ b/frontend/src/core/utils/automationExecutor.ts @@ -1,4 +1,4 @@ -import axios from 'axios'; +import apiClient from '@app/services/apiClient'; import { ToolRegistry } from '@app/data/toolsTaxonomy'; import { ToolId } from '@app/types/toolId'; import { AUTOMATION_CONSTANTS } from '@app/constants/automation'; @@ -58,7 +58,7 @@ const executeApiRequest = async ( filePrefix: string, preserveBackendFilename?: boolean ): Promise => { - const response = await axios.post(endpoint, formData, { + const response = await apiClient.post(endpoint, formData, { responseType: 'blob', timeout: AUTOMATION_CONSTANTS.OPERATION_TIMEOUT }); @@ -158,8 +158,8 @@ export const executeToolOperationWithPrefix = async ( try { // Check if tool uses custom processor (like Convert tool) if (config.customProcessor) { - const resultFiles = await config.customProcessor(parameters, files); - return resultFiles; + const result = await config.customProcessor(parameters, files); + return result.files; } // Execute based on tool type diff --git a/frontend/src/core/utils/automationFileProcessor.ts b/frontend/src/core/utils/automationFileProcessor.ts index b07fe961be..311848bfd6 100644 --- a/frontend/src/core/utils/automationFileProcessor.ts +++ b/frontend/src/core/utils/automationFileProcessor.ts @@ -2,7 +2,7 @@ * File processing utilities specifically for automation workflows */ -import axios from 'axios'; +import apiClient from '@app/services/apiClient'; import { zipFileService } from '@app/services/zipFileService'; import { ResourceManager } from '@app/utils/resourceManager'; import { AUTOMATION_CONSTANTS } from '@app/constants/automation'; @@ -97,7 +97,7 @@ export class AutomationFileProcessor { options: AutomationProcessingOptions = {} ): Promise { try { - const response = await axios.post(endpoint, formData, { + const response = await apiClient.post(endpoint, formData, { responseType: options.responseType || 'blob', timeout: options.timeout || AUTOMATION_CONSTANTS.OPERATION_TIMEOUT }); @@ -139,7 +139,7 @@ export class AutomationFileProcessor { options: AutomationProcessingOptions = {} ): Promise { try { - const response = await axios.post(endpoint, formData, { + const response = await apiClient.post(endpoint, formData, { responseType: options.responseType || 'blob', timeout: options.timeout || AUTOMATION_CONSTANTS.OPERATION_TIMEOUT }); diff --git a/frontend/src/core/utils/convertUtils.ts b/frontend/src/core/utils/convertUtils.ts index ef2836058f..b1e87161ba 100644 --- a/frontend/src/core/utils/convertUtils.ts +++ b/frontend/src/core/utils/convertUtils.ts @@ -60,6 +60,18 @@ export const isWebFormat = (extension: string): boolean => { return ['html', 'zip'].includes(extension.toLowerCase()); }; +/** + * Checks if the given extension is an office format (Word, Excel, PowerPoint, OpenOffice) + * These formats use LibreOffice for conversion and require individual file processing + */ +export const isOfficeFormat = (extension: string): boolean => { + return [ + 'docx', 'doc', 'odt', // Word processors + 'xlsx', 'xls', 'ods', // Spreadsheets + 'pptx', 'ppt', 'odp' // Presentations + ].includes(extension.toLowerCase()); +}; + /** * Gets available target extensions for a given source extension * Extracted from useConvertParameters to be reusable in automation settings diff --git a/frontend/src/core/utils/fileUtils.ts b/frontend/src/core/utils/fileUtils.ts index 0f14714019..4061884b55 100644 --- a/frontend/src/core/utils/fileUtils.ts +++ b/frontend/src/core/utils/fileUtils.ts @@ -52,6 +52,29 @@ export function detectFileExtension(filename: string): string { return extension; } +/** + * Removes the file extension from a filename + * @param filename - The filename to process + * @param options - Options for processing + * @param options.preserveCase - If true, preserves original case. If false (default), converts to lowercase + * @returns Filename without extension + * @example + * getFilenameWithoutExtension('document.pdf') // 'document' + * getFilenameWithoutExtension('my.file.name.txt') // 'my.file.name' + * getFilenameWithoutExtension('REPORT.PDF', { preserveCase: true }) // 'REPORT' + */ +export function getFilenameWithoutExtension( + filename: string, + options: { preserveCase?: boolean } = {} +): string { + if (!filename || typeof filename !== 'string') return ''; + + const { preserveCase = false } = options; + const withoutExtension = filename.replace(/\.[^.]+$/, ''); + + return preserveCase ? withoutExtension : withoutExtension.toLowerCase(); +} + /** * Checks if a file is a PDF based on extension and MIME type * @param file - File or file-like object with name and type properties diff --git a/frontend/src/core/utils/urlMapping.ts b/frontend/src/core/utils/urlMapping.ts index aa2a3c57c0..9cc5d1839e 100644 --- a/frontend/src/core/utils/urlMapping.ts +++ b/frontend/src/core/utils/urlMapping.ts @@ -97,6 +97,7 @@ export const URL_TO_TOOL_MAP: Record = { '/automate': 'automate', '/sign': 'sign', '/add-text': 'addText', + '/pdf-text-editor': 'pdfTextEditor', // Developer tools '/dev-api': 'devApi', diff --git a/frontend/src/desktop/components/SetupWizard/DesktopAuthLayout.tsx b/frontend/src/desktop/components/SetupWizard/DesktopAuthLayout.tsx new file mode 100644 index 0000000000..92558857b4 --- /dev/null +++ b/frontend/src/desktop/components/SetupWizard/DesktopAuthLayout.tsx @@ -0,0 +1,72 @@ +import React, { useEffect, useMemo, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import LoginRightCarousel from '@app/components/shared/LoginRightCarousel'; +import buildLoginSlides from '@app/components/shared/loginSlides'; +import styles from '@app/routes/authShared/AuthLayout.module.css'; +import { useLogoVariant } from '@app/hooks/useLogoVariant'; + +interface DesktopAuthLayoutProps { + children: React.ReactNode; +} + +export const DesktopAuthLayout: React.FC = ({ children }) => { + const { t } = useTranslation(); + const cardRef = useRef(null); + const [hideRightPanel, setHideRightPanel] = useState(false); + const logoVariant = useLogoVariant(); + const imageSlides = useMemo(() => buildLoginSlides(logoVariant, t), [logoVariant, t]); + + // Force light mode on auth pages + useEffect(() => { + const htmlElement = document.documentElement; + const previousColorScheme = htmlElement.getAttribute('data-mantine-color-scheme'); + + // Set light mode + htmlElement.setAttribute('data-mantine-color-scheme', 'light'); + + // Cleanup: restore previous theme when leaving auth pages + return () => { + if (previousColorScheme) { + htmlElement.setAttribute('data-mantine-color-scheme', previousColorScheme); + } + }; + }, []); + + useEffect(() => { + const update = () => { + // Use viewport to avoid hysteresis when the card is already in single-column mode + const viewportWidth = window.innerWidth; + const viewportHeight = window.innerHeight; + const cardWidthIfTwoCols = Math.min(1180, viewportWidth * 0.96); // matches min(73.75rem, 96vw) + const columnWidth = cardWidthIfTwoCols / 2; + const tooNarrow = columnWidth < 470; + const tooShort = viewportHeight < 740; + setHideRightPanel(tooNarrow || tooShort); + }; + update(); + window.addEventListener('resize', update); + window.addEventListener('orientationchange', update); + return () => { + window.removeEventListener('resize', update); + window.removeEventListener('orientationchange', update); + }; + }, []); + + return ( +
+
+
+
+ {children} +
+
+ {!hideRightPanel && ( + + )} +
+
+ ); +}; diff --git a/frontend/src/desktop/components/SetupWizard/DesktopOAuthButtons.tsx b/frontend/src/desktop/components/SetupWizard/DesktopOAuthButtons.tsx new file mode 100644 index 0000000000..49b55a3869 --- /dev/null +++ b/frontend/src/desktop/components/SetupWizard/DesktopOAuthButtons.tsx @@ -0,0 +1,110 @@ +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { authService, UserInfo } from '@app/services/authService'; +import { buildOAuthCallbackHtml } from '@app/utils/oauthCallbackHtml'; +import { BASE_PATH } from '@app/constants/app'; +import '@app/routes/authShared/auth.css'; + +export type OAuthProvider = 'google' | 'github' | 'keycloak' | 'azure' | 'apple' | 'oidc'; + +interface DesktopOAuthButtonsProps { + onOAuthSuccess: (userInfo: UserInfo) => Promise; + onError: (error: string) => void; + isDisabled: boolean; + serverUrl: string; + providers: OAuthProvider[]; +} + +export const DesktopOAuthButtons: React.FC = ({ + onOAuthSuccess, + onError, + isDisabled, + serverUrl, + providers, +}) => { + const { t } = useTranslation(); + const [oauthLoading, setOauthLoading] = useState(false); + + const handleOAuthLogin = async (provider: OAuthProvider) => { + // Prevent concurrent OAuth attempts + if (oauthLoading || isDisabled) { + return; + } + + try { + setOauthLoading(true); + + // Build callback page HTML with translations and dark mode support + const successHtml = buildOAuthCallbackHtml({ + title: t('oauth.success.title', 'Authentication Successful'), + message: t('oauth.success.message', 'You can close this window and return to Stirling PDF.'), + isError: false, + }); + + const errorHtml = buildOAuthCallbackHtml({ + title: t('oauth.error.title', 'Authentication Failed'), + message: t('oauth.error.message', 'Authentication was not successful. You can close this window and try again.'), + isError: true, + errorPlaceholder: true, // {error} will be replaced by Rust + }); + + const userInfo = await authService.loginWithOAuth(provider, serverUrl, successHtml, errorHtml); + + // Call the onOAuthSuccess callback to complete setup + await onOAuthSuccess(userInfo); + } catch (error) { + console.error('OAuth login failed:', error); + + const errorMessage = error instanceof Error + ? error.message + : t('setup.login.error.oauthFailed', 'OAuth login failed. Please try again.'); + + onError(errorMessage); + setOauthLoading(false); + } + }; + + const providerConfig: Record = { + google: { label: 'Google', file: 'google.svg' }, + github: { label: 'GitHub', file: 'github.svg' }, + keycloak: { label: 'Keycloak', file: 'keycloak.svg' }, + azure: { label: 'Microsoft', file: 'microsoft.svg' }, + apple: { label: 'Apple', file: 'apple.svg' }, + oidc: { label: 'OpenID', file: 'oidc.svg' }, + }; + + if (providers.length === 0) { + return null; + } + + return ( +
+ {providers + .filter((providerId) => providerId in providerConfig) + .map((providerId) => { + const provider = providerConfig[providerId]; + return ( + + ); + })} + {oauthLoading && ( +

+ {t('setup.login.oauthPending', 'Opening browser for authentication...')} +

+ )} +
+ ); +}; diff --git a/frontend/src/desktop/components/SetupWizard/LoginForm.tsx b/frontend/src/desktop/components/SetupWizard/LoginForm.tsx deleted file mode 100644 index 4ad388822f..0000000000 --- a/frontend/src/desktop/components/SetupWizard/LoginForm.tsx +++ /dev/null @@ -1,225 +0,0 @@ -import React, { useState } from 'react'; -import { Stack, TextInput, PasswordInput, Button, Text, Divider, Group, Collapse, Anchor, Box } from '@mantine/core'; -import { useTranslation } from 'react-i18next'; -import { authService } from '@app/services/authService'; -import { STIRLING_SAAS_URL } from '@app/constants/connection'; -import { buildOAuthCallbackHtml } from '@app/utils/oauthCallbackHtml'; -import { BASE_PATH } from '@app/constants/app'; - -interface LoginFormProps { - serverUrl: string; - isSaaS?: boolean; - onLogin: (username: string, password: string) => Promise; - loading: boolean; -} - -export const LoginForm: React.FC = ({ serverUrl, isSaaS = false, onLogin, loading }) => { - const { t } = useTranslation(); - const [username, setUsername] = useState(''); - const [password, setPassword] = useState(''); - const [validationError, setValidationError] = useState(null); - const [oauthLoading, setOauthLoading] = useState(false); - const [showInstructions, setShowInstructions] = useState(false); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - - // Validation - if (!username.trim()) { - setValidationError(isSaaS - ? t('setup.login.error.emptyEmail', 'Please enter your email') - : t('setup.login.error.emptyUsername', 'Please enter your username')); - return; - } - - if (!password) { - setValidationError(t('setup.login.error.emptyPassword', 'Please enter your password')); - return; - } - - setValidationError(null); - await onLogin(username.trim(), password); - }; - - const handleOAuthLogin = async (provider: 'google' | 'github') => { - // Prevent concurrent OAuth attempts - if (oauthLoading || loading) { - return; - } - - try { - setOauthLoading(true); - setValidationError(null); - - // For SaaS, use configured SaaS URL; for self-hosted, derive from serverUrl - const authServerUrl = isSaaS - ? STIRLING_SAAS_URL - : serverUrl; // Self-hosted might have its own auth - - // Build callback page HTML with translations and dark mode support - const successHtml = buildOAuthCallbackHtml({ - title: t('oauth.success.title', 'Authentication Successful'), - message: t('oauth.success.message', 'You can close this window and return to Stirling PDF.'), - isError: false, - }); - - const errorHtml = buildOAuthCallbackHtml({ - title: t('oauth.error.title', 'Authentication Failed'), - message: t('oauth.error.message', 'Authentication was not successful. You can close this window and try again.'), - isError: true, - errorPlaceholder: true, // {error} will be replaced by Rust - }); - - const userInfo = await authService.loginWithOAuth(provider, authServerUrl, successHtml, errorHtml); - - // Call the onLogin callback to complete setup (username/password not needed for OAuth) - await onLogin(userInfo.username, ''); - } catch (error) { - console.error('OAuth login failed:', error); - - const errorMessage = error instanceof Error - ? error.message - : t('setup.login.error.oauthFailed', 'OAuth login failed. Please try again.'); - - setValidationError(errorMessage); - setOauthLoading(false); - } - }; - - return ( -
- - - {t('setup.login.connectingTo', 'Connecting to:')} {isSaaS ? 'stirling.com' : serverUrl} - - - {/* Login requirement note for self-hosted servers */} - {!isSaaS && ( - - - {t('setup.login.serverRequirement', 'Note: The server must have login enabled.')}{' '} - setShowInstructions(!showInstructions)} - style={{ cursor: 'pointer' }} - > - {showInstructions - ? t('setup.login.hideInstructions', 'Hide instructions') - : t('setup.login.showInstructions', 'How to enable?')} - - - - - - - {t('setup.login.instructions', 'To enable login on your Stirling PDF server:')} - - - {t('setup.login.instructionsEnvVar', 'Set the environment variable:')} - - - SECURITY_ENABLELOGIN=true - - - {t('setup.login.instructionsOrYml', 'Or in settings.yml:')} - - - security.enableLogin: true - - - {t('setup.login.instructionsRestart', 'Then restart your server for the changes to take effect.')} - - - - - )} - - {/* OAuth Login Buttons - Only show for SaaS */} - {isSaaS && ( - <> - - - - - - - - {oauthLoading && ( - - {t('setup.login.oauthPending', 'Opening browser for authentication...')} - - )} - - - - - )} - - { - setUsername(e.target.value); - setValidationError(null); - }} - disabled={loading} - required - /> - - { - setPassword(e.target.value); - setValidationError(null); - }} - disabled={loading} - required - /> - - {validationError && ( - - {validationError} - - )} - - - -
- ); -}; diff --git a/frontend/src/desktop/components/SetupWizard/ModeSelection.tsx b/frontend/src/desktop/components/SetupWizard/ModeSelection.tsx deleted file mode 100644 index 8242fa5ed0..0000000000 --- a/frontend/src/desktop/components/SetupWizard/ModeSelection.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import React from 'react'; -import { Stack, Button, Text } from '@mantine/core'; -import { useTranslation } from 'react-i18next'; -import CloudIcon from '@mui/icons-material/Cloud'; -import ComputerIcon from '@mui/icons-material/Computer'; - -interface ModeSelectionProps { - onSelect: (mode: 'saas' | 'selfhosted') => void; - loading: boolean; -} - -export const ModeSelection: React.FC = ({ onSelect, loading }) => { - const { t } = useTranslation(); - - return ( - - - - - - ); -}; diff --git a/frontend/src/desktop/components/SetupWizard/SaaSLoginScreen.tsx b/frontend/src/desktop/components/SetupWizard/SaaSLoginScreen.tsx new file mode 100644 index 0000000000..ab82bde1ed --- /dev/null +++ b/frontend/src/desktop/components/SetupWizard/SaaSLoginScreen.tsx @@ -0,0 +1,95 @@ +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import LoginHeader from '@app/routes/login/LoginHeader'; +import ErrorMessage from '@app/routes/login/ErrorMessage'; +import EmailPasswordForm from '@app/routes/login/EmailPasswordForm'; +import DividerWithText from '@app/components/shared/DividerWithText'; +import { DesktopOAuthButtons } from '@app/components/SetupWizard/DesktopOAuthButtons'; +import { SelfHostedLink } from '@app/components/SetupWizard/SelfHostedLink'; +import { UserInfo } from '@app/services/authService'; +import '@app/routes/authShared/auth.css'; + +interface SaaSLoginScreenProps { + serverUrl: string; + onLogin: (username: string, password: string) => Promise; + onOAuthSuccess: (userInfo: UserInfo) => Promise; + onSelfHostedClick: () => void; + loading: boolean; + error: string | null; +} + +export const SaaSLoginScreen: React.FC = ({ + serverUrl, + onLogin, + onOAuthSuccess, + onSelfHostedClick, + loading, + error, +}) => { + const { t } = useTranslation(); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [validationError, setValidationError] = useState(null); + + const handleEmailPasswordSubmit = async () => { + // Validation + if (!email.trim()) { + setValidationError(t('setup.login.error.emptyEmail', 'Please enter your email')); + return; + } + + if (!password) { + setValidationError(t('setup.login.error.emptyPassword', 'Please enter your password')); + return; + } + + setValidationError(null); + await onLogin(email.trim(), password); + }; + + const handleOAuthError = (errorMessage: string) => { + setValidationError(errorMessage); + }; + + const displayError = error || validationError; + + return ( + <> + + + + + + + + + { + setEmail(value); + setValidationError(null); + }} + setPassword={(value) => { + setPassword(value); + setValidationError(null); + }} + onSubmit={handleEmailPasswordSubmit} + isSubmitting={loading} + submitButtonText={t('setup.login.submit', 'Login')} + /> + + + + ); +}; diff --git a/frontend/src/desktop/components/SetupWizard/SelfHostedLink.tsx b/frontend/src/desktop/components/SetupWizard/SelfHostedLink.tsx new file mode 100644 index 0000000000..6a184cc584 --- /dev/null +++ b/frontend/src/desktop/components/SetupWizard/SelfHostedLink.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import '@app/routes/authShared/auth.css'; + +interface SelfHostedLinkProps { + onClick: () => void; + disabled?: boolean; +} + +export const SelfHostedLink: React.FC = ({ onClick, disabled = false }) => { + const { t } = useTranslation(); + + return ( +
+ +
+ ); +}; diff --git a/frontend/src/desktop/components/SetupWizard/SelfHostedLoginScreen.tsx b/frontend/src/desktop/components/SetupWizard/SelfHostedLoginScreen.tsx new file mode 100644 index 0000000000..9a68b91561 --- /dev/null +++ b/frontend/src/desktop/components/SetupWizard/SelfHostedLoginScreen.tsx @@ -0,0 +1,105 @@ +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Text } from '@mantine/core'; +import LoginHeader from '@app/routes/login/LoginHeader'; +import ErrorMessage from '@app/routes/login/ErrorMessage'; +import EmailPasswordForm from '@app/routes/login/EmailPasswordForm'; +import DividerWithText from '@app/components/shared/DividerWithText'; +import { DesktopOAuthButtons, OAuthProvider } from '@app/components/SetupWizard/DesktopOAuthButtons'; +import { UserInfo } from '@app/services/authService'; +import '@app/routes/authShared/auth.css'; + +interface SelfHostedLoginScreenProps { + serverUrl: string; + enabledOAuthProviders?: string[]; + onLogin: (username: string, password: string) => Promise; + onOAuthSuccess: (userInfo: UserInfo) => Promise; + loading: boolean; + error: string | null; +} + +export const SelfHostedLoginScreen: React.FC = ({ + serverUrl, + enabledOAuthProviders, + onLogin, + onOAuthSuccess, + loading, + error, +}) => { + const { t } = useTranslation(); + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [validationError, setValidationError] = useState(null); + + const handleSubmit = async () => { + // Validation + if (!username.trim()) { + setValidationError(t('setup.login.error.emptyUsername', 'Please enter your username')); + return; + } + + if (!password) { + setValidationError(t('setup.login.error.emptyPassword', 'Please enter your password')); + return; + } + + setValidationError(null); + await onLogin(username.trim(), password); + }; + + const handleOAuthError = (errorMessage: string) => { + setValidationError(errorMessage); + }; + + const displayError = error || validationError; + + return ( + <> + + + + + + {t('setup.login.connectingTo', 'Connecting to:')} {serverUrl} + + + {/* Show OAuth buttons if providers are available */} + {enabledOAuthProviders && enabledOAuthProviders.length > 0 && ( + <> + + + + + )} + + { + setUsername(value); + setValidationError(null); + }} + setPassword={(value) => { + setPassword(value); + setValidationError(null); + }} + onSubmit={handleSubmit} + isSubmitting={loading} + submitButtonText={t('setup.login.submit', 'Login')} + /> + + ); +}; diff --git a/frontend/src/desktop/components/SetupWizard/ServerSelection.tsx b/frontend/src/desktop/components/SetupWizard/ServerSelection.tsx index 3ca5ea65b2..f8a0d4f9e1 100644 --- a/frontend/src/desktop/components/SetupWizard/ServerSelection.tsx +++ b/frontend/src/desktop/components/SetupWizard/ServerSelection.tsx @@ -1,8 +1,9 @@ import React, { useState } from 'react'; -import { Stack, Button, TextInput } from '@mantine/core'; +import { Stack, Button, TextInput, Alert, Text } from '@mantine/core'; import { useTranslation } from 'react-i18next'; import { ServerConfig } from '@app/services/connectionModeService'; import { connectionModeService } from '@app/services/connectionModeService'; +import LocalIcon from '@app/components/shared/LocalIcon'; interface ServerSelectionProps { onSelect: (config: ServerConfig) => void; @@ -14,11 +15,13 @@ export const ServerSelection: React.FC = ({ onSelect, load const [customUrl, setCustomUrl] = useState(''); const [testing, setTesting] = useState(false); const [testError, setTestError] = useState(null); + const [securityDisabled, setSecurityDisabled] = useState(false); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); - const url = customUrl.trim(); + // Normalize URL: trim and remove trailing slashes + const url = customUrl.trim().replace(/\/+$/, ''); if (!url) { setTestError(t('setup.server.error.emptyUrl', 'Please enter a server URL')); @@ -28,6 +31,7 @@ export const ServerSelection: React.FC = ({ onSelect, load // Test connection before proceeding setTesting(true); setTestError(null); + setSecurityDisabled(false); try { const isReachable = await connectionModeService.testConnection(url); @@ -38,9 +42,67 @@ export const ServerSelection: React.FC = ({ onSelect, load return; } - // Connection successful + // Fetch OAuth providers and check if login is enabled + let enabledProviders: string[] = []; + try { + const response = await fetch(`${url}/api/v1/proprietary/ui-data/login`); + + // Check if security is disabled (status 403 or error response) + if (!response.ok) { + if (response.status === 403 || response.status === 401) { + setSecurityDisabled(true); + setTesting(false); + return; + } + // Other error statuses - show generic error + setTestError( + t('setup.server.error.configFetch', 'Failed to fetch server configuration (status {{status}})', { + status: response.status + }) + ); + setTesting(false); + return; + } + + const data = await response.json(); + console.log('Login UI data:', data); + + // Check if the response indicates security is disabled + if (data.enableLogin === false || data.securityEnabled === false) { + setSecurityDisabled(true); + setTesting(false); + return; + } + + // Extract provider IDs from authorization URLs + // Example: "/oauth2/authorization/google" → "google" + enabledProviders = Object.keys(data.providerList || {}) + .map(key => key.split('/').pop()) + .filter((id): id is string => id !== undefined); + + console.log('[ServerSelection] Detected OAuth providers:', enabledProviders); + } catch (err) { + console.error('[ServerSelection] Failed to fetch login configuration', err); + + // Check if it's a security disabled error + if (err instanceof Error && (err.message.includes('403') || err.message.includes('401'))) { + setSecurityDisabled(true); + setTesting(false); + return; + } + + // For any other error (network, CORS, invalid JSON, etc.), show error and don't proceed + setTestError( + t('setup.server.error.configFetch', 'Failed to fetch server configuration. Please check the URL and try again.') + ); + setTesting(false); + return; + } + + // Connection successful - pass URL and OAuth providers onSelect({ url, + enabledOAuthProviders: enabledProviders.length > 0 ? enabledProviders : undefined, }); } catch (error) { console.error('Connection test failed:', error); @@ -64,6 +126,7 @@ export const ServerSelection: React.FC = ({ onSelect, load onChange={(e) => { setCustomUrl(e.target.value); setTestError(null); + setSecurityDisabled(false); }} disabled={loading || testing} error={testError} @@ -73,6 +136,28 @@ export const ServerSelection: React.FC = ({ onSelect, load )} /> + {securityDisabled && ( + } + title={t('setup.server.error.securityDisabled.title', 'Login Not Enabled')} + > + + + {t('setup.server.error.securityDisabled.body', 'This server does not have login enabled. To connect to this server, you must enable authentication:')} + + +
    +
  1. {t('setup.server.error.securityDisabled.step1', 'Set DOCKER_ENABLE_SECURITY=true in your environment')}
  2. +
  3. {t('setup.server.error.securityDisabled.step2', 'Or set security.enableLogin=true in settings.yml')}
  4. +
  5. {t('setup.server.error.securityDisabled.step3', 'Restart the server')}
  6. +
+
+
+
+ )} + - )} - - - -
+ {/* Back Button */} + {activeStep > SetupStep.SaaSLogin && !loading && ( +
+ +
+ )} + ); }; diff --git a/frontend/src/desktop/services/apiClient.ts b/frontend/src/desktop/services/apiClient.ts index 8773afc5e4..257099c801 100644 --- a/frontend/src/desktop/services/apiClient.ts +++ b/frontend/src/desktop/services/apiClient.ts @@ -14,7 +14,7 @@ import { getApiBaseUrl } from '@app/services/apiClientConfig'; const apiClient = create({ baseURL: getApiBaseUrl(), responseType: 'json', - withCredentials: true, + withCredentials: false, // Desktop doesn't need credentials }); // Setup interceptors (desktop-specific auth and backend ready checks) diff --git a/frontend/src/desktop/services/apiClientSetup.ts b/frontend/src/desktop/services/apiClientSetup.ts index d01c0d9973..ee9cbcf55f 100644 --- a/frontend/src/desktop/services/apiClientSetup.ts +++ b/frontend/src/desktop/services/apiClientSetup.ts @@ -48,13 +48,21 @@ export function setupApiInterceptors(client: AxiosInstance): void { // Debug logging console.debug(`[apiClientSetup] Request to: ${extendedConfig.url}`); - // Add auth token for remote requests + // Add auth token for remote requests and enable credentials const isRemote = await operationRouter.isSelfHostedMode(); if (isRemote) { + // Self-hosted mode: enable credentials for session management + extendedConfig.withCredentials = true; + const token = await authService.getAuthToken(); if (token) { extendedConfig.headers.Authorization = `Bearer ${token}`; + } else { + console.warn('[apiClientSetup] Self-hosted mode but no auth token available'); } + } else { + // SaaS mode: disable credentials (security disabled on local backend) + extendedConfig.withCredentials = false; } // Backend readiness check (for local backend) @@ -85,7 +93,9 @@ export function setupApiInterceptors(client: AxiosInstance): void { // Response interceptor: Handle auth errors client.interceptors.response.use( - (response) => response, + (response) => { + return response; + }, async (error) => { const originalRequest = error.config as ExtendedRequestConfig; diff --git a/frontend/src/desktop/services/authService.ts b/frontend/src/desktop/services/authService.ts index 76f8aa1577..ed8be911ff 100644 --- a/frontend/src/desktop/services/authService.ts +++ b/frontend/src/desktop/services/authService.ts @@ -25,6 +25,7 @@ export class AuthService { private static instance: AuthService; private authStatus: AuthStatus = 'unauthenticated'; private userInfo: UserInfo | null = null; + private cachedToken: string | null = null; private authListeners = new Set<(status: AuthStatus, userInfo: UserInfo | null) => void>(); static getInstance(): AuthService { @@ -38,13 +39,32 @@ export class AuthService { * Save token to all storage locations and notify listeners */ private async saveTokenEverywhere(token: string): Promise { - // Save to Tauri store - await invoke('save_auth_token', { token }); - console.log('[Desktop AuthService] Token saved to Tauri store'); + // Validate token before caching + if (!token || token.trim().length === 0) { + console.warn('[Desktop AuthService] Attempted to save invalid/empty token'); + throw new Error('Invalid token'); + } - // Sync to localStorage for web layer - localStorage.setItem('stirling_jwt', token); - console.log('[Desktop AuthService] Token saved to localStorage'); + try { + // Save to Tauri store + await invoke('save_auth_token', { token }); + console.log('[Desktop AuthService] āœ… Token saved to Tauri store'); + } catch (error) { + console.error('[Desktop AuthService] āŒ Failed to save token to Tauri store:', error); + // Don't throw - we can still use localStorage + } + + try { + // Sync to localStorage for web layer + localStorage.setItem('stirling_jwt', token); + console.log('[Desktop AuthService] āœ… Token saved to localStorage'); + } catch (error) { + console.error('[Desktop AuthService] āŒ Failed to save token to localStorage:', error); + } + + // Cache the valid token in memory + this.cachedToken = token; + console.log('[Desktop AuthService] āœ… Token cached in memory'); // Notify other parts of the system window.dispatchEvent(new CustomEvent('jwt-available')); @@ -56,20 +76,25 @@ export class AuthService { */ private async getTokenFromAnySource(): Promise { // Try Tauri store first - console.log('[Desktop AuthService] Retrieving token from Tauri store...'); - const token = await invoke('get_auth_token'); + try { + const token = await invoke('get_auth_token'); - if (token) { - console.log(`[Desktop AuthService] Token found in Tauri store (length: ${token.length})`); - return token; + if (token) { + console.log(`[Desktop AuthService] āœ… Token found in Tauri store (length: ${token.length})`); + return token; + } + + console.log('[Desktop AuthService] ā„¹ļø No token in Tauri store, checking localStorage...'); + } catch (error) { + console.error('[Desktop AuthService] āŒ Failed to read from Tauri store:', error); } - console.log('[Desktop AuthService] No token in Tauri store'); - // Fallback to localStorage const localStorageToken = localStorage.getItem('stirling_jwt'); if (localStorageToken) { - console.log('[Desktop AuthService] Token found in localStorage (length:', localStorageToken.length, ')'); + console.log(`[Desktop AuthService] āœ… Token found in localStorage (length: ${localStorageToken.length})`); + } else { + console.log('[Desktop AuthService] āŒ No token found in any storage'); } return localStorageToken; @@ -79,6 +104,10 @@ export class AuthService { * Clear token from all storage locations */ private async clearTokenEverywhere(): Promise { + // Invalidate cache + this.cachedToken = null; + console.log('[Desktop AuthService] Cache invalidated'); + await invoke('clear_auth_token'); localStorage.removeItem('stirling_jwt'); } @@ -183,7 +212,22 @@ export class AuthService { async getAuthToken(): Promise { try { - return await this.getTokenFromAnySource(); + // Return cached token if available + if (this.cachedToken) { + console.debug('[Desktop AuthService] āœ… Returning cached token'); + return this.cachedToken; + } + + console.debug('[Desktop AuthService] Cache miss, fetching from storage...'); + const token = await this.getTokenFromAnySource(); + + // Cache the token if valid + if (token && token.trim().length > 0) { + this.cachedToken = token; + console.log('[Desktop AuthService] āœ… Token cached in memory after retrieval'); + } + + return token; } catch (error) { console.error('[Desktop AuthService] Failed to get auth token:', error); return null; diff --git a/frontend/src/desktop/services/connectionModeService.ts b/frontend/src/desktop/services/connectionModeService.ts index f01dbc40a6..cf454a50b3 100644 --- a/frontend/src/desktop/services/connectionModeService.ts +++ b/frontend/src/desktop/services/connectionModeService.ts @@ -5,6 +5,7 @@ export type ConnectionMode = 'saas' | 'selfhosted'; export interface ServerConfig { url: string; + enabledOAuthProviders?: string[]; } export interface ConnectionConfig { diff --git a/frontend/src/desktop/services/tauriHttpClient.ts b/frontend/src/desktop/services/tauriHttpClient.ts index 89c87bfbb1..b5bbceb930 100644 --- a/frontend/src/desktop/services/tauriHttpClient.ts +++ b/frontend/src/desktop/services/tauriHttpClient.ts @@ -61,7 +61,7 @@ class TauriHttpClient { headers: {}, timeout: 120000, responseType: 'json', - withCredentials: true, + withCredentials: false, // Desktop doesn't need credentials (backend has allowCredentials=false) }; public interceptors: Interceptors = { @@ -173,14 +173,15 @@ class TauriHttpClient { } try { - // Debug logging - console.debug(`[tauriHttpClient] Fetch request:`, { url, method }); + // Convert withCredentials to fetch API's credentials option + const credentials: RequestCredentials = finalConfig.withCredentials ? 'include' : 'omit'; // Make the request using Tauri's native HTTP client (standard Fetch API) const response = await fetch(url, { method, headers, body, + credentials, }); // Parse response based on responseType diff --git a/frontend/src/proprietary/auth/oauthTypes.ts b/frontend/src/proprietary/auth/oauthTypes.ts new file mode 100644 index 0000000000..2d38f1b3e5 --- /dev/null +++ b/frontend/src/proprietary/auth/oauthTypes.ts @@ -0,0 +1,24 @@ +/** + * Known OAuth providers with dedicated UI support. + * Custom providers are also supported - the backend determines availability. + */ +export const KNOWN_OAUTH_PROVIDERS = [ + 'github', + 'google', + 'apple', + 'azure', + 'keycloak', + 'cloudron', + 'authentik', + 'oidc', +] as const; + +export type KnownOAuthProvider = typeof KNOWN_OAUTH_PROVIDERS[number]; + +/** + * OAuth provider ID - can be any known provider or custom string. + * The backend configuration determines which providers are available. + * + * @example 'github' | 'google' | 'mycompany' | 'authentik' + */ +export type OAuthProvider = KnownOAuthProvider | (string & {}); diff --git a/frontend/src/proprietary/auth/springAuthClient.ts b/frontend/src/proprietary/auth/springAuthClient.ts index 2f1aa36cb5..646b711823 100644 --- a/frontend/src/proprietary/auth/springAuthClient.ts +++ b/frontend/src/proprietary/auth/springAuthClient.ts @@ -10,6 +10,7 @@ import apiClient from '@app/services/apiClient'; import { AxiosError } from 'axios'; import { BASE_PATH } from '@app/constants/app'; +import { type OAuthProvider } from '@app/auth/oauthTypes'; // Helper to extract error message from axios error function getErrorMessage(error: unknown, fallback: string): string { @@ -248,11 +249,14 @@ class SpringAuthClient { } /** - * Sign in with OAuth provider (GitHub, Google, etc.) + * Sign in with OAuth provider (GitHub, Google, Authentik, etc.) * This redirects to the Spring OAuth2 authorization endpoint + * + * @param params.provider - OAuth provider ID (e.g., 'github', 'google', 'authentik', 'mycompany') + * Can be any known provider or custom string - the backend determines available providers */ async signInWithOAuth(params: { - provider: 'github' | 'google' | 'apple' | 'azure' | 'keycloak' | 'oidc'; + provider: OAuthProvider; options?: { redirectTo?: string; queryParams?: Record }; }): Promise<{ error: AuthError | null }> { try { diff --git a/frontend/src/proprietary/components/shared/ChangeUserPasswordModal.tsx b/frontend/src/proprietary/components/shared/ChangeUserPasswordModal.tsx new file mode 100644 index 0000000000..9e0cd222c4 --- /dev/null +++ b/frontend/src/proprietary/components/shared/ChangeUserPasswordModal.tsx @@ -0,0 +1,283 @@ +import { useEffect, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + ActionIcon, + Button, + Checkbox, + CloseButton, + Group, + Modal, + PasswordInput, + Stack, + Text, + Tooltip, +} from '@mantine/core'; +import LocalIcon from '@app/components/shared/LocalIcon'; +import { alert } from '@app/components/toast'; +import { ChangeUserPasswordRequest, User, userManagementService } from '@app/services/userManagementService'; +import { Z_INDEX_OVER_CONFIG_MODAL } from '@app/styles/zIndex'; + +interface ChangeUserPasswordModalProps { + opened: boolean; + onClose: () => void; + user: User | null; + onSuccess: () => void; + mailEnabled: boolean; +} + +function generateSecurePassword() { + const charset = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz0123456789@$!%*?&'; + const length = 14; + let password = ''; + const charsetLength = charset.length; + const uint8Array = new Uint8Array(length); + window.crypto.getRandomValues(uint8Array); + // To avoid modulo bias, discard values >= 256 - (256 % charsetLength) + for (let i = 0; password.length < length; ) { + const randomByte = uint8Array[i]; + i++; + if (randomByte >= Math.floor(256 / charsetLength) * charsetLength) { + // Discard and generate a new random value + if (i >= uint8Array.length) { + // Exhausted the array, fill a new one + window.crypto.getRandomValues(uint8Array); + i = 0; + } + continue; + } + const randomIndex = randomByte % charsetLength; + password += charset[randomIndex]; + } + return password; +} + +export default function ChangeUserPasswordModal({ opened, onClose, user, onSuccess, mailEnabled }: ChangeUserPasswordModalProps) { + const { t } = useTranslation(); + const [form, setForm] = useState({ + newPassword: '', + confirmPassword: '', + generateRandom: false, + sendEmail: false, + includePassword: false, + forcePasswordChange: false, + }); + const [processing, setProcessing] = useState(false); + + const disabled = !user; + + const handleGeneratePassword = () => { + const generated = generateSecurePassword(); + setForm((prev) => ({ ...prev, newPassword: generated, confirmPassword: generated, generateRandom: true })); + }; + + const handleCopyPassword = async () => { + if (!form.newPassword) return; + try { + await navigator.clipboard.writeText(form.newPassword); + alert({ alertType: 'success', title: t('workspace.people.changePassword.copiedToClipboard', 'Password copied to clipboard') }); + } catch (_error) { + alert({ alertType: 'error', title: t('workspace.people.changePassword.copyFailed', 'Failed to copy password') }); + } + }; + + const resetState = () => { + setForm({ + newPassword: '', + confirmPassword: '', + generateRandom: false, + sendEmail: false, + includePassword: false, + forcePasswordChange: false, + }); + }; + + const handleClose = () => { + if (processing) return; + resetState(); + onClose(); + }; + + const handleSubmit = async () => { + if (!user) return; + + if (!form.generateRandom && !form.newPassword.trim()) { + alert({ alertType: 'error', title: t('workspace.people.changePassword.passwordRequired', 'Please enter a new password') }); + return; + } + + if (!form.generateRandom && form.newPassword !== form.confirmPassword) { + alert({ alertType: 'error', title: t('workspace.people.changePassword.passwordMismatch', 'Passwords do not match') }); + return; + } + + const payload: ChangeUserPasswordRequest = { + username: user.username, + newPassword: form.newPassword, // Always send the password (frontend generates it when generateRandom is true) + generateRandom: false, // Not needed since we're generating on frontend + sendEmail: form.sendEmail, + includePassword: form.includePassword, + forcePasswordChange: form.forcePasswordChange, + }; + + try { + setProcessing(true); + await userManagementService.changeUserPassword(payload); + alert({ alertType: 'success', title: t('workspace.people.changePassword.success', 'Password updated successfully') }); + onSuccess(); + handleClose(); + } catch (error: any) { + const errorMessage = error.response?.data?.message || error.response?.data?.error || error.message || t('workspace.people.changePassword.error', 'Failed to update password'); + alert({ alertType: 'error', title: errorMessage }); + } finally { + setProcessing(false); + } + }; + + useEffect(() => { + if (opened) { + setForm({ + newPassword: '', + confirmPassword: '', + generateRandom: false, + sendEmail: false, + includePassword: false, + forcePasswordChange: false, + }); + } + }, [opened, user?.username]); + + // Check if username is a valid email format + const isValidEmail = (email: string | undefined) => { + if (!email) return false; + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); + }; + + const canEmail = mailEnabled && isValidEmail(user?.username); + const passwordPreview = useMemo(() => form.newPassword && form.generateRandom ? form.newPassword : '', [form.generateRandom, form.newPassword]); + + return ( + +
+ + + + + + {t('workspace.people.changePassword.title', 'Change password')} + + + {t('workspace.people.changePassword.subtitle', 'Update the password for')} {user?.username} + + + + + setForm({ ...form, newPassword: event.currentTarget.value, generateRandom: false })} + disabled={processing || disabled || form.generateRandom} + data-autofocus + /> + setForm({ ...form, confirmPassword: event.currentTarget.value, generateRandom: false })} + disabled={processing || disabled || form.generateRandom} + error={!form.generateRandom && form.confirmPassword && form.newPassword !== form.confirmPassword ? t('workspace.people.changePassword.passwordMismatch', 'Passwords do not match') : undefined} + /> + + { + const checked = event.currentTarget.checked; + setForm((prev) => ({ ...prev, generateRandom: checked })); + if (event.currentTarget.checked) { + handleGeneratePassword(); + } + }} + /> + {passwordPreview && ( + + + {t('workspace.people.changePassword.generatedPreview', 'Generated password:')} {passwordPreview} + + + + + + + + )} + + + + + setForm({ ...form, sendEmail: event.currentTarget.checked })} + disabled={!canEmail || processing} + /> + setForm({ ...form, includePassword: event.currentTarget.checked })} + disabled={!canEmail || !form.sendEmail || processing} + /> + setForm({ ...form, forcePasswordChange: event.currentTarget.checked })} + disabled={processing || disabled} + /> + {!canEmail && ( + + {mailEnabled + ? t('workspace.people.changePassword.emailUnavailable', "This user's email is not a valid email address. Notifications are disabled.") + : t('workspace.people.changePassword.smtpDisabled', 'Email notifications require SMTP to be enabled in settings.')} + + )} + {canEmail && !form.includePassword && form.sendEmail && ( + + {t('workspace.people.changePassword.notifyOnly', 'An email will be sent without the password, letting the user know an admin changed it.')} + + )} + + + + +
+
+ ); +} diff --git a/frontend/src/proprietary/components/shared/config/configSections/AdminGeneralSection.tsx b/frontend/src/proprietary/components/shared/config/configSections/AdminGeneralSection.tsx index faebf4d489..75f8516e9e 100644 --- a/frontend/src/proprietary/components/shared/config/configSections/AdminGeneralSection.tsx +++ b/frontend/src/proprietary/components/shared/config/configSections/AdminGeneralSection.tsx @@ -1,6 +1,6 @@ -import { useEffect, useState, useRef, useCallback } from 'react'; +import { useEffect, useState, useRef, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { TextInput, Switch, Button, Stack, Paper, Text, Loader, Group, MultiSelect, Badge, SegmentedControl } from '@mantine/core'; +import { TextInput, Switch, Button, Stack, Paper, Text, Loader, Group, MultiSelect, Badge, SegmentedControl, Select } from '@mantine/core'; import { alert } from '@app/components/toast'; import RestartConfirmationModal from '@app/components/shared/config/RestartConfirmationModal'; import { useRestartServer } from '@app/components/shared/config/useRestartServer'; @@ -11,6 +11,8 @@ import { useLoginRequired } from '@app/hooks/useLoginRequired'; import LoginRequiredBanner from '@app/components/shared/config/LoginRequiredBanner'; import { usePreferences } from '@app/contexts/PreferencesContext'; import { useUnsavedChanges } from '@app/contexts/UnsavedChangesContext'; +import { supportedLanguages, toUnderscoreFormat, toUnderscoreLanguages } from '@app/i18n'; +import { Z_INDEX_CONFIG_MODAL } from '@app/styles/zIndex'; interface GeneralSettingsData { ui: { @@ -49,6 +51,12 @@ export default function AdminGeneralSection() { const { restartModalOpened, showRestartModal, closeRestartModal, restartServer } = useRestartServer(); const { preferences, updatePreference } = usePreferences(); const { setIsDirty, markClean } = useUnsavedChanges(); + const languageOptions = useMemo( + () => Object.entries(supportedLanguages) + .map(([code, label]) => ({ value: toUnderscoreFormat(code), label: `${label} (${code})` })) + .sort((a, b) => a.label.localeCompare(b.label)), + [] + ); // Track original settings for dirty detection const [originalSettingsSnapshot, setOriginalSettingsSnapshot] = useState(''); @@ -73,9 +81,11 @@ export default function AdminGeneralSection() { apiClient.get('/api/v1/admin/settings/section/premium') ]); - const ui = uiResponse.data || {}; - const system = systemResponse.data || {}; - const premium = premiumResponse.data || {}; + const ui = { ...(uiResponse.data || {}) }; + const system = { ...(systemResponse.data || {}) }; + const premium = { ...(premiumResponse.data || {}) }; + + ui.languages = Array.isArray(ui.languages) ? toUnderscoreLanguages(ui.languages) : []; const result: any = { ui, @@ -152,6 +162,21 @@ export default function AdminGeneralSection() { } }); + const selectedLanguages = useMemo( + () => toUnderscoreLanguages(settings.ui?.languages || []), + [settings.ui?.languages] + ); + + // Filter default locale options based on available languages setting + const defaultLocaleOptions = useMemo(() => { + // If no languages are selected (empty), show all languages + if (!selectedLanguages || selectedLanguages.length === 0) { + return languageOptions; + } + // Otherwise, only show languages that are in the selected list + return languageOptions.filter(option => selectedLanguages.includes(option.value)); + }, [selectedLanguages, languageOptions]); + useEffect(() => { // Only fetch real settings if login is enabled if (loginEnabled) { @@ -369,30 +394,19 @@ export default function AdminGeneralSection() { } description={t('admin.settings.general.languages.description', 'Limit which languages are available (empty = all languages)')} - value={settings.ui?.languages || []} + value={selectedLanguages} onChange={(value) => setSettings({ ...settings, ui: { ...settings.ui, languages: value } })} - data={[ - { value: 'de_DE', label: 'Deutsch' }, - { value: 'es_ES', label: 'EspaƱol' }, - { value: 'fr_FR', label: 'FranƧais' }, - { value: 'it_IT', label: 'Italiano' }, - { value: 'pl_PL', label: 'Polski' }, - { value: 'pt_BR', label: 'PortuguĆŖs (Brasil)' }, - { value: 'ru_RU', label: 'Русский' }, - { value: 'zh_CN', label: '简体中文' }, - { value: 'ja_JP', label: 'ę—„ęœ¬čŖž' }, - { value: 'ko_KR', label: 'ķ•œźµ­ģ–“' }, - ]} + data={languageOptions} searchable clearable placeholder={t('admin.settings.general.languages.placeholder', 'Select languages')} - comboboxProps={{ zIndex: 1400 }} + comboboxProps={{ zIndex: Z_INDEX_CONFIG_MODAL }} disabled={!loginEnabled} />
- {t('admin.settings.general.defaultLocale.label', 'Default Locale')} @@ -400,9 +414,13 @@ export default function AdminGeneralSection() { } description={t('admin.settings.general.defaultLocale.description', 'The default language for new users (e.g., en_US, es_ES)')} - value={ settings.system?.defaultLocale || ''} - onChange={(e) => setSettings({ ...settings, system: { ...settings.system, defaultLocale: e.target.value } })} - placeholder="en_US" + value={settings.system?.defaultLocale || ''} + onChange={(value) => setSettings({ ...settings, system: { ...settings.system, defaultLocale: value || '' } })} + data={defaultLocaleOptions} + searchable + clearable + placeholder="en_GB" + comboboxProps={{ zIndex: Z_INDEX_CONFIG_MODAL }} disabled={!loginEnabled} />
diff --git a/frontend/src/proprietary/components/shared/config/configSections/AdminSecuritySection.tsx b/frontend/src/proprietary/components/shared/config/configSections/AdminSecuritySection.tsx index 8934f4c457..19721b7600 100644 --- a/frontend/src/proprietary/components/shared/config/configSections/AdminSecuritySection.tsx +++ b/frontend/src/proprietary/components/shared/config/configSections/AdminSecuritySection.tsx @@ -13,7 +13,6 @@ import LoginRequiredBanner from '@app/components/shared/config/LoginRequiredBann interface SecuritySettingsData { enableLogin?: boolean; - csrfDisabled?: boolean; loginMethod?: string; loginAttemptCount?: number; loginResetTimeMinutes?: number; @@ -123,7 +122,6 @@ export default function AdminSecuritySection() { const deltaSettings: Record = { // Security settings 'security.enableLogin': securitySettings.enableLogin, - 'security.csrfDisabled': securitySettings.csrfDisabled, 'security.loginMethod': securitySettings.loginMethod, 'security.loginAttemptCount': securitySettings.loginAttemptCount, 'security.loginResetTimeMinutes': securitySettings.loginResetTimeMinutes, @@ -282,23 +280,6 @@ export default function AdminSecuritySection() { disabled={!loginEnabled} /> - -
-
- {t('admin.settings.security.csrfDisabled.label', 'Disable CSRF Protection')} - - {t('admin.settings.security.csrfDisabled.description', 'Disable Cross-Site Request Forgery protection (not recommended)')} - -
- - setSettings({ ...settings, csrfDisabled: e.target.checked })} - disabled={!loginEnabled} - /> - - -
diff --git a/frontend/src/proprietary/components/shared/config/configSections/PeopleSection.tsx b/frontend/src/proprietary/components/shared/config/configSections/PeopleSection.tsx index 9c4f56ba07..a43ac3078e 100644 --- a/frontend/src/proprietary/components/shared/config/configSections/PeopleSection.tsx +++ b/frontend/src/proprietary/components/shared/config/configSections/PeopleSection.tsx @@ -30,6 +30,7 @@ import LoginRequiredBanner from '@app/components/shared/config/LoginRequiredBann import { useNavigate } from 'react-router-dom'; import UpdateSeatsButton from '@app/components/shared/UpdateSeatsButton'; import { useLicense } from '@app/contexts/LicenseContext'; +import ChangeUserPasswordModal from '@app/components/shared/ChangeUserPasswordModal'; export default function PeopleSection() { const { t } = useTranslation(); @@ -43,8 +44,11 @@ export default function PeopleSection() { const [searchQuery, setSearchQuery] = useState(''); const [inviteModalOpened, setInviteModalOpened] = useState(false); const [editUserModalOpened, setEditUserModalOpened] = useState(false); + const [changePasswordModalOpened, setChangePasswordModalOpened] = useState(false); + const [passwordUser, setPasswordUser] = useState(null); const [selectedUser, setSelectedUser] = useState(null); const [processing, setProcessing] = useState(false); + const [mailEnabled, setMailEnabled] = useState(false); // License information const [licenseInfo, setLicenseInfo] = useState<{ @@ -119,6 +123,7 @@ export default function PeopleSection() { premiumEnabled: adminData.premiumEnabled, totalUsers: adminData.totalUsers, }); + setMailEnabled(adminData.mailEnabled); } else { // Provide example data when login is disabled const exampleUsers: User[] = [ @@ -179,6 +184,7 @@ export default function PeopleSection() { setUsers(exampleUsers); setTeams(exampleTeams); + setMailEnabled(false); // Example license information setLicenseInfo({ @@ -267,6 +273,16 @@ export default function PeopleSection() { setEditUserModalOpened(true); }; + const openChangePasswordModal = (user: User) => { + setPasswordUser(user); + setChangePasswordModalOpened(true); + }; + + const closeChangePasswordModal = () => { + setChangePasswordModalOpened(false); + setPasswordUser(null); + }; + const closeEditModal = () => { setEditUserModalOpened(false); setSelectedUser(null); @@ -514,7 +530,6 @@ export default function PeopleSection() { ) : ( - — + — )} @@ -549,7 +564,7 @@ export default function PeopleSection() { withArrow zIndex={Z_INDEX_OVER_CONFIG_MODAL + 10} > - + @@ -557,12 +572,25 @@ export default function PeopleSection() { {/* Actions menu */} - + - openEditModal(user)} disabled={!loginEnabled}>{t('workspace.people.editRole')} + } + onClick={() => openEditModal(user)} + disabled={!loginEnabled} + > + {t('workspace.people.editRole')} + + } + onClick={() => openChangePasswordModal(user)} + disabled={!loginEnabled} + > + {t('workspace.people.changePassword.action', 'Change password')} + : } onClick={() => handleToggleEnabled(user)} @@ -591,6 +619,14 @@ export default function PeopleSection() { onSuccess={fetchData} /> + + {/* Edit User Modal */} >({}); const [addMemberModalOpened, setAddMemberModalOpened] = useState(false); const [changeTeamModalOpened, setChangeTeamModalOpened] = useState(false); + const [changePasswordModalOpened, setChangePasswordModalOpened] = useState(false); + const [passwordUser, setPasswordUser] = useState(null); const [selectedUser, setSelectedUser] = useState(null); const [selectedUserId, setSelectedUserId] = useState(''); const [selectedTeamId, setSelectedTeamId] = useState(''); @@ -47,6 +50,7 @@ export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectio const [licenseInfo, setLicenseInfo] = useState<{ availableSlots: number; } | null>(null); + const [mailEnabled, setMailEnabled] = useState(false); useEffect(() => { fetchTeamDetails(); @@ -70,6 +74,7 @@ export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectio setLicenseInfo({ availableSlots: adminData.availableSlots, }); + setMailEnabled(adminData.mailEnabled); } catch (error) { console.error('Failed to fetch team details:', error); alert({ alertType: 'error', title: t('workspace.teams.loadError', 'Failed to load team details') }); @@ -172,6 +177,16 @@ export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectio setChangeTeamModalOpened(true); }; + const openChangePasswordModal = (user: User) => { + setPasswordUser(user); + setChangePasswordModalOpened(true); + }; + + const closeChangePasswordModal = () => { + setChangePasswordModalOpened(false); + setPasswordUser(null); + }; + const handleChangeTeam = async () => { if (!selectedUser || !selectedTeamId) { alert({ alertType: 'error', title: t('workspace.teams.changeTeam.selectTeamRequired', 'Please select a team') }); @@ -398,6 +413,13 @@ export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectio > {t('workspace.teams.changeTeam.label', 'Change Team')} + } + onClick={() => openChangePasswordModal(user)} + disabled={processing} + > + {t('workspace.people.changePassword.action', 'Change password')} + {team.name !== 'Internal' && team.name !== 'Default' && ( } @@ -427,6 +449,14 @@ export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectio + + {/* Add Member Modal */} = ({ planGroup, isCurrentTier, isDowngra radius="md" withBorder style={getBaseCardStyle(isCurrentTier)} + className="plan-card" > {isCurrentTier && ( = ({ planGroup, isCurrentTier, isDowngra
-