@lopatnov/translate-angular
v2.0.0
Published
Manual testing UI for the Lopatnov.Translate gRPC service — Angular 21 + SSR + Express.
Maintainers
Readme
Translate Studio
Manual testing UI for the Lopatnov.Translate gRPC service. Angular 21 · SSR · Express · Bootstrap 5.
Exercises every gRPC endpoint of Lopatnov.Translate through a clean web interface — no grpcurl commands needed.
Browser → Angular (port 4200) → Express SSR → @grpc/grpc-js → gRPC service (port 5100)Table of Contents
- Pages
- Prerequisites
- Getting Started
- npm Scripts
- Architecture
- Debugging
- End-to-End Tests
- Environment Variables
- Project Structure
- Built With
- Contributing
- License
Pages
| Route | Purpose | gRPC RPC |
| ------------- | --------------------------------- | ----------------------- |
| / | Service status, available models | GetCapabilities |
| /translate | Text translation with auto-detect | TranslateText |
| /detect | Language detection + confidence | DetectLanguage |
| /localize | JSON i18n file translation | TranslateLocalization |
| /transcribe | WAV → transcript + segments | TranscribeAudio |
Prerequisites
| Tool | Version | Notes |
| ------------------ | ------- | ----------------------------------------------------------- |
| Node.js | 20 LTS+ | |
| npm | 11+ | |
| Angular CLI | 21 | npm i -g @angular/cli |
| buf CLI | 1.x | Required only to regenerate gRPC client after proto changes |
| Lopatnov.Translate | running | Default: localhost:5100 |
Getting Started
git clone https://github.com/lopatnov/translate-angular.git
cd translate-angular
cp .env .env.local # adjust if your gRPC service runs on a different host
npm install
npm startOpen http://localhost:4200 — the Dashboard will show service status if the gRPC service is reachable.
To point at a different gRPC host:
TRANSLATE_GRPC_URL=my-server:5100 npm startnpm Scripts
| Script | Description |
| ------------------- | --------------------------------------------------------------------------- |
| npm start | Dev server with HMR (port 4200) |
| npm run build | Production build → dist/ |
| npm run generate | Regenerate gRPC client from src/protos/translate.proto via buf + ts-proto |
| npm run lint | Biome lint with auto-fix |
| npm run format | Biome format (write) |
| npm run check | Biome check — reports only, for CI |
| npm run e2e | Playwright end-to-end tests (headless) |
| npm run e2e:ui | Playwright with interactive UI |
| npm run debug:ssr | Start built SSR server with Node inspector on port 9229 |
Architecture
Request flow
Browser
└─► ng serve (4200)
└─► Express (server.ts)
└─► Router (server/routes.ts)
├─ GET /api/capabilities ─► getCapabilities()
├─ POST /api/translate ─► translateText()
├─ POST /api/detect ─► detectLanguage()
├─ POST /api/localize ─► translateLocalization()
└─ POST /api/transcribe ─► transcribeAudio()
└─► @grpc/grpc-js → localhost:5100
(30 s deadline; 120 s for transcribe)gRPC client (generated)
The TypeScript gRPC client is generated from src/protos/translate.proto using buf + ts-proto:
npm run generate # runs: buf generateGenerated output lands in src/server/generated/translate.ts (gitignored — always regenerate locally).
After updating translate.proto, run npm run generate and the TypeScript compiler will surface any breaking changes immediately.
Debugging (VS Code / Cursor)
Three launch configurations are pre-configured in .vscode/launch.json:
| Config | Description |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Open browser (ng serve) | Starts ng serve, opens Chrome at 4200 |
| Debug SSR backend | Builds in dev mode, launches server.mjs with --enable-source-maps. Set breakpoints in server.ts / server/grpc-client.ts. Server runs on port 4000. |
| Debug SSR + Chrome | Compound: SSR debugger + Chrome side by side |
Press F5 → pick a config → breakpoints work in TypeScript source files.
End-to-End Tests
Tests live in e2e/ and run against the ng serve dev server (started automatically by Playwright).
npm run e2e # headless, all browsers
npm run e2e:ui # Playwright UI — interactive trace viewer| File | Coverage |
| -------------------- | --------------------------------------------------------- |
| example.spec.ts | Navigation: sidebar links, routing for all 5 pages |
| dashboard.spec.ts | Heading, loading/capabilities/error states, gRPC URL hint |
| translate.spec.ts | Form controls, model select, button enable/disable, Clear |
| detect.spec.ts | Form controls, button enable/disable, Clear |
| localize.spec.ts | JSON textarea, model select, Upload JSON, details panel |
| transcribe.spec.ts | WAV file input, accept attribute, language select, Clear |
Tests do not require the gRPC service to be running — they validate UI structure and form behavior only.
Environment Variables
| Variable | Default | Description |
| ----------------------- | ---------------- | -------------------------------------------------------- |
| TRANSLATE_GRPC_URL | localhost:5100 | gRPC service address |
| PORT | 4000 | SSR server port (production / debug mode only) |
| TRANSCRIBE_DEADLINE_MS| 120000 | gRPC deadline for TranscribeAudio calls (milliseconds) |
Project Structure
src/
├── protos/
│ └── translate.proto # Source of truth for gRPC contract
├── server/
│ ├── generated/
│ │ └── translate.ts # ← generated by buf (gitignored)
│ ├── grpc-client.ts # Singleton gRPC client, Promise API
│ └── routes.ts # Express Router — /api/* → gRPC
├── server.ts # Express entry: middleware, SSR handler
├── shared/
│ └── api.types.ts # Request/response interfaces (shared by server + client)
└── app/
├── core/
│ ├── interceptors/
│ │ └── error.interceptor.ts # HTTP retry on 502/503/504
│ ├── services/
│ │ ├── app-error.service.ts # Global error signal
│ │ ├── capabilities.service.ts # Singleton: GetCapabilities as signals
│ │ └── translate-api.service.ts # HttpClient → /api/*
│ └── utils/
│ ├── api-error.util.ts # Extract message from HttpErrorResponse
│ ├── lang-format.util.ts # Composable: language_format signal + isNative
│ └── languages.ts # NLLB-200 language list + format options
├── features/
│ ├── dashboard/ # GetCapabilities → service status, model list
│ ├── detect-language/ # DetectLanguage → language + confidence
│ ├── locale-files/ # TranslateLocalization → JSON i18n translation
│ ├── speech-to-text/ # TranscribeAudio → transcript + segments
│ └── text-translation/ # TranslateText → translated text
├── shared/
│ └── components/
│ ├── copy-button/ # Clipboard copy with execCommand fallback
│ ├── credits/ # GitHub + LinkedIn links
│ ├── error-alert/ # Dismissible error banner
│ ├── language-select/ # Searchable datalist input (183 languages)
│ ├── page-header/ # Page title + subtitle
│ └── submit-button/ # Loading-aware submit button
├── app.ts / app.html / app.scss # Shell: sidebar nav + router outlet
├── app.routes.ts # Lazy-loaded feature routes
└── app.config.tsBuilt With
| Layer | Technology |
| --------- | ------------------------------------------------ |
| Framework | Angular 21 (standalone components, signals, SSR) |
| UI | Bootstrap 5, dark theme |
| Server | Express 5 + @angular/ssr |
| gRPC | @grpc/grpc-js + ts-proto generated client |
| Code gen | buf CLI + ts-proto 2.x |
| Linting | Biome 2.x |
| Testing | Playwright 1.x |
Contributing
Contributions are welcome. Please read CONTRIBUTING.md before opening a pull request.
- Bug reports and feature requests → open an issue
- Found it useful? A star on GitHub helps others discover the project
License
Apache 2.0 © 2026 Oleksandr Lopatnov · LinkedIn
