{"id":50719237,"url":"https://github.com/lopatnov/translate-angular","last_synced_at":"2026-06-09T22:01:50.988Z","repository":{"id":355700390,"uuid":"1219145980","full_name":"lopatnov/translate-angular","owner":"lopatnov","description":"Angular client for the Translate gRPC API","archived":false,"fork":false,"pushed_at":"2026-05-20T10:52:43.000Z","size":679,"stargazers_count":0,"open_issues_count":7,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-20T14:45:28.389Z","etag":null,"topics":["angular","biomejs","bootstrap","express","grpc","nlp","ssr","stt","testing-ui","translate"],"latest_commit_sha":null,"homepage":"https://lopatnov.github.io/translate-angular/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lopatnov.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-23T15:18:28.000Z","updated_at":"2026-05-12T18:46:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/lopatnov/translate-angular","commit_stats":null,"previous_names":["lopatnov/translate-angular"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/lopatnov/translate-angular","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lopatnov%2Ftranslate-angular","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lopatnov%2Ftranslate-angular/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lopatnov%2Ftranslate-angular/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lopatnov%2Ftranslate-angular/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lopatnov","download_url":"https://codeload.github.com/lopatnov/translate-angular/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lopatnov%2Ftranslate-angular/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34127345,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","biomejs","bootstrap","express","grpc","nlp","ssr","stt","testing-ui","translate"],"created_at":"2026-06-09T22:01:49.310Z","updated_at":"2026-06-09T22:01:50.983Z","avatar_url":"https://github.com/lopatnov.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Translate Studio\n\n\u003e Manual testing UI for the [Lopatnov.Translate](https://github.com/lopatnov/translate) gRPC service. **Angular 21 · SSR · Express · Bootstrap 5.**\n\n[![CI](https://github.com/lopatnov/translate-angular/actions/workflows/ci.yml/badge.svg)](https://github.com/lopatnov/translate-angular/actions/workflows/ci.yml)\n[![npm](https://img.shields.io/npm/v/@lopatnov/translate-angular)](https://www.npmjs.com/package/@lopatnov/translate-angular)\n[![Docker](https://ghcr-badge.egpl.dev/lopatnov/translate-angular/latest_tag?label=ghcr.io)](https://github.com/lopatnov/translate-angular/pkgs/container/translate-angular)\n[![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)\n[![GitHub issues](https://img.shields.io/github/issues/lopatnov/translate-angular)](https://github.com/lopatnov/translate-angular/issues)\n[![GitHub stars](https://img.shields.io/github/stars/lopatnov/translate-angular?style=social)](https://github.com/lopatnov/translate-angular/stargazers)\n\nExercises every gRPC endpoint of Lopatnov.Translate through a clean web interface — no `grpcurl` commands needed.\n\n```txt\nBrowser → Angular (port 4000) → Express SSR → @grpc/grpc-js → gRPC service (port 5100)\n```\n\n\u003e **Requires** the [Lopatnov.Translate](https://github.com/lopatnov/translate) gRPC service running on `localhost:5100` (or set `TRANSLATE_GRPC_URL`).\n\n---\n\n## Table of Contents\n\n- [Pages](#pages)\n- [Getting Started](#getting-started)\n  - [Docker](#docker)\n  - [npm](#npm)\n  - [Development](#development)\n- [Environment Variables](#environment-variables)\n- [npm Scripts](#npm-scripts)\n- [Architecture](#architecture)\n- [Debugging](#debugging-vs-code--cursor)\n- [End-to-End Tests](#end-to-end-tests)\n- [Project Structure](#project-structure)\n- [Built With](#built-with)\n- [Contributing](#contributing)\n- [License](#license)\n\n---\n\n## Pages\n\n| Route         | Purpose                           | gRPC RPC                |\n| ------------- | --------------------------------- | ----------------------- |\n| `/`           | Service status, available models  | `GetCapabilities`       |\n| `/translate`  | Text translation with auto-detect | `TranslateText`         |\n| `/detect`     | Language detection + confidence   | `DetectLanguage`        |\n| `/localize`   | JSON i18n file translation        | `TranslateLocalization` |\n| `/transcribe` | WAV → transcript + segments       | `TranscribeAudio`       |\n\n---\n\n## Getting Started\n\n### Docker\n\nThe quickest way — no Node.js required.\n\n```bash\ndocker run -p 4000:4000 \\\n  -e TRANSLATE_GRPC_URL=host.docker.internal:5100 \\\n  ghcr.io/lopatnov/translate-angular:latest\n```\n\nOpen **http://localhost:4000**.\n\nWith Docker Compose alongside the gRPC service:\n\n```yaml\nservices:\n  translate-studio:\n    image: ghcr.io/lopatnov/translate-angular:latest\n    ports:\n      - \"4000:4000\"\n    environment:\n      TRANSLATE_GRPC_URL: translate:5100\n    depends_on:\n      - translate\n\n  translate:\n    image: ghcr.io/lopatnov/translate:latest\n    ports:\n      - \"5100:5100\"\n```\n\n### npm\n\nInstall globally and run as a CLI:\n\n```bash\nnpm install -g @lopatnov/translate-angular\nTRANSLATE_GRPC_URL=localhost:5100 translate-studio\n```\n\nOpen **http://localhost:4000**.\n\n### Development\n\nClone and run the dev server with hot module replacement:\n\n```bash\ngit clone https://github.com/lopatnov/translate-angular.git\ncd translate-angular\nnpm install\nnpm start          # dev server on port 4200\n```\n\nTo point at a different gRPC host:\n\n```bash\nTRANSLATE_GRPC_URL=my-server:5100 npm start\n```\n\n---\n\n## Environment Variables\n\n| Variable                | Default          | Description                                                        |\n| ----------------------- | ---------------- | ------------------------------------------------------------------ |\n| `TRANSLATE_GRPC_URL`    | `localhost:5100` | gRPC service address                                               |\n| `PORT`                  | `4000`           | HTTP server port                                                   |\n| `TRANSLATE_TIMEOUT_MS`  | _(none)_         | Deadline for TranslateText (ms). Unset = no deadline               |\n| `LOCALIZE_TIMEOUT_MS`   | _(none)_         | Deadline for TranslateLocalization (ms). Unset = no deadline       |\n| `TRANSCRIBE_TIMEOUT_MS` | _(none)_         | Deadline for TranscribeAudio / TranslateAudio. Unset = no deadline |\n\n---\n\n## npm Scripts\n\n| Script              | Description                                                                 |\n| ------------------- | --------------------------------------------------------------------------- |\n| `npm start`         | Dev server with HMR (port 4200)                                             |\n| `npm run build`     | Production build → `dist/`                                                  |\n| `npm run generate`  | Regenerate gRPC client from `src/protos/translate.proto` via buf + ts-proto |\n| `npm run lint`      | Biome lint with auto-fix                                                    |\n| `npm run format`    | Biome format (write)                                                        |\n| `npm run check`     | Biome check — reports only, for CI                                          |\n| `npm run e2e`       | Playwright end-to-end tests (headless)                                      |\n| `npm run e2e:ui`    | Playwright with interactive UI                                              |\n| `npm run debug:ssr` | Start built SSR server with Node inspector on port 9229                     |\n\n---\n\n## Architecture\n\n### Request flow\n\n```txt\nBrowser\n  └─► Express SSR (4000)\n        └─► Router (server/routes.ts)\n              ├─ GET /api/capabilities    ─► getCapabilities()        (30 s)\n              ├─ POST /api/translate      ─► translateText()          (TRANSLATE_TIMEOUT_MS)\n              ├─ POST /api/detect         ─► detectLanguage()         (30 s)\n              ├─ POST /api/localize       ─► translateLocalization()  (LOCALIZE_TIMEOUT_MS)\n              ├─ POST /api/transcribe     ─► transcribeAudio()        (TRANSCRIBE_TIMEOUT_MS)\n              ├─ POST /api/translate-audio ─► translateAudio()        (TRANSCRIBE_TIMEOUT_MS)\n              └─ POST /api/synthesize     ─► synthesizeSpeech()       (30 s)\n                                                └─► @grpc/grpc-js → localhost:5100\n```\n\n### gRPC client (generated)\n\nThe TypeScript gRPC client is generated from `src/protos/translate.proto` using [buf](https://buf.build) + [ts-proto](https://github.com/stephenh/ts-proto):\n\n```bash\nnpm run generate        # runs: buf generate\n```\n\nAfter updating `translate.proto`, run `npm run generate` and the TypeScript compiler will surface any breaking changes immediately.\n\n---\n\n## Debugging (VS Code / Cursor)\n\nThree launch configurations are pre-configured in `.vscode/launch.json`:\n\n| Config                      | Description                                                                                                                                                    |\n| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Open browser (ng serve)** | Starts `ng serve`, opens Chrome at 4200                                                                                                                        |\n| **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**. |\n| **Debug SSR + Chrome**      | Compound: SSR debugger + Chrome side by side                                                                                                                   |\n\nPress **F5** → pick a config → breakpoints work in TypeScript source files.\n\n---\n\n## End-to-End Tests\n\nTests live in `e2e/` and run against the `ng serve` dev server (started automatically by Playwright).\n\n```bash\nnpm run e2e           # headless, all browsers\nnpm run e2e:ui        # Playwright UI — interactive trace viewer\n```\n\n| File                 | Coverage                                                  |\n| -------------------- | --------------------------------------------------------- |\n| `example.spec.ts`    | Navigation: sidebar links, routing for all 5 pages        |\n| `dashboard.spec.ts`  | Heading, loading/capabilities/error states, gRPC URL hint |\n| `translate.spec.ts`  | Form controls, model select, button enable/disable, Clear |\n| `detect.spec.ts`     | Form controls, button enable/disable, Clear               |\n| `localize.spec.ts`   | JSON textarea, model select, Upload JSON, details panel   |\n| `transcribe.spec.ts` | WAV file input, accept attribute, language select, Clear  |\n\nTests do **not** require the gRPC service to be running — they validate UI structure and form behavior only.\n\n---\n\n## Project Structure\n\n```txt\nsrc/\n├── protos/\n│   └── translate.proto              # Source of truth for gRPC contract\n├── server/\n│   ├── generated/\n│   │   └── translate.ts             # ← generated by buf\n│   ├── grpc-client.ts               # Singleton gRPC client, Promise API\n│   └── routes.ts                    # Express Router — /api/* → gRPC\n├── server.ts                        # Express entry: middleware, SSR handler\n├── shared/\n│   └── api.types.ts                 # Request/response interfaces (server + client)\n└── app/\n    ├── core/\n    │   ├── interceptors/\n    │   │   └── error.interceptor.ts # HTTP retry on 502/503/504\n    │   ├── services/\n    │   │   ├── app-error.service.ts     # Global error signal\n    │   │   ├── capabilities.service.ts  # Singleton: GetCapabilities as signals\n    │   │   └── translate-api.service.ts # HttpClient → /api/*\n    │   └── utils/\n    │       ├── api-error.util.ts    # Extract message from HttpErrorResponse\n    │       ├── lang-format.util.ts  # Composable: language_format signal + isNative\n    │       └── languages.ts         # NLLB-200 language list + format options\n    ├── features/\n    │   ├── dashboard/               # GetCapabilities → service status, model list\n    │   ├── detect-language/         # DetectLanguage → language + confidence\n    │   ├── locale-files/            # TranslateLocalization → JSON i18n translation\n    │   ├── speech-to-text/          # TranscribeAudio → transcript + segments\n    │   └── text-translation/        # TranslateText → translated text\n    ├── shared/components/\n    │   ├── copy-button/             # Clipboard copy\n    │   ├── credits/                 # GitHub + LinkedIn links\n    │   ├── error-alert/             # Dismissible error banner\n    │   ├── language-select/         # Searchable datalist (183 languages)\n    │   ├── page-header/             # Page title + subtitle\n    │   └── submit-button/           # Loading-aware submit button\n    ├── app.ts / app.html / app.scss # Shell: sidebar nav + router outlet\n    ├── app.routes.ts                # Lazy-loaded feature routes\n    └── app.config.ts\n```\n\n---\n\n## Built With\n\n| Layer     | Technology                                                                                      |\n| --------- | ----------------------------------------------------------------------------------------------- |\n| Framework | [Angular 21](https://angular.dev) (standalone components, signals, SSR)                         |\n| UI        | [Bootstrap 5](https://getbootstrap.com), dark theme                                             |\n| Server    | [Express 5](https://expressjs.com) + `@angular/ssr`                                             |\n| gRPC      | [`@grpc/grpc-js`](https://github.com/grpc/grpc-node) + ts-proto generated client                |\n| Code gen  | [buf CLI](https://buf.build) + [ts-proto](https://github.com/stephenh/ts-proto) 2.x             |\n| Linting   | [Biome](https://biomejs.dev) 2.x                                                                |\n| Testing   | [Playwright](https://playwright.dev) 1.x                                                        |\n| Container | Docker · [GHCR](https://github.com/lopatnov/translate-angular/pkgs/container/translate-angular) |\n\n---\n\n## Contributing\n\nContributions are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request.\n\n- Bug reports and feature requests → [open an issue](https://github.com/lopatnov/translate-angular/issues)\n- Found it useful? A [star on GitHub](https://github.com/lopatnov/translate-angular/stargazers) helps others discover the project\n\n---\n\n## License\n\n[Apache 2.0](LICENSE) © 2026 [Oleksandr Lopatnov](https://github.com/lopatnov) · [LinkedIn](https://www.linkedin.com/in/lopatnov/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flopatnov%2Ftranslate-angular","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flopatnov%2Ftranslate-angular","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flopatnov%2Ftranslate-angular/lists"}