Skip to content

Conversation

@tassoevan
Copy link
Contributor

@tassoevan tassoevan commented Sep 16, 2025

Proposed changes (including videos or screenshots)

It rearranges some maintenance scripts for i18n resources.

Issue(s)

Steps to test or reproduce

Further comments

Summary by CodeRabbit

  • Internationalization

    • Standardized and reformatted many translations (SAML descriptions, placeholders), escaped SMS examples, added new SAML-related keys in some locales, and removed several deprecated locale strings across languages.
  • Tests

    • Switched test runtime loader and updated test scripts for more consistent test execution across environments.
  • Chores

    • Added i18n build/check tooling, broadened TypeScript/ESLint support for modern module types, and tightened TypeScript configs to disallow compiling JavaScript.

@dionisio-bot
Copy link
Contributor

dionisio-bot bot commented Sep 16, 2025

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is targeting the wrong base branch. It should target 7.12.0, but it targets 7.11.0

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot
Copy link

changeset-bot bot commented Sep 16, 2025

⚠️ No Changeset found

Latest commit: 7943027

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 16, 2025

Walkthrough

Replaces ts-node with tsx for Meteor Mocha runs and removes TS_NODE env injections; deletes several Meteor translation CLI scripts; removes allowJs across many tsconfigs; refactors packages/i18n (module type/exports, new scripts and shared helpers, typed normalization, CJS bridge, build changes); updates ESLint/Jest presets and many locale JSON files; adds small testunit scripts.

Changes

Cohort / File(s) Summary
Meteor Mocha config & env
apps/meteor/.mocharc.base.json, apps/meteor/.mocharc.definition.js, apps/meteor/.mocharc.js
Switch Mocha require from ts-node/register to tsx; remove Object.assign injection of TS_NODE_* env vars.
Meteor package scripts & deps
apps/meteor/package.json
Update test scripts to use mocha/jest with new configs; remove translation scripts; add tsx to deps/devDeps; set TS_NODE_COMPILER_OPTIONS for client Jest.
Removed Meteor translation CLIs
apps/meteor/.scripts/*
Delete translation maintenance scripts (replaceTranslationSprintfParams.ts, translation-check.ts, translation-diff.ts, translation-fix-order.ts).
EE apps tsconfig updates
ee/apps/*/tsconfig.json (account-service, authorization-service, ddp-streamer, omnichannel-transcript, presence-service, queue-worker, stream-hub-service)
Remove allowJs: true from multiple EE app tsconfigs.
Small packages test scripts
packages/{base64,random,sha256}/package.json
Add testunit npm script running jest.
Small packages tsconfig updates
packages/{base64,random,sha256}/tsconfig.json
Remove allowJs: true.
ESLint resolver & TS extensions
packages/eslint-config/standard/index.js
Add .cts and .mts to resolver extensions and override file globs.
Jest presets & TS config
packages/jest-presets/package.json, packages/jest-presets/src/client/index.ts, packages/jest-presets/tsconfig.json
Add "type":"module" and exports map; change setupFilesAfterEnv to omit .js extension; adjust TS target/module resolution.
i18n package packaging & TS configs
packages/i18n/package.json, packages/i18n/tsconfig.json, packages/i18n/tsconfig.build.json (removed: tsconfig.cjs.json, tsconfig.esm.json)
Switch to ESM type/exports map; add build tsconfig; remove legacy cjs/esm tsconfigs; tighten compilerOptions (node18/nodenext-style, allowJs:false).
i18n entrypoints & types
packages/i18n/src/index.ts, packages/i18n/src/index.cts
Adjust i18next typing and applyCustomTranslations param type; add index.cts CommonJS re-export bridge.
i18n tooling: new & updated scripts
packages/i18n/src/scripts/{common.mts,build.mts,check.mts,normalize.mts,replace-sprintf.mts}, packages/i18n/src/scripts/normalize.spec.ts
Add shared common.mts helpers; new check.mts task runner; typed normalize.mts; replace-sprintf.mts CLI; rewrite build.mts output and error handling; update spec import to .mts.
i18n locales
packages/i18n/src/locales/* (many files)
Remove various keys across locales (e.g., Delete_Room_Warning, Delete_roomType_description, many Offline_* entries), standardize/replace placeholders (%s{{name}}, legacy __name__{{name}}), normalize SAML/SMS description formatting (backticks/asterisks), add SAML_Custom_* keys in some locales, and numerous locale-specific placeholder/format tweaks.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Dev
  participant CLI as replace-sprintf.mts
  participant Common as scripts/common.mts
  participant FS as FileSystem

  Dev->>CLI: node replace-sprintf.mts <translationKey>
  CLI->>Common: getResourceLanguages()
  Common-->>CLI: [languages]
  CLI->>Common: readResource(baseLang)
  Common-->>CLI: base JSON (validate key & tokenCount)
  CLI->>Dev: Prompt for param names (if %s present)
  loop For each language
    CLI->>Common: readResource(lang)
    Common-->>CLI: lang JSON
    CLI->>FS: writeResource(lang, updated JSON with {{name}} substitutions)
  end
  CLI-->>Dev: Log completion or exit non-zero on error
Loading
sequenceDiagram
  autonumber
  actor Dev
  participant Check as check.mts
  participant Tasks as Task Registry
  participant Common as scripts/common.mts
  participant FS as FileSystem

  Dev->>Check: node check.mts [--fix] [--task ...]
  Check->>Tasks: resolve selected tasks
  loop For each task
    Check->>Common: getResourceLanguages()
    Common-->>Check: [languages]
    loop For each language
      Check->>Common: readResource(lang)
      Common-->>Check: resource JSON
      alt --fix
        Check->>FS: writeResource(lang, fixed JSON)
      else lint-only
        Check-->>Dev: emit lint findings
      end
    end
  end
  Check-->>Dev: exit success or non-zero with aggregated error count
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested labels

stat: ready to merge, stat: QA assured

Suggested reviewers

  • MartinSchoeler
  • dougfabris

Poem

I hop through keys and scripts so bright,
Swapping runners in the moonlit night. 🥕
I tidy locales, types, and build,
Shared helpers planted, checks fulfilled.
Tests now run, and i18n's trimmed right.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The title references modifying i18n build scripts but does not capture the broader scope of changes, which include new check and normalize scripts, mass updates to locale files, removal of legacy tooling scripts, and updates to test and TS configurations. While it correctly identifies a component of the PR, it is too narrow to convey the primary impact to teammates reviewing history. Consequently, it fails to summarize the main change effectively. Consider revising the title to reflect the full scope of the refactor, for example “chore(i18n): overhaul resource scripts and apply locale updates,” so it clearly signals both the tooling and translation file changes.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/i18n-scripts

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.

@tassoevan tassoevan force-pushed the chore/i18n-scripts branch 2 times, most recently from 84a67aa to 97490c5 Compare September 17, 2025 05:45
@codecov
Copy link

codecov bot commented Sep 17, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 67.40%. Comparing base (dca0b3a) to head (7943027).
⚠️ Report is 21 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #36960      +/-   ##
===========================================
+ Coverage    67.38%   67.40%   +0.01%     
===========================================
  Files         3328     3334       +6     
  Lines       113342   113509     +167     
  Branches     20562    20612      +50     
===========================================
+ Hits         76375    76507     +132     
+ Misses       34363    34322      -41     
- Partials      2604     2680      +76     
Flag Coverage Δ
e2e 57.27% <ø> (-0.01%) ⬇️
unit 71.13% <ø> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@tassoevan tassoevan force-pushed the chore/i18n-scripts branch 14 times, most recently from 0bceb66 to b5c47a0 Compare September 23, 2025 21:20
@tassoevan tassoevan force-pushed the chore/i18n-scripts branch 7 times, most recently from 20a04f8 to 6537be9 Compare September 26, 2025 18:02
@tassoevan tassoevan requested a review from a team September 26, 2025 18:26
@tassoevan tassoevan added this to the 7.12.0 milestone Sep 26, 2025
@tassoevan tassoevan marked this pull request as ready for review September 26, 2025 18:27
Copy link
Contributor

@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.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/i18n/src/locales/nb.i18n.json (1)

4469-4527: Restore SAML placeholders to canonical tokens and code formatting

The SAML template strings still use Italic _ formatting and the translated __utsteder__ token. These break the documented variable names and must stay as the canonical English placeholders surrounded by backticks for inline code. Please revert the localized token and apply the backtick formatting to every placeholder in these entries.

-  "SAML_AuthnRequest_Template_Description": "Følgende variabler er tilgjengelige:  \n- *\\_\\_newId\\_\\_*: Tilfeldig generert id-streng  \n- *\\_\\_instant\\_\\_*: Gjeldende tidsstempel  \n- *\\_\\_callbackUrl\\_\\_*: Rocket.Chats tilbakeringings-URL.  \n- *\\_\\_entryPoint\\_\\_*: Verdien for *Custom Entry Point*-innstillingen.  \n- **\\_\\_utsteder\\_\\_**: Verdien for *Custom Issuer*-innstillingen.  \n- *\\_\\_identifierFormatTag\\_\\_*: Innholdet i *NameID Policy Template* hvis en gyldig *Identifier Format* er konfigurert.  \n- *\\_\\_identifierFormat\\_\\_*: Verdien for *Identifier Format*-innstillingen.  \n- *\\_\\_authnContextTag\\_\\_*: Innholdet i *AuthnContext Template* hvis en gyldig *Custom Authn Context* er konfigurert.  \n- *\\_\\_authnContextComparison\\_\\_*: Verdien for *Authn Context Comparison*-innstillingen.  \n- *\\_\\_authnContext\\_\\_*: Verdien til *Custom Authn Context*-innstillingen.",
+  "SAML_AuthnRequest_Template_Description": "Følgende variabler er tilgjengelige:  \n- `__newId__`: Tilfeldig generert id-streng  \n- `__instant__`: Gjeldende tidsstempel  \n- `__callbackUrl__`: Rocket.Chats tilbakeringings-URL.  \n- `__entryPoint__`: Verdien for *Custom Entry Point*-innstillingen.  \n- `__issuer__`: Verdien for *Custom Issuer*-innstillingen.  \n- `__identifierFormatTag__`: Innholdet i *NameID Policy Template* hvis en gyldig *Identifier Format* er konfigurert.  \n- `__identifierFormat__`: Verdien for *Identifier Format*-innstillingen.  \n- `__authnContextTag__`: Innholdet i *AuthnContext Template* hvis en gyldig *Custom Authn Context* er konfigurert.  \n- `__authnContextComparison__`: Verdien for *Authn Context Comparison*-innstillingen.  \n- `__authnContext__`: Verdien til *Custom Authn Context*-innstillingen.",
-  "SAML_LogoutRequest_Template_Description": "Følgende variabler er tilgjengelige:  \n- *\\_\\_newId\\_\\_*: Tilfeldig generert id-streng  \n- *\\_\\_instant\\_\\_*: Gjeldende tidsstempel  \n- *\\_\\_idpSLORedirectURL\\_\\_*: IDP Single LogOut URL det skal omdirigeres til.  \n- **\\_\\_utsteder\\_\\_**: Verdien for *Custom Issuer*-innstillingen.  \n- *\\_\\_identifierFormat\\_\\_*: Verdien for *Identifier Format*-innstillingen.  \n- *\\_\\_nameID\\_\\_*: NameID mottatt fra IdP når brukeren logget på.  \n- *\\_\\_sessionIndex\\_\\_*: SessionIndex mottatt fra IdP når brukeren logget på.",
+  "SAML_LogoutRequest_Template_Description": "Følgende variabler er tilgjengelige:  \n- `__newId__`: Tilfeldig generert id-streng  \n- `__instant__`: Gjeldende tidsstempel  \n- `__idpSLORedirectURL__`: IDP Single LogOut URL det skal omdirigeres til.  \n- `__issuer__`: Verdien for *Custom Issuer*-innstillingen.  \n- `__identifierFormat__`: Verdien for *Identifier Format*-innstillingen.  \n- `__nameID__`: NameID mottatt fra IdP når brukeren logget på.  \n- `__sessionIndex__`: SessionIndex mottatt fra IdP når brukeren logget på.",
-  "SAML_LogoutResponse_Template_Description": "Følgende variabler er tilgjengelige:  \n- *\\_\\_newId\\_\\_*: Tilfeldig generert id-streng  \n- *\\_\\_inResponseToId\\_\\_*: IDen til utloggingsforespørselen mottatt fra IdP  \n- *\\_\\_instant\\_\\_*: Gjeldende tidsstempel  \n- *\\_\\_idpSLORedirectURL\\_\\_*: IDP Single LogOut URL det skal omdirigeres til.  \n- **\\_\\_utsteder\\_\\_**: Verdien for *Custom Issuer*-innstillingen.  \n- *\\_\\_identifierFormat\\_\\_*: Verdien for *Identifier Format*-innstillingen.  \n- *\\_\\_nameID\\_\\_*: NameID mottatt fra IdP-utloggingsforespørselen.  \n- *\\_\\_sessionIndex\\_\\_*: SessionIndex mottatt fra IdP utloggingsforespørselen.",
+  "SAML_LogoutResponse_Template_Description": "Følgende variabler er tilgjengelige:  \n- `__newId__`: Tilfeldig generert id-streng  \n- `__inResponseToId__`: IDen til utloggingsforespørselen mottatt fra IdP  \n- `__instant__`: Gjeldende tidsstempel  \n- `__idpSLORedirectURL__`: IDP Single LogOut URL det skal omdirigeres til.  \n- `__issuer__`: Verdien for *Custom Issuer*-innstillingen.  \n- `__identifierFormat__`: Verdien for *Identifier Format*-innstillingen.  \n- `__nameID__`: NameID mottatt fra IdP-utloggingsforespørselen.  \n- `__sessionIndex__`: SessionIndex mottatt fra IdP utloggingsforespørselen.",
-  "SAML_Metadata_Certificate_Template_Description": "Følgende variabler er tilgjengelige:  \n- *\\_\\_certificate\\_\\_*: Det private sertifikatet for påstandskryptering.",
+  "SAML_Metadata_Certificate_Template_Description": "Følgende variabler er tilgjengelige:  \n- `__certificate__`: Det private sertifikatet for påstandskryptering.",
-  "SAML_Metadata_Template_Description": "Følgende variabler er tilgjengelige:  \n- *\\_\\_sloLocation\\_\\_*: Rocket.Chats Single Logout URL.  \n- **\\_\\_utsteder\\_\\_**: Verdien for *Custom Issuer*-innstillingen.  \n- *\\_\\_identifierFormat\\_\\_*: Verdien for *Identifier Format*-innstillingen.  \n- *\\_\\_certificateTag\\_\\_*: Hvis et privat sertifikat er konfigurert, vil dette inkludere *Metadata Certificate Template*, ellers vil det bli ignorert.  \n- *\\_\\_callbackUrl\\_\\_*: Rocket.Chats tilbakeringings-URL.",
+  "SAML_Metadata_Template_Description": "Følgende variabler er tilgjengelige:  \n- `__sloLocation__`: Rocket.Chats Single Logout URL.  \n- `__issuer__`: Verdien for *Custom Issuer*-innstillingen.  \n- `__identifierFormat__`: Verdien for *Identifier Format*-innstillingen.  \n- `__certificateTag__`: Hvis et privat sertifikat er konfigurert, vil dette inkludere *Metadata Certificate Template*, ellers vil det bli ignorert.  \n- `__callbackUrl__`: Rocket.Chats tilbakeringings-URL.",
packages/i18n/src/locales/sv.i18n.json (1)

4427-4487: Anpassa plats­hållare till kodstil

Engelska originalet bytte ut Markdown-italics (*__placeholder__*) mot kodstil (`__placeholder__`). Den svenska översättningen lämnades kvar med kursivering, så platshållarna renderas annorlunda och ser inte längre ut som kod. Uppdatera raderna nedan till backticks så att formatet matchar källan.

-  "SAML_AuthnRequest_Template_Description": "Följande variabler är tillgängliga:  \n- *\\_\\_newId\\_\\_*: Slumpmässigt genererad id-sträng  \n- *\\_\\_instant\\_\\_*: Aktuell tidsstämpel  \n- *\\_\\_callbackUrl\\_\\_*: Återanrops-URL:en för Rocket.Chat.  \n- *\\_\\_entryPoint\\_\\_*: Värdet för inställningen *Custom Entry Point*.  \n- *\\_\\_issuer\\_\\_*: Värdet för inställningen *Custom Issuer*.  \n- *\\_\\_identifierFormatTag\\_\\_*: Innehållet i *NameID Policy Template* om ett giltigt *Identifier Format* har konfigurerats.  \n- *\\_\\_identifierFormat\\_\\_*: Värdet för inställningen *Identifier Format*.  \n- *\\_\\_authnContextTag\\_\\_*: Innehållet i *AuthnContext Template* om en giltig *Custom Authn Context* is har konfigurerats.  \n- *\\_\\_authnContextComparison\\_\\_*: Värdet för inställningen *Authn Context Comparison*.  \n- *\\_\\_authnContext\\_\\_*: Värdet för inställningen *Custom Authn Context*.",
+  "SAML_AuthnRequest_Template_Description": "Följande variabler är tillgängliga:  \n- `__newId__`: Slumpmässigt genererad id-sträng  \n- `__instant__`: Aktuell tidsstämpel  \n- `__callbackUrl__`: Återanrops-URL:en för Rocket.Chat.  \n- `__entryPoint__`: Värdet för inställningen *Custom Entry Point*.  \n- `__issuer__`: Värdet för inställningen *Custom Issuer*.  \n- `__identifierFormatTag__`: Innehållet i *NameID Policy Template* om ett giltigt *Identifier Format* har konfigurerats.  \n- `__identifierFormat__`: Värdet för inställningen *Identifier Format*.  \n- `__authnContextTag__`: Innehållet i *AuthnContext Template* om en giltig *Custom Authn Context* har konfigurerats.  \n- `__authnContextComparison__`: Värdet för inställningen *Authn Context Comparison*.  \n- `__authnContext__`: Värdet för inställningen *Custom Authn Context*.",
-  "SAML_LogoutRequest_Template_Description": "Följande variabler är tillgängliga:  \n- *\\_\\_newId\\_\\_*: Slumpmässigt genererad id-sträng  \n- *\\_\\_inResponseToId\\_\\_*: Id:t för utloggningsförfrågningen som mottagits från idP  \n- *\\_\\_instant\\_\\_*: Aktuell tidsstämpel  \n- *\\_\\_idpSLORedirectURL\\_\\_*: identitetsintygarens URL för enkel utloggning som omdirigering ska göras till.  \n- *\\_\\_issuer\\_\\_*: Värdet för inställningen *Custom Issuer*.  \n- *\\_\\_identifierFormat\\_\\_*: Värdet för inställningen *Identifier Format*.  \n- *\\_\\_nameID\\_\\_*: Värdet för NameID som mottogs från identitetsintygaren när användaren loggade in.  \n- *\\_\\_sessionIndex\\_\\_*: Värdet för sessionIndex som mottogs från identitetsintygaren när användaren loggade in.",
+  "SAML_LogoutRequest_Template_Description": "Följande variabler är tillgängliga:  \n- `__newId__`: Slumpmässigt genererad id-sträng  \n- `__inResponseToId__`: Id:t för utloggningsförfrågningen som mottagits från idP  \n- `__instant__`: Aktuell tidsstämpel  \n- `__idpSLORedirectURL__`: identitetsintygarens URL för enkel utloggning som omdirigering ska göras till.  \n- `__issuer__`: Värdet för inställningen *Custom Issuer*.  \n- `__identifierFormat__`: Värdet för inställningen *Identifier Format*.  \n- `__nameID__`: Värdet för NameID som mottogs från identitetsintygaren när användaren loggade in.  \n- `__sessionIndex__`: Värdet för sessionIndex som mottogs från identitetsintygaren när användaren loggade in.",
-  "SAML_LogoutResponse_Template_Description": "Följande variabler är tillgängliga:  \n- *\\_\\_newId\\_\\_*: Slumpmässigt genererad id-sträng  \n- *\\_\\_inResponseToId\\_\\_*: Id:t för utloggningsförfrågningen som mottagits från idP  \n- *\\_\\_instant\\_\\_*: Aktuell tidsstämpel  \n- *\\_\\_idpSLORedirectURL\\_\\_*: IdP URL för Single LogOut som omdirigering ska göras till.  \n- *\\_\\_issuer\\_\\_*: Värdet för inställningen *Custom Issuer*.  \n- *\\_\\_identifierFormat\\_\\_*: Värdet för inställningen *Identifier Format*.  \n- *\\_\\_nameID\\_\\_*: NameID som mottas från idP Logout Request.  \n- *\\_\\_sessionIndex\\_\\_*: sessionsIndex som mottas från idP Logout Request",
+  "SAML_LogoutResponse_Template_Description": "Följande variabler är tillgängliga:  \n- `__newId__`: Slumpmässigt genererad id-sträng  \n- `__inResponseToId__`: Id:t för utloggningsförfrågningen som mottagits från idP  \n- `__instant__`: Aktuell tidsstämpel  \n- `__idpSLORedirectURL__`: IdP URL för Single LogOut som omdirigering ska göras till.  \n- `__issuer__`: Värdet för inställningen *Custom Issuer*.  \n- `__identifierFormat__`: Värdet för inställningen *Identifier Format*.  \n- `__nameID__`: NameID som mottas från idP Logout Request.  \n- `__sessionIndex__`: sessionsIndex som mottas från idP Logout Request",
-  "SAML_Metadata_Certificate_Template_Description": "Följande variabler är tillgängliga:  \n- *\\_\\_certificate\\_\\_*: Det privata certifikatet för verifieringskryptering.",
+  "SAML_Metadata_Certificate_Template_Description": "Följande variabler är tillgängliga:  \n- `__certificate__`: Det privata certifikatet för verifieringskryptering.",
-  "SAML_Metadata_Template_Description": "Följande variabler är tillgängliga:  \n- *\\_\\_sloLocation\\_\\_*: URL för enkel utloggning för Rocket.Chat.  \n- *\\_\\_issuer\\_\\_*: Värdet för inställningen *Custom Issuer*.  \n- *\\_\\_identifierFormat\\_\\_*: Värdet för inställningen *Identifier Format*.  \n- *\\_\\_certificateTag\\_\\_*: Om ett privat certifikat har konfigurerats ska det innehålla *Metadata Certificate Template*, annars ignoreras det.  \n- *\\_\\_callbackUrl\\_\\_*: Återanrops-URL för Rocket.Chat.",
+  "SAML_Metadata_Template_Description": "Följande variabler är tillgängliga:  \n- `__sloLocation__`: URL för enkel utloggning för Rocket.Chat.  \n- `__issuer__`: Värdet för inställningen *Custom Issuer*.  \n- `__identifierFormat__`: Värdet för inställningen *Identifier Format*.  \n- `__certificateTag__`: Om ett privat certifikat har konfigurerats ska det innehålla *Metadata Certificate Template*, annars ignoreras det.  \n- `__callbackUrl__`: Återanrops-URL för Rocket.Chat.",

Glöm inte att uppdatera samtliga platshållare i samma stil.

🧹 Nitpick comments (2)
packages/i18n/src/locales/hu.i18n.json (2)

3443-3443: Use code-style backticks for SAML template tokens (replace italic + escaped underscores).

Current strings wrap variables like newId, instant, etc. with asterisks and escaped underscores (e.g., "\\newId\\"). For consistency and readability across locales and renderers, prefer code-style markers: __newId__, __instant__, etc. This avoids heavy escaping and aligns with the “code-style markers” normalization mentioned elsewhere in the PR.

Please update all SAML template description lists in these entries to use backticks for the token names:

  • Line 3443 (SAML_AuthnContext_Template_Description): __authnContext__
  • Line 3445 (SAML_AuthnRequest_Template_Description): all listed tokens
  • Line 3449 (SAML_Custom_Authn_Context_description): reference to the template tokens
  • Line 3494 (SAML_LogoutRequest_Template_Description): all listed tokens
  • Line 3496 (SAML_LogoutResponse_Template_Description): all listed tokens
  • Line 3498 (SAML_Metadata_Certificate_Template_Description): __certificate__
  • Line 3500 (SAML_Metadata_Template_Description): __sloLocation__, __issuer__, __identifierFormat__, __certificateTag__, __callbackUrl__

Example transformation (apply similarly to the others):

  • from: - \\newId\\: …
  • to: - __newId__: …

Also applies to: 3445-3445, 3449-3449, 3494-3494, 3496-3496, 3498-3498, 3500-3500


3498-3498: Fix terminology and formatting for certificate token.

The metadata includes a public certificate; “személyes tanúsítvány” is misleading. Also switch token to code-style.

Apply:

-  "SAML_Metadata_Certificate_Template_Description": "A következő változók érhetők el:  \n- *\\_\\_certificate\\_\\_*: a személyes tanúsítvány a helyességi feltétel titkosításához.",
+  "SAML_Metadata_Certificate_Template_Description": "A következő változók érhetők el:  \n- `__certificate__`: a tanúsítvány a helyességi feltétel aláírásához/titkosításához."
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between da15f89 and 7943027.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (92)
  • apps/meteor/.mocharc.base.json (1 hunks)
  • apps/meteor/.mocharc.definition.js (0 hunks)
  • apps/meteor/.mocharc.js (0 hunks)
  • apps/meteor/.scripts/replaceTranslationSprintfParams.ts (0 hunks)
  • apps/meteor/.scripts/translation-check.ts (0 hunks)
  • apps/meteor/.scripts/translation-diff.ts (0 hunks)
  • apps/meteor/.scripts/translation-fix-order.ts (0 hunks)
  • apps/meteor/package.json (2 hunks)
  • ee/apps/account-service/tsconfig.json (0 hunks)
  • ee/apps/authorization-service/tsconfig.json (0 hunks)
  • ee/apps/ddp-streamer/tsconfig.json (0 hunks)
  • ee/apps/omnichannel-transcript/tsconfig.json (0 hunks)
  • ee/apps/presence-service/tsconfig.json (0 hunks)
  • ee/apps/queue-worker/tsconfig.json (0 hunks)
  • ee/apps/stream-hub-service/tsconfig.json (0 hunks)
  • packages/base64/package.json (1 hunks)
  • packages/base64/tsconfig.json (0 hunks)
  • packages/eslint-config/standard/index.js (3 hunks)
  • packages/i18n/jest.config.ts (0 hunks)
  • packages/i18n/package.json (1 hunks)
  • packages/i18n/src/index.cts (1 hunks)
  • packages/i18n/src/index.ts (2 hunks)
  • packages/i18n/src/locales/ar.i18n.json (3 hunks)
  • packages/i18n/src/locales/az.i18n.json (0 hunks)
  • packages/i18n/src/locales/bn-BD.i18n.json (1 hunks)
  • packages/i18n/src/locales/ca.i18n.json (2 hunks)
  • packages/i18n/src/locales/cs.i18n.json (2 hunks)
  • packages/i18n/src/locales/da.i18n.json (2 hunks)
  • packages/i18n/src/locales/de-AT.i18n.json (0 hunks)
  • packages/i18n/src/locales/de.i18n.json (4 hunks)
  • packages/i18n/src/locales/el.i18n.json (0 hunks)
  • packages/i18n/src/locales/en.i18n.json (5 hunks)
  • packages/i18n/src/locales/eo.i18n.json (0 hunks)
  • packages/i18n/src/locales/es.i18n.json (4 hunks)
  • packages/i18n/src/locales/fa.i18n.json (1 hunks)
  • packages/i18n/src/locales/fi.i18n.json (3 hunks)
  • packages/i18n/src/locales/fr.i18n.json (3 hunks)
  • packages/i18n/src/locales/he.i18n.json (0 hunks)
  • packages/i18n/src/locales/hi-IN.i18n.json (11 hunks)
  • packages/i18n/src/locales/hr.i18n.json (0 hunks)
  • packages/i18n/src/locales/hu.i18n.json (2 hunks)
  • packages/i18n/src/locales/id.i18n.json (0 hunks)
  • packages/i18n/src/locales/it.i18n.json (1 hunks)
  • packages/i18n/src/locales/ja.i18n.json (3 hunks)
  • packages/i18n/src/locales/ka-GE.i18n.json (5 hunks)
  • packages/i18n/src/locales/km.i18n.json (0 hunks)
  • packages/i18n/src/locales/ko.i18n.json (3 hunks)
  • packages/i18n/src/locales/ku.i18n.json (0 hunks)
  • packages/i18n/src/locales/lo.i18n.json (0 hunks)
  • packages/i18n/src/locales/lt.i18n.json (0 hunks)
  • packages/i18n/src/locales/lv.i18n.json (6 hunks)
  • packages/i18n/src/locales/mn.i18n.json (3 hunks)
  • packages/i18n/src/locales/ms-MY.i18n.json (2 hunks)
  • packages/i18n/src/locales/nb.i18n.json (4 hunks)
  • packages/i18n/src/locales/nl.i18n.json (3 hunks)
  • packages/i18n/src/locales/nn.i18n.json (3 hunks)
  • packages/i18n/src/locales/pl.i18n.json (3 hunks)
  • packages/i18n/src/locales/pt-BR.i18n.json (4 hunks)
  • packages/i18n/src/locales/pt.i18n.json (0 hunks)
  • packages/i18n/src/locales/ro.i18n.json (0 hunks)
  • packages/i18n/src/locales/ru.i18n.json (2 hunks)
  • packages/i18n/src/locales/sk-SK.i18n.json (0 hunks)
  • packages/i18n/src/locales/sl-SI.i18n.json (0 hunks)
  • packages/i18n/src/locales/sq.i18n.json (0 hunks)
  • packages/i18n/src/locales/sr.i18n.json (0 hunks)
  • packages/i18n/src/locales/sv.i18n.json (4 hunks)
  • packages/i18n/src/locales/ta-IN.i18n.json (1 hunks)
  • packages/i18n/src/locales/th-TH.i18n.json (0 hunks)
  • packages/i18n/src/locales/tr.i18n.json (0 hunks)
  • packages/i18n/src/locales/ug.i18n.json (0 hunks)
  • packages/i18n/src/locales/uk.i18n.json (1 hunks)
  • packages/i18n/src/locales/vi-VN.i18n.json (0 hunks)
  • packages/i18n/src/locales/zh-HK.i18n.json (2 hunks)
  • packages/i18n/src/locales/zh-TW.i18n.json (4 hunks)
  • packages/i18n/src/locales/zh.i18n.json (3 hunks)
  • packages/i18n/src/scripts/build.mts (5 hunks)
  • packages/i18n/src/scripts/check.mts (1 hunks)
  • packages/i18n/src/scripts/common.mts (1 hunks)
  • packages/i18n/src/scripts/normalize.mts (3 hunks)
  • packages/i18n/src/scripts/normalize.spec.ts (1 hunks)
  • packages/i18n/src/scripts/replace-sprintf.mts (1 hunks)
  • packages/i18n/tsconfig.build.json (1 hunks)
  • packages/i18n/tsconfig.cjs.json (0 hunks)
  • packages/i18n/tsconfig.esm.json (0 hunks)
  • packages/i18n/tsconfig.json (1 hunks)
  • packages/jest-presets/package.json (1 hunks)
  • packages/jest-presets/src/client/index.ts (1 hunks)
  • packages/jest-presets/tsconfig.json (1 hunks)
  • packages/random/package.json (1 hunks)
  • packages/random/tsconfig.json (0 hunks)
  • packages/sha256/package.json (1 hunks)
  • packages/sha256/tsconfig.json (0 hunks)
💤 Files with no reviewable changes (40)
  • ee/apps/ddp-streamer/tsconfig.json
  • packages/random/tsconfig.json
  • ee/apps/omnichannel-transcript/tsconfig.json
  • ee/apps/authorization-service/tsconfig.json
  • packages/i18n/src/locales/he.i18n.json
  • packages/i18n/src/locales/eo.i18n.json
  • ee/apps/queue-worker/tsconfig.json
  • apps/meteor/.scripts/translation-check.ts
  • packages/i18n/src/locales/ku.i18n.json
  • packages/i18n/src/locales/ro.i18n.json
  • packages/i18n/src/locales/tr.i18n.json
  • packages/i18n/src/locales/pt.i18n.json
  • packages/i18n/src/locales/lt.i18n.json
  • packages/i18n/src/locales/id.i18n.json
  • packages/i18n/jest.config.ts
  • packages/i18n/src/locales/km.i18n.json
  • packages/i18n/src/locales/th-TH.i18n.json
  • ee/apps/stream-hub-service/tsconfig.json
  • packages/i18n/src/locales/sl-SI.i18n.json
  • apps/meteor/.mocharc.definition.js
  • packages/i18n/src/locales/de-AT.i18n.json
  • packages/i18n/src/locales/sq.i18n.json
  • packages/i18n/src/locales/el.i18n.json
  • packages/i18n/src/locales/ug.i18n.json
  • packages/i18n/src/locales/hr.i18n.json
  • packages/i18n/src/locales/az.i18n.json
  • ee/apps/account-service/tsconfig.json
  • packages/i18n/src/locales/lo.i18n.json
  • apps/meteor/.scripts/translation-diff.ts
  • packages/i18n/src/locales/sk-SK.i18n.json
  • packages/sha256/tsconfig.json
  • packages/i18n/src/locales/sr.i18n.json
  • packages/base64/tsconfig.json
  • packages/i18n/tsconfig.esm.json
  • packages/i18n/src/locales/vi-VN.i18n.json
  • apps/meteor/.scripts/translation-fix-order.ts
  • apps/meteor/.scripts/replaceTranslationSprintfParams.ts
  • apps/meteor/.mocharc.js
  • ee/apps/presence-service/tsconfig.json
  • packages/i18n/tsconfig.cjs.json
🚧 Files skipped from review as they are similar to previous changes (22)
  • packages/random/package.json
  • packages/i18n/tsconfig.json
  • packages/base64/package.json
  • packages/i18n/src/locales/ta-IN.i18n.json
  • packages/sha256/package.json
  • packages/eslint-config/standard/index.js
  • packages/i18n/src/locales/bn-BD.i18n.json
  • apps/meteor/.mocharc.base.json
  • packages/i18n/src/scripts/common.mts
  • packages/i18n/src/scripts/replace-sprintf.mts
  • packages/i18n/src/locales/mn.i18n.json
  • packages/i18n/src/scripts/normalize.spec.ts
  • packages/i18n/src/locales/en.i18n.json
  • packages/i18n/src/locales/da.i18n.json
  • packages/i18n/src/locales/ja.i18n.json
  • packages/jest-presets/package.json
  • packages/i18n/src/locales/nl.i18n.json
  • packages/i18n/src/locales/fi.i18n.json
  • apps/meteor/package.json
  • packages/i18n/src/locales/uk.i18n.json
  • packages/jest-presets/src/client/index.ts
  • packages/i18n/src/locales/cs.i18n.json
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
PR: RocketChat/Rocket.Chat#0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-09-16T22:08:51.490Z
Learning: Applies to apps/meteor/tests/e2e/**/*.{ts,tsx,js,jsx} : Write concise, technical TypeScript/JavaScript with accurate typing
Learnt from: CR
PR: RocketChat/Rocket.Chat#0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-09-16T22:08:51.490Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Ensure tests run reliably in parallel without shared state conflicts
Learnt from: CR
PR: RocketChat/Rocket.Chat#0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-09-16T22:08:51.490Z
Learning: Applies to apps/meteor/tests/e2e/**/*.{ts,tsx,js,jsx} : Avoid code comments in the implementation
Learnt from: CR
PR: RocketChat/Rocket.Chat#0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-09-16T22:08:51.490Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Ensure a clean state for each test execution
🧬 Code graph analysis (3)
packages/i18n/src/scripts/check.mts (1)
packages/i18n/src/scripts/common.mts (6)
  • readResource (28-34)
  • baseLanguage (7-7)
  • writeResource (36-39)
  • getResourceLanguages (9-12)
  • getLanguagePlurals (14-22)
  • readContent (24-26)
packages/i18n/src/index.ts (1)
apps/meteor/app/utils/lib/i18n.ts (1)
  • i18n (5-5)
packages/i18n/src/scripts/build.mts (2)
packages/i18n/src/scripts/common.mts (4)
  • resourcesDirectory (42-42)
  • languageFromBasename (46-46)
  • distDirectory (43-43)
  • resourceBasename (45-45)
packages/i18n/src/scripts/normalize.mts (1)
  • normalizeI18nInterpolations (93-107)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
🔇 Additional comments (16)
packages/i18n/src/locales/fa.i18n.json (1)

363-364: Localized placeholder looks good

Switching to the {{price}} placeholder keeps the string consistent with other locales while fixing the old Markdown-style markup. Looks correct.

packages/i18n/src/locales/ar.i18n.json (1)

3180-3180: Restore the missing closing backtick

The placeholder __authnContext__ lacks its terminating backtick, so everything afterward renders as code. Please add the closing ``` to fix the markup.

packages/i18n/src/locales/pt-BR.i18n.json (1)

4395-4398: Fix typo in Authn context description.

Ainda aparece “authn contex” (faltando o “to” e sem concordância) em vez de “contexto de autenticação”. Segue correção sugerida.

-  "SAML_Custom_Authn_Context_description": "Deixe vazio para omitir o authn contex da solicitação.  \n  \nPara adicionar vários authn contexts, inclua os adicionais diretamente à configuração *AuthnContext Template*.",
+  "SAML_Custom_Authn_Context_description": "Deixe vazio para omitir o contexto de autenticação (authn) da solicitação.  \n  \nPara adicionar vários contextos de autenticação, inclua-os diretamente na configuração “AuthnContext Template”.",
packages/i18n/src/locales/nn.i18n.json (1)

4281-4281: Keep placeholders unescaped in translated strings.

The SMS example still renders as \{\{SERVER_URL\}\}; drop the backslashes so the UI shows {{SERVER_URL}}. Same for the department placeholder.

Apply:

-    "Hvis angitt, vil alle nye innkommende chatter initiert av denne integrasjonen bli rutet til denne avdelingen.  \nDenne innstillingen kan overskrives ved å sende avdelingsspørringsparameter i forespørselen.  \nf.eks. `https://\{\{SERVER_URL\}\}/api/v1/livechat/sms-incoming/twilio?department=\{\{Department Id or Name\}\}`.  \nMerk: Hvis du bruker Avdelingsnavn, bør det være URL-sikkert.",
+    "Hvis angitt, vil alle nye innkommende chatter initiert av denne integrasjonen bli rutet til denne avdelingen.  \nDenne innstillingen kan overskrives ved å sende avdelingsspørringsparameter i forespørselen.  \nf.eks. `https://{{SERVER_URL}}/api/v1/livechat/sms-incoming/twilio?department={{Department Id or Name}}`.  \nMerk: Hvis du bruker Avdelingsnavn, bør det være URL-sikkert.",
packages/i18n/src/locales/ko.i18n.json (1)

