From a 2016 Macro-Based Converter to a Specification-Driven Translation Engine
I originally created Persian-to-Braille in 2016.
The first version belonged to a very different software environment. Translation behavior was distributed across Microsoft Office macros, database-backed mappings, conversion tables, and application-specific workflows.
For its original purpose, that architecture worked.
A Persian character could be looked up, mapped to a Braille representation, and converted inside the Office environment in which the software was being used.
Ten years later, however, I was no longer interested in simply updating the old macros.
I wanted to answer a more fundamental engineering question:
How should Persian Braille translation be designed if the rules must exist independently of the application that executes them?
That question became the foundation of the 2026 rebuild.
The first complete application built on the new architecture is the Persian-to-Braille command-line interface.
The Problem with the Original Architecture
The 2016 implementation can be simplified as:
Persian text
↓
Database mappings
↓
Office / VBA logic
↓
Braille output
This architecture was practical, but the translation model and the application were tightly coupled.
That created several long-term problems.
What was the authoritative Persian Braille rule?
Was a database record a formal rule, or simply historical implementation data?
What happened when Word VBA, Excel VBA, and SQL contained different mappings?
How should Unicode edge cases behave?
Could another application use the translator without reproducing the old Office logic?
Could translation behavior be tested independently from Microsoft Office?
These were not primarily performance problems.
They were architecture, ownership, and specification problems.
The 2026 Architecture
The rebuilt system separates those responsibilities explicitly:
Canonical Specification
↓
Translation Core
↓
Public TypeScript SDK
↓
CLI
The architectural principle is:
The specification owns the rules.
The Core executes them.
The SDK exposes them.
The CLI consumes them.
This means the CLI contains no independent Persian Braille mapping table.
It does not decide how a Persian character should be represented in Braille.
It does not define normalization behavior.
It does not determine rule precedence.
It does not create its own fallback behavior for unsupported input.
The CLI is deliberately only a consumer boundary.
That distinction is central to the entire redesign.
From Mapping Tables to an Executable Specification
The legacy project was largely built around mappings:
Persian character → Braille representation
The modern project treats mapping as only one part of a larger translation system.
The executable profile at this milestone is:
id: fa-ir-g1
version: 0.1.0
status: draft
direction: print-to-braille
At the CLI milestone, the runtime specification contained:
175 admitted rules
37 normative rules
138 candidate rules
Those rules are not limited to simple one-character replacements.
The translation model also includes concepts such as:
character rules
sequences
context-sensitive behavior
rule precedence
layout behavior
modes
normalization behavior
explicit failure states
This is an important architectural shift.
A language translation system — even a specialized Braille translator — cannot always be modeled safely as a dictionary lookup.
Unicode as Part of the Contract
Persian text processing requires explicit Unicode handling.
The rebuilt engine preserves both:
code-point index
UTF-16 index
when reporting relevant locations and failures.
Known Unicode format controls are handled deliberately.
Unknown format controls fail closed rather than disappearing silently.
This matters because a translation system should not quietly modify an input simply because a particular Unicode value was inconvenient for the implementation.
Unsupported or ambiguous states are part of the public translation contract.
Deterministic Translation Core
The Core executes runtime data generated from the canonical specification.
It does not maintain another handwritten copy of the same mappings.
Rule selection considers:
context eligibility
sequence matching
precedence
character fallback
structural behavior
normalization behavior
explicit failure conditions
The same input under the same profile should produce the same public result.
When the engine cannot translate an input according to the admitted rules, it does not silently guess.
It fails explicitly.
Current public failure categories include:
PREPROCESSING_FAILED
UNKNOWN_CHARACTER
UNKNOWN_SEQUENCE
AMBIGUOUS_MATCH
UNSUPPORTED_ENGINE_STATE
Failure is not treated as an implementation accident.
Failure semantics are part of the API.
A Real Command-Line Interface
The executable is:
persian-braille
Translation can be performed directly:
persian-braille translate "سلام"
or from standard input:
persian-braille translate --stdin
The bundled profile can also be inspected:
persian-braille profile
The CLI provides three primary output representations:
unicode
cells
json
For example:
persian-braille translate "سلام"
returns Unicode Braille.
For Braille cell notation:
persian-braille translate "سلام" --format cells
For software integration:
persian-braille translate "سلام" --format json
The JSON representation is deterministic and machine-readable, making the CLI useful as more than an interactive utility.
It can act as an automation boundary for scripts, testing tools, pipelines, and future integrations.
Stable Process Semantics
A CLI intended for automation needs more than readable output.
It needs a predictable process contract.
The current exit codes distinguish between:
0 success
1 expected translation failure
2 usage or input-contract error
3 unexpected internal failure
An unsupported translation input is therefore not treated as the same type of failure as invalid CLI usage.
And neither is confused with an unexpected software defect.
Human-readable execution and machine-readable execution are handled separately as well.
Expected failures can be reported clearly to a terminal user while JSON consumers still receive structured data they can reliably process.
Input Is Not Silently Rewritten
One deceptively small design decision was preserving input received through stdin.
The CLI does not automatically trim translation input.
Whitespace and line boundaries may have structural meaning.
An application should therefore not modify the translation language before the Core receives it unless the specification explicitly defines that transformation.
This reflects the broader rule of the architecture:
Consumers do not own translation semantics.
Determinism and Reproducibility
Repeated JSON translation of the same input is expected to produce byte-identical output.
That requirement improves:
regression testing
automation
snapshots
reproducible pipelines
debugging
future third-party integration
The CLI deliberately avoids introducing irrelevant runtime state, such as timestamps, into deterministic translation envelopes.
The repository also validates clean-state dependency construction:
Core
↓
SDK
↓
CLI
A successful build should not depend on a stale dist directory left behind from yesterday.
Reproducibility has to work from a clean workspace.
Verification at the CLI Milestone
At this stage of the project, the validation baseline included:
Core tests: 210 / 210
SDK tests: 12 / 12
CLI tests: 13 / 13
Conformance tests: 22 / 22
Profile scenarios: 15 / 15
Repository-level validation also covered:
Unicode normalization
architectural dependency boundaries
runtime-specification consumption
specification determinism
SDK packaging
repository hygiene
clean-state reproducibility
GitHub CI
The question is no longer merely:
Does the macro appear to produce the expected output?
It is now:
Can the complete translation architecture reproduce its defined behavior from a clean state through a controlled public interface?
Legacy Sources as Evidence
The modern project does not pretend that the 2016 version never existed.
The opposite is true.
Historical Word VBA, Excel VBA, and SQL implementations were extracted and compared as evidence.
Agreement between legacy sources is useful evidence.
Disagreement is recorded as disagreement.
But historical implementation behavior is not automatically promoted into a formal standard.
That creates an important separation:
Legacy behavior is evidence.
The specification is authority.
This allows the project to preserve its history without allowing historical implementation accidents to silently become permanent language rules.
Why Start with a CLI?
A CLI is an effective architectural test.
There is very little interface complexity behind which business logic can hide.
It forces a software system to answer clearly:
What is the input?
What is the output?
What constitutes failure?
What goes to stdout?
What goes to stderr?
What is the exit status?
Can another program automate it?
Can the application use the engine without accessing its internals?
If a translation system can expose a clean command-line interface while keeping its internal rule engine private, its reusable boundary is beginning to become real.
Ten Years of Architectural Change
The visible transition is simple:
2016
Database + Macros
↓
2026
CLI
The actual transition is much larger:
2016
Database mappings
↓
Office macros
↓
Application-owned translation
2026
Canonical specification
↓
Unicode-aware Core
↓
Public SDK
↓
CLI
In 2016, the macros and database were effectively the project.
In 2026, they became historical implementation sources.
The translation rules became specification data.
The converter became a deterministic Core.
The reusable software boundary became a public SDK.
And the first complete executable consumer became a CLI.
That is the modernization I wanted from this project.
Not:
old interface → new interface
but:
implementation-coupled translation → application-independent translation architecture
Repository
GitHub:
https://github.com/soroushneyestani/Persian-to-Braille
Persian-to-Braille is an open-source project developed around accessibility, Persian language processing, Unicode correctness, deterministic software behavior, and reusable developer tooling.