From bc0df21a3035da124b59a2baefdef73890a31c83 Mon Sep 17 00:00:00 2001 From: Jairo Olaya Date: Thu, 12 Nov 2026 22:04:29 -0500 Subject: [PATCH] Add spanish translation --- AGENTS.md | 2 +- assets/i18n/{en.json => en_US.json} | 9 +- assets/i18n/es_419.json | 2185 ++++++++++++++++++++++++ assets/i18n/pt_BR.json | 9 +- assets/i18n/{vi.json => vi_VN.json} | 9 +- assets/i18n/zh_CN.json | 9 +- crates/benchup-app/src/app_tests.rs | 5 +- crates/benchup-app/src/estimate_pdf.rs | 4 +- crates/benchup-app/src/export_scale.rs | 17 +- crates/benchup-app/src/language.rs | 36 +- crates/benchup-app/src/settings.rs | 81 +- crates/benchup-app/src/sidebar.rs | 8 +- crates/benchup-app/src/space_mouse.rs | 3 +- crates/benchup-core/build.rs | 9 +- crates/benchup-core/src/history.rs | 4 +- crates/benchup-core/src/i18n.rs | 33 +- index.html | 2 +- scripts/test_project_save.cjs | 2 +- scripts/test_web_i18n.cjs | 4 +- 19 files changed, 2362 insertions(+), 69 deletions(-) rename assets/i18n/{en.json => en_US.json} (99%) create mode 100644 assets/i18n/es_419.json rename assets/i18n/{vi.json => vi_VN.json} (99%) diff --git a/AGENTS.md b/AGENTS.md index 1a4da90..23532dd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -53,7 +53,7 @@ All current and future right-sidebar tables use `crate::inspector_table::Inspect English is the source language for all user-facing application text, even when the user requests features or changes in any language. Display text in the user's selected application language through the existing translation system. This includes section titles, menus, controls, tooltips, notifications, and errors. Preserve stable internal preference keys and saved-project compatibility when translating visible text. - Every new or changed user-facing label must be translatable. Use the existing `tr!` message lookup with a stable catalog key instead of hard-coded display text. -- Reuse an existing message key when appropriate; otherwise add the English source message to `assets/i18n/en.json` and corresponding translations to every supported language catalog in `assets/i18n/`. Keep named placeholders and plural forms consistent across catalogs. +- Reuse an existing message key when appropriate; otherwise add the English source message to `assets/i18n/en_US.json` and corresponding translations to every supported language catalog in `assets/i18n/`. Keep named placeholders and plural forms consistent across catalogs. # PDF export header diff --git a/assets/i18n/en.json b/assets/i18n/en_US.json similarity index 99% rename from assets/i18n/en.json rename to assets/i18n/en_US.json index e060115..85619e2 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en_US.json @@ -1342,7 +1342,7 @@ "settings.inference_points": "Inference points", "settings.infinite_grid": "Infinite grid", "settings.language": "Language", - "settings.language_english": "English", + "settings.language.english": "English", "settings.length_of_each_side_of_the_square_centered": "Length of each side of the square, centered on the model origin. The boundary stays fixed when zooming or rotating.", "settings.material": "Material", "settings.materials_estimates": "Materials & estimates", @@ -2067,9 +2067,10 @@ "settings.invert_pan": "Invert pan", "settings.invert_tilt_and_twist": "Invert tilt and twist", "settings.invert_zoom": "Invert zoom", - "settings.language_simplified_chinese": "Simplified Chinese", - "settings.language_brazilian_portuguese": "Brazilian Portuguese", - "settings.language_vietnamese": "Vietnamese", + "settings.language.simplified_chinese": "Simplified Chinese", + "settings.language.brazilian_portuguese": "Brazilian Portuguese", + "settings.language.vietnamese": "Vietnamese", + "settings.language.spanish": "Spanish", "settings.look_sensitivity": "Look sensitivity", "settings.mouse_trackpad": "Mouse/Trackpad", "settings.navigation_type": "Navigation type", diff --git a/assets/i18n/es_419.json b/assets/i18n/es_419.json new file mode 100644 index 0000000..cd3e14d --- /dev/null +++ b/assets/i18n/es_419.json @@ -0,0 +1,2185 @@ +{ + "account.account": "Cuenta", + "account.are_you_sure_you_want_to_log_out": "¿Seguro que deseas cerrar sesión?", + "account.cannot_connect_to_server": "No se puede conectar con el servidor", + "account.cannot_connect_to_the_configured_server_please_retry": "No se puede conectar con el servidor configurado. Inténtalo de nuevo.", + "account.cannot_contact_project_server": "No se puede contactar con el servidor de proyectos", + "account.cannot_load_account_picture": "No se puede cargar la imagen de la cuenta", + "account.cannot_locate_account_storage": "No se puede ubicar el almacenamiento de la cuenta", + "account.cannot_read_account_picture": "No se puede leer la imagen de la cuenta", + "account.cannot_read_server_response": "No se puede leer la respuesta del servidor", + "account.cannot_save_guest_identity": "No se puede guardar la identidad de invitado", + "account.complete_login_and_authorize_benchup_in_your_browser": "Completa el inicio de sesión y autoriza BenchUp en tu navegador…", + "account.config_cannot_read_file": "No se puede leer {path}: {error}", + "account.config_file_error": "{path}: {error}", + "account.config_invalid_unicode": "{key} debe estar en formato UTF-8", + "account.config_invalid_value": "{key} debe contener la URL de un servidor", + "account.config_missing": "No hay ningún servidor de cuentas configurado. Define {key} en .env o al compilar, o inicia con --server-url URL.", + "account.config_missing_argument": "--server-url requiere una URL", + "account.config_missing_equals": "Falta '=' para {key} en la línea {line}", + "account.config_trailing_text": "Texto inesperado después de {key} en la línea {line}", + "account.config_unclosed_quote": "Comillas sin cerrar para {key} en la línea {line}", + "account.connect_your_account_using_google_in_your_browser": "Conecta tu cuenta usando Google en tu navegador.", + "account.continue_with_google": "Continuar con Google", + "account.invalid_account_server_configuration_use_https_or_http": "Configuración de servidor de cuentas no válida: usa HTTPS, o HTTP en localhost.", + "account.invalid_login_address": "Dirección de inicio de sesión no válida", + "account.invalid_logout_response": "Respuesta de cierre de sesión no válida", + "account.invalid_project_response": "Respuesta del proyecto no válida", + "account.invalid_project_server_configuration": "Configuración del servidor de proyectos no válida", + "account.invalid_server_address": "Dirección del servidor no válida", + "account.invalid_server_login_response": "Respuesta de inicio de sesión del servidor no válida", + "account.invalid_server_profile": "Perfil del servidor no válido", + "account.invalid_server_session_response": "Respuesta de sesión del servidor no válida", + "account.log_in_to_benchup": "Iniciar sesión en BenchUp", + "account.log_out": "Cerrar sesión", + "account.logging_out": "Cerrando sesión…", + "account.login_timed_out_continue_with_google_to_try": "Se agotó el tiempo de inicio de sesión. Continúa con Google para intentarlo de nuevo.", + "account.missing_http_status": "Falta el estado HTTP", + "account.no_browser": "No hay navegador disponible", + "account.project_server_unavailable": "Servidor de proyectos no disponible: {e}", + "account.signed_out_but_could_not_remove_the_saved": "Se cerró la sesión, pero no se pudo eliminar la credencial guardada.", + "account.storage_unavailable": "Almacenamiento no disponible", + "account.the_server_did_not_sign_out": "El servidor no cerró la sesión.", + "account.use_https_or_http_on_localhost_for_development": "Usa HTTPS, o HTTP en localhost para desarrollo.", + "app.about_benchup": "Acerca de BenchUp", + "app.account": "Cuenta", + "app.account_not_connected": "Cuenta · Sin conexión", + "app.aligned": " · {} alineado", + "app.angle": "Ángulo", + "app.appearance": "Apariencia", + "app.ascii_or_binary_geometry_only_coordinates_interpreted_as": "ASCII o binario. Solo geometría; las coordenadas se interpretan en milímetros, eje Z hacia arriba.", + "app.assign_stock_material": "Asignar material de stock", + "app.automatically_align_drawing_with_the_red_green_and": "Alinea automáticamente el dibujo con los ejes rojo, verde y azul. Pasa el cursor sobre un extremo, punto medio o arista para capturar ejes desde esa posición. Las teclas de flecha pueden bloquear un eje cuando esta opción está desactivada.", + "app.autosaved_on_this_device_use_file_save_to": "Guardado automáticamente en este dispositivo. Usa Archivo → Guardar para actualizar el archivo del proyecto.", + "app.binary_stl_of_visible_faces_in_millimetres_z": "STL binario de caras visibles en milímetros, eje Z hacia arriba. Sin aristas ni materiales. Asegúrate de que los sólidos estén cerrados antes de imprimir; no hay reparación automática.", + "app.cannot_paste_this_geometry_here_a_component_cannot": "No se puede pegar esta geometría aquí; un componente no puede contener su propia definición", + "app.center_view_on_axis_origin_0_0_0": "Centrar vista en el origen de ejes (0, 0, 0)", + "app.check_for_updates": "Buscar actualizaciones…", + "app.choose_a_modeling_tool": "Elige una herramienta de modelado", + "app.clean_aligned_vertices": "Limpiar vértices alineados", + "app.clear_color": "Quitar color", + "app.clear_color_2": "Quitar color", + "app.click_a_face_in_a_group_or_component": "Haz clic en una cara dentro de un grupo o componente para pintar todas sus caras.", + "app.click_a_face_to_apply_this_color": "Haz clic en una cara para aplicar este color.", + "app.click_a_filled_face_first": "Haz clic primero en una cara rellena", + "app.click_a_reference_point_away_from_the_pivot": "Haz clic primero en un punto de referencia alejado del pivote", + "app.click_a_reference_point_away_from_the_pivot_2": "Haz clic en un punto de referencia alejado del pivote · Esc para cancelar", + "app.click_the_opposite_corner_type_width_height_5m": "Haz clic en la esquina opuesta · Escribe ancho,alto (5m,50mm) + Enter", + "app.click_to_confirm_esc_to_cancel": "{} · Haz clic para confirmar · Esc para cancelar", + "app.click_to_confirm_esc_to_cancel_2": "{value:.3} × · Haz clic para confirmar · Esc para cancelar", + "app.click_to_confirm_esc_to_cancel_3": "{}{}{alignment} · Haz clic para confirmar · Esc para cancelar", + "app.click_to_finish_type_length_enter_esc_to": "Haz clic para finalizar · Escribe longitud + Enter · Esc para detener", + "app.click_to_place_the_copy_escape_to_cancel": "Haz clic para situar la copia · Escape para cancelar", + "app.click_to_set_the_radius_type_a_radius": "Haz clic para definir el radio · Escribe un radio (5cm) + Enter · Esc para cancelar", + "app.close_level_esc": "Cerrar nivel (Esc)", + "app.component": "Componente", + "app.configure_edge_face_face_center_axis_and_grid": "Configura el ajuste a aristas, caras, centros de cara, ejes y cuadrícula", + "app.copies": "Copias", + "app.copy": "Copiar · ", + "app.copy_ctrl_toggles_move_click_to_place_type": "Copiar · Ctrl alterna Mover · Haz clic para situar · Escribe distancia + Enter", + "app.copy_option_toggles_move_click_to_place_type": "Copiar · Opción alterna Mover · Haz clic para situar · Escribe distancia + Enter", + "app.copy_placed_type_3_to_divide_the_span": "Copia situada · Escribe /3 para dividir el tramo o 3x para repetir · Enter para aplicar", + "app.could_not_restore_the_project_file_location_after": "No se pudo restaurar la ubicación del archivo del proyecto tras actualizar: {error}", + "app.could_not_restore_your_model_a_new_workspace": "No se pudo restaurar tu modelo: {e}. Hay un nuevo espacio de trabajo listo.", + "app.create_a_printable_label_for_each_piece_in": "Crea una etiqueta imprimible para cada pieza de la selección o de todo el proyecto.", + "app.create_component": "Crear componente", + "app.create_component_2": "Crear componente", + "app.delete_guides": "Eliminar guías", + "app.dimension": "cota", + "app.distance": "Distancia", + "app.download_desktop_app": "Descargar aplicación de escritorio", + "app.arc_choose_end": "Haz clic en el segundo extremo · Escribe longitud de cuerda + Enter · Esc para cancelar", + "app.arc_choose_through_point": "Haz clic en un punto del arco · Escribe altura de arco + Enter · Esc para cancelar", + "app.arc_chord": "Longitud de cuerda", + "app.arc_distinct_endpoints": "Elige un segundo extremo distinto.", + "app.arc_height": "Altura de arco", + "app.arc_lock_must_contain_endpoints": "El plano bloqueado debe contener ambos extremos del arco.", + "app.arc_point_off_chord": "Elige un punto alejado de la línea entre los extremos.", + "app.draw_arc": "Dibujar arco", + "app.draw_circle": "Dibujar círculo", + "app.draw_freehand": "Dibujar a mano alzada", + "app.draw_line": "Dibujar línea", + "app.draw_polygon": "Dibujar polígono", + "app.draw_rectangle": "Dibujar rectángulo", + "app.dxf_2d_dxf": "DXF 2D (.dxf)…", + "app.edge_and_face_selection": "{} · {}", + "app.edit": "Editar", + "app.editable_groups_holes_and_solid_colors_no_textures": "Grupos editables, huecos y colores sólidos. Sin texturas ni anotaciones.", + "app.enter_an_angle_or_formula_such_as_90": "Introduce un ángulo o fórmula, como 90 o (45 + 45) / 2. Los números simples usan {}. Añade deg, °, rad o gon tras la fórmula para anular la unidad.", + "app.erase_geometry": "Borrar geometría", + "app.erase_selection": "Borrar selección", + "app.export": "Exportar", + "app.export_time_and_cost_estimates_as_pdf": "Exportar estimaciones de tiempo y costo como PDF", + "app.faces_and_lines_including_negative_vertex_indices_metres": "Caras y líneas, incluidos índices de vértices negativos. Metros, eje Z hacia arriba. Sin materiales, texturas ni jerarquía de grupos.", + "app.factor": "Factor", + "app.file": "Archivo", + "app.fit_model_to_view": "Ajustar modelo a la vista", + "app.fit_selection_to_view": "Ajustar selección a la vista", + "app.generate_time_cost_estimates": "Generar estimaciones de tiempo y costo…", + "app.github": "GitHub", + "app.give_your_selection_a_name": "Asigna un nombre a tu selección.", + "app.group": "Grupo", + "app.help": "Ayuda", + "app.history_menu_action": "{action} {label}", + "app.hole_side_lengths": "Longitudes de los lados del hueco {index}", + "app.import": "Importar", + "app.inspector": "Inspector", + "app.benchup": "{} - BenchUp", + "app.benchup_is_a_3d_modeling_app_for_turning": "BenchUp es una aplicación de modelado 3D para convertir ideas en formas. Dibuja geometría precisa, da forma a superficies y organiza tu modelo con grupos y componentes.", + "app.keyboard_shortcuts": "Atajos de teclado", + "app.length": "Longitud", + "app.locked": "Bloqueado", + "app.locked_2": "{} · Bloqueado", + "app.make_group": "Crear grupo", + "app.make_group_2": "Crear grupo", + "app.maximize": "Maximizar", + "app.minimize": "Minimizar", + "app.missing_material": "Falta el material", + "app.mixed": "Mixto", + "app.model": "Modelo", + "app.modified_saved_locally": "Modificado · guardado localmente", + "app.move": "Mover · ", + "app.move_copy": "Mover copia", + "app.move_ctrl_toggles_copy_click_base_point_and": "Mover · Ctrl alterna Copiar · Haz clic en el punto base y en el destino", + "app.move_option_toggles_copy_click_base_point_and": "Mover · Option alterna Copiar · Haz clic en el punto base y en el destino", + "app.move_over_the_drawing_plane_to_set_a": "Mueve el cursor sobre el plano de dibujo para definir una dirección", + "app.move_the_cursor_to_orbit_hold_shift_to": "Mueve el cursor para orbitar · Mantén Shift para desplazar · Suelta Ctrl / ⌘ para seguir dibujando", + "app.move_the_pointer_to_choose_a_direction_first": "Mueve el puntero para elegir primero una dirección", + "app.move_the_pointer_to_give_the_shape_some": "Mueve el puntero para darle tamaño a la forma", + "app.new_faces": "Caras nuevas · ", + "app.new_project": "Nuevo proyecto", + "app.new_project_ready": "Nuevo proyecto listo", + "app.new_workspace": "Nuevo espacio de trabajo", + "app.no_group_open": "Ningún grupo abierto", + "app.no_objects_selected": "Ningún objeto seleccionado", + "app.nothing_selected": "Nada seleccionado", + "app.object_selection": "{kind}: {}", + "app.offset": "Desfase", + "app.orbiting_hold_shift_to_pan": "Orbitando · Mantén Shift para desplazar", + "app.paint_face": "Pintar cara", + "app.paint_group": "Pintar grupo", + "app.panning_release_shift_to_orbit": "Desplazando · Suelta Shift para orbitar", + "app.panning_hold_shift_to_orbit": "Desplazando · Mantén Shift para orbitar", + "app.orbiting_release_shift_to_pan": "Orbitando · Suelta Shift para desplazar", + "app.paste": "Pegar", + "app.perimeter": "Perímetro", + "app.plane_locked": "Plano {} · Bloqueado", + "app.plane_locked_2": "{} · Plano bloqueado", + "app.polygon_sides": "Lados del polígono", + "app.prepare_cutting_plan": "Preparar plan de corte…", + "app.print_workshop_labels": "Imprimir etiquetas de taller…", + "app.push_pull_new_faces_status": "Empujar/Tirar · Caras nuevas · Ctrl alterna el modo normal · Haz clic en una cara o confirma el empuje", + "app.quit": "Salir", + "app.radius": "Radio", + "app.release_notes": "Notas de la versión", + "app.release_to_finish_type_total_length_enter_esc": "Suelta para finalizar · Escribe la longitud total + Enter · Esc para cancelar", + "app.remove_color_from_the_selected_items_and_all": "Quita el color de los elementos seleccionados y de todos sus elementos secundarios", + "app.report_a_bug": "Reportar un error", + "app.restore": "Restaurar", + "app.save_as": "Guardar como…", + "app.saved_locally": "Guardado localmente", + "app.saving": "Guardando…", + "app.scale_factor_symbol": "×", + "app.screenshot": "Captura de pantalla", + "app.select_a_group_or_component_to_edit_its": "Selecciona un grupo o componente para editar su material, lijado y acabado.", + "app.select_an_entity_to_see_its_details": "Selecciona una entidad para ver sus detalles.", + "app.select_or_click_an_edge_or_face_first": "Selecciona o haz clic primero en una arista o cara", + "app.selected": "Seleccionado: {selection}", + "app.selection_dimensions": { + "one": "{count} cota", + "other": "{count} cotas" + }, + "app.selection_edges": { + "one": "{count} arista", + "other": "{count} aristas" + }, + "app.selection_entities": { + "one": "{count} entidad", + "other": "{count} entidades" + }, + "app.selection_faces": { + "one": "{count} cara", + "other": "{count} caras" + }, + "app.selection_guides": { + "one": "{count} guía", + "other": "{count} guías" + }, + "app.selection_separator": " · ", + "app.selection_text_labels": { + "one": "{count} etiqueta de texto", + "other": "{count} etiquetas de texto" + }, + "app.settings": "Configuración…", + "app.side_lengths": "Longitudes de los lados", + "app.sketchup_skp": "SketchUp (.skp)…", + "app.snap_points_and_moves_to_the_grid_spacing": "Ajusta los puntos y los movimientos al espaciado de la cuadrícula según el zoom actual. Los objetivos de objetos y los bloqueos de eje tienen prioridad.", + "app.snap_to_axes": "Ajustar a los ejes", + "app.snap_to_edge": "Ajustar a la arista", + "app.snap_to_face": "Ajustar a la cara", + "app.snap_to_face_center": "Ajustar al centro de la cara", + "app.snap_to_grid": "Ajustar a la cuadrícula", + "app.snap_only_visible": "Solo al elemento visible", + "app.snap_only_visible_tooltip": "Ajusta solo a la arista o cara que está justo debajo del cursor, excluyendo la geometría que queda detrás de otros objetos. El ajuste a ejes y a la cuadrícula sigue siendo independiente.", + "app.snap_to_object_edges_including_their_endpoints_and": "Ajusta a las aristas de los objetos, incluidos sus extremos y puntos medios.", + "app.snap_to_object_surfaces_independently_of_edge_and": "Ajusta a las superficies de los objetos, independientemente del ajuste a aristas y centros de cara.", + "app.snap_to_the_area_center_of_a_visible": "Ajusta al centro del área de una cara visible, independientemente del ajuste a aristas y superficies.", + "app.snapping": "Ajuste", + "app.stl_stl_millimetres": "STL (.stl; milímetros)…", + "app.stock_material": "Material de stock", + "app.suggest_a_feature": "Sugerir una función", + "app.suggest_a_feature_on_github": "Sugerir una función en GitHub", + "app.summarize_matching_parts_and_export_finished_cutting_dimensions": "Resume las piezas coincidentes y exporta las dimensiones finales de corte de la selección o de todo el proyecto.", + "app.surface_area": "Área de superficie", + "app.switch_to_dark_mode": "Cambiar a modo oscuro", + "app.switch_to_light_mode": "Cambiar a modo claro", + "app.system_information": "Información del sistema", + "app.tape": "Cinta", + "app.text_dxf_line_straight_lwpolyline_arc_and_circle": "DXF de texto: LINE, LWPOLYLINE recta, ARC y CIRCLE en XY. Las curvas se aproximan con hasta 96 segmentos por círculo. Sin bloques, curvaturas ni anchos. Unidades declaradas; los archivos sin unidades usan milímetros.", + "app.the_clipboard_geometry_could_not_be_read": "No se pudo leer la geometría del portapapeles", + "app.the_reference_lies_on_the_rotation_axis_clear": "La referencia está sobre el eje de rotación; quita el bloqueo o reinicia con otra referencia", + "app.toggle_model_inspector_tab": "Mostrar u ocultar el inspector del modelo (Tab)", + "app.toggle_x_axis_lock_select_up_to_two": "Alternar el bloqueo del eje X (→). Selecciona hasta dos ejes para bloquear su plano; ↓ quita todos los bloqueos. Las formas y Girar usan un solo bloqueo como normal de su plano.", + "app.toggle_y_axis_lock_select_up_to_two": "Alternar el bloqueo del eje Y (←). Selecciona hasta dos ejes para bloquear su plano; ↓ quita todos los bloqueos. Las formas y Girar usan un solo bloqueo como normal de su plano.", + "app.toggle_z_axis_lock_select_up_to_two": "Alternar el bloqueo del eje Z (↑). Selecciona hasta dos ejes para bloquear su plano; ↓ quita todos los bloqueos. Las formas y Girar usan un solo bloqueo como normal de su plano.", + "app.tool_with_shortcut": "{tool} ({shortcut})", + "app.tools": "Herramientas", + "app.total_edge_length": "Longitud total de aristas", + "app.total_perimeter": "Perímetro total", + "app.unassigned": "Sin asignar", + "app.unlock_an_axis_before_locking_another_maximum_two": "Desbloquea un eje antes de bloquear otro (máximo dos)", + "app.unlock_the_face_before_editing_it": "Desbloquea la cara antes de editarla", + "app.unlock_the_object_before_opening_it": "Desbloquea el objeto antes de abrirlo", + "app.unlock_the_selection_before_editing_it": "Desbloquea la selección antes de editarla", + "app.unsaved": "Sin guardar", + "app.update_ready": "Actualización lista", + "app.use_lengths_or_formulas_60cm_89mm_1m_20cm": "Usa longitudes o fórmulas: 60cm - 89mm, (1m + 20cm) / 2. Operadores: + - * / y paréntesis. Los números sin unidad usan {} por defecto; los multiplicadores no llevan unidad. Unidades: mm, cm, m, km, in, ft, yd. Los rectángulos aceptan una fórmula por dimensión, separadas por una coma. Escalar usa números sin unidades.", + "app.version": "Versión {}", + "app.view": "Vista", + "app.visible_edges_and_triangulated_faces_in_metres_z": "Aristas visibles y caras trianguladas en metros, eje Z hacia arriba. Solo geometría.", + "app.volume": "Volumen", + "app.wavefront_obj_obj": "Wavefront OBJ (.obj)…", + "app.wavefront_obj_obj_metres": "Wavefront OBJ (.obj; metros)…", + "app.width_height": "Ancho, alto", + "app.wood_joinery": "Ensambles de madera", + "app.your_model_stays_on_this_device": "Tu modelo permanece en este dispositivo", + "app.your_work_is_saved_automatically_on_this_device": "Tu trabajo se guarda automáticamente en este dispositivo. Guarda un proyecto .bup para conservar una copia portátil. También se admiten archivos .bup antiguos.", + "app_updates.automatic_checks_run_at_most_once_a_day": "Las comprobaciones automáticas se ejecutan como máximo una vez al día. Las actualizaciones descargadas se instalan en el siguiente inicio. Si desactivas esta opción, se detienen las futuras comprobaciones y descargas automáticas.", + "app_updates.automatically_check_and_download_updates": "Buscar y descargar actualizaciones automáticamente", + "app_updates.check_now": "Comprobar ahora", + "app_updates.could_not_save_your_workspace_for_restart": "No se pudo guardar tu espacio de trabajo para reiniciar: {e}", + "app_updates.finish_the_current_file_operation_or_modeling_preview": "Termina la operación de archivo actual o la vista previa de modelado antes de reiniciar.", + "app_updates.benchup": "BenchUp {}", + "app_updates.release_notes": "Notas de la versión", + "app_updates.restart_now": "Reiniciar ahora", + "app_updates.restarting_saves_your_model_locally_and_restores_it": "Al reiniciar, tu modelo se guarda localmente y se restaura después de actualizar. Termina antes cualquier dibujo o vista previa de modelado.", + "app_updates.software_updates": "Actualizaciones de software", + "app_updates.your_model_could_not_be_saved_benchup_will": "No se pudo guardar tu modelo. BenchUp permanecerá abierto: {error}", + "app_updates.your_previous_workspace_could_not_be_restored_make": "No se pudo restaurar tu espacio de trabajo anterior. Haz una edición y espera al autoguardado local para conservar este espacio de trabajo antes de reiniciar.", + "common.add": "Agregar", + "common.apply": "Aplicar", + "common.cancel": "Cancelar", + "common.clear": "Limpiar", + "common.close": "Cerrar", + "common.create": "Crear", + "common.delete": "Eliminar", + "common.name": "Nombre", + "common.none": "Ninguno", + "common.open": "Abrir…", + "common.pieces": { + "one": "{count} pieza", + "other": "{count} piezas" + }, + "common.redo": "Rehacer", + "common.remove": "Quitar", + "common.save": "Guardar", + "common.undo": "Deshacer", + "construction_guides.delete_guides": "Eliminar guías", + "context_menu.automatic_fasteners": "Fijaciones automáticas", + "context_menu.choose_a_name_for_this_object": "Elige un nombre para este objeto.", + "context_menu.clean_aligned_vertices": "Limpiar vértices alineados", + "context_menu.click_to_place_the_copy_escape_to_cancel": "Haz clic para colocar la copia · Escape para cancelar", + "context_menu.close_editing_context": "Cerrar contexto de edición", + "context_menu.copy": "Copiar", + "context_menu.create_component": "Crear componente…", + "context_menu.cut": "Cortar", + "context_menu.duplicate": "Duplicar", + "context_menu.edit_component": "Editar componente", + "context_menu.edit_group": "Editar grupo", + "context_menu.edit_text": "Editar texto…", + "context_menu.explode": "Descomponer", + "context_menu.hide": "Ocultar", + "context_menu.lock": "Bloquear", + "context_menu.make_group": "Crear grupo…", + "context_menu.no_redundant_aligned_vertices_in_the_selection": "No hay vértices alineados redundantes en la selección", + "context_menu.paste": "Pegar", + "context_menu.paste_in_place": "Pegar en el mismo lugar", + "context_menu.removed_redundant_vertices": { + "one": "Se quitó {count} vértice redundante", + "other": "Se quitaron {count} vértices redundantes" + }, + "context_menu.rename": "Cambiar nombre…", + "context_menu.rename_2": "Cambiar nombre", + "context_menu.ungroup": "Desagrupar", + "context_menu.unhide": "Mostrar", + "context_menu.unlock": "Desbloquear", + "core.annotations.enter_text_between_1_and_4096_bytes": "Escribe un texto de entre 1 y 4096 bytes", + "core.annotations.invalid_text_annotation": "Anotación de texto no válida", + "core.annotations.text_coordinates_must_be_finite_and_within_the": "Las coordenadas del texto deben ser finitas y estar dentro de los límites del modelo", + "core.annotations.text_is_outside_the_model_limits": "El texto está fuera de los límites del modelo", + "core.annotations.text_no_longer_exists": "El texto ya no existe", + "core.annotations.unlock_the_text_before_editing_it": "Desbloquea el texto antes de editarlo", + "core.camera.back": "Posterior", + "core.camera.bottom": "Inferior", + "core.camera.front": "Frontal", + "core.camera.iso": "Iso", + "core.camera.left": "Izquierda", + "core.camera.right": "Derecha", + "core.camera.top": "Superior", + "core.cleanup.unlock_the_selection_before_cleaning_it": "Desbloquea la selección antes de limpiarla", + "core.connections.angled_screw": "Tornillo en ángulo", + "core.connections.close_to_an_edge_check_the_geometric_clearance": "Cerca de una arista: revisa la holgura geométrica", + "core.connections.degenerate_face": "Cara degenerada", + "core.connections.fastener_is_outside_the_entry_face": "La fijación {} está fuera de la cara de entrada", + "core.connections.fastener_is_outside_the_second_part": "La fijación está fuera de la segunda pieza", + "core.connections.fasteners_overlap_or_there_is_not_enough_space": "Las fijaciones se superponen o no hay espacio suficiente para este patrón", + "core.connections.format": " · ", + "core.connections.invalid_settings_or_pair_of_parts": "Configuración o par de piezas no válidos", + "core.connections.invalid_vertices": "Vértices no válidos", + "core.connections.maximum_of_1_000_fasteners_per_connection": "Máximo de 1000 fijaciones por conexión", + "core.connections.nail": "Clavo", + "core.connections.no_penetration_into_the_second_part": "Sin penetración en la segunda pieza", + "core.connections.no_shared_area_on_this_face": "No hay área compartida en esta cara", + "core.connections.only_planar_faces_are_supported": "Solo se admiten caras planas", + "core.connections.part_is_missing": "Falta la pieza", + "core.connections.part_is_missing_or_has_been_deleted": "Falta la pieza o fue eliminada", + "core.connections.screw": "Tornillo", + "core.connections.select_two_visible_editable_boards_in_the_current": "Selecciona dos tablas visibles y editables en el contexto actual", + "core.connections.the_entry_face_is_missing_or_does_not": "Falta la cara de entrada o no pertenece a la primera pieza", + "core.connections.the_part_must_be_a_closed_solid": "La pieza debe ser un sólido cerrado", + "core.connections.the_parts_have_no_usable_contact_in_this": "Las piezas no tienen contacto utilizable en esta dirección", + "core.connections.the_tip_protrudes_beyond_the_second_part": "La punta sobresale de la segunda pieza", + "core.connections.these_pieces_already_have_a_wood_joint_edit": "Estas piezas ya tienen un ensamble de madera. Edita o elimina primero el ensamble existente.", + "core.csg.these_solids_are_too_complex_for_this_operation": "Estos sólidos son demasiado complejos para esta operación", + "core.csg.these_solids_need_too_many_clipping_planes": "Estos sólidos requieren demasiados planos de recorte", + "core.display.hidden_line": "Líneas ocultas", + "core.display.monochrome": "Monocromo", + "core.display.shaded": "Sombreado", + "core.display.shaded_with_materials": "Sombreado con materiales", + "core.display.wireframe": "Estructura alámbrica", + "core.document.a_map_or_an_empty_legacy_array": "un mapa o una matriz antigua vacía", + "core.document.coordinates_must_be_finite": "Las coordenadas deben ser finitas", + "core.document.coordinates_must_be_within_ten_billion_meters_of": "Las coordenadas deben estar a menos de diez mil millones de metros del origen", + "core.document.expected_a_map_only_empty_legacy_arrays_are": "se esperaba un mapa; solo se admiten matrices antiguas vacías", + "core.document.face_boundary_is_missing_an_edge": "Al contorno de la cara le falta una arista", + "core.document.give_your_line_a_little_length": "Dale un poco de longitud a tu línea", + "core.document.invalid_camera": "Cámara no válida", + "core.document.invalid_connection_parameters": "Parámetros de conexión no válidos", + "core.document.invalid_construction_guide": "Guía de construcción no válida", + "core.document.invalid_display_settings": "Configuración de visualización no válida", + "core.document.invalid_edge": "Arista no válida", + "core.document.invalid_face_boundary": "Contorno de cara no válido", + "core.document.invalid_face_id": "ID de cara no válido", + "core.document.invalid_face_surface": "Superficie de cara no válida", + "core.document.invalid_model_ids": "ID de modelo no válidos", + "core.document.invalid_vertex": "Vértice no válido", + "core.document.model_is_too_large": "El modelo es demasiado grande", + "core.document.paint_refers_to_a_missing_face": "La pintura hace referencia a una cara inexistente", + "core.document.that_edge_already_exists": "Esa arista ya existe", + "core.document.unsupported_document_version": "Versión de documento no compatible", + "core.document.untitled_model": "Modelo sin título", + "core.edit_context.a_component_cannot_contain_its_own_definition": "Un componente no puede contener su propia definición", + "core.edit_context.cannot_move_objects_between_instances_of_the_same": "No se pueden mover objetos entre instancias del mismo componente", + "core.edit_context.cyclic_component_definitions": "Definiciones de componente cíclicas", + "core.edit_context.missing_component": "Falta el componente", + "core.edit_context.missing_component_definition": "Falta la definición del componente", + "core.edit_context.missing_editing_context": "Falta el contexto de edición", + "core.edit_context.missing_object": "Falta el objeto", + "core.edit_context.no_object_is_open_for_editing": "Ningún objeto está abierto para edición", + "core.edit_context.the_editing_context_no_longer_exists": "El contexto de edición ya no existe", + "core.edit_context.unlock_the_object_before_editing_it": "Desbloquea el objeto antes de editarlo", + "core.guides.choose_a_direction_for_the_guide": "Elige una dirección para la guía", + "core.guides.invalid_guide_coordinates": "Coordenadas de guía no válidas", + "core.inference.aligned_axes": "Ejes alineados", + "core.inference.blue_axis": "Eje azul", + "core.inference.endpoint": "Extremo", + "core.inference.face_center": "Centro de cara", + "core.inference.green_axis": "Eje verde", + "core.inference.guide_point": "Punto de guía", + "core.inference.midpoint": "Punto medio", + "core.inference.on_blue_axis": "En el eje azul", + "core.inference.on_edge": "En la arista", + "core.inference.on_face": "En la cara", + "core.inference.on_green_axis": "En el eje verde", + "core.inference.on_guide": "En la guía", + "core.inference.on_plane": "En el plano", + "core.inference.on_red_axis": "En el eje rojo", + "core.inference.origin": "Origen", + "core.inference.red_axis": "Eje rojo", + "core.joinery.boards_must_have_finite_positive_dimensions": "Las tablas deben tener dimensiones finitas y positivas", + "core.joinery.cannot_read_joint_result": "No se puede leer el resultado de la unión", + "core.joinery.choose_a_tail_width_that_fits_the_overlap": "Elige un ancho de cola que se ajuste al solapamiento y produzca como máximo 64 colas", + "core.joinery.choose_boards_with_positive_solid_volume": "Elige tablas con volumen sólido positivo", + "core.joinery.choose_two_different_boards": "Elige dos tablas distintas", + "core.joinery.choose_two_groups_or_components": "Elige dos grupos o componentes", + "core.joinery.clearance_is_too_large_for_this_joint": "La holgura es demasiado grande para esta unión", + "core.joinery.dovetail": "Cola de milano", + "core.joinery.dovetail_angle_or_depth_leaves_no_material_at": "El ángulo o la profundidad de la cola de milano no deja material en el cuello", + "core.joinery.each_part_must_contain_only_board_geometry_select": "Cada pieza debe contener solo geometría de tabla; selecciona tablas individuales, no ensamblajes", + "core.joinery.finger_joint": "Unión de dedos", + "core.joinery.first_board": "Primera tabla", + "core.joinery.half_lap": "Media madera", + "core.joinery.invalid_wood_joint_settings_or_pair_of_boards": "Configuración o par de tablas no válidos para el ensamble de madera", + "core.joinery.joint_cut": "Corte de la unión", + "core.joinery.joint_details_are_too_small_for_the_model": "Los detalles de la unión son demasiado pequeños para la precisión del modelo", + "core.joinery.joint_result": "Resultado de la unión", + "core.joinery.lower_end": "Extremo inferior", + "core.joinery.mortise_and_tenon": "Caja y espiga", + "core.joinery.position_the_boards_with_a_shared_volume_their": "Coloca las tablas con un volumen compartido: su solapamiento define la profundidad de la unión", + "core.joinery.rabbet": "Rebaje", + "core.joinery.second_board": "Segunda tabla", + "core.joinery.the_board_has_no_geometry": "La tabla no tiene geometría", + "core.joinery.the_clearance_is_too_small_for_the_model": "La holgura es demasiado pequeña para la precisión del modelo", + "core.joinery.the_joint_cannot_be_represented_as_valid_solid": "La unión no se puede representar como geometría sólida válida", + "core.joinery.the_joint_would_remove_an_entire_board": "La unión eliminaría una tabla completa", + "core.joinery.the_profile_does_not_divide_the_shared_material": "El perfil no divide el material compartido; elige otra orientación u otro tamaño de perfil", + "core.joinery.tongue_and_groove": "Machihembrado", + "core.joinery.upper_end": "Extremo superior", + "core.joinery_frame.choose_two_groups_or_components": "Elige dos grupos o componentes", + "core.joinery_frame.each_part_must_contain_only_board_geometry_select": "Cada pieza debe contener solo geometría de tabla; selecciona tablas individuales, no ensamblajes", + "core.joinery_frame.the_board_has_no_geometry": "La tabla no tiene geometría", + "core.joinery_frame.the_board_has_no_planar_faces": "La tabla no tiene caras planas", + "core.joinery_trim.missing_board_geometry": "Falta la geometría de la tabla", + "core.joinery_trim.the_joint_would_remove_an_entire_board": "La unión eliminaría una tabla completa", + "core.joinery_trim.trimming_would_remove_an_entire_board_keep_the": "Al recortar se eliminaría una tabla completa; conserva el otro extremo", + "core.measurements.choose_angle_points_away_from_vertex": "Elige dos puntos de ángulo alejados del vértice", + "core.measurements.choose_two_different_dimension_endpoints": "Elige dos extremos de cota distintos", + "core.measurements.dimension_coordinates_must_be_finite_and_within_the": "Las coordenadas de la cota deben ser finitas y estar dentro de los límites del modelo", + "core.measurements.dimension_is_outside_the_model_limits": "La cota está fuera de los límites del modelo", + "core.measurements.invalid_dimension": "Cota no válida", + "core.measurements.invalid_dimension_attachment": "Anclaje de cota no válido", + "core.measurements.invalid_dimension_attachment_weights": "Pesos de anclaje de cota no válidos", + "core.measurements.invalid_dimension_position": "Posición de cota no válida", + "core.modeling.cannot_offset_a_folded_boundary": "No se puede desfasar un contorno plegado", + "core.modeling.cannot_offset_this_corner": "No se puede desfasar esta esquina", + "core.modeling.choose_a_rotation_axis_first": "Elige primero un eje de rotación", + "core.modeling.choose_an_axis_that_is_not_parallel_to": "Elige un eje que no sea paralelo a la cara, o quita el bloqueo del eje", + "core.modeling.click_a_face_in_a_group_or_component": "Haz clic en una cara de un grupo o componente para pintarla", + "core.modeling.click_a_filled_face_first": "Haz clic primero en una cara rellena", + "core.modeling.click_a_filled_face_to_paint_it": "Haz clic en una cara rellena para pintarla", + "core.modeling.double_click_the_object_to_edit_its_faces": "Haz doble clic en el objeto para editar sus caras", + "core.modeling.enter_a_finite_displacement": "Introduce un desplazamiento finito", + "core.modeling.enter_a_finite_rotation_and_pivot": "Introduce una rotación y un pivote finitos", + "core.modeling.enter_a_nonzero_distance_in_meters": "Introduce una distancia distinta de cero en metros", + "core.modeling.enter_a_nonzero_offset_in_meters": "Introduce un desfase distinto de cero en metros", + "core.modeling.enter_a_positive_scale_factor_such_as_2": "Introduce un factor de escala positivo, como 2 o 0.5", + "core.modeling.offset_boundaries_would_overlap": "Los límites del desfase se superpondrían", + "core.modeling.offset_is_too_large_for_this_face": "El desfase es demasiado grande para esta cara", + "core.modeling.offset_is_too_large_or_crosses_itself": "El desfase es demasiado grande o se cruza consigo mismo", + "core.modeling.scoped_moves_require_component_synchronization": "Los movimientos con alcance requieren sincronizar los componentes", + "core.modeling.select_geometry_a_dimension_text_or_a_guide": "Selecciona primero geometría, una cota, un texto o una guía", + "core.modeling.select_geometry_to_flip_first": "Selecciona primero la geometría que quieres voltear", + "core.modeling.the_face_must_be_planar": "La cara debe ser plana", + "core.modeling.the_group_or_component_has_no_faces_to": "El grupo o componente no tiene caras para pintar", + "core.modeling.this_change_would_collapse_a_guide": "Este cambio colapsaría una guía", + "core.modeling.this_change_would_collapse_an_edge_or_bend": "Este cambio colapsaría una arista o doblaría una cara; selecciona su geometría conectada", + "core.modeling.unlock_all_faces_in_the_group_or_component": "Desbloquea todas las caras del grupo o componente antes de pintarlo", + "core.modeling.unlock_the_connected_geometry_before_editing_it": "Desbloquea la geometría conectada antes de editarla", + "core.modeling.unlock_the_face_before_painting_it": "Desbloquea la cara antes de pintarla", + "core.modeling.unlock_the_group_or_component_before_painting_it": "Desbloquea el grupo o componente antes de pintarlo", + "core.modeling.unlock_the_object_before_editing_it": "Desbloquea el objeto antes de editarlo", + "core.modeling.unlock_the_selected_items_and_their_children_before": "Desbloquea los elementos seleccionados y sus elementos secundarios antes de quitar el color", + "core.modeling.unlock_the_selection_before_editing_it": "Desbloquea la selección antes de editarla", + "core.objects.a_group_cannot_have_a_component_mapping": "Un grupo no puede tener una asignación de componente", + "core.objects.component": "Componente", + "core.objects.cyclic_object_membership": "Pertenencia cíclica de objetos", + "core.objects.duplicate_entity_id": "ID de entidad duplicado", + "core.objects.group": "Grupo", + "core.objects.invalid_grain_direction": "Dirección de veta no válida", + "core.objects.select_an_unlocked_part": "Selecciona una pieza desbloqueada.", + "core.objects.incomplete_component_instance_mapping": "Asignación de instancias de componente incompleta", + "core.objects.invalid_component_definition": "Definición de componente no válida", + "core.objects.invalid_component_instance_mapping": "Asignación de instancias de componente no válida", + "core.objects.invalid_entity_state": "Estado de entidad no válido", + "core.objects.invalid_object": "Objeto no válido", + "core.objects.invalid_object_membership": "Pertenencia de objeto no válida", + "core.objects.mismatched_component_instances": "Instancias de componente no coincidentes", + "core.reparent.an_object_cannot_be_moved_into_itself_or": "Un objeto no se puede mover dentro de sí mismo ni de sus descendientes", + "core.reparent.choose_an_insertion_point_outside_the_dragged_objects": "Elige un punto de inserción fuera de los objetos arrastrados", + "core.reparent.drag_groups_or_components_to_move_them_in": "Arrastra grupos o componentes para moverlos en el esquema", + "core.reparent.drop_inside_a_group_or_between_sibling_objects": "Suelta dentro de un grupo o entre objetos del mismo nivel", + "core.reparent.show_and_unlock_the_destination_group_before_moving": "Muestra y desbloquea el grupo de destino antes de mover objetos a él", + "core.reparent.show_the_parent_object_before_moving_its_contents": "Muestra el objeto principal antes de mover su contenido", + "core.reparent.unlock_the_objects_and_their_contents_before_moving": "Desbloquea los objetos y su contenido antes de moverlos", + "core.solids.cannot_join_the_clipped_face_boundaries": "No se pueden unir los contornos de cara recortados", + "core.solids.cannot_orient_the_clipped_face_boundaries": "No se pueden orientar los contornos de cara recortados", + "core.solids.choose_a_body_in_the_current_editing_context": "Elige un cuerpo en el contexto de edición actual", + "core.solids.choose_a_closed_solid_group_component_or_connected": "Elige un grupo sólido cerrado, un componente o un cuerpo conectado", + "core.solids.choose_a_closed_solid_with_filled_faces": "Elige un sólido cerrado con caras rellenas", + "core.solids.choose_geometry_with_filled_faces": "Elige geometría con caras rellenas", + "core.solids.choose_overlapping_solids_for_this_operation": "Elige sólidos que se superpongan para esta operación", + "core.solids.choose_two_different_solids": "Elige dos sólidos distintos", + "core.solids.combine": "Combinar", + "core.solids.each_body_must_be_a_closed_solid_fill": "Cada cuerpo debe ser un sólido cerrado; rellena las caras que falten y elimina la geometría interna o suelta", + "core.solids.intersect": "Intersecar", + "core.solids.intersection": "Intersección", + "core.solids.missing_object_contents": "Falta el contenido del objeto", + "core.solids.open_bodies_must_lie_in_the_same_plane": "Los cuerpos abiertos deben estar en el mismo plano para intersecarse entre sí", + "core.solids.remove_overlapping_faces_inside_the_planar_body": "Elimina las caras superpuestas dentro del cuerpo plano", + "core.solids.remove_stray_edges_before_using_a_solid_tool": "Elimina las aristas sueltas antes de usar una herramienta de sólidos", + "core.solids.show_both_solids_before_editing_them": "Muestra ambos sólidos antes de editarlos", + "core.solids.split": "Dividir", + "core.solids.split_a": "Dividir A", + "core.solids.split_b": "Dividir B", + "core.solids.split_overlap": "Superposición de la división", + "core.solids.the_bodies_have_no_shared_surface": "Los cuerpos no comparten ninguna superficie", + "core.solids.the_body_has_no_enclosed_volume": "El cuerpo no tiene volumen cerrado", + "core.solids.the_body_needs_filled_faces_to_form_a": "El cuerpo necesita caras rellenas para formar un sólido cerrado", + "core.solids.the_clipped_boundary_crosses_itself": "El contorno recortado se cruza consigo mismo", + "core.solids.the_result_cannot_be_represented_as_valid_planar": "El resultado no se puede representar como geometría plana válida", + "core.solids.the_result_cannot_form_a_closed_solid_at": "El resultado no puede formar un sólido cerrado con esta precisión", + "core.solids.the_result_cannot_form_a_valid_closed_solid": "El resultado no puede formar un sólido cerrado válido con esta precisión", + "core.solids.the_result_contains_a_collapsed_face": "El resultado contiene una cara colapsada", + "core.solids.the_result_contains_details_smaller_than_the_model": "El resultado contiene detalles más pequeños que la precisión del modelo", + "core.solids.the_result_has_too_many_surface_fragments": "El resultado tiene demasiados fragmentos de superficie", + "core.solids.the_solid_contains_a_collapsed_face": "El sólido contiene una cara colapsada", + "core.solids.the_solid_has_inconsistent_surface_topology": "El sólido tiene una topología de superficie inconsistente", + "core.solids.the_solid_is_too_small": "El sólido es demasiado pequeño", + "core.solids.the_solids_have_no_shared_volume": "Los sólidos no comparten volumen", + "core.solids.these_solids_have_too_many_vertices_for_this": "Estos sólidos tienen demasiados vértices para esta operación", + "core.solids.this_solid_has_too_many_faces_for_this": "Este sólido tiene demasiadas caras para esta operación", + "core.solids.trim": "Recortar", + "core.solids.trimmed": "Recortado", + "core.solids.union": "Unión", + "core.solids.unlock_both_solids_before_editing_them": "Desbloquea ambos sólidos antes de editarlos", + "core.surface_treatment.a_list_or_legacy_tuple_of_sanding_settings": "una lista o tupla antigua de ajustes de lijado", + "core.surface_treatment.grit": "Grano {grit}", + "core.surface_treatment.grit_after_coat": "Grano {grit} (después de la mano {coat})", + "core.surface_treatment.pre_stain_coat": "Pretinte · mano {coat}", + "core.surface_treatment.sanding_grit": "Lijado · grano {grit}", + "core.surface_treatment.sanding_grit_after_coat": "Lijado · grano {grit} (después de la mano {coat})", + "core.surface_treatment.stain_coat": "Tinte · mano {coat}", + "core.surface_treatment.varnish_coat": "Barniz · mano {coat}", + "core.tags.a_tag_with_that_name_already_exists": "Ya existe una etiqueta con ese nombre", + "core.tags.choose_a_tag_name_of_1128_characters_other": "Elige un nombre de etiqueta de 1 a 128 caracteres distinto de Sin etiqueta", + "core.tags.invalid_tags": "Etiquetas no válidas", + "core.tags.missing_tag": "Falta la etiqueta", + "core.tags.unlock_the_selection_before_assigning_a_tag": "Desbloquea la selección antes de asignar una etiqueta", + "core.units.acres_ac": "Acres (ac)", + "core.units.centimeters_cm": "Centímetros (cm)", + "core.units.cubic_centimeters_cm3": "Centímetros cúbicos (cm³)", + "core.units.cubic_feet_ft3": "Pies cúbicos (ft³)", + "core.units.cubic_inches_in3": "Pulgadas cúbicas (in³)", + "core.units.cubic_meters_m3": "Metros cúbicos (m³)", + "core.units.cubic_millimeters_mm3": "Milímetros cúbicos (mm³)", + "core.units.cubic_yards_yd3": "Yardas cúbicas (yd³)", + "core.units.degrees": "Grados (°)", + "core.units.feet_ft": "Pies (ft)", + "core.units.gradians_gon": "Gradianes (gon)", + "core.units.hectares_ha": "Hectáreas (ha)", + "core.units.inches_in": "Pulgadas (in)", + "core.units.kilometers_km": "Kilómetros (km)", + "core.units.liters_l": "Litros (L)", + "core.units.meters_m": "Metros (m)", + "core.units.milliliters_ml": "Mililitros (mL)", + "core.units.millimeters_mm": "Milímetros (mm)", + "core.units.radians_rad": "Radianes (rad)", + "core.units.square_centimeters_cm2": "Centímetros cuadrados (cm²)", + "core.units.square_feet_ft2": "Pies cuadrados (ft²)", + "core.units.square_inches_in2": "Pulgadas cuadradas (in²)", + "core.units.square_kilometers_km2": "Kilómetros cuadrados (km²)", + "core.units.square_meters_m2": "Metros cuadrados (m²)", + "core.units.square_millimeters_mm2": "Milímetros cuadrados (mm²)", + "core.units.square_yards_yd2": "Yardas cuadradas (yd²)", + "core.units.yards_yd": "Yardas (yd)", + "currency.australian_dollar": "Dólar australiano", + "currency.brazilian_real": "Real brasileño", + "currency.british_pound": "Libra esterlina", + "currency.canadian_dollar": "Dólar canadiense", + "currency.chilean_peso": "Peso chileno", + "currency.chinese_yuan": "Yuan chino", + "currency.code_and_name": "{code} — {name}", + "currency.colombian_peso": "Peso colombiano", + "currency.czech_koruna": "Corona checa", + "currency.danish_krone": "Corona danesa", + "currency.euro": "Euro", + "currency.hong_kong_dollar": "Dólar de Hong Kong", + "currency.hungarian_forint": "Forinto húngaro", + "currency.indian_rupee": "Rupia india", + "currency.indonesian_rupiah": "Rupia indonesia", + "currency.israeli_shekel": "Séquel israelí", + "currency.japanese_yen": "Yen japonés", + "currency.malaysian_ringgit": "Ringgit malasio", + "currency.mexican_peso": "Peso mexicano", + "currency.new_zealand_dollar": "Dólar neozelandés", + "currency.no_currencies_match_your_search": "Ninguna moneda coincide con tu búsqueda.", + "currency.norwegian_krone": "Corona noruega", + "currency.philippine_peso": "Peso filipino", + "currency.polish_zloty": "Esloti polaco", + "currency.romanian_leu": "Leu rumano", + "currency.saudi_riyal": "Rial saudí", + "currency.search_code_or_name": "Buscar por código o nombre…", + "currency.singapore_dollar": "Dólar de Singapur", + "currency.south_african_rand": "Rand sudafricano", + "currency.south_korean_won": "Won surcoreano", + "currency.swedish_krona": "Corona sueca", + "currency.swiss_franc": "Franco suizo", + "currency.taiwan_dollar": "Nuevo dólar taiwanés", + "currency.thai_baht": "Baht tailandés", + "currency.turkish_lira": "Lira turca", + "currency.uae_dirham": "Dírham de los EAU", + "currency.us_dollar": "Dólar estadounidense", + "currency.vietnamese_dong": "Dong vietnamita", + "cutting_layout.stock_dimensions_must_be_1_1_000_000": "Las dimensiones del stock deben estar entre 1 y 1 000 000 mm, y el ancho de corte entre 0 y 100 mm.", + "cutting_plan.approx_nominal": "Aprox. {stock} nominal", + "cutting_plan.board": "TABLA", + "cutting_plan.boards_required": { + "one": "Stock necesario: {count} tabla | Ancho de corte: {kerf}", + "other": "Stock necesario: {count} tablas | Ancho de corte: {kerf}" + }, + "cutting_plan.check_shape_bounding_blank_only": "REVISA LA FORMA: solo bloque delimitador", + "cutting_plan.cm": "{} cm", + "cutting_plan.continued": "{title} (continuación)", + "cutting_plan.continued_2": "(continuación)", + "cutting_plan.cut_length": "LONGITUD DE CORTE", + "cutting_plan.cut_list": "LISTA DE CORTES", + "cutting_plan.cut_list_summary": "{pieces} | {sizes} | Dimensiones en mm o cm", + "cutting_plan.cutting_plan": "Plan de corte", + "cutting_plan.date_unavailable": "Fecha no disponible", + "cutting_plan.does_not_fit_selected_stock": "NO CABE EN EL STOCK SELECCIONADO", + "cutting_plan.footer_note": "Medidas finales: verifica antes de cortar", + "cutting_plan.group": "GRUPO", + "cutting_plan.mark": "MARCA", + "cutting_plan.material_x_x": "Material: {} ({} x {} x {})", + "cutting_plan.mm": "{} mm", + "cutting_plan.mm_2": "{}.{:01} mm", + "cutting_plan.net_cut_area_m2": "{pieces} | Área neta de corte: {area:.2} m2", + "cutting_plan.no_visible_three_dimensional_parts_with_assigned_stock": "No se encontraron piezas tridimensionales visibles con materiales de stock asignados. Asigna un material desde la tabla de precios de stock en Información de la entidad. Modela cada tabla como un grupo o componente independiente, o como un sólido desconectado. Selecciona una tabla o un ensamblaje, o usa todo el proyecto.", + "cutting_plan.omitted_items": { + "one": "Omitido: {count} elemento sin material de stock asignado ni grosor utilizable.", + "other": "Omitidos: {count} elementos sin material de stock asignado ni grosor utilizable." + }, + "cutting_plan.oversized_cut": { + "one": "{mark}: {count} pieza, {width} x {length} - elige un stock más grande", + "other": "{mark}: {count} piezas, {width} x {length} - elige un stock más grande" + }, + "cutting_plan.page_number": "Página {page} / {count}", + "cutting_plan.qty": "CANT.", + "cutting_plan.rotation_used": " (con rotación)", + "cutting_plan.section_t_x_w": "SECCIÓN (G x A)", + "cutting_plan.selected_parts_visible_only": "Piezas seleccionadas (solo visibles)", + "cutting_plan.sizes": { + "one": "{count} tamaño", + "other": "{count} tamaños" + }, + "cutting_plan.stock": "STOCK", + "cutting_plan.stock_count_covers_fitting_cuts_only_oversized_cuts": "El recuento de stock solo incluye los cortes que caben; los cortes demasiado grandes se listan por separado.", + "cutting_plan.stock_page_heading": "{} {} de {}", + "cutting_plan.stock_required": { + "one": "Stock necesario: {count} pieza de stock | Ancho de corte: {kerf}", + "other": "Stock necesario: {count} piezas de stock | Ancho de corte: {kerf}" + }, + "cutting_plan.stock_x_kerf_gray_offcut_kerf": "Stock: {} x {} | Ancho de corte: {} | Gris: sobrante / corte", + "cutting_plan.the_project_name_is_too_long_to_fit": "El nombre del proyecto es demasiado largo para caber sobre las etiquetas de taller.", + "cutting_plan.unnamed_part": "Pieza sin nombre", + "cutting_plan.workshop_labels": "Etiquetas de taller", + "desktop_files.missing_project_output_path": "Falta la ruta de salida del proyecto de BenchUp", + "desktop_files.missing_sketchup_file_path": "Falta la ruta del archivo de SketchUp", + "drawing_groups.the_drawing_could_not_be_grouped": "No se pudo agrupar el dibujo", + "entity_gizmo.axis_lengths": "Longitudes de los ejes: {}. {}. {}.", + "entity_gizmo.object_lenghts": "Longitudes del objeto", + "entity_gizmo.solid_lengths_along_world_axes_orientation_follows_the": "Longitudes del sólido a lo largo de los ejes del mundo. La orientación sigue la vista activa.", + "environment.background": "Fondo", + "environment.environment": "Entorno", + "environment.ground": "Suelo", + "environment.horizon": "Horizonte", + "environment.reset_environment_colors": "Restablecer colores del entorno", + "environment.sky": "Cielo", + "estimate_pdf.amount": "Importe ({})", + "estimate_pdf.april": "abril", + "estimate_pdf.august": "agosto", + "estimate_pdf.continued": "{group} (continuación)", + "estimate_pdf.cost_estimate": "Estimación de costos", + "estimate_pdf.could_not_load_report_font": "No se pudo cargar la fuente del informe: {error}", + "estimate_pdf.could_not_load_report_symbol_font": "No se pudo cargar la fuente de símbolos del informe: {error}", + "estimate_pdf.could_not_read_the_generation_date": "No se pudo leer la fecha de generación: {error}", + "estimate_pdf.count": "Recuento", + "estimate_pdf.december": "diciembre", + "estimate_pdf.drying_waiting": "Secado / Espera", + "estimate_pdf.february": "febrero", + "estimate_pdf.generated_utc": "Generado: {day} de {} de {year} (UTC)", + "estimate_pdf.hands_on": "Trabajo manual", + "estimate_pdf.hands_on_work": "Trabajo manual", + "estimate_pdf.includes_final_drying_objects_dry_together_between_steps": "Incluye el secado final. Los objetos se secan juntos entre pasos.", + "estimate_pdf.item": "Elemento", + "estimate_pdf.january": "enero", + "estimate_pdf.joinery_whole_project": "Ensambles (todo el proyecto)", + "estimate_pdf.joint": "Unión", + "estimate_pdf.july": "julio", + "estimate_pdf.june": "junio", + "estimate_pdf.benchup_planning_estimate_verify_before_purchasing": "BenchUp - Estimación de planificación, verifica antes de comprar", + "estimate_pdf.march": "marzo", + "estimate_pdf.materials": "Materiales", + "estimate_pdf.may": "mayo", + "estimate_pdf.minutes_joint": "Minutos / unión", + "estimate_pdf.no_configured_wood_joints": "No hay ensambles de madera configurados.", + "estimate_pdf.november": "noviembre", + "estimate_pdf.october": "octubre", + "estimate_pdf.page_number": "{} / {count}", + "estimate_pdf.quantity": "Cantidad", + "estimate_pdf.september": "septiembre", + "estimate_pdf.sequential_total": "Total secuencial", + "estimate_pdf.summary": "Resumen", + "estimate_pdf.task": "Tarea", + "estimate_pdf.the_pdf_font_cannot_display_u_replace_this": "La fuente del PDF no puede mostrar '{ch}' (U+{:04X}). Reemplaza este carácter antes de exportar; el informe no se truncó.", + "estimate_pdf.time_cost_estimates": "Estimaciones de tiempo y costos", + "estimate_pdf.too_many_distinct_characters_for_one_estimate_pdf": "Demasiados caracteres distintos para un PDF de estimación.", + "estimate_pdf.total": "Total", + "estimate_pdf.unit_price": "Precio unitario ({})", + "fasteners.angle_from_face_normal": "Ángulo respecto a la normal de la cara", + "fasteners.angled_axis_only_the_pocket_recess_and_kreg": "Solo eje en ángulo; no se simulan el rebaje de la cavidad ni los ajustes de la plantilla Kreg.", + "fasteners.angled_screw": "Tornillo en ángulo", + "fasteners.auto_face": "Cara automática", + "fasteners.automatic": "Automático", + "fasteners.automatic_entry_face": "Cara de entrada automática", + "fasteners.automatic_face": "Cara automática", + "fasteners.automatic_fasteners": "Fijaciones automáticas", + "fasteners.back_to_automatic_fasteners": "Volver a Fijaciones automáticas", + "fasteners.both_objects_must_be_visible_and_selectable_in": "Ambos objetos deben estar visibles y ser seleccionables en el contexto de edición actual.", + "fasteners.changes_apply_immediately_change_the_selection_to_return": "Los cambios se aplican de inmediato. Cambia la selección para volver al inspector.", + "fasteners.click_a_numbered_entry_face_handle_in_the": "Haz clic en un tirador numerado de cara de entrada en la vista.", + "fasteners.connection_direction": "#{id}: {} → {}", + "fasteners.count_per_row": "Cantidad por fila", + "fasteners.create_fasteners": "Crear fijaciones", + "fasteners.dimensions_shown_for_the_first_eight_fasteners": "Se muestran las dimensiones de las primeras ocho fijaciones.", + "fasteners.edit_fasteners": "Editar fijaciones", + "fasteners.entry_face": "Cara de entrada #{face}", + "fasteners.face": "Cara #{id}", + "fasteners.fastener": "Fijación", + "fasteners.finish_the_current_dialog_or_history_preview_before": "Termina el diálogo actual o la vista previa del historial antes de editar esta unión.", + "fasteners.for_cut_joints_choose_wood_joinery_from_the": "Para uniones cortadas, elige Ensambles de madera en el menú Herramientas. Las fijaciones no modifican la geometría.", + "fasteners.geometric_clearance": "Holgura geométrica", + "fasteners.geometric_sizing_automatic_length_passes_through_the_first": "Dimensionado geométrico: la longitud automática atraviesa la primera pieza y llega hasta la mitad de la profundidad disponible de la segunda. El diámetro inicial es un ejemplo editable. No se especifican la idoneidad del material, el taladrado piloto, el tipo de rosca ni la resistencia; consulta al fabricante del producto.", + "fasteners.grid": "Cuadrícula", + "fasteners.hardware_dimensions": "{kind} · {:.1} × {:.1}", + "fasteners.hardware_list_preview_dimensions": "Lista de herrajes (dimensiones de vista previa)", + "fasteners.hardware_mm": "Herraje: {} × {}, Ø {:.1} mm", + "fasteners.invalid_connections": { + "one": "{count} conexión no válida: las cantidades están incompletas. Resuelve los errores antes de comprar.", + "other": "{count} conexiones no válidas: las cantidades están incompletas. Resuelve los errores antes de comprar." + }, + "fasteners.invalid_fastener_connection": "⚠ Conexión de fijación no válida", + "fasteners.kreg_screw_selector_thicknesses_and_materials": "Selector de tornillos Kreg (grosores y materiales)", + "fasteners.length_0_geometry": "Longitud (0 = geometría)", + "fasteners.length_first_part_penetration_mm": "{}: longitud {:.1} · primera pieza {:.1} · penetración {:.1} mm", + "fasteners.missing_part": "Falta la pieza #{id}", + "fasteners.nail": "Clavo", + "fasteners.no_material_assigned": "Sin material asignado", + "fasteners.part": "Pieza {}: {} · {material}", + "fasteners.pattern": "Patrón", + "fasteners.pick_entry_face_in_viewport": "Elige la cara de entrada en la vista…", + "fasteners.preview_before_creating": "Vista previa antes de crear", + "fasteners.preview_diameter": "Diámetro de la vista previa", + "fasteners.preview_fasteners": { + "one": "Vista previa: {count} fijación", + "other": "Vista previa: {count} fijaciones" + }, + "fasteners.quantity": "Cantidad", + "fasteners.reverse_direction": "Invertir dirección", + "fasteners.review_geometric_warnings_before_purchasing_structural_strength_has": "Revisa las advertencias geométricas antes de comprar. No se ha verificado la resistencia estructural.", + "fasteners.row": "Fila", + "fasteners.row_spacing_0_auto": "Espaciado entre filas (0 = automático)", + "fasteners.rows": "Filas", + "fasteners.screw": "Tornillo", + "fasteners.select_both_objects_belonging_to_this_joint_to": "Selecciona ambos objetos de esta unión para editarla o quitarla.", + "fasteners.select_exactly_two_objects_and_reopen_automatic_fasteners": "Selecciona exactamente dos objetos y vuelve a abrir Fijaciones automáticas desde el menú Editar.", + "fasteners.select_exactly_two_touching_closed_solid_parts_the": "Selecciona exactamente dos piezas sólidas cerradas que se toquen. La cabeza entra en la primera pieza. Las líneas transparentes muestran el vástago y la punta a través del modelo.", + "fasteners.show_fasteners_through_the_model": "Mostrar fijaciones a través del modelo", + "fasteners.simpson_strong_tie_technical_guides": "Guías técnicas de Simpson Strong-Tie", + "fasteners.single": "Individual", + "fasteners.spacing_0_auto": "Espaciado (0 = automático)", + "fasteners.swap_the_selected_parts": "Intercambiar las piezas seleccionadas", + "fasteners.the_object_pair_is_no_longer_editable_no": "El par de objetos ya no es editable. No se creó ninguna fijación.", + "fasteners.the_objects_permissions_or_connection_changed_reopen_automatic": "Los objetos, los permisos o la conexión cambiaron. Vuelve a abrir Fijaciones automáticas antes de editar.", + "fasteners.two_rows": "Dos filas", + "fasteners.type_length_mm": "Tipo · Ø × longitud mm", + "fasteners.unlock_both_objects_before_editing_or_removing_their": "Desbloquea ambos objetos antes de editar o quitar su unión.", + "fasteners.use_this_entry_face": "Usar esta cara de entrada", + "finish_settings.add_finishing_product": "+ Agregar producto de acabado", + "finish_settings.area_covered_by_one_container_for_one_coat": "Área cubierta por un envase en una mano. Usa la etiqueta del producto; 0 significa desconocido. Actualiza este valor al cambiar el tamaño o la unidad del envase.", + "finish_settings.e_g_golden_oak_stain": "p. ej., Tinte roble dorado", + "finish_settings.finishing_product": "Producto de acabado", + "finish_settings.appearance_color": "Color", + "finish_settings.reset_appearance_value": "Restablecer valores predeterminados", + "finish_settings.clear_color": "Quitar", + "finish_settings.coat_strength": "Intensidad de la mano", + "finish_settings.coat_strength_help": "Controla el efecto visual de cada mano. No cambia la cobertura ni el costo.", + "finish_settings.sheen": "Brillo", + "finish_settings.matte": "Mate", + "finish_settings.satin": "Satinado", + "finish_settings.gloss": "Brillante", + "finish_settings.default_sheen": "Predeterminado ({})", + "finish_settings.formula": "Fórmula", + "finish_settings.standard_formula": "Estándar", + "finish_settings.dye": "Colorante", + "finish_settings.pigment": "Pigmento", + "finish_settings.gel": "Gel", + "finish_settings.water_based": "Base agua", + "finish_settings.oil_based": "Base aceite", + "finish_settings.apply_step": "Aplicar paso", + "finish_settings.move_sanding_up": "Subir pasada de lijado", + "finish_settings.move_sanding_down": "Bajar pasada de lijado", + "finish_settings.viewport_preview_help": "Previsualiza la madera y los acabados asignados en el estilo de cara Materiales. Los tiempos de trabajo y de secado solo afectan las estimaciones.", + "finish_settings.finishing_products": "Productos de acabado", + "finish_settings.format": "×", + "finish_settings.l": "L", + "finish_settings.lacquer": "Laca", + "finish_settings.m2_container_coat": "m² / envase / mano", + "finish_settings.ml": "mL", + "finish_settings.no_finishing_products_yet_add_a_product_to": "Aún no hay productos de acabado. Agrega un producto para guardar su tipo, tamaño de envase y precio.", + "finish_settings.no_product": "Sin producto", + "finish_settings.no_products_match_your_search": "Ningún producto coincide con tu búsqueda.", + "finish_settings.oil": "Aceite", + "finish_settings.other": "Otro", + "finish_settings.paint": "Pintura", + "finish_settings.per_container": "{currency} por envase", + "finish_settings.pre_stain": "Pretinte", + "finish_settings.pre_stain_conditioner_stain_paint_varnish_and_other": "Acondicionador previo al tinte, tinte, pintura, barniz y otros acabados. Ingresa la cantidad y el precio de un envase.", + "finish_settings.price_container": "Precio / envase", + "finish_settings.prices_in_per_container_coverage_in_m2_per": "Precios en {currency} por envase · Cobertura en m² por envase para una mano · 0 significa desconocido", + "finish_settings.primer": "Imprimación", + "finish_settings.product": "Producto", + "finish_settings.quantity": "Cantidad", + "finish_settings.remove": "Quitar {}", + "finish_settings.sealer": "Sellador", + "finish_settings.search_name_or_type": "Buscar por nombre o tipo…", + "finish_settings.stain": "Tinte", + "finish_settings.type": "Tipo", + "finish_settings.unavailable_product": "Producto no disponible", + "finish_settings.unit": "Unidad", + "finish_settings.us_fl_oz": "fl oz (EE. UU.)", + "finish_settings.us_gal": "gal (EE. UU.)", + "finish_settings.us_qt": "qt (EE. UU.)", + "finish_settings.varnish": "Barniz", + "finish_settings.wax": "Cera", + "grain_direction.assign": "Asignar dirección de la veta", + "grain_direction.click_to_confirm": "Haz clic para definir la dirección de la veta. Usa las teclas de flecha para bloquear ejes o pulsa Esc para cancelar.", + "grain_direction.clear": "Quitar", + "grain_direction.clear_history": "Borrar dirección de la veta", + "grain_direction.set_direction": "Definir dirección", + "grain_direction.show": "Mostrar dirección de la veta", + "frame_rate.fps": "— FPS", + "frame_rate.fps_2": "{fps:.0} FPS", + "inspector_docking.divider_gestures": "Arrastra para cambiar el tamaño de las áreas. Haz doble clic para combinarlas.", + "inspector_docking.drop_within_15px_inside_an_area_s_edge": "Suelta a menos de 15 px del borde de un área para dividirla; cruza el punto medio vertical de una sección para reordenar, o suelta en el centro de otra área para unirlas", + "inspector_docking.export_time_and_cost_estimates_as_pdf": "Exportar estimaciones de tiempo y costos como PDF", + "inspector_docking.generate_pdf": "Generar PDF", + "inspector_docking.manage_available_stock_materials_sizes_and_prices": "Administra los materiales de stock disponibles, sus tamaños y precios", + "inspector_docking.section_actions": "Acciones de la sección", + "inspector_docking.settings": "Configuración", + "interchange.could_not_read_sketchup_file": "No se pudo leer el archivo de SketchUp: {e}", + "interchange.not_enough_memory_to_read_this_sketchup_file": "No hay memoria suficiente para leer este archivo de SketchUp", + "interchange.sketchup_group": "Grupo de SketchUp", + "interchange.sketchup_import_is_limited_to_64_mib_per": "La importación de SketchUp está limitada a 64 MiB por archivo", + "interchange.sketchup_reader_failed": "Falló el lector de SketchUp", + "interchange.sketchup_reader_returned_no_model": "El lector de SketchUp no devolvió ningún modelo", + "interchange.skp_disconnected_boundary": "Una cara de SketchUp tiene un contorno desconectado", + "interchange.skp_incomplete_import": "El lector de SketchUp no pudo completar esta importación", + "interchange.skp_instance_transform": "Transformación de instancia de SketchUp no compatible", + "interchange.skp_invalid_boundary": "Una cara de SketchUp tiene un contorno no válido", + "interchange.skp_invalid_coordinates": "Coordenadas de SketchUp no válidas", + "interchange.skp_missing_definition": "Una instancia de SketchUp hace referencia a una definición inexistente", + "interchange.skp_missing_edge": "Una cara de SketchUp hace referencia a una arista inexistente", + "interchange.skp_missing_vertex": "Una arista de SketchUp hace referencia a un vértice inexistente", + "interchange.skp_model_too_large": "El modelo importado es demasiado grande (máximo de un millón de entidades)", + "interchange.skp_nested_components": "Componentes de SketchUp recursivos o con un anidamiento excesivo", + "interchange.skp_no_geometry": "Este archivo de SketchUp no contiene aristas ni caras importables", + "interchange.skp_worker_failed": "Falló el proceso de SketchUp", + "interchange.skp_worker_message": "No se pudo recibir el modelo de SketchUp", + "interchange.there_is_no_visible_geometry_to_export": "No hay geometría visible para exportar", + "joinery.automatic_longest_axis": "Automático (eje más largo)", + "joinery.board": "Tabla ", + "joinery.board_x": "Tabla X", + "joinery.board_y": "Tabla Y", + "joinery.board_z": "Tabla Z", + "joinery.create_a_dynamic_relation_moving_either_board_updates": "Crea una relación dinámica. Al mover cualquiera de las tablas, la unión se actualiza automáticamente.", + "joinery.create_dynamic_wood_joint": "Crear ensamble de madera dinámico", + "joinery.create_joinery": "Crear ensamble", + "joinery.degree_symbol": "°", + "joinery.delete_joint": "Eliminar unión", + "joinery.depth_width": "Profundidad {axis_prefix}{} / Ancho {axis_prefix}{}", + "joinery.diagnostic_separator": " · ", + "joinery.direction_in_the_selected_board_s_local_axes": "Dirección en los ejes locales de la tabla seleccionada.", + "joinery.dovetail_angle": "Ángulo de la cola de milano", + "joinery.dynamic_wood_joint": "Ensamble de madera dinámico #{id}", + "joinery.edit_dynamic_wood_joint": "Editar ensamble de madera dinámico", + "joinery.edit_wood_joint": "Editar ensamble de madera #{id}", + "joinery.fingers_on_first_part": "Dedos en la primera pieza", + "joinery.fit_maximum_tails": "Ajustar el máximo de colas", + "joinery.keep_end": "Conservar extremo", + "joinery.keep_the_lower_or_upper_end_along_the": "Conserva el extremo inferior o superior según la dirección de recorte seleccionada.", + "joinery.keep_the_tail_width_and_fit_as_many": "Mantiene el ancho de cola y ajusta tantas colas como permita el espaciado de las espigas.", + "joinery.minimum_pin_width": "Ancho mínimo de espiga", + "joinery.minimum_wood_left_between_receiving_sockets_after_clearance": "Madera mínima que queda entre los huecos receptores después de la holgura. Las espigas más pequeñas permiten más colas.", + "joinery.mm": " mm", + "joinery.no_supported_orientation": "No hay una orientación compatible", + "joinery.orientation": "Orientación", + "joinery.profile_width_of_overlap": "Ancho del perfil (% del solapamiento)", + "joinery.receiver_clearance_per_side": "Holgura del receptor por lado", + "joinery.review_sharing_status": "Revisar el estado de uso compartido…", + "joinery.select_exactly_two_boards_and_reopen_wood_joinery": "Selecciona exactamente dos tablas y vuelve a abrir Ensambles de madera desde el menú Herramientas.", + "joinery.shorten_the_selected_board_past_the_joint_following": "Acorta la tabla seleccionada más allá de la unión, siguiendo una cara exterior inclinada cuando sea posible.", + "joinery.swap_first_and_second_boards": "Intercambiar la primera y la segunda tabla", + "joinery.swap_parts": "Intercambiar piezas", + "joinery.tail_and_pin_widths_must_fit_the_joint": "Los anchos de cola y de espiga deben ajustarse a la unión y producir de 1 a 64 colas.", + "joinery.tail_width": "Ancho de cola", + "joinery.tails_fit_across_this_joint": { + "one": "{count} cola cabe a lo largo de esta unión.", + "other": "{count} colas caben a lo largo de esta unión." + }, + "joinery.tails_in_the_saved_layout": { + "one": "{count} cola en el diseño guardado.", + "other": "{count} colas en el diseño guardado." + }, + "joinery.tenon_height_of_overlap": "Altura de la espiga (% del solapamiento)", + "joinery.the_boards_or_permissions_changed_review_the_updated": "Las tablas o los permisos cambiaron. Revisa la vista previa actualizada de la unión antes de aplicar.", + "joinery.trim_direction": "Dirección de recorte", + "joinery.trim_excess": "Recortar excedente", + "joinery.unlock_both_objects_before_editing_their_joint": "Desbloquea ambos objetos antes de editar su unión.", + "joinery.wood_joinery": "Ensambles de madera", + "joinery.wood_joint_click_to_edit": "Ensamble de madera #{id}: {message}\nHaz clic para editar", + "joinery.wood_joint_suspended": "Ensamble de madera #{id} suspendido: {error}", + "joinery_cards.a_continuous_thin_tongue_fits_into_a_matching": "Una lengüeta continua y delgada encaja en una ranura correspondiente.", + "joinery_cards.a_flared_tail_fits_into_a_matching_tapered": "Una cola acampanada encaja en un hueco cónico correspondiente.", + "joinery_cards.a_stepped_edge_fits_against_the_complementary_rebate": "Un borde escalonado encaja con el rebaje complementario.", + "joinery_cards.a_tenon_with_shoulders_fits_into_a_rectangular": "Una espiga con hombros encaja en una caja rectangular.", + "joinery_cards.alternating_straight_fingers_interlock_across_the_joint": "Dedos rectos alternados se entrelazan a lo largo de la unión.", + "joinery_cards.opposing_half_depth_cuts_let_the_two_parts": "Cortes opuestos a media profundidad permiten que las dos piezas se solapen.", + "measurement_tools.add_angle_dimension": "Agregar cota angular", + "measurement_tools.add_dimension": "Agregar cota", + "measurement_tools.add_guide": "Agregar guía", + "measurement_tools.adjust_angle_dimension": "Ajustar cota angular", + "measurement_tools.adjust_guide": "Ajustar guía", + "measurement_tools.choose_a_point_away_from_the_measurement_origin": "Elige un punto alejado del origen de la medición", + "measurement_tools.choose_a_starting_point_first": "Elige primero un punto de inicio", + "measurement_tools.click_a_baseline_point_arrow_keys_lock_axes": "Haz clic en un punto de la línea base · Las flechas bloquean ejes · Esc para cancelar", + "measurement_tools.click_the_angle_point_arrow_keys_lock_axes": "Haz clic en el punto del ángulo · Las flechas bloquean ejes · Esc para cancelar", + "measurement_tools.click_the_second_endpoint_arrow_keys_align_x": "Haz clic en el segundo extremo · Las flechas alinean X / Y / Z · Esc para cancelar", + "measurement_tools.click_the_second_point_arrow_keys_lock_axes": "Haz clic en el segundo punto · Las flechas bloquean ejes · Esc para cancelar", + "measurement_tools.create_guide_click_the_angle_point_or_type": "Crear guía · Haz clic en el punto del ángulo o escribe el ángulo + Enter · Ctrl/Comando alterna Medir", + "measurement_tools.create_guide_click_to_place_or_type_distance": "Crear guía · Haz clic para colocarla o escribe la distancia + Enter · Las flechas bloquean ejes · Ctrl/Comando alterna Medir", + "measurement_tools.create_guides_click_a_reference_edge_or_point": "Crear guías · Haz clic en una arista o punto de referencia · Ctrl/Comando alterna Medir · Esc para cancelar", + "measurement_tools.guide_created_type_a_value_enter_to_adjust": "Guía creada · Escribe un valor + Enter para ajustar · Haz clic para iniciar otra · Ctrl/Comando alterna Medir", + "measurement_tools.measure_only_click_measurement_points_ctrl_command_toggles": "Solo medir · Haz clic en los puntos de medición · Ctrl/Comando alterna Crear guías · Esc para cancelar", + "measurement_tools.measurement_complete_click_to_start_another_ctrl_command": "Medición completa · Haz clic para iniciar otra · Ctrl/Comando alterna Crear guías", + "measurement_tools.measurement_complete_click_to_start_another_esc_to": "Medición completa · Haz clic para iniciar otra · Esc para borrar", + "measurement_tools.move_over_the_model_or_drawing_plane_to": "Mueve el cursor sobre el modelo o el plano de dibujo para elegir un punto", + "measurement_tools.move_the_pointer_to_choose_a_direction_first": "Mueve el puntero para elegir primero una dirección", + "measurement_tools.move_to_place_arrow_keys_align_x_y": "Mueve para colocar · Las flechas alinean X / Y / Z · Abajo quita el bloqueo", + "measurement_tools.move_to_position_the_text_arrow_keys_lock": "Mueve para ubicar el texto · Las flechas bloquean ejes · Haz clic para colocarlo · Esc para cancelar", + "measurement_tools.undefined_angle": "Ángulo indefinido", + "mesh_exchange.cannot_export_degenerate_stl_triangle": "No se puede exportar un triángulo STL degenerado", + "mesh_exchange.degenerate_edge": "Arista degenerada", + "mesh_exchange.degenerate_geometry": "Geometría degenerada", + "mesh_exchange.dxf_bulges_and_polyline_widths_are_unsupported": "No se admiten curvaturas (bulges) ni anchos de polilínea en DXF", + "mesh_exchange.dxf_must_end_with_eof": "El DXF debe terminar con EOF", + "mesh_exchange.dxf_radius_must_be_positive": "El radio del DXF debe ser positivo", + "mesh_exchange.dxf_units_unsupported_use_mm_cm_m_inches": "Unidades de DXF no compatibles (usa mm, cm, m, pulgadas o pies)", + "mesh_exchange.expected_ascii_stl_solid": "Se esperaba un sólido STL ASCII", + "mesh_exchange.expected_dxf_entity": "Se esperaba una entidad DXF", + "mesh_exchange.expected_stl_facet_normal": "Se esperaba una normal de faceta STL", + "mesh_exchange.expected_stl_outer_loop": "Se esperaba un bucle exterior STL", + "mesh_exchange.expected_stl_vertex": "Se esperaba un vértice STL", + "mesh_exchange.import_exceeds_coordinate_or_200_000_vertex_limit": "La importación supera el límite de coordenadas o de 200 000 vértices", + "mesh_exchange.import_exceeds_one_million_entities": "La importación supera un millón de entidades", + "mesh_exchange.import_is_limited_to_64_mib": "La importación está limitada a 64 MiB", + "mesh_exchange.invalid_binary_stl_length": "Longitud de STL binario no válida", + "mesh_exchange.invalid_dxf_group_code": "Código de grupo DXF no válido", + "mesh_exchange.invalid_dxf_polyline_vertex_count": "Cantidad de vértices de polilínea DXF no válida", + "mesh_exchange.invalid_dxf_section": "Sección DXF no válida", + "mesh_exchange.invalid_dxf_units": "Unidades de DXF no válidas", + "mesh_exchange.invalid_number": "Número no válido: {s}", + "mesh_exchange.invalid_obj_vertex_index": "Índice de vértice OBJ no válido", + "mesh_exchange.invalid_stl_ending": "Final de STL no válido", + "mesh_exchange.invalid_stl_facet_ending": "Final de faceta STL no válido", + "mesh_exchange.missing_dxf_field": "Falta el campo DXF {code}", + "mesh_exchange.missing_dxf_polyline_x": "Falta la X de la polilínea DXF", + "mesh_exchange.missing_dxf_polyline_y": "Falta la Y de la polilínea DXF", + "mesh_exchange.missing_or_incomplete_dxf_entities_section": "Falta la sección ENTITIES del DXF o está incompleta", + "mesh_exchange.no_supported_geometry_in_file": "No hay geometría compatible en el archivo", + "mesh_exchange.non_finite_or_excessive_coordinate": "Coordenada no finita o excesiva", + "mesh_exchange.obj_must_be_utf_8_text": "El OBJ debe ser texto UTF-8", + "mesh_exchange.obj_polygon_exceeds_4096_vertices": "El polígono OBJ supera los 4096 vértices", + "mesh_exchange.obj_vertex_index_out_of_range_forward_references": "Índice de vértice OBJ fuera de rango (no se admiten referencias hacia adelante)", + "mesh_exchange.obj_vertices_must_have_exactly_xyz_coordinates": "Los vértices OBJ deben tener exactamente coordenadas XYZ", + "mesh_exchange.only_dxf_geometry_in_the_xy_plane_with": "Solo se admite geometría DXF en el plano XY con extrusión predeterminada", + "mesh_exchange.only_text_dxf_is_supported": "Solo se admite DXF de texto", + "mesh_exchange.stl_coordinate_overflow": "Desbordamiento de coordenadas STL", + "mesh_exchange.stl_export_exceeds_200_000_triangles": "La exportación STL supera los 200 000 triángulos", + "mesh_exchange.there_are_no_visible_faces_to_export_as": "No hay caras visibles para exportar como STL", + "mesh_exchange.too_many_obj_primitives": "Demasiadas primitivas OBJ", + "mesh_exchange.truncated_dxf_code_value_pair": "Par código/valor de DXF truncado", + "mesh_exchange.truncated_stl": "STL truncado", + "mesh_exchange.unsupported_dxf_entity_supported_line_lwpolyline_without_bulges": "Entidad DXF no compatible {kind}. Compatibles: LINE, LWPOLYLINE sin curvaturas ni anchos, CIRCLE, ARC; solo plano XY.", + "mesh_exchange.unsupported_dxf_polyline_flags": "Indicadores de polilínea DXF no compatibles", + "mesh_exchange.unsupported_import_format": "Formato de importación no compatible", + "mesh_exchange.unsupported_obj_statement": "Instrucción OBJ no compatible: {}", + "modal.close_dialog": "Cerrar diálogo", + "modal.close_esc": "Cerrar (Esc)", + "move_tool.cannot_copy_this_geometry_here_check_the_distance": "No se puede copiar esta geometría aquí; revisa la distancia y el anidamiento de componentes", + "move_tool.ctrl": "Ctrl", + "move_tool.enter_a_finite_displacement": "Introduce un desplazamiento finito", + "move_tool.enter_a_whole_array_count_from_1_to": "Introduce una cantidad entera de copias de 1 a 1000", + "move_tool.enter_an_array_count_such_as_3_or": "Introduce una cantidad de copias, como /3 o 3x", + "move_tool.move_the_pointer_to_choose_a_direction_first": "Mueve el puntero para elegir primero una dirección", + "move_tool.option": "Option", + "outliner.angle_dimension": "Cota angular", + "outliner.click_to_select_shift_click_to_select_a": "Haz clic para seleccionar · Shift + clic para seleccionar un rango · Ctrl/Cmd + clic para alternar la selección · Usa el botón del ojo para alternar la visibilidad; revisa los objetos principales y las etiquetas para ver la visibilidad heredada", + "outliner.click_to_select_shift_click_to_select_a_2": "Haz clic para seleccionar · Shift + clic para seleccionar un rango · Ctrl/Cmd + clic para alternar la selección · Doble clic para editar · Arrastra sobre un grupo para anidar, o entre filas para reordenar", + "outliner.collapse_children": "Contraer elementos secundarios", + "outliner.component": "Componente", + "outliner.dimension": "Cota", + "outliner.drag_move_objects": { + "one": "Mover {count} objeto", + "other": "Mover {count} objetos" + }, + "outliner.draw_geometry_or_import_a_model_to_see": "Dibuja geometría o importa un modelo para ver aquí sus objetos.", + "outliner.edge": "Arista", + "outliner.editing": " · Editando", + "outliner.empty_object": "Objeto vacío", + "outliner.entity": "{kind}: {label}\nEntidad #{id}{}\n{}", + "outliner.entity_accessibility": "{kind}: {label}", + "outliner.entity_number": "#{id}", + "outliner.expand_children": "Expandir elementos secundarios", + "outliner.face": "Cara", + "outliner.find_groups_and_components_at_any_depth_by": "Busca grupos y componentes a cualquier profundidad por nombre o #ID · Ctrl/Cmd+F", + "outliner.full_material_assignment": "Asignación de material completa", + "outliner.geometry": "Geometría ({count})", + "outliner.group": "Grupo", + "outliner.hide": "Ocultar", + "outliner.locked": " · Bloqueado", + "outliner.material_accessibility": "{} — {kind}: {label}", + "outliner.move_objects": "Mover objetos", + "outliner.no_matching_objects": "Ningún objeto coincide", + "outliner.no_material_assigned": "Sin material asignado", + "outliner.no_objects_or_geometry_yet": "Aún no hay objetos ni geometría", + "outliner.paint": "\nPintura: #{r:02X}{g:02X}{b:02X}", + "outliner.paint_mixed_face_colors": "\nPintura: colores de cara mixtos", + "outliner.partial_material_assignment": "Asignación de material parcial", + "outliner.search_objects": "Buscar objetos", + "outliner.show": "Mostrar", + "outliner.text": "Texto", + "outliner.try_another_name_or_clear_the_search_to": "Prueba con otro nombre o borra la búsqueda para ver todos los objetos.", + "outliner.unhide": "Mostrar", + "outliner.unhide_the_parent_object_before_selecting_its_contents": "Muestra el objeto principal antes de seleccionar su contenido", + "outliner.unlock_the_parent_object_before_selecting_its_contents": "Desbloquea el objeto principal antes de seleccionar su contenido", + "outliner.visibility_accessibility": "{} {kind}: {label}", + "outliner.visibility_is_also_controlled_by_the_parent_object": "{name}\nLa visibilidad también la controlan el objeto principal y las etiquetas", + "precision.add_or_subtract_quantities_with_matching_units": "Suma o resta cantidades con las mismas unidades", + "precision.cannot_divide_by_zero": "No se puede dividir entre cero", + "precision.close_each_opening_parenthesis": "Cierra cada paréntesis de apertura", + "precision.draw_a_stroke_before_setting_its_total_length": "Dibuja un trazo antes de definir su longitud total", + "precision.enter_a_finite_angle": "Introduce un ángulo finito", + "precision.enter_a_formula_such_as_60cm_89mm_or": "Introduce una fórmula como 60cm - 89mm o (1m + 20cm) / 2", + "precision.enter_a_positive_length_between_0_0000001_m": "Introduce una longitud positiva entre 0.0000001 m y 10000000 m", + "precision.enter_width_height_such_as_5m_50mm": "Introduce ancho,alto, por ejemplo 5m,50mm", + "precision.scale_factors_must_use_numbers_without_units": "Los factores de escala deben usar números sin unidades", + "precision.the_formula_is_too_long": "La fórmula es demasiado larga", + "precision.the_formula_must_produce_a_finite_value": "La fórmula debe producir un valor finito", + "precision.the_formula_must_produce_a_length_not_an": "La fórmula debe producir una longitud, no un área ni un volumen", + "precision.use_fewer_nested_parentheses": "Usa menos paréntesis anidados", + "precision.use_length_units_mm_cm_m_km_in": "Usa unidades de longitud mm, cm, m, km, in, ft o yd", + "project_files.preparing_pdf": "Preparando PDF…", + "project_files.allow_pop_ups_for_benchup_then_generate_the": "Permite las ventanas emergentes para BenchUp y vuelve a generar el PDF.", + "project_files.already_exists_select_that_file_in_the_save": "{} ya existe. Selecciona ese archivo .{extension} en el diálogo de guardado para reemplazarlo.", + "project_files.could_not_compress_project": "No se pudo comprimir el proyecto: {error}", + "project_files.could_not_decompress_project": "No se pudo descomprimir el proyecto: {error}", + "project_files.cutting_plan": "plan de corte", + "project_files.estimates": "{name} - estimaciones", + "project_files.export_geometry": "Exportar geometría", + "project_files.export_screenshot": "Exportar captura de pantalla", + "project_files.export_time_and_cost_estimates": "Exportar estimaciones de tiempo y costos", + "project_files.export_woodworking_cutting_plan": "Exportar plan de corte de carpintería", + "project_files.export_workshop_labels": "Exportar etiquetas de taller", + "project_files.geometry": "Geometría", + "project_files.geometry_import_is_limited_to_64_mib_per": "La importación de geometría está limitada a 64 MiB por archivo", + "project_files.import_geometry_stl_mm_obj_metres_dxf_declared": "Importar geometría (STL: mm; OBJ: metros; DXF: unidades declaradas o mm)", + "project_files.benchup_project": "Proyecto de BenchUp", + "project_files.no_browser_document": "No hay documento del navegador", + "project_files.no_browser_document_body": "No hay cuerpo del documento del navegador", + "project_files.no_browser_window": "No hay ventana del navegador", + "project_files.open_benchup_project": "Abrir proyecto de BenchUp", + "project_files.pdf_document": "Documento PDF", + "project_files.pdf_saved_to_but_its_viewer_could_not": "El PDF se guardó en {}, pero no se pudo abrir su visor: {error}", + "project_files.report_name": "{name} - {suffix}", + "project_files.save_benchup_project_as": "Guardar proyecto de BenchUp como", + "project_files.screenshot": "Captura de pantalla", + "project_files.screenshot_2": "{name} - captura de pantalla", + "project_files.sketchup_import": "Importación de SketchUp", + "project_files.the_browser_could_not_download_this_obj_file": "El navegador no pudo descargar este archivo OBJ", + "project_files.the_browser_could_not_download_this_screenshot": "El navegador no pudo descargar esta captura de pantalla", + "project_files.the_browser_could_not_open_this_pdf": "El navegador no pudo abrir este PDF", + "project_files.the_browser_could_not_read_this_geometry_file": "El navegador no pudo leer este archivo de geometría", + "project_files.the_browser_could_not_read_this_project": "El navegador no pudo leer este proyecto", + "project_files.the_browser_could_not_save_this_project": "El navegador no pudo guardar este proyecto", + "project_files.the_imported_geometry_cannot_be_inserted_in_this": "La geometría importada no se puede insertar en este contexto de edición", + "project_files.the_project_is_not_utf_8_text": "El proyecto no es texto UTF-8", + "project_files.untitled_model": "Modelo sin título", + "project_files.workshop_labels": "etiquetas de taller", + "projects.allow_rotating_pieces_90_degrees": "Permitir girar las piezas 90 grados", + "projects.browser_file_browser_hides_the_full_path_reselect": "Archivo del navegador: {file} (el navegador oculta la ruta completa; vuelve a seleccionarlo para abrirlo)", + "projects.could_not_complete_file_operation": "No se pudo completar la operación de archivo: {error}", + "projects.could_not_save_project": "No se pudo guardar el proyecto: {error}", + "projects.create_a_pdf_with_quantities_and_finished_cut": "Crea un PDF con las cantidades y las medidas finales de corte.", + "projects.create_one_label_per_piece_with_its_cutting": "Crea una etiqueta por pieza con su marca del plan de corte, nombre y grosor, ancho y largo finales. Imprime a tamaño real en papel A4.", + "projects.cutting_plan_pdf_opened_in_a_new_tab": "El PDF del plan de corte se abrió en una pestaña nueva", + "projects.cutting_plan_pdf_saved_and_opened": "El PDF del plan de corte se guardó y se abrió", + "projects.discard_changes": "Descartar cambios", + "projects.exporting_geometry": "Exportando geometría…", + "projects.exporting_screenshot": "Exportando captura de pantalla…", + "projects.generate_pdf": "Generar PDF…", + "projects.estimates_pdf_opened": "El PDF de estimaciones de tiempo y costos se abrió en una pestaña nueva", + "projects.estimates_pdf_saved": "El PDF de estimaciones de tiempo y costos se guardó y se abrió", + "projects.geometry_download_started": "Se inició la descarga de la geometría", + "projects.geometry_exported": "Geometría exportada", + "projects.geometry_imported_as_editable_groups": "Geometría importada como grupos editables", + "projects.geometry_recovered_from_a_damaged_sketchup_archive": "Geometría recuperada de un archivo de SketchUp dañado. Es posible que falten algunos materiales.", + "projects.respect_wood_grain_direction": "Respetar la dirección de la veta de la madera", + "projects.align_part_grain_with_stock_length": "Alinea la veta asignada de cada pieza con la longitud del stock.", + "projects.show_wood_joinery": "Mostrar ensambles de madera", + "projects.show_joinery_as_dashed_outlines": "Muestra los cortes de ensamble como contornos finos de líneas discontinuas en cada pieza.", + "cutting_plan.joinery_dashed_outlines": "Los contornos discontinuos muestran el ensamble proyectado sobre la cara del stock.", + "cutting_plan.grain_along_stock_length": "La veta asignada sigue la longitud del stock.", + "cutting_plan.grain_through_thickness": "La veta atraviesa el grosor y no puede seguir la longitud del stock.", + "cutting_plan.joinery_omitted": "Se omiten los ensambles que no se pudieron evaluar.", + "projects.import_geometry": "Importar geometría", + "projects.import_sketchup": "Importar SketchUp", + "projects.importing_geometry": "Importando geometría…", + "projects.joint_error_separator": " · ", + "projects.layouts_use_assigned_stock_dimensions_and_saw_kerf": "Los diseños usan las dimensiones del stock asignado y el ancho de corte. Confirma la veta, los ensambles y las formas irregulares con el modelo; es una distribución práctica, no un mínimo garantizado.", + "projects.leave_off_when_the_length_must_follow_the": "Déjalo desactivado cuando la longitud deba seguir la veta del stock.", + "projects.location_and_share_link": "{location}\n{url}", + "projects.mm": " mm", + "projects.no_recent_projects": "No hay proyectos recientes", + "projects.only_visible_parts_with_assigned_stock_materials_are": "Solo se incluyen las piezas visibles con materiales de stock asignados. Asigna materiales en Información de la entidad usando la tabla de precios de stock. Las piezas anidadas heredan el material de su ensamblaje, a menos que se asignen por separado.", + "projects.opening_project": "Abriendo proyecto…", + "projects.please_wait": "Espera un momento…", + "projects.preparing_estimates_pdf": "Preparando PDF de estimaciones de tiempo y costos…", + "projects.preparing_cutting_plan_pdf": "Preparando PDF del plan de corte…", + "projects.preparing_workshop_labels_pdf": "Preparando PDF de etiquetas de taller…", + "projects.project_download_started": "Se inició la descarga del proyecto", + "projects.project_file": "Archivo de proyecto", + "projects.project_opened": "Proyecto abierto", + "projects.project_saved": "Proyecto guardado", + "projects.recent_project_detail": "\n{detail}", + "projects.recent_projects": "Proyectos recientes", + "projects.remove_from_recent_projects": "Quitar de proyectos recientes", + "projects.reselect_to_reopen_this_browser_file": "Vuelve a seleccionar {} para reabrir este archivo del navegador", + "projects.restart_with_local_recovery": "Reiniciar con recuperación local", + "projects.save_changes_to_as_a_project_file_before": "¿Guardar los cambios de “{}” como archivo de proyecto antes de continuar?", + "projects.save_project_changes": "¿Guardar los cambios del proyecto?", + "projects.saving_project": "Guardando proyecto…", + "projects.saw_kerf_cut_width": "Ancho de corte de sierra", + "projects.screenshot_download_started": "Se inició la descarga de la captura de pantalla", + "projects.screenshot_exported": "Captura de pantalla exportada", + "projects.select_at_least_one_part_or_choose_the": "Selecciona al menos una pieza o elige todo el proyecto.", + "projects.selected_parts_assemblies_visible_only": "Piezas / ensamblajes seleccionados (solo visibles)", + "projects.the_file_operation_did_not_complete": "La operación de archivo no se completó", + "projects.this_project_belongs_to_connect_to_that_server": "Este proyecto pertenece a {server}. Conéctate a ese servidor en Cuenta antes de abrirlo.", + "projects.whole_project_visible_only": "Todo el proyecto (solo visible)", + "projects.wood_joint": "Ensamble de madera #{id}: {error}", + "projects.woodworking_cutting_plan": "Plan de corte de carpintería", + "projects.workshop_labels": "Etiquetas de taller", + "projects.workshop_labels_pdf_opened_in_a_new_tab": "El PDF de etiquetas de taller se abrió en una pestaña nueva", + "projects.workshop_labels_pdf_saved_and_opened": "El PDF de etiquetas de taller se guardó y se abrió", + "realtime.invalid_websocket_event": "Evento de WebSocket no válido", + "realtime.unsupported_websocket_tls_stream": "Flujo TLS de WebSocket no compatible", + "realtime.url_ticket_connection_queue_wake": "url,ticket,connection,queue,wake", + "realtime.websocket_connection_failed": "Falló la conexión de WebSocket", + "realtime.websocket_disconnected": "WebSocket desconectado", + "realtime.websocket_disconnected_2": "WebSocket desconectado: {e}", + "realtime.websocket_host_missing": "Falta el host de WebSocket", + "realtime.websocket_unavailable": "WebSocket no disponible", + "release_notes.format": "•", + "release_notes.latest": " · Última", + "release_notes.newer": "Más recientes", + "release_notes.older": "Anteriores", + "release_notes.release_notes": "Notas de la versión", + "release_notes.v": "v{} · {}{latest}", + "release_notes.v_latest": "v{} · Última", + "release_notes.you_re_using_benchup": "Estás usando BenchUp {}", + "rotate_tool.click_a_reference_point_away_from_the_pivot": "Haz clic en un punto de referencia alejado del pivote · Las flechas bloquean el plano de rotación · Esc para cancelar", + "rotate_tool.click_a_reference_point_away_from_the_pivot_2": "Haz clic primero en un punto de referencia alejado del pivote", + "rotate_tool.click_to_rotate_type_an_angle_or_formula": "Haz clic para girar · Escribe un ángulo o fórmula + Enter · Las flechas bloquean el plano de rotación · Esc para cancelar", + "rotate_tool.select_geometry_click_a_pivot_on_or_offset": "Selecciona geometría · Haz clic en un pivote (sobre la selección o con desfase) · Las flechas bloquean el plano de rotación", + "rotate_tool.the_reference_lies_on_the_rotation_axis_clear": "La referencia está sobre el eje de rotación; quita el bloqueo o reinicia con otra referencia", + "screenshot_dialog.a_compatible_gpu_renderer_is_required_for_screenshots": "Se requiere un renderizador de GPU compatible para las capturas de pantalla.", + "screenshot_dialog.all_views_in_one_image": "Todas las vistas en una imagen", + "screenshot_dialog.choose_one_or_more_views": "Elige una o más vistas.", + "screenshot_dialog.choose_your_views_page_layout_and_export_format": "Elige tus vistas, el diseño de página y el formato de exportación.", + "screenshot_dialog.combined_sheet": "Hoja combinada", + "screenshot_dialog.could_not_read_screenshot": "No se pudo leer la captura de pantalla: {e}", + "screenshot_dialog.dpi": "{} DPI", + "screenshot_dialog.dpi_2": "{dpi} DPI", + "screenshot_dialog.export": "Exportar", + "screenshot_dialog.fit_the_visible_model_in_parallel_projection": "Ajusta el modelo visible en proyección paralela.", + "screenshot_dialog.fit_visible_content_using_the_current_viewing_angle": "Ajusta el contenido visible usando el ángulo de visión y la proyección actuales.", + "screenshot_dialog.format": "Formato", + "screenshot_dialog.generate": "Generar", + "screenshot_dialog.generate_screenshot": "Generar captura de pantalla", + "screenshot_dialog.individual_views": "Vistas individuales", + "screenshot_dialog.landscape": "Horizontal", + "screenshot_dialog.light_mode_transparent_background": "Modo claro · Fondo transparente", + "screenshot_dialog.mm": "{width:.0} × {height:.0} mm · {}", + "screenshot_dialog.model_views": "Vistas del modelo", + "screenshot_dialog.one_image_per_view": "Una imagen por vista", + "screenshot_dialog.one_pdf_page_per_view": "Una página de PDF por vista.", + "screenshot_dialog.page": "Página", + "screenshot_dialog.paper_size": "Tamaño de papel", + "screenshot_dialog.pixels_per_sheet": "{w} × {h} píxeles por hoja", + "screenshot_dialog.portrait": "Vertical", + "screenshot_dialog.preparing_preview": "Preparando vista previa…", + "screenshot_dialog.preview_unavailable": "Vista previa no disponible", + "screenshot_dialog.preview_viewport": "Área de vista previa", + "screenshot_dialog.rendering_export": "Renderizando exportación…", + "screenshot_dialog.resolution": "Resolución", + "screenshot_dialog.screenshot_color": "Color de la captura de pantalla", + "screenshot_dialog.screenshot_depth": "Profundidad de la captura de pantalla", + "screenshot_dialog.screenshot_preview": "screenshot-preview-{i}", + "screenshot_dialog.screenshot_readback": "Lectura de la captura de pantalla", + "screenshot_dialog.screenshot_readback_did_not_complete": "La lectura de la captura de pantalla no se completó.", + "screenshot_dialog.screenshot_rendering_timed_out_try_fewer_views_or": "Se agotó el tiempo de renderizado de la captura de pantalla. Prueba con menos vistas o una resolución menor.", + "screenshot_dialog.screenshot_viewport": "Área de la captura de pantalla", + "screenshot_dialog.select_a_view_to_preview": "Selecciona una vista para previsualizar", + "screenshot_dialog.selected": "Seleccionadas: {count}", + "screenshot_dialog.separate_images_packaged_in_a_zip_file": "Imágenes separadas empaquetadas en un archivo ZIP.", + "screenshot_dialog.this_image_exceeds_the_gpu_texture_size_choose": "Esta imagen supera el tamaño de textura de la GPU. Elige un papel más pequeño o una resolución menor.", + "screenshot_dialog.views": "Vistas", + "screenshots.a3_297_420_mm": "A3 · 297 × 420 mm", + "screenshots.a4_210_297_mm": "A4 · 210 × 297 mm", + "screenshots.a5_148_210_mm": "A5 · 148 × 210 mm", + "screenshots.back": "Posterior", + "screenshots.bottom": "Inferior", + "screenshots.current": "Actual", + "screenshots.date_unavailable": "Fecha no disponible", + "screenshots.devicegray": "DeviceGray", + "screenshots.devicergb": "DeviceRGB", + "screenshots.front": "Frontal", + "screenshots.isometric": "Isométrica", + "screenshots.jpeg": "JPEG", + "screenshots.jpeg_cannot_preserve_transparency_choose_png_webp_or": "JPEG no puede conservar la transparencia. Elige PNG, WebP o PDF.", + "screenshots.left": "Izquierda", + "screenshots.legal_8_5_14_in": "Legal · 8.5 × 14 in", + "screenshots.letter_8_5_11_in": "Letter · 8.5 × 11 in", + "screenshots.model_views": "Vistas del modelo", + "screenshots.pdf": "PDF", + "screenshots.png": "PNG", + "screenshots.right": "Derecha", + "screenshots.select_at_least_one_view": "Selecciona al menos una vista.", + "screenshots.smask_0_r": " /SMask {id} 0 R", + "screenshots.some_screenshot_views_did_not_finish_rendering": "Algunas vistas de la captura de pantalla no terminaron de renderizarse.", + "screenshots.tabloid_11_17_in": "Tabloid · 11 × 17 in", + "screenshots.this_export_is_too_large_choose_a_lower": "Esta exportación es demasiado grande. Elige una resolución menor, un papel más pequeño o menos vistas.", + "screenshots.top": "Superior", + "screenshots.webp": "WebP", + "router.angle": "Ángulo de bisel", + "router.angle_does_not_fit": "El ángulo de bisel no cabe entre las dos caras.", + "router.applied": "Fresar aristas", + "router.bead": "Cordón", + "router.bevel": "Bisel", + "router.chamfer": "Chaflán", + "router.click_edges": "Haz clic en una arista para seleccionarla y luego en un perfil para aplicar el fresado.", + "router.convex_edge_required": "Elige una arista de contorno recta y convexa, o dibuja una trayectoria de fresado sobre una cara sólida.", + "router.cove": "Media caña", + "router.defaults": "Perfil para aristas nuevas", + "router.depth": "Profundidad", + "router.edge": "Arista #{id}", + "router.edge_error": "Arista #{id}: {error}", + "router.edit": "Editar", + "router.groove": "Ranura", + "router.invalid_settings": "Ajustes de fresado no válidos.", + "router.none": "Ninguno", + "router.offset": "Retranqueo", + "router.ogee": "Gola romana", + "router.profile": "Perfil", + "router.profile_does_not_fit": "El perfil no cabe en el stock disponible.", + "router.profile_too_small": "El perfil contiene detalles demasiado pequeños para esta pieza.", + "router.protected": "Desbloquea la pieza y haz visibles las aristas seleccionadas para editar el fresado.", + "router.rabbet": "Rebaje", + "router.radius": "Radio", + "router.remove": "Quitar", + "router.removed": "Quitar fresado de aristas", + "router.reverse": "Intercambiar cara de referencia", + "router.roundover": "Redondeo", + "router.router": "Fresadora", + "router.saved": "Fresados guardados", + "router.select_edges": "Selecciona una arista y luego haz clic en un perfil para aplicar el fresado. Elige Ninguno para quitar el fresado.", + "router.shoulder": "Hombro", + "router.size": "Tamaño", + "router.solid_piece_required": "El fresado requiere un sólido cerrado. Se admiten aristas dibujadas sobre sus caras y aristas de contorno existentes.", + "router.surface_path_required": "Dibuja una arista recta sobre una cara sólida o elige una arista de contorno de sólido existente.", + "router.square_edge_required": "Este perfil requiere una arista de 90°.", + "router.suspended": "Fresado de la arista #{id} suspendido: {error}", + "router.width": "Ancho", + "section_gizmo.drag_section_along_its_axis_esc_to_cancel": "Arrastra la sección {} a lo largo de su eje · Esc para cancelar. Si se ve a lo largo del eje, arrastra hacia arriba/abajo.", + "settings.adjust_mouse_and_trackpad_navigation_sensitivity": "Ajusta la sensibilidad de la navegación con el mouse y el trackpad.", + "settings.automatically_group_new_shapes": "Agrupar automáticamente las formas nuevas", + "settings.automatically_size_grid_when_zooming": "Ajustar automáticamente el tamaño de la cuadrícula al hacer zoom", + "settings.axis_and_inference_colors": "Colores de ejes e inferencias", + "settings.axis_inferences_use_the_corresponding_axis_color": "Las inferencias de eje usan el color del eje correspondiente.", + "settings.changes_are_saved_automatically_on_this_device": "Los cambios se guardan automáticamente en este dispositivo.", + "settings.clear_both_axis_locks_after_completing_an_edit": "Quita ambos bloqueos de eje después de completar una edición o medición, al cancelar o al cambiar de herramienta. Desactívalo para conservar los bloqueos hasta que los quites manualmente.", + "settings.click_a_shortcut_then_press_a_letter_or": "Haz clic en un atajo y luego pulsa una letra o la barra espaciadora. Escape cancela. Reutilizar una tecla intercambia las asignaciones. Los comandos del proyecto y los gestos de navegación conservan sus atajos estándar.", + "settings.controls": "Controles", + "settings.default_units": "Unidades predeterminadas", + "settings.defaults_for_objects_in_all_projects_objects_use": "Valores predeterminados para los objetos de todos los proyectos. Los objetos usan estos valores hasta que se personalicen en Información de la entidad.", + "settings.diagnostics": "Diagnóstico", + "settings.display_the_rendering_frame_rate_in_the_lower": "Muestra la velocidad de fotogramas del renderizado en la esquina inferior izquierda de cada vista. Activa el renderizado continuo mientras está visible.", + "settings.format": "×", + "settings.grid": "Cuadrícula", + "settings.grid_spacing": "Espaciado de la cuadrícula", + "settings.group_new_shapes_outside_existing_objects": "Crea un grupo independiente para cada forma nueva dibujada fuera de un grupo o componente existente. Los segmentos de línea conectados comparten un grupo. Dibujar dentro de un objeto edita ese objeto.", + "settings.inference_points": "Puntos de inferencia", + "settings.infinite_grid": "Cuadrícula infinita", + "settings.language": "Idioma", + "settings.language.english": "English", + "settings.length_of_each_side_of_the_square_centered": "Longitud de cada lado del cuadrado, centrado en el origen del modelo. El límite permanece fijo al hacer zoom o rotar.", + "settings.material": "Material", + "settings.materials_estimates": "Materiales y estimaciones", + "settings.maximum_length": "Longitud máxima", + "settings.midpoints": "Puntos medios", + "settings.navigation": "Navegación", + "settings.no_tools_match_your_search": "Ninguna herramienta coincide con tu búsqueda.", + "settings.orbit_speed": "Velocidad de órbita", + "settings.pan_speed": "Velocidad de desplazamiento", + "settings.rendering": "Renderizado", + "settings.reset_axis_locks_on_each_action": "Restablecer los bloqueos de eje en cada acción", + "settings.restore_default_colors": "Restaurar colores predeterminados", + "settings.restore_default_shortcuts": "Restaurar atajos predeterminados", + "settings.reverse_scroll_zoom": "Invertir zoom con la rueda", + "settings.sanding_finishing": "Lijado y acabado", + "settings.search_tools": "Buscar herramientas…", + "settings.settings": "Configuración", + "settings.shadow_antialiasing": "Suavizado de sombras", + "settings.shortcut": "Atajo", + "settings.shortcuts_management": "Administración de atajos", + "settings.show_frames_per_second": "Mostrar fotogramas por segundo", + "settings.show_grid": "Mostrar cuadrícula", + "settings.smooth_shadow_boundaries_with_4_multisampling_only_shadows": "Suaviza los bordes de las sombras con multimuestreo 4×. Solo afecta a las sombras. Los cambios se aplican de inmediato.", + "settings.snap_to_grid": "Ajustar a la cuadrícula", + "settings.spacing_controls_both_grid_lines_and_grid_snapping": "El espaciado controla tanto las líneas de la cuadrícula como el ajuste a la cuadrícula. Cada quinta línea aparece resaltada.", + "settings.system": "Sistema", + "settings.tool": "Herramienta", + "settings.tools": "Herramientas", + "settings.tools_for_checking_performance_and_troubleshooting": "Herramientas para comprobar el rendimiento y solucionar problemas.", + "settings.used_for_new_projects_change_the_current_project": "Se usa para proyectos nuevos. Cambia las unidades del proyecto actual en el inspector de Unidades.", + "settings.workspace": "Espacio de trabajo", + "settings.x_axis": "Eje X", + "settings.y_axis": "Eje Y", + "settings.z_axis": "Eje Z", + "settings.zoom_speed": "Velocidad de zoom", + "sharing.access_approved": "aprobado", + "sharing.access_denied": "denegado", + "sharing.access_edit": "edición", + "sharing.access_is_awaiting_approval": "El acceso está pendiente de aprobación", + "sharing.access_none": "ninguno", + "sharing.access_notifications": { + "one": "Notificación de acceso ({count})", + "other": "Notificaciones de acceso ({count})" + }, + "sharing.access_owner": "propietario", + "sharing.access_pending": "pendiente", + "sharing.access_reference_revision": "Acceso: {} · revisión de referencia: {}", + "sharing.access_request": "{} · {} · {}", + "sharing.access_view": "lectura", + "sharing.account": "Cuenta", + "sharing.account_changed": "Cuenta cambiada", + "sharing.account_sharing_connection": "Cuenta: {account} · Conexión de uso compartido: {connection}", + "sharing.approve": "Aprobar", + "sharing.avatar_request_ended": "Finalizó la solicitud del avatar", + "sharing.avatar_unavailable": "Avatar no disponible", + "sharing.being_edited_by": "Editado por {name}{}", + "sharing.checking_server_revision_and_file_timestamp_before_saving": "Comprobando la revisión del servidor y la marca de tiempo del archivo antes de guardar…", + "sharing.connected_server_identity_not_reported": "Conectado; el servidor no informó su identidad", + "sharing.copy_link": "Copiar enlace", + "sharing.create_invitation_link": "Crear enlace de invitación", + "sharing.deny": "Denegar", + "sharing.editing_access_has_not_been_confirmed_check_your": "No se ha confirmado el acceso de edición. Revisa tu cuenta y Archivo → Compartir.", + "sharing.guest": "Invitado", + "sharing.guest_2": " · Invitado", + "sharing.invalid_server_access_response": "Respuesta de acceso del servidor no válida", + "sharing.invalid_websocket_endpoint": "Punto de conexión de WebSocket no válido", + "sharing.invalid_websocket_project_state": "Estado del proyecto de WebSocket no válido", + "sharing.keep_local_as_separate_project": "Conservar la versión local como proyecto aparte", + "sharing.local_session": "Sesión local", + "sharing.missing_guest_identity": "Falta la identidad de invitado", + "sharing.new_project_access_notification_file_share": "Notificación de acceso a un proyecto nuevo — Archivo → Compartir", + "sharing.not_connected": "Sin conexión", + "sharing.not_signed_in": "Sin iniciar sesión", + "sharing.objects_occupied_by_file_share": "Objetos ocupados por {} · Archivo → Compartir", + "sharing.occupied_by": "Ocupado por {}: {:?}", + "sharing.open_an_invitation_link": "Abrir un enlace de invitación", + "sharing.open_shared_project": "Abrir proyecto compartido", + "sharing.original_project_author": "Autor original del proyecto: {}", + "sharing.persistent_invitation_link_author_approval_required_for_reading": "Enlace de invitación persistente · se requiere la aprobación del autor para leer y editar", + "sharing.project_changed": "Proyecto modificado", + "sharing.project_request_failed": "Falló la solicitud del proyecto", + "sharing.project_request_interrupted": "Solicitud del proyecto interrumpida", + "sharing.publish_local_version": "Publicar versión local", + "sharing.refresh_access": "Actualizar acceso", + "sharing.refreshing_sharing_identity": "Actualizando la identidad de uso compartido", + "sharing.request_editing_access": "Solicitar acceso de edición", + "sharing.request_viewing_access": "Solicitar acceso de lectura", + "sharing.resolve_the_project_version_conflict_in_file_share": "Resuelve el conflicto de versiones del proyecto en Archivo → Compartir antes de editar.", + "sharing.review_project": "Revisar proyecto", + "sharing.save_your_current_project_before_opening_an_invitation": "Guarda tu proyecto actual antes de abrir una invitación.", + "sharing.server_revision_differs_from_this_file_local_changes": "La revisión del servidor {} ({}) difiere de este archivo. Se conservaron los cambios locales. Elige una versión; se volverá a comprobar si hay otra actualización simultánea.", + "sharing.server_timestamp_file_timestamp_utc_epoch_seconds": "Marca de tiempo del servidor: {} · marca de tiempo del archivo: {} (segundos de época UTC)", + "sharing.server_unavailable": "Servidor no disponible", + "sharing.share": "Compartir…", + "sharing.share_2": "Compartir… ({pending})", + "sharing.share_project": "Compartir proyecto", + "sharing.share_uploads_this_project_to_your_configured_benchup": "Compartir sube este proyecto a tu servidor de BenchUp configurado. Los visitantes deben solicitar acceso.", + "sharing.shared": "Compartido · {}", + "sharing.sign_in_to_your_account": "Inicia sesión en tu cuenta", + "sharing.sync": "Sincronización: {error}", + "sharing.sync_conflict_file_share": "Conflicto de sincronización · Archivo → Compartir", + "sharing.the_access_response_belongs_to_another_project": "La respuesta de acceso pertenece a otro proyecto", + "sharing.the_server_confirmed_access_but_did_not_provide": "El servidor confirmó el acceso de {} pero no proporcionó los datos del proyecto", + "sharing.the_server_confirmed_access_but_the_project_data": "El servidor confirmó el acceso de {}, pero no se pudieron cargar los datos del proyecto: {error}", + "sharing.the_server_confirmed_editing_access_but_project_data": "El servidor confirmó el acceso de edición, pero los datos del proyecto no están disponibles. Revisa el error en Archivo → Compartir.", + "sharing.the_sharing_connection_uses_a_different_identity_refresh": "La conexión de uso compartido usa una identidad diferente. Actualiza el acceso para reconectar con tu cuenta actual.", + "sharing.this_file_belongs_to_a_different_or_invalid": "Este archivo pertenece a un servidor distinto o no válido. Abre Compartir para inspeccionarlo; no se enviaron credenciales.", + "sharing.use_an_invitation_from_the_configured_server": "Usa una invitación del servidor configurado.", + "sharing.use_server_version": "Usar la versión del servidor", + "sharing.waiting_for_exclusive_selection": "Esperando la selección exclusiva…", + "sharing.waiting_for_server_confirmation": "Esperando la confirmación del servidor", + "sharing.waiting_for_the_server_connection_before_editing_your": "Esperando la conexión con el servidor antes de editar. Tu acceso de edición no ha cambiado.", + "sharing.waiting_for_the_server_to_confirm_the_object": "Esperando que el servidor confirme el bloqueo de selección del objeto.", + "sharing.websocket_connection_timed_out": "Se agotó el tiempo de conexión de WebSocket", + "sharing.websocket_disconnected": "WebSocket desconectado", + "sharing.websocket_ticket_request_interrupted": "Solicitud de ticket de WebSocket interrumpida", + "sharing.websocket_unavailable": "WebSocket no disponible", + "shortcuts.acquire_reference_axes": "Adquirir ejes de referencia", + "shortcuts.axis_lock_hover": "Bloqueo de eje + pasar el cursor", + "shortcuts.axis_lock_keys": "→ / ← / ↑", + "shortcuts.c": "{command} + C", + "shortcuts.cancel_current_operation": "Cancelar la operación actual", + "shortcuts.circle": "Círculo", + "shortcuts.clear_all_axis_locks": "Quitar todos los bloqueos de eje", + "shortcuts.close_editing_level": "Cerrar nivel de edición", + "shortcuts.connected_lines": "Líneas conectadas", + "shortcuts.copy": "Copiar", + "shortcuts.ctrl_a": "Ctrl + A", + "shortcuts.ctrl_f": "Ctrl + F", + "shortcuts.ctrl_move_pointer": "Ctrl + mover el puntero", + "shortcuts.cut": "Cortar", + "shortcuts.delete_backspace": "Suprimir / Retroceso", + "shortcuts.delete_selection": "Eliminar selección", + "shortcuts.dimensions": "Cotas", + "shortcuts.divide_a_copy_span": "Dividir un tramo de copias", + "shortcuts.double_click": "Doble clic", + "shortcuts.drawing_measuring": "Dibujo y medición", + "shortcuts.edit_group_or_component": "Editar grupo o componente", + "shortcuts.erase_edges_or_faces": "Borrar aristas o caras", + "shortcuts.escape": "Esc", + "shortcuts.find_a_tool_command_or_gesture": "Busca una herramienta, un comando o un gesto.", + "shortcuts.flip_solid_operations": "Voltear y operaciones con sólidos", + "shortcuts.freehand": "Mano alzada", + "shortcuts.hover_geometry": "Pasar el cursor sobre la geometría", + "shortcuts.keyboard_shortcuts": "Atajos de teclado", + "shortcuts.lengths_accept_units_and_formulas_1m_20cm_2": "Las longitudes aceptan unidades y fórmulas: (1m + 20cm) / 2", + "shortcuts.lock_a_plane": "Bloquear un plano", + "shortcuts.lock_shape_plane_normal_x_y_z": "Bloquear la normal del plano de la forma X / Y / Z", + "shortcuts.lock_x_y_z_axis": "Bloquear eje X / Y / Z", + "shortcuts.match_reference_coordinate": "Hacer coincidir la coordenada de referencia", + "shortcuts.middle_drag": "Arrastrar con el botón central", + "shortcuts.modeling": "Modelado", + "shortcuts.move_geometry": "Mover geometría", + "shortcuts.move_pointer": "⌘ + mover el puntero", + "shortcuts.n": "{command} + N", + "shortcuts.n_enter": "/N + Enter", + "shortcuts.navigation": "Navegación", + "shortcuts.new_project": "Nuevo proyecto", + "shortcuts.no_shortcuts_found": "No se encontraron atajos", + "shortcuts.nx_enter": "Nx + Enter", + "shortcuts.o": "{command} + O", + "shortcuts.object_clipboard_menu": "Menú de objetos y portapapeles", + "shortcuts.offset_a_face": "Desfasar una cara", + "shortcuts.open_project": "Abrir proyecto", + "shortcuts.orbit": "Órbita", + "shortcuts.orbit_while_drawing": "Orbitar mientras se dibuja", + "shortcuts.paint_faces": "Pintar caras", + "shortcuts.pan_while_orbiting": "Desplazar mientras se orbita", + "shortcuts.paste": "Pegar", + "shortcuts.paste_in_place": "Pegar en el mismo lugar", + "shortcuts.polygon": "Polígono", + "shortcuts.precision_inference": "Precisión e inferencia", + "shortcuts.press_a_key": "Pulsa una tecla…", + "shortcuts.project_clipboard": "Proyecto y portapapeles", + "shortcuts.protractor": "Transportador", + "shortcuts.push_pull_a_face": "Empujar/Tirar una cara", + "shortcuts.push_pull_new_faces": "Alternar caras nuevas de Empujar/Tirar", + "shortcuts.recorder_accessibility": "Atajo {key}. {action}", + "shortcuts.recorder_idle": "Haz clic para cambiar", + "shortcuts.recorder_listening": "Pulsa una letra o la barra espaciadora; Escape cancela", + "shortcuts.recorder_tooltip": "Haz clic y luego pulsa una letra o la barra espaciadora. Escape cancela.", + "shortcuts.rectangle": "Rectángulo", + "shortcuts.repeat_copies": "Repetir copias", + "shortcuts.right_click": "Clic derecho", + "shortcuts.rotate_geometry": "Girar geometría", + "shortcuts.s": "{command} + S", + "shortcuts.save_as": "Guardar como", + "shortcuts.scale_geometry": "Escalar geometría", + "shortcuts.scroll": "Rueda", + "shortcuts.search_outliner": "Buscar en el esquema", + "shortcuts.search_shortcuts": "Buscar atajos…", + "shortcuts.select": "Seleccionar", + "shortcuts.select_all": "Seleccionar todo", + "shortcuts.selection_objects": "Selección y objetos", + "shortcuts.set_length_or_radius": "Definir longitud o radio", + "shortcuts.set_rectangle_size": "Definir tamaño del rectángulo", + "shortcuts.shift_click": "Shift + clic", + "shortcuts.shift_drag": "Shift + arrastrar", + "shortcuts.shift_middle_drag": "Shift + arrastrar con el botón central", + "shortcuts.shift_s": "{command} + Shift + S", + "shortcuts.shift_v": "{command} + Shift + V", + "shortcuts.shift_z": "{command} + Shift + Z", + "shortcuts.space": "Espacio", + "shortcuts.tab": "Tab", + "shortcuts.tape_measure": "Cinta métrica", + "shortcuts.temporary_orbit": "Órbita temporal", + "shortcuts.temporary_pan": "Desplazamiento temporal", + "shortcuts.toggle_move_copy": "Alternar Mover / Copiar", + "shortcuts.toggle_selection": "Alternar selección", + "shortcuts.toggle_sidebar": "Mostrar u ocultar la barra lateral", + "shortcuts.toggle_two_axis_locks": "Alternar dos bloqueos de eje", + "shortcuts.try_a_tool_name_like_orbit_or_a": "Prueba con el nombre de una herramienta, como “órbita”, o con una tecla, como “{command}”.", + "shortcuts.union_flyout": "Menú desplegable de Unión", + "shortcuts.v": "{command} + V", + "shortcuts.value_enter": "Valor + Enter", + "shortcuts.width_height_enter": "Ancho,alto + Enter", + "shortcuts.x": "{command} + X", + "shortcuts.z": "{command} + Z", + "shortcuts.zoom_to_pointer": "Zoom al puntero", + "sidebar.angle": "Ángulo", + "sidebar.angle_precision": "Precisión de ángulos", + "sidebar.area": "Área", + "sidebar.area_precision": "Precisión de áreas", + "sidebar.automatic_fasteners": "Fijaciones automáticas", + "sidebar.axes": "Ejes", + "sidebar.back_edges": "Aristas posteriores", + "sidebar.background_fog_color": "Color de fondo / niebla", + "sidebar.changes_to_your_project_will_appear_here_make": "Los cambios de tu proyecto aparecerán aquí. Haz una edición para iniciar su historial.", + "sidebar.click_to_place_the_component_escape_to_cancel": "Haz clic para colocar el componente · Escape para cancelar", + "sidebar.color_by_tag": "Color por etiqueta", + "sidebar.component_instances": { + "one": "{count} instancia", + "other": "{count} instancias" + }, + "sidebar.components": "Componentes", + "sidebar.current": " · Actual", + "sidebar.darkness": "Oscuridad", + "sidebar.depth_cue": "Indicador de profundidad", + "sidebar.dimensions": "Cotas", + "sidebar.direction": "Dirección", + "sidebar.display": "Visualización", + "sidebar.display_fog": "Mostrar niebla", + "sidebar.display_shadows": "Mostrar sombras", + "sidebar.edge_style": "Estilo de aristas", + "sidebar.edges": "Aristas", + "sidebar.elevation": "Elevación", + "sidebar.emphasize_group": "Enfocar el grupo editado", + "sidebar.emphasize_the_group_component_currently_being_edited_and": "Resalta el grupo o componente que se está editando y muestra todo lo demás en estructura alámbrica atenuada. No tiene efecto visual fuera de un grupo o componente.", + "sidebar.end": "Fin", + "sidebar.endpoints": "Extremos", + "sidebar.environment": "Entorno", + "sidebar.extensions": "Extensiones", + "sidebar.face_style": "Estilo de caras", + "sidebar.fog": "Niebla", + "sidebar.format": "×", + "sidebar.grid": "Cuadrícula", + "sidebar.group_names": "Nombres de grupos", + "sidebar.guest": "Invitado", + "sidebar.guides": "Guías", + "sidebar.hidden_geometry": "Geometría oculta", + "sidebar.hidden_objects": "Objetos ocultos", + "sidebar.history": "Historial", + "sidebar.history_entry": "{}. {label}{suffix}", + "sidebar.history_entry_detail": "{label} · {who}{when}", + "sidebar.hover_to_preview_an_edit_click_to_restore": "Pasa el cursor para previsualizar una edición. Haz clic para restaurar ese estado.", + "sidebar.length": "Longitud", + "sidebar.length_precision": "Precisión de longitudes", + "sidebar.make_component": "Crear componente…", + "sidebar.materials": "Materiales", + "sidebar.no_components_yet": "Aún no hay componentes", + "sidebar.no_matching_components": "Ningún componente coincide", + "sidebar.no_modifications_yet": "Aún no hay modificaciones.", + "sidebar.oldest_retained_state": "Estado conservado más antiguo", + "sidebar.outliner": "Esquema", + "sidebar.place_instance": "Colocar instancia", + "sidebar.precision_controls_displayed_decimal_places_lengths_entered_without": "La precisión controla los decimales mostrados. Las longitudes introducidas sin unidad usan la unidad de longitud seleccionada.", + "sidebar.profile_width": "Ancho del perfil", + "sidebar.profiles": "Perfiles", + "sidebar.properties": "Propiedades", + "sidebar.remove_this_grit": "Quitar este grano", + "sidebar.reset_display": "Restablecer visualización", + "sidebar.search_components": "Buscar componentes", + "sidebar.select_geometry_and_choose_create_component_from_the": "Selecciona geometría y elige Crear componente en el menú Editar.", + "sidebar.server_verified_account": "Cuenta verificada por el servidor", + "sidebar.shadows": "Sombras", + "sidebar.start": "Inicio", + "sidebar.tags": "Etiquetas", + "sidebar.this_component_is_empty": "Este componente está vacío", + "sidebar.time_cost_estimates": "Estimaciones de tiempo y costos", + "sidebar.try_another_name_or_clear_the_search_to": "Prueba con otro nombre o borra la búsqueda para ver todos los componentes.", + "sidebar.undone": " · Deshecho", + "sidebar.units": "Unidades", + "sidebar.unknown_author": "Autor desconocido", + "sidebar.unknown_time": "Hora desconocida", + "sidebar.unverified": "Sin verificar", + "sidebar.up_to_100_edits_are_saved_with_this": "Se guardan hasta 100 ediciones con este proyecto. Editar un estado anterior reemplaza las ediciones posteriores.", + "sidebar.utc": " · {t} UTC", + "sidebar.utc_2": "{} · {} · {} UTC", + "sidebar.view": "Vista", + "sidebar.volume": "Volumen", + "sidebar.volume_precision": "Precisión de volúmenes", + "sidebar.wood_joinery": "Ensambles de madera", + "sidebar.x_ray": "Rayos X", + "solid_tools.choose_first_body_again": "Elige de nuevo el primer cuerpo", + "solid_tools.choose_groups_components_or_connected_bodies_results_are": "Elige grupos, componentes o cuerpos conectados. Los resultados son grupos editables.", + "solid_tools.click_a_group_component_or_connected_body": "Haz clic en un grupo, componente o cuerpo conectado", + "solid_tools.connected_body": "Cuerpo conectado", + "solid_tools.cutter_selected_click_the_target_solid_to_trim": "Cortador seleccionado · Haz clic en el sólido de destino para recortar · Esc para cancelar", + "solid_tools.first_body_selected_click_the_second_body_esc": "Primer cuerpo seleccionado · Haz clic en el segundo cuerpo · Esc para cancelar", + "solid_tools.first_body_selected_click_the_second_body_or": "Primer cuerpo seleccionado · Haz clic en el segundo cuerpo o cara · Esc para cancelar", + "solid_tools.flip_x": "Voltear X", + "solid_tools.flip_y": "Voltear Y", + "solid_tools.flip_z": "Voltear Z", + "solid_tools.intersect_also_keeps_the_portion_of_a_face": "Intersecar también conserva la parte de una cara que queda dentro de un cuerpo cerrado.", + "solid_tools.mirror_around_the_selection_center": "Reflejar respecto al centro de la selección:", + "solid_tools.the_first_click_chooses_the_cutter_it_stays": "El primer clic elige el cortador. Permanece en el modelo.", + "solid_tools.the_target_was_entirely_removed_by_the_cutter": "El cortador eliminó por completo el sólido de destino", + "solid_tools.unlock_the_body_before_editing_it": "Desbloquea el cuerpo antes de editarlo", + "solid_tools.use_closed_bodies_with_no_stray_edges_or": "Usa cuerpos cerrados sin aristas sueltas ni caras faltantes.", + "solid_tools.volume": "Volumen", + "solid_tools.x_red": "X / rojo", + "solid_tools.y_green": "Y / verde", + "solid_tools.z_blue": "Z / azul", + "startup.benchup_3d_workspace": "BenchUp — espacio de trabajo 3D", + "startup.benchup_could_not_start_its_gpu_renderer_enable": "BenchUp no pudo iniciar su renderizador de GPU. Activa la aceleración por hardware o prueba con un navegador compatible con WebGPU o WebGL2.", + "startup.the_bundled_benchup_icon_must_be_a_valid": "El ícono de BenchUp incluido debe ser un PNG válido", + "stock_settings.add_wood_stock": "+ Agregar stock de madera", + "stock_settings.assigned_objects": { + "one": "{project}{current} · {count} objeto asignado", + "other": "{project}{current} · {count} objetos asignados" + }, + "stock_settings.currency": "Moneda", + "stock_settings.current_project": " (proyecto actual)", + "stock_settings.e_g_pine_2_4": "p. ej., Pino 2 × 4", + "stock_settings.existing_projects_keep_their_material_definitions_and_assignments": "Los proyectos existentes conservan sus definiciones y asignaciones de material. Ya no recibirán actualizaciones del catálogo para este material.", + "stock_settings.format": "×", + "stock_settings.hardboard": "Tablero duro", + "stock_settings.known_project_associations_on_this_device": "Asociaciones de proyectos conocidas en este dispositivo", + "stock_settings.length": "Longitud", + "stock_settings.mm": "{} ({} × {} × {} mm)", + "stock_settings.no_known_project_associations": "No hay asociaciones de proyectos conocidas.", + "stock_settings.no_stock_materials_match_your_search": "Ningún material de stock coincide con tu búsqueda.", + "stock_settings.no_wood_stock_yet_add_a_board_or": "Aún no hay stock de madera. Agrega una tabla o lámina y luego introduce su tamaño y precio.", + "stock_settings.no_wood_types_match_your_search": "Ningún tipo de madera coincide con tu búsqueda.", + "stock_settings.other": "Otro", + "stock_settings.particleboard": "Aglomerado", + "stock_settings.per_piece": "{} por pieza", + "stock_settings.plywood": "Contrachapado", + "stock_settings.price_piece": "Precio / pieza", + "stock_settings.project_material": "{} (material del proyecto)", + "stock_settings.remove": "Quitar {}", + "stock_settings.remove_from_settings_material": "¿Quitar “{name}” de Configuración → Material?", + "stock_settings.remove_material": "¿Quitar material?", + "stock_settings.remove_material_2": "Quitar material", + "stock_settings.save_the_wood_stock_and_finishing_products_you": "Guarda el stock de madera y los productos de acabado que puedes comprar, con los precios de tu proveedor. Usa las dimensiones reales de las tablas o láminas.", + "stock_settings.search_name_or_size": "Buscar por nombre o tamaño…", + "stock_settings.search_wood_types": "Buscar tipos de madera…", + "stock_settings.sizes_in_prices_in_per_piece": "Tamaños en {} · Precios en {} por pieza", + "stock_settings.sizing_units": "Unidades de tamaño", + "stock_settings.solid_wood": "Madera maciza", + "stock_settings.stock": "Stock", + "stock_settings.thickness": "Grosor", + "stock_settings.this_material_may_be_associated_with_objects_in": "Este material puede estar asociado con objetos de otros proyectos.", + "stock_settings.type": "Tipo", + "stock_settings.unassigned": "Sin asignar", + "stock_settings.unspecified": "Sin especificar", + "stock_settings.usage_is_based_on_projects_opened_on_this": "El uso se basa en los proyectos abiertos en este dispositivo y puede estar desactualizado. Es posible que proyectos sin abrir también hagan referencia a este material.", + "stock_settings.used_for_all_prices_and_cost_estimates_in": "Se usa para todos los precios y estimaciones de costos de la aplicación. Cambiar la moneda no convierte los precios ingresados.", + "stock_settings.veneer": "Chapa", + "stock_settings.width": "Ancho", + "stock_settings.wood": "Madera", + "stock_settings.wood_stock": "Stock de madera", + "storage.cannot_locate_the_application_data_directory": "No se puede localizar el directorio de datos de la aplicación", + "storage.cannot_read_local_storage": "No se puede leer el almacenamiento local", + "storage.invalid_storage_path": "Ruta de almacenamiento no válida", + "storage.local_storage_is_blocked": "El almacenamiento local está bloqueado", + "storage.local_storage_is_blocked_or_full": "El almacenamiento local está bloqueado o lleno", + "storage.local_storage_is_unavailable": "El almacenamiento local no está disponible", + "storage.no_browser_window": "No hay ventana del navegador", + "system_info.available_logical_cpus": "CPU lógicas disponibles: {threads}", + "system_info.copied": "Copiado", + "system_info.copy_to_clipboard": "Copiar al portapapeles", + "system_info.dark": "Oscuro", + "system_info.debug": "Debug", + "system_info.describe_your_feature_suggestion_or_bug_here_system": "Describe aquí tu sugerencia de función o el error.\n\n---\n\n## Información del sistema\n\n{fence}text\n{report}\n{fence}\n", + "system_info.graphics_adapter_information_unavailable": "Información del adaptador gráfico: no disponible\n", + "system_info.include_these_details_when_reporting_a_bug": "Incluye estos detalles al reportar un error.", + "system_info.benchup_build": "BenchUp {}\nBuild: {}\n", + "system_info.light": "Claro", + "system_info.os_version": "Versión del SO: {}", + "system_info.platform": "Plataforma: {:?}", + "system_info.release": "Release", + "system_info.renderer_wgpu_graphics_backend_gpu_gpu_type_gpu": "Renderizador: wgpu\nBackend gráfico: {:?}\nGPU: {}\nTipo de GPU: {:?}\nFabricante/dispositivo de GPU: {:04x}/{:04x}\nControlador: {}\nDetalles del controlador: {}\nFormato de superficie: {:?}", + "system_info.runtime_browser_webassembly_browser_os": "Entorno de ejecución: navegador (WebAssembly)\nNavegador / SO: {}", + "system_info.runtime_desktop_os_architecture": "Entorno de ejecución: escritorio\nSO: {}\nArquitectura: {}", + "system_info.system_information": "Información del sistema", + "system_info.window_pixels_ui_scale_pixels_point_ui_zoom": "Ventana: {:.0} × {:.0} píxeles\nEscala de la interfaz: {scale:.2} píxeles/punto\nZoom de la interfaz: {:.0}%\nTema: {}", + "tag_menu.add_tag": "Agregar etiqueta", + "tag_menu.assign": "Asignar: {}", + "tag_menu.assign_tag": "Asignar etiqueta", + "tag_menu.assign_this_tag_to_the_selection_untick_to": "Asigna esta etiqueta a la selección; desmárcala para quitarla", + "tag_menu.cancel_renaming": "Cancelar cambio de nombre", + "tag_menu.click_to_edit_the_tag_color": "Haz clic para editar el color de la etiqueta", + "tag_menu.close_color_editor": "Cerrar editor de color", + "tag_menu.color": "Color: {}", + "tag_menu.color_2": "Color · {}", + "tag_menu.create_a_tag_above_to_organize_objects_and": "Crea una etiqueta arriba para organizar objetos y controlar su visibilidad.", + "tag_menu.delete": "Eliminar: {}", + "tag_menu.delete_tag": "Eliminar etiqueta", + "tag_menu.delete_tag_its_entities_become_untagged": "Eliminar etiqueta; sus entidades quedan Sin etiqueta", + "tag_menu.edit_tags": "Editar etiquetas", + "tag_menu.format": "×", + "tag_menu.hide": "Ocultar", + "tag_menu.hide_tag": "Ocultar etiqueta", + "tag_menu.new_tag_name": "Nombre de la etiqueta nueva", + "tag_menu.no_tags_yet": "Aún no hay etiquetas", + "tag_menu.rename": "Cambiar nombre: {}", + "tag_menu.rename_tag": "Cambiar nombre de la etiqueta", + "tag_menu.save_name": "Guardar nombre", + "tag_menu.show": "Mostrar", + "tag_menu.show_tag": "Mostrar etiqueta", + "tag_menu.tag": "Etiqueta {}: {}", + "tag_menu.tag_color": "Color de la etiqueta", + "tag_menu.unassign_tag": "Quitar etiqueta", + "text_tools.add_a_label_to_your_model": "Agrega una anotación a tu modelo.", + "text_tools.add_text": "Agregar texto", + "text_tools.attach_text": "Adjuntar texto", + "text_tools.click_a_visible_edge_face_group_or_component": "Haz clic en una arista, cara, grupo o componente visible para adjuntar texto", + "text_tools.edit_text": "Editar texto", + "text_tools.edit_text_2": "Editar texto", + "text_tools.edit_text_3": "Editar texto…", + "text_tools.enter_label_text": "Escribe el texto de la anotación", + "text_tools.enter_to_save_shift_enter_for_a_new": "Enter para guardar · Shift+Enter para una nueva línea · Esc para cancelar", + "text_tools.text": "Texto", + "time_estimates.add_finishing_products_in_settings_material": "Agrega productos de acabado en Configuración → Material.", + "time_estimates.add_grit": "Agregar grano", + "time_estimates.allowances_cover_layout_cutting_and_fitting_both_parts": "Los márgenes cubren el trazado, el corte y el ajuste de ambas piezas de cada unión. Los valores predeterminados suponen herramientas eléctricas de taller y plantillas. El tiempo real varía según las herramientas y el flujo de trabajo.", + "time_estimates.amount": "Importe ({currency})", + "time_estimates.apply_materials_to_children": "Aplicar materiales a los elementos secundarios", + "time_estimates.apply_to_all_children": "Aplicar a todos los elementos secundarios", + "time_estimates.assigned_items_omitted": { + "one": "{count} elemento asignado omitido: sin grosor utilizable.", + "other": "{count} elementos asignados omitidos: sin grosor utilizable." + }, + "time_estimates.between_coats": "Entre manos", + "time_estimates.between_coats_applies_to_varnish_only_enable_varnish": "Entre manos aplica solo al barniz. Activa el barniz con al menos 2 manos.", + "time_estimates.between_varnish_coats_sand_after_each_coat_except": "Entre manos de barniz: lija después de cada mano excepto la última, en lugar de antes del acabado.", + "time_estimates.change_sanding_and_finishing": "Cambiar lijado y acabado", + "time_estimates.choose_a_product_for_each_enabled_finishing_step": "Elige un producto para cada paso de acabado habilitado.", + "time_estimates.choose_sanding_grits_or_finishes_for_objects_in": "Elige granos de lijado o acabados para los objetos en Materiales.", + "time_estimates.coats": "Manos", + "time_estimates.container": "\nEnvase: {} {}", + "time_estimates.container_count": "{value:.0}", + "time_estimates.containers": { + "one": "{count:.0} envase", + "other": "{count:.0} envases" + }, + "time_estimates.copy_each_selected_object_s_stock_material_sanding": "Copia el material de stock, el lijado y los ajustes de acabado de cada objeto seleccionado a todos los grupos y componentes descendientes. Los objetos anidados seleccionados conservan sus propios ajustes.", + "time_estimates.cost_estimate": "Estimación de costos", + "time_estimates.cost_exceeds_estimate_range": "El costo supera el rango de estimación.", + "time_estimates.count": "Recuento", + "time_estimates.counts_all_configured_wood_joints_regardless_of_selection": "Cuenta todos los ensambles de madera configurados, sin importar la selección, la visibilidad ni el contexto de edición.", + "time_estimates.coverage_m2_per_container_per_coat": "; cobertura de {coverage} m² por envase por mano", + "time_estimates.coverage_missing_set_m2_per_container_per_coat": "Falta la cobertura: define los m² por envase por mano en Configuración → Material.", + "time_estimates.cuts_excluded_label": { + "one": "{count} corte no cabe · Excluido del costo", + "other": "{count} cortes no caben · Excluidos del costo" + }, + "time_estimates.cuts_excluded_note": { + "one": "\n{count} corte no cabe - Excluido del costo.", + "other": "\n{count} cortes no caben - Excluidos del costo." + }, + "time_estimates.default_allowance_is_minutes_per_joint": "El margen predeterminado es de {} minutos por unión.", + "time_estimates.different_wait_after_final_coat": "Espera distinta después de la última mano", + "time_estimates.draw_or_import_parts_to_estimate_time_and": "Dibuja o importa piezas para estimar el tiempo y el costo.", + "time_estimates.drying_waiting": "Secado / Espera", + "time_estimates.enter_any_positive_grit_number_including_values_above": "Introduce cualquier número de grano positivo, incluidos valores superiores a 320", + "time_estimates.estimate": "Estimación", + "time_estimates.estimate_incomplete": "Estimación incompleta", + "time_estimates.estimate_incomplete_items_omitted": { + "one": "Estimación incompleta: {count} elemento asignado omitido: sin grosor utilizable.", + "other": "Estimación incompleta: {count} elementos asignados omitidos: sin grosor utilizable." + }, + "time_estimates.estimate_incomplete_the_priced_subtotal_excludes_items_with": "Estimación incompleta: el subtotal con precio excluye los elementos sin precios o cobertura y los cortes que no caben. Revisa todas las advertencias de los elementos.", + "time_estimates.exceeds_estimate_range": "Supera el rango de estimación", + "time_estimates.final_wait": "Espera final", + "time_estimates.finishes_whole_containers_needs_combined_across_objects_and": "Acabados: envases completos · Las necesidades se combinan entre objetos y manos antes de redondear", + "time_estimates.finishes_whole_containers_purchased_surface_area_and_coats": "Acabados: se compran envases completos. La superficie y las manos se combinan entre objetos y pasos para cada producto antes de redondear. La cobertura es por envase configurado y por mano. La mano de obra no está incluida en el precio.", + "time_estimates.finishing": "Acabado", + "time_estimates.finishing_applies_only_to_objects_assigned_a_material": "El acabado solo se aplica a los objetos con un material asignado desde Configuración → Material → Madera.", + "time_estimates.grit_2": "Grano {grit}", + "time_estimates.grit_3": "Grano", + "time_estimates.h_min": "{} h {} min", + "time_estimates.hands_on": "Trabajo manual", + "time_estimates.hands_on_work": "Trabajo manual", + "time_estimates.include_in_estimate": "Incluir en la estimación", + "time_estimates.includes_final_drying_objects_dry_together_between_steps": "Incluye el secado final · Los objetos se secan juntos entre pasos", + "time_estimates.item": "Elemento", + "time_estimates.joinery_whole_project": "Ensambles · Todo el proyecto", + "time_estimates.joinery_whole_project_2": "Ensambles (todo el proyecto)", + "time_estimates.joinery_whole_project_3": "Ensambles (todo el proyecto). {JOINERY_SCOPE} {JOINERY_ALLOWANCE_NOTE}", + "time_estimates.joint": "Unión", + "time_estimates.kerf_rotation": "Ancho de corte {} · Rotación {}", + "time_estimates.kerf_rotation_stock_uses_each_part_s_full": "Ancho de corte {} - Rotación {}. El stock usa el cuadro delimitador completo de cada pieza, incluido el desperdicio de corte; las muescas y los recortes no reducen el bloque.", + "time_estimates.materials_all_visible_parts": "Materiales · Todas las piezas visibles", + "time_estimates.materials_selected_parts": "Materiales · Piezas seleccionadas", + "time_estimates.min_joint": "Min / unión", + "time_estimates.minutes_m2": "Minutos / m²", + "time_estimates.mixed_sanding_and_finishing_settings_changes_apply_to": "Ajustes de lijado y acabado mixtos. Los cambios se aplican a los objetos de madera seleccionados, incluidos los objetos de madera anidados.", + "time_estimates.no_estimates_yet": "Aún no hay estimaciones", + "time_estimates.no_finishing_tasks": "No hay tareas de acabado", + "time_estimates.no_stock_estimate_yet": "Aún no hay estimación de stock", + "time_estimates.no_stock_estimate_yet_assign_stock_materials_to": "Aún no hay estimación de stock: {message}. Asigna materiales de stock a las piezas sólidas y configura los tamaños y precios del stock en Configuración > Material.", + "time_estimates.no_surfaces_to_estimate": "No hay superficies para estimar", + "time_estimates.no_treatment_tasks_enabled_choose_sanding_grits_or": "No hay tareas de tratamiento habilitadas. Elige granos de lijado o acabados para los objetos en Materiales.", + "time_estimates.no_visible_surfaces_with_assigned_wood_materials": "No hay superficies visibles con materiales de madera asignados.", + "time_estimates.no_visible_surfaces_with_assigned_wood_materials_in": "No hay superficies visibles con materiales de madera asignados en el alcance actual.", + "time_estimates.objects": { + "one": "{title} ({count} objeto)", + "other": "{title} ({count} objetos)" + }, + "time_estimates.partial": " (parcial)", + "time_estimates.partial_2": "{} {} (parcial)", + "time_estimates.pre_stain": "Pretinte", + "time_estimates.price": "Precio", + "time_estimates.price_missing_set_the_price_per_container_in": "Falta el precio: define el precio por envase en Configuración → Material.", + "time_estimates.price_unavailable": "Precio no disponible", + "time_estimates.price_unavailable_add_a_price_in_settings_material": "\nPrecio no disponible. Agrega un precio en Configuración > Material.", + "time_estimates.product": "Producto", + "time_estimates.product_issue": "\n{issue}", + "time_estimates.quantity": "Cantidad", + "time_estimates.restore_sanding_and_finishing_defaults_from_settings_for": "Restaura los valores predeterminados de lijado y acabado de Configuración para los objetos de madera seleccionados.", + "time_estimates.revert_to_defaults": "Volver a los valores predeterminados", + "time_estimates.rotation_off": "desactivada", + "time_estimates.rotation_on": "activada", + "time_estimates.router_allowance": "Margen de planificación: preparación una vez por perfil, más trabajo por metro. Ajusta según el material, la profundidad y las pasadas múltiples.", + "time_estimates.router_empty": "Aplica un perfil de fresado a una arista para estimar el trabajo de fresadora.", + "time_estimates.router_rate": "Trabajo (min/m)", + "time_estimates.router_report_note": "Fresadora (todo el proyecto): {setup} min de preparación por perfil + {rate} min/m. Cuenta las trayectorias guardadas sin importar la selección ni la visibilidad; ajusta según el material, la profundidad y las pasadas.", + "time_estimates.router_scope": "Cuenta las trayectorias de fresado guardadas en todo el proyecto, sin importar la selección ni la visibilidad.", + "time_estimates.router_setup": "Preparación por perfil (min)", + "time_estimates.router_task": "Fresadora (todo el proyecto)", + "time_estimates.router_whole_project": "Fresadora · Todo el proyecto", + "time_estimates.sanding": "Lijado", + "time_estimates.select_faces_or_groups_with_assigned_wood_materials": "Selecciona caras o grupos con materiales de madera asignados.", + "time_estimates.selected": "Seleccionado", + "time_estimates.selected_wood_surfaces": "Superficies de madera seleccionadas", + "time_estimates.sequential_total": "Total secuencial", + "time_estimates.soak_per_coat": "Remojo por mano", + "time_estimates.some_cuts_need_larger_stock_their_cost_is": "Algunos cortes necesitan un stock más grande. Su costo está excluido.", + "time_estimates.stain": "Tinte", + "time_estimates.stock_pieces_without_price": { + "one": "{count} pieza de stock no tiene precio. Agrega precios en Configuración → Material.", + "other": "{count} piezas de stock no tienen precio. Agrega precios en Configuración → Material." + }, + "time_estimates.task": "Tarea", + "time_estimates.the_selected_product_is_no_longer_in_settings": "El producto seleccionado ya no está en Configuración → Material. Elige un reemplazo o quítalo.", + "time_estimates.time_includes_whole_project_joinery_sanding_finishing_and": "El tiempo incluye ensambles y fresado de todo el proyecto, lijado, acabado y secado final.", + "time_estimates.total": "Total", + "time_estimates.unassigned_finishing_product": "Producto de acabado sin asignar", + "time_estimates.unavailable": "No disponible", + "time_estimates.unavailable_finishing_product": "Producto de acabado no disponible", + "time_estimates.unit_price": "Precio unitario ({currency})", + "time_estimates.unknown": "Desconocido", + "time_estimates.unresolved_finishing_products": "Productos de acabado sin resolver", + "time_estimates.uses_each_part_s_full_bounding_box_with": "Usa el cuadro delimitador completo de cada pieza con el mismo empaquetado de stock y los mismos ajustes que Preparar plan de corte. Las muescas y los recortes no reducen el bloque necesario. Los precios cubren el material comprado, incluido el desperdicio de corte. No se incluye la mano de obra. Los productos de acabado se estiman por separado a partir de la superficie, las manos y la cobertura del envase.", + "time_estimates.value": "Valor", + "time_estimates.varnish": "Barniz", + "time_estimates.visible": "Visible", + "time_estimates.visible_wood_surfaces": "Superficies de madera visibles", + "time_estimates.wait_per_coat": "Espera por mano", + "time_estimates.wood_stock": "Stock de madera", + "time_estimates.wood_surfaces_finishing_applies_only_to_objects_assigned": "{} - {} superficies de madera. El acabado solo se aplica a los objetos con un material asignado desde Configuración > Material > Madera.", + "tools.arc": "Arco", + "tools.arc_instructions": "Haz clic en dos extremos y luego en un punto del arco · Las flechas bloquean el plano de dibujo", + "tools.choose_a_color_and_face_or_group_in": "Elige un color y Cara o Grupo en el inspector · Haz clic en una cara para pintar", + "tools.circle": "Círculo", + "tools.click_a_center_a_baseline_then_an_angle": "Haz clic en un centro, una línea base y luego un ángulo · Ctrl/Comando alterna Crear guías / Medir", + "tools.click_a_face_move_along_its_normal_arrow": "Haz clic en una cara · Muévete a lo largo de su normal · Ctrl alterna caras nuevas · Las flechas bloquean ejes · Haz clic o escribe la distancia + Enter", + "tools.click_a_face_near_its_boundary_move_inward": "Haz clic en una cara cerca de su contorno · Muévete hacia adentro o hacia afuera · Escribe la distancia + Enter", + "tools.click_a_reference_edge_or_point_ctrl_command": "Haz clic en una arista o punto de referencia · Ctrl/Comando alterna Crear guías / Medir · Esc para borrar", + "tools.click_an_edge_or_face_drag_to_box": "Haz clic en una arista o cara · Doble clic en una cara para ver sus aristas · Triple clic para la geometría conectada · Arrastra para seleccionar con un cuadro · Shift para alternar", + "tools.click_an_object_then_click_to_place_its": "Haz clic en un objeto y luego haz clic para colocar su etiqueta de texto · Esc para cancelar", + "tools.click_groups_components_or_faces_to_keep_their": "Haz clic en grupos, componentes o caras para conservar su superposición", + "tools.click_or_drag_across_edges_or_faces_to": "Haz clic o arrastra sobre aristas o caras para borrarlas · Ctrl / ⌘ Z para deshacer", + "tools.click_the_center_then_a_point_on_the": "Haz clic en el centro y luego en un punto del círculo · Las flechas bloquean el plano de dibujo", + "tools.click_the_center_then_a_vertex_arrow_keys": "Haz clic en el centro y luego en un vértice · Las flechas bloquean el plano de dibujo", + "tools.click_the_cutter_first_then_the_solid_to": "Haz clic primero en el cortador y luego en el sólido que quieres recortar · El cortador permanece", + "tools.click_to_start_a_line_arrow_keys_lock": "Haz clic para iniciar una línea · Las flechas bloquean ejes · Escribe una longitud para mayor precisión", + "tools.click_two_closed_solids_to_combine_their_outer": "Haz clic en dos sólidos cerrados para combinar sus superficies exteriores", + "tools.click_two_closed_solids_to_combine_their_volumes": "Haz clic en dos sólidos cerrados para combinar sus volúmenes", + "tools.click_two_closed_solids_to_keep_the_overlap": "Haz clic en dos sólidos cerrados para conservar la superposición y las piezas restantes por separado", + "tools.click_two_endpoints_then_click_to_place_the": "Haz clic en dos extremos y luego haz clic para colocar la cota · Esc para cancelar", + "tools.click_two_opposite_corners_arrow_keys_lock_the": "Haz clic en dos esquinas opuestas · Las flechas bloquean el plano de dibujo", + "tools.combine": "Combinar", + "tools.dimensions": "Cotas", + "tools.drag_to_draw_release_to_finish_arrow_keys": "Arrastra para dibujar · Suelta para finalizar · Las flechas bloquean el plano de dibujo", + "tools.drag_to_orbit_hold_shift_to_pan_scroll": "Arrastra para orbitar · Mantén Shift para desplazar · Usa la rueda para hacer zoom", + "tools.erase": "Borrar", + "tools.flip": "Voltear", + "tools.freehand": "Mano alzada", + "tools.grain_direction": "Dirección de la veta", + "tools.place_the_grain_anchor": "Haz clic en una pieza, arista o cara para colocar el ancla de la dirección de la veta.", + "tools.intersect": "Intersecar", + "tools.line": "Línea", + "tools.move": "Mover", + "tools.offset": "Desfase", + "tools.orbit": "Órbita", + "tools.paint": "Pintar", + "tools.polygon": "Polígono", + "tools.protractor": "Transportador", + "tools.push_pull": "Empujar/Tirar", + "tools.rectangle": "Rectángulo", + "tools.rotate": "Girar", + "tools.scale": "Escalar", + "tools.select": "Seleccionar", + "tools.select_geometry_choose_a_red_green_or_blue": "Selecciona geometría · Elige un plano de volteo rojo, verde o azul · Las flechas eligen X / Y / Z", + "tools.select_geometry_click_a_base_point_and_destination": "Selecciona geometría · Haz clic en un punto base y un destino · Las flechas bloquean ejes", + "tools.select_geometry_click_a_pivot_reference_point_then": "Selecciona geometría · Haz clic en un pivote, un punto de referencia y luego un ángulo · Las flechas bloquean el plano de rotación", + "tools.select_geometry_click_a_reference_point_move_or": "Selecciona geometría · Haz clic en un punto de referencia · Mueve o escribe un factor de escala + Enter", + "tools.split": "Dividir", + "tools.tape_measure": "Cinta métrica", + "tools.text": "Texto", + "tools.trim": "Recortar", + "tools.union": "Unión", + "updates.automatic_updates_are_available_in_the_packaged_desktop": "Las actualizaciones automáticas están disponibles en la aplicación de escritorio empaquetada. Descárgala desde GitHub Releases.", + "updates.checking_for_updates": "Buscando actualizaciones…", + "updates.could_not_read_update_preferences": "No se pudieron leer las preferencias de actualización: {error}", + "updates.could_not_save_update_preferences": "No se pudieron guardar las preferencias de actualización: {error}", + "updates.could_not_update_benchup": "No se pudo actualizar BenchUp: {error}", + "updates.downloading_benchup_you_can_keep_working": "Descargando BenchUp {version}… Puedes seguir trabajando.", + "updates.benchup_is_ready_it_will_install_on_your": "BenchUp {} está listo. Se instalará en el próximo inicio, o puedes reiniciar ahora.", + "updates.benchup_is_up_to_date": "BenchUp está actualizado.", + "updates.no_downloaded_update_is_ready": "No hay ninguna actualización descargada lista.", + "updates.no_updates_have_been_published_for_this_platform": "Aún no se han publicado actualizaciones para esta plataforma.", + "updates.ready_to_check_for_updates": "Listo para buscar actualizaciones.", + "updates.the_update_check_did_not_complete_please_try": "La búsqueda de actualizaciones no se completó. Inténtalo de nuevo.", + "view_controls.camera": "Cámara", + "view_controls.choose_color": "Elegir color", + "view_controls.choose_how_to_paint_and_a_paint_color": "Elige cómo pintar y un color de pintura", + "view_controls.color": "Color", + "view_controls.configure_section_planes_cuts_fill_and_axis_positions": "Configura planos de sección, cortes, relleno y posiciones de ejes", + "view_controls.degree_symbol": "°", + "view_controls.face": "Cara", + "view_controls.field_of_view": "Campo de visión", + "view_controls.fit_model": "Ajustar al modelo", + "view_controls.fit_selection": "Ajustar a la selección", + "view_controls.group": "Grupo", + "view_controls.keeps_geometry_on_the_negative_side_of_this": "Conserva la geometría en el lado negativo de este eje.", + "view_controls.orthographic_projection_parallel_lines_stay_parallel": "Proyección ortogonal: las líneas paralelas se mantienen paralelas", + "view_controls.paint_by": "Pintar por", + "view_controls.parallel": "Paralela", + "view_controls.perspective": "Perspectiva", + "view_controls.preset_colors": "Colores predefinidos", + "view_controls.projection": "Proyección", + "view_controls.reset_camera": "Restablecer cámara", + "view_controls.section_cuts": "Cortes de sección", + "view_controls.section_fill": "Relleno de sección", + "view_controls.sections": "Secciones", + "view_controls.standard_views": "Vistas estándar", + "view_controls.view": "Vista {}", + "view_controls.x_vertical": "X · vertical", + "view_controls.y_vertical": "Y · vertical", + "view_controls.z_horizontal": "Z · horizontal", + "viewports.a_compatible_gpu_renderer_is_required_try_updating": "Se requiere un renderizador de GPU compatible. Prueba a actualizar el controlador de gráficos.", + "viewports.create_viewport": "Crear vista", + "viewports.current": "Actual", + "viewports.drag_to_a_viewport_edge_to_split_or": "Arrastra hasta el borde de una vista para dividirla, o hasta su centro para unir pestañas. Haz clic derecho para cambiar el nombre o cerrar.", + "viewports.rename": "Cambiar nombre…", + "viewports.rename_view": "Cambiar nombre de la vista", + "viewports.view": "Vista {id}", + "web.canvas_label": "Espacio de trabajo de modelado de BenchUp", + "web.initial_title": "Modelo sin título - BenchUp", + "web.loading.01": "Haciendo espacio para tu próxima gran idea…", + "web.loading.02": "Da forma a tus ideas con BenchUp…", + "web.loading.03": "Un pequeño toque de inspiración…", + "web.loading.04": "Calentando tu rincón creativo…", + "web.loading.05": "Las cosas buenas van tomando forma…", + "web.loading.06": "Dándole un giro fresco a las cosas…", + "web.loading.07": "Tu imaginación es bienvenida aquí…", + "web.loading.08": "Dibuja un poco, sueña mucho…", + "web.loading.09": "Desplegando la cuadrícula de bienvenida…", + "web.loading.10": "Agregando una pizca de posibilidades…", + "web.loading.11": "Acerca una silla y un lienzo nuevo…", + "web.loading.12": "Ya casi es hora de crear…", + "image_tools.add_reference_image": "Agregar imagen de referencia", + "image_tools.brightness": "Brillo", + "image_tools.choose_an_image_smaller_than_32_mb": "Elige una imagen de menos de 32 MB.", + "image_tools.choose_reference_image": "Elegir imagen de referencia", + "image_tools.contrast": "Contraste", + "image_tools.could_not_open_image": "No se pudo abrir la imagen: {e}", + "image_tools.draw_two_opposite_corners_then_choose_a_png_jpeg_or_webp_image_images_are_embedded_in_the_project": "Dibuja dos esquinas opuestas y luego elige una imagen PNG, JPEG o WebP. Las imágenes se incrustan en el proyecto.", + "image_tools.edit_reference_image": "Editar imagen de referencia", + "image_tools.finish_or_cancel_the_current_image_picker_first": "Termina o cancela primero el selector de imágenes actual.", + "image_tools.flip_horizontal": "Voltear horizontalmente", + "image_tools.flip_vertical": "Voltear verticalmente", + "image_tools.grayscale": "Escala de grises", + "image_tools.image": "Imagen", + "image_tools.image_defaults": "Valores predeterminados de imagen", + "image_tools.image_import_cancelled_because_the_project_or_editing_context_changed": "La importación de la imagen se canceló porque el proyecto o el contexto de edición cambió.", + "image_tools.image_tool": "Herramienta de imagen", + "image_tools.images_png_jpeg_webp": "Imágenes (PNG, JPEG, WebP)", + "image_tools.opacity": "Opacidad", + "image_tools.reference_image": "Imagen de referencia", + "image_tools.replace_reference_image": "Reemplazar imagen de referencia", + "image_tools.rotation": "Rotación", + "image_tools.the_decoded_image_is_too_large_choose_a_smaller_image": "La imagen decodificada es demasiado grande. Elige una imagen más pequeña.", + "image_tools.the_image_picker_could_not_finish": "El selector de imágenes no pudo terminar.", + "image_tools.unlock_the_image_before_replacing_it": "Desbloquea la imagen antes de reemplazarla.", + "image_tools.unlock_the_image_to_edit_its_properties": "Desbloquea la imagen para editar sus propiedades.", + "image_tools.zoom": "Zoom", + "settings.3d_mouse": "Mouse 3D", + "settings.adjust_navigation_sensitivity_for_the_selected_device": "Ajusta la sensibilidad de navegación del dispositivo seleccionado.", + "settings.body_depth_length": "Profundidad del cuerpo (longitud)", + "settings.body_width": "Ancho del cuerpo", + "settings.connect_3d_mouse": "Conectar Mouse 3D", + "settings.connected": "Conectado: {device}", + "settings.disabled": "Desactivado", + "settings.enable_3d_mouse": "Activar Mouse 3D", + "settings.eye_height": "Altura de los ojos", + "settings.eye_height_is_measured_above_the_floor_body_width_and_depth_determine_obstacle_clearance_allow_12_cm_above_the_eyes_for_the_head": "La altura de los ojos se mide desde el suelo. El ancho y la profundidad del cuerpo determinan la holgura frente a obstáculos; deja 12 cm sobre los ojos para la cabeza.", + "settings.invert_pan": "Invertir desplazamiento", + "settings.invert_tilt_and_twist": "Invertir inclinación y giro", + "settings.invert_zoom": "Invertir zoom", + "settings.language.simplified_chinese": "简体中文", + "settings.language.brazilian_portuguese": "Português (Brasil)", + "settings.language.vietnamese": "Tiếng Việt", + "settings.language.spanish": "Español", + "settings.look_sensitivity": "Sensibilidad de la mirada", + "settings.mouse_trackpad": "Mouse/Trackpad", + "settings.navigation_type": "Tipo de navegación", + "settings.middle_mouse_drag": "Arrastre con el botón central del mouse", + "settings.orbit_shift_to_pan": "Órbita (Shift para desplazar)", + "settings.pan_shift_to_orbit": "Desplazamiento (Shift para orbitar)", + "settings.no_device_detected": "No se detectó ningún dispositivo", + "settings.scales_mouse_look_while_walking_100_matches_orbit_sensitivity": "Escala la mirada con el mouse al caminar. 100 % equivale a la sensibilidad de Órbita.", + "settings.tilt_or_twist_the_cap_to_orbit_slide_it_to_pan_and_push_or_pull_it_to_zoom_the_fit_button_frames_the_model": "Inclina o gira la tapa para orbitar, deslízala para desplazar, y empújala o tira de ella para hacer zoom. El botón Ajustar encuadra el modelo.", + "settings.walking_person": "Persona caminando", + "settings.walking_speed": "Velocidad al caminar", + "settings.wasd_arrows_walk_shift_crouch_space_jump_alt_sit_stand_move_mouse_look_escape_exit": "WASD / flechas: caminar · Shift: agacharse · Espacio: saltar · Alt: sentarse / levantarse · Mover el mouse: mirar · Escape: salir", + "shortcuts.crouch_jump_sit_or_stand": "Agacharse / Saltar / Sentarse o levantarse", + "shortcuts.exit_walk": "Salir de Caminar", + "shortcuts.look_while_walking": "Mirar al caminar", + "shortcuts.move_mouse": "Mover el mouse", + "shortcuts.move_while_walking": "Moverse al caminar", + "shortcuts.walk_button_click_a_floor_face": "Botón Caminar → haz clic en una cara del suelo", + "shortcuts.walk_through_model": "Caminar por el modelo", + "walk.cannot_change_posture_here_check_head_clearance_and_floor_support": "No se puede cambiar de postura aquí: revisa el espacio sobre la cabeza y el apoyo del suelo.", + "walk.choose_a_floor_or_a_slope_of_45_or_less": "Elige un suelo o una pendiente de 45° o menos.", + "walk.click_a_floor_face_to_place_the_walking_person": "Haz clic en una cara del suelo para colocar a la persona caminando.", + "walk.seated_move_mouse_to_look_180_alt_stand_tab_sidebar_escape_exit_walk": "Sentado · Mueve el mouse para mirar (180°) · Alt: levantarse · Tab: barra lateral · Escape: salir de Caminar", + "walk.the_new_body_dimensions_do_not_fit_here_click_a_floor_face_to_reposition": "Las nuevas dimensiones del cuerpo no caben aquí. Haz clic en una cara del suelo para reposicionar.", + "walk.there_is_not_enough_space_here_for_the_configured_body_size_and_eye_height": "Aquí no hay espacio suficiente para el tamaño de cuerpo y la altura de ojos configurados.", + "walk.walk": "Caminar", + "walk.walk_through_the_model_click_a_floor_face_to_place_yourself_at_eye_level": "Camina por el modelo. Haz clic en una cara del suelo para colocarte a la altura de los ojos.", + "walk.walking_controls": "WASD / flechas: caminar · Mover el mouse para mirar · Shift: agacharse · Espacio: saltar · Alt: sentarse · Tab: barra lateral · Escape: salir de Caminar", + "wood_glue.tool": "Cola para madera", + "wood_glue.instructions": "Haz clic en una cara visible y desbloqueada para activar o desactivar la cola para madera.", + "wood_glue.automatic": "Encolar superficies de contacto seleccionadas", + "wood_glue.automatic_hint": "Cubre las áreas en contacto de las caras y los grupos seleccionados a ambos lados de cada unión.", + "wood_glue.applied": "Aplicar cola para madera", + "wood_glue.removed": "Quitar cola para madera", + "wood_glue.contacts_applied": "Encolar superficies de contacto", + "wood_glue.no_new_contacts": "No se encontraron superficies de contacto sin cola en la selección.", + "wood_glue.invalid_coverage": "La cola para madera hace referencia a una cara o recubrimiento no válidos.", + "wood_glue.estimate_label": "Cobertura de cola para madera (todo el proyecto)", + "wood_glue.estimate_note": "Cobertura de cola para madera (todo el proyecto): {area}", + "wood_glue.area_m2": "{value} m²", + "settings.export_scales": "Escalas de exportación", + "export_scale.define_ratios": "Define proporciones para la exportación STL. Proporción = tamaño exportado : tamaño del modelo.", + "export_scale.actual_size_hint": "El tamaño real (1:1) siempre está disponible. El escalado se aplica a todas las dimensiones, incluidas las uniones y los orificios.", + "export_scale.nmra_reference": "Referencia de escalas NMRA (Norteamérica)", + "export_scale.preset_standards_hint": "Los ajustes preestablecidos de maqueta de carpintería son una comodidad, no estándares NMRA. Los nombres de las escalas ferroviarias pueden usar proporciones distintas en otras regiones.", + "export_scale.exported": "Exportado", + "export_scale.add_preset": "Agregar ajuste preestablecido", + "export_scale.new_scale": "Escala nueva", + "export_scale.actual_size": "Tamaño real (1:1)", + "export_scale.custom_ratio": "Proporción personalizada", + "export_scale.ratio_units_hint": "Tamaño exportado : tamaño del modelo (con las mismas unidades)", + "export_scale.scale_preview": "Escala: {percent:.6}% — 1000 mm del modelo se exportan como {size:.4} mm.", + "export_scale.invalid_ratio": "Introduce dos números finitos mayores que cero.", + "export_scale.stl_units_hint": "Las coordenadas STL se exportan en milímetros. El proyecto conserva su tamaño original.", + "export_scale.manage_presets_hint": "Administra los ajustes preestablecidos en Configuración → Escalas de exportación.", + "export_scale.export_stl": "Exportar STL", + "export_scale.woodworking_mockup_1_2": "Maqueta de carpintería 1:2", + "export_scale.woodworking_mockup_1_5": "Maqueta de carpintería 1:5", + "export_scale.woodworking_mockup_1_10": "Maqueta de carpintería 1:10", + "export_scale.miniature_1_12": "Miniatura 1:12", + "orbit.visible_model_center": "Centro del modelo visible", + "orbit.selection_center": "Centro de la selección", + "orbit.world_origin": "Origen del mundo", + "orbit.view_center": "Centro de la vista", + "orbit.focused": "Enfocado", + "orbit.set_focus": "Definir enfoque", + "orbit.set_focus_hint": "Haz clic en un punto de la vista para definir el enfoque de la órbita. Haz clic derecho para cancelar. Enfocado usa el centro de la vista hasta que se defina un enfoque.", + "orbit.pivot_hint": "Elige el pivote de la órbita. Centro de la selección usa el centro del modelo visible cuando no hay nada seleccionado.", + "scale_tool.axis_hint": "Escalar eje {axis}", + "scale_tool.plane_hint": "Escalar en el plano {plane}", + "scale_tool.uniform_hint": "Escalar de forma uniforme", + "core.reference_images.contain": "Contener", + "core.reference_images.cover": "Cubrir", + "core.reference_images.stretch": "Estirar", + "core.reference_images.invalid_rectangle": "Imagen de referencia o rectángulo no válidos.", + "core.reference_images.missing_frame": "Falta el marco de la imagen de referencia.", + "core.reference_images.invalid_image": "Imagen de referencia no válida.", + "core.modeling.edit_reference_image_tools": "Usa Propiedades, Mover, Girar o Escalar de la imagen para editar una imagen de referencia.", + "core.document.autosave_edits_mismatch": "Las ediciones del autoguardado no coinciden con el proyecto guardado.", + "core.document.autosave_history_mismatch": "El historial del autoguardado no coincide con el proyecto guardado.", + "project_files.invalid_preview": "Vista previa del proyecto no válida", + "project_files.empty_preview": "La vista previa de la ventana gráfica está vacía.", + "project_files.pdf_viewer_closed": "El visor de PDF se cerró antes de que terminara la generación.", + "project_files.no_saved_preview": "Este proyecto no tiene una vista previa guardada. Guárdalo primero en BenchUp.", + "project_files.invalid_chunk_size": "Tamaño de fragmento del proyecto no válido", + "project_files.truncated_chunk": "Fragmento del proyecto truncado", + "project_files.invalid_checksum": "La suma de verificación de los datos del proyecto no es válida.", + "project_files.png_has_no_project": "Este PNG no contiene un proyecto de BenchUp.", + "projects.could_not_capture_preview": "No se pudo capturar la vista previa del proyecto: {error}", + "storage.cannot_restore_edits": "No se pueden restaurar las ediciones del autoguardado: {error}", + "storage.cannot_migrate_workspace": "No se puede migrar el espacio de trabajo: {error}", + "storage.browser_unavailable": "El almacenamiento del espacio de trabajo del navegador no está disponible: {error}", + "updates.unexpected_package_identity": "Identidad inesperada del paquete de actualización: {id}", + "selection_measurements.worker_unavailable": "El proceso de medición no está disponible.", + "selection_measurements.invalid_result": "Resultado de medición no válido.", + "pdf_export.could_not_read": "No se pudo leer el PDF generado.", + "pdf_export.could_not_generate": "No se pudo generar el PDF.", + "space_mouse.connect_hint": "Selecciona Conectar Mouse 3D para dar acceso a tu dispositivo.", + "space_mouse.disconnected": "Mouse 3D desconectado. Vuelve a conectarlo o selecciona Conectar Mouse 3D.", + "space_mouse.unsupported": "Este navegador no admite el acceso al Mouse 3D. Usa Chrome o Edge con compatibilidad con WebHID.", + "space_mouse.insecure": "Abre BenchUp mediante HTTPS o localhost para conectar un Mouse 3D.", + "space_mouse.scanning": "Buscando un Mouse 3D conectado anteriormente...", + "space_mouse.choosing": "Elige un Mouse 3D en tu navegador.", + "space_mouse.no_selection": "No se seleccionó ningún Mouse 3D. Selecciona Conectar Mouse 3D para intentarlo de nuevo.", + "space_mouse.no_device": "No hay ningún Mouse 3D conectado. Selecciona Conectar Mouse 3D.", + "space_mouse.denied": "No se permitió el acceso al Mouse 3D. Revisa los permisos de dispositivos de este sitio e inténtalo de nuevo.", + "space_mouse.open_failed": "No se pudo abrir el Mouse 3D. Cierra otras aplicaciones o controladores que lo usen, como 3DxWare, e inténtalo de nuevo.", + "app.measurements_unavailable": "Mediciones no disponibles.", + "app.calculating_measurements": "Calculando mediciones…", + "sidebar.move_wireframe": "Estructura alámbrica de otros objetos al mover", + "sidebar.selected_edge_lengths": "Longitudes de aristas seleccionadas" +} diff --git a/assets/i18n/pt_BR.json b/assets/i18n/pt_BR.json index 95f77fd..c47e62d 100644 --- a/assets/i18n/pt_BR.json +++ b/assets/i18n/pt_BR.json @@ -1340,7 +1340,7 @@ "settings.inference_points": "Pontos de inferência", "settings.infinite_grid": "Grade infinita", "settings.language": "Idioma", - "settings.language_english": "English", + "settings.language.english": "English", "settings.length_of_each_side_of_the_square_centered": "Comprimento de cada lado do quadrado, centralizado na origem do modelo. O limite permanece fixo ao dar zoom ou girar.", "settings.material": "Material", "settings.materials_estimates": "Materiais e estimativas", @@ -2065,9 +2065,10 @@ "settings.invert_pan": "Inverter mover vista", "settings.invert_tilt_and_twist": "Inverter inclinação e giro", "settings.invert_zoom": "Inverter zoom", - "settings.language_simplified_chinese": "简体中文", - "settings.language_brazilian_portuguese": "Português (Brasil)", - "settings.language_vietnamese": "Vietnamita", + "settings.language.simplified_chinese": "简体中文", + "settings.language.brazilian_portuguese": "Português (Brasil)", + "settings.language.vietnamese": "Tiếng Việt", + "settings.language.spanish": "Español", "settings.look_sensitivity": "Sensibilidade do olhar", "settings.mouse_trackpad": "Mouse/trackpad", "settings.navigation_type": "Tipo de navegação", diff --git a/assets/i18n/vi.json b/assets/i18n/vi_VN.json similarity index 99% rename from assets/i18n/vi.json rename to assets/i18n/vi_VN.json index 20d5269..61c80a6 100644 --- a/assets/i18n/vi.json +++ b/assets/i18n/vi_VN.json @@ -1342,7 +1342,7 @@ "settings.inference_points": "Điểm bắt", "settings.infinite_grid": "Lưới vô hạn", "settings.language": "Ngôn ngữ", - "settings.language_english": "Tiếng Anh", + "settings.language.english": "English", "settings.length_of_each_side_of_the_square_centered": "Chiều dài mỗi cạnh của hình vuông, lấy gốc mô hình làm tâm. Ranh giới giữ nguyên khi thu phóng hoặc xoay.", "settings.material": "Vật liệu", "settings.materials_estimates": "Vật liệu và dự toán", @@ -2067,9 +2067,10 @@ "settings.invert_pan": "Đảo chiều dịch chuyển", "settings.invert_tilt_and_twist": "Đảo chiều nghiêng và xoay", "settings.invert_zoom": "Đảo chiều thu phóng", - "settings.language_simplified_chinese": "Tiếng Trung giản thể", - "settings.language_brazilian_portuguese": "Tiếng Bồ Đào Nha (Brazil)", - "settings.language_vietnamese": "Tiếng Việt", + "settings.language.simplified_chinese": "简体中文", + "settings.language.brazilian_portuguese": "Português (Brasil)", + "settings.language.vietnamese": "Tiếng Việt", + "settings.language.spanish": "Español", "settings.look_sensitivity": "Độ nhạy nhìn", "settings.mouse_trackpad": "Chuột/Bàn di chuột", "settings.navigation_type": "Kiểu điều hướng", diff --git a/assets/i18n/zh_CN.json b/assets/i18n/zh_CN.json index 0812708..a98d99d 100644 --- a/assets/i18n/zh_CN.json +++ b/assets/i18n/zh_CN.json @@ -1342,7 +1342,7 @@ "settings.inference_points": "推断点", "settings.infinite_grid": "无限网格", "settings.language": "语言", - "settings.language_english": "English", + "settings.language.english": "English", "settings.length_of_each_side_of_the_square_centered": "以模型原点为中心的正方形边长。缩放或旋转时,边界保持固定。", "settings.material": "材质", "settings.materials_estimates": "材料与估算", @@ -2067,9 +2067,10 @@ "settings.invert_pan": "反转平移", "settings.invert_tilt_and_twist": "反转倾斜和扭转", "settings.invert_zoom": "反转缩放", - "settings.language_simplified_chinese": "简体中文", - "settings.language_brazilian_portuguese": "Português (Brasil)", - "settings.language_vietnamese": "越南语", + "settings.language.simplified_chinese": "简体中文", + "settings.language.brazilian_portuguese": "Português (Brasil)", + "settings.language.vietnamese": "Tiếng Việt", + "settings.language.spanish": "Español", "settings.look_sensitivity": "视角灵敏度", "settings.mouse_trackpad": "鼠标/触控板", "settings.navigation_type": "导航类型", diff --git a/crates/benchup-app/src/app_tests.rs b/crates/benchup-app/src/app_tests.rs index 8a4733b..77eca1f 100644 --- a/crates/benchup-app/src/app_tests.rs +++ b/crates/benchup-app/src/app_tests.rs @@ -7281,10 +7281,11 @@ fn view_menu_localizes_display_options_and_toggles_selected_edge_lengths_without let _reset = ResetLanguage; let mut h = Harness::new(); for (language, view, wireframe, lengths) in [ - ("en", "View", "Wireframe other objects while moving", "Selected Edge Lengths"), + ("en_US", "View", "Wireframe other objects while moving", "Selected Edge Lengths"), ("zh_CN", "视图", "移动时以线框显示其他对象", "所选边的长度"), ("pt_BR", "Visualizar", "Exibir outros objetos em aramado ao mover", "Comprimentos das arestas selecionadas"), - ("en", "View", "Wireframe other objects while moving", "Selected Edge Lengths"), + ("es_419", "Vista", "Estructura alámbrica de otros objetos al mover", "Longitudes de aristas seleccionadas"), + ("en_US", "View", "Wireframe other objects while moving", "Selected Edge Lengths"), ] { h.app.general_settings.language = language.into(); benchup_core::i18n::set_language(language); diff --git a/crates/benchup-app/src/estimate_pdf.rs b/crates/benchup-app/src/estimate_pdf.rs index b07aa40..cdb9b59 100644 --- a/crates/benchup-app/src/estimate_pdf.rs +++ b/crates/benchup-app/src/estimate_pdf.rs @@ -656,14 +656,14 @@ mod tests { } } let _reset = ResetLanguage; - benchup_core::i18n::set_language("vi"); + benchup_core::i18n::set_language("vi_VN"); let mut report = Report { name: "Xưởng gỗ Việt".into(), currency: "VND".into(), notes: vec![tr!("wood_glue.estimate_note", area = "1 m²")], ..Default::default() }; - for language in ["vi", "en"] { + for language in ["vi_VN", "en_US"] { benchup_core::i18n::set_language(language); let bytes = render(&report).expect("Vietnamese project names and notes can be exported"); diff --git a/crates/benchup-app/src/export_scale.rs b/crates/benchup-app/src/export_scale.rs index 36c3aed..70eafd7 100644 --- a/crates/benchup-app/src/export_scale.rs +++ b/crates/benchup-app/src/export_scale.rs @@ -362,10 +362,11 @@ mod tests { prefs.presets.push(custom); let saved = serde_json::to_string(&prefs).unwrap(); for (language, mockup) in [ - ("en", "Woodworking mockup 1:2"), + ("en_US", "Woodworking mockup 1:2"), ("zh_CN", "木工样机 1:2"), ("pt_BR", "Maquete de marcenaria 1:2"), - ("en", "Woodworking mockup 1:2"), + ("es_419", "Maqueta de carpintería 1:2"), + ("en_US", "Woodworking mockup 1:2"), ] { benchup_core::i18n::set_language(language); let output = ctx.run_test_ui( @@ -401,7 +402,7 @@ mod tests { app.project.export_scale_open = true; for (language, title, actual, custom, preview, error) in [ ( - "en", + "en_US", "Export STL", "Actual size (1:1)", "Custom ratio", @@ -425,7 +426,15 @@ mod tests { "Insira dois números finitos maiores que zero.", ), ( - "en", + "es_419", + "Exportar STL", + "Tamaño real (1:1)", + "Proporción personalizada", + "Escala: 50.000000%", + "Introduce dos números finitos mayores que cero.", + ), + ( + "en_US", "Export STL", "Actual size (1:1)", "Custom ratio", diff --git a/crates/benchup-app/src/language.rs b/crates/benchup-app/src/language.rs index 106e3a9..9dc34bf 100644 --- a/crates/benchup-app/src/language.rs +++ b/crates/benchup-app/src/language.rs @@ -50,14 +50,16 @@ fn supported_language(locale: &str) -> Option<&'static str> { .find_map(|&(code, _)| (code == "zh_CN").then_some(code)); } - // Brazilian Portuguese is the only Portuguese catalog, so it also serves - // bare pt and other Portuguese regions instead of falling back to English. - if parts.first() == Some(&"pt") { - return supported - .iter() - .find_map(|&(code, _)| (code == "pt_BR").then_some(code)); + // Each remaining language has a single regional catalog (en_US, pt_BR, + // es_419), so it also serves the bare language and its other regions + // instead of falling back to English. Chinese is resolved above. + let primary = *parts.first()?; + if primary == "zh" { + return None; } - None + supported + .iter() + .find_map(|&(code, _)| (code.split('_').next() == Some(primary)).then_some(code)) } #[cfg(test)] @@ -67,7 +69,7 @@ mod tests { #[test] fn matches_supported_browser_and_os_locale_variants() { for locale in ["en", "en-US", "EN_gb.UTF-8", "en-CA", "en-US-u-hc-h12"] { - assert_eq!(preferred_language([locale]), "en", "{locale}"); + assert_eq!(preferred_language([locale]), "en_US", "{locale}"); } for locale in [ "zh", @@ -84,7 +86,10 @@ mod tests { assert_eq!(preferred_language([locale]), "pt_BR", "{locale}"); } for locale in ["vi", "vi-VN", "vi_VN", "vi_VN.UTF-8"] { - assert_eq!(preferred_language([locale]), "vi", "{locale}"); + assert_eq!(preferred_language([locale]), "vi_VN", "{locale}"); + } + for locale in ["es", "es_ES", "es-ES", "es_MX.UTF-8", "es-419", "es-CO", "ES-ar"] { + assert_eq!(preferred_language([locale]), "es_419", "{locale}"); } } @@ -101,18 +106,21 @@ mod tests { "C", "", ] { - assert_eq!(preferred_language([locale]), "en", "{locale}"); + assert_eq!(preferred_language([locale]), "en_US", "{locale}"); } - assert_eq!(preferred_language(std::iter::empty::<&str>()), "en"); + assert_eq!(preferred_language(std::iter::empty::<&str>()), "en_US"); } #[test] fn uses_the_first_supported_language_in_preference_order() { assert_eq!(preferred_language(["fr-CA", "zh-Hans", "en-US"]), "zh_CN"); - assert_eq!(preferred_language(["en-CA", "zh-CN"]), "en"); + assert_eq!(preferred_language(["en-CA", "zh-CN"]), "en_US"); assert_eq!(preferred_language(["zh-Hant", "zh-Hans"]), "zh_CN"); assert_eq!(preferred_language(["fr-CA", "pt-BR", "en-US"]), "pt_BR"); - assert_eq!(preferred_language(["fr-CA", "vi-VN", "en-US"]), "vi"); - assert_eq!(preferred_language(["en-US", "pt-BR"]), "en"); + assert_eq!(preferred_language(["fr-CA", "vi-VN", "en-US"]), "vi_VN"); + assert_eq!(preferred_language(["en-US", "pt-BR"]), "en_US"); + assert_eq!(preferred_language(["fr-CA", "es-MX", "en-US"]), "es_419"); + assert_eq!(preferred_language(["es-419", "pt-BR"]), "es_419"); + assert_eq!(preferred_language(["en-US", "es-ES"]), "en_US"); } } diff --git a/crates/benchup-app/src/settings.rs b/crates/benchup-app/src/settings.rs index 5450f5d..d7b813a 100644 --- a/crates/benchup-app/src/settings.rs +++ b/crates/benchup-app/src/settings.rs @@ -151,7 +151,7 @@ pub(super) const TOOLS: [(Tool, &str); 18] = [ (Tool::Image, "J"), ]; -const DEFAULT_LANGUAGE: &str = "en"; +const DEFAULT_LANGUAGE: &str = "en_US"; #[path = "language.rs"] mod language; @@ -159,12 +159,13 @@ pub(super) fn detected_language() -> &'static str { language::detected_language() } -fn languages() -> [(&'static str, &'static str); 4] { +fn languages() -> [(&'static str, &'static str); 5] { [ - (DEFAULT_LANGUAGE, tr!("settings.language_english")), - ("zh_CN", tr!("settings.language_simplified_chinese")), - ("pt_BR", tr!("settings.language_brazilian_portuguese")), - ("vi", tr!("settings.language_vietnamese")), + (DEFAULT_LANGUAGE, tr!("settings.language.english")), + ("zh_CN", tr!("settings.language.simplified_chinese")), + ("pt_BR", tr!("settings.language.brazilian_portuguese")), + ("vi_VN", tr!("settings.language.vietnamese")), + ("es_419", tr!("settings.language.spanish")), ] } @@ -291,6 +292,13 @@ impl GeneralSettings { self.space_mouse.normalize(); self.stock.normalize(); self.export_scale.normalize(); + // Earlier builds saved bare codes; every catalog now uses language_REGION. + match self.language.as_str() { + "en" => self.language = "en_US".into(), + "vi" => self.language = "vi_VN".into(), + "es" => self.language = "es_419".into(), + _ => {} + } if !languages().iter().any(|&(code, _)| code == self.language.as_str()) { self.language = DEFAULT_LANGUAGE.into(); } @@ -1192,11 +1200,59 @@ mod tests { #[test] fn supported_languages_include_vietnamese() { - assert!(languages().contains(&("vi", "Vietnamese"))); + assert!(languages().contains(&("vi_VN", "Vietnamese"))); let mut settings = GeneralSettings::default(); - settings.language = "vi".into(); + settings.language = "vi_VN".into(); settings.normalize(); - assert_eq!(settings.language, "vi"); + assert_eq!(settings.language, "vi_VN"); + } + + #[test] + fn supported_languages_include_spanish() { + assert!(languages().contains(&("es_419", "Spanish"))); + let mut settings = GeneralSettings::default(); + settings.language = "es_419".into(); + settings.normalize(); + assert_eq!(settings.language, "es_419"); + } + + #[test] + fn every_language_code_uses_language_and_region() { + for (code, _) in languages() { + let (language, region) = code.split_once('_').expect(code); + assert!(!language.is_empty() && !region.is_empty(), "{code}"); + } + } + + #[test] + fn saved_bare_language_codes_migrate_to_regional_codes() { + for (saved, expected) in [("en", "en_US"), ("vi", "vi_VN"), ("es", "es_419"), ("unsupported", "en_US")] { + let mut settings = GeneralSettings::default(); + settings.language = saved.into(); + settings.normalize(); + assert_eq!(settings.language, expected, "{saved}"); + } + assert_eq!(GeneralSettings::default().language, "en_US"); + } + + #[test] + fn language_picker_shows_native_names_in_translated_catalogs() { + struct Reset; + impl Drop for Reset { + fn drop(&mut self) { benchup_core::i18n::set_language("en"); } + } + let _reset = Reset; + let native = [ + ("en_US", "English"), + ("zh_CN", "简体中文"), + ("pt_BR", "Português (Brasil)"), + ("vi_VN", "Tiếng Việt"), + ("es_419", "Español"), + ]; + for (language, _) in &native[1..] { + benchup_core::i18n::set_language(language); + assert_eq!(languages(), native, "{language}"); + } } #[test] @@ -1210,11 +1266,12 @@ mod tests { let mut app = BenchUpApp::with_document(Document::default(), None, true); app.settings_open = true; for (language, title, group, export_scales) in [ - ("en", "Settings", "Workspace", "Export scales"), + ("en_US", "Settings", "Workspace", "Export scales"), ("zh_CN", "设置", "工作区", "导出比例"), ("pt_BR", "Configurações", "Área de trabalho", "Escalas de exportação"), - ("vi", "Cài đặt", "Không gian làm việc", "Tỷ lệ xuất"), - ("en", "Settings", "Workspace", "Export scales"), + ("vi_VN", "Cài đặt", "Không gian làm việc", "Tỷ lệ xuất"), + ("es_419", "Configuración", "Espacio de trabajo", "Escalas de exportación"), + ("en_US", "Settings", "Workspace", "Export scales"), ] { app.general_settings.language = language.into(); let input = || egui::RawInput { diff --git a/crates/benchup-app/src/sidebar.rs b/crates/benchup-app/src/sidebar.rs index a4f4fb1..4d52174 100644 --- a/crates/benchup-app/src/sidebar.rs +++ b/crates/benchup-app/src/sidebar.rs @@ -3046,7 +3046,7 @@ mod preference_tests { #[test] fn first_launch_language_is_saved_without_a_model_edit_and_not_redetected() { - for language in ["en", "zh_CN", "pt_BR", "vi"] { + for language in ["en_US", "zh_CN", "pt_BR", "vi_VN", "es_419"] { let ctx = egui::Context::default(); let mut storage = SavedPreferences::default(); let mut app = BenchUpApp::with_document(Document::default(), None, true); @@ -3084,7 +3084,7 @@ mod preference_tests { Some(&storage), || panic!("Existing preferences must not trigger language detection"), ); - assert_eq!(app.general_settings.language, "en"); + assert_eq!(app.general_settings.language, "en_US"); assert_eq!(app.general_settings.orbit_speed, 2.0); } } @@ -3093,7 +3093,7 @@ mod preference_tests { fn language_defaults_and_saves_without_a_model_edit() { let mut saved: InspectorPreferences = serde_json::from_str(r#"{"general":{"orbit_speed":2.0}}"#).unwrap(); - assert_eq!(saved.general.language, "en"); + assert_eq!(saved.general.language, "en_US"); saved.general.language = "unsupported".into(); let mut storage = SavedPreferences::default(); @@ -3101,7 +3101,7 @@ mod preference_tests { let ctx = egui::Context::default(); let mut app = BenchUpApp::with_document(Document::default(), None, true); app.restore_inspector_preferences(&ctx, Some(&storage)); - assert_eq!(app.general_settings.language, "en"); + assert_eq!(app.general_settings.language, "en_US"); assert_eq!(app.general_settings.orbit_speed, 2.); app.general_settings.language = "zh_CN".into(); diff --git a/crates/benchup-app/src/space_mouse.rs b/crates/benchup-app/src/space_mouse.rs index 02a8672..6730a3a 100644 --- a/crates/benchup-app/src/space_mouse.rs +++ b/crates/benchup-app/src/space_mouse.rs @@ -863,7 +863,8 @@ mod tests { for (language, connected, denied) in [ ("zh_CN", "已连接:My SpaceMouse", "未获准访问 3D 鼠标"), ("pt_BR", "Conectado: My SpaceMouse", "O acesso ao mouse 3D não foi permitido"), - ("en", "Connected: My SpaceMouse", "3D Mouse access was not allowed"), + ("es_419", "Conectado: My SpaceMouse", "No se permitió el acceso al Mouse 3D"), + ("en_US", "Connected: My SpaceMouse", "3D Mouse access was not allowed"), ] { benchup_core::i18n::set_language(language); assert_eq!(browser_status_label("connected", Some("My SpaceMouse")), connected); diff --git a/crates/benchup-core/build.rs b/crates/benchup-core/build.rs index 46ba62d..ccd881a 100644 --- a/crates/benchup-core/build.rs +++ b/crates/benchup-core/build.rs @@ -111,7 +111,7 @@ fn plural(one: &str, other: &str, singular: bool) -> String { } fn main() { - let messages = read_catalog("../../assets/i18n/en.json", "English"); + let messages = read_catalog("../../assets/i18n/en_US.json", "English"); let translations = [ Translation { variant: "SimplifiedChinese", @@ -125,9 +125,14 @@ fn main() { }, Translation { variant: "Vietnamese", - catalog: read_catalog("../../assets/i18n/vi.json", "Vietnamese"), + catalog: read_catalog("../../assets/i18n/vi_VN.json", "Vietnamese"), singular: false, }, + Translation { + variant: "Spanish", + catalog: read_catalog("../../assets/i18n/es_419.json", "Spanish"), + singular: true, + }, ]; // Generate every format string so Rust continues checking named arguments. diff --git a/crates/benchup-core/src/history.rs b/crates/benchup-core/src/history.rs index 18583ea..2dbd0e9 100644 --- a/crates/benchup-core/src/history.rs +++ b/crates/benchup-core/src/history.rs @@ -760,7 +760,7 @@ mod tests { let _reset = ResetLanguage; let key = "wood_glue.contacts_applied"; for saved_label in [ - crate::i18n::catalog_message("en", key), + crate::i18n::catalog_message("en_US", key), crate::i18n::catalog_message("zh_CN", key), crate::i18n::catalog_message("pt_BR", key), key.to_owned(), @@ -778,7 +778,7 @@ mod tests { entry.signature = Some("preserved-signature".into()); let saved = serde_json::to_string(&doc.history).unwrap(); let mut history: History = serde_json::from_str(&saved).unwrap(); - for language in ["en", "zh_CN", "pt_BR", "zh_CN"] { + for language in ["en_US", "zh_CN", "pt_BR", "vi_VN", "es_419", "zh_CN"] { crate::i18n::set_language(language); let translated = crate::i18n::catalog_message(language, key); let label = translated.as_str(); diff --git a/crates/benchup-core/src/i18n.rs b/crates/benchup-core/src/i18n.rs index 2d8c2a9..b518901 100644 --- a/crates/benchup-core/src/i18n.rs +++ b/crates/benchup-core/src/i18n.rs @@ -7,6 +7,7 @@ pub enum Language { SimplifiedChinese, BrazilianPortuguese, Vietnamese, + Spanish, } thread_local! { @@ -18,7 +19,8 @@ pub fn set_language(language: &str) { LANGUAGE.set(match language { "zh_CN" | "zh-CN" | "zh-Hans" => Language::SimplifiedChinese, "pt_BR" | "pt-BR" => Language::BrazilianPortuguese, - "vi" | "vi_VN" | "vi-VN" => Language::Vietnamese, + "vi_VN" | "vi-VN" => Language::Vietnamese, + "es_419" | "es-419" => Language::Spanish, _ => Language::English, }); } @@ -52,8 +54,9 @@ pub(crate) fn catalog_message(language: &str, key: &str) -> String { let catalog = match language { "zh_CN" => include_str!("../../../assets/i18n/zh_CN.json"), "pt_BR" => include_str!("../../../assets/i18n/pt_BR.json"), - "vi" => include_str!("../../../assets/i18n/vi.json"), - "en" => include_str!("../../../assets/i18n/en.json"), + "vi_VN" => include_str!("../../../assets/i18n/vi_VN.json"), + "es_419" => include_str!("../../../assets/i18n/es_419.json"), + "en_US" => include_str!("../../../assets/i18n/en_US.json"), _ => panic!("unsupported test language: {language}"), }; let messages: serde_json::Value = serde_json::from_str(catalog).unwrap(); @@ -118,7 +121,7 @@ mod tests { ); assert_eq!(tr!("app.selection_entities", count = 1), "1 entidade"); assert_eq!(tr!("app.selection_entities", count = 2), "2 entidades"); - super::set_language("vi"); + super::set_language("vi_VN"); assert_eq!(tr!("common.cancel"), "Hủy"); assert_eq!(tr!("settings.settings"), "Cài đặt"); // Vietnamese uses the `other` form even for one selected item. @@ -154,10 +157,30 @@ mod tests { super::Language::Vietnamese ); super::set_language("en"); + super::set_language("es_419"); + assert_eq!(tr!("common.cancel"), "Cancelar"); + assert_eq!(tr!("settings.settings"), "Configuración"); + for (fit, key) in [ + (crate::ImageFit::Contain, "core.reference_images.contain"), + (crate::ImageFit::Cover, "core.reference_images.cover"), + (crate::ImageFit::Stretch, "core.reference_images.stretch"), + ] { + assert_eq!(fit.label(), super::catalog_message("es_419", key)); + } + #[cfg(not(target_arch = "wasm32"))] + assert_eq!( + super::spawn_localized(super::current_language).join().unwrap(), + super::Language::Spanish + ); + assert_eq!(tr!("app.selection_entities", count = 1), "1 entidad"); + assert_eq!(tr!("app.selection_entities", count = 2), "2 entidades"); + super::set_language("es-419"); + assert_eq!(super::current_language(), super::Language::Spanish); + super::set_language("en_US"); assert_eq!(tr!("common.cancel"), "Cancel"); assert_eq!( crate::ImageFit::Contain.label(), - super::catalog_message("en", "core.reference_images.contain") + super::catalog_message("en_US", "core.reference_images.contain") ); super::set_language("unsupported"); assert_eq!(tr!("settings.settings"), "Settings"); diff --git a/index.html b/index.html index 0bd24ea..419650f 100644 --- a/index.html +++ b/index.html @@ -256,7 +256,7 @@