2782-2782: Remove the extra escaping in the SMS URL example

The sample URL is still rendered with backslashes (https://\{\{SERVER_URL\}\}/…), which is confusing for admins. Please drop the redundant escapes so the placeholder shows up as {{…}}.

-  "SMS_Default_Omnichannel_Department_Description": "설정하면 이 통합으로 시작된 모든 새로운 수신 채팅이 이 부서로 라우팅됩니다.  \n요청에서 부서 쿼리 매개 변수를 전달하여 이 설정을 덮어쓸 수 있습니다.  \n예: `https://\\{\\{SERVER_URL\\}\\}/api/v1/livechat/sms-incoming/twilio?department=\\{\\{Department Id or Name\\}\\}`.  \n참고: 부서 이름을 사용하는 경우 URL에 안전해야 합니다.",
+  "SMS_Default_Omnichannel_Department_Description": "설정하면 이 통합으로 시작된 모든 새로운 수신 채팅이 이 부서로 라우팅됩니다.  \n요청에서 부서 쿼리 매개 변수를 전달하여 이 설정을 덮어쓸 수 있습니다.  \n예: `https://{{SERVER_URL}}/api/v1/livechat/sms-incoming/twilio?department={{Department Id or Name}}`.  \n참고: 부서 이름을 사용하는 경우 URL에 안전해야 합니다.",
packages/i18n/src/locales/de.i18n.json (1)

4488-4488: Restore {{at}} placeholder in Voip_call_started.

The placeholder is still missing, so the UI drops the call timestamp. Please restore {{at}} to keep the dynamic value.

packages/i18n/src/locales/fr.i18n.json (1)

3227-3227: Fix the lingering “identificationt” typo.

The wording still reads “Chaîne d'identificationt générée aléatoirement”; please drop the trailing “t” so the sentence is grammatically correct.

-"SAML_LogoutRequest_Template_Description": "Les variables suivantes sont disponibles :  \n- *__newId__* : Chaîne d'identificationt générée aléatoirement  \n- *__instant__* : Horodatage actuel  \n- *__idpSLORedirectURL__* : URL de déconnexion unique IDP vers laquelle effectuer la redirection.  \n- *__issuer__* : Valeur du paramètre *Custom Issuer*.  \n- *__identifierFormat__* : Valeur du paramètre *Identifier Format*.  \n- *__nameID__* : NameID reçu de l'IdP lorsque l'utilisateur s'est connecté.  \n- *__sessionIndex__* : SessionIndex reçu de l'IdP lorsque l'utilisateur s'est connecté.",
+"SAML_LogoutRequest_Template_Description": "Les variables suivantes sont disponibles :  \n- *__newId__* : Chaîne d'identification générée aléatoirement  \n- *__instant__* : Horodatage actuel  \n- *__idpSLORedirectURL__* : URL de déconnexion unique IDP vers laquelle effectuer la redirection.  \n- *__issuer__* : Valeur du paramètre *Custom Issuer*.  \n- *__identifierFormat__* : Valeur du paramètre *Identifier Format*.  \n- *__nameID__* : NameID reçu de l'IdP lorsque l'utilisateur s'est connecté.  \n- *__sessionIndex__* : SessionIndex reçu de l'IdP lorsque l'utilisateur s'est connecté.",
packages/i18n/src/locales/ru.i18n.json (1)

3312-3314: Remove escaping inside inline code placeholder

Инлайн‑код не обрабатывает Markdown, поэтому \_\_authnContext\_\_ будет показан с обратными слешами. Нужно вернуть __authnContext__, чтобы админы видели корректный плейсхолдер.

packages/i18n/src/locales/pl.i18n.json (1)

3510-3510: Przywróć placeholder __issuer__ i napraw format listy SAML.

Wystrzeliliśmy placeholder __issuer__, Markdown jest popsuty (**\\Wydawca), a wpis dla Identifier Format ma błędne formatowanie. To psuje instrukcję dla administratora i jest niezgodne z resztą lokalizacji. Przywróć poprawną listę z literalnymi placeholderami.

-  "SAML_Metadata_Template_Description": "Dostępne są następujące zmienne:  \n- *\\_\\_sloLocation\\_\\_*:  Rocket.Chat Single LogOut URL.  \n- **\\Wydawca: Wartość ustawienia *Custom Issuer*.  \n- *\\_\\_identifierFormat\\_\\_*: Wartość ustawienia __Identifier Format_.  \n- *\\_\\_certificateTag\\_\\_*: Jeśli skonfigurowany jest certyfikat prywatny, będzie on zawierał *Metadata Certificate Template*, w przeciwnym razie będzie on ignorowany.  \n- *\\_\\_callbackUrl\\_\\_*: Rocket.Chat callback URL.",
+  "SAML_Metadata_Template_Description": "Dostępne są następujące zmienne:  \n- *\\_\\_sloLocation\\_\\_*: Rocket.Chat Single LogOut URL.  \n- *\\_\\_issuer\\_\\_*: Wartość ustawienia *Custom Issuer*.  \n- *\\_\\_identifierFormat\\_\\_*: Wartość ustawienia *Identifier Format*.  \n- *\\_\\_certificateTag\\_\\_*: Jeśli skonfigurowany jest certyfikat prywatny, będzie on zawierał *Metadata Certificate Template*, w przeciwnym razie będzie on ignorowany.  \n- *\\_\\_callbackUrl\\_\\_*: Rocket.Chat callback URL.",
packages/i18n/src/locales/ka-GE.i18n.json (1)

2841-2841: Fix Georgian genitive suffix in “Thread_message”.

Genitive suffix still separated from the username; it should be attached with a hyphen. Please apply the previous suggestion.

-  "Thread_message": "კომენტარი გააკეთა *{{username}}* ის გზავნილზე: _{{msg}}_",
+  "Thread_message": "კომენტარი გააკეთა *{{username}}*-ის გზავნილზე: _{{msg}}_",
packages/i18n/src/locales/lv.i18n.json (1)

2236-2236: Fix spacing around placeholders in user audit strings.

Both translations lost the spaces adjacent to the <em> placeholders, so rendered text now glues words together (e.g., pievienojaJānis). Restore the spaces to keep the Latvian output readable.

-  "User_added_by": "Lietotāju <em>{{user_added}}</em>pievienoja <em>{{user_by}}</em>.",
+  "User_added_by": "Lietotāju <em>{{user_added}}</em> pievienoja <em>{{user_by}}</em>.",-  "User_unmuted_by": "Lietotājam <em>{{user_unmuted}}</em>atļāva rakstīt<em>{{user_by}}</em>.",
+  "User_unmuted_by": "Lietotājam <em>{{user_unmuted}}</em> atļāva rakstīt <em>{{user_by}}</em>.",

Also applies to: 2264-2264

packages/i18n/src/scripts/check.mts (2)

451-456: Reset the %s matcher before each translation

The global sprintfRegex still carries lastIndex across translations, so after the first match subsequent strings are skipped. Please restore the earlier fix (reset sprintfRegex.lastIndex = 0; before each exec).


319-327: Fix placeholder replacement to preserve each captured token

Because the replacement string reuses match[1], every placeholder in the string is rewritten to the same name as the first match (e.g., __foo__ ... __bar__ becomes {{foo}} ... {{foo}}). Swap to a replacer callback so each occurrence keeps its own capture.

-							fixedResource[key] = {
-								...(fixedResource[key] as Record<string, string>),
-								[plural]: translation.replace(placeholderRegex, `{{${match[1]}}}`),
-							};
+							fixedResource[key] = {
+								...(fixedResource[key] as Record<string, string>),
+								[plural]: translation.replace(placeholderRegex, (_, placeholder) => `{{${placeholder}}}`),
+							};
 						} else {
-							fixedResource[key] = translation.replace(placeholderRegex, `{{${match[1]}}}`);
+							fixedResource[key] = translation.replace(placeholderRegex, (_, placeholder) => `{{${placeholder}}}`);
packages/i18n/src/locales/hi-IN.i18n.json (2)

827-828: Queue count strings still read as an instruction

The Hindi copy keeps “कॉल करें”, which means “place a call”, not “calls waiting in queue”. Please rephrase both forms to state the number of queued calls (e.g. “कतार में {{count}} कॉल है/हैं”).


2771-2771: Fix mistranslated MAU label

"MAU_value": "हमेशा {{value}}" renders “Always {{value}}”, breaking the metric label. Restore the MAU wording so the UI shows the intended metric name.

-  "MAU_value": "हमेशा {{value}}",
+  "MAU_value": "MAU {{value}}",
packages/i18n/src/locales/es.i18n.json (1)

3261-3318: Placeholders are still escaped instead of rendered as inline code

The _ characters remain escaped and the values are wrapped in *…*, so the UI still emits the literal \_\_placeholder\_\_ rather than the intended inline code. Please drop the escapes and switch these bullets to proper backtick formatting so the placeholders render as code (same issue flagged previously).

-  "SAML_AuthnContext_Template_Description": "Puedes usar cualquier variable de la plantilla AuthnRequest aquí.  \n  \nPara añadir contextos de autenticación adicionales, duplica la etiqueta `AuthnContextClassRef` y reemplaza la variable `\\_\\_authnContext\\_\\_` con el nuevo contexto.",
+  "SAML_AuthnContext_Template_Description": "Puedes usar cualquier variable de la plantilla AuthnRequest aquí.  \n  \nPara añadir contextos de autenticación adicionales, duplica la etiqueta `AuthnContextClassRef` y reemplaza la variable `__authnContext__` con el nuevo contexto.",
@@
-  "SAML_AuthnRequest_Template_Description": "Las siguientes variables están disponibles:  \n- *\\_\\_newId\\_\\_*: Cadena de identificación generada aleatoriamente  \n- *\\_\\_instant\\_\\_*: Fecha y hora actual  \n- *\\_\\_callbackUrl\\_\\_*: URL de devolución de llamada de Rocket.Chat  \n- *\\_\\_entryPoint\\_\\_*: Valor del ajuste *Custom Entry Point*  \n- *\\_\\_issuer\\_\\_*: Valor del ajuste *Custom Issuer*  \n- *\\_\\_identifierFormatTag\\_\\_*: Contenido de *NameID Policy Template* si un *Identifier Format* válido está configurado  \n- *\\_\\_identifierFormat\\_\\_*: Valor del ajuste *Identifier Format*  \n- *\\_\\_authnContextTag\\_\\_*: Contenido de *AuthnContext Template* si un *Custom Authn Context* válido está configurado  \n- *\\_\\_authnContextComparison\\_\\_*: Valor del ajuste *Authn Context Comparison*  \n- *\\_\\_authnContext\\_\\_*: Valor del ajuste *Custom Authn Context*",
+  "SAML_AuthnRequest_Template_Description": "Las siguientes variables están disponibles:  \n- `__newId__`: Cadena de identificación generada aleatoriamente  \n- `__instant__`: Fecha y hora actual  \n- `__callbackUrl__`: URL de devolución de llamada de Rocket.Chat  \n- `__entryPoint__`: Valor del ajuste *Custom Entry Point*  \n- `__issuer__`: Valor del ajuste *Custom Issuer*  \n- `__identifierFormatTag__`: Contenido de *NameID Policy Template* si un *Identifier Format* válido está configurado  \n- `__identifierFormat__`: Valor del ajuste *Identifier Format*  \n- `__authnContextTag__`: Contenido de *AuthnContext Template* si un *Custom Authn Context* válido está configurado  \n- `__authnContextComparison__`: Valor del ajuste *Authn Context Comparison*  \n- `__authnContext__`: Valor del ajuste *Custom Authn Context*",
@@
-  "SAML_Custom_Authn_Context_description": "Dejar esto vacío para omitir el contexto de autenticación de la solicitud.  \n  \nPara añadir múltiples contextos de autenticación, añade los adicionales directamente al ajuste *AuthnContext Template*.",
+  "SAML_Custom_Authn_Context_description": "Dejar esto vacío para omitir el contexto de autenticación de la solicitud.  \n  \nPara añadir múltiples contextos de autenticación, añade los adicionales directamente al ajuste *AuthnContext Template*.",
@@
-  "SAML_LogoutRequest_Template_Description": "Las siguientes variables están disponibles:  \n- *\\_\\_newId\\_\\_*: Cadena de identificación generada aleatoriamente  \n- *\\_\\_instant\\_\\_*: Fecha y hora actual  \n- *\\_\\_idpSLORedirectURL\\_\\_*: URL de inicio de sesión único de IDP a la que redireccionar  \n- *\\_\\_issuer\\_\\_*: Valor del ajuste *Custom Issuer*  \n- *\\_\\_identifierFormat\\_\\_*: Valor del ajuste *Identifier Format*  \n- *\\_\\_nameID\\_\\_*: NameID recibido desde el IdP cuando el usuario ha iniciado sesión  \n- *\\_\\_sessionIndex\\_\\_*: SessionIndex recibido desde el IdP cuando el usuario ha cerrado sesión",
+  "SAML_LogoutRequest_Template_Description": "Las siguientes variables están disponibles:  \n- `__newId__`: Cadena de identificación generada aleatoriamente  \n- `__instant__`: Fecha y hora actual  \n- `__idpSLORedirectURL__`: URL de inicio de sesión único de IDP a la que redireccionar  \n- `__issuer__`: Valor del ajuste *Custom Issuer*  \n- `__identifierFormat__`: Valor del ajuste *Identifier Format*  \n- `__nameID__`: NameID recibido desde el IdP cuando el usuario ha iniciado sesión  \n- `__sessionIndex__`: SessionIndex recibido desde el IdP cuando el usuario ha cerrado sesión",
@@
-  "SAML_LogoutResponse_Template_Description": "Las siguientes variables están disponibles:  \n- *\\_\\_newId\\_\\_*: Cadena de identificación generada aleatoriamente  \n- *\\_\\_inResponseToId\\_\\_*: ID de la respuesta de cierre de sesión recibida desde el IdP  \n- *\\_\\_instant\\_\\_*: Fecha y hora actual  \n- *\\_\\_idpSLORedirectURL\\_\\_*: URL de inicio de sesión único de IDP a la que redireccionar  \n- *\\_\\_issuer\\_\\_*: Valor del ajuste *Custom Issuer*  \n- *\\_\\_identifierFormat\\_\\_*: Valor del ajuste *Identifier Format*  \n- *\\_\\_nameID\\_\\_*: NameID recibido desde la solicitud de cierre de sesión de IdP  \n- *\\_\\_sessionIndex\\_\\_*: SessionIndex recibido desde la solicitud de cierre de sesión de IdP",
+  "SAML_LogoutResponse_Template_Description": "Las siguientes variables están disponibles:  \n- `__newId__`: Cadena de identificación generada aleatoriamente  \n- `__inResponseToId__`: ID de la respuesta de cierre de sesión recibida desde el IdP  \n- `__instant__`: Fecha y hora actual  \n- `__idpSLORedirectURL__`: URL de inicio de sesión único de IDP a la que redireccionar  \n- `__issuer__`: Valor del ajuste *Custom Issuer*  \n- `__identifierFormat__`: Valor del ajuste *Identifier Format*  \n- `__nameID__`: NameID recibido desde la solicitud de cierre de sesión de IdP  \n- `__sessionIndex__`: SessionIndex recibido desde la solicitud de cierre de sesión de IdP",
@@
-  "SAML_Metadata_Certificate_Template_Description": "Las siguientes variables están disponibles:  \n- *\\_\\_certificate\\_\\_*: Certificado privado para cifrado de aserción",
+  "SAML_Metadata_Certificate_Template_Description": "Las siguientes variables están disponibles:  \n- `__certificate__`: Certificado privado para cifrado de aserción",
@@
-  "SAML_Metadata_Template_Description": "Las siguientes variables están disponibles:  \n- *\\_\\_sloLocation\\_\\_*: URL de cierre de sesión única de Rocket.Chat  \n- *\\_\\_issuer\\_\\_*: Valor del ajuste  *Custom Issuer*  \n- *\\_\\_identifierFormat\\_\\_*: Valor del ajuste  *Identifier Format*  \n- *\\_\\_certificateTag\\_\\_*: Si un certificado privado está configurado, esto incluirá el *Metadata Certificate Template*, de lo contrario se ignorará  \n- *\\_\\_callbackUrl\\_\\_*: URL de respaldo de Rocket.Chat",
+  "SAML_Metadata_Template_Description": "Las siguientes variables están disponibles:  \n- `__sloLocation__`: URL de cierre de sesión única de Rocket.Chat  \n- `__issuer__`: Valor del ajuste *Custom Issuer*  \n- `__identifierFormat__`: Valor del ajuste *Identifier Format*  \n- `__certificateTag__`: Si un certificado privado está configurado, esto incluirá el *Metadata Certificate Template*, de lo contrario se ignorará  \n- `__callbackUrl__`: URL de respaldo de Rocket.Chat",
@@
-  "SMS_Default_Omnichannel_Department_Description": "Si se establece esta opción, todos los nuevos chats entrantes iniciados por esta integración se enrutarán a este departamento.  \nEste ajuste se puede sobrescribir pasando el parámetro de consulta del departamento en la solicitud.  \nPor ejemplo, `https://\\{\\{SERVER_URL\\}\\}/api/v1/livechat/sms-incoming/twilio?department=\\{\\{Department Id or Name\\}\\}`.  \nNota: Si estás usando el nombre del departamento, debería poder usarse en la URL.",
+  "SMS_Default_Omnichannel_Department_Description": "Si se establece esta opción, todos los nuevos chats entrantes iniciados por esta integración se enrutarán a este departamento.  \nEste ajuste se puede sobrescribir pasando el parámetro de consulta del departamento en la solicitud.  \nPor ejemplo, `https://{{SERVER_URL}}/api/v1/livechat/sms-incoming/twilio?department={{Department Id or Name}}`.  \nNota: Si estás usando el nombre del departamento, debería poder usarse en la URL.",

Comment on lines +6 to +22
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./dist/resources": {
"types": "./dist/resources.d.ts",
"import": "./dist/resources.js",
"require": "./dist/resources.cjs"
},
"./dist/languages": {
"types": "./dist/languages.d.ts",
"import": "./dist/languages.js",
"require": "./dist/languages.cjs"
}
},
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Verify exports map doesn’t break existing deep imports

Moving to an explicit exports map narrows the surface area to ".", "./dist/resources", and "./dist/languages". Any existing call sites that reach for other deep paths (e.g., @rocket.chat/i18n/dist/index.js, @rocket.chat/i18n/dist/index.mjs, @rocket.chat/i18n/dist/locales/*) will now throw ERR_PACKAGE_PATH_NOT_EXPORTED. Please scan the repo and confirm we’re not relying on those legacy entry points, or expand the map accordingly.


🏁 Script executed:

#!/bin/bash
# Find imports that will be blocked by the new exports map.
rg -nP "@rocket\\.chat/i18n/(dist|src)/[^'\"]+" --type-add 'mjs:*.mjs' --type-add 'mts:*.mts' --type-add 'cjs:*.cjs' --type-add 'cts:*.cts' --type-add 'cts:*.cts' --type ts --type tsx --type js --type jsx --type mjs --type mts --type cjs --type cts

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Search for any deep imports/require calls into @rocket.chat/i18n beyond the new exports map
rg -n "@rocket\\.chat/i18n/(dist|src)/" -g "*.{js,jsx,ts,tsx,mjs,mts,cjs,cts}"

Length of output: 539


Expand exports or update deep imports to match allowed entrypoints

Deep imports will break under the new exports map. Add missing paths or adjust imports to use the package entrypoint:

• packages/mock-providers/src/MockedAppRootBuilder.tsx: @rocket.chat/i18n/dist/languages
• ee/apps/omnichannel-transcript/src/i18n.ts: @rocket.chat/i18n/dist/resources
• apps/meteor/client/providers/TranslationProvider.tsx:
@rocket.chat/i18n/dist/languages
@rocket.chat/i18n/dist/resources/en.i18n.json
• apps/meteor/server/lib/i18n.ts: @rocket.chat/i18n/dist/resources

Either expand exports in packages/i18n/package.json to include these deep paths (e.g. "./dist/resources/en.i18n.json") or refactor imports to use import … from '@rocket.chat/i18n'.

🤖 Prompt for AI Agents
In packages/i18n/package.json around lines 6–22, the exports map currently omits
the deep paths used by consumers (e.g. @rocket.chat/i18n/dist/languages and
@rocket.chat/i18n/dist/resources and the specific resources file en.i18n.json),
which breaks imports; fix by expanding the exports map to add entries for
"./dist/languages", "./dist/resources" and the specific JSON file
"./dist/resources/en.i18n.json" (each with types/import/require as appropriate),
or alternatively update the callers listed in the review to import from the
package entrypoint ('@rocket.chat/i18n') instead of deep-importing.

@@ -0,0 +1 @@
module.exports = require('./index.js');
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix the CommonJS bridge to avoid ERR_REQUIRE_ESM.

index.js is now emitted as an ES module ("type": "module" in the package export map). Calling require('./index.js') from this CJS bridge will throw ERR_REQUIRE_ESM, so every CommonJS consumer will crash. Point the bridge at the compiled CommonJS build (e.g., ./index.cjs) or create a dedicated CJS bundle and export that instead.

🤖 Prompt for AI Agents
In packages/i18n/src/index.cts around line 1, the CommonJS bridge currently does
require('./index.js') which will throw ERR_REQUIRE_ESM because index.js is
emitted as an ES module; change the bridge to point at the compiled CommonJS
output (for example require('./index.cjs') or the appropriate CJS bundle path),
or alternatively produce and export a dedicated CJS bundle and require that
instead so CommonJS consumers no longer attempt to require an ES module.

"SAML_Metadata_Certificate_Template_Description": "Les següents variables estan disponibles: \n- *\\_\\_newId\\_\\_*: Cadena d'identificació generada aleatòriament",
"SAML_Metadata_Template": "Plantilla de metadades",
"SAML_Metadata_Template_Description": "Les següents variables estan disponibles: \n - ** \\ _ \\ _ sloLocation \\ _ \\ _ **: L'URL de tancament de sessió simple de Rocket.Chat \n- ** \\ _ \\ _ issuer \\ _ \\ _ **: The value of the {{Custom Issuer}} setting. \n- ** \\ _ \\ _ identifierFormat \\ _ \\ _ **: el valor de l'opció {{Identifier Format }} \n- ** \\ _ \\ _ certificateTag \\ _ \\ _ **: Si un certificat privat és configurat, això inclourà el {{Metadata Certificate Template}}, en cas contrari serà ignorado. \n- ** \\ _ \\ _ callbackUrl \\ _ \\ _ **: L'URL de crida de Rocket.Chat",
"SAML_Metadata_Template_Description": "Les següents variables estan disponibles: \n - *\\_\\_sloLocation\\_\\_*: L'URL de tancament de sessió simple de Rocket.Chat \n- *\\_\\_issuer\\_\\_*: The value of the *Custom Issuer* setting. \n- *\\_\\_identifierFormat\\_\\_*: el valor de l'opció *Identifier Format* \n- *\\_\\_certificateTag\\_\\_*: Si un certificat privat és configurat, això inclourà el *Metadata Certificate Template*, en cas contrari serà ignorado. \n- *\\_\\_callbackUrl\\_\\_*: L'URL de crida de Rocket.Chat",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix untranslated fragment in metadata description.

The updated Catalan string now contains the English sentence The value of the *Custom Issuer* setting. (Line 3178), breaking locale consistency. Please translate this fragment back to Catalan (e.g., “El valor de l’opció Custom Issuer”) to keep the localization intact.

🤖 Prompt for AI Agents
In packages/i18n/src/locales/ca.i18n.json around line 3178, the
SAML_Metadata_Template_Description contains an English fragment "The value of
the *Custom Issuer* setting."; replace that fragment with the Catalan
translation (for example: "El valor de l'opció *Custom Issuer*") preserving
surrounding punctuation, newline escapes, and Markdown/asterisk formatting so
the full string remains consistent and properly escaped.

Comment on lines +3541 to 3542
"room_changed_description": "ოთახის აღწერა შეიცვალა: {{room_description}} მიერ {{user_by}}",
"room_changed_topic": "ოთახის თემა შეიცვალა: {{room_topic}} {{user_by}}",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Rephrase “room_changed_*” announcements for correct Georgian grammar.

Both strings leave the agent without the required “-მა” suffix and read awkwardly. Restructure to put the actor before the content.

-  "room_changed_announcement": "ოთახის განცხადება შეიცვალა {{room_announcement}} მიერ {{user_by}}",
-  "room_changed_description": "ოთახის აღწერა შეიცვალა: {{room_description}} მიერ {{user_by}}",
+  "room_changed_announcement": "{{user_by}}-მა შეცვალა ოთახის განცხადება: {{room_announcement}}",
+  "room_changed_description": "{{user_by}}-მა შეცვალა ოთახის აღწერა: {{room_description}}",

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/i18n/src/locales/ka-GE.i18n.json around lines 3541–3542, the
"room_changed_description" and "room_changed_topic" keys are grammatically
incorrect in Georgian because the actor lacks the required "-მა" suffix and the
phrasing places the actor after the content; update both strings to put the
actor before the changed content and append the instrumental suffix to the actor
placeholder (e.g., move {{user_by}} to the front and use {{user_by}}-მა),
ensuring spacing and punctuation are adjusted for natural Georgian word order.

"user-generate-access-token_description": "მომხმარებლისთვის წვდომის ტოკენის გენერირების უფლება",
"user_sent_an_attachment": "{{username}}– მა გაგზავნა დანართი",
"user_sent_an_attachment": "{{user}}– მა გაგზავნა დანართი",
"view-broadcast-member-list": "წევრების სია იხილეთ სამაუწყებლო ოთახში",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Tighten the dash in “user_sent_an_attachment”.

There’s an extra space after the dash; join the agent suffix back to the username.

-  "user_sent_an_attachment": "{{user}}– მა გაგზავნა დანართი",
+  "user_sent_an_attachment": "{{user}}–მა გაგზავნა დანართი",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"view-broadcast-member-list": "წევრების სია იხილეთ სამაუწყებლო ოთახში",
"view-broadcast-member-list": "წევრების სია იხილეთ სამაუწყებლო ოთახში",
"user_sent_an_attachment": "{{user}}–მა გაგზავნა დანართი",
🤖 Prompt for AI Agents
In packages/i18n/src/locales/ka-GE.i18n.json around line 3626, the translation
for the key "user_sent_an_attachment" has an extra space after the dash
separating the username and the agent suffix; remove that extra space so the
dash is immediately followed by the agent suffix (i.e., join the suffix back to
the username), and ensure you use the correct dash character consistent with
other translations.

Comment on lines +2642 to 2643
"room_changed_description": "Istabas apraksts ir mainīts uz: <em>{{room_description}}</em> <em>{{user_by}}</em>",
"room_is_blocked": "Šī istaba ir bloķēta",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Restore actor clause in room change announcements.

room_changed_announcement/room_changed_description now drop the connective before {{user_by}}, so the message renders as … <em>teksts</em> Jānis, missing “ko mainīja” (or equivalent). Please reintroduce the user clause to preserve meaning.

-  "room_changed_announcement": "Istabas paziņojums mainīts uz: <em>{{room_announcement}}</em> <em>{{user_by}}</em>",
-  "room_changed_description": "Istabas apraksts ir mainīts uz: <em>{{room_description}}</em> <em>{{user_by}}</em>",
+  "room_changed_announcement": "Istabas paziņojums mainīts uz: <em>{{room_announcement}}</em>, ko mainīja <em>{{user_by}}</em>",
+  "room_changed_description": "Istabas apraksts ir mainīts uz: <em>{{room_description}}</em>, ko mainīja <em>{{user_by}}</em>",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"room_changed_description": "Istabas apraksts ir mainīts uz: <em>{{room_description}}</em> <em>{{user_by}}</em>",
"room_is_blocked": "Šī istaba ir bloķēta",
"room_changed_announcement": "Istabas paziņojums mainīts uz: <em>{{room_announcement}}</em>, ko mainīja <em>{{user_by}}</em>",
"room_changed_description": "Istabas apraksts ir mainīts uz: <em>{{room_description}}</em>, ko mainīja <em>{{user_by}}</em>",
"room_is_blocked": "Šī istaba ir bloķēta",
🤖 Prompt for AI Agents
In packages/i18n/src/locales/lv.i18n.json around lines 2642-2643, the translated
string for "room_changed_description" dropped the connective before {{user_by}},
causing output like "… <em>teksts</em> Jānis"; restore an actor clause so the
sentence reads naturally (e.g., insert a phrase such as "ko mainīja {{user_by}}"
or equivalent Latvian connective before {{user_by}}) so the message becomes
"Istabas apraksts ir mainīts uz: <em>{{room_description}}</em> ko mainīja
<em>{{user_by}}</em>" (or a grammatically correct variant).

Comment on lines +32 to +38
const replaceNullValuesInterpolation = (translation: string) => {
return [undefined, translation === null] as const;
};

const generator = (fn, id) => (dictionary, language, cb) => {
return Object.entries(dictionary).reduce((dic, [key, value]) => {
const [replacement, exist] = fn(value);
if (exist) {
cb?.(id, { language, key });
}
if (replacement) {
dic[key] = replacement;
}
return dic;
}, dictionary);
};
const generator =
(fn: (translation: string) => readonly [string | undefined, boolean], id: string) =>
(dictionary: Record<string, any>, language: string, cb?: (id: string, info: { language: string; key: string }) => void) =>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix TypeScript null comparison regression

Line 33 compares translation against null, but since the parameter was tightened to string, the compiler now raises TS2367 (“this condition will always return false”) under strictNullChecks, breaking the build. Nested plural normalization can still surface real null entries, so we need to allow them through the typing. Please widen the helper (and the generator it feeds) to accept string | null before landing.

-const replaceI18nInterpolation = (translation: string) => {
+const replaceI18nInterpolation = (translation: string | null) => {
@@
-const replaceSprintfInterpolation = (translation: string) => {
+const replaceSprintfInterpolation = (translation: string | null) => {
@@
-const replaceI18nextComponentsArrayInterpolation = (translation: string) => {
+const replaceI18nextComponentsArrayInterpolation = (translation: string | null) => {
@@
-const replaceNullValuesInterpolation = (translation: string) => {
+const replaceNullValuesInterpolation = (translation: string | null) => {
@@
-const generator =
-	(fn: (translation: string) => readonly [string | undefined, boolean], id: string) =>
+const generator =
+	(fn: (translation: string | null) => readonly [string | undefined, boolean], id: string) =>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const replaceNullValuesInterpolation = (translation: string) => {
return [undefined, translation === null] as const;
};
const generator = (fn, id) => (dictionary, language, cb) => {
return Object.entries(dictionary).reduce((dic, [key, value]) => {
const [replacement, exist] = fn(value);
if (exist) {
cb?.(id, { language, key });
}
if (replacement) {
dic[key] = replacement;
}
return dic;
}, dictionary);
};
const generator =
(fn: (translation: string) => readonly [string | undefined, boolean], id: string) =>
(dictionary: Record<string, any>, language: string, cb?: (id: string, info: { language: string; key: string }) => void) =>
const replaceNullValuesInterpolation = (translation: string | null) => {
return [undefined, translation === null] as const;
};
const generator =
(fn: (translation: string | null) => readonly [string | undefined, boolean], id: string) =>
(dictionary: Record<string, any>, language: string, cb?: (id: string, info: { language: string; key: string }) => void) =>
🤖 Prompt for AI Agents
In packages/i18n/src/scripts/normalize.mts around lines 32 to 38, the helper and
its generator are typed to accept only string which causes a TS2367 error when
comparing to null; widen the types so they accept string | null and permit null
in the tuple first element: change replaceNullValuesInterpolation to accept
(translation: string | null) and update the generator fn type from (translation:
string) => readonly [string | undefined, boolean] to (translation: string |
null) => readonly [string | undefined | null, boolean], ensuring downstream
usage expects the widened types.

Comment on lines +7 to +9
"target": "ES2024",
"module": "nodenext",
"moduleResolution": "nodenext",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Hold on ES2024 target—verify Node/Jest runtime support.

These presets ship JS that Jest executes directly under whatever Node version the consumer runs. Targeting ES2024 means the compiler will emit 2024-level syntax (e.g., using declarations, RegExp v flag) without downleveling. Node 18 and 20—which we still support for Rocket.Chat and which Jest users may rely on—do not implement those features, so a single usage would crash at runtime. Unless we’re officially bumping the minimum Node requirement for this package to ≥22, please keep the target aligned with the current supported runtime (e.g., stay on ES2022) or document the requirement bump and update the release notes accordingly.

-		"target": "ES2024",
+		"target": "ES2022",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"target": "ES2024",
"module": "nodenext",
"moduleResolution": "nodenext",
"target": "ES2022",
"module": "nodenext",
"moduleResolution": "nodenext",
🤖 Prompt for AI Agents
In packages/jest-presets/tsconfig.json around lines 7 to 9, the TS target is set
to ES2024 which can emit syntax not supported by Node/Jest runtimes we still
support (Node 18/20); change the compiler target to ES2022 (or another
runtime-compatible level) so emitted code runs on current supported Node
versions, or if you intend to require Node >=22, update package metadata
(engines), CI, and release notes to document the minimum Node bump before
keeping ES2024.

@tassoevan tassoevan requested a review from dougfabris October 1, 2025 17:46
Copy link
Member

@dougfabris dougfabris left a comment

Choose a reason for hiding this comment

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

@ggazzo ggazzo added the stat: QA assured Means it has been tested and approved by a company insider label Oct 8, 2025
@dionisio-bot dionisio-bot bot added the stat: ready to merge PR tested and approved waiting for merge label Oct 8, 2025
@ggazzo ggazzo merged commit 5403210 into develop Oct 8, 2025
109 of 113 checks passed
@ggazzo ggazzo deleted the chore/i18n-scripts branch October 8, 2025 16:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stat: QA assured Means it has been tested and approved by a company insider stat: ready to merge PR tested and approved waiting for merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants