mirror of https://github.com/halo-dev/halo
				
				
				
			feat: add i18n tooling scripts and refine translations (#7269)
#### What type of PR is this? /area ui /kind feature /milestone 2.20.x #### What this PR does / why we need it: Added three Node.js scripts to help manage YAML translation files: 1. `find_missing_translations.mjs`: Identifies missing translations by comparing language files with the English base file and generates _missing_translations_*.yaml files. 2. `apply_missing_translations.mjs`: Merges translated entries from missing translation files into the main language files, while preserving untranslated entries for future work. 3. `fix_translations.mjs`: Removes keys that exist in language files but not in the English base file. Usage Example: ``` # Find missing translations node scripts/find_missing_translations.mjs # Apply only the translated entries node scripts/apply_missing_translations.mjs # Remove extra keys node scripts/fix_translations.mjs ``` These scripts streamline the translation workflow and help maintain consistency across language files. #### Which issue(s) this PR fixes: None #### Special notes for your reviewer: #### Does this PR introduce a user-facing change? ```release-note None ```pull/7272/head
							parent
							
								
									1d8a25cd69
								
							
						
					
					
						commit
						62f479253e
					
				| 
						 | 
				
			
			@ -119,9 +119,9 @@
 | 
			
		|||
    "@types/qs": "^6.9.7",
 | 
			
		||||
    "@types/randomstring": "^1.1.8",
 | 
			
		||||
    "@types/ua-parser-js": "^0.7.39",
 | 
			
		||||
    "@vitejs/plugin-legacy": "^6.0.0",
 | 
			
		||||
    "@vitejs/plugin-vue": "^5.2.1",
 | 
			
		||||
    "@vitejs/plugin-vue-jsx": "^4.1.1",
 | 
			
		||||
    "@vitejs/plugin-legacy": "^6.0.0",
 | 
			
		||||
    "@vitest/ui": "^0.34.1",
 | 
			
		||||
    "@vue/compiler-sfc": "^3.5.13",
 | 
			
		||||
    "@vue/eslint-config-prettier": "^7.1.0",
 | 
			
		||||
| 
						 | 
				
			
			@ -133,6 +133,7 @@
 | 
			
		|||
    "eslint": "^8.43.0",
 | 
			
		||||
    "eslint-plugin-vue": "^9.17.0",
 | 
			
		||||
    "husky": "^8.0.3",
 | 
			
		||||
    "js-yaml": "^4.1.0",
 | 
			
		||||
    "jsdom": "^20.0.3",
 | 
			
		||||
    "lint-staged": "^13.2.2",
 | 
			
		||||
    "npm-run-all": "^4.1.5",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -294,6 +294,9 @@ importers:
 | 
			
		|||
      husky:
 | 
			
		||||
        specifier: ^8.0.3
 | 
			
		||||
        version: 8.0.3
 | 
			
		||||
      js-yaml:
 | 
			
		||||
        specifier: ^4.1.0
 | 
			
		||||
        version: 4.1.0
 | 
			
		||||
      jsdom:
 | 
			
		||||
        specifier: ^20.0.3
 | 
			
		||||
        version: 20.0.3
 | 
			
		||||
| 
						 | 
				
			
			@ -10284,8 +10287,8 @@ packages:
 | 
			
		|||
  vue-component-type-helpers@2.0.19:
 | 
			
		||||
    resolution: {integrity: sha512-cN3f1aTxxKo4lzNeQAkVopswuImUrb5Iurll9Gaw5cqpnbTAxtEMM1mgi6ou4X79OCyqYv1U1mzBHJkzmiK82w==}
 | 
			
		||||
 | 
			
		||||
  vue-component-type-helpers@2.2.0:
 | 
			
		||||
    resolution: {integrity: sha512-cYrAnv2me7bPDcg9kIcGwjJiSB6Qyi08+jLDo9yuvoFQjzHiPTzML7RnkJB1+3P6KMsX/KbCD4QE3Tv/knEllw==}
 | 
			
		||||
  vue-component-type-helpers@2.2.8:
 | 
			
		||||
    resolution: {integrity: sha512-4bjIsC284coDO9om4HPA62M7wfsTvcmZyzdfR0aUlFXqq4tXxM1APyXpNVxPC8QazKw9OhmZNHBVDA6ODaZsrA==}
 | 
			
		||||
 | 
			
		||||
  vue-demi@0.13.11:
 | 
			
		||||
    resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==}
 | 
			
		||||
| 
						 | 
				
			
			@ -15056,7 +15059,7 @@ snapshots:
 | 
			
		|||
      ts-dedent: 2.2.0
 | 
			
		||||
      type-fest: 2.19.0
 | 
			
		||||
      vue: 3.5.13(typescript@5.6.2)
 | 
			
		||||
      vue-component-type-helpers: 2.2.0
 | 
			
		||||
      vue-component-type-helpers: 2.2.8
 | 
			
		||||
    transitivePeerDependencies:
 | 
			
		||||
      - encoding
 | 
			
		||||
      - supports-color
 | 
			
		||||
| 
						 | 
				
			
			@ -22218,7 +22221,7 @@ snapshots:
 | 
			
		|||
 | 
			
		||||
  vue-component-type-helpers@2.0.19: {}
 | 
			
		||||
 | 
			
		||||
  vue-component-type-helpers@2.2.0: {}
 | 
			
		||||
  vue-component-type-helpers@2.2.8: {}
 | 
			
		||||
 | 
			
		||||
  vue-demi@0.13.11(vue@3.5.13(typescript@5.6.2)):
 | 
			
		||||
    dependencies:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,240 @@
 | 
			
		|||
/**
 | 
			
		||||
 * Apply Missing Translations
 | 
			
		||||
 * -------------------------
 | 
			
		||||
 * This script applies translated entries from "_missing_translations_[lang].yaml" files
 | 
			
		||||
 * to their corresponding language files.
 | 
			
		||||
 *
 | 
			
		||||
 * For each missing translations file, it:
 | 
			
		||||
 * 1. Compares entries with the English base file
 | 
			
		||||
 * 2. Identifies which entries have been translated (values different from English)
 | 
			
		||||
 * 3. Merges only the translated entries into the main language file
 | 
			
		||||
 * 4. Updates the missing translations file to keep only untranslated entries
 | 
			
		||||
 *
 | 
			
		||||
 * Usage:
 | 
			
		||||
 * node scripts/apply_missing_translations.mjs
 | 
			
		||||
 *
 | 
			
		||||
 * Example output:
 | 
			
		||||
 * Processing: src/locales/_missing_translations_zh-TW.yaml for language: zh-TW
 | 
			
		||||
 * Updated src/locales/zh-TW.yaml with 15 translated entries.
 | 
			
		||||
 * Updated src/locales/_missing_translations_zh-TW.yaml with 10 remaining untranslated entries.
 | 
			
		||||
 *
 | 
			
		||||
 * This script is designed to be run repeatedly as you translate more entries in the
 | 
			
		||||
 * missing translations files. It will only apply entries that differ from the English version.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import { existsSync } from "fs";
 | 
			
		||||
import fs from "fs/promises";
 | 
			
		||||
import yaml from "js-yaml";
 | 
			
		||||
import path from "path";
 | 
			
		||||
import { fileURLToPath } from "url";
 | 
			
		||||
 | 
			
		||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
 | 
			
		||||
const translationsDirPath = path.resolve(__dirname, "../src/locales");
 | 
			
		||||
const baseFile = path.join(translationsDirPath, "en.yaml");
 | 
			
		||||
 | 
			
		||||
const VERBOSE = true;
 | 
			
		||||
 | 
			
		||||
async function main() {
 | 
			
		||||
  try {
 | 
			
		||||
    const dirEntries = await fs.readdir(translationsDirPath, {
 | 
			
		||||
      withFileTypes: true,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const missingFiles = dirEntries
 | 
			
		||||
      .filter(
 | 
			
		||||
        (entry) =>
 | 
			
		||||
          entry.isFile() &&
 | 
			
		||||
          entry.name.includes("_missing_translations_") &&
 | 
			
		||||
          entry.name.endsWith(".yaml")
 | 
			
		||||
      )
 | 
			
		||||
      .map((entry) => path.join(translationsDirPath, entry.name));
 | 
			
		||||
 | 
			
		||||
    if (missingFiles.length === 0) {
 | 
			
		||||
      console.log("No missing translation files found.");
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const enTranslations = await loadYamlFile(baseFile);
 | 
			
		||||
 | 
			
		||||
    for (const missingFile of missingFiles) {
 | 
			
		||||
      const fileName = path.basename(missingFile, ".yaml");
 | 
			
		||||
      const langCode = fileName.replace("_missing_translations_", "");
 | 
			
		||||
      const targetFile = path.join(translationsDirPath, `${langCode}.yaml`);
 | 
			
		||||
 | 
			
		||||
      console.log(`\nProcessing: ${missingFile} for language: ${langCode}`);
 | 
			
		||||
 | 
			
		||||
      if (!existsSync(targetFile)) {
 | 
			
		||||
        console.log(`Target translation file ${targetFile} does not exist`);
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        const missingTranslations = await loadYamlFile(missingFile);
 | 
			
		||||
        const currentTranslations = await loadYamlFile(targetFile);
 | 
			
		||||
 | 
			
		||||
        const translatedEntries = {};
 | 
			
		||||
        const untranslatedEntries = {};
 | 
			
		||||
        const stats = { added: 0, skipped: 0 };
 | 
			
		||||
 | 
			
		||||
        const keyPaths = collectKeyPaths(missingTranslations);
 | 
			
		||||
        console.log(
 | 
			
		||||
          `Found ${keyPaths.length} keys in missing translations file.`
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        for (const keyPath of keyPaths) {
 | 
			
		||||
          const missingValue = getValueByPath(missingTranslations, keyPath);
 | 
			
		||||
          const enValue = getValueByPath(enTranslations, keyPath);
 | 
			
		||||
 | 
			
		||||
          if (
 | 
			
		||||
            missingValue !== enValue &&
 | 
			
		||||
            missingValue !== null &&
 | 
			
		||||
            missingValue !== undefined
 | 
			
		||||
          ) {
 | 
			
		||||
            setValueByPath(translatedEntries, keyPath, missingValue);
 | 
			
		||||
            stats.added++;
 | 
			
		||||
            if (VERBOSE) {
 | 
			
		||||
              console.log(
 | 
			
		||||
                `✓ TRANSLATED: ${keyPath.join(
 | 
			
		||||
                  "."
 | 
			
		||||
                )} = "${missingValue}" (EN: "${enValue}")`
 | 
			
		||||
              );
 | 
			
		||||
            }
 | 
			
		||||
          } else {
 | 
			
		||||
            setValueByPath(untranslatedEntries, keyPath, missingValue);
 | 
			
		||||
            stats.skipped++;
 | 
			
		||||
            if (VERBOSE) {
 | 
			
		||||
              console.log(
 | 
			
		||||
                `✗ NOT TRANSLATED: ${keyPath.join(
 | 
			
		||||
                  "."
 | 
			
		||||
                )} = "${missingValue}" (same as EN: "${enValue}")`
 | 
			
		||||
              );
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (stats.added > 0) {
 | 
			
		||||
          const updatedTranslations = deepMerge(
 | 
			
		||||
            currentTranslations,
 | 
			
		||||
            translatedEntries
 | 
			
		||||
          );
 | 
			
		||||
 | 
			
		||||
          await saveYamlFile(updatedTranslations, targetFile);
 | 
			
		||||
          console.log(
 | 
			
		||||
            `Updated ${targetFile} with ${stats.added} translated entries.`
 | 
			
		||||
          );
 | 
			
		||||
 | 
			
		||||
          await saveYamlFile(untranslatedEntries, missingFile);
 | 
			
		||||
          console.log(
 | 
			
		||||
            `Updated ${missingFile} with ${stats.skipped} remaining untranslated entries.`
 | 
			
		||||
          );
 | 
			
		||||
        } else {
 | 
			
		||||
          console.log(
 | 
			
		||||
            `No translated entries found in ${missingFile}, files not updated.`
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        console.log(`\nSummary for ${langCode}:`);
 | 
			
		||||
        console.log(`- Added: ${stats.added} translated entries`);
 | 
			
		||||
        console.log(`- Remaining: ${stats.skipped} untranslated entries`);
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        console.error(`Error processing ${missingFile}:`, e);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    console.error(`Error:`, e);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function loadYamlFile(filePath) {
 | 
			
		||||
  try {
 | 
			
		||||
    const content = await fs.readFile(filePath, "utf8");
 | 
			
		||||
    return yaml.load(content) || {};
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    console.error(`Error loading file ${filePath}:`, error);
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function saveYamlFile(data, filePath) {
 | 
			
		||||
  try {
 | 
			
		||||
    const yamlContent = yaml.dump(data, {
 | 
			
		||||
      indent: 2,
 | 
			
		||||
      lineWidth: -1,
 | 
			
		||||
    });
 | 
			
		||||
    await fs.writeFile(filePath, yamlContent, "utf8");
 | 
			
		||||
    return true;
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    console.error(`Error saving file ${filePath}:`, error);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function collectKeyPaths(obj, currentPath = [], result = []) {
 | 
			
		||||
  if (obj === null || typeof obj !== "object") {
 | 
			
		||||
    return result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (const key of Object.keys(obj)) {
 | 
			
		||||
    const newPath = [...currentPath, key];
 | 
			
		||||
 | 
			
		||||
    if (obj[key] === null || typeof obj[key] !== "object") {
 | 
			
		||||
      result.push(newPath);
 | 
			
		||||
    } else {
 | 
			
		||||
      collectKeyPaths(obj[key], newPath, result);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getValueByPath(obj, path) {
 | 
			
		||||
  let current = obj;
 | 
			
		||||
 | 
			
		||||
  for (const key of path) {
 | 
			
		||||
    if (
 | 
			
		||||
      current === null ||
 | 
			
		||||
      current === undefined ||
 | 
			
		||||
      typeof current !== "object"
 | 
			
		||||
    ) {
 | 
			
		||||
      return undefined;
 | 
			
		||||
    }
 | 
			
		||||
    current = current[key];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return current;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function setValueByPath(obj, path, value) {
 | 
			
		||||
  let current = obj;
 | 
			
		||||
 | 
			
		||||
  for (let i = 0; i < path.length - 1; i++) {
 | 
			
		||||
    const key = path[i];
 | 
			
		||||
 | 
			
		||||
    if (!current[key] || typeof current[key] !== "object") {
 | 
			
		||||
      current[key] = {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    current = current[key];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const lastKey = path[path.length - 1];
 | 
			
		||||
  current[lastKey] = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function deepMerge(target, source) {
 | 
			
		||||
  const result = { ...target };
 | 
			
		||||
 | 
			
		||||
  for (const key of Object.keys(source)) {
 | 
			
		||||
    if (source[key] === null || typeof source[key] !== "object") {
 | 
			
		||||
      result[key] = source[key];
 | 
			
		||||
    } else if (target[key] === null || typeof target[key] !== "object") {
 | 
			
		||||
      result[key] = { ...source[key] };
 | 
			
		||||
    } else {
 | 
			
		||||
      result[key] = deepMerge(target[key], source[key]);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
main();
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,112 @@
 | 
			
		|||
/**
 | 
			
		||||
 * Find Missing Translations
 | 
			
		||||
 * -------------------------
 | 
			
		||||
 * This script identifies missing translations in language files by comparing them
 | 
			
		||||
 * with the English base file (en.yaml).
 | 
			
		||||
 *
 | 
			
		||||
 * For each language file, it:
 | 
			
		||||
 * 1. Compares it with the English base file
 | 
			
		||||
 * 2. Identifies keys that are missing in the target language
 | 
			
		||||
 * 3. Creates a "_missing_translations_[lang].yaml" file with those missing keys
 | 
			
		||||
 *
 | 
			
		||||
 * Usage:
 | 
			
		||||
 * node scripts/find_missing_translations.mjs
 | 
			
		||||
 *
 | 
			
		||||
 * Example output:
 | 
			
		||||
 * Generated src/locales/_missing_translations_zh-TW.yaml with 25 missing translations
 | 
			
		||||
 *
 | 
			
		||||
 * After running this script, you can translate the missing entries in the generated files,
 | 
			
		||||
 * then use apply_missing_translations.mjs to merge them into the main language files.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import fs from "fs/promises";
 | 
			
		||||
import yaml from "js-yaml";
 | 
			
		||||
import path from "path";
 | 
			
		||||
import { fileURLToPath } from "url";
 | 
			
		||||
 | 
			
		||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
 | 
			
		||||
const translationsDirPath = path.resolve(__dirname, "../src/locales");
 | 
			
		||||
const baseFile = `${translationsDirPath}/en.yaml`;
 | 
			
		||||
 | 
			
		||||
async function main() {
 | 
			
		||||
  try {
 | 
			
		||||
    const baseTranslations = await loadYamlFile(baseFile);
 | 
			
		||||
 | 
			
		||||
    const dirEntries = await fs.readdir(translationsDirPath, {
 | 
			
		||||
      withFileTypes: true,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const translationFiles = dirEntries
 | 
			
		||||
      .filter(
 | 
			
		||||
        (entry) =>
 | 
			
		||||
          entry.isFile() &&
 | 
			
		||||
          entry.name.endsWith(".yaml") &&
 | 
			
		||||
          entry.name !== "en.yaml" &&
 | 
			
		||||
          !entry.name.includes("_missing_translations_")
 | 
			
		||||
      )
 | 
			
		||||
      .map((entry) => path.join(translationsDirPath, entry.name));
 | 
			
		||||
 | 
			
		||||
    for (const transFile of translationFiles) {
 | 
			
		||||
      const langCode = path.basename(transFile, ".yaml");
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        const translations = await loadYamlFile(transFile);
 | 
			
		||||
        const missing = findMissingTranslations(baseTranslations, translations);
 | 
			
		||||
 | 
			
		||||
        if (Object.keys(missing).length > 0) {
 | 
			
		||||
          const missingFile = `${translationsDirPath}/_missing_translations_${langCode}.yaml`;
 | 
			
		||||
          await saveYamlFile(missing, missingFile);
 | 
			
		||||
          console.log(
 | 
			
		||||
            `Generated ${missingFile} with ${
 | 
			
		||||
              Object.keys(missing).length
 | 
			
		||||
            } missing translations`
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        console.log(`Error processing ${transFile}: ${e}`);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    console.log(`Error: ${e}`);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function loadYamlFile(filePath) {
 | 
			
		||||
  const content = await fs.readFile(filePath, "utf8");
 | 
			
		||||
  return yaml.load(content) || {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function saveYamlFile(data, filePath) {
 | 
			
		||||
  const yamlContent = yaml.dump(data, {
 | 
			
		||||
    indent: 2,
 | 
			
		||||
    lineWidth: -1,
 | 
			
		||||
  });
 | 
			
		||||
  await fs.writeFile(filePath, yamlContent, "utf8");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function findMissingTranslations(baseDict, compareDict) {
 | 
			
		||||
  const missing = {};
 | 
			
		||||
 | 
			
		||||
  for (const key of Object.keys(baseDict)) {
 | 
			
		||||
    if (!Object.prototype.hasOwnProperty.call(compareDict, key)) {
 | 
			
		||||
      missing[key] = baseDict[key];
 | 
			
		||||
    } else if (
 | 
			
		||||
      typeof baseDict[key] === "object" &&
 | 
			
		||||
      baseDict[key] !== null &&
 | 
			
		||||
      typeof compareDict[key] === "object" &&
 | 
			
		||||
      compareDict[key] !== null
 | 
			
		||||
    ) {
 | 
			
		||||
      const subMissing = findMissingTranslations(
 | 
			
		||||
        baseDict[key],
 | 
			
		||||
        compareDict[key]
 | 
			
		||||
      );
 | 
			
		||||
      if (Object.keys(subMissing).length > 0) {
 | 
			
		||||
        missing[key] = subMissing;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return missing;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
main();
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,117 @@
 | 
			
		|||
/**
 | 
			
		||||
 * Fix Translations
 | 
			
		||||
 * -------------------------
 | 
			
		||||
 * This script removes translation keys that exist in language files but are not
 | 
			
		||||
 * present in the English base file (en.yaml).
 | 
			
		||||
 *
 | 
			
		||||
 * For each language file, it:
 | 
			
		||||
 * 1. Compares it with the English base file
 | 
			
		||||
 * 2. Identifies keys that exist in the language file but not in the English file
 | 
			
		||||
 * 3. Removes these extra keys from the language file
 | 
			
		||||
 *
 | 
			
		||||
 * Usage:
 | 
			
		||||
 * node scripts/fix_translations.mjs
 | 
			
		||||
 *
 | 
			
		||||
 * Example output:
 | 
			
		||||
 * Extra key found: common.outdatedKey
 | 
			
		||||
 * Removed 5 extra keys from src/locales/zh-TW.yaml
 | 
			
		||||
 *
 | 
			
		||||
 * This script helps maintain consistency across language files by ensuring they
 | 
			
		||||
 * only contain keys that are present in the English base file.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import fs from "fs/promises";
 | 
			
		||||
import yaml from "js-yaml";
 | 
			
		||||
import path from "path";
 | 
			
		||||
import { fileURLToPath } from "url";
 | 
			
		||||
 | 
			
		||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
 | 
			
		||||
const translationsDirPath = path.resolve(__dirname, "../src/locales");
 | 
			
		||||
const baseFile = path.join(translationsDirPath, "en.yaml");
 | 
			
		||||
 | 
			
		||||
async function main() {
 | 
			
		||||
  try {
 | 
			
		||||
    const baseTranslations = await loadYamlFile(baseFile);
 | 
			
		||||
 | 
			
		||||
    const dirEntries = await fs.readdir(translationsDirPath, {
 | 
			
		||||
      withFileTypes: true,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const translationFiles = dirEntries
 | 
			
		||||
      .filter(
 | 
			
		||||
        (entry) =>
 | 
			
		||||
          entry.isFile() &&
 | 
			
		||||
          entry.name.endsWith(".yaml") &&
 | 
			
		||||
          entry.name !== "en.yaml" &&
 | 
			
		||||
          !entry.name.includes("_missing_translations_")
 | 
			
		||||
      )
 | 
			
		||||
      .map((entry) => path.join(translationsDirPath, entry.name));
 | 
			
		||||
 | 
			
		||||
    for (const transFile of translationFiles) {
 | 
			
		||||
      try {
 | 
			
		||||
        const translations = await loadYamlFile(transFile);
 | 
			
		||||
 | 
			
		||||
        const extraKeysCount = removeExtraTranslations(
 | 
			
		||||
          translations,
 | 
			
		||||
          baseTranslations
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if (extraKeysCount > 0) {
 | 
			
		||||
          await saveYamlFile(translations, transFile);
 | 
			
		||||
          console.log(`Removed ${extraKeysCount} extra keys from ${transFile}`);
 | 
			
		||||
        } else {
 | 
			
		||||
          console.log(`No extra keys found in ${transFile}`);
 | 
			
		||||
        }
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        console.log(`Error processing ${transFile}: ${e}`);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    console.log(`Error: ${e}`);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function loadYamlFile(filePath) {
 | 
			
		||||
  const content = await fs.readFile(filePath, "utf8");
 | 
			
		||||
  return yaml.load(content) || {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function saveYamlFile(data, filePath) {
 | 
			
		||||
  const yamlContent = yaml.dump(data, {
 | 
			
		||||
    indent: 2,
 | 
			
		||||
    lineWidth: -1,
 | 
			
		||||
  });
 | 
			
		||||
  await fs.writeFile(filePath, yamlContent, "utf8");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function removeExtraTranslations(translations, baseTranslations) {
 | 
			
		||||
  let extraKeysCount = 0;
 | 
			
		||||
 | 
			
		||||
  function cleanObject(obj, baseObj, path = "") {
 | 
			
		||||
    const keysToDelete = [];
 | 
			
		||||
 | 
			
		||||
    for (const key of Object.keys(obj)) {
 | 
			
		||||
      if (!Object.prototype.hasOwnProperty.call(baseObj, key)) {
 | 
			
		||||
        keysToDelete.push(key);
 | 
			
		||||
        extraKeysCount++;
 | 
			
		||||
        console.log(`Extra key found: ${path}${key}`);
 | 
			
		||||
      } else if (
 | 
			
		||||
        typeof obj[key] === "object" &&
 | 
			
		||||
        obj[key] !== null &&
 | 
			
		||||
        typeof baseObj[key] === "object" &&
 | 
			
		||||
        baseObj[key] !== null
 | 
			
		||||
      ) {
 | 
			
		||||
        cleanObject(obj[key], baseObj[key], `${path}${key}.`);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const key of keysToDelete) {
 | 
			
		||||
      delete obj[key];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  cleanObject(translations, baseTranslations);
 | 
			
		||||
  return extraKeysCount;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
main();
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,700 @@
 | 
			
		|||
core:
 | 
			
		||||
  sidebar:
 | 
			
		||||
    menu:
 | 
			
		||||
      items:
 | 
			
		||||
        tools: Tools
 | 
			
		||||
    operations:
 | 
			
		||||
      logout:
 | 
			
		||||
        tooltip: Logout
 | 
			
		||||
        description: Clicking Confirm will redirect to the logout page. Please ensure that the content you are editing is saved.
 | 
			
		||||
      profile:
 | 
			
		||||
        tooltip: Profile
 | 
			
		||||
    profile:
 | 
			
		||||
      aggregate_role: Aggregate Role
 | 
			
		||||
  uc_sidebar:
 | 
			
		||||
    menu:
 | 
			
		||||
      items:
 | 
			
		||||
        profile: Profile
 | 
			
		||||
        notification: Notifications
 | 
			
		||||
        posts: Posts
 | 
			
		||||
    operations:
 | 
			
		||||
      console:
 | 
			
		||||
        tooltip: Console
 | 
			
		||||
    profile:
 | 
			
		||||
      aggregate_role: Aggregate Role
 | 
			
		||||
  dashboard:
 | 
			
		||||
    widgets:
 | 
			
		||||
      presets:
 | 
			
		||||
        recent_published:
 | 
			
		||||
          publishTime: Publish Time {publishTime}
 | 
			
		||||
        notification:
 | 
			
		||||
          title: Notifications
 | 
			
		||||
          empty:
 | 
			
		||||
            title: No unread notifications
 | 
			
		||||
  post:
 | 
			
		||||
    operations:
 | 
			
		||||
      publish_in_batch:
 | 
			
		||||
        title: Publish posts
 | 
			
		||||
        description: Batch publish posts, the selected posts will be set to published status
 | 
			
		||||
      cancel_publish_in_batch:
 | 
			
		||||
        title: Cancel publish posts
 | 
			
		||||
        description: Batch cancel publish posts, the selected posts will be set to unpublished status
 | 
			
		||||
      batch_setting:
 | 
			
		||||
        button: Batch settings
 | 
			
		||||
    filters:
 | 
			
		||||
      status:
 | 
			
		||||
        items:
 | 
			
		||||
          scheduling: Scheduling publish
 | 
			
		||||
      sort:
 | 
			
		||||
        items:
 | 
			
		||||
          last_modify_time_desc: Recently Updated
 | 
			
		||||
          last_modify_time_asc: Earliest Updated
 | 
			
		||||
    list:
 | 
			
		||||
      fields:
 | 
			
		||||
        schedule_publish:
 | 
			
		||||
          tooltip: Schedule publish
 | 
			
		||||
    settings:
 | 
			
		||||
      fields:
 | 
			
		||||
        publish_time:
 | 
			
		||||
          help:
 | 
			
		||||
            schedule_publish: Schedule a timed task and publish it at {datetime}
 | 
			
		||||
        owner:
 | 
			
		||||
          label: Owner
 | 
			
		||||
    tag:
 | 
			
		||||
      filters:
 | 
			
		||||
        sort:
 | 
			
		||||
          items:
 | 
			
		||||
            create_time_desc: Latest Created
 | 
			
		||||
            create_time_asc: Earliest Created
 | 
			
		||||
            display_name_desc: Descending order by tag name
 | 
			
		||||
            display_name_asc: Ascending order by tag name
 | 
			
		||||
    batch_setting_modal:
 | 
			
		||||
      title: Post batch settings
 | 
			
		||||
      fields:
 | 
			
		||||
        common:
 | 
			
		||||
          enabled: Enabled
 | 
			
		||||
          op:
 | 
			
		||||
            label: Operate
 | 
			
		||||
            options:
 | 
			
		||||
              add: Add
 | 
			
		||||
              replace: Replace
 | 
			
		||||
              remove_all: Remove all
 | 
			
		||||
        category_group: Category
 | 
			
		||||
        category_names: Select categories
 | 
			
		||||
        tag_group: Tag
 | 
			
		||||
        tag_names: Select tags
 | 
			
		||||
        visible_group: Visible
 | 
			
		||||
        visible_value: "Select visible option "
 | 
			
		||||
        allow_comment_group: " Allow comment"
 | 
			
		||||
        allow_comment_value: Choose whether to allow comments
 | 
			
		||||
        owner_group: Owner
 | 
			
		||||
        owner_value: Select owner
 | 
			
		||||
  post_editor:
 | 
			
		||||
    actions:
 | 
			
		||||
      snapshots: Snapshots
 | 
			
		||||
  post_tag:
 | 
			
		||||
    operations:
 | 
			
		||||
      delete_in_batch:
 | 
			
		||||
        title: Delete the selected tags
 | 
			
		||||
  post_category:
 | 
			
		||||
    editing_modal:
 | 
			
		||||
      fields:
 | 
			
		||||
        template:
 | 
			
		||||
          help: Customize the rendering template of the category archive page, which requires support from the theme
 | 
			
		||||
        post_template:
 | 
			
		||||
          label: Custom post template
 | 
			
		||||
          help: Customize the rendering template of posts in the current category, which requires support from the theme
 | 
			
		||||
        hide_from_list:
 | 
			
		||||
          label: Hide from list
 | 
			
		||||
          help: After turning on this option, this category and its subcategories, as well as its posts, will not be displayed in the front-end list. You need to actively visit the category archive page. This feature is only effective for the first-level directory.
 | 
			
		||||
    list:
 | 
			
		||||
      fields:
 | 
			
		||||
        prevent_parent_post_cascade_query: Prevent parent category from including this category and its subcategories in cascade post queries
 | 
			
		||||
        hide_from_list: This category is hidden, This category and its subcategories, as well as its posts, will not be displayed in the front-end list. You need to actively visit the category archive page
 | 
			
		||||
  page_editor:
 | 
			
		||||
    actions:
 | 
			
		||||
      snapshots: Snapshots
 | 
			
		||||
  attachment:
 | 
			
		||||
    filters:
 | 
			
		||||
      sort:
 | 
			
		||||
        items:
 | 
			
		||||
          display_name_asc: Ascending order by display name
 | 
			
		||||
          display_name_desc: Descending order by display name
 | 
			
		||||
    group_editing_modal:
 | 
			
		||||
      toast:
 | 
			
		||||
        group_name_exists: Group name already exists
 | 
			
		||||
    policy_editing_modal:
 | 
			
		||||
      toast:
 | 
			
		||||
        policy_name_exists: Storage policy name already exists
 | 
			
		||||
  uc_attachment:
 | 
			
		||||
    empty:
 | 
			
		||||
      title: There are no attachments.
 | 
			
		||||
      message: There are no attachments, you can try refreshing or uploading attachments.
 | 
			
		||||
      actions:
 | 
			
		||||
        upload: Upload Attachment
 | 
			
		||||
    filters:
 | 
			
		||||
      sort:
 | 
			
		||||
        items:
 | 
			
		||||
          create_time_desc: Latest uploaded
 | 
			
		||||
          create_time_asc: Earliest uploaded
 | 
			
		||||
          size_desc: Descending order by file size
 | 
			
		||||
          size_asc: Ascending order by file size
 | 
			
		||||
          display_name_asc: Ascending order by display name
 | 
			
		||||
          display_name_desc: Descending order by display name
 | 
			
		||||
      view_type:
 | 
			
		||||
        items:
 | 
			
		||||
          grid: Grid Mode
 | 
			
		||||
          list: List Mode
 | 
			
		||||
    detail_modal:
 | 
			
		||||
      title: "Attachment: {display_name}"
 | 
			
		||||
      fields:
 | 
			
		||||
        preview: Preview
 | 
			
		||||
        display_name: Display name
 | 
			
		||||
        media_type: Media type
 | 
			
		||||
        size: Size
 | 
			
		||||
        owner: Owner
 | 
			
		||||
        creation_time: Creation time
 | 
			
		||||
        permalink: Permalink
 | 
			
		||||
      preview:
 | 
			
		||||
        click_to_exit: Click to exit preview
 | 
			
		||||
        video_not_support: The current browser does not support video playback.
 | 
			
		||||
        audio_not_support: The current browser does not support audio playback.
 | 
			
		||||
        not_support: This file does not support preview.
 | 
			
		||||
    upload_modal:
 | 
			
		||||
      title: Upload attachment
 | 
			
		||||
    select_modal:
 | 
			
		||||
      title: Select attachment
 | 
			
		||||
      providers:
 | 
			
		||||
        default:
 | 
			
		||||
          label: Attachments
 | 
			
		||||
      operations:
 | 
			
		||||
        select:
 | 
			
		||||
          result: ({count} items selected)
 | 
			
		||||
  theme:
 | 
			
		||||
    operations:
 | 
			
		||||
      existed_during_installation:
 | 
			
		||||
        title: The theme already exists.
 | 
			
		||||
        description: The currently installed theme already exists, do you want to upgrade?
 | 
			
		||||
      clear_templates_cache:
 | 
			
		||||
        button: Clear templates cache
 | 
			
		||||
        title: Clear templates cache
 | 
			
		||||
        description: This feature allows you to refresh the cache to view the latest web results after modifying template files at runtime.
 | 
			
		||||
      export_configuration:
 | 
			
		||||
        button: Export theme configuration
 | 
			
		||||
      import_configuration:
 | 
			
		||||
        button: Import theme configuration
 | 
			
		||||
        version_mismatch:
 | 
			
		||||
          title: Version mismatch
 | 
			
		||||
          description: The imported configuration file version does not match the  current theme version, which may lead to compatibility issues. Do you want to continue importing?
 | 
			
		||||
        invalid_format: Invalid theme configuration file
 | 
			
		||||
        mismatched_theme: Configuration file does not match the selected theme
 | 
			
		||||
    list_modal:
 | 
			
		||||
      tabs:
 | 
			
		||||
        local_upload: Local install / upgrade
 | 
			
		||||
        remote_download:
 | 
			
		||||
          label: Remote
 | 
			
		||||
          fields:
 | 
			
		||||
            url: Remote URL
 | 
			
		||||
    detail:
 | 
			
		||||
      fields:
 | 
			
		||||
        homepage: Website
 | 
			
		||||
        description: Description
 | 
			
		||||
        license: License
 | 
			
		||||
        issues: Issues feedback
 | 
			
		||||
  plugin:
 | 
			
		||||
    operations:
 | 
			
		||||
      uninstall_in_batch:
 | 
			
		||||
        title: Uninstall the selected plugins
 | 
			
		||||
      uninstall_and_delete_config:
 | 
			
		||||
        button: Uninstall and delete config
 | 
			
		||||
      uninstall_and_delete_config_in_batch:
 | 
			
		||||
        button: Uninstall and delete config
 | 
			
		||||
        title: Uninstall the selected plugins and its corresponding configuration
 | 
			
		||||
      change_status_in_batch:
 | 
			
		||||
        activate_title: Activate the selected plugins
 | 
			
		||||
        inactivate_title: Inactivate the selected plugins
 | 
			
		||||
      reload_window:
 | 
			
		||||
        button: Reload required
 | 
			
		||||
    detail:
 | 
			
		||||
      fields:
 | 
			
		||||
        homepage: Homepage
 | 
			
		||||
        repo: Source Repository
 | 
			
		||||
        load_location: Storage Location
 | 
			
		||||
        issues: Issues feedback
 | 
			
		||||
      operations:
 | 
			
		||||
        view_conditions:
 | 
			
		||||
          button: View recent conditions
 | 
			
		||||
    conditions_modal:
 | 
			
		||||
      title: Recent conditions
 | 
			
		||||
      fields:
 | 
			
		||||
        type: Type
 | 
			
		||||
        status: Status
 | 
			
		||||
        reason: Reason
 | 
			
		||||
        message: Message
 | 
			
		||||
        last_transition_time: Last transition time
 | 
			
		||||
    extension-settings:
 | 
			
		||||
      title: Extension settings
 | 
			
		||||
      extension-point-definition:
 | 
			
		||||
        title: Extension point definitions
 | 
			
		||||
      extension-definition:
 | 
			
		||||
        empty:
 | 
			
		||||
          title: There is currently no extension point implemented
 | 
			
		||||
    actions:
 | 
			
		||||
      extension-point-settings: Extension settings
 | 
			
		||||
  user:
 | 
			
		||||
    grant_permission_modal:
 | 
			
		||||
      roles_preview:
 | 
			
		||||
        all: The currently selected role contains all permissions
 | 
			
		||||
        includes: "The currently selected role contains the following permissions:"
 | 
			
		||||
    detail:
 | 
			
		||||
      actions:
 | 
			
		||||
        grant_permission:
 | 
			
		||||
          title: Grant permission
 | 
			
		||||
        profile:
 | 
			
		||||
          title: Profile
 | 
			
		||||
      fields:
 | 
			
		||||
        email_verified:
 | 
			
		||||
          tooltip: Verified
 | 
			
		||||
        email_not_verified:
 | 
			
		||||
          tooltip: Not verified
 | 
			
		||||
  role:
 | 
			
		||||
    editing_modal:
 | 
			
		||||
      fields:
 | 
			
		||||
        disallow_access_console:
 | 
			
		||||
          label: Disable access to Console
 | 
			
		||||
          help: Once checked, this role will not be able to access the Console
 | 
			
		||||
  identity_authentication:
 | 
			
		||||
    fields:
 | 
			
		||||
      display_name:
 | 
			
		||||
        local: Login with credentials
 | 
			
		||||
      description:
 | 
			
		||||
        local: Default login method built into Halo
 | 
			
		||||
    list:
 | 
			
		||||
      types:
 | 
			
		||||
        form: Basic Authentication Method
 | 
			
		||||
        oauth2: Third-party Authentication Method
 | 
			
		||||
  uc_profile:
 | 
			
		||||
    title: Profile
 | 
			
		||||
    tabs:
 | 
			
		||||
      detail: Detail
 | 
			
		||||
      notification-preferences: Notification Preferences
 | 
			
		||||
      pat: Personal Access Tokens
 | 
			
		||||
      2fa: 2FA
 | 
			
		||||
      devices: Devices
 | 
			
		||||
    actions:
 | 
			
		||||
      update_profile:
 | 
			
		||||
        title: Update profile
 | 
			
		||||
      change_password:
 | 
			
		||||
        title: Change password
 | 
			
		||||
    detail:
 | 
			
		||||
      fields:
 | 
			
		||||
        display_name: Display name
 | 
			
		||||
        username: Username
 | 
			
		||||
        email: Email
 | 
			
		||||
        roles: Roles
 | 
			
		||||
        bio: Bio
 | 
			
		||||
        creation_time: Creation time
 | 
			
		||||
        identity_authentication: Identity authentication
 | 
			
		||||
      operations:
 | 
			
		||||
        bind:
 | 
			
		||||
          button: Bind
 | 
			
		||||
        unbind:
 | 
			
		||||
          button: Unbind
 | 
			
		||||
          title: Unbind the login method for {display_name}
 | 
			
		||||
      email_not_set:
 | 
			
		||||
        description: Your email address has not been set yet. Click the button below to set it up.
 | 
			
		||||
        title: Set up email
 | 
			
		||||
      email_not_verified:
 | 
			
		||||
        description: Your email address has not been verified yet, click the button below to verify it
 | 
			
		||||
        title: Verify email
 | 
			
		||||
      email_verified:
 | 
			
		||||
        tooltip: Verified
 | 
			
		||||
    2fa:
 | 
			
		||||
      operations:
 | 
			
		||||
        enable:
 | 
			
		||||
          button: Enable 2FA
 | 
			
		||||
          title: Enable 2FA
 | 
			
		||||
        disable:
 | 
			
		||||
          title: Disable 2FA
 | 
			
		||||
        disable_totp:
 | 
			
		||||
          title: Disable TOTP
 | 
			
		||||
      password_validation_form:
 | 
			
		||||
        fields:
 | 
			
		||||
          password:
 | 
			
		||||
            label: Password
 | 
			
		||||
            help: Login password of the current account
 | 
			
		||||
      methods:
 | 
			
		||||
        title: Two-factor methods
 | 
			
		||||
        totp:
 | 
			
		||||
          title: TOTP
 | 
			
		||||
          description: Configure two-step verification with TOTP application
 | 
			
		||||
          fields:
 | 
			
		||||
            status:
 | 
			
		||||
              configured: Configured
 | 
			
		||||
              not_configured: Not configured
 | 
			
		||||
          operations:
 | 
			
		||||
            reconfigure:
 | 
			
		||||
              button: Reconfigure
 | 
			
		||||
            configure:
 | 
			
		||||
              button: Configure
 | 
			
		||||
              title: TOTP configuration
 | 
			
		||||
              fields:
 | 
			
		||||
                code:
 | 
			
		||||
                  label: Verification code
 | 
			
		||||
                  help: 6-digit verification code obtained from the validator application
 | 
			
		||||
                password:
 | 
			
		||||
                  label: Password
 | 
			
		||||
                  help: Login password of the current account
 | 
			
		||||
                qrcode:
 | 
			
		||||
                  label: "Use the validator application to scan the QR code below:"
 | 
			
		||||
                manual:
 | 
			
		||||
                  label: If you can't scan the QR code, click to view the alternative steps.
 | 
			
		||||
                  help: "Manually configure the validator application with the following code:"
 | 
			
		||||
    pat:
 | 
			
		||||
      operations:
 | 
			
		||||
        delete:
 | 
			
		||||
          title: Delete Personal Access Token
 | 
			
		||||
          description: Are you sure you want to delete this personal access token?
 | 
			
		||||
        revoke:
 | 
			
		||||
          button: Revoke
 | 
			
		||||
          title: Revoke Personal Access Token
 | 
			
		||||
          description: Are you sure you want to revoke this personal access token?
 | 
			
		||||
          toast_success: Revocation succeeded
 | 
			
		||||
        copy:
 | 
			
		||||
          title: Please copy and save immediately, Token will only be displayed once.
 | 
			
		||||
        restore:
 | 
			
		||||
          button: Restore
 | 
			
		||||
          toast_success: Restore successfully
 | 
			
		||||
      list:
 | 
			
		||||
        empty:
 | 
			
		||||
          title: No personal access tokens have been created
 | 
			
		||||
          message: You can try refreshing or creating a new personal access token
 | 
			
		||||
        fields:
 | 
			
		||||
          expiresAt:
 | 
			
		||||
            dynamic: Expires on {expiresAt}
 | 
			
		||||
            forever: Never expires
 | 
			
		||||
          status:
 | 
			
		||||
            normal: Normal
 | 
			
		||||
            revoked: Revoked
 | 
			
		||||
            expired: Expired
 | 
			
		||||
      creation_modal:
 | 
			
		||||
        title: Create Personal Access Token
 | 
			
		||||
        groups:
 | 
			
		||||
          general: General
 | 
			
		||||
          permissions: Permissions
 | 
			
		||||
        fields:
 | 
			
		||||
          name:
 | 
			
		||||
            label: Name
 | 
			
		||||
          expiresAt:
 | 
			
		||||
            label: Expiration Time
 | 
			
		||||
            help: Leave empty for no expiration
 | 
			
		||||
          description:
 | 
			
		||||
            label: Description
 | 
			
		||||
    notification-preferences:
 | 
			
		||||
      fields:
 | 
			
		||||
        type: Type
 | 
			
		||||
    editing_modal:
 | 
			
		||||
      title: Edit Profile
 | 
			
		||||
      groups:
 | 
			
		||||
        general: General
 | 
			
		||||
        annotations: Annotations
 | 
			
		||||
      fields:
 | 
			
		||||
        username:
 | 
			
		||||
          label: Username
 | 
			
		||||
          validation: Can only contain numbers, lowercase letters, periods (.), and hyphens (-), and cannot start or end with a period (.) or hyphen (-).
 | 
			
		||||
        display_name:
 | 
			
		||||
          label: Display name
 | 
			
		||||
        email:
 | 
			
		||||
          label: Email
 | 
			
		||||
        phone:
 | 
			
		||||
          label: Phone
 | 
			
		||||
        avatar:
 | 
			
		||||
          label: Avatar
 | 
			
		||||
        bio:
 | 
			
		||||
          label: Bio
 | 
			
		||||
    change_password_modal:
 | 
			
		||||
      title: Change password
 | 
			
		||||
      fields:
 | 
			
		||||
        new_password:
 | 
			
		||||
          label: New password
 | 
			
		||||
        confirm_password:
 | 
			
		||||
          label: Confirm password
 | 
			
		||||
        old_password:
 | 
			
		||||
          label: Old password
 | 
			
		||||
    email_verify_modal:
 | 
			
		||||
      fields:
 | 
			
		||||
        code:
 | 
			
		||||
          label: Verification code
 | 
			
		||||
        email:
 | 
			
		||||
          label: Email address
 | 
			
		||||
        new_email:
 | 
			
		||||
          label: New email address
 | 
			
		||||
        password:
 | 
			
		||||
          label: Password
 | 
			
		||||
          help: The login password for the current account
 | 
			
		||||
      operations:
 | 
			
		||||
        send_code:
 | 
			
		||||
          buttons:
 | 
			
		||||
            countdown: Resend in {timer} seconds
 | 
			
		||||
            send: Send the verification code
 | 
			
		||||
            sending: sending
 | 
			
		||||
          toast_email_empty: Please enter your email address
 | 
			
		||||
          toast_success: Verification code sent
 | 
			
		||||
        verify:
 | 
			
		||||
          toast_success: Verification successful
 | 
			
		||||
      titles:
 | 
			
		||||
        modify: Modify email address
 | 
			
		||||
        verify: Verify email
 | 
			
		||||
    device:
 | 
			
		||||
      list:
 | 
			
		||||
        fields:
 | 
			
		||||
          current: Current
 | 
			
		||||
          last_accessed_time: "Last accessed time: {time}"
 | 
			
		||||
      detail_modal:
 | 
			
		||||
        title: Login device details
 | 
			
		||||
        fields:
 | 
			
		||||
          os: OS
 | 
			
		||||
          browser: Browser
 | 
			
		||||
          creation_timestamp: Creation time
 | 
			
		||||
          last_accessed_times: Last accessed time
 | 
			
		||||
          last_authenticated_time: Last authenticated time
 | 
			
		||||
      operations:
 | 
			
		||||
        revoke:
 | 
			
		||||
          title: Revoke device
 | 
			
		||||
          description: Are you sure you want to revoke this device? After revoking, this device will be logged out
 | 
			
		||||
        revoke_others:
 | 
			
		||||
          title: Revoke all other devices
 | 
			
		||||
          description: Are you sure you want to revoke all other devices? After you revoke, other devices will be logged out
 | 
			
		||||
          toast_success: Login status of other devices has been revoked
 | 
			
		||||
  uc_notification:
 | 
			
		||||
    title: Notifications
 | 
			
		||||
    tabs:
 | 
			
		||||
      unread: Unread
 | 
			
		||||
      read: Read
 | 
			
		||||
    empty:
 | 
			
		||||
      titles:
 | 
			
		||||
        unread: No unread notifications
 | 
			
		||||
        read: No read notifications
 | 
			
		||||
    operations:
 | 
			
		||||
      mark_as_read:
 | 
			
		||||
        button: Mark as read
 | 
			
		||||
      delete:
 | 
			
		||||
        description: Are you sure you want to delete this notification?
 | 
			
		||||
        title: Delete
 | 
			
		||||
  overview:
 | 
			
		||||
    fields:
 | 
			
		||||
      activated_theme: Activated theme
 | 
			
		||||
      enabled_plugins: Enabled plugins
 | 
			
		||||
  backup:
 | 
			
		||||
    operations:
 | 
			
		||||
      remote_download:
 | 
			
		||||
        button: Download and restore
 | 
			
		||||
      restore_by_backup:
 | 
			
		||||
        button: Restore
 | 
			
		||||
        title: Restore from backup file
 | 
			
		||||
        description: After clicking OK, data will be restored from the backup file {filename}.
 | 
			
		||||
    restore:
 | 
			
		||||
      tabs:
 | 
			
		||||
        local:
 | 
			
		||||
          label: Upload
 | 
			
		||||
        remote:
 | 
			
		||||
          label: Remote
 | 
			
		||||
          fields:
 | 
			
		||||
            url: Remote URL
 | 
			
		||||
        backup:
 | 
			
		||||
          label: Restore from backup files
 | 
			
		||||
          empty:
 | 
			
		||||
            title: No backup files
 | 
			
		||||
            message: Currently no backup files are scanned. You can manually upload the backup files to the backups directory of the Halo working directory.
 | 
			
		||||
  rbac:
 | 
			
		||||
    role-template-manage-posts: Post Manage
 | 
			
		||||
    role-template-post-author: Allows you to manage your own posts
 | 
			
		||||
    role-template-post-contributor: Contributions allowed
 | 
			
		||||
    role-template-post-publisher: Allow to publish own posts
 | 
			
		||||
    role-template-post-attachment-manager: Allow images to be uploaded in posts
 | 
			
		||||
    Actuator Management: System Information
 | 
			
		||||
    Actuator Manage: Access System Information
 | 
			
		||||
    Cache Management: Cache
 | 
			
		||||
    Cache Manage: Cache Manage
 | 
			
		||||
    Notification Configuration: Notification Configuration
 | 
			
		||||
    role-template-notifier-config: Configure Notifier
 | 
			
		||||
    Post Attachment Manager: Allow images to be uploaded in posts
 | 
			
		||||
    Post Author: Allows you to manage your own posts
 | 
			
		||||
    Post Contributor: Contributions allowed
 | 
			
		||||
    Post Publisher: Allow to publish own posts
 | 
			
		||||
    UC Attachment Manage: Allow to manage own attachments
 | 
			
		||||
    role-template-uc-attachment-manager: Allow to manage own attachments
 | 
			
		||||
    Recycle My Post: Recycle My Post
 | 
			
		||||
    role-template-recycle-my-post: Recycle My Post
 | 
			
		||||
  components:
 | 
			
		||||
    annotations_form:
 | 
			
		||||
      buttons:
 | 
			
		||||
        expand: View more
 | 
			
		||||
        collapse: Collapse
 | 
			
		||||
    default_editor:
 | 
			
		||||
      extensions:
 | 
			
		||||
        upload:
 | 
			
		||||
          error: Upload failed
 | 
			
		||||
          click_retry: Click to retry
 | 
			
		||||
          loading: Loading
 | 
			
		||||
          attachment:
 | 
			
		||||
            title: Attachment Library
 | 
			
		||||
          permalink:
 | 
			
		||||
            title: Input Link
 | 
			
		||||
            placeholder: Input link and press Enter to confirm
 | 
			
		||||
          operations:
 | 
			
		||||
            replace:
 | 
			
		||||
              button: Replace
 | 
			
		||||
      toolbox:
 | 
			
		||||
        show_hide_sidebar: Show/Hide Sidebar
 | 
			
		||||
      title_placeholder: Please enter the title
 | 
			
		||||
    user_avatar:
 | 
			
		||||
      title: Avatar
 | 
			
		||||
      toast_upload_failed: Failed to upload avatar
 | 
			
		||||
      toast_remove_failed: Failed to delete avatar
 | 
			
		||||
      cropper_modal:
 | 
			
		||||
        title: Crop Avatar
 | 
			
		||||
      remove:
 | 
			
		||||
        title: Delete avatar
 | 
			
		||||
      tooltips:
 | 
			
		||||
        upload: Upload
 | 
			
		||||
        zoom_in: Zoom In
 | 
			
		||||
        zoom_out: Zoom Out
 | 
			
		||||
        flip_horizontal: Flip Horizontal
 | 
			
		||||
        flip_vertical: Flip Vertical
 | 
			
		||||
        reset: Reset
 | 
			
		||||
    editor_provider_selector:
 | 
			
		||||
      tooltips:
 | 
			
		||||
        disallow: The content format is different and cannot be switched
 | 
			
		||||
    uppy:
 | 
			
		||||
      image_editor:
 | 
			
		||||
        revert: Revert
 | 
			
		||||
        rotate: Rotate
 | 
			
		||||
        zoom_in: Zoom in
 | 
			
		||||
        zoom_out: Zoom out
 | 
			
		||||
        flip_horizontal: Flip horizontal
 | 
			
		||||
        aspect_ratio_square: Crop square
 | 
			
		||||
        aspect_ratio_landscape: Crop landscape (16:9)
 | 
			
		||||
        aspect_ratio_portrait: Crop portrait (9:16)
 | 
			
		||||
    h2_warning_alert:
 | 
			
		||||
      title: "Warning: H2 database in use"
 | 
			
		||||
      description: The H2 database is only suitable for development and testing environments and is not recommended for use in production environments. H2 is very easy to cause data file corruption due to improper operation. If you must use it, please back up the data on time.
 | 
			
		||||
  formkit:
 | 
			
		||||
    select:
 | 
			
		||||
      no_data: No data
 | 
			
		||||
    validation:
 | 
			
		||||
      password: "The password can only use uppercase and lowercase letters (A-Z, a-z), numbers (0-9), and the following special characters: !{'@'}#$%^&*"
 | 
			
		||||
    verification_form:
 | 
			
		||||
      no_action_defined: "{label} interface not defined"
 | 
			
		||||
      verify_success: "{label} successful"
 | 
			
		||||
      verify_failed: "{label} failed"
 | 
			
		||||
    secret:
 | 
			
		||||
      creation_label: Create a new secret based on the text entered
 | 
			
		||||
      placeholder: Search for an existing secret or enter new content to create one
 | 
			
		||||
      required_key_missing_label: The needed fields are missing, Please select and complete them
 | 
			
		||||
      creation_modal:
 | 
			
		||||
        title: Create secret
 | 
			
		||||
      edit_modal:
 | 
			
		||||
        title: Edit secret
 | 
			
		||||
      list_modal:
 | 
			
		||||
        title: Secrets
 | 
			
		||||
      operations:
 | 
			
		||||
        delete:
 | 
			
		||||
          title: Delete secret
 | 
			
		||||
          description: Are you sure you want to delete this secret? Please make sure that this secret is not being used anywhere, otherwise you need to reset it in a specific place
 | 
			
		||||
      form:
 | 
			
		||||
        fields:
 | 
			
		||||
          description: Description
 | 
			
		||||
          string_data: String Data
 | 
			
		||||
    code:
 | 
			
		||||
      fullscreen:
 | 
			
		||||
        exit: Exit fullscreen
 | 
			
		||||
        enter: Enter fullscreen to edit
 | 
			
		||||
  common:
 | 
			
		||||
    buttons:
 | 
			
		||||
      activate: Activate
 | 
			
		||||
      inactivate: Inactivate
 | 
			
		||||
      select: Select
 | 
			
		||||
      view_all: View all
 | 
			
		||||
      verify: Verify
 | 
			
		||||
      modify: Modify
 | 
			
		||||
      access: Access
 | 
			
		||||
      schedule_publish: Schedule publish
 | 
			
		||||
      revoke: Revoke
 | 
			
		||||
      disable: Disable
 | 
			
		||||
      enable: Enable
 | 
			
		||||
      continue: Continue
 | 
			
		||||
    toast:
 | 
			
		||||
      disable_success: Disabled successfully
 | 
			
		||||
      enable_success: Enabled successfully
 | 
			
		||||
    dialog:
 | 
			
		||||
      titles:
 | 
			
		||||
        login_expired: Login expired
 | 
			
		||||
      descriptions:
 | 
			
		||||
        login_expired: The current session has expired. Click Confirm to go to the login page. Please ensure that the current content is saved. You can click Cancel to manually copy any unsaved content.
 | 
			
		||||
    status:
 | 
			
		||||
      starting_up: Starting
 | 
			
		||||
    text:
 | 
			
		||||
      system_protection: System protection
 | 
			
		||||
  uc_post:
 | 
			
		||||
    creation_modal:
 | 
			
		||||
      title: Create post
 | 
			
		||||
    operations:
 | 
			
		||||
      cancel_publish:
 | 
			
		||||
        description: Are you sure you want to cancel publishing?
 | 
			
		||||
        title: Cancel publish
 | 
			
		||||
      delete:
 | 
			
		||||
        title: Delete post
 | 
			
		||||
        description: This action will move the post to the recycle bin, where it will be managed by the site administrator.
 | 
			
		||||
    publish_modal:
 | 
			
		||||
      title: Publish post
 | 
			
		||||
    setting_modal:
 | 
			
		||||
      title: Post settings
 | 
			
		||||
    title: My posts
 | 
			
		||||
  tool:
 | 
			
		||||
    title: Tools
 | 
			
		||||
    empty:
 | 
			
		||||
      title: There are no tools available
 | 
			
		||||
      message: There are currently no tools available, and system tools may be provided by plugins
 | 
			
		||||
  post_snapshots:
 | 
			
		||||
    operations:
 | 
			
		||||
      revert:
 | 
			
		||||
        button: Revert
 | 
			
		||||
        title: Revert snapshot
 | 
			
		||||
        description: Are you sure you want to restore this snapshot? This operation will create a new snapshot based on this one and publish it.
 | 
			
		||||
        toast_success: Reverted successfully
 | 
			
		||||
      delete:
 | 
			
		||||
        title: Delete snapshot
 | 
			
		||||
        description: Are you sure you want to delete this snapshot? This operation is irreversible.
 | 
			
		||||
      cleanup:
 | 
			
		||||
        button: Cleanup
 | 
			
		||||
        title: Cleanup snapshots
 | 
			
		||||
        description: Are you sure you want to delete all unused snapshots? Only published, base version, and draft versions will be retained.
 | 
			
		||||
        toast_empty: There are no snapshots to be cleaned up
 | 
			
		||||
        toast_success: Cleanup completed
 | 
			
		||||
    status:
 | 
			
		||||
      released: Released
 | 
			
		||||
      draft: Draft
 | 
			
		||||
      base: Base
 | 
			
		||||
    title: Post snapshots
 | 
			
		||||
  page_snapshots:
 | 
			
		||||
    operations:
 | 
			
		||||
      revert:
 | 
			
		||||
        button: Revert
 | 
			
		||||
        title: Revert snapshot
 | 
			
		||||
        description: Are you sure you want to restore this snapshot? This operation will create a new snapshot based on this one and publish it.
 | 
			
		||||
        toast_success: Reverted successfully
 | 
			
		||||
      delete:
 | 
			
		||||
        title: Delete snapshot
 | 
			
		||||
        description: Are you sure you want to delete this snapshot? This operation is irreversible.
 | 
			
		||||
      cleanup:
 | 
			
		||||
        button: Cleanup
 | 
			
		||||
        title: Cleanup snapshots
 | 
			
		||||
        description: Are you sure you want to delete all unused snapshots? Only published, base version, and draft versions will be retained.
 | 
			
		||||
        toast_empty: There are no snapshots to be cleaned up
 | 
			
		||||
        toast_success: Cleanup completed
 | 
			
		||||
    status:
 | 
			
		||||
      released: Released
 | 
			
		||||
      draft: Draft
 | 
			
		||||
      base: Base
 | 
			
		||||
    title: Page snapshots
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
{}
 | 
			
		||||
| 
						 | 
				
			
			@ -1075,8 +1075,8 @@ core:
 | 
			
		|||
          title: Update profile
 | 
			
		||||
        change_password:
 | 
			
		||||
          title: Change password
 | 
			
		||||
          grant_permission:
 | 
			
		||||
            title: Grant permission
 | 
			
		||||
        grant_permission:
 | 
			
		||||
          title: Grant permission
 | 
			
		||||
        profile:
 | 
			
		||||
          title: Profile
 | 
			
		||||
      fields:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,10 +23,8 @@ core:
 | 
			
		|||
        backup: Respaldo
 | 
			
		||||
    operations:
 | 
			
		||||
      logout:
 | 
			
		||||
        button: Cerrar sesión
 | 
			
		||||
        title: ¿Estás seguro de que deseas cerrar sesión?
 | 
			
		||||
      profile:
 | 
			
		||||
        button: Perfil
 | 
			
		||||
      profile: {}
 | 
			
		||||
      visit_homepage:
 | 
			
		||||
        title: Visitar página de inicio
 | 
			
		||||
  dashboard:
 | 
			
		||||
| 
						 | 
				
			
			@ -74,15 +72,8 @@ core:
 | 
			
		|||
            refresh_search_engine:
 | 
			
		||||
              title: Actualizar Motor de Búsqueda
 | 
			
		||||
              dialog_title: ¿Deseas actualizar el índice del motor de búsqueda?
 | 
			
		||||
              dialog_content: >-
 | 
			
		||||
                Esta operación recreará los índices del motor de búsqueda para
 | 
			
		||||
                todas las publicaciones publicadas.
 | 
			
		||||
              dialog_content: Esta operación recreará los índices del motor de búsqueda para todas las publicaciones publicadas.
 | 
			
		||||
              success_message: Índice del motor de búsqueda actualizado exitosamente.
 | 
			
		||||
            evict_page_cache:
 | 
			
		||||
              title: Actualizar Caché de Página
 | 
			
		||||
              dialog_title: ¿Deseas actualizar el caché de las páginas?
 | 
			
		||||
              dialog_content: Esta operación borrará la caché de todas las páginas.
 | 
			
		||||
              success_message: Caché de página actualizada exitosamente.
 | 
			
		||||
        user_stats:
 | 
			
		||||
          title: Usuarios
 | 
			
		||||
        comment_stats:
 | 
			
		||||
| 
						 | 
				
			
			@ -101,14 +92,10 @@ core:
 | 
			
		|||
    operations:
 | 
			
		||||
      delete:
 | 
			
		||||
        title: ¿Estás seguro de que deseas eliminar esta publicación?
 | 
			
		||||
        description: >-
 | 
			
		||||
          Esta operación moverá la publicación a la papelera de reciclaje, y
 | 
			
		||||
          podrá ser restaurada desde la papelera de reciclaje posteriormente.
 | 
			
		||||
        description: Esta operación moverá la publicación a la papelera de reciclaje, y podrá ser restaurada desde la papelera de reciclaje posteriormente.
 | 
			
		||||
      delete_in_batch:
 | 
			
		||||
        title: ¿Estás seguro de que deseas eliminar las publicaciones seleccionadas?
 | 
			
		||||
        description: >-
 | 
			
		||||
          Esta operación moverá las publicaciones a la papelera de reciclaje, y
 | 
			
		||||
          podrán ser restauradas desde la papelera de reciclaje posteriormente.
 | 
			
		||||
        description: Esta operación moverá las publicaciones a la papelera de reciclaje, y podrán ser restauradas desde la papelera de reciclaje posteriormente.
 | 
			
		||||
    filters:
 | 
			
		||||
      status:
 | 
			
		||||
        items:
 | 
			
		||||
| 
						 | 
				
			
			@ -154,9 +141,7 @@ core:
 | 
			
		|||
          label: Título
 | 
			
		||||
        slug:
 | 
			
		||||
          label: Slug
 | 
			
		||||
          help: >-
 | 
			
		||||
            Usualmente usado para generar el enlace permanente a las
 | 
			
		||||
            publicaciones
 | 
			
		||||
          help: Usualmente usado para generar el enlace permanente a las publicaciones
 | 
			
		||||
          refresh_message: Regenerar slug basado en el título.
 | 
			
		||||
        categories:
 | 
			
		||||
          label: Categorías
 | 
			
		||||
| 
						 | 
				
			
			@ -188,20 +173,14 @@ core:
 | 
			
		|||
        title: ¿Estás seguro de que deseas eliminar permanentemente esta publicación?
 | 
			
		||||
        description: Después de la eliminación, no será posible recuperarla.
 | 
			
		||||
      delete_in_batch:
 | 
			
		||||
        title: >-
 | 
			
		||||
          ¿Estás seguro de que deseas eliminar permanentemente las publicaciones
 | 
			
		||||
          seleccionadas?
 | 
			
		||||
        title: ¿Estás seguro de que deseas eliminar permanentemente las publicaciones seleccionadas?
 | 
			
		||||
        description: Después de la eliminación, no será posible recuperarlas.
 | 
			
		||||
      recovery:
 | 
			
		||||
        title: ¿Quieres restaurar esta publicación?
 | 
			
		||||
        description: >-
 | 
			
		||||
          Esta operación restaurará la publicación a su estado antes de la
 | 
			
		||||
          eliminación.
 | 
			
		||||
        description: Esta operación restaurará la publicación a su estado antes de la eliminación.
 | 
			
		||||
      recovery_in_batch:
 | 
			
		||||
        title: ¿Estás seguro de que deseas restaurar las publicaciones seleccionadas?
 | 
			
		||||
        description: >-
 | 
			
		||||
          Esta operación restaurará las publicaciones a su estado antes de la
 | 
			
		||||
          eliminación.
 | 
			
		||||
        description: Esta operación restaurará las publicaciones a su estado antes de la eliminación.
 | 
			
		||||
  post_editor:
 | 
			
		||||
    title: Edición de publicación
 | 
			
		||||
    untitled: Publicación sin título
 | 
			
		||||
| 
						 | 
				
			
			@ -215,9 +194,7 @@ core:
 | 
			
		|||
    operations:
 | 
			
		||||
      delete:
 | 
			
		||||
        title: ¿Estás seguro de que deseas eliminar esta etiqueta?
 | 
			
		||||
        description: >-
 | 
			
		||||
          Después de eliminar esta etiqueta, se eliminará la asociación con el
 | 
			
		||||
          artículo correspondiente. Esta operación no se puede deshacer.
 | 
			
		||||
        description: Después de eliminar esta etiqueta, se eliminará la asociación con el artículo correspondiente. Esta operación no se puede deshacer.
 | 
			
		||||
    editing_modal:
 | 
			
		||||
      titles:
 | 
			
		||||
        update: Actualizar etiqueta de publicación
 | 
			
		||||
| 
						 | 
				
			
			@ -230,9 +207,7 @@ core:
 | 
			
		|||
          label: Nombre para mostrar
 | 
			
		||||
        slug:
 | 
			
		||||
          label: Slug
 | 
			
		||||
          help: >-
 | 
			
		||||
            Usualmente utilizado para generar el enlace permanente de las
 | 
			
		||||
            etiquetas
 | 
			
		||||
          help: Usualmente utilizado para generar el enlace permanente de las etiquetas
 | 
			
		||||
          refresh_message: Regenerar slug basado en el nombre para mostrar.
 | 
			
		||||
        color:
 | 
			
		||||
          label: Color
 | 
			
		||||
| 
						 | 
				
			
			@ -250,9 +225,7 @@ core:
 | 
			
		|||
    operations:
 | 
			
		||||
      delete:
 | 
			
		||||
        title: ¿Estás seguro de que deseas eliminar esta categoría?
 | 
			
		||||
        description: >-
 | 
			
		||||
          Después de eliminar esta categoría, se eliminará la asociación con los
 | 
			
		||||
          artículos correspondientes. Esta operación no se puede deshacer.
 | 
			
		||||
        description: Después de eliminar esta categoría, se eliminará la asociación con los artículos correspondientes. Esta operación no se puede deshacer.
 | 
			
		||||
      add_sub_category:
 | 
			
		||||
        button: Agregar subcategoría
 | 
			
		||||
    editing_modal:
 | 
			
		||||
| 
						 | 
				
			
			@ -269,9 +242,7 @@ core:
 | 
			
		|||
          label: Nombre para mostrar
 | 
			
		||||
        slug:
 | 
			
		||||
          label: Slug
 | 
			
		||||
          help: >-
 | 
			
		||||
            Usualmente utilizado para generar el enlace permanente de las
 | 
			
		||||
            categorías
 | 
			
		||||
          help: Usualmente utilizado para generar el enlace permanente de las categorías
 | 
			
		||||
          refresh_message: Regenerar slug basado en el nombre para mostrar.
 | 
			
		||||
        template:
 | 
			
		||||
          label: Plantilla personalizada
 | 
			
		||||
| 
						 | 
				
			
			@ -283,9 +254,7 @@ core:
 | 
			
		|||
          help: Se requiere adaptación del tema para ser compatible
 | 
			
		||||
        prevent_parent_post_cascade_query:
 | 
			
		||||
          label: Evitar consulta en cascada de publicación principal
 | 
			
		||||
          help: >-
 | 
			
		||||
            Si se selecciona, las publicaciones de las subcategorías no se
 | 
			
		||||
            agregarán a la categoría principal
 | 
			
		||||
          help: Si se selecciona, las publicaciones de las subcategorías no se agregarán a la categoría principal
 | 
			
		||||
  page:
 | 
			
		||||
    title: Páginas
 | 
			
		||||
    actions:
 | 
			
		||||
| 
						 | 
				
			
			@ -296,14 +265,10 @@ core:
 | 
			
		|||
    operations:
 | 
			
		||||
      delete:
 | 
			
		||||
        title: ¿Estás seguro de que deseas eliminar esta página?
 | 
			
		||||
        description: >-
 | 
			
		||||
          Esta operación moverá la página a la papelera de reciclaje, y podrá
 | 
			
		||||
          ser restaurada desde la papelera de reciclaje posteriormente.
 | 
			
		||||
        description: Esta operación moverá la página a la papelera de reciclaje, y podrá ser restaurada desde la papelera de reciclaje posteriormente.
 | 
			
		||||
      delete_in_batch:
 | 
			
		||||
        title: ¿Estás seguro de que deseas eliminar las páginas seleccionadas?
 | 
			
		||||
        description: >-
 | 
			
		||||
          Esta operación moverá las páginas a la papelera de reciclaje, y podrá
 | 
			
		||||
          ser restaurada desde la papelera de reciclaje posteriormente.
 | 
			
		||||
        description: Esta operación moverá las páginas a la papelera de reciclaje, y podrá ser restaurada desde la papelera de reciclaje posteriormente.
 | 
			
		||||
    filters:
 | 
			
		||||
      status:
 | 
			
		||||
        items:
 | 
			
		||||
| 
						 | 
				
			
			@ -339,9 +304,7 @@ core:
 | 
			
		|||
          label: Título
 | 
			
		||||
        slug:
 | 
			
		||||
          label: Slug
 | 
			
		||||
          help: >-
 | 
			
		||||
            Usualmente utilizado para generar el enlace permanente de las
 | 
			
		||||
            páginas
 | 
			
		||||
          help: Usualmente utilizado para generar el enlace permanente de las páginas
 | 
			
		||||
          refresh_message: Regenerar slug basado en el título.
 | 
			
		||||
        auto_generate_excerpt:
 | 
			
		||||
          label: Generar Extracto Automáticamente
 | 
			
		||||
| 
						 | 
				
			
			@ -369,20 +332,14 @@ core:
 | 
			
		|||
        title: ¿Estás seguro de que deseas eliminar permanentemente esta página?
 | 
			
		||||
        description: Después de la eliminación, no será posible recuperarla.
 | 
			
		||||
      delete_in_batch:
 | 
			
		||||
        title: >-
 | 
			
		||||
          ¿Estás seguro de que deseas eliminar permanentemente las páginas
 | 
			
		||||
          seleccionadas?
 | 
			
		||||
        title: ¿Estás seguro de que deseas eliminar permanentemente las páginas seleccionadas?
 | 
			
		||||
        description: Después de la eliminación, no será posible recuperarlas.
 | 
			
		||||
      recovery:
 | 
			
		||||
        title: ¿Quieres restaurar esta página?
 | 
			
		||||
        description: >-
 | 
			
		||||
          Esta operación restaurará la página a su estado antes de la
 | 
			
		||||
          eliminación.
 | 
			
		||||
        description: Esta operación restaurará la página a su estado antes de la eliminación.
 | 
			
		||||
      recovery_in_batch:
 | 
			
		||||
        title: ¿Estás seguro de que deseas restaurar las páginas seleccionadas?
 | 
			
		||||
        description: >-
 | 
			
		||||
          Esta operación restaurará las páginas a su estado antes de la
 | 
			
		||||
          eliminación.
 | 
			
		||||
        description: Esta operación restaurará las páginas a su estado antes de la eliminación.
 | 
			
		||||
  page_editor:
 | 
			
		||||
    title: Edición de Página
 | 
			
		||||
    untitled: Página Sin Título
 | 
			
		||||
| 
						 | 
				
			
			@ -398,24 +355,16 @@ core:
 | 
			
		|||
    operations:
 | 
			
		||||
      delete_comment:
 | 
			
		||||
        title: ¿Estás seguro de que deseas eliminar este comentario?
 | 
			
		||||
        description: >-
 | 
			
		||||
          Todas las respuestas bajo los comentarios se eliminarán al mismo
 | 
			
		||||
          tiempo, y esta operación no se puede deshacer.
 | 
			
		||||
        description: Todas las respuestas bajo los comentarios se eliminarán al mismo tiempo, y esta operación no se puede deshacer.
 | 
			
		||||
      delete_comment_in_batch:
 | 
			
		||||
        title: ¿Estás seguro de que deseas eliminar los comentarios seleccionados?
 | 
			
		||||
        description: >-
 | 
			
		||||
          Todas las respuestas bajo los comentarios se eliminarán al mismo
 | 
			
		||||
          tiempo, y esta operación no se puede deshacer.
 | 
			
		||||
        description: Todas las respuestas bajo los comentarios se eliminarán al mismo tiempo, y esta operación no se puede deshacer.
 | 
			
		||||
      approve_comment_in_batch:
 | 
			
		||||
        button: Aprobar
 | 
			
		||||
        title: >-
 | 
			
		||||
          ¿Estás seguro de que deseas aprobar los comentarios seleccionados para
 | 
			
		||||
          su revisión?
 | 
			
		||||
        title: ¿Estás seguro de que deseas aprobar los comentarios seleccionados para su revisión?
 | 
			
		||||
      approve_applies_in_batch:
 | 
			
		||||
        button: Aprobar todas las respuestas
 | 
			
		||||
        title: >-
 | 
			
		||||
          ¿Estás seguro de que deseas aprobar todas las respuestas a este
 | 
			
		||||
          comentario para su revisión?
 | 
			
		||||
        title: ¿Estás seguro de que deseas aprobar todas las respuestas a este comentario para su revisión?
 | 
			
		||||
      delete_reply:
 | 
			
		||||
        title: ¿Estás seguro de que deseas eliminar esta respuesta?
 | 
			
		||||
      approve_reply:
 | 
			
		||||
| 
						 | 
				
			
			@ -464,9 +413,7 @@ core:
 | 
			
		|||
      storage_policies: Políticas de Almacenamiento
 | 
			
		||||
    empty:
 | 
			
		||||
      title: No hay adjuntos en el grupo actual.
 | 
			
		||||
      message: >-
 | 
			
		||||
        El grupo actual no tiene adjuntos, puedes intentar actualizar o cargar
 | 
			
		||||
        adjuntos.
 | 
			
		||||
      message: El grupo actual no tiene adjuntos, puedes intentar actualizar o cargar adjuntos.
 | 
			
		||||
      actions:
 | 
			
		||||
        upload: Cargar Adjunto
 | 
			
		||||
    operations:
 | 
			
		||||
| 
						 | 
				
			
			@ -536,26 +483,18 @@ core:
 | 
			
		|||
        delete:
 | 
			
		||||
          button: Y mover adjunto a sin grupo
 | 
			
		||||
          title: ¿Estás seguro de que deseas eliminar este grupo?
 | 
			
		||||
          description: >-
 | 
			
		||||
            El grupo se eliminará, y los adjuntos bajo el grupo se moverán a sin
 | 
			
		||||
            grupo. Esta operación no se puede deshacer.
 | 
			
		||||
          description: El grupo se eliminará, y los adjuntos bajo el grupo se moverán a sin grupo. Esta operación no se puede deshacer.
 | 
			
		||||
          toast_success: Eliminación exitosa, {total} adjuntos se han movido a sin grupo
 | 
			
		||||
        delete_with_attachments:
 | 
			
		||||
          button: También eliminar adjuntos
 | 
			
		||||
          title: ¿Estás seguro de que deseas eliminar este grupo?
 | 
			
		||||
          description: >-
 | 
			
		||||
            Al eliminar el grupo y todos los adjuntos dentro de él, esta acción
 | 
			
		||||
            no se puede deshacer.
 | 
			
		||||
          toast_success: >-
 | 
			
		||||
            Eliminación exitosa, {total} adjuntos se han eliminado
 | 
			
		||||
            simultáneamente
 | 
			
		||||
          description: Al eliminar el grupo y todos los adjuntos dentro de él, esta acción no se puede deshacer.
 | 
			
		||||
          toast_success: Eliminación exitosa, {total} adjuntos se han eliminado simultáneamente
 | 
			
		||||
    policies_modal:
 | 
			
		||||
      title: Políticas de Almacenamiento
 | 
			
		||||
      empty:
 | 
			
		||||
        title: Actualmente no hay estrategias de almacenamiento disponibles.
 | 
			
		||||
        message: >-
 | 
			
		||||
          No hay políticas de almacenamiento disponibles en este momento. Puedes
 | 
			
		||||
          intentar actualizar o crear una nueva política.
 | 
			
		||||
        message: No hay políticas de almacenamiento disponibles en este momento. Puedes intentar actualizar o crear una nueva política.
 | 
			
		||||
      operations:
 | 
			
		||||
        delete:
 | 
			
		||||
          title: ¿Estás seguro de que deseas eliminar esta política?
 | 
			
		||||
| 
						 | 
				
			
			@ -579,9 +518,7 @@ core:
 | 
			
		|||
          label: "Seleccionar política de almacenamiento:"
 | 
			
		||||
          empty:
 | 
			
		||||
            title: Sin política de almacenamiento
 | 
			
		||||
            description: >-
 | 
			
		||||
              Antes de cargar, es necesario crear una nueva política de
 | 
			
		||||
              almacenamiento.
 | 
			
		||||
            description: Antes de cargar, es necesario crear una nueva política de almacenamiento.
 | 
			
		||||
          not_select: Por favor, selecciona una política de almacenamiento primero
 | 
			
		||||
    select_modal:
 | 
			
		||||
      title: Seleccionar adjunto
 | 
			
		||||
| 
						 | 
				
			
			@ -610,61 +547,29 @@ core:
 | 
			
		|||
        title: ¿Estás seguro de activar el tema actual?
 | 
			
		||||
        toast_success: Tema activado exitosamente
 | 
			
		||||
      reset:
 | 
			
		||||
        title: >-
 | 
			
		||||
          ¿Estás seguro de que deseas restablecer todas las configuraciones del
 | 
			
		||||
          tema?
 | 
			
		||||
        description: >-
 | 
			
		||||
          Esta operación eliminará la configuración guardada y la restablecerá a
 | 
			
		||||
          los ajustes predeterminados.
 | 
			
		||||
        title: ¿Estás seguro de que deseas restablecer todas las configuraciones del tema?
 | 
			
		||||
        description: Esta operación eliminará la configuración guardada y la restablecerá a los ajustes predeterminados.
 | 
			
		||||
        toast_success: Configuración restablecida exitosamente
 | 
			
		||||
      reload:
 | 
			
		||||
        button: Recargar
 | 
			
		||||
        title: >-
 | 
			
		||||
          ¿Estás seguro de que deseas recargar todas las configuraciones del
 | 
			
		||||
          tema?
 | 
			
		||||
        description: >-
 | 
			
		||||
          Esta operación solo recargará la configuración del tema y la
 | 
			
		||||
          definición del formulario de ajustes, y no eliminará ninguna
 | 
			
		||||
          configuración guardada.
 | 
			
		||||
        title: ¿Estás seguro de que deseas recargar todas las configuraciones del tema?
 | 
			
		||||
        description: Esta operación solo recargará la configuración del tema y la definición del formulario de ajustes, y no eliminará ninguna configuración guardada.
 | 
			
		||||
        toast_success: Recarga de configuración exitosa
 | 
			
		||||
      uninstall:
 | 
			
		||||
        title: ¿Estás seguro de que deseas desinstalar este tema?
 | 
			
		||||
      uninstall_and_delete_config:
 | 
			
		||||
        button: Desinstalar y eliminar configuración
 | 
			
		||||
        title: >-
 | 
			
		||||
          ¿Estás seguro de que deseas desinstalar este tema y su configuración
 | 
			
		||||
          correspondiente?
 | 
			
		||||
        title: ¿Estás seguro de que deseas desinstalar este tema y su configuración correspondiente?
 | 
			
		||||
      remote_download:
 | 
			
		||||
        title: Se ha detectado una dirección de descarga remota, ¿deseas descargar?
 | 
			
		||||
        description: >-
 | 
			
		||||
          Por favor, verifica cuidadosamente si esta dirección es confiable:
 | 
			
		||||
          {url}
 | 
			
		||||
    upload_modal:
 | 
			
		||||
      titles:
 | 
			
		||||
        install: Instalar tema
 | 
			
		||||
        upgrade: Actualizar tema ({display_name})
 | 
			
		||||
      operations:
 | 
			
		||||
        existed_during_installation:
 | 
			
		||||
          title: El tema ya existe.
 | 
			
		||||
          description: El tema instalado actualmente ya existe, deseas actualizarlo?
 | 
			
		||||
      tabs:
 | 
			
		||||
        local: Local
 | 
			
		||||
        remote:
 | 
			
		||||
          title: Remoto
 | 
			
		||||
          fields:
 | 
			
		||||
            url: URL Remota
 | 
			
		||||
        description: "Por favor, verifica cuidadosamente si esta dirección es confiable: {url}"
 | 
			
		||||
    list_modal:
 | 
			
		||||
      titles:
 | 
			
		||||
        installed_themes: Temas Instalados
 | 
			
		||||
        not_installed_themes: Temas no Instalados
 | 
			
		||||
      tabs:
 | 
			
		||||
        installed: Instalados
 | 
			
		||||
        not_installed: No Instalados
 | 
			
		||||
      empty:
 | 
			
		||||
        title: No hay temas instalados actualmente.
 | 
			
		||||
        message: >-
 | 
			
		||||
          No hay temas instalados actualmente, puedes intentar actualizar o
 | 
			
		||||
          instalar un nuevo tema.
 | 
			
		||||
        message: No hay temas instalados actualmente, puedes intentar actualizar o instalar un nuevo tema.
 | 
			
		||||
      not_installed_empty:
 | 
			
		||||
        title: No hay temas actualmente no instalados.
 | 
			
		||||
    preview_model:
 | 
			
		||||
| 
						 | 
				
			
			@ -676,7 +581,6 @@ core:
 | 
			
		|||
    detail:
 | 
			
		||||
      fields:
 | 
			
		||||
        author: Autor
 | 
			
		||||
        website: Sitio Web
 | 
			
		||||
        repo: Repositorio Fuente
 | 
			
		||||
        version: Versión
 | 
			
		||||
        requires: Requiere
 | 
			
		||||
| 
						 | 
				
			
			@ -700,14 +604,10 @@ core:
 | 
			
		|||
        toast_success: Configuración exitosa
 | 
			
		||||
      delete_menu:
 | 
			
		||||
        title: ¿Estás seguro de que deseas eliminar este menú?
 | 
			
		||||
        description: >-
 | 
			
		||||
          Todos los elementos de menú de este menú se eliminarán al mismo tiempo
 | 
			
		||||
          y esta operación no se puede deshacer.
 | 
			
		||||
        description: Todos los elementos de menú de este menú se eliminarán al mismo tiempo y esta operación no se puede deshacer.
 | 
			
		||||
      delete_menu_item:
 | 
			
		||||
        title: ¿Estás seguro de que deseas eliminar este elemento de menú?
 | 
			
		||||
        description: >-
 | 
			
		||||
          Todos los subelementos de menú se eliminarán al mismo tiempo y no se
 | 
			
		||||
          pueden restaurar después de la eliminación.
 | 
			
		||||
        description: Todos los subelementos de menú se eliminarán al mismo tiempo y no se pueden restaurar después de la eliminación.
 | 
			
		||||
      add_sub_menu_item:
 | 
			
		||||
        button: Agregar subelemento de menú
 | 
			
		||||
    list:
 | 
			
		||||
| 
						 | 
				
			
			@ -758,32 +658,21 @@ core:
 | 
			
		|||
      detail: Detail
 | 
			
		||||
    empty:
 | 
			
		||||
      title: There are no installed plugins currently.
 | 
			
		||||
      message: >-
 | 
			
		||||
        There are no installed plugins currently, you can try refreshing or
 | 
			
		||||
        installing new plugins.
 | 
			
		||||
      message: There are no installed plugins currently, you can try refreshing or installing new plugins.
 | 
			
		||||
      actions:
 | 
			
		||||
        install: Install Plugin
 | 
			
		||||
    operations:
 | 
			
		||||
      reset:
 | 
			
		||||
        title: Are you sure you want to reset all configurations of the plugin?
 | 
			
		||||
        description: >-
 | 
			
		||||
          This operation will delete the saved configuration and reset it to
 | 
			
		||||
          default settings.
 | 
			
		||||
        description: This operation will delete the saved configuration and reset it to default settings.
 | 
			
		||||
        toast_success: Reset configuration successfully
 | 
			
		||||
      uninstall:
 | 
			
		||||
        title: Are you sure you want to uninstall this plugin?
 | 
			
		||||
      uninstall_and_delete_config:
 | 
			
		||||
        title: >-
 | 
			
		||||
          Are you sure you want to uninstall this plugin and its corresponding
 | 
			
		||||
          configuration?
 | 
			
		||||
        title: Are you sure you want to uninstall this plugin and its corresponding configuration?
 | 
			
		||||
      uninstall_when_enabled:
 | 
			
		||||
        confirm_text: Stop running and uninstall
 | 
			
		||||
        description: >-
 | 
			
		||||
          The current plugin is still in the enabled state and will be
 | 
			
		||||
          uninstalled after it stops running. This operation cannot be undone.
 | 
			
		||||
      change_status:
 | 
			
		||||
        active_title: Are you sure you want to active this plugin?
 | 
			
		||||
        inactive_title: Are you sure you want to inactive this plugin?
 | 
			
		||||
        description: The current plugin is still in the enabled state and will be uninstalled after it stops running. This operation cannot be undone.
 | 
			
		||||
      remote_download:
 | 
			
		||||
        title: Remote download address detected, do you want to download?
 | 
			
		||||
        description: "Please carefully verify whether this address can be trusted: {url}"
 | 
			
		||||
| 
						 | 
				
			
			@ -796,9 +685,6 @@ core:
 | 
			
		|||
        items:
 | 
			
		||||
          create_time_desc: Latest Installed
 | 
			
		||||
          create_time_asc: Earliest Installed
 | 
			
		||||
    list:
 | 
			
		||||
      actions:
 | 
			
		||||
        uninstall_and_delete_config: Uninstall and delete config
 | 
			
		||||
    upload_modal:
 | 
			
		||||
      titles:
 | 
			
		||||
        install: Install plugin
 | 
			
		||||
| 
						 | 
				
			
			@ -815,15 +701,12 @@ core:
 | 
			
		|||
          description: Would you like to activate the currently installed plugin?
 | 
			
		||||
        existed_during_installation:
 | 
			
		||||
          title: The plugin already exists.
 | 
			
		||||
          description: >-
 | 
			
		||||
            The currently installed plugin already exists, do you want to
 | 
			
		||||
            upgrade?
 | 
			
		||||
          description: The currently installed plugin already exists, do you want to upgrade?
 | 
			
		||||
    detail:
 | 
			
		||||
      title: Plugin detail
 | 
			
		||||
      header:
 | 
			
		||||
        title: Plugin information
 | 
			
		||||
      fields:
 | 
			
		||||
        display_name: Display Name
 | 
			
		||||
        description: Description
 | 
			
		||||
        version: Version
 | 
			
		||||
        requires: Requires
 | 
			
		||||
| 
						 | 
				
			
			@ -846,9 +729,7 @@ core:
 | 
			
		|||
      identity_authentication: Autenticación de Identidad
 | 
			
		||||
    empty:
 | 
			
		||||
      title: Actualmente no hay usuarios que cumplan con los criterios de filtrado.
 | 
			
		||||
      message: >-
 | 
			
		||||
        No hay usuarios que coincidan con los criterios de filtrado en este
 | 
			
		||||
        momento. Puedes intentar actualizar o crear un nuevo usuario.
 | 
			
		||||
      message: No hay usuarios que coincidan con los criterios de filtrado en este momento. Puedes intentar actualizar o crear un nuevo usuario.
 | 
			
		||||
    operations:
 | 
			
		||||
      delete:
 | 
			
		||||
        title: ¿Estás seguro de que deseas eliminar a este usuario?
 | 
			
		||||
| 
						 | 
				
			
			@ -911,14 +792,6 @@ core:
 | 
			
		|||
          title: Actualizar perfil
 | 
			
		||||
        change_password:
 | 
			
		||||
          title: Cambiar contraseña
 | 
			
		||||
      operations:
 | 
			
		||||
        bind:
 | 
			
		||||
          button: Vincular
 | 
			
		||||
        unbind:
 | 
			
		||||
          button: Desvincular
 | 
			
		||||
          title: >-
 | 
			
		||||
            ¿Estás seguro de que deseas desvincular el método de inicio de
 | 
			
		||||
            sesión para {display_name}?
 | 
			
		||||
      fields:
 | 
			
		||||
        display_name: Nombre para mostrar
 | 
			
		||||
        username: Nombre de usuario
 | 
			
		||||
| 
						 | 
				
			
			@ -927,21 +800,6 @@ core:
 | 
			
		|||
        bio: Biografía
 | 
			
		||||
        creation_time: Fecha de creación
 | 
			
		||||
        identity_authentication: Autenticación de identidad
 | 
			
		||||
      avatar:
 | 
			
		||||
        title: Avatar
 | 
			
		||||
        toast_upload_failed: No se pudo cargar el avatar
 | 
			
		||||
        toast_remove_failed: No se pudo eliminar el avatar
 | 
			
		||||
        cropper_modal:
 | 
			
		||||
          title: Recortar Avatar
 | 
			
		||||
        remove:
 | 
			
		||||
          title: ¿Estás seguro de que deseas eliminar el avatar?
 | 
			
		||||
        tooltips:
 | 
			
		||||
          upload: Cargar
 | 
			
		||||
          zoom_in: Acercar
 | 
			
		||||
          zoom_out: Alejar
 | 
			
		||||
          flip_horizontal: Voltear Horizontalmente
 | 
			
		||||
          flip_vertical: Voltear Verticalmente
 | 
			
		||||
          reset: Restablecer
 | 
			
		||||
  role:
 | 
			
		||||
    title: Roles
 | 
			
		||||
    common:
 | 
			
		||||
| 
						 | 
				
			
			@ -955,9 +813,7 @@ core:
 | 
			
		|||
    operations:
 | 
			
		||||
      delete:
 | 
			
		||||
        title: ¿Estás seguro de que deseas eliminar este rol?
 | 
			
		||||
        description: >-
 | 
			
		||||
          Una vez eliminado el rol, se eliminarán las asignaciones de rol de los
 | 
			
		||||
          usuarios asociados y esta operación no se puede deshacer.
 | 
			
		||||
        description: Una vez eliminado el rol, se eliminarán las asignaciones de rol de los usuarios asociados y esta operación no se puede deshacer.
 | 
			
		||||
      create_based_on_this_role:
 | 
			
		||||
        button: Crear basado en este rol
 | 
			
		||||
    detail:
 | 
			
		||||
| 
						 | 
				
			
			@ -974,9 +830,7 @@ core:
 | 
			
		|||
        creation_time: Fecha de creación
 | 
			
		||||
    permissions_detail:
 | 
			
		||||
      system_reserved_alert:
 | 
			
		||||
        description: >-
 | 
			
		||||
          El rol reservado del sistema no admite modificaciones. Se recomienda
 | 
			
		||||
          crear un nuevo rol basado en este.
 | 
			
		||||
        description: El rol reservado del sistema no admite modificaciones. Se recomienda crear un nuevo rol basado en este.
 | 
			
		||||
    editing_modal:
 | 
			
		||||
      titles:
 | 
			
		||||
        create: Crear rol
 | 
			
		||||
| 
						 | 
				
			
			@ -993,17 +847,11 @@ core:
 | 
			
		|||
      setting: Configuración
 | 
			
		||||
    operations:
 | 
			
		||||
      enable:
 | 
			
		||||
        title: >-
 | 
			
		||||
          ¿Estás seguro de que deseas habilitar este método de autenticación de
 | 
			
		||||
          identidad?
 | 
			
		||||
        title: ¿Estás seguro de que deseas habilitar este método de autenticación de identidad?
 | 
			
		||||
      disable:
 | 
			
		||||
        title: >-
 | 
			
		||||
          ¿Estás seguro de que deseas deshabilitar este método de autenticación
 | 
			
		||||
          de identidad?
 | 
			
		||||
        title: ¿Estás seguro de que deseas deshabilitar este método de autenticación de identidad?
 | 
			
		||||
      disable_privileged:
 | 
			
		||||
        tooltip: >-
 | 
			
		||||
          El método de autenticación reservado por el sistema no se puede
 | 
			
		||||
          deshabilitar
 | 
			
		||||
        tooltip: El método de autenticación reservado por el sistema no se puede deshabilitar
 | 
			
		||||
    detail:
 | 
			
		||||
      title: Detalle de la autenticación de identidad
 | 
			
		||||
      fields:
 | 
			
		||||
| 
						 | 
				
			
			@ -1044,11 +892,7 @@ core:
 | 
			
		|||
      database: "Base de datos: {database}"
 | 
			
		||||
      os: "Sistema operativo: {os}"
 | 
			
		||||
    alert:
 | 
			
		||||
      external_url_invalid: >-
 | 
			
		||||
        La URL de acceso externo detectada no coincide con la URL de acceso
 | 
			
		||||
        actual, lo que podría causar que algunos enlaces no se redirijan
 | 
			
		||||
        correctamente. Por favor, verifica la configuración de la URL de acceso
 | 
			
		||||
        externo.
 | 
			
		||||
      external_url_invalid: La URL de acceso externo detectada no coincide con la URL de acceso actual, lo que podría causar que algunos enlaces no se redirijan correctamente. Por favor, verifica la configuración de la URL de acceso externo.
 | 
			
		||||
  backup:
 | 
			
		||||
    title: Copia de Seguridad y Restauración
 | 
			
		||||
    tabs:
 | 
			
		||||
| 
						 | 
				
			
			@ -1061,19 +905,14 @@ core:
 | 
			
		|||
      create:
 | 
			
		||||
        button: Crear copia de seguridad
 | 
			
		||||
        title: Crear copia de seguridad
 | 
			
		||||
        description: >-
 | 
			
		||||
          ¿Estás seguro de que deseas crear una copia de seguridad? Esta
 | 
			
		||||
          operación puede tomar un tiempo.
 | 
			
		||||
        description: ¿Estás seguro de que deseas crear una copia de seguridad? Esta operación puede tomar un tiempo.
 | 
			
		||||
        toast_success: Solicitud de creación de copia de seguridad realizada
 | 
			
		||||
      delete:
 | 
			
		||||
        title: Eliminar copia de seguridad
 | 
			
		||||
        description: ¿Estás seguro de que deseas eliminar esta copia de seguridad?
 | 
			
		||||
      restore:
 | 
			
		||||
        title: Restauración exitosa
 | 
			
		||||
        description: >-
 | 
			
		||||
          Después de una restauración exitosa, necesitas reiniciar Halo para
 | 
			
		||||
          cargar los recursos del sistema normalmente. Después de hacer clic en
 | 
			
		||||
          OK, reiniciaremos automáticamente Halo.
 | 
			
		||||
        description: Después de una restauración exitosa, necesitas reiniciar Halo para cargar los recursos del sistema normalmente. Después de hacer clic en OK, reiniciaremos automáticamente Halo.
 | 
			
		||||
      restart:
 | 
			
		||||
        toast_success: Solicitud de reinicio realizada
 | 
			
		||||
    list:
 | 
			
		||||
| 
						 | 
				
			
			@ -1086,15 +925,9 @@ core:
 | 
			
		|||
        expiresAt: Expira el {expiresAt}
 | 
			
		||||
    restore:
 | 
			
		||||
      tips:
 | 
			
		||||
        first: >-
 | 
			
		||||
          1. El proceso de restauración puede tomar un tiempo, por favor no
 | 
			
		||||
          actualices la página durante este período.
 | 
			
		||||
        second: >-
 | 
			
		||||
          2. Durante el proceso de restauración, aunque los datos existentes no
 | 
			
		||||
          se eliminarán, si hay un conflicto, los datos se sobrescribirán.
 | 
			
		||||
        third: >-
 | 
			
		||||
          3. Después de completar la restauración, necesitas reiniciar Halo para
 | 
			
		||||
          cargar los recursos del sistema normalmente.
 | 
			
		||||
        first: 1. El proceso de restauración puede tomar un tiempo, por favor no actualices la página durante este período.
 | 
			
		||||
        second: 2. Durante el proceso de restauración, aunque los datos existentes no se eliminarán, si hay un conflicto, los datos se sobrescribirán.
 | 
			
		||||
        third: 3. Después de completar la restauración, necesitas reiniciar Halo para cargar los recursos del sistema normalmente.
 | 
			
		||||
        complete: Restauración completada, esperando reinicio...
 | 
			
		||||
      start: Iniciar Restauración
 | 
			
		||||
  exception:
 | 
			
		||||
| 
						 | 
				
			
			@ -1211,9 +1044,7 @@ core:
 | 
			
		|||
      size_label: elementos por página
 | 
			
		||||
      total_label: Total de {total} elementos
 | 
			
		||||
    app_download_alert:
 | 
			
		||||
      description: >-
 | 
			
		||||
        Los temas y complementos para Halo se pueden descargar en las siguientes
 | 
			
		||||
        direcciones:
 | 
			
		||||
      description: "Los temas y complementos para Halo se pueden descargar en las siguientes direcciones:"
 | 
			
		||||
      sources:
 | 
			
		||||
        app_store: "Tienda de aplicaciones oficial: {url}"
 | 
			
		||||
        github: "GitHub: {url}"
 | 
			
		||||
| 
						 | 
				
			
			@ -1253,7 +1084,6 @@ core:
 | 
			
		|||
      preview: Vista previa
 | 
			
		||||
      recovery: Recuperar
 | 
			
		||||
      delete_permanently: Borrar permanentemente
 | 
			
		||||
      active: Activar
 | 
			
		||||
      download: Descargar
 | 
			
		||||
      copy: Copiar
 | 
			
		||||
      upload: Subir
 | 
			
		||||
| 
						 | 
				
			
			@ -1298,9 +1128,7 @@ core:
 | 
			
		|||
        warning: Advertencia
 | 
			
		||||
      descriptions:
 | 
			
		||||
        cannot_be_recovered: Esta operación es irreversible.
 | 
			
		||||
        editor_not_found: >-
 | 
			
		||||
          No se encontró ningún editor que coincida con el formato {raw_type}.
 | 
			
		||||
          Por favor verifica si el complemento del editor ha sido instalado.
 | 
			
		||||
        editor_not_found: No se encontró ningún editor que coincida con el formato {raw_type}. Por favor verifica si el complemento del editor ha sido instalado.
 | 
			
		||||
    filters:
 | 
			
		||||
      results:
 | 
			
		||||
        keyword: "Palabra clave: {keyword}"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -223,6 +223,14 @@ core:
 | 
			
		|||
        allow_comment_value: 選擇是否允許評論
 | 
			
		||||
        owner_group: 作者
 | 
			
		||||
        owner_value: 設置作者
 | 
			
		||||
    tag:
 | 
			
		||||
      filters:
 | 
			
		||||
        sort:
 | 
			
		||||
          items:
 | 
			
		||||
            create_time_desc: 較近建立
 | 
			
		||||
            create_time_asc: 較早建立
 | 
			
		||||
            display_name_desc: 標籤名稱降序
 | 
			
		||||
            display_name_asc: 標籤名稱升序
 | 
			
		||||
  deleted_post:
 | 
			
		||||
    title: 文章回收站
 | 
			
		||||
    empty:
 | 
			
		||||
| 
						 | 
				
			
			@ -1246,6 +1254,7 @@ core:
 | 
			
		|||
      list:
 | 
			
		||||
        fields:
 | 
			
		||||
          last_accessed_time: 上次訪問:{time}
 | 
			
		||||
          current: 目前設備
 | 
			
		||||
      detail_modal:
 | 
			
		||||
        title: 登入設備詳情
 | 
			
		||||
        fields:
 | 
			
		||||
| 
						 | 
				
			
			@ -1547,9 +1556,7 @@ core:
 | 
			
		|||
        aspect_ratio_portrait: 裁剪為縱向 (9:16)
 | 
			
		||||
    h2_warning_alert:
 | 
			
		||||
      title: 警告:正在使用 H2 資料庫
 | 
			
		||||
      description: >-
 | 
			
		||||
        H2 資料庫僅適用於開發環境和測試環境,不建議在生產環境中使用,H2
 | 
			
		||||
        非常容易因為操作不當而導致資料檔案損壞。如果必須要使用,請按時進行資料備份。
 | 
			
		||||
      description: H2 資料庫僅適用於開發環境和測試環境,不建議在生產環境中使用,H2 非常容易因為操作不當而導致資料檔案損壞。如果必須要使用,請按時進行資料備份。
 | 
			
		||||
  composables:
 | 
			
		||||
    content_cache:
 | 
			
		||||
      toast_recovered: 已從緩存中恢復未保存的內容
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue