fix: the translation on the filter options#7667
fix: the translation on the filter options#7667gulshank0 wants to merge 2 commits intoformbricks:mainfrom
Conversation
WalkthroughThe pull request adds internationalization (i18n) support to survey filtering functionality. The 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ 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. Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
apps/web/app/lib/surveys/surveys.ts (3)
538-566:⚠️ Potential issue | 🔴 CriticalCritical:
META_OP_MAPis undefined at module scope, causing runtime error.
processMetaFiltersreferencesMETA_OP_MAPat line 551, butMETA_OP_MAPis now only defined insidegenerateElementAndFilterOptions(line 157). This will cause aReferenceErrorat runtime when any meta filter is applied.Proposed fix: Define META_OP_MAP at module scope or pass it as parameter
Option 1: Keep a module-level map for logic comparisons (using English keys for internal logic):
+// Internal operation mapping - uses English keys for logic comparisons +const META_OP_MAP = { + Equals: "equals", + "Not equals": "notEquals", + Contains: "contains", + "Does not contain": "doesNotContain", + "Starts with": "startsWith", + "Does not start with": "doesNotStartWith", + "Ends with": "endsWith", + "Does not end with": "doesNotEndWith", +} as const;Option 2: Pass the map as a parameter to
processMetaFilters.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/app/lib/surveys/surveys.ts` around lines 538 - 566, processMetaFilters currently references META_OP_MAP which is defined only inside generateElementAndFilterOptions, causing a ReferenceError; fix by either (A) defining META_OP_MAP at module scope so processMetaFilters can access it, or (B) add a parameter (e.g., metaOpMap) to processMetaFilters and pass the existing META_OP_MAP from generateElementAndFilterOptions (update all callers accordingly); ensure you update the function signature of processMetaFilters and every call site (including generateElementAndFilterOptions) or move the constant above the function definition so the symbol is available at module scope.
302-403:⚠️ Potential issue | 🔴 CriticalCritical: Filter logic still compares against hardcoded English strings, breaking i18n.
The UI now displays translated filter values (e.g.,
t("environments.surveys.filter.filled_out")), but these helper functions still compare against hardcoded English strings like"Filled out","Skipped","Clicked","Dismissed","Accepted","Includes either","Includes all","Is equal to", etc.When a non-English user selects a filter, the translated string will not match these hardcoded comparisons, causing filters to silently fail.
You need a translation strategy that maintains internal identifiers while translating at the UI layer. Consider using stable keys/identifiers for logic and only translating at display time.
Suggested approach: Use stable identifiers for logic
Instead of comparing translated UI strings, use stable operation keys:
-const processFilledOutSkippedFilter = ( - filterType: FilterValue["filterType"], - elementId: string, - filters: TResponseFilterCriteria -) => { - if (filterType.filterComboBoxValue === "Filled out") { - filters.data![elementId] = { op: "filledOut" }; - } else if (filterType.filterComboBoxValue === "Skipped") { - filters.data![elementId] = { op: "skipped" }; - } -}; +// Use a mapping from translated labels to operation keys +const FILTER_VALUE_TO_OP = { + filledOut: "filledOut", + skipped: "skipped", + // ... etc +} as const; + +// Store the operation key alongside the translated label in filterComboBoxValue +// or use a separate field for the operation identifierThe filter options should store both the display label (translated) and a stable identifier for logic comparisons.
Based on learnings: "In apps/web/app/lib/surveys/surveys.ts, the conditionOptions and filterOptions objects contain hardcoded user-facing strings... that need to be marked as translatable. These values are used both for UI display in ElementFilterComboBox and for logic comparisons in getFormattedFilters, requiring a translation strategy that maintains internal identifiers while translating at the UI layer."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/app/lib/surveys/surveys.ts` around lines 302 - 403, The helper functions (processFilledOutSkippedFilter, processRankingFilter, processMultipleChoiceFilter, processNPSRatingFilter, processCTAFilter, processConsentFilter) are comparing against hardcoded English labels which breaks i18n; change the filter option shape so UI displays translated labels but also carries a stable identifier (e.g., add fields like optionId / valueId / comboId on conditionOptions/filterOptions and ensure ElementFilterComboBox passes those ids into getFormattedFilters), then update each helper to switch on these stable identifiers instead of filterType.filterComboBoxValue or filterType.filterValue (and still parse ints for NPS/rating when the identifier expects numeric values). Ensure UI uses the label for display only and logic uses the stable id for comparisons.
405-431:⚠️ Potential issue | 🔴 CriticalCritical: Picture selection filter parsing breaks with translated labels.
Line 421 parses the picture index using
option.split(" ")[1], assuming "Picture N" format. However, the translated labelt("environments.surveys.filter.picture_idx", { idx: idx + 1 })may produce different formats across languages (e.g., "Bild 1" in German, "Image 1" in French), breaking this parsing logic.Proposed fix: Store index alongside translated label
Store the picture index as data rather than parsing it from the display string:
case TSurveyElementTypeEnum.PictureSelection: return { ...baseOption, - filterComboBoxOptions: element.choices?.map((_, idx) => - t("environments.surveys.filter.picture_idx", { idx: idx + 1 }) - ) ?? [""], + filterComboBoxOptions: element.choices?.map((choice, idx) => ({ + label: t("environments.surveys.filter.picture_idx", { idx: idx + 1 }), + id: choice.id, + index: idx, + })) ?? [], };Then update
processPictureSelectionFilterto use the stored index/id instead of parsing.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/app/lib/surveys/surveys.ts` around lines 405 - 431, processPictureSelectionFilter currently parses the choice index from the translated display string (option.split(" ")[1]) which breaks localization; instead store the choice index or id when building the combo options (e.g., include an explicit value/index field on each filterComboBoxValue entry) and update processPictureSelectionFilter to read that stored index/id (from filterType.filterComboBoxValue[*].value or similar) to map to element.choices (use element?.choices[index]?.id or the stored choice id) so parsing of translated labels is eliminated. Ensure the code references processPictureSelectionFilter, filterType.filterComboBoxValue, and getElementsFromBlocks element.apps/web/app/lib/surveys/surveys.test.ts (1)
565-584: 🧹 Nitpick | 🔵 TrivialNote: Tests use English strings matching current (broken) implementation.
The
filterComboBoxValue: "Filled out"at line 577 tests against the current implementation whereprocessFilledOutSkippedFiltercompares against hardcoded English"Filled out". Once the implementation is fixed to use stable identifiers, these test inputs will need updating.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/app/lib/surveys/surveys.test.ts` around lines 565 - 584, The test is using the English string "Filled out" which matches the old broken implementation; update the selectedFilter fixture so filter.filter[0].filterType.filterComboBoxValue uses the stable identifier the code now expects (e.g., the enum/value used by processFilledOutSkippedFilter such as "filledOut" or FilterComboBoxValue.FilledOut) for the openTextQ case so getFormattedFilters(survey, selectedFilter, ...) produces { op: "filledOut" } as asserted; adjust the selectedFilter construction in this test (and any similar tests) to reference that stable identifier rather than the English label.
♻️ Duplicate comments (1)
apps/web/app/lib/surveys/surveys.ts (1)
568-587:⚠️ Potential issue | 🔴 CriticalCritical: Quota filter logic uses hardcoded English strings.
The
statusMapat lines 574-578 contains hardcoded English strings ("Screened in","Screened out (overquota)","Not in quota"), but the UI now displays translated versions. This will break quota filtering for non-English users.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/app/lib/surveys/surveys.ts` around lines 568 - 587, processQuotaFilters uses hardcoded, localized strings in statusMap which breaks for translations; change it to map from an unlocalized identifier (e.g., the combo value id/key) instead of the displayed text. In the processQuotaFilters function, replace the statusMap keys that use String(filterType.filterComboBoxValue) with the stable identifier field on the filter combo value (for example filterType.filterComboBoxValueId or filterType.filterComboBoxValue.key), update the lookup to use that id (e.g., const op = statusMap[filterType.filterComboBoxValueId]) and ensure you keep a safe fallback (no-op) if the id is missing; this preserves the existing behavior while removing reliance on translated strings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@apps/web/app/lib/surveys/surveys.test.ts`:
- Around line 565-584: The test is using the English string "Filled out" which
matches the old broken implementation; update the selectedFilter fixture so
filter.filter[0].filterType.filterComboBoxValue uses the stable identifier the
code now expects (e.g., the enum/value used by processFilledOutSkippedFilter
such as "filledOut" or FilterComboBoxValue.FilledOut) for the openTextQ case so
getFormattedFilters(survey, selectedFilter, ...) produces { op: "filledOut" } as
asserted; adjust the selectedFilter construction in this test (and any similar
tests) to reference that stable identifier rather than the English label.
In `@apps/web/app/lib/surveys/surveys.ts`:
- Around line 538-566: processMetaFilters currently references META_OP_MAP which
is defined only inside generateElementAndFilterOptions, causing a
ReferenceError; fix by either (A) defining META_OP_MAP at module scope so
processMetaFilters can access it, or (B) add a parameter (e.g., metaOpMap) to
processMetaFilters and pass the existing META_OP_MAP from
generateElementAndFilterOptions (update all callers accordingly); ensure you
update the function signature of processMetaFilters and every call site
(including generateElementAndFilterOptions) or move the constant above the
function definition so the symbol is available at module scope.
- Around line 302-403: The helper functions (processFilledOutSkippedFilter,
processRankingFilter, processMultipleChoiceFilter, processNPSRatingFilter,
processCTAFilter, processConsentFilter) are comparing against hardcoded English
labels which breaks i18n; change the filter option shape so UI displays
translated labels but also carries a stable identifier (e.g., add fields like
optionId / valueId / comboId on conditionOptions/filterOptions and ensure
ElementFilterComboBox passes those ids into getFormattedFilters), then update
each helper to switch on these stable identifiers instead of
filterType.filterComboBoxValue or filterType.filterValue (and still parse ints
for NPS/rating when the identifier expects numeric values). Ensure UI uses the
label for display only and logic uses the stable id for comparisons.
- Around line 405-431: processPictureSelectionFilter currently parses the choice
index from the translated display string (option.split(" ")[1]) which breaks
localization; instead store the choice index or id when building the combo
options (e.g., include an explicit value/index field on each filterComboBoxValue
entry) and update processPictureSelectionFilter to read that stored index/id
(from filterType.filterComboBoxValue[*].value or similar) to map to
element.choices (use element?.choices[index]?.id or the stored choice id) so
parsing of translated labels is eliminated. Ensure the code references
processPictureSelectionFilter, filterType.filterComboBoxValue, and
getElementsFromBlocks element.
---
Duplicate comments:
In `@apps/web/app/lib/surveys/surveys.ts`:
- Around line 568-587: processQuotaFilters uses hardcoded, localized strings in
statusMap which breaks for translations; change it to map from an unlocalized
identifier (e.g., the combo value id/key) instead of the displayed text. In the
processQuotaFilters function, replace the statusMap keys that use
String(filterType.filterComboBoxValue) with the stable identifier field on the
filter combo value (for example filterType.filterComboBoxValueId or
filterType.filterComboBoxValue.key), update the lookup to use that id (e.g.,
const op = statusMap[filterType.filterComboBoxValueId]) and ensure you keep a
safe fallback (no-op) if the id is missing; this preserves the existing behavior
while removing reliance on translated strings.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 4e2dbdf7-c4c6-41ac-ad4f-fd1667d27bf5
⛔ Files ignored due to path filters (14)
apps/web/locales/de-DE.jsonis excluded by!apps/web/locales/**apps/web/locales/en-US.jsonis excluded by!apps/web/locales/**apps/web/locales/es-ES.jsonis excluded by!apps/web/locales/**apps/web/locales/fr-FR.jsonis excluded by!apps/web/locales/**apps/web/locales/hu-HU.jsonis excluded by!apps/web/locales/**apps/web/locales/ja-JP.jsonis excluded by!apps/web/locales/**apps/web/locales/nl-NL.jsonis excluded by!apps/web/locales/**apps/web/locales/pt-BR.jsonis excluded by!apps/web/locales/**apps/web/locales/pt-PT.jsonis excluded by!apps/web/locales/**apps/web/locales/ro-RO.jsonis excluded by!apps/web/locales/**apps/web/locales/ru-RU.jsonis excluded by!apps/web/locales/**apps/web/locales/sv-SE.jsonis excluded by!apps/web/locales/**apps/web/locales/zh-Hans-CN.jsonis excluded by!apps/web/locales/**apps/web/locales/zh-Hant-TW.jsonis excluded by!apps/web/locales/**
📒 Files selected for processing (3)
apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/ResponseFilter.tsxapps/web/app/lib/surveys/surveys.test.tsapps/web/app/lib/surveys/surveys.ts
|
@Dhruwang not relevant for 4.9, lets get this wrapped up for 5.0 :) |
|
Hey @gulshank0, thanks for working on this! Unfortunately, we need to close this PR due to a fundamental design issue. The problem: The current approach uses translated strings as both display labels and machine-comparable values. This means filtering breaks in every non-English locale. For example, the filter options now produce translated values like There's also a runtime bug where The correct approach would be to separate filter values (locale-independent keys used for logic) from display labels (translated text shown in the UI). For example: type FilterOption = { value: string; label: string };
// value stays constant, label gets translated
{ value: "equals", label: t("environments.surveys.filter.equals") }This way the comparison logic in We appreciate the effort and the thorough translations across all 14 locales! If you'd like to take another crack at it with the value/label separation approach, we'd be happy to review a new PR. |
What does this PR do?
This PR solves the " Filter criteria as translatable "
Fixed: Added proper native translations to all languages.
Fixes #7653
Previously:
After Fix:

How should this be tested?
Checklist
Required
pnpm buildconsole.logsgit pull origin mainAppreciated