Skip to main content

Localize TipKit Tips and Compose Rich Tooltips Without Leaving English Feature Hints

2026-11-10

Localize TipKit Tips and Compose Rich Tooltips Without Leaving English Feature Hints

A multilingual shopping, productivity, or utility app can look finished on every screen and still teach new features in English. TipKit popovers show English titles and messages. Action buttons at the bottom of a tip stay on "Try now" or "Learn more". Jetpack Compose PlainTooltip and RichTooltip overlays keep English title, body, dismiss, and content-description strings copied from sample code. Translators usually finished the product UI. The coaching layer still drifted because Apple's Tip protocol and Android's TooltipBox APIs own education chrome that never entered the ordinary screen catalog.

Give feature tips the same release discipline as checkout or permissions. Inventory TipKit and Compose tooltip strings apart from host screens, localize Action labels and tooltip parameters on purpose, and prove both surfaces after real Language and Region changes.

Why tip and tooltip chrome drifts from the app language

Feature education has two owners. The screen owns the control you are teaching. The tip or tooltip owns the short coaching copy the system or Material layer draws beside that control. Apple's Tip protocol defines tip content through a title, optional message, optional image, and a list of actions. Highlighting app features with TipKit shows how apps call Tips.configure, place inline TipView or popover tips, and attach Action buttons that send people to more help. Android's Compose tooltip guide explains TooltipBox with PlainTooltip for short labels and RichTooltip for title, multi-line body, and optional action slots.

The localization gap shows up in six places:

  1. Screen strings live in the host String Catalog or strings.xml. Tip title / message and Compose tooltip parameters often start as English literals inside tip structs or composable defaults.
  2. TipKit Text titles extract into catalogs when you use LocalizedStringKey or comment-bearing Text initializers, but Tip.Action titles take String, so a different localization path is required.
  3. Official Compose samples ship English defaults such as "Add to favorites", "Rich Tooltip", and "Dismiss", which teams paste forward without wrapping stringResource.
  4. Tips.configure controls display frequency and eligibility. It does not translate strings. A configured tip can still render English copy.
  5. Compact tip chrome truncates long German or French coaching text. Action rows and rich-tooltip titles need translator length notes.
  6. After an in-app or system language change, tips that stored eagerly resolved String values keep the old language until the tip type rebuilds its localized fields.

Practitioner materials line up with that split. A public Stack Overflow thread about TipKit integration includes English sample titles such as "Useless title" and "Add a new game", while an answer demonstrates Text("…", comment:) for tip copy and Action(title: String(localized: …)) for buttons (TipKit SwiftUI or UIKit integration). That is author-reported evidence that Action labels are easy to leave in English even when someone starts localizing tip titles. tanaschita's TipKit tutorial uses Text(LocalizedStringKey(...)) for title and message, which is the right pattern for those fields, but it does not cover Action strings or Compose RichTooltip parameters (How to use TipKit to create tool tips in SwiftUI). Phrase's iOS localization overview and the live i18nAgent iOS guide cover catalogs and UI strings. They do not connect TipKit Action localization to Compose tooltip resource ownership. The Android localization guide likewise stops short of RichTooltip education chrome. General in-app language refresh, first-launch language choice, and hard-coded-string CI scans covered elsewhere are different problems: recomposing a screen or failing a string lint is not the same as shipping localized tip Actions and tooltip parameters.

Build a TipKit and tooltip string inventory

Localize Tip.Action button titles with String(localized:) or an equivalent deferred String path: Text-based tip title and message extraction does not cover Action labels.

For each tip or tooltip string, record:

Field Why it matters
Stable key Shared meaning across iOS tip and Android tooltip when the coaching role matches
Apple surface Tip title, message, each Tip.Action title, optional accessibility strings
Android surface PlainTooltip text, RichTooltip title, body, action label, contentDescription
Bundle owner App target catalog for TipKit types; Compose module res/values-* for tooltip params
Display path Inline TipView, popoverTip, UIKit TipUIPopoverViewController, TooltipBox long-press or hover
Fallback Base localization that remains English-complete without masking missing tip translations in QA
Verification Device Language and Region, per-app language, first tip display after cold start, Action tap, rich-tooltip dismiss

Apple's Tip documentation requires a title for a tip to be valid and presents actions as buttons under the tip body. Tip.Action describes controls created with an id and a title string: the API that "generates its label from a string." Put tip Text through localizable initializers with translator comments that the string appears in compact coaching chrome. Put every Action title through String(localized:) (or construct it so the string lands in the catalog) instead of a raw English literal. A tip whose title is localized but whose "Try now" Action stayed English still reads unfinished.

Localize Compose tooltip parameters on purpose

Treat Compose PlainTooltip and RichTooltip title, body, action, and contentDescription parameters as first-class string resources instead of shipping the English defaults from Android samples.

Android's tooltip guide shows PlainTooltip with a single text slot and RichTooltip with title, body, and optional action composables. The documented samples use English parameter defaults and English icon contentDescription values. Replace those with stringResource(R.string…) (or your localization layer) before the tooltip becomes user-visible. Keep content descriptions in the same inventory: TalkBack users hear tooltip-trigger labels even when the overlay is hidden.

Give translators a length budget. Rich tooltip titles and Action-like dismiss labels expand in German and French. If the English title already fills a phone-width card, supply a shorter preferred synonym and a maximum character note. RTL locales need the tooltip position provider and mirrored caret behavior checked on a real device, not only a screenshot of LTR English.

When the same coaching moment exists on both platforms, share the semantic key and intent in your glossary even if the Apple Tip and Android RichTooltip copy differ slightly for chrome constraints. Do not force identical wording when one surface has an Action row and the other has a single dismiss text button.

Verify after device and per-app language changes

Re-verify TipKit and Compose tooltip chrome after real device language and per-app language changes because Tips.configure and a translated host screen alone do not prove tip Action strings or tooltip parameters resolve for the new locale.

Tips.configure belongs in app startup so eligibility and display frequency work. It is not a localization proof. After someone switches Language and Region or your in-app language preference, force a tip that should still be eligible (or reset tip state in a debug build) and confirm title, message, and every Action label match the new language. On Android, long-press or programmatically show the TooltipBox and confirm title, body, action, and content description.

Watch for eagerly resolved strings. If a tip struct stores let titleText = String(localized: "…") once at construction and later feeds a non-localizing API, a language change may leave stale copy until process restart. Prefer Text / LocalizedStringKey for tip title and message, and rebuild Action titles with String(localized:) when the tip content is evaluated for display. Invalidate or reset tips in QA when you need to re-see first-run coaching after a locale switch.

Release checks that catch English tip chrome:

  • The String Catalog or .lproj contains every TipKit title, message, and Action title used by shipping tips.
  • Compose modules resolve tooltip title, body, action, and content-description resources for each required values-* folder.
  • QA opened the tip or tooltip on a device in the target language after a language change, not only on the host screen in English.
  • Compact-chrome length review passed for German and French on the smallest supported phone class.
  • Action taps still route correctly when labels change width after translation.

Automate what you can. Fail CI when tip keys present in English catalogs are missing in a required locale. Fail when Compose tooltip string resources are absent for a locale the host ships. Still keep a manual tip-display pass. Eligibility rules and MaxDisplayCount can hide a tip from automation that only launches the screen once.

What not to confuse with this workflow

Do not treat this as a second article about applying in-app language changes across the whole UI tree. Screen recomposition after a language switch remains a separate release surface. Do not reopen first-launch language selection or generic hard-coded-string detection here either. Those govern onboarding pickers and CI lint, not TipKit Action labels or Compose RichTooltip parameters. Home-screen widgets, Control Center controls, and launcher shortcuts also own non-Activity chrome, but their inventories and system hosts are different products with different failure modes.

Keep the rule narrow. If your app draws coaching chrome to teach a control, that chrome needs an owner, a catalog entry for every title, message, Action, and tooltip parameter, translator context for compact layout, and a post-language-change verification step. TipKit and Compose tooltips are where users notice first when that owner is missing.