Skip to content

feat: poll translations (#3917) - #3941

Merged
isekovanic merged 1 commit into
mainfrom
develop
Nov 5, 2026
Merged

isekovanic merged 1 commit into
mainfrom
develop

Conversation

@isekovanic

Copy link
Copy Markdown
Contributor

🎯 Goal

The backend now auto-translates polls, and stream-chat exposes the translations as name_i18n on the poll, text_i18n on options and answer_text_i18n on comments. They're already in our ^9.54.4, so no bump is needed. Until now the SDK always showed the original text. With this PR, polls show in the user's language, the same way auto-translated message text already does.

React counterpart: GetStream/stream-chat-react#3340.

Ticket:
https://linear.app/stream/issue/REACT-1344/support-automatic-poll-translations-in-the-react-sdk

🛠 Implementation details

The user's language is client.user.language, the same as for messages. Any text without a translation falls back to the original.

Translated:

  • the question in the poll header and in the all-options and results dialogs
  • option text in the message and in the results, including the vote button's accessibility label
  • comments in the answers list
  • the poll previews in the channel list, quoted replies and the thread list ("created: …", "voted: …")

There's no "View original" toggle. v9 has none for messages either, so polls always show the translation, like messages do. Editing your own comment still prefills the original text, not the translation.

API changes, all additive:

  • New usePollTextTranslation() hook. It returns translate(original, i18n), for custom poll components.
  • usePollState() also returns nameI18n.

The <lang>_text lookup lives in one internal helper, getTranslatedText, and useTranslatedMessage now uses it too. One edge case changes: a message whose i18n has the user's language key set to undefined now shows the original text instead of nothing.

Offline DB:

  • The poll table gets name_i18n and description_i18n columns. Option and comment translations were already stored, inside the options and answers JSON. The description isn't displayed anywhere yet; it's stored so the poll round-trips intact.
  • Both columns are always written, as null when the poll has no translation. The upsert skips undefined values, so leaving them out would keep a stale translation after the question is edited.
  • dbVersion goes from 16 to 17, so the offline DB is rebuilt once after upgrade, as with earlier schema changes. The cache is fetched again, and offline actions that haven't synced yet are dropped.

🎨 UI Changes

The layout is unchanged; translated text replaces the original.

🧪 Testing

Unit tests cover:

  • translated, partially translated and untranslated polls, in the message and in the dialogs
  • a translation arriving through poll.updated
  • the channel list and message previews
  • offline storage: kept, replaced, cleared, and a poll that never had translations

On the iOS simulator I checked the poll message, the results and comments dialogs, and the channel preview, including a real vote. The demo app doesn't produce poll translations, so I injected poll.updated events with translations into the running client. Not checked on Android, and not checked with offline mode on a device.

To try it, you need an app with auto-translation enabled and a user whose language differs from the poll's.

☑️ Checklist

  • I have signed the Stream CLA (required)
  • PR targets the develop branch
  • Documentation is updated
  • New code is tested in main example apps, including all possible scenarios
    • SampleApp iOS and Android
    • Expo iOS and Android

🎯 Goal

🛠 Implementation details

🎨 UI Changes

iOS
Before After
Android
Before After

🧪 Testing

☑️ Checklist

  • I have signed the Stream CLA (required)
  • PR targets the develop branch
  • Documentation is updated
  • New code is tested in main example apps, including all possible scenarios
    • SampleApp iOS and Android
    • Expo iOS and Android

## 🎯 Goal

The backend now auto-translates polls, and `stream-chat` exposes the
translations as `name_i18n` on the poll, `text_i18n` on options and
`answer_text_i18n` on comments. They're already in our `^9.54.4`, so no
bump is needed. Until now the SDK always showed the original text. With
this PR, polls show in the user's language, the same way auto-translated
message text already does.

React counterpart: GetStream/stream-chat-react#3340.

Ticket:
https://linear.app/stream/issue/REACT-1344/support-automatic-poll-translations-in-the-react-sdk

## 🛠 Implementation details

The user's language is `client.user.language`, the same as for messages.
Any text without a translation falls back to the original.

Translated:
- the question in the poll header and in the all-options and results
dialogs
- option text in the message and in the results, including the vote
button's accessibility label
- comments in the answers list
- the poll previews in the channel list, quoted replies and the thread
list ("created: …", "voted: …")

There's no "View original" toggle. v9 has none for messages either, so
polls always show the translation, like messages do. Editing your own
comment still prefills the original text, not the translation.

API changes, all additive:
- New `usePollTextTranslation()` hook. It returns `translate(original,
i18n)`, for custom poll components.
- `usePollState()` also returns `nameI18n`.

The `<lang>_text` lookup lives in one internal helper,
`getTranslatedText`, and `useTranslatedMessage` now uses it too. One
edge case changes: a message whose `i18n` has the user's language key
set to `undefined` now shows the original text instead of nothing.

Offline DB:
- The `poll` table gets `name_i18n` and `description_i18n` columns.
Option and comment translations were already stored, inside the options
and answers JSON. The description isn't displayed anywhere yet; it's
stored so the poll round-trips intact.
- Both columns are always written, as `null` when the poll has no
translation. The upsert skips `undefined` values, so leaving them out
would keep a stale translation after the question is edited.
- `dbVersion` goes from 16 to 17, so the offline DB is rebuilt once
after upgrade, as with earlier schema changes. The cache is fetched
again, and offline actions that haven't synced yet are dropped.

## 🎨 UI Changes

The layout is unchanged; translated text replaces the original.

## 🧪 Testing

Unit tests cover:
- translated, partially translated and untranslated polls, in the
message and in the dialogs
- a translation arriving through `poll.updated`
- the channel list and message previews
- offline storage: kept, replaced, cleared, and a poll that never had
translations

On the iOS simulator I checked the poll message, the results and
comments dialogs, and the channel preview, including a real vote. The
demo app doesn't produce poll translations, so I injected `poll.updated`
events with translations into the running client. Not checked on
Android, and not checked with offline mode on a device.

To try it, you need an app with auto-translation enabled and a user
whose `language` differs from the poll's.

## ☑️ Checklist

- [ ] I have signed the [Stream
CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform)
(required)
- [x] PR targets the `develop` branch
- [ ] Documentation is updated
- [ ] New code is tested in main example apps, including all possible
scenarios
  - [ ] SampleApp iOS and Android
  - [ ] Expo iOS and Android
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown

Next releases

v9.12.0

9.12.0 (2026-11-05)

Features

@Stream-SDK-Bot

Copy link
Copy Markdown
Contributor

SDK Size

title develop branch diff status
js_bundle_size 2041 KB 2041 KB 0 B 🟢
ai_components_size 163 KB 163 KB 0 B 🟢

@isekovanic
isekovanic merged commit 32e897f into main Nov 5, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants