Merge pull request #4854 from pangeachat/4853-play-test-121525
4853 play test 121525
This commit is contained in:
commit
a3e70d623d
62 changed files with 837 additions and 224 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"@@locale": "ar",
|
||||
"@@last_modified": "2025-12-15 13:10:00.150906",
|
||||
"@@last_modified": "2025-12-15 14:43:38.968604",
|
||||
"about": "حول",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -10918,5 +10918,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "لقد قمت بتعيين الرموز التعبيرية لـ {lemma}! سنستخدم هذا الرمز التعبيري لتمثيل الكلمة في أنشطة الممارسة في المستقبل.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1915,7 +1915,7 @@
|
|||
"playWithAI": "Пакуль гуляйце з ШІ",
|
||||
"courseStartDesc": "Pangea Bot гатовы да працы ў любы час!\n\n...але навучанне лепш з сябрамі!",
|
||||
"@@locale": "be",
|
||||
"@@last_modified": "2025-12-15 13:09:51.707905",
|
||||
"@@last_modified": "2025-12-15 14:43:29.836415",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -11800,5 +11800,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Вы ўсталявалі эмодзі для {lemma}! Мы будзем выкарыстоўваць гэтае эмодзі для прадстаўлення слова ў практычных дзейнасцях у будучыні.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"@@last_modified": "2025-12-15 13:10:11.203206",
|
||||
"@@last_modified": "2025-12-15 14:43:52.981237",
|
||||
"about": "সম্পর্কে",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -11805,5 +11805,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "আপনি {lemma} এর জন্য ইমোজি সেট করেছেন! আমরা ভবিষ্যতে অনুশীলন কার্যক্রমে এই ইমোজিটি শব্দটি উপস্থাপন করতে ব্যবহার করব।",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4282,7 +4282,7 @@
|
|||
"joinPublicTrip": "མི་ཚེས་ལ་ལོག་འབད།",
|
||||
"startOwnTrip": "ངེད་རང་གི་ལོག་ལ་སྦྱོར་བཅོས།",
|
||||
"@@locale": "bo",
|
||||
"@@last_modified": "2025-12-15 13:10:08.949661",
|
||||
"@@last_modified": "2025-12-15 14:43:49.837126",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -10455,5 +10455,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "A’n setti emoji për {lemma}! Ne do ta përdorim këtë emoji për të përfaqësuar fjalën në aktivitetet praktike në vazhdim.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"@@last_modified": "2025-12-15 13:09:52.878217",
|
||||
"@@last_modified": "2025-12-15 14:43:31.134664",
|
||||
"about": "Quant a",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -10725,5 +10725,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Has establert l'emoji per {lemma}! Utilitzarem aquesta emoji per representar la paraula en les activitats pràctiques d'ara endavant.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"@@locale": "cs",
|
||||
"@@last_modified": "2025-12-15 13:09:49.179864",
|
||||
"@@last_modified": "2025-12-15 14:43:26.456306",
|
||||
"about": "O aplikaci",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -11308,5 +11308,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Nastavili jste emoji pro {lemma}! Toto emoji použijeme k reprezentaci slova v praktických aktivitách v budoucnu.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1934,7 +1934,7 @@
|
|||
"playWithAI": "Leg med AI for nu",
|
||||
"courseStartDesc": "Pangea Bot er klar til at starte når som helst!\n\n...men læring er bedre med venner!",
|
||||
"@@locale": "da",
|
||||
"@@last_modified": "2025-12-15 13:09:27.476427",
|
||||
"@@last_modified": "2025-12-15 14:42:53.307740",
|
||||
"@aboutHomeserver": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
|
|
@ -11762,5 +11762,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Du har indstillet emoji'en for {lemma}! Vi vil bruge denne emoji til at repræsentere ordet i praksisaktiviteter fremover.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"@@locale": "de",
|
||||
"@@last_modified": "2025-12-15 13:09:43.173559",
|
||||
"@@last_modified": "2025-12-15 14:43:18.204232",
|
||||
"alwaysUse24HourFormat": "true",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"description": "Set to true to always display time of day in 24 hour format."
|
||||
|
|
@ -10708,5 +10708,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Sie haben das Emoji für {lemma} festgelegt! Wir werden dieses Emoji verwenden, um das Wort in zukünftigen Übungsaktivitäten darzustellen.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4460,7 +4460,7 @@
|
|||
"playWithAI": "Παίξτε με την Τεχνητή Νοημοσύνη προς το παρόν",
|
||||
"courseStartDesc": "Ο Pangea Bot είναι έτοιμος να ξεκινήσει οποιαδήποτε στιγμή!\n\n...αλλά η μάθηση είναι καλύτερη με φίλους!",
|
||||
"@@locale": "el",
|
||||
"@@last_modified": "2025-12-15 13:10:16.812716",
|
||||
"@@last_modified": "2025-12-15 14:43:58.410031",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -11759,5 +11759,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Έχετε ορίσει το emoji για {lemma}! Θα χρησιμοποιήσουμε αυτό το emoji για να εκπροσωπήσουμε τη λέξη σε πρακτικές δραστηριότητες στο εξής.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5002,5 +5002,14 @@
|
|||
"inOngoingActivity": "You have an ongoing activity!",
|
||||
"vocabEmoji": "Vocab emoji",
|
||||
"requestRegeneration": "Request regeneration",
|
||||
"optionalRegenerateReason": "(Optional) Reason"
|
||||
"optionalRegenerateReason": "(Optional) Reason",
|
||||
"emojiSelectedSnackbar": "You’ve set the emoji for {lemma}! We’ll use this emoji to represent the word in practice activities going forward.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"@@last_modified": "2025-12-15 13:10:20.520198",
|
||||
"@@last_modified": "2025-12-15 14:44:02.283480",
|
||||
"about": "Prio",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -11790,5 +11790,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Vi havas metitan la emoji por {lemma}! Ni uzos ĉi tiun emoji por reprezenti la vorton en praktikaj aktivadoj en la estonteco.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"@@locale": "es",
|
||||
"@@last_modified": "2025-12-15 13:09:22.556485",
|
||||
"@@last_modified": "2025-12-15 14:42:48.159542",
|
||||
"about": "Acerca de",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -7935,5 +7935,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "¡Has establecido el emoji para {lemma}! Usaremos este emoji para representar la palabra en las actividades prácticas de ahora en adelante.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"@@locale": "et",
|
||||
"@@last_modified": "2025-12-15 13:09:41.916204",
|
||||
"@@last_modified": "2025-12-15 14:43:16.857704",
|
||||
"about": "Rakenduse teave",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -10972,5 +10972,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Oled seadnud emotikoni {lemma} jaoks! Kasutame seda emotikoni sõna esindamiseks praktika tegevustes edaspidi.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"@@locale": "eu",
|
||||
"@@last_modified": "2025-12-15 13:09:39.405643",
|
||||
"@@last_modified": "2025-12-15 14:43:14.531001",
|
||||
"about": "Honi buruz",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -10701,5 +10701,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "{lemma} hitzaren emoji-a ezarri duzu! Etorkizuneko praktiketan hitz hori irudikatzeko emoji hau erabiliko dugu.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"@@last_modified": "2025-12-15 13:10:12.289964",
|
||||
"@@last_modified": "2025-12-15 14:43:54.300365",
|
||||
"repeatPassword": "تکرار رمزعبور",
|
||||
"@repeatPassword": {},
|
||||
"about": "درباره",
|
||||
|
|
@ -11433,5 +11433,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "شما ایموجی را برای {lemma} تنظیم کردهاید! ما از این ایموجی برای نمایش کلمه در فعالیتهای عملی در آینده استفاده خواهیم کرد.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4013,7 +4013,7 @@
|
|||
"playWithAI": "Leiki tekoälyn kanssa nyt",
|
||||
"courseStartDesc": "Pangea Bot on valmis milloin tahansa!\n\n...mutta oppiminen on parempaa ystävien kanssa!",
|
||||
"@@locale": "fi",
|
||||
"@@last_modified": "2025-12-15 13:09:25.461324",
|
||||
"@@last_modified": "2025-12-15 14:42:51.625904",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -11324,5 +11324,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Olet asettanut emojin {lemma} varten! Käytämme tätä emojia sanan edustamiseen käytännön aktiviteeteissa tulevaisuudessa.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2791,7 +2791,7 @@
|
|||
"selectAll": "Piliin lahat",
|
||||
"deselectAll": "Huwag piliin lahat",
|
||||
"@@locale": "fil",
|
||||
"@@last_modified": "2025-12-15 13:09:57.098421",
|
||||
"@@last_modified": "2025-12-15 14:43:36.288461",
|
||||
"@setCustomPermissionLevel": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -11677,5 +11677,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Naitakda mo na ang emoji para sa {lemma}! Gagamitin namin ang emoji na ito upang kumatawan sa salita sa mga aktibidad sa pagsasanay mula ngayon.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"@@locale": "fr",
|
||||
"@@last_modified": "2025-12-15 13:10:26.870783",
|
||||
"@@last_modified": "2025-12-15 14:44:08.960073",
|
||||
"about": "À propos",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -11025,5 +11025,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Vous avez défini l'emoji pour {lemma} ! Nous utiliserons cet emoji pour représenter le mot dans les activités pratiques à l'avenir.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4521,7 +4521,7 @@
|
|||
"playWithAI": "Imir le AI faoi láthair",
|
||||
"courseStartDesc": "Tá Bot Pangea réidh chun dul am ar bith!\n\n...ach is fearr foghlaim le cairde!",
|
||||
"@@locale": "ga",
|
||||
"@@last_modified": "2025-12-15 13:10:25.578909",
|
||||
"@@last_modified": "2025-12-15 14:44:07.686971",
|
||||
"@customReaction": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -10699,5 +10699,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Tá an emoji socraithe agat do {lemma}! Úsáidfimid an emoji seo chun an focal a chur in iúl i ngníomhaíochtaí cleachtais amach anseo.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"@@locale": "gl",
|
||||
"@@last_modified": "2025-12-15 13:09:24.082576",
|
||||
"@@last_modified": "2025-12-15 14:42:50.213816",
|
||||
"about": "Acerca de",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -10698,5 +10698,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Estableciches o emoji para {lemma}! Usaremos este emoji para representar a palabra nas actividades prácticas a partir de agora.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"@@last_modified": "2025-12-15 13:09:36.374126",
|
||||
"@@last_modified": "2025-12-15 14:43:10.892818",
|
||||
"about": "אודות",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -11750,5 +11750,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "הגדרת את האימוג'י עבור {lemma}! נשתמש באימוג'י הזה כדי לייצג את המילה בפעילויות תרגול מעתה ואילך.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4487,7 +4487,7 @@
|
|||
"playWithAI": "अभी के लिए एआई के साथ खेलें",
|
||||
"courseStartDesc": "पैंजिया बॉट कभी भी जाने के लिए तैयार है!\n\n...लेकिन दोस्तों के साथ सीखना बेहतर है!",
|
||||
"@@locale": "hi",
|
||||
"@@last_modified": "2025-12-15 13:10:19.180543",
|
||||
"@@last_modified": "2025-12-15 14:44:00.905904",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -11786,5 +11786,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "आपने {lemma} के लिए इमोजी सेट किया है! हम आगे के अभ्यास गतिविधियों में इस इमोजी का उपयोग करेंगे।",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"@@locale": "hr",
|
||||
"@@last_modified": "2025-12-15 13:09:35.275706",
|
||||
"@@last_modified": "2025-12-15 14:43:09.046460",
|
||||
"about": "Informacije",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -11073,5 +11073,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Postavili ste emoji za {lemma}! Ovaj emoji ćemo koristiti za predstavljanje riječi u praktičnim aktivnostima ubuduće.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"@@locale": "hu",
|
||||
"@@last_modified": "2025-12-15 13:09:28.639253",
|
||||
"@@last_modified": "2025-12-15 14:42:55.436150",
|
||||
"about": "Névjegy",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -10702,5 +10702,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Beállítottad a {lemma} emoji-t! Ezt az emojit fogjuk használni a szó képviseletére a gyakorlati tevékenységek során a jövőben.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1962,7 +1962,7 @@
|
|||
"playWithAI": "Joca con le IA pro ora",
|
||||
"courseStartDesc": "Pangea Bot es preste a comenzar a qualunque momento!\n\n...ma apprender es melior con amicos!",
|
||||
"@@locale": "ia",
|
||||
"@@last_modified": "2025-12-15 13:09:37.510659",
|
||||
"@@last_modified": "2025-12-15 14:43:12.499796",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -11779,5 +11779,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Ați setat emoji-ul pentru {lemma}! Vom folosi acest emoji pentru a reprezenta cuvântul în activitățile practice de acum înainte.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"@@last_modified": "2025-12-15 13:09:30.033862",
|
||||
"@@last_modified": "2025-12-15 14:42:56.674775",
|
||||
"setAsCanonicalAlias": "Atur sebagai alias utama",
|
||||
"@setAsCanonicalAlias": {
|
||||
"type": "String",
|
||||
|
|
@ -10692,5 +10692,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Anda telah menetapkan emoji untuk {lemma}! Kami akan menggunakan emoji ini untuk mewakili kata dalam aktivitas praktik ke depan.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4376,7 +4376,7 @@
|
|||
"playWithAI": "Joca con AI pro ora",
|
||||
"courseStartDesc": "Pangea Bot es preste a partir a qualunque momento!\n\n...ma apprender es melior con amicos!",
|
||||
"@@locale": "ie",
|
||||
"@@last_modified": "2025-12-15 13:09:34.373708",
|
||||
"@@last_modified": "2025-12-15 14:43:07.923977",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -11675,5 +11675,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Tú setaste an emoji pa {lemma}! Usaremos este emoji pa representar la palabra en les actividaes de práctica d'ora en adelante.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"@@last_modified": "2025-12-15 13:09:46.808373",
|
||||
"@@last_modified": "2025-12-15 14:43:23.466924",
|
||||
"about": "Informazioni",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -10704,5 +10704,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Hai impostato l'emoji per {lemma}! Useremo questa emoji per rappresentare la parola nelle attività pratiche in futuro.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"@@locale": "ja",
|
||||
"@@last_modified": "2025-12-15 13:10:17.963932",
|
||||
"@@last_modified": "2025-12-15 14:43:59.878807",
|
||||
"about": "このアプリについて",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -11491,5 +11491,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "{lemma}の絵文字が設定されました!今後の練習活動ではこの絵文字を使ってその単語を表現します。",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2598,7 +2598,7 @@
|
|||
"playWithAI": "ამ დროისთვის ითამაშეთ AI-თან",
|
||||
"courseStartDesc": "Pangea Bot მზადაა ნებისმიერ დროს გასასვლელად!\n\n...მაგრამ სწავლა უკეთესია მეგობრებთან ერთად!",
|
||||
"@@locale": "ka",
|
||||
"@@last_modified": "2025-12-15 13:10:22.979686",
|
||||
"@@last_modified": "2025-12-15 14:44:05.262676",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -11731,5 +11731,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "თქვენ დააყენეთ ემოჯი {lemma}-ისთვის! ჩვენ ამ ემოჯის გამოყენებას ვაპირებთ სიტყვების პრაქტიკული აქტივობების წარმოსადგენად.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"@@last_modified": "2025-12-15 13:09:21.214976",
|
||||
"@@last_modified": "2025-12-15 14:42:46.121270",
|
||||
"about": "소개",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -10809,5 +10809,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "{lemma}에 대한 이모지를 설정했습니다! 앞으로 연습 활동에서 이 이모지를 사용하여 단어를 나타낼 것입니다.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3865,7 +3865,7 @@
|
|||
"playWithAI": "Žaiskite su dirbtiniu intelektu dabar",
|
||||
"courseStartDesc": "Pangea botas pasiruošęs bet kada pradėti!\n\n...bet mokymasis yra geresnis su draugais!",
|
||||
"@@locale": "lt",
|
||||
"@@last_modified": "2025-12-15 13:10:04.199235",
|
||||
"@@last_modified": "2025-12-15 14:43:43.050040",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -11506,5 +11506,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Jūs nustatėte emociją {lemma}! Mes naudosime šią emociją, kad atstovautume žodžiui praktinėse veiklose ateityje.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4486,7 +4486,7 @@
|
|||
"playWithAI": "Tagad spēlējiet ar AI",
|
||||
"courseStartDesc": "Pangea bots ir gatavs jebkurā laikā!\n\n...bet mācīties ir labāk ar draugiem!",
|
||||
"@@locale": "lv",
|
||||
"@@last_modified": "2025-12-15 13:09:58.402497",
|
||||
"@@last_modified": "2025-12-15 14:43:37.722676",
|
||||
"analyticsInactiveTitle": "Pieprasījumi neaktīviem lietotājiem nevar tikt nosūtīti",
|
||||
"analyticsInactiveDesc": "Neaktīvi lietotāji, kuri nav pieteikušies kopš šīs funkcijas ieviešanas, neredzēs jūsu pieprasījumu.\n\nPieprasījuma poga parādīsies, kad viņi atgriezīsies. Jūs varat atkārtoti nosūtīt pieprasījumu vēlāk, noklikšķinot uz pieprasījuma pogas viņu vārdā, kad tā būs pieejama.",
|
||||
"accessRequestedTitle": "Pieprasījums piekļūt analītikai",
|
||||
|
|
@ -10687,5 +10687,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Jūs esat iestatījis emocijzīmi {lemma}! Mēs izmantosim šo emocijzīmi, lai pārstāvētu vārdu praktiskajās aktivitātēs turpmāk.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"@@last_modified": "2025-12-15 13:09:50.414856",
|
||||
"@@last_modified": "2025-12-15 14:43:27.926249",
|
||||
"about": "Om",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -11794,5 +11794,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Du har satt emoji for {lemma}! Vi vil bruke denne emojien for å representere ordet i praksisaktiviteter fremover.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"@@last_modified": "2025-12-15 13:10:08.027282",
|
||||
"@@last_modified": "2025-12-15 14:43:48.784024",
|
||||
"about": "Over ons",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -10701,5 +10701,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Je hebt de emoji voor {lemma} ingesteld! We zullen deze emoji gebruiken om het woord in de praktijkactiviteiten voortaan weer te geven.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"@@locale": "pl",
|
||||
"@@last_modified": "2025-12-15 13:10:13.564120",
|
||||
"@@last_modified": "2025-12-15 14:43:55.454694",
|
||||
"about": "O aplikacji",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -10699,5 +10699,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Ustawiłeś emoji dla {lemma}! Będziemy używać tego emoji do reprezentowania słowa w nadchodzących aktywnościach praktycznych.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"@@last_modified": "2025-12-15 13:09:40.663100",
|
||||
"@@last_modified": "2025-12-15 14:43:15.620362",
|
||||
"copiedToClipboard": "Copiada para a área de transferência",
|
||||
"@copiedToClipboard": {
|
||||
"type": "String",
|
||||
|
|
@ -11801,5 +11801,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Você definiu o emoji para {lemma}! Usaremos este emoji para representar a palavra nas atividades práticas daqui para frente.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"@@last_modified": "2025-12-15 13:09:38.322859",
|
||||
"@@last_modified": "2025-12-15 14:43:13.571791",
|
||||
"about": "Sobre",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -11059,5 +11059,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Você definiu o emoji para {lemma}! Usaremos este emoji para representar a palavra nas atividades práticas daqui para frente.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3335,7 +3335,7 @@
|
|||
"selectAll": "Selecionar tudo",
|
||||
"deselectAll": "Desmarcar tudo",
|
||||
"@@locale": "pt_PT",
|
||||
"@@last_modified": "2025-12-15 13:09:54.999629",
|
||||
"@@last_modified": "2025-12-15 14:43:33.549800",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -11730,5 +11730,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Você definiu o emoji para {lemma}! Usaremos este emoji para representar a palavra nas atividades práticas daqui para frente.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"@@last_modified": "2025-12-15 13:09:31.920399",
|
||||
"@@last_modified": "2025-12-15 14:42:58.811266",
|
||||
"about": "Despre",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -11436,5 +11436,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Ai setat emoji-ul pentru {lemma}! Vom folosi acest emoji pentru a reprezenta cuvântul în activitățile practice de acum înainte.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"@@locale": "ru",
|
||||
"@@last_modified": "2025-12-15 13:10:22.056511",
|
||||
"@@last_modified": "2025-12-15 14:44:03.535697",
|
||||
"about": "О проекте",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -10806,5 +10806,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Вы установили эмодзи для {lemma}! Мы будем использовать этот эмодзи для представления слова в практических заданиях в будущем.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"@@locale": "sk",
|
||||
"@@last_modified": "2025-12-15 13:09:33.330473",
|
||||
"@@last_modified": "2025-12-15 14:43:00.627770",
|
||||
"about": "O aplikácii",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -11785,5 +11785,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Nastavili ste emoji pre {lemma}! Tento emoji budeme používať na reprezentáciu slova v praktických aktivitách v budúcnosti.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2468,7 +2468,7 @@
|
|||
"playWithAI": "Za zdaj igrajte z AI-jem",
|
||||
"courseStartDesc": "Pangea Bot je pripravljen kadarkoli!\n\n...ampak je bolje učiti se s prijatelji!",
|
||||
"@@locale": "sl",
|
||||
"@@last_modified": "2025-12-15 13:09:44.475627",
|
||||
"@@last_modified": "2025-12-15 14:43:19.279445",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -11782,5 +11782,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Nastavili ste emoji za {lemma}! Ta emoji bomo uporabili za predstavitev besede v praktičnih dejavnostih v prihodnje.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"@@last_modified": "2025-12-15 13:10:24.291700",
|
||||
"@@last_modified": "2025-12-15 14:44:06.530821",
|
||||
"about": "О програму",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -11803,5 +11803,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Postavili ste emoji za {lemma}! Ovaj emoji ćemo koristiti da predstavimo reč u praktičnim aktivnostima ubuduće.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"@@last_modified": "2025-12-15 13:10:15.788663",
|
||||
"@@last_modified": "2025-12-15 14:43:56.890657",
|
||||
"about": "Om",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -11179,5 +11179,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Du har ställt in emojin för {lemma}! Vi kommer att använda denna emoji för att representera ordet i praktiska aktiviteter framöver.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"@@last_modified": "2025-12-15 13:10:06.701558",
|
||||
"@@last_modified": "2025-12-15 14:43:47.415907",
|
||||
"acceptedTheInvitation": "👍 {username} அழைப்பை ஏற்றுக்கொண்டது",
|
||||
"@acceptedTheInvitation": {
|
||||
"type": "String",
|
||||
|
|
@ -10925,5 +10925,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "{lemma} க்கான எமோஜியை நீங்கள் அமைத்துள்ளீர்கள்! எதிர்காலத்தில் பயிற்சி செயல்களில் அந்த சொல்லை பிரதிநிதித்துவப்படுத்த எமோஜியை நாங்கள் பயன்படுத்துவோம்.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1924,7 +1924,7 @@
|
|||
"playWithAI": "ఇప్పుడే AI తో ఆడండి",
|
||||
"courseStartDesc": "పాంజియా బాట్ ఎప్పుడైనా సిద్ధంగా ఉంటుంది!\n\n...కానీ స్నేహితులతో నేర్చుకోవడం మెరుగైనది!",
|
||||
"@@locale": "te",
|
||||
"@@last_modified": "2025-12-15 13:10:02.606443",
|
||||
"@@last_modified": "2025-12-15 14:43:41.716893",
|
||||
"@setCustomPermissionLevel": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -11790,5 +11790,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "{lemma} కోసం మీరు ఈ ఎమోజీని సెట్ చేసారు! మేము ఈ ఎమోజీని ప్రాక్టీస్ కార్యకలాపాలలో పదాన్ని ప్రాతినిధ్యం వహించడానికి ఉపయోగిస్తాము.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4460,7 +4460,7 @@
|
|||
"playWithAI": "เล่นกับ AI ชั่วคราว",
|
||||
"courseStartDesc": "Pangea Bot พร้อมที่จะเริ่มต้นได้ทุกเมื่อ!\n\n...แต่การเรียนรู้ดีกว่ากับเพื่อน!",
|
||||
"@@locale": "th",
|
||||
"@@last_modified": "2025-12-15 13:09:53.958347",
|
||||
"@@last_modified": "2025-12-15 14:43:32.503180",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -11759,5 +11759,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "คุณได้ตั้งค่าอีโมจิสำหรับ {lemma} แล้ว! เราจะใช้อีโมจินี้เพื่อแทนคำในกิจกรรมการฝึกฝนต่อไป",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"@@locale": "tr",
|
||||
"@@last_modified": "2025-12-15 13:10:01.271793",
|
||||
"@@last_modified": "2025-12-15 14:43:40.183100",
|
||||
"about": "Hakkında",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -10923,5 +10923,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "{lemma} için emojiyi ayarladınız! Bu emojiyi, pratik aktivitelerde kelimeyi temsil etmek için kullanacağız.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"@@locale": "uk",
|
||||
"@@last_modified": "2025-12-15 13:09:47.980810",
|
||||
"@@last_modified": "2025-12-15 14:43:25.022859",
|
||||
"about": "Про застосунок",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -10695,5 +10695,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Ви встановили емодзі для {lemma}! Ми будемо використовувати цей емодзі для представлення слова в практичних завданнях у майбутньому.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"@@last_modified": "2025-12-15 13:10:05.261556",
|
||||
"@@last_modified": "2025-12-15 14:43:45.911331",
|
||||
"about": "Giới thiệu",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -6271,5 +6271,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "Bạn đã đặt biểu tượng cảm xúc cho {lemma}! Chúng tôi sẽ sử dụng biểu tượng cảm xúc này để đại diện cho từ trong các hoạt động thực hành trong tương lai.",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1860,7 +1860,7 @@
|
|||
"selectAll": "全選",
|
||||
"deselectAll": "取消全選",
|
||||
"@@locale": "yue",
|
||||
"@@last_modified": "2025-12-15 13:09:45.810126",
|
||||
"@@last_modified": "2025-12-15 14:43:20.879510",
|
||||
"@ignoreUser": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -11792,5 +11792,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "你已為 {lemma} 設定了表情符號!我們將在未來的練習活動中使用這個表情符號來代表這個詞。",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"@@locale": "zh",
|
||||
"@@last_modified": "2025-12-15 13:10:10.038504",
|
||||
"@@last_modified": "2025-12-15 14:43:51.241424",
|
||||
"about": "关于",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -10692,5 +10692,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "您已为 {lemma} 设置了表情符号!我们将使用此表情符号在今后的实践活动中表示该词。",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"@@last_modified": "2025-12-15 13:09:56.043510",
|
||||
"@@last_modified": "2025-12-15 14:43:35.064582",
|
||||
"about": "關於",
|
||||
"@about": {
|
||||
"type": "String",
|
||||
|
|
@ -10699,5 +10699,14 @@
|
|||
"@optionalRegenerateReason": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"emojiSelectedSnackbar": "您已為 {lemma} 設定了表情符號!我們將在未來的練習活動中使用這個表情符號來代表這個詞。",
|
||||
"@emojiSelectedSnackbar": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"lemma": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,11 +4,12 @@ import 'package:flutter/material.dart';
|
|||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.dart';
|
||||
import 'package:fluffychat/pangea/common/network/requests.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
import 'package:fluffychat/pangea/languages/language_constants.dart';
|
||||
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_features_enum.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_meaning/morph_info_repo.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_meaning/morph_info_request.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class MorphMeaningWidget extends StatefulWidget {
|
||||
|
|
@ -29,16 +30,16 @@ class MorphMeaningWidget extends StatefulWidget {
|
|||
|
||||
class MorphMeaningWidgetState extends State<MorphMeaningWidget> {
|
||||
bool _editMode = false;
|
||||
|
||||
late TextEditingController _controller;
|
||||
static const int maxCharacters = 140;
|
||||
String? _cachedResponse;
|
||||
|
||||
String? _definition;
|
||||
bool _isLoading = true;
|
||||
Object? _error;
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant MorphMeaningWidget oldWidget) {
|
||||
if (oldWidget.tag != widget.tag || oldWidget.feature != widget.feature) {
|
||||
_cachedResponse = null;
|
||||
_isLoading = true;
|
||||
_loadMorphMeaning();
|
||||
}
|
||||
|
|
@ -58,28 +59,44 @@ class MorphMeaningWidgetState extends State<MorphMeaningWidget> {
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
MorphInfoRequest get _request => MorphInfoRequest(
|
||||
userL1: MatrixState.pangeaController.userController.userL1?.langCode ??
|
||||
LanguageKeys.defaultLanguage,
|
||||
userL2: MatrixState.pangeaController.userController.userL2?.langCode ??
|
||||
LanguageKeys.defaultLanguage,
|
||||
);
|
||||
|
||||
Future<void> _loadMorphMeaning() async {
|
||||
try {
|
||||
final response = await _morphMeaning();
|
||||
_setMeaningText(response);
|
||||
} catch (e) {
|
||||
_error = e;
|
||||
} finally {
|
||||
if (mounted) setState(() => _isLoading = false);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
_definition = null;
|
||||
});
|
||||
}
|
||||
|
||||
final response = await _morphMeaning();
|
||||
_controller.text = response.substring(
|
||||
0,
|
||||
min(response.length, maxCharacters),
|
||||
);
|
||||
_definition = response;
|
||||
|
||||
if (mounted) setState(() => _isLoading = false);
|
||||
}
|
||||
|
||||
Future<String> _morphMeaning() async {
|
||||
if (_cachedResponse != null) {
|
||||
return _cachedResponse!;
|
||||
final result = await MorphInfoRepo.get(
|
||||
MatrixState.pangeaController.userController.accessToken,
|
||||
_request,
|
||||
);
|
||||
|
||||
if (result.isError) {
|
||||
return L10n.of(context).meaningNotFound;
|
||||
}
|
||||
|
||||
final response = await MorphInfoRepo.get(
|
||||
feature: widget.feature,
|
||||
tag: widget.tag,
|
||||
);
|
||||
_cachedResponse = response;
|
||||
return response ?? L10n.of(context).meaningNotFound;
|
||||
final morph = result.result!.getFeatureByCode(widget.feature.name);
|
||||
final data = morph?.getTagByCode(widget.tag);
|
||||
return data?.l1Description ?? L10n.of(context).meaningNotFound;
|
||||
}
|
||||
|
||||
void _toggleEditMode(bool value) => setState(() => _editMode = value);
|
||||
|
|
@ -90,22 +107,15 @@ class MorphMeaningWidgetState extends State<MorphMeaningWidget> {
|
|||
? userEdit.substring(0, maxCharacters)
|
||||
: userEdit;
|
||||
|
||||
await MorphInfoRepo.setMorphDefinition(
|
||||
await MorphInfoRepo.update(
|
||||
_request,
|
||||
feature: widget.feature,
|
||||
tag: widget.tag,
|
||||
defintion: truncatedEdit,
|
||||
definition: truncatedEdit,
|
||||
);
|
||||
|
||||
// Update the cached response
|
||||
_cachedResponse = truncatedEdit;
|
||||
_toggleEditMode(false);
|
||||
}
|
||||
|
||||
void _setMeaningText(String initialText) {
|
||||
_controller.text = initialText.substring(
|
||||
0,
|
||||
min(initialText.length, maxCharacters),
|
||||
);
|
||||
_loadMorphMeaning();
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -114,27 +124,11 @@ class MorphMeaningWidgetState extends State<MorphMeaningWidget> {
|
|||
return const TextLoadingShimmer();
|
||||
}
|
||||
|
||||
if (_error != null) {
|
||||
return Center(
|
||||
child: _error is UnsubscribedException
|
||||
? ErrorIndicator(
|
||||
message: L10n.of(context).subscribeToUnlockDefinitions,
|
||||
onTap: () {
|
||||
MatrixState.pangeaController.subscriptionController
|
||||
.showPaywall(context);
|
||||
},
|
||||
)
|
||||
: ErrorIndicator(
|
||||
message: L10n.of(context).errorFetchingDefinition,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (_editMode) {
|
||||
return MorphEditView(
|
||||
morphFeature: widget.feature,
|
||||
morphTag: widget.tag,
|
||||
meaning: _cachedResponse ?? "",
|
||||
meaning: _definition ?? "",
|
||||
controller: _controller,
|
||||
toggleEditMode: _toggleEditMode,
|
||||
editMorphMeaning: editMorphMeaning,
|
||||
|
|
@ -149,7 +143,7 @@ class MorphMeaningWidgetState extends State<MorphMeaningWidget> {
|
|||
onDoubleTap: () => _toggleEditMode(true),
|
||||
child: Text(
|
||||
textAlign: TextAlign.center,
|
||||
_cachedResponse ?? L10n.of(context).meaningNotFound,
|
||||
_definition ?? L10n.of(context).meaningNotFound,
|
||||
style: widget.style,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,21 +1,24 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/shrinkable_text.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_level_enum.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
|
||||
|
||||
class VocabAnalyticsListTile extends StatefulWidget {
|
||||
const VocabAnalyticsListTile({
|
||||
super.key,
|
||||
required this.emoji,
|
||||
required this.constructUse,
|
||||
required this.onTap,
|
||||
required this.constructId,
|
||||
required this.textColor,
|
||||
required this.icon,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
final String? emoji;
|
||||
final void Function() onTap;
|
||||
final ConstructUses constructUse;
|
||||
final void Function()? onTap;
|
||||
final ConstructIdentifier constructId;
|
||||
final Color textColor;
|
||||
final Widget icon;
|
||||
|
||||
@override
|
||||
VocabAnalyticsListTileState createState() => VocabAnalyticsListTileState();
|
||||
|
|
@ -43,9 +46,7 @@ class VocabAnalyticsListTileState extends State<VocabAnalyticsListTile> {
|
|||
padding: EdgeInsets.all(padding),
|
||||
decoration: BoxDecoration(
|
||||
color: _isHovered
|
||||
? widget.constructUse.constructLevel
|
||||
.color(context)
|
||||
.withAlpha(20)
|
||||
? widget.textColor.withAlpha(20)
|
||||
: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
),
|
||||
|
|
@ -55,28 +56,18 @@ class VocabAnalyticsListTileState extends State<VocabAnalyticsListTile> {
|
|||
Container(
|
||||
alignment: Alignment.center,
|
||||
height: (maxWidth - padding * 2) * 0.6,
|
||||
child: widget.emoji != null
|
||||
? Text(
|
||||
widget.emoji!,
|
||||
style: const TextStyle(
|
||||
fontSize: 22,
|
||||
),
|
||||
)
|
||||
: widget.constructUse.constructLevel.icon(36.0),
|
||||
child: widget.icon,
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.topCenter,
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
height: (maxWidth - padding * 2) * 0.4,
|
||||
child: ShrinkableText(
|
||||
text: widget.constructUse.lemma,
|
||||
text: widget.constructId.lemma,
|
||||
maxWidth: maxWidth - padding * 2,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).brightness == Brightness.light
|
||||
? widget.constructUse.constructLevel
|
||||
.darkColor(context)
|
||||
: widget.constructUse.constructLevel.color(context),
|
||||
color: widget.textColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -177,8 +177,20 @@ class VocabAnalyticsListView extends StatelessWidget {
|
|||
onTap: () => context.go(
|
||||
"/rooms/analytics/${vocabItem.id.type.string}/${Uri.encodeComponent(vocabItem.id.string)}",
|
||||
),
|
||||
constructUse: vocabItem,
|
||||
constructId: vocabItem.id,
|
||||
textColor:
|
||||
Theme.of(context).brightness == Brightness.light
|
||||
? vocabItem.lemmaCategory.darkColor(context)
|
||||
: vocabItem.lemmaCategory.color(context),
|
||||
emoji: vocabItem.id.userSetEmoji.firstOrNull,
|
||||
icon: vocabItem.id.userSetEmoji.isNotEmpty
|
||||
? Text(
|
||||
vocabItem.id.userSetEmoji.first,
|
||||
style: const TextStyle(
|
||||
fontSize: 22,
|
||||
),
|
||||
)
|
||||
: vocabItem.lemmaCategory.icon(36.0),
|
||||
);
|
||||
},
|
||||
childCount: _filteredVocab.length,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/analytics_details_popup/vocab_analytics_list_tile.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
mixin LemmaEmojiSetter {
|
||||
mixin LemmaEmojiSetter<T extends StatefulWidget> on State<T> {
|
||||
Future<void> setLemmaEmoji(
|
||||
ConstructIdentifier constructId,
|
||||
String emoji,
|
||||
|
|
@ -19,6 +26,50 @@ mixin LemmaEmojiSetter {
|
|||
await constructId.setUserLemmaInfo(
|
||||
constructId.userLemmaInfo.copyWith(emojis: [emoji]),
|
||||
);
|
||||
|
||||
_showSnackbar(constructId, emoji);
|
||||
}
|
||||
|
||||
void _showSnackbar(ConstructIdentifier constructId, String emoji) {
|
||||
if (InstructionsEnum.setLemmaEmoji.isToggledOff) return;
|
||||
InstructionsEnum.setLemmaEmoji.setToggledOff(true);
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Row(
|
||||
spacing: 8.0,
|
||||
children: [
|
||||
VocabAnalyticsListTile(
|
||||
constructId: constructId,
|
||||
emoji: emoji,
|
||||
textColor: Theme.of(context).colorScheme.surface,
|
||||
icon: Text(
|
||||
emoji,
|
||||
style: const TextStyle(
|
||||
fontSize: 22,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||
context.go(
|
||||
"/rooms/analytics/${constructId.type.name}/${Uri.encodeComponent(constructId.string)}",
|
||||
);
|
||||
},
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
L10n.of(context).emojiSelectedSnackbar(constructId.lemma),
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
duration: const Duration(seconds: 30),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _sendEmojiAnalytics(
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ enum InstructionsEnum {
|
|||
chatParticipantTooltip,
|
||||
courseParticipantTooltip,
|
||||
noSavedActivitiesYet,
|
||||
setLemmaEmoji,
|
||||
}
|
||||
|
||||
extension InstructionsEnumExtension on InstructionsEnum {
|
||||
|
|
@ -57,6 +58,7 @@ extension InstructionsEnumExtension on InstructionsEnum {
|
|||
case InstructionsEnum.activityAnalyticsList:
|
||||
case InstructionsEnum.levelAnalytics:
|
||||
case InstructionsEnum.noSavedActivitiesYet:
|
||||
case InstructionsEnum.setLemmaEmoji:
|
||||
ErrorHandler.logError(
|
||||
e: Exception("No title for this instruction"),
|
||||
m: 'InstructionsEnumExtension.title',
|
||||
|
|
@ -117,6 +119,8 @@ extension InstructionsEnumExtension on InstructionsEnum {
|
|||
return l10n.levelInfoTooltip;
|
||||
case InstructionsEnum.noSavedActivitiesYet:
|
||||
return l10n.noSavedActivitiesYet;
|
||||
case InstructionsEnum.setLemmaEmoji:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,12 +10,14 @@ class LanguageModel {
|
|||
final String langCode;
|
||||
final String displayName;
|
||||
final String script;
|
||||
final String? localeEmoji;
|
||||
final L2SupportEnum l2Support;
|
||||
final TextDirection? _textDirection;
|
||||
|
||||
LanguageModel({
|
||||
required this.langCode,
|
||||
required this.displayName,
|
||||
this.localeEmoji,
|
||||
this.script = LanguageKeys.unknownLanguage,
|
||||
this.l2Support = L2SupportEnum.na,
|
||||
TextDirection? textDirection,
|
||||
|
|
@ -40,6 +42,7 @@ class LanguageModel {
|
|||
(e) => e.name == json['text_direction'],
|
||||
)
|
||||
: null,
|
||||
localeEmoji: json['locale_emoji'],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -49,6 +52,7 @@ class LanguageModel {
|
|||
'script': script,
|
||||
'l2_support': l2Support.storageString,
|
||||
'text_direction': textDirection.name,
|
||||
'locale_emoji': localeEmoji,
|
||||
};
|
||||
|
||||
bool get l2 => l2Support != L2SupportEnum.na;
|
||||
|
|
@ -296,7 +300,13 @@ class LanguageModel {
|
|||
"zuDisplayName": l10n.zuDisplayName,
|
||||
};
|
||||
|
||||
return displayNameMap[langKey] ?? displayName;
|
||||
final display = displayNameMap[langKey] ?? displayName;
|
||||
if (langCode.contains('-') && localeEmoji != null) {
|
||||
// use regex to replace parentheses content with the locale emoji
|
||||
final regex = RegExp(r'\s*\(.*?\)\s*');
|
||||
return display.replaceFirst(regex, ' $localeEmoji ');
|
||||
}
|
||||
return display;
|
||||
}
|
||||
|
||||
String get langCodeShort => langCode.split('-').first;
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ class LanguageSelectionPageState extends State<LanguageSelectionPage> {
|
|||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final languages = MatrixState.pangeaController.pLanguageStore.targetOptions;
|
||||
final isColumnMode = FluffyThemes.isColumnMode(context);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
|
|
@ -138,8 +139,8 @@ class LanguageSelectionPageState extends State<LanguageSelectionPage> {
|
|||
bottom: 60.0,
|
||||
),
|
||||
child: Wrap(
|
||||
spacing: 8.0,
|
||||
runSpacing: 8.0,
|
||||
spacing: isColumnMode ? 16.0 : 8.0,
|
||||
runSpacing: isColumnMode ? 16.0 : 8.0,
|
||||
alignment: WrapAlignment.center,
|
||||
children: languages
|
||||
.where(
|
||||
|
|
@ -164,7 +165,9 @@ class LanguageSelectionPageState extends State<LanguageSelectionPage> {
|
|||
),
|
||||
label: Text(
|
||||
l.getDisplayName(context),
|
||||
style: theme.textTheme.bodyMedium,
|
||||
style: isColumnMode
|
||||
? theme.textTheme.bodyLarge
|
||||
: theme.textTheme.bodyMedium,
|
||||
),
|
||||
onSelected: (selected) {
|
||||
_setSelectedLanguage(
|
||||
|
|
|
|||
|
|
@ -1,130 +1,192 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:async/async.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/common/network/requests.dart';
|
||||
import 'package:fluffychat/pangea/common/network/urls.dart';
|
||||
import 'package:fluffychat/pangea/languages/language_constants.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_features_enum.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_meaning/morph_info_request.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_meaning/morph_info_response.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
|
||||
class _APICallCacheItem {
|
||||
final DateTime time;
|
||||
final Future<MorphInfoResponse> future;
|
||||
class _MorphInfoCacheItem {
|
||||
final Future<Result<MorphInfoResponse>> resultFuture;
|
||||
final DateTime timestamp;
|
||||
|
||||
_APICallCacheItem(this.time, this.future);
|
||||
const _MorphInfoCacheItem({
|
||||
required this.resultFuture,
|
||||
required this.timestamp,
|
||||
});
|
||||
}
|
||||
|
||||
class MorphInfoRepo {
|
||||
static final GetStorage _morphMeaningStorage =
|
||||
GetStorage('morph_meaning_storage');
|
||||
static final shortTermCache = <String, _APICallCacheItem>{};
|
||||
static const int _cacheDurationMinutes = 1;
|
||||
// In-memory cache
|
||||
static final Map<String, _MorphInfoCacheItem> _cache = {};
|
||||
static const Duration _cacheDuration = Duration(minutes: 10);
|
||||
|
||||
static void set(MorphInfoRequest request, MorphInfoResponse response) {
|
||||
_morphMeaningStorage.write(request.storageKey, response.toJson());
|
||||
}
|
||||
// Persistent storage
|
||||
static final GetStorage _storage = GetStorage('morph_info_storage');
|
||||
|
||||
static Future<MorphInfoResponse> _fetch(MorphInfoRequest request) async {
|
||||
try {
|
||||
final Requests req = Requests(
|
||||
choreoApiKey: Environment.choreoApiKey,
|
||||
accessToken: MatrixState.pangeaController.userController.accessToken,
|
||||
);
|
||||
|
||||
final Response res = await req.post(
|
||||
url: PApiUrls.morphDictionary,
|
||||
body: request.toJson(),
|
||||
);
|
||||
|
||||
final decodedBody = jsonDecode(utf8.decode(res.bodyBytes));
|
||||
final response = MorphInfoResponse.fromJson(decodedBody);
|
||||
|
||||
set(request, response);
|
||||
|
||||
return response;
|
||||
} catch (e) {
|
||||
debugPrint('Error fetching morph info: $e');
|
||||
return Future.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<MorphInfoResponse> _get(MorphInfoRequest request) async {
|
||||
request.userL1 == request.userL1.split('-').first;
|
||||
request.userL2 == request.userL2.split('-').first;
|
||||
|
||||
final cachedJson = _morphMeaningStorage.read(request.storageKey);
|
||||
if (cachedJson != null) {
|
||||
return MorphInfoResponse.fromJson(cachedJson);
|
||||
static Future<Result<MorphInfoResponse>> get(
|
||||
String accessToken,
|
||||
MorphInfoRequest request,
|
||||
) {
|
||||
// 1. Try memory cache
|
||||
final cached = _getCached(request);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
final _APICallCacheItem? cachedCall = shortTermCache[request.storageKey];
|
||||
if (cachedCall != null) {
|
||||
if (DateTime.now().difference(cachedCall.time).inMinutes <
|
||||
_cacheDurationMinutes) {
|
||||
return cachedCall.future;
|
||||
} else {
|
||||
shortTermCache.remove(request.storageKey);
|
||||
}
|
||||
// 2. Try disk cache
|
||||
final stored = _getStored(request);
|
||||
if (stored != null) {
|
||||
return Future.value(Result.value(stored));
|
||||
}
|
||||
|
||||
final future = _fetch(request);
|
||||
shortTermCache[request.storageKey] =
|
||||
_APICallCacheItem(DateTime.now(), future);
|
||||
// 3. Fetch from network (safe future)
|
||||
final future = _safeFetch(accessToken, request);
|
||||
|
||||
// 4. Save to in-memory cache
|
||||
_cache[request.hashCode.toString()] = _MorphInfoCacheItem(
|
||||
resultFuture: future,
|
||||
timestamp: DateTime.now(),
|
||||
);
|
||||
|
||||
// 5. Write to disk *after* the fetch finishes, without rethrowing
|
||||
writeToDisk(request, future);
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
static Future<String?> get({
|
||||
required MorphFeaturesEnum feature,
|
||||
required String tag,
|
||||
}) async {
|
||||
final res = await _get(
|
||||
MorphInfoRequest(
|
||||
userL1: MatrixState.pangeaController.userController.userL1?.langCode ??
|
||||
LanguageKeys.defaultLanguage,
|
||||
userL2: MatrixState.pangeaController.userController.userL2?.langCode ??
|
||||
LanguageKeys.defaultLanguage,
|
||||
),
|
||||
);
|
||||
final morph = res.getFeatureByCode(feature.name);
|
||||
|
||||
final data = morph?.getTagByCode(tag);
|
||||
|
||||
return data?.l1Description;
|
||||
static Future<void> set(
|
||||
MorphInfoRequest request,
|
||||
MorphInfoResponse resultFuture,
|
||||
) async {
|
||||
final key = request.hashCode.toString();
|
||||
try {
|
||||
await _storage.write(key, resultFuture.toJson());
|
||||
_cache.remove(key); // Invalidate in-memory cache
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {'request': request.toJson()},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> setMorphDefinition({
|
||||
static Future<void> update(
|
||||
MorphInfoRequest request, {
|
||||
required MorphFeaturesEnum feature,
|
||||
required String tag,
|
||||
required String defintion,
|
||||
required String definition,
|
||||
}) async {
|
||||
final userL1 =
|
||||
MatrixState.pangeaController.userController.userL1?.langCode ??
|
||||
LanguageKeys.defaultLanguage;
|
||||
final userL2 =
|
||||
MatrixState.pangeaController.userController.userL2?.langCode ??
|
||||
LanguageKeys.defaultLanguage;
|
||||
final userL1Short = userL1.split('-').first;
|
||||
final userL2Short = userL2.split('-').first;
|
||||
final cachedJson = _morphMeaningStorage.read(userL1Short + userL2Short);
|
||||
try {
|
||||
final cachedJson = await _getCached(request);
|
||||
final resp = cachedJson?.result ??
|
||||
MorphInfoResponse(
|
||||
userL1: request.userL1,
|
||||
userL2: request.userL2,
|
||||
features: [],
|
||||
);
|
||||
|
||||
MorphInfoResponse? resp = MorphInfoResponse(
|
||||
userL1: userL1,
|
||||
userL2: userL2,
|
||||
features: [],
|
||||
resp.setMorphDefinition(feature.name, tag, definition);
|
||||
await set(request, resp);
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {'request': request.toJson()},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<Result<MorphInfoResponse>> _safeFetch(
|
||||
String token,
|
||||
MorphInfoRequest request,
|
||||
) async {
|
||||
try {
|
||||
final resp = await _fetch(token, request);
|
||||
return Result.value(resp);
|
||||
} catch (e, s) {
|
||||
// Ensure error is logged and converted to a Result
|
||||
ErrorHandler.logError(e: e, s: s, data: request.toJson());
|
||||
return Result.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<MorphInfoResponse> _fetch(
|
||||
String accessToken,
|
||||
MorphInfoRequest request,
|
||||
) async {
|
||||
final req = Requests(
|
||||
choreoApiKey: Environment.choreoApiKey,
|
||||
accessToken: accessToken,
|
||||
);
|
||||
|
||||
if (cachedJson is Map<String, dynamic>) {
|
||||
resp = MorphInfoResponse.fromJson(cachedJson);
|
||||
final Response res = await req.post(
|
||||
url: PApiUrls.morphDictionary,
|
||||
body: request.toJson(),
|
||||
);
|
||||
|
||||
if (res.statusCode != 200) {
|
||||
throw HttpException(
|
||||
'Failed to fetch morph info: ${res.statusCode} ${res.reasonPhrase}',
|
||||
);
|
||||
}
|
||||
|
||||
resp.setMorphDefinition(feature.name, tag, defintion);
|
||||
await _morphMeaningStorage.write(userL1Short + userL2Short, resp.toJson());
|
||||
return MorphInfoResponse.fromJson(
|
||||
jsonDecode(utf8.decode(res.bodyBytes)),
|
||||
);
|
||||
}
|
||||
|
||||
static Future<Result<MorphInfoResponse>>? _getCached(
|
||||
MorphInfoRequest request,
|
||||
) {
|
||||
final now = DateTime.now();
|
||||
final key = request.hashCode.toString();
|
||||
|
||||
// Remove stale entries first
|
||||
_cache.removeWhere(
|
||||
(_, item) => now.difference(item.timestamp) >= _cacheDuration,
|
||||
);
|
||||
|
||||
final item = _cache[key];
|
||||
return item?.resultFuture;
|
||||
}
|
||||
|
||||
static Future<void> writeToDisk(
|
||||
MorphInfoRequest request,
|
||||
Future<Result<MorphInfoResponse>> resultFuture,
|
||||
) async {
|
||||
final result = await resultFuture; // SAFE: never throws
|
||||
|
||||
if (!result.isValue) return; // only cache successful responses
|
||||
await set(request, result.asValue!.value);
|
||||
}
|
||||
|
||||
static MorphInfoResponse? _getStored(
|
||||
MorphInfoRequest request,
|
||||
) {
|
||||
final key = request.hashCode.toString();
|
||||
try {
|
||||
final entry = _storage.read(key);
|
||||
if (entry == null) return null;
|
||||
|
||||
return MorphInfoResponse.fromJson(entry);
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {'request': request.toJson()},
|
||||
);
|
||||
_storage.remove(key);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue