Skip to content

feat: add LibreTranslate message auto-translation provider - #40900

Merged
dionisio-bot[bot] merged 3 commits into
RocketChat:developfrom
PhoenixRising666:feat/autotranslate-libretranslate-provider
Jun 20, 2026
Merged

dionisio-bot[bot] merged 3 commits into
RocketChat:developfrom
PhoenixRising666:feat/autotranslate-libretranslate-provider

Conversation

@PhoenixRising666

@PhoenixRising666 PhoenixRising666 commented Jun 11, 2026 •

Copy link
Copy Markdown
Contributor

Proposed changes

Adds LibreTranslate as a message auto-translation provider, alongside the existing Google, DeepL and Microsoft providers.

Unlike the current providers, LibreTranslate is open-source and can be self-hosted, which enables fully on-premise / offline message auto-translation with no third-party API dependency or per-character cost. This is useful for air-gapped, privacy-sensitive, or cost-constrained deployments.

It integrates through the existing AutoTranslate provider abstraction, so it populates the native translations message metadata and works with the standard per-user translate toggle — no client changes.

Implementation

  • New provider libre-translate (apps/meteor/app/autotranslate/server/libreTranslate.ts) extending AutoTranslate, registered via TranslationProviderRegistry.
  • Two new settings under the Auto-Translate section:
    • AutoTranslate_LibreTranslateAPIURL — base URL of the LibreTranslate instance (e.g. http://libretranslate.internal:5000)
    • AutoTranslate_LibreTranslateAPIKey — optional, only when the instance enforces API keys
  • Added libre-translate to the AutoTranslate_ServiceProvider dropdown and the corresponding i18n labels.
  • Supported languages are fetched live from the instance's /languages endpoint; translation uses /translate.

How to test

  1. Run a LibreTranslate instance (e.g. docker run -p 5000:5000 libretranslate/libretranslate).
  2. Admin → Settings → Message → Auto-Translate: enable Auto-Translate, set Service Provider = LibreTranslate, set the API URL.
  3. In a room, enable Auto-Translate for a user and pick a target language; post a message and confirm it is translated.

Validated end-to-end against a self-hosted LibreTranslate instance (message translated and stored in message.translations).

Types of changes

  • New feature (non-breaking change which adds functionality)

Further comments

A changeset is included (@rocket.chat/meteor: minor, @rocket.chat/i18n: patch).

CORE-2301

Summary by CodeRabbit

  • New Features
    • Added LibreTranslate as an additional auto-translation service provider alongside Google, DeepL, and Microsoft
    • Enabled admin configuration for LibreTranslate, including support for self-hosted API endpoint URL
    • Added optional API key authentication for LibreTranslate, stored as a secret
    • Added the corresponding Auto-Translate provider and API URL labels to the English UI language pack
  • Bug Fixes
    • Improved handling of supported language normalization and per-language translation errors during LibreTranslate requests

Adds LibreTranslate as a fourth message auto-translation provider alongside
Google, DeepL and Microsoft. Unlike the existing providers, LibreTranslate is
open-source and can be self-hosted, enabling fully on-premise / offline message
auto-translation with no third-party API dependency.

- New provider `libre-translate` registered in TranslationProviderRegistry
- Configurable instance URL (AutoTranslate_LibreTranslateAPIURL) and optional
  API key (AutoTranslate_LibreTranslateAPIKey) under the Auto-Translate section
- Added to the Service Provider dropdown and i18n labels
@PhoenixRising666
PhoenixRising666 requested a review from a team as a code owner June 11, 2026 15:04
@dionisio-bot

dionisio-bot Bot commented Jun 11, 2026 •

Copy link
Copy Markdown
Contributor

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 8.6.0, but it targets 6.6.0

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot

changeset-bot Bot commented Jun 11, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 781eb8d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@rocket.chat/meteor Minor
@rocket.chat/i18n Patch
@rocket.chat/core-typings Minor
@rocket.chat/rest-typings Minor

Not sure what this means? Click here to learn what changesets are.

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

@CLAassistant

CLAassistant commented Jun 11, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai

coderabbitai Bot commented Jun 11, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This pull request introduces LibreTranslate as a new auto-translation provider for Rocket.Chat messages and attachments. The implementation includes a provider class that communicates with LibreTranslate HTTP APIs, admin settings to configure the API endpoint and optional authentication key, and localization strings for the UI.

Changes

LibreTranslate Provider Implementation

Layer / File(s) Summary
LibreTranslate provider class and logger
apps/meteor/app/autotranslate/server/libreTranslate.ts, apps/meteor/app/autotranslate/server/logger.ts
Defines LibreTranslateAutoTranslate extending AutoTranslate base with type definitions for LibreTranslate language structure, timeout constant, and language code normalization helper; constructor subscribes to watched runtime settings for API URL and optional key; metadata and settings accessors expose provider identity and current configuration; language discovery via cached HTTP fetch to /languages endpoint with per-target caching and code normalization; language resolution falls back to 2-character codes when exact matches unavailable; _query() POSTs to /translate with query lines, source auto-detection, and optional API key with timeout enforcement; message translation splits by newline, translates per target language with per-language error logging, and de-tokenizes successful results; attachment description translation translates per language without de-tokenization; provider registration in TranslationProviderRegistry; logger section exported for provider diagnostics.
Admin settings and configuration
apps/meteor/server/settings/message.ts
Adds libre-translate option to AutoTranslate_ServiceProvider select values and introduces AutoTranslate_LibreTranslateAPIURL and AutoTranslate_LibreTranslateAPIKey settings, both conditionally enabled when AutoTranslate is active and LibreTranslate is selected; API key setting is marked as secret.
Localization strings
packages/i18n/src/locales/en.i18n.json
Introduces English i18n keys AutoTranslate_LibreTranslate (provider name) and AutoTranslate_LibreTranslate_API_URL (API URL field label).
Module integration
apps/meteor/app/autotranslate/server/index.ts
Imports ./libreTranslate module to register the provider during server initialization.
Release documentation
.changeset/libretranslate-autotranslate-provider.md
Documents version bumps for @rocket.chat/meteor and @rocket.chat/i18n with release notes describing LibreTranslate as a new self-hosted translation provider option with API key support.

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and clearly summarizes the main change: introducing LibreTranslate as a new message auto-translation provider for Rocket.Chat, which aligns with all file changes and PR objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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


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.

@coderabbitai coderabbitai Bot added the type: feature Pull requests that introduces new feature label Jun 11, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (1)
apps/meteor/app/autotranslate/server/libreTranslate.ts (1)

16-25: ⚡ Quick win

Remove implementation comments in this TS file to match repository guideline.

As per coding guidelines, "Avoid code comments in the implementation."

Also applies to: 31-34, 41-47, 52-56, 65-69, 77-83, 110-118, 149-156, 177-183, 204-204

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/meteor/app/autotranslate/server/libreTranslate.ts` around lines 16 - 25,
Remove all implementation-style comments inside the LibreTranslate class so the
file follows the "no implementation comments" guideline: delete the inline/block
comment blocks that describe implementation details (not JSDoc) around the
LibreTranslate class and its methods (e.g., constructor, getLanguages,
translate, detectLanguage, and any helper functions) and leave only necessary
JSDoc/type comments if present; ensure no commented-out code remains and run a
quick build/tests to confirm no functional code was accidentally removed.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/meteor/app/autotranslate/server/libreTranslate.ts`:
- Line 160: The call to getSupportedLanguages('en') can reject and abort the
entire translation flow; wrap both calls to getSupportedLanguages('en') in a
try/catch inside the LibreTranslate class method that drives per-language
translations (where getSupportedLanguages is invoked), log the error via the
existing logger, and fall back to a safe default (e.g., an empty array or a
minimal hardcoded list) so the per-target-language loop still runs and each
individual translate call can handle its own failures; ensure you reference
getSupportedLanguages('en') and the surrounding translate/processing method when
making the change so you catch both bootstrap sites.
- Around line 94-96: The two outbound fetch calls to
`${this.apiEndPointUrl}/languages` and the POST to
`${this.apiEndPointUrl}/translate` lack a timeout/abort mechanism; update both
fetches in apps/meteor/app/autotranslate/server/libreTranslate.ts to supply a
timeout-supported option via the `@rocket.chat/server-fetch` helper or attach an
AbortSignal from an AbortController (e.g., create an AbortController, set a
reasonable timeout like 5–15s to call controller.abort(), and pass
controller.signal to fetch). Also ensure the code that calls these fetches (the
methods referencing this.apiEndPointUrl and the response handling) catches
abort/timeout errors and returns/logs an appropriate failure rather than leaving
the worker hanging.
- Around line 93-96: The fetch call in the LibreTranslate client is bypassing
SSRF protection by setting ignoreSsrfValidation: true; update the code in the
method that calls fetch (the languages request in LibreTranslate client) to
remove ignoreSsrfValidation and instead perform proper SSRF validation — either
rely on the built-in checkForSsrfWithIp flow by omitting the ignore flag, or
validate the configured AutoTranslate_LibreTranslateAPIURL against the SSRF
allowlist (SSRF_Allowlist) before calling fetch; ensure the same change is
applied to the other fetch usage in the same file (lines 130-134 equivalent) so
runtime-configured API URLs are validated rather than unconditionally bypassing
SSRF checks.

In `@apps/meteor/server/settings/message.ts`:
- Around line 401-417: The setting registration for
'AutoTranslate_LibreTranslateAPIKey' uses this.add(...) but is missing the
secret flag, so the API key can be exposed; update the options object passed to
this.add for the 'AutoTranslate_LibreTranslateAPIKey' setting to include secret:
true (alongside existing properties like type, group, section, public,
i18nLabel, and enableQuery) so the value is treated as a secret in admin
surfaces and logs.

In `@packages/i18n/src/locales/en.i18n.json`:
- Line 869: The i18n key "AutoTranslate_LibreTranslate_API_URL" does not match
the configured setting ID "AutoTranslate_LibreTranslateAPIURL", causing label
resolution to fail; rename the JSON key to "AutoTranslate_LibreTranslateAPIURL"
(replacing the underscored variant) so it exactly matches the setting ID used by
the admin settings resolver, and run a quick search to update any other locale
files or references that use the old key.

---

Nitpick comments:
In `@apps/meteor/app/autotranslate/server/libreTranslate.ts`:
- Around line 16-25: Remove all implementation-style comments inside the
LibreTranslate class so the file follows the "no implementation comments"
guideline: delete the inline/block comment blocks that describe implementation
details (not JSDoc) around the LibreTranslate class and its methods (e.g.,
constructor, getLanguages, translate, detectLanguage, and any helper functions)
and leave only necessary JSDoc/type comments if present; ensure no commented-out
code remains and run a quick build/tests to confirm no functional code was
accidentally removed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5bdc6f1c-a9fd-438a-ab9e-c83b7536e550

📥 Commits

Reviewing files that changed from the base of the PR and between 1a9ced9 and 34e3544.

📒 Files selected for processing (5)
  • .changeset/libretranslate-autotranslate-provider.md
  • apps/meteor/app/autotranslate/server/index.ts
  • apps/meteor/app/autotranslate/server/libreTranslate.ts
  • apps/meteor/server/settings/message.ts
  • packages/i18n/src/locales/en.i18n.json
📜 Review details
⏰ 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). (4)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: CodeQL-Build
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: Hacktron Security Check
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/app/autotranslate/server/index.ts
  • apps/meteor/server/settings/message.ts
  • apps/meteor/app/autotranslate/server/libreTranslate.ts
🧠 Learnings (4)
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.

Applied to files:

  • apps/meteor/app/autotranslate/server/index.ts
  • apps/meteor/server/settings/message.ts
  • apps/meteor/app/autotranslate/server/libreTranslate.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.

Applied to files:

  • apps/meteor/app/autotranslate/server/index.ts
  • apps/meteor/server/settings/message.ts
  • apps/meteor/app/autotranslate/server/libreTranslate.ts
📚 Learning: 2026-05-06T12:21:44.083Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 40256
File: apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx:121-149
Timestamp: 2026-05-06T12:21:44.083Z
Learning: Field wrappers in rocket.chat/fuselage-forms (Field, FieldLabel, FieldRow, FieldError, FieldHint) auto-create htmlFor/id associations, aria-describedby, and role="alert" for errors. Do not manually set htmlFor, id, aria-describedby, or role attributes when using these wrappers. This automatic wiring does not apply to plain rocket.chat/fuselage components, which require explicit ID wiring per the accessibility docs. In code reviews, prefer using fuselage-forms wrappers for form fields and verify there is no unnecessary manual ID/aria wiring in files that use these wrappers. If a component uses plain fuselage components, ensure proper id wiring as per docs.

Applied to files:

  • apps/meteor/app/autotranslate/server/index.ts
  • apps/meteor/server/settings/message.ts
  • apps/meteor/app/autotranslate/server/libreTranslate.ts
📚 Learning: 2026-03-16T21:50:37.589Z
Learnt from: amitb0ra
Repo: RocketChat/Rocket.Chat PR: 39676
File: .changeset/migrate-users-register-openapi.md:3-3
Timestamp: 2026-03-16T21:50:37.589Z
Learning: For changes related to OpenAPI migrations in Rocket.Chat/OpenAPI, when removing endpoint types and validators from rocket.chat/rest-typings (e.g., UserRegisterParamsPOST, /v1/users.register) document this as a minor changeset (not breaking) per RocketChat/Rocket.Chat-Open-API#150 Rule 7. Note that the endpoint type is re-exposed via a module augmentation .d.ts in the consuming package (e.g., packages/web-ui-registration/src/users-register.d.ts). In reviews, ensure the changeset clearly states: this is a non-breaking change, the major version should not be bumped, and the changeset reflects a minor version bump. Do not treat this as a breaking change during OpenAPI migrations.

Applied to files:

  • .changeset/libretranslate-autotranslate-provider.md
🔇 Additional comments (3)
.changeset/libretranslate-autotranslate-provider.md (1)

1-7: LGTM!

apps/meteor/server/settings/message.ts (1)

319-322: LGTM!

Also applies to: 383-399

apps/meteor/app/autotranslate/server/index.ts (1)

13-13: LGTM!

Comment thread apps/meteor/app/autotranslate/server/libreTranslate.ts Outdated
Comment thread apps/meteor/app/autotranslate/server/libreTranslate.ts Outdated
Comment thread apps/meteor/app/autotranslate/server/libreTranslate.ts
Comment thread apps/meteor/server/settings/message.ts
Comment thread packages/i18n/src/locales/en.i18n.json

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

3 issues found across 5 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread apps/meteor/server/settings/message.ts
Comment thread apps/meteor/app/autotranslate/server/libreTranslate.ts
Comment thread apps/meteor/app/autotranslate/server/libreTranslate.ts
@codecov

codecov Bot commented Jun 11, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 70.11%. Comparing base (1a9ced9) to head (781eb8d).
⚠️ Report is 43 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #40900      +/-   ##
===========================================
+ Coverage    70.02%   70.11%   +0.08%     
===========================================
  Files         3355     3358       +3     
  Lines       129162   129565     +403     
  Branches     22337    22490     +153     
===========================================
+ Hits         90443    90839     +396     
+ Misses       35421    35409      -12     
- Partials      3298     3317      +19     
Flag Coverage Δ
e2e 59.33% <ø> (+0.07%) ⬆️
e2e-api 46.20% <100.00%> (+0.02%) ⬆️
unit 70.03% <ø> (+0.10%) ⬆️

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.

@rc-layne

rc-layne Bot commented Jun 11, 2026 •

Copy link
Copy Markdown

Warning

These are security findings reported by the security scanners configured in Layne. Findings may contain false positives - review them and fix what makes sense.

Layne found 2 high issues in this PR.

View 2 finding(s)
Severity Scanner File Rule Description
🟠 High semgrep apps/meteor/app/autotranslate/server/libreTranslate.ts:78 app.config.semgrep.rules.rocketchat.ssrf-validation-bypass-without-justification SSRF validation is disabled. Ensure the URL is not user-controlled or is restricted to a known-safe allowlist.
🟠 High semgrep apps/meteor/app/autotranslate/server/libreTranslate.ts:118 app.config.semgrep.rules.rocketchat.ssrf-validation-bypass-without-justification SSRF validation is disabled. Ensure the URL is not user-controlled or is restricted to a known-safe allowlist.

Comment thread apps/meteor/app/autotranslate/server/libreTranslate.ts
Comment thread apps/meteor/app/autotranslate/server/libreTranslate.ts Outdated
Comment thread apps/meteor/app/autotranslate/server/libreTranslate.ts
Comment thread apps/meteor/app/autotranslate/server/libreTranslate.ts Outdated
…ovider

- Replace underscore (`_.findWhere`) with a native `Array.find`
- Wrap `new Intl.Locale(code)` so an unsupported language code can't throw
- Guard `getSupportedLanguages`/translation when the instance URL is unset
- Isolate supported-language fetch failures so one error no longer aborts all
  target translations for a message
- Set an explicit request timeout on the outbound LibreTranslate calls
- Mark `AutoTranslate_LibreTranslateAPIKey` as `secret`
Comment thread apps/meteor/app/autotranslate/server/libreTranslate.ts Outdated
Comment thread apps/meteor/app/autotranslate/server/libreTranslate.ts Outdated
Comment thread apps/meteor/app/autotranslate/server/libreTranslate.ts Outdated
Comment thread apps/meteor/app/autotranslate/server/libreTranslate.ts Outdated
Comment thread apps/meteor/app/autotranslate/server/libreTranslate.ts Outdated
- Move error handling inside getSupportedLanguages (drop the wrapper helper)
- Use a dedicated AutoTranslate `LibreTranslate` logger section instead of SystemLogger
- Use Array.includes and object spread for the API key
- Keep request timeouts and admin-only URL handling
@scuciatto scuciatto added the stat: QA assured Means it has been tested and approved by a company insider label Jun 19, 2026
@yasnagat

Copy link
Copy Markdown
Member

/layne exception-approve LAYNE-66b4c90a9bc8b034 LAYNE-66573dc98a53d745 reason: URL is not user-controlled and is admin-only

@rc-layne

rc-layne Bot commented Jun 19, 2026

Copy link
Copy Markdown

✅ Exception recorded for LAYNE-66b4c90a9bc8b034, LAYNE-66573dc98a53d745 by @yasnagat: "URL is not user-controlled and is admin-only". Re-running scan...

@KevLehman KevLehman added this to the 8.6.0 milestone Jun 19, 2026
@KevLehman KevLehman added the stat: ready to merge PR tested and approved waiting for merge label Jun 19, 2026
@scuciatto scuciatto removed this from the 8.6.0 milestone Jun 19, 2026
@dionisio-bot dionisio-bot Bot removed the stat: ready to merge PR tested and approved waiting for merge label Jun 19, 2026
@scuciatto scuciatto added this to the 8.6.0 milestone Jun 19, 2026
@KevLehman KevLehman added the stat: ready to merge PR tested and approved waiting for merge label Jun 19, 2026
@dionisio-bot
dionisio-bot Bot added this pull request to the merge queue Jun 19, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jun 20, 2026
@scuciatto scuciatto removed this from the 8.6.0 milestone Jun 20, 2026
@dionisio-bot dionisio-bot Bot removed the stat: ready to merge PR tested and approved waiting for merge label Jun 20, 2026
@scuciatto scuciatto added this to the 8.6.0 milestone Jun 20, 2026
@dionisio-bot
dionisio-bot Bot added this pull request to the merge queue Jun 20, 2026
Merged via the queue into RocketChat:develop with commit 25722db Jun 20, 2026
150 of 158 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-security-review stat: QA assured Means it has been tested and approved by a company insider type: feature Pull requests that introduces new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants