Skip to content

Localisation

Every word the built-in chrome shows or reads to a screen reader comes from one strings map with English defaults. Hand the player a partial map in any language: the keys you set replace the English text, the keys you leave out stay English. The key set is the same on Android, iOS, the web, smart TVs, React Native and Flutter, so the one map your app already keeps next to its other translations localises every client.

There is no automatic language detection: the chrome stays English until you pass strings, and it never reads your app’s own resource bundles or string tables. You choose the language the way you choose it for the rest of your UI, then hand the player the matching map.

  • Every visible word of the chrome — menu rows (subtitles Off, speed Normal, quality Auto), the LIVE chip, the up-next card, the ad chip, pod position, Skip and Learn more, the error overlay’s message and Retry, the casting and AirPlay status, the vertical feed’s sponsored chip, the web ad-blocker notice and the Android download notification.
  • Every accessibility label — the same map is what TalkBack, VoiceOver and screen readers on the web announce for each control (see Accessibility below).

Not part of the map:

  • Time formats — m:ss / h:mm:ss, the minus before a live offset and the / between position and duration.
  • Content-rating pictograms — the age and descriptor icons are artwork, not text.
  • Technical error messages — OGPlayerError.message stays English; it is meant for your logs. What the viewer reads is errorGeneric or your own error copy.
  • Your content — titles, track labels from the manifest and subtitle text appear as delivered.
PlatformPlayer chromeVertical feedTrack-name language
Android (incl. Android TV)OGUiConfig.Builder().setStrings(OGStrings)OGVerticalFeedConfig.Builder().setStrings(…)— (manifest label)
iOS (incl. Apple TV)OGUIConfig.stringsOGVerticalFeedConfig.strings— (system)
Webel.config = { strings }feed.config = { strings }<og-player lang> or config.locale
Smart TV (ogplayer/tv)el.config = { strings } — the web API— (no feed on TV)<og-player lang> or config.locale
React NativeuiConfig={{ strings }}config={{ strings }}— (native players)
FlutterOGUIConfig(strings: OGStrings(…))OGVerticalFeedConfig(strings: …)— (native players)

A partial Dutch map on each platform — the full list of keys is in the key table below.

// One flat key → text map — the same keys on every OGPlayer platform.
val dutch = OGStrings.fromMap(
mapOf(
"subtitles" to "Ondertiteling",
"subtitlesOff" to "Uit",
"playbackSpeed" to "Afspeelsnelheid",
"speedNormal" to "Normaal",
"quality" to "Videokwaliteit",
"qualityAuto" to "Automatisch",
"seekForward" to "{seconds} seconden vooruit",
"seekBackward" to "{seconds} seconden terug",
"goLive" to "Naar live",
"errorGeneric" to "Afspeelfout {code}",
"retry" to "Opnieuw proberen",
),
)
// …or typed: OGStrings(subtitles = "Ondertiteling", retry = "Opnieuw proberen")
OGPlayerView(
player = player,
uiConfig = OGUiConfig.Builder()
.setStrings(dutch)
.build(),
)

OGStrings is a Kotlin data class with every key as a property; OGStrings.fromMap keeps English for missing keys and ignores keys it does not know.

Keys a platform does not show are simply unused there — the AirPlay keys on Android, the cast keys on iOS, the ad-blocker notice outside the web — so one complete map fits every client unchanged. Keeping the map as JSON beside your other translations works directly with OGStrings.fromMap (Android, Flutter), OGStrings(map:) (iOS) and strings (web, React Native).

Some strings carry {name} placeholders that the player fills in:

PlaceholderFilled withKeys
{seconds}a number of secondsseekForward, seekBackward, upNext, skipIn
{title}the next item’s titleupNext, playNext
{n}a 1-based positioncustomAction, audioFallback, subtitlesFallback
{code}the numeric error codeerrorGeneric
{index}, {count}position and size of an ad breakadPod
{count}the number of running downloadsdownloadingItems
{device}the receiver’s namecastingTo, airPlayTo
{speed}a playback rate such as 1.5speedValue
{height}a rendition’s height in pixelsqualityHeight
{kbps}a rendition’s bitratequalityBitrate
{name}, {channels}a track name and its channel countaudioChannels
  • Values are inserted verbatim — no escaping, no plural rules, no ICU syntax. Where a count of one reads differently, the map has its own key (downloadingItemsOne).
  • Put the placeholders wherever your language needs them, or leave one out: "Volgende over {seconds}" and "{title} begint zo" are both valid upNext values.
  • An unknown placeholder is left in the text as written; an inserted value is never scanned again, so a title that contains {seconds} stays literal.

Three options that predate the strings map keep working and win where you set them, so an integration that already localises these keeps its copy:

Shown textWinsThenThen
Error overlay messageerrorMessageProvider (Android, iOS, web) · errorMessages (React Native, Flutter), where it has text for the codestrings.errorGenericPlayback error {code}
Retry buttonretryButtonLabelstrings.retryRetry
Up-next cardupNextTextstrings.upNextNext in {seconds}

A provider that returns nothing for a code falls through to errorGeneric, so you can write copy for the codes you care about and let the map cover the rest.

The subtitle and audio menus list the stream’s own track names. Where a track has no name, each platform names it its own way:

  • Web — a track that carries only a language code is named through the browser’s Intl.DisplayNames, in the language you set with config.locale or the element’s lang attribute (<og-player lang="nl"> turns de into Duits). The default is English — the page’s or the browser’s language is never assumed — and config.locale wins over the attribute. Headless pages set player.locale on the engine.
  • iOS — AVFoundation names the tracks in the device language; no option is needed.
  • Android — the manifest’s label, else the language code in capitals (NL), else trackUnknown.
  • React Native and Flutter — locale is accepted so one config fits every platform; the Android and iOS players name tracks as described above.

A track that is still nameless reads audioFallback / subtitlesFallback (Audio 2) on iOS and the web.

The vertical feed takes the same strings type through its own config. It reads play, retry, errorGeneric and sponsored:

OGVerticalFeedView(
items = items,
config = OGVerticalFeedConfig.Builder()
.setStrings(dutch)
.build(),
)

On Android, iOS and the web the feed’s errorMessageProvider still wins over errorGeneric.

The offline downloads notification lives in ogplayer-core, so it takes its three keys through the downloads config. OGStrings.toDownloadStrings() hands them over from the map you already built:

OGDownloads.initialize(
context,
OGDownloadsConfig.Builder()
.setNotificationChannelName("Downloads")
.setStrings(dutch.toDownloadStrings())
// or OGDownloadStrings(downloading = "Downloaden", …) without the UI module
.build(),
)

A single download shows its own title (downloading when it has none), several show downloadingItems, and with nothing running the notification carries the channel name. React Native and Flutter accept the download keys as part of the map; their download notification keeps the English text.

The strings map is also the chrome’s accessibility vocabulary, and every control carries a label on every platform — TalkBack on Android, VoiceOver on iOS and tvOS, ARIA labels on the web and the TV bundle; React Native and Flutter inherit the native players’ labels.

  • Play/pause announces the action a press takes — Play, Pause, or Replay once the item has ended.
  • Seek buttons announce their step: Seek forward 10 seconds.
  • Scrub bar and volume are announced as sliders on every platform (seekBar, volume): each reports its current value — the playback position, the volume level — and adjusts with the screen reader’s slider gestures.
  • The LIVE chip is announced as Go to live (goLive) — pressing it returns to the live edge.
  • Mute flips with the state: Mute while the sound is on, Unmute while muted.
  • Custom action icons read their own accessibilityLabel, or Custom action 1, 2, … when you give none.

The demo apps on every platform include a Localised chrome (nl) scenario with a complete Dutch map — see Demo apps.

Every key, its English default and where it appears. Keys marked with a platform are shown only there; the rest appear wherever the element exists (a TV chrome, for instance, has no fullscreen button).

KeyEnglish defaultWhere it appearsPlaceholders
playPlayPlay button (screen readers); the vertical feed’s play glyph; the play action of Android’s picture-in-picture window—
pausePausePause button (screen readers); the pause action of Android’s picture-in-picture window—
replayReplayPlay button once the item has ended (screen readers)—
seekForwardSeek forward {seconds} secondsSeek-forward button (screen readers){seconds}
seekBackwardSeek back {seconds} secondsSeek-back button (screen readers){seconds}
nextNextPlaylist next button of the TV remote chrome (screen readers)—
previousPreviousPlaylist previous button of the TV remote chrome (screen readers)—
volumeVolumeVolume button and volume slider (screen readers)—
muteMuteMute action while the sound is on (screen readers)—
unmuteUnmuteUnmute action while muted (screen readers)—
enterFullscreenEnter fullscreenFullscreen button while embedded (screen readers)—
exitFullscreenExit fullscreenFullscreen button in fullscreen (screen readers)—
seekBarSeekScrub bar (screen readers)—
customActionCustom action {n}A custom action icon without its own accessibility label{n}
KeyEnglish defaultWhere it appearsPlaceholders
subtitlesSubtitlesSubtitles button (screen readers)—
subtitlesOffOffThe subtitles-menu row that turns subtitles off—
audioAudioAudio-track button (screen readers)—
playbackSpeedPlayback speedSpeed button (screen readers)—
speedNormalNormalThe 1× row of the speed menu—
speedValue{speed}×Every other row of the speed menu{speed}
qualityVideo qualityQuality button (screen readers)—
qualityAutoAutoThe adaptive row of the quality menu—
qualityHeight{height}pA resolution row of the quality menu{height}
qualityBitrate{kbps} kbpsWeb: the bitrate beside a quality row{kbps}
qualityAdaptiveadaptiveWeb: the note beside the adaptive row—
audioDefaultDefaultWeb: the single row of an audio menu without tracks—
trackUnknownUnknownAndroid: a track with neither a label nor a language—
audioFallbackAudio {n}iOS, web: an audio track without a name{n}
subtitlesFallbackSubtitles {n}iOS, web: a subtitle track without a name{n}
audioChannels{name} · {channels}chAn audio row qualified by its channel count{name}, {channels}
KeyEnglish defaultWhere it appearsPlaceholders
liveLIVEThe LIVE chip; on Android and iOS also the time readout at the live edge—
goLiveGo to liveThe LIVE chip (screen readers) — pressing it returns to the live edge—
KeyEnglish defaultWhere it appearsPlaceholders
upNextNext in {seconds}The up-next card{seconds}
playNextPlay next: {title}The up-next card (screen readers){title}
nextVideonext videoThe title in playNext when the next item has none—
KeyEnglish defaultWhere it appearsPlaceholders
adADThe ad chip—
adPod{index}/{count}Position within a multi-ad break{index}, {count}
learnMoreLearn moreAndroid, iOS: the clickthrough button of the SDK’s ad chrome—
skipAdSkip adAndroid, web remote chrome: the skip button once the ad can be skipped—
skipInSkip in {seconds}Android, web remote chrome: the skip countdown{seconds}
pauseAdPause adAd play/pause button while the ad plays (screen readers)—
resumeAdResume adAd play/pause button while the ad is paused (screen readers)—
adBlockedTitleAds are blockedWeb: the ad-blocker notice title—
adBlockedTextThis video is made available with ads, but your ad blocker prevented them from loading. Your video plays on without ads — please consider allowing ads for this site to support it.Web: the ad-blocker notice, adBlockerPolicy: "notice"—
adBlockedTextHardThis video is only available with ads. Please disable your ad blocker for this site, then reload.Web: the ad-blocker notice, adBlockerPolicy: "block"—
adBlockedDismissGot itWeb: the notice button, notice policy—
adBlockedReloadI disabled it — reloadWeb: the notice button, block policy—
dismissDismissWeb: the notice button (screen readers)—
KeyEnglish defaultWhere it appearsPlaceholders
errorGenericPlayback error {code}The error overlay’s message; the vertical feed’s error state{code}
retryRetryThe Retry button of the error overlay and the vertical feed—
KeyEnglish defaultWhere it appearsPlaceholders
downloadingDownloadingA single download without a title—
downloadingItemsOneDownloading 1 itemThe counted title for one download—
downloadingItemsDownloading {count} itemsThe counted title for several downloads{count}
KeyEnglish defaultWhere it appearsPlaceholders
castCastAndroid: the cast button (screen readers)—
castConnectingConnecting to cast deviceAndroid: the cast button while connecting—
castingCastingAndroid: the cast button while casting; the casting overlay without a device name—
castConnectingStatusConnecting…Android: the casting overlay while connecting—
castingToCasting to {device}Android: the casting overlay{device}
airPlayAirPlayiOS: the AirPlay overlay without a device name—
airPlayToAirPlay — {device}iOS: the AirPlay overlay{device}
pipPlayingPlaying in picture in pictureiOS: status text on the player while the video plays in picture in picture—
KeyEnglish defaultWhere it appearsPlaceholders
sponsoredSponsoredThe feed’s default sponsored chip—