Skip to content

✨ feat: add suggestions, translation & key endpoints to api-client - #74

Merged
elisiondan merged 7 commits into
mainfrom
LOC-4645_add_suggestions_translation__key_endpoints_to_apiclient
Aug 12, 2026
Merged

✨ feat: add suggestions, translation & key endpoints to api-client#74
elisiondan merged 7 commits into
mainfrom
LOC-4645_add_suggestions_translation__key_endpoints_to_apiclient

Conversation

@elisiondan

@elisiondan elisiondan commented Aug 12, 2026

Copy link
Copy Markdown
Member

Closes LOC-4645.

Wraps four public API capabilities that were missing from @localazy/api-client, plus two pre-existing JsonUtils fixes found while testing.

What's new

api.suggestionstm(), mt(), ai() over /keys/{id}/suggestions/{tm|mt|ai}. ai() is a POST because it spends credits, mirroring the API. The response envelope is mirrored exactly so enabled, errors and empty items stay distinguishable — collapsing them is how a caller ends up reporting "MT is off" when the key simply had no source text.

api.keyssubmitTranslation, setTags, setPriority. The bulk methods use the batch routes and return BooleanResult.

plural() — a plural value spelled once and rendered per endpoint: @-prefixed on import, plain on submit. On import the prefix is the only thing separating a plural from a nested key group, and both readings are well-typed, so the compiler cannot catch the mistake.

Pre-existing fixes (please review separately)

Neither is caused by this feature; both are in 16ae6cd and affect every import:

  1. mergeChunkValues overflowed the call stack — it spread one argument per leaf, so any import approaching CHUNK_LIMIT (99,000) threw RangeError before a request was sent. Large imports were simply broken.
  2. A plural was not atomic when chunked — one leaf per class meant a key's forms could land in different chunks and upload as two files with partial forms.

The second was unreachable because the first crashed first, so fixing either alone would have been incomplete.

Also fixed: the ai.translate @see link pointed at a 404, and keys.deprecate was missing from the reference doc.

Verification

Full gate green — 78 tests. Exercised live against production (project Figma app):

Call Result
suggestions.tm / mt real hits; both code and numeric language forms
keys.submitTranslation singular + plural, round-tripped and read back
keys.setTags / setPriority confirmed in the UI — tags applied to both keys, priority only to the one targeted
import.json with plural() creates a real plural key; unmarked objects still nest
104,001-key payload 2 chunks, all keys preserved, boundary plural intact

Notes

  • Engine names are intentionally open unions (Known… | (string & Record<never, never>)): the active engine set is deployment configuration, so a closed union would go wrong the moment an engine ships server-side.
  • Bulk methods do not auto-chunk at 1000; that is the caller's responsibility and is documented as such.

elisiondan and others added 7 commits August 12, 2026 11:52
The `@see` link on `ai.translate` referenced `/docs/api/ai-translation`, which
returns 404. The published page is `/docs/api/ai-translation-api`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
## Features
- Add `api.suggestions` with `tm()`, `mt()` and `ai()` over
  `/projects/{p}/keys/{id}/suggestions/{tm|mt|ai}`
- `ai()` is a POST because it spends AI credits, mirroring the API
- Accept a locale code or numeric language id for `to` and `from`
- Mirror the response envelope exactly so `enabled`, `errors` and empty
  `items` stay distinguishable from one another
- Type engine names as open unions: the active engine set is deployment
  configuration and can grow without a client release

## Tests
- Cover both language forms, soft errors, the disabled case and the
  preservation of caller-supplied `config.params`

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
## Features
- Add `keys.submitTranslation` for one key in one target language
- Add `keys.setTags` and `keys.setPriority` over the batch routes, both
  returning `BooleanResult` so a caller can see the API answered — note it
  reports the request was processed, not that anything changed
- URL-escape the locale so script-qualified codes such as `zh#Hans` survive
  instead of being truncated at the fragment marker
- Accept the `@`-prefixed plural form the read API returns and strip it, so a
  value taken straight from `files.listKeys()` round-trips safely
- Derive `PluralClass` and `KeyPriority` from single runtime lists

## Bug fixes
- Guard `getIds` against a non-array, and reject a nullish language instead of
  coercing it to the literal string `"null"`

## Tests
- Cover locale escaping, plural prefix stripping, mixed key arrays and the
  rejection paths

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
## Features
- Add `plural()` so a plural value is spelled once and rendered per endpoint:
  `@`-prefixed on import, plain on `keys.submitTranslation`
- Resolve markers before the import payload is chunked, since the chunker
  recurses into every plain object and would otherwise split one
- Leave unmarked objects untouched: without the prefix `{ one, other }` is a
  legitimate nested key group, so the prefix cannot be inferred

## Tests
- Assert the marker never reaches the wire, that unmarked objects stay nested,
  and that every CLDR class is recognised by iterating the shared list

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…rals

Both defects predate the suggestion work and affect every import, not only
plural payloads. They were found while testing the plural import path.

## Bug fixes
- Fold chunk leaves with `reduce` instead of spreading them into `merge`: one
  argument per leaf overflowed the call stack, so any import approaching
  `CHUNK_LIMIT` threw `RangeError` before a request was ever sent
- Treat a plural object as a single leaf, the way `@meta:` keys already were.
  Emitting one leaf per class let a key's forms land in different chunks and
  upload as two files with partial forms

The second defect was unreachable in practice because the first crashed
first, so fixing either alone would have been incomplete.

## Tests
- Cover atomicity, a payload at the chunk limit, and a plural sitting on a
  real chunk boundary

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
## Documentation
- Add reference entries for the suggestion, translation, tag and priority
  methods, each with a runnable example
- Add a Plural keys section covering the per-endpoint spelling, `plural()`,
  and why an unmarked object declares nested keys instead
- State what the types check and what they cannot: excess-property checking
  only applies to object literals
- Backfill the missing `keys.deprecate` entry and correct the AI translation
  link that pointed at a 404

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Generated by `pnpm run main-ts:build`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 96.14% 374 / 389
🔵 Statements 96.03% 388 / 404
🔵 Functions 95.91% 94 / 98
🔵 Branches 80.8% 101 / 125
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/main.ts 0% 0% 0% 0%
src/api/api-client.ts 100% 100% 100% 100%
src/api/methods/api-ai.ts 100% 100% 100% 100%
src/api/methods/api-base.ts 100% 92.3% 100% 100%
src/api/methods/api-import.ts 80% 75% 80% 80% 59-65
src/api/methods/api-keys.ts 67.85% 0% 71.42% 67.85% 53-72
src/api/methods/api-suggestions.ts 100% 100% 100% 100%
src/enums/key-priority.ts 100% 100% 100% 100%
src/enums/plural-class.ts 100% 100% 100% 100%
src/enums/translation-engine.ts 100% 100% 100% 100%
src/types/ai-suggestion.ts 0% 0% 0% 0%
src/types/ai-suggestions-response.ts 0% 0% 0% 0%
src/types/boolean-result.ts 0% 0% 0% 0%
src/types/import-json-request.ts 0% 0% 0% 0%
src/types/key-priority.ts 0% 0% 0% 0%
src/types/key-set-priority-request.ts 0% 0% 0% 0%
src/types/key-set-tags-request.ts 0% 0% 0% 0%
src/types/key-submit-translation-request.ts 0% 0% 0% 0%
src/types/localazy-ai-engine-name.ts 0% 0% 0% 0%
src/types/mt-suggestion.ts 0% 0% 0% 0%
src/types/mt-suggestions-response.ts 0% 0% 0% 0%
src/types/plural-class.ts 0% 0% 0% 0%
src/types/plural-marker.ts 100% 100% 100% 100%
src/types/submit-translation-response.ts 0% 0% 0% 0%
src/types/suggestions-request.ts 0% 0% 0% 0%
src/types/tm-suggestion.ts 0% 0% 0% 0%
src/types/tm-suggestions-response.ts 0% 0% 0% 0%
src/types/translation-engine-name.ts 0% 0% 0% 0%
src/types/translation-value.ts 0% 0% 0% 0%
src/utils/json-utils.ts 100% 90% 100% 100%
src/utils/plural.ts 100% 85.71% 100% 100%
src/utils/translation-value-utils.ts 100% 90.9% 100% 100%
tests/fixtures/full-project/index.ts 100% 100% 100% 100%
tests/fixtures/full-project/suggestionsAi.json 100% 100% 100% 100%
tests/fixtures/full-project/suggestionsMt.json 100% 100% 100% 100%
tests/fixtures/full-project/suggestionsMtDisabled.json 100% 100% 100% 100%
tests/fixtures/full-project/suggestionsTm.json 100% 100% 100% 100%
Generated in workflow #109 for commit 17efacb by the Vitest Coverage Report Action

@elisiondan
elisiondan merged commit 005bb11 into main Aug 12, 2026
3 checks passed
@elisiondan
elisiondan deleted the LOC-4645_add_suggestions_translation__key_endpoints_to_apiclient branch August 12, 2026 09:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants