Skip to content

feat(i18n): update @routup/i18n for routup v5#769

Merged
tada5hi merged 3 commits intomasterfrom
746-i18n
Apr 9, 2026
Merged

feat(i18n): update @routup/i18n for routup v5#769
tada5hi merged 3 commits intomasterfrom
746-i18n

Conversation

@tada5hi
Copy link
Copy Markdown
Contributor

@tada5hi tada5hi commented Apr 9, 2026

closes #746

Summary by CodeRabbit

  • Bug Fixes

    • Standardized error message format across request body parsing and validation endpoints.
  • Chores

    • Updated framework dependencies for enhanced compatibility.
    • Refactored internal middleware architecture.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 9, 2026

📝 Walkthrough

Walkthrough

Updates error payload fields from statusMessage to message across body helper modules, and completes routup v5 migration for the i18n package by switching middleware to event-based handlers, converting constants to Symbols, and updating tests to use Fetch API.

Changes

Cohort / File(s) Summary
Body Package Error Field Updates
packages/body/src/helpers/parse-json.ts, parse-url-encoded.ts, read-raw.ts, read-stream.ts, packages/body/src/utils/decompress.ts
Replaced statusMessage with message field in error payloads for HTTP 400, 413, and 415 responses. No control flow changes.
i18n Package Dependencies
packages/i18n/package.json
Updated routup peerDependency from ^4.0.1 to ^5.0.0-beta.3; removed supertest and @types/supertest from devDependencies and upgraded routup devDependency to ^5.0.0-beta.3.
i18n Constants Migration
packages/i18n/src/constants.ts
Converted REQUEST_INSTANCE_SYMBOL and REQUEST_LOCALE_SYMBOL from string literals to Symbol.for(...) equivalents for use with event store.
i18n Implementation Migration
packages/i18n/src/module.ts, src/types.ts, src/use-translator.ts
Switched middleware from coreHandler(req, res, next) to defineCoreHandler(event), updated locale function signature to accept IRoutupEvent, replaced request environment setup with direct event store writes, and updated error handling with explicit status codes.
i18n Test Updates
packages/i18n/test/unit/module.spec.ts
Migrated tests from supertest with createNodeDispatcher to direct router.fetch calls; updated handler callback to use event, assertions to Fetch API shape (status, text()), and removed HeaderName.ACCEPT_LANGUAGE dependency.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Middleware as i18n<br/>Handler
    participant Store as Event<br/>Store
    participant i18nInstance as i18n<br/>Instance

    Note over Client,i18nInstance: Routup v5 Event-Based Flow (New)
    Client->>Middleware: HTTP Request (event)
    Middleware->>Store: Retrieve instance from<br/>event.store[symbol]
    Middleware->>Middleware: Resolve locale<br/>locale(event)
    Middleware->>Store: Write instance & locale to<br/>event.store[symbols]
    Middleware->>Client: Return event.next()
    Client->>i18nInstance: Later: useTranslator(event)<br/>retrieves from event.store
    i18nInstance->>Client: Return translator
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 From statusMessage to message we hop,
And req becomes event with nary a flop!
Symbols in the store, handlers anew,
Routup v5 magic—the i18n flew! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Changes in parse-json.ts, parse-url-encoded.ts, read-raw.ts, read-stream.ts, and decompress.ts (statusMessage→message) are not explicitly covered by issue #746 requirements. Clarify whether error payload changes across multiple files are part of the routup v5 migration or should be addressed in a separate pull request.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(i18n): update @routup/i18n for routup v5' directly and accurately reflects the main objective of the pull request.
Linked Issues check ✅ Passed All coding requirements from issue #746 are met: peer dependency updated [#746], handlers rewritten to event-based signature [#746], useTranslator adapted to event API [#746], tests updated for new API [#746], and symbols migrated [#746].

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 746-i18n

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
packages/i18n/src/module.ts (1)

50-67: Variable shadowing: locales declared twice.

On line 51, const locales = await resolveLocales(instance) shadows the outer locales variable declared on line 12. While this works correctly due to scoping, it can be confusing during maintenance.

♻️ Suggested rename for clarity
-            router.use(defineCoreHandler(async (event) => {
-                const locales = await resolveLocales(instance);
+            router.use(defineCoreHandler(async (event) => {
+                const availableLocales = await resolveLocales(instance);

                 event.store[REQUEST_INSTANCE_SYMBOL] = instance;

                 let reqLocale : string | undefined;
                 if (typeof locale === 'function') {
                     reqLocale = await locale(event);
                 }
                 if (typeof reqLocale === 'undefined') {
                     const acceptLanguage = event.headers.get('accept-language');
-                    reqLocale = negotiateLanguage(acceptLanguage, locales);
+                    reqLocale = negotiateLanguage(acceptLanguage, availableLocales);
                 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/i18n/src/module.ts` around lines 50 - 67, The inner variable name
`locales` inside the router.use handler shadows the outer `locales` variable;
rename the inner binding returned by resolveLocales (e.g., to `resolvedLocales`
or `availableLocales`) to avoid confusion. Update the reference used by
negotiateLanguage(acceptLanguage, ...) to use the new name and keep all other
symbols unchanged (router.use, defineCoreHandler, resolveLocales,
REQUEST_INSTANCE_SYMBOL, REQUEST_LOCALE_SYMBOL, locale).
packages/i18n/test/unit/module.spec.ts (1)

14-108: Tests look good, consider adding edge case coverage.

The tests properly validate the migration to routup v5's event-based API using router.fetch() and defineCoreHandler. The three test cases cover the core functionality well.

For more robust coverage, consider adding tests for edge cases:

  • Empty or missing accept-language header
  • No matching locale in the store (fallback behavior)
  • Locale function returning undefined
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/i18n/test/unit/module.spec.ts` around lines 14 - 108, Add unit tests
covering edge cases: create new it() cases using Router, MemoryStore, i18n,
defineCoreHandler and useTranslator to verify behavior when (1) the
Accept-Language header is missing or empty — assert default/fallback locale
result and 200 response; (2) the store has no matching locale — assert fallback
string/behavior (e.g., key returned or error handling) and response status; and
(3) the provided locale function returns undefined — assert that i18n falls back
to default locale behavior. Reuse router.fetch and createTestRequest patterns
from existing tests and assert response.status and response.text for each edge
case.
packages/i18n/src/utils.ts (1)

6-37: Consider validating quality bounds per RFC 7231.

The q quality value should be between 0 and 1 per HTTP specification. Currently, values outside this range (e.g., q=2 or q=-1) are accepted as-is. This is unlikely to cause issues in practice since malformed headers are rare, but clamping might improve robustness.

♻️ Optional: clamp quality to valid range
         for (const segment of segments.slice(1)) {
             const param = segment.trim();
             if (param.startsWith('q=')) {
                 quality = Number.parseFloat(param.substring(2));
                 if (Number.isNaN(quality)) {
                     quality = 0;
                 }
+                quality = Math.max(0, Math.min(1, quality));
             }
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/i18n/src/utils.ts` around lines 6 - 37, The parseAcceptLanguage
function currently accepts q values outside RFC 7231 bounds; after parsing the q
value in parseAcceptLanguage (function name) and after the NaN check, clamp
quality into the valid range [0,1] (e.g., quality = Math.min(Math.max(quality,
0), 1)) before using or pushing the entry into the entries array (LanguageEntry
objects), so any values like q=2 or q=-1 are constrained to 1 and 0
respectively.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/i18n/src/module.ts`:
- Around line 50-67: The inner variable name `locales` inside the router.use
handler shadows the outer `locales` variable; rename the inner binding returned
by resolveLocales (e.g., to `resolvedLocales` or `availableLocales`) to avoid
confusion. Update the reference used by negotiateLanguage(acceptLanguage, ...)
to use the new name and keep all other symbols unchanged (router.use,
defineCoreHandler, resolveLocales, REQUEST_INSTANCE_SYMBOL,
REQUEST_LOCALE_SYMBOL, locale).

In `@packages/i18n/src/utils.ts`:
- Around line 6-37: The parseAcceptLanguage function currently accepts q values
outside RFC 7231 bounds; after parsing the q value in parseAcceptLanguage
(function name) and after the NaN check, clamp quality into the valid range
[0,1] (e.g., quality = Math.min(Math.max(quality, 0), 1)) before using or
pushing the entry into the entries array (LanguageEntry objects), so any values
like q=2 or q=-1 are constrained to 1 and 0 respectively.

In `@packages/i18n/test/unit/module.spec.ts`:
- Around line 14-108: Add unit tests covering edge cases: create new it() cases
using Router, MemoryStore, i18n, defineCoreHandler and useTranslator to verify
behavior when (1) the Accept-Language header is missing or empty — assert
default/fallback locale result and 200 response; (2) the store has no matching
locale — assert fallback string/behavior (e.g., key returned or error handling)
and response status; and (3) the provided locale function returns undefined —
assert that i18n falls back to default locale behavior. Reuse router.fetch and
createTestRequest patterns from existing tests and assert response.status and
response.text for each edge case.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 42fd513d-2105-44d5-ad7e-1cda62af9817

📥 Commits

Reviewing files that changed from the base of the PR and between bf2f693 and 43c21dc.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • packages/body/src/helpers/parse-json.ts
  • packages/body/src/helpers/parse-url-encoded.ts
  • packages/body/src/helpers/read-raw.ts
  • packages/body/src/helpers/read-stream.ts
  • packages/body/src/utils/decompress.ts
  • packages/i18n/package.json
  • packages/i18n/src/constants.ts
  • packages/i18n/src/module.ts
  • packages/i18n/src/types.ts
  • packages/i18n/src/use-translator.ts
  • packages/i18n/src/utils.ts
  • packages/i18n/test/unit/module.spec.ts

@tada5hi
Copy link
Copy Markdown
Contributor Author

tada5hi commented Apr 9, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 9, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@tada5hi
Copy link
Copy Markdown
Contributor Author

tada5hi commented Apr 9, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 9, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@tada5hi
Copy link
Copy Markdown
Contributor Author

tada5hi commented Apr 9, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 9, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/i18n/test/unit/module.spec.ts (1)

31-31: Consider deduplicating repeated accept-language fixtures.

The long header values are repeated across tests; extracting constants would reduce noise and ease updates.

♻️ Optional cleanup
+const ACCEPT_LANGUAGE_DE = 'de-CH,de-DE;q=0.9,de;q=0.8,en-US;q=0.7,en;q=0.6';
+const ACCEPT_LANGUAGE_EN = 'en-US;q=0.9,en-GB;q=0.8';
...
- let response = await router.fetch(createTestRequest('/', { headers: { 'accept-language': 'de-CH,de-DE;q=0.9,de;q=0.8,en-US;q=0.7,en;q=0.6' } }));
+ let response = await router.fetch(createTestRequest('/', { headers: { 'accept-language': ACCEPT_LANGUAGE_DE } }));
...
- response = await router.fetch(createTestRequest('/', { headers: { 'accept-language': 'en-US;q=0.9,en-GB;q=0.8' } }));
+ response = await router.fetch(createTestRequest('/', { headers: { 'accept-language': ACCEPT_LANGUAGE_EN } }));

Also applies to: 63-63, 94-94

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/i18n/test/unit/module.spec.ts` at line 31, Several tests duplicate
the long accept-language header string; extract these repeated fixtures into
named constants (e.g., ACCEPT_LANGUAGE_DE_CH or ACCEPT_LANGUAGE_DE_DE) and
replace inline usages in the calls to createTestRequest(...) / router.fetch(...)
in module.spec.ts to reduce duplication and simplify updates. Locate the
repeated strings used in the createTestRequest calls (see the invocation with
headers: { 'accept-language': ... }) and define the constants near the top of
the test file (or in a shared test helper), then update the three occurrences to
reference those constants.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/i18n/test/unit/module.spec.ts`:
- Line 31: Several tests duplicate the long accept-language header string;
extract these repeated fixtures into named constants (e.g.,
ACCEPT_LANGUAGE_DE_CH or ACCEPT_LANGUAGE_DE_DE) and replace inline usages in the
calls to createTestRequest(...) / router.fetch(...) in module.spec.ts to reduce
duplication and simplify updates. Locate the repeated strings used in the
createTestRequest calls (see the invocation with headers: { 'accept-language':
... }) and define the constants near the top of the test file (or in a shared
test helper), then update the three occurrences to reference those constants.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ce5e5f31-a64e-4b4e-acc1-7ef737ed275d

📥 Commits

Reviewing files that changed from the base of the PR and between 43c21dc and 18986f3.

📒 Files selected for processing (2)
  • packages/i18n/src/module.ts
  • packages/i18n/test/unit/module.spec.ts

@tada5hi tada5hi merged commit 872de6f into master Apr 9, 2026
5 checks passed
@github-actions github-actions Bot mentioned this pull request Apr 9, 2026
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.

feat: update @routup/i18n for routup v5

1 participant