cletter 0.2.1

Locale-correct business correspondence: composes cgreet, cfarewell, cink and cdate with locale tables for opening, subject, resolution and orthography
Documentation
# cletter

**Locale-correct correspondence, from salutation to closing.**

[![crates.io](https://img.shields.io/crates/v/cletter.svg)](https://crates.io/crates/cletter) [![npm](https://img.shields.io/npm/v/@corbet-labs/cletter.svg)](https://www.npmjs.com/package/@corbet-labs/cletter) [![Rust API](https://docs.rs/cletter/badge.svg)](https://docs.rs/cletter)

Compose a formal letter from deterministic rules: resolve a document locale, address a recipient, write an application subject, format the date, choose a closing, and prepare a handwritten signature image. Rust, JavaScript, and Python share the same conformance vectors.

```js
import { salutation } from '@corbet-labs/cletter';

salutation('de-ch', 'Frau Dr. Müller');
// Sehr geehrte Frau Dr. Müller
```

## Install

| Environment | Command |
| --- | --- |
| Rust / Cargo | `cargo add cletter` |
| Node.js / npm | `npm install @corbet-labs/cletter` |
| pnpm | `pnpm add @corbet-labs/cletter` |
| Yarn | `yarn add @corbet-labs/cletter` |
| Bun | `bun add @corbet-labs/cletter` |
| Deno | `deno add npm:@corbet-labs/cletter` |

The 0.2.1 JavaScript distribution includes compiled ESM, CommonJS,
TypeScript declarations, and a standalone browser module. Node.js 20+ is
supported; no TypeScript loader is required.

```js
// CommonJS
const { salutation } = require('@corbet-labs/cletter');
```

```html
<script type="module">
  import { salutation } from 'https://cdn.jsdelivr.net/npm/@corbet-labs/cletter@0.2.1/dist/browser.js';
  console.log(salutation('de-ch', 'Frau Dr. Müller'));
</script>
```

Python wheels, source distributions, and CLI instructions are in the
[installation guide](https://github.com/corbet-labs/cletter/blob/main/docs/installation.md).
JSR publication and Typst availability are listed there explicitly.

## Rust

```rust
use cletter::{closing, long_date, resolve_locale, salutation};

let locale = resolve_locale(Some("de"), Some("Zürich, Zug"));
assert_eq!(salutation(&locale, "Frau Dr. Müller"), "Sehr geehrte Frau Dr. Müller");
assert_eq!(closing(&locale, None), "Freundliche Grüsse");
assert_eq!(long_date(&locale, 2026, 9, 7).as_deref(), Some("7. September 2026"));
```

## Python

```python
from cletter import salutation

assert salutation("de-ch", "Frau Dr. Müller") == 'Sehr geehrte Frau Dr. Müller'
```

## API

| JavaScript / Python or Rust | Purpose |
| --- | --- |
| `resolveLocale` / `resolve_locale` | Language and location → document locale |
| `salutation`, `opening` | Recipient address or formal opening |
| `subject` | Application subject with optional prefix override |
| `closing` | Locale-specific valediction |
| `longDate` / `long_date` and other date helpers | Gregorian datelines |
| `applyOrtho` / `apply_ortho` | Explicit `ß` → `ss`, `ẞ` → `SS` for caller-selected `de-ch`/`de-li` prose |
| `orthographyIssues` / `orthography_issues` | Non-mutating matched source/replacement pairs |
| `orthographyReplacements` / `orthography_replacements` | Applicable spelling pairs from the shared table |
| `normalize`, `signatureSize` / `signature_size` | Signature-image preparation |
| `warnings` | Advisories for incomplete recipient details |

Openings and closings have 40 locale entries. Date formatting currently covers German and English variants; other dates fall back to English. Locale IDs are lowercase in tables and outputs; lookups accept mixed case and explicit overrides win. Subject defaults are for job applications; supply a prefix override for other correspondence. The library produces letter components; your application owns the body, layout, and PDF rendering. Spelling transformations require caller-selected prose: preserve exact names, quotations, URLs and source material; use diagnostics when those boundaries are unavailable.

The self-contained [Typst facade](https://github.com/corbet-labs/cletter/blob/main/typst/README.md) consumes the same greeting, closing and date helpers as the other ports.

## Correspondence family

| Library | Responsibility |
| --- | --- |
| [cletter](https://github.com/corbet-labs/cletter) | Compose the correspondence helpers |
| [cgreet](https://github.com/corbet-labs/cgreet) | German salutations and titles |
| [cfarewell](https://github.com/corbet-labs/cfarewell) | Locale-specific closings |
| [cdate](https://github.com/corbet-labs/cdate) | Calendar-date formatting |
| [cink](https://github.com/corbet-labs/cink) | Handwritten signature images |


## Development

Behavior is defined by [the locale tables](https://github.com/corbet-labs/cletter/tree/main/tables)
and [shared conformance vectors](https://github.com/corbet-labs/cletter/tree/main/tests/vectors).
Rust, JavaScript, and Python run the same vectors. Selected CI checks exercise
installed JavaScript tarballs, Python wheels and command-line entrypoints, and
Typst packages. Release validation records the actual runtime and platform;
Linux results do not establish native Windows or macOS coverage.
All five Rust crates forbid unsafe code in their own source.

See [the release guide](https://github.com/corbet-labs/cletter/blob/main/docs/releasing.md)
for generation, verification, and publication commands.

## License

Copyright 2026 Julian Y. Richard Corbet. Licensed under [Apache-2.0](LICENSES/Apache-2.0.txt). Vendored dependencies retain their notices; `typst/vendor/cgreet/` offers MIT OR Apache-2.0 and the other components use Apache-2.0.