Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,40 @@ jobs:
working-directory: ${{ matrix.module }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
with:
go-version-file: ${{ matrix.module }}/go.mod
cache-dependency-path: ${{ matrix.module }}/go.sum
- run: go test -race -covermode=atomic -coverprofile=cover.out ./...
- run: go vet ./...

gottext:
runs-on: ubuntu-latest
defaults:
run:
working-directory: gottext
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
with:
persist-credentials: false
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
with:
package_json_file: gottext/package.json
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: 22
cache: pnpm
cache-dependency-path: gottext/pnpm-lock.yaml
- run: pnpm install --frozen-lockfile
- run: pnpm run typecheck
- run: pnpm run cover
- run: pnpm run lint
- run: pnpm exec knip
- run: pnpm run build
- run: node -e "Promise.all(['@gopherium/gottext', '@gopherium/gottext/build', '@gopherium/gottext/sync', '@gopherium/gottext/testing'].map((entry) => import(entry))).then(() => console.log('every entry loads'))"

lint:
runs-on: ubuntu-latest
strategy:
Expand All @@ -35,6 +62,8 @@ jobs:
module: ["mailkit"]
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
with:
go-version-file: ${{ matrix.module }}/go.mod
Expand All @@ -55,6 +84,8 @@ jobs:
working-directory: ${{ matrix.module }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
with:
go-version-file: ${{ matrix.module }}/go.mod
Expand Down
60 changes: 60 additions & 0 deletions .github/workflows/publish-gottext.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: publish-gottext

on:
push:
tags:
- "gottext/v*"

permissions:
contents: read

jobs:
pack:
runs-on: ubuntu-latest
defaults:
run:
working-directory: gottext
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
with:
package_json_file: gottext/package.json
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: 22
- name: Verify the tag matches the package version
run: |
tag_version="${GITHUB_REF_NAME#gottext/v}"
pkg_version="$(node -p "require('./package.json').version")"
if [ "$tag_version" != "$pkg_version" ]; then
echo "tag $tag_version does not match package.json version $pkg_version" >&2
exit 1
fi
- run: pnpm install --frozen-lockfile
- run: pnpm run cover
- run: pnpm run build
- run: npm pack
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: package
path: gottext/*.tgz
retention-days: 1

publish:
needs: pack
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: ">=22.14.0 <23"
- name: Update npm for trusted publishing
run: npm install -g npm@11.19.1 --ignore-scripts
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: package
- run: npm publish ./*.tgz
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,18 @@ need and ignore the rest.

## Modules

- [`gottext`](gottext/) reads, writes and syncs gettext catalogs for
TypeScript applications, published to npm as `@gopherium/gottext`.
- [`mailkit`](mailkit/) renders mail from template files and sends it
over SMTP.

## Design

One repository, one Go module per brick, no shared code between them.
Every module carries its own go.mod, its own CHANGELOG and its own
lint configuration, and is released independently under a path
prefixed tag such as `mailkit/v0.1.0`. Modules depend on published
One repository, one self-contained brick per directory, no shared code
between them. A Go brick carries its own go.mod and a TypeScript brick
its own package.json, each with its own CHANGELOG and lint
configuration, released independently under a path-prefixed tag such
as `mailkit/v0.1.0` or `gottext/v0.4.0`. Bricks depend on published
versions only, never on sibling source, so what you pin is what you
get.

Expand Down
3 changes: 3 additions & 0 deletions gottext/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
dist/
coverage/
83 changes: 83 additions & 0 deletions gottext/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Changelog

Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
versioning follows [Semantic Versioning](https://semver.org/). While at 0.x,
minor releases may break. Releases are tagged `gottext/vX.Y.Z` and publish
from CI.

## [0.3.0] - 2026-08-25

### Added

- `pushTranslations`, which carries every held catalogue of a supported language
to the platform with its fuzzy flags, trimmed to the template so a stale file
revives nothing, and holding back every answer the platform has settled so a
push never overwrites a reviewer.
- `addLanguage` on the platform seam, and the push adds and fills a supported
language the platform does not list yet.
- Upload pacing, one paced retry when the platform answers its rate refusal.
- A fuzzy aware merge, where a reviewed answer settles a fuzzy one and a fuzzy
export never replaces a settled answer, restored answers keeping their flag.
- `unreviewed`, which names the answers still carrying the fuzzy flag.

### Changed

- Clearing a fuzzy flag alone now counts as a meaningful change, so a pull
writes an approval home even when the text did not move.

## [0.2.0] - 2026-08-24

### Added

- `errorText`, which shows a refused answer in the reader's own language,
filling the template its code names from the values the answer carries and
speaking the server's own words when no template fits. The caller supplies
the templates and the words to fall back on, so both stay translatable.
- A `./testing` entry with `resetLocale`, which takes every text domain and the
display locale back to their sources between tests.
- `globCatalogs`, which turns the lazy chunks a bundler globbed into the loader
a catalogue entry wants, reading the locale from each file's own name.

### Changed

- `startLocale` shows the default locale from the moment it is asked, so
`displayLocale` and `formatDate` never answer en-US to a consumer whose
sources are written in another locale.

## [0.1.1] - 2026-08-21

### Added

- `resolvedVersions` and `pinnedVersions`, which read a pnpm lockfile and the
manifests that pin a package, so a repository can gate itself against
resolving two copies of anything that holds module state.

### Fixed

- A message sharing its name with an object prototype member, `constructor` and
`toString` among them, no longer crashes the gates or slips past the orphan
check.
- `serializeCatalog` answers readable JSON for a catalogue carrying no metadata
entry, where it used to write the bare word undefined.

## [0.1.0] - 2026-08-20

### Added

- `startLocale`, which resolves a locale, loads every catalogue in parallel and
sets each under its own text domain before it returns. A catalogue naming no
domain lands under the default domain the WordPress packages read.
- `displayLocale`, `rememberLocale` and `formatDate`, the seam dates and numbers
follow. `formatDate` takes a date or the text a server stored, and the options
bag any real format needs.
- `pot`, which extracts messages through the four gettext call shapes and
optionally through Go markers and templates. The Go walk reads nothing unless
it is given roots.
- `compileCatalog` and `serializeCatalog`, turning PO sources into the JSON a
locale chunk ships, metadata entry first and deterministic.
- `untranslated`, `orphaned` and `mismatched`, the three gates over a
catalogue's health. `mismatched` refuses a translation whose placeholders
its message does not name, bare placeholders included.
- `syncTranslations` and `poeditorAt`, which carry translations home from a
platform. A translation once set is never removed by a sync, and the summary
says how many answers were kept where the platform holds nothing.
Loading