Give every screenshot a predictable destination and sequence number, and your next caption revision becomes a repeatable file operation. The same organization also makes manual uploads easier: you can find one language, one device, and the intended first image without opening every PNG.
This worked example turns localized exports into fastlane's two different directory structures. It focuses on the boundary between your image generator and your uploader.
Disclosure: I build ScreenParcel, a browser tool that combines app screenshots with your own translated caption text and exports multi-locale screenshot packs as one ZIP. It does not auto-translate.
Start with the destination contract.
Suppose a release has two screenshots per language, with iPhone, iPad, and Android phone artwork. Keep iOS and Android captures in separate exports when their interfaces differ.
For iOS, put the images directly inside fastlane/screenshots/<locale>/:
fastlane/screenshots/ ├── de-DE/ │ ├── 01-device--iphone-69.png │ ├── 02-device--iphone-69.png │ ├── 01-device--ipad-13.png │ └── 02-device--ipad-13.png └── zh-Hans/ ├── 01-device--iphone-69.png ├── 02-device--iphone-69.png ├── 01-device--ipad-13.png └── 02-device--ipad-13.png Deliver detects device types from image resolution. The device suffix makes flattened filenames unique and readable; it does not assign the device slot. Its loader reads image files directly within each language folder, so an extra ordinary iphone-69/ subfolder is unsuitable here. See the deliver documentation and screenshot loader.
For Android, the device category is a directory under images/:
fastlane/metadata/android/ ├── de-DE/ │ └── images/ │ └── phoneScreenshots/ │ ├── 01-device.png │ └── 02-device.png └── zh-CN/ └── images/ └── phoneScreenshots/ ├── 01-device.png └── 02-device.png Supply also recognizes sevenInchScreenshots/, tenInchScreenshots/, tvScreenshots/, and wearScreenshots/. Add those only for appropriate assets. ScreenParcel currently exports a Google Play phone size; the script below maps that size alone to phoneScreenshots/. These directory names come from supply's image documentation.
Keep your repository's normal metadata alongside these images. A screenshot staging directory does not need to contain your titles, descriptions, or changelogs.
Treat this tree as generated output. Keep the original exports outside it, and rebuild the staging directory when artwork changes. Otherwise, removing screenshot three from an export can leave yesterday's third PNG in the upload folder. Copying a new pair over an old set cannot express that deletion.
For this example, position one demonstrates the app's main action and position two shows its result. Use that same sequence for every language and device. A reviewer can compare the two positions across folders without guessing which image belongs where.
Map store locales explicitly.
A locale is an uploader input. Do not derive it by blindly appending a country code.
| Language or audience | ScreenParcel folder | deliver folder | supply folder |
|---|---|---|---|
| German | de-DE | de-DE | de-DE |
| Simplified Chinese | zh-Hans | zh-Hans | zh-CN |
| Brazilian Portuguese | pt-BR | pt-BR | pt-BR |
| Mexican Spanish / Latin American Spanish* | es-MX | es-MX | es-419* |
| Norwegian | nb | no | no-NO |
These target identifiers are supported by fastlane's deliver language list and Google's listing-language reference.
Mexico and Latin America are different audience scopes. Reuse an es-MX export for es-419 only after approving its wording for the broader listing. A directory rename does not adapt the copy.
Norwegian deserves special attention: ScreenParcel's current code normalizes no input to nb, while deliver's accepted directory is no. Google lists no-NO, rather than nb-NO, for its Norwegian store listing. Preserve your own app's internal locale identifiers; apply this mapping at the export boundary.
Make the filename carry the sequence.
Use 01-, 02-, through 10- consistently. Plain alphabetical sorting can put 10- before 2-; padding removes that ambiguity across scripts and file pickers.
Supply uses alphanumeric filename order. Deliver's upload code applies natural filename sorting within screenshot sets. Fixed-width prefixes work with both. Deliver's sorting implementation shows that ordering step.
Keep the number first, followed by a stable label and optional device suffix. When two sizes share one iOS locale folder, 01-device--iphone-69.png and 01-device--ipad-13.png remain distinct. Their dimensions put them into separate device sets.
The current ScreenParcel editor uses one headline and subtitle per locale across uploaded screenshots. If you export separate passes for different captions, assign their final sequence numbers before combining them: every pass starts its filenames at 01.
Adapt the actual ZIP structure.
ScreenParcel currently writes PNG paths as:
<locale>/<size-id>/<two-digit-source-number>-<template>.png For example, selecting German, the device template, and these sizes produces:
de-DE/iphone-69/01-device.png de-DE/iphone-69/02-device.png de-DE/ipad-13/01-device.png de-DE/ipad-13/02-device.png de-DE/google-phone/01-device.png de-DE/google-phone/02-device.png manifest.json README.txt That is the export layout, before any fastlane adaptation. The manifest records file paths and dimensions, plus export settings, watermark status, and overflow warnings. Keep it with the original ZIP for reference.
The filename number follows uploaded source order. Keep that order stable between platform exports so 01 still refers to the same position in the screenshot story. Renaming files later should preserve that relationship.
This Bash script requires unzip. Give it an iOS export, an Android export, and a new output directory. Select the intended Apple sizes in the first export and google-phone in the second. The Play allowlist covers the table above plus US English; add explicit mappings for other languages.
#!/usr/bin/env bash set -euo pipefail # Usage: bash stage-screens.sh ios.zip android.zip release-fastlane ios_zip=${1:?Provide the iOS ZIP} android_zip=${2:?Provide the Android ZIP} out=${3:?Provide a NEW output directory} mkdir -- "$out" # Refuse to mix with an existing directory. tmp=$(mktemp -d) trap 'rm -rf -- "$tmp"' EXIT unzip -q "$ios_zip" -d "$tmp/ios" unzip -q "$android_zip" -d "$tmp/android" for platform in ios android; do root="$tmp/$platform" for src in "$root"/*/*/*.png; do [[ -f "$src" ]] || continue rel=${src#"$root/"} locale=${rel%%/*} tail=${rel#*/} size=${tail%%/*} name=${tail##*/} case "$platform:$size" in ios:iphone-69|ios:iphone-65|ios:iphone-medium|ios:ipad-13) [[ "$locale" != nb ]] || locale=no dest="$out/screenshots/$locale/${name%.png}--$size.png" ;; android:google-phone) case "$locale" in en-US|de-DE|pt-BR) ;; zh-Hans) locale=zh-CN ;; es-MX) locale=es-419 ;; # Only with approved LatAm copy. nb) locale=no-NO ;; *) printf 'Add a Play mapping for %s\n' "$locale" >&2; exit 1 ;; esac dest="$out/metadata/android/$locale/images/phoneScreenshots/$name" ;; *) continue ;; esac mkdir -p -- "${dest%/*}" cp -- "$src" "$dest" done done The script copies files without changing their pixels. It flattens Apple size folders, preserves sequence prefixes, and maps Play phone assets into their device directory. Unsupported Play locales stop the script; fix the mapping and rerun with a fresh output directory.
Point fastlane at the staged files.
With authentication and app identifiers already configured, these commands upload screenshots from the new tree:
bundle exec fastlane deliver \ --screenshots_path ./release-fastlane/screenshots \ --skip_binary_upload true --skip_metadata true bundle exec fastlane supply \ --metadata_path ./release-fastlane/metadata/android \ --skip_upload_apk true --skip_upload_aab true \ --skip_upload_metadata true --skip_upload_images true \ --skip_upload_changelogs true Review existing remote assets when choosing replacement behavior. Supply replaces the screenshot set it uploads. Deliver offers overwrite_screenshots to clear existing screenshots before upload; use it only when the staged files cover the intended replacement scope. Both behaviors are documented in the linked action references.
For manual uploads, use the same staged tree. On Apple, filter a locale folder by its device suffix, then select the numbered images. On Play, open the matching phoneScreenshots/ folder. Sort by name and confirm the console's displayed sequence; a file picker need not preserve selection order.
Try ScreenParcel with your own screenshots and translated captions. Two locales are free with a watermark; the Release Pack is $9 and the 30-day Pass is $19. The ZIP gives you organized source files, and a small adapter gives your uploader the layout it expects.



