Skip to content

Commit 32ea51a

Browse files
Copilothotlong
andcommitted
fix: address code review - improve type safety and warning message in I18nServicePlugin
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 219aaf5 commit 32ea51a

3 files changed

Lines changed: 22 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Changed
11+
- **i18n route self-registration** — Moved i18n REST endpoint registration from `RestServer` to
12+
`I18nServicePlugin` (and kernel fallback). The i18n plugin now self-registers `/api/v1/i18n/*`
13+
routes via the `kernel:ready` hook, following the same autonomous plugin pattern used by
14+
`AuthPlugin`, `WorkflowPlugin`, and other service plugins. `RestServer` no longer registers or
15+
manages any i18n endpoints, keeping it strictly a protocol-driven gateway.
16+
- Removed `enableI18n` flag from `RestApiConfig` schema (`rest-server.zod.ts`) — i18n endpoints
17+
are now controlled by the i18n service plugin's own `registerRoutes` option (default: `true`).
18+
- Removed `registerI18nEndpoints()` method from `RestServer` class.
19+
- `I18nServicePlugin` now accepts `registerRoutes` and `basePath` options for HTTP route control.
20+
- i18n endpoints now work independently of `RestServer`, enabling MSW/mock test environments
21+
to serve i18n routes without any REST API gateway dependency.
22+
1023
### Added
1124
- **i18n as core built-in service** — The i18n service is now a `core` criticality service with
1225
automatic in-memory fallback. When no plugin (e.g. `I18nServicePlugin`) registers an i18n service,
1326
the kernel auto-injects `createMemoryI18n` (in-memory Map-backed II18nService implementation)
1427
during `validateSystemRequirements()`. This ensures `/api/v1/i18n/*` routes and discovery always
1528
report i18n as available, even without `plugin-i18n` installed.
16-
- **REST i18n route auto-registration**`RestServer.registerRoutes()` now automatically registers
17-
`/api/v1/i18n/locales` and `/api/v1/i18n/translations/:locale` endpoints, controlled by the new
18-
`enableI18n` config flag (default: `true`).
1929
- `createMemoryI18n` fallback factory in `@objectstack/core` (packages/core/src/fallbacks/memory-i18n.ts)
2030
implementing `II18nService` contract with translation loading, dot-notation key resolution, parameter
2131
interpolation, and locale management.
22-
- `enableI18n` flag in `RestApiConfig` schema (`rest-server.zod.ts`) for toggling i18n API endpoints.
2332

2433
### Changed
2534
- `ServiceRequirementDef.i18n` upgraded from `'optional'` to `'core'` — kernel now warns (instead

ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,7 @@ Final polish and advanced features.
842842
| 19 | Automation Service | `IAutomationService` || `@objectstack/service-automation` | DAG engine + HTTP API CRUD + Client SDK + typed returns (67 tests) |
843843
| 20 | Workflow Service | `IWorkflowService` || `@objectstack/service-workflow` (planned) | Spec only |
844844
| 21 | GraphQL Service | `IGraphQLService` || `@objectstack/service-graphql` (planned) | Spec only |
845-
| 22 | i18n Service | `II18nService` || `@objectstack/service-i18n` | File-based locale loading |
845+
| 22 | i18n Service | `II18nService` || `@objectstack/service-i18n` | File-based locale loading, self-registered REST routes |
846846
| 23 | UI Service | `IUIService` | ⚠️ || **Deprecated** — merged into `IMetadataService` |
847847
| 24 | Schema Driver | `ISchemaDriver` ||| Spec only |
848848
| 25 | Startup Orchestrator | `IStartupOrchestrator` ||| Kernel handles basics |

packages/services/service-i18n/src/i18n-service-plugin.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export class I18nServicePlugin implements Plugin {
8181
this.i18n = new FileI18nAdapter(adapterOptions);
8282
ctx.registerService('i18n', this.i18n);
8383
ctx.logger.info(
84-
`I18nServicePlugin: registered file-based i18n adapter (default: ${this.i18n.getDefaultLocale!()})`,
84+
`I18nServicePlugin: registered file-based i18n adapter (default: ${this.i18n.getDefaultLocale?.() ?? 'en'})`,
8585
);
8686
}
8787

@@ -105,7 +105,7 @@ export class I18nServicePlugin implements Plugin {
105105
} else {
106106
ctx.logger.warn(
107107
'No HTTP server available — i18n routes not registered. ' +
108-
'i18n service is still available for MSW/mock environments via HttpDispatcher.'
108+
'i18n service is still available programmatically via kernel.getService("i18n").'
109109
);
110110
}
111111
});
@@ -169,8 +169,12 @@ export class I18nServicePlugin implements Plugin {
169169
res.status(400).json({ error: 'Missing object or locale parameter' });
170170
return;
171171
}
172-
if (typeof (i18n as any).getFieldLabels === 'function') {
173-
const labels = (i18n as any).getFieldLabels(objectName, locale);
172+
// Some implementations may provide a dedicated getFieldLabels method
173+
const hasGetFieldLabels = 'getFieldLabels' in i18n
174+
&& typeof (i18n as Record<string, unknown>)['getFieldLabels'] === 'function';
175+
if (hasGetFieldLabels) {
176+
const labels = (i18n as II18nService & { getFieldLabels(obj: string, loc: string): Record<string, string> })
177+
.getFieldLabels(objectName, locale);
174178
res.json({ data: { object: objectName, locale, labels } });
175179
} else {
176180
// Fallback: derive field labels from full translation bundle

0 commit comments

Comments
 (0)