feat(surveys): re-translate displayed survey on language change#686
feat(surveys): re-translate displayed survey on language change#686bs1180 wants to merge 4 commits into
Conversation
Once a survey was on screen, later updates to the user's `language` person property had no effect — the translation was resolved only at display time. Now the SDK re-resolves the survey language whenever the person properties used for flags change and updates the on-screen survey in place, preserving the current question and progress. - Post an internal notification when person properties for flags change. - Survey integration observes it, re-resolves translations for the active survey, and pushes an in-place update only when the matched language actually changed (so `$survey_language` and renders aren't churned needlessly). - Add optional `updateSurvey(_:)` to `PostHogSurveysDelegate`; the default delegate updates the live survey via the display controller. The sheet now observes the controller so re-translations render without restarting. Closes #66998 Generated-By: PostHog Code Task-Id: 50affc3a-7798-47a6-94fe-c86b2b971dac
|
posthog-ios Compliance ReportDate: 2026-07-06 12:23:58 UTC ✅ All Tests Passed!45/45 tests passed Capture Tests✅ 29/29 tests passed View Details
Feature_Flags Tests✅ 16/16 tests passed View Details
|
- Keep PostHogSurveyIntegration.swift under the 1200-line SwiftLint limit by moving self-contained helper types (survey state, condition matching, rating buckets) into a new PostHogSurveyMatching.swift, registered in the Xcode project. - Use the non-availability-gated `_surveysConfig` backing in tests; the public `surveysConfig` accessor is iOS 15+ only and broke the simulator build. - Add the new optional `updateSurvey(_:)` delegate method to the public API snapshot. Generated-By: PostHog Code Task-Id: 50affc3a-7798-47a6-94fe-c86b2b971dac
Address Greptile review on #686: - When a survey is within its display-delay window and its language updates, `updateSurvey` refreshes `pendingSurvey`, but the scheduled work item showed the captured (pre-update) copy. Read the latest `pendingSurvey` at execution time so the re-translated content is shown. - Distinguish the "no survey displayed" vs "different survey" cases in the display controller's update log to make debugging clearer. Generated-By: PostHog Code Task-Id: 50affc3a-7798-47a6-94fe-c86b2b971dac
|
@lucasheriques I know you worked a bit on this, mind giving this a review? |
|
Reviews (2): Last reviewed commit: "fix(surveys): apply latest translation w..." | Re-trigger Greptile |
|
Thanks @bs1180 instead of a CHANGELOG we actually need a changes, please see |
|
@bs1180 friendly ping on this one |
Follow the repo's Changesets release flow: add a minor changeset describing the live survey re-translation feature and revert the direct CHANGELOG.md edit (the release workflow regenerates CHANGELOG.md from changesets). Generated-By: PostHog Code Task-Id: 50affc3a-7798-47a6-94fe-c86b2b971dac
|
Hey @turnipdabeets and @ioannisj, sorry for the delay. This PR is the product of letting PostHog AI loose on a customer complaint :D I'm not an IOS dev so not sure if this implementation makes sense and I've done literally zero QA. Is that OK with you? Won't be offended this just gets rejected. |
|
@bs1180 thank you, I can do the QA part and see also how this will affect Flutter surveys as well. we'll probably need to land a similar feature for posthog-android as well I imagine? To get some more context though, do you have the original link to the customer complaint? I personally don't think that on-the-fly language changes are the norm so I'm not completely sure we should be supporting this |
|
Would like to know @turnipdabeets thoughts as well on this, maybe it's just me :) |
|
Yup totally understood - PostHog/posthog#66998 is the original feature request, and this is the customer Slack thread |
I think we need on-device QA, if you want to take that @ioannisj. Maybe we need to check out the sheet dismissal animation too. Not sure if we need @PostHog/team-surveys on par before making this change? And it makes sense to do this for the other mobile if it's needed, android flutter and RN. |
💡 Motivation and Context
Once a translated survey was displayed, later changes to the user's language (e.g. via
setPersonProperties/identifywith alanguageproperty) had no effect — the survey kept the language it was first rendered with. The translation was resolved only at display time.This makes a survey already on screen follow the user's language: when the person properties used for flags change and a matching translation exists, the survey re-translates in place, keeping the current question and any progress.
Closes #66998
💚 How did you test it?
Added unit tests (run on the iOS simulator, where survey code compiles):
languageperson property while a survey is active pushes a re-translated survey to the delegate and updates the tracked survey language.$survey_languageor re-renders).SurveyDisplayController.updateSurveypreserves the current question index and completion state, and ignores updates for a different / absent survey.I was unable to run
make test/ the iOS build in this environment (no Xcode available), so CI should be relied on for the full build + simulator test run.📝 Checklist
If releasing new changes
pnpm changesetto generate a changeset file🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Implemented from a Slack thread request, logged as #66998. Built with the PostHog Slack app (Claude Code). Key decisions:
NotificationCentersignal posted fromPostHogRemoteConfig.setPersonPropertiesForFlags(the single chokepoint all person-property updates flow through) rather than sniffing the captured event stream — the$set/$identifyordering relative to the flag-property update is inconsistent, so the notification is the order-correct, robust hook.updateSurvey(_:)to the delegate and madeSurveySheetobserve the display controller (instead of the snapshot passed by.sheet(item:)), so an in-place content swap with the same survey id renders live while preserving SwiftUI@State(current question, typed answers).One known limitation worth a reviewer's eye: for choice questions, selection is keyed by the option's string value, so a mid-survey language switch can drop an in-progress selection on those question types (labels change). Text/rating answers are preserved.
Created with PostHog from a Slack thread