Skip to content

Add Amharic language support - #346

Open
yosephfeleke251 wants to merge 3689 commits into
barebaric:mainfrom
yosephfeleke251:main
Open

Add Amharic language support#346
yosephfeleke251 wants to merge 3689 commits into
barebaric:mainfrom
yosephfeleke251:main

Conversation

@yosephfeleke251

Copy link
Copy Markdown

This PR adds the Amharic language code mapping to the general preferences UI page and initializes the base translation file directory structure under locale/am/LC_MESSAGES/.

knipknap and others added 30 commits July 30, 2026 10:24
Remove push_transform_to_model from WorkPieceElement and GroupElement, remove no-op trigger_ops_rerender stub and call site, remove circumferential_axis property from CanvasProjection and its test.
Remove unused SceneRenderer Protocol, unused mvp_ui_gl/mvp_scene_gl properties from RenderContext, drop Protocol import.
Remove close_handle and forget from ArtifactStore, remove encoded_output_bytes from JobArtifact (param, field, lazy-decode, serialization). Update 3 test files.
Delete CairoEncoder, TextureEncoder and their test files. Remove stitch_chunk_to_bitmap function and its tests.
Remove WorldSpace, WorkareaSpace, and PixelSpace classes from coordspace and their tests.
fix(mac): restore Monterey-compatible bundles
Normalize OpenCV ChArUco corners and IDs before conversion so both singleton-dimension and flat result shapes are supported. Reject malformed or incomplete results and add regression coverage for the supported shapes and invalid detections.
The settle-driven preview refresh re-triggered job generation forever
when the document could not produce a job aggregate. The intent
builder also emitted job nodes referencing steps with no compute
upstream, causing a missing-dependency error on every rebuild. Skip
job/machinexform/encode nodes when no step has upstream compute, and
fail fast in generate_job_artifact when the doc has no visible step
with workpieces.
…nings

- Workpiece.to_part() now uses Part.from_geometry_multi_face so disjoint
  pockets become separate faces instead of a single merged contour
- Add assembly_warnings module translating raygeo AssemblyWarnings into
  user-facing strings
- Emit pipeline_warnings from the intent controller on completed nodes
  with warnings, relayed through the pipeline and shown as toasts
- Bump raygeo to 1.28.0
Add a new built-in addon 'CNC Essentials' providing mill capabilities:
  - MILL capability with tool diameter, spindle RPM, feed/plunge/travel
    speeds, target depth, depth-per-pass and safe Z settings.
  - Step base (CncAssemblerStep) that builds raygeo assembler specs and
    compute payloads, plus steps for adaptive clearing, flat spiral,
    helix plunge, ramp entry, slot, toroidal clear, profile inner and
    profile outer.
  - Addon worker/frontend entry points and a CNC settings widget.

Multi-face and sequential CNC support:
  - WorkPiece.to_part() now uses Part.from_geometry_multi_face so
    disjoint pockets become separate faces.
  - Steps can opt into sequential global state (uses_global_state):
    intent builder chains Compute steps via state_source_keys, links
    aggregates sequentially (LinkMode.sequential) and feeds the
    predecessor token into position-sensitive caching.
  - Pipeline surfaces non-fatal assembler warnings (FACE_FAILED /
    REGION_FAILED) through a new assembly_warnings signal; the doc
    editor translates them (gettext) into UI toasts.

Bump raygeo to 1.28.0 in pixi.toml, requirements.txt and
debian/requirements-bundle.txt, and update pixi.lock.

Tests: capability MILL registration, multi-face workpiece to_part,
assembly warning translation, sequential-state intent controller
behaviour, and adaptive clearing step spec generation.
Introduce MachineCapability (LASER/MILL) computed as explicit config
union head-inferred capabilities, and generalize heads into a Head base
with LaserHead and SpindleHead subtypes. Steps declare required machine
caps and the step pickers filter by the active machine, so CNC steps
only appear once a spindle is configured. Machine settings gain a Heads
page (add/remove Laser/Spindle heads with type-specific detail widgets)
and a Capabilities readout page. ConfigWizard asks for head type on
review.
CncAssemblerStep and its concrete subclasses did not override
to_dict/from_dict, so CNC parameters (tool_diameter, spindle_rpm,
target_depth, etc.) were silently dropped on save. Add polymorphic
serialization overrides plus a Step._serialized_keys() hook so
unknown keys from newer files still land in extra instead of being
mistaken for known CNC attributes.
Rename the step head-selection attribute and serialization key to be
domain-neutral. get_selected_laser() becomes get_selected_head() and
returns a generic Head. Old project and recipe files load via alias
migration, keeping the legacy key out of extra.

Also restrict LaserHeadVar choices to laser heads (via the
machine_capability property, avoiding a circular import) and guard
the kerf-sync handler so selecting a SpindleHead cannot crash.
Create LaserStep(Step) owning the laser process attributes and
behaviour (initial ops, summary, typed get_selected_laser, laser
setters, serialization overrides). The base declares the attributes
before super().__init__ so capability defaults still apply. All six
laser step classes now inherit from it. Purely additive - core Step
keeps its laser surface until the later removal iteration.
EngraveStep overloaded max_power (laser hardware max in mW) as its
raster power ceiling (0-1 float), colliding semantics under one
serialized key. Rename the raster range to min_power_level /
max_power_level. LaserStep.max_power is now cleanly typed as int.

Legacy engrave files keyed the raster range as min_power/max_power;
from_dict migrates those into the _level attributes and resets the
hardware max_power slot instead of inheriting the raster ceiling.
EngraveStep gains _serialized_keys for its raster keys.
Pass the Machine to build_compute_payload/assembler_token_params and
let each domain step resolve its own process parameters from it. This
eliminates the laser-flavoured MachineDefaults object that was shared
through the pipeline and removes it from core entirely.

Laser steps read the machine (arc tolerance, head spot) directly via
LaserStep.get_laser_spot; CNC steps ignore it. The pipeline and its
controller now require a machine, so the step methods' machine
parameter is typed non-optional. CNC payload power is expressed as
the spindle RPM / max RPM ratio so toolpaths render at the right
intensity instead of the zero-power colour.
Move laser-only attributes (power, kerf, air assist, etc.) from the core
Step into the laser addon's LaserStep base and its concrete steps. Steps
now own their import-settings application via apply_import_settings, so
the lightburn importer no longer special-cases laser attributes and the
assembler no longer mutates step settings directly.
Build the op player before extracting playback offsets during the
chunked artifact upload, so the ring/scanline renderer gets a valid
playhead on the first render instead of drawing every vertex. Also
upload the colour LUTs before vertex data and give the scanline
overlay a power brightness ramp so it dims by power.
Drivers now report plain DriverFeatures data (PWMParams for PWM
support) instead of building concrete Capability objects. The machine
resolves a head's effective capabilities through an addon-registered
resolver registry, so core no longer depends on the laser addon's
PWMCapability.

Move PWMCapability into the laser addon and resolve it there. Head
spot resolution moves from LaserStep to a static LaserHead.get_spot_size,
and head-specific process defaults move to Head.get_defaults. Steps no
longer carry driver-capability or head-resolution methods.
knipknap and others added 21 commits August 12, 2026 17:30
The machine-transform stage now subtracts WCS offsets in machine space
(after w2m) instead of world space (before w2m), fixing wrong G-code
with axis reversal + WCS.

Adds parametrized regression test covering all origin/reversal/WCS
combinations.
Move display-facing coordinate conversions from MachineSpace to
MachinePanel, which holds a reference to the Machine and derives a
MachineSpace on demand. All UI callers (canvas, doceditor, simulator)
now use machine.panel.* instead of machine.get_coordinate_space().*.

MachinePanel gains proxy methods for world↔machine point/item
transforms, workarea geometry, axis labels, and command offsets. A
reference_position_world property replaces the removed
Machine.get_reference_position_world method.

Backend callers (encoder, frame generation) continue using
MachineSpace.from_machine directly.
MachineSpace now operates purely in MACHINE_SPACE, WORLD_SPACE, and
COMMAND_SPACE. All bed-rotation logic (PanelOrientation, rotation
matrices, composed transforms, presented geometry) lives on
MachinePanel. The panel observes machine.changed to reproject camera
calibration when bed dimensions change.

MachinePanel defines PanelOrientation (NATIVE / ROTATED_LEFT /
ROTATED_RIGHT), owns the orientation state, composes the panel-to-
native rotation on top of the native MachineSpace transforms, and
exposes presented extents, margins, workarea size, and axis-label
origin. Machine delegates panel_orientation and set_panel_orientation
to the panel and serializes the orientation in to_dict/from_dict.
Add extent_frame and has_custom_work_area to MachinePanel so the
presented geometry API is complete. ViewportConfig.from_machine_with_wcs
now derives width/depth, model-matrix flips, margin_shift, extent frame,
and axis flags from machine.panel instead of raw machine attributes.
WorkSurface._machine_coords_to_canvas delegates to
panel.machine_point_to_world() and _update_extent_frame_flat reads
panel.extents/margins/workarea_size.
MultiSelectionGroup bounding boxes now use
CanvasElement.get_world_bounding_box() instead of manual corner math,
and the framing-selection path in Canvas consumes the same helper.

Add MachinePanel.work_area_center() and replace three duplicate
work-area center computations in array_dialog, layer_column, and
drag_drop_cmd. Move selection bounds reporting from MainWindow into
WorkSurface.get_selection_bounds(), which uses
panel.get_workarea_world_rect() for the machine fallback.
Add MachinePanel.calculate_jog() which maps visual jog directions
(EAST/WEST/NORTH/SOUTH) to native axis deltas through the composed
world-to-machine matrix, so a rotated bed drives the orthogonal native
axis. UP/DOWN delegate to Machine.calculate_jog for Axis.Z.

JogWidget now aggregates per-direction deltas and checks joggability
and soft limits against the axes a direction actually drives.
Add MachinePanel.machine_to_panel, the 90-degree bed rotation from
MACHINE-bed geometry into the panel presentation (inverse of the
panel-to-native rotation). Add MachinePanel.nogo_zones, which returns
detached rotation-projected copies of the machine's no-go zones so
callers never mutate the native zones.
Step renaming was not reflected in the WorkflowView (the step list in
the main window's right pane) because:

1. Step.set_name() did not explicitly fire the 'updated' signal like
   Layer.set_name() does. While the inherited name property setter does
   fire it, adding the explicit send makes the notification path
   consistent with how Layer renaming works and ensures all listeners
   are reliably notified.

2. WorkflowView only listened to workflow.updated,
   workflow.descendant_added, and workflow.descendant_removed, but not
   workflow.descendant_updated. When a step's name changes, the signal
   bubbles up as descendant_updated, which WorkflowView was missing,
   so it never refreshed its step list. Added the missing connection.

Closes barebaric#343
When importing with the Map to Existing layer mode, document layers
that still carry auto-generated names (e.g. "Layer 1") are renamed
to the imported layer's name, since they were never manually renamed.
Manually renamed layers are left untouched. The rename is undoable as
part of the import transaction.
@knipknap

Copy link
Copy Markdown
Contributor

Hi there, thank you for opening the PR. I see that the file has no actual translations though, I think those would be needed before merging this. I am also wondering about maintenance - without frequent updates I expect this will be outdated quickly. Are you willing to take on maintainership for this language?

@yosephfeleke251

Copy link
Copy Markdown
Author

Hi @knipknap thanks for the feedback!

​I am actively working on the translation now and will be pushing updates to this PR as I progress.

​I am adding Amharic support primarily to help my employees operate our laser cutter for paper card production. Because of this specific focus, I plan to translate the core UI they’ll come across daily, but I likely won't be translating features outside that workflow.

I completely understand if you'd prefer not to merge it without full coverage and ongoing maintainership, but I'm happy to provide these core strings if you're open to having a starting point for others to build on!

What do you think?

@knipknap

Copy link
Copy Markdown
Contributor

Ok, I guess it does not hurt even if this does turn out to be outdated. Thank you for the effort!

@yosephfeleke251

Copy link
Copy Markdown
Author

@knipknap Thank you! I appreciate it!

I plan to submit the translations in small batches as I work through them. Would it be okay to merge the initial batches along the way? That will help me test subsequent translations more easily.

@knipknap

Copy link
Copy Markdown
Contributor

Perhaps you can use the attached AI translation as a basis?

rayforge.zip

@yosephfeleke251

Copy link
Copy Markdown
Author

@knipknap Thanks for setting this up! Having this complete base file as a reference is going to save me a lot of time.

​AI often struggles with Amharic fluency, so there are bound to be inconsistencies and mistakes with technical terms, but it will be great to build on. I'll go through it, refine the core UI strings for our workflow, and push the updates.

​Out of curiosity, which tool did you use to generate the translations?

@knipknap

knipknap commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Great, Thank you! I used aistudio.google.com with Gemini 3.1 Pro in this case - it does not allow uploading .po files, but I just copy/pasted the content in the chat box. It is too large for it to respond with everything in one response, but I just asked it to continue in a second prompt and then combined the two responses in one file.

@yosephfeleke251

Copy link
Copy Markdown
Author

Ah, gotcha! Thanks for explaining the workflow.

​I'll dive in and push the updates to the PR soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants