Documenta il gap i18n Android→iOS per continuare sul Mac.
Checklist operativa con file, chiavi stringa e bump versione per allineare l’app iOS alla release Android 2.0.3. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,184 @@
|
|||||||
|
# Gap Android → iOS: allineamento app nativa
|
||||||
|
|
||||||
|
Documento operativo per continuare su **Mac** lo sviluppo iOS e rilasciare un’app allineata a quanto già fatto (e rilasciato) su Android.
|
||||||
|
|
||||||
|
**Data:** 24 luglio 2026
|
||||||
|
**Riferimento Android rilasciato:** `2.0.3-native` (`versionCode` 24), API `https://www.matchlivetv.it`
|
||||||
|
**Stato iOS attuale:** marketing `2.0.0`, build `21` — i18n solo parziale
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Contesto
|
||||||
|
|
||||||
|
Dopo il rilascio web i18n (IT/EN/FR/DE/ES) e dell’APK Android di produzione, l’app Android ha:
|
||||||
|
|
||||||
|
- selettore lingua persistito
|
||||||
|
- Login + hub Partite localizzati in 5 lingue
|
||||||
|
- bump versione 2.0.3 / 24
|
||||||
|
|
||||||
|
Su iOS l’infrastruttura lingua esiste (`AppLanguage` + picker), ma **Matches e slogan restano in italiano** e la versione è indietro.
|
||||||
|
|
||||||
|
API produzione: entrambe le app puntano già a `https://www.matchlivetv.it` di default — **nessun gap di endpoint**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Stato a confronto
|
||||||
|
|
||||||
|
| Area | Android | iOS |
|
||||||
|
|------|---------|-----|
|
||||||
|
| Persistenza lingua | SharedPreferences `mltv_locale` + `AppLocale.wrap` + recreate | UserDefaults `mltv.appLanguage` + notification |
|
||||||
|
| Picker lingua | `LanguagePickerDialog` su Login + Matches | `LanguagePickerView` su Login + Matches |
|
||||||
|
| Login localizzato | Sì (5 lingue) | Sì (5 lingue via `L10n`) |
|
||||||
|
| Hub Partite localizzato | Sì (~20 stringhe hub) | **No** — testi IT hardcoded |
|
||||||
|
| Slogan wordmark | `R.string.app_slogan` | Chiave `app.slogan` in `L10n` **non usata** |
|
||||||
|
| Catalogo stringhe | `res/values*` (47 chiavi × 5) | Dizionario in `AppLanguage.swift` (~14 chiavi) |
|
||||||
|
| Logout UI | Icona | Testo «Esci» |
|
||||||
|
| Versione | `2.0.3-native` / 24 | `2.0.0` / 21 |
|
||||||
|
| RTMP ingest rewrite | `RtmpIngestUrl.kt` + test | `MediaUrl.swift` (ok in prod; semantica dev leggermente diversa) |
|
||||||
|
| Wizard / Broadcast | Ancora IT hardcoded | Ancora IT hardcoded (stesso debito) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File di riferimento
|
||||||
|
|
||||||
|
### Android (sorgente “già fatto”)
|
||||||
|
|
||||||
|
| Ruolo | Path |
|
||||||
|
|-------|------|
|
||||||
|
| Locale | `native/android/app/src/main/kotlin/.../core/AppLocale.kt` |
|
||||||
|
| Picker | `native/android/app/src/main/kotlin/.../ui/components/LanguagePickerDialog.kt` |
|
||||||
|
| Activity | `native/android/app/src/main/kotlin/.../MainActivity.kt` |
|
||||||
|
| Stringhe IT | `native/android/app/src/main/res/values/strings.xml` |
|
||||||
|
| Stringhe EN/FR/DE/ES | `native/android/app/src/main/res/values-{en,fr,de,es}/strings.xml` |
|
||||||
|
| Hub | `.../ui/matches/MatchesScreen.kt`, `MatchSheets.kt` |
|
||||||
|
| Login | `.../ui/login/LoginScreen.kt` |
|
||||||
|
| Versione | `native/android/app/build.gradle.kts` |
|
||||||
|
|
||||||
|
### iOS (da aggiornare)
|
||||||
|
|
||||||
|
| Ruolo | Path |
|
||||||
|
|-------|------|
|
||||||
|
| Lingua + L10n | `native/ios/MatchLiveTv/Core/AppLanguage.swift` |
|
||||||
|
| Login | `native/ios/MatchLiveTv/UI/Login/LoginScreen.swift` |
|
||||||
|
| Hub | `native/ios/MatchLiveTv/UI/Matches/MatchesScreen.swift` |
|
||||||
|
| Wordmark | `native/ios/MatchLiveTv/UI/Components/MatchLiveWordmark.swift` (verificare path esatto) |
|
||||||
|
| Versioni | `native/ios/generate_xcodeproj.py`, `MatchLiveTv.xcodeproj/project.pbxproj`, `Info.plist` |
|
||||||
|
| Build release | `scripts/build_ios_release.sh` |
|
||||||
|
| README | `native/ios/README.md` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Checklist allineamento iOS (ordine consigliato)
|
||||||
|
|
||||||
|
### 1. Completare stringhe hub (priorità alta)
|
||||||
|
|
||||||
|
In `AppLanguage.swift` (`L10n.table`), aggiungere chiavi allineate ad Android per **it/en/fr/de/es**. Mapping suggerito:
|
||||||
|
|
||||||
|
| Chiave iOS (`L10n`) | Android `strings.xml` | Note |
|
||||||
|
|---------------------|----------------------|------|
|
||||||
|
| `matches.hello` | `matches_hello` | formato `"Ciao, %@"` / `"Hi, %@"` |
|
||||||
|
| `matches.subtitle` | `matches_subtitle` | |
|
||||||
|
| `matches.schedule` | `matches_schedule` | |
|
||||||
|
| `matches.new` | `matches_new` | |
|
||||||
|
| `matches.empty_title` | `matches_empty_title` | |
|
||||||
|
| `matches.scheduled_title` | `matches_scheduled_title` | |
|
||||||
|
| `matches.ready_title` | `matches_ready_title` | |
|
||||||
|
| `matches.empty_hint` | `matches_empty_hint` | |
|
||||||
|
| `matches.active_team` | `matches_active_team` | `%@` |
|
||||||
|
| `matches.multi_team_hint` | `matches_multi_team_hint` | |
|
||||||
|
| `matches.no_other` | `matches_no_other` | |
|
||||||
|
| `matches.retry` | `matches_retry` | |
|
||||||
|
| `matches.load_error` | `matches_load_error` | |
|
||||||
|
| `matches.no_team_title` | `matches_no_team_title` | |
|
||||||
|
| `matches.no_team_body` | `matches_no_team_body` | |
|
||||||
|
| `matches.tap_change_team` | `matches_tap_change_team` | |
|
||||||
|
| `matches.team_for_live` | `matches_team_for_live` | |
|
||||||
|
| `matches.change` | `matches_change` | |
|
||||||
|
| `action.save` | `action_save` | se usato in sheet |
|
||||||
|
| `language.*` | già presenti | |
|
||||||
|
|
||||||
|
Copiare le traduzioni da `values-en|fr|de|es/strings.xml` (testo già validato su Android).
|
||||||
|
|
||||||
|
Poi in `MatchesScreen.swift`:
|
||||||
|
|
||||||
|
- sostituire ogni `Text("…")` / titolo sezione / empty state / alert con `L10n.t(...)`
|
||||||
|
- empty “nessuna squadra” → `matches.no_team_title` / `matches.no_team_body`
|
||||||
|
- assicurarsi che `.id(languageTick)` (o equivalente) ricarichi l’hub al cambio lingua
|
||||||
|
|
||||||
|
### 2. Slogan wordmark (priorità alta)
|
||||||
|
|
||||||
|
In `MatchLiveWordmark` (o componente equivalente):
|
||||||
|
|
||||||
|
- usare `L10n.t("app.slogan")` al posto della stringa italiana fissa
|
||||||
|
- verificare sul Login in EN/FR/DE/ES
|
||||||
|
|
||||||
|
### 3. Versione release (priorità media)
|
||||||
|
|
||||||
|
Allineare a Android (o policy store equivalente):
|
||||||
|
|
||||||
|
- `MARKETING_VERSION` → `2.0.3` (o `2.0.3-native` se accettato da App Store Connect)
|
||||||
|
- `CURRENT_PROJECT_VERSION` → `24` (o build successiva se 21/22/23 già usati su TestFlight)
|
||||||
|
|
||||||
|
Aggiornare **sia** `generate_xcodeproj.py` **sia** `Info.plist` (`CFBundleShortVersionString` / `CFBundleVersion`) prima di regenerare il progetto.
|
||||||
|
|
||||||
|
### 4. Logout icon (priorità bassa)
|
||||||
|
|
||||||
|
In top bar Matches: SF Symbol tipo `rectangle.portrait.and.arrow.right` + `accessibilityLabel` da `L10n.t("action.logout")`, come icona logout Android.
|
||||||
|
|
||||||
|
### 5. Opzionale — tooling stringhe
|
||||||
|
|
||||||
|
Migrare da dizionario Swift a `Localizable.xcstrings` (o `*.lproj`) per parità con Android `values-*` e revisione traduzioni in Xcode.
|
||||||
|
|
||||||
|
### 6. Fuori scope immediato (stesso debito Android)
|
||||||
|
|
||||||
|
Wizard 3 step, Broadcast overlay, dialoghi set/partita: ancora IT hardcoded su **entrambe** le piattaforme. Non bloccano l’allineamento Login/Matches/i18n di questa release.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verifica manuale su Mac (pre-release)
|
||||||
|
|
||||||
|
1. `cd native/ios && python3 generate_xcodeproj.py`
|
||||||
|
2. Build debug su simulatore; login con utenza di produzione (o staging).
|
||||||
|
3. Cambia lingua Login → English: label + errori + slogan in EN.
|
||||||
|
4. Entra in Matches: saluto, sezioni, empty state, CTA in EN (non italiano).
|
||||||
|
5. Kill app e riapri: lingua persistita.
|
||||||
|
6. Ripeti spot-check FR/DE/ES.
|
||||||
|
7. Release: dalla root repo
|
||||||
|
`./scripts/build_ios_release.sh`
|
||||||
|
(default API già `https://www.matchlivetv.it`; override con `API_BASE_URL=...` se serve).
|
||||||
|
|
||||||
|
Test automatici (da README iOS):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd native/ios
|
||||||
|
python3 generate_xcodeproj.py
|
||||||
|
xcodebuild -project MatchLiveTv.xcodeproj -scheme MatchLiveTv \
|
||||||
|
-destination 'platform=iOS Simulator,name=iPhone 17' \
|
||||||
|
-derivedDataPath build/DerivedData \
|
||||||
|
ONLY_ACTIVE_ARCH=YES ARCHS=arm64 EXCLUDED_ARCHS=x86_64 \
|
||||||
|
test -only-testing:MatchLiveTvTests
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Note API / produzione
|
||||||
|
|
||||||
|
| | Android | iOS |
|
||||||
|
|--|---------|-----|
|
||||||
|
| Default | `-PAPI_BASE_URL` / BuildConfig | `API_BASE_URL` in build settings → Info.plist |
|
||||||
|
| Fallback | `https://www.matchlivetv.it` | idem in `AppConfig` |
|
||||||
|
| REST | `{base}/api/v1` | idem |
|
||||||
|
| Cable | `wss://…/cable` | idem |
|
||||||
|
|
||||||
|
Nessuna modifica server richiesta per allineare iOS: stesso backend già in produzione con i18n web.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sintesi priorità
|
||||||
|
|
||||||
|
1. **Alta** — Localizzare hub Matches (oggi cambio lingua incompleto).
|
||||||
|
2. **Alta** — Collegare slogan wordmark a `L10n`.
|
||||||
|
3. **Media** — Bump versione 2.0.3 / 24.
|
||||||
|
4. **Bassa** — Icona logout; migrazione a xcstrings; fine-tuning RTMP solo per dev.
|
||||||
|
|
||||||
|
Quando questi punti sono chiusi, l’app iOS è allineata alla release Android `2.0.3-native` per UX lingua e store versioning.
|
||||||
@@ -79,6 +79,7 @@ xcodebuild -project MatchLiveTv.xcodeproj -scheme MatchLiveTv \
|
|||||||
|
|
||||||
## Documentazione correlata
|
## Documentazione correlata
|
||||||
|
|
||||||
|
- [`docs/IOS_ANDROID_I18N_GAP.md`](../../docs/IOS_ANDROID_I18N_GAP.md) — gap i18n/versione vs Android (checklist allineamento su Mac)
|
||||||
- [`docs/TABELLONI_E_OVERLAY.md`](../../docs/TABELLONI_E_OVERLAY.md) — regolamenti, board, overlay
|
- [`docs/TABELLONI_E_OVERLAY.md`](../../docs/TABELLONI_E_OVERLAY.md) — regolamenti, board, overlay
|
||||||
- [`docs/MULTI_SPORT.md`](../../docs/MULTI_SPORT.md) — catalogo sport e engine scoring
|
- [`docs/MULTI_SPORT.md`](../../docs/MULTI_SPORT.md) — catalogo sport e engine scoring
|
||||||
- [`docs/ARCHITECTURE.md`](../../docs/ARCHITECTURE.md) — infrastruttura e flussi live/HLS
|
- [`docs/ARCHITECTURE.md`](../../docs/ARCHITECTURE.md) — infrastruttura e flussi live/HLS
|
||||||
|
|||||||
Reference in New Issue
Block a user