You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Split out of #631. That issue fixed one field (crm_lead.disqualification_reason) and added the guard that found the rest; this one is the backlog the guard is holding open.
What the guard does
test/metadata-references.test.ts → describe('select fields are translated in every locale') asserts, for every select field on every object, that each of the four locale packs carries the field's label and a label for every option value. A missing entry is not a runtime error — the resolver falls back to the English label in code — so an untranslated picklist renders its raw stored value (not_a_fit, waiting_customer, net_30) in the middle of an otherwise translated screen, and in en the fallback is indistinguishable from a correct translation.
The fields that were already incomplete when the guard landed are listed in PENDING_SELECT_LABELS, keyed object.field → the locales that lack them. The ledger may only ever SHRINK. It is already rot-proof in both directions:
an entry that has since been translated fails the suite as stale and must be deleted;
an entry naming a field or locale that no longer exists fails as a ghost.
So no separate bookkeeping is needed here — delete the entry when you translate the field, and the tests tell you if you got it wrong.
Scope
111 (locale, field) pairs across 34 fields and 12 objects, roughly 380 option labels. Per locale: ja-JP 34, es-ES 34, en 28, zh-CN 15.
This is deliberately splittable — one object per PR is a clean unit, and each PR shrinks the ledger by its own rows without touching anyone else's.
The ledger
ALL = all four locales (en, zh-CN, ja-JP, es-ES).
Field
Options
Locales missing
Note
crm_account.tier
4
ja-JP, es-ES
no entry at all
crm_account.segment
4
ja-JP, es-ES
no entry at all
crm_account.health_score
4
ja-JP, es-ES
no entry at all
crm_campaign.type
8
en, ja-JP, es-ES
crm_campaign.channel
5
en, ja-JP, es-ES
crm_campaign.status
4
en, ja-JP, es-ES
crm_case.status
7
en, ja-JP, es-ES
crm_case.priority
4
en, ja-JP, es-ES
crm_case.type
4
ALL
crm_contact.salutation
5
ALL
crm_contact.lead_source
12
en, ja-JP, es-ES
crm_contract.status
5
en, ja-JP, es-ES
crm_contract.billing_frequency
4
ALL
crm_contract.payment_terms
5
ALL
crm_contract.contract_type
6
ALL
crm_knowledge_article.category
7
ALL
partial — only release_notes, policy missing
crm_knowledge_article.tags
8
ALL
partial — only reports, performance, data_import, webhooks missing
crm_lead.salutation
5
en, ja-JP, es-ES
crm_lead.industry
15
en, ja-JP, es-ES
zh-CN is complete; the same 15 options are fully translated on crm_account.industry in all four — copy them
crm_opportunity.competitors
3
ALL
crm_opportunity.approval_status
4
ja-JP, es-ES
no entry at all
crm_opportunity.win_reason
6
ja-JP, es-ES
no entry at all
crm_opportunity.loss_reason
7
ja-JP, es-ES
no entry at all
crm_product.category
5
en, ja-JP, es-ES
crm_product.family
4
ALL
crm_product.billing_type
5
ALL
no entry at all
crm_product.unit_of_measure
6
ALL
no entry at all
crm_quote.status
6
en, ja-JP, es-ES
crm_quote.payment_terms
5
ALL
identical option set to crm_contract.payment_terms — do both together
crm_task.status
5
en, ja-JP, es-ES
crm_task.priority
4
en, ja-JP, es-ES
crm_task.type
6
ALL
crm_task.related_to_type
5
ALL
options are object names (crm_account, crm_contact, …); reuse each object's own label from the same bundle
crm_task.recurrence_type
4
ALL
Eight of these have no translation entry whatsoever in the locales listed — the field label is untranslated too, not just its options.
Notes for whoever picks this up
crm_contact.salutation and crm_lead.salutation share an option set; so do crm_contact.lead_source, crm_lead.lead_source and crm_opportunity.lead_source — but note crm_lead.lead_source and crm_opportunity.lead_source are already complete in all four, so those are the reference translations to copy from rather than invent.
Do not "fix" this by relaxing the guard or by widening the ledger. Adding a row is a regression; the only legal edit to PENDING_SELECT_LABELS is deleting one.
Split out of #631. That issue fixed one field (
crm_lead.disqualification_reason) and added the guard that found the rest; this one is the backlog the guard is holding open.What the guard does
test/metadata-references.test.ts→describe('select fields are translated in every locale')asserts, for every select field on every object, that each of the four locale packs carries the field'slabeland a label for every option value. A missing entry is not a runtime error — the resolver falls back to the Englishlabelin code — so an untranslated picklist renders its raw stored value (not_a_fit,waiting_customer,net_30) in the middle of an otherwise translated screen, and inenthe fallback is indistinguishable from a correct translation.The fields that were already incomplete when the guard landed are listed in
PENDING_SELECT_LABELS, keyedobject.field→ the locales that lack them. The ledger may only ever SHRINK. It is already rot-proof in both directions:So no separate bookkeeping is needed here — delete the entry when you translate the field, and the tests tell you if you got it wrong.
Scope
111 (locale, field) pairs across 34 fields and 12 objects, roughly 380 option labels. Per locale:
ja-JP34,es-ES34,en28,zh-CN15.This is deliberately splittable — one object per PR is a clean unit, and each PR shrinks the ledger by its own rows without touching anyone else's.
The ledger
ALL= all four locales (en,zh-CN,ja-JP,es-ES).crm_account.tiercrm_account.segmentcrm_account.health_scorecrm_campaign.typecrm_campaign.channelcrm_campaign.statuscrm_case.statuscrm_case.prioritycrm_case.typecrm_contact.salutationcrm_contact.lead_sourcecrm_contract.statuscrm_contract.billing_frequencycrm_contract.payment_termscrm_contract.contract_typecrm_knowledge_article.categoryrelease_notes,policymissingcrm_knowledge_article.tagsreports,performance,data_import,webhooksmissingcrm_lead.salutationcrm_lead.industrycrm_account.industryin all four — copy themcrm_opportunity.competitorscrm_opportunity.approval_statuscrm_opportunity.win_reasoncrm_opportunity.loss_reasoncrm_product.categorycrm_product.familycrm_product.billing_typecrm_product.unit_of_measurecrm_quote.statuscrm_quote.payment_termscrm_contract.payment_terms— do both togethercrm_task.statuscrm_task.prioritycrm_task.typecrm_task.related_to_typecrm_account,crm_contact, …); reuse each object's ownlabelfrom the same bundlecrm_task.recurrence_typeEight of these have no translation entry whatsoever in the locales listed — the field label is untranslated too, not just its options.
Notes for whoever picks this up
crm_contact.salutationandcrm_lead.salutationshare an option set; so docrm_contact.lead_source,crm_lead.lead_sourceandcrm_opportunity.lead_source— but notecrm_lead.lead_sourceandcrm_opportunity.lead_sourceare already complete in all four, so those are the reference translations to copy from rather than invent.PENDING_SELECT_LABELSis deleting one.crm_lead.disqualification_reasonhas no i18n entry in any of the four locale bundles #631's zh-CN entry followsunqualified: '未通过'and is therefore未通过原因, not a literal rendering of "disqualification".