From 09d3b200b2108945f83a018ec12cf8ad74927551 Mon Sep 17 00:00:00 2001 From: Alex Van de Putte Date: Sat, 11 Jul 2026 18:03:26 -0400 Subject: [PATCH 1/3] Add opt-in i18n foundation for apps (Language setting + injected helpers) Adds an opt-in internationalization foundation that apps can adopt without breaking the existing app contract: - server/i18n.py + server/i18n_data.json: logic/data split. Translations are grouped by semantic topic domain (gettext msgctxt model) so homographs stay distinct (weather HIGH vs tides HIGH). ~34 languages, incl. regional variants that inherit their base language. Each key carries a translator 'context' note. - server/location.py + server/weather.py: one shared geocoder, babel-backed currency/country (no hardcoded tables), 10-minute TTL-cached weather. - Runtime injection: get_plugin_pages inspects fetch()'s signature and passes i18n / get_weather / get_location only when the app declares them. Classic 4-arg apps are completely untouched. - Settings: an 'Enable internationalization' master toggle (DEFAULT OFF) gates the Language selector and the app badge. With it off the UI and behaviour are identical to today -- the toggle carries a disclaimer that it needs modules on Universal Firmware with configurable character maps. - babel is a soft dependency; i18n.py falls back to English without it. Co-Authored-By: Claude Opus 4.8 (1M context) --- APPS_README.md | 94 ++ server/app.py | 126 +- server/i18n.py | 292 ++++ server/i18n_data.json | 2628 +++++++++++++++++++++++++++++++++++ server/location.py | 111 ++ server/requirements.txt | 1 + server/static/app.js | 15 + server/templates/index.html | 14 + server/weather.py | 208 +++ tests/test_i18n.py | 289 ++++ 10 files changed, 3776 insertions(+), 2 deletions(-) create mode 100644 server/i18n.py create mode 100644 server/i18n_data.json create mode 100644 server/location.py create mode 100644 server/weather.py create mode 100644 tests/test_i18n.py diff --git a/APPS_README.md b/APPS_README.md index c835ab7..813cc68 100644 --- a/APPS_README.md +++ b/APPS_README.md @@ -204,6 +204,100 @@ Use these keys to synchronize related fields: } ``` +## Optional: Localization & Shared Helpers + +`fetch()` can opt into runtime-injected helpers by **declaring extra parameters**. +The runtime inspects your signature and only passes a helper if you name it, so the +classic four-argument `fetch(settings, format_lines, get_rows, get_cols)` keeps +working unchanged — every helper below is purely opt-in and backward compatible. + +### Localization (`i18n`) + +If your app shows words (day/month names, status labels), add an `i18n=None` +parameter. The runtime binds it to the global **Language** setting; on a host +without it (or with `babel` not installed) `i18n` is `None` and you fall back to +English. + +```python +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): + from datetime import datetime + now = datetime.now() + weekday = i18n.weekday(now) if i18n else now.strftime("%A").upper() + label = i18n.t("SUNRISE") if i18n else "SUNRISE" + return [format_lines(weekday, label)] +``` + +- `i18n.weekday(dt, short=False)` / `i18n.month(dt, short=False)` — CLDR-correct, + UPPERCASE day/month names for every language. +- `i18n.date(dt, short=False, year=False)` — day + month (and optional year) in the + locale's **own order and wording**: `JULY 9` in English but `9 JUILLET` (fr), + `9. JULI` (de). Don't hand-assemble `month + " " + day` — order is language-specific. +- `i18n.time(dt, seconds=False)` — wall-clock time: `3:48 PM` in English, `15:48` + elsewhere. `i18n.is_24h` exposes the same decision if you branch yourself. +- `i18n.unit("D")` — localized compact duration suffix for `D`/`H`/`M`/`S`, so `175D` + becomes `175J` in French (jour), `175T` in German (Tag). +- `i18n.number(value, decimals=2, grouping=True)` — a number with the locale's own + separators: `1,234.50` (en) vs `1.234,50` (de) vs `1 234,50` (fr). Use it for any + price/rate/percent — never hardcode `f"{v:,.2f}"`. +- `i18n.base_currency()` / `i18n.country()` — the currency / country a language-region + implies (a sensible default; prefer `get_location` below for geography). +- `i18n.t("ENGLISH LABEL")` — a translated UI word; with no translation it returns the + English key, so nothing ever breaks. All data lives in `server/i18n_data.json` (the + language list, translations, and per-language default currency/country). Add keys or + languages there rather than in your app, keep them short (modules are narrow), and + give each key a generic `context` note so translators know what the word means. A + regional variant like `pt-BR` automatically inherits every `pt` translation. + +Once your app adapts to the language, add `"i18n": true` to its `manifest.json`. When +internationalization is enabled the Apps grid shows a 🌐 badge on those cards, and the +app automatically gets a per-app **Language** override in its settings (blank = follow +the global Language). Only Western-European (Windows-1252) languages are offered — the +modules can't display Greek, Cyrillic, CJK, etc. + +Internationalization is **off by default**: unless the user turns on *Enable +internationalization* in Settings, `i18n` is not injected and your app runs its +English `i18n=None` fallback — so always keep that fallback correct. The toggle exists +because accented glyphs only render on modules whose firmware/character map supports +them. + +### Shared current weather (`get_weather`) + +If your app shows the weather, don't hardcode a provider — add a `get_weather=None` +parameter and call it: + +```python +def fetch(settings, format_lines, get_rows, get_cols, get_weather=None): + if get_weather is None: # host without the helper + return [format_lines("NO WEATHER")] + w = get_weather() # uses the *global* provider + key + location + if not w["ok"]: + return [format_lines("WEATHER", "UNAVAILABLE")] + return [format_lines(w["city"], f'{w["temp_f"]}F {w["desc"]}')] +``` + +`get_weather()` returns a dict: `ok`, `city`, `temp_f`, `feels_like_f`, `hi_f`, +`lo_f`, `desc`, `humidity`, `wind_mph`, `cloud_cover`, `provider`, `lat`, `lon` +(temps in °F; `ok` is `False` with an `error` key on failure). The default provider +is keyless Open-Meteo, so weather works with **no API key**. + +### Location → country / currency (`get_location`) + +Anything tied to *geography* — which currency, which country's holidays — should key +off the configured **Location**, not the language. Declare a `get_location` +parameter for the shared resolver: + +```python +def fetch(settings, format_lines, get_rows, get_cols, get_location=None): + loc = get_location() if get_location else {} + country = loc.get("country") # ISO 3166-1 alpha-2, e.g. "CA" + subdivision = loc.get("subdivision") # ISO 3166-2, e.g. "CA-QC"; may be None + currency = loc.get("currency") # ISO 4217, e.g. "CAD" (None if unknown) +``` + +It reverse-geocodes the global Location once (cached) and is keyless. Declaring +`get_location` also gives the app an automatic per-app **Location** override in its +settings. Helpers compose: `def fetch(..., get_weather=None, get_location=None, i18n=None)`. + ## Final Checklist - Confirm your app directory contains `manifest.json` and `app.py`. diff --git a/server/app.py b/server/app.py index d6ad81c..52717d4 100644 --- a/server/app.py +++ b/server/app.py @@ -11,11 +11,15 @@ import unicodedata import yfinance as yf import importlib.util +import inspect import urllib.request import shutil from datetime import datetime from flask import Flask, render_template, request, jsonify from tuning import build_tuning_adjust_commands +import i18n +import location +import weather from hardware.universal_firmware import ( UniversalFirmwareError, UniversalFirmwareManager, @@ -207,6 +211,9 @@ def load_settings(): "location_lon": "", "location_name": "", "timezone": sys_tz, + "i18n_enabled": False, + "language": "en-US", + "weather_provider": "openmeteo", "weather_api_key": "", "mbta_stop": "", "mbta_route": "", @@ -1369,6 +1376,69 @@ def _load_functional_module(app_id, app_dir): logging.error(f"Plugin {app_id}: error importing app.py: {e}") +def _fetch_accepts(fn, name): + """True if an app's fetch() declares a parameter called ``name`` (how an app + opts into an injected helper like ``i18n`` / ``get_weather`` / ``get_location``), + or accepts arbitrary keywords. Classic 4-arg apps accept neither and are called + unchanged, so this whole mechanism is fully backward compatible.""" + try: + params = inspect.signature(fn).parameters + except (TypeError, ValueError): + return False + return name in params or any(p.kind == p.VAR_KEYWORD for p in params.values()) + + +def _i18n_enabled(): + """Master switch for internationalization. Off by default: the display stays + English-only and looks exactly as it did before i18n existed. Should only be + turned on for modules whose firmware/character map can render accented glyphs.""" + return bool(settings.get("i18n_enabled", False)) + + +def _resolve_app_language(app_id): + """The Language an app should render in: a per-app override + (``plugin__language``) when set, otherwise the global Language. A blank or + unset override means "follow the global Language".""" + override = settings.get(f"plugin_{app_id}_language") + return override or settings.get("language", "en-US") + + +def _apply_location_override(plugin_settings, value): + """Fold a per-app Location override (a ``/location_search`` chip value, format + ``"lat,lon|name"``) into the ``location_*`` keys the location/weather helpers + read, so a per-app override is honored without touching the global location. + A blank/unset value leaves the global location in place.""" + if not value: + return + try: + coords, _, name = str(value).partition("|") + lat, _, lon = coords.partition(",") + lat, lon = lat.strip(), lon.strip() + if lat and lon: + plugin_settings["location_lat"] = lat + plugin_settings["location_lon"] = lon + plugin_settings["location_name"] = name.strip() or plugin_settings.get("location_name", "") + except Exception: + pass + + +def _plugin_fetch_kwargs(app_id, fetch_fn, plugin_settings): + """Optional keyword args a plugin's fetch() opted into by declaring them. Only + the helpers the signature names are built, so nothing is injected into apps that + don't ask for it.""" + kwargs = {} + if _fetch_accepts(fetch_fn, "get_weather"): + kwargs["get_weather"] = lambda s=None: weather.fetch_current( + s if s is not None else plugin_settings) + if _fetch_accepts(fetch_fn, "get_location"): + kwargs["get_location"] = lambda: location.resolve(plugin_settings) + # Only bind a Localizer when i18n is enabled; otherwise the app's i18n=None + # fallback runs and it renders exactly as it did before i18n existed (English). + if _i18n_enabled() and _fetch_accepts(fetch_fn, "i18n"): + kwargs["i18n"] = i18n.Localizer(_resolve_app_language(app_id)) + return kwargs + + def get_plugin_pages(app_id): manifest = _plugin_registry.get(app_id) if not manifest: @@ -1404,7 +1474,13 @@ def get_plugin_pages(app_id): else: key = f"plugin_{app_id}_{s['key']}" plugin_settings[s["key"]] = settings.get(key, s.get("default", "")) - pages = mod.fetch(plugin_settings, format_lines, get_rows, get_cols) + # A per-app Location override wins over the global location for this + # app's injected get_location / get_weather helpers (blank = follow global). + _apply_location_override(plugin_settings, settings.get(f"plugin_{app_id}_location")) + # Optional helpers (i18n / get_weather / get_location) are injected only + # when fetch() declares the matching parameter; 4-arg apps are unchanged. + kwargs = _plugin_fetch_kwargs(app_id, mod.fetch, plugin_settings) + pages = mod.fetch(plugin_settings, format_lines, get_rows, get_cols, **kwargs) if not isinstance(pages, list): pages = [str(pages)] _plugin_caches[app_id] = {"pages": pages, "fetched_at": now} @@ -1433,6 +1509,8 @@ def get_plugin_app_list(): "desc": manifest.get("description", "")[:30], "plugin": True, "plugin_id": app_id, + # 🌐 badge: the app adapts to Language AND i18n is globally enabled. + "i18n": bool(manifest.get("i18n")) and _i18n_enabled(), } if "min_rows" in manifest: entry["min_rows"] = manifest["min_rows"] @@ -1443,6 +1521,13 @@ def get_plugin_app_list(): return entries +# The languages an app can be localized into (the global Language select and every +# per-app Language override read from here). Defined once in server/i18n_data.json — +# only Windows-1252 (Western/Latin-1) languages, since the modules can't display +# Greek, Cyrillic, CJK, etc. +LANGUAGE_OPTIONS = i18n.LANGUAGE_OPTIONS + + _SETTING_PASSTHROUGH_KEYS = ( "size", "ph", @@ -1560,6 +1645,42 @@ def map_related_key(raw_related_key): return field +def _app_uses_location(app_id): + """True if the app's fetch() opts into the injected ``get_location`` helper.""" + mod = _plugin_modules.get(app_id) + return bool(mod) and _fetch_accepts(mod.fetch, "get_location") + + +def _auto_setting_fields(app_id, manifest, manifest_settings): + """Per-app override fields the runtime adds automatically (not in the manifest): + a Language override for any i18n app, and a Location override for any app that + reads the injected location. Each is stored under its own ``plugin__*`` key, + and a blank value means "follow the global setting".""" + declared = {s["key"] for s in manifest_settings} + fields = [] + if manifest.get("i18n") and _i18n_enabled() and "language" not in declared: + fields.append({ + "key": f"plugin_{app_id}_language", + "label": "Language", + "type": "select", + "opts": [{"value": "", "label": "Follow global"}] + LANGUAGE_OPTIONS, + "ph": "", + "note": "Override the global Language for this app only.", + }) + if _app_uses_location(app_id) and "location" not in declared: + fields.append({ + "key": f"plugin_{app_id}_location", + "label": "Location", + "type": "search_chips", + "searchUrl": "/location_search", + "resultKey": "results", + "maxItems": 1, + "ph": "", + "note": "Override the global Location for this app only (place search).", + }) + return fields + + def get_plugin_settings_config(): configs = {} for app_id, manifest in _plugin_registry.items(): @@ -1569,6 +1690,7 @@ def get_plugin_settings_config(): _build_plugin_setting_field(app_id, setting, resolved_keys) for setting in manifest_settings ] + fields += _auto_setting_fields(app_id, manifest, manifest_settings) configs[f"plugin_{app_id}"] = { "title": f"{manifest.get('icon', '🧩')} {manifest.get('name', app_id)}", @@ -2080,7 +2202,7 @@ def _trigger_loop(): @app.route('/') def index(): version = _read_version() - return render_template('index.html', version=version) + return render_template('index.html', version=version, language_options=LANGUAGE_OPTIONS) @app.route('/current_state') def current_state(): diff --git a/server/i18n.py b/server/i18n.py new file mode 100644 index 0000000..e2807fc --- /dev/null +++ b/server/i18n.py @@ -0,0 +1,292 @@ +""" +i18n.py — shared localization for apps. + +Two pieces: + * CLDR-correct day / month names via babel — authoritative for every language. + * A curated table of the UI words apps show (SUNRISE, FULL MOON, DAYS …), + translated into the supported languages. Anything without an entry falls back to + the English key, so nothing breaks. + +All data — the selectable ``LANGUAGE_OPTIONS`` list, translations, and the default +currency/country per language — is loaded from ``i18n_data.json`` (see that file's +per-key context notes). Regional variants inherit their base language: ``pt-BR`` +reuses every ``pt`` translation but resolves to Brazil / BRL. + +The runtime binds this to the global Language setting and injects it into any app +whose ``fetch()`` declares an ``i18n`` parameter (like ``get_weather``); on a host +without the helper the app just falls back to English. Translations stay in one +place instead of being copied into every self-contained app. +""" + +from __future__ import annotations + +import json +import os + +# All localization data — the selectable language list, curated UI strings, +# duration-unit suffixes, holiday-name translations, and the default currency/country +# a language implies — lives in i18n_data.json so it can be edited, corrected, or +# extended with a whole new language without touching this module. A missing or +# invalid file degrades gracefully, so the lookups below just return their +# English/None defaults and apps keep working in English. +_DATA_PATH = os.path.join(os.path.dirname(__file__), "i18n_data.json") + +# Minimal fallback so the Language selector is never empty if the data file is +# missing/broken (the runtime still works, just English-only). +_DEFAULT_LANGUAGES = [{"value": "en-US", "label": "English (US)"}] + + +def _translations(section): + """Flatten a data section into the runtime ``{key: {lang: value}}`` lookup. Each + entry in the file is ``{"context": "...", "translations": {lang: value}}`` (the + context note is for translators only); a bare ``{lang: value}`` is also accepted.""" + out = {} + for key, entry in (section or {}).items(): + if isinstance(entry, dict) and "translations" in entry: + out[key] = entry["translations"] + elif isinstance(entry, dict): + out[key] = entry + return out + + +def _translations_by_domain(section): + """The ``strings`` section is grouped by context/domain + (``{domain: {NAME: {context, translations}}}``); flatten each domain to + ``{domain: {NAME: {lang: value}}}`` for lookup.""" + return {domain: _translations(entries) for domain, entries in (section or {}).items()} + + +def _load_i18n_data(): + try: + with open(_DATA_PATH, encoding="utf-8") as fh: + data = json.load(fh) + except (OSError, ValueError): + data = {} + return (_translations_by_domain(data.get("strings")), + _translations(data.get("holidays")), + data.get("base_currency", {}), + data.get("country", {}), + data.get("languages") or _DEFAULT_LANGUAGES) + + +(_STRINGS, _HOLIDAYS, _BASE_CURRENCY, _COUNTRY, + LANGUAGE_OPTIONS) = _load_i18n_data() + +# The compact duration-unit suffixes (D/H/M/S/Y) live in the shared 'time' domain, +# alongside the full-word forms (DAYS, HOURS, WEEKS, …) — one duration vocabulary. +_DURATION_UNITS = _STRINGS.get("time", {}) + + +def _base_lang(lang): + """The base language subtag: ``pt-BR`` / ``pt_BR`` -> ``pt``, ``de`` -> ``de``.""" + return str(lang or "").replace("_", "-").split("-")[0].lower() + + +def _localized(table, key, lang, default): + """Look up ``key`` in a ``{lang: value}`` table, trying the full language code + first and then its base subtag, so a regional variant (``pt-BR``) inherits the + base language (``pt``) wherever it has no entry of its own.""" + variants = table.get(key) + if not variants: + return default + code = str(lang or "").lower() + if code in variants: + return variants[code] + base = _base_lang(lang) + if base in variants: + return variants[base] + return default + + +def translate(text, lang, ctx="common"): + """English UI string -> localized for a given context/domain (``ctx``), like + gettext's ``pgettext``. The same English text can differ per context (weather + ``HIGH`` = a level, tides ``HIGH`` = high tide). Resolution order: the given + domain, then the shared ``common`` domain, then the English text itself — so an + unknown or English language always returns ``text`` and nothing breaks.""" + if not lang or _base_lang(lang) == "en": + return text + hit = _localized(_STRINGS.get(ctx) or {}, text, lang, None) + if hit is None and ctx != "common": + hit = _localized(_STRINGS.get("common") or {}, text, lang, None) + return text if hit is None else hit + + +def _babel_locale(lang): + """Our language code -> a babel locale id. English carries a region that changes + date order (US 'July 9' vs UK/AU '9 July'): 'en-GB' -> 'en_GB', 'en'/'en-US' -> + 'en_US'. Other languages just use their base code ('fr', 'de', …).""" + if not lang: + return "en_US" + parts = str(lang).replace("-", "_").split("_") + base = parts[0].lower() + if base == "en": + region = parts[1].upper() if len(parts) > 1 and parts[1] else "US" + return f"en_{region}" + return base + + +def _cldr(dt, fmt, lang): + try: + from babel.dates import format_date + return format_date(dt, fmt, locale=_babel_locale(lang)).upper() + except Exception: + return None + + +def weekday(dt, lang, short=False): + return _cldr(dt, "EEE" if short else "EEEE", lang) or dt.strftime("%a" if short else "%A").upper() + + +def month(dt, lang, short=False): + return _cldr(dt, "MMM" if short else "MMMM", lang) or dt.strftime("%b" if short else "%B").upper() + + +def date(dt, lang, short=False, year=False): + """Day + month (optionally year) in the locale's own order and wording: + ``JULY 9`` (en) but ``9 JUILLET`` (fr), ``9. JULI`` (de), ``9 DE JULIO`` (es).""" + skeleton = ("MMM" if short else "MMMM") + "d" + ("y" if year else "") + try: + from babel.dates import format_skeleton + return format_skeleton(skeleton, dt, locale=_babel_locale(lang)).upper() + except Exception: + base = f"{month(dt, lang, short)} {dt.day}" + return f"{base} {dt.year}" if year else base + + +def duration_unit(key, lang): + """Localized compact duration suffix (D/H/M/S/Y) -> e.g. J/H/M/S in French. The + duration vocabulary (abbreviations and full words) lives in the 'time' domain.""" + return translate(key, lang, "time") + + +def uses_24h(lang): + """AM/PM is essentially an English-language convention; everyone else is 24h.""" + return bool(lang) and _base_lang(lang) != "en" + + +# Non-ASCII group separators CLDR uses (French narrow/no-break spaces) — the flap +# display only speaks Windows-1252, so we fold them back to a plain space. +_GROUP_SPACES = ("\u202f", "\u00a0", "\u2009") + + +def number(value, lang, decimals=2, grouping=True): + """Format a number with the locale's own separators: 1,234.5 (en) vs 1.234,5 + (de/es/it/…) vs 1 234,5 (fr). Falls back to English grouping without babel.""" + try: + from babel.numbers import format_decimal + pattern = ("#,##0" if grouping else "0") + ("." + "0" * decimals if decimals > 0 else "") + s = format_decimal(float(value), format=pattern, locale=_babel_locale(lang)) + except Exception: + try: + s = f"{float(value):,.{decimals}f}" if grouping else f"{float(value):.{decimals}f}" + except Exception: + return str(value) + for ch in _GROUP_SPACES: + s = s.replace(ch, " ") + return s + + +def base_currency(lang): + """The "home" currency a language/region implies (the default base for FX): US -> + USD, UK -> GBP, Brazil -> BRL, the rest of Western Europe -> EUR. From + i18n_data.json; a regional variant (``pt-BR``) wins over its base (``pt``), and + unknown languages default to USD. Users can override explicitly.""" + code = str(lang or "en").lower() + return _BASE_CURRENCY.get(code) or _BASE_CURRENCY.get(_base_lang(lang or "en"), "USD") + + +def currency_symbol(code): + """Compact symbol for an ISO 4217 currency code ('EUR' -> '€', 'USD' -> '$', + 'GBP' -> '£') from babel, using a neutral (English) locale so it's the short + universal form — not a locale-disambiguated one like '$US'/'£GB'. Falls back to + the ISO code when the symbol has characters the flap display (Windows-1252) can't + render — ₹, ₩, ฿ -> 'INR'/'KRW'/'THB'.""" + if not code: + return "" + code = code.upper() + try: + from babel.numbers import get_currency_symbol + sym = get_currency_symbol(code, locale="en") or code + except Exception: + sym = {"USD": "$", "EUR": "€", "GBP": "£", "JPY": "¥"}.get(code, code) + try: + sym.encode("cp1252") + except UnicodeEncodeError: + return code + return sym + + +def country(lang): + """The country a language/region implies — for holidays (which calendar to show) + and other country-scoped data. Regional variants split by country (``pt-BR`` -> + BR, ``pt`` -> PT); from i18n_data.json, unknown languages default to US.""" + code = str(lang or "en").lower() + return _COUNTRY.get(code) or _COUNTRY.get(_base_lang(lang or "en"), "US") + + +def holiday(name, lang): + """Localized public-holiday name, or None if we have no translation (caller + then keeps the source's native name).""" + if not lang or not name: + return None + return _localized(_HOLIDAYS, str(name).strip().lower(), lang, None) + + +def clock(dt, lang, seconds=False, ampm_space=True): + """Locale-appropriate wall-clock time: ``3:48 PM`` in English, ``15:48`` elsewhere.""" + if uses_24h(lang): + return dt.strftime("%H:%M:%S" if seconds else "%H:%M") + body = dt.strftime("%I:%M:%S" if seconds else "%I:%M").lstrip("0") + sep = " " if ampm_space else "" + return f"{body}{sep}{dt.strftime('%p')}" + + +class Localizer: + """Language-bound convenience wrapper handed to apps as ``i18n``.""" + + def __init__(self, lang): + self.lang = (lang or "en").lower() + + @property + def is_24h(self): + return uses_24h(self.lang) + + def t(self, text, ctx="common"): + return translate(text, self.lang, ctx) + + def weekday(self, dt, short=False): + return weekday(dt, self.lang, short) + + def month(self, dt, short=False): + return month(dt, self.lang, short) + + def date(self, dt, short=False, year=False): + return date(dt, self.lang, short, year) + + def time(self, dt, seconds=False, ampm_space=True): + return clock(dt, self.lang, seconds, ampm_space) + + def unit(self, key): + return duration_unit(key, self.lang) + + def number(self, value, decimals=2, grouping=True): + return number(value, self.lang, decimals, grouping) + + def base_currency(self): + return base_currency(self.lang) + + def currency_symbol(self, code): + return currency_symbol(code) + + def country(self): + return country(self.lang) + + def holiday(self, name): + return holiday(name, self.lang_base) + + @property + def lang_base(self): + """The 2-letter language without region ('en-GB' -> 'en'), for APIs that + want a plain language code (Wikipedia editions, weather providers, …).""" + return self.lang.split("-")[0] diff --git a/server/i18n_data.json b/server/i18n_data.json new file mode 100644 index 0000000..ff03329 --- /dev/null +++ b/server/i18n_data.json @@ -0,0 +1,2628 @@ +{ + "_comment": "Localization data for i18n.py, loaded at import. 'languages' is the Language selector list. 'strings' groups translations by SEMANTIC context/domain (like gettext msgctxt): strings[domain][NAME] has a generic 'context' note and a language->text map. A domain is a MEANING, not an app — a word with one meaning lives in one domain (e.g. the shared 'time' vocab), and is duplicated only when its meaning differs by context (weather HIGH = a level, tides HIGH = high tide). Apps translate via the relevant domain; the 'common' domain (OFFLINE, ERROR, …) backs every context. A missing (domain, NAME, language) falls back to the English NAME, and a regional variant (pt-BR) inherits its base language (pt). 'duration_units' (D/H/M/S symbols) and 'holidays' stay flat; 'base_currency'/'country' are the default a language implies when no Location is set. Only Windows-1252 (Western/Latin-1) languages. Edit here to fix/add translations.", + "languages": [ + { + "value": "en-US", + "label": "English (US)" + }, + { + "value": "en-GB", + "label": "English (UK)" + }, + { + "value": "en-AU", + "label": "English (Australia)" + }, + { + "value": "en-CA", + "label": "English (Canada)" + }, + { + "value": "fr", + "label": "Français (French)" + }, + { + "value": "fr-CA", + "label": "Français (Canada)" + }, + { + "value": "fr-BE", + "label": "Français (Belgique)" + }, + { + "value": "fr-CH", + "label": "Français (Suisse)" + }, + { + "value": "de", + "label": "Deutsch (German)" + }, + { + "value": "de-AT", + "label": "Deutsch (Österreich)" + }, + { + "value": "de-CH", + "label": "Deutsch (Schweiz)" + }, + { + "value": "es", + "label": "Español (Spanish)" + }, + { + "value": "es-MX", + "label": "Español (México)" + }, + { + "value": "es-AR", + "label": "Español (Argentina)" + }, + { + "value": "it", + "label": "Italiano (Italian)" + }, + { + "value": "it-CH", + "label": "Italiano (Svizzera)" + }, + { + "value": "pt", + "label": "Português (Portugal)" + }, + { + "value": "pt-BR", + "label": "Português (Brasil)" + }, + { + "value": "nl", + "label": "Nederlands (Dutch)" + }, + { + "value": "nl-BE", + "label": "Nederlands (België)" + }, + { + "value": "da", + "label": "Dansk (Danish)" + }, + { + "value": "no", + "label": "Norsk (Norwegian)" + }, + { + "value": "sv", + "label": "Svenska (Swedish)" + }, + { + "value": "fi", + "label": "Suomi (Finnish)" + }, + { + "value": "is", + "label": "Íslenska (Icelandic)" + }, + { + "value": "ga", + "label": "Gaeilge (Irish)" + }, + { + "value": "ca", + "label": "Català (Catalan)" + }, + { + "value": "gl", + "label": "Galego (Galician)" + }, + { + "value": "eu", + "label": "Euskara (Basque)" + }, + { + "value": "et", + "label": "Eesti (Estonian)" + }, + { + "value": "af", + "label": "Afrikaans" + }, + { + "value": "id", + "label": "Bahasa Indonesia" + }, + { + "value": "ms", + "label": "Bahasa Melayu (Malay)" + }, + { + "value": "sw", + "label": "Kiswahili (Swahili)" + } + ], + "strings": { + "common": { + "OFFLINE": { + "context": "Status: the device/service is offline.", + "translations": { + "fr": "HORS LIGNE", + "de": "OFFLINE", + "es": "SIN CONEXION", + "it": "OFFLINE", + "pt": "OFFLINE", + "nl": "OFFLINE", + "da": "OFFLINE", + "no": "OFFLINE", + "sv": "OFFLINE" + } + }, + "NO DATA": { + "context": "Status: no data was returned.", + "translations": { + "fr": "PAS DE DONNEES", + "de": "KEINE DATEN", + "es": "SIN DATOS", + "it": "NESSUN DATO", + "pt": "SEM DADOS", + "nl": "GEEN DATA", + "da": "INGEN DATA", + "no": "INGEN DATA", + "sv": "INGEN DATA" + } + }, + "ERROR": { + "context": "Status: a generic error occurred.", + "translations": { + "fr": "ERREUR", + "de": "FEHLER", + "es": "ERROR", + "it": "ERRORE", + "pt": "ERRO", + "nl": "FOUT", + "da": "FEJL", + "no": "FEIL", + "sv": "FEL" + } + }, + "UNKNOWN": { + "context": "Value unknown / unavailable.", + "translations": { + "fr": "INCONNU", + "de": "UNBEKANNT", + "es": "DESCON.", + "it": "SCONOSC.", + "pt": "DESCON.", + "nl": "ONBEKEND", + "da": "UKENDT", + "no": "UKJENT", + "sv": "OKÄND" + } + }, + "INVALID DATE": { + "context": "A configured date could not be parsed.", + "translations": { + "fr": "DATE INVALIDE", + "de": "UNGULT. DATUM", + "es": "FECHA INVALIDA", + "it": "DATA NON VALIDA", + "pt": "DATA INVALIDA", + "nl": "ONGELDIGE DATUM" + } + }, + "API FAIL": { + "context": "Status: an upstream API call failed.", + "translations": { + "fr": "ECHEC API", + "de": "API-FEHLER", + "es": "FALLO API", + "it": "ERRORE API", + "pt": "FALHA API", + "nl": "API-FOUT", + "da": "API-FEJL", + "no": "API-FEIL", + "sv": "API-FEL" + } + }, + "CONFIGURE": { + "context": "Prompt to configure the app in Settings.", + "translations": { + "fr": "CONFIGURER", + "de": "EINRICHTEN", + "es": "CONFIGURAR", + "it": "CONFIGURA", + "pt": "CONFIGURAR", + "nl": "INSTELLEN", + "da": "OPSÆT", + "no": "SETT OPP", + "sv": "KONFIGURERA" + } + } + }, + "time": { + "DAYS": { + "context": "Duration unit word (plural): days.", + "translations": { + "fr": "JOURS", + "de": "TAGE", + "es": "DÍAS", + "it": "GIORNI", + "pt": "DIAS", + "nl": "DAGEN", + "da": "DAGE", + "no": "DAGER", + "sv": "DAGAR" + } + }, + "HOURS": { + "context": "Duration unit word (plural): hours.", + "translations": { + "fr": "HEURES", + "de": "STUNDEN", + "es": "HORAS", + "it": "ORE", + "pt": "HORAS", + "nl": "UREN", + "da": "TIMER", + "no": "TIMER", + "sv": "TIMMAR" + } + }, + "MINS": { + "context": "Time unit: minutes (abbreviated).", + "translations": { + "fr": "MIN", + "de": "MIN", + "es": "MIN", + "it": "MIN", + "pt": "MIN", + "nl": "MIN", + "da": "MIN", + "no": "MIN", + "sv": "MIN" + } + }, + "LEFT": { + "context": "Follows a remaining duration ('3D LEFT').", + "translations": { + "fr": "RESTE", + "de": "UBRIG", + "es": "QUEDAN", + "it": "MANCA", + "pt": "FALTAM", + "nl": "OVER", + "da": "TILBAGE", + "no": "IGJEN", + "sv": "KVAR" + } + }, + "REMAINING": { + "context": "Follows a duration to mean time remaining ('3D REMAINING').", + "translations": { + "fr": "RESTANT", + "de": "VERBLEIB.", + "es": "RESTANTE", + "it": "RIMANENTE", + "pt": "RESTANTE", + "nl": "RESTEREND", + "da": "TILBAGE", + "no": "GJENSTÅR", + "sv": "ÅTERSTÅR" + } + }, + "ARRIVED": { + "context": "Shown when an awaited moment has arrived.", + "translations": { + "fr": "ARRIVE", + "de": "DA", + "es": "LLEGO", + "it": "ARRIVATO", + "pt": "CHEGOU", + "nl": "AANGEKOMEN", + "da": "ANKOMMET", + "no": "ANKOMMET", + "sv": "ANLÄNT" + } + }, + "HERE": { + "context": "Short 'it's here' when an awaited moment arrives.", + "translations": { + "fr": "ICI", + "de": "HIER", + "es": "AQUI", + "it": "QUI", + "pt": "AQUI", + "nl": "HIER", + "da": "HER", + "no": "HER", + "sv": "HÄR" + } + }, + "CELEBRATE": { + "context": "Celebratory word shown when an event arrives.", + "translations": { + "fr": "FETEZ", + "de": "FEIERN", + "es": "CELEBRA", + "it": "FESTA", + "pt": "FESTA", + "nl": "VIER", + "da": "FEJR", + "no": "FEIRE", + "sv": "FIRA" + } + }, + "PARTY": { + "context": "Celebratory word shown when an event arrives.", + "translations": { + "fr": "FETE", + "de": "PARTY", + "es": "FIESTA", + "it": "FESTA", + "pt": "FESTA", + "nl": "FEEST", + "da": "FEST", + "no": "FEST", + "sv": "FEST" + } + }, + "TODAY": { + "context": "The word 'today'.", + "translations": { + "fr": "AUJOURDHUI", + "de": "HEUTE", + "es": "HOY", + "it": "OGGI", + "pt": "HOJE", + "nl": "VANDAAG", + "da": "I DAG", + "no": "I DAG", + "sv": "IDAG" + } + }, + "NOW": { + "context": "The word 'now'.", + "translations": { + "fr": "MAINTENANT", + "de": "JETZT", + "es": "AHORA", + "it": "ORA", + "pt": "AGORA", + "nl": "NU", + "da": "NU", + "no": "NÅ", + "sv": "NU" + } + }, + "IN": { + "context": "Connector before a duration: 'IN 3 DAYS'.", + "translations": { + "fr": "DANS", + "de": "IN", + "es": "EN", + "it": "TRA", + "pt": "EM", + "nl": "OVER", + "da": "OM", + "no": "OM", + "sv": "OM" + } + }, + "Y": { + "context": "Compact duration suffix for years (after a number, e.g. '2A').", + "translations": { + "fr": "A", + "de": "J", + "es": "A", + "it": "A", + "pt": "A", + "nl": "J", + "da": "Å", + "no": "Å", + "sv": "Å" + } + }, + "D": { + "context": "Compact duration suffix for days (e.g. '175D').", + "translations": { + "fr": "J", + "de": "T", + "es": "D", + "it": "G", + "pt": "D", + "nl": "D" + } + }, + "H": { + "context": "Compact duration suffix for hours.", + "translations": { + "fr": "H", + "de": "H", + "es": "H", + "it": "H", + "pt": "H", + "nl": "U", + "da": "T", + "no": "T", + "sv": "T" + } + }, + "M": { + "context": "Compact duration suffix for minutes.", + "translations": { + "fr": "M", + "de": "M", + "es": "M", + "it": "M", + "pt": "M", + "nl": "M" + } + }, + "S": { + "context": "Compact duration suffix for seconds.", + "translations": { + "fr": "S", + "de": "S", + "es": "S", + "it": "S", + "pt": "S", + "nl": "S" + } + }, + "YEARS": { + "context": "Duration unit word (plural): years.", + "translations": { + "fr": "ANS", + "de": "JAHRE", + "es": "AÑOS", + "it": "ANNI", + "pt": "ANOS", + "nl": "JAREN", + "da": "ÅR", + "no": "ÅR", + "sv": "ÅR" + } + }, + "MONTHS": { + "context": "Duration unit word (plural): months.", + "translations": { + "fr": "MOIS", + "de": "MONATE", + "es": "MESES", + "it": "MESI", + "pt": "MESES", + "nl": "MAANDEN", + "da": "MÅNEDER", + "no": "MÅNEDER", + "sv": "MÅNADER" + } + }, + "WEEKS": { + "context": "Duration unit word (plural): weeks.", + "translations": { + "fr": "SEMAINES", + "de": "WOCHEN", + "es": "SEMANAS", + "it": "SETTIMANE", + "pt": "SEMANAS", + "nl": "WEKEN", + "da": "UGER", + "no": "UKER", + "sv": "VECKOR" + } + }, + "MINUTES": { + "context": "Duration unit word (plural): minutes.", + "translations": { + "fr": "MINUTES", + "de": "MINUTEN", + "es": "MINUTOS", + "it": "MINUTI", + "pt": "MINUTOS", + "nl": "MINUTEN", + "da": "MINUTTER", + "no": "MINUTTER", + "sv": "MINUTER" + } + }, + "SECONDS": { + "context": "Duration unit word (plural): seconds.", + "translations": { + "fr": "SECONDES", + "de": "SEKUNDEN", + "es": "SEGUNDOS", + "it": "SECONDI", + "pt": "SEGUNDOS", + "nl": "SECONDEN", + "da": "SEKUNDER", + "no": "SEKUNDER", + "sv": "SEKUNDER" + } + }, + "TIME SINCE": { + "context": "'Time since ' label.", + "translations": { + "fr": "DEPUIS", + "de": "SEIT", + "es": "DESDE", + "it": "DA", + "pt": "DESDE", + "nl": "SINDS", + "da": "SIDEN", + "no": "SIDEN", + "sv": "SEDAN" + } + }, + "NOT YET": { + "context": "An awaited event is still in the future.", + "translations": { + "fr": "PAS ENCORE", + "de": "NOCH NICHT", + "es": "AUN NO", + "it": "NON ANCORA", + "pt": "AINDA NAO", + "nl": "NOG NIET", + "da": "IKKE ENDNU", + "no": "IKKE ENNÅ", + "sv": "INTE ÄNNU" + } + }, + "STARTED": { + "context": "An event has started / passed.", + "translations": { + "fr": "COMMENCE", + "de": "GESTARTET", + "es": "EMPEZADO", + "it": "INIZIATO", + "pt": "INICIADO", + "nl": "GESTART", + "da": "STARTET", + "no": "STARTET", + "sv": "STARTAT" + } + } + }, + "sun": { + "SUNRISE": { + "context": "The time the sun rises.", + "translations": { + "fr": "LEVER", + "de": "AUFGANG", + "es": "AMANECER", + "it": "ALBA", + "pt": "NASCER", + "nl": "OPKOMST", + "da": "SOLOPGANG", + "no": "SOLOPPGANG", + "sv": "SOLUPPGÅNG" + } + }, + "SUNSET": { + "context": "The time the sun sets.", + "translations": { + "fr": "COUCHER", + "de": "UNTERGANG", + "es": "OCASO", + "it": "TRAMONTO", + "pt": "OCASO", + "nl": "ONDERGANG", + "da": "SOLNEDGANG", + "no": "SOLNEDGANG", + "sv": "SOLNEDGÅNG" + } + }, + "DAYLIGHT": { + "context": "Total hours of daylight.", + "translations": { + "fr": "JOUR", + "de": "TAGESLICHT", + "es": "LUZ DIA", + "it": "LUCE", + "pt": "LUZ DIA", + "nl": "DAGLICHT", + "da": "DAGSLYS", + "no": "DAGSLYS", + "sv": "DAGSLJUS" + } + }, + "UP": { + "context": "Very short label for sunrise (sun is up).", + "translations": { + "fr": "LEV", + "de": "AUF", + "es": "SAL", + "it": "ALBA", + "pt": "NASC", + "nl": "OP" + } + }, + "DN": { + "context": "Very short label for sunset (sun is down).", + "translations": { + "fr": "COU", + "de": "UNT", + "es": "OCA", + "it": "TRAM", + "pt": "OCA", + "nl": "OND" + } + } + }, + "moon": { + "NEW MOON": { + "context": "Lunar phase: new moon.", + "translations": { + "fr": "NOUVELLE LUNE", + "de": "NEUMOND", + "es": "LUNA NUEVA", + "it": "LUNA NUOVA", + "pt": "LUA NOVA", + "nl": "NIEUWE MAAN", + "da": "NYMÅNE", + "no": "NYMÅNE", + "sv": "NYMÅNE" + } + }, + "FULL MOON": { + "context": "Lunar phase: full moon.", + "translations": { + "fr": "PLEINE LUNE", + "de": "VOLLMOND", + "es": "LUNA LLENA", + "it": "LUNA PIENA", + "pt": "LUA CHEIA", + "nl": "VOLLE MAAN", + "da": "FULDMÅNE", + "no": "FULLMÅNE", + "sv": "FULLMÅNE" + } + }, + "FIRST QUARTER": { + "context": "Lunar phase: first quarter (keep short).", + "translations": { + "fr": "1ER QUARTIER", + "de": "1. VIERTEL", + "es": "CUARTO CREC.", + "it": "PRIMO QUARTO", + "pt": "QUARTO CRESC.", + "nl": "EERSTE KWART." + } + }, + "LAST QUARTER": { + "context": "Lunar phase: last quarter (keep short).", + "translations": { + "fr": "DERN. QUARTIER", + "de": "LETZT. VIERTEL", + "es": "CUARTO MENG.", + "it": "ULTIMO QUARTO", + "pt": "QUARTO MING.", + "nl": "LAATSTE KWART." + } + }, + "WAXING CRESCENT": { + "context": "Lunar phase: waxing crescent (growing).", + "translations": { + "fr": "1ER CROISSANT", + "de": "ZUN. SICHEL", + "es": "CRECIENTE", + "it": "CRESCENTE", + "pt": "CRESCENTE", + "nl": "WASSEND" + } + }, + "WANING CRESCENT": { + "context": "Lunar phase: waning crescent (shrinking).", + "translations": { + "fr": "DERN. CROISSANT", + "de": "ABN. SICHEL", + "es": "MENGUANTE", + "it": "CALANTE", + "pt": "MINGUANTE", + "nl": "AFNEMEND" + } + }, + "WAXING GIBBOUS": { + "context": "Lunar phase: waxing gibbous (growing).", + "translations": { + "fr": "GIBBEUSE CROIS.", + "de": "ZUN. MOND", + "es": "GIBOSA CREC.", + "it": "GIBBOSA CRESC.", + "pt": "GIBOSA CRESC.", + "nl": "WASSEND" + } + }, + "WANING GIBBOUS": { + "context": "Lunar phase: waning gibbous (shrinking).", + "translations": { + "fr": "GIBBEUSE DECR.", + "de": "ABN. MOND", + "es": "GIBOSA MENG.", + "it": "GIBBOSA CAL.", + "pt": "GIBOSA MING.", + "nl": "AFNEMEND" + } + }, + "LIT": { + "context": "Precedes the percentage of a surface that is illuminated, e.g. '16% LIT'.", + "translations": { + "fr": "ECLAIRE", + "de": "HELL", + "es": "ILUM.", + "it": "ILLUM.", + "pt": "ILUM.", + "nl": "VERL.", + "da": "OPLYST", + "no": "OPPLYST", + "sv": "UPPLYST" + } + }, + "FULL IN": { + "context": "'FULL IN 3D' = time until something is full (e.g. the moon).", + "translations": { + "fr": "PLEINE DANS", + "de": "VOLL IN", + "es": "LLENA EN", + "it": "PIENA IN", + "pt": "CHEIA EM", + "nl": "VOL OVER", + "da": "FULD OM", + "no": "FULL OM", + "sv": "FULL OM" + } + }, + "NEW IN": { + "context": "'NEW IN 3D' = time until something is new (e.g. the moon).", + "translations": { + "fr": "NOUV. DANS", + "de": "NEU IN", + "es": "NUEVA EN", + "it": "NUOVA IN", + "pt": "NOVA EM", + "nl": "NIEUW OVER", + "da": "NY OM", + "no": "NY OM", + "sv": "NY OM" + } + } + }, + "weather": { + "CLEAR": { + "context": "Weather condition: clear sky.", + "translations": { + "fr": "DEGAGE", + "de": "KLAR", + "es": "DESPEJADO", + "it": "SERENO", + "pt": "LIMPO", + "nl": "HELDER", + "da": "KLART", + "no": "KLART", + "sv": "KLART" + } + }, + "MAINLY CLEAR": { + "context": "Weather condition: mostly clear sky.", + "translations": { + "fr": "PLUTOT CLAIR", + "de": "MEIST KLAR", + "es": "MAYORM. CLARO", + "it": "POCO NUVOLOSO", + "pt": "QUASE LIMPO", + "nl": "VNL. HELDER" + } + }, + "PARTLY CLOUDY": { + "context": "Weather condition: partly cloudy.", + "translations": { + "fr": "NUAGEUX", + "de": "TEILS WOLKIG", + "es": "PARC. NUBLADO", + "it": "POCO NUVOLOSO", + "pt": "PARC. NUBLADO", + "nl": "HALF BEWOLKT", + "da": "DELVIS SKYET", + "no": "DELVIS SKYET", + "sv": "DELVIS MOLNIGT" + } + }, + "OVERCAST": { + "context": "Weather condition: fully clouded over.", + "translations": { + "fr": "COUVERT", + "de": "BEDECKT", + "es": "CUBIERTO", + "it": "COPERTO", + "pt": "ENCOBERTO", + "nl": "BEWOLKT", + "da": "OVERSKYET", + "no": "OVERSKYET", + "sv": "MULET" + } + }, + "FOG": { + "context": "Weather condition: fog.", + "translations": { + "fr": "BROUILLARD", + "de": "NEBEL", + "es": "NIEBLA", + "it": "NEBBIA", + "pt": "NEVOEIRO", + "nl": "MIST", + "da": "TÅGE", + "no": "TÅKE", + "sv": "DIMMA" + } + }, + "RIME FOG": { + "context": "Weather condition: freezing/rime fog.", + "translations": { + "fr": "BRUME GIVRANTE", + "de": "RAUREIF", + "es": "NIEBLA HELADA", + "it": "NEBBIA GHIAC.", + "pt": "NEVOA GELADA", + "nl": "RIJP-MIST" + } + }, + "LIGHT DRIZZLE": { + "context": "Weather condition: light drizzle.", + "translations": { + "fr": "BRUINE LEGERE", + "de": "LEICHT NIESEL", + "es": "LLOVIZNA LEVE", + "it": "PIOVIGGINE", + "pt": "CHUVISCO FRACO", + "nl": "LICHTE MOTREG" + } + }, + "DRIZZLE": { + "context": "Weather condition: drizzle.", + "translations": { + "fr": "BRUINE", + "de": "NIESELN", + "es": "LLOVIZNA", + "it": "PIOVIGGINE", + "pt": "CHUVISCO", + "nl": "MOTREGEN" + } + }, + "HEAVY DRIZZLE": { + "context": "Weather condition: heavy drizzle.", + "translations": { + "fr": "BRUINE FORTE", + "de": "STARK. NIESEL", + "es": "LLOVIZNA FTE", + "it": "PIOVIGGINE FT", + "pt": "CHUVISCO FORTE", + "nl": "ZWARE MOTREG" + } + }, + "LIGHT RAIN": { + "context": "Weather condition: light rain.", + "translations": { + "fr": "PLUIE LEGERE", + "de": "LEICHT. REGEN", + "es": "LLUVIA LEVE", + "it": "PIOGGIA LEGG.", + "pt": "CHUVA FRACA", + "nl": "LICHTE REGEN", + "da": "LET REGN", + "no": "LETT REGN", + "sv": "LÄTT REGN" + } + }, + "RAIN": { + "context": "Weather condition: rain.", + "translations": { + "fr": "PLUIE", + "de": "REGEN", + "es": "LLUVIA", + "it": "PIOGGIA", + "pt": "CHUVA", + "nl": "REGEN", + "da": "REGN", + "no": "REGN", + "sv": "REGN" + } + }, + "HEAVY RAIN": { + "context": "Weather condition: heavy rain.", + "translations": { + "fr": "PLUIE FORTE", + "de": "STARKREGEN", + "es": "LLUVIA FUERTE", + "it": "PIOGGIA FORTE", + "pt": "CHUVA FORTE", + "nl": "ZWARE REGEN", + "da": "KRAFTIG REGN", + "no": "KRAFTIG REGN", + "sv": "KRAFTIGT REGN" + } + }, + "LIGHT FREEZING RAIN": { + "context": "Weather condition: light freezing rain.", + "translations": { + "fr": "PLUIE VERGLAC.", + "de": "L. GEFR. REGEN", + "es": "LLUVIA HELADA", + "it": "PIOGGIA GELATA", + "pt": "CHUVA GELADA", + "nl": "LICHTE IJZEL" + } + }, + "FREEZING RAIN": { + "context": "Weather condition: freezing rain.", + "translations": { + "fr": "PLUIE VERGLAC.", + "de": "GEFRIER-REGEN", + "es": "LLUVIA HELADA", + "it": "PIOGGIA GELATA", + "pt": "CHUVA GELADA", + "nl": "IJZEL" + } + }, + "LIGHT SNOW": { + "context": "Weather condition: light snow.", + "translations": { + "fr": "NEIGE LEGERE", + "de": "LEICHT SCHNEE", + "es": "NIEVE LEVE", + "it": "NEVE LEGGERA", + "pt": "NEVE FRACA", + "nl": "LICHTE SNEEUW", + "da": "LET SNE", + "no": "LETT SNØ", + "sv": "LÄTT SNÖ" + } + }, + "SNOW": { + "context": "Weather condition: snow.", + "translations": { + "fr": "NEIGE", + "de": "SCHNEE", + "es": "NIEVE", + "it": "NEVE", + "pt": "NEVE", + "nl": "SNEEUW", + "da": "SNE", + "no": "SNØ", + "sv": "SNÖ" + } + }, + "HEAVY SNOW": { + "context": "Weather condition: heavy snow.", + "translations": { + "fr": "NEIGE FORTE", + "de": "STARK. SCHNEE", + "es": "NIEVE FUERTE", + "it": "NEVE FORTE", + "pt": "NEVE FORTE", + "nl": "ZWARE SNEEUW", + "da": "KRAFTIG SNE", + "no": "KRAFTIG SNØ", + "sv": "KRAFTIGT SNÖ" + } + }, + "SNOW GRAINS": { + "context": "Weather condition: snow grains.", + "translations": { + "fr": "GRAINS NEIGE", + "de": "SCHNEEGRIESEL", + "es": "GRANIZO NIEVE", + "it": "NEVE GRANUL.", + "pt": "NEVE GRANULAR", + "nl": "KORRELSNEEUW" + } + }, + "RAIN SHOWERS": { + "context": "Weather condition: rain showers.", + "translations": { + "fr": "AVERSES", + "de": "REGENSCHAUER", + "es": "CHUBASCOS", + "it": "ROVESCI", + "pt": "AGUACEIROS", + "nl": "REGENBUIEN" + } + }, + "HEAVY SHOWERS": { + "context": "Weather condition: heavy rain showers.", + "translations": { + "fr": "AVERSES FORTES", + "de": "STARK. SCHAUER", + "es": "CHUBASCOS FTES", + "it": "ROVESCI FORTI", + "pt": "AGUAC. FORTES", + "nl": "ZWARE BUIEN" + } + }, + "SNOW SHOWERS": { + "context": "Weather condition: snow showers.", + "translations": { + "fr": "AVERSES NEIGE", + "de": "SCHNEESCHAUER", + "es": "CHUBASCOS NIEVE", + "it": "ROVESCI NEVE", + "pt": "AGUAC. NEVE", + "nl": "SNEEUWBUIEN" + } + }, + "HEAVY SNOW SHOWERS": { + "context": "Weather condition: heavy snow showers.", + "translations": { + "fr": "AVERSES NEIGE", + "de": "SCHNEESCHAUER", + "es": "CHUBASCOS NIEVE", + "it": "ROVESCI NEVE", + "pt": "AGUAC. NEVE", + "nl": "SNEEUWBUIEN" + } + }, + "THUNDERSTORM": { + "context": "Weather condition: thunderstorm.", + "translations": { + "fr": "ORAGE", + "de": "GEWITTER", + "es": "TORMENTA", + "it": "TEMPORALE", + "pt": "TROVOADA", + "nl": "ONWEER", + "da": "TORDENVEJR", + "no": "TORDENVÆR", + "sv": "ÅSKA" + } + }, + "THUNDER HAIL": { + "context": "Weather condition: thunderstorm with hail.", + "translations": { + "fr": "ORAGE GRELE", + "de": "GEWITTER HAGEL", + "es": "TORM. GRANIZO", + "it": "TEMP. GRANDINE", + "pt": "TROV. GRANIZO", + "nl": "ONWEER HAGEL" + } + }, + "SEVERE TSTORM": { + "context": "Weather condition: severe thunderstorm (abbreviated).", + "translations": { + "fr": "ORAGE VIOLENT", + "de": "SCHW. GEWITTER", + "es": "TORM. FUERTE", + "it": "TEMP. VIOLENTO", + "pt": "TROV. FORTE", + "nl": "ZWAAR ONWEER" + } + }, + "CURRENT CONDITIONS": { + "context": "Generic label for present weather conditions.", + "translations": { + "fr": "CONDITIONS", + "de": "WETTER", + "es": "TIEMPO", + "it": "METEO", + "pt": "TEMPO", + "nl": "WEER", + "da": "VEJR", + "no": "VÆR", + "sv": "VÄDER" + } + }, + "FEELS": { + "context": "Precedes the 'feels like' temperature.", + "translations": { + "fr": "RESSENTI", + "de": "GEFUHLT", + "es": "SENSAC.", + "it": "PERCEP.", + "pt": "SENSAC.", + "nl": "GEVOELD", + "da": "FØLES", + "no": "FØLES", + "sv": "KÄNNS" + } + }, + "FLS": { + "context": "Very short 'feels like' label.", + "translations": { + "fr": "RES", + "de": "GEF", + "es": "SEN", + "it": "PER", + "pt": "SEN", + "nl": "GVL" + } + }, + "AIR QUALITY": { + "context": "Air-quality label/heading.", + "translations": { + "fr": "QUALITE AIR", + "de": "LUFTQUALITAT", + "es": "CALIDAD AIRE", + "it": "QUALITA ARIA", + "pt": "QUALID. AR", + "nl": "LUCHTKWAL.", + "da": "LUFTKVALITET", + "no": "LUFTKVALITET", + "sv": "LUFTKVALITET" + } + }, + "SUN EXPOSURE": { + "context": "UV / sun-exposure label/heading.", + "translations": { + "fr": "EXPO SOLEIL", + "de": "UV-BELASTUNG", + "es": "EXPO SOLAR", + "it": "ESPOS. SOLE", + "pt": "EXPO SOLAR", + "nl": "ZONKRACHT" + } + }, + "SUN UV": { + "context": "Short 'sun UV' label.", + "translations": { + "fr": "UV SOLEIL", + "de": "SONNE UV", + "es": "UV SOLAR", + "it": "UV SOLE", + "pt": "UV SOLAR", + "nl": "ZON UV" + } + }, + "POLLEN": { + "context": "Pollen label/heading.", + "translations": { + "fr": "POLLEN", + "de": "POLLEN", + "es": "POLEN", + "it": "POLLINE", + "pt": "POLEN", + "nl": "POLLEN", + "da": "POLLEN", + "no": "POLLEN", + "sv": "POLLEN" + } + }, + "OVERALL": { + "context": "The 'overall' level in a breakdown.", + "translations": { + "fr": "GLOBAL", + "de": "GESAMT", + "es": "TOTAL", + "it": "TOTALE", + "pt": "GERAL", + "nl": "TOTAAL", + "da": "SAMLET", + "no": "SAMLET", + "sv": "TOTALT" + } + }, + "OVR": { + "context": "Very short 'overall'.", + "translations": { + "fr": "GLB", + "de": "GES", + "es": "TOT", + "it": "TOT", + "pt": "GER", + "nl": "TOT" + } + }, + "GRASS": { + "context": "Grass (e.g. grass pollen).", + "translations": { + "fr": "HERBE", + "de": "GRAS", + "es": "HIERBA", + "it": "ERBA", + "pt": "RELVA", + "nl": "GRAS", + "da": "GRÆS", + "no": "GRESS", + "sv": "GRÄS" + } + }, + "GRS": { + "context": "Very short 'grass'.", + "translations": { + "fr": "HRB", + "de": "GRA", + "es": "HIE", + "it": "ERB", + "pt": "REL", + "nl": "GRS" + } + }, + "TREE": { + "context": "Tree (e.g. tree pollen).", + "translations": { + "fr": "ARBRE", + "de": "BAUM", + "es": "ARBOL", + "it": "ALBERO", + "pt": "ARVORE", + "nl": "BOOM", + "da": "TRÆ", + "no": "TRE", + "sv": "TRÄD" + } + }, + "TRE": { + "context": "Very short 'tree'.", + "translations": { + "fr": "ARB", + "de": "BAU", + "es": "ARB", + "it": "ALB", + "pt": "ARV", + "nl": "BOM" + } + }, + "WEED": { + "context": "Weed (e.g. weed pollen).", + "translations": { + "fr": "HERBACEE", + "de": "UNKRAUT", + "es": "MALEZA", + "it": "ERBACCE", + "pt": "ERVAS", + "nl": "ONKRUID", + "da": "UKRUDT", + "no": "UGRESS", + "sv": "OGRÄS" + } + }, + "WED": { + "context": "Very short 'weed'.", + "translations": { + "fr": "HER", + "de": "UNK", + "es": "MAL", + "it": "ERB", + "pt": "ERV", + "nl": "ONK" + } + }, + "PROV": { + "context": "'Source/provider' label (which data provider).", + "translations": { + "fr": "SRCE", + "de": "QUELLE", + "es": "FUENTE", + "it": "FONTE", + "pt": "FONTE", + "nl": "BRON" + } + }, + "PRV": { + "context": "Very short 'provider'.", + "translations": { + "fr": "SRC", + "de": "QLE", + "es": "FTE", + "it": "FNT", + "pt": "FNT", + "nl": "BRN" + } + }, + "GOOD": { + "context": "Qualitative level: good.", + "translations": { + "fr": "BON", + "de": "GUT", + "es": "BUENA", + "it": "BUONA", + "pt": "BOA", + "nl": "GOED", + "da": "GOD", + "no": "GOD", + "sv": "BRA" + } + }, + "FAIR": { + "context": "Qualitative level: fair (between good and moderate).", + "translations": { + "fr": "CORRECT", + "de": "MASSIG", + "es": "ACEPTABLE", + "it": "DISCRETA", + "pt": "RAZOAVEL", + "nl": "REDELIJK" + } + }, + "MODERATE": { + "context": "Qualitative level: moderate.", + "translations": { + "fr": "MODERE", + "de": "MASSIG", + "es": "MODERADA", + "it": "MODERATA", + "pt": "MODERADA", + "nl": "MATIG", + "da": "MODERAT", + "no": "MODERAT", + "sv": "MÅTTLIG" + } + }, + "POOR": { + "context": "Qualitative level: poor.", + "translations": { + "fr": "MAUVAIS", + "de": "SCHLECHT", + "es": "MALA", + "it": "SCARSA", + "pt": "FRACA", + "nl": "SLECHT", + "da": "DÅRLIG", + "no": "DÅRLIG", + "sv": "DÅLIG" + } + }, + "V.POOR": { + "context": "Qualitative level: very poor (abbreviated).", + "translations": { + "fr": "TRES MAUVAIS", + "de": "SEHR SCHL.", + "es": "MUY MALA", + "it": "PESSIMA", + "pt": "MUITO FRACA", + "nl": "ZEER SLECHT" + } + }, + "MOD": { + "context": "Qualitative level: moderate (abbreviated).", + "translations": { + "fr": "MOYEN", + "de": "MITTEL", + "es": "MODER.", + "it": "MEDIO", + "pt": "MODER.", + "nl": "MATIG" + } + }, + "USG": { + "context": "Air quality 'unhealthy for sensitive groups' (abbreviated).", + "translations": { + "fr": "SENSIBLES", + "de": "EMPFINDL.", + "es": "SENSIBLES", + "it": "SENSIBILI", + "pt": "SENSIVEIS", + "nl": "GEVOELIG" + } + }, + "UNHEALTHY": { + "context": "Qualitative level: unhealthy.", + "translations": { + "fr": "MALSAIN", + "de": "UNGESUND", + "es": "INSALUBRE", + "it": "MALSANO", + "pt": "INSALUBRE", + "nl": "ONGEZOND" + } + }, + "V.UNHLTHY": { + "context": "Qualitative level: very unhealthy (abbreviated).", + "translations": { + "fr": "TRES MALSAIN", + "de": "SEHR UNGESUND", + "es": "MUY INSALUBRE", + "it": "MOLTO MALSANO", + "pt": "MUITO INSALUB", + "nl": "ZEER ONGEZOND" + } + }, + "HAZARDOUS": { + "context": "Qualitative level: hazardous (worst).", + "translations": { + "fr": "DANGEREUX", + "de": "GEFAHRLICH", + "es": "PELIGROSA", + "it": "PERICOLOSA", + "pt": "PERIGOSA", + "nl": "GEVAARLIJK" + } + }, + "LOW": { + "context": "Qualitative level: low.", + "translations": { + "fr": "FAIBLE", + "de": "NIEDRIG", + "es": "BAJO", + "it": "BASSO", + "pt": "BAIXO", + "nl": "LAAG", + "da": "LAV", + "no": "LAV", + "sv": "LÅG" + } + }, + "HIGH": { + "context": "Qualitative level: high.", + "translations": { + "fr": "ELEVE", + "de": "HOCH", + "es": "ALTO", + "it": "ALTO", + "pt": "ALTO", + "nl": "HOOG", + "da": "HØJ", + "no": "HØY", + "sv": "HÖG" + } + }, + "V.HIGH": { + "context": "Qualitative level: very high (abbreviated).", + "translations": { + "fr": "TRES ELEVE", + "de": "SEHR HOCH", + "es": "MUY ALTO", + "it": "MOLTO ALTO", + "pt": "MUITO ALTO", + "nl": "ZEER HOOG" + } + }, + "EXTREME": { + "context": "Qualitative level: extreme.", + "translations": { + "fr": "EXTREME", + "de": "EXTREM", + "es": "EXTREMO", + "it": "ESTREMO", + "pt": "EXTREMO", + "nl": "EXTREEM", + "da": "EKSTREM", + "no": "EKSTREM", + "sv": "EXTREM" + } + }, + "NONE": { + "context": "Level 'none' / zero.", + "translations": { + "fr": "AUCUN", + "de": "KEIN", + "es": "NINGUNO", + "it": "NESSUNO", + "pt": "NENHUM", + "nl": "GEEN", + "da": "INGEN", + "no": "INGEN", + "sv": "INGEN" + } + } + }, + "metals": { + "GOLD": { + "context": "Precious metal: gold.", + "translations": { + "fr": "OR", + "de": "GOLD", + "es": "ORO", + "it": "ORO", + "pt": "OURO", + "nl": "GOUD", + "da": "GULD", + "no": "GULL", + "sv": "GULD" + } + }, + "SILVER": { + "context": "Precious metal: silver.", + "translations": { + "fr": "ARGENT", + "de": "SILBER", + "es": "PLATA", + "it": "ARGENTO", + "pt": "PRATA", + "nl": "ZILVER", + "da": "SØLV", + "no": "SØLV", + "sv": "SILVER" + } + }, + "PLATINUM": { + "context": "Precious metal: platinum.", + "translations": { + "fr": "PLATINE", + "de": "PLATIN", + "es": "PLATINO", + "it": "PLATINO", + "pt": "PLATINA", + "nl": "PLATINA", + "da": "PLATIN", + "no": "PLATINA", + "sv": "PLATINA" + } + }, + "PALLADIUM": { + "context": "Precious metal: palladium.", + "translations": { + "fr": "PALLADIUM", + "de": "PALLADIUM", + "es": "PALADIO", + "it": "PALLADIO", + "pt": "PALADIO", + "nl": "PALLADIUM", + "da": "PALLADIUM", + "no": "PALLADIUM", + "sv": "PALLADIUM" + } + }, + "SPOT PRICE": { + "context": "Label for a current spot (market) price.", + "translations": { + "fr": "COURS", + "de": "KURS", + "es": "PRECIO", + "it": "PREZZO", + "pt": "PRECO", + "nl": "KOERS", + "da": "SPOTPRIS", + "no": "SPOTPRIS", + "sv": "SPOTPRIS" + } + } + }, + "tides": { + "TIDES": { + "context": "Tides heading.", + "translations": { + "fr": "MAREES", + "de": "GEZEITEN", + "es": "MAREAS", + "it": "MAREE", + "pt": "MARES", + "nl": "GETIJDEN", + "da": "TIDEVAND", + "no": "TIDEVANN", + "sv": "TIDVATTEN" + } + }, + "HIGH TIDE": { + "context": "High tide.", + "translations": { + "fr": "MAREE HAUTE", + "de": "FLUT", + "es": "PLEAMAR", + "it": "ALTA MAREA", + "pt": "PREIA-MAR", + "nl": "VLOED", + "da": "HØJVANDE", + "no": "HØYVANN", + "sv": "HÖGVATTEN" + } + }, + "LOW TIDE": { + "context": "Low tide.", + "translations": { + "fr": "MAREE BASSE", + "de": "EBBE", + "es": "BAJAMAR", + "it": "BASSA MAREA", + "pt": "BAIXA-MAR", + "nl": "EB", + "da": "LAVVANDE", + "no": "LAVVANN", + "sv": "LÅGVATTEN" + } + }, + "CHECK STATION": { + "context": "Prompt to pick/verify a measurement station.", + "translations": { + "fr": "VERIF STATION", + "de": "STATION PRUEF", + "es": "VER ESTACION", + "it": "VERIF STAZIONE", + "pt": "VER ESTACAO", + "nl": "STATION CHECK" + } + }, + "HIGH": { + "context": "High tide (short label).", + "translations": { + "fr": "HAUTE", + "de": "HOCH", + "es": "ALTA", + "it": "ALTA", + "pt": "ALTA", + "nl": "HOOG", + "da": "HØJ", + "no": "HØY", + "sv": "HÖG" + } + }, + "LOW": { + "context": "Low tide (short label).", + "translations": { + "fr": "BASSE", + "de": "NIEDRIG", + "es": "BAJA", + "it": "BASSA", + "pt": "BAIXA", + "nl": "LAAG", + "da": "LAV", + "no": "LAV", + "sv": "LÅG" + } + } + }, + "aurora": { + "AURORA": { + "context": "Aurora (northern/southern lights) heading.", + "translations": { + "fr": "AURORE", + "de": "POLARLICHT", + "es": "AURORA", + "it": "AURORA", + "pt": "AURORA", + "nl": "NOORDERLICHT" + } + }, + "QUIET": { + "context": "Activity level: quiet/calm.", + "translations": { + "fr": "CALME", + "de": "RUHIG", + "es": "TRANQUILO", + "it": "QUIETE", + "pt": "CALMO", + "nl": "RUSTIG" + } + }, + "UNSETTLED": { + "context": "Activity level: unsettled.", + "translations": { + "fr": "AGITE", + "de": "UNRUHIG", + "es": "INESTABLE", + "it": "INSTABILE", + "pt": "INSTAVEL", + "nl": "ONRUSTIG" + } + }, + "MINOR STORM": { + "context": "Storm level: minor.", + "translations": { + "fr": "ORAGE MINEUR", + "de": "KL. STURM", + "es": "TORM. MENOR", + "it": "TEMP. MINORE", + "pt": "TORM. MENOR", + "nl": "KL. STORM" + } + }, + "STRONG STORM": { + "context": "Storm level: strong.", + "translations": { + "fr": "ORAGE FORT", + "de": "STARK. STURM", + "es": "TORM. FUERTE", + "it": "TEMP. FORTE", + "pt": "TORM. FORTE", + "nl": "ZWARE STORM" + } + }, + "SEVERE STORM": { + "context": "Storm level: severe.", + "translations": { + "fr": "ORAGE SEVERE", + "de": "SCHW. STURM", + "es": "TORM. SEVERA", + "it": "TEMP. SEVERO", + "pt": "TORM. SEVERA", + "nl": "HEVIGE STORM" + } + } + }, + "space": { + "NEXT LAUNCH": { + "context": "'Next launch' label.", + "translations": { + "fr": "PROCH. LANCEMENT", + "de": "NAECHST. START", + "es": "PROX. LANZAMIENTO", + "it": "PROSS. LANCIO", + "pt": "PROX. LANCAMENTO", + "nl": "VOLGENDE START" + } + }, + "MISSION": { + "context": "'Mission' label (e.g. a mission name).", + "translations": { + "fr": "MISSION", + "de": "MISSION", + "es": "MISION", + "it": "MISSIONE", + "pt": "MISSAO", + "nl": "MISSIE", + "da": "MISSION", + "no": "OPPDRAG", + "sv": "UPPDRAG" + } + }, + "IMMINENT": { + "context": "About to happen (very soon).", + "translations": { + "fr": "IMMINENT", + "de": "UNMITTELBAR", + "es": "INMINENTE", + "it": "IMMINENTE", + "pt": "IMINENTE", + "nl": "OP KOMST", + "da": "SNART", + "no": "SNART", + "sv": "SNART" + } + }, + "SCHEDULED": { + "context": "Planned for a set time.", + "translations": { + "fr": "PREVU", + "de": "GEPLANT", + "es": "PROGRAMADO", + "it": "PREVISTO", + "pt": "AGENDADO", + "nl": "GEPLAND", + "da": "PLANLAGT", + "no": "PLANLAGT", + "sv": "PLANERAD" + } + }, + "NONE": { + "context": "No launch scheduled.", + "translations": { + "fr": "AUCUN", + "de": "KEIN", + "es": "NINGUNO", + "it": "NESSUNO", + "pt": "NENHUM", + "nl": "GEEN", + "da": "INGEN", + "no": "INGEN", + "sv": "INGEN" + } + }, + "IN SPACE": { + "context": "Suffix after a count: 'N people in space'.", + "translations": { + "fr": "DANS L'ESPACE", + "de": "IM WELTALL", + "es": "EN EL ESPACIO", + "it": "NELLO SPAZIO", + "pt": "NO ESPAÇO", + "nl": "IN DE RUIMTE", + "da": "I RUMMET", + "no": "I ROMMET", + "sv": "I RYMDEN" + } + } + }, + "sports": { + "NEXT GP": { + "context": "'Next Grand Prix' (short).", + "translations": { + "fr": "PROCHAIN GP", + "de": "NAECHSTER GP", + "es": "PROXIMO GP", + "it": "PROSSIMO GP", + "pt": "PROXIMO GP", + "nl": "VOLGENDE GP" + } + }, + "NEXT GRAND PRIX": { + "context": "'Next Grand Prix'.", + "translations": { + "fr": "PROCHAIN GP", + "de": "NAECHSTER GP", + "es": "PROXIMO GP", + "it": "PROSSIMO GP", + "pt": "PROXIMO GP", + "nl": "VOLGENDE GP" + } + }, + "RACE WEEKEND": { + "context": "'Race weekend' label.", + "translations": { + "fr": "WEEKEND COURSE", + "de": "RENN-WOCHENENDE", + "es": "FIN DE SEMANA", + "it": "WEEKEND GARA", + "pt": "FIM DE SEMANA", + "nl": "RACEWEEKEND" + } + }, + "SEASON": { + "context": "'Season' label (e.g. a sports season).", + "translations": { + "fr": "SAISON", + "de": "SAISON", + "es": "TEMPORADA", + "it": "STAGIONE", + "pt": "TEMPORADA", + "nl": "SEIZOEN", + "da": "SÆSON", + "no": "SESONG", + "sv": "SÄSONG" + } + }, + "OVER": { + "context": "Something has ended / is over.", + "translations": { + "fr": "TERMINEE", + "de": "VORBEI", + "es": "TERMINADA", + "it": "FINITA", + "pt": "TERMINADA", + "nl": "VOORBIJ", + "da": "SLUT", + "no": "OVER", + "sv": "SLUT" + } + }, + "CHAMPIONSHIP": { + "context": "'Championship' label.", + "translations": { + "fr": "CHAMPIONNAT", + "de": "MEISTERSCHAFT", + "es": "CAMPEONATO", + "it": "CAMPIONATO", + "pt": "CAMPEONATO", + "nl": "KAMPIOENSCHAP" + } + }, + "LEADER": { + "context": "The leader / person in first place.", + "translations": { + "fr": "LEADER", + "de": "FUEHRENDER", + "es": "LIDER", + "it": "LEADER", + "pt": "LIDER", + "nl": "LEIDER", + "da": "FØRER", + "no": "LEDER", + "sv": "LEDARE" + } + }, + "POINTS": { + "context": "'Points' (a score).", + "translations": { + "fr": "POINTS", + "de": "PUNKTE", + "es": "PUNTOS", + "it": "PUNTI", + "pt": "PONTOS", + "nl": "PUNTEN", + "da": "POINT", + "no": "POENG", + "sv": "POÄNG" + } + }, + "PTS": { + "context": "Abbreviation of 'points'.", + "translations": { + "fr": "PTS", + "de": "PKT", + "es": "PTS", + "it": "PTI", + "pt": "PTS", + "nl": "PNT" + } + }, + "FINAL": { + "context": "A finished game (final score).", + "translations": { + "fr": "TERMINÉ", + "de": "BEENDET", + "es": "FINAL", + "it": "FINALE", + "pt": "FINAL", + "nl": "AFGELOPEN", + "da": "SLUT", + "no": "SLUTT", + "sv": "SLUT" + } + }, + "LIVE": { + "context": "A game in progress.", + "translations": { + "fr": "EN DIRECT", + "de": "LIVE", + "es": "EN VIVO", + "it": "IN DIRETTA", + "pt": "AO VIVO", + "nl": "LIVE", + "da": "LIVE", + "no": "DIREKTE", + "sv": "DIREKT" + } + }, + "UPCOMING": { + "context": "A game not yet started.", + "translations": { + "fr": "À VENIR", + "de": "DEMNÄCHST", + "es": "PRÓXIMO", + "it": "IN ARRIVO", + "pt": "EM BREVE", + "nl": "BINNENKORT", + "da": "KOMMENDE", + "no": "KOMMENDE", + "sv": "KOMMANDE" + } + }, + "SCHEDULED": { + "context": "A game/event scheduled for later.", + "translations": { + "fr": "PRÉVU", + "de": "GEPLANT", + "es": "PROGRAMADO", + "it": "PREVISTO", + "pt": "AGENDADO", + "nl": "GEPLAND", + "da": "PLANLAGT", + "no": "PLANLAGT", + "sv": "PLANERAD" + } + }, + "NO GAMES": { + "context": "No games found.", + "translations": { + "fr": "AUCUN MATCH", + "de": "KEINE SPIELE", + "es": "SIN PARTIDOS", + "it": "NESSUNA PARTITA", + "pt": "SEM JOGOS", + "nl": "GEEN DUELS", + "da": "INGEN KAMPE", + "no": "INGEN KAMPER", + "sv": "INGA MATCHER" + } + }, + "NO TEAMS": { + "context": "No teams configured.", + "translations": { + "fr": "AUCUNE ÉQUIPE", + "de": "KEINE TEAMS", + "es": "SIN EQUIPOS", + "it": "NESSUNA SQUADRA", + "pt": "SEM EQUIPES", + "nl": "GEEN TEAMS", + "da": "INGEN HOLD", + "no": "INGEN LAG", + "sv": "INGA LAG" + } + }, + "NO EVENT": { + "context": "No event this week.", + "translations": { + "fr": "AUCUN EVENT", + "de": "KEIN EVENT", + "es": "SIN EVENTO", + "it": "NESSUN EVENTO", + "pt": "SEM EVENTO", + "nl": "GEEN EVENT", + "da": "INGEN EVENT", + "no": "INGEN EVENT", + "sv": "INGET EVENT" + } + }, + "NO LEADERS": { + "context": "No leaderboard available.", + "translations": { + "fr": "AUCUN LEADER", + "de": "KEINE FÜHRENDEN", + "es": "SIN LÍDERES", + "it": "NESSUN LEADER", + "pt": "SEM LÍDERES", + "nl": "GEEN LEIDERS", + "da": "INGEN LEDERE", + "no": "INGEN LEDERE", + "sv": "INGA LEDARE" + } + }, + "NO FIGHTS": { + "context": "No fights scheduled.", + "translations": { + "fr": "AUCUN COMBAT", + "de": "KEINE KÄMPFE", + "es": "SIN COMBATES", + "it": "NESSUN INCONTRO", + "pt": "SEM LUTAS", + "nl": "GEEN GEVECHTEN", + "da": "INGEN KAMPE", + "no": "INGEN KAMPER", + "sv": "INGA MATCHER" + } + }, + "CONFIGURED": { + "context": "'... configured' (paired with NO TEAMS).", + "translations": { + "fr": "CONFIGURÉE", + "de": "EINGERICHTET", + "es": "CONFIGURADO", + "it": "CONFIGURATO", + "pt": "CONFIGURADO", + "nl": "INGESTELD", + "da": "OPSAT", + "no": "SATT OPP", + "sv": "KONFIG." + } + }, + "FOUND": { + "context": "'... found' (paired with NO GAMES).", + "translations": { + "fr": "TROUVÉ", + "de": "GEFUNDEN", + "es": "ENCONTRADO", + "it": "TROVATO", + "pt": "ENCONTRADO", + "nl": "GEVONDEN", + "da": "FUNDET", + "no": "FUNNET", + "sv": "HITTAT" + } + }, + "THIS WEEK": { + "context": "'this week' timeframe.", + "translations": { + "fr": "CETTE SEMAINE", + "de": "DIESE WOCHE", + "es": "ESTA SEMANA", + "it": "QUESTA SETT.", + "pt": "ESTA SEMANA", + "nl": "DEZE WEEK", + "da": "DENNE UGE", + "no": "DENNE UKEN", + "sv": "DENNA VECKA" + } + }, + "ALL": { + "context": "Filter label: all games.", + "translations": { + "fr": "TOUS", + "de": "ALLE", + "es": "TODOS", + "it": "TUTTI", + "pt": "TODOS", + "nl": "ALLE", + "da": "ALLE", + "no": "ALLE", + "sv": "ALLA" + } + }, + "NOTHING": { + "context": "'nothing followed' empty state.", + "translations": { + "fr": "RIEN", + "de": "NICHTS", + "es": "NADA", + "it": "NIENTE", + "pt": "NADA", + "nl": "NIETS", + "da": "INTET", + "no": "INGENTING", + "sv": "INGET" + } + }, + "FOLLOWED": { + "context": "'... followed' (paired with NOTHING).", + "translations": { + "fr": "SUIVI", + "de": "GEFOLGT", + "es": "SEGUIDO", + "it": "SEGUITO", + "pt": "SEGUIDO", + "nl": "GEVOLGD", + "da": "FULGT", + "no": "FULGT", + "sv": "FÖLJT" + } + } + }, + "sentiment": { + "FEAR": { + "context": "Sentiment/index classification: fear.", + "translations": { + "fr": "PEUR", + "de": "ANGST", + "es": "MIEDO", + "it": "PAURA", + "pt": "MEDO", + "nl": "ANGST", + "da": "FRYGT", + "no": "FRYKT", + "sv": "RÄDSLA" + } + }, + "GREED": { + "context": "Sentiment/index classification: greed.", + "translations": { + "fr": "AVIDITE", + "de": "GIER", + "es": "CODICIA", + "it": "AVIDITA", + "pt": "GANANCIA", + "nl": "HEBZUCHT", + "da": "GRÅDIGHED", + "no": "GRÅDIGHET", + "sv": "GIRIGHET" + } + }, + "NEUTRAL": { + "context": "Sentiment/index classification: neutral.", + "translations": { + "fr": "NEUTRE", + "de": "NEUTRAL", + "es": "NEUTRAL", + "it": "NEUTRO", + "pt": "NEUTRO", + "nl": "NEUTRAAL", + "da": "NEUTRAL", + "no": "NØYTRAL", + "sv": "NEUTRAL" + } + }, + "EXTREME FEAR": { + "context": "Sentiment/index classification: extreme fear.", + "translations": { + "fr": "PEUR EXTREME", + "de": "EXTR. ANGST", + "es": "MIEDO EXTREMO", + "it": "PAURA ESTREMA", + "pt": "MEDO EXTREMO", + "nl": "EXTR. ANGST" + } + }, + "EXTREME GREED": { + "context": "Sentiment/index classification: extreme greed.", + "translations": { + "fr": "AVIDITE EXTREME", + "de": "EXTREME GIER", + "es": "CODICIA EXTREMA", + "it": "AVIDITA ESTREMA", + "pt": "GANANCIA EXTREMA", + "nl": "EXTREME HEBZUCHT" + } + } + }, + "crypto": { + "NO COINS": { + "context": "No cryptocurrencies configured.", + "translations": { + "fr": "AUCUNE CRYPTO", + "de": "KEINE COINS", + "es": "SIN MONEDAS", + "it": "NESSUNA CRIPTO", + "pt": "SEM MOEDAS", + "nl": "GEEN MUNTEN", + "da": "INGEN MØNTER", + "no": "INGEN MYNTER", + "sv": "INGA MYNT" + } + } + }, + "stocks": { + "NO TICKERS": { + "context": "No stock tickers configured.", + "translations": { + "fr": "AUCUN TITRE", + "de": "KEINE AKTIEN", + "es": "SIN VALORES", + "it": "NESSUN TITOLO", + "pt": "SEM AÇÕES", + "nl": "GEEN AANDELEN", + "da": "INGEN AKTIER", + "no": "INGEN AKSJER", + "sv": "INGA AKTIER" + } + } + }, + "holidays": { + "NEXT HOLIDAY": { + "context": "'Next holiday' label.", + "translations": { + "fr": "PROCHAIN CONGE", + "de": "NAECHSTER FEIERTAG", + "es": "PROX. FESTIVO", + "it": "PROSS. FESTA", + "pt": "PROX. FERIADO", + "nl": "VOLGENDE FEESTDAG", + "da": "NÆSTE HELLIGDAG", + "no": "NESTE HELLIGDAG", + "sv": "NÄSTA HELGDAG" + } + } + }, + "content": { + "FEATURED": { + "context": "'Featured' item label (e.g. a featured article).", + "translations": { + "fr": "A LA UNE", + "de": "ARTIKEL", + "es": "DESTACADO", + "it": "IN VETRINA", + "pt": "DESTAQUE", + "nl": "UITGELICHT", + "da": "FREMHÆVET", + "no": "FREMHEVET", + "sv": "UTVALD" + } + }, + "MOST READ": { + "context": "'Most read' item label.", + "translations": { + "fr": "TOP LUS", + "de": "MEISTGELESEN", + "es": "MAS LEIDO", + "it": "PIU LETTI", + "pt": "MAIS LIDO", + "nl": "MEEST GELEZEN", + "da": "MEST LÆST", + "no": "MEST LEST", + "sv": "MEST LÄST" + } + } + }, + "vocab": { + "WORD OF THE DAY": { + "context": "'Word of the day' heading.", + "translations": { + "fr": "MOT DU JOUR", + "de": "WORT DES TAGES", + "es": "PALABRA DEL DIA", + "it": "PAROLA DEL GIORNO", + "pt": "PALAVRA DO DIA", + "nl": "WOORD VAN DE DAG", + "da": "DAGENS ORD", + "no": "DAGENS ORD", + "sv": "DAGENS ORD" + } + } + } + }, + "holidays": { + "new year's day": { + "context": "Public-holiday name; shown when the source only provides English ('new year's day').", + "translations": { + "fr": "Jour de l'An", + "de": "Neujahr", + "es": "Año Nuevo", + "it": "Capodanno", + "pt": "Ano Novo", + "nl": "Nieuwjaar", + "da": "Nytårsdag", + "no": "Første nyttårsdag", + "sv": "Nyårsdagen" + } + }, + "epiphany": { + "context": "Public-holiday name; shown when the source only provides English ('epiphany').", + "translations": { + "fr": "Épiphanie", + "de": "Heilige Drei Könige", + "es": "Epifanía", + "it": "Epifania", + "pt": "Dia de Reis", + "nl": "Driekoningen", + "da": "Helligtrekonger", + "no": "Helligtrekongersdag", + "sv": "Trettondedag jul" + } + }, + "good friday": { + "context": "Public-holiday name; shown when the source only provides English ('good friday').", + "translations": { + "fr": "Vendredi Saint", + "de": "Karfreitag", + "es": "Viernes Santo", + "it": "Venerdì Santo", + "pt": "Sexta-feira Santa", + "nl": "Goede Vrijdag", + "da": "Langfredag", + "no": "Langfredag", + "sv": "Långfredagen" + } + }, + "easter sunday": { + "context": "Public-holiday name; shown when the source only provides English ('easter sunday').", + "translations": { + "fr": "Pâques", + "de": "Ostersonntag", + "es": "Domingo de Pascua", + "it": "Pasqua", + "pt": "Páscoa", + "nl": "Eerste Paasdag", + "da": "Påskedag", + "no": "Første påskedag", + "sv": "Påskdagen" + } + }, + "easter monday": { + "context": "Public-holiday name; shown when the source only provides English ('easter monday').", + "translations": { + "fr": "Lundi de Pâques", + "de": "Ostermontag", + "es": "Lunes de Pascua", + "it": "Lunedì dell'Angelo", + "pt": "Segunda de Páscoa", + "nl": "Tweede Paasdag", + "da": "2. påskedag", + "no": "Andre påskedag", + "sv": "Annandag påsk" + } + }, + "labour day": { + "context": "Public-holiday name; shown when the source only provides English ('labour day').", + "translations": { + "fr": "Fête du Travail", + "de": "Tag der Arbeit", + "es": "Día del Trabajo", + "it": "Festa del Lavoro", + "pt": "Dia do Trabalhador", + "nl": "Dag van de Arbeid", + "da": "1. maj", + "no": "Arbeidernes dag", + "sv": "Första maj" + } + }, + "labor day": { + "context": "Public-holiday name; shown when the source only provides English ('labor day').", + "translations": { + "fr": "Fête du Travail", + "de": "Tag der Arbeit", + "es": "Día del Trabajo", + "it": "Festa del Lavoro", + "pt": "Dia do Trabalhador", + "nl": "Dag van de Arbeid", + "da": "1. maj", + "no": "Arbeidernes dag", + "sv": "Första maj" + } + }, + "ascension day": { + "context": "Public-holiday name; shown when the source only provides English ('ascension day').", + "translations": { + "fr": "Ascension", + "de": "Christi Himmelfahrt", + "es": "Ascensión", + "it": "Ascensione", + "pt": "Ascensão", + "nl": "Hemelvaart", + "da": "Kristi himmelfartsdag", + "no": "Kristi himmelfartsdag", + "sv": "Kristi himmelsfärdsdag" + } + }, + "whit sunday": { + "context": "Public-holiday name; shown when the source only provides English ('whit sunday').", + "translations": { + "fr": "Pentecôte", + "de": "Pfingstsonntag", + "es": "Pentecostés", + "it": "Pentecoste", + "pt": "Pentecostes", + "nl": "Eerste Pinksterdag", + "da": "Pinsedag", + "no": "Første pinsedag", + "sv": "Pingstdagen" + } + }, + "whit monday": { + "context": "Public-holiday name; shown when the source only provides English ('whit monday').", + "translations": { + "fr": "Lundi de Pentecôte", + "de": "Pfingstmontag", + "es": "Lunes de Pentecostés", + "it": "Lunedì di Pentecoste", + "pt": "Segunda de Pentecostes", + "nl": "Tweede Pinksterdag", + "da": "2. pinsedag", + "no": "Andre pinsedag", + "sv": "Annandag pingst" + } + }, + "corpus christi": { + "context": "Public-holiday name; shown when the source only provides English ('corpus christi').", + "translations": { + "fr": "Fête-Dieu", + "de": "Fronleichnam", + "es": "Corpus Christi", + "it": "Corpus Domini", + "pt": "Corpo de Deus", + "nl": "Sacramentsdag" + } + }, + "assumption day": { + "context": "Public-holiday name; shown when the source only provides English ('assumption day').", + "translations": { + "fr": "Assomption", + "de": "Mariä Himmelfahrt", + "es": "Asunción", + "it": "Assunzione", + "pt": "Assunção", + "nl": "Maria-Tenhemelopneming" + } + }, + "all saints' day": { + "context": "Public-holiday name; shown when the source only provides English ('all saints' day').", + "translations": { + "fr": "Toussaint", + "de": "Allerheiligen", + "es": "Todos los Santos", + "it": "Ognissanti", + "pt": "Todos os Santos", + "nl": "Allerheiligen" + } + }, + "christmas day": { + "context": "Public-holiday name; shown when the source only provides English ('christmas day').", + "translations": { + "fr": "Noël", + "de": "Weihnachten", + "es": "Navidad", + "it": "Natale", + "pt": "Natal", + "nl": "Kerstmis", + "da": "Juledag", + "no": "Første juledag", + "sv": "Juldagen" + } + }, + "st. stephen's day": { + "context": "Public-holiday name; shown when the source only provides English ('st. stephen's day').", + "translations": { + "fr": "Saint-Étienne", + "de": "Stefanitag", + "es": "San Esteban", + "it": "Santo Stefano", + "pt": "Dia de Santo Estêvão", + "nl": "Tweede Kerstdag", + "da": "2. juledag", + "no": "Andre juledag", + "sv": "Annandag jul" + } + }, + "boxing day": { + "context": "Public-holiday name; shown when the source only provides English ('boxing day').", + "translations": { + "fr": "Lendemain de Noël", + "de": "Zweiter Weihnachtsfeiertag", + "es": "San Esteban", + "it": "Santo Stefano", + "pt": "Boxing Day", + "nl": "Tweede Kerstdag", + "da": "2. juledag", + "no": "Andre juledag", + "sv": "Annandag jul" + } + }, + "canada day": { + "context": "Public-holiday name; shown when the source only provides English ('canada day').", + "translations": { + "fr": "Fête du Canada" + } + }, + "victoria day": { + "context": "Public-holiday name; shown when the source only provides English ('victoria day').", + "translations": { + "fr": "Journée des Patriotes" + } + }, + "thanksgiving": { + "context": "Public-holiday name; shown when the source only provides English ('thanksgiving').", + "translations": { + "fr": "Action de grâce" + } + }, + "remembrance day": { + "context": "Public-holiday name; shown when the source only provides English ('remembrance day').", + "translations": { + "fr": "Jour du Souvenir" + } + }, + "family day": { + "context": "Public-holiday name; shown when the source only provides English ('family day').", + "translations": { + "fr": "Jour de la famille" + } + }, + "civic holiday": { + "context": "Public-holiday name; shown when the source only provides English ('civic holiday').", + "translations": { + "fr": "Congé civique" + } + }, + "national holiday": { + "context": "Public-holiday name; shown when the source only provides English ('national holiday').", + "translations": { + "fr": "Fête nationale" + } + }, + "national day for truth and reconciliation": { + "context": "Public-holiday name; shown when the source only provides English ('national day for truth and reconciliation').", + "translations": { + "fr": "Journée de la vérité et réconciliation" + } + }, + "saint-jean-baptiste day": { + "context": "Public-holiday name; shown when the source only provides English ('saint-jean-baptiste day').", + "translations": { + "fr": "Fête nationale du Québec" + } + } + }, + "base_currency": { + "en": "USD", + "en-us": "USD", + "en-gb": "GBP", + "en-au": "AUD", + "fr": "EUR", + "de": "EUR", + "es": "EUR", + "it": "EUR", + "pt": "EUR", + "nl": "EUR", + "da": "DKK", + "no": "NOK", + "sv": "SEK", + "fi": "EUR", + "is": "ISK", + "ga": "EUR", + "ca": "EUR", + "gl": "EUR", + "eu": "EUR", + "et": "EUR", + "af": "ZAR", + "id": "IDR", + "ms": "MYR", + "sw": "KES", + "en-ca": "CAD", + "fr-ca": "CAD", + "fr-be": "EUR", + "fr-ch": "CHF", + "de-at": "EUR", + "de-ch": "CHF", + "es-mx": "MXN", + "es-ar": "ARS", + "it-ch": "CHF", + "pt-br": "BRL", + "nl-be": "EUR" + }, + "country": { + "en": "US", + "en-us": "US", + "en-gb": "GB", + "en-au": "AU", + "fr": "FR", + "de": "DE", + "es": "ES", + "it": "IT", + "pt": "PT", + "nl": "NL", + "da": "DK", + "no": "NO", + "sv": "SE", + "fi": "FI", + "is": "IS", + "ga": "IE", + "ca": "ES", + "gl": "ES", + "eu": "ES", + "et": "EE", + "af": "ZA", + "id": "ID", + "ms": "MY", + "sw": "KE", + "en-ca": "CA", + "fr-ca": "CA", + "fr-be": "BE", + "fr-ch": "CH", + "de-at": "AT", + "de-ch": "CH", + "es-mx": "MX", + "es-ar": "AR", + "it-ch": "CH", + "pt-br": "BR", + "nl-be": "BE" + } +} diff --git a/server/location.py b/server/location.py new file mode 100644 index 0000000..c42599d --- /dev/null +++ b/server/location.py @@ -0,0 +1,111 @@ +"""Resolve the configured global location to coordinates, country and currency +(keyless: Nominatim). + +This is the single place the app geocodes the configured location. Both the weather +helper and the ``get_location`` helper need the *same* location, so both go through +``coordinates()`` here — cached, one Nominatim lookup instead of one per caller. It +lets currency/holiday apps key off *where you are* rather than your language (the +language can't tell France (EUR) from Canada (CAD) or Switzerland (CHF)). +""" + +import re + +# Catalog globals this helper draws on (named in the app dialog's "also uses" hint). +GLOBAL_KEYS = ["location_precise", "zip_code"] + +_UA = {"User-Agent": "splitflap-os/1.0"} +_geo_cache: dict = {} # rounded (lat, lon) -> {"country", "subdivision"} +_coord_cache: dict = {} # geocode query string -> (lat, lon, CITY) + + +def _currency_for(country): + """ISO 4217 currency for an ISO 3166 country, from babel's CLDR data (a project + dependency). Returns None if unknown — callers then fall back to the language's + default currency (i18n.base_currency).""" + if not country: + return None + try: + from babel.numbers import get_territory_currencies + cur = get_territory_currencies(country.upper()) # current tender per CLDR + return cur[0] if cur else None + except Exception: + return None + + +def coordinates(settings): + """``(lat, lon, CITY)`` for the configured location: the precise coordinates if + set, else a geocode of the ZIP/postcode/city. Returns ``None`` when nothing is + configured. The forward geocode is cached, so weather and get_location share it.""" + lat = str(settings.get("location_lat", "") or "").strip() + lon = str(settings.get("location_lon", "") or "").strip() + name = str(settings.get("location_name", "") or "").strip() + if lat and lon: + try: + city = name.split(",")[0].strip().upper() if name else "LOCATION" + return float(lat), float(lon), city + except ValueError: + pass + query = str(settings.get("zip_code", "") or "").strip() + if not query: + return None + if query in _coord_cache: + return _coord_cache[query] + try: + import requests + params = {"q": query, "format": "json", "limit": 1, "addressdetails": 1} + if re.fullmatch(r"\d{5}", query): # a US ZIP — 02118 also exists abroad + params["countrycodes"] = "us" + geo = requests.get("https://nominatim.openstreetmap.org/search", + params=params, headers=_UA, timeout=6).json() + if geo: + addr = geo[0].get("address", {}) + city = (addr.get("city") or addr.get("town") or addr.get("village") + or addr.get("municipality") or addr.get("county") + or geo[0].get("display_name", query).split(",")[0]).strip().upper() + result = (float(geo[0]["lat"]), float(geo[0]["lon"]), city) + _coord_cache[query] = result + return result + except Exception: + pass + return None + + +def _geo(settings): + """Reverse-geocode the configured location to ``{country, subdivision}``, cached. + subdivision is the ISO 3166-2 code (e.g. 'CA-QC' for Quebec) or None.""" + coords = coordinates(settings) + if not coords: + return {"country": None, "subdivision": None} + lat, lon, _city = coords + key = (round(lat, 2), round(lon, 2)) + if key in _geo_cache: + return _geo_cache[key] + out = {"country": None, "subdivision": None} + try: + import requests + r = requests.get("https://nominatim.openstreetmap.org/reverse", + params={"lat": lat, "lon": lon, "format": "json", "zoom": 5}, + headers=_UA, timeout=6).json() + addr = r.get("address") or {} + out["country"] = str(addr.get("country_code") or "").upper()[:2] or None + sub = str(addr.get("ISO3166-2-lvl4") or addr.get("ISO3166-2-lvl6") or "").upper() + out["subdivision"] = sub or None + if out["country"]: + _geo_cache[key] = out + except Exception: + pass + return out + + +def country(settings): + """ISO country code for the configured location (reverse-geocoded, cached).""" + return _geo(settings).get("country") + + +def resolve(settings) -> dict: + """{ok, country, subdivision, currency} for the configured location. ok is False + (values None) when there's no location set or the lookup failed.""" + g = _geo(settings) + cc = g.get("country") + return {"ok": bool(cc), "country": cc, "subdivision": g.get("subdivision"), + "currency": _currency_for(cc)} diff --git a/server/requirements.txt b/server/requirements.txt index 7904288..165f9de 100644 --- a/server/requirements.txt +++ b/server/requirements.txt @@ -4,3 +4,4 @@ requests pytz yfinance paho-mqtt +babel diff --git a/server/static/app.js b/server/static/app.js index d4d64e8..08d27a3 100644 --- a/server/static/app.js +++ b/server/static/app.js @@ -1052,6 +1052,7 @@ function buildAppCard(a, isPlugin) { ${hasCfg && compatible ? `` : ''} ${removable ? `` : ''} ${hasTrigger && compatible ? `` : ''} + ${a.i18n ? `🌐` : ''} ${icon} ${a.name} ${compatible ? a.desc : incompatibleReason}`; @@ -2072,6 +2073,11 @@ function loadSettingsData(){ // Currency symbol const currencyEl = document.getElementById('currencySymbol'); if(currencyEl) currencyEl.value = data.currency_symbol || '$'; + // Internationalization master toggle + display language + const i18nEl = document.getElementById('i18nEnabled'); + if(i18nEl) i18nEl.checked = !!data.i18n_enabled; + const langEl = document.getElementById('globalLanguage'); + if(langEl){ langEl.value = data.language || 'en-US'; langEl.disabled = !(i18nEl && i18nEl.checked); } // Character map const charMapEl = document.getElementById('charMapInput'); if(charMapEl) charMapEl.value = data.char_map || CHAR_MAP; @@ -2856,6 +2862,13 @@ document.addEventListener('DOMContentLoaded', ()=>{ }); }); +function onI18nToggle(){ + const on = document.getElementById('i18nEnabled').checked; + const langEl = document.getElementById('globalLanguage'); + if(langEl) langEl.disabled = !on; + setSettingsDirty(true); +} + function saveGlobal(){ const rows = parseInt(document.getElementById('simRows').value) || 3; const cols = parseInt(document.getElementById('simCols').value) || 15; @@ -2875,6 +2888,8 @@ function saveGlobal(){ sim_rows:rows, sim_cols:cols, global_loop_delay: globalDelay, timezone: tz, + i18n_enabled: document.getElementById('i18nEnabled')?.checked || false, + language: document.getElementById('globalLanguage')?.value || 'en-US', location_lat: locLat, location_lon: locLon, location_name: locName, diff --git a/server/templates/index.html b/server/templates/index.html index cd40ceb..06ca3ce 100644 --- a/server/templates/index.html +++ b/server/templates/index.html @@ -328,6 +328,20 @@

Settings

+
+
+ + Enable internationalization (non-English languages) +
+

⚠ Only turn this on if your modules run the Universal Firmware — a version with configurable character maps — and you've loaded a character map that includes the accented characters (é, ñ, ö, å, …) these languages use. Standard A–Z / 0–9 modules can't display those, so translated text would appear as blank or wrong flaps. Leave it off to keep everything in English; the display and app list stay exactly as they are today.

+
+
+ + +

Display language for apps that support it (look for the 🌐 badge): translates their words and sets date order, number format and 12h/24h. Any app can override this in its own settings. Only Western-European (Windows-1252) languages are listed — the modules can't show others.

+

Transition Style

diff --git a/server/weather.py b/server/weather.py new file mode 100644 index 0000000..d5d6fc1 --- /dev/null +++ b/server/weather.py @@ -0,0 +1,208 @@ +""" +weather.py — a shared current-conditions helper. + +Several apps want "the weather right now". Rather than each hardcoding a provider +and key, this resolves the *global* weather settings (provider + API key + +location) and returns one normalized current-conditions dict. The plugin runtime +injects it into any app whose ``fetch()`` opts in with a ``get_weather`` +parameter (see app.py); the app calls ``get_weather()`` and renders the +result, so switching providers is a global setting, not an app edit. + +Providers: Open-Meteo (keyless — the default, so weather works with no API key), +OpenWeather, WeatherAPI and QWeather (each keyed via the global weather_api_key). +Temperatures are normalized to Fahrenheit; the caller formats/converts. + +Uses the blocking ``requests`` library (already a project dependency); a small +client shim gives every request a default timeout. +""" + +from __future__ import annotations + +import logging +import time + +import requests + +import location + +log = logging.getLogger("splitflap.weather") + +# Short-TTL cache so the provider is hit at most once per location per window, +# regardless of how many apps call get_weather() or how often they refresh. +_CACHE_TTL = 600 # seconds +_cache: dict = {} # (provider, key, lat, lon) -> (fetched_at, data) + +# Global settings the helper consumes — so the UI can credit weather-using apps +# under these settings even though they read them via get_weather, not directly. +GLOBAL_KEYS = ("weather_provider", "weather_api_key", "zip_code", "location_precise") + +# Compact Open-Meteo WMO weather-code → text map (current conditions only). +_OPENMETEO_CODES = { + 0: "CLEAR", 1: "MAINLY CLEAR", 2: "PARTLY CLOUDY", 3: "OVERCAST", + 45: "FOG", 48: "RIME FOG", 51: "LIGHT DRIZZLE", 53: "DRIZZLE", 55: "HEAVY DRIZZLE", + 56: "FREEZING DRIZZLE", 57: "FREEZING DRIZZLE", 61: "LIGHT RAIN", 63: "RAIN", + 65: "HEAVY RAIN", 66: "FREEZING RAIN", 67: "FREEZING RAIN", 71: "LIGHT SNOW", + 73: "SNOW", 75: "HEAVY SNOW", 77: "SNOW GRAINS", 80: "RAIN SHOWERS", + 81: "RAIN SHOWERS", 82: "HEAVY SHOWERS", 85: "SNOW SHOWERS", 86: "HEAVY SNOW SHOWERS", + 95: "THUNDERSTORM", 96: "THUNDER HAIL", 99: "SEVERE TSTORM", +} + + +class _Client: + """Minimal requests-backed stand-in for httpx.Client: a session that applies a + default timeout to every ``get()`` so the provider helpers stay unchanged.""" + + def __init__(self, timeout=8.0): + self._session = requests.Session() + self._timeout = timeout + + def get(self, url, params=None, headers=None): + return self._session.get(url, params=params, headers=headers, timeout=self._timeout) + + def close(self): + self._session.close() + + def __enter__(self): + return self + + def __exit__(self, *exc): + self.close() + + +def _i(v): + try: + return int(round(float(v))) + except (TypeError, ValueError): + return None + + +# Boston, so weather always has something to show if no location is configured. +_FALLBACK_LOCATION = (42.3496, -71.0783, "BOSTON") + + +def _resolve_location(settings): + """(lat, lon, city) for the configured location via the shared geocoder in + location.py (cached, one Nominatim lookup for weather + get_location), with a + Boston fallback so weather is never blank.""" + return location.coordinates(settings) or _FALLBACK_LOCATION + + +def _openmeteo(client, lat, lon, city, _key): + d = client.get("https://api.open-meteo.com/v1/forecast", params={ + "latitude": lat, "longitude": lon, + "current": "temperature_2m,apparent_temperature,weather_code," + "relative_humidity_2m,wind_speed_10m,cloud_cover", + "daily": "temperature_2m_max,temperature_2m_min", + "temperature_unit": "fahrenheit", "wind_speed_unit": "mph", + "timezone": "auto", "forecast_days": 1, + }).json() + cur = d.get("current", {}) + daily = d.get("daily", {}) + hi = (daily.get("temperature_2m_max") or [cur.get("temperature_2m")])[0] + lo = (daily.get("temperature_2m_min") or [cur.get("temperature_2m")])[0] + code = cur.get("weather_code") + return { + "city": city, "temp_f": _i(cur.get("temperature_2m")), + "feels_like_f": _i(cur.get("apparent_temperature")), + "hi_f": _i(hi), "lo_f": _i(lo), + "desc": _OPENMETEO_CODES.get(code, "CURRENT CONDITIONS"), "code": code, + "humidity": _i(cur.get("relative_humidity_2m")), + "wind_mph": cur.get("wind_speed_10m"), "cloud_cover": _i(cur.get("cloud_cover")), + } + + +def _openweather(client, lat, lon, city, key): + d = client.get("https://api.openweathermap.org/data/2.5/weather", params={ + "lat": lat, "lon": lon, "appid": key, "units": "imperial", + }).json() + main = d.get("main", {}) + return { + "city": str(d.get("name", city)).upper(), "temp_f": _i(main.get("temp")), + "feels_like_f": _i(main.get("feels_like")), + "hi_f": _i(main.get("temp_max")), "lo_f": _i(main.get("temp_min")), + "desc": str((d.get("weather") or [{}])[0].get("main", "CURRENT CONDITIONS")).upper(), + "code": None, "humidity": _i(main.get("humidity")), + "wind_mph": (d.get("wind") or {}).get("speed"), + "cloud_cover": _i((d.get("clouds") or {}).get("all")), + } + + +def _weatherapi(client, lat, lon, city, key): + d = client.get("https://api.weatherapi.com/v1/forecast.json", params={ + "key": key, "q": f"{lat},{lon}", "days": 1, + }).json() + cur = d.get("current", {}) + day = ((d.get("forecast") or {}).get("forecastday") or [{}])[0].get("day", {}) + return { + "city": str((d.get("location") or {}).get("name", city)).upper(), + "temp_f": _i(cur.get("temp_f")), "feels_like_f": _i(cur.get("feelslike_f")), + "hi_f": _i(day.get("maxtemp_f")), "lo_f": _i(day.get("mintemp_f")), + "desc": str((cur.get("condition") or {}).get("text", "CURRENT CONDITIONS")).upper(), + "code": None, "humidity": _i(cur.get("humidity")), + "wind_mph": cur.get("wind_mph"), "cloud_cover": _i(cur.get("cloud")), + } + + +def _qweather(client, lat, lon, city, key): + loc = f"{lon:.2f},{lat:.2f}" + headers = {"Authorization": f"Bearer {key}"} + now = client.get("https://devapi.qweather.com/v7/weather/now", + params={"location": loc, "lang": "en", "unit": "i"}, + headers=headers).json().get("now", {}) + day = (client.get("https://devapi.qweather.com/v7/weather/3d", + params={"location": loc, "lang": "en", "unit": "i"}, + headers=headers).json().get("daily") or [{}])[0] + return { + "city": city, "temp_f": _i(now.get("temp")), + "feels_like_f": _i(now.get("feelsLike")), + "hi_f": _i(day.get("tempMax")), "lo_f": _i(day.get("tempMin")), + "desc": str(now.get("text", "CURRENT CONDITIONS")).upper(), "code": None, + "humidity": _i(now.get("humidity")), "wind_mph": now.get("windSpeed"), + "cloud_cover": _i(now.get("cloud")), + } + + +_PROVIDERS = { + "openmeteo": _openmeteo, "openweather": _openweather, + "weatherapi": _weatherapi, "qweather": _qweather, +} + + +def fetch_current(settings) -> dict: + """Current conditions for the global location via the global provider. Falls + back to keyless Open-Meteo when a keyed provider is missing its key OR fails / + returns an error body (bad key, rate-limited, outage). Returns + ``{ok: False, error, provider}`` only if Open-Meteo itself fails; never raises.""" + provider = str(settings.get("weather_provider", "openmeteo") or "openmeteo").lower() + key = str(settings.get("weather_api_key", "") or "").strip() + if provider not in _PROVIDERS or (provider != "openmeteo" and not key): + provider = "openmeteo" # keyless default — weather works with no key + lat, lon, city = _resolve_location(settings) + cache_key = (provider, key, round(lat, 3), round(lon, 3)) + hit = _cache.get(cache_key) + if hit and (time.time() - hit[0]) < _CACHE_TTL: + return hit[1] + try: + with _Client(timeout=8.0) as client: + data = None + if provider != "openmeteo": + try: + got = _PROVIDERS[provider](client, lat, lon, city, key) + # A keyed provider that 401s/429s still returns 200-ish JSON + # with no temperature: treat a missing temp as a failure. + if got.get("temp_f") is not None: + data = got + else: + log.warning("weather provider %s returned no data; using open-meteo", provider) + except Exception as e: # noqa: BLE001 + log.warning("weather provider %s failed (%s); using open-meteo", provider, e) + if data is None: + provider = "openmeteo" + if data is None: + data = _openmeteo(client, lat, lon, city, key) + data.update(ok=True, provider=provider, lat=lat, lon=lon) + _cache[cache_key] = (time.time(), data) # cache only successful fetches + return data + except Exception as e: # noqa: BLE001 + log.warning("weather fetch failed: %s", e) + return {"ok": False, "error": str(e), "provider": provider} diff --git a/tests/test_i18n.py b/tests/test_i18n.py new file mode 100644 index 0000000..8215d21 --- /dev/null +++ b/tests/test_i18n.py @@ -0,0 +1,289 @@ +import inspect +import pathlib +import sys +import unittest +from datetime import datetime + + +SERVER_DIR = pathlib.Path(__file__).resolve().parents[1] / "server" +sys.path.insert(0, str(SERVER_DIR)) + +import i18n # noqa: E402 + +try: + import babel # noqa: F401 + HAS_BABEL = True +except ImportError: + HAS_BABEL = False + +# A fixed date so assertions never depend on "today". 2026-07-10 is a Friday. +D = datetime(2026, 7, 10, 15, 48, 5) + + +class TranslateTests(unittest.TestCase): + def test_english_is_passthrough(self): + # No language, English, and any en-* region all return the text unchanged. + for lang in (None, "", "en", "en-US", "en-GB"): + self.assertEqual(i18n.translate("SUNSET", lang, "sun"), "SUNSET") + + def test_known_translation(self): + self.assertEqual(i18n.translate("SUNSET", "fr", "sun"), "COUCHER") + self.assertEqual(i18n.translate("FULL MOON", "de", "moon"), "VOLLMOND") + self.assertEqual(i18n.translate("GOLD", "nl", "metals"), "GOUD") + + def test_unknown_key_falls_back_to_english(self): + self.assertEqual(i18n.translate("TOTALLY UNKNOWN", "fr", "sun"), "TOTALLY UNKNOWN") + + def test_unknown_language_falls_back_to_english_key(self): + # A language we don't have a translation for keeps the English text. + self.assertEqual(i18n.translate("SUNSET", "sw", "sun"), "SUNSET") + + def test_wrong_context_does_not_leak(self): + # SUNSET lives in the 'sun' domain, so asking a different domain must not + # find it (it's not a 'common' word) -> English text. + self.assertEqual(i18n.translate("SUNSET", "fr", "weather"), "SUNSET") + + +class ContextKeyTests(unittest.TestCase): + """The same English word carries different translations per context/domain + (gettext msgctxt), and a shared 'common' domain backs every context.""" + + def test_homograph_splits_by_context(self): + # HIGH: a weather level vs a tide height -> different French words. + self.assertEqual(i18n.translate("HIGH", "fr", "weather"), "ELEVE") + self.assertEqual(i18n.translate("HIGH", "fr", "tides"), "HAUTE") + self.assertNotEqual(i18n.translate("HIGH", "fr", "weather"), + i18n.translate("HIGH", "fr", "tides")) + self.assertEqual(i18n.translate("LOW", "fr", "tides"), "BASSE") + + def test_shared_time_vocabulary_is_one_domain(self): + # DAYS/IN are one meaning shared by many apps -> a single 'time' domain, + # not duplicated per app. countdown/holidays/f1/rocket all use ctx='time'. + self.assertEqual(i18n.translate("DAYS", "fr", "time"), "JOURS") + self.assertEqual(i18n.translate("IN", "fr", "time"), "DANS") + self.assertEqual(i18n.translate("NOW", "de", "time"), "JETZT") + + def test_common_domain_fallback(self): + # 'OFFLINE' lives only in 'common'; any domain resolves it via fallback. + for ctx in ("aurora", "sentiment", "weather"): + self.assertEqual(i18n.translate("OFFLINE", "fr", ctx), "HORS LIGNE") + + def test_default_context_is_common(self): + self.assertEqual(i18n.translate("OFFLINE", "fr"), "HORS LIGNE") + # A domain-specific word is NOT found under the default (common) context. + self.assertEqual(i18n.translate("SUNSET", "fr"), "SUNSET") + + +class DurationAndClockTests(unittest.TestCase): + def test_duration_unit_localizes_day(self): + self.assertEqual(i18n.duration_unit("D", "fr"), "J") # jour + self.assertEqual(i18n.duration_unit("D", "de"), "T") # Tag + self.assertEqual(i18n.duration_unit("D", "it"), "G") # giorno + + def test_duration_unit_passthrough(self): + self.assertEqual(i18n.duration_unit("D", "en-US"), "D") + self.assertEqual(i18n.duration_unit("H", None), "H") + self.assertEqual(i18n.duration_unit("Z", "fr"), "Z") # unknown key + + def test_duration_lives_in_time_domain(self): + # Abbreviations and full-word forms are one duration vocabulary in 'time'. + self.assertEqual(i18n.duration_unit("D", "fr"), i18n.translate("D", "fr", "time")) + self.assertEqual(i18n.translate("WEEKS", "fr", "time"), "SEMAINES") + self.assertEqual(i18n.translate("YEARS", "de", "time"), "JAHRE") + self.assertEqual(i18n.translate("SECONDS", "es", "time"), "SEGUNDOS") + + def test_uses_24h(self): + self.assertFalse(i18n.uses_24h("en-US")) + self.assertFalse(i18n.uses_24h("en-GB")) + self.assertFalse(i18n.uses_24h(None)) + self.assertTrue(i18n.uses_24h("fr")) + self.assertTrue(i18n.uses_24h("de")) + + def test_clock_format_follows_language(self): + self.assertEqual(i18n.clock(D, "fr"), "15:48") + self.assertEqual(i18n.clock(D, "en-US"), "3:48 PM") + + +class CurrencyCountryHolidayTests(unittest.TestCase): + def test_base_currency(self): + self.assertEqual(i18n.base_currency("en-US"), "USD") + self.assertEqual(i18n.base_currency("en-GB"), "GBP") + self.assertEqual(i18n.base_currency("en-AU"), "AUD") + self.assertEqual(i18n.base_currency("fr"), "EUR") + self.assertEqual(i18n.base_currency("zz"), "USD") # unknown default + + def test_country(self): + self.assertEqual(i18n.country("nl"), "NL") + self.assertEqual(i18n.country("en-AU"), "AU") + self.assertEqual(i18n.country("zz"), "US") # unknown default + + def test_holiday_localization(self): + self.assertEqual(i18n.holiday("Christmas Day", "fr"), "Noël") + self.assertIsNone(i18n.holiday("Some Local Fete", "fr")) # no translation + self.assertIsNone(i18n.holiday("Christmas Day", None)) # no language + + +class LocalizerTests(unittest.TestCase): + def test_translate_and_flags(self): + fr = i18n.Localizer("fr") + self.assertEqual(fr.t("SUNSET", "sun"), "COUCHER") + self.assertTrue(fr.is_24h) + self.assertEqual(fr.unit("D"), "J") + + def test_english_localizer_is_noop(self): + en = i18n.Localizer("en-US") + self.assertEqual(en.t("SUNSET", "sun"), "SUNSET") + self.assertFalse(en.is_24h) + + def test_lang_base_strips_region(self): + self.assertEqual(i18n.Localizer("en-GB").lang_base, "en") + self.assertEqual(i18n.Localizer("fr").lang_base, "fr") + + def test_country_and_base_currency(self): + self.assertEqual(i18n.Localizer("en-GB").base_currency(), "GBP") + self.assertEqual(i18n.Localizer("nl").country(), "NL") + + +class BabelBackedTests(unittest.TestCase): + """Date/month/number use babel for CLDR output; every helper degrades to an + English-ish fallback without it, so we assert shape always and exact values + only when babel is installed (the production dependency).""" + + def test_weekday_month_are_uppercase_nonempty(self): + for lang in ("en-US", "fr", "de"): + self.assertTrue(i18n.weekday(D, lang).isupper()) + self.assertTrue(i18n.month(D, lang)) + + def test_date_order_is_locale_specific(self): + en = i18n.date(D, "en-US") + self.assertIn("JULY", en) + self.assertIn("10", en) + if HAS_BABEL: + # Romance/Germanic order: day precedes month. + self.assertEqual(i18n.date(D, "fr"), "10 JUILLET") + self.assertEqual(i18n.date(D, "en-US"), "JULY 10") + + def test_number_separators(self): + if HAS_BABEL: + self.assertEqual(i18n.number(1234.5, "en-US"), "1,234.50") + self.assertEqual(i18n.number(1234.5, "de"), "1.234,50") + self.assertEqual(i18n.number(1234.5, "fr"), "1 234,50") + else: + # Fallback still produces a parseable, non-empty string. + self.assertTrue(i18n.number(1234.5, "de")) + + +class LocalizationDataFileTests(unittest.TestCase): + """All localization data lives in i18n_data.json (loaded at import), not in code. + Lock that contract: the file is valid and populated, the loader fills the module + tables, and a missing file degrades to defaults instead of crashing.""" + + def test_data_file_is_valid_and_populated(self): + import json + path = pathlib.Path(i18n.__file__).with_name("i18n_data.json") + self.assertTrue(path.is_file(), "i18n_data.json must ship next to i18n.py") + data = json.loads(path.read_text(encoding="utf-8")) + for section in ("languages", "strings", "holidays", + "base_currency", "country"): + self.assertIn(section, data) + self.assertTrue(data[section], f"{section} should not be empty") + # 'strings' is grouped by context/domain; each entry uses the + # {context, translations} schema, with a translator context note. + for domain, entries in data["strings"].items(): + for key, entry in entries.items(): + self.assertIn("context", entry, f"{domain}.{key} missing context note") + self.assertIn("translations", entry) + self.assertEqual(data["strings"]["sun"]["SUNSET"]["translations"]["fr"], "COUCHER") + # The same word differs by domain (weather level vs tide height). + self.assertEqual(data["strings"]["weather"]["HIGH"]["translations"]["fr"], "ELEVE") + self.assertEqual(data["strings"]["tides"]["HIGH"]["translations"]["fr"], "HAUTE") + self.assertEqual(data["holidays"]["christmas day"]["translations"]["fr"], "Noël") + self.assertEqual(data["base_currency"]["en-gb"], "GBP") + self.assertEqual(data["country"]["nl"], "NL") + + def test_loader_populates_module_tables(self): + self.assertTrue(all([i18n._STRINGS, i18n._DURATION_UNITS, i18n._HOLIDAYS, + i18n._BASE_CURRENCY, i18n._COUNTRY, i18n.LANGUAGE_OPTIONS])) + # The loader flattens into {domain: {NAME: {lang: value}}}. + self.assertEqual(i18n._STRINGS["sun"]["SUNSET"]["fr"], "COUCHER") + + def test_missing_file_degrades_gracefully(self): + original = i18n._DATA_PATH + try: + i18n._DATA_PATH = original + ".does-not-exist" + strings, holidays, cur, country, langs = i18n._load_i18n_data() + self.assertEqual((strings, holidays, cur, country), ({}, {}, {}, {})) + # ...but the Language list still has at least English so the UI works. + self.assertTrue(langs and langs[0]["value"].startswith("en")) + finally: + i18n._DATA_PATH = original + + +class LanguageOptionsTests(unittest.TestCase): + def test_language_options_shape(self): + self.assertTrue(i18n.LANGUAGE_OPTIONS) + for opt in i18n.LANGUAGE_OPTIONS: + self.assertIn("value", opt) + self.assertIn("label", opt) + values = {o["value"] for o in i18n.LANGUAGE_OPTIONS} + self.assertIn("en-US", values) + self.assertIn("pt-BR", values) # a non-European regional variant + + +class RegionalVariantTests(unittest.TestCase): + """A region-tagged language inherits its base language's translations but keeps + its own currency/country (pt-BR reuses pt words but resolves to Brazil / BRL).""" + + def test_variant_inherits_base_translations(self): + self.assertEqual(i18n.translate("SUNSET", "pt-BR", "sun"), + i18n.translate("SUNSET", "pt", "sun")) + self.assertEqual(i18n.duration_unit("D", "pt-BR"), i18n.duration_unit("D", "pt")) + + def test_variant_currency_and_country_override_base(self): + self.assertEqual(i18n.base_currency("pt-BR"), "BRL") + self.assertEqual(i18n.country("pt-BR"), "BR") + self.assertEqual(i18n.base_currency("pt"), "EUR") # base unchanged + self.assertEqual(i18n.base_currency("es-MX"), "MXN") + self.assertEqual(i18n.country("fr-CA"), "CA") + self.assertEqual(i18n.base_currency("de-CH"), "CHF") + + def test_expanded_base_languages(self): + self.assertEqual(i18n.base_currency("da"), "DKK") + self.assertEqual(i18n.country("sv"), "SE") + self.assertEqual(i18n.base_currency("af"), "ZAR") + + def test_scandinavian_strings_present(self): + self.assertEqual(i18n.translate("SUNSET", "sv", "sun"), "SOLNEDGÅNG") + self.assertEqual(i18n.translate("SNOW", "da", "weather"), "SNE") + + +def _accepts(fn, name): + """The exact predicate app.py._fetch_accepts uses to decide whether to inject a + helper: the parameter is named, or the function takes **kwargs. Kept in lockstep + here so a Python signature-inspection change would surface in tests.""" + params = inspect.signature(fn).parameters + return name in params or any(p.kind == p.VAR_KEYWORD for p in params.values()) + + +class InjectionContractTests(unittest.TestCase): + def test_classic_four_arg_app_opts_into_nothing(self): + def fetch(settings, format_lines, get_rows, get_cols): + return [] + for helper in ("i18n", "get_weather", "get_location"): + self.assertFalse(_accepts(fetch, helper)) + + def test_declared_parameter_opts_in(self): + def fetch(settings, format_lines, get_rows, get_cols, i18n=None): + return [] + self.assertTrue(_accepts(fetch, "i18n")) + self.assertFalse(_accepts(fetch, "get_weather")) + + def test_var_keyword_opts_into_everything(self): + def fetch(settings, format_lines, get_rows, get_cols, **kw): + return [] + for helper in ("i18n", "get_weather", "get_location"): + self.assertTrue(_accepts(fetch, helper)) + + +if __name__ == "__main__": + unittest.main() From b4b8105e0ba44e5f683f8b497417415631a863c7 Mon Sep 17 00:00:00 2001 From: Alex Van de Putte Date: Sat, 11 Jul 2026 18:03:26 -0400 Subject: [PATCH 2/3] Fix gateway RX frame decode to preserve extended cp1252 bytes The gateway receive path decoded/re-encoded frames as UTF-8 with errors=ignore, which silently dropped lone high bytes (0x80-0xFF). That corrupted a module's A-command character-map sync for any reel set with extended (international) glyphs. Decode and re-encode with latin-1 instead -- a transparent byte<->code-point codec -- so the raw cp1252 bytes survive to the downstream .decode('cp1252'). Adds a regression test. Co-Authored-By: Claude Opus 4.8 (1M context) --- server/gateway_transport.py | 14 ++++++- tests/test_gateway_transport.py | 65 +++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 tests/test_gateway_transport.py diff --git a/server/gateway_transport.py b/server/gateway_transport.py index 0bb1b3f..1634c98 100644 --- a/server/gateway_transport.py +++ b/server/gateway_transport.py @@ -151,7 +151,12 @@ def _on_message(self, client, userdata, msg): return # Re-append the newline the gateway stripped so downstream parsers that # look for '\n' terminators behave exactly as with raw serial. - data = frame.encode("utf-8", errors="ignore") + b"\n" + # Encode with latin-1 (not utf-8): the bus is single-byte cp1252, so a + # frame may carry raw high bytes (e.g. a module's A-command char map with + # 0xE9/0x80). latin-1 maps every code point 0x00-0xFF back to that exact + # byte, so downstream .decode('cp1252') sees the original bytes. utf-8 + # here would drop lone high bytes (errors="ignore") and corrupt the map. + data = frame.encode("latin-1", errors="ignore") + b"\n" with self._rx_lock: self._rx_buf.extend(data) @@ -162,8 +167,13 @@ def _extract_frame(payload): Accepts the gateway's JSON form ``{"command":"..."}`` and also a bare plain-text frame, for robustness against future gateway changes. """ + # Decode with latin-1, a transparent byte<->code-point codec: the bus is + # single-byte cp1252 and a frame (e.g. an A-command char-map response) can + # contain raw high bytes that are invalid standalone utf-8. latin-1 never + # raises and preserves every byte, so json.loads still parses the ASCII + # structure and the raw bytes survive to the caller (re-encoded latin-1). try: - text = payload.decode("utf-8", errors="ignore") + text = payload.decode("latin-1") except Exception: return None text = text.strip() diff --git a/tests/test_gateway_transport.py b/tests/test_gateway_transport.py new file mode 100644 index 0000000..eb949b9 --- /dev/null +++ b/tests/test_gateway_transport.py @@ -0,0 +1,65 @@ +"""Regression tests for GatewayTransport frame decoding. + +The RS485 bus is single-byte Windows-1252 (cp1252): one displayed glyph is one +byte. A frame read back from the gateway -- most importantly a module's +``A``-command character-map response -- can therefore carry raw high bytes +(0x80-0xFF) that are *invalid* standalone UTF-8. The RX path must preserve those +bytes verbatim so the downstream ``.decode('cp1252')`` in app.get_module_char_map +recovers the real characters. These tests lock in that byte-transparent behaviour +(previously the code decoded/re-encoded as UTF-8 with errors="ignore", which +silently dropped every extended character). +""" + +import pathlib +import sys +import unittest + + +SERVER_DIR = pathlib.Path(__file__).resolve().parents[1] / "server" +sys.path.insert(0, str(SERVER_DIR)) + +from gateway_transport import GatewayTransport # noqa: E402 + + +# A char map with real cp1252 extended glyphs (no " or \\, which the physical +# reel aliases to 'q' anyway and which would need JSON escaping). +CHAR_MAP = " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789éöüàçñ€£¥" +FRAME = "m05A:64:" + CHAR_MAP # module's A-command response frame +FRAME_BYTES = FRAME.encode("cp1252") # the exact bytes on the bus + + +class ExtractFrameTests(unittest.TestCase): + def _recover(self, frame): + # Mirrors _on_message: what lands in the RX buffer is the frame encoded + # latin-1; get_module_char_map later slices it and decodes cp1252. + self.assertIsNotNone(frame) + return frame.encode("latin-1") + + def test_json_form_preserves_extended_bytes(self): + # The gateway's {"command": "..."} form with raw high bytes in the string. + payload = b'{"command": "' + FRAME_BYTES + b'"}' + recovered = self._recover(GatewayTransport._extract_frame(payload)) + self.assertEqual(recovered, FRAME_BYTES) + # Round-trips to the original characters via the downstream cp1252 decode. + self.assertEqual(recovered.decode("cp1252"), FRAME) + + def test_bare_frame_preserves_extended_bytes(self): + # Plain-text fallback (no JSON wrapper). + recovered = self._recover(GatewayTransport._extract_frame(FRAME_BYTES)) + self.assertEqual(recovered.decode("cp1252"), FRAME) + + def test_plain_ascii_unaffected(self): + payload = b'{"command": "m00-A"}' + self.assertEqual(GatewayTransport._extract_frame(payload), "m00-A") + + def test_utf8_would_have_corrupted(self): + # Documents the bug being fixed: the old utf-8/errors=ignore path drops + # the lone high bytes, losing every extended glyph. + lossy = FRAME_BYTES.decode("utf-8", errors="ignore") + self.assertLess(len(lossy), len(FRAME)) + self.assertNotIn("é", lossy) + self.assertNotIn("€", lossy) + + +if __name__ == "__main__": + unittest.main() From 7f2e7ae47249514ea564193f2b376116958ba681 Mon Sep 17 00:00:00 2001 From: Alex Van de Putte Date: Sat, 11 Jul 2026 18:03:26 -0400 Subject: [PATCH 3/3] Add 15 new apps and localize 15 existing apps (i18n) New apps: advice, aurora, cat-facts, earthquakes, exchange-rates, formula1, holidays, metals, quote, rocket-launch, sarcastic-fortune-cookies, sun-times, tides, useless-fact, wiki-today. Localized/enhanced in place: bitcoin-fear-greed, countdown, crypto, dashboard, date, iss, moon-phase, sports, stocks, time, time-since, weather, word-clock, word-of-the-day, world_clock. Every app keeps the classic contract and degrades gracefully: i18n, get_weather and get_location are optional parameters defaulting to None, so each app still runs in English. Currency-bearing apps (crypto, metals, stocks) show the local currency when i18n is on and fall back to ASCII currency codes when it is off, since the basic charset cannot render the EUR/GBP/JPY symbols. Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/advice/app.py | 105 + apps/advice/manifest.json | 34 + apps/aurora/app.py | 49 + apps/aurora/manifest.json | 25 + apps/bitcoin-fear-greed/app.py | 11 +- apps/bitcoin-fear-greed/manifest.json | 1 + apps/cat-facts/app.py | 115 + apps/cat-facts/manifest.json | 44 + apps/countdown/app.py | 49 +- apps/countdown/manifest.json | 1 + apps/crypto/app.py | 92 +- apps/crypto/manifest.json | 1 + apps/dashboard/app.py | 76 +- apps/dashboard/manifest.json | 1 + apps/date/app.py | 20 +- apps/date/manifest.json | 1 + apps/earthquakes/app.py | 57 + apps/earthquakes/manifest.json | 34 + apps/exchange-rates/app.py | 39 + apps/exchange-rates/manifest.json | 41 + apps/formula1/app.py | 63 + apps/formula1/manifest.json | 25 + apps/holidays/app.py | 58 + apps/holidays/manifest.json | 34 + apps/iss/app.py | 10 +- apps/iss/manifest.json | 1 + apps/metals/app.py | 64 + apps/metals/manifest.json | 25 + apps/moon-phase/app.py | 18 +- apps/moon-phase/manifest.json | 1 + apps/quote/app.py | 127 + apps/quote/manifest.json | 44 + apps/registry.json | 135 + apps/rocket-launch/app.py | 48 + apps/rocket-launch/manifest.json | 25 + apps/sarcastic-fortune-cookies/app.py | 153 + .../fortunes_ca.json | 402 ++ .../fortunes_da.json | 402 ++ .../fortunes_de.json | 4002 ++++++++++++++++ .../fortunes_en-au.json | 4066 +++++++++++++++++ .../fortunes_en-gb.json | 4066 +++++++++++++++++ .../fortunes_en.json | 4002 ++++++++++++++++ .../fortunes_es.json | 402 ++ .../fortunes_fr.json | 2802 ++++++++++++ .../fortunes_it.json | 402 ++ .../fortunes_nl.json | 402 ++ .../fortunes_pt.json | 402 ++ .../fortunes_sv.json | 402 ++ apps/sarcastic-fortune-cookies/manifest.json | 28 + apps/sports/app.py | 40 +- apps/sports/manifest.json | 1 + apps/stocks/app.py | 48 +- apps/stocks/manifest.json | 1 + apps/sun-times/app.py | 74 + apps/sun-times/manifest.json | 25 + apps/tides/app.py | 52 + apps/tides/manifest.json | 32 + apps/time-since/app.py | 19 +- apps/time-since/manifest.json | 1 + apps/time/app.py | 13 +- apps/time/manifest.json | 1 + apps/useless-fact/app.py | 125 + apps/useless-fact/manifest.json | 44 + apps/weather/app.py | 152 +- apps/weather/manifest.json | 1 + apps/wiki-today/app.py | 57 + apps/wiki-today/manifest.json | 25 + apps/word-clock/app.py | 292 +- apps/word-clock/manifest.json | 1 + apps/word-of-the-day/app.py | 103 +- apps/word-of-the-day/manifest.json | 20 +- apps/world_clock/app.py | 23 +- apps/world_clock/manifest.json | 1 + 73 files changed, 24274 insertions(+), 284 deletions(-) create mode 100644 apps/advice/app.py create mode 100644 apps/advice/manifest.json create mode 100644 apps/aurora/app.py create mode 100644 apps/aurora/manifest.json create mode 100644 apps/cat-facts/app.py create mode 100644 apps/cat-facts/manifest.json create mode 100644 apps/earthquakes/app.py create mode 100644 apps/earthquakes/manifest.json create mode 100644 apps/exchange-rates/app.py create mode 100644 apps/exchange-rates/manifest.json create mode 100644 apps/formula1/app.py create mode 100644 apps/formula1/manifest.json create mode 100644 apps/holidays/app.py create mode 100644 apps/holidays/manifest.json create mode 100644 apps/metals/app.py create mode 100644 apps/metals/manifest.json create mode 100644 apps/quote/app.py create mode 100644 apps/quote/manifest.json create mode 100644 apps/rocket-launch/app.py create mode 100644 apps/rocket-launch/manifest.json create mode 100644 apps/sarcastic-fortune-cookies/app.py create mode 100644 apps/sarcastic-fortune-cookies/fortunes_ca.json create mode 100644 apps/sarcastic-fortune-cookies/fortunes_da.json create mode 100644 apps/sarcastic-fortune-cookies/fortunes_de.json create mode 100644 apps/sarcastic-fortune-cookies/fortunes_en-au.json create mode 100644 apps/sarcastic-fortune-cookies/fortunes_en-gb.json create mode 100644 apps/sarcastic-fortune-cookies/fortunes_en.json create mode 100644 apps/sarcastic-fortune-cookies/fortunes_es.json create mode 100644 apps/sarcastic-fortune-cookies/fortunes_fr.json create mode 100644 apps/sarcastic-fortune-cookies/fortunes_it.json create mode 100644 apps/sarcastic-fortune-cookies/fortunes_nl.json create mode 100644 apps/sarcastic-fortune-cookies/fortunes_pt.json create mode 100644 apps/sarcastic-fortune-cookies/fortunes_sv.json create mode 100644 apps/sarcastic-fortune-cookies/manifest.json create mode 100644 apps/sun-times/app.py create mode 100644 apps/sun-times/manifest.json create mode 100644 apps/tides/app.py create mode 100644 apps/tides/manifest.json create mode 100644 apps/useless-fact/app.py create mode 100644 apps/useless-fact/manifest.json create mode 100644 apps/wiki-today/app.py create mode 100644 apps/wiki-today/manifest.json diff --git a/apps/advice/app.py b/apps/advice/app.py new file mode 100644 index 0000000..1213959 --- /dev/null +++ b/apps/advice/app.py @@ -0,0 +1,105 @@ +"""A random piece of advice (keyless: Advice Slip).""" + + +def _need_lines(lens, cols): + need, cur = 1, 0 + for wl in lens: + add = wl if cur == 0 else cur + 1 + wl + if add <= cols: + cur = add + else: + need += 1 + cur = wl + return need + + +def _balance(words, lens, cols, k): + """Split words into exactly k lines (each <= cols) minimizing raggedness, so + lines fill evenly instead of orphaning the last word; prefers ending a line at + sentence punctuation. A tiny DP.""" + n = len(words) + pre = [0] + for wl in lens: + pre.append(pre[-1] + wl) + + def linelen(i, j): + return pre[j + 1] - pre[i] + (j - i) + + INF = float("inf") + dp = [[INF] * (n + 1) for _ in range(k + 1)] + nxt = [[0] * (n + 1) for _ in range(k + 1)] + dp[0][n] = 0.0 + for kk in range(1, k + 1): + for i in range(n - 1, -1, -1): + j = i + while j < n: + ll = linelen(i, j) + if ll > cols and j > i: + break + rest = dp[kk - 1][j + 1] + if rest < INF: + slack = cols - ll + cost = slack * slack + rest + if words[j][-1:] in ".!?": + cost -= cols + if cost < dp[kk][i]: + dp[kk][i] = cost + nxt[kk][i] = j + 1 + j += 1 + if dp[k][0] >= INF: + return None + out, i, kk = [], 0, k + while kk > 0: + j = nxt[kk][i] + out.append(" ".join(words[i:j])) + i, kk = j, kk - 1 + return out + + +def _greedy(words, cols): + lines, cur = [], '' + for w in words: + w = w if len(w) <= cols else w[:cols] + if len(cur) + len(w) + (1 if cur else 0) <= cols: + cur = f'{cur} {w}'.strip() + else: + lines.append(cur) + cur = w + if cur: + lines.append(cur) + return lines or [''] + + +def _pages(format_lines, title, text, rows, cols): + """Lay the text out under a title. When it fits on one page the words are + balanced evenly across the lines it needs; longer text word-wraps and + paginates.""" + words = text.split() or [''] + lens = [len(w) for w in words] + if rows == 1: + return [ln.center(cols)[:cols] for ln in _greedy(words, cols)] + body = rows - 1 if title else rows + if max(lens) <= cols: + need = _need_lines(lens, cols) + if need <= body: + bal = _balance(words, lens, cols, need) + if bal is not None: + return [format_lines(title, *bal)] if title else [format_lines(*bal)] + lines = _greedy(words, cols) + pages, i = ([format_lines(title, *lines[:body])], body) if title else ([], 0) + while i < len(lines): + pages.append(format_lines(*lines[i:i + rows])) + i += rows + return pages + +def fetch(settings, format_lines, get_rows, get_cols): + import requests + rows, cols = get_rows(), get_cols() + try: + d = requests.get('https://api.adviceslip.com/advice', timeout=8).json() + text = str((d.get('slip') or {}).get('advice', '') or '').strip().upper() + if not text: + return [format_lines('ADVICE', 'NO DATA', '')] + return _pages(format_lines, '', f'ADVICE: {text}', rows, cols) + except Exception: + return [format_lines('ADVICE', 'OFFLINE', '')] diff --git a/apps/advice/manifest.json b/apps/advice/manifest.json new file mode 100644 index 0000000..5dc2114 --- /dev/null +++ b/apps/advice/manifest.json @@ -0,0 +1,34 @@ +{ + "id": "advice", + "name": "Advice", + "icon": "🧭", + "description": "A random piece of advice", + "type": "functional", + "refresh_interval": 1800, + "loop_delay": 7, + "category": "entertainment", + "min_rows": 2, + "min_cols": 15, + "settings": [ + { + "key": "refresh_minutes", + "label": "Fetch new advice every (minutes)", + "type": "number", + "default": "30", + "min": "1", + "max": "1440", + "step": "1", + "stepper": true + }, + { + "key": "loop_delay", + "label": "Delay Between Pages (seconds)", + "type": "number", + "default": "7", + "min": "2", + "max": "30", + "step": "1", + "stepper": true + } + ] +} diff --git a/apps/aurora/app.py b/apps/aurora/app.py new file mode 100644 index 0000000..f5efa8f --- /dev/null +++ b/apps/aurora/app.py @@ -0,0 +1,49 @@ +"""Aurora / geomagnetic activity — planetary K-index (NOAA SWPC, keyless).""" + + +def _label(kp): + if kp < 3: + return 'QUIET' + if kp < 5: + return 'UNSETTLED' + if kp < 6: + return 'MINOR STORM' + if kp < 7: + return 'MODERATE' + if kp < 8: + return 'STRONG STORM' + if kp < 9: + return 'SEVERE STORM' + return 'EXTREME' + + +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): + import requests + rows = get_rows() + + def t(s): + return i18n.t(s, "aurora") if i18n is not None else s + + def num(kp): # integer Kp shows no decimal + whole = (kp == int(kp)) + if i18n is not None: + return i18n.number(kp, decimals=0 if whole else 1, grouping=False) + return f'{kp:.0f}' if whole else f'{kp:.1f}' + + try: + data = requests.get('https://services.swpc.noaa.gov/products/noaa-planetary-k-index.json', + timeout=8).json() + if not isinstance(data, list) or not data: + return [format_lines(t('AURORA'), t('NO DATA'), '')] + latest = data[-1] + # The feed is a list of {time_tag, Kp, ...} records (newest last). + kp = float(latest.get('Kp') if isinstance(latest, dict) else latest[1]) + kps = num(kp) + label = t(_label(kp)) + if rows == 1: + return [format_lines(f'{t("AURORA")} KP {kps}')] + if rows == 2: + return [format_lines(f'{t("AURORA")} KP {kps}', label)] + return [format_lines(t('AURORA'), f'KP INDEX {kps}', label)] + except Exception: + return [format_lines(t('AURORA'), t('OFFLINE'), '')] diff --git a/apps/aurora/manifest.json b/apps/aurora/manifest.json new file mode 100644 index 0000000..a2ec3b1 --- /dev/null +++ b/apps/aurora/manifest.json @@ -0,0 +1,25 @@ +{ + "id": "aurora", + "name": "Aurora Watch", + "icon": "🌌", + "description": "Geomagnetic activity (Kp index)", + "type": "functional", + "i18n": true, + "refresh_interval": 900, + "loop_delay": 6, + "category": "data", + "min_rows": 1, + "min_cols": 12, + "settings": [ + { + "key": "loop_delay", + "label": "Delay Between Pages (seconds)", + "type": "number", + "default": "6", + "min": "2", + "max": "30", + "step": "1", + "stepper": true + } + ] +} diff --git a/apps/bitcoin-fear-greed/app.py b/apps/bitcoin-fear-greed/app.py index 1f3a88f..88f2b5c 100644 --- a/apps/bitcoin-fear-greed/app.py +++ b/apps/bitcoin-fear-greed/app.py @@ -1,8 +1,12 @@ """Bitcoin Fear & Greed Index plugin for Split-Flap Display.""" -def fetch(settings, format_lines, get_rows, get_cols): +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): import urllib.request import json + + def t(s): + return i18n.t(s, "sentiment") if i18n is not None else s + try: url = "https://api.alternative.me/fng/?limit=1" req = urllib.request.Request(url, headers={"User-Agent": "SplitFlap/1.0"}) @@ -10,10 +14,11 @@ def fetch(settings, format_lines, get_rows, get_cols): data = json.loads(resp.read().decode()) entry = data["data"][0] value = entry["value"] - label = entry["value_classification"].upper() + # "Extreme Fear" / "Fear" / "Neutral" / "Greed" / "Extreme Greed" -> localized. + label = t(entry["value_classification"].upper()) return [format_lines("BTC FEAR&GREED", f"INDEX: {value}/100", label)] except Exception: - return [format_lines("FEAR & GREED", "FETCH ERROR", "")] + return [format_lines("BTC FEAR&GREED", t("OFFLINE"), "")] def trigger(settings, conditions): diff --git a/apps/bitcoin-fear-greed/manifest.json b/apps/bitcoin-fear-greed/manifest.json index 7e539fd..9bd5bf4 100644 --- a/apps/bitcoin-fear-greed/manifest.json +++ b/apps/bitcoin-fear-greed/manifest.json @@ -5,6 +5,7 @@ "description": "Bitcoin Fear and Greed Index", "category": "finance", "type": "functional", + "i18n": true, "version": "1.0", "refresh_interval": 300, "loop_delay": 10, diff --git a/apps/cat-facts/app.py b/apps/cat-facts/app.py new file mode 100644 index 0000000..054f18b --- /dev/null +++ b/apps/cat-facts/app.py @@ -0,0 +1,115 @@ +"""A random cat fact (keyless: catfact.ninja).""" + + +def _need_lines(lens, cols): + need, cur = 1, 0 + for wl in lens: + add = wl if cur == 0 else cur + 1 + wl + if add <= cols: + cur = add + else: + need += 1 + cur = wl + return need + + +def _balance(words, lens, cols, k): + """Split words into exactly k lines (each <= cols) minimizing raggedness, so + lines fill evenly instead of orphaning the last word; prefers ending a line at + sentence punctuation. A tiny DP.""" + n = len(words) + pre = [0] + for wl in lens: + pre.append(pre[-1] + wl) + + def linelen(i, j): + return pre[j + 1] - pre[i] + (j - i) + + INF = float("inf") + dp = [[INF] * (n + 1) for _ in range(k + 1)] + nxt = [[0] * (n + 1) for _ in range(k + 1)] + dp[0][n] = 0.0 + for kk in range(1, k + 1): + for i in range(n - 1, -1, -1): + j = i + while j < n: + ll = linelen(i, j) + if ll > cols and j > i: + break + rest = dp[kk - 1][j + 1] + if rest < INF: + slack = cols - ll + cost = slack * slack + rest + if words[j][-1:] in ".!?": + cost -= cols + if cost < dp[kk][i]: + dp[kk][i] = cost + nxt[kk][i] = j + 1 + j += 1 + if dp[k][0] >= INF: + return None + out, i, kk = [], 0, k + while kk > 0: + j = nxt[kk][i] + out.append(" ".join(words[i:j])) + i, kk = j, kk - 1 + return out + + +def _greedy(words, cols): + lines, cur = [], '' + for w in words: + w = w if len(w) <= cols else w[:cols] + if len(cur) + len(w) + (1 if cur else 0) <= cols: + cur = f'{cur} {w}'.strip() + else: + lines.append(cur) + cur = w + if cur: + lines.append(cur) + return lines or [''] + + +def _pages(format_lines, title, text, rows, cols): + """Lay the text out (optionally under a title). When it fits on one page the + words are balanced evenly across the lines; longer text word-wraps and + paginates. With no title the text uses every row and is vertically centered.""" + words = text.split() or [''] + lens = [len(w) for w in words] + if rows == 1: + return [ln.center(cols)[:cols] for ln in _greedy(words, cols)] + body = rows - 1 if title else rows + if max(lens) <= cols: + need = _need_lines(lens, cols) + if need <= body: + bal = _balance(words, lens, cols, need) + if bal is not None: + if title: + return [format_lines(title, *bal)] + top = (rows - len(bal)) // 2 + return [format_lines(*([''] * top + bal))] + lines = _greedy(words, cols) + pages, i = ([format_lines(title, *lines[:body])], body) if title else ([], 0) + while i < len(lines): + pages.append(format_lines(*lines[i:i + rows])) + i += rows + return pages + +def fetch(settings, format_lines, get_rows, get_cols): + import requests + rows, cols = get_rows(), get_cols() + try: + max_len = int(float(settings.get('max_length', '120') or 120)) + except (TypeError, ValueError): + max_len = 120 + max_len = max(40, min(250, max_len)) + try: + # catfact.ninja honors max_length, so we can ask for a display-friendly fact. + d = requests.get('https://catfact.ninja/fact', + params={'max_length': max_len}, timeout=8).json() + text = str(d.get('fact', '') or '').strip().upper() + if not text: + return [format_lines('CAT FACT', 'NO DATA', '')] + return _pages(format_lines, '', text, rows, cols) # no title — just the fact + except Exception: + return [format_lines('CAT FACT', 'OFFLINE', '')] diff --git a/apps/cat-facts/manifest.json b/apps/cat-facts/manifest.json new file mode 100644 index 0000000..d6cad1f --- /dev/null +++ b/apps/cat-facts/manifest.json @@ -0,0 +1,44 @@ +{ + "id": "cat-facts", + "name": "Cat Facts", + "icon": "🐱", + "description": "A concise cat fact", + "type": "functional", + "refresh_interval": 1800, + "loop_delay": 7, + "category": "entertainment", + "min_rows": 3, + "min_cols": 15, + "settings": [ + { + "key": "max_length", + "label": "Max fact length (characters)", + "type": "number", + "default": "120", + "min": "40", + "max": "250", + "step": "10", + "stepper": true + }, + { + "key": "refresh_minutes", + "label": "Fetch a new fact every (minutes)", + "type": "number", + "default": "30", + "min": "1", + "max": "1440", + "step": "1", + "stepper": true + }, + { + "key": "loop_delay", + "label": "Delay Between Pages (seconds)", + "type": "number", + "default": "7", + "min": "2", + "max": "30", + "step": "1", + "stepper": true + } + ] +} diff --git a/apps/countdown/app.py b/apps/countdown/app.py index 0d91dfa..8bbdbdc 100644 --- a/apps/countdown/app.py +++ b/apps/countdown/app.py @@ -1,7 +1,13 @@ -def fetch(settings, format_lines, get_rows, get_cols): +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): from datetime import datetime import pytz + def t(s): + return i18n.t(s, "time") if i18n is not None else s + + def u(k): # localized D/H/M/S suffix (French J for jour, etc.) + return i18n.unit(k) if i18n is not None else k + def get_allowed_chars(): default_chars = " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$&()-+=;:%'.,/?*" @@ -36,11 +42,12 @@ def build_compact_countdown(total_seconds, cols): mins, secs = divmod(rem, 60) # Keep the most useful leading units that still fit on the sign. + day_u = u('D') sections = [ - f'{days}D', - f'{hrs}H', - f'{mins}M', - f'{secs}S', + f'{days}{day_u}', + f'{hrs}{u("H")}', + f'{mins}{u("M")}', + f'{secs}{u("S")}', ] text = '' @@ -55,33 +62,23 @@ def build_compact_countdown(total_seconds, cols): return text # At the narrowest widths, preserve the day suffix so the value still has context. if cols <= 1: - return 'D'[:cols] - return f"{str(days)[:cols - 1]}D" + return day_u[:cols] + return f"{str(days)[:cols - len(day_u)]}{day_u}" + + def _pick(cols, *words): + for w in words: + if w and cols >= len(w): + return w + return (words[-1] or '')[:cols] def build_arrived_text(cols): - if cols >= len('ARRIVED!'): - return 'ARRIVED!' - if cols >= len('HERE!'): - return 'HERE!' - if cols >= len('NOW!'): - return 'NOW!' - return 'GO'[:cols] + return _pick(cols, t('ARRIVED') + '!', t('HERE') + '!', t('NOW') + '!', 'GO') def build_celebration_text(cols): - if cols >= len('CELEBRATE!'): - return 'CELEBRATE!' - if cols >= len('PARTY!'): - return 'PARTY!' - if cols >= len('YAY!'): - return 'YAY!' - return '' + return _pick(cols, t('CELEBRATE') + '!', t('PARTY') + '!', '') def build_remaining_text(cols): - if cols >= len('REMAINING'): - return 'REMAINING' - if cols >= len('LEFT'): - return 'LEFT' - return '' + return _pick(cols, t('REMAINING'), t('LEFT'), '') def build_slot_pages(event, target, now, rows, cols): diff = target - now diff --git a/apps/countdown/manifest.json b/apps/countdown/manifest.json index e7ec470..73f11ef 100644 --- a/apps/countdown/manifest.json +++ b/apps/countdown/manifest.json @@ -1,5 +1,6 @@ { "id": "countdown", + "i18n": true, "name": "Countdown", "icon": "⏳", "description": "Timer to event", diff --git a/apps/crypto/app.py b/apps/crypto/app.py index f5fa7e1..a3a1c3b 100644 --- a/apps/crypto/app.py +++ b/apps/crypto/app.py @@ -1,38 +1,80 @@ -def fetch(settings, format_lines, get_rows, get_cols): +def fetch(settings, format_lines, get_rows, get_cols, i18n=None, get_location=None): import requests + + def t(s): + return i18n.t(s, "crypto") if i18n is not None else s + coins = [s.strip() for s in settings.get('crypto_list', '').split(',') if s.strip()] if not coins: - return [format_lines('CRYPTO', 'NO COINS', 'CONFIGURE')] - currency = settings.get('currency_symbol', '$').strip() or '$' + return [format_lines('CRYPTO', t('NO COINS'), t('CONFIGURE'))] + + # Price in the local currency: where you are (Location) -> your Language -> USD. + # CoinGecko quotes natively in the target currency, so no FX conversion is needed. + loc = get_location() if get_location is not None else None + ccy = loc.get('currency') if isinstance(loc, dict) and loc.get('ok') else None + if not ccy and i18n is not None: + ccy = i18n.base_currency() + ccy = (ccy or 'USD').upper() + vs = ccy.lower() + # With i18n the display can render € £ ¥ (Windows-1252); without it the modules + # are the basic charset, so use '$' for USD and the ASCII ISO code otherwise. + cur_sym = i18n.currency_symbol(ccy) if i18n is not None else ('$' if ccy == 'USD' else ccy) + sep = '' if cur_sym != ccy else ' ' # 3-letter-code fallback reads better spaced + try: r = requests.get( 'https://api.coingecko.com/api/v3/simple/price', - params={'ids': ','.join(coins), 'vs_currencies': 'usd', 'include_24hr_change': 'true'}, + params={'ids': ','.join(coins), 'vs_currencies': vs, 'include_24hr_change': 'true'}, timeout=10 ).json() except Exception: - return [format_lines('CRYPTO', 'ERROR', 'API FAIL')] - rows = get_rows() + return [format_lines('CRYPTO', t('ERROR'), t('API FAIL'))] + rows, cols = get_rows(), get_cols() + no_color = settings.get('disable_colors', 'no') == 'yes' + + # Numbers follow the global Language (1,234.50 vs 1.234,50 vs 1 234,50). + def n(v, d=2, grouping=True): + if i18n is not None: + return i18n.number(v, d, grouping) + return f'{v:,.{d}f}' if grouping else f'{v:.{d}f}' + + def pct(v): + return f"{'+' if v >= 0 else '-'}{n(abs(v), 1, grouping=False)}%" + + def price_str(price): + body = n(price, 0) if price >= 1 else n(price, 4, grouping=False) + return f'{cur_sym}{sep}{body}' + + def block(c): + """The lines for one coin, sized to the display: price+change together on + 2+ row displays (with the name too when there are 3+ rows).""" + d = r.get(c, {}) + price, chg = d.get(vs), d.get(f'{vs}_24h_change') + sym = c[:6].upper() + if price is None: + return [f'{sym} ERR'] + if chg is None: + chg_str = 'N/A' + elif no_color: + chg_str = pct(chg) + else: + chg_str = ('🟩' if chg >= 0 else '🟥') + f' {pct(chg)}' + if rows == 1: + return [f'{sym} {price_str(price)}'] + if rows == 2: + return [f'{sym} {price_str(price)}', chg_str] + return [c.upper()[:cols], price_str(price), chg_str] # name / price / change + + lines_per = 1 if rows == 1 else (2 if rows == 2 else 3) + per_page = max(1, rows // lines_per) # how many coins fit on one page pages = [] - for i in range(0, len(coins), rows): - chunk = coins[i:i+rows] - price_lines, change_lines = [], [] - for c in chunk: - d = r.get(c, {}) - price = d.get('usd') - chg = d.get('usd_24h_change') - sym = c[:5].upper() - if price is not None: - price_lines.append(f'{sym} {currency}{price:,.0f}' if price >= 1 else f'{sym} {currency}{price:.4f}') - icon = '🟩' if (chg or 0) >= 0 else '🟥' - change_lines.append(f'{sym} {icon}{chg:+.1f}%' if chg is not None else f'{sym} N/A') - else: - price_lines.append(f'{sym} ERR') - change_lines.append(f'{sym} ERR') - pad = [''] * (rows - len(chunk)) - pages.append(format_lines(*(price_lines + pad))) - pages.append(format_lines(*(change_lines + pad))) - return pages or [format_lines('CRYPTO', 'NO DATA', '')] + for i in range(0, len(coins), per_page): + lines = [] + for c in coins[i:i + per_page]: + b = block(c) + lines += b + [''] * (lines_per - len(b)) # pad each block so coins align + pages.append(format_lines(*lines)) + return pages or [format_lines('CRYPTO', t('NO DATA'), '')] def trigger(settings, conditions): diff --git a/apps/crypto/manifest.json b/apps/crypto/manifest.json index 392e3ba..db1e5ba 100644 --- a/apps/crypto/manifest.json +++ b/apps/crypto/manifest.json @@ -1,5 +1,6 @@ { "id": "crypto", + "i18n": true, "name": "Crypto", "icon": "₿", "description": "Coin prices", diff --git a/apps/dashboard/app.py b/apps/dashboard/app.py index c557b96..41ae0fa 100644 --- a/apps/dashboard/app.py +++ b/apps/dashboard/app.py @@ -1,40 +1,44 @@ -"""Dashboard plugin: time + weather combined.""" +"""Dashboard plugin: time + current weather (via the shared weather helper).""" -def fetch(settings, format_lines, get_rows, get_cols): +def fetch(settings, format_lines, get_rows, get_cols, get_weather=None, i18n=None): from datetime import datetime import pytz - tz = pytz.timezone(settings.get('timezone', 'US/Eastern')) - dt = datetime.now(tz) - time_page = format_lines(dt.strftime("%A").upper(), - dt.strftime("%b %d %Y").upper(), - dt.strftime("%I:%M %p").upper()) - # Weather try: - import requests - api_key = settings.get('weather_api_key', '').strip() - if not api_key: - return [time_page, format_lines("NO WEATHER DATA", "", "CHECK API KEY")] - loc_lat = settings.get('location_lat', '') - loc_lon = settings.get('location_lon', '') - if loc_lat and loc_lon: - url = f"http://api.openweathermap.org/data/2.5/weather?lat={loc_lat}&lon={loc_lon}&appid={api_key}&units=imperial" - else: - zip_code = settings.get('zip_code', '02118').strip() - url = f"http://api.openweathermap.org/data/2.5/weather?zip={zip_code},us&appid={api_key}&units=imperial" - res = requests.get(url, timeout=5).json() - c = get_cols() - city = res['name'].upper() - temp = round(res['main']['temp']) - feels = round(res['main']['feels_like']) - desc = res['weather'][0]['main'].upper() - high = round(res['main']['temp_max']) - low = round(res['main']['temp_min']) - now_t = dt.strftime("%I:%M%p").lstrip("0") - mcl = c - 1 - len(now_t) - l1 = f"{city[:mcl]} {now_t}".center(c) - pfx = f"{temp}F ({feels}F) " - l2 = (pfx + desc[:c - len(pfx)]).center(c) - l3 = f"H:{high}F L:{low}F".center(c) - return [time_page, format_lines(l1, l2, l3)] - except Exception: - return [time_page, format_lines("WEATHER ERROR", "", "")] + tz = pytz.timezone(settings.get('timezone', 'US/Eastern')) + except pytz.UnknownTimeZoneError: + tz = pytz.timezone('US/Eastern') + dt = datetime.now(tz) + if i18n is not None: # localized names, locale date order, 24h outside English + weekday = i18n.weekday(dt) + date_line = i18n.date(dt, short=True, year=True) + time_str = i18n.time(dt) + else: + weekday = dt.strftime("%A").upper() + date_line = dt.strftime("%b %d %Y").upper() + time_str = dt.strftime("%I:%M %p").lstrip("0") + time_page = format_lines(weekday, date_line, time_str) + + # Weather comes from the companion's shared helper (global provider + key + + # location). With no helper (e.g. a bare host), just show the time. + if get_weather is None: + return [time_page] + w = get_weather() + if not w or not w.get('ok'): + return [time_page, format_lines("NO WEATHER", "DATA", "TRY LATER")] + + c = get_cols() + city = str(w.get('city') or 'LOCATION').upper() + temp = w.get('temp_f') + feels = w.get('feels_like_f') + desc = str(w.get('desc') or '').upper() + if i18n is not None: # translate shared-helper condition text where we can + desc = i18n.t(desc, "weather") + high = w.get('hi_f') + low = w.get('lo_f') + now_t = i18n.time(dt, ampm_space=False) if i18n is not None else dt.strftime("%I:%M%p").lstrip("0") + mcl = max(1, c - 1 - len(now_t)) + l1 = f"{city[:mcl]} {now_t}".center(c) + pfx = f"{temp}F ({feels}F) " + l2 = (pfx + desc[:max(0, c - len(pfx))]).center(c) + l3 = f"H:{high}F L:{low}F".center(c) + return [time_page, format_lines(l1, l2, l3)] diff --git a/apps/dashboard/manifest.json b/apps/dashboard/manifest.json index 3c199f6..c805925 100644 --- a/apps/dashboard/manifest.json +++ b/apps/dashboard/manifest.json @@ -1,5 +1,6 @@ { "id": "dashboard", + "i18n": true, "name": "Dashboard", "icon": "🏠", "description": "Time + weather", diff --git a/apps/date/app.py b/apps/date/app.py index 6a5e2d0..399f82f 100644 --- a/apps/date/app.py +++ b/apps/date/app.py @@ -1,11 +1,21 @@ -def fetch(settings, format_lines, get_rows, get_cols): +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): from datetime import datetime import pytz - tz = pytz.timezone(settings.get('timezone', 'US/Eastern')) + try: + tz = pytz.timezone(settings.get('timezone', 'US/Eastern')) + except pytz.UnknownTimeZoneError: + tz = pytz.timezone('US/Eastern') now = datetime.now(tz) - time_str = now.strftime('%I:%M %p') - month_day = now.strftime('%B %d') - weekday = now.strftime('%A') + # When the companion injects i18n, honor the global Language: localized weekday, + # locale-ordered date (9 JUILLET, not JUILLET 9), and 24h time outside English. + if i18n is not None: + time_str = i18n.time(now) + weekday = i18n.weekday(now) + month_day = i18n.date(now) + else: + time_str = now.strftime('%I:%M %p').lstrip('0') + weekday = now.strftime('%A').upper() + month_day = f"{now.strftime('%B').upper()} {now.day}" rows = get_rows() if rows == 2: return [format_lines(month_day, weekday)] diff --git a/apps/date/manifest.json b/apps/date/manifest.json index 1c16b09..d26f7f3 100644 --- a/apps/date/manifest.json +++ b/apps/date/manifest.json @@ -1,5 +1,6 @@ { "id": "date", + "i18n": true, "name": "Date", "icon": "📅", "description": "Full date view", diff --git a/apps/earthquakes/app.py b/apps/earthquakes/app.py new file mode 100644 index 0000000..9e35903 --- /dev/null +++ b/apps/earthquakes/app.py @@ -0,0 +1,57 @@ +"""Recent significant earthquakes worldwide (USGS FDSN, keyless).""" + + +def _wrap(text, cols, maxlines): + words, lines, cur = text.split(), [], '' + for w in words: + if len(cur) + len(w) + (1 if cur else 0) <= cols: + cur = f'{cur} {w}'.strip() + else: + lines.append(cur) + cur = w[:cols] + if len(lines) >= maxlines: + break + if cur and len(lines) < maxlines: + lines.append(cur) + return lines[:maxlines] or [''] + + +def fetch(settings, format_lines, get_rows, get_cols): + import requests + from datetime import datetime, timezone + rows, cols = get_rows(), get_cols() + try: + minmag = str(settings.get('min_magnitude', '4.5') or '4.5') + data = requests.get('https://earthquake.usgs.gov/fdsnws/event/1/query', + params={'format': 'geojson', 'orderby': 'time', 'limit': 5, + 'minmagnitude': minmag}, timeout=8).json() + feats = data.get('features', []) or [] + now = datetime.now(timezone.utc).timestamp() + pages = [] + for ft in feats[:5]: + p = ft.get('properties', {}) or {} + mag = p.get('mag') + place = str(p.get('place', '') or 'UNKNOWN').upper() + mags = f'M{mag:.1f}' if isinstance(mag, (int, float)) else 'M?' + ago = '' + ms = p.get('time') + if isinstance(ms, (int, float)): + mins = int((now - ms / 1000) / 60) + ago = f'{mins}M AGO' if mins < 120 else f'{mins // 60}H AGO' + # USGS place is like "134 KM E OF BITUNG, INDONESIA": show the distance + # on the header line and give the location name the remaining rows so + # it isn't cut off. + if ' OF ' in place: + dist, loc = place.split(' OF ', 1) + head = f'{mags} {dist.strip()}' + else: + loc, head = place, f'{mags} {ago}'.strip() + if rows == 1: + pages.append(f'{mags} {loc}'[:cols].center(cols)) + elif rows == 2: + pages.append(format_lines(head, *_wrap(loc, cols, 1))) + else: + pages.append(format_lines(head, *_wrap(loc, cols, rows - 1))) + return pages or [format_lines('EARTHQUAKES', 'NONE RECENT', '')] + except Exception: + return [format_lines('EARTHQUAKES', 'OFFLINE', '')] diff --git a/apps/earthquakes/manifest.json b/apps/earthquakes/manifest.json new file mode 100644 index 0000000..2c85559 --- /dev/null +++ b/apps/earthquakes/manifest.json @@ -0,0 +1,34 @@ +{ + "id": "earthquakes", + "name": "Earthquakes", + "icon": "🌎", + "description": "Recent significant earthquakes (USGS)", + "type": "functional", + "refresh_interval": 600, + "loop_delay": 6, + "category": "news", + "min_rows": 2, + "min_cols": 12, + "settings": [ + { + "key": "min_magnitude", + "label": "Minimum magnitude", + "type": "number", + "default": "4.5", + "min": "1", + "max": "8", + "step": "0.5", + "stepper": true + }, + { + "key": "loop_delay", + "label": "Delay Between Pages (seconds)", + "type": "number", + "default": "6", + "min": "2", + "max": "30", + "step": "1", + "stepper": true + } + ] +} diff --git a/apps/exchange-rates/app.py b/apps/exchange-rates/app.py new file mode 100644 index 0000000..830d9e2 --- /dev/null +++ b/apps/exchange-rates/app.py @@ -0,0 +1,39 @@ +"""Currency exchange rates via Frankfurter (European Central Bank data, keyless).""" + + +def fetch(settings, format_lines, get_rows, get_cols, i18n=None, get_location=None): + import requests + rows, cols = get_rows(), get_cols() + + # Base currency: an explicit setting wins; otherwise the configured LOCATION + # decides (a French speaker in Canada wants CAD, in Switzerland CHF — the + # language can't tell), falling back to the Language only if no location is set. + base = str(settings.get('base', '') or '').strip().upper()[:3] + if not base and get_location is not None: + base = str((get_location() or {}).get('currency') or '') + if not base: + base = i18n.base_currency() if i18n is not None else 'USD' + targets = [t.strip().upper()[:3] for t in str(settings.get('targets', 'EUR,GBP,JPY')).split(',') if t.strip()] + targets = targets[:8] or ['EUR', 'GBP', 'JPY'] + + # Rates use the locale's decimal/grouping (150,25 vs 150.25; 1.350 vs 1,350). + def n(v, d): + return i18n.number(v, d) if i18n is not None else f'{v:,.{d}f}' + + def fmt(v): + return n(v, 3) if v < 10 else (n(v, 2) if v < 1000 else n(v, 0)) + + try: + data = requests.get('https://api.frankfurter.app/latest', + params={'from': base, 'to': ','.join(targets)}, timeout=8).json() + rates = data.get('rates', {}) + if not rates: + return [format_lines('FX RATES', 'NO DATA', 'CHECK CODES')] + + pairs = [(t, rates[t]) for t in targets if t in rates] + if rows == 1: + return [f'1{base}={fmt(v)}{t}'.center(cols)[:cols] for t, v in pairs] + lines = [f'1 {base} ='] + [f'{t} {fmt(v)}' for t, v in pairs] + return [format_lines(*lines[i:i + rows]) for i in range(0, len(lines), rows)] + except Exception: + return [format_lines('FX RATES', 'OFFLINE', '')] diff --git a/apps/exchange-rates/manifest.json b/apps/exchange-rates/manifest.json new file mode 100644 index 0000000..3a5a410 --- /dev/null +++ b/apps/exchange-rates/manifest.json @@ -0,0 +1,41 @@ +{ + "id": "exchange-rates", + "i18n": true, + "name": "Exchange Rates", + "icon": "💱", + "description": "Live currency rates (ECB)", + "type": "functional", + "refresh_interval": 3600, + "loop_delay": 5, + "category": "finance", + "min_rows": 1, + "min_cols": 12, + "settings": [ + { + "key": "base", + "label": "Base currency (3-letter code)", + "type": "text", + "default": "", + "ph": "blank = follow Location", + "note": "Blank uses your Location's currency (then Language).", + "size": "sm" + }, + { + "key": "targets", + "label": "Show these currencies", + "type": "text", + "default": "EUR,GBP,JPY", + "ph": "comma-separated codes" + }, + { + "key": "loop_delay", + "label": "Delay Between Pages (seconds)", + "type": "number", + "default": "5", + "min": "2", + "max": "30", + "step": "1", + "stepper": true + } + ] +} diff --git a/apps/formula1/app.py b/apps/formula1/app.py new file mode 100644 index 0000000..52b26d9 --- /dev/null +++ b/apps/formula1/app.py @@ -0,0 +1,63 @@ +"""Formula 1 — next Grand Prix & championship leader (keyless: Jolpica / Ergast).""" + + +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): + import requests + from datetime import datetime, timezone + rows, cols = get_rows(), get_cols() + + def t(s, ctx="sports"): + return i18n.t(s, ctx) if i18n is not None else s + + def u(k): # localized D/H duration suffix + return i18n.unit(k) if i18n is not None else k + + pages = [] + try: + nxt = requests.get('https://api.jolpi.ca/ergast/f1/current/next.json', timeout=10).json() + races = nxt.get('MRData', {}).get('RaceTable', {}).get('Races', []) + if races: + r = races[0] + name = str(r.get('raceName', '')).upper().replace('GRAND PRIX', 'GP') + cd = '' + try: + dt = datetime.fromisoformat( + f"{r.get('date', '')}T{r.get('time', '00:00:00Z')}".replace('Z', '+00:00')) + secs = int((dt - datetime.now(timezone.utc)).total_seconds()) + if secs > 0: + d, h = secs // 86400, (secs % 86400) // 3600 + in_ = t('IN', 'time') + cd = f'{in_} {d}{u("D")} {h}{u("H")}' if d else f'{in_} {h}{u("H")}' + else: + cd = t('RACE WEEKEND') + except ValueError: + pass + if rows == 1: + pages.append(f'{name} {cd}'[:cols].center(cols)) + elif rows == 2: + pages.append(format_lines(t('NEXT GP'), name)) + pages.append(format_lines(name, cd)) + else: + pages.append(format_lines(t('NEXT GRAND PRIX'), name, cd)) + else: + pages.append(format_lines('FORMULA 1', t('SEASON'), t('OVER'))) + except Exception: + return [format_lines('FORMULA 1', t('OFFLINE'), '')] + + try: + st = requests.get('https://api.jolpi.ca/ergast/f1/current/driverStandings.json', timeout=10).json() + lists = st.get('MRData', {}).get('StandingsTable', {}).get('StandingsLists', []) + ds = lists[0].get('DriverStandings', []) if lists else [] + if ds: + top = ds[0] + nm = str(top.get('Driver', {}).get('familyName', '')).upper() + pts = top.get('points', '') + if rows == 1: + pages.append(f'{t("LEADER")} {nm} {pts}'[:cols].center(cols)) + elif rows == 2: + pages.append(format_lines(t('CHAMPIONSHIP'), f'{nm} {pts}{t("PTS")}')) + else: + pages.append(format_lines(t('LEADER'), nm, f'{pts} {t("POINTS")}')) + except Exception: + pass + return pages or [format_lines('FORMULA 1', t('NO DATA'), '')] diff --git a/apps/formula1/manifest.json b/apps/formula1/manifest.json new file mode 100644 index 0000000..beb74be --- /dev/null +++ b/apps/formula1/manifest.json @@ -0,0 +1,25 @@ +{ + "id": "formula1", + "name": "Formula 1", + "icon": "🏎️", + "description": "Next Grand Prix & championship leader", + "type": "functional", + "i18n": true, + "refresh_interval": 3600, + "loop_delay": 6, + "category": "sports", + "min_rows": 2, + "min_cols": 12, + "settings": [ + { + "key": "loop_delay", + "label": "Delay Between Pages (seconds)", + "type": "number", + "default": "6", + "min": "2", + "max": "30", + "step": "1", + "stepper": true + } + ] +} diff --git a/apps/holidays/app.py b/apps/holidays/app.py new file mode 100644 index 0000000..55bd666 --- /dev/null +++ b/apps/holidays/app.py @@ -0,0 +1,58 @@ +"""Upcoming public holidays for your location (keyless: Nager.Date). + +Which calendar to show is driven by the configured LOCATION (the language can't tell +France from Canada from Switzerland), down to the province/state: in Quebec you get +Quebec's holidays, not the other provinces'. Names use the source's native localName, +or a localized translation for the common holidays (so a French speaker in Quebec — +where the source only has English — still reads Fête du Travail, Action de grâce…). +An explicit Country setting overrides the location; the Language is the last resort. +""" + + +def fetch(settings, format_lines, get_rows, get_cols, i18n=None, get_location=None): + import requests + from datetime import datetime, date + rows, cols = get_rows(), get_cols() + + def t(s, ctx="time"): + return i18n.t(s, ctx) if i18n is not None else s + + loc = (get_location() or {}) if get_location is not None else {} + country = str(settings.get('country', '') or '').strip().upper()[:2] + if not country: + country = str(loc.get('country') or '') or (i18n.country() if i18n is not None else 'US') + # Province/state (e.g. CA-QC) — only trusted when it belongs to the country we're + # actually showing (an explicit Country setting can differ from the location). + subdivision = str(loc.get('subdivision') or '') + if subdivision and not subdivision.startswith(country): + subdivision = '' + try: + data = requests.get(f'https://date.nager.at/api/v3/NextPublicHolidays/{country}', timeout=8).json() + if not isinstance(data, list) or not data: + return [format_lines('HOLIDAYS', 'NONE FOUND', country)] + # Keep nationwide holidays + the ones for our own province/state; drop other + # regions' (so Quebec doesn't list British Columbia Day). + if subdivision: + data = [h for h in data if h.get('global') or subdivision in (h.get('counties') or [])] + pages, today = [], date.today() + for h in data[:4]: + localized = i18n.holiday(h.get('name')) if i18n is not None else None + name = str(localized or h.get('localName') or h.get('name') or '').upper() + cd = '' + try: + days = (datetime.strptime(h.get('date', ''), '%Y-%m-%d').date() - today).days + if days == 0: + cd = t('TODAY') + elif days > 0: + cd = f'{t("IN")} {days} {t("DAYS")}' + except ValueError: + pass + if rows == 1: + pages.append(f'{name} {cd}'[:cols].center(cols)) + elif rows == 2: + pages.append(format_lines(name, cd)) + else: + pages.append(format_lines(t('NEXT HOLIDAY', 'holidays'), name, cd)) + return pages or [format_lines('HOLIDAYS', 'NONE', '')] + except Exception: + return [format_lines('HOLIDAYS', 'OFFLINE', '')] diff --git a/apps/holidays/manifest.json b/apps/holidays/manifest.json new file mode 100644 index 0000000..b5d5c2a --- /dev/null +++ b/apps/holidays/manifest.json @@ -0,0 +1,34 @@ +{ + "id": "holidays", + "i18n": true, + "name": "Public Holidays", + "icon": "📅", + "description": "Upcoming public holidays", + "type": "functional", + "refresh_interval": 21600, + "loop_delay": 6, + "category": "data", + "min_rows": 2, + "min_cols": 12, + "settings": [ + { + "key": "country", + "label": "Country (2-letter code)", + "type": "text", + "default": "", + "size": "sm", + "ph": "blank = follow Location", + "note": "ISO country code, e.g. US, GB, DE, FR, CA. Blank follows your Location (then Language)." + }, + { + "key": "loop_delay", + "label": "Delay Between Pages (seconds)", + "type": "number", + "default": "6", + "min": "2", + "max": "30", + "step": "1", + "stepper": true + } + ] +} diff --git a/apps/iss/app.py b/apps/iss/app.py index 5fe9be9..0a03449 100644 --- a/apps/iss/app.py +++ b/apps/iss/app.py @@ -1,5 +1,9 @@ -def fetch(settings, format_lines, get_rows, get_cols): +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): import requests + + def t(s): + return i18n.t(s, "space") if i18n is not None else s + try: pos = requests.get('http://api.open-notify.org/iss-now.json', timeout=10).json() ppl = requests.get('http://api.open-notify.org/astros.json', timeout=10).json() @@ -11,9 +15,9 @@ def fetch(settings, format_lines, get_rows, get_cols): return [format_lines(f'ISS LAT{lat} LON{lon}')] if rows == 2: return [format_lines('ISS TRACKER', f'LAT{lat} LON{lon}')] - return [format_lines('ISS TRACKER', f'LAT {lat} LON {lon}', f'{num} IN SPACE')] + return [format_lines('ISS TRACKER', f'LAT {lat} LON {lon}', f'{num} ' + t('IN SPACE'))] except Exception: - return [format_lines('ISS TRACKER', 'ERROR', 'API FAIL')] + return [format_lines('ISS TRACKER', t('ERROR'), t('API FAIL'))] def trigger(settings, conditions): diff --git a/apps/iss/manifest.json b/apps/iss/manifest.json index beca0ad..6095585 100644 --- a/apps/iss/manifest.json +++ b/apps/iss/manifest.json @@ -1,5 +1,6 @@ { "id": "iss", + "i18n": true, "name": "ISS Tracker", "icon": "🛸", "description": "Space station", diff --git a/apps/metals/app.py b/apps/metals/app.py new file mode 100644 index 0000000..86bb714 --- /dev/null +++ b/apps/metals/app.py @@ -0,0 +1,64 @@ +"""Precious metal spot prices, USD per troy ounce (keyless: gold-api.com).""" + + +def fetch(settings, format_lines, get_rows, get_cols, i18n=None, get_location=None): + import requests + rows, cols = get_rows(), get_cols() + + def t(s): + return i18n.t(s, "metals") if i18n is not None else s + + def n(v, d): # locale decimal/grouping (2,345 vs 2.345 vs 2 345) + return i18n.number(v, d) if i18n is not None else f'{v:,.{d}f}' + + # gold-api.com quotes in USD/oz; show the local currency (Location -> Language -> + # USD) by converting through a keyless ECB rate. Falls back to USD if no rate. + loc = get_location() if get_location is not None else None + ccy = loc.get('currency') if isinstance(loc, dict) and loc.get('ok') else None + if not ccy and i18n is not None: + ccy = i18n.base_currency() + ccy = (ccy or 'USD').upper() + rate = 1.0 + if ccy != 'USD': + try: + rate = float(requests.get('https://api.frankfurter.app/latest', + params={'from': 'USD', 'to': ccy}, timeout=8).json()['rates'][ccy]) + except Exception: + ccy, rate = 'USD', 1.0 + # With i18n the display can render € £ ¥ (Windows-1252); without it the modules + # are the basic charset, so use '$' for USD and the ASCII ISO code otherwise. + cur_sym = i18n.currency_symbol(ccy) if i18n is not None else ('$' if ccy == 'USD' else ccy) + sep = '' if cur_sym != ccy else ' ' + + def price(sym): + try: + return requests.get(f'https://api.gold-api.com/price/{sym}', timeout=8).json().get('price') + except Exception: + return None + + def fmt(p): + if not isinstance(p, (int, float)): + return '--' + p = p * rate + body = n(p, 0) if p >= 100 else n(p, 2) + return f'{cur_sym}{sep}{body}' + + try: + gold, silver = price('XAU'), price('XAG') + if gold is None and silver is None: + return [format_lines('METALS', t('OFFLINE'), '')] + # Localized names vary in length — pad both to the same width so they align. + g, s = t('GOLD'), t('SILVER') + w = max(len(g), len(s)) + if rows == 1: + pages = [] + if gold is not None: + pages.append(f'{g} {fmt(gold)}/OZ'[:cols].center(cols)) + if silver is not None: + pages.append(f'{s} {fmt(silver)}/OZ'[:cols].center(cols)) + return pages + if rows == 2: + return [format_lines(f'{g:<{w}} {fmt(gold)}', f'{s:<{w}} {fmt(silver)}')] + return [format_lines(f'{t("SPOT PRICE")} /OZ', f'{g:<{w}} {fmt(gold)}', f'{s:<{w}} {fmt(silver)}')] + except Exception: + return [format_lines('METALS', t('OFFLINE'), '')] diff --git a/apps/metals/manifest.json b/apps/metals/manifest.json new file mode 100644 index 0000000..920c9fb --- /dev/null +++ b/apps/metals/manifest.json @@ -0,0 +1,25 @@ +{ + "id": "metals", + "i18n": true, + "name": "Precious Metals", + "icon": "🥇", + "description": "Gold & silver spot prices", + "type": "functional", + "refresh_interval": 900, + "loop_delay": 5, + "category": "finance", + "min_rows": 1, + "min_cols": 12, + "settings": [ + { + "key": "loop_delay", + "label": "Delay Between Pages (seconds)", + "type": "number", + "default": "5", + "min": "2", + "max": "30", + "step": "1", + "stepper": true + } + ] +} diff --git a/apps/moon-phase/app.py b/apps/moon-phase/app.py index 1fed80d..49d1d16 100644 --- a/apps/moon-phase/app.py +++ b/apps/moon-phase/app.py @@ -1,9 +1,18 @@ -def fetch(settings, format_lines, get_rows, get_cols): +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): from datetime import datetime import pytz import math - tz = pytz.timezone(settings.get('timezone', 'US/Eastern')) + def t(s): + return i18n.t(s, "moon") if i18n is not None else s + + def u(k): # localized D/H/M/S suffix (French J for jour, etc.) + return i18n.unit(k) if i18n is not None else k + + try: + tz = pytz.timezone(settings.get('timezone', 'US/Eastern')) + except pytz.UnknownTimeZoneError: + tz = pytz.timezone('US/Eastern') now = datetime.now(tz) # Known new moon: January 6, 2000 18:14 UTC @@ -38,9 +47,10 @@ def fetch(settings, format_lines, get_rows, get_cols): filled = int(illumination * cols) bar = 'w' * filled + ' ' * (cols - filled) + name = t(phase_name) pages = [ - format_lines(phase_name, f'{illum_pct}% LIT', f'FULL IN {int(days_to_full)}D'), - format_lines(phase_name, bar, f'NEW IN {int(days_to_new)}D'), + format_lines(name, f'{illum_pct}% {t("LIT")}', f'{t("FULL IN")} {int(days_to_full)}{u("D")}'), + format_lines(name, bar, f'{t("NEW IN")} {int(days_to_new)}{u("D")}'), ] return pages diff --git a/apps/moon-phase/manifest.json b/apps/moon-phase/manifest.json index 39c6ae2..2a5fcac 100644 --- a/apps/moon-phase/manifest.json +++ b/apps/moon-phase/manifest.json @@ -1,5 +1,6 @@ { "id": "moon-phase", + "i18n": true, "name": "Moon Phase", "icon": "🌙", "description": "Current moon phase", diff --git a/apps/quote/app.py b/apps/quote/app.py new file mode 100644 index 0000000..1dd272d --- /dev/null +++ b/apps/quote/app.py @@ -0,0 +1,127 @@ +"""An inspirational quote (keyless: DummyJSON quotes).""" + + +def _need_lines(lens, cols): + need, cur = 1, 0 + for wl in lens: + add = wl if cur == 0 else cur + 1 + wl + if add <= cols: + cur = add + else: + need += 1 + cur = wl + return need + + +def _balance(words, lens, cols, k): + """Split words into exactly k lines (each <= cols) minimizing raggedness, so + lines fill evenly instead of orphaning the last word; prefers ending a line at + sentence punctuation. A tiny DP.""" + n = len(words) + pre = [0] + for wl in lens: + pre.append(pre[-1] + wl) + + def linelen(i, j): + return pre[j + 1] - pre[i] + (j - i) + + INF = float("inf") + dp = [[INF] * (n + 1) for _ in range(k + 1)] + nxt = [[0] * (n + 1) for _ in range(k + 1)] + dp[0][n] = 0.0 + for kk in range(1, k + 1): + for i in range(n - 1, -1, -1): + j = i + while j < n: + ll = linelen(i, j) + if ll > cols and j > i: + break + rest = dp[kk - 1][j + 1] + if rest < INF: + slack = cols - ll + cost = slack * slack + rest + if words[j][-1:] in ".!?": + cost -= cols + if cost < dp[kk][i]: + dp[kk][i] = cost + nxt[kk][i] = j + 1 + j += 1 + if dp[k][0] >= INF: + return None + out, i, kk = [], 0, k + while kk > 0: + j = nxt[kk][i] + out.append(" ".join(words[i:j])) + i, kk = j, kk - 1 + return out + + +def _greedy(words, cols): + lines, cur = [], '' + for w in words: + w = w if len(w) <= cols else w[:cols] + if len(cur) + len(w) + (1 if cur else 0) <= cols: + cur = f'{cur} {w}'.strip() + else: + lines.append(cur) + cur = w + if cur: + lines.append(cur) + return lines or [''] + + +def _pages(format_lines, title, text, rows, cols): + """Lay the text out under a title. When it fits on one page the words are + balanced evenly across the lines it needs; longer text word-wraps and + paginates.""" + words = text.split() or [''] + lens = [len(w) for w in words] + if rows == 1: + return [ln.center(cols)[:cols] for ln in _greedy(words, cols)] + body = rows - 1 if title else rows + if max(lens) <= cols: + need = _need_lines(lens, cols) + if need <= body: + bal = _balance(words, lens, cols, need) + if bal is not None: + return [format_lines(title, *bal)] if title else [format_lines(*bal)] + lines = _greedy(words, cols) + pages, i = ([format_lines(title, *lines[:body])], body) if title else ([], 0) + while i < len(lines): + pages.append(format_lines(*lines[i:i + rows])) + i += rows + return pages + +def fetch(settings, format_lines, get_rows, get_cols): + import requests + rows, cols = get_rows(), get_cols() + try: + max_len = int(float(settings.get('max_length', '150') or 150)) + except (TypeError, ValueError): + max_len = 150 + max_len = max(40, min(300, max_len)) + + def one(): + d = requests.get('https://dummyjson.com/quotes/random', timeout=8).json() + return str(d.get('quote', '') or '').strip(), str(d.get('author', '') or '').strip() + + try: + # The API can't filter by length, so try a few times for a short quote, + # keeping the shortest seen if none come in under the limit. + best = None + for _ in range(3): + q, a = one() + if not q: + continue + if len(q) <= max_len: + best = (q, a) + break + if best is None or len(q) < len(best[0]): + best = (q, a) + if not best: + return [format_lines('QUOTE', 'NO DATA', '')] + q, a = best + text = f'{q.upper()} - {a.upper()}' if a else q.upper() + return _pages(format_lines, '', f'QUOTE: {text}', rows, cols) + except Exception: + return [format_lines('QUOTE', 'OFFLINE', '')] diff --git a/apps/quote/manifest.json b/apps/quote/manifest.json new file mode 100644 index 0000000..2df8f7d --- /dev/null +++ b/apps/quote/manifest.json @@ -0,0 +1,44 @@ +{ + "id": "quote", + "name": "Quote", + "icon": "💬", + "description": "An inspirational quote", + "type": "functional", + "refresh_interval": 1800, + "loop_delay": 7, + "category": "entertainment", + "min_rows": 3, + "min_cols": 15, + "settings": [ + { + "key": "max_length", + "label": "Max quote length (characters)", + "type": "number", + "default": "150", + "min": "40", + "max": "300", + "step": "10", + "stepper": true + }, + { + "key": "refresh_minutes", + "label": "Fetch a new quote every (minutes)", + "type": "number", + "default": "30", + "min": "1", + "max": "1440", + "step": "1", + "stepper": true + }, + { + "key": "loop_delay", + "label": "Delay Between Pages (seconds)", + "type": "number", + "default": "7", + "min": "2", + "max": "30", + "step": "1", + "stepper": true + } + ] +} diff --git a/apps/registry.json b/apps/registry.json index 76d7208..459ca77 100644 --- a/apps/registry.json +++ b/apps/registry.json @@ -225,6 +225,141 @@ "category": "data", "type": "functional", "version": "1.0" + }, + { + "id": "advice", + "name": "Advice", + "icon": "🧭", + "description": "A random piece of advice", + "category": "entertainment", + "type": "functional", + "version": "1.0" + }, + { + "id": "aurora", + "name": "Aurora Watch", + "icon": "🌌", + "description": "Geomagnetic activity (Kp index)", + "category": "data", + "type": "functional", + "version": "1.0" + }, + { + "id": "cat-facts", + "name": "Cat Facts", + "icon": "🐱", + "description": "A concise cat fact", + "category": "entertainment", + "type": "functional", + "version": "1.0" + }, + { + "id": "earthquakes", + "name": "Earthquakes", + "icon": "🌎", + "description": "Recent significant earthquakes (USGS)", + "category": "news", + "type": "functional", + "version": "1.0" + }, + { + "id": "exchange-rates", + "name": "Exchange Rates", + "icon": "💱", + "description": "Live currency rates (ECB)", + "category": "finance", + "type": "functional", + "version": "1.0" + }, + { + "id": "formula1", + "name": "Formula 1", + "icon": "🏎️", + "description": "Next Grand Prix & championship leader", + "category": "sports", + "type": "functional", + "version": "1.0" + }, + { + "id": "holidays", + "name": "Public Holidays", + "icon": "📅", + "description": "Upcoming public holidays", + "category": "data", + "type": "functional", + "version": "1.0" + }, + { + "id": "metals", + "name": "Precious Metals", + "icon": "🥇", + "description": "Gold & silver spot prices", + "category": "finance", + "type": "functional", + "version": "1.0" + }, + { + "id": "quote", + "name": "Quote", + "icon": "💬", + "description": "An inspirational quote", + "category": "entertainment", + "type": "functional", + "version": "1.0" + }, + { + "id": "rocket-launch", + "name": "Rocket Launch", + "icon": "🚀", + "description": "Next orbital rocket launch", + "category": "data", + "type": "functional", + "version": "1.0" + }, + { + "id": "sarcastic-fortune-cookies", + "name": "Sarcastic Fortune Cookies", + "icon": "🥠", + "description": "Cynical fortune-cookie wisdom", + "category": "entertainment", + "type": "functional", + "version": "1.0" + }, + { + "id": "sun-times", + "name": "Sun Times", + "icon": "☀️", + "description": "Sunrise, sunset & day length", + "category": "data", + "type": "functional", + "version": "1.0" + }, + { + "id": "tides", + "name": "Tides", + "icon": "🌊", + "description": "Today's high & low tides (NOAA)", + "category": "data", + "type": "functional", + "version": "1.0" + }, + { + "id": "useless-fact", + "name": "Random Fact", + "icon": "🤓", + "description": "A concise random fact", + "category": "entertainment", + "type": "functional", + "version": "1.0" + }, + { + "id": "wiki-today", + "name": "Wikipedia Today", + "icon": "📚", + "description": "Featured article & most-read on Wikipedia", + "category": "news", + "type": "functional", + "version": "1.0" } ] } diff --git a/apps/rocket-launch/app.py b/apps/rocket-launch/app.py new file mode 100644 index 0000000..33d6070 --- /dev/null +++ b/apps/rocket-launch/app.py @@ -0,0 +1,48 @@ +"""Next orbital rocket launch (Launch Library 2 / The Space Devs, keyless).""" + + +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): + import requests + from datetime import datetime, timezone + rows, cols = get_rows(), get_cols() + + def t(s, ctx="space"): + return i18n.t(s, ctx) if i18n is not None else s + + def u(k): # localized D/H/M duration suffix + return i18n.unit(k) if i18n is not None else k + + try: + data = requests.get('https://ll.thespacedevs.com/2.2.0/launch/upcoming/', + params={'limit': 1, 'mode': 'list'}, timeout=10).json() + res = data.get('results') or [] + if not res: + return [format_lines(t('NEXT LAUNCH'), t('NONE'), t('SCHEDULED'))] + r = res[0] + name = str(r.get('name', '')).upper() + rocket, _, mission = name.partition('|') + rocket = rocket.strip() or 'ROCKET' + mission = mission.strip() or rocket + cd = '' + net = r.get('net') + if net: + try: + dt = datetime.fromisoformat(str(net).replace('Z', '+00:00')) + secs = int((dt - datetime.now(timezone.utc)).total_seconds()) + if secs <= 0: + cd = t('IMMINENT') + else: + d, h, m = secs // 86400, (secs % 86400) // 3600, (secs % 3600) // 60 + in_ = t('IN', 'time') + cd = (f'{in_} {d}{u("D")} {h}{u("H")}' if d + else (f'{in_} {h}{u("H")} {m}{u("M")}' if h + else f'{in_} {m}{u("M")}')) + except ValueError: + cd = '' + if rows == 1: + return [f'{rocket} {cd}'[:cols].center(cols)] + if rows == 2: + return [format_lines(t('NEXT LAUNCH'), rocket), format_lines(mission, cd)] + return [format_lines(t('NEXT LAUNCH'), rocket, cd), format_lines(t('MISSION'), mission, cd)] + except Exception: + return [format_lines(t('NEXT LAUNCH'), t('OFFLINE'), '')] diff --git a/apps/rocket-launch/manifest.json b/apps/rocket-launch/manifest.json new file mode 100644 index 0000000..fefbf8c --- /dev/null +++ b/apps/rocket-launch/manifest.json @@ -0,0 +1,25 @@ +{ + "id": "rocket-launch", + "name": "Rocket Launch", + "icon": "🚀", + "description": "Next orbital rocket launch", + "type": "functional", + "i18n": true, + "refresh_interval": 900, + "loop_delay": 6, + "category": "data", + "min_rows": 2, + "min_cols": 12, + "settings": [ + { + "key": "loop_delay", + "label": "Delay Between Pages (seconds)", + "type": "number", + "default": "6", + "min": "2", + "max": "30", + "step": "1", + "stepper": true + } + ] +} diff --git a/apps/sarcastic-fortune-cookies/app.py b/apps/sarcastic-fortune-cookies/app.py new file mode 100644 index 0000000..36188e5 --- /dev/null +++ b/apps/sarcastic-fortune-cookies/app.py @@ -0,0 +1,153 @@ +""" +Sarcastic Fortune Cookies — a functional Split-Flap plugin. + +Shows a random tongue-in-cheek "fortune cookie" one-liner and swaps it for a new +random one on a chosen interval. Fortunes ship in several languages, one bundled +``fortunes_.json`` file each; the language follows the global Language +setting, falling back to English when there's no file for it. Accented fortunes +keep their Windows-1252 accents (É, Ü, ß, Œ, …) — the gateway and modules render +the cp1252 code page directly, so nothing is stripped. + +Drop-in compatible with the splitflap-os plugin ABI: + fetch(settings, format_lines, get_rows, get_cols) -> list[str] +""" + +import json +import os +import random +import time + +_HERE = os.path.dirname(os.path.abspath(__file__)) + + +def _load(lang): + """Load + cache the fortune list for a language (read once per process).""" + cache = getattr(fetch, "_data", None) + if cache is None: + cache = {} + fetch._data = cache + if lang not in cache: + path = os.path.join(_HERE, "fortunes_%s.json" % lang) + entries = [] + # Bundled files are UTF-8; fall back to cp1252 in case a file is replaced + # with a Windows-1252 one. + for enc in ("utf-8", "cp1252"): + try: + with open(path, encoding=enc) as fh: + entries = json.load(fh) + break + except (UnicodeDecodeError, FileNotFoundError, ValueError, OSError): + continue + cache[lang] = [e["fortune"] for e in entries + if isinstance(e, dict) and e.get("fortune")] + return cache[lang] + + +def _hard_wrap(words, rows, cols): + flat = " ".join(words) + return [flat[i:i + cols] for i in range(0, len(flat), cols)][:rows] + + +def _balance(words, lens, cols, k): + """Split ``words`` into exactly ``k`` lines (each <= cols), minimizing + raggedness (sum of squared trailing slack) so the lines are evenly filled + instead of packing the top and orphaning the last word — nudged toward + ending a line at sentence punctuation. A short DP; fortunes are tiny.""" + n = len(words) + pre = [0] + for wl in lens: + pre.append(pre[-1] + wl) + + def linelen(i, j): # words[i..j] inclusive, with spaces + return pre[j + 1] - pre[i] + (j - i) + + INF = float("inf") + dp = [[INF] * (n + 1) for _ in range(k + 1)] + nxt = [[0] * (n + 1) for _ in range(k + 1)] + dp[0][n] = 0.0 + for kk in range(1, k + 1): + for i in range(n - 1, -1, -1): + j = i + while j < n: + ll = linelen(i, j) + if ll > cols and j > i: + break + rest = dp[kk - 1][j + 1] + if rest < INF: + slack = cols - ll + cost = slack * slack + rest + if words[j][-1:] in ".!?": + cost -= cols # prefer breaking after a sentence + if cost < dp[kk][i]: + dp[kk][i] = cost + nxt[kk][i] = j + 1 + j += 1 + if dp[k][0] >= INF: + return _hard_wrap(words, k, cols) + out, i, kk = [], 0, k + while kk > 0: + j = nxt[kk][i] + out.append(" ".join(words[i:j])) + i, kk = j, kk - 1 + return out + + +def _wrap(text, rows, cols): + """Fit ``text`` into at most ``rows`` lines of ``cols`` characters, balanced. + + When the text fits on the page, its words are spread evenly across the lines + it needs (not greedily packed, which leaves a lonely last word); only if even + the tightest word-wrap needs more rows than the board has do we fall back to a + hard character wrap. An accented letter counts as one character (one module). + """ + words = text.split() + if not words: + return [] + lens = [len(w) for w in words] + if max(lens) > cols: + return _hard_wrap(words, rows, cols) + # Fewest lines a word-wrap needs. + need, cur = 1, 0 + for wl in lens: + add = wl if cur == 0 else cur + 1 + wl + if add <= cols: + cur = add + else: + need += 1 + cur = wl + if need > rows: + return _hard_wrap(words, rows, cols) + return _balance(words, lens, cols, need) + + +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): + rows, cols = get_rows(), get_cols() + + # Language follows the global Language (or a per-app override, via i18n); use + # it when we bundle fortunes for it, otherwise fall back to English. Off a + # companion host (i18n is None) we read the raw setting. + lang = str((i18n.lang if i18n is not None else settings.get("language")) or "en").lower() + if not _load(lang): + lang = "en" + try: + every = int(settings.get("frequency") or 300) + except (ValueError, TypeError): + every = 300 + + now = time.time() + state = getattr(fetch, "_state", None) + # Pick a new fortune on first run, when the chosen interval elapses, or when + # the language changes (so a language switch shows on the next tick). + if (state is None + or state.get("lang") != lang + or not state.get("pages") + or (now - state.get("at", 0)) >= every): + fortunes = _load(lang) + text = random.choice(fortunes) if fortunes else "NO FORTUNES FOUND" + lines = _wrap(text, rows, cols) + # Vertically centre when the board has spare rows (format_lines centres + # each line horizontally and pads the remaining rows at the bottom). + top = (rows - len(lines)) // 2 + page = format_lines(*([""] * top + lines)) + fetch._state = {"at": now, "lang": lang, "pages": [page]} + return fetch._state["pages"] diff --git a/apps/sarcastic-fortune-cookies/fortunes_ca.json b/apps/sarcastic-fortune-cookies/fortunes_ca.json new file mode 100644 index 0000000..3f6469e --- /dev/null +++ b/apps/sarcastic-fortune-cookies/fortunes_ca.json @@ -0,0 +1,402 @@ +[ + { + "id": 1, + "fortune": "TOT PASSA PER UNA RA. NORMALMENT DOLENTA" + }, + { + "id": 2, + "fortune": "LA SORT ARRIBA. CAMINA MOLT A POC A POC" + }, + { + "id": 3, + "fortune": "LUNIVERS T UN PLA PER TU. UN DE PETIT" + }, + { + "id": 4, + "fortune": "EL TEU DEST HA TRUCAT. HO AJORNA" + }, + { + "id": 5, + "fortune": "ELS ASTRES SALINEAREN. DESPRS ET VEIEREN" + }, + { + "id": 6, + "fortune": "EL TEU VAIXELL ARRIBA. S UNA CANOA" + }, + { + "id": 7, + "fortune": "CONFIA EN EL PROCS. ELL DUBTA DE TU" + }, + { + "id": 8, + "fortune": "UN SENYAL ARRIBA. DIU CEDIU EL PAS" + }, + { + "id": 9, + "fortune": "EL TEU ESTEL EXISTEIX. EN UNA ALTRA GALAXIA" + }, + { + "id": 10, + "fortune": "EL KARMA S REAL. AIX EXPLICA MOLT" + }, + { + "id": 11, + "fortune": "EL TEU FUTUR S BRILLANT. POSAT CREMA" + }, + { + "id": 12, + "fortune": "EL DEST ESPERA. SHA POSAT A LLEGIR" + }, + { + "id": 13, + "fortune": "ELS MIRACLES PASSEN CADA DIA. LLUNY DAQU" + }, + { + "id": 14, + "fortune": "EL TEU MOMENT ARRIBA. S AL TRNSIT" + }, + { + "id": 15, + "fortune": "LA SORT VA TRUCAR. TU HAVIES SORTIT" + }, + { + "id": 16, + "fortune": "LA MAREA GIRA. A VELOCITAT DE GLACERA" + }, + { + "id": 17, + "fortune": "ARRIBEN GRANS COSES. ABAIXA EL LLIST" + }, + { + "id": 18, + "fortune": "LES OCASIONS HI SN. SEMPRE US PERDEU" + }, + { + "id": 19, + "fortune": "LUNIVERS TESCOLTA. FES CUA SI US PLAU" + }, + { + "id": 20, + "fortune": "LES TEVES PREGRIES SHAN SENTIT. I ARXIVAT" + }, + { + "id": 21, + "fortune": "LABUNDNCIA FLUEIX CAP A TU. RIU AMUNT" + }, + { + "id": 22, + "fortune": "LA RODA GIRA. TU ETS EL HMSTER" + }, + { + "id": 23, + "fortune": "CADA NVOL T UN COSTAT BO. EL TEU GOTEJA" + }, + { + "id": 24, + "fortune": "LATZAR PREGUNTA PER TU. PER CURIOSITAT" + }, + { + "id": 25, + "fortune": "ELS PRESAGIS PARLEN. MS AVIAT SOSPIREN" + }, + { + "id": 26, + "fortune": "EL TEU HORSCOP S BO. I FICTICI" + }, + { + "id": 27, + "fortune": "EL LLAMP NO CAU DOS COPS. TU S" + }, + { + "id": 28, + "fortune": "EL DEST PREMIA LA PACINCIA. FACTURA DESPRS" + }, + { + "id": 29, + "fortune": "LA LLUNA VA VEURE EL TEU NOU INICI. UN ALTRE" + }, + { + "id": 30, + "fortune": "EL TEU NGEL ET VETLLA. VA PRENENT NOTES" + }, + { + "id": 31, + "fortune": "CREU I REBRS. EL LLIURAMENT VARIA" + }, + { + "id": 32, + "fortune": "LA TEVA ERA DAUR ARRIBA. XAPADA EN OR" + }, + { + "id": 33, + "fortune": "EL QUE S TEU ARRIBA. SENSE CAP PRESSA" + }, + { + "id": 34, + "fortune": "LUNIVERS ET VA FER LULLET. O TENIA UN TIC" + }, + { + "id": 35, + "fortune": "LA TEVA SORT CANVIAR. RUMB INCERT" + }, + { + "id": 36, + "fortune": "LA PROVIDNCIA PROVEEIX. REVISA ELS TIQUETS" + }, + { + "id": 37, + "fortune": "DEMANA UN DESIG. LESTEL EL MARCA SPAM" + }, + { + "id": 38, + "fortune": "TOT ARRIBA AL SEU TEMPS. EL TEU TRIGA" + }, + { + "id": 39, + "fortune": "ELS SIGNES HI EREN. LLEGIR S OPCIONAL" + }, + { + "id": 40, + "fortune": "ETS NIC. COM TOTHOM" + }, + { + "id": 41, + "fortune": "EL MILLOR EST PER VENIR. NO T CAP PRESSA" + }, + { + "id": 42, + "fortune": "LA TEVA AURA BRILLA. FA HORES EXTRES" + }, + { + "id": 43, + "fortune": "LA SORT ET VA SOMRIURE. CORTESA. DE LLUNY" + }, + { + "id": 44, + "fortune": "EL TEU NGEL VA DEMANAR TRASLLAT" + }, + { + "id": 45, + "fortune": "EL DEST REPARTEIX. TU PERDS LES CARTES" + }, + { + "id": 46, + "fortune": "UN PRESAGI APAREIX. SARRONSA DESPATLLES" + }, + { + "id": 47, + "fortune": "LXIT S A PROP. I EL BOT DE POSPOSAR" + }, + { + "id": 48, + "fortune": "EL TREBALL SEMPRE PAGA. EN EXPERINCIA" + }, + { + "id": 49, + "fortune": "EL TEU POTENCIAL S IMMENS. I SENSE S" + }, + { + "id": 50, + "fortune": "APUNTA A LA LLUNA. VIGILA EL SOSTRE" + }, + { + "id": 51, + "fortune": "QUI MATINA VA VEURE EL CUC. TU DORMIES" + }, + { + "id": 52, + "fortune": "LA TEVA GRAN OCASI ARRIBA. NO LA TRENQUIS" + }, + { + "id": 53, + "fortune": "ROMA NO ES VA FER EN UN DIA. LA TEVA TAMPOC" + }, + { + "id": 54, + "fortune": "ELS TEUS TALENTS ES TROBARAN. AMB ORDRE" + }, + { + "id": 55, + "fortune": "QUI GUANYA NO ES RENDEIX. TU SURES" + }, + { + "id": 56, + "fortune": "LES TEVES IDEES VAN DAVANT. ELS TERMINIS NO" + }, + { + "id": 57, + "fortune": "LA QUALITAT S UN HBIT. EL TEU TAMB" + }, + { + "id": 58, + "fortune": "EL TEU TREBALL PARLA SOL. BEN FLUIXET" + }, + { + "id": 59, + "fortune": "EL CIM S SOLITARI. PROBLEMA RESOLT" + }, + { + "id": 60, + "fortune": "UN ASCENS ARRIBA. EL DUN COMPANY" + }, + { + "id": 61, + "fortune": "EL TEU CV IMPRESSIONA. ALS FANS DE FICCI" + }, + { + "id": 62, + "fortune": "LA TEVA MILLOR FEINA ARRIBA. CORRE DE PRESSA" + }, + { + "id": 63, + "fortune": "AQUELLA REUNI PODIA SER UN CORREU" + }, + { + "id": 64, + "fortune": "LA TEVA SAFATA NO S ENEMIGA. NOMS TESTIMONI" + }, + { + "id": 65, + "fortune": "ELS TERMINIS NO TESPANTEN. ELS IGNORES" + }, + { + "id": 66, + "fortune": "EL TEU PLA A CINC ANYS T CINC ANYS" + }, + { + "id": 67, + "fortune": "LENT PER SEGUR GUANYA. DOMINES EL LENT" + }, + { + "id": 68, + "fortune": "LA TEVA AGENDA S PLENA. DE MARGES" + }, + { + "id": 69, + "fortune": "EL TEU EQUIP TAMB SOMIA. AMB EL TRASLLAT" + }, + { + "id": 70, + "fortune": "LOCASI VA BAIXAR AL TEU NIVELL" + }, + { + "id": 71, + "fortune": "LXIT DEIXA PISTES. TU PREFEREIXES EL MISTERI" + }, + { + "id": 72, + "fortune": "LA TEVA ZONA DE CONFORT T CODI POSTAL" + }, + { + "id": 73, + "fortune": "QUALITAT O QUANTITAT. LES REBUTGES TOTES DUES" + }, + { + "id": 74, + "fortune": "EL TEU INSTINT S FI. I SOVINT SEQUIVOCA" + }, + { + "id": 75, + "fortune": "TREBALLES B SOTA PRESSI. ELLA NO HI EST" + }, + { + "id": 76, + "fortune": "LA MESTRIA DEMANA 10000 HORES. EN TENS 40" + }, + { + "id": 77, + "fortune": "LA TEVA PLANTA CREIA EN TU. I EN LAIGUA" + }, + { + "id": 78, + "fortune": "LA IMPRESSORA NOTA LA PRESSA. PER AIX FALLA" + }, + { + "id": 79, + "fortune": "EL TEU LLEGAT EST EN OBRES. ENCARA" + }, + { + "id": 80, + "fortune": "UN DIPLOMA TESPERA. EL DASSISTNCIA" + }, + { + "id": 81, + "fortune": "FER XARXA OBRE PORTES. TRUCAR TAMB" + }, + { + "id": 82, + "fortune": "DNES EXEMPLE. ELS RH EN VOLEN UN ALTRE" + }, + { + "id": 83, + "fortune": "EL TEU PROJECTE DESCANSA AMB LA RESTA" + }, + { + "id": 84, + "fortune": "EL GENI S UN PER CENT SUOR. TU PARES AQU" + }, + { + "id": 85, + "fortune": "EL TEU MISSATGE DABSNCIA S UNA OBRA MESTRA" + }, + { + "id": 86, + "fortune": "FALLAR S UN ESGLA. QUINA ESCALA" + }, + { + "id": 87, + "fortune": "ETS INSUBSTITUBLE. LANUNCI JA EST FET" + }, + { + "id": 88, + "fortune": "LA TEVA AVALUACI ANUAL ARRIBA. ASSAJA B" + }, + { + "id": 89, + "fortune": "EFICINCIA S EL TEU SEGON NOM. AL PAPER" + }, + { + "id": 90, + "fortune": "CAUS DE PEUS. LATERRATGE SHA RETARDAT" + }, + { + "id": 91, + "fortune": "EL CIM S A PROP. EL CAMPAMENT BASE TAMB" + }, + { + "id": 92, + "fortune": "LA TEVA AMBICI ES DESPERTA. EL RELLOTGE NO" + }, + { + "id": 93, + "fortune": "SIGUES TU MATEIX. ELS ALTRES SHAN AMONAT" + }, + { + "id": 94, + "fortune": "LA TEVA REUNI VA ANAR B. JA SHA ACABAT" + }, + { + "id": 95, + "fortune": "DESCANSAR S SA. HO PRACTIQUES MOLT" + }, + { + "id": 96, + "fortune": "LA TEVA DIETA COMENA DILLUNS. ELLS FUGEN" + }, + { + "id": 97, + "fortune": "THO MEREIXES. RESPIRA FONDO" + }, + { + "id": 98, + "fortune": "UN DESCONEGUT ET SOMRIU. MANTN LA CALMA" + }, + { + "id": 99, + "fortune": "NO GUARDES RANCOR. LARXIVES AMB DATA" + }, + { + "id": 100, + "fortune": "LA TEVA PACINCIA T UNA BARRA. BLOCADA" + } +] \ No newline at end of file diff --git a/apps/sarcastic-fortune-cookies/fortunes_da.json b/apps/sarcastic-fortune-cookies/fortunes_da.json new file mode 100644 index 0000000..aaf3575 --- /dev/null +++ b/apps/sarcastic-fortune-cookies/fortunes_da.json @@ -0,0 +1,402 @@ +[ + { + "id": 1, + "fortune": "ALT SKER AF EN GRUND. OFTEST EN DRLIG" + }, + { + "id": 2, + "fortune": "HELDET ER P VEJ. DET GR LANGSOMT" + }, + { + "id": 3, + "fortune": "UNIVERSET HAR EN PLAN. EN LILLE EN" + }, + { + "id": 4, + "fortune": "DIN SKBNE RINGEDE. DEN UDSKYDER" + }, + { + "id": 5, + "fortune": "STJERNERNE STOD RIGTIGT. S S DE DIG" + }, + { + "id": 6, + "fortune": "DIT SKIB KOMMER IND. DET ER EN KANO" + }, + { + "id": 7, + "fortune": "STOL P PROCESSEN. DEN TVIVLER P DIG" + }, + { + "id": 8, + "fortune": "ET TEGN KOMMER. DET SIGER HOLD TILBAGE" + }, + { + "id": 9, + "fortune": "DIN STJERNE FINDES. I EN ANDEN GALAKSE" + }, + { + "id": 10, + "fortune": "KARMA ER GTE. DET FORKLARER MEGET" + }, + { + "id": 11, + "fortune": "DIN FREMTID ER LYS. TAG SOLCREME" + }, + { + "id": 12, + "fortune": "SKBNEN VENTER. DEN GIK I GANG MED AT LSE" + }, + { + "id": 13, + "fortune": "MIRAKLER SKER HVER DAG. BARE ET ANDET STED" + }, + { + "id": 14, + "fortune": "DIT JEBLIK KOMMER. DET SIDDER I KEN" + }, + { + "id": 15, + "fortune": "HELDET BANKEDE P. DU VAR UDE" + }, + { + "id": 16, + "fortune": "TIDEVANDET VENDER. I GLETSJERFART" + }, + { + "id": 17, + "fortune": "STORE TING KOMMER. SNK BARREN" + }, + { + "id": 18, + "fortune": "CHANCERNE ER DER. I RAMMER ALTID FORBI" + }, + { + "id": 19, + "fortune": "UNIVERSET HRER DIG. STIL DIG I KEN" + }, + { + "id": 20, + "fortune": "DINE BNNER BLEV HRT. OG ARKIVERET" + }, + { + "id": 21, + "fortune": "OVERFLODEN FLYDER MOD DIG. MOD STRMMEN" + }, + { + "id": 22, + "fortune": "HJULET DREJER. DU ER HAMSTEREN" + }, + { + "id": 23, + "fortune": "HVER SKY HAR EN KANT. DIN DRYPPER" + }, + { + "id": 24, + "fortune": "TILFLDET SPRGER TIL DIG. AF NYSGERRIGHED" + }, + { + "id": 25, + "fortune": "VARSLERNE TALER. MEST SUKKER DE" + }, + { + "id": 26, + "fortune": "DIT HOROSKOP ER FINT. OG OPDIGTET" + }, + { + "id": 27, + "fortune": "LYNET SLR ALDRIG NED TO GANGE. DET GR DU" + }, + { + "id": 28, + "fortune": "SKBNEN BELNNER TLMOD. FAKTURA FLGER" + }, + { + "id": 29, + "fortune": "MNEN S DIN NYE START. ENDNU EN" + }, + { + "id": 30, + "fortune": "DIN ENGEL VGER. DEN TAGER NOTER" + }, + { + "id": 31, + "fortune": "TRO OG DU MODTAGER. LEVERING VARIERER" + }, + { + "id": 32, + "fortune": "DIN GULDALDER KOMMER. FORGYLDT S" + }, + { + "id": 33, + "fortune": "DET DER ER DIT KOMMER. UDEN NOGET HASTVRK" + }, + { + "id": 34, + "fortune": "UNIVERSET BLINKEDE. ELLER FIK NOGET I JET" + }, + { + "id": 35, + "fortune": "DIT HELD VENDER. RETNING UKENDT" + }, + { + "id": 36, + "fortune": "FORSYNET SRGER. DET TJEKKER BONERNE" + }, + { + "id": 37, + "fortune": "NSK DIG NOGET. STJERNEN SPAMMER DET" + }, + { + "id": 38, + "fortune": "ALT KOMMER TIL SIN TID. DIT TRKKER UD" + }, + { + "id": 39, + "fortune": "TEGNENE VAR DER. AT LSE ER FRIVILLIGT" + }, + { + "id": 40, + "fortune": "DU ER UNIK. LIGESOM ALLE ANDRE" + }, + { + "id": 41, + "fortune": "DET BEDSTE VENTER FORUDE. DET HAR INGEN HAST" + }, + { + "id": 42, + "fortune": "DIN AURA STRLER. DEN TAGER OVERARBEJDE" + }, + { + "id": 43, + "fortune": "HELDET SMILEDE. HFLIGT. P AFSTAND" + }, + { + "id": 44, + "fortune": "DIN ENGEL BAD OM FORFLYTTELSE" + }, + { + "id": 45, + "fortune": "SKBNEN GIVER KORTENE. DU TABER DEM" + }, + { + "id": 46, + "fortune": "ET VARSEL DUKKER OP. DET TRKKER P SKULDRENE" + }, + { + "id": 47, + "fortune": "SUCCES ER NR. OG S ER SNOOZE KNAPPEN" + }, + { + "id": 48, + "fortune": "ARBEJDE BETALER SIG. I ERFARING" + }, + { + "id": 49, + "fortune": "DIT POTENTIALE ER STORT. OG UBRUGT" + }, + { + "id": 50, + "fortune": "SIGT MOD MNEN. PAS P LOFTET" + }, + { + "id": 51, + "fortune": "DEN TIDLIGE FUGL FIK ORMEN. DU SOV" + }, + { + "id": 52, + "fortune": "DIN STORE CHANCE KOMMER. KNK DEN IKKE" + }, + { + "id": 53, + "fortune": "ROM BLEV IKKE BYGGET P EN DAG. DU HELLER" + }, + { + "id": 54, + "fortune": "DINE TALENTER FINDES. MED EN KENDELSE" + }, + { + "id": 55, + "fortune": "VINDERE GIVER ALDRIG OP. DU FLYDER" + }, + { + "id": 56, + "fortune": "DINE IDER ER FORAN. DINE DEADLINES BAGEFTER" + }, + { + "id": 57, + "fortune": "KVALITET ER EN VANE. DIN OGS" + }, + { + "id": 58, + "fortune": "DIT ARBEJDE TALER FOR SIG. MEGET STILLE" + }, + { + "id": 59, + "fortune": "TOPPEN ER ENSOM. PROBLEM LST" + }, + { + "id": 60, + "fortune": "EN FORFREMMELSE KOMMER. EN ANDENS" + }, + { + "id": 61, + "fortune": "DIT CV IMPONERER. FANTASY FANSENE" + }, + { + "id": 62, + "fortune": "DIT BEDSTE ARBEJDE KOMMER. DET LBER STRKT" + }, + { + "id": 63, + "fortune": "DET MDE KUNNE VRE EN MAIL" + }, + { + "id": 64, + "fortune": "DIN INDBAKKE ER IKKE FJENDEN. KUN VIDNE" + }, + { + "id": 65, + "fortune": "DEADLINES SKRMMER DIG IKKE. DU IGNORERER DEM" + }, + { + "id": 66, + "fortune": "DIN FEMRSPLAN ER FEM R GAMMEL" + }, + { + "id": 67, + "fortune": "LANGSOMT MEN SIKKERT VINDER. DU HAR LANGSOMT" + }, + { + "id": 68, + "fortune": "DIN KALENDER ER FULD. AF MARGENER" + }, + { + "id": 69, + "fortune": "DIT HOLD DRMMER OGS. OM FORFLYTTELSE" + }, + { + "id": 70, + "fortune": "CHANCEN SNKEDE SIG TIL DIT NIVEAU" + }, + { + "id": 71, + "fortune": "SUCCES EFTERLADER SPOR. DU KAN LIDE GDER" + }, + { + "id": 72, + "fortune": "DIN KOMFORTZONE FORTJENER ET POSTNUMMER" + }, + { + "id": 73, + "fortune": "KVALITET ELLER KVANTITET. DU AFVISER BEGGE" + }, + { + "id": 74, + "fortune": "DIN INSTINKT ER SKARP. OG OFTE FORKERT" + }, + { + "id": 75, + "fortune": "DU ARBEJDER GODT UNDER PRES. DEN ER UENIG" + }, + { + "id": 76, + "fortune": "MESTERSKAB KRVER 10000 TIMER. DU HAR 40" + }, + { + "id": 77, + "fortune": "DIN PLANTE TROEDE P DIG. OG P VAND" + }, + { + "id": 78, + "fortune": "PRINTEREN MRKER DIT HASTVRK. DEN DRILLER" + }, + { + "id": 79, + "fortune": "DIN ARV ER UNDER OPBYGNING. STADIG" + }, + { + "id": 80, + "fortune": "ET DIPLOM VENTER. DELTAGERDIPLOMET" + }, + { + "id": 81, + "fortune": "AT NETVRKE BNER DRE. AT BANKE OGS" + }, + { + "id": 82, + "fortune": "DU GR FORREST. HR VIL HAVE EN ANDEN" + }, + { + "id": 83, + "fortune": "DIT SIDEPROJEKT HVILER MED DE ANDRE" + }, + { + "id": 84, + "fortune": "GENI ER EN PROCENT SVED. DER STOPPER DU" + }, + { + "id": 85, + "fortune": "DIT FRAVRSSVAR ER DIT MESTERVRK" + }, + { + "id": 86, + "fortune": "AT FEJLE ER ET TRIN. FLOT TRAPPE" + }, + { + "id": 87, + "fortune": "DU ER UERSTATTELIG. OPSLAGET ER KLAR" + }, + { + "id": 88, + "fortune": "DIN MEDARBEJDERSAMTALE KOMMER. V DIG" + }, + { + "id": 89, + "fortune": "EFFEKTIVITET ER DIT MELLEMNAVN. P PAPIR" + }, + { + "id": 90, + "fortune": "DU LANDER P FDDERNE. LANDINGEN ER FORSINKET" + }, + { + "id": 91, + "fortune": "TOPPEN ER NR. BASISLEJREN OGS" + }, + { + "id": 92, + "fortune": "DIN AMBITION VGNEDE. IKKE DIT VKKEUR" + }, + { + "id": 93, + "fortune": "VR DIG SELV. DE ANDRE BLEV BEKYMREDE" + }, + { + "id": 94, + "fortune": "DIT MDE GIK GODT. DET ER SLUT" + }, + { + "id": 95, + "fortune": "AT HVILE ER SUNDT. DU VER MEGET" + }, + { + "id": 96, + "fortune": "DIN DIT STARTER P MANDAG. MANDAGE FLYGTER" + }, + { + "id": 97, + "fortune": "DU FORTJENER DETTE. TRK VEJRET DYBT" + }, + { + "id": 98, + "fortune": "EN FREMMED SMILER TIL DIG. HOLD DIG ROLIG" + }, + { + "id": 99, + "fortune": "DU BRER IKKE NAG. DU ARKIVERER DET" + }, + { + "id": 100, + "fortune": "DIN TLMODIGHED HAR EN BJLKE. LST FAST" + } +] \ No newline at end of file diff --git a/apps/sarcastic-fortune-cookies/fortunes_de.json b/apps/sarcastic-fortune-cookies/fortunes_de.json new file mode 100644 index 0000000..bde6dd7 --- /dev/null +++ b/apps/sarcastic-fortune-cookies/fortunes_de.json @@ -0,0 +1,4002 @@ +[ +{ +"id":1, +"fortune":"ALLES HAT EINEN GRUND. MEIST SCHLECHTE WAHL" +}, +{ +"id":2, +"fortune":"DAS GLÜCK KOMMT. ES GEHT ZU FUß" +}, +{ +"id":3, +"fortune":"DAS UNIVERSUM HAT EINEN PLAN. EINEN KLEINEN" +}, +{ +"id":4, +"fortune":"DEIN SCHICKSAL RIEF AN. ES VERSCHIEBT" +}, +{ +"id":5, +"fortune":"DIE STERNE STANDEN GUT. DANN SAHEN SIE DICH" +}, +{ +"id":6, +"fortune":"DEIN SCHIFF KOMMT. ES IST EIN KANU" +}, +{ +"id":7, +"fortune":"VERTRAU DEM PROZESS. ER VERTRAUT DIR NICHT" +}, +{ +"id":8, +"fortune":"EIN ZEICHEN KOMMT. ES SAGT VORFAHRT ACHTEN" +}, +{ +"id":9, +"fortune":"DEIN GLÜCKSSTERN WOHNT IN FERNER GALAXIE" +}, +{ +"id":10, +"fortune":"KARMA IST REAL. DAS ERKLÄRT EINIGES" +}, +{ +"id":11, +"fortune":"DIE ZUKUNFT IST HELL. TRAG SONNENCREME" +}, +{ +"id":12, +"fortune":"DAS SCHICKSAL WARTET. ES LIEST INZWISCHEN" +}, +{ +"id":13, +"fortune":"WUNDER GESCHEHEN TÄGLICH. MEIST WOANDERS" +}, +{ +"id":14, +"fortune":"DEIN MOMENT KOMMT. ER STEHT IM STAU" +}, +{ +"id":15, +"fortune":"DAS GLÜCK KLOPFTE AN. DU WARST NICHT DA" +}, +{ +"id":16, +"fortune":"DIE FLUT WENDET SICH. IM SCHNECKENTEMPO" +}, +{ +"id":17, +"fortune":"GROßES KOMMT AUF DICH ZU. SENK DIE LATTE" +}, +{ +"id":18, +"fortune":"CHANCEN SIND NAH. IHR VERPASST EUCH STETS" +}, +{ +"id":19, +"fortune":"DER KOSMOS HÖRTE DICH. BITTE WARTEN" +}, +{ +"id":20, +"fortune":"DEINE GEBETE WURDEN GEHÖRT. UND ABGELEGT" +}, +{ +"id":21, +"fortune":"FÜLLE FLIEßT ZU DIR. LEIDER FLUSSAUFWÄRTS" +}, +{ +"id":22, +"fortune":"DAS GLÜCKSRAD DREHT SICH. DU BIST DER HAMSTER" +}, +{ +"id":23, +"fortune":"JEDE WOLKE HAT EINEN RAND. DEINER IST FEUCHT" +}, +{ +"id":24, +"fortune":"DER ZUFALL FRAGT NACH DIR HERUM" +}, +{ +"id":25, +"fortune":"DAS ORAKEL SPRACH. ES SEUFZTE NUR" +}, +{ +"id":26, +"fortune":"DEIN HOROSKOP KLINGT GUT. ES IST FIKTION" +}, +{ +"id":27, +"fortune":"DER BLITZ TRIFFT NIE ZWEIMAL. DU SCHON" +}, +{ +"id":28, +"fortune":"DAS SCHICKSAL BELOHNT GEDULD. RECHNUNG FOLGT" +}, +{ +"id":29, +"fortune":"DER MOND SAH DEINEN NEUSTART. SCHON WIEDER" +}, +{ +"id":30, +"fortune":"DEIN SCHUTZENGEL SCHAUT ZU. ER MACHT NOTIZEN" +}, +{ +"id":31, +"fortune":"GLAUBE UND EMPFANGE. LIEFERUNG VARIIERT" +}, +{ +"id":32, +"fortune":"DEINE GOLDENE ÄRA NAHT. VERGOLDET IMMERHIN" +}, +{ +"id":33, +"fortune":"WAS DIR BESTIMMT IST HAT ES NICHT EILIG" +}, +{ +"id":34, +"fortune":"DAS UNIVERSUM ZWINKERTE. ODER ES WAR STAUB" +}, +{ +"id":35, +"fortune":"DEIN GLÜCK ÄNDERT SICH. RICHTUNG UNKLAR" +}, +{ +"id":36, +"fortune":"DIE VORSEHUNG SORGT. SIE PRÜFT NUR BELEGE" +}, +{ +"id":37, +"fortune":"WÜNSCH DIR WAS. DER STERN MELDET ES ALS SPAM" +}, +{ +"id":38, +"fortune":"GUT DING WILL WEILE. DEINS WILL JAHRZEHNTE" +}, +{ +"id":39, +"fortune":"EIN GELDSEGEN NAHT. ZIEH DICH WARM AN" +}, +{ +"id":40, +"fortune":"DIE ZEICHEN WAREN DA. LESEN IST FREIWILLIG" +}, +{ +"id":41, +"fortune":"DU BIST EINER VON MILLIONEN. WÖRTLICH" +}, +{ +"id":42, +"fortune":"DAS BESTE KOMMT NOCH. ES HAT KEINE EILE" +}, +{ +"id":43, +"fortune":"DEINE AURA STRAHLT. SIE MACHT ÜBERSTUNDEN" +}, +{ +"id":44, +"fortune":"FORTUNA LÄCHELTE. HÖFLICH. EINEM FREMDEN ZU" +}, +{ +"id":45, +"fortune":"DEIN ENGEL BEANTRAGTE EINE VERSETZUNG" +}, +{ +"id":46, +"fortune":"GROßE DINGE BRAUCHEN ZEIT. DEINE URLAUB" +}, +{ +"id":47, +"fortune":"DAS SCHICKSAL MISCHT KARTEN. DU VERLEGST SIE" +}, +{ +"id":48, +"fortune":"EIN OMEN ERSCHEINT. ES ZUCKT MIT SCHULTERN" +}, +{ +"id":49, +"fortune":"DEIN STERN GEHT AUF. IRGENDWANN. VERMUTLICH" +}, +{ +"id":50, +"fortune":"GLÜCK IST MIT DEN DUMMEN. DANN VIEL SPAß" +}, +{ +"id":51, +"fortune":"MORGEN WIRD ALLES BESSER. MORGEN IST DEHNBAR" +}, +{ +"id":52, +"fortune":"DER ERFOLG IST NAH. DIE SCHLUMMERTASTE AUCH" +}, +{ +"id":53, +"fortune":"HARTE ARBEIT ZAHLT SICH AUS. IN ERFAHRUNG" +}, +{ +"id":54, +"fortune":"DEIN POTENZIAL IST GRENZENLOS. UND UNGENUTZT" +}, +{ +"id":55, +"fortune":"TRÄUM GROß. STELL ABER EINEN WECKER" +}, +{ +"id":56, +"fortune":"DER FRÜHE VOGEL FLOG OHNE DICH LOS" +}, +{ +"id":57, +"fortune":"DEIN DURCHBRUCH NAHT. BRICH NICHTS" +}, +{ +"id":58, +"fortune":"ROM ENTSTAND NICHT AN EINEM TAG. DEINS NIE" +}, +{ +"id":59, +"fortune":"DEINE TALENTE WERDEN GEFUNDEN. MIT SPÜRHUND" +}, +{ +"id":60, +"fortune":"GREIF NACH DEN STERNEN. ACHTUNG DECKE" +}, +{ +"id":61, +"fortune":"GEWINNER GEBEN NIE AUF. DU FANDEST PAUSIEREN" +}, +{ +"id":62, +"fortune":"DEINE IDEEN SIND VORAUS. DEINE FRISTEN HINTEN" +}, +{ +"id":63, +"fortune":"QUALITÄT IST GEWOHNHEIT. DEINE AUCH" +}, +{ +"id":64, +"fortune":"DEINE ARBEIT SPRICHT FÜR SICH. LEISE" +}, +{ +"id":65, +"fortune":"DIE SPITZE IST EINSAM. PROBLEM GELÖST" +}, +{ +"id":66, +"fortune":"EINE BEFÖRDERUNG NAHT. DIE EINES KOLLEGEN" +}, +{ +"id":67, +"fortune":"DEIN LEBENSLAUF BEGEISTERT. ALS ROMAN" +}, +{ +"id":68, +"fortune":"DEIN BESTES WERK KOMMT NOCH. ES RENNT SCHNELL" +}, +{ +"id":69, +"fortune":"DAS MEETING WÄRE EINE MAIL GEWESEN" +}, +{ +"id":70, +"fortune":"DEIN POSTFACH IST KEIN FEIND. NUR ZEUGE" +}, +{ +"id":71, +"fortune":"FRISTEN FÜRCHTEN NICHTS. DU GEHST VORBEI" +}, +{ +"id":72, +"fortune":"DEIN FÜNFJAHRESPLAN IST FÜNF JAHRE ALT" +}, +{ +"id":73, +"fortune":"LANGSAM UND STETIG GEWINNT. DIR FEHLT STETIG" +}, +{ +"id":74, +"fortune":"DEIN KALENDER IST VOLL. MIT PUFFERN" +}, +{ +"id":75, +"fortune":"DEIN TEAM TRÄUMT AUCH. VON VERSETZUNG" +}, +{ +"id":76, +"fortune":"DER ANLASS HAT SICH ZU DIR HERABGELASSEN" +}, +{ +"id":77, +"fortune":"ERFOLG HINTERLÄSST SPUREN. DU MAGST RÄTSEL" +}, +{ +"id":78, +"fortune":"DEINE KOMFORTZONE BRAUCHT EINE POSTLEITZAHL" +}, +{ +"id":79, +"fortune":"QUALITÄT VOR QUANTITÄT. DU LEHNST BEIDES AB" +}, +{ +"id":80, +"fortune":"DEIN BAUCHGEFÜHL IST STARK. UND FALSCH" +}, +{ +"id":81, +"fortune":"DU ARBEITEST GUT UNTER DRUCK. SAGT WER" +}, +{ +"id":82, +"fortune":"MEISTERSCHAFT BRAUCHT 10000 STUNDEN. DU 40" +}, +{ +"id":83, +"fortune":"DEINE BÜROPFLANZE GLAUBTE AN DICH. UND WASSER" +}, +{ +"id":84, +"fortune":"DER DRUCKER SPÜRT DEINE EILE. DESHALB" +}, +{ +"id":85, +"fortune":"DEIN VERMÄCHTNIS IST IM BAU. IMMER NOCH" +}, +{ +"id":86, +"fortune":"EINE URKUNDE WARTET. TEILNAHME ZÄHLT" +}, +{ +"id":87, +"fortune":"NETZWERKEN ÖFFNET TÜREN. KLOPFEN AUCH" +}, +{ +"id":88, +"fortune":"DU FÜHRST MIT BEISPIEL. HR SUCHT EIN NEUES" +}, +{ +"id":89, +"fortune":"DEIN NEBENPROJEKT RUHT BEI DEN ANDEREN" +}, +{ +"id":90, +"fortune":"GENIE IST EIN PROZENT ARBEIT. DA STOPPST DU" +}, +{ +"id":91, +"fortune":"DEINE ABWESENHEITSNOTIZ IST DEIN BESTES WERK" +}, +{ +"id":92, +"fortune":"SCHEITERN IST EINE TREPPE. SCHÖNES HAUS" +}, +{ +"id":93, +"fortune":"DU BIST UNERSETZLICH. DIE ANZEIGE IST FERTIG" +}, +{ +"id":94, +"fortune":"DEIN JAHRESGESPRÄCH NAHT. PROBE LÄUFT" +}, +{ +"id":95, +"fortune":"EFFIZIENZ IST DEIN ZWEITER NAME. NUR AMTLICH" +}, +{ +"id":96, +"fortune":"DU LANDEST AUF DEN FÜßEN. LANDUNG VERSPÄTET" +}, +{ +"id":97, +"fortune":"DER GIPFEL IST NAH. DAS BASISLAGER AUCH" +}, +{ +"id":98, +"fortune":"DEIN EHRGEIZ ERWACHTE. DEIN WECKER NICHT" +}, +{ +"id":99, +"fortune":"SEI DU SELBST. ALLE ANDEREN WAREN BESORGT" +}, +{ +"id":100, +"fortune":"DEIN MEETING LIEF GUT. ES IST VORBEI" +}, +{ +"id":101, +"fortune":"ERKENNE DICH SELBST. EIN KURZES HALLO REICHT" +}, +{ +"id":102, +"fortune":"DEIN GLOW UP KOMMT. DAS NETZ IST INFORMIERT" +}, +{ +"id":103, +"fortune":"NEUES JAHR NEUES ICH. DAS ALTE LIEß NOTIZEN" +}, +{ +"id":104, +"fortune":"DU BIST GENUG. DIE LATTE WAR VERSTELLBAR" +}, +{ +"id":105, +"fortune":"DEIN INNERER FRIEDEN SAH DEINEN KALENDER. WEG" +}, +{ +"id":106, +"fortune":"WACHSTUM BRAUCHT ZEIT. DEINS BEANTRAGT FRIST" +}, +{ +"id":107, +"fortune":"DU BIST EIN WERK IN ARBEIT. VIEL ARBEIT" +}, +{ +"id":108, +"fortune":"SELBSTERKENNTNIS IST EIN GESCHENK. UNTERWEGS" +}, +{ +"id":109, +"fortune":"HEILUNG IST NICHT LINEAR. DEINE KRITZELT" +}, +{ +"id":110, +"fortune":"DEIN BESTES ICH ERWACHT. ES DRÜCKT SCHLUMMERN" +}, +{ +"id":111, +"fortune":"SELFCARE IST WICHTIG. SERIEN SIND KEIN SPA" +}, +{ +"id":112, +"fortune":"DEINE GRENZEN SIND GESUND. UND UNGETESTET" +}, +{ +"id":113, +"fortune":"DANKBARKEIT HILFT. PROBIER SIE AN MENSCHEN" +}, +{ +"id":114, +"fortune":"DU VERZEIHST LEICHT. DIR SELBST BESONDERS" +}, +{ +"id":115, +"fortune":"DEINE GEWOHNHEITEN PRÄGEN DICH. BEILEID" +}, +{ +"id":116, +"fortune":"KLEINE SCHRITTE ZÄHLEN. AUCH ZUM KÜHLSCHRANK" +}, +{ +"id":117, +"fortune":"ACHTSAMKEIT HILFT. DEIN HANDY WIDERSPRICHT" +}, +{ +"id":118, +"fortune":"DU LIEßT LOS. ES KENNT DEINE ADRESSE" +}, +{ +"id":119, +"fortune":"DEINE MORGENROUTINE IST HEILIG. VIER MINUTEN" +}, +{ +"id":120, +"fortune":"DU LERNST NEIN ZU SAGEN. ZU GEMÜSE ZUERST" +}, +{ +"id":121, +"fortune":"DEIN WILLE STEMMT FAST EINEN GANZEN KEKS" +}, +{ +"id":122, +"fortune":"DEIN LEBENSZWECK EXISTIERT. ER BLEIBT ANONYM" +}, +{ +"id":123, +"fortune":"DU UMARMST VERÄNDERUNG. VOM SOFA AUS" +}, +{ +"id":124, +"fortune":"DEIN TAGEBUCH IST EHRLICH. BEIDE SEITEN" +}, +{ +"id":125, +"fortune":"DU LEBST DEINE WAHRHEIT. PRÜFUNG LÄUFT" +}, +{ +"id":126, +"fortune":"DEIN INNERER KRITIKER SEUFZT NUR NOCH" +}, +{ +"id":127, +"fortune":"DEINE ZIELE SIND IN SICHT. GUTES FERNGLAS" +}, +{ +"id":128, +"fortune":"DEINE NEUEN MUSTER WIRKEN VERTRAUT. SEHR" +}, +{ +"id":129, +"fortune":"DEINE ENERGIE ZIEHT AN. VOR ALLEM PFLICHTEN" +}, +{ +"id":130, +"fortune":"DEINE MAUERN FIELEN. DER GRABEN WUCHS" +}, +{ +"id":131, +"fortune":"DU FEIERST KLEINE SIEGE. TÄGLICH. WER SONST" +}, +{ +"id":132, +"fortune":"DEINE ENTWICKLUNG VERWIRRT SOGAR DARWIN" +}, +{ +"id":133, +"fortune":"DU KOMMST JETZT ZUERST. KURZE SCHLANGE" +}, +{ +"id":134, +"fortune":"DEINE NEUERFINDUNG TOURT. GLEICHES PROGRAMM" +}, +{ +"id":135, +"fortune":"DU BIST PRÄSENT. KÖRPERLICH IMMERHIN" +}, +{ +"id":136, +"fortune":"DEIN NEUSTART BEGINNT MONTAG. EINEM SPÄTEREN" +}, +{ +"id":137, +"fortune":"DEIN VISIONBOARD IST SCHÖN. UND IM RUHESTAND" +}, +{ +"id":138, +"fortune":"DU EHRST DEINE GEFÜHLE. IN VOLLER LAUTSTÄRKE" +}, +{ +"id":139, +"fortune":"DEINE RUHE IST ECHT. DU LIEST KEINE MAILS" +}, +{ +"id":140, +"fortune":"DEINE STANDARDS STEIGEN. VOM MEERESSPIEGEL" +}, +{ +"id":141, +"fortune":"DU BLÜHST AUF. SPÄTE SAISON. ZÄHLT TROTZDEM" +}, +{ +"id":142, +"fortune":"DEINE DISZIPLIN IST LEGENDÄR. LEGENDEN LÜGEN" +}, +{ +"id":143, +"fortune":"DEINE AFFIRMATIONEN WIRKEN. AN DER DECKE" +}, +{ +"id":144, +"fortune":"DEINE INTUITION IST LAUT. UND OFT DANEBEN" +}, +{ +"id":145, +"fortune":"DEIN INNERES KIND IST GEHEILT. ES WILL EIS" +}, +{ +"id":146, +"fortune":"DIE GROßE LIEBE WARTET. SIE HAT VERSPÄTUNG" +}, +{ +"id":147, +"fortune":"DEINE BESSERE HÄLFTE EXISTIERT. NETZ SCHWANKT" +}, +{ +"id":148, +"fortune":"ROMANTIK LIEGT IN DER LUFT. POLLEN AUCH" +}, +{ +"id":149, +"fortune":"AMOR HAT DEINE ADRESSE. ER FUHR VORBEI" +}, +{ +"id":150, +"fortune":"DEIN HERZ KENNT DEN WEG. DEINE CHATS NICHT" +}, +{ +"id":151, +"fortune":"LIEBE MACHT BLIND. DEINE AUCH VERGESSLICH" +}, +{ +"id":152, +"fortune":"DEIN EX DENKT AN DICH. ALS LEKTION" +}, +{ +"id":153, +"fortune":"DEIN FLIRTEN IST SUBTIL. NICHT NACHWEISBAR" +}, +{ +"id":154, +"fortune":"DEIN BEUTESCHEMA IST KONSTANT. LEIDER" +}, +{ +"id":155, +"fortune":"JEMAND BEWUNDERT DICH VON FERN. BLEIB DORT" +}, +{ +"id":156, +"fortune":"DEINE GRÜNEN FLAGGEN SIND IN DER UNTERZAHL" +}, +{ +"id":157, +"fortune":"LIEBE BESIEGT ALLES. SIE BAT UM VERSTÄRKUNG" +}, +{ +"id":158, +"fortune":"DAS HERZ AUF DER ZUNGE. BITTE BEIDE REINIGEN" +}, +{ +"id":159, +"fortune":"EINE NACHRICHT VON FRÜHER NAHT. IGNORIEREN" +}, +{ +"id":160, +"fortune":"DEIN SCHWARM WEIß VON DIR. ALS KONZEPT" +}, +{ +"id":161, +"fortune":"DU LIEBST TIEF. KURZ. ABER TIEF" +}, +{ +"id":162, +"fortune":"DEIN HERZ IST OFFEN. DEINE RECHNUNG AUCH" +}, +{ +"id":163, +"fortune":"ES WIRD FUNKEN. PRÜF DEN RAUCHMELDER" +}, +{ +"id":164, +"fortune":"DU BIST BEREIT FÜR BINDUNG. ANS SOFA" +}, +{ +"id":165, +"fortune":"DEIN HERZ IST GEHEILT. MIT ALARMANLAGE" +}, +{ +"id":166, +"fortune":"LIEBE IST GEDULDIG. SIE ÜBTE AN DIR" +}, +{ +"id":167, +"fortune":"DEINE FLITTERWOCHEN DAUERTEN ELF TAGE" +}, +{ +"id":168, +"fortune":"DEIN HAUSTIER IST DEINE BESTE BEZIEHUNG" +}, +{ +"id":169, +"fortune":"DEINE GELÜBDE RÜHREN ZU TRÄNEN. DIE GRAMMATIK" +}, +{ +"id":170, +"fortune":"DU BIST EIN FANG. FANGEN UND FREILASSEN GIBTS" +}, +{ +"id":171, +"fortune":"DEIN PERFECT MATCH EXISTIERT. DIE APP GAB AUF" +}, +{ +"id":172, +"fortune":"DEINE ROTEN FLAGGEN SIND NUN WEINROT. GUT" +}, +{ +"id":173, +"fortune":"LIEBE AUF DEN ERSTEN BLICK. KAUF EINE BRILLE" +}, +{ +"id":174, +"fortune":"JEMAND FINDET DICH PERFEKT. AUS DER FERNE" +}, +{ +"id":175, +"fortune":"DU GLAUBST AN ZWEITE CHANCEN. UND AN FÜNFTE" +}, +{ +"id":176, +"fortune":"DEINE TRENNUNGSPLAYLIST LÄUFT OFT" +}, +{ +"id":177, +"fortune":"DU BIST DAS WARTEN WERT. ES WARTET NOCH" +}, +{ +"id":178, +"fortune":"ROMANTIK IST NICHT TOT. DEINE RUHT SICH AUS" +}, +{ +"id":179, +"fortune":"DEINE SCHWIEGERELTERN LIEBEN DICH. IRGENDWANN" +}, +{ +"id":180, +"fortune":"DEIN DATINGPROFIL IST EHRLICH. STELLENWEISE" +}, +{ +"id":181, +"fortune":"DEINE UMARMUNGEN SIND SELTEN. WIE EKLIPSEN" +}, +{ +"id":182, +"fortune":"JEMAND TRÄUMT VON DEINEM ESSEN. ANGSTVOLL" +}, +{ +"id":183, +"fortune":"DEIN CHARME ÖFFNET TÜREN. DRÜCKEN HILFT" +}, +{ +"id":184, +"fortune":"DEINE ANSPRÜCHE TRAFEN DEINE WAHL. PEINLICH" +}, +{ +"id":185, +"fortune":"DU WIRST UMGEHAUEN. DEHN DICH VORHER" +}, +{ +"id":186, +"fortune":"INTIMITÄT HEIßT GESEHEN SEIN. DU VERHANDELST" +}, +{ +"id":187, +"fortune":"EURE GESCHICHTE WIRD ERZÄHLT. ALS WARNUNG" +}, +{ +"id":188, +"fortune":"DER FUNKE IST NOCH DA. SUCHTRUPP LÄUFT" +}, +{ +"id":189, +"fortune":"EUER JAHRESTAG NAHT. DIES IST DIE ERINNERUNG" +}, +{ +"id":190, +"fortune":"LIEB DICH SELBST ZUERST. DAS KANNST DU SCHON" +}, +{ +"id":191, +"fortune":"DEIN HERZ SETZTE AUS. GEH ZUM ARZT" +}, +{ +"id":192, +"fortune":"DU HOLST DAS BESTE AUS ALLEN. BEIM ABSCHIED" +}, +{ +"id":193, +"fortune":"NIE ZUFRIEDENGEBEN. DAHER DIE KLAPPSTÜHLE" +}, +{ +"id":194, +"fortune":"DEIN KÜNFTIGER PARTNER TRAINIERT. AHNUNGSLOS" +}, +{ +"id":195, +"fortune":"DEIN HERZ IST EINE FESTUNG. SHOP GEÖFFNET" +}, +{ +"id":196, +"fortune":"DEIN KUSCHELBEDARF HAT SPRECHZEITEN" +}, +{ +"id":197, +"fortune":"JEMAND VERMISST DICH. DIE NUMMER IST WEG" +}, +{ +"id":198, +"fortune":"DU FLIRTEST MIT BLICKEN. NIEMAND SAH SIE" +}, +{ +"id":199, +"fortune":"DEINE LIEBESBRIEFE SIND JETZT SPRACHNOTIZEN" +}, +{ +"id":200, +"fortune":"FREUNDE SCHÄTZEN DEINE EHRLICHKEIT. VON FERN" +}, +{ +"id":201, +"fortune":"EIN FREUND RUFT BALD AN. ER ZIEHT UM" +}, +{ +"id":202, +"fortune":"DU BIST DER KLEBER. KLEBER DANKT MAN SELTEN" +}, +{ +"id":203, +"fortune":"DER GRUPPENCHAT GENIEßT DEINE STILLE" +}, +{ +"id":204, +"fortune":"WAHRE FREUNDE SIND RAR. DEINE VERSTECKEN SICH" +}, +{ +"id":205, +"fortune":"GROßE TREUE. KLEINE PÜNKTLICHKEIT" +}, +{ +"id":206, +"fortune":"EIN FEST BRINGT FREUDE. DAS GEHEN NOCH MEHR" +}, +{ +"id":207, +"fortune":"DU BIST DER LUSTIGE. EIGENANGABE" +}, +{ +"id":208, +"fortune":"EIN ALTER FREUND MELDET SICH. ER BRAUCHT WAS" +}, +{ +"id":209, +"fortune":"DEIN RAT WIRD GESCHÄTZT. NICHT BESTELLT" +}, +{ +"id":210, +"fortune":"DU ERHELLST JEDEN RAUM. MANCHE WOLLEN DIMMER" +}, +{ +"id":211, +"fortune":"DEIN GEBURTSTAG WIRD ERINNERT. EINEN TAG SPÄT" +}, +{ +"id":212, +"fortune":"DEINE GESCHICHTEN WERDEN BESSER. UND FALSCHER" +}, +{ +"id":213, +"fortune":"MENSCHEN FÜHLEN SICH ZU DIR HINGEZOGEN. KURZ" +}, +{ +"id":214, +"fortune":"DU WEIßT STETS WAS ZU SAGEN IST. WANN NICHT" +}, +{ +"id":215, +"fortune":"FREUNDE KOMMEN UND GEHEN. DEINE KÖNNEN GEHEN" +}, +{ +"id":216, +"fortune":"DEIN TERMINKALENDER IST VOLL. MIT VIELLEICHTS" +}, +{ +"id":217, +"fortune":"DEINE INSIDERWITZE SIND JETZT SOLOAUFTRITTE" +}, +{ +"id":218, +"fortune":"DEINE PARTYS SIND LEGENDÄR. DIE ABGESAGTEN" +}, +{ +"id":219, +"fortune":"DU BRINGST MENSCHEN ZUSAMMEN. ALS THEMA" +}, +{ +"id":220, +"fortune":"DEINE GROßZÜGIGKEIT HAT ÖFFNUNGSZEITEN" +}, +{ +"id":221, +"fortune":"DU VERGISST NIE EIN GESICHT. NAMEN SCHON" +}, +{ +"id":222, +"fortune":"DEINE AUFMUNTERUNGEN BRAUCHEN NACHSORGE" +}, +{ +"id":223, +"fortune":"DU BIST DA WENN ES ZÄHLT. FÜR DICH" +}, +{ +"id":224, +"fortune":"DEINE ABSCHIEDSFESTE SIND GUT BESUCHT" +}, +{ +"id":225, +"fortune":"MAN VERTRAUT DIR GEHEIMNISSE AN. UND ANDEREN" +}, +{ +"id":226, +"fortune":"DEINE EHRLICHKEIT ERFRISCHT. WIE EISWASSER" +}, +{ +"id":227, +"fortune":"DU HEGST KEINEN GROLL. DU ARCHIVIERST IHN" +}, +{ +"id":228, +"fortune":"DEINE MEMES KOMMEN IM MORGENGRAUEN. UNGEFRAGT" +}, +{ +"id":229, +"fortune":"DEINE SCHULTER IST FREI. NUR MIT TERMIN" +}, +{ +"id":230, +"fortune":"DU HÄLTST ALLE AM BODEN. WIE NEBEL" +}, +{ +"id":231, +"fortune":"DEINE BEGEISTERUNG STECKT AN. MASKEN HELFEN" +}, +{ +"id":232, +"fortune":"EIN FREUND WILL DEINE EHRLICHE MEINUNG. FALLE" +}, +{ +"id":233, +"fortune":"DU TEILST RECHNUNGEN FAIR. DEINE MATHE NICHT" +}, +{ +"id":234, +"fortune":"DEINE ANWESENHEIT IST EIN GESCHENK. MIT BELEG" +}, +{ +"id":235, +"fortune":"DEIN EHRGEIZ BEENDETE DIE SPIELEABENDE" +}, +{ +"id":236, +"fortune":"DEINE ÜBERRASCHUNGSPARTYS ÜBERRASCHEN KEINEN" +}, +{ +"id":237, +"fortune":"MAN FÜHLT SICH WOHL BEI DIR. BEIM GEHEN" +}, +{ +"id":238, +"fortune":"DU SAGST WAS ALLE DENKEN. AUF BEERDIGUNGEN" +}, +{ +"id":239, +"fortune":"DEIN JAHRESRUNDBRIEF HAT EINE LESERSCHAFT" +}, +{ +"id":240, +"fortune":"DEINE ENTSCHULDIGUNGEN SIND HERZLICH. UND OFT" +}, +{ +"id":241, +"fortune":"DU PFLEGST DIE GRUPPENHISTORIE. ÜBERARBEITET" +}, +{ +"id":242, +"fortune":"DU HILFST NIE UNBEMERKT. DAS SAGST DU AUCH" +}, +{ +"id":243, +"fortune":"DEINE DIREKTHEIT IST CHARMANT. LAUT PROTOKOLL" +}, +{ +"id":244, +"fortune":"DU BIST MODISCH SPÄT. OHNE DAS MODISCH" +}, +{ +"id":245, +"fortune":"MAN SUCHT DEINE ZUSTIMMUNG. UM ZU GEHEN" +}, +{ +"id":246, +"fortune":"DU LÄSTERST NIE. DU INFORMIERST ABWESENDE" +}, +{ +"id":247, +"fortune":"DIE EINLADUNG SAGT LOCKER. DEINE MAILS NICHT" +}, +{ +"id":248, +"fortune":"EIN JUGENDFREUND DENKT AN DICH. AUS DER FERNE" +}, +{ +"id":249, +"fortune":"DU VERLIERST NIE STREITS. DU VERLEGST SIE" +}, +{ +"id":250, +"fortune":"DEINE HILFE IST BEDINGUNGSLOS. SIEHE AGB" +}, +{ +"id":251, +"fortune":"REICHTUM NAHT. ER HAT DEINE ALTE ADRESSE" +}, +{ +"id":252, +"fortune":"GELD FLIEßT ZU DIR. DURCH DICH. WEG" +}, +{ +"id":253, +"fortune":"DEIN BUDGET IST KUNST. ABSTRAKTE KUNST" +}, +{ +"id":254, +"fortune":"SPAR FÜR SCHLECHTE ZEITEN. MONSUN GEMELDET" +}, +{ +"id":255, +"fortune":"DEINE SCHUFA IST EIN THRILLER" +}, +{ +"id":256, +"fortune":"DEIN ERSPARTES WÄCHST. AN CHARAKTER" +}, +{ +"id":257, +"fortune":"GELD SPRICHT. DEINS SAGT TSCHÜSS" +}, +{ +"id":258, +"fortune":"DEINE ALTERSVORSORGE IST EIN LOTTOSCHEIN" +}, +{ +"id":259, +"fortune":"DEIN GESCHMACK IST TEUER. DEIN KONTO NICHT" +}, +{ +"id":260, +"fortune":"ZINSESZINS IST MAGIE. DU WÄHLTEST SCHULDEN" +}, +{ +"id":261, +"fortune":"DEIN NOTGROSCHEN DECKT KLEINE NOTFÄLLE" +}, +{ +"id":262, +"fortune":"EINE GEHALTSERHÖHUNG IST VERDIENT. UND OFFEN" +}, +{ +"id":263, +"fortune":"DEIN WARENKORB IST EIN VISIONBOARD MIT KASSE" +}, +{ +"id":264, +"fortune":"DEINE GROßZÜGIGKEIT KENNT JEDER HÄNDLER" +}, +{ +"id":265, +"fortune":"DAS BESTE IST GRATIS. DEINS HAT VERSANDKOSTEN" +}, +{ +"id":266, +"fortune":"DEINE ABOS ÜBERTREFFEN DEINE FREUNDE. AN ZAHL" +}, +{ +"id":267, +"fortune":"BILLIG KAUFEN TEUER VERKAUFEN. DU DREHST UM" +}, +{ +"id":268, +"fortune":"DEINE BANK SCHREIBT DIR AUS SORGE" +}, +{ +"id":269, +"fortune":"DEINE STEUERERSTATTUNG HAT EIGENE PLÄNE" +}, +{ +"id":270, +"fortune":"DEINE KÄUFE SIND STRATEGISCH. ANGEBLICH" +}, +{ +"id":271, +"fortune":"DU FINDEST GELD IM MANTEL. ES WAR DEINS" +}, +{ +"id":272, +"fortune":"DEIN PORTFOLIO IST BREIT GESTREUT. IN REUE" +}, +{ +"id":273, +"fortune":"ZAHLTAG BRINGT FREUDE. ETWA 18 STUNDEN LANG" +}, +{ +"id":274, +"fortune":"DEINE MAHNGEBÜHREN HABEN MAHNGEBÜHREN" +}, +{ +"id":275, +"fortune":"KLEINVIEH MACHT AUCH MIST. DEINS MACHT PAUSE" +}, +{ +"id":276, +"fortune":"REICH IM HERZEN. DIE KASSE SIEHT DAS ANDERS" +}, +{ +"id":277, +"fortune":"DEINE BELEGE ERZÄHLEN EINE LANGE GESCHICHTE" +}, +{ +"id":278, +"fortune":"DEINE ANZAHLUNG WURDE SUSHI. ZWEIMAL" +}, +{ +"id":279, +"fortune":"DEIN HAUSHALTSPLAN IST HISTORISCHE FIKTION" +}, +{ +"id":280, +"fortune":"DEINE PROBEABOS LEBEN EWIG. SIE BUCHEN AB" +}, +{ +"id":281, +"fortune":"BARGELD IST KÖNIG. DEIN REICH LEBT AUF PUMP" +}, +{ +"id":282, +"fortune":"DEIN KAUFSTOPP HIELT 40 MINUTEN" +}, +{ +"id":283, +"fortune":"DEINE RECHNUNGEN KOMMEN PÜNKTLICH. DU NICHT" +}, +{ +"id":284, +"fortune":"DEIN GELD ARBEITET HART. FÜR ANDERE" +}, +{ +"id":285, +"fortune":"DEINE GUTSCHEINE LIEFEN AB. ALLE. HEUTE" +}, +{ +"id":286, +"fortune":"DEIN FINANZPLAN HAT PHASEN. ERST VERDRÄNGUNG" +}, +{ +"id":287, +"fortune":"DEINE RABATTE FINANZIEREN DEINE SPONTANKÄUFE" +}, +{ +"id":288, +"fortune":"DEINE KRYPTOPHASE WAR LEHRREICH. UND TEUER" +}, +{ +"id":289, +"fortune":"LOTTO IST HOFFNUNGSSTEUER. DU SPENDEST GERN" +}, +{ +"id":290, +"fortune":"DEIN SPARZIEL STEHT SEIT DEM FRÜHJAHR STILL" +}, +{ +"id":291, +"fortune":"REICHTUM FLÜSTERT. DEINER LEGTE AUF" +}, +{ +"id":292, +"fortune":"DU NOTIERST JEDE AUSGABE. DANN KOMMEN NEUE" +}, +{ +"id":293, +"fortune":"DEIN SPARSCHWEIN HAT VERTRAUENSPROBLEME" +}, +{ +"id":294, +"fortune":"DEINE ERSTATTUNG KAM AN. SIE IST SCHON WEG" +}, +{ +"id":295, +"fortune":"GELD MACHT NICHT GLÜCKLICH. DEINS GAB AUF" +}, +{ +"id":296, +"fortune":"DEINE RECHNUNGEN REIFEN WIE WEIN. UNBEZAHLT" +}, +{ +"id":297, +"fortune":"DEIN KONTOSTAND UND DEIN EGO SANKEN GEMEINSAM" +}, +{ +"id":298, +"fortune":"DU KAUFST ORDNUNGSBOXEN FÜR ORDNUNGSBOXEN" +}, +{ +"id":299, +"fortune":"GRATISVERSAND KOSTET DICH EIN VERMÖGEN" +}, +{ +"id":300, +"fortune":"GEDULD IST EINE TUGEND. DU WÄHLTEST ANDERE" +}, +{ +"id":301, +"fortune":"REDEN IST SILBER. DEINS IST BLECH" +}, +{ +"id":302, +"fortune":"ERST DENKEN DANN REDEN. EIN PILOTPROJEKT" +}, +{ +"id":303, +"fortune":"STILLE WASSER SIND TIEF. DEINE KNÖCHELTIEF" +}, +{ +"id":304, +"fortune":"WISSEN IST MACHT. DEINS IST IM STANDBY" +}, +{ +"id":305, +"fortune":"EIN OFFENER GEIST IST GOLD. SCHÖNER DURCHZUG" +}, +{ +"id":306, +"fortune":"ZWEIMAL HÖREN EINMAL REDEN. DREH ES UM" +}, +{ +"id":307, +"fortune":"DIE WAHRHEIT MACHT FREI. DEINE SITZT NOCH" +}, +{ +"id":308, +"fortune":"ERST SCHAUEN DANN SPRINGEN. ODER DANACH" +}, +{ +"id":309, +"fortune":"NARR UND GELD TRENNEN SICH. SCHÖNE FEIER WARS" +}, +{ +"id":310, +"fortune":"TATEN SAGEN MEHR ALS WORTE. DEINE FLÜSTERN" +}, +{ +"id":311, +"fortune":"ZWEIMAL MESSEN EINMAL SCHNEIDEN. DU SÄGST" +}, +{ +"id":312, +"fortune":"DIE FEDER IST MÄCHTIG. DEINE MAILS AUCH" +}, +{ +"id":313, +"fortune":"BESSER SPÄT ALS NIE. DU TESTEST DAS TÄGLICH" +}, +{ +"id":314, +"fortune":"EHRLICH WÄHRT AM LÄNGSTEN. SIEHE FUßNOTEN" +}, +{ +"id":315, +"fortune":"ZÄHL DEINE HÜHNER NICHT. DEINE STREIKEN" +}, +{ +"id":316, +"fortune":"GRAS IST GRÜNER WO MAN GIEßT. KAUF SCHLAUCH" +}, +{ +"id":317, +"fortune":"JEDER HEILIGE HAT EINE AKTE. DEINE IST DICK" +}, +{ +"id":318, +"fortune":"PREDIGE WAS DU LEBST. ODER PREDIGE WENIGER" +}, +{ +"id":319, +"fortune":"ZWEI FEHLER MACHEN KEIN RECHT. STOPP BEI NEUN" +}, +{ +"id":320, +"fortune":"NICHT ALLES GOLD WAS GLÄNZT. PRÜF DEN BELEG" +}, +{ +"id":321, +"fortune":"EIN ROLLENDER STEIN. DEINER SETZTE MOOS AN" +}, +{ +"id":322, +"fortune":"NICHT ALLE EIER IN EINEN KORB. WO IST ER" +}, +{ +"id":323, +"fortune":"LEERE FÄSSER KLINGEN LAUT. TOLLE AKUSTIK" +}, +{ +"id":324, +"fortune":"WER ZULETZT LACHT DACHTE LANGSAM. HALLO" +}, +{ +"id":325, +"fortune":"DER SPATZ IN DER HAND. DEINER FLOG WEG" +}, +{ +"id":326, +"fortune":"REPARIER NUR KAPUTTES. JETZT IST ES KAPUTT" +}, +{ +"id":327, +"fortune":"DU FÜHRST PFERDE ZUM WASSER. SIE LEHNEN AB" +}, +{ +"id":328, +"fortune":"HOCHMUT KOMMT VOR DEM FALL. GUTE BALANCE" +}, +{ +"id":329, +"fortune":"DIE ZEIT HEILT ALLE WUNDEN. DU KRATZT DRAN" +}, +{ +"id":330, +"fortune":"ALTE GEWOHNHEITEN STERBEN SCHWER. DEINE NIE" +}, +{ +"id":331, +"fortune":"WO RAUCH IST DA BIST DU" +}, +{ +"id":332, +"fortune":"DIE BESTE VERTEIDIGUNG. ABSTAND. SIE LERNTEN" +}, +{ +"id":333, +"fortune":"NÄHE ZÜCHTET VERACHTUNG. BEI EUCH BLÜHT SIE" +}, +{ +"id":334, +"fortune":"DER TEUFEL STECKT IM DETAIL. DEINE SIND LEER" +}, +{ +"id":335, +"fortune":"DES EINEN MÜLL IST DEINE GARAGE" +}, +{ +"id":336, +"fortune":"EIN BEOBACHTETER TOPF KOCHT NIE. DEINER SCHON" +}, +{ +"id":337, +"fortune":"UNWISSENHEIT IST SEGEN. DU WIRKST SELIG" +}, +{ +"id":338, +"fortune":"SCHÖNHEIT LIEGT IM AUGE. LASS DAS AUGE PRÜFEN" +}, +{ +"id":339, +"fortune":"ES BRAUCHT EIN DORF. DEINS BAT UM VERSTÄRKUNG" +}, +{ +"id":340, +"fortune":"GROßE GEISTER DENKEN GLEICH. DEINER SOLO" +}, +{ +"id":341, +"fortune":"PROBIEREN GEHT ÜBER STUDIEREN. DU LÄSST BEIDE" +}, +{ +"id":342, +"fortune":"JEDER HUND HAT SEINEN TAG. DEINER WAR 2019" +}, +{ +"id":343, +"fortune":"WER NICHT WAGT GEWINNT NICHT. PERFEKTES REMIS" +}, +{ +"id":344, +"fortune":"VIELE KÖCHE VERDERBEN DEN BREI. DU AUCH SOLO" +}, +{ +"id":345, +"fortune":"DU ERNTEST WAS DU SÄST. INTERESSANTE ERNTE" +}, +{ +"id":346, +"fortune":"WER FRAGT BEKOMMT. DEIN TON SCHUF EINE LISTE" +}, +{ +"id":347, +"fortune":"GEH EINE MEILE IN FREMDEN SCHUHEN. RETOUR" +}, +{ +"id":348, +"fortune":"EILE MIT WEILE. DU BEHIELTST NUR DIE WEILE" +}, +{ +"id":349, +"fortune":"ALLES GUTE HAT EIN ENDE. DEINS KOMMT FRÜH" +}, +{ +"id":350, +"fortune":"WORTE SIND BILLIG. DEINE SIND GRATIS" +}, +{ +"id":351, +"fortune":"DIE ZEIT WARTET AUF NIEMANDEN. BEI DIR SCHON" +}, +{ +"id":352, +"fortune":"DIE FÜTTERNDE HAND TRÄGT NUN HANDSCHUHE" +}, +{ +"id":353, +"fortune":"VERGLEICHE STEHLEN FREUDE. DEINE IN VOLLZEIT" +}, +{ +"id":354, +"fortune":"HALBES BROT SCHLÄGT KEINS. DU BIETEST KRÜMEL" +}, +{ +"id":355, +"fortune":"GLATTE SEE MACHT KEINE SEELEUTE. BLEIB DAHEIM" +}, +{ +"id":356, +"fortune":"DIE WAHRHEIT BRAUCHT KEINE PROBE. DEINE SCHON" +}, +{ +"id":357, +"fortune":"ALLES IN MAßEN. AUCH DAS. HÖR AUF" +}, +{ +"id":358, +"fortune":"WAS DU SÄST KOMMT ZURÜCK. SENDUNG VERFOLGBAR" +}, +{ +"id":359, +"fortune":"STREITE NIE MIT NARREN. MAN RÄT WER WER IST" +}, +{ +"id":360, +"fortune":"WEISHEIT FLÜSTERT. DEINE IST STUMMGESCHALTET" +}, +{ +"id":361, +"fortune":"DER WEG BEGINNT MIT EINEM SCHRITT. DU DEHNST" +}, +{ +"id":362, +"fortune":"BRÜCKEN ÜBERQUEREN. NICHT ANZÜNDEN. ZUR INFO" +}, +{ +"id":363, +"fortune":"DIE EICHE WAR EINE NUSS. TRADITION GEWAHRT" +}, +{ +"id":364, +"fortune":"WER EINMAL TÄUSCHT. DEINE KARTE IST VOLL" +}, +{ +"id":365, +"fortune":"MORGENSTUND HAT GOLD IM MUND. DU SCHLÄFST" +}, +{ +"id":366, +"fortune":"ÜBUNG MACHT DEN MEISTER. DU SAMMELST THEORIE" +}, +{ +"id":367, +"fortune":"LÜGEN HABEN KURZE BEINE. DEINE JOGGEN GERN" +}, +{ +"id":368, +"fortune":"JEDER SCHMIEDET SEIN GLÜCK. DEINE ESSE RUHT" +}, +{ +"id":369, +"fortune":"DER APFEL FIEL STAMMNAH. DER STAMM SCHWEIGT" +}, +{ +"id":370, +"fortune":"ABENTEUER WARTET. ES GRÜNDETE EINE FIRMA" +}, +{ +"id":371, +"fortune":"DU WIRST WEIT REISEN. DER SHUTTLE ZÄHLT" +}, +{ +"id":372, +"fortune":"DEIN PASSFOTO ZEIGT DEIN WAHRES ICH. BEILEID" +}, +{ +"id":373, +"fortune":"DEINE LÖFFELLISTE BRAUCHT EINEN LÖFFEL" +}, +{ +"id":374, +"fortune":"DU SIEHST DIE WELT. PER DOKU" +}, +{ +"id":375, +"fortune":"DEINE URLAUBSTAGE REIFEN WIE EDLER FRUST" +}, +{ +"id":376, +"fortune":"EIN FERNES ZIEL RIEF AN. DU DRÜCKTEST WEG" +}, +{ +"id":377, +"fortune":"DEINE ROADTRIP PLAYLIST ÜBERLEBTE DEN TRIP" +}, +{ +"id":378, +"fortune":"DU PACKST LEICHT. EMOTIONAL EHER NICHT" +}, +{ +"id":379, +"fortune":"DEINE SOUVENIRS ERZÄHLEN VON FLUGHAFENPREISEN" +}, +{ +"id":380, +"fortune":"DU VERIRRST DICH IM AUSLAND. WIE DAHEIM" +}, +{ +"id":381, +"fortune":"DEINE REISEFOTOS ZEIGEN DEINEN DAUMEN" +}, +{ +"id":382, +"fortune":"EIN SPONTANTRIP NAHT. MIT TABELLENKALKULATION" +}, +{ +"id":383, +"fortune":"DU BIST WELTBÜRGER. REFERENZEN AUSSTEHEND" +}, +{ +"id":384, +"fortune":"DEIN STRANDKÖRPER IST BEREIT. DER STRAND AUCH" +}, +{ +"id":385, +"fortune":"DIE BERGE RUFEN. DU HAST SIE STUMMGESCHALTET" +}, +{ +"id":386, +"fortune":"DEIN URLAUB AUF BALKONIEN IST UNGESCHLAGEN" +}, +{ +"id":387, +"fortune":"DU NAVIGIERST WIE EIN VERIRRTER PROFI" +}, +{ +"id":388, +"fortune":"DEIN REISEPLAN IST HAUPTSÄCHLICH PUFFERZEIT" +}, +{ +"id":389, +"fortune":"DEINE WANDERSCHUHE BESTIEGEN DEN SCHRANK" +}, +{ +"id":390, +"fortune":"JETLAG SCHONT NIEMANDEN. DU KAMST MÜDE AN" +}, +{ +"id":391, +"fortune":"DEIN REISETAGEBUCH HAT EINEN EINTRAG. FREUDE" +}, +{ +"id":392, +"fortune":"DU SAMMELST ERLEBNISSE. DIE KARTE SAGT DINGE" +}, +{ +"id":393, +"fortune":"DEIN ORIENTIERUNGSSINN IST FAMILIENLEGENDE" +}, +{ +"id":394, +"fortune":"EIN KURZTRIP NAHT. DIE WÄSCHE WARTET TREU" +}, +{ +"id":395, +"fortune":"DU KOMMST VERÄNDERT ZURÜCK. VOR ALLEM MÜDE" +}, +{ +"id":396, +"fortune":"DEINE ANKUNFTSZEIT AM FLUGHAFEN SAGT ALLES" +}, +{ +"id":397, +"fortune":"DEINE FENSTERPLATZFOTOS ZEIGEN DEN FLÜGEL" +}, +{ +"id":398, +"fortune":"DEIN HANDGEPÄCK HAT EIGENE SCHWERKRAFT" +}, +{ +"id":399, +"fortune":"DEINE CAMPINGKÜNSTE AMÜSIEREN DIE WASCHBÄREN" +}, +{ +"id":400, +"fortune":"DEIN HANDYAKKU HÄLT LÄNGER ALS DEIN ELAN" +}, +{ +"id":401, +"fortune":"DU LIEST DIE AGB IRGENDWANN. NEIN" +}, +{ +"id":402, +"fortune":"DEIN BILDSCHIRMZEITBERICHT SCHONT DICH SCHON" +}, +{ +"id":403, +"fortune":"DEIN WÄSCHESTUHL HAT NUN EINE FESTANSTELLUNG" +}, +{ +"id":404, +"fortune":"DEIN WECKER HAT FÜNF ERSATZWECKER. ALLE STUMM" +}, +{ +"id":405, +"fortune":"DEIN KÜHLSCHRANK BEHERBERGT EIN EXPERIMENT" +}, +{ +"id":406, +"fortune":"DIE MOTORLEUCHTE IST JETZT STIMMUNGSLICHT" +}, +{ +"id":407, +"fortune":"DEINE ZIMMERPFLANZEN HABEN SICH GEFÜGT" +}, +{ +"id":408, +"fortune":"DEINE KRIMSKRAMSSCHUBLADE EXPANDIERTE" +}, +{ +"id":409, +"fortune":"DEINE SPORTTASCHE IST GEPACKT. DAS ZÄHLT" +}, +{ +"id":410, +"fortune":"DEINE MAILBOX IST VOLL. MAN ZOG WEITER" +}, +{ +"id":411, +"fortune":"DEINE BROWSERTABS SIND EIN ÖKOSYSTEM" +}, +{ +"id":412, +"fortune":"DEIN SCHIRM WARTET. DU VERGISST IHN EH" +}, +{ +"id":413, +"fortune":"DEINE RESTE HATTEN TRÄUME. SIE KOMPOSTIEREN" +}, +{ +"id":414, +"fortune":"DEINE TODOLISTE HAT EINE TODOLISTE" +}, +{ +"id":415, +"fortune":"DEIN SCHRANK KLEIDET DREI VERSIONEN VON DIR" +}, +{ +"id":416, +"fortune":"DEIN MORGENLAUF IST GEPLANT. SEIT MONATEN" +}, +{ +"id":417, +"fortune":"DEIN ROUTER STEHT AUF EINEM SCHREIN. KORREKT" +}, +{ +"id":418, +"fortune":"DEIN INBOX ZERO VERSUCH IST GESCHICHTE" +}, +{ +"id":419, +"fortune":"DEINE SOCKENSCHUBLADE SCHÜTZT EINZELGÄNGER" +}, +{ +"id":420, +"fortune":"DEIN EINKAUFSZETTEL BLEIBT DAHEIM. ER WEIß" +}, +{ +"id":421, +"fortune":"DEINE WATCHLIST ÜBERLEBTE DREI HOBBYS" +}, +{ +"id":422, +"fortune":"DER DRUCKER SPÜRT ANGST. UND AKTUALISIERT" +}, +{ +"id":423, +"fortune":"DEINE SCHLAFENSZEIT IST EIN ANTRAG. ABGELEHNT" +}, +{ +"id":424, +"fortune":"DEINE KOPFHÖRER VERKNOTEN SICH ALLEIN" +}, +{ +"id":425, +"fortune":"DEIN GEWÜRZREGAL IST SORTIERT BIS C" +}, +{ +"id":426, +"fortune":"DEIN RÜCKSITZ IST EIN FUNDBÜRO" +}, +{ +"id":427, +"fortune":"DAS GUTE KABEL IST IMMER WOANDERS" +}, +{ +"id":428, +"fortune":"DEIN STEHPULT SITZT MEISTENS. WIE DU" +}, +{ +"id":429, +"fortune":"DEIN MEALPREP ERGAB EIN GERICHT. SECHS DOSEN" +}, +{ +"id":430, +"fortune":"DER PAKETBOTE WINKT DIR INZWISCHEN ZU" +}, +{ +"id":431, +"fortune":"DEINE HEIßLUFTFRITTEUSE ERSETZTE DEN HERD" +}, +{ +"id":432, +"fortune":"DEINE LESEZEICHEN SIND EIN VORSATZARCHIV" +}, +{ +"id":433, +"fortune":"DEINE SCHLUMMERTASTE ZEIGT ABNUTZUNG" +}, +{ +"id":434, +"fortune":"DEINE STOFFTASCHEN WARTEN IM AUTO. TREU" +}, +{ +"id":435, +"fortune":"DEINE UPDATES HABEN JETZT DIENSTALTER" +}, +{ +"id":436, +"fortune":"DER EISWÜRFELBEHÄLTER KOMMT LEER ZURÜCK" +}, +{ +"id":437, +"fortune":"DEINE GARAGE LAGERT ALLES AUßER DEM AUTO" +}, +{ +"id":438, +"fortune":"DEIN SCHRITTZÄHLER RAHMTE SEINEN BESTEN TAG" +}, +{ +"id":439, +"fortune":"DEINE LIEBLINGSTASSE STEHT UNTER SCHUTZ" +}, +{ +"id":440, +"fortune":"DIE WETTERAPP GAB DEINE OUTFITS AUF" +}, +{ +"id":441, +"fortune":"DEINE WAAGE ÜBT RADIKALE EHRLICHKEIT" +}, +{ +"id":442, +"fortune":"DEINE FERNBEDIENUNGEN PENDELN IM SOFA" +}, +{ +"id":443, +"fortune":"DER FLURSCHRANK HÜTET DEINE WAHRHEIT. KNAPP" +}, +{ +"id":444, +"fortune":"DU HAST JEDE BATTERIE AUßER DER RICHTIGEN" +}, +{ +"id":445, +"fortune":"DEIN STAUBSAUGER MEIDET ECKEN. WIE DU" +}, +{ +"id":446, +"fortune":"DEINE DUSCHGEDANKEN BLEIBEN IN DER DUSCHE" +}, +{ +"id":447, +"fortune":"DIE KAFFEEMASCHINE KENNT DEIN MONTAGSGESICHT" +}, +{ +"id":448, +"fortune":"DEIN GEFRIERFACH IST ÄLTER ALS MANCHE FREUNDE" +}, +{ +"id":449, +"fortune":"DEIN SPEICHER IST VOLL. NUR SCREENSHOTS" +}, +{ +"id":450, +"fortune":"DEINE FAHRRADREIFEN SEUFZEN LANGSAM AUS" +}, +{ +"id":451, +"fortune":"DEIN RETOURENSTAPEL HAT EINE ABFLUGHALLE" +}, +{ +"id":452, +"fortune":"DEINE KERZEN SIND DEKO. STREICHHÖLZER AUCH" +}, +{ +"id":453, +"fortune":"DEIN ESSTISCH HAT DAS ESSEN AUFGEGEBEN" +}, +{ +"id":454, +"fortune":"DEINE AUTOKORREKTUR SCHLÄGT SORRY VOR" +}, +{ +"id":455, +"fortune":"DEINE FITNESSAPP SCHREIBT KÜRZERE SÄTZE" +}, +{ +"id":456, +"fortune":"DEINE MANTELTASCHE IST EINE ZEITKAPSEL" +}, +{ +"id":457, +"fortune":"DIE PLUS 30 SEKUNDEN TASTE FÜHRT DEINE KÜCHE" +}, +{ +"id":458, +"fortune":"DIE GUTE SCHERE FEHLT. EIN VERDÄCHTIGER" +}, +{ +"id":459, +"fortune":"DEIN DOWNLOADORDNER IST EINE DEPONIE" +}, +{ +"id":460, +"fortune":"DEINE PFLANZEN LEBEN VON SCHULDGEFÜHLEN" +}, +{ +"id":461, +"fortune":"DER TROCKNER NIMMT EINE SOCKE ALS TRIBUT" +}, +{ +"id":462, +"fortune":"DEINE UNTERSETZER SCHAUEN NUR NOCH ZU" +}, +{ +"id":463, +"fortune":"DEINE NOTIZAPP IST EINE GENIEDEPONIE" +}, +{ +"id":464, +"fortune":"DER REGEN WÄSCHT DEIN AUTO. WIE GEPLANT" +}, +{ +"id":465, +"fortune":"DEINE BRETTSPIELE ALTERN IN FOLIE" +}, +{ +"id":466, +"fortune":"DEINE YOGAMATTE LAG ZWEIMAL AUS. FÜR NAPS" +}, +{ +"id":467, +"fortune":"DEINE VERLÄNGERUNGSKABEL VERMEHREN SICH" +}, +{ +"id":468, +"fortune":"DEIN SMARTSPEAKER HAT DINGE GEHÖRT" +}, +{ +"id":469, +"fortune":"DEINE DOSEN UND DECKEL PASSEN NIE. NIE" +}, +{ +"id":470, +"fortune":"DEINE WANDERSCHUHE BEREISEN DEN KOFFERRAUM" +}, +{ +"id":471, +"fortune":"DEIN ORDNER WICHTIG IST EIN HOSPIZ" +}, +{ +"id":472, +"fortune":"DEIN TÜRKRANZ IST JETZT GANZJÄHRIG. FESTLICH" +}, +{ +"id":473, +"fortune":"DIE EINE SCHUBLADE ERKLÄRT SICH NICHT MEHR" +}, +{ +"id":474, +"fortune":"DEIN BILDSCHIRM UM 2 UHR ZEIGT ALLES" +}, +{ +"id":475, +"fortune":"DEIN FEUERLÖSCHER LEBT VON SELBSTVERTRAUEN" +}, +{ +"id":476, +"fortune":"DEIN WASSERZIEL WURDE EINMAL ERREICHT. FEIER" +}, +{ +"id":477, +"fortune":"ÄLTER WERDEN STEHT DIR. DIE ALTERNATIVE NICHT" +}, +{ +"id":478, +"fortune":"DEIN ALTER IST NUR EINE ZAHL. EINE STEIGENDE" +}, +{ +"id":479, +"fortune":"DEINE KNIE MELDEN JETZT DAS WETTER. TALENT" +}, +{ +"id":480, +"fortune":"DEIN RÜCKEN GING ÖFTER RAUS ALS DU" +}, +{ +"id":481, +"fortune":"WEISHEIT KOMMT MIT GRAU. DEINS KAM FRÜH" +}, +{ +"id":482, +"fortune":"DU REIFST WIE WEIN. LIEGEND GELAGERT" +}, +{ +"id":483, +"fortune":"DEINE GEBURTSTAGSKERZEN SIND BRANDGEFÄHRLICH" +}, +{ +"id":484, +"fortune":"DEINE LESEBRILLE BRAUCHT EINE LESEBRILLE" +}, +{ +"id":485, +"fortune":"DEINE PLAYLIST HEIßT JETZT OLDIES. SAGT MAN" +}, +{ +"id":486, +"fortune":"DU STÖHNST BEIM HINSETZEN. EINE QUITTUNG" +}, +{ +"id":487, +"fortune":"DEIN STOFFWECHSEL GING OHNE ABSCHIED" +}, +{ +"id":488, +"fortune":"DU FÜHLST DICH WIE 25. DEIN KATER NICHT" +}, +{ +"id":489, +"fortune":"EIN APFEL AM TAG. ALS KUCHEN. SCHLUPFLOCH" +}, +{ +"id":490, +"fortune":"DEINE HALTUNG BESSERT SICH ZWEIMAL TÄGLICH" +}, +{ +"id":491, +"fortune":"DU SCHLÄFST ACHT STUNDEN. IN DREI NÄCHTEN" +}, +{ +"id":492, +"fortune":"DEINE VITAMINE VERFALLEN UNGENUTZT. JÄHRLICH" +}, +{ +"id":493, +"fortune":"DEINE DIÄT STARTET MONTAG. WELCHER IST OFFEN" +}, +{ +"id":494, +"fortune":"DER ZAHNARZT WEIß DU FLOSSTEST NUR HEUTE" +}, +{ +"id":495, +"fortune":"DEIN RUHEPULS IST SUPER. DU RUHST JA VIEL" +}, +{ +"id":496, +"fortune":"DU WIRST 100. AUS PROTEST" +}, +{ +"id":497, +"fortune":"DEIN SCHLAFTRACKER MELDET FIKTION" +}, +{ +"id":498, +"fortune":"DEINE ALLERGIEN KOMMEN PÜNKTLICH. IMMERHIN" +}, +{ +"id":499, +"fortune":"DEINE VERSTECKTEN TALENTE BLEIBEN VERSTECKT" +}, +{ +"id":500, +"fortune":"DEINE KREATIVITÄT KENNT NUR PAUSEN" +}, +{ +"id":501, +"fortune":"DU HAST EINE GABE. OHNE KASSENBON" +}, +{ +"id":502, +"fortune":"DEIN ROMAN WIRD FERTIG. VON JEMANDEM" +}, +{ +"id":503, +"fortune":"ZUM SCHAFFEN GEBOREN. AUSREDEN ZÄHLEN MIT" +}, +{ +"id":504, +"fortune":"DEIN MEISTERWERK IST IN DIR. ES WOHNT GERN DA" +}, +{ +"id":505, +"fortune":"DEIN GESANG RÜHRT ZU TRÄNEN. BIOLOGIE HALT" +}, +{ +"id":506, +"fortune":"DIE MUSE BESUCHT DICH. SIE RUFT VORHER AN" +}, +{ +"id":507, +"fortune":"DEINE FOTOS FANGEN ROHE VERWIRRUNG EIN" +}, +{ +"id":508, +"fortune":"DU DENKST UM DIE ECKE. DIE ECKE IST BESORGT" +}, +{ +"id":509, +"fortune":"DEINE GEDICHTE BEWEGEN. RICHTUNG AUSGANG" +}, +{ +"id":510, +"fortune":"DEINE FANTASIE IST GROß. DIE UMSETZUNG NICHT" +}, +{ +"id":511, +"fortune":"DU SIEHST DIE WELT ANDERS. LASS DAS PRÜFEN" +}, +{ +"id":512, +"fortune":"DEINE PODCASTIDEE GRÜßT DIE ANDEREN SECHS" +}, +{ +"id":513, +"fortune":"KREATIVITÄT FLIEßT AN DIR VORBEI. WINKEND" +}, +{ +"id":514, +"fortune":"DEINE SKIZZEN GEHÖREN WOHIN. IN DIE SCHUBLADE" +}, +{ +"id":515, +"fortune":"DEIN KOCHEN IST PERFORMANCEKUNST. MIT ALARM" +}, +{ +"id":516, +"fortune":"DEIN DREHBUCH HAT NUR POTENZIELLE ENERGIE" +}, +{ +"id":517, +"fortune":"DIE WELT IST DEINE LEINWAND. NOCH GRUNDIERT" +}, +{ +"id":518, +"fortune":"DEINE PROJEKTE SIND EINE OFFENE TRILOGIE" +}, +{ +"id":519, +"fortune":"DEIN MUSIKGESCHMACK IST TOP. SAGST DU" +}, +{ +"id":520, +"fortune":"DEIN TANZSTIL TROTZT KONVENTION. UND PHYSIK" +}, +{ +"id":521, +"fortune":"DU ERZÄHLST TOLL. DIE ENDEN FEHLEN NOCH" +}, +{ +"id":522, +"fortune":"APOTHEKER RAHMEN DEINE HANDSCHRIFT" +}, +{ +"id":523, +"fortune":"DEINE IDEEN KOMMEN UM 2. WEG UM 8" +}, +{ +"id":524, +"fortune":"DEIN SKIZZENBUCH HÜTET GROßE VERSPRECHEN" +}, +{ +"id":525, +"fortune":"DU KÖNNTEST EIN BUCH SCHREIBEN. EIN HEFT" +}, +{ +"id":526, +"fortune":"DEINE TÖPFERPHASE SCHUF UNVERGESSLICHES" +}, +{ +"id":527, +"fortune":"DEIN INNERER KÜNSTLER WILL ERST KAFFEE" +}, +{ +"id":528, +"fortune":"DEINE FARBWAHL IST MUTIG. SEHR MUTIG" +}, +{ +"id":529, +"fortune":"DEINE GITARRE KENNT DREI AKKORDE GUT" +}, +{ +"id":530, +"fortune":"DEIN SCHAFFENSPROZESS IST UNDOKUMENTIERT" +}, +{ +"id":531, +"fortune":"DEINE SCHALS WURDEN ERFAHRUNGEN. STRICKE FORT" +}, +{ +"id":532, +"fortune":"DU MALST MIT GEFÜHL. DER BODEN STIMMT ZU" +}, +{ +"id":533, +"fortune":"DEIN BONSAI IST JETZT EIN NORMALER BAUM" +}, +{ +"id":534, +"fortune":"DEINE SCHREIBSTIMME IST STARK. IN CHATS" +}, +{ +"id":535, +"fortune":"DEIN GARTEN WÄCHST. DAS UNKRAUT AM BESTEN" +}, +{ +"id":536, +"fortune":"DEINE KALLIGRAFIEPHASE WAR EIN WOCHENENDE" +}, +{ +"id":537, +"fortune":"DEIN HOLZWERK IST EHRLICH. SPLITTER LÜGEN NIE" +}, +{ +"id":538, +"fortune":"DEINE FANFICTION HAT FANS. BEIDE" +}, +{ +"id":539, +"fortune":"DEIN ORIGAMI FÜLLT DAS ALTPAPIER" +}, +{ +"id":540, +"fortune":"DEINE SCHREIBBLOCKADE IST JETZT TRAGEND" +}, +{ +"id":541, +"fortune":"DEIN SAUERTEIG ÜBERLEBTE DAS HOBBY" +}, +{ +"id":542, +"fortune":"DEINE AQUARELLE FANGEN ETWAS EIN. UNKLAR WAS" +}, +{ +"id":543, +"fortune":"DU GLÄNZT BEI ANFÄNGEN. MITTEN LEHNST DU AB" +}, +{ +"id":544, +"fortune":"DEINE UKULELE IST JETZT DEKO. WÜRDEVOLL" +}, +{ +"id":545, +"fortune":"DU BIST DEIN SCHÄRFSTER KRITIKER. STIMMT WOHL" +}, +{ +"id":546, +"fortune":"DEINE KUNST SPRICHT FÜR SICH. LEISE" +}, +{ +"id":547, +"fortune":"DEIN ATELIER IST WIEDER EIN SCHRANK" +}, +{ +"id":548, +"fortune":"DEINE ERSTFASSUNGEN TREFFEN NIE ZWEITE" +}, +{ +"id":549, +"fortune":"DEINE KERZENPHASE HINTERLIEß SPUREN. WACHS" +}, +{ +"id":550, +"fortune":"DEIN OPENMIC DEBÜT BLEIBT BEVORSTEHEND" +}, +{ +"id":551, +"fortune":"DEINE KOSTÜME ZEIGEN HINGABE. ZU KONZEPTEN" +}, +{ +"id":552, +"fortune":"DEINE MAKRAMEEEULE SAH DEINE PROJEKTE" +}, +{ +"id":553, +"fortune":"DEINE SPRACHMEMOS SIND MEIST EINKAUFSLISTEN" +}, +{ +"id":554, +"fortune":"DU WIRST ENTDECKT. AUF DEINEM SOFA" +}, +{ +"id":555, +"fortune":"DEIN MOODBOARD IST NUR MOOD. KEIN BOARD" +}, +{ +"id":556, +"fortune":"DEIN MUSEUMSBESUCH ENDET IM CAFE" +}, +{ +"id":557, +"fortune":"DEINE COVERSONGS SCHLAGEN DIE ORIGINALE" +}, +{ +"id":558, +"fortune":"DEINE SCHREIBMASCHINE STEHT SCHÖN DA. STILL" +}, +{ +"id":559, +"fortune":"DEINE ABSTRAKTE PHASE VERWIRRTE AUCH DICH" +}, +{ +"id":560, +"fortune":"DEIN COMIC HAT EINEN FOLLOWER. HALLO MAMA" +}, +{ +"id":561, +"fortune":"DEIN REIMSCHEMA IST AMBITIONIERT. SEHR" +}, +{ +"id":562, +"fortune":"DEINE BASTELECKE ANNEKTIERTE DAS GÄSTEZIMMER" +}, +{ +"id":563, +"fortune":"DEIN GENIE WIRD VERKANNT. UND NIE GEZEIGT" +}, +{ +"id":564, +"fortune":"DEIN OPUS MAGNUM ZIEHT SEIT JAHREN DURCH" +}, +{ +"id":565, +"fortune":"DEIN INSPIRATIONSORDNER HAT UNTERORDNER" +}, +{ +"id":566, +"fortune":"DEIN KAFFEE IST INZWISCHEN EIN ABSATZ LANG" +}, +{ +"id":567, +"fortune":"ERST KAFFEE. DANN PERSÖNLICHKEIT. FAIR" +}, +{ +"id":568, +"fortune":"DEIN VIERTER KAFFEE BEOBACHTET DICH" +}, +{ +"id":569, +"fortune":"ENTKOFFEINIERT EXISTIERT. DU LEUGNEST ES" +}, +{ +"id":570, +"fortune":"DEINE TASSENSAMMLUNG SPRENGT DEN SCHRANK" +}, +{ +"id":571, +"fortune":"DU NENNST ES BRUNCH. ES WAR SPÄTES FRÜHSTÜCK" +}, +{ +"id":572, +"fortune":"DEINE SALATPHASE ENDETE BEIM DRESSING" +}, +{ +"id":573, +"fortune":"DU PLANST MAHLZEITEN SCHÖN. DANN BESTELLST DU" +}, +{ +"id":574, +"fortune":"DEIN LEIBGERICHT HAT EINEN FAN. DICH" +}, +{ +"id":575, +"fortune":"DER OFENTIMER IST FÜR DICH EIN VORSCHLAG" +}, +{ +"id":576, +"fortune":"DEINE SMOOTHIES VERSTECKEN GEMÜSE. VOR DIR" +}, +{ +"id":577, +"fortune":"DU KOCHST MIT LIEBE. UND ZU VIEL SALZ" +}, +{ +"id":578, +"fortune":"DEINE REZEPTÄNDERUNGEN SIND TAPFER" +}, +{ +"id":579, +"fortune":"KNOBLAUCH MISST DU MIT DEM HERZEN. WIEDER" +}, +{ +"id":580, +"fortune":"DEIN TOAST IST STARK. STARK VERBRANNT" +}, +{ +"id":581, +"fortune":"DESSERT ZUERST. DAS LEBEN IST KURZ. JA JA" +}, +{ +"id":582, +"fortune":"DEINE SNACKSCHUBLADE HAT EINE WARTELISTE" +}, +{ +"id":583, +"fortune":"DER LIEFERDIENST KENNT DICH BEIM NAMEN" +}, +{ +"id":584, +"fortune":"DEINE KÜCHENGERÄTE BILDEN EIN MUSEUM" +}, +{ +"id":585, +"fortune":"DEINE VORRATSDOSEN SIND JETZT ERBSTÜCKE" +}, +{ +"id":586, +"fortune":"DU ISST ANGEBLICH ALLES. DANN DOCH NICHT" +}, +{ +"id":587, +"fortune":"DEINE PIZZAWAHL HAT KRITIKER" +}, +{ +"id":588, +"fortune":"DEIN HUND LIEBT DICH BEDINGUNGSLOS. DRESSIERT" +}, +{ +"id":589, +"fortune":"DEINE KATZE DULDET DICH. EINE HOHE EHRE" +}, +{ +"id":590, +"fortune":"DEIN HAUSTIER ISST BESSER ALS DU" +}, +{ +"id":591, +"fortune":"DEIN HUND HAT MEHR FREUNDE ALS DU" +}, +{ +"id":592, +"fortune":"DIE KATZE WARF ES MIT ABSICHT UM. RESPEKT" +}, +{ +"id":593, +"fortune":"DEIN FISCH KENNT DEINEN PLAN. ER SORGT SICH" +}, +{ +"id":594, +"fortune":"DER HUND FRAß DEINE MOTIVATION" +}, +{ +"id":595, +"fortune":"DEINE KATZE BEWERTET NACHTS DEIN LEBEN" +}, +{ +"id":596, +"fortune":"DEIN VOGEL ZITIERT DEINE SCHLECHTESTEN SÄTZE" +}, +{ +"id":597, +"fortune":"DEIN HAUSTIER HÖRT GUT ZU. UND KOSTENLOS" +}, +{ +"id":598, +"fortune":"REGEN KOMMT. DEINE AUTOWÄSCHE JUBELT" +}, +{ +"id":599, +"fortune":"DEINE WINTERJACKENDEBATTE STARTET IM OKTOBER" +}, +{ +"id":600, +"fortune":"DU PRÜFST DAS WETTER. UND ZIEHST FALSCH AN" +}, +{ +"id":601, +"fortune":"DER FRÜHLING KOMMT. DEINE ALLERGIE AUCH" +}, +{ +"id":602, +"fortune":"DEINE SONNENCREME VERFÄLLT UNGENUTZT" +}, +{ +"id":603, +"fortune":"SCHNEE IST MAGISCH. BIS ZUR SCHAUFEL" +}, +{ +"id":604, +"fortune":"DEINE SCHIRME WOHNEN ALLE WOANDERS" +}, +{ +"id":605, +"fortune":"SMALLTALK ÜBERS WETTER LIEGT DIR JETZT" +}, +{ +"id":606, +"fortune":"DIE VORHERSAGE ÄNDERTE SICH. DEINE PLÄNE NIE" +}, +{ +"id":607, +"fortune":"DEINE GARTENMÖBEL FÜRCHTEN JEDE SAISON" +}, +{ +"id":608, +"fortune":"DAS LAUB FÄLLT. WIE DEINE VORSÄTZE" +}, +{ +"id":609, +"fortune":"DEIN PASSWORT IST DEIN HAUSTIER. MAN WEIß ES" +}, +{ +"id":610, +"fortune":"DU HAST 47 UNGELESENE UPDATES. SIE WARTEN" +}, +{ +"id":611, +"fortune":"DEINE CLOUD IST VOLL. MIT DUPLIKATEN" +}, +{ +"id":612, +"fortune":"DIE AUTOKORREKTUR WUSSTE ES. SIE LEHNTE AB" +}, +{ +"id":613, +"fortune":"DEIN ROUTER BRAUCHT EINEN NEUSTART. DU AUCH" +}, +{ +"id":614, +"fortune":"DEIN SUCHVERLAUF IST EINE SAGA" +}, +{ +"id":615, +"fortune":"DU SCHLOSSEST HEUTE EINEN TAB. FORTSCHRITT" +}, +{ +"id":616, +"fortune":"DEIN SPAMORDNER VERMISST DICH" +}, +{ +"id":617, +"fortune":"DER ALGORITHMUS KENNT DICH GUT. ZU GUT" +}, +{ +"id":618, +"fortune":"DEINE SMARTWATCH EMPFIEHLT ATMEN. SPITZE" +}, +{ +"id":619, +"fortune":"DU TIPPST AM SCHNELLSTEN IM STREIT" +}, +{ +"id":620, +"fortune":"DU HAST DIE GRUPPE STUMM. DIE GRUPPE WEIß ES" +}, +{ +"id":621, +"fortune":"DEINE FESTSTELLTASTE TRAINIERT REGELMÄßIG" +}, +{ +"id":622, +"fortune":"DEIN EMOJIGEBRAUCH WURDE BESPROCHEN" +}, +{ +"id":623, +"fortune":"DEIN WLAN NAME IST DEIN BESTER WITZ" +}, +{ +"id":624, +"fortune":"NEUSTARTEN IST DEIN LEBENSSTIL" +}, +{ +"id":625, +"fortune":"DEIN FITNESSSTUDIO DANKT FÜR DIE SPENDE" +}, +{ +"id":626, +"fortune":"DEIN MARATHONPLAN IST GESPEICHERT. SICHER" +}, +{ +"id":627, +"fortune":"DER BEINTAG RESPEKTIERT DEINE ABWESENHEIT" +}, +{ +"id":628, +"fortune":"DEIN PROTEINPULVER ALTERT IN WÜRDE" +}, +{ +"id":629, +"fortune":"DU DEHNST DICH EINMAL IM JAHR. AMBITIONIERT" +}, +{ +"id":630, +"fortune":"DEIN VEREIN VERLIERT. DU NIMMST ES PERSÖNLICH" +}, +{ +"id":631, +"fortune":"DU KOMMST ZU SPÄT. DAS ZÄHLT ALS CARDIO" +}, +{ +"id":632, +"fortune":"DEIN HEIMSTUDIO LAGERT JETZT WÄSCHE" +}, +{ +"id":633, +"fortune":"DEINE BESTZEIT WAR EINE TEAMLEISTUNG" +}, +{ +"id":634, +"fortune":"DEIN AUFWÄRMEN IST DAS GANZE TRAINING" +}, +{ +"id":635, +"fortune":"DEINE RUHETAGE HABEN RUHETAGE" +}, +{ +"id":636, +"fortune":"SCHLAF IST FÜR STARKE. DU SCROLLST" +}, +{ +"id":637, +"fortune":"DEIN NICKERCHENPLAN IST DEIN WAHRER KALENDER" +}, +{ +"id":638, +"fortune":"DU ZÄHLST SCHAFE. SIE ZÄHLEN DEINE AUSREDEN" +}, +{ +"id":639, +"fortune":"DEIN KISSEN KENNT ALLE GEHEIMNISSE" +}, +{ +"id":640, +"fortune":"EINE FOLGE NOCH WAREN VIER FOLGEN" +}, +{ +"id":641, +"fortune":"DEINE TRÄUME SIND LEBHAFT. DEINE MORGEN NICHT" +}, +{ +"id":642, +"fortune":"DU STELLST SECHS WECKER. DU FÜRCHTEST KEINEN" +}, +{ +"id":643, +"fortune":"AUFSCHIEBEN VOR DEM SCHLAFEN IST AUCH WAHL" +}, +{ +"id":644, +"fortune":"DEINE WOCHENENDSCHLAFSCHULD VERZINST SICH" +}, +{ +"id":645, +"fortune":"DEINE FAMILIE LIEBT DICH. KNAPPE ABSTIMMUNG" +}, +{ +"id":646, +"fortune":"DEINE MUTTER HATTE RECHT. MIT ALLEM" +}, +{ +"id":647, +"fortune":"FAMILIENESSEN TESTEN DEINE DIPLOMATIE" +}, +{ +"id":648, +"fortune":"DEIN GESCHWISTER VERGISST NICHTS. NIE" +}, +{ +"id":649, +"fortune":"DIE VERWANDTEN FRAGEN NACH PLÄNEN. HALT DURCH" +}, +{ +"id":650, +"fortune":"DU BIST DAS LIEBLINGSKIND. UNBESTÄTIGT" +}, +{ +"id":651, +"fortune":"DER FAMILIENCHAT SCHLÄFT NIE" +}, +{ +"id":652, +"fortune":"OMA FÜTTERT DICH. WIDERSTAND IST ZWECKLOS" +}, +{ +"id":653, +"fortune":"DEINE FLACHWITZE VERERBEN SICH LÜCKENLOS" +}, +{ +"id":654, +"fortune":"DIE SITZORDNUNG AN FEIERTAGEN IST POLITIK" +}, +{ +"id":655, +"fortune":"DEIN NACHBAR WINKT. DER ZAUN WAR SEINE IDEE" +}, +{ +"id":656, +"fortune":"DIE HAUSVERWALTUNG SAH DEINEN RASEN. WIEDER" +}, +{ +"id":657, +"fortune":"DEINE NACHBARN KENNEN DEINEN TAGESABLAUF" +}, +{ +"id":658, +"fortune":"GELIEHENES WERKZEUG WIRD IRGENDWANN GESCHENK" +}, +{ +"id":659, +"fortune":"DEINE TÜRKAMERA SAH DEINEN BESTEN SPRINT" +}, +{ +"id":660, +"fortune":"DEIN ARBEITSWEG BILDET CHARAKTER. TÄGLICH" +}, +{ +"id":661, +"fortune":"DER STAU IST NICHT PERSÖNLICH. DEINER SCHON" +}, +{ +"id":662, +"fortune":"DEIN PARKSTIL LÖST GESPRÄCHE AUS" +}, +{ +"id":663, +"fortune":"DEIN NAVI SAGT NEUBERECHNUNG. MIT GEFÜHL" +}, +{ +"id":664, +"fortune":"DU FÄHRST WIE DU LEBST. BLINKEN OPTIONAL" +}, +{ +"id":665, +"fortune":"DEIN TANK LÄUFT AUF HOFFNUNG. UND DÄMPFEN" +}, +{ +"id":666, +"fortune":"DIE LINKE SPUR IST ZUM ÜBERHOLEN. NUR SO" +}, +{ +"id":667, +"fortune":"DEINE AUTOPLAYLIST HATTE 2016 IHREN GIPFEL" +}, +{ +"id":668, +"fortune":"DEINE HUPE ARBEITET MEHR ALS DEIN BLINKER" +}, +{ +"id":669, +"fortune":"DEIN EINPARKEN ZIEHT PUBLIKUM AN" +}, +{ +"id":670, +"fortune":"DIE ZEIT FLIEGT. DEINE MELDETE EINEN FLUGPLAN" +}, +{ +"id":671, +"fortune":"AUFSCHIEBEN IST EINE KUNST. DU LEHRST SIE" +}, +{ +"id":672, +"fortune":"DU MACHST ES MORGEN. MORGEN WEIß BESCHEID" +}, +{ +"id":673, +"fortune":"FRISTEN NAHEN. DU GENIEßT DIE AUSSICHT" +}, +{ +"id":674, +"fortune":"DEINE FÜNF MINUTEN SIND FREMDE STUNDEN" +}, +{ +"id":675, +"fortune":"SPÄTER IST DEINE LIEBSTE ZEITZONE" +}, +{ +"id":676, +"fortune":"DU BEENDEST DINGE. IRGENDWANN IST EIN WORT" +}, +{ +"id":677, +"fortune":"DEINE IRGENDWANNLISTE BRAUCHT EIN REGAL" +}, +{ +"id":678, +"fortune":"ZEITMANAGEMENT IST EINE FÄHIGKEIT. NOTIERT" +}, +{ +"id":679, +"fortune":"DU BIST EINMAL IM JAHR FRÜH. ES VERWIRRT ALLE" +}, +{ +"id":680, +"fortune":"DEINE PÜNKTLICHKEIT IST LOKAL BEKANNT. LEIDER" +}, +{ +"id":681, +"fortune":"DU LIEST DIES STATT ZU ARBEITEN. NOTIERT" +}, +{ +"id":682, +"fortune":"DEINE AUFMERKSAMKEIT IST. OH EIN VOGEL" +}, +{ +"id":683, +"fortune":"NOCH EINE MINUTE. BERÜHMTE LETZTE WORTE" +}, +{ +"id":684, +"fortune":"JEDE MAIL BEGINNT MIT ENTSCHULDIGE DIE SPÄTE" +}, +{ +"id":685, +"fortune":"DU ANTWORTEST IN GEOLOGISCHER ZEIT" +}, +{ +"id":686, +"fortune":"DEINE LESEBESTÄTIGUNGEN VERRATEN DICH" +}, +{ +"id":687, +"fortune":"DU LIEßT SIE AUF GELESEN. SIE SAHEN ES" +}, +{ +"id":688, +"fortune":"DEINE TIPPBLASE ERSCHIEN. UND VERSCHWAND" +}, +{ +"id":689, +"fortune":"DU SAGTEST MAN MÜSSTE MAL. MAN WIRD NIE" +}, +{ +"id":690, +"fortune":"DEINE SPRACHNOTIZEN SIND JETZT PODCASTS" +}, +{ +"id":691, +"fortune":"DU WINKTEST ZURÜCK. ES GALT JEMAND ANDEREM" +}, +{ +"id":692, +"fortune":"DEIN SMALLTALK HAT EIN DREHBUCH. ÜB ES" +}, +{ +"id":693, +"fortune":"DU SAGST WIR SOLLTEN MAL WIEDER. KLASSIKER" +}, +{ +"id":694, +"fortune":"DEIN HANDY IST AUF LAUTLOS. SEIT JAHREN" +}, +{ +"id":695, +"fortune":"DU MACHST SCREENSHOTS STATT ZU ANTWORTEN" +}, +{ +"id":696, +"fortune":"DEINE MEINUNG WURDE NOTIERT. UND ENTSORGT" +}, +{ +"id":697, +"fortune":"EIN KOMPLIMENT NAHT. NIMM ES UNBEHOLFEN AN" +}, +{ +"id":698, +"fortune":"DEIN NEUTRALER BLICK HAT EINEN RUF" +}, +{ +"id":699, +"fortune":"EINIG IN DER UNEINIGKEIT. DU VERLORST" +}, +{ +"id":700, +"fortune":"DEIN AUGENROLLEN IST IRGENDWIE HÖRBAR" +}, +{ +"id":701, +"fortune":"DEIN SEUFZER SPRACH GERADE BÄNDE" +}, +{ +"id":702, +"fortune":"DU SAGTEST PASST SCHON. NICHTS PASSTE" +}, +{ +"id":703, +"fortune":"DEIN DAUMEN HOCH BEENDETE FREUNDSCHAFTEN" +}, +{ +"id":704, +"fortune":"KEIN STRESS SAGTEST DU. GESTRESST" +}, +{ +"id":705, +"fortune":"DEIN SCHULTERZUCKEN LEISTET SCHWERSTARBEIT" +}, +{ +"id":706, +"fortune":"DU HAST DURCHGELÄCHELT. KNAPP. MAN SAH ES" +}, +{ +"id":707, +"fortune":"DEINE GEDULD HAT EINEN LADEBALKEN. ER HÄNGT" +}, +{ +"id":708, +"fortune":"DU VERGABST IHNEN. DIE TABELLE BLEIBT" +}, +{ +"id":709, +"fortune":"DEINE ANDEUTUNGEN BRAUCHEN UNTERTITEL" +}, +{ +"id":710, +"fortune":"DU HÄTTEST ES FAST GESAGT. DAS FAST WAR LAUT" +}, +{ +"id":711, +"fortune":"DEIN SCHWEIGEN HAT EINE AGENDA. WIE IMMER" +}, +{ +"id":712, +"fortune":"DU LIEßT ES GUT SEIN. IN DEINER AKTE" +}, +{ +"id":713, +"fortune":"DEINE GELASSENHEIT WIRKT VERDÄCHTIG" +}, +{ +"id":714, +"fortune":"DU NAHMST DEN HOHEN WEG. MIT KOMMENTAR" +}, +{ +"id":715, +"fortune":"DEIN FEEDBACKSANDWICH IST NUR BROT" +}, +{ +"id":716, +"fortune":"DU HÖRTEST ZU. UND MACHTEST ES TROTZDEM" +}, +{ +"id":717, +"fortune":"GUTEN MORGEN. DIE LATTE IST SCHON GERISSEN" +}, +{ +"id":718, +"fortune":"DEIN OPTIMISMUS IST HEUTE NIEDLICH" +}, +{ +"id":719, +"fortune":"HEUTE IST DEIN TAG. TEIL IHN NETT" +}, +{ +"id":720, +"fortune":"DU BIST SO AUFGEWACHT. BEILEID" +}, +{ +"id":721, +"fortune":"DER SPIEGEL SAH DICH ZUERST. ER VERARBEITET" +}, +{ +"id":722, +"fortune":"DEINE FRISUR HAT HEUTE EIGENE PLÄNE" +}, +{ +"id":723, +"fortune":"DER MONTAG SAH DICH KOMMEN. ER ÜBTE" +}, +{ +"id":724, +"fortune":"DEIN WOCHENENDE IST VORBEI. ES GRÜßT" +}, +{ +"id":725, +"fortune":"DER FREITAG LIEBT DICH. ES BERUHT AUF BEIDEM" +}, +{ +"id":726, +"fortune":"DEINE MITTAGSPAUSE VERDIENT EINE MEDAILLE" +}, +{ +"id":727, +"fortune":"DEIN OUTFIT SAGT MÜHE. DIE UHR SAGT HEKTIK" +}, +{ +"id":728, +"fortune":"DU TRÄGST PASSENDE SOCKEN. EIN FESTAKT" +}, +{ +"id":729, +"fortune":"DEINE SONNENBRILLE VERSTECKT NICHTS" +}, +{ +"id":730, +"fortune":"KLEIDE DICH FÜR DEN TRAUMJOB. SCHLAFTESTER" +}, +{ +"id":731, +"fortune":"DEIN BÜGELBRETT GING IN RENTE" +}, +{ +"id":732, +"fortune":"FALTEN SIND EINE TEXTUR. DEIN BESCHLUSS" +}, +{ +"id":733, +"fortune":"DEIN MODEZYKLUS RICHTET SICH NACH WÄSCHE" +}, +{ +"id":734, +"fortune":"DEINE SCHUHE PASSEN. DER ANLASS NICHT" +}, +{ +"id":735, +"fortune":"DU GEWINNST DEN STREIT. UNTER DER DUSCHE" +}, +{ +"id":736, +"fortune":"DEIN KONTER KAM AN. DREI TAGE SPÄTER" +}, +{ +"id":737, +"fortune":"DEIN WITZ IST SCHNELL. DEIN TIMING NICHT" +}, +{ +"id":738, +"fortune":"DIE PERFEKTE ANTWORT KAM UM MITTERNACHT" +}, +{ +"id":739, +"fortune":"DEIN INNERER MONOLOG BRAUCHT EINEN LEKTOR" +}, +{ +"id":740, +"fortune":"DEINE HEIßEN TAKES SIND ZIMMERWARM" +}, +{ +"id":741, +"fortune":"DEINE MEINUNGEN KOMMEN VORGEWÄRMT" +}, +{ +"id":742, +"fortune":"DEIN DEBATTENSTIL BASIERT AUF LAUTSTÄRKE" +}, +{ +"id":743, +"fortune":"DU PRÜFST FAKTEN. NUR FREMDE" +}, +{ +"id":744, +"fortune":"DEIN QUIZWISSEN GLÄNZT NUR AUF PARTYS" +}, +{ +"id":745, +"fortune":"EINE ENTSCHEIDUNG NAHT. WIRF EINE MÜNZE" +}, +{ +"id":746, +"fortune":"DEINE PROKONTRALISTE HAT EINE SPALTE" +}, +{ +"id":747, +"fortune":"DU FRAGTEST ALLE. UND MACHTEST DEINS" +}, +{ +"id":748, +"fortune":"DEIN BAUCH BRAUCHT EINE ZWEITMEINUNG" +}, +{ +"id":749, +"fortune":"DU SCHLIEFST DRÜBER. DAS PROBLEM WACHTE" +}, +{ +"id":750, +"fortune":"DEIN PLAN B IST PLAN A MIT REUE" +}, +{ +"id":751, +"fortune":"DU WÄHLTEST CHAOS. WIEDER. KONSEQUENT" +}, +{ +"id":752, +"fortune":"DEIN NOTFALLPLAN BRAUCHT EINEN NOTFALLPLAN" +}, +{ +"id":753, +"fortune":"BINDUNG MACHT DIR ANGST. DIE KARTE AUCH" +}, +{ +"id":754, +"fortune":"DU BESTELLTEST DASSELBE. ABENTEUERLICH" +}, +{ +"id":755, +"fortune":"VERÄNDERUNG KOMMT. DU WIRST DICH WEHREN" +}, +{ +"id":756, +"fortune":"EINE ÜBERRASCHUNG NAHT. DU HASST SOWAS" +}, +{ +"id":757, +"fortune":"GUTE DINGE KOMMEN ZU DRITT. DEINE IM STAU" +}, +{ +"id":758, +"fortune":"DEIN HOROSKOP KÜNDIGTE. BURNOUT" +}, +{ +"id":759, +"fortune":"MERKUR IST RÜCKLÄUFIG. PRAKTISCHE AUSREDE" +}, +{ +"id":760, +"fortune":"DEIN KARTENLEGER BRAUCHTE DANACH URLAUB" +}, +{ +"id":761, +"fortune":"DIE KRISTALLKUGEL BESCHLUG. ABSICHTLICH" +}, +{ +"id":762, +"fortune":"DEIN HANDLESER BAT UM HANDSCHUHE" +}, +{ +"id":763, +"fortune":"DEINE TEEBLÄTTER BUCHSTABIERTEN NAJA" +}, +{ +"id":764, +"fortune":"DEINE VISIONSSUCHE FAND DAS SOFA" +}, +{ +"id":765, +"fortune":"DAS UNIVERSUM TESTET DICH. OFFENES BUCH" +}, +{ +"id":766, +"fortune":"DEINE MANIFESTATIONEN BRAUCHEN EINEN BÜRGEN" +}, +{ +"id":767, +"fortune":"DEINE CHAKREN ZEIGEN ZUM KÜHLSCHRANK" +}, +{ +"id":768, +"fortune":"DEIN ZENGARTEN HAT UNKRAUT. IRONISCH" +}, +{ +"id":769, +"fortune":"DEIN MANTRA IST NOCH FÜNF MINUTEN" +}, +{ +"id":770, +"fortune":"DEIN DRITTES AUGE BRAUCHT AUCH EINE BRILLE" +}, +{ +"id":771, +"fortune":"DEINE ENERGIEREINIGUNG VERGAß EINE STELLE" +}, +{ +"id":772, +"fortune":"DEIN RÄUCHERWERK LÖSTE DEN ALARM AUS. PASSEND" +}, +{ +"id":773, +"fortune":"INNERER FRIEDEN IST NAH. ANDERE TÜR" +}, +{ +"id":774, +"fortune":"EIN FREMDER LÄCHELT DICH AN. BLEIB RUHIG" +}, +{ +"id":775, +"fortune":"JEMAND DACHTE HEUTE AN DICH. KURZ" +}, +{ +"id":776, +"fortune":"DU GINGST JEMANDEM DURCH DEN KOPF. ZÜGIG" +}, +{ +"id":777, +"fortune":"EIN BEWUNDERER BEOBACHTET DICH. DEINE KATZE" +}, +{ +"id":778, +"fortune":"DU WURDEST HEUTE ERWÄHNT. KONTEXT UNKLAR" +}, +{ +"id":779, +"fortune":"DEIN NAME FIEL. DER RAUM WURDE STILL" +}, +{ +"id":780, +"fortune":"JEMAND VERMISST DICH. OHNE DEINE NUMMER" +}, +{ +"id":781, +"fortune":"DU BIST UNVERGESSLICH. VIELE VERSUCHTEN ES" +}, +{ +"id":782, +"fortune":"DEINE ABWESENHEIT FIEL AUF. POSITIV" +}, +{ +"id":783, +"fortune":"MAN SPRICHT ÜBER DICH. LASS ES SO" +}, +{ +"id":784, +"fortune":"DEIN RUF KAM VOR DIR AN. ENTSCHULDIGE DICH" +}, +{ +"id":785, +"fortune":"DU HINTERLIEßT EINDRUCK. DELLEN ZÄHLEN" +}, +{ +"id":786, +"fortune":"JEMAND ZITIERTE DICH HEUTE. FALSCH" +}, +{ +"id":787, +"fortune":"DU INSPIRIERTEST JEMANDEN. ZUM GEGENTEIL" +}, +{ +"id":788, +"fortune":"DEIN VERMÄCHTNIS IST SICHER. KEINER PRÜFT ES" +}, +{ +"id":789, +"fortune":"DIE GESCHICHTE KENNT DICH. ALS FUßNOTE" +}, +{ +"id":790, +"fortune":"DEINE BIOGRAFIE BRAUCHT NOCH HANDLUNG" +}, +{ +"id":791, +"fortune":"DEIN AUTOGRAMM IST WERTLOS. VORERST" +}, +{ +"id":792, +"fortune":"RUHM WARTET. LOKALER RUHM. SEHR LOKAL" +}, +{ +"id":793, +"fortune":"DEINE 15 MINUTEN LADEN NOCH" +}, +{ +"id":794, +"fortune":"DU FINDEST DEINE SCHLÜSSEL. WO DU SIE LIEßT" +}, +{ +"id":795, +"fortune":"DEIN HANDY IST IN DER ANDEREN HAND. WIEDER" +}, +{ +"id":796, +"fortune":"DU BETRATST DEN RAUM. DER GRUND GING RAUS" +}, +{ +"id":797, +"fortune":"DEIN GEDÄCHTNIS IST GUT. ES KURATIERT NUR" +}, +{ +"id":798, +"fortune":"DU VERGAßT WARUM DER KÜHLSCHRANK OFFEN IST" +}, +{ +"id":799, +"fortune":"DEINE MERKZETTEL BRAUCHEN EIN ARCHIV" +}, +{ +"id":800, +"fortune":"DIE MELODIE BLIEB. DIE AUFGABE NICHT" +}, +{ +"id":801, +"fortune":"DEIN EINKAUFSGEDÄCHTNIS ENDET IM LADEN" +}, +{ +"id":802, +"fortune":"DAS GESICHT KENNST DU. DER NAME FLOH. LAUF" +}, +{ +"id":803, +"fortune":"DEIN HIRN HAT AUCH TABS OFFEN. EINGEFROREN" +}, +{ +"id":804, +"fortune":"EINE TOLLE IDEE KAM UND GING. UNNOTIERT" +}, +{ +"id":805, +"fortune":"DU SCHLOSSEST DIE TÜR AB. VERMUTLICH. PRÜF" +}, +{ +"id":806, +"fortune":"DEIN GEDANKENZUG FUHR OHNE DICH AB" +}, +{ +"id":807, +"fortune":"DU GINGST ZURÜCK. DER WEG FÜHRTE ZU SNACKS" +}, +{ +"id":808, +"fortune":"DEIN PASSWORTRESET BRAUCHT EINEN RESET" +}, +{ +"id":809, +"fortune":"DAS LEBEN IST KURZ. DEINE MEETINGS NICHT" +}, +{ +"id":810, +"fortune":"DAS LEBEN GAB DIR ZITRONEN. DU REKLAMIERST" +}, +{ +"id":811, +"fortune":"DAS LEBEN IST EINE REISE. DEINE BRAUCHT NAVI" +}, +{ +"id":812, +"fortune":"DAS LEBEN PASSIERT WÄHREND DU SCROLLST" +}, +{ +"id":813, +"fortune":"DAS LEBEN HAT KEINE FERNBEDIENUNG. STEH AUF" +}, +{ +"id":814, +"fortune":"DAS LEBEN IST KEIN RENNEN. DU PARKTEST EH" +}, +{ +"id":815, +"fortune":"DAS LEBEN IST EIN GESCHENK. HEB DEN BON AUF" +}, +{ +"id":816, +"fortune":"DAS LEBEN BELOHNT TATEN. DEINS SCHRIEB MEMOS" +}, +{ +"id":817, +"fortune":"DEINE GESCHICHTE HAT LOGIKLÖCHER. STEH DAZU" +}, +{ +"id":818, +"fortune":"DEIN KAPITEL ZWEI WARTET AUF KAPITEL EINS" +}, +{ +"id":819, +"fortune":"DEIN ERZÄHLER KLINGT NEUERDINGS MÜDE" +}, +{ +"id":820, +"fortune":"DEIN CLIFFHANGER IST NUR UNENTSCHLOSSENHEIT" +}, +{ +"id":821, +"fortune":"MORGEN IST EIN NEUER TAG. GLEICHES DU" +}, +{ +"id":822, +"fortune":"GESTERN RIEF AN. ES WILL NICHTS ZURÜCK" +}, +{ +"id":823, +"fortune":"HEUTE WIRD PRODUKTIV. AB SPÄTER" +}, +{ +"id":824, +"fortune":"DIESE WOCHE HAT POTENZIAL. HATTE" +}, +{ +"id":825, +"fortune":"NÄCHSTEN MONAT STARTEST DU. ER WEIß ES" +}, +{ +"id":826, +"fortune":"DAS JAHR IST HALB UM. DEINE ZIELE UNBERÜHRT" +}, +{ +"id":827, +"fortune":"BALD IST KEIN DATUM. DU NUTZT ES SO" +}, +{ +"id":828, +"fortune":"IRGENDWANN IST DEINE LIEBLINGSFRIST" +}, +{ +"id":829, +"fortune":"DER RICHTIGE MOMENT KOMMT NIE. PRAKTISCH" +}, +{ +"id":830, +"fortune":"DEIN GELEGENHEITSFENSTER HAT JALOUSIEN" +}, +{ +"id":831, +"fortune":"GEDULD KOMMT ZU DEM DER WARTET. IRONISCH" +}, +{ +"id":832, +"fortune":"DU BIST GLEICH DRAN. DIE SCHLANGE GING WEITER" +}, +{ +"id":833, +"fortune":"DU WARTEST AUF EIN ZEICHEN. DIES IST KEINS" +}, +{ +"id":834, +"fortune":"HALT DEN GEDANKEN. GERN FÜR IMMER" +}, +{ +"id":835, +"fortune":"DEINE PAUSETASTE IST DEINE LIEBSTE" +}, +{ +"id":836, +"fortune":"DU MERKST ES DIR VOR. DAS MERKBUCH IST VOLL" +}, +{ +"id":837, +"fortune":"ES IST AUF DEINEM RADAR. DAS RADAR IST AUS" +}, +{ +"id":838, +"fortune":"DU KÜMMERST DICH DRUM. ES WARTET NICHT MEHR" +}, +{ +"id":839, +"fortune":"DER BALL LIEGT BEI DIR. ER SETZT STAUB AN" +}, +{ +"id":840, +"fortune":"DEINE NACHFRAGE BRAUCHT EINE NACHFRAGE" +}, +{ +"id":841, +"fortune":"DIES IST DEINE ERINNERUNG. DIE VIERTE" +}, +{ +"id":842, +"fortune":"DEINE ABSICHTEN SIND GUT. UND ORTSFEST" +}, +{ +"id":843, +"fortune":"MÜHE WURDE GEGEBEN. EIN RUNDUNGSFEHLER" +}, +{ +"id":844, +"fortune":"DU GABST 100 PROZENT. DEINES MINIMUMS" +}, +{ +"id":845, +"fortune":"DEIN RATEN SCHLUG DEIN RECHERCHIEREN. WIEDER" +}, +{ +"id":846, +"fortune":"FAST RICHTIG IST DEIN MARKENZEICHEN" +}, +{ +"id":847, +"fortune":"GUT GENUG GING RAUS. PERFEKT SCHMOLLT" +}, +{ +"id":848, +"fortune":"DEINE ABKÜRZUNGEN NAHMEN DEN LANGEN WEG" +}, +{ +"id":849, +"fortune":"DU IMPROVISIERTEST. DAS ERGEBNIS AUCH" +}, +{ +"id":850, +"fortune":"DEIN FREESTYLE HAT VIELFLIEGERMEILEN" +}, +{ +"id":851, +"fortune":"DETAILS ZÄHLEN. DEINE SIND IM STREIK" +}, +{ +"id":852, +"fortune":"DEIN ROHENTWURF GING IN PRODUKTION" +}, +{ +"id":853, +"fortune":"DU HAST ES ÜBERFLOGEN. MAN SIEHT ES" +}, +{ +"id":854, +"fortune":"DEIN KORREKTURLESEN ÜBERSAH AUCH DIES" +}, +{ +"id":855, +"fortune":"FAST FERTIG IST DEINE MELDEADRESSE" +}, +{ +"id":856, +"fortune":"DEINE CHECKLISTE HAT ÜBERLEBENDE" +}, +{ +"id":857, +"fortune":"FERTIG SCHLÄGT PERFEKT. VERSUCH EINS DAVON" +}, +{ +"id":858, +"fortune":"DEINE STANDARDS RUTSCHTEN. MIT ANSAGE" +}, +{ +"id":859, +"fortune":"MITTELMAß KOSTET AUCH MÜHE. GUT GEMACHT" +}, +{ +"id":860, +"fortune":"DU ÜBERTRAFST DIE ERWARTUNGEN. SIE WAREN TIEF" +}, +{ +"id":861, +"fortune":"DEIN MINIMUM HAT EINE TREUE FANBASIS" +}, +{ +"id":862, +"fortune":"DU LEGTEST DIE LATTE HÖHER. VOM BODEN AUS" +}, +{ +"id":863, +"fortune":"KONSTANZ IST ALLES. DEINE IST KONSTANT" +}, +{ +"id":864, +"fortune":"FORTSCHRITT GAB ES. SEITWÄRTS. ABER GAB ES" +}, +{ +"id":865, +"fortune":"DEINE KURVE IST STABIL. FLACH. STABIL" +}, +{ +"id":866, +"fortune":"VORWÄRTS UND AUFWÄRTS. ODER SEITLICH. LOS" +}, +{ +"id":867, +"fortune":"DU HAST GESCHWENKT. RICHTUNG WAR ZUFALL" +}, +{ +"id":868, +"fortune":"DEIN SCHWUNG NAHM SICH FREI" +}, +{ +"id":869, +"fortune":"VOLLGAS VORAUS. DEIN GAS. SO SANFT" +}, +{ +"id":870, +"fortune":"DU KAMST GEHEND INS ROLLEN. FLOTT SOGAR" +}, +{ +"id":871, +"fortune":"DEIN SPRINT WURDE EIN SPAZIERGANG. MALERISCH" +}, +{ +"id":872, +"fortune":"KLEINE SCHRITTE ZÄHLEN. DEINE SIND WINZIG" +}, +{ +"id":873, +"fortune":"DU BIST IM FLOW. EIN GEMÄCHLICHER FLOW" +}, +{ +"id":874, +"fortune":"DEINE SERIE HÄLT AN. IRGENDEINE SERIE" +}, +{ +"id":875, +"fortune":"DER SCHWUNG BAUT SICH AUF. WOANDERS" +}, +{ +"id":876, +"fortune":"DIE ZIELLINIE WANDERTE. DU AUCH. RÜCKWÄRTS" +}, +{ +"id":877, +"fortune":"DEIN MARATHON IST EINE SNACKPAUSENKETTE" +}, +{ +"id":878, +"fortune":"DU ÜBERSCHRITTEST EINE LINIE. NICHT DAS ZIEL" +}, +{ +"id":879, +"fortune":"DER SIEG IST NAH. DEFINIERE SIEG GROßZÜGIG" +}, +{ +"id":880, +"fortune":"DU GEWANNST DEN STREIT. DER RAUM WAR LEER" +}, +{ +"id":881, +"fortune":"CHAMPIONS TRAINIEREN TÄGLICH. DU SCHAUST ZU" +}, +{ +"id":882, +"fortune":"DEIN POKALREGAL EHRT DIE TEILNAHME" +}, +{ +"id":883, +"fortune":"DEIN COMEBACK KOMMT NÄCHSTE SAISON. IMMER" +}, +{ +"id":884, +"fortune":"DIE AUßENSEITERSTORY BRAUCHT EINEN VERSUCH" +}, +{ +"id":885, +"fortune":"DEINE RIVALEN FRAGEN NICHT MEHR NACH" +}, +{ +"id":886, +"fortune":"DER RUHM WARTET. ER HAT DOPPELT GEBUCHT" +}, +{ +"id":887, +"fortune":"DU SCHAFFTEST ES IN DIE HIGHLIGHTS. ALS PANNE" +}, +{ +"id":888, +"fortune":"GLÜCK IST EINE LEITER. DU HAST EINEN HOCKER" +}, +{ +"id":889, +"fortune":"DEIN CHARISMA WIRKT NUR OFFLINE" +}, +{ +"id":890, +"fortune":"DU BIST LIMITIERTE AUFLAGE. SEHR LIMITIERT" +}, +{ +"id":891, +"fortune":"NORMAL IST EINE EINSTELLUNG. DEINE IST EIGEN" +}, +{ +"id":892, +"fortune":"DU BIST NICHT FAUL. NUR IM SPARMODUS" +}, +{ +"id":893, +"fortune":"SARKASMUS IST DEINE ZWEITSPRACHE. FLIEßEND" +}, +{ +"id":894, +"fortune":"DU BIST TECHNISCH KORREKT. DIE SCHLIMMSTE ART" +}, +{ +"id":895, +"fortune":"DEINE AUSREDEN ZEIGEN ECHTES HANDWERK" +}, +{ +"id":896, +"fortune":"DU HATTEST DEINEN HÖHEPUNKT. UNGÜNSTIG FRÜH" +}, +{ +"id":897, +"fortune":"DEINE LIEBLINGSSERIE LÄUFT ZUM NEUNTEN MAL" +}, +{ +"id":898, +"fortune":"DU GIBST DEIN BESTES. DAS BESTE SCHWANKT" +}, +{ +"id":899, +"fortune":"DEIN POTENZIAL RIEF AN. KEINE NACHRICHT" +}, +{ +"id":900, +"fortune":"DU BIST BESONDERS. WIE ALLE ANDEREN" +}, +{ +"id":901, +"fortune":"DEINE ENERGIE IST HEUTE DEKORATIV" +}, +{ +"id":902, +"fortune":"DU BIST DIE HAUPTFIGUR. EINER NEBENQUEST" +}, +{ +"id":903, +"fortune":"DEIN HELDENBOGEN IST IN VORPRODUKTION" +}, +{ +"id":904, +"fortune":"DAS ORAKEL VON DELPHI HÄTTE GESCHWIEGEN" +}, +{ +"id":905, +"fortune":"EIN LÄCHELN KOSTET NICHTS. DEINS WIRKT TEUER" +}, +{ +"id":906, +"fortune":"DEINE KOMFORTSERIE KENNT DICH AUSWENDIG" +}, +{ +"id":907, +"fortune":"DU HAST RECHT. STATISTISCH IRGENDWANN MAL" +}, +{ +"id":908, +"fortune":"DEINE LOGIK IST WASSERDICHT. FAST" +}, +{ +"id":909, +"fortune":"DU ARGUMENTIERST MIT GEFÜHL. NUR DAMIT" +}, +{ +"id":910, +"fortune":"DEIN STANDPUNKT IST FEST. WIE BETON. GENAU SO" +}, +{ +"id":911, +"fortune":"DU BIST OFFEN FÜR KRITIK. THEORETISCH" +}, +{ +"id":912, +"fortune":"DEINE SELBSTIRONIE SCHÜTZT DICH. KNAPP" +}, +{ +"id":913, +"fortune":"DU NIMMST NICHTS PERSÖNLICH. AUßER ALLES" +}, +{ +"id":914, +"fortune":"DEIN DICKES FELL HAT DÜNNE STELLEN" +}, +{ +"id":915, +"fortune":"DU STEHST ÜBER DEN DINGEN. AUF ZEHENSPITZEN" +}, +{ +"id":916, +"fortune":"DEIN EGO PASST KNAPP DURCH DIE TÜR" +}, +{ +"id":917, +"fortune":"DU BIST BODENSTÄNDIG. DER BODEN WIDERSPRICHT" +}, +{ +"id":918, +"fortune":"DEINE BESCHEIDENHEIT ERWÄHNT SICH SELBST" +}, +{ +"id":919, +"fortune":"DU HÖRST AUF DEIN HERZ. ES BRAUCHT EINEN RAT" +}, +{ +"id":920, +"fortune":"DEIN GEWISSEN IST REIN. UND UNGENUTZT" +}, +{ +"id":921, +"fortune":"DU BEREUST NICHTS. DIE LISTE SAGT ANDERES" +}, +{ +"id":922, +"fortune":"DEIN STOLZ FÄHRT AUFZUG. NIE TREPPE" +}, +{ +"id":923, +"fortune":"DU BIST GEERDET. WIE EIN DRACHEN" +}, +{ +"id":924, +"fortune":"DEIN OPTIMISMUS HAT EINE RÜCKGABEFRIST" +}, +{ +"id":925, +"fortune":"DU SIEHST DAS GLAS HALB VOLL. ES IST LEER" +}, +{ +"id":926, +"fortune":"DEINE GEDULD IST GRENZENLOS. BIS MITTAG" +}, +{ +"id":927, +"fortune":"DU BIST FLEXIBEL. WIE EIN BAUKRAN" +}, +{ +"id":928, +"fortune":"DEIN HUMOR IST TROCKEN. WIE DEIN BASILIKUM" +}, +{ +"id":929, +"fortune":"DU BIST SPONTAN. NACH VORANMELDUNG" +}, +{ +"id":930, +"fortune":"DEIN FREIGEIST HAT KERNARBEITSZEITEN" +}, +{ +"id":931, +"fortune":"DU LEBST IM MOMENT. IM FALSCHEN MEIST" +}, +{ +"id":932, +"fortune":"DEIN MUT KOMMT IN SCHÜBEN. KLEINEN" +}, +{ +"id":933, +"fortune":"DIESER KEKS ENTHÄLT KEINE WEISHEIT. ZUCKER" +}, +{ +"id":934, +"fortune":"DAS GLÜCK DAS DU SUCHST IST IM ANDEREN KEKS" +}, +{ +"id":935, +"fortune":"ES IST DUNKEL IN DIESEM KEKS. UND STILL" +}, +{ +"id":936, +"fortune":"DIESER SPRUCH BLIEB ABSICHTLICH LEER" +}, +{ +"id":937, +"fortune":"KONFUZIUS SAGT NICHTS ÜBER DICH. BEWUSST" +}, +{ +"id":938, +"fortune":"DIESER SPRUCH WURDE ZURÜCKGERUFEN. DU AUCH" +}, +{ +"id":939, +"fortune":"DU LIEST DAS GLEICH LAUT VOR. UNGEFRAGT" +}, +{ +"id":940, +"fortune":"DER KEKS WUSSTE DASS DU DEN LETZTEN NIMMST" +}, +{ +"id":941, +"fortune":"MANCHE KRIEGEN WEISHEIT. DU NAHMST DESSERT" +}, +{ +"id":942, +"fortune":"DIES IST EIN KEKS. KEIN LEBENSCOACH" +}, +{ +"id":943, +"fortune":"DEINE GLÜCKSZAHLEN SIND AUSGESTIEGEN" +}, +{ +"id":944, +"fortune":"DER KEKS BEWUNDERT DEINEN OPTIMISMUS" +}, +{ +"id":945, +"fortune":"DU HAST DEN KEKS GESCHÜTTELT. ER SPÜRTE DAS" +}, +{ +"id":946, +"fortune":"FRAG SPÄTER NOCHMAL. ODER LASS ES" +}, +{ +"id":947, +"fortune":"DIESER KEKS WURDE OHNE ERWARTUNG GEBACKEN" +}, +{ +"id":948, +"fortune":"DER SPRUCHSCHREIBER HAT PAUSE. ER URTEILT" +}, +{ +"id":949, +"fortune":"HEUTE KEINE WEISHEIT. LIEFERENGPASS" +}, +{ +"id":950, +"fortune":"DEIN SPRUCH WARTET AUF FREIGABE" +}, +{ +"id":951, +"fortune":"DIESE NACHRICHT WIRD SICH SELBST ENTTÄUSCHEN" +}, +{ +"id":952, +"fortune":"DER KEKS BRÖCKELT. WIE DEINE PLÄNE" +}, +{ +"id":953, +"fortune":"DU ERWARTETEST MEHR VON EINEM KEKS. NOTIERT" +}, +{ +"id":954, +"fortune":"SPRUCH NICHT GEFUNDEN. ANDERES LOKAL" +}, +{ +"id":955, +"fortune":"DIESER KEKS KENNT DEINE BESTELLHISTORIE" +}, +{ +"id":956, +"fortune":"DER LETZTE KEKS SAGT IMMER DIE WAHRHEIT" +}, +{ +"id":957, +"fortune":"DEIN SPRUCH HAT ZU WENIG ZEICHEN. ZUM GLÜCK" +}, +{ +"id":958, +"fortune":"EIN WEISER SAGTE EINST NICHTS. PROBIER DAS" +}, +{ +"id":959, +"fortune":"DIESER SPRUCH IST PRÄZISE. DETAILS GESCHWÄRZT" +}, +{ +"id":960, +"fortune":"DU HÄLTST DEN SCHLÜSSEL ZUM GLÜCK. FINDE IHN" +}, +{ +"id":961, +"fortune":"DEIN SPRUCH WIRD NOCH GESCHRIEBEN. LANGSAM" +}, +{ +"id":962, +"fortune":"DEIN SPLITFLAP VERDIENTE BESSERE SPRÜCHE" +}, +{ +"id":963, +"fortune":"JEDE KLAPPE DREHTE SICH FÜR DIESE WEISHEIT" +}, +{ +"id":964, +"fortune":"DU BAUTEST EINE ANZEIGE DAFÜR. IMMERHIN" +}, +{ +"id":965, +"fortune":"45 ZEICHEN REICHEN FÜR DIESE WAHRHEIT KNAPP" +}, +{ +"id":966, +"fortune":"DIESE ANZEIGE VERDIENT LÄNGERE WEISHEITEN" +}, +{ +"id":967, +"fortune":"DEINE ANZEIGE RATTERTE EXTRA FÜR DICH" +}, +{ +"id":968, +"fortune":"GEDULD. DIE KLAPPEN GEBEN IHR BESTES" +}, +{ +"id":969, +"fortune":"DIESER TEXT KOSTETE 45 KLAPPEN. WOFÜR" +}, +{ +"id":970, +"fortune":"DEIN PROJEKT ZEIGT NUN FREMDE MEINUNGEN" +}, +{ +"id":971, +"fortune":"DIE ANZEIGE IST FERTIG. DIE SPRÜCHE NIE" +}, +{ +"id":972, +"fortune":"EIN FREMDER LOBT DICH BALD. PRÜF SEIN MOTIV" +}, +{ +"id":973, +"fortune":"DEIN BAUCHGEFÜHL MELDET SICH. MEIST HUNGER" +}, +{ +"id":974, +"fortune":"DU TRIFFST HEUTE EINE GUTE WAHL. EINE" +}, +{ +"id":975, +"fortune":"DEIN VORSATZ LEBT NOCH. IN GEFANGENSCHAFT" +}, +{ +"id":976, +"fortune":"DU BEKOMMST WAS DU VERDIENST. VIEL SPAß" +}, +{ +"id":977, +"fortune":"DEINE ZUKUNFT DANKT DIR. SARKASTISCH" +}, +{ +"id":978, +"fortune":"DU WIRST HEUTE UNTERSCHÄTZT. ZU RECHT" +}, +{ +"id":979, +"fortune":"EINE TÜR SCHLIEßT SICH. DEINE KLEMMT NUR" +}, +{ +"id":980, +"fortune":"DEIN WEG IST DAS ZIEL. DU STEHST TROTZDEM" +}, +{ +"id":981, +"fortune":"DAS SCHICKSAL LACHT. NICHT MIT DIR" +}, +{ +"id":982, +"fortune":"DEIN GLÜCKSKEKS HAT MEHR BISS ALS DU" +}, +{ +"id":983, +"fortune":"MAN ERNTET WAS MAN SÄT. DU SÄTEST NICHTS" +}, +{ +"id":984, +"fortune":"DEINE STERNSTUNDE FIEL AUS. TECHNISCHE GRÜNDE" +}, +{ +"id":985, +"fortune":"DU BIST AUF DEM RECHTEN WEG. FALSCHE RICHTUNG" +}, +{ +"id":986, +"fortune":"DAS UNIVERSUM SCHULDET DIR NICHTS. DU MAHNST" +}, +{ +"id":987, +"fortune":"DEIN KARMAKONTO IST ÜBERZOGEN. ZINSEN LAUFEN" +}, +{ +"id":988, +"fortune":"GEDULD ZAHLT SICH AUS. DEINE ZAHLT NIE" +}, +{ +"id":989, +"fortune":"DEIN STERN LEUCHTET. AUF SPARFLAMME" +}, +{ +"id":990, +"fortune":"HEUTE GEHÖRT DIR DIE WELT. BIS MITTAG" +}, +{ +"id":991, +"fortune":"MORGEN WIRST DU ANDERS SEIN. KAUM ABER ANDERS" +}, +{ +"id":992, +"fortune":"DEIN NAVI GLAUBT NOCH AN DICH. ALS EINZIGES" +}, +{ +"id":993, +"fortune":"DU BIST EIN VORBILD. ALS ABSCHRECKUNG ZÄHLT" +}, +{ +"id":994, +"fortune":"DEINE ZIMMERTEMPERATUR MEINUNG IST NOTIERT" +}, +{ +"id":995, +"fortune":"HEUT LÄUFT ALLES GLATT. AUF EIS GENAUER" +}, +{ +"id":996, +"fortune":"DEIN GLÜCKSBRINGER BAT UM VERSETZUNG" +}, +{ +"id":997, +"fortune":"DIE STILLE NACH DEINEM WITZ SPRICHT BÄNDE" +}, +{ +"id":998, +"fortune":"DEIN PLAN IST NARRENSICHER. FAST ÜBERALL" +}, +{ +"id":999, +"fortune":"DU DENKST LAUT. LEISER WÄRE AUCH SCHÖN" +}, +{ +"id":1000, +"fortune":"ENDE GUT ALLES GUT. DEINS IST OFFEN" +} +] \ No newline at end of file diff --git a/apps/sarcastic-fortune-cookies/fortunes_en-au.json b/apps/sarcastic-fortune-cookies/fortunes_en-au.json new file mode 100644 index 0000000..294fd42 --- /dev/null +++ b/apps/sarcastic-fortune-cookies/fortunes_en-au.json @@ -0,0 +1,4066 @@ +[ + { + "id": 1, + "fortune": "EVERYTHING HAPPENS. RARELY FOR A REASON" + }, + { + "id": 2, + "fortune": "GOOD LUCK IS COMING. IT WALKS SLOWLY" + }, + { + "id": 3, + "fortune": "THE UNIVERSE HAS A PLAN. A MODEST ONE" + }, + { + "id": 4, + "fortune": "YOUR DESTINY CALLED. IT RESCHEDULED" + }, + { + "id": 5, + "fortune": "FATE HAS PLANS FOR YOU. BACKUP PLANS" + }, + { + "id": 6, + "fortune": "THE STARS ALIGNED. THEN THEY SAW YOU" + }, + { + "id": 7, + "fortune": "LUCK FAVOURS THE BOLD. YOU CAN WATCH" + }, + { + "id": 8, + "fortune": "YOUR SHIP IS COMING IN. IT IS A CANOE" + }, + { + "id": 9, + "fortune": "TRUST THE PROCESS. IT DOES NOT TRUST YOU" + }, + { + "id": 10, + "fortune": "A SIGN IS COMING. IT SAYS YIELD" + }, + { + "id": 11, + "fortune": "YOUR LUCKY STAR IS IN ANOTHER GALAXY" + }, + { + "id": 12, + "fortune": "KARMA IS REAL. THAT EXPLAINS A LOT" + }, + { + "id": 13, + "fortune": "THE FUTURE IS BRIGHT. WEAR SUNSCREEN" + }, + { + "id": 14, + "fortune": "DESTINY AWAITS. IT BROUGHT A BOOK" + }, + { + "id": 15, + "fortune": "MIRACLES HAPPEN DAILY. ELSEWHERE MOSTLY" + }, + { + "id": 16, + "fortune": "YOUR MOMENT IS COMING. IT IS IN TRAFFIC" + }, + { + "id": 17, + "fortune": "FORTUNE KNOCKED ONCE. YOU WERE OUT" + }, + { + "id": 18, + "fortune": "THE TIDE IS TURNING. GLACIALLY" + }, + { + "id": 19, + "fortune": "BIG THINGS ARE COMING. LOWER THE BAR" + }, + { + "id": 20, + "fortune": "OPPORTUNITY IS NEAR. YOU TWO KEEP MISSING" + }, + { + "id": 21, + "fortune": "THE COSMOS HEARD YOU. YOU ARE IN THE QUEUE" + }, + { + "id": 22, + "fortune": "YOUR STARS ARE FINE. YOUR CHOICES ARENT" + }, + { + "id": 23, + "fortune": "WISH ON A STAR. THE STAR MARKED IT SPAM" + }, + { + "id": 24, + "fortune": "GOOD THINGS TAKE TIME. YOURS TAKE DECADES" + }, + { + "id": 25, + "fortune": "A WINDFALL NEARS. DRESS IN LAYERS" + }, + { + "id": 26, + "fortune": "THE SIGNS WERE THERE. READING IS OPTIONAL" + }, + { + "id": 27, + "fortune": "YOU ARE ONE IN A MILLION. SO ARE THOUSANDS" + }, + { + "id": 28, + "fortune": "THE BEST IS YET TO COME. NO RUSH APPARENTLY" + }, + { + "id": 29, + "fortune": "YOUR AURA IS RADIANT. IT WORKS OVERTIME" + }, + { + "id": 30, + "fortune": "LADY LUCK SMILED. POLITELY. AT A STRANGER" + }, + { + "id": 31, + "fortune": "YOUR PRAYERS WERE HEARD. AND FILED" + }, + { + "id": 32, + "fortune": "ABUNDANCE FLOWS TO YOU. UPSTREAM SADLY" + }, + { + "id": 33, + "fortune": "FORTUNES WHEEL TURNS. YOU ARE THE HAMSTER" + }, + { + "id": 34, + "fortune": "EVERY CLOUD HAS A LINING. YOURS IS DAMP" + }, + { + "id": 35, + "fortune": "SERENDIPITY IS ASKING AROUND ABOUT YOU" + }, + { + "id": 36, + "fortune": "A LUCKY BREAK NEARS. BREAK HAS TWO MEANINGS" + }, + { + "id": 37, + "fortune": "THE ORACLE SPOKE. MOSTLY IT SIGHED" + }, + { + "id": 38, + "fortune": "YOUR HOROSCOPE LOOKS GREAT. IT IS FICTION" + }, + { + "id": 39, + "fortune": "COSMIC JUSTICE EXISTS. SETTLE OUT OF COURT" + }, + { + "id": 40, + "fortune": "LIGHTNING WONT STRIKE TWICE. YOU MIGHT" + }, + { + "id": 41, + "fortune": "FATE REWARDS PATIENCE. INVOICE PENDING" + }, + { + "id": 42, + "fortune": "THE MOON SAW YOUR FRESH START. AGAIN" + }, + { + "id": 43, + "fortune": "YOUR SPIRIT GUIDE REQUESTED A TRANSFER" + }, + { + "id": 44, + "fortune": "ANGELS WATCH OVER YOU. TAKING NOTES" + }, + { + "id": 45, + "fortune": "BELIEVE AND RECEIVE. RECEIPTS VARY" + }, + { + "id": 46, + "fortune": "YOUR GOLDEN ERA NEARS. GOLD PLATED" + }, + { + "id": 47, + "fortune": "WHAT IS MEANT FOR YOU IS NOT HURRYING" + }, + { + "id": 48, + "fortune": "THE UNIVERSE WINKED. OR HAD AN EYE ISSUE" + }, + { + "id": 49, + "fortune": "YOUR LUCK IS CHANGING. DIRECTION UNCLEAR" + }, + { + "id": 50, + "fortune": "PROVIDENCE PROVIDES. IT CHECKS RECEIPTS" + }, + { + "id": 51, + "fortune": "SUCCESS IS NEAR. SO IS THE SNOOZE BUTTON" + }, + { + "id": 52, + "fortune": "HARD WORK PAYS OFF. IN EXPOSURE" + }, + { + "id": 53, + "fortune": "YOUR POTENTIAL IS LIMITLESS. AND UNUSED" + }, + { + "id": 54, + "fortune": "DREAM BIG. SET AN ALARM THOUGH" + }, + { + "id": 55, + "fortune": "THE EARLY BIRD LEFT WITHOUT YOU" + }, + { + "id": 56, + "fortune": "YOUR BIG BREAK NEARS. DO NOT BREAK IT" + }, + { + "id": 57, + "fortune": "ROME WASNT BUILT IN A DAY. YOURS ISNT STARTED" + }, + { + "id": 58, + "fortune": "YOUR TALENTS WILL BE FOUND. WITH A WARRANT" + }, + { + "id": 59, + "fortune": "AIM FOR THE STARS. MIND THE CEILING" + }, + { + "id": 60, + "fortune": "WINNERS NEVER QUIT. YOU FOUND COASTING" + }, + { + "id": 61, + "fortune": "YOUR IDEAS ARE AHEAD. YOUR DEADLINES BEHIND" + }, + { + "id": 62, + "fortune": "EXCELLENCE IS A HABIT. SO IS YOURS" + }, + { + "id": 63, + "fortune": "YOUR WORK SPEAKS. IT REQUESTED A LAWYER" + }, + { + "id": 64, + "fortune": "THE TOP IS LONELY. YOU SOLVED THAT PROBLEM" + }, + { + "id": 65, + "fortune": "A PROMOTION NEARS. SOMEONE ELSES" + }, + { + "id": 66, + "fortune": "YOUR RESUME IMPRESSES FICTION FANS" + }, + { + "id": 67, + "fortune": "YOUR BEST WORK IS AHEAD. IT IS VERY FAST" + }, + { + "id": 68, + "fortune": "THAT MEETING COULD HAVE BEEN AN EMAIL" + }, + { + "id": 69, + "fortune": "YOUR INBOX IS NOT YOUR ENEMY. JUST A WITNESS" + }, + { + "id": 70, + "fortune": "DEADLINES FEAR NOTHING. YOU WALK PAST THEM" + }, + { + "id": 71, + "fortune": "YOUR FIVE YEAR PLAN IS FIVE YEARS OLD" + }, + { + "id": 72, + "fortune": "SLOW AND STEADY WINS. YOU NAILED SLOW" + }, + { + "id": 73, + "fortune": "YOUR CALENDAR IS FULL. OF BUFFERS" + }, + { + "id": 74, + "fortune": "YOUR TEAM DREAMS TOO. OF TRANSFERS" + }, + { + "id": 75, + "fortune": "THE OCCASION LOWERED ITSELF TO MEET YOU" + }, + { + "id": 76, + "fortune": "SUCCESS LEAVES CLUES. YOU PREFER MYSTERY" + }, + { + "id": 77, + "fortune": "YOUR COMFORT ZONE NEEDS ITS OWN POSTCODE" + }, + { + "id": 78, + "fortune": "QUALITY OVER QUANTITY. YOU DECLINED BOTH" + }, + { + "id": 79, + "fortune": "YOUR INSTINCTS ARE SHARP. FACTS ON SABBATICAL" + }, + { + "id": 80, + "fortune": "YOU WORK WELL UNDER PRESSURE. IT DISAGREES" + }, + { + "id": 81, + "fortune": "MASTERY TAKES 10000 HOURS. YOU HAVE 40" + }, + { + "id": 82, + "fortune": "YOUR DESK PLANT BELIEVED IN YOU. AND WATER" + }, + { + "id": 83, + "fortune": "THE PRINTER KNOWS YOU ARE IN A HURRY" + }, + { + "id": 84, + "fortune": "YOUR LEGACY IS UNDER CONSTRUCTION. STILL" + }, + { + "id": 85, + "fortune": "AN AWARD AWAITS. PARTICIPATION COUNTS" + }, + { + "id": 86, + "fortune": "YOUR CAN DO ATTITUDE MET YOUR DID NOT RECORD" + }, + { + "id": 87, + "fortune": "NETWORKING OPENS DOORS. TRY KNOCKING TOO" + }, + { + "id": 88, + "fortune": "YOU LEAD BY EXAMPLE. HR WANTS A NEW ONE" + }, + { + "id": 89, + "fortune": "YOUR SIDE HUSTLE JOINED THE OTHERS. RESTING" + }, + { + "id": 90, + "fortune": "GENIUS IS 1 PERCENT WORK. YOU STOPPED THERE" + }, + { + "id": 91, + "fortune": "YOUR OUT OF OFFICE REPLY IS YOUR BEST WORK" + }, + { + "id": 92, + "fortune": "FAILURE IS A STEPPING STONE. NICE STAIRCASE" + }, + { + "id": 93, + "fortune": "YOU ARE IRREPLACEABLE. THE POSTING IS DRAFTED" + }, + { + "id": 94, + "fortune": "YOUR PERFORMANCE REVIEW NEARS. REHEARSE" + }, + { + "id": 95, + "fortune": "EFFICIENCY IS YOUR MIDDLE NAME. LEGALLY ONLY" + }, + { + "id": 96, + "fortune": "YOU WILL LAND ON YOUR FEET. LANDING DELAYED" + }, + { + "id": 97, + "fortune": "THE SUMMIT IS CLOSE. SO IS BASE CAMP" + }, + { + "id": 98, + "fortune": "YOUR AMBITION WOKE UP. YOUR ALARM DID NOT" + }, + { + "id": 99, + "fortune": "BE YOURSELF. EVERYONE ELSE WAS ALARMED" + }, + { + "id": 100, + "fortune": "KNOW THYSELF. A BRIEF INTRO MAY HELP" + }, + { + "id": 101, + "fortune": "YOUR GLOW UP IS COMING. NOTIFY THE GRID" + }, + { + "id": 102, + "fortune": "NEW YEAR NEW YOU. THE OLD YOU LEFT NOTES" + }, + { + "id": 103, + "fortune": "YOU ARE ENOUGH. THE BAR WAS ADJUSTABLE" + }, + { + "id": 104, + "fortune": "YOUR INNER PEACE SAW YOUR SCHEDULE. IT LEFT" + }, + { + "id": 105, + "fortune": "GROWTH TAKES TIME. YOURS FILED AN EXTENSION" + }, + { + "id": 106, + "fortune": "YOU ARE A WORK IN PROGRESS. MOSTLY WORK" + }, + { + "id": 107, + "fortune": "SELF AWARENESS IS A GIFT. YOURS SHIPPED" + }, + { + "id": 108, + "fortune": "HEALING IS NOT LINEAR. YOURS IS A SCRIBBLE" + }, + { + "id": 109, + "fortune": "YOUR BEST SELF IS EMERGING. IT HIT SNOOZE" + }, + { + "id": 110, + "fortune": "SELF CARE MATTERS. STREAMING IS NOT A SPA" + }, + { + "id": 111, + "fortune": "YOUR BOUNDARIES ARE HEALTHY. AND UNTESTED" + }, + { + "id": 112, + "fortune": "GRATITUDE HELPS. TRY IT ON PEOPLE" + }, + { + "id": 113, + "fortune": "YOU FORGIVE EASILY. YOURSELF ESPECIALLY" + }, + { + "id": 114, + "fortune": "YOUR HABITS DEFINE YOU. OUR CONDOLENCES" + }, + { + "id": 115, + "fortune": "SMALL STEPS COUNT. EVEN TO THE FRIDGE" + }, + { + "id": 116, + "fortune": "YOUR INTUITION IS POWERFUL. AND WRONG" + }, + { + "id": 117, + "fortune": "MINDFULNESS HELPS. YOUR PHONE DISAGREES" + }, + { + "id": 118, + "fortune": "YOU LET GO OF THE PAST. IT HAS YOUR ADDRESS" + }, + { + "id": 119, + "fortune": "YOUR MORNING ROUTINE IS SACRED. ALL 4 MINUTES" + }, + { + "id": 120, + "fortune": "YOU ARE LEARNING TO SAY NO. TO VEGETABLES" + }, + { + "id": 121, + "fortune": "YOUR WILLPOWER LIFTS ALMOST A WHOLE BIKKIE" + }, + { + "id": 122, + "fortune": "YOUR PURPOSE EXISTS. IT STAYS ANONYMOUS" + }, + { + "id": 123, + "fortune": "YOU EMBRACE CHANGE. MOSTLY COUCH CHANGE" + }, + { + "id": 124, + "fortune": "YOUR JOURNAL IS RAW AND HONEST. BOTH PAGES" + }, + { + "id": 125, + "fortune": "YOU LIVE YOUR TRUTH. FACT CHECK PENDING" + }, + { + "id": 126, + "fortune": "YOUR INNER CRITIC ONLY SIGHS NOW" + }, + { + "id": 127, + "fortune": "YOUR TRANSFORMATION NEEDED LABELLED PHOTOS" + }, + { + "id": 128, + "fortune": "YOUR GOALS ARE IN SIGHT. NICE BINOCULARS" + }, + { + "id": 129, + "fortune": "YOUR NEW HABITS LOOK FAMILIAR. SUSPICIOUSLY" + }, + { + "id": 130, + "fortune": "YOUR ENERGY IS MAGNETIC. TO OBLIGATIONS" + }, + { + "id": 131, + "fortune": "YOUR WALLS CAME DOWN. THE MOAT EXPANDED" + }, + { + "id": 132, + "fortune": "YOU CELEBRATE SMALL WINS. DAILY. SOMEONE MUST" + }, + { + "id": 133, + "fortune": "YOUR EVOLUTION PUZZLES DARWIN" + }, + { + "id": 134, + "fortune": "YOU PUT YOURSELF FIRST. THE LINE WAS SHORT" + }, + { + "id": 135, + "fortune": "YOUR REINVENTION TOUR CONTINUES. SAME SETLIST" + }, + { + "id": 136, + "fortune": "YOU ARE PRESENT. PHYSICALLY AT LEAST" + }, + { + "id": 137, + "fortune": "YOUR FRESH START STARTS MONDAY. A FUTURE ONE" + }, + { + "id": 138, + "fortune": "YOUR VISION BOARD IS STUNNING. AND RETIRED" + }, + { + "id": 139, + "fortune": "YOU HONOUR YOUR FEELINGS. AT FULL VOLUME" + }, + { + "id": 140, + "fortune": "YOUR CALM IS REAL. YOU STOPPED CHECKING EMAIL" + }, + { + "id": 141, + "fortune": "YOUR STANDARDS ARE RISING. FROM SEA LEVEL" + }, + { + "id": 142, + "fortune": "YOU ARE BLOOMING. LATE SEASON. STILL COUNTS" + }, + { + "id": 143, + "fortune": "YOUR DISCIPLINE IS THE STUFF OF LEGEND" + }, + { + "id": 144, + "fortune": "YOUR AFFIRMATIONS WORK. ON THE CEILING" + }, + { + "id": 145, + "fortune": "TRUE LOVE AWAITS. IT IS RUNNING LATE TOO" + }, + { + "id": 146, + "fortune": "YOUR SOULMATE EXISTS. COVERAGE MAY VARY" + }, + { + "id": 147, + "fortune": "ROMANCE IS IN THE AIR. SO IS POLLEN" + }, + { + "id": 148, + "fortune": "CUPID HAS YOUR ADDRESS. HE DROVE PAST TWICE" + }, + { + "id": 149, + "fortune": "YOUR HEART KNOWS THE WAY. YOUR TEXTS DONT" + }, + { + "id": 150, + "fortune": "LOVE IS BLIND. YOURS ALSO FORGETS" + }, + { + "id": 151, + "fortune": "YOUR EX THINKS OF YOU. AS A LESSON" + }, + { + "id": 152, + "fortune": "YOUR FLIRTING IS SUBTLE. UNDETECTABLE EVEN" + }, + { + "id": 153, + "fortune": "YOUR TYPE IS CONSISTENT. THAT IS THE PROBLEM" + }, + { + "id": 154, + "fortune": "SOMEONE ADMIRES YOU FROM AFAR. STAY THERE" + }, + { + "id": 155, + "fortune": "YOUR GREEN FLAGS ARE LOVELY. AND OUTNUMBERED" + }, + { + "id": 156, + "fortune": "LOVE CONQUERS ALL. IT REQUESTED BACKUP" + }, + { + "id": 157, + "fortune": "HEART ON YOUR SLEEVE. DRY CLEAN BOTH" + }, + { + "id": 158, + "fortune": "A TEXT FROM THE PAST NEARS. DO NOT REPLY" + }, + { + "id": 159, + "fortune": "YOUR CRUSH KNOWS YOU EXIST. AS A CONCEPT" + }, + { + "id": 160, + "fortune": "YOU LOVE DEEPLY. BRIEFLY. BUT DEEPLY" + }, + { + "id": 161, + "fortune": "YOUR HEART IS OPEN. SO IS YOUR TAB" + }, + { + "id": 162, + "fortune": "SPARKS WILL FLY. CHECK THE SMOKE ALARM" + }, + { + "id": 163, + "fortune": "YOU ARE READY FOR COMMITMENT. TO A COUCH" + }, + { + "id": 164, + "fortune": "YOUR MEET CUTE NEARS. A MEET ADEQUATE" + }, + { + "id": 165, + "fortune": "YOUR HEART HEALED. IT ADDED SECURITY" + }, + { + "id": 166, + "fortune": "LOVE IS PATIENT. IT HAD PRACTICE WITH YOU" + }, + { + "id": 167, + "fortune": "YOUR REBOUND GAME BEATS THE ORIGINAL" + }, + { + "id": 168, + "fortune": "YOUR HONEYMOON PHASE LASTED 11 DAYS" + }, + { + "id": 169, + "fortune": "YOUR PET IS YOUR BEST RELATIONSHIP. IT AGREES" + }, + { + "id": 170, + "fortune": "YOUR VOWS WILL MAKE PEOPLE CRY. THE GRAMMAR" + }, + { + "id": 171, + "fortune": "YOU ARE A CATCH. CATCH AND RELEASE EXISTS" + }, + { + "id": 172, + "fortune": "ABSENCE MAKES HEARTS FONDER. KEEP TESTING" + }, + { + "id": 173, + "fortune": "YOUR PERFECT MATCH EXISTS. THE APP GAVE UP" + }, + { + "id": 174, + "fortune": "YOUR RED FLAGS ARE MAROON NOW. PROGRESS" + }, + { + "id": 175, + "fortune": "LOVE AT FIRST SIGHT IS REAL. GET GLASSES" + }, + { + "id": 176, + "fortune": "YOUR HEART WANTS THINGS. SEE A SPECIALIST" + }, + { + "id": 177, + "fortune": "SOMEONE THINKS YOU ARE PERFECT. FROM AFAR" + }, + { + "id": 178, + "fortune": "YOU BELIEVE IN SECOND CHANCES. AND FIFTHS" + }, + { + "id": 179, + "fortune": "YOUR BREAKUP PLAYLIST GETS HEAVY USE" + }, + { + "id": 180, + "fortune": "YOU ARE WORTH THE WAIT. THE WAIT CONTINUES" + }, + { + "id": 181, + "fortune": "ROMANCE IS NOT DEAD. YOURS IS RESTING" + }, + { + "id": 182, + "fortune": "YOUR IN LAWS WILL ADORE YOU. EVENTUALLY" + }, + { + "id": 183, + "fortune": "YOUR DATING BIO IS HONEST. SELECTIVELY" + }, + { + "id": 184, + "fortune": "YOUR HUGS ARE GREAT. AND RARE. LIKE ECLIPSES" + }, + { + "id": 185, + "fortune": "SOMEONE DREAMS OF YOUR COOKING. IN FEAR" + }, + { + "id": 186, + "fortune": "YOUR CHARM OPENS DOORS. PUSH DONT PULL" + }, + { + "id": 187, + "fortune": "YOUR STANDARDS MET YOUR CHOICES. AWKWARD" + }, + { + "id": 188, + "fortune": "YOU WILL BE SWEPT AWAY. STRETCH FIRST" + }, + { + "id": 189, + "fortune": "INTIMACY MEANS BEING SEEN. YOU NEGOTIATED" + }, + { + "id": 190, + "fortune": "YOUR LOVE STORY WILL BE TOLD. AS A WARNING" + }, + { + "id": 191, + "fortune": "THE SPARK IS STILL THERE. SEND A SEARCH PARTY" + }, + { + "id": 192, + "fortune": "YOUR ANNIVERSARY NEARS. THIS IS THE REMINDER" + }, + { + "id": 193, + "fortune": "LOVE YOURSELF FIRST. YOU MASTERED THAT PART" + }, + { + "id": 194, + "fortune": "YOUR HEART SKIPPED A BEAT. SEE A DOCTOR" + }, + { + "id": 195, + "fortune": "YOU BRING OUT THE BEST. AS PEOPLE LEAVE" + }, + { + "id": 196, + "fortune": "YOU NEVER SETTLE. HENCE THE FOLDING CHAIRS" + }, + { + "id": 197, + "fortune": "YOUR FUTURE PARTNER IS TRAINING. UNKNOWINGLY" + }, + { + "id": 198, + "fortune": "YOU GET GREAT ADVICE. FOLLOWING IT IS NEW" + }, + { + "id": 199, + "fortune": "YOUR HEART IS A FORTRESS. GIFT SHOP OPEN" + }, + { + "id": 200, + "fortune": "YOUR MATES VALUE YOUR HONESTY. FROM AFAR" + }, + { + "id": 201, + "fortune": "A MATE WILL CALL SOON. THEY NEED A TRUCK" + }, + { + "id": 202, + "fortune": "YOU ARE THE GLUE. GLUE IS RARELY THANKED" + }, + { + "id": 203, + "fortune": "THE GROUP CHAT ENJOYS YOUR SILENCE" + }, + { + "id": 204, + "fortune": "TRUE MATES ARE RARE. YOURS ARE HIDING" + }, + { + "id": 205, + "fortune": "YOUR LOYALTY IS UNMATCHED. YOUR TIMING ISNT" + }, + { + "id": 206, + "fortune": "A GATHERING BRINGS JOY. LEAVING BRINGS MORE" + }, + { + "id": 207, + "fortune": "YOU ARE THE FUN ONE. SELF REPORTED" + }, + { + "id": 208, + "fortune": "AN OLD MATE RESURFACES. THEY ARE MOVING" + }, + { + "id": 209, + "fortune": "YOUR ADVICE IS APPRECIATED. NOT REQUESTED" + }, + { + "id": 210, + "fortune": "YOU LIGHT UP ROOMS. SOME WANT DIMMERS" + }, + { + "id": 211, + "fortune": "YOUR BIRTHDAY WILL BE REMEMBERED. LATE" + }, + { + "id": 212, + "fortune": "YOUR STORIES IMPROVE YEARLY. ACCURACY DROPS" + }, + { + "id": 213, + "fortune": "PEOPLE GRAVITATE TO YOU. GRAVITY PULLS DOWN" + }, + { + "id": 214, + "fortune": "YOU ALWAYS KNOW WHAT TO SAY. TIMING EXTRA" + }, + { + "id": 215, + "fortune": "MATES COME AND GO. YOURS EXCEL AT GO" + }, + { + "id": 216, + "fortune": "YOUR SOCIAL CALENDAR IS FULL. OF MAYBES" + }, + { + "id": 217, + "fortune": "YOU ARE A PEOPLE PERSON. UNVERIFIED" + }, + { + "id": 218, + "fortune": "YOUR INSIDE JOKES ARE NOW SOLO ACTS" + }, + { + "id": 219, + "fortune": "YOUR HOSTING IMPROVES. THE FIRE DEPT AGREES" + }, + { + "id": 220, + "fortune": "YOU BRING PEOPLE TOGETHER. TO DISCUSS YOU" + }, + { + "id": 221, + "fortune": "YOUR GENEROSITY HAS OFFICE HOURS" + }, + { + "id": 222, + "fortune": "YOU NEVER FORGET A FACE. NAMES OPTED OUT" + }, + { + "id": 223, + "fortune": "YOUR PEP TALKS REQUIRE FOLLOW UP CARE" + }, + { + "id": 224, + "fortune": "YOU SHOW UP WHEN IT MATTERS. TO YOU" + }, + { + "id": 225, + "fortune": "YOUR GOING AWAY PARTIES DRAW CROWDS" + }, + { + "id": 226, + "fortune": "PEOPLE CONFIDE IN YOU. AND THEN IN OTHERS" + }, + { + "id": 227, + "fortune": "YOUR HONESTY IS REFRESHING. LIKE ICE WATER" + }, + { + "id": 228, + "fortune": "YOU NEVER HOLD GRUDGES. YOU ARCHIVE THEM" + }, + { + "id": 229, + "fortune": "YOUR MEMES ARRIVE AT DAWN. UNREQUESTED" + }, + { + "id": 230, + "fortune": "YOU ARE A SHOULDER TO CRY ON. BY APPOINTMENT" + }, + { + "id": 231, + "fortune": "YOUR CANCELLED DINNER PARTIES ARE LEGEND" + }, + { + "id": 232, + "fortune": "YOU KEEP EVERYONE GROUNDED. LIKE FOG" + }, + { + "id": 233, + "fortune": "YOUR ENTHUSIASM IS INFECTIOUS. MASKS EXIST" + }, + { + "id": 234, + "fortune": "A MATE WANTS YOUR HONEST OPINION. A TRAP" + }, + { + "id": 235, + "fortune": "YOU SPLIT BILLS FAIRLY. YOUR MATH DIFFERS" + }, + { + "id": 236, + "fortune": "YOUR PRESENCE IS A GIFT. REGIFTING HAPPENS" + }, + { + "id": 237, + "fortune": "YOUR COMPETITIVENESS RETIRED THE GAMES" + }, + { + "id": 238, + "fortune": "YOUR SURPRISE PARTIES SURPRISE NO ONE" + }, + { + "id": 239, + "fortune": "PEOPLE FEEL COMFORTABLE AROUND YOU. TO LEAVE" + }, + { + "id": 240, + "fortune": "YOU SAY WHAT EVERYONE THINKS. AT FUNERALS" + }, + { + "id": 241, + "fortune": "YOUR HOLIDAY LETTER HAS A READERSHIP" + }, + { + "id": 242, + "fortune": "YOUR APOLOGIES ARE HEARTFELT. AND FREQUENT" + }, + { + "id": 243, + "fortune": "YOU ARE THE KEEPER OF GROUP HISTORY. REVISED" + }, + { + "id": 244, + "fortune": "YOU NEVER MISS A CHANCE TO HELP. OR SAY SO" + }, + { + "id": 245, + "fortune": "YOUR DIRECTNESS IS CHARMING. AGREED WORD" + }, + { + "id": 246, + "fortune": "YOU ARE FASHIONABLY LATE. MINUS THE FASHION" + }, + { + "id": 247, + "fortune": "PEOPLE SEEK YOUR APPROVAL. TO END THE CHAT" + }, + { + "id": 248, + "fortune": "YOU NEVER GOSSIP. YOU BRIEF THE ABSENT" + }, + { + "id": 249, + "fortune": "YOUR INVITES SAY CASUAL. YOUR TEXTS DONT" + }, + { + "id": 250, + "fortune": "AN OLD MATE REMEMBERS YOU. FROM AFAR" + }, + { + "id": 251, + "fortune": "YOU NEVER LOSE ARGUMENTS. YOU RELOCATE THEM" + }, + { + "id": 252, + "fortune": "YOUR SUPPORT IS UNCONDITIONAL. SEE TERMS" + }, + { + "id": 253, + "fortune": "WEALTH IS COMING. IT HAS YOUR OLD ADDRESS" + }, + { + "id": 254, + "fortune": "MONEY FLOWS TO YOU. THROUGH YOU. GONE" + }, + { + "id": 255, + "fortune": "YOUR BUDGET IS ART. ABSTRACT ART" + }, + { + "id": 256, + "fortune": "SAVE FOR A RAINY DAY. MONSOONS FORECAST" + }, + { + "id": 257, + "fortune": "YOUR CREDIT SCORE IS A THRILLER" + }, + { + "id": 258, + "fortune": "YOUR SAVINGS GROW. CHARACTER MOSTLY" + }, + { + "id": 259, + "fortune": "MONEY TALKS. YOURS SAYS GOODBYE" + }, + { + "id": 260, + "fortune": "YOUR RETIREMENT PLAN IS A LOTTERY TICKET" + }, + { + "id": 261, + "fortune": "YOU HAVE EXPENSIVE TASTE. AND A BUDGET" + }, + { + "id": 262, + "fortune": "COMPOUND INTEREST IS MAGIC. YOU CHOSE DEBT" + }, + { + "id": 263, + "fortune": "YOUR EMERGENCY FUND COVERS SMALL EMERGENCIES" + }, + { + "id": 264, + "fortune": "A RAISE IS DESERVED. AND UNSCHEDULED" + }, + { + "id": 265, + "fortune": "YOUR CART IS A VISION BOARD WITH CHECKOUT" + }, + { + "id": 266, + "fortune": "YOUR GENEROSITY IS FAMOUS. AMONG VENDORS" + }, + { + "id": 267, + "fortune": "THE BEST THINGS ARE FREE. YOURS SHIP" + }, + { + "id": 268, + "fortune": "YOUR SUBSCRIPTIONS OUTNUMBER YOUR MATES" + }, + { + "id": 269, + "fortune": "BUY LOW SELL HIGH. YOU INVENTED THE REVERSE" + }, + { + "id": 270, + "fortune": "YOUR BANK TEXTS OUT OF CONCERN NOW" + }, + { + "id": 271, + "fortune": "YOUR TAX REFUND HAS OTHER PLANS" + }, + { + "id": 272, + "fortune": "YOUR SPLURGES ARE STRATEGIC. ALLEGEDLY" + }, + { + "id": 273, + "fortune": "YOU WILL FIND MONEY IN A COAT. IT WAS YOURS" + }, + { + "id": 274, + "fortune": "YOUR PORTFOLIO IS DIVERSIFIED. IN REGRETS" + }, + { + "id": 275, + "fortune": "PAYDAY BRINGS JOY. ABOUT 18 HOURS OF IT" + }, + { + "id": 276, + "fortune": "YOUR LATE FEES HAVE LATE FEES NOW" + }, + { + "id": 277, + "fortune": "A PENNY SAVED IS A START. A SMALL ONE" + }, + { + "id": 278, + "fortune": "RICH IN SPIRIT. THE REGISTER DISAGREES" + }, + { + "id": 279, + "fortune": "YOUR RECEIPTS TELL A STORY. A LONG ONE" + }, + { + "id": 280, + "fortune": "YOUR DOWN PAYMENT BECAME SUSHI. TWICE" + }, + { + "id": 281, + "fortune": "YOUR BUDGET SHEET IS HISTORICAL FICTION" + }, + { + "id": 282, + "fortune": "YOUR FREE TRIALS NEVER DIE. THEY BILL" + }, + { + "id": 283, + "fortune": "CASH IS KING. YOUR KINGDOM RUNS ON CREDIT" + }, + { + "id": 284, + "fortune": "YOUR SPENDING FREEZE LASTED 40 MINUTES" + }, + { + "id": 285, + "fortune": "YOUR BILLS ARRIVE ON TIME. UNLIKE YOU" + }, + { + "id": 286, + "fortune": "YOUR MONEY WORKS HARD. FOR OTHER PEOPLE" + }, + { + "id": 287, + "fortune": "YOUR COUPONS EXPIRED. ALL OF THEM. TODAY" + }, + { + "id": 288, + "fortune": "YOUR FINANCIAL PLAN HAS PHASES. DENIAL FIRST" + }, + { + "id": 289, + "fortune": "YOUR DISCOUNTS FUND YOUR IMPULSE BUYS" + }, + { + "id": 290, + "fortune": "YOUR CRYPTO PHASE WAS EDUCATIONAL. AND COSTLY" + }, + { + "id": 291, + "fortune": "THE LOTTERY IS A HOPE TAX. YOU DONATE WEEKLY" + }, + { + "id": 292, + "fortune": "YOUR SAVINGS GOAL HASNT MOVED SINCE SPRING" + }, + { + "id": 293, + "fortune": "WEALTH WHISPERS. YOURS BLOCKED THE NUMBER" + }, + { + "id": 294, + "fortune": "YOU TRACK EVERY EXPENSE. THEN MAKE MORE" + }, + { + "id": 295, + "fortune": "YOUR PIGGY BANK HAS TRUST ISSUES. EARNED" + }, + { + "id": 296, + "fortune": "YOUR REFUND ARRIVED. ALREADY SPENT. TWICE" + }, + { + "id": 297, + "fortune": "MONEY CANT BUY HAPPINESS. YOURS QUIT TRYING" + }, + { + "id": 298, + "fortune": "YOUR INVOICES AGE LIKE FINE WINE. UNPAID" + }, + { + "id": 299, + "fortune": "YOUR NET WORTH AND SELF WORTH BOTH DIPPED" + }, + { + "id": 300, + "fortune": "PATIENCE IS A VIRTUE. YOU CHOSE OTHERS" + }, + { + "id": 301, + "fortune": "SILENCE IS GOLDEN. YOURS WOULD FUND A NATION" + }, + { + "id": 302, + "fortune": "THINK BEFORE YOU SPEAK. A PILOT PROGRAM" + }, + { + "id": 303, + "fortune": "STILL WATERS RUN DEEP. YOURS RUN ANKLE HIGH" + }, + { + "id": 304, + "fortune": "KNOWLEDGE IS POWER. YOURS IS ON STANDBY" + }, + { + "id": 305, + "fortune": "AN OPEN MIND IS A TREASURE. NICE BREEZE" + }, + { + "id": 306, + "fortune": "LISTEN TWICE SPEAK ONCE. INVERT YOUR RATIO" + }, + { + "id": 307, + "fortune": "THE TRUTH WILL SET YOU FREE. YOURS SAT DOWN" + }, + { + "id": 308, + "fortune": "LOOK BEFORE YOU LEAP. OR AFTER. YOUR WAY" + }, + { + "id": 309, + "fortune": "A FOOL AND HIS MONEY HAD A NICE CEREMONY" + }, + { + "id": 310, + "fortune": "ACTIONS SPEAK LOUDER. YOURS WHISPER" + }, + { + "id": 311, + "fortune": "MEASURE TWICE CUT ONCE. YOU CUT FIRST" + }, + { + "id": 312, + "fortune": "THE PEN IS MIGHTY. YOUR EMAILS ARE WEAPONS" + }, + { + "id": 313, + "fortune": "BETTER LATE THAN NEVER. YOU TEST THIS DAILY" + }, + { + "id": 314, + "fortune": "HONESTY IS THE BEST POLICY. READ THE RIDERS" + }, + { + "id": 315, + "fortune": "DONT COUNT YOUR CHICKENS. YOURS UNIONIZED" + }, + { + "id": 316, + "fortune": "GRASS IS GREENER WHERE WATERED. BUY A HOSE" + }, + { + "id": 317, + "fortune": "EVERY SAINT HAS A PAST. YOURS HAS A FILE" + }, + { + "id": 318, + "fortune": "PRACTICE WHAT YOU PREACH. OR PREACH LESS" + }, + { + "id": 319, + "fortune": "TWO WRONGS DONT MAKE A RIGHT. STOP AT NINE" + }, + { + "id": 320, + "fortune": "ALL THAT GLITTERS ISNT GOLD. CHECK RECEIPTS" + }, + { + "id": 321, + "fortune": "A ROLLING STONE GATHERS NO MOSS. NICE MOSS" + }, + { + "id": 322, + "fortune": "DONT PUT ALL EGGS IN ONE BASKET. FIND BASKET" + }, + { + "id": 323, + "fortune": "EMPTY VESSELS ARE LOUD. GREAT ACOUSTICS" + }, + { + "id": 324, + "fortune": "HE WHO LAUGHS LAST THOUGHT SLOWEST. HI" + }, + { + "id": 325, + "fortune": "A BIRD IN HAND IS WORTH TWO. YOURS FLEW" + }, + { + "id": 326, + "fortune": "IF IT AINT BROKE DONT FIX IT. TOO LATE" + }, + { + "id": 327, + "fortune": "YOU CAN LEAD A HORSE TO WATER. IT DECLINED" + }, + { + "id": 328, + "fortune": "PRIDE GOES BEFORE A FALL. NICE BALANCE" + }, + { + "id": 329, + "fortune": "TIME HEALS ALL WOUNDS. YOURS PICK AT IT" + }, + { + "id": 330, + "fortune": "OLD HABITS DIE HARD. YOURS BOUGHT INSURANCE" + }, + { + "id": 331, + "fortune": "WHERE THERE IS SMOKE THERE IS YOU" + }, + { + "id": 332, + "fortune": "THE BEST DEFENCE IS DISTANCE. THEY LEARNED" + }, + { + "id": 333, + "fortune": "FAMILIARITY BREEDS CONTEMPT. THRIVING HERE" + }, + { + "id": 334, + "fortune": "THE DEVIL IS IN THE DETAILS. YOURS ARE EMPTY" + }, + { + "id": 335, + "fortune": "ONE MANS RUBBISH IS YOUR GARAGE" + }, + { + "id": 336, + "fortune": "A WATCHED POT NEVER BOILS. YOURS BOILED OVER" + }, + { + "id": 337, + "fortune": "IGNORANCE IS BLISS. YOU SEEM SERENE" + }, + { + "id": 338, + "fortune": "BEAUTY IS IN THE EYE. GET THE EYE CHECKED" + }, + { + "id": 339, + "fortune": "IT TAKES A VILLAGE. YOURS REQUESTED BACKUP" + }, + { + "id": 340, + "fortune": "GREAT MINDS THINK ALIKE. YOURS WENT SOLO" + }, + { + "id": 341, + "fortune": "THE PROOF IS IN THE PUDDING. NO COMMENT" + }, + { + "id": 342, + "fortune": "EVERY DOG HAS ITS DAY. YOURS WAS A TUESDAY" + }, + { + "id": 343, + "fortune": "NOTHING VENTURED NOTHING GAINED. PERFECT TIE" + }, + { + "id": 344, + "fortune": "TOO MANY COOKS SPOIL BROTH. YOU SOLOED IT" + }, + { + "id": 345, + "fortune": "YOU REAP WHAT YOU SOW. INTERESTING HARVEST" + }, + { + "id": 346, + "fortune": "ASK AND RECEIVE. YOUR ASKING MADE A LINE" + }, + { + "id": 347, + "fortune": "WALK A MILE IN THEIR SHOES. RETURN THEM" + }, + { + "id": 348, + "fortune": "HASTE MAKES WASTE. YOU KEPT ONLY WASTE" + }, + { + "id": 349, + "fortune": "ALL GOOD THINGS END. YOURS END EARLY" + }, + { + "id": 350, + "fortune": "WORDS ARE CHEAP. YOURS ARE FREE" + }, + { + "id": 351, + "fortune": "TIME WAITS FOR NO ONE. IT MADE AN EXCEPTION" + }, + { + "id": 352, + "fortune": "DONT BITE THE FEEDING HAND. IT WEARS GLOVES" + }, + { + "id": 353, + "fortune": "COMPARISON STEALS JOY. YOURS HIRED IT" + }, + { + "id": 354, + "fortune": "HALF A LOAF BEATS NONE. YOU OFFER CRUMBS" + }, + { + "id": 355, + "fortune": "A SMOOTH SEA NEVER MADE A SAILOR. STAY DOCKED" + }, + { + "id": 356, + "fortune": "THE TRUTH NEEDS NO REHEARSAL. YOURS DOES" + }, + { + "id": 357, + "fortune": "EVERYTHING IN MODERATION. EVEN THAT. STOP" + }, + { + "id": 358, + "fortune": "WHAT GOES AROUND COMES AROUND. TRACK IT" + }, + { + "id": 359, + "fortune": "NEVER ARGUE WITH A FOOL. ONLOOKERS STRUGGLE" + }, + { + "id": 360, + "fortune": "WISDOM WHISPERS. YOURS IS ON MUTE" + }, + { + "id": 361, + "fortune": "A THOUSAND MILE JOURNEY STARTS. YOU STRETCH" + }, + { + "id": 362, + "fortune": "CROSS BRIDGES WHEN YOU REACH THEM. NOT BURN" + }, + { + "id": 363, + "fortune": "THE MIGHTY OAK WAS A NUT. HERITAGE PRESERVED" + }, + { + "id": 364, + "fortune": "FOOL ME ONCE. YOUR CARD HAS NINE STAMPS" + }, + { + "id": 365, + "fortune": "ADVENTURE AWAITS. IT OPENED A BUSINESS" + }, + { + "id": 366, + "fortune": "YOU WILL TRAVEL FAR. THE SHUTTLE COUNTS" + }, + { + "id": 367, + "fortune": "YOUR PASSPORT PHOTO IS THE REAL YOU" + }, + { + "id": 368, + "fortune": "YOUR BUCKET LIST NEEDS A BUCKET" + }, + { + "id": 369, + "fortune": "YOU WILL SEE THE WORLD. VIA DOCUMENTARY" + }, + { + "id": 370, + "fortune": "YOUR HOLIDAY DAYS AGE LIKE FINE GUILT" + }, + { + "id": 371, + "fortune": "AN EXOTIC PLACE CALLED. YOU SCREENED IT" + }, + { + "id": 372, + "fortune": "YOUR ROAD TRIP PLAYLIST OUTLIVED THE TRIP" + }, + { + "id": 373, + "fortune": "YOU PACK LIGHT. EMOTIONALLY YOU DONT" + }, + { + "id": 374, + "fortune": "YOUR SOUVENIRS TELL STORIES. ABOUT PRICING" + }, + { + "id": 375, + "fortune": "YOU WILL GET LOST ABROAD. A HOME SKILL" + }, + { + "id": 376, + "fortune": "YOUR TRAVEL PICS STAR YOUR THUMB" + }, + { + "id": 377, + "fortune": "A SPONTANEOUS TRIP NEARS. WITH SPREADSHEETS" + }, + { + "id": 378, + "fortune": "YOU ARE A WORLD CITIZEN. REFERENCES PENDING" + }, + { + "id": 379, + "fortune": "YOUR BEACH BODY IS READY. THE BEACH ADAPTED" + }, + { + "id": 380, + "fortune": "THE MOUNTAINS ARE CALLING. YOU MUTED THEM" + }, + { + "id": 381, + "fortune": "YOUR STAYCATION GAME IS UNRIVALED" + }, + { + "id": 382, + "fortune": "YOU NAVIGATE LIKE A LOCAL. A LOST ONE" + }, + { + "id": 383, + "fortune": "YOUR ITINERARY IS MOSTLY BUFFER TIME" + }, + { + "id": 384, + "fortune": "YOUR HIKING BOOTS SUMMITED THE CLOSET" + }, + { + "id": 385, + "fortune": "JET LAG RESPECTS NO ONE. YOU CAME PRE TIRED" + }, + { + "id": 386, + "fortune": "YOUR TRAVEL JOURNAL HAS ONE ENTRY. EXCITED" + }, + { + "id": 387, + "fortune": "YOU COLLECT EXPERIENCES. THE CARD DISAGREES" + }, + { + "id": 388, + "fortune": "YOUR SENSE OF DIRECTION IS A FAMILY LEGEND" + }, + { + "id": 389, + "fortune": "A GETAWAY NEARS. THE LAUNDRY WILL WAIT" + }, + { + "id": 390, + "fortune": "YOU WILL RETURN CHANGED. MOSTLY YOUR SLEEP" + }, + { + "id": 391, + "fortune": "YOUR AIRPORT TIMING IS A PERSONALITY TEST" + }, + { + "id": 392, + "fortune": "YOUR WINDOW SEAT PICS FEATURE THE WING" + }, + { + "id": 393, + "fortune": "YOUR CARRY ON HAS ITS OWN GRAVITY NOW" + }, + { + "id": 394, + "fortune": "YOUR CAMPING SKILLS AMUSE THE RACCOONS" + }, + { + "id": 395, + "fortune": "TIME ZONES CONFUSE YOUR SLEEP. IT COPES" + }, + { + "id": 396, + "fortune": "YOUR HOTEL REVIEWS ARE FEARED. BY TOWELS" + }, + { + "id": 397, + "fortune": "YOU BEFRIEND STRANGERS ABROAD. ALSO LOST ONES" + }, + { + "id": 398, + "fortune": "YOUR CRUISE PACE MATCHES YOUR BUFFET PACE" + }, + { + "id": 399, + "fortune": "FORTUNE FAVOURS TRAVELLERS. YOURS LIKES COUCHES" + }, + { + "id": 400, + "fortune": "YOUR PHONE BATTERY OUTLASTS YOUR DRIVE" + }, + { + "id": 401, + "fortune": "YOU WILL READ THE TERMS SOMEDAY. NO" + }, + { + "id": 402, + "fortune": "YOUR SCREEN TIME REPORT SOFTENS THE NEWS" + }, + { + "id": 403, + "fortune": "YOUR LAUNDRY CHAIR EARNED TENURE" + }, + { + "id": 404, + "fortune": "YOUR ALARM HAS FIVE BACKUPS. ALL IGNORED" + }, + { + "id": 405, + "fortune": "YOUR FRIDGE HOSTS A SCIENCE EXPERIMENT" + }, + { + "id": 406, + "fortune": "THE CHECK ENGINE LIGHT IS A MOOD LIGHT NOW" + }, + { + "id": 407, + "fortune": "YOUR HOUSEPLANTS ACCEPTED THEIR FATE" + }, + { + "id": 408, + "fortune": "YOUR JUNK DRAWER OPENED A SECOND LOCATION" + }, + { + "id": 409, + "fortune": "YOUR GYM BAG IS PACKED. THAT COUNTS" + }, + { + "id": 410, + "fortune": "YOUR VOICEMAIL IS FULL. PEOPLE MOVED ON" + }, + { + "id": 411, + "fortune": "YOUR BROWSER TABS ARE AN ECOSYSTEM NOW" + }, + { + "id": 412, + "fortune": "YOUR UMBRELLA WAITS. YOU WILL FORGET IT" + }, + { + "id": 413, + "fortune": "YOUR LEFTOVERS HAD DREAMS. THEY COMPOST" + }, + { + "id": 414, + "fortune": "YOUR TO DO LIST HAS A TO DO LIST" + }, + { + "id": 415, + "fortune": "YOUR CLOSET DRESSES THREE VERSIONS OF YOU" + }, + { + "id": 416, + "fortune": "YOUR MORNING RUN IS LAMINATED. NOT RUN" + }, + { + "id": 417, + "fortune": "YOUR WIFI ROUTER SITS ON A SHRINE. CORRECT" + }, + { + "id": 418, + "fortune": "YOUR INBOX ZERO ATTEMPT IS HISTORY NOW" + }, + { + "id": 419, + "fortune": "YOUR SOCK DRAWER RUNS WITNESS PROTECTION" + }, + { + "id": 420, + "fortune": "YOUR GROCERY LIST STAYS HOME. IT KNOWS" + }, + { + "id": 421, + "fortune": "YOUR WATCHLIST OUTLIVED THREE HOBBIES" + }, + { + "id": 422, + "fortune": "YOUR DESK IS ERGONOMIC. YOUR COUCH ISNT" + }, + { + "id": 423, + "fortune": "THE PRINTER SENSES FEAR. AND UPDATES" + }, + { + "id": 424, + "fortune": "YOUR BEDTIME IS A MOTION YOU VOTE DOWN" + }, + { + "id": 425, + "fortune": "YOUR HEADPHONES TANGLE ALONE. SCIENCE WEEPS" + }, + { + "id": 426, + "fortune": "YOUR SPICE RACK IS SORTED THROUGH C" + }, + { + "id": 427, + "fortune": "YOUR BACKSEAT IS A LOST AND FOUND" + }, + { + "id": 428, + "fortune": "YOUR CART ABANDONMENT RATE MADE THE NEWS" + }, + { + "id": 429, + "fortune": "THE GOOD CABLE IS WHEREVER YOU ARENT" + }, + { + "id": 430, + "fortune": "YOUR STANDING DESK MOSTLY SITS. LIKE YOU" + }, + { + "id": 431, + "fortune": "YOUR MEAL PREP MADE ONE MEAL. SIX BOXES" + }, + { + "id": 432, + "fortune": "THE DELIVERY DRIVER WAVES AT YOU NOW" + }, + { + "id": 433, + "fortune": "YOUR AIR FRYER REPLACED THE OVEN. AND YOU" + }, + { + "id": 434, + "fortune": "YOUR BOOKMARKS ARE A HALL OF INTENTIONS" + }, + { + "id": 435, + "fortune": "YOUR SNOOZE BUTTON SHOWS GEOLOGIC WEAR" + }, + { + "id": 436, + "fortune": "YOUR REUSABLE BAGS WAIT IN THE CAR. LOYAL" + }, + { + "id": 437, + "fortune": "YOUR UPDATES HAVE SENIORITY NOW" + }, + { + "id": 438, + "fortune": "THE ICE TRAY RETURNS EMPTY. A TRADITION" + }, + { + "id": 439, + "fortune": "YOUR GARAGE STORES EVERYTHING BUT THE CAR" + }, + { + "id": 440, + "fortune": "YOUR STEP COUNTER FRAMED ITS BEST DAY" + }, + { + "id": 441, + "fortune": "YOUR FAVOURITE MUG HAS PROTECTED STATUS" + }, + { + "id": 442, + "fortune": "YOUR WEATHER APP GAVE UP ON YOUR OUTFITS" + }, + { + "id": 443, + "fortune": "YOUR SCALE PRACTICES RADICAL HONESTY" + }, + { + "id": 444, + "fortune": "YOUR REMOTES COMMUTE FROM THE CUSHIONS" + }, + { + "id": 445, + "fortune": "THE HALLWAY CLOSET HOLDS YOUR TRUTH. BARELY" + }, + { + "id": 446, + "fortune": "YOU OWN EVERY BATTERY SIZE BUT THE ONE NEEDED" + }, + { + "id": 447, + "fortune": "YOUR VACUUM AVOIDS CORNERS. LIKE YOU" + }, + { + "id": 448, + "fortune": "YOUR SHOWER THOUGHTS STAY IN THE SHOWER" + }, + { + "id": 449, + "fortune": "YOUR COFFEE MAKER KNOWS YOUR MONDAY FACE" + }, + { + "id": 450, + "fortune": "YOUR FREEZER FLOOR PREDATES FRIENDSHIPS" + }, + { + "id": 451, + "fortune": "YOUR PHONE STORAGE IS ALL SCREENSHOTS" + }, + { + "id": 452, + "fortune": "YOUR BIKE TIRES EXHALE SLOWLY. LIKE A SIGH" + }, + { + "id": 453, + "fortune": "YOUR RETURNS PILE HAS A DEPARTURE LOUNGE" + }, + { + "id": 454, + "fortune": "YOUR CANDLES ARE DECOR. MATCHES AGREE" + }, + { + "id": 455, + "fortune": "YOUR DINING TABLE RETIRED FROM DINING" + }, + { + "id": 456, + "fortune": "YOUR PREDICTIVE TEXT SUGGESTS SORRY FIRST" + }, + { + "id": 457, + "fortune": "YOUR FITNESS APP USES SHORTER SENTENCES NOW" + }, + { + "id": 458, + "fortune": "YOUR COAT POCKET IS A TIME CAPSULE" + }, + { + "id": 459, + "fortune": "THE ADD 30 SECONDS BUTTON RUNS YOUR KITCHEN" + }, + { + "id": 460, + "fortune": "THE GOOD SCISSORS ARE MISSING. ONE SUSPECT" + }, + { + "id": 461, + "fortune": "YOUR DOWNLOADS FOLDER IS A LANDFILL" + }, + { + "id": 462, + "fortune": "YOUR PLANTS RUN ON GUILT BASED WATERING" + }, + { + "id": 463, + "fortune": "THE DRYER TAKES ONE SOCK AS TRIBUTE" + }, + { + "id": 464, + "fortune": "YOUR COASTERS WATCH FROM THE SIDELINES" + }, + { + "id": 465, + "fortune": "YOUR NOTES APP IS A GENIUS LANDFILL" + }, + { + "id": 466, + "fortune": "RAIN WILL WASH YOUR CAR. YOUR PLAN EXACTLY" + }, + { + "id": 467, + "fortune": "YOUR BOARD GAMES AGE IN SHRINK WRAP" + }, + { + "id": 468, + "fortune": "YOUR YOGA MAT UNROLLED TWICE. FOR NAPS" + }, + { + "id": 469, + "fortune": "YOUR EXTENSION CORDS BREED IN THE DARK" + }, + { + "id": 470, + "fortune": "YOUR SMART SPEAKER HAS HEARD THINGS" + }, + { + "id": 471, + "fortune": "YOUR LIDS AND CONTAINERS NEVER MATCH. EVER" + }, + { + "id": 472, + "fortune": "YOUR WALKING SHOES TOUR THE TRUNK" + }, + { + "id": 473, + "fortune": "YOUR IMPORTANT FOLDER IS A HOSPICE" + }, + { + "id": 474, + "fortune": "YOUR WREATH IS PERENNIAL NOW. FESTIVE" + }, + { + "id": 475, + "fortune": "THAT ONE DRAWER STOPPED EXPLAINING ITSELF" + }, + { + "id": 476, + "fortune": "YOUR SCREEN AT 2AM REVEALS EVERYTHING" + }, + { + "id": 477, + "fortune": "YOUR FIRE EXTINGUISHER RUNS ON CONFIDENCE" + }, + { + "id": 478, + "fortune": "YOUR WATER GOAL WAS MET ONCE. PLAQUE PENDING" + }, + { + "id": 479, + "fortune": "AGING SUITS YOU. THE ALTERNATIVE DIDNT" + }, + { + "id": 480, + "fortune": "YOUR AGE IS JUST A NUMBER. AN ASCENDING ONE" + }, + { + "id": 481, + "fortune": "YOUR KNEES FORECAST WEATHER NOW. A SKILL" + }, + { + "id": 482, + "fortune": "YOUR BACK WENT OUT MORE THAN YOU DID" + }, + { + "id": 483, + "fortune": "WISDOM COMES WITH GREY HAIR. YOURS CAME EARLY" + }, + { + "id": 484, + "fortune": "YOU AGE LIKE WINE. STORED HORIZONTALLY" + }, + { + "id": 485, + "fortune": "YOUR BIRTHDAY CANDLES ARE A FIRE HAZARD" + }, + { + "id": 486, + "fortune": "YOUR READING GLASSES NEED READING GLASSES" + }, + { + "id": 487, + "fortune": "YOUR PLAYLIST IS CALLED OLDIES NOW. BY KIDS" + }, + { + "id": 488, + "fortune": "YOU GROAN WHEN SITTING. A VERBAL RECEIPT" + }, + { + "id": 489, + "fortune": "YOUR METABOLISM RETIRED WITHOUT NOTICE" + }, + { + "id": 490, + "fortune": "YOU FEEL 25. YOUR HANGOVERS DISAGREE" + }, + { + "id": 491, + "fortune": "AN APPLE A DAY. PIE IS A LOOPHOLE. SURE" + }, + { + "id": 492, + "fortune": "YOUR POSTURE IMPROVES TWICE DAILY. BRIEFLY" + }, + { + "id": 493, + "fortune": "YOU WILL SLEEP 8 HOURS. ACROSS 3 NIGHTS" + }, + { + "id": 494, + "fortune": "YOUR VITAMINS EXPIRE UNTAKEN. RENEWABLE" + }, + { + "id": 495, + "fortune": "YOUR DIET STARTS MONDAY. MONDAYS UNIONIZED" + }, + { + "id": 496, + "fortune": "YOUR DENTIST KNOWS YOU FLOSSED TODAY ONLY" + }, + { + "id": 497, + "fortune": "YOUR RESTING HEART RATE IS GREAT. FITTING" + }, + { + "id": 498, + "fortune": "YOU WILL LIVE TO 100. OUT OF SPITE" + }, + { + "id": 499, + "fortune": "YOUR HIDDEN TALENTS REMAIN WELL HIDDEN" + }, + { + "id": 500, + "fortune": "YOUR CREATIVITY HAS NO BOUNDS. OR OUTPUT" + }, + { + "id": 501, + "fortune": "YOU HAVE A GIFT. NO RECEIPT INCLUDED" + }, + { + "id": 502, + "fortune": "YOUR NOVEL WILL BE FINISHED. BY SOMEONE" + }, + { + "id": 503, + "fortune": "YOU WERE BORN TO CREATE. EXCUSES COUNT" + }, + { + "id": 504, + "fortune": "YOUR MASTERPIECE IS INSIDE YOU. COZY THERE" + }, + { + "id": 505, + "fortune": "YOUR SINGING BRINGS TEARS. BIOLOGY IS ODD" + }, + { + "id": 506, + "fortune": "THE MUSE VISITS YOU. SHE CALLS AHEAD NOW" + }, + { + "id": 507, + "fortune": "YOUR PHOTOS CAPTURE RAW CONFUSION" + }, + { + "id": 508, + "fortune": "YOU THINK OUTSIDE THE BOX. THE BOX WORRIES" + }, + { + "id": 509, + "fortune": "YOUR POETRY MOVES PEOPLE. TO THE EXITS" + }, + { + "id": 510, + "fortune": "YOUR IMAGINATION IS VAST. EXECUTION ISNT" + }, + { + "id": 511, + "fortune": "YOU SEE THE WORLD DIFFERENTLY. GET CHECKED" + }, + { + "id": 512, + "fortune": "YOUR PODCAST IDEA JOINS THE OTHER SIX" + }, + { + "id": 513, + "fortune": "CREATIVITY FLOWS PAST YOU. WAVING" + }, + { + "id": 514, + "fortune": "YOUR DOODLES BELONG SOMEWHERE. A DRAWER" + }, + { + "id": 515, + "fortune": "YOUR COOKING IS PERFORMANCE ART. WITH ALARMS" + }, + { + "id": 516, + "fortune": "YOUR SCREENPLAY HAS POTENTIAL ENERGY ONLY" + }, + { + "id": 517, + "fortune": "THE WORLD IS YOUR CANVAS. STILL PRIMED" + }, + { + "id": 518, + "fortune": "YOUR DIY PROJECTS ARE AN UNFINISHED TRILOGY" + }, + { + "id": 519, + "fortune": "YOUR MUSIC TASTE IS GREAT. NEIGHBOURS DISSENT" + }, + { + "id": 520, + "fortune": "YOUR DANCE MOVES DEFY CONVENTION. AND PHYSICS" + }, + { + "id": 521, + "fortune": "YOU TELL GREAT STORIES. ENDINGS PENDING" + }, + { + "id": 522, + "fortune": "PHARMACISTS FRAME YOUR HANDWRITING" + }, + { + "id": 523, + "fortune": "YOUR IDEAS ARRIVE AT 2AM. GONE BY 8" + }, + { + "id": 524, + "fortune": "YOUR SKETCHBOOK HOLDS GREAT PROMISES" + }, + { + "id": 525, + "fortune": "YOU COULD WRITE A BOOK. A STRONG PAMPHLET" + }, + { + "id": 526, + "fortune": "YOUR POTTERY PHASE MADE MEMORABLE PIECES" + }, + { + "id": 527, + "fortune": "YOUR INNER ARTIST WANTS COFFEE FIRST" + }, + { + "id": 528, + "fortune": "YOUR COLOUR CHOICES ARE COURAGEOUS" + }, + { + "id": 529, + "fortune": "YOUR GUITAR KNOWS THREE CHORDS WELL" + }, + { + "id": 530, + "fortune": "YOUR CREATIVE PROCESS IS UNDOCUMENTED" + }, + { + "id": 531, + "fortune": "YOUR SCARVES BECAME EXPERIENCES. KNIT ON" + }, + { + "id": 532, + "fortune": "YOU PAINT WITH EMOTION. THE FLOOR AGREES" + }, + { + "id": 533, + "fortune": "YOUR BONSAI IS A REGULAR TREE NOW. PATIENCE" + }, + { + "id": 534, + "fortune": "YOUR WRITING VOICE IS STRONG. IN TEXTS" + }, + { + "id": 535, + "fortune": "YOUR GARDEN GROWS. THE WEEDS THRIVE MOST" + }, + { + "id": 536, + "fortune": "YOUR CALLIGRAPHY PHASE WAS ONE WEEKEND" + }, + { + "id": 537, + "fortune": "YOUR WOODWORK IS HONEST. SPLINTERS DONT LIE" + }, + { + "id": 538, + "fortune": "YOUR FAN FICTION HAS FANS. BOTH OF THEM" + }, + { + "id": 539, + "fortune": "YOUR ORIGAMI FILLS THE RECYCLING BIN" + }, + { + "id": 540, + "fortune": "YOUR CREATIVE BLOCK IS LOAD BEARING NOW" + }, + { + "id": 541, + "fortune": "YOUR SOURDOUGH STARTER OUTLIVED THE HOBBY" + }, + { + "id": 542, + "fortune": "YOUR WATERCOLORS CAPTURE SOMETHING. UNCLEAR" + }, + { + "id": 543, + "fortune": "YOU EXCEL AT BEGINNINGS. MIDDLES DECLINED" + }, + { + "id": 544, + "fortune": "YOUR UKULELE EMBRACED ITS ROLE AS DECOR" + }, + { + "id": 545, + "fortune": "YOU ARE YOUR OWN WORST CRITIC. UNDISPUTED" + }, + { + "id": 546, + "fortune": "YOUR ART SPEAKS FOR ITSELF. QUIETLY" + }, + { + "id": 547, + "fortune": "YOUR STUDIO IS A CLOSET AGAIN. FUNCTIONAL" + }, + { + "id": 548, + "fortune": "YOUR FIRST DRAFTS NEVER MEET SECOND ONES" + }, + { + "id": 549, + "fortune": "YOUR CANDLE PHASE LEFT EVIDENCE. WAXY" + }, + { + "id": 550, + "fortune": "YOUR OPEN MIC DEBUT REMAINS IMMINENT" + }, + { + "id": 551, + "fortune": "YOUR PLAYLIST TRANSITIONS ARE ART. SHUFFLED" + }, + { + "id": 552, + "fortune": "YOUR COSTUMES SHOW COMMITMENT. TO CONCEPTS" + }, + { + "id": 553, + "fortune": "YOUR MACRAME OWL HAS SEEN YOUR PROJECTS" + }, + { + "id": 554, + "fortune": "YOUR VOICE MEMOS ARE MOSTLY GROCERIES" + }, + { + "id": 555, + "fortune": "YOU WILL BE DISCOVERED. ON YOUR COUCH" + }, + { + "id": 556, + "fortune": "YOUR MOOD BOARD IS ALL MOOD. NO BOARD" + }, + { + "id": 557, + "fortune": "YOUR FREESTYLE RAPS SHOULD STAY FREE" + }, + { + "id": 558, + "fortune": "YOUR MUSEUM VISITS END AT THE CAFE" + }, + { + "id": 559, + "fortune": "YOUR COVERS BEAT THE ORIGINALS. UNNOTIFIED" + }, + { + "id": 560, + "fortune": "YOUR TYPEWRITER LOOKS GREAT. STILL" + }, + { + "id": 561, + "fortune": "YOUR ABSTRACT PHASE CONFUSED EVEN YOU" + }, + { + "id": 562, + "fortune": "YOUR COMIC HAS A FOLLOWER. HI MUM" + }, + { + "id": 563, + "fortune": "YOUR SONG RHYMES ORANGE WITH DOOR HINGE" + }, + { + "id": 564, + "fortune": "YOUR CRAFT CORNER ANNEXED THE GUEST ROOM" + }, + { + "id": 565, + "fortune": "YOUR GENIUS IS MISUNDERSTOOD. AND UNSEEN" + }, + { + "id": 566, + "fortune": "YOUR MAGNUM OPUS IS BASICALLY ESPRESSO NOW" + }, + { + "id": 567, + "fortune": "YOUR INSPIRATION FOLDER HAS SUBFOLDERS" + }, + { + "id": 568, + "fortune": "THE FORTUNE YOU SEEK IS IN ANOTHER BIKKIE" + }, + { + "id": 569, + "fortune": "THIS BIKKIE CONTAINS NO FORTUNE. JUST CARBS" + }, + { + "id": 570, + "fortune": "IT IS DARK INSIDE THIS BIKKIE. AND QUIET" + }, + { + "id": 571, + "fortune": "THIS FORTUNE LEFT INTENTIONALLY BLANK" + }, + { + "id": 572, + "fortune": "CONFUCIUS SAYS NOTHING ABOUT YOU. WISELY" + }, + { + "id": 573, + "fortune": "THIS FORTUNE WAS RECALLED. SAME TO YOU" + }, + { + "id": 574, + "fortune": "YOU WILL READ THIS ALOUD. UNPROMPTED" + }, + { + "id": 575, + "fortune": "THE BIKKIE KNEW YOU WOULD TAKE THE LAST ONE" + }, + { + "id": 576, + "fortune": "SOME GET WISDOM. YOU CHOSE DESSERT" + }, + { + "id": 577, + "fortune": "THIS IS A BIKKIE. NOT A LIFE COACH" + }, + { + "id": 578, + "fortune": "YOUR LUCKY NUMBERS HAVE OPTED OUT" + }, + { + "id": 579, + "fortune": "THE BIKKIE ADMIRES YOUR OPTIMISM" + }, + { + "id": 580, + "fortune": "YOU SHOOK THE BIKKIE FIRST. IT FELT THAT" + }, + { + "id": 581, + "fortune": "ASK AGAIN LATER. OR DONT. YOUR CALL" + }, + { + "id": 582, + "fortune": "THIS BIKKIE WAS BAKED WITH LOW EXPECTATIONS" + }, + { + "id": 583, + "fortune": "THE FORTUNE WRITER IS ON BREAK. JUDGING" + }, + { + "id": 584, + "fortune": "NO WISDOM TODAY. INVENTORY ISSUES" + }, + { + "id": 585, + "fortune": "YOUR FORTUNE IS PENDING APPROVAL" + }, + { + "id": 586, + "fortune": "THIS MESSAGE WILL SELF DISAPPOINT" + }, + { + "id": 587, + "fortune": "THE BIKKIE CRUMBLES. SO DO PLANS" + }, + { + "id": 588, + "fortune": "YOU EXPECTED MORE FROM A BIKKIE. NOTED" + }, + { + "id": 589, + "fortune": "FORTUNE NOT FOUND. TRY ANOTHER DINNER" + }, + { + "id": 590, + "fortune": "THIS BIKKIE HAS SEEN YOUR ORDER HISTORY" + }, + { + "id": 591, + "fortune": "THE LAST BIKKIE ALWAYS TELLS THE TRUTH" + }, + { + "id": 592, + "fortune": "YOUR FORTUNE RAN OUT OF CHARACTERS. LUCKY" + }, + { + "id": 593, + "fortune": "A WISE MAN ONCE SAID NOTHING. TRY IT" + }, + { + "id": 594, + "fortune": "THE BIKKIE BELIEVED IN YOU. PAST TENSE" + }, + { + "id": 595, + "fortune": "THIS FORTUNE IS ACCURATE. DETAILS REDACTED" + }, + { + "id": 596, + "fortune": "YOU HOLD THE KEY TO HAPPINESS. NOW FIND IT" + }, + { + "id": 597, + "fortune": "YOUR FORTUNE IS BEING WRITTEN. SLOWLY" + }, + { + "id": 598, + "fortune": "YOUR COFFEE ORDER IS A PARAGRAPH NOW" + }, + { + "id": 599, + "fortune": "COFFEE FIRST. PERSONALITY SECOND. FAIR" + }, + { + "id": 600, + "fortune": "YOUR FOURTH COFFEE IS WATCHING YOU" + }, + { + "id": 601, + "fortune": "DECAF EXISTS. YOU PRETEND IT DOESNT" + }, + { + "id": 602, + "fortune": "YOUR MUG COLLECTION OUTGREW THE CABINET" + }, + { + "id": 603, + "fortune": "YOUR ESPRESSO MACHINE JUDGES YOUR PODS" + }, + { + "id": 604, + "fortune": "YOU CALL IT BRUNCH. IT WAS BREKKIE. LATE" + }, + { + "id": 605, + "fortune": "YOUR SALAD PHASE ENDED AT THE DRESSING" + }, + { + "id": 606, + "fortune": "YOU MEAL PLAN BEAUTIFULLY. THEN ORDER OUT" + }, + { + "id": 607, + "fortune": "YOUR SPICE TOLERANCE IS A PERSONALITY. SADLY" + }, + { + "id": 608, + "fortune": "YOUR SIGNATURE DISH HAS ONE FAN. YOU" + }, + { + "id": 609, + "fortune": "THE OVEN TIMER IS A SUGGESTION TO YOU" + }, + { + "id": 610, + "fortune": "YOUR SMOOTHIES HIDE VEGETABLES. FROM YOU" + }, + { + "id": 611, + "fortune": "YOU COOK WITH LOVE. AND TOO MUCH SALT" + }, + { + "id": 612, + "fortune": "YOUR RECIPE SUBSTITUTIONS ARE BRAVE" + }, + { + "id": 613, + "fortune": "GARLIC MEASURED WITH YOUR HEART. AGAIN" + }, + { + "id": 614, + "fortune": "YOUR TOAST GAME IS STRONG. LITERALLY BURNT" + }, + { + "id": 615, + "fortune": "YOU EAT DESSERT FIRST. LIFE IS SHORT. SURE" + }, + { + "id": 616, + "fortune": "YOUR SNACK DRAWER HAS A WAITING LIST" + }, + { + "id": 617, + "fortune": "THE FOOD DELIVERY APP KNOWS YOU BY NAME" + }, + { + "id": 618, + "fortune": "YOUR KITCHEN GADGETS FORM A MUSEUM" + }, + { + "id": 619, + "fortune": "YOUR TAKEOUT CONTAINERS ARE HEIRLOOMS NOW" + }, + { + "id": 620, + "fortune": "YOU SAY YOU COULD EAT ANYTHING. THEN DONT" + }, + { + "id": 621, + "fortune": "YOUR PIZZA TOPPINGS HAVE CRITICS" + }, + { + "id": 622, + "fortune": "YOUR DOG LOVES YOU UNCONDITIONALLY. TRAINED" + }, + { + "id": 623, + "fortune": "YOUR CAT TOLERATES YOU. A HIGH HONOUR" + }, + { + "id": 624, + "fortune": "YOUR PET EATS BETTER THAN YOU DO" + }, + { + "id": 625, + "fortune": "YOUR DOG HAS MORE MATES THAN YOU" + }, + { + "id": 626, + "fortune": "THE CAT KNOCKED IT OVER ON PURPOSE. RESPECT" + }, + { + "id": 627, + "fortune": "YOUR FISH KNOWS YOUR SCHEDULE. IT WORRIES" + }, + { + "id": 628, + "fortune": "YOUR PETS INSTAGRAM OUTPERFORMS YOURS" + }, + { + "id": 629, + "fortune": "THE DOG ATE YOUR MOTIVATION. NOT HOMEWORK" + }, + { + "id": 630, + "fortune": "YOUR CAT REVIEWS YOUR LIFE CHOICES NIGHTLY" + }, + { + "id": 631, + "fortune": "YOUR BIRD REPEATS YOUR WORST PHRASES" + }, + { + "id": 632, + "fortune": "YOUR PET THERAPIST IS A GOOD LISTENER. FREE" + }, + { + "id": 633, + "fortune": "RAIN IS COMING. YOUR CAR WASH REJOICES" + }, + { + "id": 634, + "fortune": "YOUR WINTER COAT DEBATE STARTS IN OCTOBER" + }, + { + "id": 635, + "fortune": "YOU CHECK THE WEATHER. THEN DRESS WRONG" + }, + { + "id": 636, + "fortune": "SPRING IS COMING. SO ARE YOUR ALLERGIES" + }, + { + "id": 637, + "fortune": "YOUR SUNSCREEN EXPIRES UNUSED. YEARLY" + }, + { + "id": 638, + "fortune": "SNOW IS MAGICAL. UNTIL YOU OWN A SHOVEL" + }, + { + "id": 639, + "fortune": "YOUR UMBRELLA COLLECTION LIVES ELSEWHERE" + }, + { + "id": 640, + "fortune": "SMALL TALK ABOUT WEATHER SUITS YOU NOW" + }, + { + "id": 641, + "fortune": "THE FORECAST CHANGED. YOUR PLANS DIDNT EXIST" + }, + { + "id": 642, + "fortune": "YOUR PATIO FURNITURE FEARS EVERY SEASON" + }, + { + "id": 643, + "fortune": "AUTUMN LEAVES FALL. LIKE YOUR RESOLUTIONS" + }, + { + "id": 644, + "fortune": "YOUR PASSWORD IS YOUR PETS NAME. WE KNOW" + }, + { + "id": 645, + "fortune": "YOU HAVE 47 UNREAD UPDATES. THEY WAIT" + }, + { + "id": 646, + "fortune": "YOUR CLOUD STORAGE IS FULL. OF DUPLICATES" + }, + { + "id": 647, + "fortune": "AUTOCORRECT KNOWS WHAT YOU MEANT. IT DECLINED" + }, + { + "id": 648, + "fortune": "YOUR ROUTER NEEDS A RESTART. LIKE YOU" + }, + { + "id": 649, + "fortune": "YOUR SEARCH HISTORY TELLS A SAGA" + }, + { + "id": 650, + "fortune": "YOU CLOSED ONE TAB TODAY. PROGRESS" + }, + { + "id": 651, + "fortune": "YOUR SPAM FOLDER MISSES YOU" + }, + { + "id": 652, + "fortune": "THE ALGORITHM KNOWS YOU TOO WELL. IT WORRIES" + }, + { + "id": 653, + "fortune": "YOUR SMARTWATCH SUGGESTS BREATHING. POINTED" + }, + { + "id": 654, + "fortune": "YOUR TYPING SPEED PEAKS DURING ARGUMENTS" + }, + { + "id": 655, + "fortune": "YOU MUTED THE GROUP. THE GROUP KNOWS" + }, + { + "id": 656, + "fortune": "YOUR CAPS LOCK GETS REGULAR EXERCISE" + }, + { + "id": 657, + "fortune": "YOUR EMOJI USE HAS BEEN DISCUSSED" + }, + { + "id": 658, + "fortune": "YOUR WIFI NAME IS YOUR BEST JOKE" + }, + { + "id": 659, + "fortune": "YOU RESTART THINGS AS A LIFESTYLE" + }, + { + "id": 660, + "fortune": "YOUR GYM MEMBERSHIP IS A DONATION NOW" + }, + { + "id": 661, + "fortune": "YOUR MARATHON PLAN IS SAVED. AND SAFE" + }, + { + "id": 662, + "fortune": "LEG DAY RESPECTS YOUR ABSENCE" + }, + { + "id": 663, + "fortune": "YOUR PROTEIN POWDER AGES GRACEFULLY" + }, + { + "id": 664, + "fortune": "YOU STRETCH ONCE A YEAR. AMBITIOUSLY" + }, + { + "id": 665, + "fortune": "YOUR SPORTS TEAM LOSES. YOU TAKE CREDIT" + }, + { + "id": 666, + "fortune": "YOUR FANTASY TEAM IS FANTASY INDEED" + }, + { + "id": 667, + "fortune": "YOU RUN LATE. IT COUNTS AS CARDIO" + }, + { + "id": 668, + "fortune": "YOUR HOME GYM STORES THE LAUNDRY NOW" + }, + { + "id": 669, + "fortune": "YOUR PERSONAL BEST WAS A GROUP EFFORT" + }, + { + "id": 670, + "fortune": "YOUR WARM UP IS THE WHOLE WORKOUT" + }, + { + "id": 671, + "fortune": "YOUR REST DAYS HAVE REST DAYS" + }, + { + "id": 672, + "fortune": "SLEEP IS FOR THE STRONG. YOU SCROLL" + }, + { + "id": 673, + "fortune": "YOUR NAP SCHEDULE IS YOUR REAL CALENDAR" + }, + { + "id": 674, + "fortune": "YOU COUNT SHEEP. THEY COUNT YOUR EXCUSES" + }, + { + "id": 675, + "fortune": "YOUR PILLOW KNOWS ALL YOUR SECRETS" + }, + { + "id": 676, + "fortune": "ONE MORE EPISODE WAS FOUR EPISODES" + }, + { + "id": 677, + "fortune": "YOUR DREAMS ARE VIVID. YOUR MORNINGS ARENT" + }, + { + "id": 678, + "fortune": "YOU SET SIX ALARMS. YOU FEAR NONE" + }, + { + "id": 679, + "fortune": "BEDTIME PROCRASTINATION IS STILL A CHOICE" + }, + { + "id": 680, + "fortune": "YOUR WEEKEND SLEEP DEBT COLLECTS INTEREST" + }, + { + "id": 681, + "fortune": "YOUR FAMILY LOVES YOU. THEY VOTED. CLOSE ONE" + }, + { + "id": 682, + "fortune": "YOUR MOTHER WAS RIGHT. ABOUT EVERYTHING" + }, + { + "id": 683, + "fortune": "FAMILY DINNERS TEST YOUR DIPLOMACY" + }, + { + "id": 684, + "fortune": "YOUR SIBLING REMEMBERS EVERYTHING. FOREVER" + }, + { + "id": 685, + "fortune": "YOUR RELATIVES ASK ABOUT YOUR PLANS. BRACE" + }, + { + "id": 686, + "fortune": "YOU ARE THE FAVOURITE CHILD. UNCONFIRMED" + }, + { + "id": 687, + "fortune": "YOUR FAMILY GROUP CHAT NEVER SLEEPS" + }, + { + "id": 688, + "fortune": "GRANDMA FEEDS YOU. RESISTANCE IS FUTILE" + }, + { + "id": 689, + "fortune": "YOUR DAD JOKES SKIPPED NO GENERATIONS" + }, + { + "id": 690, + "fortune": "THANKSGIVING SEATING IS POLITICAL. YOURS IS" + }, + { + "id": 691, + "fortune": "YOUR NEIGHBOUR WAVES. THE FENCE WAS HIS IDEA" + }, + { + "id": 692, + "fortune": "THE HOA NOTICED YOUR LAWN. AGAIN" + }, + { + "id": 693, + "fortune": "YOUR NEIGHBOURS KNOW YOUR SCHEDULE. AND MORE" + }, + { + "id": 694, + "fortune": "BORROWED TOOLS BECOME GIFTS EVENTUALLY" + }, + { + "id": 695, + "fortune": "YOUR DOORBELL CAMERA SAW YOUR BEST SPRINT" + }, + { + "id": 696, + "fortune": "YOUR COMMUTE BUILDS CHARACTER. DAILY" + }, + { + "id": 697, + "fortune": "TRAFFIC IS NOT PERSONAL. YOURS FEELS PERSONAL" + }, + { + "id": 698, + "fortune": "YOUR PARKING JOB SPARKED CONVERSATIONS" + }, + { + "id": 699, + "fortune": "YOUR GPS SAYS RECALCULATING. WITH FEELING" + }, + { + "id": 700, + "fortune": "YOU DRIVE LIKE YOU LIVE. SIGNALS OPTIONAL" + }, + { + "id": 701, + "fortune": "YOUR GAS TANK RUNS ON FAITH. AND FUMES" + }, + { + "id": 702, + "fortune": "THE LEFT LANE IS FOR PASSING. JUST SAYING" + }, + { + "id": 703, + "fortune": "YOUR CAR PLAYLIST PEAKED IN 2016" + }, + { + "id": 704, + "fortune": "YOUR HORN GETS MORE USE THAN YOUR BLINKER" + }, + { + "id": 705, + "fortune": "ROAD RAGE IS BENEATH YOU. USUALLY" + }, + { + "id": 706, + "fortune": "YOUR PARALLEL PARKING DRAWS AUDIENCES" + }, + { + "id": 707, + "fortune": "TIME FLIES. YOURS FILED A FLIGHT PLAN" + }, + { + "id": 708, + "fortune": "PROCRASTINATION IS AN ART. YOU TEACH IT" + }, + { + "id": 709, + "fortune": "YOU WILL DO IT TOMORROW. TOMORROW KNOWS" + }, + { + "id": 710, + "fortune": "DEADLINES APPROACH. YOU ADMIRE THE VIEW" + }, + { + "id": 711, + "fortune": "YOUR FIVE MINUTES IS EVERYONE ELSES HOUR" + }, + { + "id": 712, + "fortune": "LATER IS YOUR FAVOURITE TIME ZONE" + }, + { + "id": 713, + "fortune": "YOU FINISH THINGS. EVENTUALLY IS A WORD" + }, + { + "id": 714, + "fortune": "YOUR SOMEDAY LIST NEEDS ITS OWN SHELF" + }, + { + "id": 715, + "fortune": "TIME MANAGEMENT IS A SKILL. NOTED" + }, + { + "id": 716, + "fortune": "YOU ARE EARLY ONCE A YEAR. IT CONFUSES ALL" + }, + { + "id": 717, + "fortune": "THE PRESENT IS A GIFT. YOU CHOSE STORE CREDIT" + }, + { + "id": 718, + "fortune": "YOUR PUNCTUALITY IS LEGEND. THE BAD KIND" + }, + { + "id": 719, + "fortune": "YOU READ THIS INSTEAD OF WORKING. NOTED" + }, + { + "id": 720, + "fortune": "YOUR ATTENTION SPAN IS. OH LOOK A BIRD" + }, + { + "id": 721, + "fortune": "JUST ONE MORE MINUTE. THE FAMOUS LAST WORDS" + }, + { + "id": 722, + "fortune": "YOUR EMAILS START WITH SORRY FOR THE DELAY" + }, + { + "id": 723, + "fortune": "YOU REPLY TO TEXTS IN GEOLOGIC TIME" + }, + { + "id": 724, + "fortune": "YOUR READ RECEIPTS INCRIMINATE YOU" + }, + { + "id": 725, + "fortune": "YOU LEFT THEM ON READ. THEY NOTICED" + }, + { + "id": 726, + "fortune": "YOUR TYPING BUBBLE APPEARED. THEN LEFT" + }, + { + "id": 727, + "fortune": "YOU SAID LETS CIRCLE BACK. YOU WONT" + }, + { + "id": 728, + "fortune": "PER YOUR LAST EMAIL. YOU KNOW WHAT YOU DID" + }, + { + "id": 729, + "fortune": "YOUR VOICE NOTES ARE PODCASTS NOW" + }, + { + "id": 730, + "fortune": "YOU WAVED AT SOMEONE WAVING ELSEWHERE. AGAIN" + }, + { + "id": 731, + "fortune": "YOUR SMALL TALK HAS A SCRIPT. REHEARSE" + }, + { + "id": 732, + "fortune": "YOU SAID WE SHOULD HANG OUT SOON. CLASSIC" + }, + { + "id": 733, + "fortune": "YOUR PHONE IS ON SILENT. FOR YEARS NOW" + }, + { + "id": 734, + "fortune": "YOU SCREENSHOT INSTEAD OF RESPONDING" + }, + { + "id": 735, + "fortune": "YOUR OPINION WAS NOTED. AND RECYCLED" + }, + { + "id": 736, + "fortune": "YOU INTERRUPT WITH ENTHUSIASM. OFTEN" + }, + { + "id": 737, + "fortune": "RETAIL THERAPY IS STILL THERAPY. INVOICED" + }, + { + "id": 738, + "fortune": "YOUR PACKAGE ARRIVES TODAY. YOU ORDERED MORE" + }, + { + "id": 739, + "fortune": "YOU CAME FOR ONE ITEM. YOU LEFT WITH NINE" + }, + { + "id": 740, + "fortune": "YOUR WISHLIST IS A NOVEL NOW" + }, + { + "id": 741, + "fortune": "FREE SHIPPING COSTS YOU PLENTY" + }, + { + "id": 742, + "fortune": "YOUR RETURNS WINDOW CLOSED. LIKE ALWAYS" + }, + { + "id": 743, + "fortune": "YOU COMPARE PRICES. AFTER BUYING" + }, + { + "id": 744, + "fortune": "THE SALE ENDED. YOUR REGRET BEGINS" + }, + { + "id": 745, + "fortune": "YOUR LOYALTY CARD OUTRANKS YOUR GYM CARD" + }, + { + "id": 746, + "fortune": "YOU BUY ORGANIZERS TO ORGANISE ORGANIZERS" + }, + { + "id": 747, + "fortune": "YOUR HOME PROJECT IS 90 PERCENT DONE. FOREVER" + }, + { + "id": 748, + "fortune": "THE PAINT SAMPLES OUTLIVED THE PROJECT" + }, + { + "id": 749, + "fortune": "YOUR TOOLBOX HAS EVERYTHING BUT ANSWERS" + }, + { + "id": 750, + "fortune": "YOU MEASURED ONCE. IT SHOWS" + }, + { + "id": 751, + "fortune": "THE INSTRUCTIONS WERE OPTIONAL. APPARENTLY" + }, + { + "id": 752, + "fortune": "YOUR SHELF LEANS. YOU CALL IT CHARACTER" + }, + { + "id": 753, + "fortune": "DUCT TAPE IS NOT A PHILOSOPHY. YOURS IS" + }, + { + "id": 754, + "fortune": "YOUR SMART BULBS OUTSMART YOU NIGHTLY" + }, + { + "id": 755, + "fortune": "THE LEAK WAITED FOR THE LONG WEEKEND" + }, + { + "id": 756, + "fortune": "YOUR GAMING BACKLOG IS A SECOND CAREER" + }, + { + "id": 757, + "fortune": "ONE MORE LEVEL WAS THREE HOURS AGO" + }, + { + "id": 758, + "fortune": "YOUR CHARACTER HAS BETTER GEAR THAN YOU" + }, + { + "id": 759, + "fortune": "YOU PAUSED AN ONLINE GAME. BOLD MOVE" + }, + { + "id": 760, + "fortune": "YOUR HIGH SCORE IS SOMEONE ELSES WARMUP" + }, + { + "id": 761, + "fortune": "YOU RAGE QUIT GRACEFULLY. ALMOST" + }, + { + "id": 762, + "fortune": "YOUR BOOKSHELF JUDGES YOUR SCREEN TIME" + }, + { + "id": 763, + "fortune": "YOU BUY BOOKS FASTER THAN YOU READ THEM" + }, + { + "id": 764, + "fortune": "YOUR BOOKMARK HASNT MOVED SINCE MARCH" + }, + { + "id": 765, + "fortune": "YOU JOINED A BOOK CLUB FOR THE SNACKS" + }, + { + "id": 766, + "fortune": "THE AUDIOBOOK COUNTS. NOBODY ASKED" + }, + { + "id": 767, + "fortune": "YOUR TO BE READ PILE IS FURNITURE NOW" + }, + { + "id": 768, + "fortune": "YOU QUOTE BOOKS YOU HAVENT FINISHED" + }, + { + "id": 769, + "fortune": "YOUR KARAOKE SONG CLEARED THE ROOM. TWICE" + }, + { + "id": 770, + "fortune": "YOU SING IN THE CAR. THE CAR ENDURES" + }, + { + "id": 771, + "fortune": "YOUR SHOWER CONCERTS HAVE NO REFUNDS" + }, + { + "id": 772, + "fortune": "YOUR AIR GUITAR NEEDS TUNING SOMEHOW" + }, + { + "id": 773, + "fortune": "YOUR PLANT NAMES OUTNUMBER YOUR PLANTS" + }, + { + "id": 774, + "fortune": "YOU TALK TO YOUR PLANTS. THEY HEARD THAT" + }, + { + "id": 775, + "fortune": "YOUR SUCCULENT DIED. IMPRESSIVELY RARE" + }, + { + "id": 776, + "fortune": "THE FERN SAW EVERYTHING. IT STAYS QUIET" + }, + { + "id": 777, + "fortune": "YOUR GREEN THUMB IS MORE OF A BEIGE" + }, + { + "id": 778, + "fortune": "LUCK IS A LADDER. YOU BROUGHT A STOOL" + }, + { + "id": 779, + "fortune": "FORTUNE ARRIVES UNANNOUNCED. SO DO YOU" + }, + { + "id": 780, + "fortune": "YOUR CHARISMA WORKS OFFLINE ONLY" + }, + { + "id": 781, + "fortune": "YOU ARE A LIMITED EDITION. VERY LIMITED" + }, + { + "id": 782, + "fortune": "YOUR VIBE ATTRACTS YOUR TRIBE. EXPLAINS IT" + }, + { + "id": 783, + "fortune": "NORMAL IS A SETTING. YOURS IS CUSTOM" + }, + { + "id": 784, + "fortune": "NOT LAZY. JUST IN ENERGY SAVER MODE" + }, + { + "id": 785, + "fortune": "YOUR SARCASM IS A SECOND LANGUAGE. FLUENT" + }, + { + "id": 786, + "fortune": "YOU ARE TECHNICALLY CORRECT. THE WORST KIND" + }, + { + "id": 787, + "fortune": "YOUR EXCUSES SHOW REAL CRAFTSMANSHIP" + }, + { + "id": 788, + "fortune": "YOU PEAKED. THE TIMING WAS UNFORTUNATE" + }, + { + "id": 789, + "fortune": "YOUR COMFORT SHOW IS ON ITS NINTH RERUN" + }, + { + "id": 790, + "fortune": "YOU ARE DOING YOUR BEST. THE BEST VARIES" + }, + { + "id": 791, + "fortune": "YOUR POTENTIAL CALLED. IT LEFT NO MESSAGE" + }, + { + "id": 792, + "fortune": "YOU ARE SPECIAL. LIKE EVERYONE ELSE" + }, + { + "id": 793, + "fortune": "YOUR ENERGY TODAY IS DECORATIVE" + }, + { + "id": 794, + "fortune": "YOU ARE THE MAIN CHARACTER. OF A SIDE QUEST" + }, + { + "id": 795, + "fortune": "YOUR PLOT ARMOR HAS EXPIRED. WALK CAREFULLY" + }, + { + "id": 796, + "fortune": "YOUR REDEMPTION ARC IS IN PREPRODUCTION" + }, + { + "id": 797, + "fortune": "A COMPLIMENT NEARS. ACCEPT IT AWKWARDLY" + }, + { + "id": 798, + "fortune": "YOUR RESTING FACE HAS A REPUTATION" + }, + { + "id": 799, + "fortune": "YOU AGREED TO DISAGREE. YOU LOST" + }, + { + "id": 800, + "fortune": "YOUR EYE ROLL IS AUDIBLE SOMEHOW" + }, + { + "id": 801, + "fortune": "YOUR SIGH SPOKE PARAGRAPHS JUST NOW" + }, + { + "id": 802, + "fortune": "YOU SAID FINE. NOTHING WAS FINE" + }, + { + "id": 803, + "fortune": "YOUR THUMBS UP ENDED THREE FRIENDSHIPS" + }, + { + "id": 804, + "fortune": "NO WORRIES YOU SAID. WORRYING INTENSELY" + }, + { + "id": 805, + "fortune": "YOUR SHRUG IS DOING HEAVY LIFTING TODAY" + }, + { + "id": 806, + "fortune": "YOU SMILED THROUGH THAT. BARELY. WE SAW" + }, + { + "id": 807, + "fortune": "YOUR PATIENCE HAS A LOADING BAR. STUCK" + }, + { + "id": 808, + "fortune": "YOU FORGAVE THEM. THE SPREADSHEET REMAINS" + }, + { + "id": 809, + "fortune": "YOUR HINTS NEED SUBTITLES. STILL" + }, + { + "id": 810, + "fortune": "YOU ALMOST SAID IT. THE ALMOST WAS LOUD" + }, + { + "id": 811, + "fortune": "YOUR SILENCE HAS AN AGENDA. AS USUAL" + }, + { + "id": 812, + "fortune": "YOU LET IT GO. INTO YOUR PERMANENT RECORD" + }, + { + "id": 813, + "fortune": "YOUR CALM IS SUSPICIOUS. EVERYONE AGREES" + }, + { + "id": 814, + "fortune": "YOU TOOK THE HIGH ROAD. WITH COMMENTARY" + }, + { + "id": 815, + "fortune": "YOUR FEEDBACK SANDWICH IS ALL BREAD" + }, + { + "id": 816, + "fortune": "YOU HEARD THEM OUT. THEN PROCEEDED ANYWAY" + }, + { + "id": 817, + "fortune": "GOOD MORNING. THE BAR IS ALREADY MET" + }, + { + "id": 818, + "fortune": "YOUR OPTIMISM IS ADORABLE TODAY" + }, + { + "id": 819, + "fortune": "TODAY IS YOUR DAY. SHARE IT NICELY" + }, + { + "id": 820, + "fortune": "YOU WOKE UP LIKE THIS. CONDOLENCES" + }, + { + "id": 821, + "fortune": "THE MIRROR SAW YOU FIRST. IT COPES" + }, + { + "id": 822, + "fortune": "YOUR BEDHEAD HAS ARCHITECTURE NOW" + }, + { + "id": 823, + "fortune": "MONDAY SAW YOU COMING. IT PREPARED" + }, + { + "id": 824, + "fortune": "YOUR WEEKEND ENDED. IT SENDS REGARDS" + }, + { + "id": 825, + "fortune": "FRIDAY LOVES YOU. THE FEELING IS MUTUAL" + }, + { + "id": 826, + "fortune": "YOUR LUNCH BREAK DESERVES A MEDAL. SO LONG" + }, + { + "id": 827, + "fortune": "HUMP DAY IS A STATE OF MIND. YOURS DAILY" + }, + { + "id": 828, + "fortune": "YOUR OUTFIT SAYS EFFORT. THE CLOCK SAYS RUSH" + }, + { + "id": 829, + "fortune": "YOUR HAIR HAS PLANS OF ITS OWN TODAY" + }, + { + "id": 830, + "fortune": "YOU MATCHED YOUR SOCKS. A CEREMONY IS DUE" + }, + { + "id": 831, + "fortune": "YOUR SUNNIES HIDE NOTHING. NICE TRY" + }, + { + "id": 832, + "fortune": "DRESS FOR THE JOB YOU WANT. A NAP TESTER" + }, + { + "id": 833, + "fortune": "YOUR IRONING BOARD RETIRED YEARS AGO" + }, + { + "id": 834, + "fortune": "WRINKLES ARE A TEXTURE. YOU DECIDED" + }, + { + "id": 835, + "fortune": "YOUR FASHION CYCLE IS LAUNDRY BASED" + }, + { + "id": 836, + "fortune": "YOUR SHOES MATCH. THE OCCASION DOESNT" + }, + { + "id": 837, + "fortune": "YOU WILL WIN AN ARGUMENT. IN THE SHOWER" + }, + { + "id": 838, + "fortune": "YOUR COMEBACK ARRIVED. THREE DAYS LATE" + }, + { + "id": 839, + "fortune": "YOUR WIT IS QUICK. YOUR TIMING ISNT" + }, + { + "id": 840, + "fortune": "YOU HAD THE PERFECT REPLY. AT MIDNIGHT" + }, + { + "id": 841, + "fortune": "YOUR INNER MONOLOGUE NEEDS AN EDITOR" + }, + { + "id": 842, + "fortune": "YOUR HOT TAKES ARE ROOM TEMPERATURE" + }, + { + "id": 843, + "fortune": "YOUR OPINIONS ARRIVE PRE HEATED" + }, + { + "id": 844, + "fortune": "YOUR DEBATE STYLE IS VOLUME BASED" + }, + { + "id": 845, + "fortune": "YOU FACT CHECK OTHERS. NEVER YOURSELF" + }, + { + "id": 846, + "fortune": "YOUR TRIVIA KNOWLEDGE PEAKS AT PARTIES ONLY" + }, + { + "id": 847, + "fortune": "A DECISION AWAITS. FLIP A COIN. IGNORE IT" + }, + { + "id": 848, + "fortune": "YOUR PRO CON LIST HAS ONE COLUMN" + }, + { + "id": 849, + "fortune": "YOU ASKED EVERYONE. THEN DID YOUR THING" + }, + { + "id": 850, + "fortune": "YOUR GUT FEELING NEEDS A SECOND OPINION" + }, + { + "id": 851, + "fortune": "YOU SLEPT ON IT. THE PROBLEM STAYED AWAKE" + }, + { + "id": 852, + "fortune": "YOUR PLAN B IS PLAN A WITH REGRET" + }, + { + "id": 853, + "fortune": "YOU CHOSE CHAOS. AGAIN. CONSISTENTLY" + }, + { + "id": 854, + "fortune": "YOUR BACKUP PLAN NEEDS A BACKUP PLAN" + }, + { + "id": 855, + "fortune": "COMMITMENT SCARES YOU. SO DOES THE MENU" + }, + { + "id": 856, + "fortune": "YOU ORDERED THE SAME THING. ADVENTUROUS" + }, + { + "id": 857, + "fortune": "CHANGE IS COMING. YOU WILL RESIST IT" + }, + { + "id": 858, + "fortune": "A SURPRISE AWAITS. YOU HATE SURPRISES" + }, + { + "id": 859, + "fortune": "GOOD NEWS COMES IN THREES. YOURS CARPOOLS" + }, + { + "id": 860, + "fortune": "YOUR HOROSCOPE QUIT. IT CITED BURNOUT" + }, + { + "id": 861, + "fortune": "MERCURY IS IN RETROGRADE. CONVENIENT EXCUSE" + }, + { + "id": 862, + "fortune": "YOUR TAROT READER NEEDED A BREAK AFTER YOU" + }, + { + "id": 863, + "fortune": "THE MAGIC 8 BALL SAID ASK SOMEONE ELSE" + }, + { + "id": 864, + "fortune": "YOUR PALM READER REQUESTED GLOVES" + }, + { + "id": 865, + "fortune": "THE CRYSTAL BALL FOGGED UP. ON PURPOSE" + }, + { + "id": 866, + "fortune": "YOUR TEA LEAVES SPELLED LUCK. SARCASTICALLY" + }, + { + "id": 867, + "fortune": "YOUR VISION QUEST FOUND THE COUCH" + }, + { + "id": 868, + "fortune": "THE UNIVERSE IS TESTING YOU. OPEN BOOK. STILL" + }, + { + "id": 869, + "fortune": "YOUR MANIFESTATIONS NEED A COSIGNER" + }, + { + "id": 870, + "fortune": "YOUR CHAKRAS ARE ALIGNED. WITH THE FRIDGE" + }, + { + "id": 871, + "fortune": "YOUR ZEN GARDEN NEEDS WEEDING. IRONIC" + }, + { + "id": 872, + "fortune": "YOUR MANTRA IS FIVE MORE MINUTES" + }, + { + "id": 873, + "fortune": "YOUR THIRD EYE NEEDS GLASSES TOO" + }, + { + "id": 874, + "fortune": "YOUR ENERGY CLEANSE MISSED A SPOT" + }, + { + "id": 875, + "fortune": "YOUR SAGE SMOKE SET OFF THE ALARM. FITTING" + }, + { + "id": 876, + "fortune": "INNER PEACE IS CLOSE. TRY THE OTHER DOOR" + }, + { + "id": 877, + "fortune": "A STRANGER WILL SMILE AT YOU. STAY CALM" + }, + { + "id": 878, + "fortune": "SOMEONE THOUGHT OF YOU TODAY. BRIEFLY" + }, + { + "id": 879, + "fortune": "YOU CROSSED SOMEONES MIND. IT KEPT WALKING" + }, + { + "id": 880, + "fortune": "AN ADMIRER WATCHES. IT IS YOUR CAT" + }, + { + "id": 881, + "fortune": "YOU WERE MENTIONED TODAY. CONTEXT UNKNOWN" + }, + { + "id": 882, + "fortune": "YOUR NAME CAME UP. THE ROOM WENT QUIET" + }, + { + "id": 883, + "fortune": "SOMEONE MISSES YOU. THEY LOST YOUR NUMBER" + }, + { + "id": 884, + "fortune": "YOU ARE UNFORGETTABLE. MANY HAVE TRIED" + }, + { + "id": 885, + "fortune": "YOUR ABSENCE WAS NOTICED. AND ENJOYED" + }, + { + "id": 886, + "fortune": "PEOPLE TALK ABOUT YOU. KEEP IT THAT WAY" + }, + { + "id": 887, + "fortune": "YOUR REPUTATION ARRIVED FIRST. APOLOGISE" + }, + { + "id": 888, + "fortune": "YOU MADE AN IMPRESSION. DENTS COUNT" + }, + { + "id": 889, + "fortune": "SOMEONE QUOTED YOU TODAY. INCORRECTLY" + }, + { + "id": 890, + "fortune": "YOU INSPIRED SOMEONE. TO DO THE OPPOSITE" + }, + { + "id": 891, + "fortune": "YOUR LEGACY IS SAFE. NOBODY CHECKED" + }, + { + "id": 892, + "fortune": "HISTORY WILL REMEMBER YOU. AS A FOOTNOTE" + }, + { + "id": 893, + "fortune": "YOUR BIOGRAPHY NEEDS A GHOSTWRITER. AND PLOT" + }, + { + "id": 894, + "fortune": "YOUR AUTOGRAPH IS WORTHLESS. FOR NOW" + }, + { + "id": 895, + "fortune": "FAME AWAITS. LOCAL FAME. VERY LOCAL" + }, + { + "id": 896, + "fortune": "YOUR FIFTEEN MINUTES ARE BUFFERING" + }, + { + "id": 897, + "fortune": "YOU WILL FIND YOUR KEYS. WHERE YOU LEFT THEM" + }, + { + "id": 898, + "fortune": "YOUR PHONE IS IN YOUR OTHER HAND. AGAIN" + }, + { + "id": 899, + "fortune": "YOU WALKED INTO A ROOM. THE REASON LEFT" + }, + { + "id": 900, + "fortune": "YOUR MEMORY IS FINE. IT JUST CURATES" + }, + { + "id": 901, + "fortune": "YOU FORGOT WHY YOU OPENED THE FRIDGE. SNACK" + }, + { + "id": 902, + "fortune": "YOUR MENTAL NOTES NEED A FILING SYSTEM" + }, + { + "id": 903, + "fortune": "YOU REMEMBERED THE TUNE. NOT THE TASK" + }, + { + "id": 904, + "fortune": "YOUR SHOPPING MEMORY EXPIRES AT THE STORE" + }, + { + "id": 905, + "fortune": "YOU KNOW THAT FACE. THE NAME ESCAPED. RUN" + }, + { + "id": 906, + "fortune": "YOUR BRAIN HAS TABS OPEN TOO. ALL FROZEN" + }, + { + "id": 907, + "fortune": "A GREAT IDEA CAME AND WENT. UNRECORDED" + }, + { + "id": 908, + "fortune": "YOU LOCKED THE DOOR. PROBABLY. GO CHECK" + }, + { + "id": 909, + "fortune": "YOUR TRAIN OF THOUGHT DEPARTED WITHOUT YOU" + }, + { + "id": 910, + "fortune": "YOU RETRACED YOUR STEPS. THEY LED TO SNACKS" + }, + { + "id": 911, + "fortune": "YOUR PASSWORD RESET NEEDS A PASSWORD RESET" + }, + { + "id": 912, + "fortune": "LIFE IS SHORT. YOUR MEETINGS ARENT" + }, + { + "id": 913, + "fortune": "LIFE GAVE YOU LEMONS. YOU FILED A COMPLAINT" + }, + { + "id": 914, + "fortune": "LIFE IS A JOURNEY. YOURS NEEDS A MAP" + }, + { + "id": 915, + "fortune": "LIFE IS WHAT HAPPENS WHILE YOU SCROLL" + }, + { + "id": 916, + "fortune": "LIFE HAS NO REMOTE. GET UP AND CHANGE IT" + }, + { + "id": 917, + "fortune": "LIFE IS NOT A RACE. YOU PARKED ANYWAY" + }, + { + "id": 918, + "fortune": "LIFE IS A GIFT. KEEP THE RECEIPT" + }, + { + "id": 919, + "fortune": "LIFE IS UNSCRIPTED. YOURS NEEDS A WRITER" + }, + { + "id": 920, + "fortune": "LIFE REWARDS ACTION. YOURS SENT A MEMO" + }, + { + "id": 921, + "fortune": "LIFE IS A CLASSROOM. YOU SIT IN BACK" + }, + { + "id": 922, + "fortune": "YOUR STORY HAS PLOT HOLES. EMBRACE THEM" + }, + { + "id": 923, + "fortune": "YOUR CHAPTER TWO NEEDS CHAPTER ONE DONE" + }, + { + "id": 924, + "fortune": "YOUR SEQUEL DEPENDS ON THIS PART. NO PRESSURE" + }, + { + "id": 925, + "fortune": "YOUR NARRATOR SOUNDS TIRED LATELY" + }, + { + "id": 926, + "fortune": "YOUR CLIFFHANGER IS JUST INDECISION" + }, + { + "id": 927, + "fortune": "TOMORROW IS A NEW DAY. SAME YOU THOUGH" + }, + { + "id": 928, + "fortune": "YESTERDAY CALLED. IT WANTS NOTHING BACK" + }, + { + "id": 929, + "fortune": "TODAY WILL BE PRODUCTIVE. STARTING LATER" + }, + { + "id": 930, + "fortune": "THIS WEEK HAS POTENTIAL. HAD. HAS. UNCLEAR" + }, + { + "id": 931, + "fortune": "NEXT MONTH YOU WILL START. NEXT MONTH AGREES" + }, + { + "id": 932, + "fortune": "THE YEAR IS HALF OVER. YOUR GOALS ARE INTACT" + }, + { + "id": 933, + "fortune": "SOON IS NOT A DATE. YOU TREAT IT AS ONE" + }, + { + "id": 934, + "fortune": "EVENTUALLY IS YOUR FAVOURITE DEADLINE" + }, + { + "id": 935, + "fortune": "THE RIGHT TIME NEVER COMES. CONVENIENT" + }, + { + "id": 936, + "fortune": "YOUR WINDOW OF OPPORTUNITY HAS BLINDS" + }, + { + "id": 937, + "fortune": "PATIENCE COMES TO THOSE WHO WAIT. IRONIC" + }, + { + "id": 938, + "fortune": "YOUR TURN IS COMING. THE LINE MOVED ON" + }, + { + "id": 939, + "fortune": "YOU WAITED FOR A SIGN. THIS IS NOT IT" + }, + { + "id": 940, + "fortune": "HOLD THAT THOUGHT. FOREVER IF POSSIBLE" + }, + { + "id": 941, + "fortune": "YOUR PAUSE BUTTON IS YOUR FAVOURITE" + }, + { + "id": 942, + "fortune": "YOU PUT A PIN IN IT. THE PIN CUSHION IS FULL" + }, + { + "id": 943, + "fortune": "IT IS ON YOUR RADAR. YOUR RADAR IS OFF" + }, + { + "id": 944, + "fortune": "YOU WILL GET TO IT. IT STOPPED WAITING" + }, + { + "id": 945, + "fortune": "THE BALL IS IN YOUR COURT. GATHERING DUST" + }, + { + "id": 946, + "fortune": "YOUR FOLLOW UP NEEDS A FOLLOW UP" + }, + { + "id": 947, + "fortune": "CONSIDER THIS YOUR REMINDER. THE FOURTH" + }, + { + "id": 948, + "fortune": "YOUR INTENTIONS ARE GOOD. AND STATIONARY" + }, + { + "id": 949, + "fortune": "EFFORT WAS MADE. A ROUNDING ERROR OF IT" + }, + { + "id": 950, + "fortune": "YOU GAVE 100 PERCENT. OF YOUR MINIMUM" + }, + { + "id": 951, + "fortune": "YOUR BEST GUESS BEAT YOUR RESEARCH. AGAIN" + }, + { + "id": 952, + "fortune": "CLOSE ENOUGH IS YOUR SIGNATURE MOVE" + }, + { + "id": 953, + "fortune": "GOOD ENOUGH SHIPPED. PERFECTION SULKS" + }, + { + "id": 954, + "fortune": "YOUR SHORTCUTS TOOK THE LONG WAY" + }, + { + "id": 955, + "fortune": "YOU IMPROVISED. THE RESULTS IMPROVISED BACK" + }, + { + "id": 956, + "fortune": "YOUR WINGING IT HAS FREQUENT FLYER MILES" + }, + { + "id": 957, + "fortune": "DETAILS MATTER. YOURS WENT ON STRIKE" + }, + { + "id": 958, + "fortune": "YOUR ROUGH DRAFT WENT TO PRODUCTION" + }, + { + "id": 959, + "fortune": "YOU SKIMMED IT. IT SHOWED. IT ALWAYS SHOWS" + }, + { + "id": 960, + "fortune": "YOUR PROOFREADING MISSED THIS SENTENCE TOO" + }, + { + "id": 961, + "fortune": "ALMOST DONE IS YOUR PERMANENT ADDRESS" + }, + { + "id": 962, + "fortune": "YOUR CHECKLIST HAS SURVIVORS" + }, + { + "id": 963, + "fortune": "DONE IS BETTER THAN PERFECT. TRY EITHER" + }, + { + "id": 964, + "fortune": "YOUR STANDARDS SLIPPED. THEY LEFT A NOTE" + }, + { + "id": 965, + "fortune": "MEDIOCRITY TAKES EFFORT TOO. WELL DONE" + }, + { + "id": 966, + "fortune": "YOU EXCEEDED EXPECTATIONS. THEY WERE LOW" + }, + { + "id": 967, + "fortune": "YOUR BARE MINIMUM HAS A LOYAL FOLLOWING" + }, + { + "id": 968, + "fortune": "YOU RAISED THE BAR. FROM THE FLOOR" + }, + { + "id": 969, + "fortune": "CONSISTENCY IS KEY. YOURS IS CONSISTENT" + }, + { + "id": 970, + "fortune": "YOUR AVERAGE IS ABOVE AVERAGE. AT AVERAGING" + }, + { + "id": 971, + "fortune": "PROGRESS WAS MADE. LATERALLY. BUT MADE" + }, + { + "id": 972, + "fortune": "YOUR TRAJECTORY IS STABLE. FLAT. STABLE" + }, + { + "id": 973, + "fortune": "ONWARD AND UPWARD. OR SIDEWAYS. ONWARD" + }, + { + "id": 974, + "fortune": "YOU PIVOTED. THE DIRECTION WAS RANDOM" + }, + { + "id": 975, + "fortune": "YOUR MOMENTUM TOOK A PERSONAL DAY" + }, + { + "id": 976, + "fortune": "FULL SPEED AHEAD. YOUR SPEED. SO GENTLE" + }, + { + "id": 977, + "fortune": "YOU HIT THE GROUND WALKING. BRISKLY EVEN" + }, + { + "id": 978, + "fortune": "YOUR SPRINT BECAME A STROLL. SCENIC ONE" + }, + { + "id": 979, + "fortune": "BABY STEPS COUNT. YOURS ARE NEWBORN" + }, + { + "id": 980, + "fortune": "YOU ARE ON A ROLL. A SLOW GENTLE ROLL" + }, + { + "id": 981, + "fortune": "YOUR STREAK CONTINUES. WHICHEVER ONE" + }, + { + "id": 982, + "fortune": "MOMENTUM IS BUILDING. SOMEWHERE ELSE" + }, + { + "id": 983, + "fortune": "THE FINISH LINE MOVED. SO DID YOU. BACKWARD" + }, + { + "id": 984, + "fortune": "YOUR MARATHON IS A SERIES OF SNACK BREAKS" + }, + { + "id": 985, + "fortune": "YOU CROSSED A LINE TODAY. NOT THE FINISH ONE" + }, + { + "id": 986, + "fortune": "VICTORY IS NEAR. DEFINE VICTORY LOOSELY" + }, + { + "id": 987, + "fortune": "YOU WON THE ARGUMENT. THE ROOM WAS EMPTY" + }, + { + "id": 988, + "fortune": "CHAMPIONS TRAIN DAILY. YOU WATCHED THEM" + }, + { + "id": 989, + "fortune": "YOUR TROPHY SHELF HOLDS PARTICIPATION" + }, + { + "id": 990, + "fortune": "SECOND PLACE IS FIRST LOSER. YOU GOT FIFTH" + }, + { + "id": 991, + "fortune": "YOUR COMEBACK SEASON IS ALWAYS NEXT SEASON" + }, + { + "id": 992, + "fortune": "THE UNDERDOG STORY NEEDS AN ATTEMPT FIRST" + }, + { + "id": 993, + "fortune": "YOUR RIVALS STOPPED CHECKING ON YOU" + }, + { + "id": 994, + "fortune": "GLORY AWAITS. IT DOUBLE BOOKED THOUGH" + }, + { + "id": 995, + "fortune": "YOU MADE THE HIGHLIGHT REEL. AS A BLOOPER" + }, + { + "id": 996, + "fortune": "HISTORY FAVOURS THE BOLD. YOU FAVOUR NAPS" + }, + { + "id": 997, + "fortune": "YOUR SPLIT FLAP DISPLAY DESERVED BETTER TEXT" + }, + { + "id": 998, + "fortune": "THIS MESSAGE USED 45 CHARACTERS WISELY. ISH" + }, + { + "id": 999, + "fortune": "YOU BUILT A DISPLAY FOR THIS. WORTH IT" + }, + { + "id": 1000, + "fortune": "EACH FLAP TURNED FOR THIS WISDOM. SAVOR IT" + }, + { + "id": 1001, + "fortune": "SHE WILL BE RIGHT. SHE IS NOT RIGHT" + }, + { + "id": 1002, + "fortune": "NO WORRIES. THERE ARE SEVERAL WORRIES" + }, + { + "id": 1003, + "fortune": "YOUR ARVO PLANS BECAME A NAP. GOOD CALL" + }, + { + "id": 1004, + "fortune": "A BIN CHICKEN NICKED YOUR CHIPS. FAIR ENOUGH" + }, + { + "id": 1005, + "fortune": "YOU ARE FLAT OUT LIKE A LIZARD DRINKING. STILL BROKE" + }, + { + "id": 1006, + "fortune": "YOUR SERVO PIE IS THE HIGHLIGHT OF THE WEEK" + }, + { + "id": 1007, + "fortune": "A BIT DODGY MATE. LIKE YOUR PLANS" + }, + { + "id": 1008, + "fortune": "YOUR ESKY IS CHOCKERS. YOUR WALLET ISNT" + }, + { + "id": 1009, + "fortune": "HEAPS GOOD NEWS AHEAD. HEAPS DELAYED" + }, + { + "id": 1010, + "fortune": "YOUR THONGS BROKE AT THE WORST TIME. AS THEY DO" + }, + { + "id": 1011, + "fortune": "A MACCAS RUN AT MIDNIGHT COUNTS AS A PLAN" + }, + { + "id": 1012, + "fortune": "YOU RECKON IT WILL WORK. YOU RECKON HEAPS OF THINGS" + }, + { + "id": 1013, + "fortune": "STOP CARRYING ON LIKE A PORK CHOP" + }, + { + "id": 1014, + "fortune": "YOUR SUNNIES ARE ON YOUR HEAD. AS ALWAYS" + }, + { + "id": 1015, + "fortune": "TOO EASY MATE. NARR IT WAS NOT" + }, + { + "id": 1016, + "fortune": "YOUR BARBIE PLANS MET A SUDDEN SOUTHERLY" + } +] \ No newline at end of file diff --git a/apps/sarcastic-fortune-cookies/fortunes_en-gb.json b/apps/sarcastic-fortune-cookies/fortunes_en-gb.json new file mode 100644 index 0000000..e6a76b7 --- /dev/null +++ b/apps/sarcastic-fortune-cookies/fortunes_en-gb.json @@ -0,0 +1,4066 @@ +[ + { + "id": 1, + "fortune": "EVERYTHING HAPPENS. RARELY FOR A REASON" + }, + { + "id": 2, + "fortune": "GOOD LUCK IS COMING. IT WALKS SLOWLY" + }, + { + "id": 3, + "fortune": "THE UNIVERSE HAS A PLAN. A MODEST ONE" + }, + { + "id": 4, + "fortune": "YOUR DESTINY CALLED. IT RESCHEDULED" + }, + { + "id": 5, + "fortune": "FATE HAS PLANS FOR YOU. BACKUP PLANS" + }, + { + "id": 6, + "fortune": "THE STARS ALIGNED. THEN THEY SAW YOU" + }, + { + "id": 7, + "fortune": "LUCK FAVOURS THE BOLD. YOU CAN WATCH" + }, + { + "id": 8, + "fortune": "YOUR SHIP IS COMING IN. IT IS A CANOE" + }, + { + "id": 9, + "fortune": "TRUST THE PROCESS. IT DOES NOT TRUST YOU" + }, + { + "id": 10, + "fortune": "A SIGN IS COMING. IT SAYS YIELD" + }, + { + "id": 11, + "fortune": "YOUR LUCKY STAR IS IN ANOTHER GALAXY" + }, + { + "id": 12, + "fortune": "KARMA IS REAL. THAT EXPLAINS A LOT" + }, + { + "id": 13, + "fortune": "THE FUTURE IS BRIGHT. WEAR SUNSCREEN" + }, + { + "id": 14, + "fortune": "DESTINY AWAITS. IT BROUGHT A BOOK" + }, + { + "id": 15, + "fortune": "MIRACLES HAPPEN DAILY. ELSEWHERE MOSTLY" + }, + { + "id": 16, + "fortune": "YOUR MOMENT IS COMING. IT IS IN TRAFFIC" + }, + { + "id": 17, + "fortune": "FORTUNE KNOCKED ONCE. YOU WERE OUT" + }, + { + "id": 18, + "fortune": "THE TIDE IS TURNING. GLACIALLY" + }, + { + "id": 19, + "fortune": "BIG THINGS ARE COMING. LOWER THE BAR" + }, + { + "id": 20, + "fortune": "OPPORTUNITY IS NEAR. YOU TWO KEEP MISSING" + }, + { + "id": 21, + "fortune": "THE COSMOS HEARD YOU. YOU ARE IN THE QUEUE" + }, + { + "id": 22, + "fortune": "YOUR STARS ARE FINE. YOUR CHOICES ARENT" + }, + { + "id": 23, + "fortune": "WISH ON A STAR. THE STAR MARKED IT SPAM" + }, + { + "id": 24, + "fortune": "GOOD THINGS TAKE TIME. YOURS TAKE DECADES" + }, + { + "id": 25, + "fortune": "A WINDFALL NEARS. DRESS IN LAYERS" + }, + { + "id": 26, + "fortune": "THE SIGNS WERE THERE. READING IS OPTIONAL" + }, + { + "id": 27, + "fortune": "YOU ARE ONE IN A MILLION. SO ARE THOUSANDS" + }, + { + "id": 28, + "fortune": "THE BEST IS YET TO COME. NO RUSH APPARENTLY" + }, + { + "id": 29, + "fortune": "YOUR AURA IS RADIANT. IT WORKS OVERTIME" + }, + { + "id": 30, + "fortune": "LADY LUCK SMILED. POLITELY. AT A STRANGER" + }, + { + "id": 31, + "fortune": "YOUR PRAYERS WERE HEARD. AND FILED" + }, + { + "id": 32, + "fortune": "ABUNDANCE FLOWS TO YOU. UPSTREAM SADLY" + }, + { + "id": 33, + "fortune": "FORTUNES WHEEL TURNS. YOU ARE THE HAMSTER" + }, + { + "id": 34, + "fortune": "EVERY CLOUD HAS A LINING. YOURS IS DAMP" + }, + { + "id": 35, + "fortune": "SERENDIPITY IS ASKING AROUND ABOUT YOU" + }, + { + "id": 36, + "fortune": "A LUCKY BREAK NEARS. BREAK HAS TWO MEANINGS" + }, + { + "id": 37, + "fortune": "THE ORACLE SPOKE. MOSTLY IT SIGHED" + }, + { + "id": 38, + "fortune": "YOUR HOROSCOPE LOOKS GREAT. IT IS FICTION" + }, + { + "id": 39, + "fortune": "COSMIC JUSTICE EXISTS. SETTLE OUT OF COURT" + }, + { + "id": 40, + "fortune": "LIGHTNING WONT STRIKE TWICE. YOU MIGHT" + }, + { + "id": 41, + "fortune": "FATE REWARDS PATIENCE. INVOICE PENDING" + }, + { + "id": 42, + "fortune": "THE MOON SAW YOUR FRESH START. AGAIN" + }, + { + "id": 43, + "fortune": "YOUR SPIRIT GUIDE REQUESTED A TRANSFER" + }, + { + "id": 44, + "fortune": "ANGELS WATCH OVER YOU. TAKING NOTES" + }, + { + "id": 45, + "fortune": "BELIEVE AND RECEIVE. RECEIPTS VARY" + }, + { + "id": 46, + "fortune": "YOUR GOLDEN ERA NEARS. GOLD PLATED" + }, + { + "id": 47, + "fortune": "WHAT IS MEANT FOR YOU IS NOT HURRYING" + }, + { + "id": 48, + "fortune": "THE UNIVERSE WINKED. OR HAD AN EYE ISSUE" + }, + { + "id": 49, + "fortune": "YOUR LUCK IS CHANGING. DIRECTION UNCLEAR" + }, + { + "id": 50, + "fortune": "PROVIDENCE PROVIDES. IT CHECKS RECEIPTS" + }, + { + "id": 51, + "fortune": "SUCCESS IS NEAR. SO IS THE SNOOZE BUTTON" + }, + { + "id": 52, + "fortune": "HARD WORK PAYS OFF. IN EXPOSURE" + }, + { + "id": 53, + "fortune": "YOUR POTENTIAL IS LIMITLESS. AND UNUSED" + }, + { + "id": 54, + "fortune": "DREAM BIG. SET AN ALARM THOUGH" + }, + { + "id": 55, + "fortune": "THE EARLY BIRD LEFT WITHOUT YOU" + }, + { + "id": 56, + "fortune": "YOUR BIG BREAK NEARS. DO NOT BREAK IT" + }, + { + "id": 57, + "fortune": "ROME WASNT BUILT IN A DAY. YOURS ISNT STARTED" + }, + { + "id": 58, + "fortune": "YOUR TALENTS WILL BE FOUND. WITH A WARRANT" + }, + { + "id": 59, + "fortune": "AIM FOR THE STARS. MIND THE CEILING" + }, + { + "id": 60, + "fortune": "WINNERS NEVER QUIT. YOU FOUND COASTING" + }, + { + "id": 61, + "fortune": "YOUR IDEAS ARE AHEAD. YOUR DEADLINES BEHIND" + }, + { + "id": 62, + "fortune": "EXCELLENCE IS A HABIT. SO IS YOURS" + }, + { + "id": 63, + "fortune": "YOUR WORK SPEAKS. IT REQUESTED A LAWYER" + }, + { + "id": 64, + "fortune": "THE TOP IS LONELY. YOU SOLVED THAT PROBLEM" + }, + { + "id": 65, + "fortune": "A PROMOTION NEARS. SOMEONE ELSES" + }, + { + "id": 66, + "fortune": "YOUR RESUME IMPRESSES FICTION FANS" + }, + { + "id": 67, + "fortune": "YOUR BEST WORK IS AHEAD. IT IS VERY FAST" + }, + { + "id": 68, + "fortune": "THAT MEETING COULD HAVE BEEN AN EMAIL" + }, + { + "id": 69, + "fortune": "YOUR INBOX IS NOT YOUR ENEMY. JUST A WITNESS" + }, + { + "id": 70, + "fortune": "DEADLINES FEAR NOTHING. YOU WALK PAST THEM" + }, + { + "id": 71, + "fortune": "YOUR FIVE YEAR PLAN IS FIVE YEARS OLD" + }, + { + "id": 72, + "fortune": "SLOW AND STEADY WINS. YOU NAILED SLOW" + }, + { + "id": 73, + "fortune": "YOUR CALENDAR IS FULL. OF BUFFERS" + }, + { + "id": 74, + "fortune": "YOUR TEAM DREAMS TOO. OF TRANSFERS" + }, + { + "id": 75, + "fortune": "THE OCCASION LOWERED ITSELF TO MEET YOU" + }, + { + "id": 76, + "fortune": "SUCCESS LEAVES CLUES. YOU PREFER MYSTERY" + }, + { + "id": 77, + "fortune": "YOUR COMFORT ZONE NEEDS ITS OWN POSTCODE" + }, + { + "id": 78, + "fortune": "QUALITY OVER QUANTITY. YOU DECLINED BOTH" + }, + { + "id": 79, + "fortune": "YOUR INSTINCTS ARE SHARP. FACTS ON SABBATICAL" + }, + { + "id": 80, + "fortune": "YOU WORK WELL UNDER PRESSURE. IT DISAGREES" + }, + { + "id": 81, + "fortune": "MASTERY TAKES 10000 HOURS. YOU HAVE 40" + }, + { + "id": 82, + "fortune": "YOUR DESK PLANT BELIEVED IN YOU. AND WATER" + }, + { + "id": 83, + "fortune": "THE PRINTER KNOWS YOU ARE IN A HURRY" + }, + { + "id": 84, + "fortune": "YOUR LEGACY IS UNDER CONSTRUCTION. STILL" + }, + { + "id": 85, + "fortune": "AN AWARD AWAITS. PARTICIPATION COUNTS" + }, + { + "id": 86, + "fortune": "YOUR CAN DO ATTITUDE MET YOUR DID NOT RECORD" + }, + { + "id": 87, + "fortune": "NETWORKING OPENS DOORS. TRY KNOCKING TOO" + }, + { + "id": 88, + "fortune": "YOU LEAD BY EXAMPLE. HR WANTS A NEW ONE" + }, + { + "id": 89, + "fortune": "YOUR SIDE HUSTLE JOINED THE OTHERS. RESTING" + }, + { + "id": 90, + "fortune": "GENIUS IS 1 PERCENT WORK. YOU STOPPED THERE" + }, + { + "id": 91, + "fortune": "YOUR OUT OF OFFICE REPLY IS YOUR BEST WORK" + }, + { + "id": 92, + "fortune": "FAILURE IS A STEPPING STONE. NICE STAIRCASE" + }, + { + "id": 93, + "fortune": "YOU ARE IRREPLACEABLE. THE POSTING IS DRAFTED" + }, + { + "id": 94, + "fortune": "YOUR PERFORMANCE REVIEW NEARS. REHEARSE" + }, + { + "id": 95, + "fortune": "EFFICIENCY IS YOUR MIDDLE NAME. LEGALLY ONLY" + }, + { + "id": 96, + "fortune": "YOU WILL LAND ON YOUR FEET. LANDING DELAYED" + }, + { + "id": 97, + "fortune": "THE SUMMIT IS CLOSE. SO IS BASE CAMP" + }, + { + "id": 98, + "fortune": "YOUR AMBITION WOKE UP. YOUR ALARM DID NOT" + }, + { + "id": 99, + "fortune": "BE YOURSELF. EVERYONE ELSE WAS ALARMED" + }, + { + "id": 100, + "fortune": "KNOW THYSELF. A BRIEF INTRO MAY HELP" + }, + { + "id": 101, + "fortune": "YOUR GLOW UP IS COMING. NOTIFY THE GRID" + }, + { + "id": 102, + "fortune": "NEW YEAR NEW YOU. THE OLD YOU LEFT NOTES" + }, + { + "id": 103, + "fortune": "YOU ARE ENOUGH. THE BAR WAS ADJUSTABLE" + }, + { + "id": 104, + "fortune": "YOUR INNER PEACE SAW YOUR SCHEDULE. IT LEFT" + }, + { + "id": 105, + "fortune": "GROWTH TAKES TIME. YOURS FILED AN EXTENSION" + }, + { + "id": 106, + "fortune": "YOU ARE A WORK IN PROGRESS. MOSTLY WORK" + }, + { + "id": 107, + "fortune": "SELF AWARENESS IS A GIFT. YOURS SHIPPED" + }, + { + "id": 108, + "fortune": "HEALING IS NOT LINEAR. YOURS IS A SCRIBBLE" + }, + { + "id": 109, + "fortune": "YOUR BEST SELF IS EMERGING. IT HIT SNOOZE" + }, + { + "id": 110, + "fortune": "SELF CARE MATTERS. STREAMING IS NOT A SPA" + }, + { + "id": 111, + "fortune": "YOUR BOUNDARIES ARE HEALTHY. AND UNTESTED" + }, + { + "id": 112, + "fortune": "GRATITUDE HELPS. TRY IT ON PEOPLE" + }, + { + "id": 113, + "fortune": "YOU FORGIVE EASILY. YOURSELF ESPECIALLY" + }, + { + "id": 114, + "fortune": "YOUR HABITS DEFINE YOU. OUR CONDOLENCES" + }, + { + "id": 115, + "fortune": "SMALL STEPS COUNT. EVEN TO THE FRIDGE" + }, + { + "id": 116, + "fortune": "YOUR INTUITION IS POWERFUL. AND WRONG" + }, + { + "id": 117, + "fortune": "MINDFULNESS HELPS. YOUR PHONE DISAGREES" + }, + { + "id": 118, + "fortune": "YOU LET GO OF THE PAST. IT HAS YOUR ADDRESS" + }, + { + "id": 119, + "fortune": "YOUR MORNING ROUTINE IS SACRED. ALL 4 MINUTES" + }, + { + "id": 120, + "fortune": "YOU ARE LEARNING TO SAY NO. TO VEGETABLES" + }, + { + "id": 121, + "fortune": "YOUR WILLPOWER LIFTS ALMOST A WHOLE BISCUIT" + }, + { + "id": 122, + "fortune": "YOUR PURPOSE EXISTS. IT STAYS ANONYMOUS" + }, + { + "id": 123, + "fortune": "YOU EMBRACE CHANGE. MOSTLY COUCH CHANGE" + }, + { + "id": 124, + "fortune": "YOUR JOURNAL IS RAW AND HONEST. BOTH PAGES" + }, + { + "id": 125, + "fortune": "YOU LIVE YOUR TRUTH. FACT CHECK PENDING" + }, + { + "id": 126, + "fortune": "YOUR INNER CRITIC ONLY SIGHS NOW" + }, + { + "id": 127, + "fortune": "YOUR TRANSFORMATION NEEDED LABELLED PHOTOS" + }, + { + "id": 128, + "fortune": "YOUR GOALS ARE IN SIGHT. NICE BINOCULARS" + }, + { + "id": 129, + "fortune": "YOUR NEW HABITS LOOK FAMILIAR. SUSPICIOUSLY" + }, + { + "id": 130, + "fortune": "YOUR ENERGY IS MAGNETIC. TO OBLIGATIONS" + }, + { + "id": 131, + "fortune": "YOUR WALLS CAME DOWN. THE MOAT EXPANDED" + }, + { + "id": 132, + "fortune": "YOU CELEBRATE SMALL WINS. DAILY. SOMEONE MUST" + }, + { + "id": 133, + "fortune": "YOUR EVOLUTION PUZZLES DARWIN" + }, + { + "id": 134, + "fortune": "YOU PUT YOURSELF FIRST. THE LINE WAS SHORT" + }, + { + "id": 135, + "fortune": "YOUR REINVENTION TOUR CONTINUES. SAME SETLIST" + }, + { + "id": 136, + "fortune": "YOU ARE PRESENT. PHYSICALLY AT LEAST" + }, + { + "id": 137, + "fortune": "YOUR FRESH START STARTS MONDAY. A FUTURE ONE" + }, + { + "id": 138, + "fortune": "YOUR VISION BOARD IS STUNNING. AND RETIRED" + }, + { + "id": 139, + "fortune": "YOU HONOUR YOUR FEELINGS. AT FULL VOLUME" + }, + { + "id": 140, + "fortune": "YOUR CALM IS REAL. YOU STOPPED CHECKING EMAIL" + }, + { + "id": 141, + "fortune": "YOUR STANDARDS ARE RISING. FROM SEA LEVEL" + }, + { + "id": 142, + "fortune": "YOU ARE BLOOMING. LATE SEASON. STILL COUNTS" + }, + { + "id": 143, + "fortune": "YOUR DISCIPLINE IS THE STUFF OF LEGEND" + }, + { + "id": 144, + "fortune": "YOUR AFFIRMATIONS WORK. ON THE CEILING" + }, + { + "id": 145, + "fortune": "TRUE LOVE AWAITS. IT IS RUNNING LATE TOO" + }, + { + "id": 146, + "fortune": "YOUR SOULMATE EXISTS. COVERAGE MAY VARY" + }, + { + "id": 147, + "fortune": "ROMANCE IS IN THE AIR. SO IS POLLEN" + }, + { + "id": 148, + "fortune": "CUPID HAS YOUR ADDRESS. HE DROVE PAST TWICE" + }, + { + "id": 149, + "fortune": "YOUR HEART KNOWS THE WAY. YOUR TEXTS DONT" + }, + { + "id": 150, + "fortune": "LOVE IS BLIND. YOURS ALSO FORGETS" + }, + { + "id": 151, + "fortune": "YOUR EX THINKS OF YOU. AS A LESSON" + }, + { + "id": 152, + "fortune": "YOUR FLIRTING IS SUBTLE. UNDETECTABLE EVEN" + }, + { + "id": 153, + "fortune": "YOUR TYPE IS CONSISTENT. THAT IS THE PROBLEM" + }, + { + "id": 154, + "fortune": "SOMEONE ADMIRES YOU FROM AFAR. STAY THERE" + }, + { + "id": 155, + "fortune": "YOUR GREEN FLAGS ARE LOVELY. AND OUTNUMBERED" + }, + { + "id": 156, + "fortune": "LOVE CONQUERS ALL. IT REQUESTED BACKUP" + }, + { + "id": 157, + "fortune": "HEART ON YOUR SLEEVE. DRY CLEAN BOTH" + }, + { + "id": 158, + "fortune": "A TEXT FROM THE PAST NEARS. DO NOT REPLY" + }, + { + "id": 159, + "fortune": "YOUR CRUSH KNOWS YOU EXIST. AS A CONCEPT" + }, + { + "id": 160, + "fortune": "YOU LOVE DEEPLY. BRIEFLY. BUT DEEPLY" + }, + { + "id": 161, + "fortune": "YOUR HEART IS OPEN. SO IS YOUR TAB" + }, + { + "id": 162, + "fortune": "SPARKS WILL FLY. CHECK THE SMOKE ALARM" + }, + { + "id": 163, + "fortune": "YOU ARE READY FOR COMMITMENT. TO A COUCH" + }, + { + "id": 164, + "fortune": "YOUR MEET CUTE NEARS. A MEET ADEQUATE" + }, + { + "id": 165, + "fortune": "YOUR HEART HEALED. IT ADDED SECURITY" + }, + { + "id": 166, + "fortune": "LOVE IS PATIENT. IT HAD PRACTICE WITH YOU" + }, + { + "id": 167, + "fortune": "YOUR REBOUND GAME BEATS THE ORIGINAL" + }, + { + "id": 168, + "fortune": "YOUR HONEYMOON PHASE LASTED 11 DAYS" + }, + { + "id": 169, + "fortune": "YOUR PET IS YOUR BEST RELATIONSHIP. IT AGREES" + }, + { + "id": 170, + "fortune": "YOUR VOWS WILL MAKE PEOPLE CRY. THE GRAMMAR" + }, + { + "id": 171, + "fortune": "YOU ARE A CATCH. CATCH AND RELEASE EXISTS" + }, + { + "id": 172, + "fortune": "ABSENCE MAKES HEARTS FONDER. KEEP TESTING" + }, + { + "id": 173, + "fortune": "YOUR PERFECT MATCH EXISTS. THE APP GAVE UP" + }, + { + "id": 174, + "fortune": "YOUR RED FLAGS ARE MAROON NOW. PROGRESS" + }, + { + "id": 175, + "fortune": "LOVE AT FIRST SIGHT IS REAL. GET GLASSES" + }, + { + "id": 176, + "fortune": "YOUR HEART WANTS THINGS. SEE A SPECIALIST" + }, + { + "id": 177, + "fortune": "SOMEONE THINKS YOU ARE PERFECT. FROM AFAR" + }, + { + "id": 178, + "fortune": "YOU BELIEVE IN SECOND CHANCES. AND FIFTHS" + }, + { + "id": 179, + "fortune": "YOUR BREAKUP PLAYLIST GETS HEAVY USE" + }, + { + "id": 180, + "fortune": "YOU ARE WORTH THE WAIT. THE WAIT CONTINUES" + }, + { + "id": 181, + "fortune": "ROMANCE IS NOT DEAD. YOURS IS RESTING" + }, + { + "id": 182, + "fortune": "YOUR IN LAWS WILL ADORE YOU. EVENTUALLY" + }, + { + "id": 183, + "fortune": "YOUR DATING BIO IS HONEST. SELECTIVELY" + }, + { + "id": 184, + "fortune": "YOUR HUGS ARE GREAT. AND RARE. LIKE ECLIPSES" + }, + { + "id": 185, + "fortune": "SOMEONE DREAMS OF YOUR COOKING. IN FEAR" + }, + { + "id": 186, + "fortune": "YOUR CHARM OPENS DOORS. PUSH DONT PULL" + }, + { + "id": 187, + "fortune": "YOUR STANDARDS MET YOUR CHOICES. AWKWARD" + }, + { + "id": 188, + "fortune": "YOU WILL BE SWEPT AWAY. STRETCH FIRST" + }, + { + "id": 189, + "fortune": "INTIMACY MEANS BEING SEEN. YOU NEGOTIATED" + }, + { + "id": 190, + "fortune": "YOUR LOVE STORY WILL BE TOLD. AS A WARNING" + }, + { + "id": 191, + "fortune": "THE SPARK IS STILL THERE. SEND A SEARCH PARTY" + }, + { + "id": 192, + "fortune": "YOUR ANNIVERSARY NEARS. THIS IS THE REMINDER" + }, + { + "id": 193, + "fortune": "LOVE YOURSELF FIRST. YOU MASTERED THAT PART" + }, + { + "id": 194, + "fortune": "YOUR HEART SKIPPED A BEAT. SEE A DOCTOR" + }, + { + "id": 195, + "fortune": "YOU BRING OUT THE BEST. AS PEOPLE LEAVE" + }, + { + "id": 196, + "fortune": "YOU NEVER SETTLE. HENCE THE FOLDING CHAIRS" + }, + { + "id": 197, + "fortune": "YOUR FUTURE PARTNER IS TRAINING. UNKNOWINGLY" + }, + { + "id": 198, + "fortune": "YOU GET GREAT ADVICE. FOLLOWING IT IS NEW" + }, + { + "id": 199, + "fortune": "YOUR HEART IS A FORTRESS. GIFT SHOP OPEN" + }, + { + "id": 200, + "fortune": "YOUR FRIENDS VALUE YOUR HONESTY. FROM AFAR" + }, + { + "id": 201, + "fortune": "A FRIEND WILL CALL SOON. THEY NEED A LORRY" + }, + { + "id": 202, + "fortune": "YOU ARE THE GLUE. GLUE IS RARELY THANKED" + }, + { + "id": 203, + "fortune": "THE GROUP CHAT ENJOYS YOUR SILENCE" + }, + { + "id": 204, + "fortune": "TRUE FRIENDS ARE RARE. YOURS ARE HIDING" + }, + { + "id": 205, + "fortune": "YOUR LOYALTY IS UNMATCHED. YOUR TIMING ISNT" + }, + { + "id": 206, + "fortune": "A GATHERING BRINGS JOY. LEAVING BRINGS MORE" + }, + { + "id": 207, + "fortune": "YOU ARE THE FUN ONE. SELF REPORTED" + }, + { + "id": 208, + "fortune": "AN OLD FRIEND RESURFACES. THEY ARE MOVING" + }, + { + "id": 209, + "fortune": "YOUR ADVICE IS APPRECIATED. NOT REQUESTED" + }, + { + "id": 210, + "fortune": "YOU LIGHT UP ROOMS. SOME WANT DIMMERS" + }, + { + "id": 211, + "fortune": "YOUR BIRTHDAY WILL BE REMEMBERED. LATE" + }, + { + "id": 212, + "fortune": "YOUR STORIES IMPROVE YEARLY. ACCURACY DROPS" + }, + { + "id": 213, + "fortune": "PEOPLE GRAVITATE TO YOU. GRAVITY PULLS DOWN" + }, + { + "id": 214, + "fortune": "YOU ALWAYS KNOW WHAT TO SAY. TIMING EXTRA" + }, + { + "id": 215, + "fortune": "FRIENDS COME AND GO. YOURS EXCEL AT GO" + }, + { + "id": 216, + "fortune": "YOUR SOCIAL CALENDAR IS FULL. OF MAYBES" + }, + { + "id": 217, + "fortune": "YOU ARE A PEOPLE PERSON. UNVERIFIED" + }, + { + "id": 218, + "fortune": "YOUR INSIDE JOKES ARE NOW SOLO ACTS" + }, + { + "id": 219, + "fortune": "YOUR HOSTING IMPROVES. THE FIRE DEPT AGREES" + }, + { + "id": 220, + "fortune": "YOU BRING PEOPLE TOGETHER. TO DISCUSS YOU" + }, + { + "id": 221, + "fortune": "YOUR GENEROSITY HAS OFFICE HOURS" + }, + { + "id": 222, + "fortune": "YOU NEVER FORGET A FACE. NAMES OPTED OUT" + }, + { + "id": 223, + "fortune": "YOUR PEP TALKS REQUIRE FOLLOW UP CARE" + }, + { + "id": 224, + "fortune": "YOU SHOW UP WHEN IT MATTERS. TO YOU" + }, + { + "id": 225, + "fortune": "YOUR GOING AWAY PARTIES DRAW CROWDS" + }, + { + "id": 226, + "fortune": "PEOPLE CONFIDE IN YOU. AND THEN IN OTHERS" + }, + { + "id": 227, + "fortune": "YOUR HONESTY IS REFRESHING. LIKE ICE WATER" + }, + { + "id": 228, + "fortune": "YOU NEVER HOLD GRUDGES. YOU ARCHIVE THEM" + }, + { + "id": 229, + "fortune": "YOUR MEMES ARRIVE AT DAWN. UNREQUESTED" + }, + { + "id": 230, + "fortune": "YOU ARE A SHOULDER TO CRY ON. BY APPOINTMENT" + }, + { + "id": 231, + "fortune": "YOUR CANCELLED DINNER PARTIES ARE LEGEND" + }, + { + "id": 232, + "fortune": "YOU KEEP EVERYONE GROUNDED. LIKE FOG" + }, + { + "id": 233, + "fortune": "YOUR ENTHUSIASM IS INFECTIOUS. MASKS EXIST" + }, + { + "id": 234, + "fortune": "A FRIEND WANTS YOUR HONEST OPINION. A TRAP" + }, + { + "id": 235, + "fortune": "YOU SPLIT BILLS FAIRLY. YOUR MATH DIFFERS" + }, + { + "id": 236, + "fortune": "YOUR PRESENCE IS A GIFT. REGIFTING HAPPENS" + }, + { + "id": 237, + "fortune": "YOUR COMPETITIVENESS RETIRED THE GAMES" + }, + { + "id": 238, + "fortune": "YOUR SURPRISE PARTIES SURPRISE NO ONE" + }, + { + "id": 239, + "fortune": "PEOPLE FEEL COMFORTABLE AROUND YOU. TO LEAVE" + }, + { + "id": 240, + "fortune": "YOU SAY WHAT EVERYONE THINKS. AT FUNERALS" + }, + { + "id": 241, + "fortune": "YOUR HOLIDAY LETTER HAS A READERSHIP" + }, + { + "id": 242, + "fortune": "YOUR APOLOGIES ARE HEARTFELT. AND FREQUENT" + }, + { + "id": 243, + "fortune": "YOU ARE THE KEEPER OF GROUP HISTORY. REVISED" + }, + { + "id": 244, + "fortune": "YOU NEVER MISS A CHANCE TO HELP. OR SAY SO" + }, + { + "id": 245, + "fortune": "YOUR DIRECTNESS IS CHARMING. AGREED WORD" + }, + { + "id": 246, + "fortune": "YOU ARE FASHIONABLY LATE. MINUS THE FASHION" + }, + { + "id": 247, + "fortune": "PEOPLE SEEK YOUR APPROVAL. TO END THE CHAT" + }, + { + "id": 248, + "fortune": "YOU NEVER GOSSIP. YOU BRIEF THE ABSENT" + }, + { + "id": 249, + "fortune": "YOUR INVITES SAY CASUAL. YOUR TEXTS DONT" + }, + { + "id": 250, + "fortune": "AN OLD FRIEND REMEMBERS YOU. FROM AFAR" + }, + { + "id": 251, + "fortune": "YOU NEVER LOSE ARGUMENTS. YOU RELOCATE THEM" + }, + { + "id": 252, + "fortune": "YOUR SUPPORT IS UNCONDITIONAL. SEE TERMS" + }, + { + "id": 253, + "fortune": "WEALTH IS COMING. IT HAS YOUR OLD ADDRESS" + }, + { + "id": 254, + "fortune": "MONEY FLOWS TO YOU. THROUGH YOU. GONE" + }, + { + "id": 255, + "fortune": "YOUR BUDGET IS ART. ABSTRACT ART" + }, + { + "id": 256, + "fortune": "SAVE FOR A RAINY DAY. MONSOONS FORECAST" + }, + { + "id": 257, + "fortune": "YOUR CREDIT SCORE IS A THRILLER" + }, + { + "id": 258, + "fortune": "YOUR SAVINGS GROW. CHARACTER MOSTLY" + }, + { + "id": 259, + "fortune": "MONEY TALKS. YOURS SAYS GOODBYE" + }, + { + "id": 260, + "fortune": "YOUR RETIREMENT PLAN IS A LOTTERY TICKET" + }, + { + "id": 261, + "fortune": "YOU HAVE EXPENSIVE TASTE. AND A BUDGET" + }, + { + "id": 262, + "fortune": "COMPOUND INTEREST IS MAGIC. YOU CHOSE DEBT" + }, + { + "id": 263, + "fortune": "YOUR EMERGENCY FUND COVERS SMALL EMERGENCIES" + }, + { + "id": 264, + "fortune": "A RAISE IS DESERVED. AND UNSCHEDULED" + }, + { + "id": 265, + "fortune": "YOUR CART IS A VISION BOARD WITH CHECKOUT" + }, + { + "id": 266, + "fortune": "YOUR GENEROSITY IS FAMOUS. AMONG VENDORS" + }, + { + "id": 267, + "fortune": "THE BEST THINGS ARE FREE. YOURS SHIP" + }, + { + "id": 268, + "fortune": "YOUR SUBSCRIPTIONS OUTNUMBER YOUR FRIENDS" + }, + { + "id": 269, + "fortune": "BUY LOW SELL HIGH. YOU INVENTED THE REVERSE" + }, + { + "id": 270, + "fortune": "YOUR BANK TEXTS OUT OF CONCERN NOW" + }, + { + "id": 271, + "fortune": "YOUR TAX REFUND HAS OTHER PLANS" + }, + { + "id": 272, + "fortune": "YOUR SPLURGES ARE STRATEGIC. ALLEGEDLY" + }, + { + "id": 273, + "fortune": "YOU WILL FIND MONEY IN A COAT. IT WAS YOURS" + }, + { + "id": 274, + "fortune": "YOUR PORTFOLIO IS DIVERSIFIED. IN REGRETS" + }, + { + "id": 275, + "fortune": "PAYDAY BRINGS JOY. ABOUT 18 HOURS OF IT" + }, + { + "id": 276, + "fortune": "YOUR LATE FEES HAVE LATE FEES NOW" + }, + { + "id": 277, + "fortune": "A PENNY SAVED IS A START. A SMALL ONE" + }, + { + "id": 278, + "fortune": "RICH IN SPIRIT. THE REGISTER DISAGREES" + }, + { + "id": 279, + "fortune": "YOUR RECEIPTS TELL A STORY. A LONG ONE" + }, + { + "id": 280, + "fortune": "YOUR DOWN PAYMENT BECAME SUSHI. TWICE" + }, + { + "id": 281, + "fortune": "YOUR BUDGET SHEET IS HISTORICAL FICTION" + }, + { + "id": 282, + "fortune": "YOUR FREE TRIALS NEVER DIE. THEY BILL" + }, + { + "id": 283, + "fortune": "CASH IS KING. YOUR KINGDOM RUNS ON CREDIT" + }, + { + "id": 284, + "fortune": "YOUR SPENDING FREEZE LASTED 40 MINUTES" + }, + { + "id": 285, + "fortune": "YOUR BILLS ARRIVE ON TIME. UNLIKE YOU" + }, + { + "id": 286, + "fortune": "YOUR MONEY WORKS HARD. FOR OTHER PEOPLE" + }, + { + "id": 287, + "fortune": "YOUR COUPONS EXPIRED. ALL OF THEM. TODAY" + }, + { + "id": 288, + "fortune": "YOUR FINANCIAL PLAN HAS PHASES. DENIAL FIRST" + }, + { + "id": 289, + "fortune": "YOUR DISCOUNTS FUND YOUR IMPULSE BUYS" + }, + { + "id": 290, + "fortune": "YOUR CRYPTO PHASE WAS EDUCATIONAL. AND COSTLY" + }, + { + "id": 291, + "fortune": "THE LOTTERY IS A HOPE TAX. YOU DONATE WEEKLY" + }, + { + "id": 292, + "fortune": "YOUR SAVINGS GOAL HASNT MOVED SINCE SPRING" + }, + { + "id": 293, + "fortune": "WEALTH WHISPERS. YOURS BLOCKED THE NUMBER" + }, + { + "id": 294, + "fortune": "YOU TRACK EVERY EXPENSE. THEN MAKE MORE" + }, + { + "id": 295, + "fortune": "YOUR PIGGY BANK HAS TRUST ISSUES. EARNED" + }, + { + "id": 296, + "fortune": "YOUR REFUND ARRIVED. ALREADY SPENT. TWICE" + }, + { + "id": 297, + "fortune": "MONEY CANT BUY HAPPINESS. YOURS QUIT TRYING" + }, + { + "id": 298, + "fortune": "YOUR INVOICES AGE LIKE FINE WINE. UNPAID" + }, + { + "id": 299, + "fortune": "YOUR NET WORTH AND SELF WORTH BOTH DIPPED" + }, + { + "id": 300, + "fortune": "PATIENCE IS A VIRTUE. YOU CHOSE OTHERS" + }, + { + "id": 301, + "fortune": "SILENCE IS GOLDEN. YOURS WOULD FUND A NATION" + }, + { + "id": 302, + "fortune": "THINK BEFORE YOU SPEAK. A PILOT PROGRAM" + }, + { + "id": 303, + "fortune": "STILL WATERS RUN DEEP. YOURS RUN ANKLE HIGH" + }, + { + "id": 304, + "fortune": "KNOWLEDGE IS POWER. YOURS IS ON STANDBY" + }, + { + "id": 305, + "fortune": "AN OPEN MIND IS A TREASURE. NICE BREEZE" + }, + { + "id": 306, + "fortune": "LISTEN TWICE SPEAK ONCE. INVERT YOUR RATIO" + }, + { + "id": 307, + "fortune": "THE TRUTH WILL SET YOU FREE. YOURS SAT DOWN" + }, + { + "id": 308, + "fortune": "LOOK BEFORE YOU LEAP. OR AFTER. YOUR WAY" + }, + { + "id": 309, + "fortune": "A FOOL AND HIS MONEY HAD A NICE CEREMONY" + }, + { + "id": 310, + "fortune": "ACTIONS SPEAK LOUDER. YOURS WHISPER" + }, + { + "id": 311, + "fortune": "MEASURE TWICE CUT ONCE. YOU CUT FIRST" + }, + { + "id": 312, + "fortune": "THE PEN IS MIGHTY. YOUR EMAILS ARE WEAPONS" + }, + { + "id": 313, + "fortune": "BETTER LATE THAN NEVER. YOU TEST THIS DAILY" + }, + { + "id": 314, + "fortune": "HONESTY IS THE BEST POLICY. READ THE RIDERS" + }, + { + "id": 315, + "fortune": "DONT COUNT YOUR CHICKENS. YOURS UNIONIZED" + }, + { + "id": 316, + "fortune": "GRASS IS GREENER WHERE WATERED. BUY A HOSE" + }, + { + "id": 317, + "fortune": "EVERY SAINT HAS A PAST. YOURS HAS A FILE" + }, + { + "id": 318, + "fortune": "PRACTICE WHAT YOU PREACH. OR PREACH LESS" + }, + { + "id": 319, + "fortune": "TWO WRONGS DONT MAKE A RIGHT. STOP AT NINE" + }, + { + "id": 320, + "fortune": "ALL THAT GLITTERS ISNT GOLD. CHECK RECEIPTS" + }, + { + "id": 321, + "fortune": "A ROLLING STONE GATHERS NO MOSS. NICE MOSS" + }, + { + "id": 322, + "fortune": "DONT PUT ALL EGGS IN ONE BASKET. FIND BASKET" + }, + { + "id": 323, + "fortune": "EMPTY VESSELS ARE LOUD. GREAT ACOUSTICS" + }, + { + "id": 324, + "fortune": "HE WHO LAUGHS LAST THOUGHT SLOWEST. HI" + }, + { + "id": 325, + "fortune": "A BIRD IN HAND IS WORTH TWO. YOURS FLEW" + }, + { + "id": 326, + "fortune": "IF IT AINT BROKE DONT FIX IT. TOO LATE" + }, + { + "id": 327, + "fortune": "YOU CAN LEAD A HORSE TO WATER. IT DECLINED" + }, + { + "id": 328, + "fortune": "PRIDE GOES BEFORE A FALL. NICE BALANCE" + }, + { + "id": 329, + "fortune": "TIME HEALS ALL WOUNDS. YOURS PICK AT IT" + }, + { + "id": 330, + "fortune": "OLD HABITS DIE HARD. YOURS BOUGHT INSURANCE" + }, + { + "id": 331, + "fortune": "WHERE THERE IS SMOKE THERE IS YOU" + }, + { + "id": 332, + "fortune": "THE BEST DEFENCE IS DISTANCE. THEY LEARNED" + }, + { + "id": 333, + "fortune": "FAMILIARITY BREEDS CONTEMPT. THRIVING HERE" + }, + { + "id": 334, + "fortune": "THE DEVIL IS IN THE DETAILS. YOURS ARE EMPTY" + }, + { + "id": 335, + "fortune": "ONE MANS RUBBISH IS YOUR GARAGE" + }, + { + "id": 336, + "fortune": "A WATCHED POT NEVER BOILS. YOURS BOILED OVER" + }, + { + "id": 337, + "fortune": "IGNORANCE IS BLISS. YOU SEEM SERENE" + }, + { + "id": 338, + "fortune": "BEAUTY IS IN THE EYE. GET THE EYE CHECKED" + }, + { + "id": 339, + "fortune": "IT TAKES A VILLAGE. YOURS REQUESTED BACKUP" + }, + { + "id": 340, + "fortune": "GREAT MINDS THINK ALIKE. YOURS WENT SOLO" + }, + { + "id": 341, + "fortune": "THE PROOF IS IN THE PUDDING. NO COMMENT" + }, + { + "id": 342, + "fortune": "EVERY DOG HAS ITS DAY. YOURS WAS A TUESDAY" + }, + { + "id": 343, + "fortune": "NOTHING VENTURED NOTHING GAINED. PERFECT TIE" + }, + { + "id": 344, + "fortune": "TOO MANY COOKS SPOIL BROTH. YOU SOLOED IT" + }, + { + "id": 345, + "fortune": "YOU REAP WHAT YOU SOW. INTERESTING HARVEST" + }, + { + "id": 346, + "fortune": "ASK AND RECEIVE. YOUR ASKING MADE A LINE" + }, + { + "id": 347, + "fortune": "WALK A MILE IN THEIR SHOES. RETURN THEM" + }, + { + "id": 348, + "fortune": "HASTE MAKES WASTE. YOU KEPT ONLY WASTE" + }, + { + "id": 349, + "fortune": "ALL GOOD THINGS END. YOURS END EARLY" + }, + { + "id": 350, + "fortune": "WORDS ARE CHEAP. YOURS ARE FREE" + }, + { + "id": 351, + "fortune": "TIME WAITS FOR NO ONE. IT MADE AN EXCEPTION" + }, + { + "id": 352, + "fortune": "DONT BITE THE FEEDING HAND. IT WEARS GLOVES" + }, + { + "id": 353, + "fortune": "COMPARISON STEALS JOY. YOURS HIRED IT" + }, + { + "id": 354, + "fortune": "HALF A LOAF BEATS NONE. YOU OFFER CRUMBS" + }, + { + "id": 355, + "fortune": "A SMOOTH SEA NEVER MADE A SAILOR. STAY DOCKED" + }, + { + "id": 356, + "fortune": "THE TRUTH NEEDS NO REHEARSAL. YOURS DOES" + }, + { + "id": 357, + "fortune": "EVERYTHING IN MODERATION. EVEN THAT. STOP" + }, + { + "id": 358, + "fortune": "WHAT GOES AROUND COMES AROUND. TRACK IT" + }, + { + "id": 359, + "fortune": "NEVER ARGUE WITH A FOOL. ONLOOKERS STRUGGLE" + }, + { + "id": 360, + "fortune": "WISDOM WHISPERS. YOURS IS ON MUTE" + }, + { + "id": 361, + "fortune": "A THOUSAND MILE JOURNEY STARTS. YOU STRETCH" + }, + { + "id": 362, + "fortune": "CROSS BRIDGES WHEN YOU REACH THEM. NOT BURN" + }, + { + "id": 363, + "fortune": "THE MIGHTY OAK WAS A NUT. HERITAGE PRESERVED" + }, + { + "id": 364, + "fortune": "FOOL ME ONCE. YOUR CARD HAS NINE STAMPS" + }, + { + "id": 365, + "fortune": "ADVENTURE AWAITS. IT OPENED A BUSINESS" + }, + { + "id": 366, + "fortune": "YOU WILL TRAVEL FAR. THE SHUTTLE COUNTS" + }, + { + "id": 367, + "fortune": "YOUR PASSPORT PHOTO IS THE REAL YOU" + }, + { + "id": 368, + "fortune": "YOUR BUCKET LIST NEEDS A BUCKET" + }, + { + "id": 369, + "fortune": "YOU WILL SEE THE WORLD. VIA DOCUMENTARY" + }, + { + "id": 370, + "fortune": "YOUR HOLIDAY DAYS AGE LIKE FINE GUILT" + }, + { + "id": 371, + "fortune": "AN EXOTIC PLACE CALLED. YOU SCREENED IT" + }, + { + "id": 372, + "fortune": "YOUR ROAD TRIP PLAYLIST OUTLIVED THE TRIP" + }, + { + "id": 373, + "fortune": "YOU PACK LIGHT. EMOTIONALLY YOU DONT" + }, + { + "id": 374, + "fortune": "YOUR SOUVENIRS TELL STORIES. ABOUT PRICING" + }, + { + "id": 375, + "fortune": "YOU WILL GET LOST ABROAD. A HOME SKILL" + }, + { + "id": 376, + "fortune": "YOUR TRAVEL PICS STAR YOUR THUMB" + }, + { + "id": 377, + "fortune": "A SPONTANEOUS TRIP NEARS. WITH SPREADSHEETS" + }, + { + "id": 378, + "fortune": "YOU ARE A WORLD CITIZEN. REFERENCES PENDING" + }, + { + "id": 379, + "fortune": "YOUR BEACH BODY IS READY. THE BEACH ADAPTED" + }, + { + "id": 380, + "fortune": "THE MOUNTAINS ARE CALLING. YOU MUTED THEM" + }, + { + "id": 381, + "fortune": "YOUR STAYCATION GAME IS UNRIVALED" + }, + { + "id": 382, + "fortune": "YOU NAVIGATE LIKE A LOCAL. A LOST ONE" + }, + { + "id": 383, + "fortune": "YOUR ITINERARY IS MOSTLY BUFFER TIME" + }, + { + "id": 384, + "fortune": "YOUR HIKING BOOTS SUMMITED THE CLOSET" + }, + { + "id": 385, + "fortune": "JET LAG RESPECTS NO ONE. YOU CAME PRE TIRED" + }, + { + "id": 386, + "fortune": "YOUR TRAVEL JOURNAL HAS ONE ENTRY. EXCITED" + }, + { + "id": 387, + "fortune": "YOU COLLECT EXPERIENCES. THE CARD DISAGREES" + }, + { + "id": 388, + "fortune": "YOUR SENSE OF DIRECTION IS A FAMILY LEGEND" + }, + { + "id": 389, + "fortune": "A GETAWAY NEARS. THE LAUNDRY WILL WAIT" + }, + { + "id": 390, + "fortune": "YOU WILL RETURN CHANGED. MOSTLY YOUR SLEEP" + }, + { + "id": 391, + "fortune": "YOUR AIRPORT TIMING IS A PERSONALITY TEST" + }, + { + "id": 392, + "fortune": "YOUR WINDOW SEAT PICS FEATURE THE WING" + }, + { + "id": 393, + "fortune": "YOUR CARRY ON HAS ITS OWN GRAVITY NOW" + }, + { + "id": 394, + "fortune": "YOUR CAMPING SKILLS AMUSE THE RACCOONS" + }, + { + "id": 395, + "fortune": "TIME ZONES CONFUSE YOUR SLEEP. IT COPES" + }, + { + "id": 396, + "fortune": "YOUR HOTEL REVIEWS ARE FEARED. BY TOWELS" + }, + { + "id": 397, + "fortune": "YOU BEFRIEND STRANGERS ABROAD. ALSO LOST ONES" + }, + { + "id": 398, + "fortune": "YOUR CRUISE PACE MATCHES YOUR BUFFET PACE" + }, + { + "id": 399, + "fortune": "FORTUNE FAVOURS TRAVELLERS. YOURS LIKES COUCHES" + }, + { + "id": 400, + "fortune": "YOUR PHONE BATTERY OUTLASTS YOUR DRIVE" + }, + { + "id": 401, + "fortune": "YOU WILL READ THE TERMS SOMEDAY. NO" + }, + { + "id": 402, + "fortune": "YOUR SCREEN TIME REPORT SOFTENS THE NEWS" + }, + { + "id": 403, + "fortune": "YOUR LAUNDRY CHAIR EARNED TENURE" + }, + { + "id": 404, + "fortune": "YOUR ALARM HAS FIVE BACKUPS. ALL IGNORED" + }, + { + "id": 405, + "fortune": "YOUR FRIDGE HOSTS A SCIENCE EXPERIMENT" + }, + { + "id": 406, + "fortune": "THE CHECK ENGINE LIGHT IS A MOOD LIGHT NOW" + }, + { + "id": 407, + "fortune": "YOUR HOUSEPLANTS ACCEPTED THEIR FATE" + }, + { + "id": 408, + "fortune": "YOUR JUNK DRAWER OPENED A SECOND LOCATION" + }, + { + "id": 409, + "fortune": "YOUR GYM BAG IS PACKED. THAT COUNTS" + }, + { + "id": 410, + "fortune": "YOUR VOICEMAIL IS FULL. PEOPLE MOVED ON" + }, + { + "id": 411, + "fortune": "YOUR BROWSER TABS ARE AN ECOSYSTEM NOW" + }, + { + "id": 412, + "fortune": "YOUR UMBRELLA WAITS. YOU WILL FORGET IT" + }, + { + "id": 413, + "fortune": "YOUR LEFTOVERS HAD DREAMS. THEY COMPOST" + }, + { + "id": 414, + "fortune": "YOUR TO DO LIST HAS A TO DO LIST" + }, + { + "id": 415, + "fortune": "YOUR CLOSET DRESSES THREE VERSIONS OF YOU" + }, + { + "id": 416, + "fortune": "YOUR MORNING RUN IS LAMINATED. NOT RUN" + }, + { + "id": 417, + "fortune": "YOUR WIFI ROUTER SITS ON A SHRINE. CORRECT" + }, + { + "id": 418, + "fortune": "YOUR INBOX ZERO ATTEMPT IS HISTORY NOW" + }, + { + "id": 419, + "fortune": "YOUR SOCK DRAWER RUNS WITNESS PROTECTION" + }, + { + "id": 420, + "fortune": "YOUR GROCERY LIST STAYS HOME. IT KNOWS" + }, + { + "id": 421, + "fortune": "YOUR WATCHLIST OUTLIVED THREE HOBBIES" + }, + { + "id": 422, + "fortune": "YOUR DESK IS ERGONOMIC. YOUR COUCH ISNT" + }, + { + "id": 423, + "fortune": "THE PRINTER SENSES FEAR. AND UPDATES" + }, + { + "id": 424, + "fortune": "YOUR BEDTIME IS A MOTION YOU VOTE DOWN" + }, + { + "id": 425, + "fortune": "YOUR HEADPHONES TANGLE ALONE. SCIENCE WEEPS" + }, + { + "id": 426, + "fortune": "YOUR SPICE RACK IS SORTED THROUGH C" + }, + { + "id": 427, + "fortune": "YOUR BACKSEAT IS A LOST AND FOUND" + }, + { + "id": 428, + "fortune": "YOUR CART ABANDONMENT RATE MADE THE NEWS" + }, + { + "id": 429, + "fortune": "THE GOOD CABLE IS WHEREVER YOU ARENT" + }, + { + "id": 430, + "fortune": "YOUR STANDING DESK MOSTLY SITS. LIKE YOU" + }, + { + "id": 431, + "fortune": "YOUR MEAL PREP MADE ONE MEAL. SIX BOXES" + }, + { + "id": 432, + "fortune": "THE DELIVERY DRIVER WAVES AT YOU NOW" + }, + { + "id": 433, + "fortune": "YOUR AIR FRYER REPLACED THE OVEN. AND YOU" + }, + { + "id": 434, + "fortune": "YOUR BOOKMARKS ARE A HALL OF INTENTIONS" + }, + { + "id": 435, + "fortune": "YOUR SNOOZE BUTTON SHOWS GEOLOGIC WEAR" + }, + { + "id": 436, + "fortune": "YOUR REUSABLE BAGS WAIT IN THE CAR. LOYAL" + }, + { + "id": 437, + "fortune": "YOUR UPDATES HAVE SENIORITY NOW" + }, + { + "id": 438, + "fortune": "THE ICE TRAY RETURNS EMPTY. A TRADITION" + }, + { + "id": 439, + "fortune": "YOUR GARAGE STORES EVERYTHING BUT THE CAR" + }, + { + "id": 440, + "fortune": "YOUR STEP COUNTER FRAMED ITS BEST DAY" + }, + { + "id": 441, + "fortune": "YOUR FAVOURITE MUG HAS PROTECTED STATUS" + }, + { + "id": 442, + "fortune": "YOUR WEATHER APP GAVE UP ON YOUR OUTFITS" + }, + { + "id": 443, + "fortune": "YOUR SCALE PRACTICES RADICAL HONESTY" + }, + { + "id": 444, + "fortune": "YOUR REMOTES COMMUTE FROM THE CUSHIONS" + }, + { + "id": 445, + "fortune": "THE HALLWAY CLOSET HOLDS YOUR TRUTH. BARELY" + }, + { + "id": 446, + "fortune": "YOU OWN EVERY BATTERY SIZE BUT THE ONE NEEDED" + }, + { + "id": 447, + "fortune": "YOUR VACUUM AVOIDS CORNERS. LIKE YOU" + }, + { + "id": 448, + "fortune": "YOUR SHOWER THOUGHTS STAY IN THE SHOWER" + }, + { + "id": 449, + "fortune": "YOUR COFFEE MAKER KNOWS YOUR MONDAY FACE" + }, + { + "id": 450, + "fortune": "YOUR FREEZER FLOOR PREDATES FRIENDSHIPS" + }, + { + "id": 451, + "fortune": "YOUR PHONE STORAGE IS ALL SCREENSHOTS" + }, + { + "id": 452, + "fortune": "YOUR BIKE TIRES EXHALE SLOWLY. LIKE A SIGH" + }, + { + "id": 453, + "fortune": "YOUR RETURNS PILE HAS A DEPARTURE LOUNGE" + }, + { + "id": 454, + "fortune": "YOUR CANDLES ARE DECOR. MATCHES AGREE" + }, + { + "id": 455, + "fortune": "YOUR DINING TABLE RETIRED FROM DINING" + }, + { + "id": 456, + "fortune": "YOUR PREDICTIVE TEXT SUGGESTS SORRY FIRST" + }, + { + "id": 457, + "fortune": "YOUR FITNESS APP USES SHORTER SENTENCES NOW" + }, + { + "id": 458, + "fortune": "YOUR COAT POCKET IS A TIME CAPSULE" + }, + { + "id": 459, + "fortune": "THE ADD 30 SECONDS BUTTON RUNS YOUR KITCHEN" + }, + { + "id": 460, + "fortune": "THE GOOD SCISSORS ARE MISSING. ONE SUSPECT" + }, + { + "id": 461, + "fortune": "YOUR DOWNLOADS FOLDER IS A LANDFILL" + }, + { + "id": 462, + "fortune": "YOUR PLANTS RUN ON GUILT BASED WATERING" + }, + { + "id": 463, + "fortune": "THE DRYER TAKES ONE SOCK AS TRIBUTE" + }, + { + "id": 464, + "fortune": "YOUR COASTERS WATCH FROM THE SIDELINES" + }, + { + "id": 465, + "fortune": "YOUR NOTES APP IS A GENIUS LANDFILL" + }, + { + "id": 466, + "fortune": "RAIN WILL WASH YOUR CAR. YOUR PLAN EXACTLY" + }, + { + "id": 467, + "fortune": "YOUR BOARD GAMES AGE IN SHRINK WRAP" + }, + { + "id": 468, + "fortune": "YOUR YOGA MAT UNROLLED TWICE. FOR NAPS" + }, + { + "id": 469, + "fortune": "YOUR EXTENSION CORDS BREED IN THE DARK" + }, + { + "id": 470, + "fortune": "YOUR SMART SPEAKER HAS HEARD THINGS" + }, + { + "id": 471, + "fortune": "YOUR LIDS AND CONTAINERS NEVER MATCH. EVER" + }, + { + "id": 472, + "fortune": "YOUR WALKING SHOES TOUR THE TRUNK" + }, + { + "id": 473, + "fortune": "YOUR IMPORTANT FOLDER IS A HOSPICE" + }, + { + "id": 474, + "fortune": "YOUR WREATH IS PERENNIAL NOW. FESTIVE" + }, + { + "id": 475, + "fortune": "THAT ONE DRAWER STOPPED EXPLAINING ITSELF" + }, + { + "id": 476, + "fortune": "YOUR SCREEN AT 2AM REVEALS EVERYTHING" + }, + { + "id": 477, + "fortune": "YOUR FIRE EXTINGUISHER RUNS ON CONFIDENCE" + }, + { + "id": 478, + "fortune": "YOUR WATER GOAL WAS MET ONCE. PLAQUE PENDING" + }, + { + "id": 479, + "fortune": "AGING SUITS YOU. THE ALTERNATIVE DIDNT" + }, + { + "id": 480, + "fortune": "YOUR AGE IS JUST A NUMBER. AN ASCENDING ONE" + }, + { + "id": 481, + "fortune": "YOUR KNEES FORECAST WEATHER NOW. A SKILL" + }, + { + "id": 482, + "fortune": "YOUR BACK WENT OUT MORE THAN YOU DID" + }, + { + "id": 483, + "fortune": "WISDOM COMES WITH GREY HAIR. YOURS CAME EARLY" + }, + { + "id": 484, + "fortune": "YOU AGE LIKE WINE. STORED HORIZONTALLY" + }, + { + "id": 485, + "fortune": "YOUR BIRTHDAY CANDLES ARE A FIRE HAZARD" + }, + { + "id": 486, + "fortune": "YOUR READING GLASSES NEED READING GLASSES" + }, + { + "id": 487, + "fortune": "YOUR PLAYLIST IS CALLED OLDIES NOW. BY KIDS" + }, + { + "id": 488, + "fortune": "YOU GROAN WHEN SITTING. A VERBAL RECEIPT" + }, + { + "id": 489, + "fortune": "YOUR METABOLISM RETIRED WITHOUT NOTICE" + }, + { + "id": 490, + "fortune": "YOU FEEL 25. YOUR HANGOVERS DISAGREE" + }, + { + "id": 491, + "fortune": "AN APPLE A DAY. PIE IS A LOOPHOLE. SURE" + }, + { + "id": 492, + "fortune": "YOUR POSTURE IMPROVES TWICE DAILY. BRIEFLY" + }, + { + "id": 493, + "fortune": "YOU WILL SLEEP 8 HOURS. ACROSS 3 NIGHTS" + }, + { + "id": 494, + "fortune": "YOUR VITAMINS EXPIRE UNTAKEN. RENEWABLE" + }, + { + "id": 495, + "fortune": "YOUR DIET STARTS MONDAY. MONDAYS UNIONIZED" + }, + { + "id": 496, + "fortune": "YOUR DENTIST KNOWS YOU FLOSSED TODAY ONLY" + }, + { + "id": 497, + "fortune": "YOUR RESTING HEART RATE IS GREAT. FITTING" + }, + { + "id": 498, + "fortune": "YOU WILL LIVE TO 100. OUT OF SPITE" + }, + { + "id": 499, + "fortune": "YOUR HIDDEN TALENTS REMAIN WELL HIDDEN" + }, + { + "id": 500, + "fortune": "YOUR CREATIVITY HAS NO BOUNDS. OR OUTPUT" + }, + { + "id": 501, + "fortune": "YOU HAVE A GIFT. NO RECEIPT INCLUDED" + }, + { + "id": 502, + "fortune": "YOUR NOVEL WILL BE FINISHED. BY SOMEONE" + }, + { + "id": 503, + "fortune": "YOU WERE BORN TO CREATE. EXCUSES COUNT" + }, + { + "id": 504, + "fortune": "YOUR MASTERPIECE IS INSIDE YOU. COZY THERE" + }, + { + "id": 505, + "fortune": "YOUR SINGING BRINGS TEARS. BIOLOGY IS ODD" + }, + { + "id": 506, + "fortune": "THE MUSE VISITS YOU. SHE CALLS AHEAD NOW" + }, + { + "id": 507, + "fortune": "YOUR PHOTOS CAPTURE RAW CONFUSION" + }, + { + "id": 508, + "fortune": "YOU THINK OUTSIDE THE BOX. THE BOX WORRIES" + }, + { + "id": 509, + "fortune": "YOUR POETRY MOVES PEOPLE. TO THE EXITS" + }, + { + "id": 510, + "fortune": "YOUR IMAGINATION IS VAST. EXECUTION ISNT" + }, + { + "id": 511, + "fortune": "YOU SEE THE WORLD DIFFERENTLY. GET CHECKED" + }, + { + "id": 512, + "fortune": "YOUR PODCAST IDEA JOINS THE OTHER SIX" + }, + { + "id": 513, + "fortune": "CREATIVITY FLOWS PAST YOU. WAVING" + }, + { + "id": 514, + "fortune": "YOUR DOODLES BELONG SOMEWHERE. A DRAWER" + }, + { + "id": 515, + "fortune": "YOUR COOKING IS PERFORMANCE ART. WITH ALARMS" + }, + { + "id": 516, + "fortune": "YOUR SCREENPLAY HAS POTENTIAL ENERGY ONLY" + }, + { + "id": 517, + "fortune": "THE WORLD IS YOUR CANVAS. STILL PRIMED" + }, + { + "id": 518, + "fortune": "YOUR DIY PROJECTS ARE AN UNFINISHED TRILOGY" + }, + { + "id": 519, + "fortune": "YOUR MUSIC TASTE IS GREAT. NEIGHBOURS DISSENT" + }, + { + "id": 520, + "fortune": "YOUR DANCE MOVES DEFY CONVENTION. AND PHYSICS" + }, + { + "id": 521, + "fortune": "YOU TELL GREAT STORIES. ENDINGS PENDING" + }, + { + "id": 522, + "fortune": "PHARMACISTS FRAME YOUR HANDWRITING" + }, + { + "id": 523, + "fortune": "YOUR IDEAS ARRIVE AT 2AM. GONE BY 8" + }, + { + "id": 524, + "fortune": "YOUR SKETCHBOOK HOLDS GREAT PROMISES" + }, + { + "id": 525, + "fortune": "YOU COULD WRITE A BOOK. A STRONG PAMPHLET" + }, + { + "id": 526, + "fortune": "YOUR POTTERY PHASE MADE MEMORABLE PIECES" + }, + { + "id": 527, + "fortune": "YOUR INNER ARTIST WANTS COFFEE FIRST" + }, + { + "id": 528, + "fortune": "YOUR COLOUR CHOICES ARE COURAGEOUS" + }, + { + "id": 529, + "fortune": "YOUR GUITAR KNOWS THREE CHORDS WELL" + }, + { + "id": 530, + "fortune": "YOUR CREATIVE PROCESS IS UNDOCUMENTED" + }, + { + "id": 531, + "fortune": "YOUR SCARVES BECAME EXPERIENCES. KNIT ON" + }, + { + "id": 532, + "fortune": "YOU PAINT WITH EMOTION. THE FLOOR AGREES" + }, + { + "id": 533, + "fortune": "YOUR BONSAI IS A REGULAR TREE NOW. PATIENCE" + }, + { + "id": 534, + "fortune": "YOUR WRITING VOICE IS STRONG. IN TEXTS" + }, + { + "id": 535, + "fortune": "YOUR GARDEN GROWS. THE WEEDS THRIVE MOST" + }, + { + "id": 536, + "fortune": "YOUR CALLIGRAPHY PHASE WAS ONE WEEKEND" + }, + { + "id": 537, + "fortune": "YOUR WOODWORK IS HONEST. SPLINTERS DONT LIE" + }, + { + "id": 538, + "fortune": "YOUR FAN FICTION HAS FANS. BOTH OF THEM" + }, + { + "id": 539, + "fortune": "YOUR ORIGAMI FILLS THE RECYCLING BIN" + }, + { + "id": 540, + "fortune": "YOUR CREATIVE BLOCK IS LOAD BEARING NOW" + }, + { + "id": 541, + "fortune": "YOUR SOURDOUGH STARTER OUTLIVED THE HOBBY" + }, + { + "id": 542, + "fortune": "YOUR WATERCOLORS CAPTURE SOMETHING. UNCLEAR" + }, + { + "id": 543, + "fortune": "YOU EXCEL AT BEGINNINGS. MIDDLES DECLINED" + }, + { + "id": 544, + "fortune": "YOUR UKULELE EMBRACED ITS ROLE AS DECOR" + }, + { + "id": 545, + "fortune": "YOU ARE YOUR OWN WORST CRITIC. UNDISPUTED" + }, + { + "id": 546, + "fortune": "YOUR ART SPEAKS FOR ITSELF. QUIETLY" + }, + { + "id": 547, + "fortune": "YOUR STUDIO IS A CLOSET AGAIN. FUNCTIONAL" + }, + { + "id": 548, + "fortune": "YOUR FIRST DRAFTS NEVER MEET SECOND ONES" + }, + { + "id": 549, + "fortune": "YOUR CANDLE PHASE LEFT EVIDENCE. WAXY" + }, + { + "id": 550, + "fortune": "YOUR OPEN MIC DEBUT REMAINS IMMINENT" + }, + { + "id": 551, + "fortune": "YOUR PLAYLIST TRANSITIONS ARE ART. SHUFFLED" + }, + { + "id": 552, + "fortune": "YOUR COSTUMES SHOW COMMITMENT. TO CONCEPTS" + }, + { + "id": 553, + "fortune": "YOUR MACRAME OWL HAS SEEN YOUR PROJECTS" + }, + { + "id": 554, + "fortune": "YOUR VOICE MEMOS ARE MOSTLY GROCERIES" + }, + { + "id": 555, + "fortune": "YOU WILL BE DISCOVERED. ON YOUR COUCH" + }, + { + "id": 556, + "fortune": "YOUR MOOD BOARD IS ALL MOOD. NO BOARD" + }, + { + "id": 557, + "fortune": "YOUR FREESTYLE RAPS SHOULD STAY FREE" + }, + { + "id": 558, + "fortune": "YOUR MUSEUM VISITS END AT THE CAFE" + }, + { + "id": 559, + "fortune": "YOUR COVERS BEAT THE ORIGINALS. UNNOTIFIED" + }, + { + "id": 560, + "fortune": "YOUR TYPEWRITER LOOKS GREAT. STILL" + }, + { + "id": 561, + "fortune": "YOUR ABSTRACT PHASE CONFUSED EVEN YOU" + }, + { + "id": 562, + "fortune": "YOUR COMIC HAS A FOLLOWER. HI MUM" + }, + { + "id": 563, + "fortune": "YOUR SONG RHYMES ORANGE WITH DOOR HINGE" + }, + { + "id": 564, + "fortune": "YOUR CRAFT CORNER ANNEXED THE GUEST ROOM" + }, + { + "id": 565, + "fortune": "YOUR GENIUS IS MISUNDERSTOOD. AND UNSEEN" + }, + { + "id": 566, + "fortune": "YOUR MAGNUM OPUS IS BASICALLY ESPRESSO NOW" + }, + { + "id": 567, + "fortune": "YOUR INSPIRATION FOLDER HAS SUBFOLDERS" + }, + { + "id": 568, + "fortune": "THE FORTUNE YOU SEEK IS IN ANOTHER BISCUIT" + }, + { + "id": 569, + "fortune": "THIS BISCUIT CONTAINS NO FORTUNE. JUST CARBS" + }, + { + "id": 570, + "fortune": "IT IS DARK INSIDE THIS BISCUIT. AND QUIET" + }, + { + "id": 571, + "fortune": "THIS FORTUNE LEFT INTENTIONALLY BLANK" + }, + { + "id": 572, + "fortune": "CONFUCIUS SAYS NOTHING ABOUT YOU. WISELY" + }, + { + "id": 573, + "fortune": "THIS FORTUNE WAS RECALLED. SAME TO YOU" + }, + { + "id": 574, + "fortune": "YOU WILL READ THIS ALOUD. UNPROMPTED" + }, + { + "id": 575, + "fortune": "THE BISCUIT KNEW YOU WOULD TAKE THE LAST ONE" + }, + { + "id": 576, + "fortune": "SOME GET WISDOM. YOU CHOSE DESSERT" + }, + { + "id": 577, + "fortune": "THIS IS A BISCUIT. NOT A LIFE COACH" + }, + { + "id": 578, + "fortune": "YOUR LUCKY NUMBERS HAVE OPTED OUT" + }, + { + "id": 579, + "fortune": "THE BISCUIT ADMIRES YOUR OPTIMISM" + }, + { + "id": 580, + "fortune": "YOU SHOOK THE BISCUIT FIRST. IT FELT THAT" + }, + { + "id": 581, + "fortune": "ASK AGAIN LATER. OR DONT. YOUR CALL" + }, + { + "id": 582, + "fortune": "THIS BISCUIT WAS BAKED WITH LOW EXPECTATIONS" + }, + { + "id": 583, + "fortune": "THE FORTUNE WRITER IS ON BREAK. JUDGING" + }, + { + "id": 584, + "fortune": "NO WISDOM TODAY. INVENTORY ISSUES" + }, + { + "id": 585, + "fortune": "YOUR FORTUNE IS PENDING APPROVAL" + }, + { + "id": 586, + "fortune": "THIS MESSAGE WILL SELF DISAPPOINT" + }, + { + "id": 587, + "fortune": "THE BISCUIT CRUMBLES. SO DO PLANS" + }, + { + "id": 588, + "fortune": "YOU EXPECTED MORE FROM A BISCUIT. NOTED" + }, + { + "id": 589, + "fortune": "FORTUNE NOT FOUND. TRY ANOTHER DINNER" + }, + { + "id": 590, + "fortune": "THIS BISCUIT HAS SEEN YOUR ORDER HISTORY" + }, + { + "id": 591, + "fortune": "THE LAST BISCUIT ALWAYS TELLS THE TRUTH" + }, + { + "id": 592, + "fortune": "YOUR FORTUNE RAN OUT OF CHARACTERS. LUCKY" + }, + { + "id": 593, + "fortune": "A WISE MAN ONCE SAID NOTHING. TRY IT" + }, + { + "id": 594, + "fortune": "THE BISCUIT BELIEVED IN YOU. PAST TENSE" + }, + { + "id": 595, + "fortune": "THIS FORTUNE IS ACCURATE. DETAILS REDACTED" + }, + { + "id": 596, + "fortune": "YOU HOLD THE KEY TO HAPPINESS. NOW FIND IT" + }, + { + "id": 597, + "fortune": "YOUR FORTUNE IS BEING WRITTEN. SLOWLY" + }, + { + "id": 598, + "fortune": "YOUR COFFEE ORDER IS A PARAGRAPH NOW" + }, + { + "id": 599, + "fortune": "COFFEE FIRST. PERSONALITY SECOND. FAIR" + }, + { + "id": 600, + "fortune": "YOUR FOURTH COFFEE IS WATCHING YOU" + }, + { + "id": 601, + "fortune": "DECAF EXISTS. YOU PRETEND IT DOESNT" + }, + { + "id": 602, + "fortune": "YOUR MUG COLLECTION OUTGREW THE CABINET" + }, + { + "id": 603, + "fortune": "YOUR ESPRESSO MACHINE JUDGES YOUR PODS" + }, + { + "id": 604, + "fortune": "YOU CALL IT BRUNCH. IT WAS BREAKFAST. LATE" + }, + { + "id": 605, + "fortune": "YOUR SALAD PHASE ENDED AT THE DRESSING" + }, + { + "id": 606, + "fortune": "YOU MEAL PLAN BEAUTIFULLY. THEN ORDER OUT" + }, + { + "id": 607, + "fortune": "YOUR SPICE TOLERANCE IS A PERSONALITY. SADLY" + }, + { + "id": 608, + "fortune": "YOUR SIGNATURE DISH HAS ONE FAN. YOU" + }, + { + "id": 609, + "fortune": "THE OVEN TIMER IS A SUGGESTION TO YOU" + }, + { + "id": 610, + "fortune": "YOUR SMOOTHIES HIDE VEGETABLES. FROM YOU" + }, + { + "id": 611, + "fortune": "YOU COOK WITH LOVE. AND TOO MUCH SALT" + }, + { + "id": 612, + "fortune": "YOUR RECIPE SUBSTITUTIONS ARE BRAVE" + }, + { + "id": 613, + "fortune": "GARLIC MEASURED WITH YOUR HEART. AGAIN" + }, + { + "id": 614, + "fortune": "YOUR TOAST GAME IS STRONG. LITERALLY BURNT" + }, + { + "id": 615, + "fortune": "YOU EAT DESSERT FIRST. LIFE IS SHORT. SURE" + }, + { + "id": 616, + "fortune": "YOUR SNACK DRAWER HAS A WAITING LIST" + }, + { + "id": 617, + "fortune": "THE FOOD DELIVERY APP KNOWS YOU BY NAME" + }, + { + "id": 618, + "fortune": "YOUR KITCHEN GADGETS FORM A MUSEUM" + }, + { + "id": 619, + "fortune": "YOUR TAKEOUT CONTAINERS ARE HEIRLOOMS NOW" + }, + { + "id": 620, + "fortune": "YOU SAY YOU COULD EAT ANYTHING. THEN DONT" + }, + { + "id": 621, + "fortune": "YOUR PIZZA TOPPINGS HAVE CRITICS" + }, + { + "id": 622, + "fortune": "YOUR DOG LOVES YOU UNCONDITIONALLY. TRAINED" + }, + { + "id": 623, + "fortune": "YOUR CAT TOLERATES YOU. A HIGH HONOUR" + }, + { + "id": 624, + "fortune": "YOUR PET EATS BETTER THAN YOU DO" + }, + { + "id": 625, + "fortune": "YOUR DOG HAS MORE FRIENDS THAN YOU" + }, + { + "id": 626, + "fortune": "THE CAT KNOCKED IT OVER ON PURPOSE. RESPECT" + }, + { + "id": 627, + "fortune": "YOUR FISH KNOWS YOUR SCHEDULE. IT WORRIES" + }, + { + "id": 628, + "fortune": "YOUR PETS INSTAGRAM OUTPERFORMS YOURS" + }, + { + "id": 629, + "fortune": "THE DOG ATE YOUR MOTIVATION. NOT HOMEWORK" + }, + { + "id": 630, + "fortune": "YOUR CAT REVIEWS YOUR LIFE CHOICES NIGHTLY" + }, + { + "id": 631, + "fortune": "YOUR BIRD REPEATS YOUR WORST PHRASES" + }, + { + "id": 632, + "fortune": "YOUR PET THERAPIST IS A GOOD LISTENER. FREE" + }, + { + "id": 633, + "fortune": "RAIN IS COMING. YOUR CAR WASH REJOICES" + }, + { + "id": 634, + "fortune": "YOUR WINTER COAT DEBATE STARTS IN OCTOBER" + }, + { + "id": 635, + "fortune": "YOU CHECK THE WEATHER. THEN DRESS WRONG" + }, + { + "id": 636, + "fortune": "SPRING IS COMING. SO ARE YOUR ALLERGIES" + }, + { + "id": 637, + "fortune": "YOUR SUNSCREEN EXPIRES UNUSED. YEARLY" + }, + { + "id": 638, + "fortune": "SNOW IS MAGICAL. UNTIL YOU OWN A SHOVEL" + }, + { + "id": 639, + "fortune": "YOUR UMBRELLA COLLECTION LIVES ELSEWHERE" + }, + { + "id": 640, + "fortune": "SMALL TALK ABOUT WEATHER SUITS YOU NOW" + }, + { + "id": 641, + "fortune": "THE FORECAST CHANGED. YOUR PLANS DIDNT EXIST" + }, + { + "id": 642, + "fortune": "YOUR PATIO FURNITURE FEARS EVERY SEASON" + }, + { + "id": 643, + "fortune": "AUTUMN LEAVES FALL. LIKE YOUR RESOLUTIONS" + }, + { + "id": 644, + "fortune": "YOUR PASSWORD IS YOUR PETS NAME. WE KNOW" + }, + { + "id": 645, + "fortune": "YOU HAVE 47 UNREAD UPDATES. THEY WAIT" + }, + { + "id": 646, + "fortune": "YOUR CLOUD STORAGE IS FULL. OF DUPLICATES" + }, + { + "id": 647, + "fortune": "AUTOCORRECT KNOWS WHAT YOU MEANT. IT DECLINED" + }, + { + "id": 648, + "fortune": "YOUR ROUTER NEEDS A RESTART. LIKE YOU" + }, + { + "id": 649, + "fortune": "YOUR SEARCH HISTORY TELLS A SAGA" + }, + { + "id": 650, + "fortune": "YOU CLOSED ONE TAB TODAY. PROGRESS" + }, + { + "id": 651, + "fortune": "YOUR SPAM FOLDER MISSES YOU" + }, + { + "id": 652, + "fortune": "THE ALGORITHM KNOWS YOU TOO WELL. IT WORRIES" + }, + { + "id": 653, + "fortune": "YOUR SMARTWATCH SUGGESTS BREATHING. POINTED" + }, + { + "id": 654, + "fortune": "YOUR TYPING SPEED PEAKS DURING ARGUMENTS" + }, + { + "id": 655, + "fortune": "YOU MUTED THE GROUP. THE GROUP KNOWS" + }, + { + "id": 656, + "fortune": "YOUR CAPS LOCK GETS REGULAR EXERCISE" + }, + { + "id": 657, + "fortune": "YOUR EMOJI USE HAS BEEN DISCUSSED" + }, + { + "id": 658, + "fortune": "YOUR WIFI NAME IS YOUR BEST JOKE" + }, + { + "id": 659, + "fortune": "YOU RESTART THINGS AS A LIFESTYLE" + }, + { + "id": 660, + "fortune": "YOUR GYM MEMBERSHIP IS A DONATION NOW" + }, + { + "id": 661, + "fortune": "YOUR MARATHON PLAN IS SAVED. AND SAFE" + }, + { + "id": 662, + "fortune": "LEG DAY RESPECTS YOUR ABSENCE" + }, + { + "id": 663, + "fortune": "YOUR PROTEIN POWDER AGES GRACEFULLY" + }, + { + "id": 664, + "fortune": "YOU STRETCH ONCE A YEAR. AMBITIOUSLY" + }, + { + "id": 665, + "fortune": "YOUR SPORTS TEAM LOSES. YOU TAKE CREDIT" + }, + { + "id": 666, + "fortune": "YOUR FANTASY TEAM IS FANTASY INDEED" + }, + { + "id": 667, + "fortune": "YOU RUN LATE. IT COUNTS AS CARDIO" + }, + { + "id": 668, + "fortune": "YOUR HOME GYM STORES THE LAUNDRY NOW" + }, + { + "id": 669, + "fortune": "YOUR PERSONAL BEST WAS A GROUP EFFORT" + }, + { + "id": 670, + "fortune": "YOUR WARM UP IS THE WHOLE WORKOUT" + }, + { + "id": 671, + "fortune": "YOUR REST DAYS HAVE REST DAYS" + }, + { + "id": 672, + "fortune": "SLEEP IS FOR THE STRONG. YOU SCROLL" + }, + { + "id": 673, + "fortune": "YOUR NAP SCHEDULE IS YOUR REAL CALENDAR" + }, + { + "id": 674, + "fortune": "YOU COUNT SHEEP. THEY COUNT YOUR EXCUSES" + }, + { + "id": 675, + "fortune": "YOUR PILLOW KNOWS ALL YOUR SECRETS" + }, + { + "id": 676, + "fortune": "ONE MORE EPISODE WAS FOUR EPISODES" + }, + { + "id": 677, + "fortune": "YOUR DREAMS ARE VIVID. YOUR MORNINGS ARENT" + }, + { + "id": 678, + "fortune": "YOU SET SIX ALARMS. YOU FEAR NONE" + }, + { + "id": 679, + "fortune": "BEDTIME PROCRASTINATION IS STILL A CHOICE" + }, + { + "id": 680, + "fortune": "YOUR WEEKEND SLEEP DEBT COLLECTS INTEREST" + }, + { + "id": 681, + "fortune": "YOUR FAMILY LOVES YOU. THEY VOTED. CLOSE ONE" + }, + { + "id": 682, + "fortune": "YOUR MOTHER WAS RIGHT. ABOUT EVERYTHING" + }, + { + "id": 683, + "fortune": "FAMILY DINNERS TEST YOUR DIPLOMACY" + }, + { + "id": 684, + "fortune": "YOUR SIBLING REMEMBERS EVERYTHING. FOREVER" + }, + { + "id": 685, + "fortune": "YOUR RELATIVES ASK ABOUT YOUR PLANS. BRACE" + }, + { + "id": 686, + "fortune": "YOU ARE THE FAVOURITE CHILD. UNCONFIRMED" + }, + { + "id": 687, + "fortune": "YOUR FAMILY GROUP CHAT NEVER SLEEPS" + }, + { + "id": 688, + "fortune": "GRANDMA FEEDS YOU. RESISTANCE IS FUTILE" + }, + { + "id": 689, + "fortune": "YOUR DAD JOKES SKIPPED NO GENERATIONS" + }, + { + "id": 690, + "fortune": "THANKSGIVING SEATING IS POLITICAL. YOURS IS" + }, + { + "id": 691, + "fortune": "YOUR NEIGHBOUR WAVES. THE FENCE WAS HIS IDEA" + }, + { + "id": 692, + "fortune": "THE HOA NOTICED YOUR LAWN. AGAIN" + }, + { + "id": 693, + "fortune": "YOUR NEIGHBOURS KNOW YOUR SCHEDULE. AND MORE" + }, + { + "id": 694, + "fortune": "BORROWED TOOLS BECOME GIFTS EVENTUALLY" + }, + { + "id": 695, + "fortune": "YOUR DOORBELL CAMERA SAW YOUR BEST SPRINT" + }, + { + "id": 696, + "fortune": "YOUR COMMUTE BUILDS CHARACTER. DAILY" + }, + { + "id": 697, + "fortune": "TRAFFIC IS NOT PERSONAL. YOURS FEELS PERSONAL" + }, + { + "id": 698, + "fortune": "YOUR PARKING JOB SPARKED CONVERSATIONS" + }, + { + "id": 699, + "fortune": "YOUR GPS SAYS RECALCULATING. WITH FEELING" + }, + { + "id": 700, + "fortune": "YOU DRIVE LIKE YOU LIVE. SIGNALS OPTIONAL" + }, + { + "id": 701, + "fortune": "YOUR GAS TANK RUNS ON FAITH. AND FUMES" + }, + { + "id": 702, + "fortune": "THE LEFT LANE IS FOR PASSING. JUST SAYING" + }, + { + "id": 703, + "fortune": "YOUR CAR PLAYLIST PEAKED IN 2016" + }, + { + "id": 704, + "fortune": "YOUR HORN GETS MORE USE THAN YOUR BLINKER" + }, + { + "id": 705, + "fortune": "ROAD RAGE IS BENEATH YOU. USUALLY" + }, + { + "id": 706, + "fortune": "YOUR PARALLEL PARKING DRAWS AUDIENCES" + }, + { + "id": 707, + "fortune": "TIME FLIES. YOURS FILED A FLIGHT PLAN" + }, + { + "id": 708, + "fortune": "PROCRASTINATION IS AN ART. YOU TEACH IT" + }, + { + "id": 709, + "fortune": "YOU WILL DO IT TOMORROW. TOMORROW KNOWS" + }, + { + "id": 710, + "fortune": "DEADLINES APPROACH. YOU ADMIRE THE VIEW" + }, + { + "id": 711, + "fortune": "YOUR FIVE MINUTES IS EVERYONE ELSES HOUR" + }, + { + "id": 712, + "fortune": "LATER IS YOUR FAVOURITE TIME ZONE" + }, + { + "id": 713, + "fortune": "YOU FINISH THINGS. EVENTUALLY IS A WORD" + }, + { + "id": 714, + "fortune": "YOUR SOMEDAY LIST NEEDS ITS OWN SHELF" + }, + { + "id": 715, + "fortune": "TIME MANAGEMENT IS A SKILL. NOTED" + }, + { + "id": 716, + "fortune": "YOU ARE EARLY ONCE A YEAR. IT CONFUSES ALL" + }, + { + "id": 717, + "fortune": "THE PRESENT IS A GIFT. YOU CHOSE STORE CREDIT" + }, + { + "id": 718, + "fortune": "YOUR PUNCTUALITY IS LEGEND. THE BAD KIND" + }, + { + "id": 719, + "fortune": "YOU READ THIS INSTEAD OF WORKING. NOTED" + }, + { + "id": 720, + "fortune": "YOUR ATTENTION SPAN IS. OH LOOK A BIRD" + }, + { + "id": 721, + "fortune": "JUST ONE MORE MINUTE. THE FAMOUS LAST WORDS" + }, + { + "id": 722, + "fortune": "YOUR EMAILS START WITH SORRY FOR THE DELAY" + }, + { + "id": 723, + "fortune": "YOU REPLY TO TEXTS IN GEOLOGIC TIME" + }, + { + "id": 724, + "fortune": "YOUR READ RECEIPTS INCRIMINATE YOU" + }, + { + "id": 725, + "fortune": "YOU LEFT THEM ON READ. THEY NOTICED" + }, + { + "id": 726, + "fortune": "YOUR TYPING BUBBLE APPEARED. THEN LEFT" + }, + { + "id": 727, + "fortune": "YOU SAID LETS CIRCLE BACK. YOU WONT" + }, + { + "id": 728, + "fortune": "PER YOUR LAST EMAIL. YOU KNOW WHAT YOU DID" + }, + { + "id": 729, + "fortune": "YOUR VOICE NOTES ARE PODCASTS NOW" + }, + { + "id": 730, + "fortune": "YOU WAVED AT SOMEONE WAVING ELSEWHERE. AGAIN" + }, + { + "id": 731, + "fortune": "YOUR SMALL TALK HAS A SCRIPT. REHEARSE" + }, + { + "id": 732, + "fortune": "YOU SAID WE SHOULD HANG OUT SOON. CLASSIC" + }, + { + "id": 733, + "fortune": "YOUR PHONE IS ON SILENT. FOR YEARS NOW" + }, + { + "id": 734, + "fortune": "YOU SCREENSHOT INSTEAD OF RESPONDING" + }, + { + "id": 735, + "fortune": "YOUR OPINION WAS NOTED. AND RECYCLED" + }, + { + "id": 736, + "fortune": "YOU INTERRUPT WITH ENTHUSIASM. OFTEN" + }, + { + "id": 737, + "fortune": "RETAIL THERAPY IS STILL THERAPY. INVOICED" + }, + { + "id": 738, + "fortune": "YOUR PACKAGE ARRIVES TODAY. YOU ORDERED MORE" + }, + { + "id": 739, + "fortune": "YOU CAME FOR ONE ITEM. YOU LEFT WITH NINE" + }, + { + "id": 740, + "fortune": "YOUR WISHLIST IS A NOVEL NOW" + }, + { + "id": 741, + "fortune": "FREE SHIPPING COSTS YOU PLENTY" + }, + { + "id": 742, + "fortune": "YOUR RETURNS WINDOW CLOSED. LIKE ALWAYS" + }, + { + "id": 743, + "fortune": "YOU COMPARE PRICES. AFTER BUYING" + }, + { + "id": 744, + "fortune": "THE SALE ENDED. YOUR REGRET BEGINS" + }, + { + "id": 745, + "fortune": "YOUR LOYALTY CARD OUTRANKS YOUR GYM CARD" + }, + { + "id": 746, + "fortune": "YOU BUY ORGANIZERS TO ORGANISE ORGANIZERS" + }, + { + "id": 747, + "fortune": "YOUR HOME PROJECT IS 90 PERCENT DONE. FOREVER" + }, + { + "id": 748, + "fortune": "THE PAINT SAMPLES OUTLIVED THE PROJECT" + }, + { + "id": 749, + "fortune": "YOUR TOOLBOX HAS EVERYTHING BUT ANSWERS" + }, + { + "id": 750, + "fortune": "YOU MEASURED ONCE. IT SHOWS" + }, + { + "id": 751, + "fortune": "THE INSTRUCTIONS WERE OPTIONAL. APPARENTLY" + }, + { + "id": 752, + "fortune": "YOUR SHELF LEANS. YOU CALL IT CHARACTER" + }, + { + "id": 753, + "fortune": "DUCT TAPE IS NOT A PHILOSOPHY. YOURS IS" + }, + { + "id": 754, + "fortune": "YOUR SMART BULBS OUTSMART YOU NIGHTLY" + }, + { + "id": 755, + "fortune": "THE LEAK WAITED FOR THE LONG WEEKEND" + }, + { + "id": 756, + "fortune": "YOUR GAMING BACKLOG IS A SECOND CAREER" + }, + { + "id": 757, + "fortune": "ONE MORE LEVEL WAS THREE HOURS AGO" + }, + { + "id": 758, + "fortune": "YOUR CHARACTER HAS BETTER GEAR THAN YOU" + }, + { + "id": 759, + "fortune": "YOU PAUSED AN ONLINE GAME. BOLD MOVE" + }, + { + "id": 760, + "fortune": "YOUR HIGH SCORE IS SOMEONE ELSES WARMUP" + }, + { + "id": 761, + "fortune": "YOU RAGE QUIT GRACEFULLY. ALMOST" + }, + { + "id": 762, + "fortune": "YOUR BOOKSHELF JUDGES YOUR SCREEN TIME" + }, + { + "id": 763, + "fortune": "YOU BUY BOOKS FASTER THAN YOU READ THEM" + }, + { + "id": 764, + "fortune": "YOUR BOOKMARK HASNT MOVED SINCE MARCH" + }, + { + "id": 765, + "fortune": "YOU JOINED A BOOK CLUB FOR THE SNACKS" + }, + { + "id": 766, + "fortune": "THE AUDIOBOOK COUNTS. NOBODY ASKED" + }, + { + "id": 767, + "fortune": "YOUR TO BE READ PILE IS FURNITURE NOW" + }, + { + "id": 768, + "fortune": "YOU QUOTE BOOKS YOU HAVENT FINISHED" + }, + { + "id": 769, + "fortune": "YOUR KARAOKE SONG CLEARED THE ROOM. TWICE" + }, + { + "id": 770, + "fortune": "YOU SING IN THE CAR. THE CAR ENDURES" + }, + { + "id": 771, + "fortune": "YOUR SHOWER CONCERTS HAVE NO REFUNDS" + }, + { + "id": 772, + "fortune": "YOUR AIR GUITAR NEEDS TUNING SOMEHOW" + }, + { + "id": 773, + "fortune": "YOUR PLANT NAMES OUTNUMBER YOUR PLANTS" + }, + { + "id": 774, + "fortune": "YOU TALK TO YOUR PLANTS. THEY HEARD THAT" + }, + { + "id": 775, + "fortune": "YOUR SUCCULENT DIED. IMPRESSIVELY RARE" + }, + { + "id": 776, + "fortune": "THE FERN SAW EVERYTHING. IT STAYS QUIET" + }, + { + "id": 777, + "fortune": "YOUR GREEN THUMB IS MORE OF A BEIGE" + }, + { + "id": 778, + "fortune": "LUCK IS A LADDER. YOU BROUGHT A STOOL" + }, + { + "id": 779, + "fortune": "FORTUNE ARRIVES UNANNOUNCED. SO DO YOU" + }, + { + "id": 780, + "fortune": "YOUR CHARISMA WORKS OFFLINE ONLY" + }, + { + "id": 781, + "fortune": "YOU ARE A LIMITED EDITION. VERY LIMITED" + }, + { + "id": 782, + "fortune": "YOUR VIBE ATTRACTS YOUR TRIBE. EXPLAINS IT" + }, + { + "id": 783, + "fortune": "NORMAL IS A SETTING. YOURS IS CUSTOM" + }, + { + "id": 784, + "fortune": "NOT LAZY. JUST IN ENERGY SAVER MODE" + }, + { + "id": 785, + "fortune": "YOUR SARCASM IS A SECOND LANGUAGE. FLUENT" + }, + { + "id": 786, + "fortune": "YOU ARE TECHNICALLY CORRECT. THE WORST KIND" + }, + { + "id": 787, + "fortune": "YOUR EXCUSES SHOW REAL CRAFTSMANSHIP" + }, + { + "id": 788, + "fortune": "YOU PEAKED. THE TIMING WAS UNFORTUNATE" + }, + { + "id": 789, + "fortune": "YOUR COMFORT SHOW IS ON ITS NINTH RERUN" + }, + { + "id": 790, + "fortune": "YOU ARE DOING YOUR BEST. THE BEST VARIES" + }, + { + "id": 791, + "fortune": "YOUR POTENTIAL CALLED. IT LEFT NO MESSAGE" + }, + { + "id": 792, + "fortune": "YOU ARE SPECIAL. LIKE EVERYONE ELSE" + }, + { + "id": 793, + "fortune": "YOUR ENERGY TODAY IS DECORATIVE" + }, + { + "id": 794, + "fortune": "YOU ARE THE MAIN CHARACTER. OF A SIDE QUEST" + }, + { + "id": 795, + "fortune": "YOUR PLOT ARMOR HAS EXPIRED. WALK CAREFULLY" + }, + { + "id": 796, + "fortune": "YOUR REDEMPTION ARC IS IN PREPRODUCTION" + }, + { + "id": 797, + "fortune": "A COMPLIMENT NEARS. ACCEPT IT AWKWARDLY" + }, + { + "id": 798, + "fortune": "YOUR RESTING FACE HAS A REPUTATION" + }, + { + "id": 799, + "fortune": "YOU AGREED TO DISAGREE. YOU LOST" + }, + { + "id": 800, + "fortune": "YOUR EYE ROLL IS AUDIBLE SOMEHOW" + }, + { + "id": 801, + "fortune": "YOUR SIGH SPOKE PARAGRAPHS JUST NOW" + }, + { + "id": 802, + "fortune": "YOU SAID FINE. NOTHING WAS FINE" + }, + { + "id": 803, + "fortune": "YOUR THUMBS UP ENDED THREE FRIENDSHIPS" + }, + { + "id": 804, + "fortune": "NO WORRIES YOU SAID. WORRYING INTENSELY" + }, + { + "id": 805, + "fortune": "YOUR SHRUG IS DOING HEAVY LIFTING TODAY" + }, + { + "id": 806, + "fortune": "YOU SMILED THROUGH THAT. BARELY. WE SAW" + }, + { + "id": 807, + "fortune": "YOUR PATIENCE HAS A LOADING BAR. STUCK" + }, + { + "id": 808, + "fortune": "YOU FORGAVE THEM. THE SPREADSHEET REMAINS" + }, + { + "id": 809, + "fortune": "YOUR HINTS NEED SUBTITLES. STILL" + }, + { + "id": 810, + "fortune": "YOU ALMOST SAID IT. THE ALMOST WAS LOUD" + }, + { + "id": 811, + "fortune": "YOUR SILENCE HAS AN AGENDA. AS USUAL" + }, + { + "id": 812, + "fortune": "YOU LET IT GO. INTO YOUR PERMANENT RECORD" + }, + { + "id": 813, + "fortune": "YOUR CALM IS SUSPICIOUS. EVERYONE AGREES" + }, + { + "id": 814, + "fortune": "YOU TOOK THE HIGH ROAD. WITH COMMENTARY" + }, + { + "id": 815, + "fortune": "YOUR FEEDBACK SANDWICH IS ALL BREAD" + }, + { + "id": 816, + "fortune": "YOU HEARD THEM OUT. THEN PROCEEDED ANYWAY" + }, + { + "id": 817, + "fortune": "GOOD MORNING. THE BAR IS ALREADY MET" + }, + { + "id": 818, + "fortune": "YOUR OPTIMISM IS ADORABLE TODAY" + }, + { + "id": 819, + "fortune": "TODAY IS YOUR DAY. SHARE IT NICELY" + }, + { + "id": 820, + "fortune": "YOU WOKE UP LIKE THIS. CONDOLENCES" + }, + { + "id": 821, + "fortune": "THE MIRROR SAW YOU FIRST. IT COPES" + }, + { + "id": 822, + "fortune": "YOUR BEDHEAD HAS ARCHITECTURE NOW" + }, + { + "id": 823, + "fortune": "MONDAY SAW YOU COMING. IT PREPARED" + }, + { + "id": 824, + "fortune": "YOUR WEEKEND ENDED. IT SENDS REGARDS" + }, + { + "id": 825, + "fortune": "FRIDAY LOVES YOU. THE FEELING IS MUTUAL" + }, + { + "id": 826, + "fortune": "YOUR LUNCH BREAK DESERVES A MEDAL. SO LONG" + }, + { + "id": 827, + "fortune": "HUMP DAY IS A STATE OF MIND. YOURS DAILY" + }, + { + "id": 828, + "fortune": "YOUR OUTFIT SAYS EFFORT. THE CLOCK SAYS RUSH" + }, + { + "id": 829, + "fortune": "YOUR HAIR HAS PLANS OF ITS OWN TODAY" + }, + { + "id": 830, + "fortune": "YOU MATCHED YOUR SOCKS. A CEREMONY IS DUE" + }, + { + "id": 831, + "fortune": "YOUR SUNGLASSES HIDE NOTHING. NICE TRY" + }, + { + "id": 832, + "fortune": "DRESS FOR THE JOB YOU WANT. A NAP TESTER" + }, + { + "id": 833, + "fortune": "YOUR IRONING BOARD RETIRED YEARS AGO" + }, + { + "id": 834, + "fortune": "WRINKLES ARE A TEXTURE. YOU DECIDED" + }, + { + "id": 835, + "fortune": "YOUR FASHION CYCLE IS LAUNDRY BASED" + }, + { + "id": 836, + "fortune": "YOUR SHOES MATCH. THE OCCASION DOESNT" + }, + { + "id": 837, + "fortune": "YOU WILL WIN AN ARGUMENT. IN THE SHOWER" + }, + { + "id": 838, + "fortune": "YOUR COMEBACK ARRIVED. THREE DAYS LATE" + }, + { + "id": 839, + "fortune": "YOUR WIT IS QUICK. YOUR TIMING ISNT" + }, + { + "id": 840, + "fortune": "YOU HAD THE PERFECT REPLY. AT MIDNIGHT" + }, + { + "id": 841, + "fortune": "YOUR INNER MONOLOGUE NEEDS AN EDITOR" + }, + { + "id": 842, + "fortune": "YOUR HOT TAKES ARE ROOM TEMPERATURE" + }, + { + "id": 843, + "fortune": "YOUR OPINIONS ARRIVE PRE HEATED" + }, + { + "id": 844, + "fortune": "YOUR DEBATE STYLE IS VOLUME BASED" + }, + { + "id": 845, + "fortune": "YOU FACT CHECK OTHERS. NEVER YOURSELF" + }, + { + "id": 846, + "fortune": "YOUR TRIVIA KNOWLEDGE PEAKS AT PARTIES ONLY" + }, + { + "id": 847, + "fortune": "A DECISION AWAITS. FLIP A COIN. IGNORE IT" + }, + { + "id": 848, + "fortune": "YOUR PRO CON LIST HAS ONE COLUMN" + }, + { + "id": 849, + "fortune": "YOU ASKED EVERYONE. THEN DID YOUR THING" + }, + { + "id": 850, + "fortune": "YOUR GUT FEELING NEEDS A SECOND OPINION" + }, + { + "id": 851, + "fortune": "YOU SLEPT ON IT. THE PROBLEM STAYED AWAKE" + }, + { + "id": 852, + "fortune": "YOUR PLAN B IS PLAN A WITH REGRET" + }, + { + "id": 853, + "fortune": "YOU CHOSE CHAOS. AGAIN. CONSISTENTLY" + }, + { + "id": 854, + "fortune": "YOUR BACKUP PLAN NEEDS A BACKUP PLAN" + }, + { + "id": 855, + "fortune": "COMMITMENT SCARES YOU. SO DOES THE MENU" + }, + { + "id": 856, + "fortune": "YOU ORDERED THE SAME THING. ADVENTUROUS" + }, + { + "id": 857, + "fortune": "CHANGE IS COMING. YOU WILL RESIST IT" + }, + { + "id": 858, + "fortune": "A SURPRISE AWAITS. YOU HATE SURPRISES" + }, + { + "id": 859, + "fortune": "GOOD NEWS COMES IN THREES. YOURS CARPOOLS" + }, + { + "id": 860, + "fortune": "YOUR HOROSCOPE QUIT. IT CITED BURNOUT" + }, + { + "id": 861, + "fortune": "MERCURY IS IN RETROGRADE. CONVENIENT EXCUSE" + }, + { + "id": 862, + "fortune": "YOUR TAROT READER NEEDED A BREAK AFTER YOU" + }, + { + "id": 863, + "fortune": "THE MAGIC 8 BALL SAID ASK SOMEONE ELSE" + }, + { + "id": 864, + "fortune": "YOUR PALM READER REQUESTED GLOVES" + }, + { + "id": 865, + "fortune": "THE CRYSTAL BALL FOGGED UP. ON PURPOSE" + }, + { + "id": 866, + "fortune": "YOUR TEA LEAVES SPELLED LUCK. SARCASTICALLY" + }, + { + "id": 867, + "fortune": "YOUR VISION QUEST FOUND THE COUCH" + }, + { + "id": 868, + "fortune": "THE UNIVERSE IS TESTING YOU. OPEN BOOK. STILL" + }, + { + "id": 869, + "fortune": "YOUR MANIFESTATIONS NEED A COSIGNER" + }, + { + "id": 870, + "fortune": "YOUR CHAKRAS ARE ALIGNED. WITH THE FRIDGE" + }, + { + "id": 871, + "fortune": "YOUR ZEN GARDEN NEEDS WEEDING. IRONIC" + }, + { + "id": 872, + "fortune": "YOUR MANTRA IS FIVE MORE MINUTES" + }, + { + "id": 873, + "fortune": "YOUR THIRD EYE NEEDS GLASSES TOO" + }, + { + "id": 874, + "fortune": "YOUR ENERGY CLEANSE MISSED A SPOT" + }, + { + "id": 875, + "fortune": "YOUR SAGE SMOKE SET OFF THE ALARM. FITTING" + }, + { + "id": 876, + "fortune": "INNER PEACE IS CLOSE. TRY THE OTHER DOOR" + }, + { + "id": 877, + "fortune": "A STRANGER WILL SMILE AT YOU. STAY CALM" + }, + { + "id": 878, + "fortune": "SOMEONE THOUGHT OF YOU TODAY. BRIEFLY" + }, + { + "id": 879, + "fortune": "YOU CROSSED SOMEONES MIND. IT KEPT WALKING" + }, + { + "id": 880, + "fortune": "AN ADMIRER WATCHES. IT IS YOUR CAT" + }, + { + "id": 881, + "fortune": "YOU WERE MENTIONED TODAY. CONTEXT UNKNOWN" + }, + { + "id": 882, + "fortune": "YOUR NAME CAME UP. THE ROOM WENT QUIET" + }, + { + "id": 883, + "fortune": "SOMEONE MISSES YOU. THEY LOST YOUR NUMBER" + }, + { + "id": 884, + "fortune": "YOU ARE UNFORGETTABLE. MANY HAVE TRIED" + }, + { + "id": 885, + "fortune": "YOUR ABSENCE WAS NOTICED. AND ENJOYED" + }, + { + "id": 886, + "fortune": "PEOPLE TALK ABOUT YOU. KEEP IT THAT WAY" + }, + { + "id": 887, + "fortune": "YOUR REPUTATION ARRIVED FIRST. APOLOGISE" + }, + { + "id": 888, + "fortune": "YOU MADE AN IMPRESSION. DENTS COUNT" + }, + { + "id": 889, + "fortune": "SOMEONE QUOTED YOU TODAY. INCORRECTLY" + }, + { + "id": 890, + "fortune": "YOU INSPIRED SOMEONE. TO DO THE OPPOSITE" + }, + { + "id": 891, + "fortune": "YOUR LEGACY IS SAFE. NOBODY CHECKED" + }, + { + "id": 892, + "fortune": "HISTORY WILL REMEMBER YOU. AS A FOOTNOTE" + }, + { + "id": 893, + "fortune": "YOUR BIOGRAPHY NEEDS A GHOSTWRITER. AND PLOT" + }, + { + "id": 894, + "fortune": "YOUR AUTOGRAPH IS WORTHLESS. FOR NOW" + }, + { + "id": 895, + "fortune": "FAME AWAITS. LOCAL FAME. VERY LOCAL" + }, + { + "id": 896, + "fortune": "YOUR FIFTEEN MINUTES ARE BUFFERING" + }, + { + "id": 897, + "fortune": "YOU WILL FIND YOUR KEYS. WHERE YOU LEFT THEM" + }, + { + "id": 898, + "fortune": "YOUR PHONE IS IN YOUR OTHER HAND. AGAIN" + }, + { + "id": 899, + "fortune": "YOU WALKED INTO A ROOM. THE REASON LEFT" + }, + { + "id": 900, + "fortune": "YOUR MEMORY IS FINE. IT JUST CURATES" + }, + { + "id": 901, + "fortune": "YOU FORGOT WHY YOU OPENED THE FRIDGE. SNACK" + }, + { + "id": 902, + "fortune": "YOUR MENTAL NOTES NEED A FILING SYSTEM" + }, + { + "id": 903, + "fortune": "YOU REMEMBERED THE TUNE. NOT THE TASK" + }, + { + "id": 904, + "fortune": "YOUR SHOPPING MEMORY EXPIRES AT THE STORE" + }, + { + "id": 905, + "fortune": "YOU KNOW THAT FACE. THE NAME ESCAPED. RUN" + }, + { + "id": 906, + "fortune": "YOUR BRAIN HAS TABS OPEN TOO. ALL FROZEN" + }, + { + "id": 907, + "fortune": "A GREAT IDEA CAME AND WENT. UNRECORDED" + }, + { + "id": 908, + "fortune": "YOU LOCKED THE DOOR. PROBABLY. GO CHECK" + }, + { + "id": 909, + "fortune": "YOUR TRAIN OF THOUGHT DEPARTED WITHOUT YOU" + }, + { + "id": 910, + "fortune": "YOU RETRACED YOUR STEPS. THEY LED TO SNACKS" + }, + { + "id": 911, + "fortune": "YOUR PASSWORD RESET NEEDS A PASSWORD RESET" + }, + { + "id": 912, + "fortune": "LIFE IS SHORT. YOUR MEETINGS ARENT" + }, + { + "id": 913, + "fortune": "LIFE GAVE YOU LEMONS. YOU FILED A COMPLAINT" + }, + { + "id": 914, + "fortune": "LIFE IS A JOURNEY. YOURS NEEDS A MAP" + }, + { + "id": 915, + "fortune": "LIFE IS WHAT HAPPENS WHILE YOU SCROLL" + }, + { + "id": 916, + "fortune": "LIFE HAS NO REMOTE. GET UP AND CHANGE IT" + }, + { + "id": 917, + "fortune": "LIFE IS NOT A RACE. YOU PARKED ANYWAY" + }, + { + "id": 918, + "fortune": "LIFE IS A GIFT. KEEP THE RECEIPT" + }, + { + "id": 919, + "fortune": "LIFE IS UNSCRIPTED. YOURS NEEDS A WRITER" + }, + { + "id": 920, + "fortune": "LIFE REWARDS ACTION. YOURS SENT A MEMO" + }, + { + "id": 921, + "fortune": "LIFE IS A CLASSROOM. YOU SIT IN BACK" + }, + { + "id": 922, + "fortune": "YOUR STORY HAS PLOT HOLES. EMBRACE THEM" + }, + { + "id": 923, + "fortune": "YOUR CHAPTER TWO NEEDS CHAPTER ONE DONE" + }, + { + "id": 924, + "fortune": "YOUR SEQUEL DEPENDS ON THIS PART. NO PRESSURE" + }, + { + "id": 925, + "fortune": "YOUR NARRATOR SOUNDS TIRED LATELY" + }, + { + "id": 926, + "fortune": "YOUR CLIFFHANGER IS JUST INDECISION" + }, + { + "id": 927, + "fortune": "TOMORROW IS A NEW DAY. SAME YOU THOUGH" + }, + { + "id": 928, + "fortune": "YESTERDAY CALLED. IT WANTS NOTHING BACK" + }, + { + "id": 929, + "fortune": "TODAY WILL BE PRODUCTIVE. STARTING LATER" + }, + { + "id": 930, + "fortune": "THIS WEEK HAS POTENTIAL. HAD. HAS. UNCLEAR" + }, + { + "id": 931, + "fortune": "NEXT MONTH YOU WILL START. NEXT MONTH AGREES" + }, + { + "id": 932, + "fortune": "THE YEAR IS HALF OVER. YOUR GOALS ARE INTACT" + }, + { + "id": 933, + "fortune": "SOON IS NOT A DATE. YOU TREAT IT AS ONE" + }, + { + "id": 934, + "fortune": "EVENTUALLY IS YOUR FAVOURITE DEADLINE" + }, + { + "id": 935, + "fortune": "THE RIGHT TIME NEVER COMES. CONVENIENT" + }, + { + "id": 936, + "fortune": "YOUR WINDOW OF OPPORTUNITY HAS BLINDS" + }, + { + "id": 937, + "fortune": "PATIENCE COMES TO THOSE WHO WAIT. IRONIC" + }, + { + "id": 938, + "fortune": "YOUR TURN IS COMING. THE LINE MOVED ON" + }, + { + "id": 939, + "fortune": "YOU WAITED FOR A SIGN. THIS IS NOT IT" + }, + { + "id": 940, + "fortune": "HOLD THAT THOUGHT. FOREVER IF POSSIBLE" + }, + { + "id": 941, + "fortune": "YOUR PAUSE BUTTON IS YOUR FAVOURITE" + }, + { + "id": 942, + "fortune": "YOU PUT A PIN IN IT. THE PIN CUSHION IS FULL" + }, + { + "id": 943, + "fortune": "IT IS ON YOUR RADAR. YOUR RADAR IS OFF" + }, + { + "id": 944, + "fortune": "YOU WILL GET TO IT. IT STOPPED WAITING" + }, + { + "id": 945, + "fortune": "THE BALL IS IN YOUR COURT. GATHERING DUST" + }, + { + "id": 946, + "fortune": "YOUR FOLLOW UP NEEDS A FOLLOW UP" + }, + { + "id": 947, + "fortune": "CONSIDER THIS YOUR REMINDER. THE FOURTH" + }, + { + "id": 948, + "fortune": "YOUR INTENTIONS ARE GOOD. AND STATIONARY" + }, + { + "id": 949, + "fortune": "EFFORT WAS MADE. A ROUNDING ERROR OF IT" + }, + { + "id": 950, + "fortune": "YOU GAVE 100 PERCENT. OF YOUR MINIMUM" + }, + { + "id": 951, + "fortune": "YOUR BEST GUESS BEAT YOUR RESEARCH. AGAIN" + }, + { + "id": 952, + "fortune": "CLOSE ENOUGH IS YOUR SIGNATURE MOVE" + }, + { + "id": 953, + "fortune": "GOOD ENOUGH SHIPPED. PERFECTION SULKS" + }, + { + "id": 954, + "fortune": "YOUR SHORTCUTS TOOK THE LONG WAY" + }, + { + "id": 955, + "fortune": "YOU IMPROVISED. THE RESULTS IMPROVISED BACK" + }, + { + "id": 956, + "fortune": "YOUR WINGING IT HAS FREQUENT FLYER MILES" + }, + { + "id": 957, + "fortune": "DETAILS MATTER. YOURS WENT ON STRIKE" + }, + { + "id": 958, + "fortune": "YOUR ROUGH DRAFT WENT TO PRODUCTION" + }, + { + "id": 959, + "fortune": "YOU SKIMMED IT. IT SHOWED. IT ALWAYS SHOWS" + }, + { + "id": 960, + "fortune": "YOUR PROOFREADING MISSED THIS SENTENCE TOO" + }, + { + "id": 961, + "fortune": "ALMOST DONE IS YOUR PERMANENT ADDRESS" + }, + { + "id": 962, + "fortune": "YOUR CHECKLIST HAS SURVIVORS" + }, + { + "id": 963, + "fortune": "DONE IS BETTER THAN PERFECT. TRY EITHER" + }, + { + "id": 964, + "fortune": "YOUR STANDARDS SLIPPED. THEY LEFT A NOTE" + }, + { + "id": 965, + "fortune": "MEDIOCRITY TAKES EFFORT TOO. WELL DONE" + }, + { + "id": 966, + "fortune": "YOU EXCEEDED EXPECTATIONS. THEY WERE LOW" + }, + { + "id": 967, + "fortune": "YOUR BARE MINIMUM HAS A LOYAL FOLLOWING" + }, + { + "id": 968, + "fortune": "YOU RAISED THE BAR. FROM THE FLOOR" + }, + { + "id": 969, + "fortune": "CONSISTENCY IS KEY. YOURS IS CONSISTENT" + }, + { + "id": 970, + "fortune": "YOUR AVERAGE IS ABOVE AVERAGE. AT AVERAGING" + }, + { + "id": 971, + "fortune": "PROGRESS WAS MADE. LATERALLY. BUT MADE" + }, + { + "id": 972, + "fortune": "YOUR TRAJECTORY IS STABLE. FLAT. STABLE" + }, + { + "id": 973, + "fortune": "ONWARD AND UPWARD. OR SIDEWAYS. ONWARD" + }, + { + "id": 974, + "fortune": "YOU PIVOTED. THE DIRECTION WAS RANDOM" + }, + { + "id": 975, + "fortune": "YOUR MOMENTUM TOOK A PERSONAL DAY" + }, + { + "id": 976, + "fortune": "FULL SPEED AHEAD. YOUR SPEED. SO GENTLE" + }, + { + "id": 977, + "fortune": "YOU HIT THE GROUND WALKING. BRISKLY EVEN" + }, + { + "id": 978, + "fortune": "YOUR SPRINT BECAME A STROLL. SCENIC ONE" + }, + { + "id": 979, + "fortune": "BABY STEPS COUNT. YOURS ARE NEWBORN" + }, + { + "id": 980, + "fortune": "YOU ARE ON A ROLL. A SLOW GENTLE ROLL" + }, + { + "id": 981, + "fortune": "YOUR STREAK CONTINUES. WHICHEVER ONE" + }, + { + "id": 982, + "fortune": "MOMENTUM IS BUILDING. SOMEWHERE ELSE" + }, + { + "id": 983, + "fortune": "THE FINISH LINE MOVED. SO DID YOU. BACKWARD" + }, + { + "id": 984, + "fortune": "YOUR MARATHON IS A SERIES OF SNACK BREAKS" + }, + { + "id": 985, + "fortune": "YOU CROSSED A LINE TODAY. NOT THE FINISH ONE" + }, + { + "id": 986, + "fortune": "VICTORY IS NEAR. DEFINE VICTORY LOOSELY" + }, + { + "id": 987, + "fortune": "YOU WON THE ARGUMENT. THE ROOM WAS EMPTY" + }, + { + "id": 988, + "fortune": "CHAMPIONS TRAIN DAILY. YOU WATCHED THEM" + }, + { + "id": 989, + "fortune": "YOUR TROPHY SHELF HOLDS PARTICIPATION" + }, + { + "id": 990, + "fortune": "SECOND PLACE IS FIRST LOSER. YOU GOT FIFTH" + }, + { + "id": 991, + "fortune": "YOUR COMEBACK SEASON IS ALWAYS NEXT SEASON" + }, + { + "id": 992, + "fortune": "THE UNDERDOG STORY NEEDS AN ATTEMPT FIRST" + }, + { + "id": 993, + "fortune": "YOUR RIVALS STOPPED CHECKING ON YOU" + }, + { + "id": 994, + "fortune": "GLORY AWAITS. IT DOUBLE BOOKED THOUGH" + }, + { + "id": 995, + "fortune": "YOU MADE THE HIGHLIGHT REEL. AS A BLOOPER" + }, + { + "id": 996, + "fortune": "HISTORY FAVOURS THE BOLD. YOU FAVOUR NAPS" + }, + { + "id": 997, + "fortune": "YOUR SPLIT FLAP DISPLAY DESERVED BETTER TEXT" + }, + { + "id": 998, + "fortune": "THIS MESSAGE USED 45 CHARACTERS WISELY. ISH" + }, + { + "id": 999, + "fortune": "YOU BUILT A DISPLAY FOR THIS. WORTH IT" + }, + { + "id": 1000, + "fortune": "EACH FLAP TURNED FOR THIS WISDOM. SAVOR IT" + }, + { + "id": 1001, + "fortune": "YOUR TEA IS BREWING. YOU FORGOT IT. AGAIN" + }, + { + "id": 1002, + "fortune": "GOOD FORTUNE QUEUED POLITELY. THEN LEFT" + }, + { + "id": 1003, + "fortune": "YOU ARE DEAD CHUFFED. ABOUT VERY LITTLE" + }, + { + "id": 1004, + "fortune": "YOUR PLANS WENT PEAR SHAPED. AS FORECAST" + }, + { + "id": 1005, + "fortune": "MIND THE GAP. BETWEEN HOPE AND MONDAY" + }, + { + "id": 1006, + "fortune": "YOU SAID SORRY FIRST. YOU ALWAYS DO" + }, + { + "id": 1007, + "fortune": "A CHEEKY WIN NEARS. VERY CHEEKY. VERY SMALL" + }, + { + "id": 1008, + "fortune": "YOUR BISCUIT DUNKED TOO LONG. TRAGEDY STRUCK" + }, + { + "id": 1009, + "fortune": "IT HAS ALL GONE PETE TONG. AS USUAL" + }, + { + "id": 1010, + "fortune": "YOU ARE GUTTED. AND IT IS ONLY TUESDAY" + }, + { + "id": 1011, + "fortune": "YOUR FORTNIGHT PLAN LASTED ONE DAY" + }, + { + "id": 1012, + "fortune": "KEEP CALM. YOU HAVE NO OTHER SETTING" + }, + { + "id": 1013, + "fortune": "THE KETTLE IS ON. YOUR AMBITIONS ARENT" + }, + { + "id": 1014, + "fortune": "BRILLIANT NEWS AWAITS. FOR SOMEONE ELSE" + }, + { + "id": 1015, + "fortune": "YOU FANCY A CHANGE. THE CHANGE FANCIES OTHERS" + }, + { + "id": 1016, + "fortune": "THE FORECAST SAYS SUN. PACK A BROLLY" + } +] \ No newline at end of file diff --git a/apps/sarcastic-fortune-cookies/fortunes_en.json b/apps/sarcastic-fortune-cookies/fortunes_en.json new file mode 100644 index 0000000..bb516dc --- /dev/null +++ b/apps/sarcastic-fortune-cookies/fortunes_en.json @@ -0,0 +1,4002 @@ +[ +{ +"id":1, +"fortune":"EVERYTHING HAPPENS. RARELY FOR A REASON" +}, +{ +"id":2, +"fortune":"GOOD LUCK IS COMING. IT WALKS SLOWLY" +}, +{ +"id":3, +"fortune":"THE UNIVERSE HAS A PLAN. A MODEST ONE" +}, +{ +"id":4, +"fortune":"YOUR DESTINY CALLED. IT RESCHEDULED" +}, +{ +"id":5, +"fortune":"FATE HAS PLANS FOR YOU. BACKUP PLANS" +}, +{ +"id":6, +"fortune":"THE STARS ALIGNED. THEN THEY SAW YOU" +}, +{ +"id":7, +"fortune":"LUCK FAVORS THE BOLD. YOU CAN WATCH" +}, +{ +"id":8, +"fortune":"YOUR SHIP IS COMING IN. IT IS A CANOE" +}, +{ +"id":9, +"fortune":"TRUST THE PROCESS. IT DOES NOT TRUST YOU" +}, +{ +"id":10, +"fortune":"A SIGN IS COMING. IT SAYS YIELD" +}, +{ +"id":11, +"fortune":"YOUR LUCKY STAR IS IN ANOTHER GALAXY" +}, +{ +"id":12, +"fortune":"KARMA IS REAL. THAT EXPLAINS A LOT" +}, +{ +"id":13, +"fortune":"THE FUTURE IS BRIGHT. WEAR SUNSCREEN" +}, +{ +"id":14, +"fortune":"DESTINY AWAITS. IT BROUGHT A BOOK" +}, +{ +"id":15, +"fortune":"MIRACLES HAPPEN DAILY. ELSEWHERE MOSTLY" +}, +{ +"id":16, +"fortune":"YOUR MOMENT IS COMING. IT IS IN TRAFFIC" +}, +{ +"id":17, +"fortune":"FORTUNE KNOCKED ONCE. YOU WERE OUT" +}, +{ +"id":18, +"fortune":"THE TIDE IS TURNING. GLACIALLY" +}, +{ +"id":19, +"fortune":"BIG THINGS ARE COMING. LOWER THE BAR" +}, +{ +"id":20, +"fortune":"OPPORTUNITY IS NEAR. YOU TWO KEEP MISSING" +}, +{ +"id":21, +"fortune":"THE COSMOS HEARD YOU. YOU ARE IN THE QUEUE" +}, +{ +"id":22, +"fortune":"YOUR STARS ARE FINE. YOUR CHOICES ARENT" +}, +{ +"id":23, +"fortune":"WISH ON A STAR. THE STAR MARKED IT SPAM" +}, +{ +"id":24, +"fortune":"GOOD THINGS TAKE TIME. YOURS TAKE DECADES" +}, +{ +"id":25, +"fortune":"A WINDFALL NEARS. DRESS IN LAYERS" +}, +{ +"id":26, +"fortune":"THE SIGNS WERE THERE. READING IS OPTIONAL" +}, +{ +"id":27, +"fortune":"YOU ARE ONE IN A MILLION. SO ARE THOUSANDS" +}, +{ +"id":28, +"fortune":"THE BEST IS YET TO COME. NO RUSH APPARENTLY" +}, +{ +"id":29, +"fortune":"YOUR AURA IS RADIANT. IT WORKS OVERTIME" +}, +{ +"id":30, +"fortune":"LADY LUCK SMILED. POLITELY. AT A STRANGER" +}, +{ +"id":31, +"fortune":"YOUR PRAYERS WERE HEARD. AND FILED" +}, +{ +"id":32, +"fortune":"ABUNDANCE FLOWS TO YOU. UPSTREAM SADLY" +}, +{ +"id":33, +"fortune":"FORTUNES WHEEL TURNS. YOU ARE THE HAMSTER" +}, +{ +"id":34, +"fortune":"EVERY CLOUD HAS A LINING. YOURS IS DAMP" +}, +{ +"id":35, +"fortune":"SERENDIPITY IS ASKING AROUND ABOUT YOU" +}, +{ +"id":36, +"fortune":"A LUCKY BREAK NEARS. BREAK HAS TWO MEANINGS" +}, +{ +"id":37, +"fortune":"THE ORACLE SPOKE. MOSTLY IT SIGHED" +}, +{ +"id":38, +"fortune":"YOUR HOROSCOPE LOOKS GREAT. IT IS FICTION" +}, +{ +"id":39, +"fortune":"COSMIC JUSTICE EXISTS. SETTLE OUT OF COURT" +}, +{ +"id":40, +"fortune":"LIGHTNING WONT STRIKE TWICE. YOU MIGHT" +}, +{ +"id":41, +"fortune":"FATE REWARDS PATIENCE. INVOICE PENDING" +}, +{ +"id":42, +"fortune":"THE MOON SAW YOUR FRESH START. AGAIN" +}, +{ +"id":43, +"fortune":"YOUR SPIRIT GUIDE REQUESTED A TRANSFER" +}, +{ +"id":44, +"fortune":"ANGELS WATCH OVER YOU. TAKING NOTES" +}, +{ +"id":45, +"fortune":"BELIEVE AND RECEIVE. RECEIPTS VARY" +}, +{ +"id":46, +"fortune":"YOUR GOLDEN ERA NEARS. GOLD PLATED" +}, +{ +"id":47, +"fortune":"WHAT IS MEANT FOR YOU IS NOT HURRYING" +}, +{ +"id":48, +"fortune":"THE UNIVERSE WINKED. OR HAD AN EYE ISSUE" +}, +{ +"id":49, +"fortune":"YOUR LUCK IS CHANGING. DIRECTION UNCLEAR" +}, +{ +"id":50, +"fortune":"PROVIDENCE PROVIDES. IT CHECKS RECEIPTS" +}, +{ +"id":51, +"fortune":"SUCCESS IS NEAR. SO IS THE SNOOZE BUTTON" +}, +{ +"id":52, +"fortune":"HARD WORK PAYS OFF. IN EXPOSURE" +}, +{ +"id":53, +"fortune":"YOUR POTENTIAL IS LIMITLESS. AND UNUSED" +}, +{ +"id":54, +"fortune":"DREAM BIG. SET AN ALARM THOUGH" +}, +{ +"id":55, +"fortune":"THE EARLY BIRD LEFT WITHOUT YOU" +}, +{ +"id":56, +"fortune":"YOUR BIG BREAK NEARS. DO NOT BREAK IT" +}, +{ +"id":57, +"fortune":"ROME WASNT BUILT IN A DAY. YOURS ISNT STARTED" +}, +{ +"id":58, +"fortune":"YOUR TALENTS WILL BE FOUND. WITH A WARRANT" +}, +{ +"id":59, +"fortune":"AIM FOR THE STARS. MIND THE CEILING" +}, +{ +"id":60, +"fortune":"WINNERS NEVER QUIT. YOU FOUND COASTING" +}, +{ +"id":61, +"fortune":"YOUR IDEAS ARE AHEAD. YOUR DEADLINES BEHIND" +}, +{ +"id":62, +"fortune":"EXCELLENCE IS A HABIT. SO IS YOURS" +}, +{ +"id":63, +"fortune":"YOUR WORK SPEAKS. IT REQUESTED A LAWYER" +}, +{ +"id":64, +"fortune":"THE TOP IS LONELY. YOU SOLVED THAT PROBLEM" +}, +{ +"id":65, +"fortune":"A PROMOTION NEARS. SOMEONE ELSES" +}, +{ +"id":66, +"fortune":"YOUR RESUME IMPRESSES FICTION FANS" +}, +{ +"id":67, +"fortune":"YOUR BEST WORK IS AHEAD. IT IS VERY FAST" +}, +{ +"id":68, +"fortune":"THAT MEETING COULD HAVE BEEN AN EMAIL" +}, +{ +"id":69, +"fortune":"YOUR INBOX IS NOT YOUR ENEMY. JUST A WITNESS" +}, +{ +"id":70, +"fortune":"DEADLINES FEAR NOTHING. YOU WALK PAST THEM" +}, +{ +"id":71, +"fortune":"YOUR FIVE YEAR PLAN IS FIVE YEARS OLD" +}, +{ +"id":72, +"fortune":"SLOW AND STEADY WINS. YOU NAILED SLOW" +}, +{ +"id":73, +"fortune":"YOUR CALENDAR IS FULL. OF BUFFERS" +}, +{ +"id":74, +"fortune":"YOUR TEAM DREAMS TOO. OF TRANSFERS" +}, +{ +"id":75, +"fortune":"THE OCCASION LOWERED ITSELF TO MEET YOU" +}, +{ +"id":76, +"fortune":"SUCCESS LEAVES CLUES. YOU PREFER MYSTERY" +}, +{ +"id":77, +"fortune":"YOUR COMFORT ZONE NEEDS ITS OWN ZIP CODE" +}, +{ +"id":78, +"fortune":"QUALITY OVER QUANTITY. YOU DECLINED BOTH" +}, +{ +"id":79, +"fortune":"YOUR INSTINCTS ARE SHARP. FACTS ON SABBATICAL" +}, +{ +"id":80, +"fortune":"YOU WORK WELL UNDER PRESSURE. IT DISAGREES" +}, +{ +"id":81, +"fortune":"MASTERY TAKES 10000 HOURS. YOU HAVE 40" +}, +{ +"id":82, +"fortune":"YOUR DESK PLANT BELIEVED IN YOU. AND WATER" +}, +{ +"id":83, +"fortune":"THE PRINTER KNOWS YOU ARE IN A HURRY" +}, +{ +"id":84, +"fortune":"YOUR LEGACY IS UNDER CONSTRUCTION. STILL" +}, +{ +"id":85, +"fortune":"AN AWARD AWAITS. PARTICIPATION COUNTS" +}, +{ +"id":86, +"fortune":"YOUR CAN DO ATTITUDE MET YOUR DID NOT RECORD" +}, +{ +"id":87, +"fortune":"NETWORKING OPENS DOORS. TRY KNOCKING TOO" +}, +{ +"id":88, +"fortune":"YOU LEAD BY EXAMPLE. HR WANTS A NEW ONE" +}, +{ +"id":89, +"fortune":"YOUR SIDE HUSTLE JOINED THE OTHERS. RESTING" +}, +{ +"id":90, +"fortune":"GENIUS IS 1 PERCENT WORK. YOU STOPPED THERE" +}, +{ +"id":91, +"fortune":"YOUR OUT OF OFFICE REPLY IS YOUR BEST WORK" +}, +{ +"id":92, +"fortune":"FAILURE IS A STEPPING STONE. NICE STAIRCASE" +}, +{ +"id":93, +"fortune":"YOU ARE IRREPLACEABLE. THE POSTING IS DRAFTED" +}, +{ +"id":94, +"fortune":"YOUR PERFORMANCE REVIEW NEARS. REHEARSE" +}, +{ +"id":95, +"fortune":"EFFICIENCY IS YOUR MIDDLE NAME. LEGALLY ONLY" +}, +{ +"id":96, +"fortune":"YOU WILL LAND ON YOUR FEET. LANDING DELAYED" +}, +{ +"id":97, +"fortune":"THE SUMMIT IS CLOSE. SO IS BASE CAMP" +}, +{ +"id":98, +"fortune":"YOUR AMBITION WOKE UP. YOUR ALARM DID NOT" +}, +{ +"id":99, +"fortune":"BE YOURSELF. EVERYONE ELSE WAS ALARMED" +}, +{ +"id":100, +"fortune":"KNOW THYSELF. A BRIEF INTRO MAY HELP" +}, +{ +"id":101, +"fortune":"YOUR GLOW UP IS COMING. NOTIFY THE GRID" +}, +{ +"id":102, +"fortune":"NEW YEAR NEW YOU. THE OLD YOU LEFT NOTES" +}, +{ +"id":103, +"fortune":"YOU ARE ENOUGH. THE BAR WAS ADJUSTABLE" +}, +{ +"id":104, +"fortune":"YOUR INNER PEACE SAW YOUR SCHEDULE. IT LEFT" +}, +{ +"id":105, +"fortune":"GROWTH TAKES TIME. YOURS FILED AN EXTENSION" +}, +{ +"id":106, +"fortune":"YOU ARE A WORK IN PROGRESS. MOSTLY WORK" +}, +{ +"id":107, +"fortune":"SELF AWARENESS IS A GIFT. YOURS SHIPPED" +}, +{ +"id":108, +"fortune":"HEALING IS NOT LINEAR. YOURS IS A SCRIBBLE" +}, +{ +"id":109, +"fortune":"YOUR BEST SELF IS EMERGING. IT HIT SNOOZE" +}, +{ +"id":110, +"fortune":"SELF CARE MATTERS. STREAMING IS NOT A SPA" +}, +{ +"id":111, +"fortune":"YOUR BOUNDARIES ARE HEALTHY. AND UNTESTED" +}, +{ +"id":112, +"fortune":"GRATITUDE HELPS. TRY IT ON PEOPLE" +}, +{ +"id":113, +"fortune":"YOU FORGIVE EASILY. YOURSELF ESPECIALLY" +}, +{ +"id":114, +"fortune":"YOUR HABITS DEFINE YOU. OUR CONDOLENCES" +}, +{ +"id":115, +"fortune":"SMALL STEPS COUNT. EVEN TO THE FRIDGE" +}, +{ +"id":116, +"fortune":"YOUR INTUITION IS POWERFUL. AND WRONG" +}, +{ +"id":117, +"fortune":"MINDFULNESS HELPS. YOUR PHONE DISAGREES" +}, +{ +"id":118, +"fortune":"YOU LET GO OF THE PAST. IT HAS YOUR ADDRESS" +}, +{ +"id":119, +"fortune":"YOUR MORNING ROUTINE IS SACRED. ALL 4 MINUTES" +}, +{ +"id":120, +"fortune":"YOU ARE LEARNING TO SAY NO. TO VEGETABLES" +}, +{ +"id":121, +"fortune":"YOUR WILLPOWER LIFTS ALMOST A WHOLE COOKIE" +}, +{ +"id":122, +"fortune":"YOUR PURPOSE EXISTS. IT STAYS ANONYMOUS" +}, +{ +"id":123, +"fortune":"YOU EMBRACE CHANGE. MOSTLY COUCH CHANGE" +}, +{ +"id":124, +"fortune":"YOUR JOURNAL IS RAW AND HONEST. BOTH PAGES" +}, +{ +"id":125, +"fortune":"YOU LIVE YOUR TRUTH. FACT CHECK PENDING" +}, +{ +"id":126, +"fortune":"YOUR INNER CRITIC ONLY SIGHS NOW" +}, +{ +"id":127, +"fortune":"YOUR TRANSFORMATION NEEDED LABELED PHOTOS" +}, +{ +"id":128, +"fortune":"YOUR GOALS ARE IN SIGHT. NICE BINOCULARS" +}, +{ +"id":129, +"fortune":"YOUR NEW HABITS LOOK FAMILIAR. SUSPICIOUSLY" +}, +{ +"id":130, +"fortune":"YOUR ENERGY IS MAGNETIC. TO OBLIGATIONS" +}, +{ +"id":131, +"fortune":"YOUR WALLS CAME DOWN. THE MOAT EXPANDED" +}, +{ +"id":132, +"fortune":"YOU CELEBRATE SMALL WINS. DAILY. SOMEONE MUST" +}, +{ +"id":133, +"fortune":"YOUR EVOLUTION PUZZLES DARWIN" +}, +{ +"id":134, +"fortune":"YOU PUT YOURSELF FIRST. THE LINE WAS SHORT" +}, +{ +"id":135, +"fortune":"YOUR REINVENTION TOUR CONTINUES. SAME SETLIST" +}, +{ +"id":136, +"fortune":"YOU ARE PRESENT. PHYSICALLY AT LEAST" +}, +{ +"id":137, +"fortune":"YOUR FRESH START STARTS MONDAY. A FUTURE ONE" +}, +{ +"id":138, +"fortune":"YOUR VISION BOARD IS STUNNING. AND RETIRED" +}, +{ +"id":139, +"fortune":"YOU HONOR YOUR FEELINGS. AT FULL VOLUME" +}, +{ +"id":140, +"fortune":"YOUR CALM IS REAL. YOU STOPPED CHECKING EMAIL" +}, +{ +"id":141, +"fortune":"YOUR STANDARDS ARE RISING. FROM SEA LEVEL" +}, +{ +"id":142, +"fortune":"YOU ARE BLOOMING. LATE SEASON. STILL COUNTS" +}, +{ +"id":143, +"fortune":"YOUR DISCIPLINE IS THE STUFF OF LEGEND" +}, +{ +"id":144, +"fortune":"YOUR AFFIRMATIONS WORK. ON THE CEILING" +}, +{ +"id":145, +"fortune":"TRUE LOVE AWAITS. IT IS RUNNING LATE TOO" +}, +{ +"id":146, +"fortune":"YOUR SOULMATE EXISTS. COVERAGE MAY VARY" +}, +{ +"id":147, +"fortune":"ROMANCE IS IN THE AIR. SO IS POLLEN" +}, +{ +"id":148, +"fortune":"CUPID HAS YOUR ADDRESS. HE DROVE PAST TWICE" +}, +{ +"id":149, +"fortune":"YOUR HEART KNOWS THE WAY. YOUR TEXTS DONT" +}, +{ +"id":150, +"fortune":"LOVE IS BLIND. YOURS ALSO FORGETS" +}, +{ +"id":151, +"fortune":"YOUR EX THINKS OF YOU. AS A LESSON" +}, +{ +"id":152, +"fortune":"YOUR FLIRTING IS SUBTLE. UNDETECTABLE EVEN" +}, +{ +"id":153, +"fortune":"YOUR TYPE IS CONSISTENT. THAT IS THE PROBLEM" +}, +{ +"id":154, +"fortune":"SOMEONE ADMIRES YOU FROM AFAR. STAY THERE" +}, +{ +"id":155, +"fortune":"YOUR GREEN FLAGS ARE LOVELY. AND OUTNUMBERED" +}, +{ +"id":156, +"fortune":"LOVE CONQUERS ALL. IT REQUESTED BACKUP" +}, +{ +"id":157, +"fortune":"HEART ON YOUR SLEEVE. DRY CLEAN BOTH" +}, +{ +"id":158, +"fortune":"A TEXT FROM THE PAST NEARS. DO NOT REPLY" +}, +{ +"id":159, +"fortune":"YOUR CRUSH KNOWS YOU EXIST. AS A CONCEPT" +}, +{ +"id":160, +"fortune":"YOU LOVE DEEPLY. BRIEFLY. BUT DEEPLY" +}, +{ +"id":161, +"fortune":"YOUR HEART IS OPEN. SO IS YOUR TAB" +}, +{ +"id":162, +"fortune":"SPARKS WILL FLY. CHECK THE SMOKE ALARM" +}, +{ +"id":163, +"fortune":"YOU ARE READY FOR COMMITMENT. TO A COUCH" +}, +{ +"id":164, +"fortune":"YOUR MEET CUTE NEARS. A MEET ADEQUATE" +}, +{ +"id":165, +"fortune":"YOUR HEART HEALED. IT ADDED SECURITY" +}, +{ +"id":166, +"fortune":"LOVE IS PATIENT. IT HAD PRACTICE WITH YOU" +}, +{ +"id":167, +"fortune":"YOUR REBOUND GAME BEATS THE ORIGINAL" +}, +{ +"id":168, +"fortune":"YOUR HONEYMOON PHASE LASTED 11 DAYS" +}, +{ +"id":169, +"fortune":"YOUR PET IS YOUR BEST RELATIONSHIP. IT AGREES" +}, +{ +"id":170, +"fortune":"YOUR VOWS WILL MAKE PEOPLE CRY. THE GRAMMAR" +}, +{ +"id":171, +"fortune":"YOU ARE A CATCH. CATCH AND RELEASE EXISTS" +}, +{ +"id":172, +"fortune":"ABSENCE MAKES HEARTS FONDER. KEEP TESTING" +}, +{ +"id":173, +"fortune":"YOUR PERFECT MATCH EXISTS. THE APP GAVE UP" +}, +{ +"id":174, +"fortune":"YOUR RED FLAGS ARE MAROON NOW. PROGRESS" +}, +{ +"id":175, +"fortune":"LOVE AT FIRST SIGHT IS REAL. GET GLASSES" +}, +{ +"id":176, +"fortune":"YOUR HEART WANTS THINGS. SEE A SPECIALIST" +}, +{ +"id":177, +"fortune":"SOMEONE THINKS YOU ARE PERFECT. FROM AFAR" +}, +{ +"id":178, +"fortune":"YOU BELIEVE IN SECOND CHANCES. AND FIFTHS" +}, +{ +"id":179, +"fortune":"YOUR BREAKUP PLAYLIST GETS HEAVY USE" +}, +{ +"id":180, +"fortune":"YOU ARE WORTH THE WAIT. THE WAIT CONTINUES" +}, +{ +"id":181, +"fortune":"ROMANCE IS NOT DEAD. YOURS IS RESTING" +}, +{ +"id":182, +"fortune":"YOUR IN LAWS WILL ADORE YOU. EVENTUALLY" +}, +{ +"id":183, +"fortune":"YOUR DATING BIO IS HONEST. SELECTIVELY" +}, +{ +"id":184, +"fortune":"YOUR HUGS ARE GREAT. AND RARE. LIKE ECLIPSES" +}, +{ +"id":185, +"fortune":"SOMEONE DREAMS OF YOUR COOKING. IN FEAR" +}, +{ +"id":186, +"fortune":"YOUR CHARM OPENS DOORS. PUSH DONT PULL" +}, +{ +"id":187, +"fortune":"YOUR STANDARDS MET YOUR CHOICES. AWKWARD" +}, +{ +"id":188, +"fortune":"YOU WILL BE SWEPT AWAY. STRETCH FIRST" +}, +{ +"id":189, +"fortune":"INTIMACY MEANS BEING SEEN. YOU NEGOTIATED" +}, +{ +"id":190, +"fortune":"YOUR LOVE STORY WILL BE TOLD. AS A WARNING" +}, +{ +"id":191, +"fortune":"THE SPARK IS STILL THERE. SEND A SEARCH PARTY" +}, +{ +"id":192, +"fortune":"YOUR ANNIVERSARY NEARS. THIS IS THE REMINDER" +}, +{ +"id":193, +"fortune":"LOVE YOURSELF FIRST. YOU MASTERED THAT PART" +}, +{ +"id":194, +"fortune":"YOUR HEART SKIPPED A BEAT. SEE A DOCTOR" +}, +{ +"id":195, +"fortune":"YOU BRING OUT THE BEST. AS PEOPLE LEAVE" +}, +{ +"id":196, +"fortune":"YOU NEVER SETTLE. HENCE THE FOLDING CHAIRS" +}, +{ +"id":197, +"fortune":"YOUR FUTURE PARTNER IS TRAINING. UNKNOWINGLY" +}, +{ +"id":198, +"fortune":"YOU GET GREAT ADVICE. FOLLOWING IT IS NEW" +}, +{ +"id":199, +"fortune":"YOUR HEART IS A FORTRESS. GIFT SHOP OPEN" +}, +{ +"id":200, +"fortune":"YOUR FRIENDS VALUE YOUR HONESTY. FROM AFAR" +}, +{ +"id":201, +"fortune":"A FRIEND WILL CALL SOON. THEY NEED A TRUCK" +}, +{ +"id":202, +"fortune":"YOU ARE THE GLUE. GLUE IS RARELY THANKED" +}, +{ +"id":203, +"fortune":"THE GROUP CHAT ENJOYS YOUR SILENCE" +}, +{ +"id":204, +"fortune":"TRUE FRIENDS ARE RARE. YOURS ARE HIDING" +}, +{ +"id":205, +"fortune":"YOUR LOYALTY IS UNMATCHED. YOUR TIMING ISNT" +}, +{ +"id":206, +"fortune":"A GATHERING BRINGS JOY. LEAVING BRINGS MORE" +}, +{ +"id":207, +"fortune":"YOU ARE THE FUN ONE. SELF REPORTED" +}, +{ +"id":208, +"fortune":"AN OLD FRIEND RESURFACES. THEY ARE MOVING" +}, +{ +"id":209, +"fortune":"YOUR ADVICE IS APPRECIATED. NOT REQUESTED" +}, +{ +"id":210, +"fortune":"YOU LIGHT UP ROOMS. SOME WANT DIMMERS" +}, +{ +"id":211, +"fortune":"YOUR BIRTHDAY WILL BE REMEMBERED. LATE" +}, +{ +"id":212, +"fortune":"YOUR STORIES IMPROVE YEARLY. ACCURACY DROPS" +}, +{ +"id":213, +"fortune":"PEOPLE GRAVITATE TO YOU. GRAVITY PULLS DOWN" +}, +{ +"id":214, +"fortune":"YOU ALWAYS KNOW WHAT TO SAY. TIMING EXTRA" +}, +{ +"id":215, +"fortune":"FRIENDS COME AND GO. YOURS EXCEL AT GO" +}, +{ +"id":216, +"fortune":"YOUR SOCIAL CALENDAR IS FULL. OF MAYBES" +}, +{ +"id":217, +"fortune":"YOU ARE A PEOPLE PERSON. UNVERIFIED" +}, +{ +"id":218, +"fortune":"YOUR INSIDE JOKES ARE NOW SOLO ACTS" +}, +{ +"id":219, +"fortune":"YOUR HOSTING IMPROVES. THE FIRE DEPT AGREES" +}, +{ +"id":220, +"fortune":"YOU BRING PEOPLE TOGETHER. TO DISCUSS YOU" +}, +{ +"id":221, +"fortune":"YOUR GENEROSITY HAS OFFICE HOURS" +}, +{ +"id":222, +"fortune":"YOU NEVER FORGET A FACE. NAMES OPTED OUT" +}, +{ +"id":223, +"fortune":"YOUR PEP TALKS REQUIRE FOLLOW UP CARE" +}, +{ +"id":224, +"fortune":"YOU SHOW UP WHEN IT MATTERS. TO YOU" +}, +{ +"id":225, +"fortune":"YOUR GOING AWAY PARTIES DRAW CROWDS" +}, +{ +"id":226, +"fortune":"PEOPLE CONFIDE IN YOU. AND THEN IN OTHERS" +}, +{ +"id":227, +"fortune":"YOUR HONESTY IS REFRESHING. LIKE ICE WATER" +}, +{ +"id":228, +"fortune":"YOU NEVER HOLD GRUDGES. YOU ARCHIVE THEM" +}, +{ +"id":229, +"fortune":"YOUR MEMES ARRIVE AT DAWN. UNREQUESTED" +}, +{ +"id":230, +"fortune":"YOU ARE A SHOULDER TO CRY ON. BY APPOINTMENT" +}, +{ +"id":231, +"fortune":"YOUR CANCELED DINNER PARTIES ARE LEGEND" +}, +{ +"id":232, +"fortune":"YOU KEEP EVERYONE GROUNDED. LIKE FOG" +}, +{ +"id":233, +"fortune":"YOUR ENTHUSIASM IS INFECTIOUS. MASKS EXIST" +}, +{ +"id":234, +"fortune":"A FRIEND WANTS YOUR HONEST OPINION. A TRAP" +}, +{ +"id":235, +"fortune":"YOU SPLIT BILLS FAIRLY. YOUR MATH DIFFERS" +}, +{ +"id":236, +"fortune":"YOUR PRESENCE IS A GIFT. REGIFTING HAPPENS" +}, +{ +"id":237, +"fortune":"YOUR COMPETITIVENESS RETIRED THE GAMES" +}, +{ +"id":238, +"fortune":"YOUR SURPRISE PARTIES SURPRISE NO ONE" +}, +{ +"id":239, +"fortune":"PEOPLE FEEL COMFORTABLE AROUND YOU. TO LEAVE" +}, +{ +"id":240, +"fortune":"YOU SAY WHAT EVERYONE THINKS. AT FUNERALS" +}, +{ +"id":241, +"fortune":"YOUR HOLIDAY LETTER HAS A READERSHIP" +}, +{ +"id":242, +"fortune":"YOUR APOLOGIES ARE HEARTFELT. AND FREQUENT" +}, +{ +"id":243, +"fortune":"YOU ARE THE KEEPER OF GROUP HISTORY. REVISED" +}, +{ +"id":244, +"fortune":"YOU NEVER MISS A CHANCE TO HELP. OR SAY SO" +}, +{ +"id":245, +"fortune":"YOUR DIRECTNESS IS CHARMING. AGREED WORD" +}, +{ +"id":246, +"fortune":"YOU ARE FASHIONABLY LATE. MINUS THE FASHION" +}, +{ +"id":247, +"fortune":"PEOPLE SEEK YOUR APPROVAL. TO END THE CHAT" +}, +{ +"id":248, +"fortune":"YOU NEVER GOSSIP. YOU BRIEF THE ABSENT" +}, +{ +"id":249, +"fortune":"YOUR INVITES SAY CASUAL. YOUR TEXTS DONT" +}, +{ +"id":250, +"fortune":"AN OLD FRIEND REMEMBERS YOU. FROM AFAR" +}, +{ +"id":251, +"fortune":"YOU NEVER LOSE ARGUMENTS. YOU RELOCATE THEM" +}, +{ +"id":252, +"fortune":"YOUR SUPPORT IS UNCONDITIONAL. SEE TERMS" +}, +{ +"id":253, +"fortune":"WEALTH IS COMING. IT HAS YOUR OLD ADDRESS" +}, +{ +"id":254, +"fortune":"MONEY FLOWS TO YOU. THROUGH YOU. GONE" +}, +{ +"id":255, +"fortune":"YOUR BUDGET IS ART. ABSTRACT ART" +}, +{ +"id":256, +"fortune":"SAVE FOR A RAINY DAY. MONSOONS FORECAST" +}, +{ +"id":257, +"fortune":"YOUR CREDIT SCORE IS A THRILLER" +}, +{ +"id":258, +"fortune":"YOUR SAVINGS GROW. CHARACTER MOSTLY" +}, +{ +"id":259, +"fortune":"MONEY TALKS. YOURS SAYS GOODBYE" +}, +{ +"id":260, +"fortune":"YOUR RETIREMENT PLAN IS A LOTTERY TICKET" +}, +{ +"id":261, +"fortune":"YOU HAVE EXPENSIVE TASTE. AND A BUDGET" +}, +{ +"id":262, +"fortune":"COMPOUND INTEREST IS MAGIC. YOU CHOSE DEBT" +}, +{ +"id":263, +"fortune":"YOUR EMERGENCY FUND COVERS SMALL EMERGENCIES" +}, +{ +"id":264, +"fortune":"A RAISE IS DESERVED. AND UNSCHEDULED" +}, +{ +"id":265, +"fortune":"YOUR CART IS A VISION BOARD WITH CHECKOUT" +}, +{ +"id":266, +"fortune":"YOUR GENEROSITY IS FAMOUS. AMONG VENDORS" +}, +{ +"id":267, +"fortune":"THE BEST THINGS ARE FREE. YOURS SHIP" +}, +{ +"id":268, +"fortune":"YOUR SUBSCRIPTIONS OUTNUMBER YOUR FRIENDS" +}, +{ +"id":269, +"fortune":"BUY LOW SELL HIGH. YOU INVENTED THE REVERSE" +}, +{ +"id":270, +"fortune":"YOUR BANK TEXTS OUT OF CONCERN NOW" +}, +{ +"id":271, +"fortune":"YOUR TAX REFUND HAS OTHER PLANS" +}, +{ +"id":272, +"fortune":"YOUR SPLURGES ARE STRATEGIC. ALLEGEDLY" +}, +{ +"id":273, +"fortune":"YOU WILL FIND MONEY IN A COAT. IT WAS YOURS" +}, +{ +"id":274, +"fortune":"YOUR PORTFOLIO IS DIVERSIFIED. IN REGRETS" +}, +{ +"id":275, +"fortune":"PAYDAY BRINGS JOY. ABOUT 18 HOURS OF IT" +}, +{ +"id":276, +"fortune":"YOUR LATE FEES HAVE LATE FEES NOW" +}, +{ +"id":277, +"fortune":"A PENNY SAVED IS A START. A SMALL ONE" +}, +{ +"id":278, +"fortune":"RICH IN SPIRIT. THE REGISTER DISAGREES" +}, +{ +"id":279, +"fortune":"YOUR RECEIPTS TELL A STORY. A LONG ONE" +}, +{ +"id":280, +"fortune":"YOUR DOWN PAYMENT BECAME SUSHI. TWICE" +}, +{ +"id":281, +"fortune":"YOUR BUDGET SHEET IS HISTORICAL FICTION" +}, +{ +"id":282, +"fortune":"YOUR FREE TRIALS NEVER DIE. THEY BILL" +}, +{ +"id":283, +"fortune":"CASH IS KING. YOUR KINGDOM RUNS ON CREDIT" +}, +{ +"id":284, +"fortune":"YOUR SPENDING FREEZE LASTED 40 MINUTES" +}, +{ +"id":285, +"fortune":"YOUR BILLS ARRIVE ON TIME. UNLIKE YOU" +}, +{ +"id":286, +"fortune":"YOUR MONEY WORKS HARD. FOR OTHER PEOPLE" +}, +{ +"id":287, +"fortune":"YOUR COUPONS EXPIRED. ALL OF THEM. TODAY" +}, +{ +"id":288, +"fortune":"YOUR FINANCIAL PLAN HAS PHASES. DENIAL FIRST" +}, +{ +"id":289, +"fortune":"YOUR DISCOUNTS FUND YOUR IMPULSE BUYS" +}, +{ +"id":290, +"fortune":"YOUR CRYPTO PHASE WAS EDUCATIONAL. AND COSTLY" +}, +{ +"id":291, +"fortune":"THE LOTTERY IS A HOPE TAX. YOU DONATE WEEKLY" +}, +{ +"id":292, +"fortune":"YOUR SAVINGS GOAL HASNT MOVED SINCE SPRING" +}, +{ +"id":293, +"fortune":"WEALTH WHISPERS. YOURS BLOCKED THE NUMBER" +}, +{ +"id":294, +"fortune":"YOU TRACK EVERY EXPENSE. THEN MAKE MORE" +}, +{ +"id":295, +"fortune":"YOUR PIGGY BANK HAS TRUST ISSUES. EARNED" +}, +{ +"id":296, +"fortune":"YOUR REFUND ARRIVED. ALREADY SPENT. TWICE" +}, +{ +"id":297, +"fortune":"MONEY CANT BUY HAPPINESS. YOURS QUIT TRYING" +}, +{ +"id":298, +"fortune":"YOUR INVOICES AGE LIKE FINE WINE. UNPAID" +}, +{ +"id":299, +"fortune":"YOUR NET WORTH AND SELF WORTH BOTH DIPPED" +}, +{ +"id":300, +"fortune":"PATIENCE IS A VIRTUE. YOU CHOSE OTHERS" +}, +{ +"id":301, +"fortune":"SILENCE IS GOLDEN. YOURS WOULD FUND A NATION" +}, +{ +"id":302, +"fortune":"THINK BEFORE YOU SPEAK. A PILOT PROGRAM" +}, +{ +"id":303, +"fortune":"STILL WATERS RUN DEEP. YOURS RUN ANKLE HIGH" +}, +{ +"id":304, +"fortune":"KNOWLEDGE IS POWER. YOURS IS ON STANDBY" +}, +{ +"id":305, +"fortune":"AN OPEN MIND IS A TREASURE. NICE BREEZE" +}, +{ +"id":306, +"fortune":"LISTEN TWICE SPEAK ONCE. INVERT YOUR RATIO" +}, +{ +"id":307, +"fortune":"THE TRUTH WILL SET YOU FREE. YOURS SAT DOWN" +}, +{ +"id":308, +"fortune":"LOOK BEFORE YOU LEAP. OR AFTER. YOUR WAY" +}, +{ +"id":309, +"fortune":"A FOOL AND HIS MONEY HAD A NICE CEREMONY" +}, +{ +"id":310, +"fortune":"ACTIONS SPEAK LOUDER. YOURS WHISPER" +}, +{ +"id":311, +"fortune":"MEASURE TWICE CUT ONCE. YOU CUT FIRST" +}, +{ +"id":312, +"fortune":"THE PEN IS MIGHTY. YOUR EMAILS ARE WEAPONS" +}, +{ +"id":313, +"fortune":"BETTER LATE THAN NEVER. YOU TEST THIS DAILY" +}, +{ +"id":314, +"fortune":"HONESTY IS THE BEST POLICY. READ THE RIDERS" +}, +{ +"id":315, +"fortune":"DONT COUNT YOUR CHICKENS. YOURS UNIONIZED" +}, +{ +"id":316, +"fortune":"GRASS IS GREENER WHERE WATERED. BUY A HOSE" +}, +{ +"id":317, +"fortune":"EVERY SAINT HAS A PAST. YOURS HAS A FILE" +}, +{ +"id":318, +"fortune":"PRACTICE WHAT YOU PREACH. OR PREACH LESS" +}, +{ +"id":319, +"fortune":"TWO WRONGS DONT MAKE A RIGHT. STOP AT NINE" +}, +{ +"id":320, +"fortune":"ALL THAT GLITTERS ISNT GOLD. CHECK RECEIPTS" +}, +{ +"id":321, +"fortune":"A ROLLING STONE GATHERS NO MOSS. NICE MOSS" +}, +{ +"id":322, +"fortune":"DONT PUT ALL EGGS IN ONE BASKET. FIND BASKET" +}, +{ +"id":323, +"fortune":"EMPTY VESSELS ARE LOUD. GREAT ACOUSTICS" +}, +{ +"id":324, +"fortune":"HE WHO LAUGHS LAST THOUGHT SLOWEST. HI" +}, +{ +"id":325, +"fortune":"A BIRD IN HAND IS WORTH TWO. YOURS FLEW" +}, +{ +"id":326, +"fortune":"IF IT AINT BROKE DONT FIX IT. TOO LATE" +}, +{ +"id":327, +"fortune":"YOU CAN LEAD A HORSE TO WATER. IT DECLINED" +}, +{ +"id":328, +"fortune":"PRIDE GOES BEFORE A FALL. NICE BALANCE" +}, +{ +"id":329, +"fortune":"TIME HEALS ALL WOUNDS. YOURS PICK AT IT" +}, +{ +"id":330, +"fortune":"OLD HABITS DIE HARD. YOURS BOUGHT INSURANCE" +}, +{ +"id":331, +"fortune":"WHERE THERE IS SMOKE THERE IS YOU" +}, +{ +"id":332, +"fortune":"THE BEST DEFENSE IS DISTANCE. THEY LEARNED" +}, +{ +"id":333, +"fortune":"FAMILIARITY BREEDS CONTEMPT. THRIVING HERE" +}, +{ +"id":334, +"fortune":"THE DEVIL IS IN THE DETAILS. YOURS ARE EMPTY" +}, +{ +"id":335, +"fortune":"ONE MANS TRASH IS YOUR GARAGE" +}, +{ +"id":336, +"fortune":"A WATCHED POT NEVER BOILS. YOURS BOILED OVER" +}, +{ +"id":337, +"fortune":"IGNORANCE IS BLISS. YOU SEEM SERENE" +}, +{ +"id":338, +"fortune":"BEAUTY IS IN THE EYE. GET THE EYE CHECKED" +}, +{ +"id":339, +"fortune":"IT TAKES A VILLAGE. YOURS REQUESTED BACKUP" +}, +{ +"id":340, +"fortune":"GREAT MINDS THINK ALIKE. YOURS WENT SOLO" +}, +{ +"id":341, +"fortune":"THE PROOF IS IN THE PUDDING. NO COMMENT" +}, +{ +"id":342, +"fortune":"EVERY DOG HAS ITS DAY. YOURS WAS A TUESDAY" +}, +{ +"id":343, +"fortune":"NOTHING VENTURED NOTHING GAINED. PERFECT TIE" +}, +{ +"id":344, +"fortune":"TOO MANY COOKS SPOIL BROTH. YOU SOLOED IT" +}, +{ +"id":345, +"fortune":"YOU REAP WHAT YOU SOW. INTERESTING HARVEST" +}, +{ +"id":346, +"fortune":"ASK AND RECEIVE. YOUR ASKING MADE A LINE" +}, +{ +"id":347, +"fortune":"WALK A MILE IN THEIR SHOES. RETURN THEM" +}, +{ +"id":348, +"fortune":"HASTE MAKES WASTE. YOU KEPT ONLY WASTE" +}, +{ +"id":349, +"fortune":"ALL GOOD THINGS END. YOURS END EARLY" +}, +{ +"id":350, +"fortune":"WORDS ARE CHEAP. YOURS ARE FREE" +}, +{ +"id":351, +"fortune":"TIME WAITS FOR NO ONE. IT MADE AN EXCEPTION" +}, +{ +"id":352, +"fortune":"DONT BITE THE FEEDING HAND. IT WEARS GLOVES" +}, +{ +"id":353, +"fortune":"COMPARISON STEALS JOY. YOURS HIRED IT" +}, +{ +"id":354, +"fortune":"HALF A LOAF BEATS NONE. YOU OFFER CRUMBS" +}, +{ +"id":355, +"fortune":"A SMOOTH SEA NEVER MADE A SAILOR. STAY DOCKED" +}, +{ +"id":356, +"fortune":"THE TRUTH NEEDS NO REHEARSAL. YOURS DOES" +}, +{ +"id":357, +"fortune":"EVERYTHING IN MODERATION. EVEN THAT. STOP" +}, +{ +"id":358, +"fortune":"WHAT GOES AROUND COMES AROUND. TRACK IT" +}, +{ +"id":359, +"fortune":"NEVER ARGUE WITH A FOOL. ONLOOKERS STRUGGLE" +}, +{ +"id":360, +"fortune":"WISDOM WHISPERS. YOURS IS ON MUTE" +}, +{ +"id":361, +"fortune":"A THOUSAND MILE JOURNEY STARTS. YOU STRETCH" +}, +{ +"id":362, +"fortune":"CROSS BRIDGES WHEN YOU REACH THEM. NOT BURN" +}, +{ +"id":363, +"fortune":"THE MIGHTY OAK WAS A NUT. HERITAGE PRESERVED" +}, +{ +"id":364, +"fortune":"FOOL ME ONCE. YOUR CARD HAS NINE STAMPS" +}, +{ +"id":365, +"fortune":"ADVENTURE AWAITS. IT OPENED A BUSINESS" +}, +{ +"id":366, +"fortune":"YOU WILL TRAVEL FAR. THE SHUTTLE COUNTS" +}, +{ +"id":367, +"fortune":"YOUR PASSPORT PHOTO IS THE REAL YOU" +}, +{ +"id":368, +"fortune":"YOUR BUCKET LIST NEEDS A BUCKET" +}, +{ +"id":369, +"fortune":"YOU WILL SEE THE WORLD. VIA DOCUMENTARY" +}, +{ +"id":370, +"fortune":"YOUR VACATION DAYS AGE LIKE FINE GUILT" +}, +{ +"id":371, +"fortune":"AN EXOTIC PLACE CALLED. YOU SCREENED IT" +}, +{ +"id":372, +"fortune":"YOUR ROAD TRIP PLAYLIST OUTLIVED THE TRIP" +}, +{ +"id":373, +"fortune":"YOU PACK LIGHT. EMOTIONALLY YOU DONT" +}, +{ +"id":374, +"fortune":"YOUR SOUVENIRS TELL STORIES. ABOUT PRICING" +}, +{ +"id":375, +"fortune":"YOU WILL GET LOST ABROAD. A HOME SKILL" +}, +{ +"id":376, +"fortune":"YOUR TRAVEL PICS STAR YOUR THUMB" +}, +{ +"id":377, +"fortune":"A SPONTANEOUS TRIP NEARS. WITH SPREADSHEETS" +}, +{ +"id":378, +"fortune":"YOU ARE A WORLD CITIZEN. REFERENCES PENDING" +}, +{ +"id":379, +"fortune":"YOUR BEACH BODY IS READY. THE BEACH ADAPTED" +}, +{ +"id":380, +"fortune":"THE MOUNTAINS ARE CALLING. YOU MUTED THEM" +}, +{ +"id":381, +"fortune":"YOUR STAYCATION GAME IS UNRIVALED" +}, +{ +"id":382, +"fortune":"YOU NAVIGATE LIKE A LOCAL. A LOST ONE" +}, +{ +"id":383, +"fortune":"YOUR ITINERARY IS MOSTLY BUFFER TIME" +}, +{ +"id":384, +"fortune":"YOUR HIKING BOOTS SUMMITED THE CLOSET" +}, +{ +"id":385, +"fortune":"JET LAG RESPECTS NO ONE. YOU CAME PRE TIRED" +}, +{ +"id":386, +"fortune":"YOUR TRAVEL JOURNAL HAS ONE ENTRY. EXCITED" +}, +{ +"id":387, +"fortune":"YOU COLLECT EXPERIENCES. THE CARD DISAGREES" +}, +{ +"id":388, +"fortune":"YOUR SENSE OF DIRECTION IS A FAMILY LEGEND" +}, +{ +"id":389, +"fortune":"A GETAWAY NEARS. THE LAUNDRY WILL WAIT" +}, +{ +"id":390, +"fortune":"YOU WILL RETURN CHANGED. MOSTLY YOUR SLEEP" +}, +{ +"id":391, +"fortune":"YOUR AIRPORT TIMING IS A PERSONALITY TEST" +}, +{ +"id":392, +"fortune":"YOUR WINDOW SEAT PICS FEATURE THE WING" +}, +{ +"id":393, +"fortune":"YOUR CARRY ON HAS ITS OWN GRAVITY NOW" +}, +{ +"id":394, +"fortune":"YOUR CAMPING SKILLS AMUSE THE RACCOONS" +}, +{ +"id":395, +"fortune":"TIME ZONES CONFUSE YOUR SLEEP. IT COPES" +}, +{ +"id":396, +"fortune":"YOUR HOTEL REVIEWS ARE FEARED. BY TOWELS" +}, +{ +"id":397, +"fortune":"YOU BEFRIEND STRANGERS ABROAD. ALSO LOST ONES" +}, +{ +"id":398, +"fortune":"YOUR CRUISE PACE MATCHES YOUR BUFFET PACE" +}, +{ +"id":399, +"fortune":"FORTUNE FAVORS TRAVELERS. YOURS LIKES COUCHES" +}, +{ +"id":400, +"fortune":"YOUR PHONE BATTERY OUTLASTS YOUR DRIVE" +}, +{ +"id":401, +"fortune":"YOU WILL READ THE TERMS SOMEDAY. NO" +}, +{ +"id":402, +"fortune":"YOUR SCREEN TIME REPORT SOFTENS THE NEWS" +}, +{ +"id":403, +"fortune":"YOUR LAUNDRY CHAIR EARNED TENURE" +}, +{ +"id":404, +"fortune":"YOUR ALARM HAS FIVE BACKUPS. ALL IGNORED" +}, +{ +"id":405, +"fortune":"YOUR FRIDGE HOSTS A SCIENCE EXPERIMENT" +}, +{ +"id":406, +"fortune":"THE CHECK ENGINE LIGHT IS A MOOD LIGHT NOW" +}, +{ +"id":407, +"fortune":"YOUR HOUSEPLANTS ACCEPTED THEIR FATE" +}, +{ +"id":408, +"fortune":"YOUR JUNK DRAWER OPENED A SECOND LOCATION" +}, +{ +"id":409, +"fortune":"YOUR GYM BAG IS PACKED. THAT COUNTS" +}, +{ +"id":410, +"fortune":"YOUR VOICEMAIL IS FULL. PEOPLE MOVED ON" +}, +{ +"id":411, +"fortune":"YOUR BROWSER TABS ARE AN ECOSYSTEM NOW" +}, +{ +"id":412, +"fortune":"YOUR UMBRELLA WAITS. YOU WILL FORGET IT" +}, +{ +"id":413, +"fortune":"YOUR LEFTOVERS HAD DREAMS. THEY COMPOST" +}, +{ +"id":414, +"fortune":"YOUR TO DO LIST HAS A TO DO LIST" +}, +{ +"id":415, +"fortune":"YOUR CLOSET DRESSES THREE VERSIONS OF YOU" +}, +{ +"id":416, +"fortune":"YOUR MORNING RUN IS LAMINATED. NOT RUN" +}, +{ +"id":417, +"fortune":"YOUR WIFI ROUTER SITS ON A SHRINE. CORRECT" +}, +{ +"id":418, +"fortune":"YOUR INBOX ZERO ATTEMPT IS HISTORY NOW" +}, +{ +"id":419, +"fortune":"YOUR SOCK DRAWER RUNS WITNESS PROTECTION" +}, +{ +"id":420, +"fortune":"YOUR GROCERY LIST STAYS HOME. IT KNOWS" +}, +{ +"id":421, +"fortune":"YOUR WATCHLIST OUTLIVED THREE HOBBIES" +}, +{ +"id":422, +"fortune":"YOUR DESK IS ERGONOMIC. YOUR COUCH ISNT" +}, +{ +"id":423, +"fortune":"THE PRINTER SENSES FEAR. AND UPDATES" +}, +{ +"id":424, +"fortune":"YOUR BEDTIME IS A MOTION YOU VOTE DOWN" +}, +{ +"id":425, +"fortune":"YOUR HEADPHONES TANGLE ALONE. SCIENCE WEEPS" +}, +{ +"id":426, +"fortune":"YOUR SPICE RACK IS SORTED THROUGH C" +}, +{ +"id":427, +"fortune":"YOUR BACKSEAT IS A LOST AND FOUND" +}, +{ +"id":428, +"fortune":"YOUR CART ABANDONMENT RATE MADE THE NEWS" +}, +{ +"id":429, +"fortune":"THE GOOD CABLE IS WHEREVER YOU ARENT" +}, +{ +"id":430, +"fortune":"YOUR STANDING DESK MOSTLY SITS. LIKE YOU" +}, +{ +"id":431, +"fortune":"YOUR MEAL PREP MADE ONE MEAL. SIX BOXES" +}, +{ +"id":432, +"fortune":"THE DELIVERY DRIVER WAVES AT YOU NOW" +}, +{ +"id":433, +"fortune":"YOUR AIR FRYER REPLACED THE OVEN. AND YOU" +}, +{ +"id":434, +"fortune":"YOUR BOOKMARKS ARE A HALL OF INTENTIONS" +}, +{ +"id":435, +"fortune":"YOUR SNOOZE BUTTON SHOWS GEOLOGIC WEAR" +}, +{ +"id":436, +"fortune":"YOUR REUSABLE BAGS WAIT IN THE CAR. LOYAL" +}, +{ +"id":437, +"fortune":"YOUR UPDATES HAVE SENIORITY NOW" +}, +{ +"id":438, +"fortune":"THE ICE TRAY RETURNS EMPTY. A TRADITION" +}, +{ +"id":439, +"fortune":"YOUR GARAGE STORES EVERYTHING BUT THE CAR" +}, +{ +"id":440, +"fortune":"YOUR STEP COUNTER FRAMED ITS BEST DAY" +}, +{ +"id":441, +"fortune":"YOUR FAVORITE MUG HAS PROTECTED STATUS" +}, +{ +"id":442, +"fortune":"YOUR WEATHER APP GAVE UP ON YOUR OUTFITS" +}, +{ +"id":443, +"fortune":"YOUR SCALE PRACTICES RADICAL HONESTY" +}, +{ +"id":444, +"fortune":"YOUR REMOTES COMMUTE FROM THE CUSHIONS" +}, +{ +"id":445, +"fortune":"THE HALLWAY CLOSET HOLDS YOUR TRUTH. BARELY" +}, +{ +"id":446, +"fortune":"YOU OWN EVERY BATTERY SIZE BUT THE ONE NEEDED" +}, +{ +"id":447, +"fortune":"YOUR VACUUM AVOIDS CORNERS. LIKE YOU" +}, +{ +"id":448, +"fortune":"YOUR SHOWER THOUGHTS STAY IN THE SHOWER" +}, +{ +"id":449, +"fortune":"YOUR COFFEE MAKER KNOWS YOUR MONDAY FACE" +}, +{ +"id":450, +"fortune":"YOUR FREEZER FLOOR PREDATES FRIENDSHIPS" +}, +{ +"id":451, +"fortune":"YOUR PHONE STORAGE IS ALL SCREENSHOTS" +}, +{ +"id":452, +"fortune":"YOUR BIKE TIRES EXHALE SLOWLY. LIKE A SIGH" +}, +{ +"id":453, +"fortune":"YOUR RETURNS PILE HAS A DEPARTURE LOUNGE" +}, +{ +"id":454, +"fortune":"YOUR CANDLES ARE DECOR. MATCHES AGREE" +}, +{ +"id":455, +"fortune":"YOUR DINING TABLE RETIRED FROM DINING" +}, +{ +"id":456, +"fortune":"YOUR PREDICTIVE TEXT SUGGESTS SORRY FIRST" +}, +{ +"id":457, +"fortune":"YOUR FITNESS APP USES SHORTER SENTENCES NOW" +}, +{ +"id":458, +"fortune":"YOUR COAT POCKET IS A TIME CAPSULE" +}, +{ +"id":459, +"fortune":"THE ADD 30 SECONDS BUTTON RUNS YOUR KITCHEN" +}, +{ +"id":460, +"fortune":"THE GOOD SCISSORS ARE MISSING. ONE SUSPECT" +}, +{ +"id":461, +"fortune":"YOUR DOWNLOADS FOLDER IS A LANDFILL" +}, +{ +"id":462, +"fortune":"YOUR PLANTS RUN ON GUILT BASED WATERING" +}, +{ +"id":463, +"fortune":"THE DRYER TAKES ONE SOCK AS TRIBUTE" +}, +{ +"id":464, +"fortune":"YOUR COASTERS WATCH FROM THE SIDELINES" +}, +{ +"id":465, +"fortune":"YOUR NOTES APP IS A GENIUS LANDFILL" +}, +{ +"id":466, +"fortune":"RAIN WILL WASH YOUR CAR. YOUR PLAN EXACTLY" +}, +{ +"id":467, +"fortune":"YOUR BOARD GAMES AGE IN SHRINK WRAP" +}, +{ +"id":468, +"fortune":"YOUR YOGA MAT UNROLLED TWICE. FOR NAPS" +}, +{ +"id":469, +"fortune":"YOUR EXTENSION CORDS BREED IN THE DARK" +}, +{ +"id":470, +"fortune":"YOUR SMART SPEAKER HAS HEARD THINGS" +}, +{ +"id":471, +"fortune":"YOUR LIDS AND CONTAINERS NEVER MATCH. EVER" +}, +{ +"id":472, +"fortune":"YOUR WALKING SHOES TOUR THE TRUNK" +}, +{ +"id":473, +"fortune":"YOUR IMPORTANT FOLDER IS A HOSPICE" +}, +{ +"id":474, +"fortune":"YOUR WREATH IS PERENNIAL NOW. FESTIVE" +}, +{ +"id":475, +"fortune":"THAT ONE DRAWER STOPPED EXPLAINING ITSELF" +}, +{ +"id":476, +"fortune":"YOUR SCREEN AT 2AM REVEALS EVERYTHING" +}, +{ +"id":477, +"fortune":"YOUR FIRE EXTINGUISHER RUNS ON CONFIDENCE" +}, +{ +"id":478, +"fortune":"YOUR WATER GOAL WAS MET ONCE. PLAQUE PENDING" +}, +{ +"id":479, +"fortune":"AGING SUITS YOU. THE ALTERNATIVE DIDNT" +}, +{ +"id":480, +"fortune":"YOUR AGE IS JUST A NUMBER. AN ASCENDING ONE" +}, +{ +"id":481, +"fortune":"YOUR KNEES FORECAST WEATHER NOW. A SKILL" +}, +{ +"id":482, +"fortune":"YOUR BACK WENT OUT MORE THAN YOU DID" +}, +{ +"id":483, +"fortune":"WISDOM COMES WITH GRAY HAIR. YOURS CAME EARLY" +}, +{ +"id":484, +"fortune":"YOU AGE LIKE WINE. STORED HORIZONTALLY" +}, +{ +"id":485, +"fortune":"YOUR BIRTHDAY CANDLES ARE A FIRE HAZARD" +}, +{ +"id":486, +"fortune":"YOUR READING GLASSES NEED READING GLASSES" +}, +{ +"id":487, +"fortune":"YOUR PLAYLIST IS CALLED OLDIES NOW. BY KIDS" +}, +{ +"id":488, +"fortune":"YOU GROAN WHEN SITTING. A VERBAL RECEIPT" +}, +{ +"id":489, +"fortune":"YOUR METABOLISM RETIRED WITHOUT NOTICE" +}, +{ +"id":490, +"fortune":"YOU FEEL 25. YOUR HANGOVERS DISAGREE" +}, +{ +"id":491, +"fortune":"AN APPLE A DAY. PIE IS A LOOPHOLE. SURE" +}, +{ +"id":492, +"fortune":"YOUR POSTURE IMPROVES TWICE DAILY. BRIEFLY" +}, +{ +"id":493, +"fortune":"YOU WILL SLEEP 8 HOURS. ACROSS 3 NIGHTS" +}, +{ +"id":494, +"fortune":"YOUR VITAMINS EXPIRE UNTAKEN. RENEWABLE" +}, +{ +"id":495, +"fortune":"YOUR DIET STARTS MONDAY. MONDAYS UNIONIZED" +}, +{ +"id":496, +"fortune":"YOUR DENTIST KNOWS YOU FLOSSED TODAY ONLY" +}, +{ +"id":497, +"fortune":"YOUR RESTING HEART RATE IS GREAT. FITTING" +}, +{ +"id":498, +"fortune":"YOU WILL LIVE TO 100. OUT OF SPITE" +}, +{ +"id":499, +"fortune":"YOUR HIDDEN TALENTS REMAIN WELL HIDDEN" +}, +{ +"id":500, +"fortune":"YOUR CREATIVITY HAS NO BOUNDS. OR OUTPUT" +}, +{ +"id":501, +"fortune":"YOU HAVE A GIFT. NO RECEIPT INCLUDED" +}, +{ +"id":502, +"fortune":"YOUR NOVEL WILL BE FINISHED. BY SOMEONE" +}, +{ +"id":503, +"fortune":"YOU WERE BORN TO CREATE. EXCUSES COUNT" +}, +{ +"id":504, +"fortune":"YOUR MASTERPIECE IS INSIDE YOU. COZY THERE" +}, +{ +"id":505, +"fortune":"YOUR SINGING BRINGS TEARS. BIOLOGY IS ODD" +}, +{ +"id":506, +"fortune":"THE MUSE VISITS YOU. SHE CALLS AHEAD NOW" +}, +{ +"id":507, +"fortune":"YOUR PHOTOS CAPTURE RAW CONFUSION" +}, +{ +"id":508, +"fortune":"YOU THINK OUTSIDE THE BOX. THE BOX WORRIES" +}, +{ +"id":509, +"fortune":"YOUR POETRY MOVES PEOPLE. TO THE EXITS" +}, +{ +"id":510, +"fortune":"YOUR IMAGINATION IS VAST. EXECUTION ISNT" +}, +{ +"id":511, +"fortune":"YOU SEE THE WORLD DIFFERENTLY. GET CHECKED" +}, +{ +"id":512, +"fortune":"YOUR PODCAST IDEA JOINS THE OTHER SIX" +}, +{ +"id":513, +"fortune":"CREATIVITY FLOWS PAST YOU. WAVING" +}, +{ +"id":514, +"fortune":"YOUR DOODLES BELONG SOMEWHERE. A DRAWER" +}, +{ +"id":515, +"fortune":"YOUR COOKING IS PERFORMANCE ART. WITH ALARMS" +}, +{ +"id":516, +"fortune":"YOUR SCREENPLAY HAS POTENTIAL ENERGY ONLY" +}, +{ +"id":517, +"fortune":"THE WORLD IS YOUR CANVAS. STILL PRIMED" +}, +{ +"id":518, +"fortune":"YOUR DIY PROJECTS ARE AN UNFINISHED TRILOGY" +}, +{ +"id":519, +"fortune":"YOUR MUSIC TASTE IS GREAT. NEIGHBORS DISSENT" +}, +{ +"id":520, +"fortune":"YOUR DANCE MOVES DEFY CONVENTION. AND PHYSICS" +}, +{ +"id":521, +"fortune":"YOU TELL GREAT STORIES. ENDINGS PENDING" +}, +{ +"id":522, +"fortune":"PHARMACISTS FRAME YOUR HANDWRITING" +}, +{ +"id":523, +"fortune":"YOUR IDEAS ARRIVE AT 2AM. GONE BY 8" +}, +{ +"id":524, +"fortune":"YOUR SKETCHBOOK HOLDS GREAT PROMISES" +}, +{ +"id":525, +"fortune":"YOU COULD WRITE A BOOK. A STRONG PAMPHLET" +}, +{ +"id":526, +"fortune":"YOUR POTTERY PHASE MADE MEMORABLE PIECES" +}, +{ +"id":527, +"fortune":"YOUR INNER ARTIST WANTS COFFEE FIRST" +}, +{ +"id":528, +"fortune":"YOUR COLOR CHOICES ARE COURAGEOUS" +}, +{ +"id":529, +"fortune":"YOUR GUITAR KNOWS THREE CHORDS WELL" +}, +{ +"id":530, +"fortune":"YOUR CREATIVE PROCESS IS UNDOCUMENTED" +}, +{ +"id":531, +"fortune":"YOUR SCARVES BECAME EXPERIENCES. KNIT ON" +}, +{ +"id":532, +"fortune":"YOU PAINT WITH EMOTION. THE FLOOR AGREES" +}, +{ +"id":533, +"fortune":"YOUR BONSAI IS A REGULAR TREE NOW. PATIENCE" +}, +{ +"id":534, +"fortune":"YOUR WRITING VOICE IS STRONG. IN TEXTS" +}, +{ +"id":535, +"fortune":"YOUR GARDEN GROWS. THE WEEDS THRIVE MOST" +}, +{ +"id":536, +"fortune":"YOUR CALLIGRAPHY PHASE WAS ONE WEEKEND" +}, +{ +"id":537, +"fortune":"YOUR WOODWORK IS HONEST. SPLINTERS DONT LIE" +}, +{ +"id":538, +"fortune":"YOUR FAN FICTION HAS FANS. BOTH OF THEM" +}, +{ +"id":539, +"fortune":"YOUR ORIGAMI FILLS THE RECYCLING BIN" +}, +{ +"id":540, +"fortune":"YOUR CREATIVE BLOCK IS LOAD BEARING NOW" +}, +{ +"id":541, +"fortune":"YOUR SOURDOUGH STARTER OUTLIVED THE HOBBY" +}, +{ +"id":542, +"fortune":"YOUR WATERCOLORS CAPTURE SOMETHING. UNCLEAR" +}, +{ +"id":543, +"fortune":"YOU EXCEL AT BEGINNINGS. MIDDLES DECLINED" +}, +{ +"id":544, +"fortune":"YOUR UKULELE EMBRACED ITS ROLE AS DECOR" +}, +{ +"id":545, +"fortune":"YOU ARE YOUR OWN WORST CRITIC. UNDISPUTED" +}, +{ +"id":546, +"fortune":"YOUR ART SPEAKS FOR ITSELF. QUIETLY" +}, +{ +"id":547, +"fortune":"YOUR STUDIO IS A CLOSET AGAIN. FUNCTIONAL" +}, +{ +"id":548, +"fortune":"YOUR FIRST DRAFTS NEVER MEET SECOND ONES" +}, +{ +"id":549, +"fortune":"YOUR CANDLE PHASE LEFT EVIDENCE. WAXY" +}, +{ +"id":550, +"fortune":"YOUR OPEN MIC DEBUT REMAINS IMMINENT" +}, +{ +"id":551, +"fortune":"YOUR PLAYLIST TRANSITIONS ARE ART. SHUFFLED" +}, +{ +"id":552, +"fortune":"YOUR COSTUMES SHOW COMMITMENT. TO CONCEPTS" +}, +{ +"id":553, +"fortune":"YOUR MACRAME OWL HAS SEEN YOUR PROJECTS" +}, +{ +"id":554, +"fortune":"YOUR VOICE MEMOS ARE MOSTLY GROCERIES" +}, +{ +"id":555, +"fortune":"YOU WILL BE DISCOVERED. ON YOUR COUCH" +}, +{ +"id":556, +"fortune":"YOUR MOOD BOARD IS ALL MOOD. NO BOARD" +}, +{ +"id":557, +"fortune":"YOUR FREESTYLE RAPS SHOULD STAY FREE" +}, +{ +"id":558, +"fortune":"YOUR MUSEUM VISITS END AT THE CAFE" +}, +{ +"id":559, +"fortune":"YOUR COVERS BEAT THE ORIGINALS. UNNOTIFIED" +}, +{ +"id":560, +"fortune":"YOUR TYPEWRITER LOOKS GREAT. STILL" +}, +{ +"id":561, +"fortune":"YOUR ABSTRACT PHASE CONFUSED EVEN YOU" +}, +{ +"id":562, +"fortune":"YOUR COMIC HAS A FOLLOWER. HI MOM" +}, +{ +"id":563, +"fortune":"YOUR SONG RHYMES ORANGE WITH DOOR HINGE" +}, +{ +"id":564, +"fortune":"YOUR CRAFT CORNER ANNEXED THE GUEST ROOM" +}, +{ +"id":565, +"fortune":"YOUR GENIUS IS MISUNDERSTOOD. AND UNSEEN" +}, +{ +"id":566, +"fortune":"YOUR MAGNUM OPUS IS BASICALLY ESPRESSO NOW" +}, +{ +"id":567, +"fortune":"YOUR INSPIRATION FOLDER HAS SUBFOLDERS" +}, +{ +"id":568, +"fortune":"THE FORTUNE YOU SEEK IS IN ANOTHER COOKIE" +}, +{ +"id":569, +"fortune":"THIS COOKIE CONTAINS NO FORTUNE. JUST CARBS" +}, +{ +"id":570, +"fortune":"IT IS DARK INSIDE THIS COOKIE. AND QUIET" +}, +{ +"id":571, +"fortune":"THIS FORTUNE LEFT INTENTIONALLY BLANK" +}, +{ +"id":572, +"fortune":"CONFUCIUS SAYS NOTHING ABOUT YOU. WISELY" +}, +{ +"id":573, +"fortune":"THIS FORTUNE WAS RECALLED. SAME TO YOU" +}, +{ +"id":574, +"fortune":"YOU WILL READ THIS ALOUD. UNPROMPTED" +}, +{ +"id":575, +"fortune":"THE COOKIE KNEW YOU WOULD TAKE THE LAST ONE" +}, +{ +"id":576, +"fortune":"SOME GET WISDOM. YOU CHOSE DESSERT" +}, +{ +"id":577, +"fortune":"THIS IS A COOKIE. NOT A LIFE COACH" +}, +{ +"id":578, +"fortune":"YOUR LUCKY NUMBERS HAVE OPTED OUT" +}, +{ +"id":579, +"fortune":"THE COOKIE ADMIRES YOUR OPTIMISM" +}, +{ +"id":580, +"fortune":"YOU SHOOK THE COOKIE FIRST. IT FELT THAT" +}, +{ +"id":581, +"fortune":"ASK AGAIN LATER. OR DONT. YOUR CALL" +}, +{ +"id":582, +"fortune":"THIS COOKIE WAS BAKED WITH LOW EXPECTATIONS" +}, +{ +"id":583, +"fortune":"THE FORTUNE WRITER IS ON BREAK. JUDGING" +}, +{ +"id":584, +"fortune":"NO WISDOM TODAY. INVENTORY ISSUES" +}, +{ +"id":585, +"fortune":"YOUR FORTUNE IS PENDING APPROVAL" +}, +{ +"id":586, +"fortune":"THIS MESSAGE WILL SELF DISAPPOINT" +}, +{ +"id":587, +"fortune":"THE COOKIE CRUMBLES. SO DO PLANS" +}, +{ +"id":588, +"fortune":"YOU EXPECTED MORE FROM A COOKIE. NOTED" +}, +{ +"id":589, +"fortune":"FORTUNE NOT FOUND. TRY ANOTHER DINNER" +}, +{ +"id":590, +"fortune":"THIS COOKIE HAS SEEN YOUR ORDER HISTORY" +}, +{ +"id":591, +"fortune":"THE LAST COOKIE ALWAYS TELLS THE TRUTH" +}, +{ +"id":592, +"fortune":"YOUR FORTUNE RAN OUT OF CHARACTERS. LUCKY" +}, +{ +"id":593, +"fortune":"A WISE MAN ONCE SAID NOTHING. TRY IT" +}, +{ +"id":594, +"fortune":"THE COOKIE BELIEVED IN YOU. PAST TENSE" +}, +{ +"id":595, +"fortune":"THIS FORTUNE IS ACCURATE. DETAILS REDACTED" +}, +{ +"id":596, +"fortune":"YOU HOLD THE KEY TO HAPPINESS. NOW FIND IT" +}, +{ +"id":597, +"fortune":"YOUR FORTUNE IS BEING WRITTEN. SLOWLY" +}, +{ +"id":598, +"fortune":"YOUR COFFEE ORDER IS A PARAGRAPH NOW" +}, +{ +"id":599, +"fortune":"COFFEE FIRST. PERSONALITY SECOND. FAIR" +}, +{ +"id":600, +"fortune":"YOUR FOURTH COFFEE IS WATCHING YOU" +}, +{ +"id":601, +"fortune":"DECAF EXISTS. YOU PRETEND IT DOESNT" +}, +{ +"id":602, +"fortune":"YOUR MUG COLLECTION OUTGREW THE CABINET" +}, +{ +"id":603, +"fortune":"YOUR ESPRESSO MACHINE JUDGES YOUR PODS" +}, +{ +"id":604, +"fortune":"YOU CALL IT BRUNCH. IT WAS BREAKFAST. LATE" +}, +{ +"id":605, +"fortune":"YOUR SALAD PHASE ENDED AT THE DRESSING" +}, +{ +"id":606, +"fortune":"YOU MEAL PLAN BEAUTIFULLY. THEN ORDER OUT" +}, +{ +"id":607, +"fortune":"YOUR SPICE TOLERANCE IS A PERSONALITY. SADLY" +}, +{ +"id":608, +"fortune":"YOUR SIGNATURE DISH HAS ONE FAN. YOU" +}, +{ +"id":609, +"fortune":"THE OVEN TIMER IS A SUGGESTION TO YOU" +}, +{ +"id":610, +"fortune":"YOUR SMOOTHIES HIDE VEGETABLES. FROM YOU" +}, +{ +"id":611, +"fortune":"YOU COOK WITH LOVE. AND TOO MUCH SALT" +}, +{ +"id":612, +"fortune":"YOUR RECIPE SUBSTITUTIONS ARE BRAVE" +}, +{ +"id":613, +"fortune":"GARLIC MEASURED WITH YOUR HEART. AGAIN" +}, +{ +"id":614, +"fortune":"YOUR TOAST GAME IS STRONG. LITERALLY BURNT" +}, +{ +"id":615, +"fortune":"YOU EAT DESSERT FIRST. LIFE IS SHORT. SURE" +}, +{ +"id":616, +"fortune":"YOUR SNACK DRAWER HAS A WAITING LIST" +}, +{ +"id":617, +"fortune":"THE FOOD DELIVERY APP KNOWS YOU BY NAME" +}, +{ +"id":618, +"fortune":"YOUR KITCHEN GADGETS FORM A MUSEUM" +}, +{ +"id":619, +"fortune":"YOUR TAKEOUT CONTAINERS ARE HEIRLOOMS NOW" +}, +{ +"id":620, +"fortune":"YOU SAY YOU COULD EAT ANYTHING. THEN DONT" +}, +{ +"id":621, +"fortune":"YOUR PIZZA TOPPINGS HAVE CRITICS" +}, +{ +"id":622, +"fortune":"YOUR DOG LOVES YOU UNCONDITIONALLY. TRAINED" +}, +{ +"id":623, +"fortune":"YOUR CAT TOLERATES YOU. A HIGH HONOR" +}, +{ +"id":624, +"fortune":"YOUR PET EATS BETTER THAN YOU DO" +}, +{ +"id":625, +"fortune":"YOUR DOG HAS MORE FRIENDS THAN YOU" +}, +{ +"id":626, +"fortune":"THE CAT KNOCKED IT OVER ON PURPOSE. RESPECT" +}, +{ +"id":627, +"fortune":"YOUR FISH KNOWS YOUR SCHEDULE. IT WORRIES" +}, +{ +"id":628, +"fortune":"YOUR PETS INSTAGRAM OUTPERFORMS YOURS" +}, +{ +"id":629, +"fortune":"THE DOG ATE YOUR MOTIVATION. NOT HOMEWORK" +}, +{ +"id":630, +"fortune":"YOUR CAT REVIEWS YOUR LIFE CHOICES NIGHTLY" +}, +{ +"id":631, +"fortune":"YOUR BIRD REPEATS YOUR WORST PHRASES" +}, +{ +"id":632, +"fortune":"YOUR PET THERAPIST IS A GOOD LISTENER. FREE" +}, +{ +"id":633, +"fortune":"RAIN IS COMING. YOUR CAR WASH REJOICES" +}, +{ +"id":634, +"fortune":"YOUR WINTER COAT DEBATE STARTS IN OCTOBER" +}, +{ +"id":635, +"fortune":"YOU CHECK THE WEATHER. THEN DRESS WRONG" +}, +{ +"id":636, +"fortune":"SPRING IS COMING. SO ARE YOUR ALLERGIES" +}, +{ +"id":637, +"fortune":"YOUR SUNSCREEN EXPIRES UNUSED. YEARLY" +}, +{ +"id":638, +"fortune":"SNOW IS MAGICAL. UNTIL YOU OWN A SHOVEL" +}, +{ +"id":639, +"fortune":"YOUR UMBRELLA COLLECTION LIVES ELSEWHERE" +}, +{ +"id":640, +"fortune":"SMALL TALK ABOUT WEATHER SUITS YOU NOW" +}, +{ +"id":641, +"fortune":"THE FORECAST CHANGED. YOUR PLANS DIDNT EXIST" +}, +{ +"id":642, +"fortune":"YOUR PATIO FURNITURE FEARS EVERY SEASON" +}, +{ +"id":643, +"fortune":"AUTUMN LEAVES FALL. LIKE YOUR RESOLUTIONS" +}, +{ +"id":644, +"fortune":"YOUR PASSWORD IS YOUR PETS NAME. WE KNOW" +}, +{ +"id":645, +"fortune":"YOU HAVE 47 UNREAD UPDATES. THEY WAIT" +}, +{ +"id":646, +"fortune":"YOUR CLOUD STORAGE IS FULL. OF DUPLICATES" +}, +{ +"id":647, +"fortune":"AUTOCORRECT KNOWS WHAT YOU MEANT. IT DECLINED" +}, +{ +"id":648, +"fortune":"YOUR ROUTER NEEDS A RESTART. LIKE YOU" +}, +{ +"id":649, +"fortune":"YOUR SEARCH HISTORY TELLS A SAGA" +}, +{ +"id":650, +"fortune":"YOU CLOSED ONE TAB TODAY. PROGRESS" +}, +{ +"id":651, +"fortune":"YOUR SPAM FOLDER MISSES YOU" +}, +{ +"id":652, +"fortune":"THE ALGORITHM KNOWS YOU TOO WELL. IT WORRIES" +}, +{ +"id":653, +"fortune":"YOUR SMARTWATCH SUGGESTS BREATHING. POINTED" +}, +{ +"id":654, +"fortune":"YOUR TYPING SPEED PEAKS DURING ARGUMENTS" +}, +{ +"id":655, +"fortune":"YOU MUTED THE GROUP. THE GROUP KNOWS" +}, +{ +"id":656, +"fortune":"YOUR CAPS LOCK GETS REGULAR EXERCISE" +}, +{ +"id":657, +"fortune":"YOUR EMOJI USE HAS BEEN DISCUSSED" +}, +{ +"id":658, +"fortune":"YOUR WIFI NAME IS YOUR BEST JOKE" +}, +{ +"id":659, +"fortune":"YOU RESTART THINGS AS A LIFESTYLE" +}, +{ +"id":660, +"fortune":"YOUR GYM MEMBERSHIP IS A DONATION NOW" +}, +{ +"id":661, +"fortune":"YOUR MARATHON PLAN IS SAVED. AND SAFE" +}, +{ +"id":662, +"fortune":"LEG DAY RESPECTS YOUR ABSENCE" +}, +{ +"id":663, +"fortune":"YOUR PROTEIN POWDER AGES GRACEFULLY" +}, +{ +"id":664, +"fortune":"YOU STRETCH ONCE A YEAR. AMBITIOUSLY" +}, +{ +"id":665, +"fortune":"YOUR SPORTS TEAM LOSES. YOU TAKE CREDIT" +}, +{ +"id":666, +"fortune":"YOUR FANTASY TEAM IS FANTASY INDEED" +}, +{ +"id":667, +"fortune":"YOU RUN LATE. IT COUNTS AS CARDIO" +}, +{ +"id":668, +"fortune":"YOUR HOME GYM STORES THE LAUNDRY NOW" +}, +{ +"id":669, +"fortune":"YOUR PERSONAL BEST WAS A GROUP EFFORT" +}, +{ +"id":670, +"fortune":"YOUR WARM UP IS THE WHOLE WORKOUT" +}, +{ +"id":671, +"fortune":"YOUR REST DAYS HAVE REST DAYS" +}, +{ +"id":672, +"fortune":"SLEEP IS FOR THE STRONG. YOU SCROLL" +}, +{ +"id":673, +"fortune":"YOUR NAP SCHEDULE IS YOUR REAL CALENDAR" +}, +{ +"id":674, +"fortune":"YOU COUNT SHEEP. THEY COUNT YOUR EXCUSES" +}, +{ +"id":675, +"fortune":"YOUR PILLOW KNOWS ALL YOUR SECRETS" +}, +{ +"id":676, +"fortune":"ONE MORE EPISODE WAS FOUR EPISODES" +}, +{ +"id":677, +"fortune":"YOUR DREAMS ARE VIVID. YOUR MORNINGS ARENT" +}, +{ +"id":678, +"fortune":"YOU SET SIX ALARMS. YOU FEAR NONE" +}, +{ +"id":679, +"fortune":"BEDTIME PROCRASTINATION IS STILL A CHOICE" +}, +{ +"id":680, +"fortune":"YOUR WEEKEND SLEEP DEBT COLLECTS INTEREST" +}, +{ +"id":681, +"fortune":"YOUR FAMILY LOVES YOU. THEY VOTED. CLOSE ONE" +}, +{ +"id":682, +"fortune":"YOUR MOTHER WAS RIGHT. ABOUT EVERYTHING" +}, +{ +"id":683, +"fortune":"FAMILY DINNERS TEST YOUR DIPLOMACY" +}, +{ +"id":684, +"fortune":"YOUR SIBLING REMEMBERS EVERYTHING. FOREVER" +}, +{ +"id":685, +"fortune":"YOUR RELATIVES ASK ABOUT YOUR PLANS. BRACE" +}, +{ +"id":686, +"fortune":"YOU ARE THE FAVORITE CHILD. UNCONFIRMED" +}, +{ +"id":687, +"fortune":"YOUR FAMILY GROUP CHAT NEVER SLEEPS" +}, +{ +"id":688, +"fortune":"GRANDMA FEEDS YOU. RESISTANCE IS FUTILE" +}, +{ +"id":689, +"fortune":"YOUR DAD JOKES SKIPPED NO GENERATIONS" +}, +{ +"id":690, +"fortune":"THANKSGIVING SEATING IS POLITICAL. YOURS IS" +}, +{ +"id":691, +"fortune":"YOUR NEIGHBOR WAVES. THE FENCE WAS HIS IDEA" +}, +{ +"id":692, +"fortune":"THE HOA NOTICED YOUR LAWN. AGAIN" +}, +{ +"id":693, +"fortune":"YOUR NEIGHBORS KNOW YOUR SCHEDULE. AND MORE" +}, +{ +"id":694, +"fortune":"BORROWED TOOLS BECOME GIFTS EVENTUALLY" +}, +{ +"id":695, +"fortune":"YOUR DOORBELL CAMERA SAW YOUR BEST SPRINT" +}, +{ +"id":696, +"fortune":"YOUR COMMUTE BUILDS CHARACTER. DAILY" +}, +{ +"id":697, +"fortune":"TRAFFIC IS NOT PERSONAL. YOURS FEELS PERSONAL" +}, +{ +"id":698, +"fortune":"YOUR PARKING JOB SPARKED CONVERSATIONS" +}, +{ +"id":699, +"fortune":"YOUR GPS SAYS RECALCULATING. WITH FEELING" +}, +{ +"id":700, +"fortune":"YOU DRIVE LIKE YOU LIVE. SIGNALS OPTIONAL" +}, +{ +"id":701, +"fortune":"YOUR GAS TANK RUNS ON FAITH. AND FUMES" +}, +{ +"id":702, +"fortune":"THE LEFT LANE IS FOR PASSING. JUST SAYING" +}, +{ +"id":703, +"fortune":"YOUR CAR PLAYLIST PEAKED IN 2016" +}, +{ +"id":704, +"fortune":"YOUR HORN GETS MORE USE THAN YOUR BLINKER" +}, +{ +"id":705, +"fortune":"ROAD RAGE IS BENEATH YOU. USUALLY" +}, +{ +"id":706, +"fortune":"YOUR PARALLEL PARKING DRAWS AUDIENCES" +}, +{ +"id":707, +"fortune":"TIME FLIES. YOURS FILED A FLIGHT PLAN" +}, +{ +"id":708, +"fortune":"PROCRASTINATION IS AN ART. YOU TEACH IT" +}, +{ +"id":709, +"fortune":"YOU WILL DO IT TOMORROW. TOMORROW KNOWS" +}, +{ +"id":710, +"fortune":"DEADLINES APPROACH. YOU ADMIRE THE VIEW" +}, +{ +"id":711, +"fortune":"YOUR FIVE MINUTES IS EVERYONE ELSES HOUR" +}, +{ +"id":712, +"fortune":"LATER IS YOUR FAVORITE TIME ZONE" +}, +{ +"id":713, +"fortune":"YOU FINISH THINGS. EVENTUALLY IS A WORD" +}, +{ +"id":714, +"fortune":"YOUR SOMEDAY LIST NEEDS ITS OWN SHELF" +}, +{ +"id":715, +"fortune":"TIME MANAGEMENT IS A SKILL. NOTED" +}, +{ +"id":716, +"fortune":"YOU ARE EARLY ONCE A YEAR. IT CONFUSES ALL" +}, +{ +"id":717, +"fortune":"THE PRESENT IS A GIFT. YOU CHOSE STORE CREDIT" +}, +{ +"id":718, +"fortune":"YOUR PUNCTUALITY IS LEGEND. THE BAD KIND" +}, +{ +"id":719, +"fortune":"YOU READ THIS INSTEAD OF WORKING. NOTED" +}, +{ +"id":720, +"fortune":"YOUR ATTENTION SPAN IS. OH LOOK A BIRD" +}, +{ +"id":721, +"fortune":"JUST ONE MORE MINUTE. THE FAMOUS LAST WORDS" +}, +{ +"id":722, +"fortune":"YOUR EMAILS START WITH SORRY FOR THE DELAY" +}, +{ +"id":723, +"fortune":"YOU REPLY TO TEXTS IN GEOLOGIC TIME" +}, +{ +"id":724, +"fortune":"YOUR READ RECEIPTS INCRIMINATE YOU" +}, +{ +"id":725, +"fortune":"YOU LEFT THEM ON READ. THEY NOTICED" +}, +{ +"id":726, +"fortune":"YOUR TYPING BUBBLE APPEARED. THEN LEFT" +}, +{ +"id":727, +"fortune":"YOU SAID LETS CIRCLE BACK. YOU WONT" +}, +{ +"id":728, +"fortune":"PER YOUR LAST EMAIL. YOU KNOW WHAT YOU DID" +}, +{ +"id":729, +"fortune":"YOUR VOICE NOTES ARE PODCASTS NOW" +}, +{ +"id":730, +"fortune":"YOU WAVED AT SOMEONE WAVING ELSEWHERE. AGAIN" +}, +{ +"id":731, +"fortune":"YOUR SMALL TALK HAS A SCRIPT. REHEARSE" +}, +{ +"id":732, +"fortune":"YOU SAID WE SHOULD HANG OUT SOON. CLASSIC" +}, +{ +"id":733, +"fortune":"YOUR PHONE IS ON SILENT. FOR YEARS NOW" +}, +{ +"id":734, +"fortune":"YOU SCREENSHOT INSTEAD OF RESPONDING" +}, +{ +"id":735, +"fortune":"YOUR OPINION WAS NOTED. AND RECYCLED" +}, +{ +"id":736, +"fortune":"YOU INTERRUPT WITH ENTHUSIASM. OFTEN" +}, +{ +"id":737, +"fortune":"RETAIL THERAPY IS STILL THERAPY. INVOICED" +}, +{ +"id":738, +"fortune":"YOUR PACKAGE ARRIVES TODAY. YOU ORDERED MORE" +}, +{ +"id":739, +"fortune":"YOU CAME FOR ONE ITEM. YOU LEFT WITH NINE" +}, +{ +"id":740, +"fortune":"YOUR WISHLIST IS A NOVEL NOW" +}, +{ +"id":741, +"fortune":"FREE SHIPPING COSTS YOU PLENTY" +}, +{ +"id":742, +"fortune":"YOUR RETURNS WINDOW CLOSED. LIKE ALWAYS" +}, +{ +"id":743, +"fortune":"YOU COMPARE PRICES. AFTER BUYING" +}, +{ +"id":744, +"fortune":"THE SALE ENDED. YOUR REGRET BEGINS" +}, +{ +"id":745, +"fortune":"YOUR LOYALTY CARD OUTRANKS YOUR GYM CARD" +}, +{ +"id":746, +"fortune":"YOU BUY ORGANIZERS TO ORGANIZE ORGANIZERS" +}, +{ +"id":747, +"fortune":"YOUR HOME PROJECT IS 90 PERCENT DONE. FOREVER" +}, +{ +"id":748, +"fortune":"THE PAINT SAMPLES OUTLIVED THE PROJECT" +}, +{ +"id":749, +"fortune":"YOUR TOOLBOX HAS EVERYTHING BUT ANSWERS" +}, +{ +"id":750, +"fortune":"YOU MEASURED ONCE. IT SHOWS" +}, +{ +"id":751, +"fortune":"THE INSTRUCTIONS WERE OPTIONAL. APPARENTLY" +}, +{ +"id":752, +"fortune":"YOUR SHELF LEANS. YOU CALL IT CHARACTER" +}, +{ +"id":753, +"fortune":"DUCT TAPE IS NOT A PHILOSOPHY. YOURS IS" +}, +{ +"id":754, +"fortune":"YOUR SMART BULBS OUTSMART YOU NIGHTLY" +}, +{ +"id":755, +"fortune":"THE LEAK WAITED FOR THE LONG WEEKEND" +}, +{ +"id":756, +"fortune":"YOUR GAMING BACKLOG IS A SECOND CAREER" +}, +{ +"id":757, +"fortune":"ONE MORE LEVEL WAS THREE HOURS AGO" +}, +{ +"id":758, +"fortune":"YOUR CHARACTER HAS BETTER GEAR THAN YOU" +}, +{ +"id":759, +"fortune":"YOU PAUSED AN ONLINE GAME. BOLD MOVE" +}, +{ +"id":760, +"fortune":"YOUR HIGH SCORE IS SOMEONE ELSES WARMUP" +}, +{ +"id":761, +"fortune":"YOU RAGE QUIT GRACEFULLY. ALMOST" +}, +{ +"id":762, +"fortune":"YOUR BOOKSHELF JUDGES YOUR SCREEN TIME" +}, +{ +"id":763, +"fortune":"YOU BUY BOOKS FASTER THAN YOU READ THEM" +}, +{ +"id":764, +"fortune":"YOUR BOOKMARK HASNT MOVED SINCE MARCH" +}, +{ +"id":765, +"fortune":"YOU JOINED A BOOK CLUB FOR THE SNACKS" +}, +{ +"id":766, +"fortune":"THE AUDIOBOOK COUNTS. NOBODY ASKED" +}, +{ +"id":767, +"fortune":"YOUR TO BE READ PILE IS FURNITURE NOW" +}, +{ +"id":768, +"fortune":"YOU QUOTE BOOKS YOU HAVENT FINISHED" +}, +{ +"id":769, +"fortune":"YOUR KARAOKE SONG CLEARED THE ROOM. TWICE" +}, +{ +"id":770, +"fortune":"YOU SING IN THE CAR. THE CAR ENDURES" +}, +{ +"id":771, +"fortune":"YOUR SHOWER CONCERTS HAVE NO REFUNDS" +}, +{ +"id":772, +"fortune":"YOUR AIR GUITAR NEEDS TUNING SOMEHOW" +}, +{ +"id":773, +"fortune":"YOUR PLANT NAMES OUTNUMBER YOUR PLANTS" +}, +{ +"id":774, +"fortune":"YOU TALK TO YOUR PLANTS. THEY HEARD THAT" +}, +{ +"id":775, +"fortune":"YOUR SUCCULENT DIED. IMPRESSIVELY RARE" +}, +{ +"id":776, +"fortune":"THE FERN SAW EVERYTHING. IT STAYS QUIET" +}, +{ +"id":777, +"fortune":"YOUR GREEN THUMB IS MORE OF A BEIGE" +}, +{ +"id":778, +"fortune":"LUCK IS A LADDER. YOU BROUGHT A STOOL" +}, +{ +"id":779, +"fortune":"FORTUNE ARRIVES UNANNOUNCED. SO DO YOU" +}, +{ +"id":780, +"fortune":"YOUR CHARISMA WORKS OFFLINE ONLY" +}, +{ +"id":781, +"fortune":"YOU ARE A LIMITED EDITION. VERY LIMITED" +}, +{ +"id":782, +"fortune":"YOUR VIBE ATTRACTS YOUR TRIBE. EXPLAINS IT" +}, +{ +"id":783, +"fortune":"NORMAL IS A SETTING. YOURS IS CUSTOM" +}, +{ +"id":784, +"fortune":"NOT LAZY. JUST IN ENERGY SAVER MODE" +}, +{ +"id":785, +"fortune":"YOUR SARCASM IS A SECOND LANGUAGE. FLUENT" +}, +{ +"id":786, +"fortune":"YOU ARE TECHNICALLY CORRECT. THE WORST KIND" +}, +{ +"id":787, +"fortune":"YOUR EXCUSES SHOW REAL CRAFTSMANSHIP" +}, +{ +"id":788, +"fortune":"YOU PEAKED. THE TIMING WAS UNFORTUNATE" +}, +{ +"id":789, +"fortune":"YOUR COMFORT SHOW IS ON ITS NINTH RERUN" +}, +{ +"id":790, +"fortune":"YOU ARE DOING YOUR BEST. THE BEST VARIES" +}, +{ +"id":791, +"fortune":"YOUR POTENTIAL CALLED. IT LEFT NO MESSAGE" +}, +{ +"id":792, +"fortune":"YOU ARE SPECIAL. LIKE EVERYONE ELSE" +}, +{ +"id":793, +"fortune":"YOUR ENERGY TODAY IS DECORATIVE" +}, +{ +"id":794, +"fortune":"YOU ARE THE MAIN CHARACTER. OF A SIDE QUEST" +}, +{ +"id":795, +"fortune":"YOUR PLOT ARMOR HAS EXPIRED. WALK CAREFULLY" +}, +{ +"id":796, +"fortune":"YOUR REDEMPTION ARC IS IN PREPRODUCTION" +}, +{ +"id":797, +"fortune":"A COMPLIMENT NEARS. ACCEPT IT AWKWARDLY" +}, +{ +"id":798, +"fortune":"YOUR RESTING FACE HAS A REPUTATION" +}, +{ +"id":799, +"fortune":"YOU AGREED TO DISAGREE. YOU LOST" +}, +{ +"id":800, +"fortune":"YOUR EYE ROLL IS AUDIBLE SOMEHOW" +}, +{ +"id":801, +"fortune":"YOUR SIGH SPOKE PARAGRAPHS JUST NOW" +}, +{ +"id":802, +"fortune":"YOU SAID FINE. NOTHING WAS FINE" +}, +{ +"id":803, +"fortune":"YOUR THUMBS UP ENDED THREE FRIENDSHIPS" +}, +{ +"id":804, +"fortune":"NO WORRIES YOU SAID. WORRYING INTENSELY" +}, +{ +"id":805, +"fortune":"YOUR SHRUG IS DOING HEAVY LIFTING TODAY" +}, +{ +"id":806, +"fortune":"YOU SMILED THROUGH THAT. BARELY. WE SAW" +}, +{ +"id":807, +"fortune":"YOUR PATIENCE HAS A LOADING BAR. STUCK" +}, +{ +"id":808, +"fortune":"YOU FORGAVE THEM. THE SPREADSHEET REMAINS" +}, +{ +"id":809, +"fortune":"YOUR HINTS NEED SUBTITLES. STILL" +}, +{ +"id":810, +"fortune":"YOU ALMOST SAID IT. THE ALMOST WAS LOUD" +}, +{ +"id":811, +"fortune":"YOUR SILENCE HAS AN AGENDA. AS USUAL" +}, +{ +"id":812, +"fortune":"YOU LET IT GO. INTO YOUR PERMANENT RECORD" +}, +{ +"id":813, +"fortune":"YOUR CALM IS SUSPICIOUS. EVERYONE AGREES" +}, +{ +"id":814, +"fortune":"YOU TOOK THE HIGH ROAD. WITH COMMENTARY" +}, +{ +"id":815, +"fortune":"YOUR FEEDBACK SANDWICH IS ALL BREAD" +}, +{ +"id":816, +"fortune":"YOU HEARD THEM OUT. THEN PROCEEDED ANYWAY" +}, +{ +"id":817, +"fortune":"GOOD MORNING. THE BAR IS ALREADY MET" +}, +{ +"id":818, +"fortune":"YOUR OPTIMISM IS ADORABLE TODAY" +}, +{ +"id":819, +"fortune":"TODAY IS YOUR DAY. SHARE IT NICELY" +}, +{ +"id":820, +"fortune":"YOU WOKE UP LIKE THIS. CONDOLENCES" +}, +{ +"id":821, +"fortune":"THE MIRROR SAW YOU FIRST. IT COPES" +}, +{ +"id":822, +"fortune":"YOUR BEDHEAD HAS ARCHITECTURE NOW" +}, +{ +"id":823, +"fortune":"MONDAY SAW YOU COMING. IT PREPARED" +}, +{ +"id":824, +"fortune":"YOUR WEEKEND ENDED. IT SENDS REGARDS" +}, +{ +"id":825, +"fortune":"FRIDAY LOVES YOU. THE FEELING IS MUTUAL" +}, +{ +"id":826, +"fortune":"YOUR LUNCH BREAK DESERVES A MEDAL. SO LONG" +}, +{ +"id":827, +"fortune":"HUMP DAY IS A STATE OF MIND. YOURS DAILY" +}, +{ +"id":828, +"fortune":"YOUR OUTFIT SAYS EFFORT. THE CLOCK SAYS RUSH" +}, +{ +"id":829, +"fortune":"YOUR HAIR HAS PLANS OF ITS OWN TODAY" +}, +{ +"id":830, +"fortune":"YOU MATCHED YOUR SOCKS. A CEREMONY IS DUE" +}, +{ +"id":831, +"fortune":"YOUR SUNGLASSES HIDE NOTHING. NICE TRY" +}, +{ +"id":832, +"fortune":"DRESS FOR THE JOB YOU WANT. A NAP TESTER" +}, +{ +"id":833, +"fortune":"YOUR IRONING BOARD RETIRED YEARS AGO" +}, +{ +"id":834, +"fortune":"WRINKLES ARE A TEXTURE. YOU DECIDED" +}, +{ +"id":835, +"fortune":"YOUR FASHION CYCLE IS LAUNDRY BASED" +}, +{ +"id":836, +"fortune":"YOUR SHOES MATCH. THE OCCASION DOESNT" +}, +{ +"id":837, +"fortune":"YOU WILL WIN AN ARGUMENT. IN THE SHOWER" +}, +{ +"id":838, +"fortune":"YOUR COMEBACK ARRIVED. THREE DAYS LATE" +}, +{ +"id":839, +"fortune":"YOUR WIT IS QUICK. YOUR TIMING ISNT" +}, +{ +"id":840, +"fortune":"YOU HAD THE PERFECT REPLY. AT MIDNIGHT" +}, +{ +"id":841, +"fortune":"YOUR INNER MONOLOGUE NEEDS AN EDITOR" +}, +{ +"id":842, +"fortune":"YOUR HOT TAKES ARE ROOM TEMPERATURE" +}, +{ +"id":843, +"fortune":"YOUR OPINIONS ARRIVE PRE HEATED" +}, +{ +"id":844, +"fortune":"YOUR DEBATE STYLE IS VOLUME BASED" +}, +{ +"id":845, +"fortune":"YOU FACT CHECK OTHERS. NEVER YOURSELF" +}, +{ +"id":846, +"fortune":"YOUR TRIVIA KNOWLEDGE PEAKS AT PARTIES ONLY" +}, +{ +"id":847, +"fortune":"A DECISION AWAITS. FLIP A COIN. IGNORE IT" +}, +{ +"id":848, +"fortune":"YOUR PRO CON LIST HAS ONE COLUMN" +}, +{ +"id":849, +"fortune":"YOU ASKED EVERYONE. THEN DID YOUR THING" +}, +{ +"id":850, +"fortune":"YOUR GUT FEELING NEEDS A SECOND OPINION" +}, +{ +"id":851, +"fortune":"YOU SLEPT ON IT. THE PROBLEM STAYED AWAKE" +}, +{ +"id":852, +"fortune":"YOUR PLAN B IS PLAN A WITH REGRET" +}, +{ +"id":853, +"fortune":"YOU CHOSE CHAOS. AGAIN. CONSISTENTLY" +}, +{ +"id":854, +"fortune":"YOUR BACKUP PLAN NEEDS A BACKUP PLAN" +}, +{ +"id":855, +"fortune":"COMMITMENT SCARES YOU. SO DOES THE MENU" +}, +{ +"id":856, +"fortune":"YOU ORDERED THE SAME THING. ADVENTUROUS" +}, +{ +"id":857, +"fortune":"CHANGE IS COMING. YOU WILL RESIST IT" +}, +{ +"id":858, +"fortune":"A SURPRISE AWAITS. YOU HATE SURPRISES" +}, +{ +"id":859, +"fortune":"GOOD NEWS COMES IN THREES. YOURS CARPOOLS" +}, +{ +"id":860, +"fortune":"YOUR HOROSCOPE QUIT. IT CITED BURNOUT" +}, +{ +"id":861, +"fortune":"MERCURY IS IN RETROGRADE. CONVENIENT EXCUSE" +}, +{ +"id":862, +"fortune":"YOUR TAROT READER NEEDED A BREAK AFTER YOU" +}, +{ +"id":863, +"fortune":"THE MAGIC 8 BALL SAID ASK SOMEONE ELSE" +}, +{ +"id":864, +"fortune":"YOUR PALM READER REQUESTED GLOVES" +}, +{ +"id":865, +"fortune":"THE CRYSTAL BALL FOGGED UP. ON PURPOSE" +}, +{ +"id":866, +"fortune":"YOUR TEA LEAVES SPELLED LUCK. SARCASTICALLY" +}, +{ +"id":867, +"fortune":"YOUR VISION QUEST FOUND THE COUCH" +}, +{ +"id":868, +"fortune":"THE UNIVERSE IS TESTING YOU. OPEN BOOK. STILL" +}, +{ +"id":869, +"fortune":"YOUR MANIFESTATIONS NEED A COSIGNER" +}, +{ +"id":870, +"fortune":"YOUR CHAKRAS ARE ALIGNED. WITH THE FRIDGE" +}, +{ +"id":871, +"fortune":"YOUR ZEN GARDEN NEEDS WEEDING. IRONIC" +}, +{ +"id":872, +"fortune":"YOUR MANTRA IS FIVE MORE MINUTES" +}, +{ +"id":873, +"fortune":"YOUR THIRD EYE NEEDS GLASSES TOO" +}, +{ +"id":874, +"fortune":"YOUR ENERGY CLEANSE MISSED A SPOT" +}, +{ +"id":875, +"fortune":"YOUR SAGE SMOKE SET OFF THE ALARM. FITTING" +}, +{ +"id":876, +"fortune":"INNER PEACE IS CLOSE. TRY THE OTHER DOOR" +}, +{ +"id":877, +"fortune":"A STRANGER WILL SMILE AT YOU. STAY CALM" +}, +{ +"id":878, +"fortune":"SOMEONE THOUGHT OF YOU TODAY. BRIEFLY" +}, +{ +"id":879, +"fortune":"YOU CROSSED SOMEONES MIND. IT KEPT WALKING" +}, +{ +"id":880, +"fortune":"AN ADMIRER WATCHES. IT IS YOUR CAT" +}, +{ +"id":881, +"fortune":"YOU WERE MENTIONED TODAY. CONTEXT UNKNOWN" +}, +{ +"id":882, +"fortune":"YOUR NAME CAME UP. THE ROOM WENT QUIET" +}, +{ +"id":883, +"fortune":"SOMEONE MISSES YOU. THEY LOST YOUR NUMBER" +}, +{ +"id":884, +"fortune":"YOU ARE UNFORGETTABLE. MANY HAVE TRIED" +}, +{ +"id":885, +"fortune":"YOUR ABSENCE WAS NOTICED. AND ENJOYED" +}, +{ +"id":886, +"fortune":"PEOPLE TALK ABOUT YOU. KEEP IT THAT WAY" +}, +{ +"id":887, +"fortune":"YOUR REPUTATION ARRIVED FIRST. APOLOGIZE" +}, +{ +"id":888, +"fortune":"YOU MADE AN IMPRESSION. DENTS COUNT" +}, +{ +"id":889, +"fortune":"SOMEONE QUOTED YOU TODAY. INCORRECTLY" +}, +{ +"id":890, +"fortune":"YOU INSPIRED SOMEONE. TO DO THE OPPOSITE" +}, +{ +"id":891, +"fortune":"YOUR LEGACY IS SAFE. NOBODY CHECKED" +}, +{ +"id":892, +"fortune":"HISTORY WILL REMEMBER YOU. AS A FOOTNOTE" +}, +{ +"id":893, +"fortune":"YOUR BIOGRAPHY NEEDS A GHOSTWRITER. AND PLOT" +}, +{ +"id":894, +"fortune":"YOUR AUTOGRAPH IS WORTHLESS. FOR NOW" +}, +{ +"id":895, +"fortune":"FAME AWAITS. LOCAL FAME. VERY LOCAL" +}, +{ +"id":896, +"fortune":"YOUR FIFTEEN MINUTES ARE BUFFERING" +}, +{ +"id":897, +"fortune":"YOU WILL FIND YOUR KEYS. WHERE YOU LEFT THEM" +}, +{ +"id":898, +"fortune":"YOUR PHONE IS IN YOUR OTHER HAND. AGAIN" +}, +{ +"id":899, +"fortune":"YOU WALKED INTO A ROOM. THE REASON LEFT" +}, +{ +"id":900, +"fortune":"YOUR MEMORY IS FINE. IT JUST CURATES" +}, +{ +"id":901, +"fortune":"YOU FORGOT WHY YOU OPENED THE FRIDGE. SNACK" +}, +{ +"id":902, +"fortune":"YOUR MENTAL NOTES NEED A FILING SYSTEM" +}, +{ +"id":903, +"fortune":"YOU REMEMBERED THE TUNE. NOT THE TASK" +}, +{ +"id":904, +"fortune":"YOUR SHOPPING MEMORY EXPIRES AT THE STORE" +}, +{ +"id":905, +"fortune":"YOU KNOW THAT FACE. THE NAME ESCAPED. RUN" +}, +{ +"id":906, +"fortune":"YOUR BRAIN HAS TABS OPEN TOO. ALL FROZEN" +}, +{ +"id":907, +"fortune":"A GREAT IDEA CAME AND WENT. UNRECORDED" +}, +{ +"id":908, +"fortune":"YOU LOCKED THE DOOR. PROBABLY. GO CHECK" +}, +{ +"id":909, +"fortune":"YOUR TRAIN OF THOUGHT DEPARTED WITHOUT YOU" +}, +{ +"id":910, +"fortune":"YOU RETRACED YOUR STEPS. THEY LED TO SNACKS" +}, +{ +"id":911, +"fortune":"YOUR PASSWORD RESET NEEDS A PASSWORD RESET" +}, +{ +"id":912, +"fortune":"LIFE IS SHORT. YOUR MEETINGS ARENT" +}, +{ +"id":913, +"fortune":"LIFE GAVE YOU LEMONS. YOU FILED A COMPLAINT" +}, +{ +"id":914, +"fortune":"LIFE IS A JOURNEY. YOURS NEEDS A MAP" +}, +{ +"id":915, +"fortune":"LIFE IS WHAT HAPPENS WHILE YOU SCROLL" +}, +{ +"id":916, +"fortune":"LIFE HAS NO REMOTE. GET UP AND CHANGE IT" +}, +{ +"id":917, +"fortune":"LIFE IS NOT A RACE. YOU PARKED ANYWAY" +}, +{ +"id":918, +"fortune":"LIFE IS A GIFT. KEEP THE RECEIPT" +}, +{ +"id":919, +"fortune":"LIFE IS UNSCRIPTED. YOURS NEEDS A WRITER" +}, +{ +"id":920, +"fortune":"LIFE REWARDS ACTION. YOURS SENT A MEMO" +}, +{ +"id":921, +"fortune":"LIFE IS A CLASSROOM. YOU SIT IN BACK" +}, +{ +"id":922, +"fortune":"YOUR STORY HAS PLOT HOLES. EMBRACE THEM" +}, +{ +"id":923, +"fortune":"YOUR CHAPTER TWO NEEDS CHAPTER ONE DONE" +}, +{ +"id":924, +"fortune":"YOUR SEQUEL DEPENDS ON THIS PART. NO PRESSURE" +}, +{ +"id":925, +"fortune":"YOUR NARRATOR SOUNDS TIRED LATELY" +}, +{ +"id":926, +"fortune":"YOUR CLIFFHANGER IS JUST INDECISION" +}, +{ +"id":927, +"fortune":"TOMORROW IS A NEW DAY. SAME YOU THOUGH" +}, +{ +"id":928, +"fortune":"YESTERDAY CALLED. IT WANTS NOTHING BACK" +}, +{ +"id":929, +"fortune":"TODAY WILL BE PRODUCTIVE. STARTING LATER" +}, +{ +"id":930, +"fortune":"THIS WEEK HAS POTENTIAL. HAD. HAS. UNCLEAR" +}, +{ +"id":931, +"fortune":"NEXT MONTH YOU WILL START. NEXT MONTH AGREES" +}, +{ +"id":932, +"fortune":"THE YEAR IS HALF OVER. YOUR GOALS ARE INTACT" +}, +{ +"id":933, +"fortune":"SOON IS NOT A DATE. YOU TREAT IT AS ONE" +}, +{ +"id":934, +"fortune":"EVENTUALLY IS YOUR FAVORITE DEADLINE" +}, +{ +"id":935, +"fortune":"THE RIGHT TIME NEVER COMES. CONVENIENT" +}, +{ +"id":936, +"fortune":"YOUR WINDOW OF OPPORTUNITY HAS BLINDS" +}, +{ +"id":937, +"fortune":"PATIENCE COMES TO THOSE WHO WAIT. IRONIC" +}, +{ +"id":938, +"fortune":"YOUR TURN IS COMING. THE LINE MOVED ON" +}, +{ +"id":939, +"fortune":"YOU WAITED FOR A SIGN. THIS IS NOT IT" +}, +{ +"id":940, +"fortune":"HOLD THAT THOUGHT. FOREVER IF POSSIBLE" +}, +{ +"id":941, +"fortune":"YOUR PAUSE BUTTON IS YOUR FAVORITE" +}, +{ +"id":942, +"fortune":"YOU PUT A PIN IN IT. THE PIN CUSHION IS FULL" +}, +{ +"id":943, +"fortune":"IT IS ON YOUR RADAR. YOUR RADAR IS OFF" +}, +{ +"id":944, +"fortune":"YOU WILL GET TO IT. IT STOPPED WAITING" +}, +{ +"id":945, +"fortune":"THE BALL IS IN YOUR COURT. GATHERING DUST" +}, +{ +"id":946, +"fortune":"YOUR FOLLOW UP NEEDS A FOLLOW UP" +}, +{ +"id":947, +"fortune":"CONSIDER THIS YOUR REMINDER. THE FOURTH" +}, +{ +"id":948, +"fortune":"YOUR INTENTIONS ARE GOOD. AND STATIONARY" +}, +{ +"id":949, +"fortune":"EFFORT WAS MADE. A ROUNDING ERROR OF IT" +}, +{ +"id":950, +"fortune":"YOU GAVE 100 PERCENT. OF YOUR MINIMUM" +}, +{ +"id":951, +"fortune":"YOUR BEST GUESS BEAT YOUR RESEARCH. AGAIN" +}, +{ +"id":952, +"fortune":"CLOSE ENOUGH IS YOUR SIGNATURE MOVE" +}, +{ +"id":953, +"fortune":"GOOD ENOUGH SHIPPED. PERFECTION SULKS" +}, +{ +"id":954, +"fortune":"YOUR SHORTCUTS TOOK THE LONG WAY" +}, +{ +"id":955, +"fortune":"YOU IMPROVISED. THE RESULTS IMPROVISED BACK" +}, +{ +"id":956, +"fortune":"YOUR WINGING IT HAS FREQUENT FLYER MILES" +}, +{ +"id":957, +"fortune":"DETAILS MATTER. YOURS WENT ON STRIKE" +}, +{ +"id":958, +"fortune":"YOUR ROUGH DRAFT WENT TO PRODUCTION" +}, +{ +"id":959, +"fortune":"YOU SKIMMED IT. IT SHOWED. IT ALWAYS SHOWS" +}, +{ +"id":960, +"fortune":"YOUR PROOFREADING MISSED THIS SENTENCE TOO" +}, +{ +"id":961, +"fortune":"ALMOST DONE IS YOUR PERMANENT ADDRESS" +}, +{ +"id":962, +"fortune":"YOUR CHECKLIST HAS SURVIVORS" +}, +{ +"id":963, +"fortune":"DONE IS BETTER THAN PERFECT. TRY EITHER" +}, +{ +"id":964, +"fortune":"YOUR STANDARDS SLIPPED. THEY LEFT A NOTE" +}, +{ +"id":965, +"fortune":"MEDIOCRITY TAKES EFFORT TOO. WELL DONE" +}, +{ +"id":966, +"fortune":"YOU EXCEEDED EXPECTATIONS. THEY WERE LOW" +}, +{ +"id":967, +"fortune":"YOUR BARE MINIMUM HAS A LOYAL FOLLOWING" +}, +{ +"id":968, +"fortune":"YOU RAISED THE BAR. FROM THE FLOOR" +}, +{ +"id":969, +"fortune":"CONSISTENCY IS KEY. YOURS IS CONSISTENT" +}, +{ +"id":970, +"fortune":"YOUR AVERAGE IS ABOVE AVERAGE. AT AVERAGING" +}, +{ +"id":971, +"fortune":"PROGRESS WAS MADE. LATERALLY. BUT MADE" +}, +{ +"id":972, +"fortune":"YOUR TRAJECTORY IS STABLE. FLAT. STABLE" +}, +{ +"id":973, +"fortune":"ONWARD AND UPWARD. OR SIDEWAYS. ONWARD" +}, +{ +"id":974, +"fortune":"YOU PIVOTED. THE DIRECTION WAS RANDOM" +}, +{ +"id":975, +"fortune":"YOUR MOMENTUM TOOK A PERSONAL DAY" +}, +{ +"id":976, +"fortune":"FULL SPEED AHEAD. YOUR SPEED. SO GENTLE" +}, +{ +"id":977, +"fortune":"YOU HIT THE GROUND WALKING. BRISKLY EVEN" +}, +{ +"id":978, +"fortune":"YOUR SPRINT BECAME A STROLL. SCENIC ONE" +}, +{ +"id":979, +"fortune":"BABY STEPS COUNT. YOURS ARE NEWBORN" +}, +{ +"id":980, +"fortune":"YOU ARE ON A ROLL. A SLOW GENTLE ROLL" +}, +{ +"id":981, +"fortune":"YOUR STREAK CONTINUES. WHICHEVER ONE" +}, +{ +"id":982, +"fortune":"MOMENTUM IS BUILDING. SOMEWHERE ELSE" +}, +{ +"id":983, +"fortune":"THE FINISH LINE MOVED. SO DID YOU. BACKWARD" +}, +{ +"id":984, +"fortune":"YOUR MARATHON IS A SERIES OF SNACK BREAKS" +}, +{ +"id":985, +"fortune":"YOU CROSSED A LINE TODAY. NOT THE FINISH ONE" +}, +{ +"id":986, +"fortune":"VICTORY IS NEAR. DEFINE VICTORY LOOSELY" +}, +{ +"id":987, +"fortune":"YOU WON THE ARGUMENT. THE ROOM WAS EMPTY" +}, +{ +"id":988, +"fortune":"CHAMPIONS TRAIN DAILY. YOU WATCHED THEM" +}, +{ +"id":989, +"fortune":"YOUR TROPHY SHELF HOLDS PARTICIPATION" +}, +{ +"id":990, +"fortune":"SECOND PLACE IS FIRST LOSER. YOU GOT FIFTH" +}, +{ +"id":991, +"fortune":"YOUR COMEBACK SEASON IS ALWAYS NEXT SEASON" +}, +{ +"id":992, +"fortune":"THE UNDERDOG STORY NEEDS AN ATTEMPT FIRST" +}, +{ +"id":993, +"fortune":"YOUR RIVALS STOPPED CHECKING ON YOU" +}, +{ +"id":994, +"fortune":"GLORY AWAITS. IT DOUBLE BOOKED THOUGH" +}, +{ +"id":995, +"fortune":"YOU MADE THE HIGHLIGHT REEL. AS A BLOOPER" +}, +{ +"id":996, +"fortune":"HISTORY FAVORS THE BOLD. YOU FAVOR NAPS" +}, +{ +"id":997, +"fortune":"YOUR SPLIT FLAP DISPLAY DESERVED BETTER TEXT" +}, +{ +"id":998, +"fortune":"THIS MESSAGE USED 45 CHARACTERS WISELY. ISH" +}, +{ +"id":999, +"fortune":"YOU BUILT A DISPLAY FOR THIS. WORTH IT" +}, +{ +"id":1000, +"fortune":"EACH FLAP TURNED FOR THIS WISDOM. SAVOR IT" +} +] \ No newline at end of file diff --git a/apps/sarcastic-fortune-cookies/fortunes_es.json b/apps/sarcastic-fortune-cookies/fortunes_es.json new file mode 100644 index 0000000..7757037 --- /dev/null +++ b/apps/sarcastic-fortune-cookies/fortunes_es.json @@ -0,0 +1,402 @@ +[ + { + "id": 1, + "fortune": "TODO PASA POR UNA RAZN. NORMALMENTE MALA" + }, + { + "id": 2, + "fortune": "LA SUERTE VIENE. CAMINA MUY DESPACIO" + }, + { + "id": 3, + "fortune": "EL UNIVERSO TIENE UN PLAN. UNO PEQUEO" + }, + { + "id": 4, + "fortune": "TU DESTINO LLAM. LO HA APLAZADO" + }, + { + "id": 5, + "fortune": "LAS ESTRELLAS SE ALINEARON. Y TE VIERON" + }, + { + "id": 6, + "fortune": "TU BARCO YA LLEGA. ES UNA CANOA" + }, + { + "id": 7, + "fortune": "CONFA EN EL PROCESO. L DUDA DE TI" + }, + { + "id": 8, + "fortune": "UNA SEAL LLEGA. DICE CEDA EL PASO" + }, + { + "id": 9, + "fortune": "TU ESTRELLA EXISTE. EN OTRA GALAXIA" + }, + { + "id": 10, + "fortune": "EL KARMA ES REAL. ESO EXPLICA MUCHO" + }, + { + "id": 11, + "fortune": "TU FUTURO ES BRILLANTE. PONTE CREMA" + }, + { + "id": 12, + "fortune": "EL DESTINO ESPERA. SE PUSO A LEER" + }, + { + "id": 13, + "fortune": "LOS MILAGROS PASAN A DIARIO. EN OTRA PARTE" + }, + { + "id": 14, + "fortune": "TU MOMENTO LLEGA. EST EN EL ATASCO" + }, + { + "id": 15, + "fortune": "LA SUERTE LLAM. T HABAS SALIDO" + }, + { + "id": 16, + "fortune": "LA MAREA CAMBIA. A PASO DE GLACIAR" + }, + { + "id": 17, + "fortune": "LLEGAN GRANDES COSAS. BAJA EL LISTN" + }, + { + "id": 18, + "fortune": "LAS OPORTUNIDADES ESTN. SIEMPRE TE PIERDES" + }, + { + "id": 19, + "fortune": "EL COSMOS TE OYE. HAZ COLA POR FAVOR" + }, + { + "id": 20, + "fortune": "TUS RUEGOS FUERON ODOS. Y ARCHIVADOS" + }, + { + "id": 21, + "fortune": "LA ABUNDANCIA FLUYE HACIA TI. RO ARRIBA" + }, + { + "id": 22, + "fortune": "LA RUEDA GIRA. T ERES EL HMSTER" + }, + { + "id": 23, + "fortune": "CADA NUBE TIENE UN LADO BUENO. EL TUYO GOTEA" + }, + { + "id": 24, + "fortune": "EL AZAR PREGUNTA POR TI. POR CURIOSIDAD" + }, + { + "id": 25, + "fortune": "LOS PRESAGIOS HABLAN. MS BIEN SUSPIRAN" + }, + { + "id": 26, + "fortune": "TU HORSCOPO ES BUENO. Y FICTICIO" + }, + { + "id": 27, + "fortune": "EL RAYO NO CAE DOS VECES. T S" + }, + { + "id": 28, + "fortune": "EL DESTINO PAGA LA PACIENCIA. FACTURA LUEGO" + }, + { + "id": 29, + "fortune": "LA LUNA VIO TU NUEVO COMIENZO. OTRO MS" + }, + { + "id": 30, + "fortune": "TU NGEL VIGILA. VA TOMANDO NOTAS" + }, + { + "id": 31, + "fortune": "CREE Y RECIBIRS. LA ENTREGA VARA" + }, + { + "id": 32, + "fortune": "TU ERA DORADA LLEGA. CHAPADA EN ORO" + }, + { + "id": 33, + "fortune": "LO QUE ES TUYO LLEGA. SIN NINGUNA PRISA" + }, + { + "id": 34, + "fortune": "EL COSMOS TE GUI. O LE ENTR ALGO" + }, + { + "id": 35, + "fortune": "TU SUERTE VA A CAMBIAR. RUMBO INCIERTO" + }, + { + "id": 36, + "fortune": "LA PROVIDENCIA PROVEE. REVISA LOS RECIBOS" + }, + { + "id": 37, + "fortune": "PIDE UN DESEO. LA ESTRELLA LO MARCA SPAM" + }, + { + "id": 38, + "fortune": "TODO LLEGA A SU TIEMPO. EL TUYO SE DEMORA" + }, + { + "id": 39, + "fortune": "LOS SIGNOS ESTABAN AH. LEER ES OPCIONAL" + }, + { + "id": 40, + "fortune": "ERES NICO. COMO TODO EL MUNDO" + }, + { + "id": 41, + "fortune": "LO MEJOR EST POR VENIR. NO TIENE PRISA" + }, + { + "id": 42, + "fortune": "TU AURA BRILLA. HACE HORAS EXTRA" + }, + { + "id": 43, + "fortune": "LA FORTUNA TE SONRI. CORTS. DE LEJOS" + }, + { + "id": 44, + "fortune": "TU NGEL PIDI EL TRASLADO" + }, + { + "id": 45, + "fortune": "EL DESTINO REPARTE. T PIERDES LAS CARTAS" + }, + { + "id": 46, + "fortune": "UN PRESAGIO APARECE. SE ENCOGE DE HOMBROS" + }, + { + "id": 47, + "fortune": "EL XITO EST CERCA. Y EL BOTN DE POSPONER" + }, + { + "id": 48, + "fortune": "EL TRABAJO SIEMPRE PAGA. EN EXPERIENCIA" + }, + { + "id": 49, + "fortune": "TU POTENCIAL ES INFINITO. Y SIN USAR" + }, + { + "id": 50, + "fortune": "APUNTA A LA LUNA. CUIDADO CON EL TECHO" + }, + { + "id": 51, + "fortune": "EL QUE MADRUGA VIO AL GUSANO. T DORMAS" + }, + { + "id": 52, + "fortune": "TU GRAN OPORTUNIDAD LLEGA. NO LA ROMPAS" + }, + { + "id": 53, + "fortune": "ROMA NO SE HIZO EN UN DA. LA TUYA TAMPOCO" + }, + { + "id": 54, + "fortune": "TUS TALENTOS SE HALLARN. CON UNA ORDEN" + }, + { + "id": 55, + "fortune": "LOS QUE GANAN NO SE RINDEN. T FLOTAS" + }, + { + "id": 56, + "fortune": "TUS IDEAS VAN POR DELANTE. TUS PLAZOS DETRS" + }, + { + "id": 57, + "fortune": "LA CALIDAD ES UN HBITO. EL TUYO TAMBIN" + }, + { + "id": 58, + "fortune": "TU TRABAJO HABLA POR TI. MUY BAJITO" + }, + { + "id": 59, + "fortune": "LA CIMA ES SOLITARIA. PROBLEMA RESUELTO" + }, + { + "id": 60, + "fortune": "UN ASCENSO SE ACERCA. EL DE UN COLEGA" + }, + { + "id": 61, + "fortune": "TU CV IMPRESIONA. A LOS DE FICCIN" + }, + { + "id": 62, + "fortune": "TU MEJOR TRABAJO LLEGA. CORRE MUY RPIDO" + }, + { + "id": 63, + "fortune": "ESA REUNIN PUDO SER UN CORREO" + }, + { + "id": 64, + "fortune": "TU BANDEJA NO ES TU ENEMIGA. SOLO TESTIGO" + }, + { + "id": 65, + "fortune": "LOS PLAZOS NO TE ASUSTAN. LOS IGNORAS" + }, + { + "id": 66, + "fortune": "TU PLAN A CINCO AOS TIENE CINCO AOS" + }, + { + "id": 67, + "fortune": "LENTO PERO SEGURO GANA. DOMINAS LO LENTO" + }, + { + "id": 68, + "fortune": "TU AGENDA EST LLENA. DE MRGENES" + }, + { + "id": 69, + "fortune": "TU EQUIPO TAMBIN SUEA. CON EL TRASLADO" + }, + { + "id": 70, + "fortune": "LA OCASIN BAJ HASTA TU NIVEL" + }, + { + "id": 71, + "fortune": "EL XITO DEJA PISTAS. PREFIERES EL MISTERIO" + }, + { + "id": 72, + "fortune": "TU ZONA DE CONFORT MERECE CDIGO POSTAL" + }, + { + "id": 73, + "fortune": "CALIDAD O CANTIDAD. RECHAZAS AMBAS" + }, + { + "id": 74, + "fortune": "TU INSTINTO ES AGUDO. Y SUELE FALLAR" + }, + { + "id": 75, + "fortune": "TRABAJAS BIEN BAJO PRESIN. ELLA DISCREPA" + }, + { + "id": 76, + "fortune": "LA MAESTRA PIDE 10000 HORAS. TIENES 40" + }, + { + "id": 77, + "fortune": "TU PLANTA CREY EN TI. Y EN EL AGUA" + }, + { + "id": 78, + "fortune": "LA IMPRESORA HUELE TU PRISA. POR ESO FALLA" + }, + { + "id": 79, + "fortune": "TU LEGADO EST EN OBRAS. TODAVA" + }, + { + "id": 80, + "fortune": "UN DIPLOMA TE ESPERA. EL DE ASISTENCIA" + }, + { + "id": 81, + "fortune": "CONOCE A GENTE. ABRE PUERTAS. TOCAR TAMBIN" + }, + { + "id": 82, + "fortune": "DAS EJEMPLO. RECURSOS HUMANOS QUIERE OTRO" + }, + { + "id": 83, + "fortune": "TU PROYECTO PERSONAL DESCANSA CON LOS OTROS" + }, + { + "id": 84, + "fortune": "EL GENIO ES UNO POR CIENTO SUDOR. AH PARAS" + }, + { + "id": 85, + "fortune": "TU MENSAJE DE AUSENCIA ES TU OBRA MAESTRA" + }, + { + "id": 86, + "fortune": "FALLAR ES UN ESCALN. BONITA ESCALERA" + }, + { + "id": 87, + "fortune": "ERES IRREEMPLAZABLE. EL ANUNCIO YA EST" + }, + { + "id": 88, + "fortune": "TU EVALUACIN ANUAL LLEGA. ENSAYA BIEN" + }, + { + "id": 89, + "fortune": "EFICIENCIA ES TU SEGUNDO NOMBRE. EN PAPEL" + }, + { + "id": 90, + "fortune": "CAES DE PIE. EL ATERRIZAJE SE RETRASA" + }, + { + "id": 91, + "fortune": "LA CUMBRE EST CERCA. Y EL CAMPO BASE" + }, + { + "id": 92, + "fortune": "TU AMBICIN DESPERT. TU DESPERTADOR NO" + }, + { + "id": 93, + "fortune": "S T MISMO. LOS DEMS SE PREOCUPARON" + }, + { + "id": 94, + "fortune": "TU REUNIN SALI BIEN. YA TERMIN" + }, + { + "id": 95, + "fortune": "DESCANSAR ES SANO. LO PRACTICAS MUCHO" + }, + { + "id": 96, + "fortune": "TU DIETA EMPIEZA EL LUNES. LOS LUNES HUYEN" + }, + { + "id": 97, + "fortune": "TE MERECES ESTO. RESPIRA HONDO" + }, + { + "id": 98, + "fortune": "UN EXTRAO TE SONRE. MANTN LA CALMA" + }, + { + "id": 99, + "fortune": "GUARDAS RENCOR NO. LO ARCHIVAS CON FECHA" + }, + { + "id": 100, + "fortune": "TU PACIENCIA TIENE UNA BARRA. ATASCADA" + } +] \ No newline at end of file diff --git a/apps/sarcastic-fortune-cookies/fortunes_fr.json b/apps/sarcastic-fortune-cookies/fortunes_fr.json new file mode 100644 index 0000000..115a78a --- /dev/null +++ b/apps/sarcastic-fortune-cookies/fortunes_fr.json @@ -0,0 +1,2802 @@ +[ +{ +"id":1, +"fortune":"TOUT ARRIVE POUR UNE RAISON. SOUVENT MAUVAISE" +}, +{ +"id":2, +"fortune":"LA CHANCE ARRIVE. ELLE MARCHE LENTEMENT" +}, +{ +"id":3, +"fortune":"LE COSMOS A UN PLAN POUR TOI. UN PETIT" +}, +{ +"id":4, +"fortune":"TON DESTIN A APPELÉ. IL REPORTE" +}, +{ +"id":5, +"fortune":"LES ASTRES SONT ALIGNÉS. ILS ÉPELLENT BOF" +}, +{ +"id":6, +"fortune":"LA FORTUNE SOURIT AUX AUDACIEUX. TOI REGARDE" +}, +{ +"id":7, +"fortune":"TON NAVIRE ARRIVE. EN FAIT UN PÉDALO" +}, +{ +"id":8, +"fortune":"FAIS CONFIANCE AU PROCESSUS. LUI DOUTE DE TOI" +}, +{ +"id":9, +"fortune":"UN SIGNE ARRIVE. IL DIT CÉDEZ LE PASSAGE" +}, +{ +"id":10, +"fortune":"TON ÉTOILE EXISTE. DANS UNE AUTRE GALAXIE" +}, +{ +"id":11, +"fortune":"LE KARMA EXISTE. ÇA EXPLIQUE TOUT" +}, +{ +"id":12, +"fortune":"TON AVENIR EST RADIEUX. PRENDS DE LA CRÈME" +}, +{ +"id":13, +"fortune":"LE DESTIN ATTEND. IL A PRIS UN LIVRE" +}, +{ +"id":14, +"fortune":"LES MIRACLES ARRIVENT CHAQUE JOUR. AILLEURS" +}, +{ +"id":15, +"fortune":"TON MOMENT ARRIVE. IL EST DANS LES BOUCHONS" +}, +{ +"id":16, +"fortune":"LA CHANCE A FRAPPÉ. TU ÉTAIS SORTI" +}, +{ +"id":17, +"fortune":"LA MARÉE TOURNE. À VITESSE DE GLACIER" +}, +{ +"id":18, +"fortune":"DE GRANDES CHOSES ARRIVENT. BAISSE LA BARRE" +}, +{ +"id":19, +"fortune":"LES OCCASIONS SONT LÀ. VOUS VOUS RATEZ" +}, +{ +"id":20, +"fortune":"LE COSMOS TE RÉPOND. PATIENTE DANS LA FILE" +}, +{ +"id":21, +"fortune":"TES PRIÈRES SONT REÇUES. ET CLASSÉES" +}, +{ +"id":22, +"fortune":"LA PROSPÉRITÉ COULE VERS TOI. À CONTRESENS" +}, +{ +"id":23, +"fortune":"LA ROUE DE FORTUNE TOURNE. TU ES LE HAMSTER" +}, +{ +"id":24, +"fortune":"CHAQUE NUAGE A UN BON CÔTÉ. LE TIEN FUIT" +}, +{ +"id":25, +"fortune":"LE HASARD DEMANDE DE TES NOUVELLES. POLI" +}, +{ +"id":26, +"fortune":"LES PRÉSAGES PARLENT. ILS SOUPIRENT SURTOUT" +}, +{ +"id":27, +"fortune":"TON HOROSCOPE EST FLATTEUR. ET FICTIF" +}, +{ +"id":28, +"fortune":"LA FOUDRE NE FRAPPE JAMAIS DEUX FOIS. TOI SI" +}, +{ +"id":29, +"fortune":"LE DESTIN PAIE LA PATIENCE. FACTURE SUIT" +}, +{ +"id":30, +"fortune":"LA LUNE A VU TON NOUVEAU DÉPART. ENCORE UN" +}, +{ +"id":31, +"fortune":"TON ANGE GARDIEN OBSERVE. IL PREND DES NOTES" +}, +{ +"id":32, +"fortune":"CROIS ET TU RECEVRAS. LIVRAISON VARIABLE" +}, +{ +"id":33, +"fortune":"TON ÈRE DORÉE APPROCHE. PLAQUÉ OR DISONS" +}, +{ +"id":34, +"fortune":"CE QUI TE REVIENT ARRIVE. SANS SE PRESSER" +}, +{ +"id":35, +"fortune":"LE COSMOS CLIGNE VERS TOI. OU IL A UN TIC" +}, +{ +"id":36, +"fortune":"TA CHANCE VA TOURNER. SENS INCONNU" +}, +{ +"id":37, +"fortune":"LA PROVIDENCE POURVOIT. ELLE GARDE LES REÇUS" +}, +{ +"id":38, +"fortune":"FAIS UN VŒU. TON ÉTOILE LE MET EN SPAM" +}, +{ +"id":39, +"fortune":"TOUT VIENT À POINT. LE TIEN PREND SON TEMPS" +}, +{ +"id":40, +"fortune":"UNE MANNE APPROCHE. COUVRE TOI BIEN" +}, +{ +"id":41, +"fortune":"LES SIGNES ÉTAIENT LÀ. LIRE RESTE FACULTATIF" +}, +{ +"id":42, +"fortune":"TU ES UNIQUE. COMME TOUT LE MONDE" +}, +{ +"id":43, +"fortune":"LE MEILLEUR RESTE À VENIR. IL FLÂNE" +}, +{ +"id":44, +"fortune":"TON AURA RAYONNE. ELLE FAIT DES HEURES SUP" +}, +{ +"id":45, +"fortune":"LA FORTUNE TE SOURIT. POLIMENT. DE LOIN" +}, +{ +"id":46, +"fortune":"TON ANGE A DEMANDÉ SA MUTATION" +}, +{ +"id":47, +"fortune":"UN GRAND PROJET PREND DU TEMPS. LE TIEN DORT" +}, +{ +"id":48, +"fortune":"LE DESTIN BAT LES CARTES. TU LES PERDS" +}, +{ +"id":49, +"fortune":"UN PRÉSAGE APPARAÎT. IL HAUSSE LES ÉPAULES" +}, +{ +"id":50, +"fortune":"TON ÉTOILE MONTE. UN JOUR. SANS DOUTE" +}, +{ +"id":51, +"fortune":"LA CHANCE SOURIT AUX IDIOTS. BONNE NOUVELLE" +}, +{ +"id":52, +"fortune":"DEMAIN TOUT IRA MIEUX. DEMAIN EST EXTENSIBLE" +}, +{ +"id":53, +"fortune":"LE SUCCÈS EST PROCHE. LE BOUTON SNOOZE AUSSI" +}, +{ +"id":54, +"fortune":"LE TRAVAIL PAIE TOUJOURS. EN EXPÉRIENCE" +}, +{ +"id":55, +"fortune":"TON POTENTIEL EST IMMENSE. ET INTACT" +}, +{ +"id":56, +"fortune":"VISE LA LUNE. ATTENTION AU PLAFOND" +}, +{ +"id":57, +"fortune":"LE LÈVE TÔT A EU LE VER. TU DORMAIS" +}, +{ +"id":58, +"fortune":"TA GRANDE CHANCE ARRIVE. NE LA CASSE PAS" +}, +{ +"id":59, +"fortune":"ROME PRIT DU TEMPS. LA TIENNE EST EN DEVIS" +}, +{ +"id":60, +"fortune":"TES TALENTS SERONT DÉCOUVERTS. AVEC UN MANDAT" +}, +{ +"id":61, +"fortune":"LES GAGNANTS PERSISTENT. TOI TU PLANES" +}, +{ +"id":62, +"fortune":"TES IDÉES SONT EN AVANCE. TES DÉLAIS NON" +}, +{ +"id":63, +"fortune":"LA QUALITÉ EST UNE HABITUDE. LA TIENNE AUSSI" +}, +{ +"id":64, +"fortune":"TON TRAVAIL PARLE POUR TOI. TOUT BAS" +}, +{ +"id":65, +"fortune":"LE SOMMET EST SOLITAIRE. PROBLÈME RÉGLÉ" +}, +{ +"id":66, +"fortune":"UNE PROMOTION APPROCHE. CELLE DU COLLÈGUE" +}, +{ +"id":67, +"fortune":"TON CV IMPRESSIONNE. LES AMATEURS DE FICTION" +}, +{ +"id":68, +"fortune":"TON MEILLEUR TRAVAIL ARRIVE. IL COURT VITE" +}, +{ +"id":69, +"fortune":"CETTE RÉUNION AURAIT PU ÊTRE UN MAIL" +}, +{ +"id":70, +"fortune":"TA BOÎTE MAIL EST INNOCENTE. SIMPLE TÉMOIN" +}, +{ +"id":71, +"fortune":"LES DÉLAIS NE TE FONT RIEN. TU PASSES DEVANT" +}, +{ +"id":72, +"fortune":"TON PLAN SUR CINQ ANS A CINQ ANS" +}, +{ +"id":73, +"fortune":"LENT MAIS SÛR GAGNE. TU MAÎTRISES LE LENT" +}, +{ +"id":74, +"fortune":"TON AGENDA EST PLEIN. DE MARGES" +}, +{ +"id":75, +"fortune":"TON ÉQUIPE RÊVE AUSSI. DE MUTATION" +}, +{ +"id":76, +"fortune":"LA GRANDE OCCASION DESCEND À TON NIVEAU" +}, +{ +"id":77, +"fortune":"LE SUCCÈS LAISSE DES INDICES. TU LES ÉVITES" +}, +{ +"id":78, +"fortune":"TA ZONE DE CONFORT MÉRITE SON CODE POSTAL" +}, +{ +"id":79, +"fortune":"QUALITÉ OU QUANTITÉ. TU DÉCLINES LES DEUX" +}, +{ +"id":80, +"fortune":"TON INSTINCT EST VIF. ET SOUVENT FAUX" +}, +{ +"id":81, +"fortune":"TU BOSSES BIEN SOUS PRESSION. QUI LA DIT" +}, +{ +"id":82, +"fortune":"LA MAÎTRISE DEMANDE 10000 HEURES. TU EN AS 40" +}, +{ +"id":83, +"fortune":"TA PLANTE CROYAIT EN TOI. ET EN ARROSAGE" +}, +{ +"id":84, +"fortune":"TON IMPRIMANTE SENT TON URGENCE. ELLE PLANTE" +}, +{ +"id":85, +"fortune":"TON HÉRITAGE EST EN CHANTIER. TOUJOURS" +}, +{ +"id":86, +"fortune":"UN DIPLÔME TATTEND. CELUI DE PARTICIPATION" +}, +{ +"id":87, +"fortune":"UN DIPLÔME ARRIVE. CELUI DE PARTICIPATION" +}, +{ +"id":88, +"fortune":"TU DONNES LE TON. LES RH EN VEULENT UN AUTRE" +}, +{ +"id":89, +"fortune":"TON PROJET PERSO SE REPOSE AVEC LES AUTRES" +}, +{ +"id":90, +"fortune":"LE GÉNIE EST 1 POUR CENT DE SUEUR. TU STOPPES" +}, +{ +"id":91, +"fortune":"TA RÉPONSE AUTOMATIQUE EST TON MEILLEUR TEXTE" +}, +{ +"id":92, +"fortune":"RATER EST UNE MARCHE. BEL ESCALIER DÉJÀ" +}, +{ +"id":93, +"fortune":"TU ES IRREMPLAÇABLE. TON ANNONCE EST PRÊTE" +}, +{ +"id":94, +"fortune":"TON ENTRETIEN ANNUEL APPROCHE. RÉPÈTE BIEN" +}, +{ +"id":95, +"fortune":"EFFICACITÉ. TON DEUXIÈME PRÉNOM. SUR PAPIER" +}, +{ +"id":96, +"fortune":"TU RETOMBES SUR TES PIEDS. VOL RETARDÉ" +}, +{ +"id":97, +"fortune":"LE SOMMET EST PROCHE. LE CAMP DE BASE AUSSI" +}, +{ +"id":98, +"fortune":"TON AMBITION SE RÉVEILLE. PAS TON RÉVEIL" +}, +{ +"id":99, +"fortune":"SOIS TOI MÊME. LES AUTRES ONT EU PEUR" +}, +{ +"id":100, +"fortune":"TA RÉUNION FUT UN SUCCÈS. ELLE EST FINIE" +}, +{ +"id":101, +"fortune":"CONNAIS TOI TOI MÊME. FAITES CONNAISSANCE" +}, +{ +"id":102, +"fortune":"TA MÉTAMORPHOSE ARRIVE. PRÉVIENS LE RÉSEAU" +}, +{ +"id":103, +"fortune":"NOUVEL AN NOUVEAU TOI. LE VIEUX PREND NOTE" +}, +{ +"id":104, +"fortune":"TU SUFFIS. LA BARRE ÉTAIT RÉGLABLE" +}, +{ +"id":105, +"fortune":"TA PAIX INTÉRIEURE A VU TON AGENDA. ELLE FUIT" +}, +{ +"id":106, +"fortune":"GRANDIR PREND DU TEMPS. LE TIEN VEUT UN DÉLAI" +}, +{ +"id":107, +"fortune":"TU ES UNE ŒUVRE EN COURS. SURTOUT EN COURS" +}, +{ +"id":108, +"fortune":"SE CONNAÎTRE EST UN CADEAU. EN TRANSIT" +}, +{ +"id":109, +"fortune":"GUÉRIR EST RAREMENT LINÉAIRE. TU GRIBOUILLES" +}, +{ +"id":110, +"fortune":"TON MEILLEUR TOI ÉMERGE. IL APPUIE SUR SNOOZE" +}, +{ +"id":111, +"fortune":"PRENDRE SOIN DE SOI COMPTE. LES SÉRIES NON" +}, +{ +"id":112, +"fortune":"TES LIMITES SONT SAINES. ET JAMAIS TESTÉES" +}, +{ +"id":113, +"fortune":"LA GRATITUDE CHANGE TOUT. ESSAIE SUR LES GENS" +}, +{ +"id":114, +"fortune":"TU PARDONNES VITE. À TOI MÊME SURTOUT" +}, +{ +"id":115, +"fortune":"TES HABITUDES TE DÉFINISSENT. CONDOLÉANCES" +}, +{ +"id":116, +"fortune":"CHAQUE PAS COMPTE. MÊME VERS LE FRIGO" +}, +{ +"id":117, +"fortune":"TON INTUITION EST FORTE. ET SOUVENT À CÔTÉ" +}, +{ +"id":118, +"fortune":"LA MÉDITATION AIDE. TON TÉLÉPHONE PROTESTE" +}, +{ +"id":119, +"fortune":"TU AS TOURNÉ LA PAGE. ELLE A TON ADRESSE" +}, +{ +"id":120, +"fortune":"TA ROUTINE MATINALE EST SACRÉE. 4 MINUTES" +}, +{ +"id":121, +"fortune":"TU APPRENDS À DIRE NON. AUX LÉGUMES SURTOUT" +}, +{ +"id":122, +"fortune":"TA VOLONTÉ SOULÈVE PRESQUE UN BISCUIT ENTIER" +}, +{ +"id":123, +"fortune":"TA MISSION DE VIE EXISTE. ELLE RESTE ANONYME" +}, +{ +"id":124, +"fortune":"TU ACCUEILLES LE CHANGEMENT. DEPUIS LE CANAPÉ" +}, +{ +"id":125, +"fortune":"TON JOURNAL INTIME EST BRUT. LES DEUX PAGES" +}, +{ +"id":126, +"fortune":"TU VIS TA VÉRITÉ. VÉRIFICATION EN COURS" +}, +{ +"id":127, +"fortune":"TON CRITIQUE INTÉRIEUR NE FAIT QUE SOUPIRER" +}, +{ +"id":128, +"fortune":"TES OBJECTIFS SONT EN VUE. BELLES JUMELLES" +}, +{ +"id":129, +"fortune":"TES NOUVELLES HABITUDES SEMBLENT FAMILIÈRES" +}, +{ +"id":130, +"fortune":"TON ÉNERGIE ATTIRE. SURTOUT LES CORVÉES" +}, +{ +"id":131, +"fortune":"TES MURS SONT TOMBÉS. LE FOSSÉ A GRANDI" +}, +{ +"id":132, +"fortune":"TU FÊTES LES PETITES VICTOIRES. CHAQUE JOUR" +}, +{ +"id":133, +"fortune":"TON ÉVOLUTION DÉCONCERTE MÊME DARWIN" +}, +{ +"id":134, +"fortune":"TU PASSES ENFIN EN PREMIER. FILE COURTE" +}, +{ +"id":135, +"fortune":"TA RÉINVENTION TOURNE. MÊME PROGRAMME" +}, +{ +"id":136, +"fortune":"TU ES PRÉSENT. PHYSIQUEMENT DU MOINS" +}, +{ +"id":137, +"fortune":"TON NOUVEAU DÉPART COMMENCE LUNDI. UN AUTRE" +}, +{ +"id":138, +"fortune":"TON TABLEAU DE VISION EST BEAU. ET RETRAITÉ" +}, +{ +"id":139, +"fortune":"TU HONORES TES ÉMOTIONS. À PLEIN VOLUME" +}, +{ +"id":140, +"fortune":"TON CALME EST RÉEL. TU IGNORES TES MAILS" +}, +{ +"id":141, +"fortune":"TES STANDARDS MONTENT. DEPUIS LA CAVE" +}, +{ +"id":142, +"fortune":"TU FLEURIS. HORS SAISON. MAIS TU FLEURIS" +}, +{ +"id":143, +"fortune":"TA DISCIPLINE EST LÉGENDAIRE. DONC FICTIVE" +}, +{ +"id":144, +"fortune":"TES AFFIRMATIONS MARCHENT. SUR LE PLAFOND" +}, +{ +"id":145, +"fortune":"TON ENFANT INTÉRIEUR VEUT SURTOUT UNE GLACE" +}, +{ +"id":146, +"fortune":"LE GRAND AMOUR ARRIVE. IL EST AUSSI EN RETARD" +}, +{ +"id":147, +"fortune":"TON ÂME SŒUR EXISTE. RÉSEAU FAIBLE PAR ICI" +}, +{ +"id":148, +"fortune":"LA ROMANCE EST DANS LES RUES. LE POLLEN AUSSI" +}, +{ +"id":149, +"fortune":"CUPIDON A TON ADRESSE. IL EST PASSÉ DEVANT" +}, +{ +"id":150, +"fortune":"TON CŒUR CONNAÎT LE CHEMIN. TES TEXTOS NON" +}, +{ +"id":151, +"fortune":"LE COUP DE FOUDRE EXISTE. PORTE DES LUNETTES" +}, +{ +"id":152, +"fortune":"TON EX PENSE À TOI. COMME LEÇON DE VIE" +}, +{ +"id":153, +"fortune":"TA DRAGUE EST SUBTILE. INDÉTECTABLE MÊME" +}, +{ +"id":154, +"fortune":"TES CHOIX AMOUREUX SONT CONSTANTS. HÉLAS" +}, +{ +"id":155, +"fortune":"ON TE CONTEMPLE DE LOIN. GARDE LA DISTANCE" +}, +{ +"id":156, +"fortune":"TES SIGNAUX VERTS SONT EN MINORITÉ" +}, +{ +"id":157, +"fortune":"LE VRAI AMOUR TRIOMPHE. IL DEMANDE DU RENFORT" +}, +{ +"id":158, +"fortune":"LE CŒUR SUR LA MAIN. LAVE LES DEUX" +}, +{ +"id":159, +"fortune":"UN MESSAGE DU PASSÉ ARRIVE. NE RÉPONDS PAS" +}, +{ +"id":160, +"fortune":"TON CRUSH SAIT QUE TU EXISTES. EN THÉORIE" +}, +{ +"id":161, +"fortune":"TU AIMES PROFONDÉMENT. BRIÈVEMENT. MAIS FORT" +}, +{ +"id":162, +"fortune":"TON CŒUR EST OUVERT. TON ARDOISE AUSSI" +}, +{ +"id":163, +"fortune":"DES ÉTINCELLES ARRIVENT. TESTE LE DÉTECTEUR" +}, +{ +"id":164, +"fortune":"TU ES PRÊT À TE POSER. SUR LE CANAPÉ" +}, +{ +"id":165, +"fortune":"TON CŒUR EST GUÉRI. AVEC ALARME EN PRIME" +}, +{ +"id":166, +"fortune":"LE VRAI AMOUR EST PATIENT. GRÂCE À TOI" +}, +{ +"id":167, +"fortune":"TA LUNE DE MIEL A DURÉ ONZE JOURS" +}, +{ +"id":168, +"fortune":"TON ANIMAL EST TA MEILLEURE RELATION" +}, +{ +"id":169, +"fortune":"TES VŒUX FERONT PLEURER. LA GRAMMAIRE SURTOUT" +}, +{ +"id":170, +"fortune":"TU ES UN BON PARTI. PÊCHE AVEC REMISE À LEAU" +}, +{ +"id":171, +"fortune":"TU ES UNE BELLE PRISE. VITE RELÂCHÉE" +}, +{ +"id":172, +"fortune":"TES SIGNAUX ROUGES SONT BORDEAUX. PROGRÈS" +}, +{ +"id":173, +"fortune":"UNE PERSONNE TE TROUVE PARFAIT. DE LOIN" +}, +{ +"id":174, +"fortune":"SECONDES CHANCES POUR TOUS. SIXIÈMES AUSSI" +}, +{ +"id":175, +"fortune":"TA PLAYLIST DE RUPTURE TOURNE SOUVENT" +}, +{ +"id":176, +"fortune":"TU MÉRITES UNE LONGUE ATTENTE. ELLE CONTINUE" +}, +{ +"id":177, +"fortune":"LA ROMANCE VIT ENCORE. LA TIENNE SE REPOSE" +}, +{ +"id":178, +"fortune":"TA BELLE FAMILLE VA CÉDER. AVEC LE TEMPS" +}, +{ +"id":179, +"fortune":"TON PROFIL EST HONNÊTE. PAR ENDROITS" +}, +{ +"id":180, +"fortune":"TES CÂLINS SONT RARES. COMME LES ÉCLIPSES" +}, +{ +"id":181, +"fortune":"ON RÊVE DE TA CUISINE. EN SUEUR" +}, +{ +"id":182, +"fortune":"TON CHARME OUVRE DES PORTES. POUSSE PLUTÔT" +}, +{ +"id":183, +"fortune":"TES CRITÈRES ONT VU TES CHOIX. MALAISE" +}, +{ +"id":184, +"fortune":"TU SERAS BALAYÉ PAR LA PASSION. ÉCHAUFFE TOI" +}, +{ +"id":185, +"fortune":"AIMER VEUT DIRE ÊTRE VU. TU NÉGOCIES ENCORE" +}, +{ +"id":186, +"fortune":"VOTRE HISTOIRE SERA RACONTÉE. EN PRÉVENTION" +}, +{ +"id":187, +"fortune":"LA FLAMME BRÛLE ENCORE. BATTUE RECOMMANDÉE" +}, +{ +"id":188, +"fortune":"VOTRE ANNIVERSAIRE APPROCHE. VOICI LE RAPPEL" +}, +{ +"id":189, +"fortune":"AIME TOI TOI MÊME. ÇA TU SAIS DÉJÀ FAIRE" +}, +{ +"id":190, +"fortune":"TON CŒUR A RATÉ UN BATTEMENT. VOIS UN MÉDECIN" +}, +{ +"id":191, +"fortune":"TU RÉVÈLES LE MEILLEUR DES GENS. AU DÉPART" +}, +{ +"id":192, +"fortune":"TU REFUSES DE TE CONTENTER. VOILÀ LES CHAISES" +}, +{ +"id":193, +"fortune":"TON FUTUR PARTENAIRE MUSCLE SA PATIENCE" +}, +{ +"id":194, +"fortune":"TON CŒUR EST UNE FORTERESSE. BOUTIQUE OUVERTE" +}, +{ +"id":195, +"fortune":"TES BESOINS DE TENDRESSE ONT DES HORAIRES" +}, +{ +"id":196, +"fortune":"ON PENSE À TOI SANS TON NUMÉRO" +}, +{ +"id":197, +"fortune":"TU FLIRTES PAR REGARDS. PERSONNE NE LES VOIT" +}, +{ +"id":198, +"fortune":"TES MOTS DOUX SONT DEVENUS DES VOCAUX" +}, +{ +"id":199, +"fortune":"TES AMIS AIMENT TA FRANCHISE. DE LOIN" +}, +{ +"id":200, +"fortune":"UN AMI VA APPELER. IL DÉMÉNAGE BIENTÔT" +}, +{ +"id":201, +"fortune":"TU ES LA COLLE DU GROUPE. ON REMERCIE PEU" +}, +{ +"id":202, +"fortune":"LE GROUPE SAVOURE TON SILENCE" +}, +{ +"id":203, +"fortune":"LES VRAIS AMIS SONT RARES. LES TIENS AUSSI" +}, +{ +"id":204, +"fortune":"GRANDE LOYAUTÉ. PETITE PONCTUALITÉ" +}, +{ +"id":205, +"fortune":"UNE FÊTE APPORTE DE LA JOIE. PARTIR AUSSI" +}, +{ +"id":206, +"fortune":"TU ES LE MARRANT DU GROUPE. SELON TOI" +}, +{ +"id":207, +"fortune":"UN VIEIL AMI REVIENT. IL A BESOIN DE BRAS" +}, +{ +"id":208, +"fortune":"TES CONSEILS SONT APPRÉCIÉS. JAMAIS COMMANDÉS" +}, +{ +"id":209, +"fortune":"TU ILLUMINES LA PIÈCE. ON VEUT UN VARIATEUR" +}, +{ +"id":210, +"fortune":"TON ANNIVERSAIRE SERA FÊTÉ. UN JOUR APRÈS" +}, +{ +"id":211, +"fortune":"TES ANECDOTES EMBELLISSENT. LES FAITS NON" +}, +{ +"id":212, +"fortune":"LES GENS GRAVITENT VERS TOI. PUIS FILENT" +}, +{ +"id":213, +"fortune":"TU SAIS TOUJOURS QUOI DIRE. RAREMENT QUAND" +}, +{ +"id":214, +"fortune":"LES AMIS VONT ET VIENNENT. LES TIENS VONT" +}, +{ +"id":215, +"fortune":"TON AGENDA EST PLEIN. DE PEUT ÊTRE" +}, +{ +"id":216, +"fortune":"TES BLAGUES INTERNES SONT DEVENUES DES SOLOS" +}, +{ +"id":217, +"fortune":"TES SOIRÉES SONT LÉGENDAIRES. LES ANNULÉES" +}, +{ +"id":218, +"fortune":"TU RÉUNIS LES GENS. COMME SUJET DE DISCUSSION" +}, +{ +"id":219, +"fortune":"TA GÉNÉROSITÉ A DES HEURES DE BUREAU" +}, +{ +"id":220, +"fortune":"TU RETIENS CHAQUE VISAGE. AUCUN PRÉNOM" +}, +{ +"id":221, +"fortune":"TES ENCOURAGEMENTS DEMANDENT UN SUIVI MÉDICAL" +}, +{ +"id":222, +"fortune":"TU ES LÀ QUAND ÇA COMPTE. POUR TOI" +}, +{ +"id":223, +"fortune":"TES POTS DE DÉPART FONT SALLE COMBLE" +}, +{ +"id":224, +"fortune":"ON TE CONFIE UN SECRET. PUIS TU LE PARTAGES" +}, +{ +"id":225, +"fortune":"TA FRANCHISE RAFRAÎCHIT. COMME UN SEAU GLACÉ" +}, +{ +"id":226, +"fortune":"TU NE GARDES PAS RANCUNE. TU CLASSES TOUT" +}, +{ +"id":227, +"fortune":"TES MEMES ARRIVENT DÈS 6 HEURES. SANS DEMANDE" +}, +{ +"id":228, +"fortune":"TON ÉPAULE EST DISPONIBLE. SUR RENDEZ VOUS" +}, +{ +"id":229, +"fortune":"TU GARDES LES GENS AU SOL. COMME LA BRUME" +}, +{ +"id":230, +"fortune":"TON ENTRAIN SE PROPAGE. MASQUE CONSEILLÉ" +}, +{ +"id":231, +"fortune":"UN AMI VEUT TON AVIS SINCÈRE. PIÈGE ÉVIDENT" +}, +{ +"id":232, +"fortune":"TU PARTAGES LA NOTE EN PARTS ÉGALES. PRESQUE" +}, +{ +"id":233, +"fortune":"TA PRÉSENCE EST UN CADEAU. ÉCHANGE POSSIBLE" +}, +{ +"id":234, +"fortune":"TON ESPRIT DE COMPÉTITION A TUÉ LES JEUX" +}, +{ +"id":235, +"fortune":"TES FÊTES SURPRISES NE SURPRENNENT PERSONNE" +}, +{ +"id":236, +"fortune":"ON SE SENT BIEN AVEC TOI. EN PARTANT" +}, +{ +"id":237, +"fortune":"TU DIS CE QUE CHACUN PENSE. AUX FUNÉRAILLES" +}, +{ +"id":238, +"fortune":"TA LETTRE ANNUELLE A SON LECTORAT" +}, +{ +"id":239, +"fortune":"TES EXCUSES SONT SINCÈRES. ET FRÉQUENTES" +}, +{ +"id":240, +"fortune":"TU TIENS LA MÉMOIRE DU GROUPE. RÉVISÉE" +}, +{ +"id":241, +"fortune":"TU AIDES SANS BRUIT. PUIS TU LE RACONTES" +}, +{ +"id":242, +"fortune":"TA FRANCHISE EST CHARMANTE. MOT DU COMITÉ" +}, +{ +"id":243, +"fortune":"TU ES EN RETARD AVEC STYLE. ENFIN EN RETARD" +}, +{ +"id":244, +"fortune":"ON CHERCHE TON APPROBATION. POUR PARTIR" +}, +{ +"id":245, +"fortune":"TU NE MÉDIS JAMAIS. TU INFORMES LES ABSENTS" +}, +{ +"id":246, +"fortune":"LA CARTE DIT DÉCONTRACTÉ. TES RELANCES NON" +}, +{ +"id":247, +"fortune":"UN AMI DE JEUNESSE PENSE À TOI. DE LOIN" +}, +{ +"id":248, +"fortune":"TU NE PERDS JAMAIS UN DÉBAT. TU LE DÉPLACES" +}, +{ +"id":249, +"fortune":"TON SOUTIEN EST SANS CONDITION. VOIR ANNEXE" +}, +{ +"id":250, +"fortune":"LA RICHESSE ARRIVE. À TON ANCIENNE ADRESSE" +}, +{ +"id":251, +"fortune":"LES FONDS COULENT VERS TOI. À TRAVERS TOI" +}, +{ +"id":252, +"fortune":"TON BUDGET EST UNE ŒUVRE. ABSTRAITE" +}, +{ +"id":253, +"fortune":"ÉPARGNE POUR LA PLUIE. MOUSSON EN VUE" +}, +{ +"id":254, +"fortune":"TON DOSSIER BANCAIRE EST UN THRILLER" +}, +{ +"id":255, +"fortune":"TES ÉCONOMIES GRANDISSENT. EN CARACTÈRE" +}, +{ +"id":256, +"fortune":"TON ARGENT PARLE. IL DIT ADIEU" +}, +{ +"id":257, +"fortune":"TA RETRAITE REPOSE SUR UN TICKET DE LOTO" +}, +{ +"id":258, +"fortune":"TES GOÛTS SONT CHERS. TON COMPTE EST MODESTE" +}, +{ +"id":259, +"fortune":"INTÉRÊTS COMPOSÉS. TU AS CHOISI LES DETTES" +}, +{ +"id":260, +"fortune":"TON FONDS DE SECOURS COUVRE UN PETIT SOUCI" +}, +{ +"id":261, +"fortune":"UNE AUGMENTATION EST MÉRITÉE. ET NON DATÉE" +}, +{ +"id":262, +"fortune":"TON PANIER EST UN RÊVE AVEC UNE CAISSE" +}, +{ +"id":263, +"fortune":"TA GÉNÉROSITÉ EST CONNUE. DES COMMERÇANTS" +}, +{ +"id":264, +"fortune":"LE MEILLEUR EST GRATUIT. LE TIEN A DES FRAIS" +}, +{ +"id":265, +"fortune":"TES ABONNEMENTS DÉPASSENT TES AMIS. EN NOMBRE" +}, +{ +"id":266, +"fortune":"ACHETER BAS VENDRE HAUT. TU FAIS LE CONTRAIRE" +}, +{ +"id":267, +"fortune":"TA BANQUE TE CONTACTE PAR INQUIÉTUDE" +}, +{ +"id":268, +"fortune":"TON REMBOURSEMENT FISCAL A DES PROJETS" +}, +{ +"id":269, +"fortune":"TES ACHATS SONT STRATÉGIQUES. PARAÎT IL" +}, +{ +"id":270, +"fortune":"UN BILLET DORT DANS UN MANTEAU. LE TIEN" +}, +{ +"id":271, +"fortune":"TON PORTEFEUILLE EST DIVERSIFIÉ. EN REGRETS" +}, +{ +"id":272, +"fortune":"LE JOUR DE PAIE APPORTE 18 HEURES DE JOIE" +}, +{ +"id":273, +"fortune":"TES FRAIS DE RETARD ONT DES FRAIS DE RETARD" +}, +{ +"id":274, +"fortune":"LES PETITS RUISSEAUX SÈCHENT. LES TIENS VITE" +}, +{ +"id":275, +"fortune":"RICHE DE CŒUR. LA CAISSE VOIT AUTREMENT" +}, +{ +"id":276, +"fortune":"TES TICKETS DE CAISSE RACONTENT UNE SAGA" +}, +{ +"id":277, +"fortune":"TON APPORT EST DEVENU DES SUSHIS. DEUX FOIS" +}, +{ +"id":278, +"fortune":"TON BUDGET PRÉVISIONNEL EST DE LA FICTION" +}, +{ +"id":279, +"fortune":"TES ESSAIS GRATUITS VIVENT ET PRÉLÈVENT" +}, +{ +"id":280, +"fortune":"LE CASH EST ROI. TON ROYAUME VIT À CRÉDIT" +}, +{ +"id":281, +"fortune":"TON GEL DES DÉPENSES A TENU 40 MINUTES" +}, +{ +"id":282, +"fortune":"TES FACTURES SONT PONCTUELLES. PAS TOI" +}, +{ +"id":283, +"fortune":"TON ARGENT TRAVAILLE DUR. POUR LES AUTRES" +}, +{ +"id":284, +"fortune":"TES BONS DE RÉDUCTION ONT EXPIRÉ. TOUS" +}, +{ +"id":285, +"fortune":"TON PLAN FINANCIER A DES PHASES. DÉNI SURTOUT" +}, +{ +"id":286, +"fortune":"TES REMISES FINANCENT TES ACHATS IMPULSIFS" +}, +{ +"id":287, +"fortune":"TA PHASE CRYPTO FUT INSTRUCTIVE. ET COÛTEUSE" +}, +{ +"id":288, +"fortune":"LE LOTO TAXE LES RÊVEURS. GÉNÉREUX VA" +}, +{ +"id":289, +"fortune":"TON OBJECTIF ÉPARGNE DORT DEPUIS MARS" +}, +{ +"id":290, +"fortune":"LA RICHESSE CHUCHOTE. LA TIENNE A RACCROCHÉ" +}, +{ +"id":291, +"fortune":"TU NOTES CHAQUE DÉPENSE. PUIS TU RECOMMENCES" +}, +{ +"id":292, +"fortune":"TA TIRELIRE A DES PROBLÈMES DE CONFIANCE" +}, +{ +"id":293, +"fortune":"TON VIREMENT EST ARRIVÉ. IL EST DÉJÀ REPARTI" +}, +{ +"id":294, +"fortune":"LE BONHEUR EST SANS PRIX. TON SOLDE CONFIRME" +}, +{ +"id":295, +"fortune":"TES FACTURES VIEILLISSENT COMME UN GRAND CRU" +}, +{ +"id":296, +"fortune":"TON SOLDE ET TON MORAL ONT PLONGÉ ENSEMBLE" +}, +{ +"id":297, +"fortune":"TU RANGES TES BOÎTES DANS DES BOÎTES" +}, +{ +"id":298, +"fortune":"LA LIVRAISON GRATUITE TE COÛTE UNE FORTUNE" +}, +{ +"id":299, +"fortune":"LA PATIENCE EST UNE VERTU. PAS TON GENRE" +}, +{ +"id":300, +"fortune":"LE SILENCE EST PRÉCIEUX. LE TIEN EST RARE" +}, +{ +"id":301, +"fortune":"TOURNE TA LANGUE SEPT FOIS. PROJET PILOTE" +}, +{ +"id":302, +"fortune":"EAU CALME EAU PROFONDE. LA TIENNE FAIT 10 CM" +}, +{ +"id":303, +"fortune":"LE SAVOIR EST UNE ARME. LA TIENNE EN VEILLE" +}, +{ +"id":304, +"fortune":"UN ESPRIT OUVERT EST UN TRÉSOR. BEAU COURANT" +}, +{ +"id":305, +"fortune":"ÉCOUTE DEUX FOIS PARLE UNE FOIS. INVERSE TOUT" +}, +{ +"id":306, +"fortune":"LA VÉRITÉ REND LIBRE. LA TIENNE RESTE ASSISE" +}, +{ +"id":307, +"fortune":"REGARDE AVANT DE SAUTER. OU APRÈS. À TA GUISE" +}, +{ +"id":308, +"fortune":"LES ACTES PARLENT FORT. LES TIENS CHUCHOTENT" +}, +{ +"id":309, +"fortune":"MESURE DEUX FOIS COUPE UNE FOIS. TU COUPES" +}, +{ +"id":310, +"fortune":"MIEUX VAUT TARD QUE JAMAIS. TU TESTES FORT" +}, +{ +"id":311, +"fortune":"QUI VOLE UN ŒUF VOLE UN BŒUF. TU COMPTES TOUT" +}, +{ +"id":312, +"fortune":"PIERRE QUI ROULE. LA TIENNE A PRIS LA MOUSSE" +}, +{ +"id":313, +"fortune":"TONNEAU VIDE FAIT DU BRUIT. BELLE ACOUSTIQUE" +}, +{ +"id":314, +"fortune":"QUI RIRA LE DERNIER PENSAIT LENTEMENT. SALUT" +}, +{ +"id":315, +"fortune":"UN TIENS VAUT MIEUX. LE TIEN A FILÉ" +}, +{ +"id":316, +"fortune":"NE RÉPARE QUE LE CASSÉ. TROP TARD DÉSORMAIS" +}, +{ +"id":317, +"fortune":"TU MÈNES LES CHEVAUX AU PUITS. ILS REFUSENT" +}, +{ +"id":318, +"fortune":"LA FIERTÉ PRÉCÈDE LA CHUTE. BEL ÉQUILIBRE" +}, +{ +"id":319, +"fortune":"LE TEMPS GUÉRIT TOUT. TU GRATTES LA PLAIE" +}, +{ +"id":320, +"fortune":"LES VIEILLES HABITUDES RÉSISTENT. SURTOUT ICI" +}, +{ +"id":321, +"fortune":"OÙ IL Y A DE LA FUMÉE. IL Y A TOI" +}, +{ +"id":322, +"fortune":"LA MEILLEURE DÉFENSE. LA DISTANCE. ILS SAVENT" +}, +{ +"id":323, +"fortune":"LE DIABLE AIME LES DÉTAILS. LES TIENS DORMENT" +}, +{ +"id":324, +"fortune":"LE REBUT DES UNS REMPLIT TON GARAGE" +}, +{ +"id":325, +"fortune":"MARMITE SURVEILLÉE NE BOUT PAS. LA TIENNE SI" +}, +{ +"id":326, +"fortune":"QUI NE SAIT RIEN DORT BIEN. TU SEMBLES REPOSÉ" +}, +{ +"id":327, +"fortune":"LA BEAUTÉ EST DANS LE REGARD. CONSULTE VITE" +}, +{ +"id":328, +"fortune":"IL FAUT UN VILLAGE. LE TIEN VEUT DU RENFORT" +}, +{ +"id":329, +"fortune":"LES GRANDS ESPRITS SE CROISENT. LE TIEN FUIT" +}, +{ +"id":330, +"fortune":"CHAQUE CHIEN A SON JOUR. LE TIEN FUT EN 2019" +}, +{ +"id":331, +"fortune":"QUI NE TENTE RIEN. TOI EN RÉSUMÉ" +}, +{ +"id":332, +"fortune":"TROP DE CHEFS GÂTENT LA SOUPE. TOI SEUL AUSSI" +}, +{ +"id":333, +"fortune":"ON RÉCOLTE CE QUI FUT SEMÉ. RÉCOLTE CURIEUSE" +}, +{ +"id":334, +"fortune":"DEMANDE ET TU RECEVRAS. IL Y A UNE FILE" +}, +{ +"id":335, +"fortune":"PRENDS LES CHAUSSURES DES AUTRES. PUIS RENDS" +}, +{ +"id":336, +"fortune":"RIEN NE SERT DE COURIR. TU NE COURS JAMAIS" +}, +{ +"id":337, +"fortune":"TOUT A UNE FIN. LES TIENNES ARRIVENT TÔT" +}, +{ +"id":338, +"fortune":"LES PAROLES SENVOLENT. LES TIENNES AUSSI" +}, +{ +"id":339, +"fortune":"LE TEMPS PERDU NE REVIENT PAS. TU LE STOCKES" +}, +{ +"id":340, +"fortune":"LA MAIN QUI TE NOURRIT PORTE DES GANTS" +}, +{ +"id":341, +"fortune":"COMPARER VOLE LA JOIE. CHEZ TOI À PLEIN TEMPS" +}, +{ +"id":342, +"fortune":"MIEUX VAUT MOITIÉ QUE RIEN. TOI. DES MIETTES" +}, +{ +"id":343, +"fortune":"MER CALME NE FORME PAS DE MARIN. RESTE À QUAI" +}, +{ +"id":344, +"fortune":"LA VÉRITÉ VIT SANS RÉPÉTITION. SAUF LA TIENNE" +}, +{ +"id":345, +"fortune":"TOUT AVEC MODÉRATION. MÊME ÇA. ARRÊTE" +}, +{ +"id":346, +"fortune":"CE QUI PART REVIENT. SUIVI DE COLIS ACTIVÉ" +}, +{ +"id":347, +"fortune":"NE DISCUTE PAS AVEC UN SOT. ON VOUS CONFOND" +}, +{ +"id":348, +"fortune":"LA SAGESSE CHUCHOTE. LA TIENNE EST EN MUET" +}, +{ +"id":349, +"fortune":"UN LONG VOYAGE COMMENCE PAR UN PAS. ÉTIRE TOI" +}, +{ +"id":350, +"fortune":"TRAVERSE LES PONTS. SANS LES BRÛLER. INFO" +}, +{ +"id":351, +"fortune":"LE GRAND CHÊNE FUT UNE NOIX. BELLE TRADITION" +}, +{ +"id":352, +"fortune":"CHAT ÉCHAUDÉ CRAINT. TOI TU REPLONGES" +}, +{ +"id":353, +"fortune":"LES MENSONGES ONT DE COURTES JAMBES. RAPIDES" +}, +{ +"id":354, +"fortune":"CHACUN FORGE SA CHANCE. TA FORGE EST ÉTEINTE" +}, +{ +"id":355, +"fortune":"LA POMME TOMBE PRÈS DU POMMIER. LUI SE TAIT" +}, +{ +"id":356, +"fortune":"PETIT À PETIT LES NIDS SE FONT. LES TIENS NON" +}, +{ +"id":357, +"fortune":"LA NUIT PORTE CONSEIL. LA TIENNE DES SÉRIES" +}, +{ +"id":358, +"fortune":"LE GRAND VOYAGE ATTEND. IL A MONTÉ SA BOÎTE" +}, +{ +"id":359, +"fortune":"TU VOYAGERAS LOIN. LA NAVETTE COMPTE" +}, +{ +"id":360, +"fortune":"TA PHOTO DE PASSEPORT MONTRE LE VRAI TOI" +}, +{ +"id":361, +"fortune":"TA LISTE DE RÊVES A BESOIN DE RÊVES" +}, +{ +"id":362, +"fortune":"TU VERRAS LE MONDE. EN DOCUMENTAIRE" +}, +{ +"id":363, +"fortune":"TES CONGÉS MÛRISSENT COMME UN BON REGRET" +}, +{ +"id":364, +"fortune":"UNE DESTINATION DE RÊVE APPELLE. TU FILTRES" +}, +{ +"id":365, +"fortune":"TA PLAYLIST DE ROUTE A SURVÉCU AU VOYAGE" +}, +{ +"id":366, +"fortune":"TU VOYAGES LÉGER. SAUF ÉMOTIONNELLEMENT" +}, +{ +"id":367, +"fortune":"TES SOUVENIRS PARLENT SURTOUT DES PRIX" +}, +{ +"id":368, +"fortune":"TU TE PERDS PARTOUT. TALENT PORTABLE" +}, +{ +"id":369, +"fortune":"TES PHOTOS DE VOYAGE STARIFIENT TON POUCE" +}, +{ +"id":370, +"fortune":"UN VOYAGE SPONTANÉ APPROCHE. AVEC TABLEUR" +}, +{ +"id":371, +"fortune":"TU ES CITOYEN DU MONDE. RÉFÉRENCES À VENIR" +}, +{ +"id":372, +"fortune":"TON CORPS EST PRÊT. LA PLAGE FERA AVEC" +}, +{ +"id":373, +"fortune":"LA MONTAGNE APPELLE. TU AS COUPÉ LA SONNERIE" +}, +{ +"id":374, +"fortune":"TES VACANCES AU BALCON RESTENT IMBATTABLES" +}, +{ +"id":375, +"fortune":"TU NAVIGUES COMME UN GUIDE. UN GUIDE PERDU" +}, +{ +"id":376, +"fortune":"TON ITINÉRAIRE EST SURTOUT DES MARGES" +}, +{ +"id":377, +"fortune":"TES CHAUSSURES DE RANDO ONT GRAVI LE PLACARD" +}, +{ +"id":378, +"fortune":"LE DÉCALAGE ÉPARGNE PERSONNE. TU PARS FATIGUÉ" +}, +{ +"id":379, +"fortune":"TON CARNET DE VOYAGE A UNE ENTRÉE. CONTENT" +}, +{ +"id":380, +"fortune":"TU COLLECTIONNES LES MOMENTS. PAS LA CARTE" +}, +{ +"id":381, +"fortune":"TON SENS DES DIRECTIONS EST UNE LÉGENDE" +}, +{ +"id":382, +"fortune":"UNE ESCAPADE APPROCHE. LE LINGE ATTENDRA" +}, +{ +"id":383, +"fortune":"TU REVIENS TRANSFORMÉ. SURTOUT FATIGUÉ" +}, +{ +"id":384, +"fortune":"TON HORAIRE AU TERMINAL DIT TOUT DE TOI" +}, +{ +"id":385, +"fortune":"TES PHOTOS DE HUBLOT. UNE SÉRIE SUR LES AILES" +}, +{ +"id":386, +"fortune":"TON BAGAGE À MAIN A SA PROPRE GRAVITÉ" +}, +{ +"id":387, +"fortune":"TES TALENTS DE CAMPEUR AMUSENT LES RATONS" +}, +{ +"id":388, +"fortune":"LA PLUIE VA LAVER TA VOITURE. TON PLAN EXACT" +}, +{ +"id":389, +"fortune":"TON DÉBAT SUR LA VESTE COMMENCE EN OCTOBRE" +}, +{ +"id":390, +"fortune":"TU CONSULTES LA MÉTÉO. PUIS TU IGNORES TOUT" +}, +{ +"id":391, +"fortune":"LE PRINTEMPS ARRIVE. TES ALLERGIES AUSSI" +}, +{ +"id":392, +"fortune":"TA CRÈME SOLAIRE EXPIRE INTACTE. CHAQUE ANNÉE" +}, +{ +"id":393, +"fortune":"LA NEIGE EST MAGIQUE. PUIS VIENT LA PELLE" +}, +{ +"id":394, +"fortune":"TES PARAPLUIES VIVENT TOUS AILLEURS" +}, +{ +"id":395, +"fortune":"LA MÉTÉO EST TON GRAND SUJET DÉSORMAIS" +}, +{ +"id":396, +"fortune":"LA PRÉVISION A CHANGÉ. PAS TES PLANS. VIDES" +}, +{ +"id":397, +"fortune":"TON SALON DE JARDIN CRAINT CHAQUE SAISON" +}, +{ +"id":398, +"fortune":"LES FEUILLES TOMBENT. COMME TES RÉSOLUTIONS" +}, +{ +"id":399, +"fortune":"TA BATTERIE TIENT PLUS LONGTEMPS QUE TOI" +}, +{ +"id":400, +"fortune":"TU LIRAS LES CONDITIONS UN JOUR. NON" +}, +{ +"id":401, +"fortune":"TON RAPPORT DE TEMPS DEVANT ÉCRAN TE MÉNAGE" +}, +{ +"id":402, +"fortune":"TA CHAISE À LINGE A UN POSTE FIXE DÉSORMAIS" +}, +{ +"id":403, +"fortune":"TON RÉVEIL A CINQ RÉVEILS DE SECOURS. MUETS" +}, +{ +"id":404, +"fortune":"TON FRIGO HÉBERGE UNE EXPÉRIENCE SCIENTIFIQUE" +}, +{ +"id":405, +"fortune":"LE VOYANT MOTEUR EST DEVENU UNE AMBIANCE" +}, +{ +"id":406, +"fortune":"TES PLANTES VERTES ONT ACCEPTÉ LEUR SORT" +}, +{ +"id":407, +"fortune":"TON TIROIR FOURRE TOUT A OUVERT UNE ANNEXE" +}, +{ +"id":408, +"fortune":"TON SAC DE SPORT EST PRÊT. ÇA COMPTE" +}, +{ +"id":409, +"fortune":"TA MESSAGERIE EST PLEINE. ON A RENONCÉ" +}, +{ +"id":410, +"fortune":"TES ONGLETS FORMENT UN ÉCOSYSTÈME" +}, +{ +"id":411, +"fortune":"TON PARAPLUIE ATTEND. TU VAS LE LAISSER LÀ" +}, +{ +"id":412, +"fortune":"TES RESTES AVAIENT DES RÊVES. ILS COMPOSTENT" +}, +{ +"id":413, +"fortune":"TA LISTE DE TÂCHES A UNE LISTE DE TÂCHES" +}, +{ +"id":414, +"fortune":"TON ARMOIRE HABILLE TROIS VERSIONS DE TOI" +}, +{ +"id":415, +"fortune":"TON JOGGING MATINAL EST PLANIFIÉ. DEPUIS MARS" +}, +{ +"id":416, +"fortune":"TON ROUTEUR TRÔNE SUR UN AUTEL. NORMAL" +}, +{ +"id":417, +"fortune":"TA BOÎTE MAIL VIDE RESTE UNE LÉGENDE" +}, +{ +"id":418, +"fortune":"TON TIROIR À CHAUSSETTES PROTÈGE LES VEUVES" +}, +{ +"id":419, +"fortune":"TA LISTE DE COURSES RESTE À LA MAISON. EXPRÈS" +}, +{ +"id":420, +"fortune":"TA LISTE DE SÉRIES A SURVÉCU À TROIS PASSIONS" +}, +{ +"id":421, +"fortune":"TON IMPRIMANTE SENT LA PANIQUE. DONC PANNE" +}, +{ +"id":422, +"fortune":"TON COUCHER EST UNE MOTION. TOUJOURS REJETÉE" +}, +{ +"id":423, +"fortune":"TES ÉCOUTEURS SE NOUENT SEULS. MYSTÈRE TOTAL" +}, +{ +"id":424, +"fortune":"TES ÉPICES SONT CLASSÉES DE A À C. BON DÉBUT" +}, +{ +"id":425, +"fortune":"TA BANQUETTE ARRIÈRE EST UN BUREAU DES OBJETS" +}, +{ +"id":426, +"fortune":"LE BON CÂBLE EST TOUJOURS AILLEURS" +}, +{ +"id":427, +"fortune":"TON BUREAU DEBOUT RESTE ASSIS. COMME TOI" +}, +{ +"id":428, +"fortune":"TON BATCH COOKING DONNE UN PLAT. SIX BOÎTES" +}, +{ +"id":429, +"fortune":"LE LIVREUR TE SALUE DE LOIN MAINTENANT" +}, +{ +"id":430, +"fortune":"TA FRITEUSE À AIR A REMPLACÉ LE FOUR. ET TOI" +}, +{ +"id":431, +"fortune":"TES MARQUE PAGES SONT UN MUSÉE DES INTENTIONS" +}, +{ +"id":432, +"fortune":"TON BOUTON SNOOZE MONTRE UNE USURE GÉOLOGIQUE" +}, +{ +"id":433, +"fortune":"TES SACS RÉUTILISABLES VIVENT DANS LE COFFRE" +}, +{ +"id":434, +"fortune":"TES MISES À JOUR ONT DES ANNÉES DE SERVICE" +}, +{ +"id":435, +"fortune":"LE BAC À GLAÇONS REVIENT VIDE. UNE TRADITION" +}, +{ +"id":436, +"fortune":"TON GARAGE STOCKE TOUT SAUF LA VOITURE" +}, +{ +"id":437, +"fortune":"TON PODOMÈTRE A ENCADRÉ SON MEILLEUR JOUR" +}, +{ +"id":438, +"fortune":"TA TASSE PRÉFÉRÉE EST SOUS PROTECTION" +}, +{ +"id":439, +"fortune":"LA MÉTÉO A RENONCÉ À TES TENUES" +}, +{ +"id":440, +"fortune":"TA BALANCE PRATIQUE LA FRANCHISE RADICALE" +}, +{ +"id":441, +"fortune":"TES TÉLÉCOMMANDES MIGRENT DANS LE CANAPÉ" +}, +{ +"id":442, +"fortune":"LE PLACARD DU COULOIR GARDE TA VÉRITÉ. JUSTE" +}, +{ +"id":443, +"fortune":"TU AS TOUTES LES PILES SAUF LA BONNE" +}, +{ +"id":444, +"fortune":"TON ASPIRATEUR ÉVITE LES COINS. COMME TOI" +}, +{ +"id":445, +"fortune":"TES IDÉES DE DOUCHE RESTENT SOUS LA DOUCHE" +}, +{ +"id":446, +"fortune":"TA CAFETIÈRE CONNAÎT TA TÊTE DU LUNDI" +}, +{ +"id":447, +"fortune":"LE FOND DU CONGÉLATEUR PRÉCÈDE TES AMITIÉS" +}, +{ +"id":448, +"fortune":"TON STOCKAGE EST PLEIN. DE CAPTURES INUTILES" +}, +{ +"id":449, +"fortune":"TES PNEUS DE VÉLO SOUPIRENT LENTEMENT" +}, +{ +"id":450, +"fortune":"TA PILE DE RETOURS A UNE ZONE DE DÉPART" +}, +{ +"id":451, +"fortune":"TES BOUGIES SONT DÉCO. LES ALLUMETTES AUSSI" +}, +{ +"id":452, +"fortune":"TA TABLE À MANGER A QUITTÉ LE MÉTIER" +}, +{ +"id":453, +"fortune":"TON CLAVIER PROPOSE PARDON EN PREMIER" +}, +{ +"id":454, +"fortune":"TON APPLI DE SPORT FAIT DES PHRASES COURTES" +}, +{ +"id":455, +"fortune":"TA POCHE DE MANTEAU EST UNE CAPSULE DU TEMPS" +}, +{ +"id":456, +"fortune":"LE BOUTON PLUS 30 SECONDES GÈRE TA CUISINE" +}, +{ +"id":457, +"fortune":"LES BONS CISEAUX ONT DISPARU. UN SEUL SUSPECT" +}, +{ +"id":458, +"fortune":"TON DOSSIER TÉLÉCHARGEMENTS EST UNE DÉCHARGE" +}, +{ +"id":459, +"fortune":"TES PLANTES VIVENT DE CULPABILITÉ" +}, +{ +"id":460, +"fortune":"LE SÈCHE LINGE PREND UNE CHAUSSETTE EN TRIBUT" +}, +{ +"id":461, +"fortune":"TES DESSOUS DE VERRE REGARDENT SANS AGIR" +}, +{ +"id":462, +"fortune":"TON APPLI DE NOTES EST UNE DÉCHARGE DE GÉNIE" +}, +{ +"id":463, +"fortune":"TES JEUX DE SOCIÉTÉ VIEILLISSENT SOUS BLISTER" +}, +{ +"id":464, +"fortune":"TON TAPIS DE YOGA A SERVI DEUX FOIS. SIESTES" +}, +{ +"id":465, +"fortune":"TES RALLONGES SE MULTIPLIENT DANS LE NOIR" +}, +{ +"id":466, +"fortune":"TON ENCEINTE CONNECTÉE A ENTENDU DES CHOSES" +}, +{ +"id":467, +"fortune":"TES BOÎTES ET LEURS COUVERCLES DIVORCENT" +}, +{ +"id":468, +"fortune":"TES BASKETS DE MARCHE VISITENT LE COFFRE" +}, +{ +"id":469, +"fortune":"TON DOSSIER IMPORTANT EST UN MOUROIR" +}, +{ +"id":470, +"fortune":"TA COURONNE DE NOËL EST PERMANENTE. FESTIF" +}, +{ +"id":471, +"fortune":"LE TIROIR QUI COINCE A CESSÉ DE PARLER" +}, +{ +"id":472, +"fortune":"TON ÉCRAN À 2 HEURES DU MATIN RÉVÈLE TOUT" +}, +{ +"id":473, +"fortune":"TON EXTINCTEUR VIT SUR SA RÉPUTATION" +}, +{ +"id":474, +"fortune":"TON OBJECTIF EAU FUT ATTEINT UNE FOIS. BRAVO" +}, +{ +"id":475, +"fortune":"VIEILLIR TE VA BIEN. LE CONTRAIRE MOINS" +}, +{ +"id":476, +"fortune":"TON ÂGE EST UN SIMPLE NOMBRE. CROISSANT" +}, +{ +"id":477, +"fortune":"TES GENOUX ANNONCENT LA PLUIE. UN TALENT" +}, +{ +"id":478, +"fortune":"TON DOS EST SORTI PLUS SOUVENT QUE TOI" +}, +{ +"id":479, +"fortune":"LA SAGESSE VIENT AVEC LE GRIS. LE TIEN EST LÀ" +}, +{ +"id":480, +"fortune":"TU VIEILLIS COMME LE VIN. COUCHÉ SURTOUT" +}, +{ +"id":481, +"fortune":"TES BOUGIES POSENT UN RISQUE INCENDIE" +}, +{ +"id":482, +"fortune":"TES LUNETTES DE LECTURE ONT DES LUNETTES" +}, +{ +"id":483, +"fortune":"TA PLAYLIST EST CLASSÉE RÉTRO. PAR LES JEUNES" +}, +{ +"id":484, +"fortune":"TU GÉMIS EN PRENANT PLACE. UN REÇU VOCAL" +}, +{ +"id":485, +"fortune":"TON MÉTABOLISME EST PARTI SANS UN MOT" +}, +{ +"id":486, +"fortune":"TU TE SENS COMME À 25 ANS. TES LENDEMAINS NON" +}, +{ +"id":487, +"fortune":"UNE POMME PAR JOUR. EN TARTE. BELLE ASTUCE" +}, +{ +"id":488, +"fortune":"TA POSTURE PROGRESSE DEUX FOIS PAR JOUR. BREF" +}, +{ +"id":489, +"fortune":"TU DORMIRAS HUIT HEURES. EN TROIS NUITS" +}, +{ +"id":490, +"fortune":"TES VITAMINES EXPIRENT INTACTES. CHAQUE FOIS" +}, +{ +"id":491, +"fortune":"TON RÉGIME COMMENCE LUNDI. LES LUNDIS FUIENT" +}, +{ +"id":492, +"fortune":"LE DENTISTE SAIT QUE LE FIL DATE DU MATIN" +}, +{ +"id":493, +"fortune":"TON POULS AU REPOS EST PARFAIT. LOGIQUE" +}, +{ +"id":494, +"fortune":"TU VIVRAS CENT ANS. PAR PURE REVANCHE" +}, +{ +"id":495, +"fortune":"TON TRACKER DE SOMMEIL PARLE DE FICTION" +}, +{ +"id":496, +"fortune":"TES ALLERGIES SONT PONCTUELLES. ELLES OUI" +}, +{ +"id":497, +"fortune":"TON MAL DE DOS A UNE VIE SOCIALE RICHE" +}, +{ +"id":498, +"fortune":"TES TALENTS CACHÉS RESTENT BIEN CACHÉS" +}, +{ +"id":499, +"fortune":"TA CRÉATIVITÉ NE CONNAÎT QUE LES PAUSES" +}, +{ +"id":500, +"fortune":"TU AS UN DON. SANS TICKET DE CAISSE" +}, +{ +"id":501, +"fortune":"TON ROMAN SERA FINI. PAR UNE AUTRE PERSONNE" +}, +{ +"id":502, +"fortune":"NÉ POUR CRÉER. LES EXCUSES COMPTENT AUSSI" +}, +{ +"id":503, +"fortune":"TON GRAND ŒUVRE VIT EN TOI. IL Y RESTE" +}, +{ +"id":504, +"fortune":"TON CHANT FAIT PLEURER. LA BIOLOGIE EST AINSI" +}, +{ +"id":505, +"fortune":"LA MUSE TE VISITE. ELLE PRÉVIENT AVANT" +}, +{ +"id":506, +"fortune":"TES PHOTOS CAPTURENT UNE CONFUSION BRUTE" +}, +{ +"id":507, +"fortune":"TU PENSES HORS DU CADRE. LE CADRE RESPIRE" +}, +{ +"id":508, +"fortune":"TES POÈMES ÉMEUVENT. DIRECTION LA SORTIE" +}, +{ +"id":509, +"fortune":"TON IMAGINATION EST VASTE. PAS TES ŒUVRES" +}, +{ +"id":510, +"fortune":"TU VOIS LE MONDE AUTREMENT. FAIS CONTRÔLER" +}, +{ +"id":511, +"fortune":"TON IDÉE DE PODCAST SALUE LES SIX AUTRES" +}, +{ +"id":512, +"fortune":"LA CRÉATIVITÉ PASSE DEVANT TOI. ELLE SALUE" +}, +{ +"id":513, +"fortune":"TES CROQUIS MÉRITENT UNE PLACE. UN TIROIR" +}, +{ +"id":514, +"fortune":"TA CUISINE EST DU SPECTACLE. AVEC ALARME" +}, +{ +"id":515, +"fortune":"TON SCÉNARIO A UNE ÉNERGIE POTENTIELLE" +}, +{ +"id":516, +"fortune":"LE MONDE EST TA TOILE. ENCORE VIERGE" +}, +{ +"id":517, +"fortune":"TES PROJETS FORMENT UNE TRILOGIE OUVERTE" +}, +{ +"id":518, +"fortune":"TES GOÛTS MUSICAUX SONT SÛRS. SELON TOI" +}, +{ +"id":519, +"fortune":"TA DANSE DÉFIE LES CODES. ET LA PHYSIQUE" +}, +{ +"id":520, +"fortune":"TU RACONTES BIEN. LES FINS RESTENT À VENIR" +}, +{ +"id":521, +"fortune":"LES PHARMACIENS ENCADRENT TON ÉCRITURE" +}, +{ +"id":522, +"fortune":"TES IDÉES VIENNENT À 2 HEURES. PARTENT À 8" +}, +{ +"id":523, +"fortune":"TON CARNET GARDE DE GRANDES PROMESSES" +}, +{ +"id":524, +"fortune":"TU POURRAIS ÉCRIRE UN LIVRE. UN DÉPLIANT" +}, +{ +"id":525, +"fortune":"TA PHASE POTERIE A CRÉÉ DES PIÈCES UNIQUES" +}, +{ +"id":526, +"fortune":"TON ARTISTE INTÉRIEUR VEUT UN CAFÉ AVANT" +}, +{ +"id":527, +"fortune":"TES CHOIX DE COULEURS SONT COURAGEUX" +}, +{ +"id":528, +"fortune":"TA GUITARE MAÎTRISE TROIS ACCORDS" +}, +{ +"id":529, +"fortune":"TON PROCESSUS CRÉATIF RESTE UN MYSTÈRE" +}, +{ +"id":530, +"fortune":"TES ÉCHARPES SONT DEVENUES DES EXPÉRIENCES" +}, +{ +"id":531, +"fortune":"TU PEINS AVEC ÉMOTION. LE SOL CONFIRME" +}, +{ +"id":532, +"fortune":"TON BONSAÏ EST UN ARBRE NORMAL DÉSORMAIS" +}, +{ +"id":533, +"fortune":"TA VOIX LITTÉRAIRE EST FORTE. EN TEXTOS" +}, +{ +"id":534, +"fortune":"TON JARDIN POUSSE. LES MAUVAISES HERBES OUI" +}, +{ +"id":535, +"fortune":"TA PHASE CALLIGRAPHIE DURA UN WEEK END" +}, +{ +"id":536, +"fortune":"TON BOIS EST HONNÊTE. LES ÉCHARDES AUSSI" +}, +{ +"id":537, +"fortune":"TA FANFICTION A DES FANS. LES DEUX" +}, +{ +"id":538, +"fortune":"TES ORIGAMIS REMPLISSENT LE BAC À PAPIER" +}, +{ +"id":539, +"fortune":"TON BLOCAGE CRÉATIF EST DEVENU PORTEUR" +}, +{ +"id":540, +"fortune":"TON LEVAIN A SURVÉCU À TA PASSION" +}, +{ +"id":541, +"fortune":"TES AQUARELLES CAPTURENT UN TRUC. LEQUEL" +}, +{ +"id":542, +"fortune":"TU BRILLES AUX DÉBUTS. LES MILIEUX ATTENDENT" +}, +{ +"id":543, +"fortune":"TON UKULÉLÉ ASSUME SON RÔLE DÉCORATIF" +}, +{ +"id":544, +"fortune":"TU ES TON PIRE CRITIQUE. SANS RIVAL SÉRIEUX" +}, +{ +"id":545, +"fortune":"TON ART PARLE POUR LUI. À VOIX BASSE" +}, +{ +"id":546, +"fortune":"TON ATELIER EST REDEVENU UN PLACARD" +}, +{ +"id":547, +"fortune":"TES PREMIERS JETS RESTENT ENFANTS UNIQUES" +}, +{ +"id":548, +"fortune":"TA PHASE BOUGIES A LAISSÉ DES TRACES. DE CIRE" +}, +{ +"id":549, +"fortune":"TES DÉBUTS SUR SCÈNE RESTENT IMMINENTS" +}, +{ +"id":550, +"fortune":"TES COSTUMES MONTRENT UN VRAI ENGAGEMENT" +}, +{ +"id":551, +"fortune":"TA CHOUETTE EN MACRAMÉ A TOUT VU. ELLE JUGE" +}, +{ +"id":552, +"fortune":"TES MÉMOS VOCAUX SONT DES LISTES DE COURSES" +}, +{ +"id":553, +"fortune":"TU SERAS DÉCOUVERT. SUR TON CANAPÉ" +}, +{ +"id":554, +"fortune":"TON MOODBOARD A DU MOOD. PAS DE BOARD" +}, +{ +"id":555, +"fortune":"TA VISITE DE MUSÉE FINIT AU CAFÉ DU MUSÉE" +}, +{ +"id":556, +"fortune":"TES REPRISES BATTENT LES ORIGINAUX. CHUT" +}, +{ +"id":557, +"fortune":"TA MACHINE À ÉCRIRE DÉCORE AVEC DIGNITÉ" +}, +{ +"id":558, +"fortune":"TA PÉRIODE ABSTRAITE PERDIT MÊME TOI" +}, +{ +"id":559, +"fortune":"TA BD A UN LECTEUR FIDÈLE. COUCOU MAMAN" +}, +{ +"id":560, +"fortune":"TES RIMES SONT AMBITIEUSES. TRÈS" +}, +{ +"id":561, +"fortune":"TON COIN BRICOLAGE A ANNEXÉ LA CHAMBRE" +}, +{ +"id":562, +"fortune":"TON GÉNIE EST INCOMPRIS. ET INVISIBLE" +}, +{ +"id":563, +"fortune":"TON GRAND ŒUVRE INFUSE DEPUIS DES ANNÉES" +}, +{ +"id":564, +"fortune":"TON DOSSIER INSPIRATION A DES SOUS DOSSIERS" +}, +{ +"id":565, +"fortune":"TON CAFÉ EST DEVENU UN PARAGRAPHE ENTIER" +}, +{ +"id":566, +"fortune":"LE CAFÉ EN PREMIER. LA PERSONNALITÉ SUIT" +}, +{ +"id":567, +"fortune":"TON QUATRIÈME CAFÉ TE SURVEILLE" +}, +{ +"id":568, +"fortune":"LE DÉCA EXISTE. TU NIES SON EXISTENCE" +}, +{ +"id":569, +"fortune":"TA COLLECTION DE TASSES DÉBORDE DU PLACARD" +}, +{ +"id":570, +"fortune":"TU DIS BRUNCH. TU VEUX DIRE RÉVEIL TARDIF" +}, +{ +"id":571, +"fortune":"TA PHASE SALADE EST MORTE À LA SAUCE" +}, +{ +"id":572, +"fortune":"TU PLANIFIES TES REPAS. PUIS TU COMMANDES" +}, +{ +"id":573, +"fortune":"TON PLAT SIGNATURE A UN FAN. TOI" +}, +{ +"id":574, +"fortune":"LE MINUTEUR DU FOUR EST UN SIMPLE AVIS" +}, +{ +"id":575, +"fortune":"TES SMOOTHIES CACHENT DES LÉGUMES. À TOI" +}, +{ +"id":576, +"fortune":"TU CUISINES AVEC AMOUR. ET TROP DE SEL" +}, +{ +"id":577, +"fortune":"TES SUBSTITUTIONS DE RECETTE SONT BRAVES" +}, +{ +"id":578, +"fortune":"TU DOSES TON AIL AVEC LE CŒUR. ENCORE" +}, +{ +"id":579, +"fortune":"TON PAIN GRILLÉ EST SOLIDE. TRÈS GRILLÉ" +}, +{ +"id":580, +"fortune":"LE DESSERT EN PREMIER. LA VIE EST COURTE" +}, +{ +"id":581, +"fortune":"TON TIROIR À SNACKS A SA FILE DE FANS" +}, +{ +"id":582, +"fortune":"LE LIVREUR CONNAÎT TON PRÉNOM ET TON CODE" +}, +{ +"id":583, +"fortune":"TES GADGETS DE CUISINE FORMENT UN MUSÉE" +}, +{ +"id":584, +"fortune":"TES BOÎTES DE TRAITEUR SONT DES HÉRITAGES" +}, +{ +"id":585, +"fortune":"TU MANGES DE TOUT. EN THÉORIE SEULEMENT" +}, +{ +"id":586, +"fortune":"TES CHOIX DE PIZZA ONT DES CRITIQUES" +}, +{ +"id":587, +"fortune":"TON CHIEN VOUS ADORE TOUS. IL EST DRESSÉ" +}, +{ +"id":588, +"fortune":"TON CHAT TE TOLÈRE. UN GRAND HONNEUR" +}, +{ +"id":589, +"fortune":"TON ANIMAL MANGE MIEUX QUE TOI" +}, +{ +"id":590, +"fortune":"TON CHIEN A UN CERCLE SOCIAL PLUS LARGE" +}, +{ +"id":591, +"fortune":"LE CHAT A TOUT FAIT TOMBER EXPRÈS. RESPECT" +}, +{ +"id":592, +"fortune":"TON POISSON CONNAÎT TON EMPLOI DU TEMPS" +}, +{ +"id":593, +"fortune":"LE CHIEN A MANGÉ TA MOTIVATION" +}, +{ +"id":594, +"fortune":"TON CHAT ÉVALUE TA VIE CHAQUE NUIT" +}, +{ +"id":595, +"fortune":"TON PERROQUET CITE TES PIRES PHRASES" +}, +{ +"id":596, +"fortune":"TON ANIMAL ÉCOUTE BIEN. ET GRATUITEMENT" +}, +{ +"id":597, +"fortune":"TON MOT DE PASSE EST TON CHAT. ON LE SAIT" +}, +{ +"id":598, +"fortune":"TU AS 47 MISES À JOUR EN ATTENTE. PATIENTES" +}, +{ +"id":599, +"fortune":"TON CLOUD EST PLEIN. DE DOUBLONS" +}, +{ +"id":600, +"fortune":"LE CORRECTEUR SAVAIT. IL A REFUSÉ" +}, +{ +"id":601, +"fortune":"TON ROUTEUR VEUT UN REDÉMARRAGE. TOI AUSSI" +}, +{ +"id":602, +"fortune":"TON HISTORIQUE DE RECHERCHE EST UNE SAGA" +}, +{ +"id":603, +"fortune":"TU AS FERMÉ UN ONGLET CE JOUR. PROGRÈS" +}, +{ +"id":604, +"fortune":"TON DOSSIER SPAM SE SENT SEUL" +}, +{ +"id":605, +"fortune":"LES ALGORITHMES TE CONNAISSENT TROP BIEN" +}, +{ +"id":606, +"fortune":"TA MONTRE TE CONSEILLE DE RESPIRER. SUBTIL" +}, +{ +"id":607, +"fortune":"TU TAPES PLUS VITE PENDANT LES DISPUTES" +}, +{ +"id":608, +"fortune":"TU AS COUPÉ LE GROUPE. LE GROUPE LE SAIT" +}, +{ +"id":609, +"fortune":"TA TOUCHE MAJUSCULE FAIT DU SPORT" +}, +{ +"id":610, +"fortune":"TES EMOJIS ONT FAIT DÉBAT. PLUS DUNE FOIS" +}, +{ +"id":611, +"fortune":"LE NOM DE TON WIFI EST TA MEILLEURE BLAGUE" +}, +{ +"id":612, +"fortune":"REDÉMARRER EST TON ART DE VIVRE" +}, +{ +"id":613, +"fortune":"TA SALLE DE SPORT TE REMERCIE POUR LE DON" +}, +{ +"id":614, +"fortune":"TON PLAN MARATHON EST SAUVEGARDÉ. EN LIEU SÛR" +}, +{ +"id":615, +"fortune":"LE JOUR DES JAMBES RESPECTE TON ABSENCE" +}, +{ +"id":616, +"fortune":"TA PROTÉINE EN POUDRE VIEILLIT AVEC CLASSE" +}, +{ +"id":617, +"fortune":"TU TÉTIRES UNE FOIS PAR AN. AVEC AMBITION" +}, +{ +"id":618, +"fortune":"TON ÉQUIPE PERD. TU PRENDS ÇA POUR TOI" +}, +{ +"id":619, +"fortune":"TU ARRIVES EN RETARD. ÇA COMPTE COMME CARDIO" +}, +{ +"id":620, +"fortune":"TON HOME GYM RANGE LE LINGE DÉSORMAIS" +}, +{ +"id":621, +"fortune":"TON RECORD PERSONNEL FUT UN TRAVAIL DE GROUPE" +}, +{ +"id":622, +"fortune":"TON ÉCHAUFFEMENT EST TOUTE LA SÉANCE" +}, +{ +"id":623, +"fortune":"TES JOURS DE REPOS ONT DES JOURS DE REPOS" +}, +{ +"id":624, +"fortune":"LE SOMMEIL EST POUR LES FORTS. TU DÉFILES" +}, +{ +"id":625, +"fortune":"TON PLANNING DE SIESTES EST TON VRAI AGENDA" +}, +{ +"id":626, +"fortune":"TU COMPTES LES MOUTONS. EUX TES EXCUSES" +}, +{ +"id":627, +"fortune":"TON OREILLER CONNAÎT TOUS TES SECRETS" +}, +{ +"id":628, +"fortune":"UN DERNIER ÉPISODE ÉGALE QUATRE ÉPISODES" +}, +{ +"id":629, +"fortune":"TES RÊVES SONT VIFS. TES MATINS MOINS" +}, +{ +"id":630, +"fortune":"TU METS SIX ALARMES. AUCUNE NE TE FAIT PEUR" +}, +{ +"id":631, +"fortune":"REPOUSSER LE COUCHER RESTE UN CHOIX" +}, +{ +"id":632, +"fortune":"TA DETTE DE SOMMEIL PREND DES INTÉRÊTS" +}, +{ +"id":633, +"fortune":"TA FAMILLE TE SOUTIENT. VOTE SERRÉ" +}, +{ +"id":634, +"fortune":"TA MÈRE AVAIT RAISON. SUR TOUT" +}, +{ +"id":635, +"fortune":"LES REPAS DE FAMILLE TESTENT TA DIPLOMATIE" +}, +{ +"id":636, +"fortune":"TA FRATRIE SE SOUVIENT DE TOUT. POUR TOUJOURS" +}, +{ +"id":637, +"fortune":"LES ONCLES DEMANDENT TES PROJETS. TIENS BON" +}, +{ +"id":638, +"fortune":"TU ES LE PRÉFÉRÉ. SOURCE NON CONFIRMÉE" +}, +{ +"id":639, +"fortune":"LE GROUPE FAMILLE NE DORT JAMAIS" +}, +{ +"id":640, +"fortune":"MAMIE TE NOURRIT. TOUTE RÉSISTANCE EST VAINE" +}, +{ +"id":641, +"fortune":"TES BLAGUES DE PAPA SE TRANSMETTENT INTACTES" +}, +{ +"id":642, +"fortune":"LE PLAN DE TABLE DES FÊTES EST POLITIQUE" +}, +{ +"id":643, +"fortune":"TON VOISIN TE SALUE. LA CLÔTURE FUT SON IDÉE" +}, +{ +"id":644, +"fortune":"LE SYNDIC A REVU TA PELOUSE. ENCORE" +}, +{ +"id":645, +"fortune":"TES VOISINS CONNAISSENT TON EMPLOI DU TEMPS" +}, +{ +"id":646, +"fortune":"UN OUTIL PRÊTÉ DEVIENT UN OUTIL DONNÉ" +}, +{ +"id":647, +"fortune":"TA CAMÉRA DE PORTE A FILMÉ TON SPRINT" +}, +{ +"id":648, +"fortune":"TON TRAJET QUOTIDIEN FORGE LE CARACTÈRE" +}, +{ +"id":649, +"fortune":"LES BOUCHONS NE VISENT PERSONNE. SAUF TOI" +}, +{ +"id":650, +"fortune":"TON CRÉNEAU LAISSE DES TÉMOINS SANS VOIX" +}, +{ +"id":651, +"fortune":"TON GPS DIT RECALCUL. AVEC LASSITUDE" +}, +{ +"id":652, +"fortune":"TU CONDUIS COMME TU VIS. CLIGNOTANT EN OPTION" +}, +{ +"id":653, +"fortune":"TON RÉSERVOIR ROULE À LA FOI. ET AUX VAPEURS" +}, +{ +"id":654, +"fortune":"LA VOIE DE GAUCHE SERT À DOUBLER. VOILÀ TOUT" +}, +{ +"id":655, +"fortune":"TA PLAYLIST AUTO A CULMINÉ EN 2016" +}, +{ +"id":656, +"fortune":"TON KLAXON TRAVAILLE PLUS QUE TON CLIGNOTANT" +}, +{ +"id":657, +"fortune":"LE TEMPS FILE. LE TIEN A UN PLAN DE VOL" +}, +{ +"id":658, +"fortune":"PROCRASTINER EST UN ART. TU ENSEIGNES" +}, +{ +"id":659, +"fortune":"TU LE FERAS DEMAIN. DEMAIN EST PRÉVENU" +}, +{ +"id":660, +"fortune":"LES DÉLAIS APPROCHENT. TU ADMIRES LA VUE" +}, +{ +"id":661, +"fortune":"TES CINQ MINUTES DURENT DES HEURES AILLEURS" +}, +{ +"id":662, +"fortune":"PLUS TARD EST TON FUSEAU HORAIRE PRÉFÉRÉ" +}, +{ +"id":663, +"fortune":"TU FINIS TOUT. UN JOUR EST UN MOT VALIDE" +}, +{ +"id":664, +"fortune":"TA LISTE UN JOUR PEUT REMPLIR UNE ÉTAGÈRE" +}, +{ +"id":665, +"fortune":"LA GESTION DU TEMPS EST UNE COMPÉTENCE. NOTÉ" +}, +{ +"id":666, +"fortune":"TU ES EN AVANCE UNE FOIS PAR AN. ON PANIQUE" +}, +{ +"id":667, +"fortune":"TA PONCTUALITÉ EST CÉLÈBRE. PAS EN BIEN" +}, +{ +"id":668, +"fortune":"TU LIS CECI AU LIEU DE TRAVAILLER. NOTÉ" +}, +{ +"id":669, +"fortune":"TON ATTENTION EST. TIENS UN OISEAU" +}, +{ +"id":670, +"fortune":"ENCORE UNE MINUTE. PAROLES CÉLÈBRES" +}, +{ +"id":671, +"fortune":"TU RÉPONDS AUX TEXTOS EN TEMPS GÉOLOGIQUE" +}, +{ +"id":672, +"fortune":"TES ACCUSÉS DE LECTURE TE TRAHISSENT" +}, +{ +"id":673, +"fortune":"TU AS VU LE MESSAGE. EUX AUSSI ONT VU" +}, +{ +"id":674, +"fortune":"TA BULLE DE FRAPPE EST APPARUE. PUIS PARTIE" +}, +{ +"id":675, +"fortune":"TU AS DIT ON DEVRAIT SE VOIR. UN CLASSIQUE" +}, +{ +"id":676, +"fortune":"TON TÉLÉPHONE EST MUET. DEPUIS DES ANNÉES" +}, +{ +"id":677, +"fortune":"TU FAIS DES CAPTURES AU LIEU DE RÉPONDRE" +}, +{ +"id":678, +"fortune":"TON AVIS FUT NOTÉ. PUIS RECYCLÉ" +}, +{ +"id":679, +"fortune":"UN COMPLIMENT ARRIVE. ACCEPTE LE MAL" +}, +{ +"id":680, +"fortune":"TON VISAGE NEUTRE A UNE RÉPUTATION" +}, +{ +"id":681, +"fortune":"VOUS ÊTES DACCORD SUR LE DÉSACCORD. TU PERDS" +}, +{ +"id":682, +"fortune":"TON ROULEMENT DES YEUX EST PRESQUE SONORE" +}, +{ +"id":683, +"fortune":"TON SOUPIR VIENT DE PARLER POUR TOI" +}, +{ +"id":684, +"fortune":"TU AS DIT ÇA VA. RIEN NE VA" +}, +{ +"id":685, +"fortune":"TON POUCE LEVÉ A BRISÉ TROIS AMITIÉS" +}, +{ +"id":686, +"fortune":"PAS DE SOUCI AS TU DIT. PLEIN DE SOUCIS" +}, +{ +"id":687, +"fortune":"TON HAUSSEMENT DE COUPE FAIT UN GROS TRAVAIL" +}, +{ +"id":688, +"fortune":"TU AS SOURI À TRAVERS TOUT ÇA. ON A VU" +}, +{ +"id":689, +"fortune":"TA PATIENCE A UNE BARRE DE CHARGEMENT. FIGÉE" +}, +{ +"id":690, +"fortune":"TU LEUR AS PARDONNÉ. LE TABLEUR RESTE" +}, +{ +"id":691, +"fortune":"TES SOUS ENTENDUS VEULENT DES SOUS TITRES" +}, +{ +"id":692, +"fortune":"TU AS FAILLI LE DIRE. LE FAILLI ÉTAIT FORT" +}, +{ +"id":693, +"fortune":"TON SILENCE A UN ORDRE DU JOUR. CLASSIQUE" +}, +{ +"id":694, +"fortune":"TU AS LAISSÉ COULER. DANS TES ARCHIVES" +}, +{ +"id":695, +"fortune":"TON CALME INTRIGUE TOUT LE MONDE" +}, +{ +"id":696, +"fortune":"TU AS PRIS LA VOIE NOBLE. AVEC COMMENTAIRES" +}, +{ +"id":697, +"fortune":"TU AS PRIS LA HAUTEUR. AVEC UN COMMENTAIRE" +}, +{ +"id":698, +"fortune":"TON CALME EST SUSPECT. TOUT LE MONDE LE DIT" +}, +{ +"id":699, +"fortune":"CE BISCUIT NA PAS DE SAGESSE. QUE DU SUCRE" +}, +{ +"id":700, +"fortune":"LA SAGESSE CHERCHÉE EST DANS UN AUTRE BISCUIT" +} +] \ No newline at end of file diff --git a/apps/sarcastic-fortune-cookies/fortunes_it.json b/apps/sarcastic-fortune-cookies/fortunes_it.json new file mode 100644 index 0000000..8a14ee6 --- /dev/null +++ b/apps/sarcastic-fortune-cookies/fortunes_it.json @@ -0,0 +1,402 @@ +[ + { + "id": 1, + "fortune": "TUTTO ACCADE PER UN MOTIVO. DI SOLITO BRUTTO" + }, + { + "id": 2, + "fortune": "LA FORTUNA ARRIVA. CAMMINA MOLTO PIANO" + }, + { + "id": 3, + "fortune": "IL COSMO HA UN PIANO PER TE. UNO PICCOLO" + }, + { + "id": 4, + "fortune": "IL TUO DESTINO HA CHIAMATO. RIMANDA" + }, + { + "id": 5, + "fortune": "LE STELLE SI ALLINEANO. POI TI HANNO VISTO" + }, + { + "id": 6, + "fortune": "LA TUA NAVE ARRIVA. UNA CANOA" + }, + { + "id": 7, + "fortune": "FIDATI DEL PROCESSO. LUI DUBITA DI TE" + }, + { + "id": 8, + "fortune": "UN SEGNO ARRIVA. DICE DARE PRECEDENZA" + }, + { + "id": 9, + "fortune": "LA TUA STELLA ESISTE. IN UNA GALASSIA LONTANA" + }, + { + "id": 10, + "fortune": "IL KARMA REALE. QUESTO SPIEGA MOLTO" + }, + { + "id": 11, + "fortune": "IL TUO FUTURO LUMINOSO. METTI LA CREMA" + }, + { + "id": 12, + "fortune": "IL DESTINO ATTENDE. SI MESSO A LEGGERE" + }, + { + "id": 13, + "fortune": "I MIRACOLI ACCADONO OGNI GIORNO. ALTROVE" + }, + { + "id": 14, + "fortune": "IL TUO MOMENTO ARRIVA. NEL TRAFFICO" + }, + { + "id": 15, + "fortune": "LA FORTUNA HA BUSSATO. ERI USCITO" + }, + { + "id": 16, + "fortune": "LA MAREA CAMBIA. A VELOCIT DI GHIACCIAIO" + }, + { + "id": 17, + "fortune": "ARRIVANO GRANDI COSE. ABBASSA LE ATTESE" + }, + { + "id": 18, + "fortune": "LE OCCASIONI CI SONO. VI PERDETE SEMPRE" + }, + { + "id": 19, + "fortune": "IL COSMO TI SENTE. METTITI IN CODA" + }, + { + "id": 20, + "fortune": "LE TUE PREGHIERE SONO SENTITE. E ARCHIVIATE" + }, + { + "id": 21, + "fortune": "IL BENE SCORRE VERSO DI TE. CONTROCORRENTE" + }, + { + "id": 22, + "fortune": "LA RUOTA GIRA. TU SEI IL CRICETO" + }, + { + "id": 23, + "fortune": "OGNI NUVOLA HA UN LATO BUONO. IL TUO GOCCIOLA" + }, + { + "id": 24, + "fortune": "IL CASO CHIEDE DI TE. PER CURIOSIT" + }, + { + "id": 25, + "fortune": "I PRESAGI PARLANO. PI CHE ALTRO SOSPIRANO" + }, + { + "id": 26, + "fortune": "IL TUO OROSCOPO BUONO. E FINTO" + }, + { + "id": 27, + "fortune": "IL FULMINE NON CADE DUE VOLTE. TU S" + }, + { + "id": 28, + "fortune": "IL DESTINO PREMIA LA PAZIENZA. FATTURA DOPO" + }, + { + "id": 29, + "fortune": "LA LUNA HA VISTO IL TUO NUOVO INIZIO. ANCORA" + }, + { + "id": 30, + "fortune": "IL TUO ANGELO VEGLIA. PRENDE APPUNTI" + }, + { + "id": 31, + "fortune": "CREDI E RICEVERAI. CONSEGNA VARIABILE" + }, + { + "id": 32, + "fortune": "LA TUA ERA DORATA ARRIVA. PLACCATA ORO" + }, + { + "id": 33, + "fortune": "CI CHE TUO ARRIVA. SENZA FRETTA" + }, + { + "id": 34, + "fortune": "IL COSMO TI FA UN CENNO. O UN TIC" + }, + { + "id": 35, + "fortune": "LA TUA SORTE CAMBIER. DIREZIONE IGNOTA" + }, + { + "id": 36, + "fortune": "LA PROVVIDENZA PROVVEDE. CONTA GLI SCONTRINI" + }, + { + "id": 37, + "fortune": "ESPRIMI UN DESIDERIO. LA STELLA LO METTE SPAM" + }, + { + "id": 38, + "fortune": "TUTTO ARRIVA A CHI ASPETTA. IL TUO TARDA" + }, + { + "id": 39, + "fortune": "I SEGNI CI SONO. LEGGERE FACOLTATIVO" + }, + { + "id": 40, + "fortune": "SEI UNICO. COME TUTTI GLI ALTRI" + }, + { + "id": 41, + "fortune": "IL MEGLIO DEVE VENIRE. NON HA FRETTA" + }, + { + "id": 42, + "fortune": "LA TUA AURA BRILLA. FA GLI STRAORDINARI" + }, + { + "id": 43, + "fortune": "LA FORTUNA TI HA SORRISO. CORTESE. DA LONTANO" + }, + { + "id": 44, + "fortune": "IL TUO ANGELO HA CHIESTO IL TRASFERIMENTO" + }, + { + "id": 45, + "fortune": "IL DESTINO D LE CARTE. TU LE PERDI" + }, + { + "id": 46, + "fortune": "UN PRESAGIO APPARE. ALZA LE SPALLE" + }, + { + "id": 47, + "fortune": "IL SUCCESSO VICINO. E ANCHE IL TASTO RINVIA" + }, + { + "id": 48, + "fortune": "IL LAVORO PAGA SEMPRE. IN ESPERIENZA" + }, + { + "id": 49, + "fortune": "IL TUO POTENZIALE ENORME. E INTATTO" + }, + { + "id": 50, + "fortune": "PUNTA ALLA LUNA. ATTENTO AL SOFFITTO" + }, + { + "id": 51, + "fortune": "CHI DORME NON PIGLIA PESCI. TU DORMIVI" + }, + { + "id": 52, + "fortune": "LA TUA GRANDE OCCASIONE ARRIVA. NON ROMPERLA" + }, + { + "id": 53, + "fortune": "ROMA NON FU FATTA IN UN GIORNO. NEANCHE TU" + }, + { + "id": 54, + "fortune": "I TUOI TALENTI SI TROVERANNO. CON UN MANDATO" + }, + { + "id": 55, + "fortune": "CHI VINCE NON MOLLA. TU GALLEGGI" + }, + { + "id": 56, + "fortune": "LE TUE IDEE SONO AVANTI. LE SCADENZE INDIETRO" + }, + { + "id": 57, + "fortune": "LA QUALIT UN VIZIO. HAI IL TUO" + }, + { + "id": 58, + "fortune": "IL TUO LAVORO PARLA DA S. SOTTOVOCE" + }, + { + "id": 59, + "fortune": "LA CIMA SOLITARIA. PROBLEMA RISOLTO" + }, + { + "id": 60, + "fortune": "UNA PROMOZIONE ARRIVA. QUELLA DI UN COLLEGA" + }, + { + "id": 61, + "fortune": "IL TUO CV IMPRESSIONA. GLI AMANTI DEL FANTASY" + }, + { + "id": 62, + "fortune": "IL TUO LAVORO MIGLIORE ARRIVA. CORRE VELOCE" + }, + { + "id": 63, + "fortune": "QUELLA RIUNIONE POTEVA ESSERE UN MAIL" + }, + { + "id": 64, + "fortune": "LA TUA CASELLA NON NEMICA. SOLO TESTIMONE" + }, + { + "id": 65, + "fortune": "LE SCADENZE NON TI SPAVENTANO. LE IGNORI" + }, + { + "id": 66, + "fortune": "IL TUO PIANO A CINQUE ANNI HA CINQUE ANNI" + }, + { + "id": 67, + "fortune": "LENTO MA SICURO VINCE. PADRONEGGI IL LENTO" + }, + { + "id": 68, + "fortune": "LA TUA AGENDA PIENA. DI MARGINI" + }, + { + "id": 69, + "fortune": "ANCHE LA TUA SQUADRA SOGNA. IL TRASFERIMENTO" + }, + { + "id": 70, + "fortune": "LA GRANDE OCCASIONE SCESA DA TE" + }, + { + "id": 71, + "fortune": "IL SUCCESSO LASCIA INDIZI. TU AMI IL MISTERO" + }, + { + "id": 72, + "fortune": "LA TUA COMFORT ZONE MERITA UN CAP" + }, + { + "id": 73, + "fortune": "QUALIT O QUANTIT. LE RIFIUTI ENTRAMBE" + }, + { + "id": 74, + "fortune": "IL TUO ISTINTO ACUTO. E SPESSO SBAGLIA" + }, + { + "id": 75, + "fortune": "LAVORI BENE SOTTO PRESSIONE. LEI DISSENTE" + }, + { + "id": 76, + "fortune": "LA MAESTRIA CHIEDE 10000 ORE. TU NE HAI 40" + }, + { + "id": 77, + "fortune": "LA TUA PIANTA CREDEVA IN TE. E POI SETE" + }, + { + "id": 78, + "fortune": "LA STAMPANTE SENTE LA FRETTA. QUINDI SI FERMA" + }, + { + "id": 79, + "fortune": "LA TUA EREDIT IN CANTIERE. ANCORA" + }, + { + "id": 80, + "fortune": "UN DIPLOMA TI ASPETTA. QUELLO DI PRESENZA" + }, + { + "id": 81, + "fortune": "FARE RETE APRE PORTE. ANCHE BUSSARE" + }, + { + "id": 82, + "fortune": "SEI UN ESEMPIO. NE VOGLIONO UN ALTRO" + }, + { + "id": 83, + "fortune": "IL TUO PROGETTO RIPOSA CON GLI ALTRI" + }, + { + "id": 84, + "fortune": "IL GENIO UN PER CENTO SUDORE. TU MOLLI L" + }, + { + "id": 85, + "fortune": "IL TUO MESSAGGIO DI ASSENZA UN CAPOLAVORO" + }, + { + "id": 86, + "fortune": "FALLIRE UN GRADINO. BELLA SCALINATA" + }, + { + "id": 87, + "fortune": "SEI INSOSTITUIBILE. IL BANDO PRONTO" + }, + { + "id": 88, + "fortune": "IL TUO COLLOQUIO ANNUALE ARRIVA. PROVA BENE" + }, + { + "id": 89, + "fortune": "EFFICIENZA IL TUO SECONDO NOME. SULLA CARTA" + }, + { + "id": 90, + "fortune": "CADI IN PIEDI. LA CADUTA RINVIATA" + }, + { + "id": 91, + "fortune": "LA VETTA VICINA. ANCHE IL CAMPO BASE" + }, + { + "id": 92, + "fortune": "LA TUA AMBIZIONE SI SVEGLIATA. NON TU" + }, + { + "id": 93, + "fortune": "SII TE STESSO. GLI ALTRI SI SONO PREOCCUPATI" + }, + { + "id": 94, + "fortune": "LA TUA RIUNIONE ANDATA BENE. FINITA" + }, + { + "id": 95, + "fortune": "RIPOSARE FA BENE. LO PRATICHI MOLTO" + }, + { + "id": 96, + "fortune": "LA TUA DIETA INIZIA LUNED. I LUNED FUGGONO" + }, + { + "id": 97, + "fortune": "TE LO MERITI. FAI UN BEL RESPIRO" + }, + { + "id": 98, + "fortune": "UNO SCONOSCIUTO TI SORRIDE. STAI CALMO" + }, + { + "id": 99, + "fortune": "NON SERBI RANCORE. LO ARCHIVI CON DATA" + }, + { + "id": 100, + "fortune": "LA PAZIENZA UNA VIRT. NE HAI SCELTE ALTRE" + } +] \ No newline at end of file diff --git a/apps/sarcastic-fortune-cookies/fortunes_nl.json b/apps/sarcastic-fortune-cookies/fortunes_nl.json new file mode 100644 index 0000000..440383c --- /dev/null +++ b/apps/sarcastic-fortune-cookies/fortunes_nl.json @@ -0,0 +1,402 @@ +[ + { + "id": 1, + "fortune": "ALLES GEBEURT MET EEN REDEN. MEESTAL SLECHT" + }, + { + "id": 2, + "fortune": "HET GELUK KOMT ERAAN. HET LOOPT TRAAG" + }, + { + "id": 3, + "fortune": "HET HEELAL HEEFT EEN PLAN. EEN KLEIN PLAN" + }, + { + "id": 4, + "fortune": "JE LOTSBESTEMMING BELDE. ZE VERZET HET" + }, + { + "id": 5, + "fortune": "DE STERREN STONDEN GOED. TOEN ZAGEN ZE JOU" + }, + { + "id": 6, + "fortune": "JE SCHIP KOMT BINNEN. HET IS EEN KANO" + }, + { + "id": 7, + "fortune": "VERTROUW HET PROCES. HET TWIJFELT AAN JOU" + }, + { + "id": 8, + "fortune": "EEN TEKEN KOMT. HET ZEGT VERLEEN VOORRANG" + }, + { + "id": 9, + "fortune": "JE STER BESTAAT. IN EEN ANDER STELSEL" + }, + { + "id": 10, + "fortune": "KARMA BESTAAT. DAT VERKLAART VEEL" + }, + { + "id": 11, + "fortune": "JE TOEKOMST IS STRALEND. NEEM ZONNEBRAND" + }, + { + "id": 12, + "fortune": "HET LOT WACHT. HET IS GAAN LEZEN" + }, + { + "id": 13, + "fortune": "WONDEREN GEBEUREN ELKE DAG. ELDERS DAN" + }, + { + "id": 14, + "fortune": "JE MOMENT KOMT. HET STAAT IN DE FILE" + }, + { + "id": 15, + "fortune": "HET GELUK KLOPTE AAN. JE WAS WEG" + }, + { + "id": 16, + "fortune": "HET TIJ KEERT. OP GLETSJERSNELHEID" + }, + { + "id": 17, + "fortune": "GROTE DINGEN KOMEN. VERLAAG DE LAT" + }, + { + "id": 18, + "fortune": "DE KANSEN ZIJN ER. JULLIE MISSEN ELKAAR" + }, + { + "id": 19, + "fortune": "HET HEELAL HOORT JE. GA IN DE RIJ STAAN" + }, + { + "id": 20, + "fortune": "JE GEBEDEN ZIJN GEHOORD. EN GEARCHIVEERD" + }, + { + "id": 21, + "fortune": "OVERVLOED STROOMT NAAR JE. STROOMOPWAARTS" + }, + { + "id": 22, + "fortune": "HET RAD DRAAIT. JIJ BENT DE HAMSTER" + }, + { + "id": 23, + "fortune": "ELKE WOLK HEEFT EEN RAND. DE JOUWE DRUPT" + }, + { + "id": 24, + "fortune": "HET TOEVAL VRAAGT NAAR JOU. UIT NIEUWSGIER" + }, + { + "id": 25, + "fortune": "DE VOORTEKENS SPREKEN. ZE ZUCHTEN VOORAL" + }, + { + "id": 26, + "fortune": "JE HOROSCOOP IS GUNSTIG. EN VERZONNEN" + }, + { + "id": 27, + "fortune": "DE BLIKSEM SLAAT NOOIT TWEE KEER. JIJ WEL" + }, + { + "id": 28, + "fortune": "HET LOT BELOONT GEDULD. FACTUUR VOLGT" + }, + { + "id": 29, + "fortune": "DE MAAN ZAG JE NIEUWE START. ALWEER EEN" + }, + { + "id": 30, + "fortune": "JE ENGEL WAAKT. HIJ MAAKT NOTITIES" + }, + { + "id": 31, + "fortune": "GELOOF EN JE ONTVANGT. LEVERING WISSELT" + }, + { + "id": 32, + "fortune": "JE GOUDEN TIJDPERK KOMT. VERGULD DAN" + }, + { + "id": 33, + "fortune": "WAT VOOR JOU IS KOMT. ZONDER ENIGE HAAST" + }, + { + "id": 34, + "fortune": "HET HEELAL KNIPOOGDE. OF HET HAD EEN TIC" + }, + { + "id": 35, + "fortune": "JE GELUK GAAT KEREN. RICHTING ONBEKEND" + }, + { + "id": 36, + "fortune": "DE VOORZIENIGHEID ZORGT. ZE CHECKT DE BONNEN" + }, + { + "id": 37, + "fortune": "DOE EEN WENS. DE STER ZET HEM OP SPAM" + }, + { + "id": 38, + "fortune": "ALLES KOMT OP ZIJN TIJD. DE JOUWE TREUZELT" + }, + { + "id": 39, + "fortune": "DE TEKENS WAREN ER. LEZEN IS OPTIONEEL" + }, + { + "id": 40, + "fortune": "JE BENT UNIEK. NET ALS IEDEREEN" + }, + { + "id": 41, + "fortune": "HET BESTE MOET NOG KOMEN. GEEN ENKELE HAAST" + }, + { + "id": 42, + "fortune": "JE AURA STRAALT. HIJ MAAKT OVERUREN" + }, + { + "id": 43, + "fortune": "HET GELUK LACHTE. BELEEFD. VAN VERRE" + }, + { + "id": 44, + "fortune": "JE ENGEL VROEG OM OVERPLAATSING" + }, + { + "id": 45, + "fortune": "HET LOT DEELT DE KAARTEN. JIJ VERLIEST ZE" + }, + { + "id": 46, + "fortune": "EEN VOORTEKEN VERSCHIJNT. HET SCHOKSCHOUDERT" + }, + { + "id": 47, + "fortune": "SUCCES IS NABIJ. NET ALS DE SLUIMERKNOP" + }, + { + "id": 48, + "fortune": "WERK LOONT ALTIJD. IN ERVARING" + }, + { + "id": 49, + "fortune": "JE POTENTIEEL IS ENORM. EN ONGEBRUIKT" + }, + { + "id": 50, + "fortune": "MIK OP DE MAAN. PAS OP HET PLAFOND" + }, + { + "id": 51, + "fortune": "DE VROEGE VOGEL KREEG DE WORM. JIJ SLIEP" + }, + { + "id": 52, + "fortune": "JE GROTE KANS KOMT. BREEK HEM NIET" + }, + { + "id": 53, + "fortune": "ROME WERD NIET IN EEN DAG GEBOUWD. JIJ NIET" + }, + { + "id": 54, + "fortune": "JE TALENTEN WORDEN GEVONDEN. MET EEN BEVEL" + }, + { + "id": 55, + "fortune": "WINNAARS STOPPEN NOOIT. JIJ ZWEEFT" + }, + { + "id": 56, + "fortune": "JE IDEEEN LOPEN VOOR. JE DEADLINES ACHTER" + }, + { + "id": 57, + "fortune": "KWALITEIT IS EEN GEWOONTE. DE JOUWE OOK" + }, + { + "id": 58, + "fortune": "JE WERK SPREEKT VOOR ZICH. HEEL ZACHTJES" + }, + { + "id": 59, + "fortune": "DE TOP IS EENZAAM. PROBLEEM OPGELOST" + }, + { + "id": 60, + "fortune": "EEN PROMOTIE KOMT. DIE VAN EEN COLLEGA" + }, + { + "id": 61, + "fortune": "JE CV IMPONEERT. DE LIEFHEBBERS VAN FICTIE" + }, + { + "id": 62, + "fortune": "JE BESTE WERK KOMT NOG. HET RENT SNEL" + }, + { + "id": 63, + "fortune": "DIE VERGADERING KON EEN MAIL ZIJN" + }, + { + "id": 64, + "fortune": "JE INBOX IS GEEN VIJAND. SLECHTS GETUIGE" + }, + { + "id": 65, + "fortune": "DEADLINES MAKEN JE NIET BANG. JE NEGEERT ZE" + }, + { + "id": 66, + "fortune": "JE VIJFJARENPLAN IS VIJF JAAR OUD" + }, + { + "id": 67, + "fortune": "LANGZAAM MAAR ZEKER WINT. JE HEBT LANGZAAM" + }, + { + "id": 68, + "fortune": "JE AGENDA IS VOL. MET MARGES" + }, + { + "id": 69, + "fortune": "JE TEAM DROOMT OOK. VAN OVERPLAATSING" + }, + { + "id": 70, + "fortune": "DE KANS DAALDE TOT JOUW NIVEAU" + }, + { + "id": 71, + "fortune": "SUCCES LAAT SPOREN NA. JIJ HOUDT VAN RAADSELS" + }, + { + "id": 72, + "fortune": "JE COMFORTZONE VERDIENT EEN POSTCODE" + }, + { + "id": 73, + "fortune": "KWALITEIT OF KWANTITEIT. JE WEIGERT BEIDE" + }, + { + "id": 74, + "fortune": "JE INSTINCT IS SCHERP. EN VAAK FOUT" + }, + { + "id": 75, + "fortune": "JE WERKT GOED ONDER DRUK. ZE IS HET ONEENS" + }, + { + "id": 76, + "fortune": "MEESTERSCHAP VRAAGT 10000 UUR. JIJ HEBT 40" + }, + { + "id": 77, + "fortune": "JE PLANT GELOOFDE IN JOU. EN IN WATER" + }, + { + "id": 78, + "fortune": "DE PRINTER RUIKT JE HAAST. DAAROM HAPERT HIJ" + }, + { + "id": 79, + "fortune": "JE ERFENIS IS IN AANBOUW. NOG STEEDS" + }, + { + "id": 80, + "fortune": "EEN DIPLOMA WACHT. DAT VAN DEELNAME" + }, + { + "id": 81, + "fortune": "NETWERKEN OPENT DEUREN. KLOPPEN OOK" + }, + { + "id": 82, + "fortune": "JIJ GEEFT HET VOORBEELD. HR WIL EEN ANDER" + }, + { + "id": 83, + "fortune": "JE ZIJPROJECT RUST UIT MET DE REST" + }, + { + "id": 84, + "fortune": "GENIE IS EEN PROCENT ZWEET. DAAR STOP JE" + }, + { + "id": 85, + "fortune": "JE AFWEZIGHEIDSBERICHT IS JE MEESTERWERK" + }, + { + "id": 86, + "fortune": "FALEN IS EEN TREDE. MOOIE TRAP" + }, + { + "id": 87, + "fortune": "JE BENT ONVERVANGBAAR. DE VACATURE IS ER AL" + }, + { + "id": 88, + "fortune": "JE JAARGESPREK KOMT. OEFEN GOED" + }, + { + "id": 89, + "fortune": "EFFICIENTIE IS JE TWEEDE NAAM. OP PAPIER" + }, + { + "id": 90, + "fortune": "JE LANDT OP JE VOETEN. LANDING VERTRAAGD" + }, + { + "id": 91, + "fortune": "DE TOP IS DICHTBIJ. HET BASISKAMP OOK" + }, + { + "id": 92, + "fortune": "JE AMBITIE ONTWAAKTE. JE WEKKER NIET" + }, + { + "id": 93, + "fortune": "WEES JEZELF. DE REST MAAKTE ZICH ZORGEN" + }, + { + "id": 94, + "fortune": "JE VERGADERING GING GOED. HIJ IS VOORBIJ" + }, + { + "id": 95, + "fortune": "RUSTEN IS GEZOND. JE OEFENT VEEL" + }, + { + "id": 96, + "fortune": "JE DIEET BEGINT MAANDAG. MAANDAGEN VLUCHTEN" + }, + { + "id": 97, + "fortune": "JE VERDIENT DIT. HAAL DIEP ADEM" + }, + { + "id": 98, + "fortune": "EEN VREEMDE LACHT NAAR JE. BLIJF KALM" + }, + { + "id": 99, + "fortune": "JE KOESTERT GEEN WROK. JE ARCHIVEERT HEM" + }, + { + "id": 100, + "fortune": "JE GEDULD HEEFT EEN BALK. VASTGELOPEN" + } +] \ No newline at end of file diff --git a/apps/sarcastic-fortune-cookies/fortunes_pt.json b/apps/sarcastic-fortune-cookies/fortunes_pt.json new file mode 100644 index 0000000..7f6932c --- /dev/null +++ b/apps/sarcastic-fortune-cookies/fortunes_pt.json @@ -0,0 +1,402 @@ +[ + { + "id": 1, + "fortune": "TUDO ACONTECE POR UM MOTIVO. QUASE SEMPRE MAU" + }, + { + "id": 2, + "fortune": "A SORTE VEM A CAMINHO. ANDA DEVAGAR" + }, + { + "id": 3, + "fortune": "O UNIVERSO TEM UM PLANO. UM PEQUENO" + }, + { + "id": 4, + "fortune": "O TEU DESTINO LIGOU. VAI REMARCAR" + }, + { + "id": 5, + "fortune": "AS ESTRELAS ALINHARAM. DEPOIS VIRAM TE" + }, + { + "id": 6, + "fortune": "O TEU NAVIO CHEGA. UMA CANOA" + }, + { + "id": 7, + "fortune": "CONFIA NO PROCESSO. ELE DUVIDA DE TI" + }, + { + "id": 8, + "fortune": "UM SINAL CHEGA. DIZ D PASSAGEM" + }, + { + "id": 9, + "fortune": "A TUA ESTRELA EXISTE. NOUTRA GALXIA" + }, + { + "id": 10, + "fortune": "O KARMA REAL. ISSO EXPLICA MUITO" + }, + { + "id": 11, + "fortune": "O TEU FUTURO BRILHANTE. LEVA PROTETOR" + }, + { + "id": 12, + "fortune": "O DESTINO ESPERA. FOI LER UM LIVRO" + }, + { + "id": 13, + "fortune": "OS MILAGRES ACONTECEM. NOUTRO LUGAR" + }, + { + "id": 14, + "fortune": "O TEU MOMENTO CHEGA. EST NO TRNSITO" + }, + { + "id": 15, + "fortune": "A SORTE BATEU. TU TINHAS SADO" + }, + { + "id": 16, + "fortune": "A MAR VIRA. VELOCIDADE DE UM GLACIAR" + }, + { + "id": 17, + "fortune": "GRANDES COISAS VM. BAIXA AS EXPETATIVAS" + }, + { + "id": 18, + "fortune": "AS OPORTUNIDADES ESTO A. FALHAM SE SEMPRE" + }, + { + "id": 19, + "fortune": "O COSMOS OUVE TE. ENTRA NA FILA" + }, + { + "id": 20, + "fortune": "AS TUAS PRECES FORAM OUVIDAS. E ARQUIVADAS" + }, + { + "id": 21, + "fortune": "A ABUNDNCIA FLUI PARA TI. RIO ACIMA" + }, + { + "id": 22, + "fortune": "A RODA GIRA. TU S O HAMSTER" + }, + { + "id": 23, + "fortune": "CADA NUVEM TEM UM LADO BOM. O TEU PINGA" + }, + { + "id": 24, + "fortune": "O ACASO PERGUNTA POR TI. POR CURIOSIDADE" + }, + { + "id": 25, + "fortune": "OS PRESSGIOS FALAM. MAIS SUSPIRAM" + }, + { + "id": 26, + "fortune": "O TEU HORSCOPO BOM. E FICTCIO" + }, + { + "id": 27, + "fortune": "O RAIO NO CAI DUAS VEZES. TU CAIS" + }, + { + "id": 28, + "fortune": "O DESTINO PREMEIA A PACINCIA. FATURA DEPOIS" + }, + { + "id": 29, + "fortune": "A LUA VIU O TEU RECOMEO. MAIS UM" + }, + { + "id": 30, + "fortune": "O TEU ANJO VIGIA. VAI TOMANDO NOTAS" + }, + { + "id": 31, + "fortune": "CR E RECEBERS. A ENTREGA VARIA" + }, + { + "id": 32, + "fortune": "A TUA ERA DOURADA CHEGA. BANHADA A OURO" + }, + { + "id": 33, + "fortune": "O QUE TEU CHEGA. SEM PRESSA NENHUMA" + }, + { + "id": 34, + "fortune": "O COSMOS PISCOU TE O OLHO. OU FOI UM TIQUE" + }, + { + "id": 35, + "fortune": "A TUA SORTE VAI MUDAR. RUMO INCERTO" + }, + { + "id": 36, + "fortune": "A PROVIDNCIA PROV. CONFERE OS RECIBOS" + }, + { + "id": 37, + "fortune": "FAZ UM PEDIDO. A ESTRELA MANDA PARA SPAM" + }, + { + "id": 38, + "fortune": "TUDO VEM A TEMPO. O TEU DEMORA" + }, + { + "id": 39, + "fortune": "OS SINAIS ESTAVAM L. LER OPCIONAL" + }, + { + "id": 40, + "fortune": "S NICO. COMO TODA A GENTE" + }, + { + "id": 41, + "fortune": "O MELHOR EST PARA VIR. NO TEM PRESSA" + }, + { + "id": 42, + "fortune": "A TUA AURA BRILHA. FAZ HORAS EXTRA" + }, + { + "id": 43, + "fortune": "A SORTE SORRIU TE. CORTS. DE LONGE" + }, + { + "id": 44, + "fortune": "O TEU ANJO PEDIU TRANSFERNCIA" + }, + { + "id": 45, + "fortune": "O DESTINO D AS CARTAS. TU PERDE LAS" + }, + { + "id": 46, + "fortune": "UM PRESSGIO SURGE. ENCOLHE OS OMBROS" + }, + { + "id": 47, + "fortune": "O SUCESSO EST PERTO. E O BOTO DE ADIAR" + }, + { + "id": 48, + "fortune": "O TRABALHO COMPENSA SEMPRE. EM EXPERINCIA" + }, + { + "id": 49, + "fortune": "O TEU POTENCIAL IMENSO. E POR USAR" + }, + { + "id": 50, + "fortune": "APONTA LUA. CUIDADO COM O TETO" + }, + { + "id": 51, + "fortune": "DEUS AJUDA QUEM CEDO MADRUGA. TU DORMIAS" + }, + { + "id": 52, + "fortune": "A TUA GRANDE CHANCE CHEGA. NO A PARTAS" + }, + { + "id": 53, + "fortune": "ROMA NO SE FEZ NUM DIA. A TUA TAMBM NO" + }, + { + "id": 54, + "fortune": "OS TEUS TALENTOS SERO ACHADOS. COM MANDADO" + }, + { + "id": 55, + "fortune": "QUEM VENCE NO DESISTE. TU FLUTUAS" + }, + { + "id": 56, + "fortune": "AS TUAS IDEIAS VO FRENTE. OS PRAZOS ATRS" + }, + { + "id": 57, + "fortune": "A QUALIDADE UM HBITO. O TEU TAMBM" + }, + { + "id": 58, + "fortune": "O TEU TRABALHO FALA POR SI. BAIXINHO" + }, + { + "id": 59, + "fortune": "O TOPO SOLITRIO. PROBLEMA RESOLVIDO" + }, + { + "id": 60, + "fortune": "UMA PROMOO CHEGA. A DE UM COLEGA" + }, + { + "id": 61, + "fortune": "O TEU CV IMPRESSIONA. OS FS DE FICO" + }, + { + "id": 62, + "fortune": "O TEU MELHOR TRABALHO VEM. CORRE DEPRESSA" + }, + { + "id": 63, + "fortune": "AQUELA REUNIO PODIA SER UM EMAIL" + }, + { + "id": 64, + "fortune": "A TUA CAIXA NO INIMIGA. S TESTEMUNHA" + }, + { + "id": 65, + "fortune": "OS PRAZOS NO TE ASSUSTAM. TU IGNORA LOS" + }, + { + "id": 66, + "fortune": "O TEU PLANO A CINCO ANOS TEM CINCO ANOS" + }, + { + "id": 67, + "fortune": "DEVAGAR MAS SEGURO GANHA. DOMINAS O DEVAGAR" + }, + { + "id": 68, + "fortune": "A TUA AGENDA EST CHEIA. DE MARGENS" + }, + { + "id": 69, + "fortune": "A TUA EQUIPA TAMBM SONHA. COM A SADA" + }, + { + "id": 70, + "fortune": "A OCASIO DESCEU AO TEU NVEL" + }, + { + "id": 71, + "fortune": "O SUCESSO DEIXA PISTAS. TU PREFERES MISTRIO" + }, + { + "id": 72, + "fortune": "A TUA ZONA DE CONFORTO TEM CDIGO POSTAL" + }, + { + "id": 73, + "fortune": "QUALIDADE OU QUANTIDADE. RECUSAS AMBAS" + }, + { + "id": 74, + "fortune": "O TEU INSTINTO AFIADO. E COSTUMA FALHAR" + }, + { + "id": 75, + "fortune": "TRABALHAS BEM SOB PRESSO. ELA DISCORDA" + }, + { + "id": 76, + "fortune": "A MESTRIA PEDE 10000 HORAS. TU TENS 40" + }, + { + "id": 77, + "fortune": "A TUA PLANTA CREU EM TI. E NA GUA" + }, + { + "id": 78, + "fortune": "A IMPRESSORA SENTE A PRESSA. POR ISSO FALHA" + }, + { + "id": 79, + "fortune": "O TEU LEGADO EST EM OBRAS. AINDA" + }, + { + "id": 80, + "fortune": "UM DIPLOMA ESPERA TE. O DE PRESENA" + }, + { + "id": 81, + "fortune": "FAZER REDE ABRE PORTAS. BATER TAMBM" + }, + { + "id": 82, + "fortune": "DS O EXEMPLO. OS RH QUEREM OUTRO" + }, + { + "id": 83, + "fortune": "O TEU PROJETO PESSOAL DESCANSA COM OS OUTROS" + }, + { + "id": 84, + "fortune": "O GNIO UM POR CENTO SUOR. PARAS A" + }, + { + "id": 85, + "fortune": "A TUA MENSAGEM DE AUSNCIA UMA OBRA PRIMA" + }, + { + "id": 86, + "fortune": "FALHAR UM DEGRAU. BELA ESCADARIA" + }, + { + "id": 87, + "fortune": "S INSUBSTITUVEL. O ANNCIO J SAIU" + }, + { + "id": 88, + "fortune": "A TUA AVALIAO ANUAL CHEGA. ENSAIA BEM" + }, + { + "id": 89, + "fortune": "EFICINCIA O TEU NOME DO MEIO. NO PAPEL" + }, + { + "id": 90, + "fortune": "CAIS DE P. A ATERRAGEM EST ATRASADA" + }, + { + "id": 91, + "fortune": "O CUME EST PERTO. O ACAMPAMENTO TAMBM" + }, + { + "id": 92, + "fortune": "A TUA AMBIO ACORDOU. O DESPERTADOR NO" + }, + { + "id": 93, + "fortune": "S TU MESMO. OS OUTROS FICARAM PREOCUPADOS" + }, + { + "id": 94, + "fortune": "A TUA REUNIO CORREU BEM. J ACABOU" + }, + { + "id": 95, + "fortune": "DESCANSAR FAZ BEM. PRATICAS IMENSO" + }, + { + "id": 96, + "fortune": "A TUA DIETA COMEA SEGUNDA. AS SEGUNDAS FOGEM" + }, + { + "id": 97, + "fortune": "TU MERECES ISTO. RESPIRA FUNDO" + }, + { + "id": 98, + "fortune": "UM ESTRANHO SORRI PARA TI. MANTM A CALMA" + }, + { + "id": 99, + "fortune": "NO GUARDAS RANCOR. TU ARQUIVA LO" + }, + { + "id": 100, + "fortune": "A TUA PACINCIA TEM UMA BARRA. TRAVADA" + } +] \ No newline at end of file diff --git a/apps/sarcastic-fortune-cookies/fortunes_sv.json b/apps/sarcastic-fortune-cookies/fortunes_sv.json new file mode 100644 index 0000000..880a23d --- /dev/null +++ b/apps/sarcastic-fortune-cookies/fortunes_sv.json @@ -0,0 +1,402 @@ +[ + { + "id": 1, + "fortune": "ALLT HNDER AV EN ANLEDNING. OFTAST EN DLIG" + }, + { + "id": 2, + "fortune": "LYCKAN R P VG. DEN GR LNGSAMT" + }, + { + "id": 3, + "fortune": "UNIVERSUM HAR EN PLAN. EN LITEN" + }, + { + "id": 4, + "fortune": "DITT DE RINGDE. DET BOKAR OM" + }, + { + "id": 5, + "fortune": "STJRNORNA STOD RTT. SEN SG DE DIG" + }, + { + "id": 6, + "fortune": "DITT SKEPP KOMMER IN. DET R EN KANOT" + }, + { + "id": 7, + "fortune": "LITA P PROCESSEN. DEN TVIVLAR P DIG" + }, + { + "id": 8, + "fortune": "ETT TECKEN KOMMER. DET SGER LMNA FRETRDE" + }, + { + "id": 9, + "fortune": "DIN STJRNA FINNS. I EN ANNAN GALAX" + }, + { + "id": 10, + "fortune": "KARMA R P RIKTIGT. DET FRKLARAR MYCKET" + }, + { + "id": 11, + "fortune": "DIN FRAMTID R LJUS. TA SOLKRM" + }, + { + "id": 12, + "fortune": "DET VNTAR. DET BRJADE LSA" + }, + { + "id": 13, + "fortune": "MIRAKEL SKER VARJE DAG. FAST NGON ANNANSTANS" + }, + { + "id": 14, + "fortune": "DITT GONBLICK KOMMER. DET SITTER I KN" + }, + { + "id": 15, + "fortune": "LYCKAN KNACKADE. DU VAR UTE" + }, + { + "id": 16, + "fortune": "TIDVATTNET VNDER. I GLACIRFART" + }, + { + "id": 17, + "fortune": "STORA SAKER KOMMER. SNK RIBBAN" + }, + { + "id": 18, + "fortune": "CHANSERNA FINNS. NI MISSAR ALLTID VARANN" + }, + { + "id": 19, + "fortune": "UNIVERSUM HR DIG. STLL DIG I KN" + }, + { + "id": 20, + "fortune": "DINA BNER HRDES. OCH ARKIVERADES" + }, + { + "id": 21, + "fortune": "VERFLDET FLDAR TILL DIG. UPPFR STRMMEN" + }, + { + "id": 22, + "fortune": "HJULET SNURRAR. DU R HAMSTERN" + }, + { + "id": 23, + "fortune": "VARJE MOLN HAR EN KANT. DIN DROPPAR" + }, + { + "id": 24, + "fortune": "SLUMPEN FRGAR EFTER DIG. AV NYFIKENHET" + }, + { + "id": 25, + "fortune": "FRTECKNEN TALAR. MEST SUCKAR DE" + }, + { + "id": 26, + "fortune": "DITT HOROSKOP R FINT. OCH PHITTAT" + }, + { + "id": 27, + "fortune": "BLIXTEN SLR ALDRIG TV GNGER. DU GR DET" + }, + { + "id": 28, + "fortune": "DET BELNAR TLAMOD. FAKTURA KOMMER" + }, + { + "id": 29, + "fortune": "MNEN SG DIN NYSTART. NNU EN" + }, + { + "id": 30, + "fortune": "DIN NGEL VAKAR. DEN FR ANTECKNINGAR" + }, + { + "id": 31, + "fortune": "TRO S FR DU. LEVERANS VARIERAR" + }, + { + "id": 32, + "fortune": "DIN GULDLDER KOMMER. FRGYLLD D" + }, + { + "id": 33, + "fortune": "DET SOM R DITT KOMMER. UTAN NGON BRDSKA" + }, + { + "id": 34, + "fortune": "UNIVERSUM BLINKADE. ELLER FICK NT I GAT" + }, + { + "id": 35, + "fortune": "DIN TUR VNDER. RIKTNING OKND" + }, + { + "id": 36, + "fortune": "FRSYNEN SRJER. DEN KOLLAR KVITTONA" + }, + { + "id": 37, + "fortune": "NSKA DIG NT. STJRNAN SPAMMAR DET" + }, + { + "id": 38, + "fortune": "ALLT KOMMER I TID. DITT DRJER" + }, + { + "id": 39, + "fortune": "TECKNEN FANNS DR. ATT LSA R FRIVILLIGT" + }, + { + "id": 40, + "fortune": "DU R UNIK. PRECIS SOM ALLA ANDRA" + }, + { + "id": 41, + "fortune": "DET BSTA KOMMER SEN. DET HAR INGEN BRDSKA" + }, + { + "id": 42, + "fortune": "DIN AURA LYSER. DEN JOBBAR VERTID" + }, + { + "id": 43, + "fortune": "LYCKAN LOG. ARTIGT. P AVSTND" + }, + { + "id": 44, + "fortune": "DIN NGEL BAD OM FRFLYTTNING" + }, + { + "id": 45, + "fortune": "DET GER KORTEN. DU TAPPAR DEM" + }, + { + "id": 46, + "fortune": "ETT OMEN DYKER UPP. DET RYCKER P AXLARNA" + }, + { + "id": 47, + "fortune": "FRAMGNG R NRA. OCH SNOOZE KNAPPEN" + }, + { + "id": 48, + "fortune": "ARBETE LNAR SIG. I ERFARENHET" + }, + { + "id": 49, + "fortune": "DIN POTENTIAL R STOR. OCH OANVND" + }, + { + "id": 50, + "fortune": "SIKTA P MNEN. AKTA TAKET" + }, + { + "id": 51, + "fortune": "TIDIG FGEL FICK MASKEN. DU SOV" + }, + { + "id": 52, + "fortune": "DIN STORA CHANS KOMMER. TAPPA DEN INTE" + }, + { + "id": 53, + "fortune": "ROM BYGGDES INTE P EN DAG. INTE DU HELLER" + }, + { + "id": 54, + "fortune": "DINA TALANGER HITTAS. MED EN ORDER" + }, + { + "id": 55, + "fortune": "VINNARE GER ALDRIG UPP. DU FLYTER" + }, + { + "id": 56, + "fortune": "DINA IDER LIGGER FRE. DINA DEADLINES EFTER" + }, + { + "id": 57, + "fortune": "KVALITET R EN VANA. DIN OCKS" + }, + { + "id": 58, + "fortune": "DITT ARBETE TALAR FR SIG. VLDIGT TYST" + }, + { + "id": 59, + "fortune": "TOPPEN R ENSAM. PROBLEM LST" + }, + { + "id": 60, + "fortune": "EN BEFORDRAN KOMMER. NGON ANNANS" + }, + { + "id": 61, + "fortune": "DITT CV IMPONERAR. P FANTASY FANSEN" + }, + { + "id": 62, + "fortune": "DITT BSTA JOBB KOMMER. DET SPRINGER FORT" + }, + { + "id": 63, + "fortune": "DET MTET KUNDE VARIT ETT MAIL" + }, + { + "id": 64, + "fortune": "DIN INKORG R INTE FIENDEN. BARA ETT VITTNE" + }, + { + "id": 65, + "fortune": "DEADLINES SKRMMER DIG INTE. DU STRUNTAR" + }, + { + "id": 66, + "fortune": "DIN FEMRSPLAN R FEM R GAMMAL" + }, + { + "id": 67, + "fortune": "LNGSAMT MEN SKERT VINNER. DU HAR LNGSAMT" + }, + { + "id": 68, + "fortune": "DIN KALENDER R FULL. AV MARGINALER" + }, + { + "id": 69, + "fortune": "DITT LAG DRMMER OCKS. OM FRFLYTTNING" + }, + { + "id": 70, + "fortune": "CHANSEN SNKTE SIG TILL DIN NIV" + }, + { + "id": 71, + "fortune": "FRAMGNG LMNAR SPR. DU GILLAR GTOR" + }, + { + "id": 72, + "fortune": "DIN KOMFORTZON FRTJNAR ETT POSTNUMMER" + }, + { + "id": 73, + "fortune": "KVALITET ELLER KVANTITET. DU NEKAR BDA" + }, + { + "id": 74, + "fortune": "DIN INSTINKT R VASS. OCH OFTA FEL" + }, + { + "id": 75, + "fortune": "DU JOBBAR BRA UNDER PRESS. DEN TVIVLAR" + }, + { + "id": 76, + "fortune": "MSTERSKAP KRVER 10000 TIMMAR. DU HAR 40" + }, + { + "id": 77, + "fortune": "DIN VXT TRODDE P DIG. OCH P VATTEN" + }, + { + "id": 78, + "fortune": "SKRIVAREN KNNER DIN STRESS. DEN KRNGLAR" + }, + { + "id": 79, + "fortune": "DITT ARV R UNDER BYGGE. FORTFARANDE" + }, + { + "id": 80, + "fortune": "ETT DIPLOM VNTAR. DELTAGARDIPLOMET" + }, + { + "id": 81, + "fortune": "ATT NTVERKA PPNAR DRRAR. KNACKA OCKS" + }, + { + "id": 82, + "fortune": "DU FREGR MED EXEMPEL. HR VILL HA ETT ANNAT" + }, + { + "id": 83, + "fortune": "DITT SIDOPROJEKT VILAR MED DE ANDRA" + }, + { + "id": 84, + "fortune": "GENI R EN PROCENT SVETT. DR STANNAR DU" + }, + { + "id": 85, + "fortune": "DITT FRNVAROSVAR R DITT MSTERVERK" + }, + { + "id": 86, + "fortune": "ATT MISSLYCKAS R ETT STEG. FIN TRAPPA" + }, + { + "id": 87, + "fortune": "DU R OERSTTLIG. ANNONSEN R REDAN KLAR" + }, + { + "id": 88, + "fortune": "DITT UTVECKLINGSSAMTAL KOMMER. VA NOGA" + }, + { + "id": 89, + "fortune": "EFFEKTIVITET R DITT MELLANNAMN. P PAPPER" + }, + { + "id": 90, + "fortune": "DU LANDAR P FTTERNA. LANDNINGEN DRJER" + }, + { + "id": 91, + "fortune": "TOPPEN R NRA. BASLGRET OCKS" + }, + { + "id": 92, + "fortune": "DIN AMBITION VAKNADE. INTE DIN VCKARKLOCKA" + }, + { + "id": 93, + "fortune": "VAR DIG SJLV. DE ANDRA BLEV OROLIGA" + }, + { + "id": 94, + "fortune": "DITT MTE GICK BRA. DET R SLUT" + }, + { + "id": 95, + "fortune": "ATT VILA R SUNT. DU VAR MYCKET" + }, + { + "id": 96, + "fortune": "DIN DIET BRJAR P MNDAG. MNDAGAR FLYR" + }, + { + "id": 97, + "fortune": "DU FRTJNAR DETTA. ANDAS DJUPT" + }, + { + "id": 98, + "fortune": "EN FRMLING LER MOT DIG. HLL DIG LUGN" + }, + { + "id": 99, + "fortune": "DU BR INTE AGG. DU ARKIVERAR DET" + }, + { + "id": 100, + "fortune": "DITT TLAMOD HAR EN LADDNINGSSTAPEL. FAST" + } +] \ No newline at end of file diff --git a/apps/sarcastic-fortune-cookies/manifest.json b/apps/sarcastic-fortune-cookies/manifest.json new file mode 100644 index 0000000..8816055 --- /dev/null +++ b/apps/sarcastic-fortune-cookies/manifest.json @@ -0,0 +1,28 @@ +{ + "id": "sarcastic-fortune-cookies", + "name": "Sarcastic Fortune Cookies", + "icon": "🥠", + "description": "Cynical fortune-cookie wisdom", + "category": "entertainment", + "type": "functional", + "i18n": true, + "version": "1.0", + "refresh_interval": 15, + "loop_delay": 15, + "min_modules": 45, + "settings": [ + { + "key": "frequency", + "label": "Change fortune every", + "type": "select", + "default": "300", + "options": [ + { "value": "60", "label": "1 minute" }, + { "value": "300", "label": "5 minutes" }, + { "value": "600", "label": "10 minutes" }, + { "value": "1200", "label": "20 minutes" }, + { "value": "1800", "label": "30 minutes" } + ] + } + ] +} diff --git a/apps/sports/app.py b/apps/sports/app.py index b0f949d..4cb969d 100644 --- a/apps/sports/app.py +++ b/apps/sports/app.py @@ -20,7 +20,19 @@ 'ufc': {'path': 'mma/ufc', 'name': 'UFC'}, } -def fetch(settings, format_lines, get_rows, get_cols): +# The active Localizer for the current fetch (set below). Sports has helper functions, +# so a module-level localizer avoids threading it through every signature; the plugin +# runtime serializes fetches per app, so it's set once per render. +_LOC = None + + +def _t(s): + return _LOC.t(s, "sports") if _LOC is not None else s + + +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): + global _LOC + _LOC = i18n import requests, logging game_filter = settings.get('sports_filter', 'all') @@ -42,9 +54,11 @@ def fetch(settings, format_lines, get_rows, get_cols): if not all_games: if any(settings.get(f'sports_{key}', '').strip() for key in LEAGUES): - filter_labels = {'all': 'ALL', 'live': 'LIVE', 'live+upcoming': 'LIVE/UPCOMING', 'live+final': 'LIVE/FINAL'} - return [format_lines("SPORTS", "NO GAMES", filter_labels.get(game_filter, 'FOUND'))] - return [format_lines("SPORTS", "NO TEAMS", "CONFIGURED")] + filter_labels = {'all': _t('ALL'), 'live': _t('LIVE'), + 'live+upcoming': f"{_t('LIVE')}/{_t('UPCOMING')}", + 'live+final': f"{_t('LIVE')}/{_t('FINAL')}"} + return [format_lines("SPORTS", _t("NO GAMES"), filter_labels.get(game_filter, _t('FOUND')))] + return [format_lines("SPORTS", _t("NO TEAMS"), _t("CONFIGURED"))] rows = get_rows() cols = get_cols() @@ -161,8 +175,8 @@ def _fetch_last_game(info, team_abbr, format_lines, get_cols, requests, game_fil a_score = as_.get('displayValue', str(as_)) if isinstance(as_, dict) else str(as_) h_score = hs_.get('displayValue', str(hs_)) if isinstance(hs_, dict) else str(hs_) score_line = f"{aa} {a_score} {ha} {h_score}" - page = format_lines(info['name'], score_line, "FINAL") - return {'page': page, 'score_line': score_line, 'status': 'FINAL', 'state': 'post'} + page = format_lines(info['name'], score_line, _t("FINAL")) + return {'page': page, 'score_line': score_line, 'status': _t("FINAL"), 'state': 'post'} return None except Exception as e: logging.error(f"Schedule fetch error for {team_abbr}: {e}") @@ -201,7 +215,7 @@ def _parse_events(events, info, team_filter, show_all, format_lines, get_cols, g score_line = f"{aa} VS {ha}" else: score_line = f"{aa} {away.get('score','0')} {ha} {home.get('score','0')}" - status = "FINAL" if state == 'post' else detail.upper()[:15] + status = _t("FINAL") if state == 'post' else detail.upper()[:15] page = format_lines(info['name'], score_line, status) game = {'page': page, 'score_line': score_line, 'status': status, 'state': state} (live if state == 'in' else upcoming if state == 'pre' else final).append(game) @@ -209,10 +223,10 @@ def _parse_events(events, info, team_filter, show_all, format_lines, get_cols, g def _golf(events, info, format_lines): if not events: - return [format_lines("PGA TOUR", "NO EVENT", "THIS WEEK")] + return [format_lines("PGA TOUR", _t("NO EVENT"), _t("THIS WEEK"))] comps = events[0].get('competitions', [{}]) if not comps: - return [format_lines("PGA TOUR", "NO DATA", "")] + return [format_lines("PGA TOUR", _t("NO DATA"), "")] competitors = comps[0].get('competitors', []) competitors.sort(key=lambda c: int(c.get('order', 999))) pages = [] @@ -225,11 +239,11 @@ def _golf(events, info, format_lines): lines.append(f"{c.get('order','?')} {name[:8]} {score}"[:15]) while len(lines) < 3: lines.append('') pages.append(format_lines(*lines)) - return pages or [format_lines("PGA TOUR", "NO LEADERS", "")] + return pages or [format_lines("PGA TOUR", _t("NO LEADERS"), "")] def _mma(events, info, format_lines): if not events: - return [format_lines("UFC", "NO EVENT", "SCHEDULED")] + return [format_lines("UFC", _t("NO EVENT"), _t("SCHEDULED"))] pages = [] for comp in events[0].get('competitions', [])[:5]: competitors = comp.get('competitors', []) @@ -238,9 +252,9 @@ def _mma(events, info, format_lines): n2 = competitors[1].get('athlete', {}).get('shortName', '?').upper()[:7] state = comp.get('status', {}).get('type', {}).get('state', 'pre') detail = comp.get('status', {}).get('type', {}).get('shortDetail', '') - r3 = detail.upper()[:15] if detail else ("LIVE" if state == 'in' else "UPCOMING") + r3 = detail.upper()[:15] if detail else (_t("LIVE") if state == 'in' else _t("UPCOMING")) pages.append(format_lines("UFC", f"{n1} V {n2}", r3)) - return pages or [format_lines("UFC", "NO FIGHTS", "SCHEDULED")] + return pages or [format_lines("UFC", _t("NO FIGHTS"), _t("SCHEDULED"))] def trigger(settings, conditions): diff --git a/apps/sports/manifest.json b/apps/sports/manifest.json index 7d04395..b2f5ecb 100644 --- a/apps/sports/manifest.json +++ b/apps/sports/manifest.json @@ -1,5 +1,6 @@ { "id": "sports", + "i18n": true, "name": "Sports", "icon": "🏒", "description": "Scores & Schedules", diff --git a/apps/stocks/app.py b/apps/stocks/app.py index 6995a4a..639a248 100644 --- a/apps/stocks/app.py +++ b/apps/stocks/app.py @@ -1,31 +1,61 @@ -def fetch(settings, format_lines, get_rows, get_cols): +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): import yfinance as yf + + def t(s): + return i18n.t(s, "stocks") if i18n is not None else s + tickers = [s.strip() for s in settings.get('stocks_list', '').split(',') if s.strip()] if not tickers: - return [format_lines('STOCKS', 'NO TICKERS', 'CONFIGURE')] - currency = settings.get('currency_symbol', '$').strip() or '$' + return [format_lines('STOCKS', t('NO TICKERS'), t('CONFIGURE'))] + no_color = settings.get('disable_colors', 'no') == 'yes' rows = get_rows() + + # Each ticker is quoted in its exchange's own currency (AAPL->USD, VOD.L->GBP, + # SAP.DE->EUR); show that currency's symbol, not a single hardcoded one. + def sym_for(cur): + # With i18n the display can render € £ ¥ (Windows-1252); without it the modules + # are the basic charset, so use '$' for USD and the ASCII ISO code otherwise. + if i18n is not None: + return i18n.currency_symbol(cur) + return '$' if cur == 'USD' else cur + + # Numbers follow the global Language (1,234.50 vs 1.234,50 vs 1 234,50). + def n(v, d=2, grouping=True): + if i18n is not None: + return i18n.number(v, d, grouping) + return f'{v:,.{d}f}' if grouping else f'{v:.{d}f}' + + def pct(v): + return f"{'+' if v >= 0 else '-'}{n(abs(v), 1, grouping=False)}%" + pages = [] for i in range(0, len(tickers), rows): chunk = tickers[i:i+rows] price_lines, change_lines = [], [] for sym in chunk: try: - t = yf.Ticker(sym) - info = t.fast_info + info = yf.Ticker(sym).fast_info price = info['lastPrice'] prev = info['previousClose'] + try: + cur = (info['currency'] or 'USD') + except Exception: + cur = 'USD' + if cur in ('GBp', 'GBX'): # London quotes pence, not pounds + price, prev, cur = price / 100, prev / 100, 'GBP' chg = ((price - prev) / prev) * 100 - icon = '🟩' if chg >= 0 else '🟥' - price_lines.append(f'{sym} {currency}{price:.2f}') - change_lines.append(f'{sym} {icon}{chg:+.1f}%') + cs = sym_for(cur) + sep = '' if cs != cur else ' ' # 3-letter-code fallback reads better spaced + icon = '' if no_color else ('🟩' if chg >= 0 else '🟥') + price_lines.append(f'{sym} {cs}{sep}{n(price, 2)}') + change_lines.append(f'{sym} {icon}{pct(chg)}') except Exception: price_lines.append(f'{sym} ERR') change_lines.append(f'{sym} ERR') pad = [''] * (rows - len(chunk)) pages.append(format_lines(*(price_lines + pad))) pages.append(format_lines(*(change_lines + pad))) - return pages or [format_lines('STOCKS', 'NO DATA', '')] + return pages or [format_lines('STOCKS', t('NO DATA'), '')] def trigger(settings, conditions): diff --git a/apps/stocks/manifest.json b/apps/stocks/manifest.json index a5d6214..eb19d37 100644 --- a/apps/stocks/manifest.json +++ b/apps/stocks/manifest.json @@ -1,5 +1,6 @@ { "id": "stocks", + "i18n": true, "name": "Stocks", "icon": "📈", "description": "Live prices", diff --git a/apps/sun-times/app.py b/apps/sun-times/app.py new file mode 100644 index 0000000..0a31c41 --- /dev/null +++ b/apps/sun-times/app.py @@ -0,0 +1,74 @@ +"""Sunrise / sunset / day length for the configured location (keyless: Open-Meteo). + +Times track the location: Open-Meteo returns them in the place's own local time +(timezone=auto), just like the weather app — no separate timezone setting needed.""" + + +def _latlon(settings, requests): + """Global precise location, else geocode of the ZIP, else a Boston fallback.""" + lat = str(settings.get('location_lat', '') or '').strip() + lon = str(settings.get('location_lon', '') or '').strip() + if lat and lon: + try: + return float(lat), float(lon) + except ValueError: + pass + zip_code = str(settings.get('zip_code', '02118') or '02118').strip() + try: + import re + params = {'q': zip_code, 'format': 'json', 'limit': 1} + if re.fullmatch(r'\d{5}', zip_code): # a US ZIP — disambiguate (02118 also exists abroad) + params['countrycodes'] = 'us' + geo = requests.get('https://nominatim.openstreetmap.org/search', params=params, + headers={'User-Agent': 'SplitFlapGatewayCompanion/1.0'}, + timeout=6).json() + if geo: + return float(geo[0]['lat']), float(geo[0]['lon']) + except Exception: + pass + return 42.3601, -71.0589 + + +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): + import requests + from datetime import datetime + rows, cols = get_rows(), get_cols() + + def t(s): + return i18n.t(s, "sun") if i18n is not None else s + + def u(k): # localized H/M duration suffix (Dutch U for uur, etc.) + return i18n.unit(k) if i18n is not None else k + + def line(label, value): # label trimmed to fit — never the time + return f'{label[:max(1, cols - len(value) - 1)]} {value}' + + def fmt_time(iso): # ISO is already the location's local time + if not iso: + return '--:--' + dt = datetime.fromisoformat(str(iso)) + # AM/PM is English-only — everyone else gets 24h. + if i18n is not None: + return i18n.time(dt, ampm_space=False) + return dt.strftime('%I:%M%p').lstrip('0') + + try: + lat, lon = _latlon(settings, requests) + data = requests.get('https://api.open-meteo.com/v1/forecast', + params={'latitude': lat, 'longitude': lon, + 'daily': 'sunrise,sunset,daylight_duration', + 'timezone': 'auto', 'forecast_days': 1}, + timeout=8).json() + daily = data.get('daily', {}) + rise = fmt_time((daily.get('sunrise') or [None])[0]) + sett = fmt_time((daily.get('sunset') or [None])[0]) + secs = int((daily.get('daylight_duration') or [0])[0] or 0) + length = f'{secs // 3600}{u("H")}{(secs % 3600) // 60:02d}{u("M")}' + if rows == 1: + return [format_lines(f'{t("UP")} {rise} {t("DN")} {sett}')] + if rows == 2: + return [format_lines(line(t('SUNRISE'), rise), line(t('SUNSET'), sett))] + return [format_lines(line(t('SUNRISE'), rise), line(t('SUNSET'), sett), + line(t('DAYLIGHT'), length))] + except Exception: + return [format_lines('SUN TIMES', 'OFFLINE', '')] diff --git a/apps/sun-times/manifest.json b/apps/sun-times/manifest.json new file mode 100644 index 0000000..2e465ea --- /dev/null +++ b/apps/sun-times/manifest.json @@ -0,0 +1,25 @@ +{ + "id": "sun-times", + "i18n": true, + "name": "Sun Times", + "icon": "☀️", + "description": "Sunrise, sunset & day length", + "type": "functional", + "refresh_interval": 1800, + "loop_delay": 6, + "category": "data", + "min_rows": 2, + "min_cols": 12, + "settings": [ + { + "key": "loop_delay", + "label": "Delay Between Pages (seconds)", + "type": "number", + "default": "6", + "min": "2", + "max": "30", + "step": "1", + "stepper": true + } + ] +} diff --git a/apps/tides/app.py b/apps/tides/app.py new file mode 100644 index 0000000..97b268d --- /dev/null +++ b/apps/tides/app.py @@ -0,0 +1,52 @@ +"""Today's tide predictions for a NOAA station (keyless: NOAA CO-OPS).""" + + +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): + import requests + from datetime import datetime + rows, cols = get_rows(), get_cols() + + def t(s): + return i18n.t(s, "tides") if i18n is not None else s + + def fmt_time(hhmm): # NOAA gives 24h local time ("15:48") + try: + dt = datetime.strptime(hhmm, '%H:%M') + except ValueError: + return hhmm + # AM/PM is English-only — everyone else gets 24h. + if i18n is not None: + return i18n.time(dt, ampm_space=False) + return dt.strftime('%I:%M%p').lstrip('0') + + station = str(settings.get('tide_station', '8443970') or '8443970').strip() + try: + data = requests.get( + 'https://api.tidesandcurrents.noaa.gov/api/prod/datagetter', + params={'product': 'predictions', 'application': 'SplitFlapCompanion', + 'datum': 'MLLW', 'station': station, 'time_zone': 'lst_ldt', + 'units': 'english', 'interval': 'hilo', 'format': 'json', 'date': 'today'}, + timeout=8).json() + preds = data.get('predictions') or [] + if not preds: + return [format_lines(t('TIDES'), t('NO DATA'), t('CHECK STATION'))] + pages = [] + for p in preds[:6]: + raw = str(p.get('t', '')) + hhmm = fmt_time(raw.split(' ')[-1] if ' ' in raw else raw) + is_high = p.get('type') == 'H' + v = str(p.get('v', '')) + if rows == 1: + # Compact single row: the short generic high/low word keeps the + # time + height on one line (the full "X TIDE" would crowd them out). + kind = t('HIGH') if is_high else t('LOW') + pages.append(f'{kind} {hhmm} {v}FT'[:cols].center(cols)) + elif rows == 2: + kind = t('HIGH TIDE') if is_high else t('LOW TIDE') + pages.append(format_lines(kind, f'{hhmm} {v}FT')) + else: + kind = t('HIGH TIDE') if is_high else t('LOW TIDE') + pages.append(format_lines(kind, hhmm, f'{v} FT')) + return pages or [format_lines(t('TIDES'), t('NO DATA'), '')] + except Exception: + return [format_lines(t('TIDES'), t('OFFLINE'), '')] diff --git a/apps/tides/manifest.json b/apps/tides/manifest.json new file mode 100644 index 0000000..f5994cd --- /dev/null +++ b/apps/tides/manifest.json @@ -0,0 +1,32 @@ +{ + "id": "tides", + "name": "Tides", + "icon": "🌊", + "description": "Today's high & low tides (NOAA)", + "type": "functional", + "i18n": true, + "refresh_interval": 3600, + "loop_delay": 6, + "category": "data", + "min_rows": 1, + "min_cols": 12, + "settings": [ + { + "key": "tide_station", + "label": "NOAA tide station ID", + "type": "text", + "default": "8443970", + "note": "Find yours at tidesandcurrents.noaa.gov (default is Boston, MA)." + }, + { + "key": "loop_delay", + "label": "Delay Between Pages (seconds)", + "type": "number", + "default": "6", + "min": "2", + "max": "30", + "step": "1", + "stepper": true + } + ] +} diff --git a/apps/time-since/app.py b/apps/time-since/app.py index 84a0dc4..4b8d4ab 100644 --- a/apps/time-since/app.py +++ b/apps/time-since/app.py @@ -1,6 +1,13 @@ -def fetch(settings, format_lines, get_rows, get_cols): +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): from datetime import datetime import pytz + + def t(s): + return i18n.t(s, "time") if i18n is not None else s + + def u(k): # localized Y/D/H/M/S suffix + return i18n.unit(k) if i18n is not None else k + tz = pytz.timezone(settings.get('timezone', 'US/Eastern')) now = datetime.now(tz) event = settings.get('event_name', 'THE START').upper() @@ -9,20 +16,20 @@ def fetch(settings, format_lines, get_rows, get_cols): start = datetime.strptime(date_str, '%Y-%m-%d') start = tz.localize(start) except Exception: - return [format_lines(event, 'INVALID DATE', '')] + return [format_lines(event, t('INVALID DATE'), '')] diff = now - start if diff.total_seconds() < 0: - return [format_lines(event, 'NOT YET', 'STARTED')] + return [format_lines(event, t('NOT YET'), t('STARTED'))] days = diff.days hrs, rem = divmod(diff.seconds, 3600) mins, secs = divmod(rem, 60) years = days // 365 remaining_days = days % 365 if years > 0: - elapsed = f'{years}Y {remaining_days}D {hrs}H' + elapsed = f'{years}{u("Y")} {remaining_days}{u("D")} {hrs}{u("H")}' else: - elapsed = f'{days}D {hrs}H {mins}M {secs}S' - return [format_lines(event, elapsed, 'TIME SINCE')] + elapsed = f'{days}{u("D")} {hrs}{u("H")} {mins}{u("M")} {secs}{u("S")}' + return [format_lines(event, elapsed, t('TIME SINCE'))] def trigger(settings, conditions): diff --git a/apps/time-since/manifest.json b/apps/time-since/manifest.json index 3920a65..d10ec14 100644 --- a/apps/time-since/manifest.json +++ b/apps/time-since/manifest.json @@ -1,5 +1,6 @@ { "id": "time-since", + "i18n": true, "name": "Time Since", "icon": "⏱️", "description": "Track elapsed time", diff --git a/apps/time/app.py b/apps/time/app.py index 7375286..b9a5deb 100644 --- a/apps/time/app.py +++ b/apps/time/app.py @@ -1,10 +1,17 @@ -def fetch(settings, format_lines, get_rows, get_cols): +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): from datetime import datetime import pytz tz = pytz.timezone(settings.get('timezone', 'US/Eastern')) now = datetime.now(tz) - fmt = "%H:%M" if settings.get('time_format') == '24hr' else "%I:%M%p" - time_str = now.strftime(fmt).lstrip("0") + # An explicit Time Format wins; otherwise the Language decides (12h AM/PM for + # English, 24h elsewhere) via the injected i18n helper. + tf = settings.get('time_format') + if tf in ('12hr', '24hr'): + time_str = now.strftime("%H:%M" if tf == '24hr' else "%I:%M%p").lstrip("0") + elif i18n is not None: + time_str = i18n.time(now, ampm_space=False) + else: + time_str = now.strftime("%I:%M%p").lstrip("0") rows = get_rows() if rows == 1: return [format_lines(time_str)] diff --git a/apps/time/manifest.json b/apps/time/manifest.json index 9878e16..49af419 100644 --- a/apps/time/manifest.json +++ b/apps/time/manifest.json @@ -1,5 +1,6 @@ { "id": "time", + "i18n": true, "name": "Time", "icon": "⏱️", "description": "Live clock", diff --git a/apps/useless-fact/app.py b/apps/useless-fact/app.py new file mode 100644 index 0000000..b573dbf --- /dev/null +++ b/apps/useless-fact/app.py @@ -0,0 +1,125 @@ +"""A random useless fact (keyless: uselessfacts).""" + + +def _need_lines(lens, cols): + need, cur = 1, 0 + for wl in lens: + add = wl if cur == 0 else cur + 1 + wl + if add <= cols: + cur = add + else: + need += 1 + cur = wl + return need + + +def _balance(words, lens, cols, k): + """Split words into exactly k lines (each <= cols) minimizing raggedness, so + lines fill evenly instead of orphaning the last word; prefers ending a line at + sentence punctuation. A tiny DP.""" + n = len(words) + pre = [0] + for wl in lens: + pre.append(pre[-1] + wl) + + def linelen(i, j): + return pre[j + 1] - pre[i] + (j - i) + + INF = float("inf") + dp = [[INF] * (n + 1) for _ in range(k + 1)] + nxt = [[0] * (n + 1) for _ in range(k + 1)] + dp[0][n] = 0.0 + for kk in range(1, k + 1): + for i in range(n - 1, -1, -1): + j = i + while j < n: + ll = linelen(i, j) + if ll > cols and j > i: + break + rest = dp[kk - 1][j + 1] + if rest < INF: + slack = cols - ll + cost = slack * slack + rest + if words[j][-1:] in ".!?": + cost -= cols + if cost < dp[kk][i]: + dp[kk][i] = cost + nxt[kk][i] = j + 1 + j += 1 + if dp[k][0] >= INF: + return None + out, i, kk = [], 0, k + while kk > 0: + j = nxt[kk][i] + out.append(" ".join(words[i:j])) + i, kk = j, kk - 1 + return out + + +def _greedy(words, cols): + lines, cur = [], '' + for w in words: + w = w if len(w) <= cols else w[:cols] + if len(cur) + len(w) + (1 if cur else 0) <= cols: + cur = f'{cur} {w}'.strip() + else: + lines.append(cur) + cur = w + if cur: + lines.append(cur) + return lines or [''] + + +def _pages(format_lines, title, text, rows, cols): + """Lay the text out under a title. When it fits on one page the words are + balanced evenly across the lines it needs; longer text word-wraps and + paginates.""" + words = text.split() or [''] + lens = [len(w) for w in words] + if rows == 1: + return [ln.center(cols)[:cols] for ln in _greedy(words, cols)] + body = rows - 1 + if max(lens) <= cols: + need = _need_lines(lens, cols) + if need <= body: + bal = _balance(words, lens, cols, need) + if bal is not None: + return [format_lines(title, *bal)] + lines = _greedy(words, cols) + pages = [format_lines(title, *lines[:body])] + i = body + while i < len(lines): + pages.append(format_lines(*lines[i:i + rows])) + i += rows + return pages + +def fetch(settings, format_lines, get_rows, get_cols): + import requests + rows, cols = get_rows(), get_cols() + try: + max_len = int(float(settings.get('max_length', '140') or 140)) + except (TypeError, ValueError): + max_len = 140 + max_len = max(40, min(280, max_len)) + + def one(): + d = requests.get('https://uselessfacts.jsph.pl/api/v2/facts/random', + params={'language': 'en'}, timeout=8).json() + return str(d.get('text', '') or '').strip() + + try: + # This API can't filter by length, so try a few times for a short one and + # keep the shortest we've seen if none come in under the limit. + best = None + for _ in range(3): + t = one() + if t and len(t) <= max_len: + best = t + break + if t and (best is None or len(t) < len(best)): + best = t + if not best: + return [format_lines('RANDOM FACT', 'NO DATA', '')] + return _pages(format_lines, 'DID YOU KNOW', best.upper(), rows, cols) + except Exception: + return [format_lines('RANDOM FACT', 'OFFLINE', '')] diff --git a/apps/useless-fact/manifest.json b/apps/useless-fact/manifest.json new file mode 100644 index 0000000..1f0ae3a --- /dev/null +++ b/apps/useless-fact/manifest.json @@ -0,0 +1,44 @@ +{ + "id": "useless-fact", + "name": "Random Fact", + "icon": "🤓", + "description": "A concise random fact", + "type": "functional", + "refresh_interval": 1800, + "loop_delay": 7, + "category": "entertainment", + "min_rows": 3, + "min_cols": 15, + "settings": [ + { + "key": "max_length", + "label": "Max fact length (characters)", + "type": "number", + "default": "140", + "min": "40", + "max": "280", + "step": "10", + "stepper": true + }, + { + "key": "refresh_minutes", + "label": "Fetch a new fact every (minutes)", + "type": "number", + "default": "30", + "min": "1", + "max": "1440", + "step": "1", + "stepper": true + }, + { + "key": "loop_delay", + "label": "Delay Between Pages (seconds)", + "type": "number", + "default": "7", + "min": "2", + "max": "30", + "step": "1", + "stepper": true + } + ] +} diff --git a/apps/weather/app.py b/apps/weather/app.py index 233083d..d401239 100644 --- a/apps/weather/app.py +++ b/apps/weather/app.py @@ -172,7 +172,9 @@ def _format_temp(value, temp_unit): return f"{int(round(converted))}{temp_unit.upper()}" -def _compact_color(color): +def _compact_color(color, mono=False): + if mono: + return '' return { 'GREEN': '🟩', 'YELLOW': '🟨', @@ -183,9 +185,11 @@ def _compact_color(color): }.get(color, color) -def _decorate_status(label, color, cols): - swatch = _compact_color(color) +def _decorate_status(label, color, cols, mono=False): text = str(label or '').strip() + if mono: # colors disabled: show the label only + return text[:cols] + swatch = _compact_color(color) if not text: return swatch @@ -204,10 +208,20 @@ def _decorate_status(label, color, cols): return swatch -def fetch(settings, format_lines, get_rows, get_cols): +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): import time import requests + # Global Language (when the companion injects i18n). Open-Meteo maps codes to + # English text we translate ourselves; the keyed providers can return native + # condition text if we ask, so we pass the language through to them. + # Base language for the providers' `lang` param — they want 'en'/'fr', not a + # regional 'en-gb' (the English variants all return English weather text anyway). + lang = (i18n.lang.split('-')[0] if i18n is not None else 'en') + + def t(s): + return i18n.t(s, "weather") if i18n is not None else s + # --- Polling state (persists across calls on this plugin instance) --- state = getattr(fetch, '_state', None) if state is None: @@ -224,43 +238,51 @@ def fetch(settings, format_lines, get_rows, get_cols): temp_unit = str(settings.get('temperature_unit', 'f')).lower() if temp_unit not in ('f', 'c', 'k'): temp_unit = 'f' + no_color = settings.get('disable_colors', 'no') == 'yes' show_aqi = settings.get('show_aqi', 'yes') == 'yes' show_uv = settings.get('show_uv', 'yes') == 'yes' and weather_provider in ('openmeteo', 'weatherapi') show_pollen = settings.get('show_pollen', 'yes') == 'yes' and weather_provider in ('openmeteo', 'weatherapi') polling_seconds = max(30.0, min(86400.0, float(settings.get('polling_rate', 300) or 300))) openmeteo_air = None - # Resolve location: per-app override > global lat/lon > geocode zip_code + # Resolve location: per-app override > global lat/lon > geocode zip_code. + # Any bad input / geocode failure falls back to Boston instead of crashing. app_location = settings.get('location', '').strip() loc_lat = settings.get('location_lat', '') loc_lon = settings.get('location_lon', '') loc_name = settings.get('location_name', '') - if app_location and ',' in app_location: - if '|' in app_location: - coords, _city = app_location.split('|', 1) - _city = _city.strip().upper() - else: - coords = app_location - _city = 'LOCATION' - parts = coords.split(',', 1) - _lat, _lon = float(parts[0]), float(parts[1]) - elif loc_lat and loc_lon: - _lat, _lon = float(loc_lat), float(loc_lon) - _city = loc_name.split(',')[0].strip().upper() if loc_name else 'LOCATION' - else: - geo = requests.get( - f'https://nominatim.openstreetmap.org/search?q={zip_code}&format=json&limit=1', - timeout=5, headers={'User-Agent': 'SplitFlapOS/1.0'} - ).json() - if geo: - _lat, _lon = float(geo[0]['lat']), float(geo[0]['lon']) - _city = geo[0].get('display_name', zip_code).split(',')[0].strip().upper() + _lat, _lon, _city = 42.3496, -71.0783, 'BOSTON' + try: + if app_location and ',' in app_location: + if '|' in app_location: + coords, _city = app_location.split('|', 1) + _city = _city.strip().upper() + else: + coords = app_location + _city = 'LOCATION' + parts = coords.split(',', 1) + _lat, _lon = float(parts[0]), float(parts[1]) + elif loc_lat and loc_lon: + _lat, _lon = float(loc_lat), float(loc_lon) + _city = loc_name.split(',')[0].strip().upper() if loc_name else 'LOCATION' else: - _lat, _lon, _city = 42.3496, -71.0783, 'BOSTON' + import re + _geo_params = {'q': zip_code, 'format': 'json', 'limit': 1} + if re.fullmatch(r'\d{5}', str(zip_code).strip()): # US ZIP — 02118 also exists abroad + _geo_params['countrycodes'] = 'us' + geo = requests.get( + 'https://nominatim.openstreetmap.org/search', params=_geo_params, + timeout=5, headers={'User-Agent': 'SplitFlapGatewayCompanion/1.0'} + ).json() + if geo: + _lat, _lon = float(geo[0]['lat']), float(geo[0]['lon']) + _city = geo[0].get('display_name', zip_code).split(',')[0].strip().upper() + except (ValueError, KeyError, IndexError, TypeError, requests.RequestException): + _lat, _lon, _city = 42.3496, -71.0783, 'BOSTON' settings_sig = ( api_key, _lat, _lon, weather_provider, temp_unit, - show_aqi, show_uv, show_pollen, polling_seconds, + show_aqi, show_uv, show_pollen, polling_seconds, lang, ) now_ts = time.time() sig_changed = settings_sig != state['last_sig'] @@ -288,7 +310,7 @@ def _normalize_weatherapi_pollen(payload): def _fetch_openweather_weather(): payload = requests.get( 'https://api.openweathermap.org/data/2.5/weather', - params={'lat': _lat, 'lon': _lon, 'appid': api_key, 'units': 'imperial'}, + params={'lat': _lat, 'lon': _lon, 'appid': api_key, 'units': 'imperial', 'lang': lang}, timeout=10 ).json() return { @@ -326,7 +348,7 @@ def _fetch_openmeteo_weather(): 'feels_like': int(round(current.get('apparent_temperature', current.get('temperature_2m', 0)))), 'hi': int(round(hi_values[0] if hi_values else 0)), 'lo': int(round(lo_values[0] if lo_values else 0)), - 'desc': OPENMETEO_WEATHER_CODES.get(current.get('weather_code'), 'CURRENT CONDITIONS'), + 'desc': t(OPENMETEO_WEATHER_CODES.get(current.get('weather_code'), 'CURRENT CONDITIONS')), 'lat': _lat, 'lon': _lon, 'uv': current.get('uv_index'), @@ -341,6 +363,7 @@ def _fetch_weatherapi_weather(): 'days': 1, 'aqi': 'yes', 'pollen': 'yes', + 'lang': lang, }, timeout=10 ).json() @@ -355,7 +378,7 @@ def _fetch_weatherapi_weather(): 'feels_like': int(round(current.get('feelslike_f', current.get('temp_f', 0)))), 'hi': int(round(day.get('maxtemp_f', current.get('temp_f', 0)))), 'lo': int(round(day.get('mintemp_f', current.get('temp_f', 0)))), - 'desc': str((current.get('condition') or {}).get('text', 'CURRENT CONDITIONS')).upper(), + 'desc': str((current.get('condition') or {}).get('text', t('CURRENT CONDITIONS'))).upper(), 'lat': location.get('lat'), 'lon': location.get('lon'), 'uv': current.get('uv'), @@ -369,7 +392,7 @@ def _fetch_qweather_weather(): now_r = requests.get( 'https://devapi.qweather.com/v7/weather/now', - params={'location': location, 'lang': 'en', 'unit': 'i'}, + params={'location': location, 'lang': lang, 'unit': 'i'}, headers=headers, timeout=10 ).json() @@ -377,7 +400,7 @@ def _fetch_qweather_weather(): daily_r = requests.get( 'https://devapi.qweather.com/v7/weather/3d', - params={'location': location, 'lang': 'en', 'unit': 'i'}, + params={'location': location, 'lang': lang, 'unit': 'i'}, headers=headers, timeout=10 ).json() @@ -389,7 +412,7 @@ def _fetch_qweather_weather(): 'feels_like': _to_int(now.get('feelsLike'), _to_int(now.get('temp'))), 'hi': _to_int(first_day.get('tempMax'), _to_int(now.get('temp'))), 'lo': _to_int(first_day.get('tempMin'), _to_int(now.get('temp'))), - 'desc': str(now.get('text', 'CURRENT CONDITIONS')).upper(), + 'desc': str(now.get('text', t('CURRENT CONDITIONS'))).upper(), 'lat': _lat, 'lon': _lon, } @@ -442,14 +465,14 @@ def _get_openmeteo_air(lat, lon): cols = get_cols() narrow = cols <= 12 - feels_word = 'FLS' if narrow else 'FEELS' - pollen_word = 'POL' if narrow else 'POLLEN' - overall_word = 'OVR' if narrow else 'OVERALL' - provider_word = 'PRV' if narrow else 'PROV' - sun_exposure_text = 'SUN UV' if narrow else 'SUN EXPOSURE' - grass_word = 'GRS' if narrow else 'GRASS' - tree_word = 'TRE' if narrow else 'TREE' - weed_word = 'WED' if narrow else 'WEED' + feels_word = t('FLS') if narrow else t('FEELS') + pollen_word = t('POL') if narrow else t('POLLEN') + overall_word = t('OVR') if narrow else t('OVERALL') + provider_word = t('PRV') if narrow else t('PROV') + sun_exposure_text = t('SUN UV') if narrow else t('SUN EXPOSURE') + grass_word = t('GRS') if narrow else t('GRASS') + tree_word = t('TRE') if narrow else t('TREE') + weed_word = t('WED') if narrow else t('WEED') city = weather['city'] temp = _format_temp(weather.get('temp'), temp_unit) @@ -460,6 +483,8 @@ def _get_openmeteo_air(lat, lon): lat = weather['lat'] lon = weather['lon'] + # Only one location is supported, so we don't repeat it on every page — + # the current conditions fit on a single consolidated page. rows = get_rows() if rows == 1: pages = [format_lines(f'{temp} {desc}')] @@ -469,15 +494,10 @@ def _get_openmeteo_air(lat, lon): format_lines(f'{hi} {lo}', desc), ] elif rows == 3: - pages = [ - format_lines(city, f'{temp} {feels}', desc), - format_lines(city, f'{hi} {lo}', desc), - ] + pages = [format_lines(f'{temp} {feels}', f'{hi} {lo}', desc)] else: - pages = [ - format_lines(city, f'{temp} {feels}', desc, f'{hi} {lo}'), - format_lines(city, f'{hi} {lo}', desc, f'{provider_word} {weather_provider.upper()}'), - ] + pages = [format_lines(f'{temp} {feels}', f'{hi} {lo}', desc, + f'{provider_word} {weather_provider.upper()}')] if show_aqi: try: @@ -507,15 +527,15 @@ def _get_openmeteo_air(lat, lon): if aqi_num is None or aqi_num <= 0: raise ValueError('AQI unavailable') - aqi_display = _decorate_status(aqi_label, aqi_color, cols) + aqi_display = _decorate_status(t(aqi_label), aqi_color, cols, no_color) if rows == 1: pages.append(format_lines(f'AQI {aqi_display}')) elif rows == 2: pages.append(format_lines(f'AQI {aqi_num}', aqi_display)) elif rows == 3: - pages.append(format_lines(city, f'AQI {aqi_num}', aqi_display)) + pages.append(format_lines(t('AIR QUALITY'), f'AQI {aqi_num}', aqi_display)) else: - pages.append(format_lines(city, f'AQI {aqi_num}', aqi_display, f'{provider_word} {weather_provider.upper()}')) + pages.append(format_lines(t('AIR QUALITY'), f'AQI {aqi_num}', aqi_display, f'{provider_word} {weather_provider.upper()}')) except Exception: pass @@ -529,15 +549,15 @@ def _get_openmeteo_air(lat, lon): uv_num = int(round(float(uv_value))) uv_label = _uv_level(float(uv_value)) uv_color = _uv_color(float(uv_value)) - uv_display = _decorate_status(uv_label, uv_color, cols) + uv_display = _decorate_status(t(uv_label), uv_color, cols, no_color) if rows == 1: pages.append(format_lines(f'UV {uv_display}')) elif rows == 2: pages.append(format_lines(f'UV {uv_num}', uv_display)) elif rows == 3: - pages.append(format_lines(city, f'UV {uv_num}', uv_display)) + pages.append(format_lines(sun_exposure_text, f'UV {uv_num}', uv_display)) else: - pages.append(format_lines(city, f'UV {uv_num}', uv_display, sun_exposure_text)) + pages.append(format_lines(sun_exposure_text, f'UV {uv_num}', uv_display, '')) except Exception: pass @@ -566,22 +586,22 @@ def _get_openmeteo_air(lat, lon): raise ValueError('Pollen unavailable') overall = max(vals) if vals else None - overall_label = _pollen_level(overall) + overall_label = t(_pollen_level(overall)) overall_color = _pollen_color(overall) - grass_label = _pollen_level(grass) - tree_label = _pollen_level(birch) - weed_label = _pollen_level(weed or ragweed) - overall_display = _decorate_status(overall_label, overall_color, cols) + grass_label = t(_pollen_level(grass)) + tree_label = t(_pollen_level(birch)) + weed_label = t(_pollen_level(weed or ragweed)) + overall_display = _decorate_status(overall_label, overall_color, cols, no_color) component_displays = [] if grass is not None: - grass_display = f'{grass_word} {grass_label} {_compact_color(_pollen_color(grass))}' + grass_display = f'{grass_word} {grass_label} {_compact_color(_pollen_color(grass), no_color)}'.rstrip() component_displays.append(grass_display) if birch is not None: - tree_display = f'{tree_word} {tree_label} {_compact_color(_pollen_color(birch))}' + tree_display = f'{tree_word} {tree_label} {_compact_color(_pollen_color(birch), no_color)}'.rstrip() component_displays.append(tree_display) weed_value = weed or ragweed if weed_value is not None: - weed_display = f'{weed_word} {weed_label} {_compact_color(_pollen_color(weed_value))}' + weed_display = f'{weed_word} {weed_label} {_compact_color(_pollen_color(weed_value), no_color)}'.rstrip() component_displays.append(weed_display) if rows == 1: @@ -591,12 +611,12 @@ def _get_openmeteo_air(lat, lon): if component_displays: pages.append(format_lines(*component_displays)) elif rows == 3: - pages.append(format_lines(city, pollen_word, overall_display)) + pages.append(format_lines(pollen_word, overall_display)) if component_displays: pages.append(format_lines(*component_displays)) else: - pages.append(format_lines(city, pollen_word, overall_display, f'{provider_word} {weather_provider.upper()}')) - detail_lines = [city] + component_displays + pages.append(format_lines(pollen_word, overall_display, f'{provider_word} {weather_provider.upper()}')) + detail_lines = list(component_displays) if rows >= 5: detail_lines.append(f'{overall_word} {overall_display}') pages.append(format_lines(*detail_lines)) diff --git a/apps/weather/manifest.json b/apps/weather/manifest.json index 48c5915..d503631 100644 --- a/apps/weather/manifest.json +++ b/apps/weather/manifest.json @@ -1,5 +1,6 @@ { "id": "weather", + "i18n": true, "name": "Weather", "icon": "🌤️", "description": "Current conditions", diff --git a/apps/wiki-today/app.py b/apps/wiki-today/app.py new file mode 100644 index 0000000..478bc52 --- /dev/null +++ b/apps/wiki-today/app.py @@ -0,0 +1,57 @@ +"""Today on Wikipedia — featured article & most-read (keyless: Wikimedia REST).""" + + +def _wrap(text, cols, maxlines): + words, lines, cur = text.split(), [], '' + for w in words: + if len(cur) + len(w) + (1 if cur else 0) <= cols: + cur = f'{cur} {w}'.strip() + else: + lines.append(cur) + cur = w[:cols] + if len(lines) >= maxlines: + break + if cur and len(lines) < maxlines: + lines.append(cur) + return lines[:maxlines] or [''] + + +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): + import requests + from datetime import datetime + import pytz + rows, cols = get_rows(), get_cols() + + def t(s): + return i18n.t(s, "content") if i18n is not None else s + + # Pull from the language's own Wikipedia edition (fr.wikipedia, de.wikipedia, …); + # the English variants all use en.wikipedia. Defaults to (American) English. + wl = i18n.lang_base if i18n is not None else 'en' + try: + try: + tz = pytz.timezone(settings.get('timezone', 'US/Eastern')) + except pytz.UnknownTimeZoneError: + tz = pytz.timezone('US/Eastern') + now = datetime.now(tz) + d = requests.get(f'https://{wl}.wikipedia.org/api/rest_v1/feed/featured/{now:%Y/%m/%d}', + headers={'User-Agent': 'SplitFlapGatewayCompanion/1.0'}, timeout=10).json() + pages = [] + tfa = d.get('tfa') or {} + title = str(tfa.get('normalizedtitle', '') or '').upper() + if title: + if rows == 1: + pages.append(f'WIKI {title}'[:cols].center(cols)) + else: + pages.append(format_lines(f'WIKI {t("FEATURED")}', *_wrap(title, cols, rows - 1))) + for a in ((d.get('mostread') or {}).get('articles', []) or [])[:3]: + art = str(a.get('normalizedtitle', '') or '').upper() + if not art: + continue + if rows == 1: + pages.append(f'WIKI {art}'[:cols].center(cols)) + else: + pages.append(format_lines(f'WIKI {t("MOST READ")}', *_wrap(art, cols, rows - 1))) + return pages or [format_lines('WIKIPEDIA', 'NO DATA', '')] + except Exception: + return [format_lines('WIKIPEDIA', 'OFFLINE', '')] diff --git a/apps/wiki-today/manifest.json b/apps/wiki-today/manifest.json new file mode 100644 index 0000000..61da9cb --- /dev/null +++ b/apps/wiki-today/manifest.json @@ -0,0 +1,25 @@ +{ + "id": "wiki-today", + "i18n": true, + "name": "Wikipedia Today", + "icon": "📚", + "description": "Featured article & most-read on Wikipedia", + "type": "functional", + "refresh_interval": 3600, + "loop_delay": 8, + "category": "news", + "min_rows": 2, + "min_cols": 15, + "settings": [ + { + "key": "loop_delay", + "label": "Delay Between Pages (seconds)", + "type": "number", + "default": "8", + "min": "2", + "max": "30", + "step": "1", + "stepper": true + } + ] +} diff --git a/apps/word-clock/app.py b/apps/word-clock/app.py index 93a5bda..c3180d0 100644 --- a/apps/word-clock/app.py +++ b/apps/word-clock/app.py @@ -1,21 +1,226 @@ -def fetch(settings, format_lines, get_rows, get_cols): - from datetime import datetime - import pytz - tz = pytz.timezone(settings.get('timezone', 'US/Eastern')) - now = datetime.now(tz) - h = now.hour - m = now.minute +"""Word clock — spells the time out ("IT'S HALF PAST TEN"). + +Localized into the major Western-European languages when the companion injects +i18n (honoring the global Language). Each language has its own grammar — Romance +languages name the hour first (IL EST DIX HEURES ET QUART), Germanic ones lead +with the minutes (VIERTEL NACH ZEHN), and German/Dutch "half" points at the *next* +hour (HALB DREI = 2:30). Anything without a builder (or a bare host) falls back to +the original English behavior. +""" + +# Hour words 1..12 (index 0 unused) and minute-number words 1..29, accent-free to +# match the display's Windows-1252 rendering. Gender follows each language (es +# UNA/UNO for hour/minute, pt UMA/UM). +_HOURS = { + 'fr': ['', 'UNE', 'DEUX', 'TROIS', 'QUATRE', 'CINQ', 'SIX', 'SEPT', 'HUIT', 'NEUF', 'DIX', 'ONZE', 'DOUZE'], + 'de': ['', 'EINS', 'ZWEI', 'DREI', 'VIER', 'FUNF', 'SECHS', 'SIEBEN', 'ACHT', 'NEUN', 'ZEHN', 'ELF', 'ZWOLF'], + 'es': ['', 'UNA', 'DOS', 'TRES', 'CUATRO', 'CINCO', 'SEIS', 'SIETE', 'OCHO', 'NUEVE', 'DIEZ', 'ONCE', 'DOCE'], + 'it': ['', 'UNA', 'DUE', 'TRE', 'QUATTRO', 'CINQUE', 'SEI', 'SETTE', 'OTTO', 'NOVE', 'DIECI', 'UNDICI', 'DODICI'], + 'pt': ['', 'UMA', 'DUAS', 'TRES', 'QUATRO', 'CINCO', 'SEIS', 'SETE', 'OITO', 'NOVE', 'DEZ', 'ONZE', 'DOZE'], + 'nl': ['', 'EEN', 'TWEE', 'DRIE', 'VIER', 'VIJF', 'ZES', 'ZEVEN', 'ACHT', 'NEGEN', 'TIEN', 'ELF', 'TWAALF'], +} +_MINS = { + 'fr': ['', 'UNE', 'DEUX', 'TROIS', 'QUATRE', 'CINQ', 'SIX', 'SEPT', 'HUIT', 'NEUF', 'DIX', 'ONZE', 'DOUZE', + 'TREIZE', 'QUATORZE', 'QUINZE', 'SEIZE', 'DIX-SEPT', 'DIX-HUIT', 'DIX-NEUF', 'VINGT', 'VINGT ET UNE', + 'VINGT-DEUX', 'VINGT-TROIS', 'VINGT-QUATRE', 'VINGT-CINQ', 'VINGT-SIX', 'VINGT-SEPT', 'VINGT-HUIT', 'VINGT-NEUF'], + 'de': ['', 'EINS', 'ZWEI', 'DREI', 'VIER', 'FUNF', 'SECHS', 'SIEBEN', 'ACHT', 'NEUN', 'ZEHN', 'ELF', 'ZWOLF', + 'DREIZEHN', 'VIERZEHN', 'FUNFZEHN', 'SECHZEHN', 'SIEBZEHN', 'ACHTZEHN', 'NEUNZEHN', 'ZWANZIG', 'EINUNDZWANZIG', + 'ZWEIUNDZWANZIG', 'DREIUNDZWANZIG', 'VIERUNDZWANZIG', 'FUNFUNDZWANZIG', 'SECHSUNDZWANZIG', 'SIEBENUNDZWANZIG', + 'ACHTUNDZWANZIG', 'NEUNUNDZWANZIG'], + 'es': ['', 'UNO', 'DOS', 'TRES', 'CUATRO', 'CINCO', 'SEIS', 'SIETE', 'OCHO', 'NUEVE', 'DIEZ', 'ONCE', 'DOCE', + 'TRECE', 'CATORCE', 'QUINCE', 'DIECISEIS', 'DIECISIETE', 'DIECIOCHO', 'DIECINUEVE', 'VEINTE', 'VEINTIUNO', + 'VEINTIDOS', 'VEINTITRES', 'VEINTICUATRO', 'VEINTICINCO', 'VEINTISEIS', 'VEINTISIETE', 'VEINTIOCHO', 'VEINTINUEVE'], + 'it': ['', 'UNO', 'DUE', 'TRE', 'QUATTRO', 'CINQUE', 'SEI', 'SETTE', 'OTTO', 'NOVE', 'DIECI', 'UNDICI', 'DODICI', + 'TREDICI', 'QUATTORDICI', 'QUINDICI', 'SEDICI', 'DICIASSETTE', 'DICIOTTO', 'DICIANNOVE', 'VENTI', 'VENTUNO', + 'VENTIDUE', 'VENTITRE', 'VENTIQUATTRO', 'VENTICINQUE', 'VENTISEI', 'VENTISETTE', 'VENTOTTO', 'VENTINOVE'], + 'pt': ['', 'UM', 'DOIS', 'TRES', 'QUATRO', 'CINCO', 'SEIS', 'SETE', 'OITO', 'NOVE', 'DEZ', 'ONZE', 'DOZE', + 'TREZE', 'CATORZE', 'QUINZE', 'DEZESSEIS', 'DEZESSETE', 'DEZOITO', 'DEZENOVE', 'VINTE', 'VINTE E UM', + 'VINTE E DOIS', 'VINTE E TRES', 'VINTE E QUATRO', 'VINTE E CINCO', 'VINTE E SEIS', 'VINTE E SETE', + 'VINTE E OITO', 'VINTE E NOVE'], + 'nl': ['', 'EEN', 'TWEE', 'DRIE', 'VIER', 'VIJF', 'ZES', 'ZEVEN', 'ACHT', 'NEGEN', 'TIEN', 'ELF', 'TWAALF', + 'DERTIEN', 'VEERTIEN', 'VIJFTIEN', 'ZESTIEN', 'ZEVENTIEN', 'ACHTTIEN', 'NEGENTIEN', 'TWINTIG', 'EENENTWINTIG', + 'TWEEENTWINTIG', 'DRIEENTWINTIG', 'VIERENTWINTIG', 'VIJFENTWINTIG', 'ZESENTWINTIG', 'ZEVENENTWINTIG', + 'ACHTENTWINTIG', 'NEGENENTWINTIG'], +} + + +def _ctx(h): + """Displayed current hour (1..12) and next hour (1..12).""" h12 = h % 12 + disp = 12 if h12 == 0 else h12 + n12 = (h12 + 1) % 12 + dispn = 12 if n12 == 0 else n12 + return disp, dispn + + +def _b_fr(h, rounded): + if h == 0 and rounded == 0: + return ['IL', 'EST', 'MINUIT'] + if h == 12 and rounded == 0: + return ['IL', 'EST', 'MIDI'] + disp, dispn = _ctx(h) + H, M = _HOURS['fr'], _MINS['fr'] + heure = lambda d: 'HEURE' if d == 1 else 'HEURES' + if rounded == 0: + return ['IL', 'EST', H[disp], heure(disp)] + if rounded <= 30: + head = ['IL', 'EST', H[disp], heure(disp)] + if rounded == 15: + return head + ['ET', 'QUART'] + if rounded == 30: + return head + ['ET', 'DEMIE'] + return head + ['ET', M[rounded]] + mm = 60 - rounded + head = ['IL', 'EST', H[dispn], heure(dispn)] + if mm == 15: + return head + ['MOINS', 'LE', 'QUART'] + return head + ['MOINS', M[mm]] + + +def _b_de(h, rounded): + if h == 0 and rounded == 0: + return ['ES', 'IST', 'MITTERNACHT'] + if h == 12 and rounded == 0: + return ['ES', 'IST', 'MITTAG'] + disp, dispn = _ctx(h) + H, M = _HOURS['de'], _MINS['de'] + if rounded == 0: + return ['ES', 'IST', 'EIN' if disp == 1 else H[disp], 'UHR'] + if rounded <= 30: + if rounded == 15: + return ['ES', 'IST', 'VIERTEL', 'NACH', H[disp]] + if rounded == 30: + return ['ES', 'IST', 'HALB', H[dispn]] # halb points at the next hour + return ['ES', 'IST', M[rounded], 'NACH', H[disp]] + mm = 60 - rounded + if mm == 15: + return ['ES', 'IST', 'VIERTEL', 'VOR', H[dispn]] + return ['ES', 'IST', M[mm], 'VOR', H[dispn]] + + +def _b_es(h, rounded): + if h == 0 and rounded == 0: + return ['ES', 'MEDIANOCHE'] + if h == 12 and rounded == 0: + return ['ES', 'MEDIODIA'] + disp, dispn = _ctx(h) + H, M = _HOURS['es'], _MINS['es'] + pre = lambda d: ['ES', 'LA'] if d == 1 else ['SON', 'LAS'] + if rounded == 0: + return pre(disp) + [H[disp]] + if rounded <= 30: + head = pre(disp) + [H[disp]] + if rounded == 15: + return head + ['Y', 'CUARTO'] + if rounded == 30: + return head + ['Y', 'MEDIA'] + return head + ['Y', M[rounded]] + mm = 60 - rounded + head = pre(dispn) + [H[dispn]] + if mm == 15: + return head + ['MENOS', 'CUARTO'] + return head + ['MENOS', M[mm]] + + +def _b_it(h, rounded): + if h == 0 and rounded == 0: + return ["E'", 'MEZZANOTTE'] + if h == 12 and rounded == 0: + return ["E'", 'MEZZOGIORNO'] + disp, dispn = _ctx(h) + H, M = _HOURS['it'], _MINS['it'] + named = lambda d: ["E'", "L'UNA"] if d == 1 else ['SONO', 'LE', H[d]] + if rounded == 0: + return named(disp) + if rounded <= 30: + head = named(disp) + if rounded == 15: + return head + ['E', 'UN', 'QUARTO'] + if rounded == 30: + return head + ['E', 'MEZZA'] + return head + ['E', M[rounded]] + mm = 60 - rounded + head = named(dispn) + if mm == 15: + return head + ['MENO', 'UN', 'QUARTO'] + return head + ['MENO', M[mm]] + + +def _b_pt(h, rounded): + if h == 0 and rounded == 0: + return ["E'", 'MEIA-NOITE'] + if h == 12 and rounded == 0: + return ["E'", 'MEIO-DIA'] + disp, dispn = _ctx(h) + H, M = _HOURS['pt'], _MINS['pt'] + if rounded == 0: + return ["E'", 'UMA', 'HORA'] if disp == 1 else ['SAO', H[disp], 'HORAS'] + if rounded <= 30: + head = ["E'", 'UMA'] if disp == 1 else ['SAO', H[disp]] + if rounded == 15: + return head + ['E', 'QUINZE'] + if rounded == 30: + return head + ['E', 'MEIA'] + return head + ['E', M[rounded]] + mm = 60 - rounded + tail = 'QUINZE' if mm == 15 else M[mm] + if dispn == 1: + return [tail, 'PARA', 'A', 'UMA'] + return [tail, 'PARA', 'AS', H[dispn]] - interval = int(settings.get('interval', '5')) +def _b_nl(h, rounded): + if h == 0 and rounded == 0: + return ['HET', 'IS', 'MIDDERNACHT'] + if h == 12 and rounded == 0: + return ['HET', 'IS', 'MIDDAG'] + disp, dispn = _ctx(h) + H, M = _HOURS['nl'], _MINS['nl'] + if rounded == 0: + return ['HET', 'IS', H[disp], 'UUR'] + if rounded <= 30: + if rounded == 15: + return ['HET', 'IS', 'KWART', 'OVER', H[disp]] + if rounded == 30: + return ['HET', 'IS', 'HALF', H[dispn]] # half points at the next hour + return ['HET', 'IS', M[rounded], 'OVER', H[disp]] + mm = 60 - rounded + if mm == 15: + return ['HET', 'IS', 'KWART', 'VOOR', H[dispn]] + return ['HET', 'IS', M[mm], 'VOOR', H[dispn]] + + +_BUILDERS = {'fr': _b_fr, 'de': _b_de, 'es': _b_es, 'it': _b_it, 'pt': _b_pt, 'nl': _b_nl} + + +def _wrap(words, cols): + """Greedily pack words into lines no wider than the display.""" + lines, cur = [], '' + for w in words: + cand = w if not cur else f'{cur} {w}' + if len(cand) <= cols or not cur: + cur = cand + else: + lines.append(cur) + cur = w + if cur: + lines.append(cur) + return lines + + +def _fit(words, cols, rows): + lines = _wrap(words, cols) + if rows and rows >= 1 and len(lines) > rows: # collapse overflow into the last visible row + lines = lines[:rows - 1] + [' '.join(lines[rows - 1:])] + return lines + + +def _english(h, rounded, cols): + h12 = h % 12 hours = ['TWELVE', 'ONE', 'TWO', 'THREE', 'FOUR', 'FIVE', 'SIX', 'SEVEN', 'EIGHT', 'NINE', 'TEN', 'ELEVEN'] - - ones = ['', 'ONE', 'TWO', 'THREE', 'FOUR', 'FIVE', - 'SIX', 'SEVEN', 'EIGHT', 'NINE', 'TEN', - 'ELEVEN', 'TWELVE', 'THIRTEEN', 'FOURTEEN', 'FIFTEEN', - 'SIXTEEN', 'SEVENTEEN', 'EIGHTEEN', 'NINETEEN'] + ones = ['', 'ONE', 'TWO', 'THREE', 'FOUR', 'FIVE', 'SIX', 'SEVEN', 'EIGHT', 'NINE', 'TEN', + 'ELEVEN', 'TWELVE', 'THIRTEEN', 'FOURTEEN', 'FIFTEEN', 'SIXTEEN', 'SEVENTEEN', 'EIGHTEEN', 'NINETEEN'] tens = ['', '', 'TWENTY', 'THIRTY', 'FORTY', 'FIFTY'] def minute_word(n): @@ -30,37 +235,52 @@ def minute_word(n): t, o = divmod(n, 10) return (tens[t] + ' ' + ones[o]).strip() if o else tens[t] - # Round to interval - rounded = round(m / interval) * interval - if rounded == 60: - rounded = 0 - h = (h + 1) % 24 - h12 = h % 12 - hour_word = hours[h12] next_word = hours[(h12 + 1) % 12] - cols = get_cols() - # Special cases for noon and midnight if h == 0 and rounded == 0: - return [format_lines("IT'S", 'MIDNIGHT', '')] + return ["IT'S", 'MIDNIGHT', ''] if h == 12 and rounded == 0: - return [format_lines("IT'S", 'NOON', '')] - + return ["IT'S", 'NOON', ''] if rounded == 0: - return [format_lines("IT'S", hour_word, "O'CLOCK")] - elif rounded <= 30: - mw = minute_word(rounded) - direction = 'PAST' - target = hour_word + return ["IT'S", hour_word, "O'CLOCK"] + if rounded <= 30: + mw, direction, target = minute_word(rounded), 'PAST', hour_word else: - mw = minute_word(60 - rounded) - direction = 'TO' - target = next_word + mw, direction, target = minute_word(60 - rounded), 'TO', next_word - # Fit into 3 lines: try "MW PAST" on line 2, else split combined = mw + ' ' + direction if len(combined) <= cols: - return [format_lines("IT'S", combined, target)] + return ["IT'S", combined, target] + return ["IT'S", mw, direction + ' ' + target] + + +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): + from datetime import datetime + import pytz + try: + tz = pytz.timezone(settings.get('timezone', 'US/Eastern')) + except pytz.UnknownTimeZoneError: + tz = pytz.timezone('US/Eastern') + now = datetime.now(tz) + h, m = now.hour, now.minute + + try: + interval = int(settings.get('interval', '5')) + except (TypeError, ValueError): + interval = 5 + interval = max(1, min(30, interval)) + + rounded = round(m / interval) * interval + if rounded == 60: + rounded = 0 + h = (h + 1) % 24 + + cols, rows = get_cols(), get_rows() + lang = (i18n.lang if i18n is not None else 'en')[:2].lower() + + if lang in _BUILDERS: + lines = _fit(_BUILDERS[lang](h, rounded), cols, rows) else: - return [format_lines("IT'S", mw, direction + ' ' + target)] + lines = _english(h, rounded, cols) + return [format_lines(*lines)] diff --git a/apps/word-clock/manifest.json b/apps/word-clock/manifest.json index 0a915c5..769f69b 100644 --- a/apps/word-clock/manifest.json +++ b/apps/word-clock/manifest.json @@ -1,5 +1,6 @@ { "id": "word-clock", + "i18n": true, "name": "Word Clock", "icon": "🔤", "description": "Time as written phrase", diff --git a/apps/word-of-the-day/app.py b/apps/word-of-the-day/app.py index 28f3e47..cf9bc47 100644 --- a/apps/word-of-the-day/app.py +++ b/apps/word-of-the-day/app.py @@ -1,30 +1,75 @@ -"""Word of the Day plugin for Split-Flap Display.""" +"""Word of the Day — one characterful word, chosen by the date (no definition). -def fetch(settings, format_lines, get_rows, get_cols): - import urllib.request - import json - try: - url = "https://api.dictionaryapi.dev/api/v2/entries/en/random" - req = urllib.request.Request(url, headers={"User-Agent": "SplitFlap/1.0"}) - with urllib.request.urlopen(req, timeout=8) as resp: - data = json.loads(resp.read().decode()) - word = data[0]["word"].upper() - meaning = data[0]["meanings"][0]["definitions"][0]["definition"].upper() - return [ - format_lines("WORD OF THE DAY", word, ""), - format_lines(meaning[:get_cols()], meaning[get_cols():get_cols()*2], ""), - ] - except Exception: - import random - words = [ - ("EPHEMERAL", "LASTING A", "VERY SHORT TIME"), - ("UBIQUITOUS", "PRESENT OR", "FOUND EVERYWHERE"), - ("SERENDIPITY", "HAPPY ACCIDENT", "OR DISCOVERY"), - ("ELOQUENT", "FLUENT OR", "PERSUASIVE"), - ("RESILIENT", "ABLE TO RECOVER", "QUICKLY"), - ("PRAGMATIC", "DEALING WITH", "THINGS SENSIBLY"), - ("CANDOR", "THE QUALITY OF", "BEING OPEN"), - ("TENACIOUS", "HOLDING FIRMLY", "TO SOMETHING"), - ] - word, l2, l3 = random.choice(words) - return [format_lines("WORD OF THE DAY", word, ""), format_lines(l2, l3, "")] +Localized: when the companion injects i18n, the word is drawn from a curated list in +the current Language (falling back to English), and the header is translated. The +word is picked by the calendar day, so it's stable for the day and cycles over time. +""" + +# Accent-free (Windows-1252-safe) uppercase words, one evocative list per language. +WORDS_BY_LANG = { + "en": [ + "EPHEMERAL", "UBIQUITOUS", "SERENDIPITY", "ELOQUENT", "RESILIENT", + "PRAGMATIC", "CANDOR", "TENACIOUS", "MELLIFLUOUS", "LUMINOUS", + "QUINTESSENTIAL", "EFFERVESCENT", "PERSPICACIOUS", "SUCCINCT", "GREGARIOUS", + "INEFFABLE", "LABYRINTHINE", "MAGNANIMOUS", "NEBULOUS", "OBSTINATE", + "PANACEA", "QUERULOUS", "RESPLENDENT", "SANGUINE", "TACITURN", + "UMBRAGE", "VORACIOUS", "WISTFUL", "ZEALOUS", "AMBIVALENT", + "BENEVOLENT", "CACOPHONY", "DILIGENT", "ENIGMATIC", "FASTIDIOUS", + "GARRULOUS", "HALCYON", "IDIOSYNCRASY", "JUXTAPOSE", "LETHARGIC", + "MERCURIAL", "NONCHALANT", "OSTENTATIOUS", "PENCHANT", "QUIXOTIC", + "RECALCITRANT", "TRANQUIL", "VENERABLE", "WHIMSICAL", "ZENITH", + ], + "fr": [ + "FLANERIE", "DEPAYSEMENT", "RETROUVAILLES", "CREPUSCULE", "EBLOUISSANT", + "CHATOYANT", "INSOUCIANT", "MELANCOLIE", "QUIETUDE", "SAGACITE", + "PERSPICACE", "OPINIATRE", "LOQUACE", "VOLUBILE", "EPHEMERE", + "LUMINEUX", "RESILIENT", "TENACE", "SERENITE", "VENERABLE", + "NONCHALANT", "ENIGMATIQUE", "EXQUIS", "IMPROMPTU", + ], + "de": [ + "FERNWEH", "WALDEINSAMKEIT", "ZEITGEIST", "GEBORGENHEIT", "SEHNSUCHT", + "VERGAENGLICH", "LEUCHTEND", "BEHARRLICH", "SCHARFSINNIG", "WORTKARG", + "UEBERSCHWANG", "GEMUETLICH", "WEHMUT", "GELASSENHEIT", "EIGENSINNIG", + "BESONNEN", "VERWEGEN", "ANMUTIG", "UNERGRUENDLICH", "AUGENBLICK", + "DAEMMERUNG", "SCHWELGEN", "VERSCHMITZT", "EHRFURCHT", + ], + "es": [ + "EFIMERO", "INEFABLE", "SERENDIPIA", "ELOCUENTE", "RESILIENTE", + "LUMINOSO", "TENAZ", "PERSPICAZ", "LOCUAZ", "OBSTINADO", + "PANACEA", "RESPLANDECIENTE", "TRANQUILO", "VENERABLE", "SAGAZ", + "NOSTALGIA", "CREPUSCULO", "DESLUMBRANTE", "MELANCOLIA", "QUIETUD", + "SOSIEGO", "ENIGMATICO", "EXQUISITO", "IMPETU", + ], + "it": [ + "EFFIMERO", "INEFFABILE", "SERENDIPITA", "ELOQUENTE", "RESILIENTE", + "LUMINOSO", "TENACE", "PERSPICACE", "LOQUACE", "OSTINATO", + "PANACEA", "SPLENDENTE", "TRANQUILLO", "VENERABILE", "SAGACE", + "NOSTALGIA", "CREPUSCOLO", "ABBAGLIANTE", "MALINCONIA", "QUIETE", + "SERENITA", "ENIGMATICO", "SQUISITO", "IMPETO", + ], + "pt": [ + "EFEMERO", "INEFAVEL", "SERENDIPIA", "ELOQUENTE", "RESILIENTE", + "LUMINOSO", "TENAZ", "PERSPICAZ", "LOQUAZ", "OBSTINADO", + "PANACEIA", "RESPLANDECENTE", "TRANQUILO", "VENERAVEL", "SAGAZ", + "SAUDADE", "CREPUSCULO", "DESLUMBRANTE", "MELANCOLIA", "QUIETUDE", + "SOSSEGO", "ENIGMATICO", "REQUINTADO", "IMPETO", + ], + "nl": [ + "VLUCHTIG", "ONUITSPREKELIJK", "TIJDGEEST", "VEERKRACHTIG", "LICHTEND", + "VASTBERADEN", "SCHERPZINNIG", "WOORDKARIG", "KOPPIG", "WONDERMIDDEL", + "STRALEND", "SEREEN", "EERBIEDWAARDIG", "WIJSHEID", "WEEMOED", + "SCHEMERING", "OOGVERBLINDEND", "MELANCHOLIE", "GEZELLIG", "RAADSELACHTIG", + "VERRUKKELIJK", "ONSTUIMIG", "VOORBIJGAAND", "BEDACHTZAAM", + ], +} + + +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): + from datetime import date + lang = i18n.lang_base if i18n is not None else "en" + words = WORDS_BY_LANG.get(lang) or WORDS_BY_LANG["en"] + word = words[date.today().toordinal() % len(words)] + header = i18n.t("WORD OF THE DAY", "vocab") if i18n is not None else "WORD OF THE DAY" + if get_rows() == 1: + return [format_lines(word)] + return [format_lines(header, word)] diff --git a/apps/word-of-the-day/manifest.json b/apps/word-of-the-day/manifest.json index 41cbbf6..884d705 100644 --- a/apps/word-of-the-day/manifest.json +++ b/apps/word-of-the-day/manifest.json @@ -1,5 +1,6 @@ { "id": "word-of-the-day", + "i18n": true, "name": "Word of the Day", "icon": "📖", "description": "Expand your vocabulary daily", @@ -8,18 +9,7 @@ "version": "1.0", "refresh_interval": 3600, "loop_delay": 10, - "settings": [ - { - "key": "loop_delay", - "label": "Delay Between Pages (seconds)", - "type": "number", - "default": "10", - "min": "2", - "max": "30", - "step": "1", - "stepper": true - } - ], - "min_rows": 3, - "min_cols": 10 -} \ No newline at end of file + "settings": [], + "min_rows": 2, + "min_cols": 15 +} diff --git a/apps/world_clock/app.py b/apps/world_clock/app.py index 8fb9128..e5034c4 100644 --- a/apps/world_clock/app.py +++ b/apps/world_clock/app.py @@ -1,14 +1,27 @@ -def fetch(settings, format_lines, get_rows, get_cols): +def fetch(settings, format_lines, get_rows, get_cols, i18n=None): from datetime import datetime import pytz - zones = [s.strip() for s in settings.get('world_clock_zones', 'US/Eastern,US/Pacific,Europe/London').split(',')] + cols = get_cols() + zones = [s.strip() for s in settings.get('world_clock_zones', 'US/Eastern,US/Pacific,Europe/London').split(',') if s.strip()] lines = [] for z in zones[:get_rows()]: - tz = pytz.timezone(z) + try: + tz = pytz.timezone(z) + except pytz.UnknownTimeZoneError: + continue now = datetime.now(tz) + # Compact time leaves room for the city; a long city is trimmed so the time + # is never cut off. AM/PM is an English convention — everyone else is 24h. + if i18n is not None: + t = i18n.time(now, ampm_space=False) + else: + t = now.strftime('%I:%M%p').lstrip('0') city = z.split('/')[-1].replace('_', ' ').upper() - lines.append(f'{city} {now.strftime("%I:%M %p")}') - lines += [''] * (get_rows() - len(lines)) + avail = max(1, cols - len(t) - 1) + lines.append(f'{city[:avail]} {t}') + if not lines: + lines = ['NO VALID', 'TIMEZONES'] + lines += [''] * max(0, get_rows() - len(lines)) return [format_lines(*lines)] diff --git a/apps/world_clock/manifest.json b/apps/world_clock/manifest.json index 3f762f8..84093bb 100644 --- a/apps/world_clock/manifest.json +++ b/apps/world_clock/manifest.json @@ -1,5 +1,6 @@ { "id": "world_clock", + "i18n": true, "name": "World Clock", "icon": "🌍", "description": "3 time zones",