1.2.0: localized App Store screenshots (es/de/fr/it/pt-BR) + 1.2.0 release notes
- Caption-localized the 5 iPhone 6.7" + 5 iPad 12.9" screenshots into the 5 non-English locales, reusing the en-US framed captures (in-frame UI stays EN). Tooling in fastlane/screenshot_localize/ (Pillow: repaints the pastel caption band, re-renders headline/subtitle in SF Pro; auto-fits + wraps per language). - Rewrote release_notes 1.2.0 in all 6 languages: since 1.1.5 never shipped publicly (last public = 1.1.4), the notes consolidate 1.1.5 + 1.2.0 features. - Fastfile: new `publish` lane — uploads metadata + screenshots for an existing TestFlight build (skip_binary_upload, no submit) so offer codes can be set up before submission. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BkanrydYtrme8wipTzWssG
@@ -38,6 +38,26 @@ platform :ios do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
desc "Upload metadata + localized screenshots for an existing TestFlight build (no rebuild, no submit)"
|
||||||
|
lane :publish do |options|
|
||||||
|
version = options[:version] || "1.2.0"
|
||||||
|
build = options[:build] || "55"
|
||||||
|
upload_to_app_store(
|
||||||
|
api_key: asc_api_key,
|
||||||
|
app_version: version,
|
||||||
|
build_number: build,
|
||||||
|
skip_binary_upload: true,
|
||||||
|
skip_metadata: false,
|
||||||
|
skip_screenshots: false,
|
||||||
|
overwrite_screenshots: true,
|
||||||
|
submit_for_review: false,
|
||||||
|
automatic_release: false,
|
||||||
|
precheck_include_in_app_purchases: false,
|
||||||
|
force: true,
|
||||||
|
run_precheck_before_submit: false
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
desc "Push a new release build to the App Store"
|
desc "Push a new release build to the App Store"
|
||||||
lane :release do
|
lane :release do
|
||||||
increment_build_number(xcodeproj: "MealMood.xcodeproj")
|
increment_build_number(xcodeproj: "MealMood.xcodeproj")
|
||||||
|
|||||||
@@ -23,6 +23,14 @@ For _fastlane_ installation instructions, see [Installing _fastlane_](https://do
|
|||||||
|
|
||||||
Push a new beta build to TestFlight
|
Push a new beta build to TestFlight
|
||||||
|
|
||||||
|
### ios publish
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[bundle exec] fastlane ios publish
|
||||||
|
```
|
||||||
|
|
||||||
|
Upload metadata + localized screenshots for an existing TestFlight build (no rebuild, no submit)
|
||||||
|
|
||||||
### ios release
|
### ios release
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
Neu in Version 1.1.5:
|
Neu in Version 1.2.0:
|
||||||
|
|
||||||
• 7 Tage kostenlos testen — probiere MealMood Premium ohne Verpflichtung. Neuer Jahresplan mit der größten Ersparnis.
|
• Sofort startklar — nach der Einführung wird deine erste Woche automatisch gefüllt: Du beginnst mit einem fertigen Plan statt einer leeren Seite.
|
||||||
|
|
||||||
• Klarere Limits — der Gerichte-Zähler zeigt, wie viele du verwendet hast, und die Hinweise beim Erreichen des Limits sind freundlicher und weniger aufdringlich.
|
• Widgets für Home- und Sperrbildschirm — sieh die Mahlzeiten von heute und dieser Woche auf einen Blick und tippe, um direkt in die App zu springen.
|
||||||
|
|
||||||
• Neue Premium-Auswahl — wähle Monats- oder Jahresplan nebeneinander, sieh deine Ersparnis und starte den Test mit einem Tipp.
|
• Hilfreiche Erinnerungen — freundliche, gut getimte Hinweise, damit du das Planen nie vergisst.
|
||||||
|
|
||||||
• Weniger Werbung — gelegentliche interne Hinweise ersetzen einige Anzeigen. Mit Premium endgültig entfernen — ein Tipp genügt.
|
• 7 Tage kostenlos testen + neuer Jahresplan — teste MealMood Premium ohne Verpflichtung und spare mehr mit dem Jahresabo.
|
||||||
|
|
||||||
|
• Klarere Limits & neue Premium-Auswahl — der Gerichte-Zähler zeigt deinen Verbrauch, und du kannst Monats- und Jahresplan nebeneinander vergleichen.
|
||||||
|
|
||||||
|
• Weniger Werbung — gelegentliche interne Hinweise ersetzen einige Anzeigen. Mit Premium mit einem Tipp für immer entfernen.
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
What's new in 1.1.5:
|
What's new in 1.2.0:
|
||||||
|
|
||||||
• 7-day free trial — try MealMood Premium with no commitment. New yearly plan with the best savings.
|
• Instant first plan — after onboarding, your first week fills in automatically, so you start with a full plan instead of a blank page.
|
||||||
|
|
||||||
• Smarter limits — the dish counter now shows how many you've used, the new contextual prompts at limits are clearer and less intrusive.
|
• Home & Lock Screen widgets — see today's and this week's meals at a glance, and tap to jump straight into the app.
|
||||||
|
|
||||||
• Better paywall — choose monthly or yearly side by side, see your savings, and start your trial in one tap.
|
• Helpful reminders — gentle, well-timed nudges so planning never slips your mind.
|
||||||
|
|
||||||
• Cleaner ads experience — occasional internal promos replace some ads. One tap to remove them forever with Premium.
|
• 7-day free trial + new yearly plan — try MealMood Premium with no commitment and save more with the annual option.
|
||||||
|
|
||||||
|
• Clearer limits & refreshed paywall — the dish counter shows what you've used, and you can compare monthly vs yearly side by side.
|
||||||
|
|
||||||
|
• Fewer ads — occasional internal promos replace some ads. One tap removes them forever with Premium.
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
Novedades en la versión 1.1.5:
|
Novedades en la versión 1.2.0:
|
||||||
|
|
||||||
• Prueba gratis de 7 días — prueba MealMood Premium sin compromiso. Nuevo plan anual con el mayor ahorro.
|
• Plan al instante — tras la introducción, tu primera semana se rellena automáticamente: empiezas con un plan completo, no con una página en blanco.
|
||||||
|
|
||||||
• Límites más claros — el contador de platos muestra cuántos has usado, y los avisos al llegar al límite son más amables y menos intrusivos.
|
• Widgets en pantalla de inicio y bloqueo — consulta las comidas de hoy y de la semana de un vistazo, y toca para entrar directo en la app.
|
||||||
|
|
||||||
• Paywall renovada — elige plan mensual o anual lado a lado, ve cuánto ahorras y empieza tu prueba en un toque.
|
• Recordatorios útiles — avisos amables y en el momento justo para que planificar nunca se te olvide.
|
||||||
|
|
||||||
|
• Prueba gratis de 7 días + nuevo plan anual — prueba MealMood Premium sin compromiso y ahorra más con la opción anual.
|
||||||
|
|
||||||
|
• Límites más claros y paywall renovada — el contador de platos muestra cuántos has usado y puedes comparar mensual y anual lado a lado.
|
||||||
|
|
||||||
• Menos anuncios — promociones internas reemplazan algunos anuncios. Un toque y los eliminas para siempre con Premium.
|
• Menos anuncios — promociones internas reemplazan algunos anuncios. Un toque y los eliminas para siempre con Premium.
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
Nouveautés dans la version 1.1.5 :
|
Nouveautés dans la version 1.2.0 :
|
||||||
|
|
||||||
• Essai gratuit de 7 jours — essayez MealMood Premium sans engagement. Nouveau plan annuel avec les meilleures économies.
|
• Plan immédiat — après l'introduction, votre première semaine se remplit automatiquement : vous démarrez avec un plan complet, pas une page blanche.
|
||||||
|
|
||||||
• Limites plus claires — le compteur de plats indique combien vous en avez utilisé, et les alertes aux limites sont plus douces et moins intrusives.
|
• Widgets écran d'accueil et verrouillage — voyez les repas du jour et de la semaine d'un coup d'œil, et touchez pour ouvrir l'app directement.
|
||||||
|
|
||||||
• Paywall améliorée — choisissez le plan mensuel ou annuel côte à côte, voyez vos économies et démarrez votre essai en un tap.
|
• Rappels utiles — des notifications douces et bien synchronisées pour ne jamais oublier de planifier.
|
||||||
|
|
||||||
|
• Essai gratuit de 7 jours + nouveau plan annuel — essayez MealMood Premium sans engagement et économisez plus avec l'option annuelle.
|
||||||
|
|
||||||
|
• Limites plus claires et paywall repensée — le compteur de plats indique votre usage, et vous comparez mensuel et annuel côte à côte.
|
||||||
|
|
||||||
• Moins de pubs — des promotions internes remplacent certaines pubs. Un tap pour les supprimer définitivement avec Premium.
|
• Moins de pubs — des promotions internes remplacent certaines pubs. Un tap pour les supprimer définitivement avec Premium.
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
Novità nella versione 1.1.5:
|
Novità nella versione 1.2.0:
|
||||||
|
|
||||||
• Prova gratuita di 7 giorni — prova MealMood Premium senza impegno. Nuovo piano annuale con il massimo risparmio.
|
• Piano immediato — dopo l'introduzione, la tua prima settimana si compila automaticamente: inizi con un piano completo, non con una pagina vuota.
|
||||||
|
|
||||||
• Limiti più chiari — il contatore di piatti mostra quanti ne hai usati, e gli avvisi al raggiungimento del limite sono più gentili e meno invadenti.
|
• Widget per schermata Home e di blocco — vedi i pasti di oggi e della settimana a colpo d'occhio e tocca per entrare subito nell'app.
|
||||||
|
|
||||||
• Pagamento ridisegnato — scegli piano mensile o annuale fianco a fianco, vedi il risparmio e inizia la prova con un tocco.
|
• Promemoria utili — avvisi gentili e ben calibrati per non dimenticare mai di pianificare.
|
||||||
|
|
||||||
• Meno pubblicità — promozioni interne occasionali sostituiscono alcune pubblicità. Un tocco per rimuoverle per sempre con Premium.
|
• Prova gratuita di 7 giorni + nuovo piano annuale — prova MealMood Premium senza impegno e risparmia di più con l'opzione annuale.
|
||||||
|
|
||||||
|
• Limiti più chiari e pagamento ridisegnato — il contatore di piatti mostra quanti ne hai usati e puoi confrontare mensile e annuale fianco a fianco.
|
||||||
|
|
||||||
|
• Meno pubblicità — promozioni interne sostituiscono alcune pubblicità. Un tocco per rimuoverle per sempre con Premium.
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
Novidades na versão 1.1.5:
|
Novidades na versão 1.2.0:
|
||||||
|
|
||||||
• Teste grátis de 7 dias — experimente o MealMood Premium sem compromisso. Novo plano anual com a maior economia.
|
• Plano na hora — após a introdução, sua primeira semana é preenchida automaticamente: você começa com um plano completo, não com uma página em branco.
|
||||||
|
|
||||||
• Limites mais claros — o contador de pratos mostra quantos você já usou, e os avisos ao chegar no limite são mais gentis e menos intrusivos.
|
• Widgets na tela de início e de bloqueio — veja as refeições de hoje e da semana num relance e toque para abrir o app direto.
|
||||||
|
|
||||||
• Tela Premium renovada — escolha plano mensal ou anual lado a lado, veja o quanto economiza e comece seu teste com um toque.
|
• Lembretes úteis — avisos gentis e na hora certa para você nunca esquecer de planejar.
|
||||||
|
|
||||||
|
• Teste grátis de 7 dias + novo plano anual — experimente o MealMood Premium sem compromisso e economize mais com a opção anual.
|
||||||
|
|
||||||
|
• Limites mais claros e tela Premium renovada — o contador de pratos mostra quantos você usou e você compara mensal e anual lado a lado.
|
||||||
|
|
||||||
• Menos anúncios — promoções internas substituem alguns anúncios. Um toque para removê-los para sempre com o Premium.
|
• Menos anúncios — promoções internas substituem alguns anúncios. Um toque para removê-los para sempre com o Premium.
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# Task: generate localized App Store screenshots for MealMood
|
||||||
|
|
||||||
|
Write `generate.py` (run with `uv run --with Pillow python3 generate.py` from the
|
||||||
|
`fastlane/screenshots/_localize/` directory) that produces localized marketing
|
||||||
|
screenshots by re-rendering ONLY the top caption text of each base image.
|
||||||
|
|
||||||
|
## Base images (source of truth — do NOT alter the phone/app area)
|
||||||
|
`fastlane/screenshots/en-US/` contains 5 screenshots × 2 devices:
|
||||||
|
- iPhone 6.7": `{i}_APP_IPHONE_67_{i}.png` (1320 × 2868)
|
||||||
|
- iPad 12.9": `{i}_APP_IPAD_PRO_3GEN_129_{i}.png` (2048 × 2732)
|
||||||
|
for i in 0..4.
|
||||||
|
|
||||||
|
Each image = flat pastel background at top holding a **headline** (bold, black,
|
||||||
|
centered, 1–3 lines) + a **subtitle** (regular, black, centered, ~2 lines),
|
||||||
|
with a device frame containing an app screenshot BELOW the text.
|
||||||
|
|
||||||
|
## What to do, per language × per base image
|
||||||
|
Languages (output folders, siblings of `en-US/`): `es-ES de-DE fr-FR it pt-BR`.
|
||||||
|
Also re-render `en-US` in place is NOT needed — leave en-US untouched.
|
||||||
|
Text comes from `translations.json` (index 0..4 matches file index `i`).
|
||||||
|
|
||||||
|
For each base image:
|
||||||
|
1. Read background color = pixel at (10,10).
|
||||||
|
2. Detect `phone_top` = first row y in [0.10h, 0.6h] that contains a horizontal
|
||||||
|
contiguous run of near-black pixels (r,g,b all < 70) wider than 0.35·w.
|
||||||
|
This is the top of the device — the caption band is `[0, phone_top)`.
|
||||||
|
3. Repaint the whole band `[0, phone_top)` with the background color
|
||||||
|
(this erases the English caption).
|
||||||
|
4. Draw the localized headline + subtitle, black, centered horizontally,
|
||||||
|
the text block vertically centered within `[0, phone_top)` with a slight
|
||||||
|
upward bias (leave ~12% of phone_top as bottom gap before the phone).
|
||||||
|
5. Save to `../<lang>/<same_filename>`.
|
||||||
|
|
||||||
|
## Typography (match the original as closely as possible)
|
||||||
|
- Font: SF Pro via `/System/Library/Fonts/SFNS.ttf`
|
||||||
|
(variable font — use `font.set_variation_by_name`). Headline = **Bold**,
|
||||||
|
subtitle = **Regular**. If a weight name isn't found, print the available
|
||||||
|
names once and pick the closest (Bold/Semibold for headline, Regular for sub).
|
||||||
|
- iPhone: headline ~72px, subtitle ~50px as a STARTING point.
|
||||||
|
iPad: headline ~96px, subtitle ~66px starting point.
|
||||||
|
- Auto-fit: if the headline at the start size would exceed 88% of image width
|
||||||
|
on its longest line, reduce the headline font size until it fits (min 44px
|
||||||
|
iPhone / 60px iPad). Wrap headline to at most 3 lines, subtitle to at most 3
|
||||||
|
lines, breaking on spaces. Some localized headlines are long (e.g. the it/fr
|
||||||
|
"…in famiglia è facile") — wrapping + shrinking must keep them inside the band
|
||||||
|
and never overlap the phone.
|
||||||
|
- Line spacing ~1.12×. Gap between headline block and subtitle ~0.5× subtitle
|
||||||
|
font size. Colour pure black (0,0,0). Antialiased.
|
||||||
|
|
||||||
|
## Output & self-check
|
||||||
|
- Every output PNG must keep the EXACT same dimensions as its base.
|
||||||
|
- After generating, print a table: lang, index, device, headline font px used,
|
||||||
|
#headline lines, #subtitle lines, and whether text bottom < phone_top (must be
|
||||||
|
true for all — flag any that fail).
|
||||||
|
- Do not touch any file outside the five language folders.
|
||||||
|
|
||||||
|
Total output = 5 langs × 5 indices × 2 devices = 60 images.
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Localize App Store caption bands. Repaints top band + redraws localized text."""
|
||||||
|
import json, os
|
||||||
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
|
||||||
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
SRC = os.path.join(HERE, "..", "en-US")
|
||||||
|
FONT = "/System/Library/Fonts/SFNS.ttf"
|
||||||
|
LANGS = ["es-ES", "de-DE", "fr-FR", "it", "pt-BR"]
|
||||||
|
FILES = [ # (index, filename, device)
|
||||||
|
(i, dev) for i in range(5)
|
||||||
|
for dev in ("IPHONE_67", "IPAD_PRO_3GEN_129")
|
||||||
|
]
|
||||||
|
|
||||||
|
with open(os.path.join(HERE, "translations.json")) as f:
|
||||||
|
T = json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
def font(size, weight):
|
||||||
|
ft = ImageFont.truetype(FONT, size)
|
||||||
|
try:
|
||||||
|
ft.set_variation_by_name(weight)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return ft
|
||||||
|
|
||||||
|
|
||||||
|
def detect_phone_top(im):
|
||||||
|
w, h = im.size
|
||||||
|
px = im.load()
|
||||||
|
def dark(c): return c[0] < 70 and c[1] < 70 and c[2] < 70
|
||||||
|
for y in range(int(h * 0.10), int(h * 0.6), 2):
|
||||||
|
run = mx = 0
|
||||||
|
for x in range(0, w, 4):
|
||||||
|
run = run + 4 if dark(px[x, y]) else 0
|
||||||
|
if run > mx:
|
||||||
|
mx = run
|
||||||
|
if mx > w * 0.35:
|
||||||
|
return y
|
||||||
|
return int(h * 0.20)
|
||||||
|
|
||||||
|
|
||||||
|
def wrap(draw, text, ft, max_w, max_lines):
|
||||||
|
words = text.split()
|
||||||
|
lines, cur = [], ""
|
||||||
|
for wd in words:
|
||||||
|
trial = (cur + " " + wd).strip()
|
||||||
|
if draw.textlength(trial, font=ft) <= max_w or not cur:
|
||||||
|
cur = trial
|
||||||
|
else:
|
||||||
|
lines.append(cur)
|
||||||
|
cur = wd
|
||||||
|
if cur:
|
||||||
|
lines.append(cur)
|
||||||
|
return lines[:max_lines] if len(lines) <= max_lines else None
|
||||||
|
|
||||||
|
|
||||||
|
def fit_headline(draw, text, max_w, start, floor, weight, max_lines=3):
|
||||||
|
size = start
|
||||||
|
while size >= floor:
|
||||||
|
ft = font(size, weight)
|
||||||
|
lines = wrap(draw, text, ft, max_w, max_lines)
|
||||||
|
if lines and all(draw.textlength(l, font=ft) <= max_w for l in lines):
|
||||||
|
return ft, lines, size
|
||||||
|
size -= 2
|
||||||
|
ft = font(floor, weight)
|
||||||
|
lines = wrap(draw, text, ft, max_w, max_lines) or [text]
|
||||||
|
return ft, lines, floor
|
||||||
|
|
||||||
|
|
||||||
|
def block_height(draw, lines, ft, lh):
|
||||||
|
asc, desc = ft.getmetrics()
|
||||||
|
return int((asc + desc) * lh * len(lines))
|
||||||
|
|
||||||
|
|
||||||
|
def render(base_path, out_path, headline, subtitle, is_ipad):
|
||||||
|
im = Image.open(base_path).convert("RGB")
|
||||||
|
w, h = im.size
|
||||||
|
bg = im.getpixel((10, 10))
|
||||||
|
phone_top = detect_phone_top(im)
|
||||||
|
d = ImageDraw.Draw(im)
|
||||||
|
# erase band
|
||||||
|
d.rectangle([0, 0, w, phone_top], fill=bg)
|
||||||
|
|
||||||
|
max_w = int(w * 0.88)
|
||||||
|
if is_ipad:
|
||||||
|
h_start, h_floor, s_size = 96, 60, 66
|
||||||
|
else:
|
||||||
|
h_start, h_floor, s_size = 72, 44, 50
|
||||||
|
lh = 1.12
|
||||||
|
|
||||||
|
hf, hlines, hpx = fit_headline(d, headline, max_w, h_start, h_floor, "Bold")
|
||||||
|
sf = font(s_size, "Regular")
|
||||||
|
slines = wrap(d, subtitle, sf, max_w, 3) or [subtitle]
|
||||||
|
|
||||||
|
hb = block_height(d, hlines, hf, lh)
|
||||||
|
sb = block_height(d, slines, sf, lh)
|
||||||
|
gap = int(s_size * 0.6)
|
||||||
|
total = hb + gap + sb
|
||||||
|
|
||||||
|
# vertically center within [0, phone_top) with slight upward bias
|
||||||
|
bottom_gap = int(phone_top * 0.12)
|
||||||
|
avail = phone_top - bottom_gap
|
||||||
|
y = max(int(phone_top * 0.06), (avail - total) // 2)
|
||||||
|
|
||||||
|
def draw_lines(lines, ft):
|
||||||
|
nonlocal y
|
||||||
|
asc, desc = ft.getmetrics()
|
||||||
|
step = int((asc + desc) * lh)
|
||||||
|
for ln in lines:
|
||||||
|
tw = d.textlength(ln, font=ft)
|
||||||
|
d.text(((w - tw) / 2, y), ln, font=ft, fill=(0, 0, 0))
|
||||||
|
y += step
|
||||||
|
|
||||||
|
draw_lines(hlines, hf)
|
||||||
|
y += gap
|
||||||
|
draw_lines(slines, sf)
|
||||||
|
|
||||||
|
im.save(out_path)
|
||||||
|
ok = y <= phone_top
|
||||||
|
return hpx, len(hlines), len(slines), ok, y, phone_top
|
||||||
|
|
||||||
|
|
||||||
|
rows = []
|
||||||
|
for lang in LANGS:
|
||||||
|
outdir = os.path.join(HERE, "..", lang)
|
||||||
|
os.makedirs(outdir, exist_ok=True)
|
||||||
|
for i, dev in FILES:
|
||||||
|
fn = f"{i}_APP_{dev}_{i}.png"
|
||||||
|
base = os.path.join(SRC, fn)
|
||||||
|
out = os.path.join(outdir, fn)
|
||||||
|
tr = T[lang][i]
|
||||||
|
hpx, hl, sl, ok, ybot, pt = render(base, out, tr["h"], tr["s"], "IPAD" in dev)
|
||||||
|
rows.append((lang, i, "ipad" if "IPAD" in dev else "iphone", hpx, hl, sl, ok, ybot, pt))
|
||||||
|
|
||||||
|
print(f"{'lang':7} {'i':2} {'dev':6} {'hpx':4} {'hL':3} {'sL':3} {'ok':3} {'ybot':5} {'ptop':5}")
|
||||||
|
bad = 0
|
||||||
|
for r in rows:
|
||||||
|
print(f"{r[0]:7} {r[1]:<2} {r[2]:6} {r[3]:<4} {r[4]:<3} {r[5]:<3} {str(r[6]):5} {r[7]:<5} {r[8]:<5}")
|
||||||
|
if not r[6]:
|
||||||
|
bad += 1
|
||||||
|
print(f"\nTotal images: {len(rows)} | text-overlaps-phone failures: {bad}")
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"en-US": [
|
||||||
|
{"h": "Weekly Meal Planner for Families", "s": "Organize lunches and dinners in one simple view."},
|
||||||
|
{"h": "Share or Print Your Weekly Plan", "s": "Create a beautiful meal plan for family use."},
|
||||||
|
{"h": "Family Meal Planning Made Easy", "s": "Search, pick, and assign meals in seconds."},
|
||||||
|
{"h": "Plan Smarter, Stay Consistent", "s": "Sync with calendar and tailor planning to your routine."},
|
||||||
|
{"h": "Your Meal Library, Always Ready", "s": "Keep favorite dishes organized and easy to reuse."}
|
||||||
|
],
|
||||||
|
"es-ES": [
|
||||||
|
{"h": "Planificador semanal de comidas familiar", "s": "Organiza comidas y cenas en una vista sencilla."},
|
||||||
|
{"h": "Comparte o imprime tu plan semanal", "s": "Crea un plan de comidas precioso para la familia."},
|
||||||
|
{"h": "Planificar comidas en familia es muy fácil", "s": "Busca, elige y asigna comidas en segundos."},
|
||||||
|
{"h": "Planifica mejor, mantén la constancia", "s": "Sincroniza con el calendario y adapta la planificación a tu rutina."},
|
||||||
|
{"h": "Tu biblioteca de platos, siempre lista", "s": "Ten tus platos favoritos organizados y listos para reutilizar."}
|
||||||
|
],
|
||||||
|
"de-DE": [
|
||||||
|
{"h": "Wochen-Essensplaner für Familien", "s": "Mittag- und Abendessen in einer einfachen Übersicht."},
|
||||||
|
{"h": "Wochenplan teilen oder drucken", "s": "Erstelle einen schönen Essensplan für die Familie."},
|
||||||
|
{"h": "Familienessen ganz einfach planen", "s": "Gerichte in Sekunden suchen, wählen und zuweisen."},
|
||||||
|
{"h": "Klüger planen, dranbleiben", "s": "Mit dem Kalender synchronisieren und an deinen Alltag anpassen."},
|
||||||
|
{"h": "Deine Gerichte-Bibliothek, immer bereit", "s": "Lieblingsgerichte organisiert und leicht wiederverwendbar."}
|
||||||
|
],
|
||||||
|
"fr-FR": [
|
||||||
|
{"h": "Planning de repas hebdo pour les familles", "s": "Organisez déjeuners et dîners d'un seul coup d'œil."},
|
||||||
|
{"h": "Partagez ou imprimez votre planning", "s": "Créez un joli plan de repas pour toute la famille."},
|
||||||
|
{"h": "La planification des repas en famille, facile", "s": "Cherchez, choisissez et assignez des repas en quelques secondes."},
|
||||||
|
{"h": "Planifiez mieux, restez régulier", "s": "Synchronisez avec le calendrier et adaptez à votre routine."},
|
||||||
|
{"h": "Votre bibliothèque de plats, toujours prête", "s": "Gardez vos plats favoris organisés et réutilisables."}
|
||||||
|
],
|
||||||
|
"it": [
|
||||||
|
{"h": "Pianificatore settimanale dei pasti per famiglie", "s": "Organizza pranzi e cene in un'unica vista."},
|
||||||
|
{"h": "Condividi o stampa il tuo piano settimanale", "s": "Crea un bel piano dei pasti per la famiglia."},
|
||||||
|
{"h": "Pianificare i pasti in famiglia è facile", "s": "Cerca, scegli e assegna i pasti in pochi secondi."},
|
||||||
|
{"h": "Pianifica meglio, resta costante", "s": "Sincronizza con il calendario e adatta alla tua routine."},
|
||||||
|
{"h": "La tua libreria di piatti, sempre pronta", "s": "Tieni i piatti preferiti organizzati e pronti da riusare."}
|
||||||
|
],
|
||||||
|
"pt-BR": [
|
||||||
|
{"h": "Planejador semanal de refeições para famílias", "s": "Organize almoços e jantares em uma visão simples."},
|
||||||
|
{"h": "Compartilhe ou imprima seu plano semanal", "s": "Crie um lindo plano de refeições para a família."},
|
||||||
|
{"h": "Planejar refeições em família ficou fácil", "s": "Busque, escolha e atribua refeições em segundos."},
|
||||||
|
{"h": "Planeje melhor, mantenha a constância", "s": "Sincronize com o calendário e adapte à sua rotina."},
|
||||||
|
{"h": "Sua biblioteca de pratos, sempre pronta", "s": "Mantenha os pratos favoritos organizados e fáceis de reutilizar."}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 277 KiB |
|
After Width: | Height: | Size: 440 KiB |
|
After Width: | Height: | Size: 237 KiB |
|
After Width: | Height: | Size: 945 KiB |
|
After Width: | Height: | Size: 1009 KiB |
|
After Width: | Height: | Size: 292 KiB |
|
After Width: | Height: | Size: 391 KiB |
|
After Width: | Height: | Size: 394 KiB |
|
After Width: | Height: | Size: 458 KiB |
|
After Width: | Height: | Size: 271 KiB |
|
After Width: | Height: | Size: 276 KiB |
|
After Width: | Height: | Size: 441 KiB |
|
After Width: | Height: | Size: 236 KiB |
|
After Width: | Height: | Size: 948 KiB |
|
After Width: | Height: | Size: 1010 KiB |
|
After Width: | Height: | Size: 295 KiB |
|
After Width: | Height: | Size: 389 KiB |
|
After Width: | Height: | Size: 394 KiB |
|
After Width: | Height: | Size: 457 KiB |
|
After Width: | Height: | Size: 270 KiB |
|
After Width: | Height: | Size: 280 KiB |
|
After Width: | Height: | Size: 445 KiB |
|
After Width: | Height: | Size: 237 KiB |
|
After Width: | Height: | Size: 948 KiB |
|
After Width: | Height: | Size: 1020 KiB |
|
After Width: | Height: | Size: 300 KiB |
|
After Width: | Height: | Size: 391 KiB |
|
After Width: | Height: | Size: 393 KiB |
|
After Width: | Height: | Size: 464 KiB |
|
After Width: | Height: | Size: 272 KiB |
|
After Width: | Height: | Size: 280 KiB |
|
After Width: | Height: | Size: 444 KiB |
|
After Width: | Height: | Size: 244 KiB |
|
After Width: | Height: | Size: 951 KiB |
|
After Width: | Height: | Size: 1008 KiB |
|
After Width: | Height: | Size: 292 KiB |
|
After Width: | Height: | Size: 386 KiB |
|
After Width: | Height: | Size: 388 KiB |
|
After Width: | Height: | Size: 454 KiB |
|
After Width: | Height: | Size: 265 KiB |
|
After Width: | Height: | Size: 283 KiB |
|
After Width: | Height: | Size: 445 KiB |
|
After Width: | Height: | Size: 243 KiB |
|
After Width: | Height: | Size: 950 KiB |
|
After Width: | Height: | Size: 1012 KiB |
|
After Width: | Height: | Size: 296 KiB |
|
After Width: | Height: | Size: 389 KiB |
|
After Width: | Height: | Size: 392 KiB |
|
After Width: | Height: | Size: 461 KiB |
|
After Width: | Height: | Size: 273 KiB |