diff --git a/app/analysis_options.yaml b/app/analysis_options.yaml index 4a68b98..f58bd3c 100644 --- a/app/analysis_options.yaml +++ b/app/analysis_options.yaml @@ -1,7 +1,11 @@ + + analyzer: exclude: + # SwiftPM 이 iOS 빌드 때 여기에 서드파티 소스를 받는다. 우리 코드가 아니라 + # 분석 대상이 아니며, 그쪽 테스트 파일이 우리 lint 결과를 덮어 버린다. + - build/** # Firebase 도구가 내려받는 서드파티 소스. 우리 분석 대상이 아니다. - '**/node_modules/**' - - build/** - android/** - ios/** diff --git a/app/android/app/src/main/AndroidManifest.xml b/app/android/app/src/main/AndroidManifest.xml index 6552c21..ae0ae0a 100644 --- a/app/android/app/src/main/AndroidManifest.xml +++ b/app/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,14 @@ + + + + + + + + + + + + + + + + fanuzu_default + + FANUZU 알림 + diff --git a/app/assets/figma/avatar-hong.png b/app/assets/figma/avatar-hong.png new file mode 100644 index 0000000..9ace710 Binary files /dev/null and b/app/assets/figma/avatar-hong.png differ diff --git a/app/assets/figma/avatar-maker.png b/app/assets/figma/avatar-maker.png new file mode 100644 index 0000000..4abd8c7 Binary files /dev/null and b/app/assets/figma/avatar-maker.png differ diff --git a/app/assets/figma/avatar-sample.png b/app/assets/figma/avatar-sample.png new file mode 100644 index 0000000..f5ed818 Binary files /dev/null and b/app/assets/figma/avatar-sample.png differ diff --git a/app/assets/figma/bg-space-detail.png b/app/assets/figma/bg-space-detail.png new file mode 100644 index 0000000..580f530 Binary files /dev/null and b/app/assets/figma/bg-space-detail.png differ diff --git a/app/assets/figma/bg-space-home.png b/app/assets/figma/bg-space-home.png new file mode 100644 index 0000000..580f530 Binary files /dev/null and b/app/assets/figma/bg-space-home.png differ diff --git a/app/assets/figma/brand-apple.png b/app/assets/figma/brand-apple.png new file mode 100644 index 0000000..38b8a0f Binary files /dev/null and b/app/assets/figma/brand-apple.png differ diff --git a/app/assets/figma/brand-google.png b/app/assets/figma/brand-google.png new file mode 100644 index 0000000..6902e20 Binary files /dev/null and b/app/assets/figma/brand-google.png differ diff --git a/app/assets/figma/brand-kakao.png b/app/assets/figma/brand-kakao.png new file mode 100644 index 0000000..5684a02 Binary files /dev/null and b/app/assets/figma/brand-kakao.png differ diff --git a/app/assets/figma/campaign-calendar.png b/app/assets/figma/campaign-calendar.png new file mode 100644 index 0000000..35a989d Binary files /dev/null and b/app/assets/figma/campaign-calendar.png differ diff --git a/app/assets/figma/campaign-camera.png b/app/assets/figma/campaign-camera.png new file mode 100644 index 0000000..88fe224 Binary files /dev/null and b/app/assets/figma/campaign-camera.png differ diff --git a/app/assets/figma/campaign-chevron-down.png b/app/assets/figma/campaign-chevron-down.png new file mode 100644 index 0000000..2a77d35 Binary files /dev/null and b/app/assets/figma/campaign-chevron-down.png differ diff --git a/app/assets/figma/campaign-clock.png b/app/assets/figma/campaign-clock.png new file mode 100644 index 0000000..9ceb453 Binary files /dev/null and b/app/assets/figma/campaign-clock.png differ diff --git a/app/assets/figma/campaign-rank-down.png b/app/assets/figma/campaign-rank-down.png new file mode 100644 index 0000000..8bec412 Binary files /dev/null and b/app/assets/figma/campaign-rank-down.png differ diff --git a/app/assets/figma/campaign-sample.png b/app/assets/figma/campaign-sample.png new file mode 100644 index 0000000..9266fa6 Binary files /dev/null and b/app/assets/figma/campaign-sample.png differ diff --git a/app/assets/figma/campaign-share-instagram.png b/app/assets/figma/campaign-share-instagram.png new file mode 100644 index 0000000..7156c23 Binary files /dev/null and b/app/assets/figma/campaign-share-instagram.png differ diff --git a/app/assets/figma/campaign-share-link.png b/app/assets/figma/campaign-share-link.png new file mode 100644 index 0000000..124746c Binary files /dev/null and b/app/assets/figma/campaign-share-link.png differ diff --git a/app/assets/figma/campaign-share-tiktok.png b/app/assets/figma/campaign-share-tiktok.png new file mode 100644 index 0000000..b99b29e Binary files /dev/null and b/app/assets/figma/campaign-share-tiktok.png differ diff --git a/app/assets/figma/campaign-share-x.png b/app/assets/figma/campaign-share-x.png new file mode 100644 index 0000000..3cf6bfd Binary files /dev/null and b/app/assets/figma/campaign-share-x.png differ diff --git a/app/assets/figma/home-plus-box.png b/app/assets/figma/home-plus-box.png new file mode 100644 index 0000000..9f984b5 Binary files /dev/null and b/app/assets/figma/home-plus-box.png differ diff --git a/app/assets/figma/icon-bell-42.png b/app/assets/figma/icon-bell-42.png new file mode 100644 index 0000000..3af3fe4 Binary files /dev/null and b/app/assets/figma/icon-bell-42.png differ diff --git a/app/assets/figma/icon-bolt.png b/app/assets/figma/icon-bolt.png new file mode 100644 index 0000000..77d3b8d Binary files /dev/null and b/app/assets/figma/icon-bolt.png differ diff --git a/app/assets/figma/icon-bpm.png b/app/assets/figma/icon-bpm.png new file mode 100644 index 0000000..48b3ff0 Binary files /dev/null and b/app/assets/figma/icon-bpm.png differ diff --git a/app/assets/figma/icon-camera.png b/app/assets/figma/icon-camera.png new file mode 100644 index 0000000..88fe224 Binary files /dev/null and b/app/assets/figma/icon-camera.png differ diff --git a/app/assets/figma/icon-chevron-down.png b/app/assets/figma/icon-chevron-down.png new file mode 100644 index 0000000..7e9ca76 Binary files /dev/null and b/app/assets/figma/icon-chevron-down.png differ diff --git a/app/assets/figma/icon-clock.png b/app/assets/figma/icon-clock.png new file mode 100644 index 0000000..9ceb453 Binary files /dev/null and b/app/assets/figma/icon-clock.png differ diff --git a/app/assets/figma/icon-community.png b/app/assets/figma/icon-community.png new file mode 100644 index 0000000..4a8dfb9 Binary files /dev/null and b/app/assets/figma/icon-community.png differ diff --git a/app/assets/figma/icon-compose-camera.png b/app/assets/figma/icon-compose-camera.png new file mode 100644 index 0000000..cf9bbe6 Binary files /dev/null and b/app/assets/figma/icon-compose-camera.png differ diff --git a/app/assets/figma/icon-contribution.png b/app/assets/figma/icon-contribution.png new file mode 100644 index 0000000..571bc46 Binary files /dev/null and b/app/assets/figma/icon-contribution.png differ diff --git a/app/assets/figma/icon-crown.png b/app/assets/figma/icon-crown.png new file mode 100644 index 0000000..3cd4179 Binary files /dev/null and b/app/assets/figma/icon-crown.png differ diff --git a/app/assets/figma/icon-file-check.png b/app/assets/figma/icon-file-check.png new file mode 100644 index 0000000..1656e29 Binary files /dev/null and b/app/assets/figma/icon-file-check.png differ diff --git a/app/assets/figma/icon-health.png b/app/assets/figma/icon-health.png new file mode 100644 index 0000000..8231961 Binary files /dev/null and b/app/assets/figma/icon-health.png differ diff --git a/app/assets/figma/icon-heart.png b/app/assets/figma/icon-heart.png new file mode 100644 index 0000000..50577ce Binary files /dev/null and b/app/assets/figma/icon-heart.png differ diff --git a/app/assets/figma/icon-image.png b/app/assets/figma/icon-image.png new file mode 100644 index 0000000..de99276 Binary files /dev/null and b/app/assets/figma/icon-image.png differ diff --git a/app/assets/figma/icon-info.png b/app/assets/figma/icon-info.png new file mode 100644 index 0000000..9b8d2fa Binary files /dev/null and b/app/assets/figma/icon-info.png differ diff --git a/app/assets/figma/icon-item.png b/app/assets/figma/icon-item.png new file mode 100644 index 0000000..c9d7e3f Binary files /dev/null and b/app/assets/figma/icon-item.png differ diff --git a/app/assets/figma/icon-list.png b/app/assets/figma/icon-list.png new file mode 100644 index 0000000..a9fae2b Binary files /dev/null and b/app/assets/figma/icon-list.png differ diff --git a/app/assets/figma/icon-message-circle.png b/app/assets/figma/icon-message-circle.png new file mode 100644 index 0000000..d869aa5 Binary files /dev/null and b/app/assets/figma/icon-message-circle.png differ diff --git a/app/assets/figma/icon-pencil.png b/app/assets/figma/icon-pencil.png new file mode 100644 index 0000000..e2db07b Binary files /dev/null and b/app/assets/figma/icon-pencil.png differ diff --git a/app/assets/figma/icon-person.png b/app/assets/figma/icon-person.png new file mode 100644 index 0000000..1371577 Binary files /dev/null and b/app/assets/figma/icon-person.png differ diff --git a/app/assets/figma/icon-pop-bolt.png b/app/assets/figma/icon-pop-bolt.png new file mode 100644 index 0000000..c3ed3e6 Binary files /dev/null and b/app/assets/figma/icon-pop-bolt.png differ diff --git a/app/assets/figma/icon-pop-small.png b/app/assets/figma/icon-pop-small.png new file mode 100644 index 0000000..77d3b8d Binary files /dev/null and b/app/assets/figma/icon-pop-small.png differ diff --git a/app/assets/figma/icon-post-more.png b/app/assets/figma/icon-post-more.png new file mode 100644 index 0000000..52b0993 Binary files /dev/null and b/app/assets/figma/icon-post-more.png differ diff --git a/app/assets/figma/icon-quests.png b/app/assets/figma/icon-quests.png new file mode 100644 index 0000000..3486271 Binary files /dev/null and b/app/assets/figma/icon-quests.png differ diff --git a/app/assets/figma/icon-referral.png b/app/assets/figma/icon-referral.png new file mode 100644 index 0000000..670c4ab Binary files /dev/null and b/app/assets/figma/icon-referral.png differ diff --git a/app/assets/figma/icon-search-20.png b/app/assets/figma/icon-search-20.png new file mode 100644 index 0000000..7c9fe41 Binary files /dev/null and b/app/assets/figma/icon-search-20.png differ diff --git a/app/assets/figma/icon-search-42.png b/app/assets/figma/icon-search-42.png new file mode 100644 index 0000000..fb9cbef Binary files /dev/null and b/app/assets/figma/icon-search-42.png differ diff --git a/app/assets/figma/icon-send.png b/app/assets/figma/icon-send.png new file mode 100644 index 0000000..990f42a Binary files /dev/null and b/app/assets/figma/icon-send.png differ diff --git a/app/assets/figma/icon-steps.png b/app/assets/figma/icon-steps.png new file mode 100644 index 0000000..08c6f71 Binary files /dev/null and b/app/assets/figma/icon-steps.png differ diff --git a/app/assets/figma/icon-tier-sprout.png b/app/assets/figma/icon-tier-sprout.png new file mode 100644 index 0000000..9b2afa6 Binary files /dev/null and b/app/assets/figma/icon-tier-sprout.png differ diff --git a/app/assets/figma/icon-wallet.png b/app/assets/figma/icon-wallet.png new file mode 100644 index 0000000..9c1c313 Binary files /dev/null and b/app/assets/figma/icon-wallet.png differ diff --git a/app/assets/figma/inside-sample-2.png b/app/assets/figma/inside-sample-2.png new file mode 100644 index 0000000..2c9a855 Binary files /dev/null and b/app/assets/figma/inside-sample-2.png differ diff --git a/app/assets/figma/inside-sample-3.png b/app/assets/figma/inside-sample-3.png new file mode 100644 index 0000000..df9f288 Binary files /dev/null and b/app/assets/figma/inside-sample-3.png differ diff --git a/app/assets/figma/inside-sample-4.png b/app/assets/figma/inside-sample-4.png new file mode 100644 index 0000000..2282489 Binary files /dev/null and b/app/assets/figma/inside-sample-4.png differ diff --git a/app/assets/figma/inside-sample-5.png b/app/assets/figma/inside-sample-5.png new file mode 100644 index 0000000..8538bd6 Binary files /dev/null and b/app/assets/figma/inside-sample-5.png differ diff --git a/app/assets/figma/lounge-post-sample.png b/app/assets/figma/lounge-post-sample.png new file mode 100644 index 0000000..f798d8f Binary files /dev/null and b/app/assets/figma/lounge-post-sample.png differ diff --git a/app/assets/figma/meteor-a.png b/app/assets/figma/meteor-a.png new file mode 100644 index 0000000..fba0907 Binary files /dev/null and b/app/assets/figma/meteor-a.png differ diff --git a/app/assets/figma/meteor-b.png b/app/assets/figma/meteor-b.png new file mode 100644 index 0000000..06e6632 Binary files /dev/null and b/app/assets/figma/meteor-b.png differ diff --git a/app/assets/figma/my-camera-badge.png b/app/assets/figma/my-camera-badge.png new file mode 100644 index 0000000..c35459c Binary files /dev/null and b/app/assets/figma/my-camera-badge.png differ diff --git a/app/assets/figma/my-planet-1.png b/app/assets/figma/my-planet-1.png new file mode 100644 index 0000000..611fd53 Binary files /dev/null and b/app/assets/figma/my-planet-1.png differ diff --git a/app/assets/figma/my-planet-2.png b/app/assets/figma/my-planet-2.png new file mode 100644 index 0000000..8b7cbd1 Binary files /dev/null and b/app/assets/figma/my-planet-2.png differ diff --git a/app/assets/figma/my-planet-3.png b/app/assets/figma/my-planet-3.png new file mode 100644 index 0000000..6505c70 Binary files /dev/null and b/app/assets/figma/my-planet-3.png differ diff --git a/app/assets/figma/nebula-card.png b/app/assets/figma/nebula-card.png new file mode 100644 index 0000000..8b7cbd1 Binary files /dev/null and b/app/assets/figma/nebula-card.png differ diff --git a/app/assets/figma/nebula-home.png b/app/assets/figma/nebula-home.png new file mode 100644 index 0000000..8b7cbd1 Binary files /dev/null and b/app/assets/figma/nebula-home.png differ diff --git a/app/assets/figma/planet-1.png b/app/assets/figma/planet-1.png new file mode 100644 index 0000000..f70a8b2 Binary files /dev/null and b/app/assets/figma/planet-1.png differ diff --git a/app/assets/figma/planet-2.png b/app/assets/figma/planet-2.png new file mode 100644 index 0000000..5eea4ed Binary files /dev/null and b/app/assets/figma/planet-2.png differ diff --git a/app/assets/figma/planet-detail-hero.png b/app/assets/figma/planet-detail-hero.png new file mode 100644 index 0000000..611fd53 Binary files /dev/null and b/app/assets/figma/planet-detail-hero.png differ diff --git a/app/assets/figma/planet-detail.png b/app/assets/figma/planet-detail.png new file mode 100644 index 0000000..f70a8b2 Binary files /dev/null and b/app/assets/figma/planet-detail.png differ diff --git a/app/assets/figma/planet-earth.png b/app/assets/figma/planet-earth.png new file mode 100644 index 0000000..f7e5359 Binary files /dev/null and b/app/assets/figma/planet-earth.png differ diff --git a/app/assets/figma/planet-home.png b/app/assets/figma/planet-home.png new file mode 100644 index 0000000..aa770d5 Binary files /dev/null and b/app/assets/figma/planet-home.png differ diff --git a/app/assets/figma/planet-shape-1.png b/app/assets/figma/planet-shape-1.png new file mode 100644 index 0000000..9aa2fb2 Binary files /dev/null and b/app/assets/figma/planet-shape-1.png differ diff --git a/app/assets/figma/planet-shape-2.png b/app/assets/figma/planet-shape-2.png new file mode 100644 index 0000000..bdeec45 Binary files /dev/null and b/app/assets/figma/planet-shape-2.png differ diff --git a/app/assets/figma/planet-shape-3.png b/app/assets/figma/planet-shape-3.png new file mode 100644 index 0000000..c70a7ca Binary files /dev/null and b/app/assets/figma/planet-shape-3.png differ diff --git a/app/assets/figma/planet-shape-4.png b/app/assets/figma/planet-shape-4.png new file mode 100644 index 0000000..6effb01 Binary files /dev/null and b/app/assets/figma/planet-shape-4.png differ diff --git a/app/assets/figma/planet-shape-5.png b/app/assets/figma/planet-shape-5.png new file mode 100644 index 0000000..5448bc9 Binary files /dev/null and b/app/assets/figma/planet-shape-5.png differ diff --git a/app/assets/figma/planet-shape-placeholder.png b/app/assets/figma/planet-shape-placeholder.png new file mode 100644 index 0000000..18a1020 Binary files /dev/null and b/app/assets/figma/planet-shape-placeholder.png differ diff --git a/app/assets/figma/quest-check.png b/app/assets/figma/quest-check.png new file mode 100644 index 0000000..cb97bf1 Binary files /dev/null and b/app/assets/figma/quest-check.png differ diff --git a/app/assets/figma/quest-done.png b/app/assets/figma/quest-done.png new file mode 100644 index 0000000..7eac71e Binary files /dev/null and b/app/assets/figma/quest-done.png differ diff --git a/app/assets/figma/quest-globe.png b/app/assets/figma/quest-globe.png new file mode 100644 index 0000000..abf4be6 Binary files /dev/null and b/app/assets/figma/quest-globe.png differ diff --git a/app/assets/figma/quest-img-bolt.png b/app/assets/figma/quest-img-bolt.png new file mode 100644 index 0000000..f7e97ca Binary files /dev/null and b/app/assets/figma/quest-img-bolt.png differ diff --git a/app/assets/figma/quest-img-bubble.png b/app/assets/figma/quest-img-bubble.png new file mode 100644 index 0000000..17c6985 Binary files /dev/null and b/app/assets/figma/quest-img-bubble.png differ diff --git a/app/assets/figma/quest-img-heart.png b/app/assets/figma/quest-img-heart.png new file mode 100644 index 0000000..a7388c3 Binary files /dev/null and b/app/assets/figma/quest-img-heart.png differ diff --git a/app/assets/figma/quest-img-shoe.png b/app/assets/figma/quest-img-shoe.png new file mode 100644 index 0000000..307885f Binary files /dev/null and b/app/assets/figma/quest-img-shoe.png differ diff --git a/app/assets/figma/quest-img-stamp.png b/app/assets/figma/quest-img-stamp.png new file mode 100644 index 0000000..3d9de15 Binary files /dev/null and b/app/assets/figma/quest-img-stamp.png differ diff --git a/app/assets/figma/quest-img-truck.png b/app/assets/figma/quest-img-truck.png new file mode 100644 index 0000000..101b9b8 Binary files /dev/null and b/app/assets/figma/quest-img-truck.png differ diff --git a/app/assets/figma/quest-kebab.png b/app/assets/figma/quest-kebab.png new file mode 100644 index 0000000..52b0993 Binary files /dev/null and b/app/assets/figma/quest-kebab.png differ diff --git a/app/assets/figma/quest-people.png b/app/assets/figma/quest-people.png new file mode 100644 index 0000000..64c9374 Binary files /dev/null and b/app/assets/figma/quest-people.png differ diff --git a/app/assets/figma/quest-pin.png b/app/assets/figma/quest-pin.png new file mode 100644 index 0000000..a5d4ae3 Binary files /dev/null and b/app/assets/figma/quest-pin.png differ diff --git a/app/assets/figma/quest-pulse.png b/app/assets/figma/quest-pulse.png new file mode 100644 index 0000000..e9fa8c7 Binary files /dev/null and b/app/assets/figma/quest-pulse.png differ diff --git a/app/assets/figma/quest-warp-planet.png b/app/assets/figma/quest-warp-planet.png new file mode 100644 index 0000000..611fd53 Binary files /dev/null and b/app/assets/figma/quest-warp-planet.png differ diff --git a/app/assets/figma/share-instagram.png b/app/assets/figma/share-instagram.png new file mode 100644 index 0000000..7156c23 Binary files /dev/null and b/app/assets/figma/share-instagram.png differ diff --git a/app/assets/figma/share-link.png b/app/assets/figma/share-link.png new file mode 100644 index 0000000..124746c Binary files /dev/null and b/app/assets/figma/share-link.png differ diff --git a/app/assets/figma/share-tiktok.png b/app/assets/figma/share-tiktok.png new file mode 100644 index 0000000..b99b29e Binary files /dev/null and b/app/assets/figma/share-tiktok.png differ diff --git a/app/assets/figma/share-x.png b/app/assets/figma/share-x.png new file mode 100644 index 0000000..3cf6bfd Binary files /dev/null and b/app/assets/figma/share-x.png differ diff --git a/app/assets/fonts/Pretendard-Black-Latin.otf b/app/assets/fonts/Pretendard-Black-Latin.otf new file mode 100644 index 0000000..ba06b9c Binary files /dev/null and b/app/assets/fonts/Pretendard-Black-Latin.otf differ diff --git a/app/assets/fx/clouds.png b/app/assets/fx/clouds.png new file mode 100644 index 0000000..3ca8eaf Binary files /dev/null and b/app/assets/fx/clouds.png differ diff --git a/app/assets/fx/nebula-figma.png b/app/assets/fx/nebula-figma.png new file mode 100644 index 0000000..8699842 Binary files /dev/null and b/app/assets/fx/nebula-figma.png differ diff --git a/app/assets/fx/planet-rings.png b/app/assets/fx/planet-rings.png new file mode 100644 index 0000000..c600526 Binary files /dev/null and b/app/assets/fx/planet-rings.png differ diff --git a/app/assets/fx/sources.json b/app/assets/fx/sources.json new file mode 100644 index 0000000..4ae3617 --- /dev/null +++ b/app/assets/fx/sources.json @@ -0,0 +1,27 @@ +{ + "nebula-figma.png": { + "source": "Figma", + "fileKey": "89rQgL1JEFlA8yxl9L77bS", + "nodeId": "373:442732", + "exportScale": 2, + "role": "Stage 0 gas/nebula; original client asset" + }, + "clouds.png": { + "source": "OpenAI imagegen", + "generatedImage": "exec-3a0587d0-b903-4b45-9b70-11ea1db22522.png", + "role": "Independent cloud layer for Stage 2+", + "references": [ + "Figma 2056:15974 layer board", + "assets/figma/planet-home.png" + ], + "direction": "Five lavender-white clay clouds, transparent background, clear center; no planet or logo" + }, + "planet-rings.png": { + "source": "OpenAI imagegen (built-in)", + "generatedImage": "exec-6b683abe-4b5c-436a-8997-a137db05fb89.png", + "reference": "planet FX verification capture (original rich ring)", + "role": "Original rich multi-band ring, slightly reduced fine glitter and highlights; split and occluded in Flutter", + "prompt": "Use case: precise-object-edit. Edit this exact transparent planetary ring sprite with a VERY SMALL reduction in visual intensity. Preserve 85-90% of its original appearance. Keep the same five purple/magenta luminous streams, same broad elliptical silhouette, same perspective, same centered position, same rocky asteroid sizes and composition, same saturated magical cosmic style. Only soften the brightest white-hot highlights slightly (about 10-15%), reduce the fine pinprick glitter density by about 20%, and make the faceted rock highlights a little less sharp. Retain most debris, fine detail, glow, contrast and energetic richness. This is a restrained polish of the original, NOT a redesign. Do not turn it into clay, a smooth solid disk, a rubber ring, or sparse ribbons. Do not desaturate it or make it dim. Keep the ring visually strong. Preserve exact framing and proportions, genuine transparent alpha background and transparent center; no planet or text. Output the edited ring alone.", + "compositing": "Diameter 1.7x body bounds, stage-weighted opacity; rear hemisphere clipped against body silhouette; no bitmap rotation that would change ring plane." + } +} diff --git a/app/assets/icons/bell.png b/app/assets/icons/bell.png new file mode 100644 index 0000000..610d008 Binary files /dev/null and b/app/assets/icons/bell.png differ diff --git a/app/assets/icons/bolt.png b/app/assets/icons/bolt.png new file mode 100644 index 0000000..77d3b8d Binary files /dev/null and b/app/assets/icons/bolt.png differ diff --git a/app/assets/icons/book_open.png b/app/assets/icons/book_open.png new file mode 100644 index 0000000..4ab3fe2 Binary files /dev/null and b/app/assets/icons/book_open.png differ diff --git a/app/assets/icons/camera.png b/app/assets/icons/camera.png new file mode 100644 index 0000000..88fe224 Binary files /dev/null and b/app/assets/icons/camera.png differ diff --git a/app/assets/icons/clock_8.png b/app/assets/icons/clock_8.png new file mode 100644 index 0000000..6579224 Binary files /dev/null and b/app/assets/icons/clock_8.png differ diff --git a/app/assets/icons/corner_down_right.png b/app/assets/icons/corner_down_right.png new file mode 100644 index 0000000..6921548 Binary files /dev/null and b/app/assets/icons/corner_down_right.png differ diff --git a/app/assets/icons/eye.png b/app/assets/icons/eye.png new file mode 100644 index 0000000..9b24f3d Binary files /dev/null and b/app/assets/icons/eye.png differ diff --git a/app/assets/icons/globe.png b/app/assets/icons/globe.png new file mode 100644 index 0000000..b527c90 Binary files /dev/null and b/app/assets/icons/globe.png differ diff --git a/app/assets/icons/heart.png b/app/assets/icons/heart.png new file mode 100644 index 0000000..50577ce Binary files /dev/null and b/app/assets/icons/heart.png differ diff --git a/app/assets/icons/image.png b/app/assets/icons/image.png new file mode 100644 index 0000000..ca7f80a Binary files /dev/null and b/app/assets/icons/image.png differ diff --git a/app/assets/icons/message_circle.png b/app/assets/icons/message_circle.png new file mode 100644 index 0000000..d869aa5 Binary files /dev/null and b/app/assets/icons/message_circle.png differ diff --git a/app/assets/icons/pencil.png b/app/assets/icons/pencil.png new file mode 100644 index 0000000..e2db07b Binary files /dev/null and b/app/assets/icons/pencil.png differ diff --git a/app/assets/icons/planet.png b/app/assets/icons/planet.png new file mode 100644 index 0000000..11e8462 Binary files /dev/null and b/app/assets/icons/planet.png differ diff --git a/app/assets/icons/send.png b/app/assets/icons/send.png new file mode 100644 index 0000000..990f42a Binary files /dev/null and b/app/assets/icons/send.png differ diff --git a/app/assets/icons/share_2.png b/app/assets/icons/share_2.png index 56e6e83..87b4584 100644 Binary files a/app/assets/icons/share_2.png and b/app/assets/icons/share_2.png differ diff --git a/app/assets/icons/water_ec.png b/app/assets/icons/water_ec.png new file mode 100644 index 0000000..c9d7e3f Binary files /dev/null and b/app/assets/icons/water_ec.png differ diff --git a/app/dart_test.yaml b/app/dart_test.yaml new file mode 100644 index 0000000..ab0d4f6 --- /dev/null +++ b/app/dart_test.yaml @@ -0,0 +1,3 @@ +tags: + # Figma 대조 렌더(test/design_match). DESIGN_MATCH_OUT 이 없으면 건너뛴다. + design: diff --git a/app/l10n.yaml b/app/l10n.yaml new file mode 100644 index 0000000..ee2115e --- /dev/null +++ b/app/l10n.yaml @@ -0,0 +1,8 @@ +# 다국어 리소스 — 정책 원장 B-76 · 요구사항 FR-I18N-01. +# 기준 언어(template)는 ko. 다른 언어 arb 에 없는 키는 ko 로 폴백한다. +# zh-TW 는 app_zh_TW.arb 를 추가하고 lib/core/i18n/app_locale.dart 의 목록에 한 줄 더하면 된다. +arb-dir: lib/l10n +template-arb-file: app_ko.arb +output-localization-file: app_localizations.dart +output-dir: lib/l10n +untranslated-messages-file: build/l10n_untranslated.json diff --git a/app/lib/app.dart b/app/lib/app.dart index db7f12a..42f79e7 100644 --- a/app/lib/app.dart +++ b/app/lib/app.dart @@ -1,31 +1,236 @@ +import 'dart:async'; + +import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:go_router/go_router.dart'; +import 'core/data/repositories.dart'; +import 'core/i18n/app_locale.dart'; +import 'core/ui/adaptive_planet_effects.dart'; +import 'core/push/push_messaging.dart'; import 'core/router/app_router.dart'; import 'core/theme/app_theme.dart'; import 'features/auth/auth_repository.dart'; +import 'features/translation/translation_comments.dart'; /// MaterialApp + 라우터 + 테마 주입. class FancakeApp extends StatefulWidget { - const FancakeApp({super.key, required this.auth}); + const FancakeApp({ + super.key, + required this.auth, + this.repositories, + this.locale, + }); final AuthRepository auth; + /// 저장소 묶음. 라우터와 푸시가 같은 것을 봐야 하므로 여기서 한 번만 만든다. + /// 테스트에서 목 모드를 고정할 때만 넘긴다. + final Repositories? repositories; + + /// 표시 언어. 테스트가 언어를 고정할 때만 넘긴다. + final LocaleController? locale; + @override State createState() => _FancakeAppState(); } -class _FancakeAppState extends State { - late final GoRouter _router = buildRouter(widget.auth); +class _FancakeAppState extends State with WidgetsBindingObserver { + late final Repositories _repos = widget.repositories ?? Repositories(); + late final GoRouter _router = buildRouter(widget.auth, repositories: _repos); + + /// 표시 언어 — 정책 원장 B-76. 서버가 붙어 있으면 `i18n/{locale}` 원격 문자열을 + /// 덮고(FR-I18N-01), 고른 언어를 `users/{uid}.locale` 에 남긴다(설계서 §2.1). + late final LocaleController _locale = + widget.locale ?? + (LocaleController( + remoteSource: _repos.live + ? (tag) => _repos.backend.db + .doc('i18n/$tag') + .snapshots() + .map((s) => s.data() ?? const {}) + : null, + onSaved: _repos.live ? _saveLocale : null, + )..restore()); + + /// 로그인 직후 — 이 기기에서 고른 언어가 없으면 프로필(`users/{uid}.locale`)을 따른다. + Future _adoptProfileLocale() async { + final uid = _repos.backend.uid; + if (uid == null) return; + try { + final snap = await _repos.backend.db.doc('users/$uid').get(); + await _locale.adoptProfile(snap.data()?['locale'] as String?); + } catch (e) { + debugPrint('[FancakeApp] profile locale: ${e.runtimeType}'); + } + } + + Future _saveLocale(String tag) async { + final uid = _repos.backend.uid; + if (uid == null) return; + await _repos.backend.db.doc('users/$uid').update({ + 'locale': tag, + 'updatedAt': FieldValue.serverTimestamp(), + }); + } + + /// 포그라운드 알림을 앱 안에서 띄울 통로. 라우터 밖이라 `context` 를 쓸 수 없다. + final _messengerKey = GlobalKey(); + + /// 푸시를 이미 켰는가. 로그인 알림은 여러 번 오므로 한 번만 켠다. + bool _pushStarted = false; + + StreamSubscription? _storeMessages; + + /// HUD 「LIVE」 입력 — 포그라운드인 동안 `users/{uid}.lastSeenAt` 을 서버 시각으로 올린다 + /// (해석 확인 요청 Q-GM-03 A안 · 서버가 최근 5분 운영값으로 센다). 간격 2분은 창보다 짧아 + /// 앱을 켜 둔 사람이 집계에서 빠지지 않게 한다. + Timer? _presence; + + void _startPresence() { + _presence?.cancel(); + if (!_repos.live || !widget.auth.isSignedIn) return; + _touchPresence(); + _presence = Timer.periodic( + const Duration(minutes: 2), + (_) => _touchPresence(), + ); + } + + Future _touchPresence() async { + final uid = _repos.backend.uid; + if (uid == null) return; + try { + await _repos.backend.db.doc('users/$uid').update({ + 'lastSeenAt': FieldValue.serverTimestamp(), + }); + } catch (e) { + debugPrint('[FancakeApp] presence: ${e.runtimeType}'); + } + } @override - Widget build(BuildContext context) => MaterialApp.router( - title: 'FANUZU', - debugShowCheckedModeBanner: false, - theme: buildAppTheme(), - // 코스믹 다크가 유일한 서피스다(결정 D-19). 라이트 테마를 두지 않는다. - themeMode: ThemeMode.dark, - darkTheme: buildAppTheme(), - routerConfig: _router, + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.resumed) { + _startPresence(); + } else if (state == AppLifecycleState.paused) { + _presence?.cancel(); + } + } + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + widget.auth.addListener(_onAuthChanged); + _onAuthChanged(); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + _presence?.cancel(); + widget.auth.removeListener(_onAuthChanged); + _repos.push?.dispose(); + if (widget.locale == null) _locale.dispose(); + unawaited(_storeMessages?.cancel()); + unawaited(_repos.store?.dispose()); + super.dispose(); + } + + /// 로그인 이후에 푸시를 켠다 — 토큰을 `users/{uid}` 에 붙이므로 UID 가 필요하다. + /// + /// **로그아웃하면 플래그를 내린다.** 내리지 않으면 같은 앱 실행 안에서 다시 + /// 로그인했을 때 토큰 저장과 토픽 구독이 통째로 건너뛰어지고, 그 계정은 알림을 + /// 하나도 받지 못한다. 화면은 정상으로 보여 원인이 드러나지 않는다. + void _onAuthChanged() { + if (!widget.auth.isSignedIn) { + _pushStarted = false; + _presence?.cancel(); + return; + } + if (_presence == null || !_presence!.isActive) _startPresence(); + // 결제 알림은 검증에 UID 가 필요해 로그인 뒤에 듣는다. 두 번 불러도 한 번만 붙는다. + final store = _repos.store; + if (store != null && _storeMessages == null) { + store.start(); + _storeMessages = store.messages.listen(_showMessage); + } + if (_pushStarted) return; + if (_repos.live) _adoptProfileLocale(); + final push = _repos.push; + if (push == null) return; // 목 모드. 플러그인을 부를 Firebase 가 없다. + _pushStarted = true; + _startPush(push); + } + + Future _startPush(PushMessaging push) async { + // 권한을 먼저 묻는다. 거부해도 나머지는 계속한다 — 토큰과 구독은 사용자가 + // 나중에 설정에서 허용했을 때 그대로 살아 있어야 한다. + await push.requestPermission(); + await push.syncToken(); + + // 팬덤 ID 는 PushMessaging 이 사용자 문서에서 직접 읽는다. + await push.syncTopics(await _repos.my.pushSettings()); + await push.listen(onForeground: _showInApp, onOpened: _openFromPush); + } + + void _showMessage(String message) => _messengerKey.currentState + ?..hideCurrentSnackBar() + ..showSnackBar( + SnackBar(behavior: SnackBarBehavior.floating, content: Text(message)), + ); + + /// 포그라운드 수신 — 시스템 알림이 뜨지 않으므로 앱이 직접 보여 준다. + /// 탭하면 알림을 눌렀을 때와 같은 곳으로 간다. + void _showInApp(PushNotice notice) { + if (!notice.hasText) return; + _messengerKey.currentState + ?..hideCurrentSnackBar() + ..showSnackBar( + SnackBar( + behavior: SnackBarBehavior.floating, + content: Text(notice.title.isEmpty ? notice.body : notice.title), + action: SnackBarAction( + label: (_messengerKey.currentContext ?? context).l10n.commonView, + onPressed: () => _openFromPush(notice), + ), + ), + ); + } + + void _openFromPush(PushNotice notice) => _router.go(notice.route); + + @override + Widget build(BuildContext context) => ListenableBuilder( + listenable: _locale, + builder: (context, _) => LocaleScope( + controller: _locale, + // 캠페인 상세 · 라운지 글 상세처럼 여러 경로로 열리는 화면이 번역 저장소를 찾는 곳. + child: TranslationScope( + repository: _repos.translation, + child: MaterialApp.router( + title: 'FANUZU', + builder: (context, child) => + AdaptivePlanetEffects(child: child ?? const SizedBox()), + debugShowCheckedModeBanner: false, + scaffoldMessengerKey: _messengerKey, + locale: _locale.locale, + supportedLocales: appLocales, + localizationsDelegates: [ + RemoteL10nDelegate(_locale.remote), + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + theme: buildAppTheme(), + // 코스믹 다크가 유일한 서피스다(결정 D-19). 라이트 테마를 두지 않는다. + themeMode: ThemeMode.dark, + darkTheme: buildAppTheme(), + routerConfig: _router, + ), + ), + ), ); } diff --git a/app/lib/core/data/backend.dart b/app/lib/core/data/backend.dart index 6808922..ad7d3bf 100644 --- a/app/lib/core/data/backend.dart +++ b/app/lib/core/data/backend.dart @@ -1,7 +1,9 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:cloud_functions/cloud_functions.dart'; import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter/foundation.dart'; +import '../i18n/app_locale.dart'; /// 서버 경계 — 앱에서 Firestore·Cloud Functions 로 나가는 유일한 통로. /// @@ -19,9 +21,11 @@ class Backend { FirebaseFirestore? firestore, FirebaseFunctions? functions, FirebaseAuth? auth, + FirebaseStorage? storage, }) : _firestore = firestore ?? FirebaseFirestore.instance, _functions = functions, - _auth = auth ?? FirebaseAuth.instance; + _auth = auth ?? FirebaseAuth.instance, + _storage = storage; static const String region = 'asia-northeast3'; @@ -35,15 +39,24 @@ class Backend { FirebaseFunctions get functions => _functions ??= FirebaseFunctions.instanceFor(region: region); + /// 사용자가 올리는 파일(프로필 사진 등)이 나가는 곳. Functions 와 같은 이유로 + /// 실제로 쓰기 전까지 만들지 않는다. + FirebaseStorage? _storage; + + FirebaseStorage get storage => _storage ??= FirebaseStorage.instance; + FirebaseFirestore get db => _firestore; String? get uid => _auth.currentUser?.uid; + /// 로그인 계정 이메일. 닉네임이 없을 때 화면이 부르는 이름의 재료다. + String? get email => _auth.currentUser?.email; + /// 로그인 필수 경로에서 UID 를 강제로 얻는다. String get requireUid { final value = uid; if (value == null) { - throw const BackendException('E-AUTH-03', '로그인이 필요합니다.'); + throw BackendException('E-AUTH-03', appL10n.authLoginRequired); } return value; } @@ -63,13 +76,34 @@ class Backend { if (data is Map) return Map.from(data); return const {}; } on FirebaseFunctionsException catch (error) { - throw BackendException( - _catalogCode(error), - error.message ?? '요청을 처리하지 못했습니다.', - ); + final code = _catalogCode(error); + throw BackendException(code, _message(code, error)); } } + /// 앱이 문구를 가진 카탈로그 코드는 표시 언어로 바꾼다. 나머지는 서버 사유 그대로. + static String _message(String code, FirebaseFunctionsException error) { + final details = error.details; + return switch (code) { + // 성운 창립자 탄생 전 탈퇴(Q-PN-03). + 'E-FDM-05' => appL10n.fandomFounderLeaveBlocked, + // 검증 활동(Quest 완료) 미달 — 서버가 `done` · `need` 를 싣는다(Campaign 개설자격 A안). + 'E-CMP-04' + when details is Map && + details['done'] is int && + details['need'] is int => + appL10n.campaignNeedsQuests( + '${details['done']}', + '${details['need']}', + '${(details['need'] as int) - (details['done'] as int)}', + ), + // 보상 없는 사용자 생성 Quest 하루 상한 — 서버가 `cap` 을 싣는다(Q-UQ-02 보완). + 'E-QST-04' when details is Map && details['cap'] is int => + appL10n.questFreeDailyCap('${details['cap']}'), + _ => error.message ?? appL10n.commonRequestFailed, + }; + } + static String _catalogCode(FirebaseFunctionsException error) { final details = error.details; if (details is Map && details['code'] is String) { @@ -97,6 +131,29 @@ class BackendException implements Exception { String toString() => 'BackendException($code): $message'; } +/// 다른 사용자에게도 보이는 이름 — 정책 원장 B-52 · G-07 +/// +/// 닉네임이 비었거나 **이메일 형태(`@` 포함)**면 [fallback] 을 쓴다. 예전에는 닉네임이 +/// 없을 때 이메일 앞부분을 이름으로 썼고, 그 값이 닉네임으로 저장돼 홈 기여자명에 +/// 노출됐다. UID 도 이름 자리에 넣지 않는다. +/// +/// 서버 · 규칙은 이름 없는 작성자를 「팬」([storedFanName])으로 저장한다 — 읽을 때는 그 값도 +/// 지금 언어의 「팬」으로 바꿔 보여 준다. +String publicName(Object? raw, [String? fallback]) { + final v = raw is String ? raw.trim() : ''; + return v.isEmpty || v.contains('@') || v == storedFanName + ? (fallback ?? appL10n.commonFan) + : v; +} + +/// 문서에 싣는 이름 없는 작성자 표기. 규칙 `publicNameOf` · 서버 `publicNameOf` 와 같은 값이라 +/// 언어와 무관하게 고정이다(번역된 「Fan」을 실으면 규칙이 사칭으로 거절한다). +const String storedFanName = '팬'; + +/// 내 이름을 문서에 실을 때 — [publicName] 과 같되 대체값은 언어 무관 [storedFanName]. +String storedPublicName(Object? nickname) => + publicName(nickname, storedFanName); + /// Firestore 문서에서 값을 꺼내는 도우미. /// /// 서버 스키마가 앞서 나가거나 필드가 아직 채워지지 않은 문서가 섞일 수 있어, diff --git a/app/lib/core/data/invite_code.dart b/app/lib/core/data/invite_code.dart new file mode 100644 index 0000000..11408ef --- /dev/null +++ b/app/lib/core/data/invite_code.dart @@ -0,0 +1,42 @@ +import 'backend.dart'; + +/// 내 초대 코드 — 공유 링크 `?ref=` 에 붙는다(원장 B-69 추천). +/// +/// 세션당 한 번 `fnInviteCodeGet` 을 부른다(없으면 서버가 만든다). 공유는 이 값을 기다리지 +/// 않는다 — 아직 없거나 실패했으면 `?ref=` 없이 나간다. 계정이 바뀌면 다시 받는다. +class InviteCode { + InviteCode._(); + + /// 받아 둔 코드. 없으면 null. + static String? current; + + static String? _uid; + static Future? _fetch; + + static Future ensure(Backend backend) { + final uid = backend.uid; + if (uid == null) return Future.value(); + if (uid != _uid) { + _uid = uid; + current = null; + _fetch = null; + } + return _fetch ??= _load(backend, uid); + } + + static Future _load(Backend backend, String uid) async { + try { + final code = (await backend.call('fnInviteCodeGet'))['code']; + if (_uid == uid && code is String && code.isNotEmpty) current = code; + } catch (_) { + if (_uid == uid) _fetch = null; // 다음 기회에 다시 받는다. + } + } + + /// 테스트가 회차마다 처음 상태로 돌린다. + static void reset() { + current = null; + _uid = null; + _fetch = null; + } +} diff --git a/app/lib/core/data/planet_visual_state.dart b/app/lib/core/data/planet_visual_state.dart new file mode 100644 index 0000000..3165e8b --- /dev/null +++ b/app/lib/core/data/planet_visual_state.dart @@ -0,0 +1,50 @@ +/// 화면은 이 서버 확정 상태를 읽기만 한다. 승급 조건/환산계수를 계산하지 않는다. +class PlanetVisualState { + const PlanetVisualState({ + this.stage, + this.level, + this.isEclipsed = false, + this.isHibernating = false, + this.isHeroTime = false, + this.heroEventIds, + this.heroEndsAt, + this.heroNicknames = const [], + this.heroClaimCount, + this.colorKey, + }); + final int? stage; + + /// 생성 때 고른 행성 컬러 키(C-50 · `fn-planet-create`). 성운이면 null 이다. + final String? colorKey; + final int? level; + final bool isEclipsed; + final bool isHibernating; + final bool isHeroTime; + + /// 남아 있는 HERO 이벤트 중 마지막 종료시각. 운영값을 추정하지 않는다. + final DateTime? heroEndsAt; + final List heroNicknames; + /// 활성 이벤트 수령 건수 합계. 여러 이벤트의 고유 참여자 수는 아니다. + final int? heroClaimCount; + + /// null은 아직 최초 서버 이벤트 조회가 끝나지 않은 상태다. + final Set? heroEventIds; + + factory PlanetVisualState.fromDocument(Map data) => + PlanetVisualState( + isHibernating: data['isHibernating'] == true, + colorKey: data['colorKey'] is String ? data['colorKey'] as String : null, + stage: switch (data['stage']) { + int n when n >= 0 && n <= 6 => n, + _ => null, + }, + level: switch (data['level']) { + int n when n >= 0 => n, + _ => null, + }, + ); +} + +typedef PlanetWatch = Stream Function(String fandomId); +Stream noPlanetWatch(String fandomId) => + const Stream.empty(); diff --git a/app/lib/core/data/repositories.dart b/app/lib/core/data/repositories.dart new file mode 100644 index 0000000..5567ed5 --- /dev/null +++ b/app/lib/core/data/repositories.dart @@ -0,0 +1,258 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'dart:async'; + +import '../config/feature_flags.dart'; +import '../monetization/store.dart'; +import '../firebase/firebase_boot.dart'; +import '../push/push_messaging.dart'; +import '../../features/galaxy/galaxy_data.dart'; +import '../../features/galaxy/galaxy_repository_firestore.dart'; +import '../../features/home/home_data.dart'; +import '../../features/home/home_repository_firestore.dart'; +import '../../features/inside/inside_data.dart'; +import '../../features/inside/inside_repository_firestore.dart'; +import '../../features/auth/auth.dart'; +import '../../features/auth/firebase_account_linker.dart'; +import '../../features/campaign/campaign_data.dart'; +import '../../features/campaign/campaign_repository_firestore.dart'; +import '../../features/eclipse/eclipse_data.dart'; +import '../../features/eclipse/eclipse_repository_firestore.dart'; +import '../../features/my/my_data.dart'; +import '../../features/notification/notification_data.dart'; +import '../../features/my/my_repository_firestore.dart'; +import '../../features/planet/planet_data.dart'; +import '../../features/quest/quest_data.dart'; +import '../../features/quest/quest_repository_firestore.dart'; +import '../../features/planet/planet_repository_firestore.dart'; +import '../../features/station/station_data.dart'; +import '../../features/station/station_repository_firestore.dart'; +import '../../features/translation/translation_data.dart'; +import 'backend.dart'; +import 'planet_visual_state.dart'; +import 'shared_stream.dart'; + +/// 저장소 선택 — 서버가 붙어 있으면 Firestore, 아니면 표본. +/// +/// **화면은 이 분기를 모른다.** 화면이 `FirebaseBoot.isLive` 를 읽기 시작하면 +/// 같은 조건문이 화면 수만큼 늘어나고, 설정 파일이 투입되는 날 전부 고쳐야 한다. +/// 갈림길을 여기 한 곳에 둔다. +/// +/// 목 모드에서 표본을 쓰는 이유는 별첨 2 2주차 확인 항목 「앱 설치·로그인·탭 이동 +/// 확인」 때문이다. 설정 파일이 없다고 화면이 빈 상태로 뜨면 확인 자체가 불가능하다. +class Repositories { + Repositories({Backend? backend, bool? live}) + : _live = live ?? FirebaseBoot.isLive, + _backend = backend; + + final bool _live; + Backend? _backend; + + /// 서버가 붙어 있는가. 저장소 밖에서 Firestore 를 직접 볼 곳(다국어 원격 문자열)이 쓴다. + bool get live => _live; + + Backend get backend => _backend ??= Backend(); + + HomeRepository get home => + _live ? FirestoreHomeRepository(backend) : const SampleHomeRepository(); + + GalaxyRepository get galaxy => _live + ? FirestoreGalaxyRepository(backend) + : const SampleGalaxyRepository(); + + /// INSIDE 와 STATION 은 소속 팬덤으로 범위가 갈리지만, 라우터는 그 값을 모른다. + /// 구현체가 사용자 문서에서 직접 읽으므로 여기서는 인자를 받지 않는다. + /// 화면을 한 번 만들면 그대로 들고 있어야 커서와 캐시가 유지된다. + late final InsideRepository inside = _live + ? FirestoreInsideRepository(backend) + : const SampleInsideRepository(); + + late final MyRepository my = _live + ? FirestoreMyRepository(backend) + : SampleMyRepository(); + + /// 퀘스트는 화면 3개(목록·상세·생성)가 같은 것을 들고 있어야 한다. 목록에서 + /// 상세로 들어갔다 돌아올 때 페이지 커서와 걸음 세션이 유지되어야 하므로 + /// 한 번 만들어 계속 쓴다. + late final QuestRepository quest = _live + ? FirestoreQuestRepository(backend) + : const SampleQuestRepository(); + + /// 캠페인(F5). 목록 · 상세 · 랭킹이 같은 것을 들고 있어야 검색어가 유지된다. + /// 예전에는 여기 없어서 서버가 붙어도 표본이 후원을 성공처럼 그렸다. + late final CampaignRepository campaign = _live + ? FirestoreCampaignRepository(backend) + : SampleCampaignRepository(); + + late final StationRepository station = _live + ? FirestoreStationRepository(backend) + : const SampleStationRepository(); + + /// 알림함(F0). 화면을 여는 곳이 HOME·GALAXY 둘이라 저장소를 한 번만 만들어 + /// 양쪽에 같은 것을 내려 준다. + late final NotificationRepository notifications = _live + ? FirestoreNotificationRepository(backend) + : SampleNotificationRepository(); + + /// 이클립스. 대상 팬덤은 호출 시점에 정해지므로(내 팬덤이거나 보고 있는 행성) + /// 저장소 자체는 하나만 둔다 — 화면마다 새로 만들면 구독이 화면 수만큼 늘어난다. + late final EclipseRepository eclipse = _live + ? FirestoreEclipseRepository(backend) + : SampleEclipseRepository(); + + /// 참여형 번역(FR-I18N-02). 번역 영역을 붙이는 화면이 같은 것을 쓴다. + late final TranslationRepository translation = _live + ? FirestoreTranslationRepository(backend) + : SampleTranslationRepository(); + + /// 계정 연결 경계 — 별첨 B v1.4 「하나의 UID 에 복수 소셜 Provider」. + /// 목 모드에서는 실제 UID 가 없어 표본이 화면 동선만 유지한다. + AccountLinker linker(List providers) => _live + ? FirebaseAccountLinker(providers: providers) + : SampleAccountLinker( + seed: const {AuthProviderKind.google: 'fan@gmail.com'}, + ); + + /// 푸시 수신 경계 — 정책 원장 B-77. 목 모드에서는 null 이다. + /// + /// 플러그인은 Firebase 가 초기화된 뒤에만 부를 수 있어서 여기서 갈라 둔다. + /// null 이면 화면은 권한 재요청 버튼을 그리지 않고 토글 동선만 유지한다. + late final PushMessaging? push = _live + ? PushMessaging(backend: backend) + : null; + + /// 인앱 결제 경계. 목 모드에서는 null 이고 화면은 「준비 중」을 띄운다. + /// + /// 결제 결과는 화면이 닫힌 뒤에도 도착하므로(OS 결제 창 · 승인 대기) 앱 전체에서 + /// 하나를 쓴다. 홈 아이템 시트와 퀘스트 광고 모달이 [Store.instance] 로 찾는다. + late final Store? store = _live ? (Store.instance = Store(backend)) : null; + + /// 원격 기능 플래그. 목 모드에서는 구독 없이 전부 꺼진 채로 둔다. + late final FeatureFlags flags = FeatureFlags( + source: _live ? _featureDocument() : null, + ); + + /// `config/features` 한 장을 구독한다. 로그인 전에도 읽어야 해서 + /// `firestore.rules` 가 `config/{id}` 를 `allow read: if true` 로 열어 두었다. + Stream> _featureDocument() => backend.db + .doc(FeatureFlags.documentPath) + .snapshots() + .map((snapshot) => snapshot.data() ?? const {}); + + final _planetStates = >{}; + + Stream watchPlanet(String fandomId) { + if (!_live || fandomId.isEmpty) return const Stream.empty(); + return (_planetStates[fandomId] ??= SharedStream( + () => _openPlanet(fandomId), + )).stream; + } + + Stream _openPlanet(String fandomId) => Stream.multi((out) { + PlanetVisualState? planet; + var isEclipsed = false; + var isHeroTime = false; + Set? heroEventIds; + Timer? heroExpiry; + DateTime? heroEndsAt; + List heroNicknames = const []; + int? heroClaimCount; + void emit() { + final current = planet; + if (current != null) + out.add( + PlanetVisualState( + stage: current.stage, + colorKey: current.colorKey, + isHibernating: current.isHibernating, + level: current.level, + isEclipsed: isEclipsed, + isHeroTime: isHeroTime, + heroEventIds: heroEventIds, + heroEndsAt: heroEndsAt, + heroNicknames: heroNicknames, + heroClaimCount: heroClaimCount, + ), + ); + } + + final document = backend.db + .doc('fandoms/$fandomId') + .snapshots(includeMetadataChanges: true) + .where((s) => !s.metadata.isFromCache && !s.metadata.hasPendingWrites) + .listen((s) { + planet = PlanetVisualState.fromDocument(s.data() ?? const {}); + emit(); + }, onError: out.addError); + final danger = eclipse.watch(fandomId: fandomId).listen((state) { + isEclipsed = + state != null && !state.defended && !state.isExpired(DateTime.now()); + emit(); + }, onError: out.addError); + // 기존 선물 이벤트만 읽는다. 구매 시도/로컬 캐시는 축제 확정이 아니다. + final gifts = backend.db + .collection('fandoms/$fandomId/heroDrops') + .where('endsAt', isGreaterThan: Timestamp.now()) + .snapshots(includeMetadataChanges: true) + .where((s) => !s.metadata.isFromCache && !s.metadata.hasPendingWrites) + .listen((snapshot) { + void refresh() { + heroExpiry?.cancel(); + final now = DateTime.now(); + final ends = []; + final ids = {}; + final names = {}; + var claims = 0; + var countsKnown = true; + for (final doc in snapshot.docs) { + final d = doc.data(); + final end = d['endsAt']; + final remaining = d['remainingPop']; + if (end is Timestamp && + end.toDate().isAfter(now) && + remaining is num && + remaining.isFinite && + remaining > 0) { + ends.add(end.toDate()); + ids.add(doc.id); + // 닉네임 없는 영웅(저장값 「팬」 · 빈 값)은 이름을 띄우지 않는다 — 서버가 「팬」을 + // 싣기 전과 같은 표시(M-7). + final nickname = publicName(d['heroNickname'], ''); + if (nickname.isNotEmpty) names.add(nickname); + final count = d['claimedCount']; + if (count is int && count >= 0) { + claims += count; + } else { + countsKnown = false; + } + } + } + heroEventIds = Set.unmodifiable(ids); + heroNicknames = List.unmodifiable(names); + heroClaimCount = ids.isNotEmpty && countsKnown ? claims : null; + ends.sort(); + heroEndsAt = ends.lastOrNull; + isHeroTime = ends.isNotEmpty; + emit(); + if (ends.isNotEmpty) { + heroExpiry = Timer(ends.first.difference(now), refresh); + } + } + + refresh(); + }, onError: out.addError); + out.onCancel = () async { + heroExpiry?.cancel(); + await gifts.cancel(); + await document.cancel(); + await danger.cancel(); + }; + }); + + /// 행성 상세는 팬덤 하나를 지정해야 하므로 호출부가 ID 를 준다. + PlanetRepository planet(String? fandomId) => + _live && fandomId != null && fandomId.isNotEmpty + ? FirestorePlanetRepository(backend, fandomId: fandomId) + // 목 모드에서도 누른 행성을 그려야 한다 — ID 를 버리면 어느 HUD 를 + // 눌러도 같은 상세가 열린다. + : SamplePlanetRepository(fandomId: fandomId); +} diff --git a/app/lib/core/data/server_clock.dart b/app/lib/core/data/server_clock.dart new file mode 100644 index 0000000..c1c95ba --- /dev/null +++ b/app/lib/core/data/server_clock.dart @@ -0,0 +1,40 @@ +import 'backend.dart'; + +/// 서버 시각 — 단말 시계와 서버 시계의 차이를 한 번 재어 앱 전체가 같이 쓴다. +/// +/// 타임어택 창(`quests.activeFrom`~`activeTo`)은 서버 시각이다. 단말 시계로 세면 시계를 +/// 돌린 단말에서 마감이 늘어나 보인다. 판정은 서버가 다시 하므로 이것은 표시용이다. +class ServerClock { + ServerClock._(); + + /// 서버 − 단말. 재지 못했으면 0 이다(단말 시계 그대로). + static Duration offset = Duration.zero; + + static Future? _synced; + + static DateTime now() => DateTime.now().add(offset); + + /// 세션당 한 번 `fnServerTime` 을 부른다. 실패하면 단말 시계로 두고 넘어간다. + static Future ensure(Backend backend) => _synced ??= _sync(backend); + + static Future _sync(Backend backend) async { + try { + final sent = DateTime.now(); + final reply = await backend.call('fnServerTime'); + final received = DateTime.now(); + final server = reply['now']; + if (server is! num) return; + // 왕복의 가운데를 서버가 답한 시각으로 본다. + final middle = sent.add(received.difference(sent) ~/ 2); + offset = DateTime.fromMillisecondsSinceEpoch(server.round()).difference(middle); + } catch (_) { + _synced = null; // 다음 기회에 다시 잰다. + } + } + + /// 테스트가 회차마다 처음 상태로 돌린다. + static void reset() { + offset = Duration.zero; + _synced = null; + } +} diff --git a/app/lib/core/firebase/firebase_boot.dart b/app/lib/core/firebase/firebase_boot.dart index a88c56f..8a6051d 100644 --- a/app/lib/core/firebase/firebase_boot.dart +++ b/app/lib/core/firebase/firebase_boot.dart @@ -41,8 +41,8 @@ abstract final class FirebaseBoot { /// 설정 파일이 없을 때 투입해야 하는 파일 목록. /// **리포 경로다 — 로그·문서 전용이고 화면에 내보내지 마라.** 화면에는 [missingLabels] 를 쓴다. static const List requiredConfigFiles = [ - 'app/android/app/google-services.json', - 'app/ios/Runner/GoogleService-Info.plist', + 'android/app/google-services.json', + 'ios/Runner/GoogleService-Info.plist', ]; /// 화면에 보여 줄 미투입 항목 이름. 내부 경로를 담지 않는다. diff --git a/app/lib/core/format.dart b/app/lib/core/format.dart new file mode 100644 index 0000000..124189e --- /dev/null +++ b/app/lib/core/format.dart @@ -0,0 +1,10 @@ +/// 천 단위 구분. `intl` 을 끌어오지 않고 끝낸다 — 이 앱에서 필요한 형식은 이것뿐이다. +String formatPop(int v) { + final s = v.abs().toString(); + final b = StringBuffer(v < 0 ? '-' : ''); + for (var i = 0; i < s.length; i++) { + if (i > 0 && (s.length - i) % 3 == 0) b.write(','); + b.write(s[i]); + } + return b.toString(); +} diff --git a/app/lib/core/i18n/app_locale.dart b/app/lib/core/i18n/app_locale.dart new file mode 100644 index 0000000..a7a6d09 --- /dev/null +++ b/app/lib/core/i18n/app_locale.dart @@ -0,0 +1,227 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../l10n/app_localizations.dart'; +import '../../l10n/remote_app_localizations.dart'; + +export '../../l10n/app_localizations.dart' show AppLocalizations; + +/// 1차 공식 지원 언어 — 정책 원장 **B-76** 확정값 `['ko','en','ja','zh-CN','es']`. +/// +/// 순서는 언어 설정 시트(명세 §2.95 `554:24126` 「소팅은 UI대로 유지」)와 같다. +/// **zh-TW 확장**: `lib/l10n/app_zh_TW.arb` 를 추가하고 여기에 `Locale('zh', 'TW')` 한 줄과 +/// [languageName] 한 줄을 더하면 끝난다. 그 전까지 zh-TW 단말은 [resolveAppLocale] 이 +/// zh-CN 으로 폴백한다. +const List appLocales = [ + Locale('ko'), + Locale('en'), + Locale('ja'), + Locale('zh', 'CN'), + Locale('es'), +]; + +/// 기준 언어(arb template). 미번역 문구는 여기로 떨어진다(FR-I18N-01). +const Locale fallbackLocale = Locale('ko'); + +/// 지원 5종에 없는 단말 언어의 기본값. 🟡 발주사 회신 전 제안값 — 한국어를 모르는 +/// 해외 단말에 한국어를 띄우지 않는다. +const Locale deviceFallbackLocale = Locale('en'); + +/// 언어 설정 시트 표기 — 명세 §2.95 원문(자기 언어로 적는다. 번역하지 않는다). +String languageName(Locale l) => switch (l.languageCode) { + 'ko' => '한국어', + 'en' => 'English', + 'ja' => '日本語', + 'zh' => '中文', + 'es' => 'Español', + _ => l.toLanguageTag(), +}; + +/// 단말 언어 목록 → 지원 5종 중 하나(B-76 「기동 시 단말 국가/언어 조회 → 지원 5종 내 매칭」). +/// +/// 언어 코드로 맞춘다. zh 는 지역·문자 체계(zh-TW · zh-Hant)와 무관하게 zh-CN 으로 +/// 간다(🟡 회신 전 제안값). 어느 것도 맞지 않으면 [deviceFallbackLocale](en). +Locale resolveAppLocale(Iterable device) { + for (final d in device) { + for (final l in appLocales) { + if (l.languageCode == d.languageCode) return l; + } + } + return deviceFallbackLocale; +} + +/// `users/{uid}.locale` · `i18n/{locale}` 문서 ID 에 쓰는 태그. zh-CN 은 하이픈이다. +String localeTag(Locale l) => l.toLanguageTag(); + +Locale? parseLocaleTag(String? tag) { + if (tag == null) return null; + for (final l in appLocales) { + if (localeTag(l) == tag) return l; + } + return null; +} + +/// 앱 표시 언어 · 원격 문자열 상태. +/// +/// - 고른 언어는 기기(SharedPreferences)에 남기고, 로그인 상태면 [onSaved] 로 +/// `users/{uid}.locale` 에도 쓴다(설계서 §2.1). +/// - 원격 문자열은 [remoteSource] 가 주는 `i18n/{locale}` 문서 필드(키 → 문자열)다. +/// 문서가 없거나 읽기가 실패하면 번들 문자열 그대로다. +class LocaleController extends ChangeNotifier { + LocaleController({ + Locale? initial, + this.remoteSource, + this.onSaved, + List? deviceLocales, + }) : _locale = + initial ?? + resolveAppLocale( + deviceLocales ?? + WidgetsBinding.instance.platformDispatcher.locales, + ) { + _watchRemote(); + } + + static const String prefsKey = 'app.locale'; + + /// 원격 문자열 구독. null 이면 번들만 쓴다(목 모드 · 테스트). + final Stream> Function(String tag)? remoteSource; + + /// 사용자가 언어를 바꿨을 때 서버 반영. 실패는 삼킨다 — 화면 언어는 이미 바뀌었다. + final Future Function(String tag)? onSaved; + + Locale _locale; + Locale get locale => _locale; + + Map _remote = const {}; + Map get remote => _remote; + + StreamSubscription>? _sub; + + /// 기기에 저장된 선택을 불러온다. 없으면 단말 언어 매칭값을 유지한다. + Future restore() async { + try { + final prefs = await SharedPreferences.getInstance(); + final saved = parseLocaleTag(prefs.getString(prefsKey)); + if (saved != null) _apply(saved); + } catch (e) { + debugPrint('[LocaleController] restore failed: ${e.runtimeType}'); + } + } + + Future select(Locale next) async { + if (!appLocales.contains(next)) return; + _apply(next); + final tag = localeTag(next); + try { + final prefs = await SharedPreferences.getInstance(); + await prefs.setString(prefsKey, tag); + } catch (e) { + debugPrint('[LocaleController] save failed: ${e.runtimeType}'); + } + try { + await onSaved?.call(tag); + } catch (e) { + debugPrint('[LocaleController] profile save failed: ${e.runtimeType}'); + } + } + + /// 로그인 직후 `users/{uid}.locale` 을 받는다. 이 기기에서 고른 언어가 있으면 그쪽이 이긴다. + /// 서버로 다시 쓰지 않는다(읽은 값 그대로). + Future adoptProfile(String? tag) async { + final profile = parseLocaleTag(tag); + if (profile == null) return; + try { + // 기기 저장값을 직접 본다 — [restore] 가 아직 안 끝났어도 판정이 맞다. + final prefs = await SharedPreferences.getInstance(); + if (prefs.getString(prefsKey) != null) return; + await prefs.setString(prefsKey, localeTag(profile)); + } catch (e) { + debugPrint('[LocaleController] profile adopt failed: ${e.runtimeType}'); + return; + } + _apply(profile); + } + + /// 언어를 바꾼다. 이전 언어의 원격 문자열은 버린다 — 새 언어 문서가 오기 전까지 번들만 쓴다. + void _apply(Locale next) { + if (next == _locale) return; + _locale = next; + _remote = const {}; + _watchRemote(); + notifyListeners(); + } + + void _watchRemote() { + unawaited(_sub?.cancel()); + _sub = null; + final source = remoteSource; + if (source == null) return; + _sub = source(localeTag(_locale)).listen( + (data) { + // 문자열 값만 받는다. 운영자가 숫자나 맵을 넣어도 화면이 깨지지 않게. + _remote = { + for (final e in data.entries) + if (e.value is String && (e.value as String).isNotEmpty) + e.key: e.value as String, + }; + notifyListeners(); + }, + onError: (Object e) => + debugPrint('[LocaleController] remote: ${e.runtimeType}'), + ); + } + + @override + void dispose() { + unawaited(_sub?.cancel()); + super.dispose(); + } +} + +/// 설정 화면처럼 트리 아래에서 언어를 바꿔야 하는 곳이 [LocaleController] 를 찾는 통로. +class LocaleScope extends InheritedNotifier { + const LocaleScope({ + super.key, + required LocaleController controller, + required super.child, + }) : super(notifier: controller); + + static LocaleController? maybeOf(BuildContext context) => + context.dependOnInheritedWidgetOfExactType()?.notifier; +} + +/// 번들 + 원격 문자열 대리자. 원격 맵이 바뀌면 다시 읽는다. +class RemoteL10nDelegate extends LocalizationsDelegate { + const RemoteL10nDelegate(this.remote); + + final Map remote; + + @override + bool isSupported(Locale locale) => + AppLocalizations.delegate.isSupported(locale); + + @override + Future load(Locale locale) => SynchronousFuture( + appL10n = RemoteAppLocalizations(lookupAppLocalizations(locale), remote), + ); + + @override + bool shouldReload(RemoteL10nDelegate old) => !identical(old.remote, remote); +} + +/// 기준 언어(ko) 문자열. `BuildContext` 가 없는 곳의 기본값으로 쓴다. +final AppLocalizations koL10n = lookupAppLocalizations(fallbackLocale); + +/// 지금 앱 언어 문자열 — `BuildContext` 가 없는 데이터층(저장소 오류 문구 · 상태 라벨)이 쓴다. +/// [RemoteL10nDelegate] 가 언어를 불러올 때마다 바뀐다. 대리자가 없는 트리(단독 위젯 테스트)는 ko. +/// 화면 코드는 이것 대신 [L10nContext.l10n] 을 쓴다 — 언어가 바뀌면 다시 그려진다. +AppLocalizations appL10n = koL10n; + +extension L10nContext on BuildContext { + /// 현재 언어 문자열. 대리자가 없는 트리(단독 위젯 테스트 등)는 ko 를 쓴다. + AppLocalizations get l10n => AppLocalizations.of(this) ?? koL10n; +} diff --git a/app/lib/core/monetization/rewarded_ad.dart b/app/lib/core/monetization/rewarded_ad.dart new file mode 100644 index 0000000..69768d9 --- /dev/null +++ b/app/lib/core/monetization/rewarded_ad.dart @@ -0,0 +1,93 @@ +import 'dart:async'; +import 'dart:io' show Platform; + +import 'package:google_mobile_ads/google_mobile_ads.dart'; + +import '../data/backend.dart'; +import '../i18n/app_locale.dart'; + +/// 광고 단위 ID — **실 값으로 바꾸는 곳은 여기 한 곳이다**(앱 ID 는 AndroidManifest · +/// Info.plist 의 `GADApplicationIdentifier`). +/// +/// 지금 값은 Google 이 공개한 **테스트 광고 단위**다(별첨 1 ⑥ 9주차 샌드박스). 테스트 +/// 단위는 실 계정 없이 항상 테스트 광고를 채우고 수익·무효 트래픽 판정이 없다. +abstract final class AdUnits { + static const androidRewarded = 'ca-app-pub-3940256099942544/5224354917'; + static const iosRewarded = 'ca-app-pub-3940256099942544/1712485313'; + + static String get rewarded => + Platform.isIOS ? iosRewarded : androidRewarded; +} + +/// 불러 둔 보상형 광고 한 편. +/// +/// 광고 네트워크 경계다. 미디에이션(AppLovin MAX 등)을 붙일 때 이 뒤만 바꾼다. +abstract interface class RewardedAdHandle { + /// 전면으로 띄우고 닫힐 때까지 기다린다. 끝까지 봐서 보상 콜백이 왔으면 노출 ID, + /// 중간에 닫았거나 띄우지 못했으면 null. 끝나면 스스로 정리한다. + Future show(); + + /// 띄우지 않고 버린다(사용자가 로딩 중 건너뛴 경우). + void dispose(); +} + +/// Google Mobile Ads 보상형 광고를 불러온다. +/// +/// 불러오지 못하면(채울 광고 없음 · 네트워크 · 시간 초과) `E-AD-01` 로 끊는다 — +/// 원장 B-03 「광고 로드 실패 시 기본 보상은 지급하고 추가 보상·횟수는 차감하지 않음」. +Future loadGoogleRewardedAd({ + Duration timeout = const Duration(seconds: 15), +}) async { + await (_initialized ??= MobileAds.instance.initialize()); + final loaded = Completer(); + await RewardedAd.load( + adUnitId: AdUnits.rewarded, + request: const AdRequest(), + rewardedAdLoadCallback: RewardedAdLoadCallback( + onAdLoaded: loaded.complete, + onAdFailedToLoad: (error) => loaded.completeError(error), + ), + ); + try { + return _GoogleRewardedAd(await loaded.future.timeout(timeout)); + } catch (_) { + // 시간 초과 뒤에 늦게 도착한 광고는 띄우지 않고 버린다. + unawaited(loaded.future.then((ad) => ad.dispose(), onError: (_) {})); + throw BackendException('E-AD-01', appL10n.adLoadFailed); + } +} + +Future? _initialized; + +class _GoogleRewardedAd implements RewardedAdHandle { + _GoogleRewardedAd(this._ad); + + final RewardedAd _ad; + + @override + Future show() async { + final closed = Completer(); + String? impression; + _ad.fullScreenContentCallback = FullScreenContentCallback( + onAdDismissedFullScreenContent: (_) => closed.complete(), + onAdFailedToShowFullScreenContent: (_, _) => closed.complete(), + ); + try { + await _ad.show( + onUserEarnedReward: (_, _) { + // 서버가 이 값으로 같은 노출의 이중 지급을 막는다(`adRewardLogs/{id}`). + impression = _ad.responseInfo?.responseId ?? + 'gma-${DateTime.now().microsecondsSinceEpoch}'; + }, + ); + await closed.future; + } finally { + // 띄우기가 던져도 네이티브 광고 객체를 남기지 않는다. + await _ad.dispose(); + } + return impression; + } + + @override + void dispose() => unawaited(_ad.dispose()); +} diff --git a/app/lib/core/monetization/store.dart b/app/lib/core/monetization/store.dart new file mode 100644 index 0000000..9aa634e --- /dev/null +++ b/app/lib/core/monetization/store.dart @@ -0,0 +1,145 @@ +import 'dart:async'; +import 'dart:io' show Platform; + +import 'package:in_app_purchase/in_app_purchase.dart'; +import 'package:in_app_purchase_android/in_app_purchase_android.dart'; + +import '../data/backend.dart'; +import '../i18n/app_locale.dart'; + +/// 인앱 결제 — 별첨 1 ⑥ 「영수증 서버검증 통과(위조 차단)」 · 정책 원장 B-02 · B-04 · B-84. +/// +/// **앱은 지급하지 않는다.** 스토어가 준 영수증(Android 구매 토큰 · iOS 영수증)을 +/// `fnIapVerify` 에 넘기고, 서버가 상품 ID 로 혜택을 정한다. 서버가 받아들인 뒤에만 +/// 스토어에 구매 완료를 알린다 — 검증 전에 완료하면 검증이 실패해도 결제가 확정되고, +/// 완료하지 않은 Android 구매는 3일 뒤 스토어가 자동 환불한다. +/// +/// 상품 ID 는 서버 상품 표(`firebase/functions/src/payment/receipt.ts`)와 Hero Gift +/// Catalog(`heroGiftProducts/{스토어 상품 ID}`)가 정한다. 스토어에 상품이 아직 없으면 +/// 조회가 비어 오고, 화면은 「스토어 상품 준비 중」으로 정직하게 멈춘다. +class Store { + Store(this._backend, {InAppPurchase? iap}) : _iap = iap ?? InAppPurchase.instance; + + /// 실 모드에서만 채워진다(`Repositories`). 목 모드와 테스트에서는 null 이라 + /// 화면이 「준비 중」을 띄운다. + static Store? instance; + + /// Focus 월 구독 — 서버 `SUBSCRIPTION_PRODUCTS` 와 같은 ID. + static const focusMonthly = 'focus.monthly'; + static const _subscriptions = {'focus.monthly', 'focus.yearly'}; + + static String get notReady => appL10n.storeNotReady; + + final Backend _backend; + final InAppPurchase _iap; + final _products = {}; + final _messages = StreamController.broadcast(); + StreamSubscription>? _purchases; + + /// 사용자에게 보여 줄 결제 결과 한 줄. 결제는 OS 창에서 끝나 화면 밖에서 도착한다. + Stream get messages => _messages.stream; + + /// 구매 알림을 듣기 시작한다. 로그인 뒤에 한 번 부른다. + /// + /// iOS 는 완료하지 않은 거래를 구독 즉시 다시 보낸다. Android 는 보내지 않으므로 + /// 복원으로 끌어온다 — 결제 대기(pending)였다가 승인된 구매와 검증에 실패했던 + /// 구매가 여기서 다시 서버로 간다. 서버는 거래 ID 로 멱등이라 여러 번 가도 한 번 지급이다. + void start() { + if (_purchases != null) return; + _purchases = _iap.purchaseStream.listen(_onPurchases, onError: (_) {}); + if (Platform.isAndroid) unawaited(_iap.restorePurchases().catchError((_) {})); + } + + Future dispose() async { + await _purchases?.cancel(); + _purchases = null; + } + + /// 스토어 상품 한 개. 스토어를 쓸 수 없거나 등록되지 않았으면 null. + Future product(String id) async { + final cached = _products[id]; + if (cached != null) return cached; + if (!await _iap.isAvailable()) return null; + final result = await _iap.queryProductDetails({id}); + if (result.productDetails.isEmpty) return null; + return _products[id] = result.productDetails.first; + } + + /// 결제 창을 띄운다. 결과는 [messages] 로 온다. + Future buy(String productId) async { + final details = await product(productId); + if (details == null) throw BackendException('E-PAY-02', notReady); + final param = PurchaseParam(productDetails: details); + if (_subscriptions.contains(productId)) { + await _iap.buyNonConsumable(purchaseParam: param); + } else { + // 자동 소비를 끈다 — 서버 검증 전에 소비하면 실패한 구매를 다시 보낼 수 없다. + await _iap.buyConsumable(purchaseParam: param, autoConsume: false); + } + } + + /// 구매 복원. Focus 구독을 다른 기기·재설치 뒤에 되살린다. + Future restore() => _iap.restorePurchases(); + + Future _onPurchases(List purchases) async { + for (final purchase in purchases) { + switch (purchase.status) { + case PurchaseStatus.pending: + _messages.add(appL10n.storePending); + case PurchaseStatus.canceled: + case PurchaseStatus.error: + if (purchase.status == PurchaseStatus.error) { + _messages.add(appL10n.storeFailed); + } + // 실패·취소 거래도 닫아야 iOS 큐에 남지 않는다. 결제가 확정되지 않은 건이다. + if (purchase.pendingCompletePurchase) { + await _iap.completePurchase(purchase); + } + case PurchaseStatus.purchased: + case PurchaseStatus.restored: + await _verify(purchase); + } + } + } + + Future _verify(PurchaseDetails purchase) async { + final details = _products[purchase.productID]; + try { + await _backend.call('fnIapVerify', { + 'platform': Platform.isIOS ? 'ios' : 'android', + 'receipt': purchase.verificationData.serverVerificationData, + 'productId': purchase.productID, + // iOS 앱 영수증은 여러 거래를 담는다 — 서버가 이 거래를 고른다. + if (purchase.purchaseID case final id?) 'transactionId': id, + // 표시용. 서버는 지급 판단에 쓰지 않는다. + if (details != null) ...{ + 'title': details.title, + 'priceAmount': details.rawPrice, + 'currency': details.currencyCode, + }, + }); + } on BackendException catch (error) { + if (purchase.status == PurchaseStatus.purchased) _messages.add(error.message); + if (error.code == 'E-PAY-01' && !Platform.isAndroid) { + // 재시도해도 같은 거부(판매 중지 · 다른 계정 · 소속 행성 없음). iOS 는 거래를 닫지 + // 않으면 실행마다 다시 온다. 환불은 Apple 경로(사용자 요청)로 간다. + if (purchase.pendingCompletePurchase) await _iap.completePurchase(purchase); + } + // 그 밖은 닫지 않는다 — 다음 실행에서 다시 보낸다. Android 는 E-PAY-01 도 확인하지 + // 않고 둔다: 3일 안에 확인되지 않은 구매는 스토어가 **자동 환불**한다. 다시 보내는 + // 복원 재전송은 조용하므로 알림은 처음 한 번뿐이다. + return; + } + if (Platform.isAndroid && !_subscriptions.contains(purchase.productID)) { + // 소비해야 같은 상품을 다시 살 수 있다. 소비가 곧 확인(acknowledge)이다. + await _iap + .getPlatformAddition() + .consumePurchase(purchase); + } else if (purchase.pendingCompletePurchase) { + await _iap.completePurchase(purchase); + } + if (purchase.status == PurchaseStatus.purchased) { + _messages.add(appL10n.storePurchased); + } + } +} diff --git a/app/lib/core/push/push_messaging.dart b/app/lib/core/push/push_messaging.dart new file mode 100644 index 0000000..1969a34 --- /dev/null +++ b/app/lib/core/push/push_messaging.dart @@ -0,0 +1,241 @@ +import 'dart:async'; + +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_messaging/firebase_messaging.dart' show FirebaseMessaging; +import 'package:flutter/foundation.dart'; + +import '../data/backend.dart'; +import '../../features/my/my_data.dart'; +import 'push_transport.dart'; + +/// FCM 수신 — 정책 원장 B-77 「Push 알림」 5 개 구분의 앱 쪽 절반. +/// +/// **구분마다 토픽을 나눈 이유가 여기 있다.** 서버는 토픽으로만 보내므로 수신자를 +/// 고를 수 없다. 사용자가 「AI Briefing 만 끄기」를 고르면 앱이 그 토픽을 해지하고, +/// 그래서 **OFF 구분의 수신이 0 건**이 된다(B-77 검증 기준). 앱이 받아서 버리는 +/// 방식이면 기기에 알림이 먼저 떠 버려 정책을 지킬 수 없다. +/// +/// Marketing 은 기본값이 OFF 라 명시 동의 전에는 구독하지 않는다. +/// +/// **iOS 는 코드 경로만이다.** APNs 키가 Apple Developer 계정 확정 대기(별첨 A-1)라 +/// 토큰이 발급되지 않는다. 키가 들어오는 날 이 파일을 고치지 않아도 되도록 +/// 지금 경로를 맞춰 둔다. +class PushMessaging { + PushMessaging({required Backend backend, PushTransport? transport}) + : _backend = backend, + _transport = transport ?? FirebaseMessagingTransport(); + + final Backend _backend; + final PushTransport _transport; + + StreamSubscription? _foreground; + StreamSubscription? _opened; + StreamSubscription? _tokenRefresh; + + /// 소속 팬덤. 토픽 이름의 일부라 매번 사용자 문서를 읽지 않도록 기억한다. + String? _fandomId; + + /// 알림 권한 상태. 화면이 「거부됨」 안내와 재요청 버튼을 그릴 근거다. + AuthorizationStatus _status = AuthorizationStatus.notDetermined; + AuthorizationStatus get status => _status; + + bool get isAuthorized => _status.isAllowed; + + /// 알림 권한 요청. + /// + /// Android 13+ 의 `POST_NOTIFICATIONS` 런타임 권한도 이 호출이 띄운다 — + /// 플러그인이 매니페스트에 권한을 선언하고 `requestPermission` 이 시스템 + /// 다이얼로그로 이어진다. 별도 권한 플러그인을 넣지 않은 이유가 이것이다. + Future requestPermission() async { + _status = await _transport.requestPermission(); + return _status; + } + + /// 현재 권한 상태만 읽는다. 다이얼로그를 띄우지 않으므로 화면 진입 시 쓴다. + Future refreshStatus() async { + _status = await _transport.currentStatus(); + return _status; + } + + /// 단말 토큰을 `users/{uid}.fcmTokens` 에 더한다. + /// + /// 배열로 두는 이유는 한 계정이 여러 단말을 쓰기 때문이다. 단일 필드면 새 단말 + /// 로그인이 이전 단말의 토큰을 지우고, 그 단말은 조용히 알림을 못 받는다. + /// 토큰 재발급도 같은 경로로 흘러 들어온다([listen] 의 `onTokenRefresh`). + /// + /// iOS 는 APNs 키 전까지 토큰이 null 이다 — 예외가 아니라 정상이다. + Future syncToken() async { + final token = await _transport.getToken(); + if (token == null || token.isEmpty) return null; + await _saveToken(token); + return token; + } + + Future _saveToken(String token) async { + final uid = _backend.uid; + if (uid == null) return; + await _backend.db.doc('users/$uid').set({ + 'fcmTokens': FieldValue.arrayUnion([token]), + 'updatedAt': FieldValue.serverTimestamp(), + }, SetOptions(merge: true)); + } + + /// 소속 팬덤 ID. 한 번 읽어 두고 다시 읽지 않는다. + Future _resolveFandomId() async { + final cached = _fandomId; + if (cached != null) return cached; + final uid = _backend.uid; + if (uid == null) return ''; + final user = await _backend.db.doc('users/$uid').get(); + final id = (user.data() ?? const {}).strOr('fandomId'); + if (id.isNotEmpty) _fandomId = id; + return id; + } + + /// 설정대로 토픽을 맞춘다. + /// + /// **구독 상태를 앱이 기억하지 않는다.** 켜진 구분은 구독을, 꺼진 구분은 해지를 + /// 조건 없이 부른다. FCM 의 두 호출은 멱등이라 이미 그 상태여도 문제가 없고, + /// 반대로 앱이 기억한 상태를 믿으면 재설치·토큰 재발급·다른 기기 로그인처럼 + /// 앱이 모르는 사이에 실제 구독이 달라진 경우를 되돌릴 방법이 없다. + /// + /// 팬덤 ID 는 사용자 문서에서 스스로 읽는다. 호출자가 넘기게 하면 설정 화면이 + /// 그 값을 알아야 하고, 화면이 알 이유가 없다. + Future syncTopics(Map prefs) async { + final fandomId = await _resolveFandomId(); + if (fandomId.isEmpty) return; + final plan = topicPlan(fandomId, prefs); + for (final topic in plan.subscribe) { + await _transport.subscribeToTopic(topic); + } + for (final topic in plan.unsubscribe) { + await _transport.unsubscribeFromTopic(topic); + } + } + + /// 수신 구독을 연다. + /// + /// 포그라운드에서는 시스템 알림이 뜨지 않으므로(Android 기본 동작) 앱이 직접 + /// 표시해야 한다. 탭은 두 경로로 들어온다 — 앱이 살아 있을 때의 + /// `onMessageOpenedApp` 과 종료 상태에서 알림으로 켠 `getInitialMessage`. + /// 후자를 빼면 「알림을 눌러 앱이 열렸는데 홈에 그냥 서 있다」가 된다. + Future listen({ + required void Function(PushNotice notice) onForeground, + required void Function(PushNotice notice) onOpened, + }) async { + _foreground = FirebaseMessaging.onMessage + .listen((m) => onForeground(PushNotice.of(m))); + _opened = FirebaseMessaging.onMessageOpenedApp + .listen((m) => onOpened(PushNotice.of(m))); + _tokenRefresh = _transport.onTokenRefresh.listen(_saveToken); + + final initial = await _transport.getInitialMessage(); + if (initial != null) onOpened(PushNotice.of(initial)); + } + + /// 로그아웃 정리 — 팬덤 토픽을 전부 해지하고 이 기기 토큰을 지운다. + /// + /// **로그아웃보다 먼저 불러야 한다.** 보안 규칙이 `users/{uid}` 쓰기를 + /// `isSelf(uid)` 로 막으므로 인증이 끊긴 뒤에는 토큰을 지울 수 없다. 그대로 두면 + /// 다음 사용자가 그 기기를 쓸 때 이전 계정의 알림이 계속 내려간다. + /// + /// 토픽 해지를 먼저 하는 이유는 실패 순서다. 토큰 삭제가 실패해도 구독은 끊겨 + /// 알림이 멈춘다. 반대로 두면 토큰만 지워진 채 토픽 알림이 계속 온다. + Future clearOnSignOut() async { + final fandomId = await _resolveFandomId(); + if (fandomId.isNotEmpty) { + for (final topic in PushTopic.values) { + await _transport.unsubscribeFromTopic(topic.topicFor(fandomId)); + } + } + final uid = _backend.uid; + final token = await _transport.getToken(); + if (uid != null && token != null && token.isNotEmpty) { + await _backend.db.doc('users/$uid').set({ + 'fcmTokens': FieldValue.arrayRemove([token]), + 'updatedAt': FieldValue.serverTimestamp(), + }, SetOptions(merge: true)); + } + // 다음 로그인은 다른 계정일 수 있다. 팬덤을 들고 있으면 그 계정의 토픽에 + // 구독해 버린다. + _fandomId = null; + } + + Future dispose() async { + await _foreground?.cancel(); + await _opened?.cancel(); + await _tokenRefresh?.cancel(); + } +} + +/// 알림을 실제로 받을 수 있는 상태인가. +/// +/// `provisional` 을 포함하는 이유는 iOS 임시 승인이다. 사용자가 아직 명시적으로 +/// 허용하지 않았어도 조용한 알림은 전달되므로, 「꺼져 있습니다」 안내를 띄우면 +/// 사실과 다르다. +extension PushAuthorization on AuthorizationStatus { + bool get isAllowed => + this == AuthorizationStatus.authorized || + this == AuthorizationStatus.provisional; +} + +/// 토픽 계획 — 무엇을 구독하고 무엇을 해지할지. +@immutable +class TopicPlan { + const TopicPlan({required this.subscribe, required this.unsubscribe}); + final Set subscribe; + final Set unsubscribe; +} + +/// 설정에서 계획을 낸다. 모든 구분이 둘 중 한쪽에 들어간다. +/// +/// 플러그인을 부르지 않는 순수 함수로 떼어 둔 이유는 검증이다. 「Marketing 을 끈 +/// 상태로는 그 토픽을 구독하지 않는다」가 정책 검증 기준이고, 그 판정을 실제 FCM +/// 없이 확인할 수 있어야 한다. +TopicPlan topicPlan(String fandomId, Map prefs) { + final subscribe = {}; + final unsubscribe = {}; + for (final topic in PushTopic.values) { + // 서비스 필수는 끌 수 없으므로 설정값을 보지 않는다. + final on = topic.locked || (prefs[topic] ?? topic.defaultOn); + (on ? subscribe : unsubscribe).add(topic.topicFor(fandomId)); + } + return TopicPlan(subscribe: subscribe, unsubscribe: unsubscribe); +} + +/// 받은 알림 한 건. 화면이 쓰는 형태로만 남긴다. +@immutable +class PushNotice { + const PushNotice({ + required this.title, + required this.body, + required this.kind, + this.fandomId, + }); + + factory PushNotice.of(RemoteMessage message) => PushNotice( + title: message.notification?.title ?? '', + body: message.notification?.body ?? '', + kind: message.data['kind']?.toString() ?? '', + fandomId: message.data['fandomId']?.toString(), + ); + + final String title; + final String body; + + /// 서버가 실은 분기 키 — `briefing` · `eclipse`. + final String kind; + final String? fandomId; + + /// 탭했을 때 열 경로. + /// + /// 브리핑 상세 화면은 아직 라우트가 없어 홈의 브리핑 블록으로 보낸다. 이클립스는 + /// 갤럭시에서 행성으로 들어가는 동선이 이미 있다. + String get route => switch (kind) { + 'eclipse' => '/galaxy', + _ => '/home', + }; + + /// 표시할 내용이 있는가. 데이터 전용 메시지는 화면에 띄우지 않는다. + bool get hasText => title.isNotEmpty || body.isNotEmpty; +} diff --git a/app/lib/core/push/push_transport.dart b/app/lib/core/push/push_transport.dart new file mode 100644 index 0000000..05b96e9 --- /dev/null +++ b/app/lib/core/push/push_transport.dart @@ -0,0 +1,67 @@ +import 'package:firebase_messaging/firebase_messaging.dart'; + +export 'package:firebase_messaging/firebase_messaging.dart' + show AuthorizationStatus, RemoteMessage; + +/// FCM 플러그인 경계. +/// +/// **왜 인터페이스를 하나 두는가** — `FirebaseMessaging` 은 비공개 생성자를 가진 +/// 구체 클래스라 테스트에서 대신 세울 수 없다. 「마케팅 토글을 끄면 그 토픽을 +/// 해지하는가」는 정책 검증 기준(B-77 「OFF 카테고리 발송 0건」)이고, 실제 FCM 없이 +/// 확인할 수 있어야 한다. 그래서 [PushMessaging] 이 쓰는 호출만 여기로 좁혔다. +/// +/// 정적 스트림(`FirebaseMessaging.onMessage`·`onMessageOpenedApp`)은 여기 넣지 +/// 않았다. 구독 판정과 무관하고, 넣으면 이 경계가 플러그인 전체를 베끼게 된다. +abstract interface class PushTransport { + /// 알림 권한 요청. 시스템 다이얼로그를 띄운다. + Future requestPermission(); + + /// 현재 권한 상태만 읽는다. 다이얼로그를 띄우지 않는다. + Future currentStatus(); + + /// 이 단말 토큰. iOS 는 APNs 키 전까지 null 이다. + Future getToken(); + + /// 토큰 재발급 알림. + Stream get onTokenRefresh; + + Future subscribeToTopic(String topic); + + Future unsubscribeFromTopic(String topic); + + /// 종료 상태에서 알림을 눌러 앱이 켜진 경우의 첫 메시지. + Future getInitialMessage(); +} + +/// 실제 플러그인에 붙는 구현. 로직이 없어야 한다 — 판정은 [PushMessaging] 이 한다. +class FirebaseMessagingTransport implements PushTransport { + FirebaseMessagingTransport([FirebaseMessaging? messaging]) + : _messaging = messaging ?? FirebaseMessaging.instance; + + final FirebaseMessaging _messaging; + + @override + Future requestPermission() async => + (await _messaging.requestPermission()).authorizationStatus; + + @override + Future currentStatus() async => + (await _messaging.getNotificationSettings()).authorizationStatus; + + @override + Future getToken() => _messaging.getToken(); + + @override + Stream get onTokenRefresh => _messaging.onTokenRefresh; + + @override + Future subscribeToTopic(String topic) => + _messaging.subscribeToTopic(topic); + + @override + Future unsubscribeFromTopic(String topic) => + _messaging.unsubscribeFromTopic(topic); + + @override + Future getInitialMessage() => _messaging.getInitialMessage(); +} diff --git a/app/lib/core/router/app_router.dart b/app/lib/core/router/app_router.dart index 21aa657..ecbc297 100644 --- a/app/lib/core/router/app_router.dart +++ b/app/lib/core/router/app_router.dart @@ -2,11 +2,13 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import '../config/feature_flags.dart'; +import '../data/repositories.dart'; import '../theme/tokens.dart'; import '../ui/app_icons.dart'; import '../../features/auth/auth_repository.dart'; import '../../features/auth/login_screen.dart'; -import '../../features/galaxy/galaxy_screen.dart'; +import '../../features/auth/signup_screens.dart'; +import '../../features/galaxy/galaxy_home_screen.dart'; import '../../features/home/home_screen.dart'; import '../../features/inside/inside_screen.dart'; import '../../features/my/my_screen.dart'; @@ -35,7 +37,7 @@ class AppTab { final bool Function(FeatureFlags flags)? gatedBy; } -/// 계약 고정 5탭. +/// 계약 고정 5탭 — 화면 대조 원장 F0 · Figma `350:215853` 「Bottom Navigation」. /// /// 근거 2개가 서로 일치한다: /// - 견적서 v2.1 「5탭 네비게이션」 @@ -76,20 +78,28 @@ List visibleTabs(FeatureFlags flags) => [ ]; const String loginPath = '/login'; +const String signupCompletePath = '/welcome'; /// 5탭 셸 라우트 + 온보딩(로그인) 라우트. /// /// 온보딩 나머지 화면(S-A-INTRO · S-A-TERMS · S-A-PROFILE · S-A-ARTIST · /// S-A-PLANET-NEW · S-A-PERMS)은 3주차 범위라 아직 라우트가 없다. -GoRouter buildRouter(AuthRepository auth, {FeatureFlags? flags}) { - final featureFlags = flags ?? FeatureFlags(); +GoRouter buildRouter(AuthRepository auth, {Repositories? repositories}) { + // 저장소 선택은 앱이 뜰 때 한 번만 한다. 탭을 옮길 때마다 다시 만들면 + // Firestore 커서 같은 화면 상태가 매번 초기화된다. + final repos = repositories ?? Repositories(); + // 한 번만 만든다. 라우트 빌더 안에서 만들면 탭을 옮길 때마다 연결 상태가 초기화된다. + final linker = repos.linker(auth.providers); return GoRouter( initialLocation: appTabs.first.path, refreshListenable: auth, redirect: (context, state) { - final atLogin = state.matchedLocation == loginPath; - if (!auth.isSignedIn) return atLogin ? null : loginPath; - if (atLogin) return appTabs.first.path; + final at = state.matchedLocation; + if (!auth.isSignedIn) return at == loginPath ? null : loginPath; + if (auth.justSignedUp) + return at == signupCompletePath ? null : signupCompletePath; + if (at == loginPath || at == signupCompletePath) + return appTabs.first.path; return null; }, routes: [ @@ -97,14 +107,29 @@ GoRouter buildRouter(AuthRepository auth, {FeatureFlags? flags}) { path: loginPath, builder: (_, _) => LoginScreen(auth: auth), ), + GoRoute( + path: signupCompletePath, + builder: (_, _) => SignupCompleteScreen(onStart: auth.finishSignup), + ), StatefulShellRoute.indexedStack( - builder: (_, _, shell) => AppShell(shell: shell, flags: featureFlags), + builder: (_, _, shell) => AppShell(shell: shell, flags: repos.flags), branches: [ StatefulShellBranch( routes: [ GoRoute( path: appTabs[0].path, - builder: (_, _) => const HomeScreen(), + builder: (_, _) => HomeScreen( + watchPlanet: repos.watchPlanet, + galaxyRepository: repos.galaxy, + planetRepository: repos.planet, + repository: repos.home, + // 홈의 「오늘의 퀘스트」에서 F4 로 들어간다. 저장소 선택은 + // repositories.dart 한 곳이므로 화면이 표본을 직접 만들지 않는다. + questRepository: repos.quest, + campaignRepository: repos.campaign, + notifications: repos.notifications, + eclipse: repos.eclipse, + ), ), ], ), @@ -112,7 +137,11 @@ GoRouter buildRouter(AuthRepository auth, {FeatureFlags? flags}) { routes: [ GoRoute( path: appTabs[1].path, - builder: (_, _) => const InsideScreen(), + builder: (_, _) => InsideScreen( + repository: repos.inside, + translation: repos.translation, + notifications: repos.notifications, + ), ), ], ), @@ -120,7 +149,15 @@ GoRouter buildRouter(AuthRepository auth, {FeatureFlags? flags}) { routes: [ GoRoute( path: appTabs[2].path, - builder: (_, _) => const GalaxyScreen(), + // 탭 루트는 시안(354:220228)의 「내 행성」 그리드. 궤도 맵은 그 안에서 연다. + builder: (_, _) => GalaxyHomeScreen( + home: repos.home, + watchPlanet: repos.watchPlanet, + repository: repos.galaxy, + planetRepository: repos.planet, + notifications: repos.notifications, + eclipse: repos.eclipse, + ), ), ], ), @@ -128,7 +165,7 @@ GoRouter buildRouter(AuthRepository auth, {FeatureFlags? flags}) { routes: [ GoRoute( path: appTabs[3].path, - builder: (_, _) => const StationScreen(), + builder: (_, _) => StationScreen(repository: repos.station), ), ], ), @@ -136,7 +173,13 @@ GoRouter buildRouter(AuthRepository auth, {FeatureFlags? flags}) { routes: [ GoRoute( path: appTabs[4].path, - builder: (_, _) => const MyScreen(), + builder: (_, _) => MyScreen( + repository: repos.my, + linker: linker, + push: repos.push, + auth: auth, + campaigns: repos.campaign, + ), ), ], ), diff --git a/app/lib/core/theme/app_theme.dart b/app/lib/core/theme/app_theme.dart index 8f0c67d..af38961 100644 --- a/app/lib/core/theme/app_theme.dart +++ b/app/lib/core/theme/app_theme.dart @@ -92,6 +92,16 @@ ThemeData buildAppTheme() { ), ), dividerTheme: const DividerThemeData(color: AppColors.cosmicOutline, thickness: 1), + // 시안 행성 상세(373:441117) 탭 — 활성 청록 글자·밑줄, 비활성 #bbbbbb, 트랙 #1a1a1a. + tabBarTheme: TabBarThemeData( + labelColor: AppColors.cosmicTeal, + unselectedLabelColor: const Color(0xFFBBBBBB), + indicatorColor: AppColors.cosmicTeal, + indicatorSize: TabBarIndicatorSize.tab, + dividerColor: const Color(0xFF1A1A1A), + labelStyle: _styleNoColor(AppType.labelL), + unselectedLabelStyle: _styleNoColor(AppType.labelL), + ), // ── 아래는 테마를 주지 않으면 Material 3 기본값(보라 시드)으로 칠해지는 것들이다. // 코스믹 다크 위에서 눈에 띄게 어긋나므로 전부 토큰으로 고정한다. diff --git a/app/lib/core/ui/adaptive_planet_effects.dart b/app/lib/core/ui/adaptive_planet_effects.dart new file mode 100644 index 0000000..57d38d2 --- /dev/null +++ b/app/lib/core/ui/adaptive_planet_effects.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; + +/// 0=원본, 1=입자/광량 축소, 2=Blur·텍스처까지 축소. 상태/결과는 그대로 둔다. +class PlanetEffectsQuality extends InheritedWidget { + const PlanetEffectsQuality({ + super.key, + required this.level, + required super.child, + }); + final int level; + static int of(BuildContext context) => + context + .dependOnInheritedWidgetOfExactType() + ?.level ?? + 0; + @override + bool updateShouldNotify(PlanetEffectsQuality old) => old.level != level; +} + +class AdaptivePlanetEffects extends StatefulWidget { + const AdaptivePlanetEffects({super.key, required this.child}); + final Widget child; + @override + State createState() => _AdaptivePlanetEffectsState(); +} + +class _AdaptivePlanetEffectsState extends State + with WidgetsBindingObserver { + int _level = 0; + int _warmup = 60; + int _frames = 0; + int _slow = 0; + double _budgetMicros = 1000000 / 60; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + SchedulerBinding.instance.addTimingsCallback(_measure); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + final hz = View.of(context).display.refreshRate; + _budgetMicros = 1000000 / (hz > 0 ? hz.clamp(30, 120) : 60); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.resumed) { + _warmup = 60; + _frames = _slow = 0; + } + } + + void _measure(List frames) { + if (!mounted || _level == 2) return; + final lifecycle = WidgetsBinding.instance.lifecycleState; + if (lifecycle != null && lifecycle != AppLifecycleState.resumed) return; + for (final frame in frames) { + if (_warmup > 0) { + _warmup--; + continue; + } + _frames++; + if (frame.buildDuration.inMicroseconds > _budgetMicros || + frame.rasterDuration.inMicroseconds > _budgetMicros) + _slow++; + if (_frames < 120) continue; + final downgrade = _slow >= 30; + _frames = _slow = 0; + if (downgrade) { + setState(() => _level++); + // ponytail: 한 앱 실행에서는 하향만 한다. 복구 시 품질 진동을 피하며, + // 실기기 측정으로 안정 구간을 확보하면 장시간 회복 히스테리시스를 추가한다. + if (_level == 2) break; + } + } + } + + @override + void dispose() { + SchedulerBinding.instance.removeTimingsCallback(_measure); + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + Widget build(BuildContext context) => + PlanetEffectsQuality(level: _level, child: widget.child); +} diff --git a/app/lib/core/ui/app_icons.dart b/app/lib/core/ui/app_icons.dart index 6be7832..b58a38a 100644 --- a/app/lib/core/ui/app_icons.dart +++ b/app/lib/core/ui/app_icons.dart @@ -19,4 +19,20 @@ abstract final class AppIcons { static const x = AssetImage('assets/icons/x.png'); static const search = AssetImage('assets/icons/search.png'); static const share = AssetImage('assets/icons/share_2.png'); + // INSIDE 시안에서 쓰던 아이콘(그룹 E) — 다른 화면도 같은 Iconset 이라 여기로 올렸다. + // heart · message_circle · eye · bell · corner_down_right 는 재추출본(2026-09-27, reextract/map.json). + static const heart = AssetImage('assets/icons/heart.png'); + static const messageCircle = AssetImage('assets/icons/message_circle.png'); + static const eye = AssetImage('assets/icons/eye.png'); + static const bell = AssetImage('assets/icons/bell.png'); + static const clock = AssetImage('assets/icons/clock_8.png'); + static const bolt = AssetImage('assets/icons/bolt.png'); + static const cornerDownRight = AssetImage( + 'assets/icons/corner_down_right.png', + ); + static const globe = AssetImage('assets/icons/globe.png'); + static const pencil = AssetImage('assets/icons/pencil.png'); + static const send = AssetImage('assets/icons/send.png'); + static const camera = AssetImage('assets/icons/camera.png'); + static const image = AssetImage('assets/icons/image.png'); } diff --git a/app/lib/core/ui/app_radio.dart b/app/lib/core/ui/app_radio.dart new file mode 100644 index 0000000..7b4eb28 --- /dev/null +++ b/app/lib/core/ui/app_radio.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; + +/// 사본 `Radio` 컴포넌트(20) — 미선택은 #2A2A2C 가는 테두리, 선택은 #D1D1D2 원 안의 어두운 점. +/// +/// Material [Radio] 는 2px 링을 그려 사본보다 굵고 밝다. 신고 사유 · 환불 사유가 같이 쓴다. +class AppRadio extends StatelessWidget { + const AppRadio({super.key, required this.selected}); + + final bool selected; + + @override + Widget build(BuildContext context) => Semantics( + checked: selected, + inMutuallyExclusiveGroup: true, + child: Container( + width: 20, + height: 20, + alignment: Alignment.center, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: selected ? const Color(0xFFD1D1D2) : null, + border: selected + ? null + : Border.all(color: const Color(0xFF2A2A2C), width: 1.5), + ), + child: selected + ? const DecoratedBox( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Color(0xFF141414), + ), + child: SizedBox.square(dimension: 8), + ) + : null, + ), + ); +} diff --git a/app/lib/core/ui/bolt_badge.dart b/app/lib/core/ui/bolt_badge.dart new file mode 100644 index 0000000..e71b87a --- /dev/null +++ b/app/lib/core/ui/bolt_badge.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; + +import '../theme/tokens.dart'; + +/// 시안의 CONTRIBUTION · 누적 기여 POP 아이콘 — 파란 그라디언트 원에 흰 번개. +/// 다른 3D 아이콘과 달리 Figma 에서 벡터라 이미지로 내보낼 수 없어 위젯으로 그린다. +class BoltBadge extends StatelessWidget { + const BoltBadge({super.key, this.size = 40}); + + final double size; + + @override + Widget build(BuildContext context) => Container( + width: size, + height: size, + decoration: const BoxDecoration( + shape: BoxShape.circle, + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [AppColors.neonCyan, AppColors.cosmicBlue], + ), + ), + child: Icon(Icons.bolt, size: size * 0.6, color: AppColors.onCosmic), + ); +} diff --git a/app/lib/core/ui/hero_time_status.dart b/app/lib/core/ui/hero_time_status.dart new file mode 100644 index 0000000..5d480ff --- /dev/null +++ b/app/lib/core/ui/hero_time_status.dart @@ -0,0 +1,108 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import '../data/planet_visual_state.dart'; +import 'live_stream.dart'; +import '../i18n/app_locale.dart'; + +/// 서버 종료시각을 기준으로 표시한다. 재진입해도 10분으로 다시 시작하지 않는다. +class HeroTimeStatus extends StatelessWidget { + const HeroTimeStatus({super.key, required this.fandomId, required this.watch, this.onParticipate}); + final String fandomId; + final PlanetWatch watch; + final VoidCallback? onParticipate; + + @override + Widget build(BuildContext context) => LiveStreamBuilder( + key: ValueKey(('hero-time', fandomId)), + create: () => watch(fandomId), + builder: (context, state) => state?.isHeroTime == true && state?.heroEndsAt != null + ? Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + _RemainingTime(endsAt: state!.heroEndsAt!, nicknames: state.heroNicknames, claims: state.heroClaimCount), + if (onParticipate != null) TextButton(onPressed: onParticipate, child: Text(context.l10n.heroParticipate)), + ]) + : const SizedBox.shrink(), + ); +} + +class _RemainingTime extends StatefulWidget { + const _RemainingTime({required this.endsAt, required this.nicknames, this.claims}); + final List nicknames; + final int? claims; + final DateTime endsAt; + @override + State<_RemainingTime> createState() => _RemainingTimeState(); +} + +class _RemainingTimeState extends State<_RemainingTime> { + Timer? _timer; + @override + void initState() { + super.initState(); + _timer = Timer.periodic(const Duration(seconds: 1), (_) => setState(() {})); + } + + @override + void dispose() { + _timer?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final seconds = (widget.endsAt.difference(DateTime.now()).inMilliseconds / 1000).ceil(); + if (seconds <= 0) return const SizedBox.shrink(); + final minutes = seconds ~/ 60; + final remainder = (seconds % 60).toString().padLeft(2, '0'); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Semantics( + label: context.l10n.heroRemainingSemantics('$minutes', '${seconds % 60}'), + excludeSemantics: true, + child: Text(context.l10n.heroRemaining('$minutes:$remainder'), + style: Theme.of(context).textTheme.labelMedium), + ), + if (widget.nicknames.isNotEmpty) + Text('by ${widget.nicknames.join(' · ')}', maxLines: 1, + overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.bodySmall), + if (widget.claims != null) + Text(context.l10n.heroClaims('${widget.claims}'), style: Theme.of(context).textTheme.bodySmall), + ], + ); + } +} + +/// 선택한 팬덤의 진행 중 이벤트에서 해당 행성으로 진입한다. +class HeroTimeBillboard extends StatelessWidget { + const HeroTimeBillboard({super.key, required this.fandomId, + required this.fandomName, required this.watch, required this.onOpen}); + final String fandomId; + final String fandomName; + final PlanetWatch watch; + final VoidCallback onOpen; + + @override + Widget build(BuildContext context) => LiveStreamBuilder( + key: ValueKey(('hero-billboard', fandomId)), + create: () => watch(fandomId), + builder: (context, state) { + if (state?.isHeroTime != true || state?.heroEndsAt == null || + !state!.heroEndsAt!.isAfter(DateTime.now())) return const SizedBox.shrink(); + return Card( + child: Padding( + padding: const EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + _RemainingTime(endsAt: state.heroEndsAt!, + nicknames: state.heroNicknames, claims: state.heroClaimCount), + TextButton(onPressed: onOpen, + child: Text(context.l10n.heroInProgress(fandomName))), + ], + ), + ), + ); + }, + ); +} diff --git a/app/lib/core/ui/live_planet.dart b/app/lib/core/ui/live_planet.dart new file mode 100644 index 0000000..1037656 --- /dev/null +++ b/app/lib/core/ui/live_planet.dart @@ -0,0 +1,104 @@ +import 'package:flutter/material.dart'; + +import '../data/planet_visual_state.dart'; +import '../theme/tokens.dart'; +import 'live_stream.dart'; +import 'planet_25d.dart'; + +/// 첫 서버 스냅샷은 기준점. 과거 캐시와의 차이를 새 진화로 오인하지 않는다. +class LivePlanet25D extends StatelessWidget { + const LivePlanet25D({ + super.key, + required this.fandomId, + required this.watch, + required this.image, + required this.size, + this.stage, + this.onOpenHistory, + this.glow = AppColors.neonViolet, + this.animate = true, + this.lowEffects = false, + }); + final VoidCallback? onOpenHistory; + final String fandomId; + final PlanetWatch watch; + final String image; + final double size; + final int? stage; + final Color glow; + final bool animate; + final bool lowEffects; + + @override + Widget build(BuildContext context) => LiveStreamBuilder( + key: ValueKey(('planet-state', fandomId)), + create: () => watch(fandomId), + builder: (context, state) => Planet25D( + key: ValueKey((fandomId, state != null)), + image: image, + onOpenHistory: onOpenHistory, + size: size, + glow: glow, + tint: planetColorOf(state?.colorKey), + stage: state == null ? stage : state.stage, + level: state?.level, + isEclipsed: state?.isEclipsed ?? false, + isHibernating: state?.isHibernating ?? false, + isHeroTime: state?.isHeroTime ?? false, + heroEventIds: state?.heroEventIds, + animate: animate, + lowEffects: lowEffects, + ), + ); +} + +/// 배경 광량만 바꿔 콘텐츠의 가독성과 화면 구조는 유지한다. +class LivePlanetLighting extends StatelessWidget { + const LivePlanetLighting({ + super.key, + required this.fandomId, + required this.watch, + required this.child, + }); + final String fandomId; + final PlanetWatch watch; + final Widget child; + + @override + Widget build(BuildContext context) => LiveStreamBuilder( + key: ValueKey(('planet-light', fandomId)), + create: () => watch(fandomId), + builder: (context, state) => AnimatedOpacity( + opacity: state?.isHibernating == true ? 0.35 : state?.isEclipsed == true ? 0.45 : 1, + duration: MediaQuery.disableAnimationsOf(context) + ? Duration.zero + : const Duration(seconds: 2), + child: Stack( + fit: StackFit.passthrough, + children: [ + child, + Positioned.fill( + child: IgnorePointer( + child: AnimatedOpacity( + opacity: state?.isHeroTime == true && state?.isEclipsed != true && state?.isHibernating != true + ? 1 + : 0, + duration: MediaQuery.disableAnimationsOf(context) + ? Duration.zero + : const Duration(seconds: 2), + child: const DecoratedBox( + decoration: BoxDecoration( + gradient: RadialGradient( + colors: [Color(0x26FFD34D), Color(0x00FFD34D)], + radius: 0.85, + ), + ), + ), + ), + ), + ), + ], + ), + ), + ); +} diff --git a/app/lib/core/ui/live_stream.dart b/app/lib/core/ui/live_stream.dart new file mode 100644 index 0000000..b19788d --- /dev/null +++ b/app/lib/core/ui/live_stream.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; + +/// 실시간 구독 한 자리 — [StreamBuilder] 에 **백그라운드 복귀 재구독**을 더한 것. +/// +/// 왜 필요한가. Firestore 리스너는 단말이 오래 잠들면 서버와 끊긴 채로 남을 수 +/// 있고, 그러면 깨어난 화면이 잠들기 전 값을 그대로 그린다. 별첨 1 ① 예외 처리가 +/// 「백그라운드 복귀·화면 회전 시 렌더링 정상 재개」를 판정 항목으로 두었으므로 +/// 복귀 시 스트림을 새로 열어 첫 스냅샷을 다시 받는다. +/// +/// [create] 는 **구독할 때마다 새 스트림을 만드는 함수**다. 이미 만들어진 스트림을 +/// 넘기면 재구독이 같은(이미 닫힌) 스트림을 다시 듣게 되므로 값이 오지 않는다. +/// +/// **구독 대상이 바뀌면 [key] 를 바꿔 준다.** [create] 는 빌드마다 새로 만들어지는 +/// 클로저라 같은 대상인지 비교할 수 없다. 비교해서 다시 열면 프레임마다 재구독이 +/// 돌고, 비교 없이 두면 대상이 바뀌어도 옛 구독이 남는다. 그래서 갈아타기는 +/// 위젯 키(예: `ValueKey(fandomId)`)로만 한다. +/// +/// 위젯이 사라지면 [StreamBuilder] 가 구독을 끊는다 — 별도 해제 코드가 없는 이유다. +class LiveStreamBuilder extends StatefulWidget { + const LiveStreamBuilder({ + super.key, + required this.create, + required this.builder, + }); + + final Stream Function() create; + + /// 값이 아직 없으면 null 로 부른다. 첫 프레임에 빈 자리를 그릴지 로딩을 그릴지는 + /// 화면이 정한다 — 배너는 빈 자리가 맞고 목록은 로딩이 맞다. + final Widget Function(BuildContext context, T? value) builder; + + @override + State> createState() => _LiveStreamBuilderState(); +} + +class _LiveStreamBuilderState extends State> + with WidgetsBindingObserver { + late Stream _stream = widget.create(); + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.resumed) { + setState(() => _stream = widget.create()); + } + } + + @override + Widget build(BuildContext context) => StreamBuilder( + stream: _stream, + builder: (context, snap) => widget.builder(context, snap.data), + ); +} diff --git a/app/lib/core/ui/mock_tag.dart b/app/lib/core/ui/mock_tag.dart new file mode 100644 index 0000000..8e23f5c --- /dev/null +++ b/app/lib/core/ui/mock_tag.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; + +import '../theme/tokens.dart'; + +/// 표본 데이터 알림(G-08) — 본문을 밀지 않도록 앱바 오른쪽에 작은 「MOCK」 알약으로 둔다. +/// 사유 문구는 길게 눌렀을 때와 스크린리더로 읽힌다. +class MockTag extends StatelessWidget { + const MockTag({super.key, required this.note}); + + final String note; + + @override + Widget build(BuildContext context) => Tooltip( + message: note, + child: Semantics( + label: 'MOCK · $note', + excludeSemantics: true, + child: Container( + margin: const EdgeInsets.only(right: 16), + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(AppRadius.sm), + border: Border.all(color: AppColors.neonViolet), + ), + child: Text( + 'MOCK', + style: const TextStyle( + fontFamily: AppFonts.body, + fontSize: 10, + fontWeight: FontWeight.w600, + height: 1.2, + letterSpacing: -0.25, + color: AppColors.neonViolet, + ), + ), + ), + ), + ); +} diff --git a/app/lib/core/ui/planet_25d.dart b/app/lib/core/ui/planet_25d.dart new file mode 100644 index 0000000..de83636 --- /dev/null +++ b/app/lib/core/ui/planet_25d.dart @@ -0,0 +1,720 @@ +import 'dart:math' as math; + +import 'package:flutter/material.dart'; + +import '../theme/tokens.dart'; +import 'planet_evolution.dart'; +import 'adaptive_planet_effects.dart'; +import '../i18n/app_locale.dart'; + +/// 행성 컬러 팔레트 — 사본 「행성 색 선택」 `373:358685` ~ `373:358692` 순서. +/// 생성 화면이 고르는 값과 렌더링이 읽는 값이 **같은 표**여야 하므로 여기 한 곳에 둔다(C-50). +/// 흰 = 색 선택 명세(`2066:17910`)의 #FFFFFF — 해석 확인 요청 Q-PN-01 권고 B안(칩의 #D9D9D9 는 +/// Figma 기본 채움). 저장값은 키(`white`)라 값만 바꾸면 기존 행성도 따라온다. +const planetColors = <(String, Color)>[ + ('white', Color(0xFFFFFFFF)), + ('black', Color(0xFF000000)), + ('blue', Color(0xFF009CFE)), + ('green', Color(0xFF00C60C)), + ('gold', Color(0xFFFFC500)), + ('orange', Color(0xFFFF7D00)), + ('red', Color(0xFFFF0049)), + ('purple', Color(0xFFC300C8)), +]; + +/// 서버 `colorKey` → 칩 색. 모르는 키(서버 기본값 `violet` 포함)는 null — 원래 일러스트 그대로. +Color? planetColorOf(String? key) => + planetColors.where((c) => c.$1 == key).firstOrNull?.$2; + +/// Figma 본체에 UX/FX v1.5 §2의 Stage별 레이어를 조합한다. +/// Stage는 서버 확정값이며, null은 단계 데이터 미제공 상태다. +class Planet25D extends StatefulWidget { + const Planet25D({ + super.key, + required this.image, + required this.size, + this.glow = AppColors.neonViolet, + this.tint, + this.stage, + this.level, + this.isEclipsed = false, + this.isHibernating = false, + this.isHeroTime = false, + this.heroEventIds, + this.onOpenHistory, + this.lowEffects = false, + this.animate = true, + }); + + /// 행성 일러스트 자산 경로(`assets/figma/planet-*.png`). + final VoidCallback? onOpenHistory; + final String image; + + /// 한 변 길이. 글로우와 링은 이 바깥으로 조금 번진다. + final double size; + + /// 아우라 색. + final Color glow; + + /// 생성 때 고른 컬러([planetColorOf]). 생성 화면 미리보기와 같은 곱하기 합성으로 본체에 입힌다. + final Color? tint; + + final int? stage; + final int? level; + final bool isEclipsed; + final bool isHibernating; + final bool isHeroTime; + final Set? heroEventIds; + + /// 저사양에서는 상태를 유지하면서 입자·Blur만 축소한다. + final bool lowEffects; + + /// 정지 화면(목록 카드 등)에서는 끈다. + final bool animate; + + /// 전역 스위치. 위젯 테스트는 `pumpAndSettle` 이 끝나야 하므로 끈다 + /// (`test/flutter_test_config.dart`). 앱에서는 항상 켜져 있다. + static bool animationsEnabled = true; + + @override + State createState() => _Planet25DState(); +} + +class _Planet25DState extends State + with SingleTickerProviderStateMixin { + int get _quality => widget.lowEffects ? 2 : PlanetEffectsQuality.of(context); + bool get _lowEffects => _quality >= 2; + bool get _celebrating => + widget.isHeroTime && !widget.isEclipsed && !widget.isHibernating; + int get _period => widget.isHibernating + ? 192 + : widget.isEclipsed + ? 96 + : _celebrating + ? 32 + : 48; + double get _light => widget.isHibernating + ? 0.35 + : widget.isEclipsed + ? 0.5 + : _celebrating + ? 1.3 + : 1; + late final AnimationController _c = AnimationController( + vsync: this, + duration: Duration(seconds: _period), + ); + + bool get _motionEnabled => + widget.animate && + Planet25D.animationsEnabled && + !MediaQuery.disableAnimationsOf(context); + + void _syncMotion() { + if (_motionEnabled && !_c.isAnimating) _c.repeat(); + if (!_motionEnabled) _c.stop(); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _syncMotion(); + } + + @override + void didUpdateWidget(Planet25D old) { + super.didUpdateWidget(old); + if (old.isHibernating != widget.isHibernating || + old.isEclipsed != widget.isEclipsed || + old.isHeroTime != widget.isHeroTime) { + _c.duration = Duration(seconds: _period); + if (_motionEnabled) _c.repeat(); + } + _syncMotion(); + } + + @override + void dispose() { + _c.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) => TweenAnimationBuilder( + tween: Tween(begin: _light, end: _light), + duration: MediaQuery.disableAnimationsOf(context) + ? Duration.zero + : const Duration(seconds: 2), + builder: (context, light, _) => PlanetEvolution( + stage: widget.stage, + onOpenHistory: widget.onOpenHistory, + level: widget.level, + heroEventIds: widget.heroEventIds, + heroEnabled: !widget.isEclipsed && !widget.isHibernating, + animate: widget.animate, + lowEffects: _lowEffects, + builder: (stage) => Opacity( + key: const ValueKey('planet-surface-light'), + opacity: light.clamp(0.0, 1.0), + child: _planet(context, stage, light), + ), + ), + ); + + Widget _tinted(Widget body) => switch (widget.tint) { + final tint? => ColorFiltered( + colorFilter: ColorFilter.mode(tint, BlendMode.modulate), + child: body, + ), + null => body, + }; + + Widget _planet(BuildContext context, int? stage, double light) { + final s = widget.size; + final validStage = stage != null && stage >= 0 && stage <= 6; + final l10n = context.l10n; + final names = l10n.stageNames.split(','); + final particleCount = stage == 0 + ? 24 + : stage == 1 + ? 6 + : stage == 5 + ? 40 + : stage == 6 + ? 18 + : 14; + final glowStrength = _quality == 0 + ? 1.0 + : _quality == 1 + ? 0.8 + : 0.6; + final ring = validStage && stage >= 4; + final glow = Color.lerp( + widget.glow, + AppColors.gold, + ((light - 1) / 0.3).clamp(0.0, 1.0), + )!; + return Semantics( + label: + '${validStage ? 'Stage $stage · ${names[stage]}' : l10n.planetStageUnknown}${widget.isHibernating + ? l10n.planetHibernatingSuffix + : widget.isEclipsed + ? l10n.planetEclipseSuffix + : _celebrating + ? l10n.planetHeroSuffix + : ''}', + image: true, + child: RepaintBoundary( + child: SizedBox( + width: s, + height: s, + child: AnimatedBuilder( + animation: _c, + builder: (context, _) { + final t = _motionEnabled ? _c.value : 0.0; // 0~1 + final wave = math.sin(t * 2 * math.pi); // -1~1 + return Stack( + clipBehavior: Clip.none, + alignment: Alignment.center, + children: [ + // 2. 글로우 아우라 — 은은하게 커졌다 작아진다. + Positioned.fill( + child: Transform.scale( + scale: + (stage == 5 ? 1.42 : 1.25) + + (stage == 6 ? 0 : wave * 0.05) + + (light - 1).clamp(0.0, 0.3), + child: DecoratedBox( + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: RadialGradient( + colors: [ + glow.withValues( + alpha: + ((stage == 1 + ? 0.22 + : stage == 5 + ? 0.55 + : 0.42) + + wave * (stage == 6 ? 0.02 : 0.05)) * + glowStrength, + ), + glow.withValues(alpha: 0.10 * glowStrength), + glow.withValues(alpha: 0), + ], + stops: const [0.45, 0.72, 1], + ), + ), + ), + ), + ), + if (stage == 6) + Positioned.fill( + child: IgnorePointer( + child: CustomPaint( + painter: _SustainedAura( + color: glow, + lowEffects: _lowEffects, + ), + ), + ), + ), + // 6. 링 — 뒤쪽 절반. 본체 뒤에 깔린다. + if (ring) + Positioned.fill( + child: _RingLayer( + compact: stage == 4, + strength: stage == 5 + ? 0.58 + : stage == 6 + ? 0.52 + : 0.48, + t: t, + color: glow, + lowEffects: _lowEffects, + front: false, + ), + ), + if (stage == 0) + Transform.rotate( + angle: t * 2 * math.pi, + child: Opacity( + opacity: 0.85, + child: Image.asset( + 'assets/fx/nebula-figma.png', + width: s, + height: s, + fit: BoxFit.contain, + ), + ), + ) + else + Transform.translate( + offset: Offset(0, wave * s * 0.025), + // 임시조치: 본체 이미지를 평면 회전시키지 않는다. + // 실제 구체 자전은 별도 표면/모델 기반 구현이 필요하다. + child: _tinted( + Image.asset( + widget.image, + width: s, + height: s, + fit: BoxFit.contain, + ), + ), + ), + if (validStage && stage >= 2) + Positioned.fill( + child: IgnorePointer( + child: Transform.rotate( + angle: -t * 2 * math.pi, + child: Opacity( + opacity: stage == 2 ? 0.65 : 0.8, + child: Image.asset( + 'assets/fx/clouds.png', + fit: BoxFit.contain, + cacheWidth: _lowEffects ? 256 : 512, + ), + ), + ), + ), + ), + if (validStage && stage >= 3) + Positioned.fill( + child: IgnorePointer( + child: CustomPaint( + painter: _OrbitPainter( + t: t, + stage: stage, + color: glow, + lowEffects: _lowEffects, + ), + ), + ), + ), + // 6. 링 — 앞쪽 절반. 본체를 가려 깊이를 만든다. + if (ring) + Positioned.fill( + child: _RingLayer( + compact: stage == 4, + strength: stage == 5 + ? 0.58 + : stage == 6 + ? 0.52 + : 0.48, + t: t, + color: glow, + lowEffects: _lowEffects, + front: true, + ), + ), + // 7. 입자 — 자리마다 위상이 달라 서로 다른 박자로 반짝인다. + Positioned.fill( + child: IgnorePointer( + child: CustomPaint( + painter: _ParticlePainter( + t: t, + color: glow, + steady: stage == 6, + count: + (particleCount * + light * + (_quality == 2 + ? 0.3 + : _quality == 1 + ? 0.6 + : 1)) + .round() + .clamp(2, 60), + lowEffects: _lowEffects, + ), + ), + ), + ), + ], + ); + }, + ), + ), + ), + ); + } +} + +/// 원본 보드의 다중 고리/잔해 질감. 뒤→본체→앞으로 합성한다. +class _RingLayer extends StatelessWidget { + const _RingLayer({ + required this.strength, + required this.compact, + required this.t, + required this.color, + required this.front, + required this.lowEffects, + }); + final double strength; + final bool compact; + final double t; + final Color color; + final bool front; + final bool lowEffects; + + @override + Widget build(BuildContext context) => LayoutBuilder( + builder: (context, box) { + final s = box.maxWidth; + return IgnorePointer( + child: OverflowBox( + maxWidth: s * 1.7, + maxHeight: s * 1.7, + child: Transform.rotate( + angle: -0.32, + child: SizedBox.square( + dimension: s * 1.7, + child: ClipPath( + clipper: _RingHalf(front, compact: compact), + child: Stack( + fit: StackFit.expand, + children: [ + Opacity( + opacity: strength * (front ? 1 : 0.7), + child: Image.asset( + 'assets/fx/planet-rings.png', + fit: BoxFit.contain, + cacheWidth: lowEffects ? 512 : 1024, + ), + ), + CustomPaint( + painter: _RingDust( + t: t, + color: color, + lowEffects: lowEffects, + ), + ), + ], + ), + ), + ), + ), + ), + ); + }, + ); +} + +class _RingHalf extends CustomClipper { + const _RingHalf(this.front, {this.compact = false}); + final bool compact; + final bool front; + @override + Path getClip(Size size) { + var half = Path() + ..addRect( + Rect.fromLTWH( + 0, + front ? size.height / 2 : 0, + size.width, + size.height / 2, + ), + ); + if (compact) { + half = Path.combine( + PathOperation.intersect, + half, + Path()..addOval( + Rect.fromCenter( + center: size.center(Offset.zero), + width: size.width * 0.80, + height: size.height * 0.32, + ), + ), + ); + } + if (front) return half; + // 본체 에셋의 가장자리 알파에도 뒤 고리가 비치지 않도록 구면 안쪽을 차폐. + final body = Path() + ..addOval( + Rect.fromCircle( + center: size.center(Offset.zero), + radius: size.width / 1.7 * 0.44, + ), + ); + return Path.combine(PathOperation.difference, half, body); + } + + @override + bool shouldReclip(_RingHalf old) => + old.front != front || old.compact != compact; +} + +/// 정적 질감 위를 실제로 공전하는 빛가루. 저사양은 수만 줄인다. +class _RingDust extends CustomPainter { + const _RingDust({ + required this.t, + required this.color, + required this.lowEffects, + }); + final double t; + final Color color; + final bool lowEffects; + @override + void paint(Canvas canvas, Size size) { + final random = math.Random(421); + final c = size.center(Offset.zero); + for (var i = 0; i < (lowEffects ? 6 : 18); i++) { + final radius = 0.34 + random.nextDouble() * 0.10; + final a = random.nextDouble() * math.pi * 2 + t * math.pi * 2; + final p = + c + + Offset( + math.cos(a) * size.width * radius, + math.sin(a) * size.height * radius * 0.52, + ); + final r = size.width * (0.0008 + random.nextDouble() * 0.002); + canvas.drawCircle( + p, + r, + Paint() + ..color = Color.lerp( + color, + Colors.white, + random.nextDouble(), + )!.withValues(alpha: 0.2 + 0.2 * math.sin(a).abs()), + ); + } + } + + @override + bool shouldRepaint(_RingDust old) => + old.t != t || old.color != color || old.lowEffects != lowEffects; +} + +/// 행성 둘레의 반짝이. 자리는 고정 시드, 밝기는 자리별 위상으로 흔든다. +class _ParticlePainter extends CustomPainter { + const _ParticlePainter({ + required this.t, + required this.color, + required this.count, + this.steady = false, + required this.lowEffects, + }); + + final int count; + final bool steady; + final bool lowEffects; + + final double t; + final Color color; + + @override + void paint(Canvas canvas, Size size) { + final rnd = math.Random(2026); + final c = size.center(Offset.zero); + for (var i = 0; i < count; i++) { + final ang = rnd.nextDouble() * 2 * math.pi; + final dist = size.width * (0.38 + rnd.nextDouble() * 0.28); + final phase = rnd.nextDouble(); + final blink = steady + ? 0.55 + : (math.sin((t + phase) * 2 * math.pi) + 1) / 2; // 0~1 + final p = Offset( + c.dx + dist * math.cos(ang), + c.dy + dist * math.sin(ang) * 0.8, + ); + final r = size.width * (0.003 + 0.006 * blink); + if (!lowEffects) + canvas.drawCircle( + p, + r * 2.5, + Paint() + ..color = color.withValues(alpha: 0.35 * blink) + ..maskFilter = lowEffects + ? null + : const MaskFilter.blur(BlurStyle.normal, 4), + ); + canvas.drawCircle( + p, + r, + Paint() + ..color = AppColors.onCosmic.withValues(alpha: 0.4 + 0.6 * blink), + ); + // 몇 개에는 가는 십자 광선. + if (!steady && i % 5 == 0) { + final ray = Paint() + ..color = AppColors.onCosmic.withValues(alpha: 0.6 * blink) + ..strokeWidth = 0.8; + canvas.drawLine(p - Offset(r * 3, 0), p + Offset(r * 3, 0), ray); + canvas.drawLine(p - Offset(0, r * 3), p + Offset(0, r * 3), ray); + } + } + } + + @override + bool shouldRepaint(_ParticlePainter old) => + old.t != t || + old.color != color || + old.count != count || + old.steady != steady || + old.lowEffects != lowEffects; +} + +/// Stage 3 공동 에너지 궤도, Stage 4+ 지속 위성. +class _OrbitPainter extends CustomPainter { + const _OrbitPainter({ + required this.t, + required this.stage, + required this.color, + required this.lowEffects, + }); + final double t; + final int stage; + final Color color; + final bool lowEffects; + + @override + void paint(Canvas canvas, Size size) { + final c = size.center(Offset.zero); + final s = size.shortestSide; + final orbit = Rect.fromCenter(center: c, width: s * 1.18, height: s * 0.52); + canvas.save(); + canvas.translate(c.dx, c.dy); + canvas.rotate(-0.35); + canvas.translate(-c.dx, -c.dy); + if (stage == 3) { + // 공유 에너지 세 무리: 표면 구름(Stage 2)과 다른 주변 구조이며 고체 위성은 아니다. + final energyOrbit = Rect.fromCenter( + center: c, + width: s * 1.32, + height: s * 1.04, + ); + for (var group = 0; group < 3; group++) { + final a = (t + group / 3) * math.pi * 2 - 0.4; + for (var j = 0; j < (lowEffects ? 3 : 6); j++) { + final angle = a - j * 0.07; + final p = + c + + Offset( + math.cos(angle) * energyOrbit.width / 2, + math.sin(angle) * energyOrbit.height / 2, + ); + final radius = s * (0.024 - j * 0.0025); + canvas.drawCircle( + p, + radius * 2.3, + Paint() + ..shader = RadialGradient( + colors: [ + color.withValues(alpha: 0.65 - j * 0.07), + color.withValues(alpha: 0), + ], + ).createShader(Rect.fromCircle(center: p, radius: radius * 2.3)), + ); + if (j == 0) + canvas.drawCircle( + p, + radius * 0.48, + Paint() + ..color = Color.lerp( + color, + Colors.white, + 0.7, + )!.withValues(alpha: 0.9 - j * 0.1), + ); + } + } + } else { + final a = t * math.pi * 2; + final p = + c + + Offset(math.cos(a) * orbit.width / 2, math.sin(a) * orbit.height / 2); + final r = s * 0.065; + canvas.drawCircle( + p, + r, + Paint() + ..shader = RadialGradient( + colors: [Colors.white, color, const Color(0xFF251744)], + stops: const [0, 0.55, 1], + center: const Alignment(-0.4, -0.4), + ).createShader(Rect.fromCircle(center: p, radius: r)), + ); + } + canvas.restore(); + } + + @override + bool shouldRepaint(_OrbitPainter old) => + old.t != t || + old.stage != stage || + old.color != color || + old.lowEffects != lowEffects; +} + +/// Stage 6: 폭죽 대신 구면 주위에 머무는 넓고 일정한 대기광. +class _SustainedAura extends CustomPainter { + const _SustainedAura({required this.color, required this.lowEffects}); + final Color color; + final bool lowEffects; + @override + void paint(Canvas canvas, Size size) { + final c = size.center(Offset.zero); + final r = size.shortestSide * 0.82; + canvas.drawCircle( + c, + r, + Paint() + ..shader = RadialGradient( + colors: [ + Colors.transparent, + Colors.transparent, + const Color(0xFFC1B9FF).withValues(alpha: lowEffects ? 0.19 : 0.3), + color.withValues(alpha: lowEffects ? 0.1 : 0.17), + Colors.transparent, + ], + stops: const [0, 0.50, 0.62, 0.76, 1], + ).createShader(Rect.fromCircle(center: c, radius: r)), + ); + } + + @override + bool shouldRepaint(_SustainedAura old) => + old.color != color || old.lowEffects != lowEffects; +} diff --git a/app/lib/core/ui/planet_evolution.dart b/app/lib/core/ui/planet_evolution.dart new file mode 100644 index 0000000..c373764 --- /dev/null +++ b/app/lib/core/ui/planet_evolution.dart @@ -0,0 +1,509 @@ +import 'dart:math' as math; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import '../theme/tokens.dart'; +import 'planet_fx_scope.dart'; +import '../i18n/app_locale.dart'; + +/// v1.5 §2.1/6. 진화·HERO 발동은 하나의 타임라인을 쓰며 첫 조회는 기준점이다. +class PlanetEvolution extends StatefulWidget { + const PlanetEvolution({ + super.key, + required this.stage, + required this.level, + required this.animate, + required this.lowEffects, + required this.builder, + this.heroEventIds, + this.onOpenHistory, + this.heroEnabled = true, + }); + final VoidCallback? onOpenHistory; + final Set? heroEventIds; + final bool heroEnabled; + final int? stage; + final int? level; + final bool animate; + final bool lowEffects; + final Widget Function(int? stage) builder; + + @override + State createState() => _PlanetEvolutionState(); +} + +class _PlanetEvolutionState extends State + with SingleTickerProviderStateMixin { + late final _fx = AnimationController(vsync: this) + ..addStatusListener((status) { + if (status == AnimationStatus.completed) + setState(() { + if (_evolution && !_hero) _completedEvolution = true; + _active = false; + _shownStage = widget.stage; + if (_pendingHero.isNotEmpty && + _canHero && + widget.animate && + TickerMode.valuesOf(context).enabled) { + _pendingHero.clear(); + _start(false, hero: true); + } else if (_pendingHeroEnd && + !_canHero && + widget.heroEnabled && + widget.animate && + TickerMode.valuesOf(context).enabled) { + _pendingHeroEnd = false; + _start(false, hero: true, ending: true); + } else { + _activity?.setActive(this, false); + } + }); + }); + PlanetFxActivity? _activity; + @override + void didChangeDependencies() { + super.didChangeDependencies(); + if (_active && !TickerMode.valuesOf(context).enabled) { + _fx.stop(); + _pendingHero.clear(); + _pendingHeroEnd = false; + _activity?.setActive(this, false); + _active = false; + _shownStage = widget.stage; + } + final next = PlanetFxScope.of(context); + if (!identical(next, _activity)) { + _activity?.setActive(this, false); + _activity = next; + _activity?.setActive(this, _active && (_evolution || _hero)); + } + } + + int? _highestStage; + int? _highestLevel; + int? _shownStage; + + @override + void initState() { + super.initState(); + _highestStage = widget.stage; + _highestLevel = widget.level; + _shownStage = widget.stage; + _heroReady = widget.heroEventIds != null; + _seenHeroIds.addAll(widget.heroEventIds ?? const {}); + } + + final _seenHeroIds = {}; + bool _heroReady = false; + bool _hero = false; + bool _heroEnding = false; + bool _pendingHeroEnd = false; + final _pendingHero = {}; + bool get _canHero => + widget.heroEnabled && (widget.heroEventIds?.isNotEmpty ?? false); + bool _active = false; + bool _evolution = false; + bool _completedEvolution = false; + + bool get _reduced => MediaQuery.disableAnimationsOf(context); + + @override + void didUpdateWidget(PlanetEvolution old) { + super.didUpdateWidget(old); + final stage = widget.stage; + final level = widget.level; + final evolved = + stage != null && + old.stage != null && + _highestStage != null && + stage > _highestStage!; + final levelled = + level != null && + old.level != null && + _highestLevel != null && + level > _highestLevel!; + if (stage != null) _highestStage = math.max(stage, _highestStage ?? stage); + if (level != null) _highestLevel = math.max(level, _highestLevel ?? level); + if (old.stage != null && (stage == null || stage < old.stage!)) { + _completedEvolution = false; + _fx.stop(); + _activity?.setActive(this, false); + _active = false; + _shownStage = stage; + } + final ids = widget.heroEventIds; + final heroEnded = + old.heroEventIds?.isNotEmpty == true && ids?.isEmpty == true; + if (_canHero || !widget.heroEnabled) _pendingHeroEnd = false; + if (_active && _heroEnding && (_canHero || !widget.heroEnabled)) { + _fx.stop(); + _activity?.setActive(this, false); + _active = false; + } + final newHeroIds = (ids ?? const {}).difference(_seenHeroIds); + _pendingHero.retainAll(ids ?? const {}); + final newHero = ids != null && _heroReady && newHeroIds.isNotEmpty; + if (ids != null) { + _seenHeroIds.addAll(ids); + _heroReady = true; + } + if (!_canHero) { + _pendingHero.clear(); + if (_active && _hero && !_heroEnding) { + _fx.stop(); + _activity?.setActive(this, false); + _active = false; + } + } + final visible = widget.animate && TickerMode.valuesOf(context).enabled; + if (!visible) { + _fx.stop(); + _activity?.setActive(this, false); + _active = false; + } + if (newHero && visible && _canHero) _pendingHero.addAll(newHeroIds); + if (!visible) { + _pendingHero.clear(); + _pendingHeroEnd = false; + } + if (heroEnded && visible && widget.heroEnabled) _pendingHeroEnd = true; + if (evolved && visible) { + // 같은 화면에서 Evolution 중 추가 승급은 최종 Stage로 합쳐 강한 FX를 겹치지 않는다. + if (!_active || !_evolution) _start(true); + } else if (_pendingHeroEnd && visible && !(_active && _evolution)) { + _pendingHeroEnd = false; + _start(false, hero: true, ending: true); + } else if (_pendingHero.isNotEmpty && + visible && + !(_active && (_evolution || _hero))) { + _pendingHero.clear(); + _start(false, hero: true); + } else if (levelled && visible && !(_active && (_evolution || _hero))) { + _start(false); + } + if (!_active) _shownStage = stage; + } + + void _start(bool evolution, {bool hero = false, bool ending = false}) { + if (evolution) _completedEvolution = false; + _hero = hero; + _heroEnding = ending; + _activity?.setActive(this, evolution || hero); + _evolution = evolution; + _active = true; + _fx.duration = Duration( + milliseconds: _reduced + ? 1200 + : ending + ? 1100 + : hero + ? 2400 + : evolution + ? 3200 + : 850, + ); + _fx.forward(from: 0); + if (ending) return; + if (evolution || hero) { + HapticFeedback.heavyImpact(); + } else { + HapticFeedback.mediumImpact(); + } + } + + @override + void dispose() { + _activity?.setActive(this, false); + _fx.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) => AnimatedBuilder( + animation: _fx, + builder: (context, _) { + final p = _fx.value; + final stage = !_active || !_evolution || _reduced || p >= 0.48 + ? widget.stage + : _shownStage; + final l10n = context.l10n; + final names = l10n.stageNames.split(','); + final stageLabel = + widget.stage != null && widget.stage! >= 0 && widget.stage! <= 6 + ? 'Stage ${widget.stage} · ${names[widget.stage!]}' + : l10n.evoPlanetGrowth; + final label = _heroEnding + ? l10n.evoHeroEnd + : _hero + ? (_reduced || p >= 0.5 ? 'HERO TIME' : l10n.evoAwakening) + : !_evolution + ? 'Lv.${widget.level}' + : _reduced + ? stageLabel + : p < 0.16 + ? l10n.evoAnomaly + : p < 0.36 + ? l10n.evoEnergy + : p < 0.48 + ? l10n.evoTransition + : p < 0.70 + ? stageLabel + : l10n.evoComplete; + return Stack( + clipBehavior: Clip.none, + alignment: Alignment.center, + children: [ + Opacity( + opacity: _active && _hero && !_heroEnding && !_reduced + ? (p < 0.18 + ? 1 - p / 0.18 * 0.3 + : p < 0.5 + ? 0.7 + (p - 0.18) / 0.32 * 0.3 + : 1) + : 1, + child: widget.builder(stage), + ), + if (_active && !_reduced) + Positioned.fill( + child: IgnorePointer( + child: CustomPaint( + painter: _hero + ? _HeroPainter( + progress: p, + lowEffects: widget.lowEffects, + ending: _heroEnding, + ) + : _EvolutionPainter( + progress: p, + evolution: _evolution, + lowEffects: widget.lowEffects, + ), + ), + ), + ), + if (!_active && _completedEvolution && widget.onOpenHistory != null) + Positioned(bottom: 0, left: 0, right: 0, + child: Center(child: TextButton( + onPressed: () { + setState(() => _completedEvolution = false); + widget.onOpenHistory!(); + }, + child: Text(l10n.evoOpenHistory), + )), + ), + if (_active) + Positioned( + bottom: 0, + left: -30, + right: -30, + child: IgnorePointer( + child: Semantics( + liveRegion: true, + child: Center( + child: DecoratedBox( + decoration: BoxDecoration( + color: AppColors.cosmicSurface.withValues(alpha: 0.9), + borderRadius: BorderRadius.circular(10), + ), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 6, + ), + child: Text( + label, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.labelMedium + ?.copyWith(color: AppColors.onCosmic), + ), + ), + ), + ), + ), + ), + ), + ], + ); + }, + ); +} + +class _EvolutionPainter extends CustomPainter { + const _EvolutionPainter({ + required this.progress, + required this.evolution, + required this.lowEffects, + }); + final double progress; + final bool evolution; + final bool lowEffects; + + @override + void paint(Canvas canvas, Size size) { + final c = size.center(Offset.zero); + final radius = size.shortestSide * 0.42; + const color = AppColors.cosmicAccent; + final p = progress; + if (!evolution) { + canvas.drawCircle( + c, + radius * (1 + p * 0.5), + Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 2 + ..color = color.withValues(alpha: (1 - p) * 0.8), + ); + canvas.drawCircle( + c, + radius * 1.4, + Paint() + ..shader = RadialGradient( + colors: [ + color.withValues(alpha: (1 - p) * 0.25), + color.withValues(alpha: 0), + ], + ).createShader(Rect.fromCircle(center: c, radius: radius * 1.4)), + ); + return; + } + if (p < 0.16) return; + if (p < 0.48) { + final focus = ((p - 0.16) / 0.20).clamp(0.0, 1.0); + final r = radius * (1.5 - focus * 1.3); + canvas.drawCircle( + c, + r, + Paint() + ..shader = RadialGradient( + colors: [ + Colors.white.withValues(alpha: 0.85 * focus), + color.withValues(alpha: 0.4), + color.withValues(alpha: 0), + ], + ).createShader(Rect.fromCircle(center: c, radius: r)), + ); + for (var i = 0; i < (lowEffects ? 6 : 16); i++) { + final a = i * 2.4 + p * 5; + canvas.drawCircle( + c + Offset(math.cos(a), math.sin(a)) * r, + 2, + Paint()..color = color.withValues(alpha: 0.8), + ); + } + } else if (p >= 0.70) { + final burst = (p - 0.70) / 0.30; + for (var i = 0; i < (lowEffects ? 8 : 28); i++) { + final x = ((i * 0.618) % 1 - 0.5) * radius * 3; + final y = -radius + ((i * 0.37) % 1 + burst) * radius * 1.3; + canvas.drawCircle( + c + Offset(x, y), + 1.5 + (i % 3) * 0.5, + Paint() + ..color = Color.lerp( + color, + Colors.white, + (i % 3) / 2, + )!.withValues(alpha: 1 - burst), + ); + } + } + } + + @override + bool shouldRepaint(_EvolutionPainter old) => + old.progress != progress || + old.evolution != evolution || + old.lowEffects != lowEffects; +} + +/// 짧은 광원 집중→코어 이동→Ring Wave. 본체 반경 안팎에서 끝낸다. +class _HeroPainter extends CustomPainter { + const _HeroPainter({ + required this.progress, + required this.lowEffects, + this.ending = false, + }); + final double progress; + final bool ending; + final bool lowEffects; + @override + void paint(Canvas canvas, Size size) { + final c = size.center(Offset.zero); + final r = size.shortestSide * 0.46; + const color = AppColors.gold; + final p = progress; + if (ending) { + final remaining = 1 - Curves.easeInCubic.transform(p); + final count = lowEffects ? 8 : 24; + for (var i = 0; i < count; i++) { + final a = i * 2.399 + p * 0.4; + final reach = r * remaining * (0.7 + (i % 4) * 0.15); + canvas.drawCircle( + c + Offset(math.cos(a), math.sin(a) * 0.7) * reach, + 1.2 + i % 3 * 0.4, + Paint()..color = color.withValues(alpha: (1 - p) * 0.65), + ); + } + return; + } + if (p < 0.5) { + final travel = ((p - 0.18) / 0.32).clamp(0.0, 1.0); + final point = Offset.lerp( + c + Offset(-r * 0.7, -r), + c, + Curves.easeInCubic.transform(travel), + )!; + final radius = r * (0.10 + travel * 0.10); + canvas.drawCircle( + point, + radius * 2, + Paint() + ..shader = RadialGradient( + colors: [ + Colors.white.withValues(alpha: 0.85), + color.withValues(alpha: 0.6), + color.withValues(alpha: 0), + ], + stops: const [0, 0.2, 1], + ).createShader(Rect.fromCircle(center: point, radius: radius * 2)), + ); + } else { + final burst = (p - 0.5) * 2; + final paint = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 2 * (1 - burst) + 0.5 + ..color = color.withValues(alpha: (1 - burst) * 0.65); + canvas.save(); + canvas.translate(c.dx, c.dy); + canvas.rotate(-0.32); + for (var i = 0; i < 2; i++) { + canvas.drawOval( + Rect.fromCenter( + center: Offset.zero, + width: r * (0.8 + burst * 2.2 - i * 0.15), + height: r * (0.3 + burst - i * 0.05), + ), + paint, + ); + } + canvas.restore(); + final count = lowEffects ? 8 : 24; + for (var i = 0; i < count; i++) { + final a = i * 2.399; + final reach = r * (0.3 + burst * (0.8 + (i % 3) * 0.12)); + canvas.drawCircle( + c + Offset(math.cos(a), math.sin(a) * 0.7) * reach, + 1 + i % 3 * 0.4, + Paint()..color = color.withValues(alpha: (1 - burst) * 0.8), + ); + } + } + } + + @override + bool shouldRepaint(_HeroPainter old) => + old.progress != progress || + old.lowEffects != lowEffects || + old.ending != ending; +} diff --git a/app/lib/core/ui/planet_fx_scope.dart b/app/lib/core/ui/planet_fx_scope.dart new file mode 100644 index 0000000..4d0d48c --- /dev/null +++ b/app/lib/core/ui/planet_fx_scope.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; + +/// 같은 화면의 강한 행성 연출을 소유자별로 추적한다. 결과 문구는 숨기지 않는다. +class PlanetFxActivity extends ChangeNotifier { + final _owners = {}; + bool _scheduled = false; + bool _disposed = false; + bool get busy => _owners.isNotEmpty; + + void setActive(Object owner, bool active) { + final wasBusy = busy; + active ? _owners.add(owner) : _owners.remove(owner); + if (wasBusy == busy || _scheduled || _disposed) return; + _scheduled = true; + // 자식의 didUpdateWidget/dispose에서도 호출되므로 빌드 중 알림은 피한다. + WidgetsBinding.instance.addPostFrameCallback((_) { + _scheduled = false; + if (!_disposed) notifyListeners(); + }); + } + + @override + void dispose() { + _disposed = true; + super.dispose(); + } +} + +class PlanetFxScope extends StatefulWidget { + const PlanetFxScope({super.key, required this.child}); + final Widget child; + + static PlanetFxActivity? of(BuildContext context) => + context.dependOnInheritedWidgetOfExactType<_FxScope>()?.notifier; + + @override + State createState() => _PlanetFxScopeState(); +} + +class _PlanetFxScopeState extends State { + final activity = PlanetFxActivity(); + @override + void dispose() { + activity.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) => + _FxScope(notifier: activity, child: widget.child); +} + +class _FxScope extends InheritedNotifier { + const _FxScope({required super.notifier, required super.child}); +} diff --git a/app/lib/core/ui/play_banner.dart b/app/lib/core/ui/play_banner.dart new file mode 100644 index 0000000..04c1c47 --- /dev/null +++ b/app/lib/core/ui/play_banner.dart @@ -0,0 +1,78 @@ +import 'package:flutter/material.dart'; + +import '../theme/tokens.dart'; +import '../i18n/app_locale.dart'; + +/// 시안 검색 화면 상단 배너(`Frame 1984081037`) — #dcdcdc r10 · 오른쪽 핑크 로고 칸 62. +/// GALAXY 검색 · INSIDE 검색이 같이 쓴다. +class PlayBanner extends StatelessWidget { + const PlayBanner({super.key}); + + static TextStyle _t(double size, double height) => TextStyle( + fontFamily: AppFonts.body, + fontSize: size, + fontWeight: FontWeight.w500, + height: height, + letterSpacing: 0, + color: const Color(0xFF737373), + ); + + @override + Widget build(BuildContext context) => Container( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), + decoration: BoxDecoration( + color: const Color(0xFFDCDCDC), + borderRadius: BorderRadius.circular(10), + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(context.l10n.playNow, style: _t(10, 1.4)), + Text( + context.l10n.playHeadline, + style: const TextStyle( + fontFamily: AppFonts.display, + fontFamilyFallback: [AppFonts.body], + fontWeight: FontWeight.w400, + fontSize: 16, + height: 20 / 16, + letterSpacing: 0, + color: Color(0xFF444444), + ), + ), + Text( + context.l10n.playBody, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: _t(12, 20 / 12), + ), + ], + ), + ), + const SizedBox(width: 11), + Container( + width: 62, + height: 62, + alignment: Alignment.center, + decoration: BoxDecoration( + color: AppColors.cosmicAccent, + borderRadius: BorderRadius.circular(10), + ), + child: const Text( + 'F', + style: TextStyle( + fontFamily: AppFonts.body, + fontSize: 34, + fontWeight: FontWeight.w900, + height: 1, + color: Colors.black, + ), + ), + ), + ], + ), + ); +} diff --git a/app/lib/core/ui/relative_time.dart b/app/lib/core/ui/relative_time.dart new file mode 100644 index 0000000..e6373e4 --- /dev/null +++ b/app/lib/core/ui/relative_time.dart @@ -0,0 +1,39 @@ +/// 상대 시각 표기. +/// +/// 명세 `523:18399`(캠페인 카드)가 규칙 전부를 정했다 — +/// *~1분 : 방금 전 · ~1시간 : n분 전 · ~24시간 : n시간 전 · ~7일 : n일 전 ·* +/// *이후 : 날짜(2026.01.01)* +/// +/// 같은 규칙이 `548:19137`(게시글 카드) · `554:22611` 에도 사본 주석으로 있다. 사본 표본의 +/// 「1달 전」은 이 규칙과 어긋나는 표본 문구라 따르지 않는다. +/// +/// 화면마다 따로 만들면 「3일 전」과 「2026.01.01」이 같은 목록에서 섞인다. +/// 한 곳에 두고 전 화면이 쓴다. +library; + +import '../i18n/app_locale.dart'; + +String relativeTime(DateTime at, {DateTime? now}) { + final d = (now ?? DateTime.now()).difference(at); + if (d.inMinutes < 1) return appL10n.timeJustNow; + if (d.inMinutes < 60) return appL10n.timeMinutesAgo('${d.inMinutes}'); + if (d.inHours < 24) return appL10n.timeHoursAgo('${d.inHours}'); + if (d.inDays < 7) return appL10n.timeDaysAgo('${d.inDays}'); + // 일주일이 넘으면 「37일 전」보다 날짜가 읽기 쉽다. + final y = at.year.toString().padLeft(4, '0'); + final m = at.month.toString().padLeft(2, '0'); + final day = at.day.toString().padLeft(2, '0'); + return '$y.$m.$day'; +} + +/// 반응 수치 표기. 명세 — *반응 수치는 K 단위 활용*. +/// +/// POP 은 천 단위 구분자를 쓰므로([formatPop]) 다른 함수다. 좋아요 1,200 은 +/// 「1.2K」로 줄이는 편이 카드 안에서 읽히지만, POP 잔액을 줄이면 금액이 흐려진다. +String formatReaction(int value) { + if (value < 1000) return '$value'; + final k = value / 1000; + // 시안은 「3K」 「1K」 처럼 소수 0 을 떼어 쓴다. + final text = k >= 10 ? '${k.round()}' : k.toStringAsFixed(1); + return '${text.endsWith('.0') ? text.substring(0, text.length - 2) : text}K'; +} diff --git a/app/lib/core/ui/section_header.dart b/app/lib/core/ui/section_header.dart new file mode 100644 index 0000000..057dad4 --- /dev/null +++ b/app/lib/core/ui/section_header.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; + +import '../theme/tokens.dart'; +import '../i18n/app_locale.dart'; + +/// 섹션 제목 + 「더 보기」. +/// +/// Figma 홈의 `TODAY'S QUEST` · `AI Daily Briefing` · `HOT ARCHIVE` · +/// `인기 K-INSIDE` 가 전부 같은 모양이라 하나로 뺐다. +class SectionHeader extends StatelessWidget { + const SectionHeader({ + super.key, + required this.title, + this.onMore, + this.moreLabel, + }); + + final String title; + + /// 「더 보기」 자리의 다른 문구. 브리핑은 이 자리가 「공유」다(Figma `266:90529`). + final String? moreLabel; + + /// 없으면 「더 보기」를 그리지 않는다. + final VoidCallback? onMore; + + @override + Widget build(BuildContext context) { + final text = Theme.of(context).textTheme; + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Text( + title, + style: text.titleLarge?.copyWith(fontWeight: FontWeight.w600), + ), + ), + if (onMore != null) + TextButton( + onPressed: onMore, + style: TextButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: AppSpace.s2), + minimumSize: const Size(0, AppSizing.touchMinApp), + foregroundColor: AppColors.onCosmicVariant, + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(moreLabel ?? context.l10n.commonMore, style: text.bodySmall), + const Icon(Icons.chevron_right, size: 16), + ], + ), + ), + ], + ); + } +} diff --git a/app/lib/l10n/app_en.arb b/app/lib/l10n/app_en.arb new file mode 100644 index 0000000..0604e78 --- /dev/null +++ b/app/lib/l10n/app_en.arb @@ -0,0 +1,791 @@ +{ + "@@locale": "en", + "commonConfirm": "OK", + "commonCancel": "Cancel", + "commonClose": "Close", + "commonSelect": "Select", + "commonRetry": "Try again", + "commonView": "View", + "commonPrev": "Back", + "commonDone": "Done", + "commonStart": "Start", + "commonNotYet": "The {where} screen arrives in the second build review (2026-09-10).", + "languageSettingsTitle": "Language", + "authLogin": "Log in", + "authSignup": "Sign up", + "authFirstTime": "New to FANUZU?", + "authAlreadyMember": "Already have an account?", + "authLoginHeadline": "Log in to FANUZU\nthe easiest way!", + "authSignupHeadline": "Join FANUZU\nthe easiest way!", + "authProviderLogin": "{provider, select, apple{Log in with Apple} google{Log in with Google} kakao{Log in with Kakao} line{Log in with LINE} other{Log in with email}}", + "authProviderSignup": "{provider, select, apple{Sign up with Apple} google{Sign up with Google} kakao{Sign up with Kakao} line{Sign up with LINE} other{Sign up with email}}", + "authNotConfiguredTitle": "{name} login coming soon", + "authNotConfiguredBody": "{name} login opens once the client keys are provided.\n\nRequired\n· {items}", + "authNotConfiguredNotice": "{name} login is not ready yet.\nRequired: {items}", + "authResetNeedEmail": "Enter the email address for the reset link first.", + "authResetSent": "We sent reset instructions to that address.", + "termsTitle": "Terms of use", + "termsAgreeAll": "Agree to all", + "termsService": "Terms of Service (required)", + "termsLocation": "Location-based Service Terms (required)", + "termsAge": "I am 14 or older. (required)", + "termsMarketing": "Receive marketing and event notifications (optional)", + "signupDoneTitle": "You're all signed up", + "signupDoneBody": "You can start your fandom activities.\nGrow your fandom planet through all kinds of activities.", + "homeItemsTooltip": "Items", + "homeNotificationsTooltip": "Notifications", + "homeFandomHistory": "Fandom history", + "homePopularInside": "Popular K-INSIDE", + "homeWhereBriefing": "AI Briefing", + "homeWhereInsideList": "INSIDE list", + "homeWhereInsideDetail": "INSIDE detail", + "homeLoadFailed": "Couldn't load Home", + "homeRetryLater": "Please try again in a moment.", + "myProfileCardTooltip": "Profile card", + "myTabPlanets": "Planets/Nebulae", + "myTabActivity": "Activity", + "myInside": "Inside", + "myCampaign": "Campaigns", + "myShareLater": "{target} sharing will be connected in the 09-10 build.", + "myEditPhoto": "Edit profile photo", + "myEditProfile": "Edit profile", + "settingsTitle": "Settings", + "settingsAccount": "Account", + "settingsNickname": "Nickname", + "settingsEmail": "Email", + "settingsSns": "Linked social accounts", + "settingsPopHistory": "POP history", + "settingsPayments": "Payments", + "settingsServicePayments": "Service payment history", + "settingsService": "Service settings", + "settingsPush": "Push notifications", + "settingsBlocked": "Blocked users", + "settingsInfo": "About", + "settingsNotice": "Notices", + "settingsInquiry": "1:1 inquiry", + "settingsAppVersion": "App version", + "settingsLatest": "Latest", + "settingsLogout": "Log out", + "settingsLogoutConfirm": "Do you want to log out?", + "settingsWithdraw": "Delete account", + "settingsWithdrawConfirm": "Do you want to delete your account?", + "settingsWithdrawBody": "Your POP will be lost and this cannot be undone.", + "settingsWithdrawLater": "Account deletion will be connected in the 09-10 build.", + "settingsUnlinkConfirm": "Unlink this account?", + "settingsUnlinkBody": "Only this social login method is removed. Your account data is not deleted.", + "settingsUnlink": "Unlink", + "settingsSameEmailOnly": "Only social accounts with the same\nemail as your account can be linked.", + "settingsLinkNotReady": "{name} linking is not ready yet. Required: {items}", + "settingsConnect": "Link", + "settingsDisconnect": "Unlink", + "settingsEmailProvider": "Email", + "pushDeviceOff": "Push notifications are turned off on this device.", + "pushDeviceOffHint": "Allow notifications in system settings to receive them.", + "pushRequesting": "Requesting…", + "pushGoToSettings": "Open settings", + "pushOpeningDeviceSettings": "Opening device settings.", + "pushEnableInDevice": "Turn on FANUZU notifications in device settings to receive them.", + "pushDeviceSection": "Device notifications", + "pushSoundVibration": "Sound & vibration", + "pushServiceSection": "Service notifications", + "pushTopicEssential": "Essential", + "pushTopicFandomEvent": "Fandom events", + "pushTopicBriefing": "AI Briefing", + "pushTopicCommunity": "Community", + "pushTopicMarketing": "Marketing", + "linkTitle": "{name} link", + "linkNotLinked": "Not linked", + "linkLinked": "Linked", + "linkSameEmailOnly": "Only social accounts with the same email as your account can be linked.", + "linkUnlink": "Unlink", + "linkLink": "Link", + "noticeBadge": "Notice", + "noticeTitle": "Notices", + "noticeEmpty": "No notices yet.", + "noticeDetailTitle": "Notice", + "translationMark": "Translated", + "translationReplyTo": "Translating {name}'s comment", + "translationWrite": "Write a translation", + "translationHint": "Enter your translation", + "translationValid": "Valid", + "translationInvalid": "Invalid", + "translationSelfMarkBlocked": "You can't mark your own translation.", + "translationSubmitted": "Translation posted. If it passes review, POP is paid after 24 hours.", + "translationMarked": "Marked.", + "translationAlreadyMarked": "You already marked this translation.", + "translationDuplicate": "You already posted a translation in this language.", + "translationFailed": "Something went wrong. Please try again shortly.", + "translationNotEligible": "Only members of this fandom whose account is at least 24 hours old can mark.", + "translationDailyCap": "You can't post more translations today.", + "translationInvalidTarget": "This content can't be translated.", + "commonRequestFailed": "Couldn't process the request.", + "commonFan": "Fan", + "adLoadFailed": "Couldn't load the ad. You'll get the basic reward only.", + "storeNotReady": "Store items coming soon", + "storePending": "Waiting for payment approval. It will apply automatically once approved.", + "storeFailed": "Couldn't complete the payment.", + "storePurchased": "Your purchase has been applied.", + "heroParticipate": "ZAP+ · Join the gift", + "heroRemainingSemantics": "HERO TIME remaining: {minutes} min {seconds} sec", + "heroRemaining": "HERO TIME · {time} left", + "heroClaims": "Gifts claimed by fans: {count}", + "heroInProgress": "{name} · HERO TIME in progress →", + "stageNames": "Nebula,Start,Growth,Together,Journey,Light,Eternity", + "planetStageUnknown": "Planet · No stage info", + "planetHibernatingSuffix": " · Dormant", + "planetEclipseSuffix": " · Eclipse in progress", + "planetHeroSuffix": " · HERO TIME in progress", + "evoPlanetGrowth": "Planet growth", + "evoHeroEnd": "HERO TIME ended", + "evoAwakening": "The planet is awakening", + "evoAnomaly": "Anomaly detected", + "evoEnergy": "Gathering energy", + "evoTransition": "Transforming", + "evoComplete": "Planet evolution complete", + "evoOpenHistory": "View PASSPORT history", + "playNow": "Play now", + "playHeadline": "Let's go meet your fandom!", + "playBody": "Create a planet, connect with fandoms, and enjoy fan activities...", + "timeJustNow": "Just now", + "timeMinutesAgo": "{n}m ago", + "timeHoursAgo": "{n}h ago", + "timeDaysAgo": "{n}d ago", + "commonMore": "More", + "authProviderStart": "{provider, select, email{Continue with email} kakao{Continue with Kakao} line{Continue with LINE} apple{Continue with Apple} other{Continue with Google}}", + "authProviderName": "{provider, select, email{Email} kakao{Kakao} line{LINE} apple{Apple} other{Google}}", + "authLastProvider": "You can't unlink your last sign-in method.", + "authLoginRequired": "Please sign in.", + "authDifferentEmail": "This account doesn't match your sign-up email.", + "authLinkFailed": "Couldn't link the account.", + "authNotLinked": "This account isn't linked.", + "authEmailUnavailable": "Email sign-in is unavailable.", + "authCheckCredentials": "Please check your email or password.", + "authSocialFailed": "Social sign-in failed.", + "authAppleUnavailable": "Sign in with Apple isn't available on this device.", + "authEmailInUse": "This email is already registered.", + "authWeakPassword": "Password is too short. Use at least 8 characters.", + "authInvalidEmail": "Invalid email format.", + "commonTryLater": "Please try again later.", + "authCredentialInUse": "This social account is already linked to another FANUZU account.", + "authAlreadyLinked": "This account is already linked.", + "authMethodUnavailable": "This sign-in method isn't available yet.", + "authRecentLogin": "For security, please sign in again and retry.", + "authOtherProviderGuide": "The {email} account was registered with a different sign-in method. Sign in with the method you first used, then link this one in MY > Settings > Linked accounts.", + "authThisAccount": "This", + "eclipseCapped": "You reached the participation cap, so some shakes weren't counted.", + "eclipseResultUnknown": "Couldn't confirm your participation. Please check again with the same request.", + "eclipseChecking": "Checking participation", + "eclipseRetrySame": "Recheck same participation", + "eclipseEnded": "The eclipse has already ended or was successfully defended.", + "eclipseTooFast": "You're participating too fast. Please try again shortly.", + "eclipseNone": "No eclipse in progress", + "eclipseSuccessTitle": "You stopped\nthe meteor impact!", + "eclipseQualified": "Participation confirmed", + "eclipseFailTitle": "The planet defense\nfailed this time", + "eclipseFailBody": "This round's defense goal wasn't reached.", + "eclipseCancelTitle": "Eclipse cancelled", + "eclipseCancelBody": "Cancelled due to a tally error. No FPI was deducted.", + "eclipsePendingTitle": "Checking defense result", + "eclipsePendingBody": "Participation time is over. Waiting for the server's decision.", + "eclipseShakeTimed": "s — shake your phone\nto stop the meteor impact!", + "eclipseShake": "Shake your phone\nto stop the meteor impact!", + "eclipseMyCount": "My shakes: {count}", + "eclipsePendingSuffix": " · +{count} pending", + "eclipseSendingSuffix": " · Sending", + "eclipseNoPopNote": "No POP is spent. Make at least {count} valid shakes to count as a participant.", + "eclipseTapToJoin": "Can't shake? Tap to join", + "eclipseTimeLeft": "Eclipse time left:", + "eclipseSeconds": " {seconds}s", + "eclipseBannerDefended": "Defended — no FPI loss for {name}", + "eclipseBannerActive": "Eclipse triggered — {name} erosion {rate}%", + "eclipseBannerDefense": "Valid participants {count} / {target}", + "timeHoursMinutesLeft": "{hours}h {minutes}m left", + "timeMinutesLeft": "{minutes}m left", + "liveEclipseDefense": "[LIVE] {name} joined the eclipse defense! (+{count})", + "liveHotTime": "[LIVE] {name}'s Artist Hot Time has started!", + "liveHeroGift": "[LIVE] A Hero's Gift from {name} has arrived!", + "liveCampaign": "[LIVE] {name} backed a campaign!", + "galaxySetPrimaryConfirm": "Set the selected planet as your main planet?", + "galaxyLoadFailed": "Couldn't load the galaxy", + "commonSearch": "Search", + "galaxyMyPlanets": "My planets", + "galaxyMap": "Galaxy map", + "nebulaCreate": "Create nebula", + "commonRecheck": "Check again", + "zapApplying": "Applying contribution", + "zapNeedsCheck": "Contribution result needs checking", + "zapNoPlanet": "You don't belong to a planet", + "commonContribute": "Contribute", + "commonItem": "Items", + "galaxyDefended": "Defended · No FPI loss", + "galaxyErosion": "Erosion {rate}% · Defense {progress}%", + "galaxyOnlineMembers": "{online} online · {members} members", + "galaxySearchHint": "Search by fandom name", + "commonRecentSearches": "Recent searches", + "commonClearAll": "Clear all", + "commonNoRecentSearches": "No recent searches.", + "galaxyNoHit": "No fandom found.\nPlease check the name.", + "galaxyMembers": "{count} members", + "galaxyPlanetRank": "{name} planet, rank {rank}", + "galaxyMapOpen": "Open galaxy map", + "zapNotEnoughPop": "Not enough POP", + "zapFailNoPop": "Not enough POP to contribute.", + "zapFailLedger": "Contribution stopped: balance and earnings history don't match.", + "zapFailRetry": "Check your connection and recheck the same contribution.", + "zapDone": "Contributed", + "zapDonePop": "Contributed {pop} POP", + "growthFounders": "Founding members", + "growthNebulaGoalPassed": "The goal period has passed · Meet the conditions and the planet will be born", + "growthFinalStage": "Final stage", + "growthPromotionPending": "Awaiting promotion", + "growthCheckConditions": "Check growth conditions", + "growthConditionChecking": "Checking {name} condition", + "growthConditionMet": "{name} condition met", + "growthConditionUnmet": "{name} condition not met", + "nebulaHeadline": "Name your fandom planet.", + "nebulaBody": "If founding members reach the POP goal within {days} days,\nit's born as an official planet!\nMembers who joined the evolution get a commemorative badge.", + "nebulaNameTaken": "This name is already taken.", + "nebulaNameHint": "Fandom name", + "nebulaGoal": "Goal ", + "nebulaFounders": " / Founders", + "nebulaGoalSemantics": "Goal {pop} POP · {members} founding members · fixed {days} days", + "commonMyPop": "My POP: ", + "commonChange": "Change", + "planetShape": "Shape {n}", + "planetBgColor": "Choose background color", + "planetColorSelect": "Choose planet color", + "planetTypeSelect": "Choose planet type", + "commonNext": "Next", + "planetCreateFailed": "Couldn't create the planet. Please try again later.", + "planetCreateConfirm": "Create a planet\nwith these options?", + "commonCreate": "Create", + "briefingOpenFailed": "Couldn't open the original.", + "campaignAchieved": "{percent}% reached", + "campaignView": "View campaign", + "growthFinalReached": "You've reached the final stage", + "zapRetrySame": "Recheck same contribution", + "heroOwnerSuffix": "'s", + "heroStartPrompt": "Create or join a planet\nand start your FANUZU journey!", + "heroNebulaPrompt": "Use the POP you earn from activities\nto contribute to the nebula!", + "heroRecentContribution": "Growing steadily with\n{pop} POP contributed.", + "heroGrowingTogether": "A fandom planet we grow together", + "heroPopProgress": "POP progress", + "heroNebula": "Nebula", + "heroNebulaTap": "Tap the nebula\nto give birth to a planet!", + "heroCreateNebula": "Create planet (nebula)", + "heroJoinPlanet": "Join a planet", + "heroCreateOrJoin": "Create or join a planet", + "insideAnswers": "Answers {count}", + "itemJoinFirst": "Join a planet to send a Hero's Gift.", + "itemBuyNotReady": "Item purchase is coming soon.", + "itemUseNotReady": "Item use is coming soon.", + "payOpenFailed": "Couldn't open the payment window.", + "itemBuy": "Buy items", + "itemSelect": "Choose an item", + "commonComingSoon": "Coming soon.", + "itemSendPop": "Send {pop} POP", + "itemBuyerPop": "Buyer +{pop} POP", + "itemOwned": "Owned: {count}", + "itemOwnedCount": "Owned: {count}", + "growthOwnerOf": "'s ", + "growthSteadySuffix": " contributed — growing steadily.", + "questAttended": "Checked in", + "questInProgressSpaced": "In progress", + "questRewardPaid": "Reward paid", + "questClaim": "Claim reward", + "questStart": "Start quest", + "questRewardDone": "Reward claimed", + "popKindGeneral": "Regular POP", + "popKindContribution": "Contribution POP", + "popTxGrant": "Granted", + "popTxSpend": "Spent", + "popTxRefund": "Refund", + "popTxVoid": "Voided", + "popTypeQuest": "Quest", + "popTypeReward": "Reward", + "popTypePayment": "Payment", + "popTypeSubscription": "Subscription", + "popTypeAdmin": "Admin", + "popTypeFee": "Fee", + "commonEtc": "Other", + "popReasonQuestSteps": "Step counter quest", + "popReasonQuestMusicShake": "Music Shake quest", + "popReasonQuestPlace": "Place visit quest", + "popReasonQuestAttend": "Check-in quest", + "popReasonAdReward": "Ad reward", + "popReasonHeroDrop": "Hero's Gift", + "popReasonHeroDropTrigger": "Hero's Gift sent", + "popReasonTranslation": "Community translation", + "popReasonIapPurchase": "Purchase", + "popReasonFocusDaily": "Focus daily reward", + "popReasonPreregBonus": "Pre-registration reward", + "popReasonReferral": "Friend invite", + "popTypeReferral": "Referral", + "popReasonAdminGrant": "Admin grant", + "popReasonAdminClawback": "Admin clawback", + "popReasonCampaignFund": "Campaign support", + "popReasonCampaignRefund": "Campaign refund", + "popReasonCampaignFeeRefund": "Campaign fee refund", + "popReasonCampaignFee": "Campaign fee", + "popReasonQnaBounty": "INSIDE bounty", + "popReasonQnaRefund": "Bounty refund", + "popReasonQnaReward": "INSIDE accepted-answer reward", + "popReasonEclipseDefense": "Zap contribution", + "popReasonOther": "POP change", + "popRepeat": "{title} ×{count}", + "payStatePaid": "Paid", + "payStateRefundRequested": "Refund requested", + "payStateRefundApproved": "Refund approved", + "payStateRefunded": "Refunded", + "payStateRefundRejected": "Refund rejected", + "pushTopicEssentialDesc": "Account, payment, and maintenance notices. These can't be turned off.", + "pushTopicFandomEventDesc": "Eclipse, campaign, quest, and Artist Hot Time news.", + "pushTopicBriefingDesc": "Daily briefing cards summarizing your planet's day.", + "pushTopicCommunityDesc": "Comments, likes, and answers on your posts.", + "pushTopicMarketingDesc": "Get event and benefit info. Only on if you agree.", + "inquiryPending": "Received", + "inquiryInProgress": "In progress", + "inquiryAnswered": "Answered", + "photoBadType": "Only JPG, PNG, WEBP, or GIF images can be uploaded.", + "photoTooBig": "The photo is over 10MB. Please choose a smaller one.", + "photoUnreadable": "Couldn't read the photo. Please choose again.", + "myListInside": "My INSIDE", + "myListInsideEmpty": "You haven't posted to INSIDE yet.", + "myListCampaign": "My campaigns", + "myListCampaignEmpty": "You haven't created a campaign yet.", + "sortLatest": "Latest", + "sortPop": "By POP", + "sortSolved": "Accepted", + "sortAchieved": "Achieved", + "payNotFound": "Payment not found.", + "payItemFallback": "Purchased item", + "payMethodInApp": "In-app purchase", + "unknownUser": "Unknown user", + "blockSelf": "You can't block yourself.", + "inquiryFallback": "Inquiry", + "photoRejectedServer": "Couldn't upload the photo. Check its format and size.", + "photoUploadFailed": "Photo upload failed. Please try again later.", + "focusClaimed": "You got today's Focus reward: +{pop} Contribution POP.", + "focusActive": "Ad-free Focus mode active", + "focusUntil": "Until {date} · Today's reward can only be claimed today", + "focusClaimedToday": "Claimed today", + "focusClaimToday": "Claim today's reward", + "storeConnectFailed": "Couldn't connect to the store. Please try again later.", + "payRestore": "Restore purchases", + "payEmpty": "No payment history.", + "payRefundGuide": "Consumables: refundable within 7 days of purchase if unused. Items that have been partly used are not refundable.\nSubscriptions: refundable within 24 hours of payment if no benefits (e.g., bonus rewards) were used.\nRefunds are completed within 7–14 days after approval.", + "payDetailTitle": "Payment details", + "payNumber": "Payment no.", + "payDate": "Payment date", + "payMethod": "Payment method", + "payStatus": "Payment status", + "payAmount": "Amount", + "payRefundInfo": "Refund policy", + "payRefundApply": "Request refund", + "payRefundUnavailable": "Not refundable", + "refundReasonMistake": "I paid by mistake", + "refundReasonExpectation": "Not what I expected", + "refundReasonError": "Couldn't use it due to an error", + "refundReasonDuplicate": "Duplicate payment", + "refundTitle": "Refund", + "refundSubmit": "Submit", + "refundReason": "Reason for refund", + "refundConfirm": "Request a refund?", + "myPop": "My POP", + "commonNoHistory": "No history yet.", + "nicknameEdit": "Edit nickname", + "nicknameReserved": "You can't use a name that could be mistaken for staff.", + "nicknameTaken": "This nickname is already in use.", + "albumItems": "Photo options", + "takePhoto": "Take photo", + "pickFromAlbum": "Choose from album", + "tierGuide": "Tier guide", + "statSteps": "Total steps", + "statContribution": "Total contribution POP", + "statQuests": "Completed quests", + "shareInstagram": "Instagram", + "shareTiktok": "TikTok", + "shareCopyLink": "Copy link", + "inquiryListTitle": "1:1 inquiries", + "inquiryEmpty": "You have no inquiries.", + "inquiryWrite": "Write inquiry", + "inquiryDetail": "Inquiry details", + "commonEdit": "Edit", + "inquiryAnswer": "Answer", + "inquiryNew": "New inquiry", + "inquiryEdit": "Edit inquiry", + "commonTitle": "Title", + "commonTitleHint": "Enter a title.", + "inquiryBody": "Details", + "inquiryBodyHint": "Describe your inquiry.", + "inquiryBodyHelper": "Writing 10+ characters helps us assist you faster.", + "inquirySubmitFailed": "Couldn't submit your inquiry. {reason}", + "inquiryEditFailed": "Couldn't update your inquiry. {reason}", + "inquirySubmitted": "Inquiry submitted.", + "inquiryEdited": "Inquiry updated.", + "blockedEmpty": "No blocked accounts.", + "unblock": "Unblock", + "unblockFailed": "Couldn't unblock. {reason}", + "tierInfoTitle": "TIER info", + "tierPromptPrefix": "Raise your ", + "tierPromptRank": "rank", + "tierPromptSuffix": "\nby contributing to planets and nebulae", + "tierPassportNote": "Passport Tier is a card appearance grade separate from your level, and it never goes down.", + "tierTotalContribution": "Total contribution", + "notifKindEclipse": "Eclipse", + "notifKindItemUsed": "Item used", + "notifKindLike": "Likes", + "notifKindComment": "Comments", + "notifKindCampaign": "Campaign goal", + "notifKindQuest": "Quest complete", + "notifTargetPost": "post", + "notifTargetCampaign": "campaign", + "notifTargetQuest": "quest details", + "dayToday": "Today", + "dayYesterday": "Yesterday", + "notifEmpty": "No notifications.", + "notifGoTo": "Going to {target}.", + "stationOfficial": "Official", + "stationShop": "Shops", + "stationTicket": "Tickets", + "stationMedia": "Media", + "stationLoadFailed": "Couldn't load STATION", + "commonAll": "All", + "stationSuggest": "Suggest a shop", + "stationEmpty": "No links yet.\nJoin a planet to see curated links.", + "stationCategoryEmpty": "No links in this category yet.", + "stationSuggestLater": "Suggestions open once the server is connected.", + "commonSubmit": "Submit", + "commonName": "Name", + "commonNameRequired": "Please enter a name.", + "commonUrl": "URL", + "commonUrlRequired": "Please enter a URL.", + "commonUrlHttps": "Please enter a URL starting with https.", + "commonCategory": "Category", + "stationReason": "Why you recommend it (optional)", + "stationSuggestNote": "Suggested links are added after review. We'll notify you of the result.", + "heroGiftUnavailable": "This gift can't be claimed right now.", + "heroGiftAlready": "You've already joined this gift. Check your POP history.", + "heroGiftRelogin": "Please sign in again to join.", + "heroGiftNoPermission": "You don't have permission to join this gift.", + "heroGiftHowTo": "Shake your phone or tap the button below to join the light energy gift.", + "heroGiftPopNote": "POP you can receive depends on the remaining gift and the per-person cap.", + "heroGiftReceived": "{total} POP received", + "heroGiftUnknown": "Couldn't confirm the payout. Please check again.", + "heroGiftNone": "No gifts to join right now.", + "heroGiftConfirmed": "Your gift claim is confirmed.", + "heroGiftChecking": "Confirming payout…", + "heroGiftRetry": "Recheck same gift", + "heroGiftTap": "Join with a button instead", + "rankMyPlanet": "My planet's {metric} ranking", + "rankPlanetEmpty": "No planet rankings yet", + "rankScopeLive": "Live", + "rankScopeCountry": "By country", + "rankMine": "My {metric} ranking", + "rankMyCountry": "My country's {metric} ranking", + "rankEmpty": "No rankings yet", + "rankScope": "{scope} ranking", + "rankLoadFailed": "Couldn't load the ranking", + "rankMeSuffix": "{name} (me)", + "reportDefamation": "Artist slander or defamation", + "reportInappropriate": "Inappropriate content", + "reportSpam": "Spam or commercial ads", + "reportPrivacy": "Personal info exposure or impersonation", + "reportTitle": "Report", + "reportSubmit": "Report", + "reportAuthor": "Author", + "reportReasonLabel": "Reason", + "reportConfirm": "Report this post?\nThis can't be undone once submitted.", + "blockMemberConfirm": "Block all posts from this member?", + "loungeMembersOnly": "Only members of this planet's fandom can post, comment, or like.", + "loungeFailed": "Couldn't complete that. Please try again later.", + "loungeTitle": "Fandom Lounge", + "likeCancel": "Unlike", + "like": "Like", + "commentDeleteConfirm": "Delete this comment?", + "commonShare": "Share", + "commonEditAction": "Edit", + "commonDeleteAction": "Delete", + "commonBlockAction": "Block", + "commentFirst": "Be the first to comment.", + "postDeleteConfirm": "Delete this post?", + "reportReceived": "Report received.", + "commonDelete": "Delete", + "commentHint": "Write a comment.", + "commonSend": "Send", + "postWrite": "Write post", + "postEdit": "Edit post", + "loungeHint": "Write freely in the Fandom Lounge.", + "photoRemove": "Remove photo {n}", + "camera": "Camera", + "album": "Album", + "historyTitle": "PASSPORT · History", + "historyMine": "My activity", + "historyEvolution": "Evolved Stage {from} → {to}", + "historyHibernation": "Planet dormant", + "historyWakeUp": "Planet awakened", + "historyZap": "ZAP contribution", + "historyQuest": "Quest reward", + "historyAdBonus": "Ad bonus reward", + "historyFunding": "Campaign contribution", + "historyGrowthPop": "{amount} POP added to growth", + "historyJoined": "You were part of this evolution", + "historyLoadFailed": "Couldn't load history.", + "historyMineEmpty": "No activity yet.", + "historyFandomEmpty": "No fandom history yet.", + "historyMore": "Load earlier history", + "countryName": "{code, select, KR{South Korea} JP{Japan} US{United States} CN{China} TW{Taiwan} HK{Hong Kong} TH{Thailand} VN{Vietnam} ID{Indonesia} PH{Philippines} MY{Malaysia} SG{Singapore} IN{India} GB{United Kingdom} FR{France} DE{Germany} ES{Spain} BR{Brazil} MX{Mexico} CA{Canada} AU{Australia} ZZ{Unspecified} other{-}}", + "loungeMaxPhotos": "You can upload up to {count} photos.", + "commonLoadFailed": "Couldn't load", + "planetPassportHistory": "PASSPORT · Fandom history", + "planetNamed": "Planet {name}", + "blockNebulaConfirm": "Block this nebula?", + "blockPlanetConfirm": "Block this planet?", + "blocked": "Blocked.", + "planetLastUpdated": "Last updated {time}", + "planetContribution": "Contributions", + "planetNebulaGrowth": "Nebula growth", + "planetLiveRanking": "Live contribution ranking", + "planetNoContributors": "No contributors yet", + "planetContributeCta": "Contribute and climb the ranking!", + "planetOverallRank": "Overall #{rank}", + "planetMyContribution": "My share", + "planetTallying": "Tallying soon", + "planetTopPercent": "Top {percent}%", + "planetHideMember": "Hide all posts from this member", + "reportReasonUnknown": "Couldn't verify the report reason.", + "reportAlready": "You've already reported this post.", + "blockUnavailable": "This author can't be blocked.", + "commonMe": "Me", + "planetSizeMedium": "Mid-size fandom", + "planetPerHead": "Power per member: {value}", + "planetJoined": "Joined", + "planetJoin": "Join", + "questDetails": "Details", + "questInProgress": "In progress", + "questTypeAttend": "Check-in", + "questTypeSteps": "Walking", + "questTypePlace": "Location", + "questTypeStreaming": "Streaming", + "questTypeUnverifiable": "The verification method for this quest type isn't set yet.", + "questTabMine": "My quests", + "placePickPrompt": "Choose where to\ncomplete the quest.", + "placeSearch": "Search places", + "commonNoResults": "No results.", + "commonSelectDone": "Done", + "adMoreChance": "Get more chances to contribute.", + "adWatchPrompt": "Watch an ad to get\na bonus reward!", + "adBonusPlaceholder": "+ Contribution POP", + "adPartyPack": "Party Pack", + "adPickProduct": "Choose item", + "adWatchBonus": "Watch ad for bonus reward", + "adBasicReward": "Get basic reward", + "focusMode": "Ad-free Focus mode", + "pricePerMonth": "{price}/mo", + "adWarpBefore": "Warping... Finding the right ad so you can contribute more to your fandom and artist.", + "adWarpAfter": "Warping... Preparing the reward for your contribution.", + "adSkip": "Skip", + "questPreviewHint": "Pick options and check the preview", + "questNoCategory": "No category selected yet", + "questNew": "New quest", + "questCategory": "Category", + "questName": "Quest name", + "questAction": "Action details", + "questPickPlace": "Choose a target location", + "questGoalValue": "Goal", + "questParticipants": "Participants", + "questRewardPop": "Completion reward POP", + "questCreateConfirm": "Create with these details?", + "questCreateSummary": "{title} · Goal {goal} · Reward {reward} POP", + "commonKeepEditing": "Keep editing", + "questCreate": "Create", + "questNamed": "{title} quest", + "questDailyHelp": "Help your planet every day", + "questTodayMust": "Quests to do today", + "questTabEmpty": "No quests in this tab yet.", + "questCreateNotReady": "Quest creation opens together with reward funding. Coming soon.", + "questNoBaseReward": "Couldn't find the basic reward record, so the ad bonus isn't available.", + "questNoSteps": "No steps recorded yet. Walk a bit and try again.", + "questNoPlace": "No location is registered for this quest.", + "questGoal": "Goal", + "questReward": "Reward", + "questPopGrant": "POP reward", + "commonChecking": "Checking…", + "questMine": "This is your quest. {count} participants.", + "questStopConfirm": "Stop this quest?", + "questStopBody": "Your progress will be reset to the beginning.", + "questStop": "Stop", + "adNotFinished": "You didn't finish the ad, so there's no bonus. You still get the basic reward.", + "openSettings": "Open settings", + "permStepsDenied": "Step permission is off. Allow \"Physical activity\" in Settings.", + "permStepsNeeded": "Physical activity permission is needed to count steps.", + "permLocationOff": "Location services are off. Turn them on and try again.", + "permLocationNeeded": "Location permission is needed to verify your visit.", + "permLocationDenied": "Location permission is off. Allow location access in Settings.", + "questRewardReceived": " received!", + "questAdBonus": "Ad bonus +{pop} POP", + "questParticipantsCount": "Participants: {count}", + "questParticipantsCap": "Participants: {count} / {cap}", + "nebulaCreateFailed": "Couldn't create the nebula. Please try again later.", + "nebulaAlreadyToday": "You've already created a nebula today.", + "nebulaCreatedKeepPrimary": "Nebula created. Your main planet stays the same because it can only be changed 30 days after the last change.", + "fandomFounderLeaveBlocked": "You can leave after the planet is born.", + "sortPopular": "Popular", + "campaignSortContributing": "Contributing", + "campaignStatusReview": "This campaign is under review.", + "campaignStatusRejected": "This campaign was rejected in review. Your registration fee has been returned.", + "campaignStatusWithdrawn": "This campaign was withdrawn by its creator.", + "campaignStatusCancelled": "This campaign was cancelled for operational reasons. The registration fee and supported POP have been returned.", + "rankWindowDaily": "Daily", + "rankWindowWeekly": "Weekly", + "campaignNotFound": "Campaign not found.", + "campaignMediaMain": "Main image", + "campaignLoadFailed": "Could not load the campaign.", + "campaignTabInfo": "Campaign info", + "campaignTabBoard": "Cheers {count}", + "campaignWithdraw": "Withdraw campaign", + "campaignContributeAction": "Contribute", + "campaignFundingStatus": "Funding status", + "campaignSupporting": "Supporters", + "campaignTimeLeft": "Left!", + "campaignReport": "Result report", + "campaignReportPhotos": "Execution photos", + "campaignBudget": "Budget breakdown", + "campaignTotal": "Total", + "campaignReceipts": "Receipts", + "campaignDaysLeft": "{days}d {time}", + "campaignWithdrawTitle": "Withdraw this campaign?", + "campaignWithdrawRefund": "Since the campaign is not public yet, the {fee} POP registration fee will be returned in the currency you paid.", + "campaignWithdrawNoRefund": "This campaign is already public, so the {fee} POP registration fee will not be returned.", + "campaignWithdrawConfirm": "Withdraw", + "shareCopied": "Share text copied.", + "campaignContributePop": "Set POP to contribute", + "campaignEmptyContributing": "You haven't contributed to any campaigns yet.", + "campaignEmpty": "No campaigns yet.", + "campaignNew": "New campaign", + "campaignMyRanking": "My contribution rank", + "campaignWindowRanking": "{window} ranking", + "campaignSearchHint": "Search campaigns", + "campaignNoRecentSearches": "No recent searches", + "searchRemoveTerm": "Remove {term}", + "campaignNoResults": "No results", + "shareTopicCampaign": "Campaign", + "sharePlanetLine": "{brand} · {name} Planet", + "campaignGaugeLabel": "Campaign progress", + "campaignPeriodTitle": "Choose the campaign period", + "campaignPeriodMax": "You can choose up to one year", + "campaignYearMonth": "{month}/{year}", + "campaignWeekdays": "S,M,T,W,T,F,S", + "campaignPeriodRange": "{startMonth}/{startDay} ~ {endMonth}/{endDay}", + "campaignPeriodSelect": "Select period", + "calendarPrevMonth": "Previous month", + "calendarNextMonth": "Next month", + "campaignShareTitle": "Share campaign", + "campaignPickMedia": "Choose image/video", + "campaignTitleHint": "Enter a campaign title.", + "campaignBodyHint": "Describe your campaign.", + "campaignReviewNote": "Once submitted, funding opens after review.", + "campaignSupportType": "Choose support type", + "campaignPeriod": "Campaign period", + "campaignDateSelect": "Select dates", + "campaignGoalPop": "Goal POP", + "campaignSubmitTitle": "Submit this campaign?", + "campaignSubmitBody": "{title} · Goal {goal} POP · {days} days\nThe {fee} POP registration fee will be deducted, and funding opens after review.", + "campaignSubmit": "Submit", + "campaignCreateFailed": "Could not submit the campaign.", + "campaignNeedsQuests": "Quests completed {done}/{need} — complete {left} more to open a campaign.", + "campaignPendingTitle": "Awaiting review", + "campaignPendingBody": "Once review is complete, funding opens and the campaign appears in the list.", + "insideRefundPending": "The acceptance deadline has passed. The bounty will be refunded.", + "insideRefundDone": "The acceptance deadline has passed. The bounty has been refunded.", + "insideRefundChecking": "The acceptance deadline has passed. We are checking the bounty refund.", + "insideErrPopShort": "Not enough POP to set the bounty.", + "insideErrSelfAccept": "You can't accept your own answer.", + "insideErrAlreadyAccepted": "An answer has already been accepted and cannot be changed.", + "insideErrNotOwner": "Only the person who asked can accept an answer.", + "commonCheckConnection": "Check your connection and try again.", + "insideAnswerLater": "Comments open once the server is connected.", + "insideAcceptConfirm": "Accept this comment?\nThe {pop} POP bounty will be given and this cannot be undone.", + "insideAccept": "Accept", + "insideMore": "More", + "insideFirstAnswer": "Be the first to comment.", + "insideAcceptDone": "Accepted", + "insideAcceptAction": "Accept", + "insideAnswerHint": "Write a comment.", + "insideLoadFailed": "Could not load INSIDE", + "insideEmpty": "No questions yet.", + "insideWrite": "Write INSIDE", + "insideSearchHint": "INSIDE title, content", + "insideBodyHint": "Ask and answer freely with other fans.", + "insideTitleRequired": "Please enter a title.", + "insideBodyRequired": "Please enter the content.", + "insideBountySet": "Set bounty", + "insideBountyHint": "Set {min} or more.", + "insideAttachLater": "Image attachments open once the server is connected.", + "insideBountyRequired": "Please enter a bounty.", + "insideNumberOnly": "Please enter a number.", + "insideBountyMin": "The bounty must be at least {min} POP.", + "insideBountyOverBalance": "Can't exceed your POP balance.", + "questTypeTimeAttack": "Flash Time Attack", + "questTimeLeft": "Time left", + "questTimeAttackClosed": "Closed", + "questTimeAttackLeft": "{time} left", + "popReasonQuestTimeAttack": "Flash time attack", + "briefingShareCopied": "Share text and link copied.", + "shareCopyText": "Copy text", + "briefingShareTextCopied": "Share text copied.", + "questAlreadyRewarded": "Reward already received (+{pop} POP).", + "questCatHealth": "Health", + "questCatPlanet": "Planet activity", + "questCatCommunity": "Community", + "questActShake": "Shake", + "questTypeVote": "Vote", + "questTypeSns": "Social media", + "questActivity": "Activity", + "questNoCreatable": "No quest activities are open for creation right now.", + "questDescriptionOptional": "Description (optional)", + "questLinkUrl": "Link URL", + "questLinkCheck": "Check link", + "questLinkInvalid": "This link isn't valid. Please check the address.", + "questLinkPreviewFailed": "Couldn't load the link info. Please enter a title.", + "questLinkTitle": "Link title", + "questEndDate": "End date", + "questEndDefault": "24 hours after publishing", + "questEndDateValue": "Ends {date} 23:59:59", + "questPoolHelper": "Deposit {pool} POP · Fee {fee} POP", + "questCreateCost": "{title}\nReward {reward} POP × {count} = {pool} POP\nFee {fee} POP (burned on publishing)\n{total} POP in total will be deducted.", + "questNoReward": "This quest has no reward.", + "questDeadline": "Deadline", + "questRemainingHours": "{hours}h left", + "questEnded": "Ended", + "questAuthorNote": "Creator's note", + "questRepeatOnce": "One entry per person", + "questRepeatEvery": "Every {hours}h · up to {count} times", + "questSubmit": "Submit proof", + "questResubmit": "Resubmit", + "questEvidenceUrl": "Proof link (optional)", + "questEvidenceText": "Proof details", + "questSubmitted": "Submitted. The reward is paid after review.", + "questReviewPending": "Under review. The reward is paid once approved.", + "questReviewDelayed": "Review is taking longer than usual. Our team is checking.", + "questApprovedPaying": "Approved. Paying your reward.", + "questRejected": "Rejected: {reason}", + "questResubmitAllowed": "You can revise and resubmit.", + "questResubmitNotAllowed": "This entry can't be resubmitted.", + "questReviewButton": "In review", + "questRejectedButton": "Rejected", + "questReviewQueue": "{count} awaiting review", + "questApprove": "Approve", + "questReject": "Reject", + "questRejectReason": "Reason", + "questAllowResubmit": "Allow resubmission", + "questEdit": "Edit title and description", + "questEditNotice": "After publishing, only the title and description can be edited, and edits are logged.", + "popReasonQuestUgc": "Quest reward", + "popReasonQuestFee": "Quest creation fee", + "questFreeDailyCap": "You can create up to {cap} no-reward quests a day.", + "popReasonQuestPoolDeposit": "Quest reward deposit", + "popReasonQuestPoolReturn": "Quest reward returned", + "campaignCommentNeedNickname": "Please set a nickname first", + "campaignCommentSetNickname": "Set nickname" +} diff --git a/app/lib/l10n/app_es.arb b/app/lib/l10n/app_es.arb new file mode 100644 index 0000000..9abc607 --- /dev/null +++ b/app/lib/l10n/app_es.arb @@ -0,0 +1,791 @@ +{ + "@@locale": "es", + "commonConfirm": "Aceptar", + "commonCancel": "Cancelar", + "commonClose": "Cerrar", + "commonSelect": "Seleccionar", + "commonRetry": "Reintentar", + "commonView": "Ver", + "commonPrev": "Atrás", + "commonDone": "Listo", + "commonStart": "Empezar", + "commonNotYet": "La pantalla {where} llega en la segunda revisión (2026-09-10).", + "languageSettingsTitle": "Idioma", + "authLogin": "Iniciar sesión", + "authSignup": "Registrarse", + "authFirstTime": "¿Nuevo en FANUZU?", + "authAlreadyMember": "¿Ya tienes una cuenta?", + "authLoginHeadline": "¡Inicia sesión en FANUZU\nde la forma más fácil!", + "authSignupHeadline": "¡Únete a FANUZU\nde la forma más fácil!", + "authProviderLogin": "{provider, select, apple{Iniciar sesión con Apple} google{Iniciar sesión con Google} kakao{Iniciar sesión con Kakao} line{Iniciar sesión con LINE} other{Iniciar sesión con correo}}", + "authProviderSignup": "{provider, select, apple{Registrarse con Apple} google{Registrarse con Google} kakao{Registrarse con Kakao} line{Registrarse con LINE} other{Registrarse con correo}}", + "authNotConfiguredTitle": "Inicio con {name} próximamente", + "authNotConfiguredBody": "El inicio con {name} se habilitará cuando se entreguen las claves.\n\nRequerido\n· {items}", + "authNotConfiguredNotice": "El inicio con {name} aún no está listo.\nRequerido: {items}", + "authResetNeedEmail": "Primero introduce el correo para recibir el enlace.", + "authResetSent": "Enviamos las instrucciones a esa dirección.", + "termsTitle": "Términos de uso", + "termsAgreeAll": "Aceptar todo", + "termsService": "Términos del servicio (obligatorio)", + "termsLocation": "Términos de servicios de ubicación (obligatorio)", + "termsAge": "Tengo 14 años o más. (obligatorio)", + "termsMarketing": "Recibir información de marketing y eventos (opcional)", + "signupDoneTitle": "Registro completado", + "signupDoneBody": "Ya puedes empezar tus actividades de fandom.\nHaz crecer tu planeta con muchas actividades.", + "homeItemsTooltip": "Objetos", + "homeNotificationsTooltip": "Notificaciones", + "homeFandomHistory": "Historia del fandom", + "homePopularInside": "K-INSIDE popular", + "homeWhereBriefing": "Resumen IA", + "homeWhereInsideList": "Lista de INSIDE", + "homeWhereInsideDetail": "Detalle de INSIDE", + "homeLoadFailed": "No se pudo cargar el inicio", + "homeRetryLater": "Inténtalo de nuevo en un momento.", + "myProfileCardTooltip": "Tarjeta de perfil", + "myTabPlanets": "Planetas/Nebulosas", + "myTabActivity": "Actividad", + "myInside": "Inside", + "myCampaign": "Campañas", + "myShareLater": "Compartir en {target} se conectará en la versión 09-10.", + "myEditPhoto": "Editar foto de perfil", + "myEditProfile": "Editar perfil", + "settingsTitle": "Ajustes", + "settingsAccount": "Cuenta", + "settingsNickname": "Apodo", + "settingsEmail": "Correo", + "settingsSns": "Cuentas sociales vinculadas", + "settingsPopHistory": "Historial de POP", + "settingsPayments": "Pagos", + "settingsServicePayments": "Historial de pagos del servicio", + "settingsService": "Ajustes del servicio", + "settingsPush": "Notificaciones push", + "settingsBlocked": "Usuarios bloqueados", + "settingsInfo": "Información", + "settingsNotice": "Avisos", + "settingsInquiry": "Consulta 1:1", + "settingsAppVersion": "Versión de la app", + "settingsLatest": "Última versión", + "settingsLogout": "Cerrar sesión", + "settingsLogoutConfirm": "¿Quieres cerrar sesión?", + "settingsWithdraw": "Eliminar cuenta", + "settingsWithdrawConfirm": "¿Quieres eliminar tu cuenta?", + "settingsWithdrawBody": "Perderás tus POP y no se puede deshacer.", + "settingsWithdrawLater": "La eliminación de cuenta se conectará en la versión 09-10.", + "settingsUnlinkConfirm": "¿Desvincular esta cuenta?", + "settingsUnlinkBody": "Solo se quita este método de inicio social; los datos de tu cuenta no se eliminan.", + "settingsUnlink": "Desvincular", + "settingsSameEmailOnly": "Solo se pueden vincular cuentas sociales\ncon el mismo correo.", + "settingsLinkNotReady": "La vinculación con {name} aún no está lista. Requerido: {items}", + "settingsConnect": "Vincular", + "settingsDisconnect": "Desvincular", + "settingsEmailProvider": "Correo", + "pushDeviceOff": "Las notificaciones push están desactivadas en este dispositivo.", + "pushDeviceOffHint": "Permite las notificaciones en los ajustes del sistema.", + "pushRequesting": "Solicitando…", + "pushGoToSettings": "Ir a ajustes", + "pushOpeningDeviceSettings": "Abriendo los ajustes del dispositivo.", + "pushEnableInDevice": "Activa las notificaciones de FANUZU en los ajustes del dispositivo.", + "pushDeviceSection": "Notificaciones del dispositivo", + "pushSoundVibration": "Sonido y vibración", + "pushServiceSection": "Notificaciones del servicio", + "pushTopicEssential": "Esenciales", + "pushTopicFandomEvent": "Eventos del fandom", + "pushTopicBriefing": "AI Briefing", + "pushTopicCommunity": "Comunidad", + "pushTopicMarketing": "Marketing", + "linkTitle": "Vincular {name}", + "linkNotLinked": "No vinculada", + "linkLinked": "Vinculada", + "linkSameEmailOnly": "Solo se pueden vincular cuentas sociales con el mismo correo.", + "linkUnlink": "Desvincular", + "linkLink": "Vincular", + "noticeBadge": "Aviso", + "noticeTitle": "Avisos", + "noticeEmpty": "No hay avisos.", + "noticeDetailTitle": "Detalle del aviso", + "translationMark": "Traducido", + "translationReplyTo": "Traduciendo el comentario de {name}", + "translationWrite": "Escribir traducción", + "translationHint": "Escribe tu traducción", + "translationValid": "Válida", + "translationInvalid": "No válida", + "translationSelfMarkBlocked": "No puedes marcar tu propia traducción.", + "translationSubmitted": "Traducción publicada. Si se valida, recibirás POP en 24 horas.", + "translationMarked": "Marcada.", + "translationAlreadyMarked": "Ya marcaste esta traducción.", + "translationDuplicate": "Ya publicaste una traducción en este idioma.", + "translationFailed": "No se pudo procesar. Inténtalo de nuevo en un momento.", + "translationNotEligible": "Solo los miembros de este fandom con más de 24 horas de antigüedad pueden marcar.", + "translationDailyCap": "Hoy ya no puedes publicar más traducciones.", + "translationInvalidTarget": "Este contenido no se puede traducir.", + "commonRequestFailed": "No se pudo procesar la solicitud.", + "commonFan": "Fan", + "adLoadFailed": "No se pudo cargar el anuncio. Solo recibirás la recompensa básica.", + "storeNotReady": "Productos de la tienda en preparación", + "storePending": "Esperando la aprobación del pago. Se aplicará automáticamente al aprobarse.", + "storeFailed": "No se pudo completar el pago.", + "storePurchased": "Tu compra se ha aplicado.", + "heroParticipate": "ZAP+ · Unirse al regalo", + "heroRemainingSemantics": "HERO TIME restante: {minutes} min {seconds} s", + "heroRemaining": "HERO TIME · quedan {time}", + "heroClaims": "Regalos recibidos por fans: {count}", + "heroInProgress": "{name} · HERO TIME en curso →", + "stageNames": "Nebulosa,Inicio,Crecimiento,Juntos,Compañía,Luz,Eternidad", + "planetStageUnknown": "Planeta · Sin información de etapa", + "planetHibernatingSuffix": " · En reposo", + "planetEclipseSuffix": " · Eclipse en curso", + "planetHeroSuffix": " · HERO TIME en curso", + "evoPlanetGrowth": "Crecimiento del planeta", + "evoHeroEnd": "HERO TIME terminado", + "evoAwakening": "El planeta despierta", + "evoAnomaly": "Reacción anómala", + "evoEnergy": "Concentrando energía", + "evoTransition": "Transición", + "evoComplete": "Evolución del planeta completa", + "evoOpenHistory": "Ver historial de PASSPORT", + "playNow": "Juega ahora", + "playHeadline": "¡Vamos a conocer a tu fandom!", + "playBody": "Crea un planeta, conecta con fandoms y disfruta de la actividad fan...", + "timeJustNow": "Ahora", + "timeMinutesAgo": "hace {n} min", + "timeHoursAgo": "hace {n} h", + "timeDaysAgo": "hace {n} d", + "commonMore": "Ver más", + "authProviderStart": "{provider, select, email{Continuar con correo} kakao{Continuar con Kakao} line{Continuar con LINE} apple{Continuar con Apple} other{Continuar con Google}}", + "authProviderName": "{provider, select, email{Correo} kakao{Kakao} line{LINE} apple{Apple} other{Google}}", + "authLastProvider": "No puedes desvincular tu último método de inicio de sesión.", + "authLoginRequired": "Debes iniciar sesión.", + "authDifferentEmail": "Esta cuenta no coincide con tu correo de registro.", + "authLinkFailed": "No se pudo vincular la cuenta.", + "authNotLinked": "Esta cuenta no está vinculada.", + "authEmailUnavailable": "El inicio de sesión con correo no está disponible.", + "authCheckCredentials": "Revisa tu correo o contraseña.", + "authSocialFailed": "Error al iniciar sesión con la red social.", + "authAppleUnavailable": "Iniciar sesión con Apple no está disponible en este dispositivo.", + "authEmailInUse": "Este correo ya está registrado.", + "authWeakPassword": "La contraseña es muy corta. Usa al menos 8 caracteres.", + "authInvalidEmail": "El formato del correo no es válido.", + "commonTryLater": "Inténtalo de nuevo más tarde.", + "authCredentialInUse": "Esta cuenta social ya está vinculada a otra cuenta de FANUZU.", + "authAlreadyLinked": "Esta cuenta ya está vinculada.", + "authMethodUnavailable": "Este método de inicio de sesión aún no está disponible.", + "authRecentLogin": "Por seguridad, vuelve a iniciar sesión e inténtalo de nuevo.", + "authOtherProviderGuide": "La cuenta {email} se registró con otro método de inicio de sesión. Inicia sesión con el método que usaste primero y vincula este en MY > Ajustes > Cuentas vinculadas.", + "authThisAccount": "Esta", + "eclipseCapped": "Alcanzaste el límite de participación; algunas no se contaron.", + "eclipseResultUnknown": "No se pudo confirmar tu participación. Vuelve a comprobarlo con la misma solicitud.", + "eclipseChecking": "Comprobando participación", + "eclipseRetrySame": "Volver a comprobar", + "eclipseEnded": "El eclipse ya terminó o se defendió con éxito.", + "eclipseTooFast": "Estás participando demasiado rápido. Inténtalo en un momento.", + "eclipseNone": "No hay ningún eclipse en curso", + "eclipseSuccessTitle": "¡Detuviste\nel impacto del meteorito!", + "eclipseQualified": "Participación confirmada", + "eclipseFailTitle": "Lamentablemente,\nla defensa del planeta falló", + "eclipseFailBody": "No se alcanzó el objetivo de defensa de esta ronda.", + "eclipseCancelTitle": "Eclipse cancelado", + "eclipseCancelBody": "Se canceló por un error de recuento. No se descontó FPI.", + "eclipsePendingTitle": "Comprobando resultado de la defensa", + "eclipsePendingBody": "El tiempo de participación terminó. Esperando la decisión del servidor.", + "eclipseShakeTimed": "s: ¡agita tu móvil\npara detener el meteorito!", + "eclipseShake": "¡Agita tu móvil\npara detener el meteorito!", + "eclipseMyCount": "Mis participaciones: {count}", + "eclipsePendingSuffix": " · +{count} pendientes", + "eclipseSendingSuffix": " · Enviando", + "eclipseNoPopNote": "No se gasta POP. Haz al menos {count} sacudidas válidas para contar como participante.", + "eclipseTapToJoin": "¿No puedes agitar? Toca para participar", + "eclipseTimeLeft": "Tiempo restante del eclipse:", + "eclipseSeconds": " {seconds} s", + "eclipseBannerDefended": "Defensa exitosa: {name} sin pérdida de FPI", + "eclipseBannerActive": "Eclipse activado: {name} erosión {rate}%", + "eclipseBannerDefense": "Participantes válidos {count} / {target}", + "timeHoursMinutesLeft": "Quedan {hours} h {minutes} min", + "timeMinutesLeft": "Quedan {minutes} min", + "liveEclipseDefense": "[LIVE] ¡{name} se unió a la defensa del eclipse! (+{count})", + "liveHotTime": "[LIVE] ¡Comenzó el Artist Hot Time de {name}!", + "liveHeroGift": "[LIVE] ¡Llegó el Regalo del Héroe de {name}!", + "liveCampaign": "[LIVE] ¡{name} apoyó una campaña!", + "galaxySetPrimaryConfirm": "¿Establecer el planeta seleccionado como principal?", + "galaxyLoadFailed": "No se pudo cargar la galaxia", + "commonSearch": "Buscar", + "galaxyMyPlanets": "Mis planetas", + "galaxyMap": "Mapa de la galaxia", + "nebulaCreate": "Crear nebulosa", + "commonRecheck": "Volver a comprobar", + "zapApplying": "Aplicando contribución", + "zapNeedsCheck": "Hay que comprobar la contribución", + "zapNoPlanet": "No perteneces a ningún planeta", + "commonContribute": "Contribuir", + "commonItem": "Objetos", + "galaxyDefended": "Defensa exitosa · Sin pérdida de FPI", + "galaxyErosion": "Erosión {rate}% · Defensa {progress}%", + "galaxyOnlineMembers": "{online} en línea · {members} miembros", + "galaxySearchHint": "Buscar por nombre de fandom", + "commonRecentSearches": "Búsquedas recientes", + "commonClearAll": "Borrar todo", + "commonNoRecentSearches": "No hay búsquedas recientes.", + "galaxyNoHit": "No se encontró el fandom.\nRevisa el nombre.", + "galaxyMembers": "{count} miembros", + "galaxyPlanetRank": "Planeta {name}, puesto {rank}", + "galaxyMapOpen": "Abrir mapa de la galaxia", + "zapNotEnoughPop": "POP insuficiente", + "zapFailNoPop": "No tienes POP suficiente para contribuir.", + "zapFailLedger": "Se detuvo la contribución: el saldo no coincide con el historial.", + "zapFailRetry": "Revisa tu conexión y vuelve a comprobar la contribución.", + "zapDone": "Contribución completada", + "zapDonePop": "{pop} POP contribuidos", + "growthFounders": "Miembros fundadores", + "growthNebulaGoalPassed": "El plazo objetivo ha pasado · Cumple las condiciones y nacerá el planeta", + "growthFinalStage": "Etapa final", + "growthPromotionPending": "Ascenso pendiente", + "growthCheckConditions": "Ver condiciones de crecimiento", + "growthConditionChecking": "Comprobando condición de {name}", + "growthConditionMet": "Condición de {name} cumplida", + "growthConditionUnmet": "Condición de {name} no cumplida", + "nebulaHeadline": "Ponle nombre al planeta de tu fandom.", + "nebulaBody": "Si los miembros fundadores alcanzan la meta de POP en {days} días,\n¡nacerá como planeta oficial!\nQuienes participen en la evolución recibirán una insignia.", + "nebulaNameTaken": "Este nombre ya está en uso.", + "nebulaNameHint": "Nombre del fandom", + "nebulaGoal": "Meta ", + "nebulaFounders": " / Fundadores", + "nebulaGoalSemantics": "Meta {pop} POP · {members} fundadores · {days} días fijos", + "commonMyPop": "Mi POP: ", + "commonChange": "Cambiar", + "planetShape": "Forma {n}", + "planetBgColor": "Elige el color de fondo", + "planetColorSelect": "Elige el color del planeta", + "planetTypeSelect": "Elige el tipo de planeta", + "commonNext": "Siguiente", + "planetCreateFailed": "No se pudo crear el planeta. Inténtalo más tarde.", + "planetCreateConfirm": "¿Crear el planeta\ncon estas opciones?", + "commonCreate": "Crear", + "briefingOpenFailed": "No se puede abrir el original.", + "campaignAchieved": "{percent}% alcanzado", + "campaignView": "Ver campaña", + "growthFinalReached": "Has llegado a la etapa final", + "zapRetrySame": "Volver a comprobar la contribución", + "heroOwnerSuffix": "aporta:", + "heroStartPrompt": "¡Crea o únete a un planeta\ny empieza en FANUZU!", + "heroNebulaPrompt": "¡Usa el POP que ganas con actividades\npara contribuir a la nebulosa!", + "heroRecentContribution": "Creciendo sin parar con\n{pop} POP aportados.", + "heroGrowingTogether": "Un planeta fan que crecemos juntos", + "heroPopProgress": "Progreso de POP", + "heroNebula": "Nebulosa", + "heroNebulaTap": "¡Toca la nebulosa\npara que nazca un planeta!", + "heroCreateNebula": "Crear planeta (nebulosa)", + "heroJoinPlanet": "Unirse a un planeta", + "heroCreateOrJoin": "Crear o unirse a un planeta", + "insideAnswers": "Respuestas {count}", + "itemJoinFirst": "Únete a un planeta para enviar un Regalo del Héroe.", + "itemBuyNotReady": "La compra de objetos llegará pronto.", + "itemUseNotReady": "El uso de objetos llegará pronto.", + "payOpenFailed": "No se pudo abrir la ventana de pago.", + "itemBuy": "Comprar objetos", + "itemSelect": "Elegir objeto", + "commonComingSoon": "Próximamente.", + "itemSendPop": "Enviar {pop} POP", + "itemBuyerPop": "Comprador +{pop} POP", + "itemOwned": "Tienes: {count}", + "itemOwnedCount": "Tienes: {count}", + "growthOwnerOf": " aportó ", + "growthSteadySuffix": " y seguimos creciendo.", + "questAttended": "Asistencia registrada", + "questInProgressSpaced": "En curso", + "questRewardPaid": "Recompensa entregada", + "questClaim": "Reclamar", + "questStart": "Iniciar misión", + "questRewardDone": "Recompensa recibida", + "popKindGeneral": "POP normal", + "popKindContribution": "POP de contribución", + "popTxGrant": "Otorgado", + "popTxSpend": "Gastado", + "popTxRefund": "Reembolso", + "popTxVoid": "Anulado", + "popTypeQuest": "Misión", + "popTypeReward": "Recompensa", + "popTypePayment": "Pago", + "popTypeSubscription": "Suscripción", + "popTypeAdmin": "Equipo", + "popTypeFee": "Tarifa", + "commonEtc": "Otros", + "popReasonQuestSteps": "Misión de pasos", + "popReasonQuestMusicShake": "Misión Music Shake", + "popReasonQuestPlace": "Misión de visita", + "popReasonQuestAttend": "Misión de asistencia", + "popReasonAdReward": "Recompensa por anuncio", + "popReasonHeroDrop": "Regalo del Héroe", + "popReasonHeroDropTrigger": "Regalo del Héroe activado", + "popReasonTranslation": "Traducción colaborativa", + "popReasonIapPurchase": "Compra", + "popReasonFocusDaily": "Recompensa diaria Focus", + "popReasonPreregBonus": "Recompensa de prerregistro", + "popReasonReferral": "Invitación de amigos", + "popTypeReferral": "Referido", + "popReasonAdminGrant": "Otorgado por el equipo", + "popReasonAdminClawback": "Retirado por el equipo", + "popReasonCampaignFund": "Apoyo a campaña", + "popReasonCampaignRefund": "Reembolso de campaña", + "popReasonCampaignFeeRefund": "Reembolso de la tarifa de campaña", + "popReasonCampaignFee": "Tarifa de campaña", + "popReasonQnaBounty": "Recompensa de INSIDE", + "popReasonQnaRefund": "Reembolso de recompensa", + "popReasonQnaReward": "Recompensa por respuesta aceptada", + "popReasonEclipseDefense": "Contribución Zap", + "popReasonOther": "Cambio de POP", + "popRepeat": "{title} ×{count}", + "payStatePaid": "Pagado", + "payStateRefundRequested": "Reembolso solicitado", + "payStateRefundApproved": "Reembolso aprobado", + "payStateRefunded": "Reembolsado", + "payStateRefundRejected": "Reembolso rechazado", + "pushTopicEssentialDesc": "Avisos de cuenta, pagos y mantenimiento. No se pueden desactivar.", + "pushTopicFandomEventDesc": "Novedades de eclipses, campañas, misiones y Artist Hot Time.", + "pushTopicBriefingDesc": "Tarjetas de resumen diario de tu planeta.", + "pushTopicCommunityDesc": "Comentarios, me gusta y respuestas en tus publicaciones.", + "pushTopicMarketingDesc": "Recibe eventos y beneficios. Solo se activa si aceptas.", + "inquiryPending": "Recibida", + "inquiryInProgress": "En proceso", + "inquiryAnswered": "Respondida", + "photoBadType": "Solo puedes subir imágenes JPG, PNG, WEBP o GIF.", + "photoTooBig": "La foto supera los 10 MB. Elige una más pequeña.", + "photoUnreadable": "No se pudo leer la foto. Elige otra vez.", + "myListInside": "Mi INSIDE", + "myListInsideEmpty": "Aún no has publicado en INSIDE.", + "myListCampaign": "Mis campañas", + "myListCampaignEmpty": "Aún no has creado campañas.", + "sortLatest": "Recientes", + "sortPop": "Por POP", + "sortSolved": "Aceptadas", + "sortAchieved": "Logradas", + "payNotFound": "No se encontró el pago.", + "payItemFallback": "Artículo comprado", + "payMethodInApp": "Compra en la app", + "unknownUser": "Usuario desconocido", + "blockSelf": "No puedes bloquearte a ti mismo.", + "inquiryFallback": "Consulta", + "photoRejectedServer": "No se pudo subir la foto. Revisa el formato y el tamaño.", + "photoUploadFailed": "Error al subir la foto. Inténtalo más tarde.", + "focusClaimed": "Recibiste la recompensa Focus de hoy: +{pop} POP de contribución.", + "focusActive": "Modo Focus sin anuncios activo", + "focusUntil": "Hasta el {date} · La recompensa de hoy solo se reclama hoy", + "focusClaimedToday": "Recibida hoy", + "focusClaimToday": "Reclamar hoy", + "storeConnectFailed": "No se pudo conectar con la tienda. Inténtalo más tarde.", + "payRestore": "Restaurar compras", + "payEmpty": "No hay historial de pagos.", + "payRefundGuide": "Consumibles: reembolsables en 7 días desde la compra si no se usaron. Los artículos usados parcialmente no son reembolsables.\nSuscripciones: reembolsables en 24 horas desde el pago si no se usaron beneficios (p. ej., recompensas extra).\nEl reembolso se completa en 7–14 días tras la aprobación.", + "payDetailTitle": "Detalle del pago", + "payNumber": "N.º de pago", + "payDate": "Fecha de pago", + "payMethod": "Método de pago", + "payStatus": "Estado del pago", + "payAmount": "Importe", + "payRefundInfo": "Política de reembolso", + "payRefundApply": "Solicitar reembolso", + "payRefundUnavailable": "No reembolsable", + "refundReasonMistake": "Pagué por error", + "refundReasonExpectation": "No es lo que esperaba", + "refundReasonError": "No pude usarlo por un error", + "refundReasonDuplicate": "Pago duplicado", + "refundTitle": "Reembolso", + "refundSubmit": "Enviar", + "refundReason": "Motivo del reembolso", + "refundConfirm": "¿Seguro que quieres solicitar el reembolso?", + "myPop": "Mi POP", + "commonNoHistory": "Sin historial.", + "nicknameEdit": "Editar apodo", + "nicknameReserved": "No puedes usar un nombre que parezca del equipo.", + "nicknameTaken": "Este apodo ya está en uso.", + "albumItems": "Opciones de foto", + "takePhoto": "Tomar foto", + "pickFromAlbum": "Elegir del álbum", + "tierGuide": "Guía de niveles", + "statSteps": "Pasos totales", + "statContribution": "POP aportado total", + "statQuests": "Misiones completadas", + "shareInstagram": "Instagram", + "shareTiktok": "TikTok", + "shareCopyLink": "Copiar enlace", + "inquiryListTitle": "Consultas 1:1", + "inquiryEmpty": "No tienes consultas.", + "inquiryWrite": "Escribir consulta", + "inquiryDetail": "Detalle de la consulta", + "commonEdit": "Editar", + "inquiryAnswer": "Respuesta", + "inquiryNew": "Nueva consulta", + "inquiryEdit": "Editar consulta", + "commonTitle": "Título", + "commonTitleHint": "Escribe un título.", + "inquiryBody": "Detalles", + "inquiryBodyHint": "Describe tu consulta.", + "inquiryBodyHelper": "Con 10 caracteres o más podremos ayudarte antes.", + "inquirySubmitFailed": "No se pudo enviar la consulta. {reason}", + "inquiryEditFailed": "No se pudo editar la consulta. {reason}", + "inquirySubmitted": "Consulta enviada.", + "inquiryEdited": "Consulta actualizada.", + "blockedEmpty": "No hay cuentas bloqueadas.", + "unblock": "Desbloquear", + "unblockFailed": "No se pudo desbloquear. {reason}", + "tierInfoTitle": "Info de TIER", + "tierPromptPrefix": "Sube tu ", + "tierPromptRank": "rango", + "tierPromptSuffix": "\ncontribuyendo a planetas y nebulosas", + "tierPassportNote": "Passport Tier es un grado de apariencia de tarjeta, independiente del nivel, y nunca baja.", + "tierTotalContribution": "Contribución total", + "notifKindEclipse": "Eclipse", + "notifKindItemUsed": "Objeto usado", + "notifKindLike": "Me gusta", + "notifKindComment": "Comentarios", + "notifKindCampaign": "Meta de campaña", + "notifKindQuest": "Misión completada", + "notifTargetPost": "la publicación", + "notifTargetCampaign": "la campaña", + "notifTargetQuest": "el detalle de la misión", + "dayToday": "Hoy", + "dayYesterday": "Ayer", + "notifEmpty": "No hay notificaciones.", + "notifGoTo": "Yendo a {target}.", + "stationOfficial": "Oficial", + "stationShop": "Tiendas", + "stationTicket": "Entradas", + "stationMedia": "Medios", + "stationLoadFailed": "No se pudo cargar STATION", + "commonAll": "Todo", + "stationSuggest": "Sugerir tienda", + "stationEmpty": "Aún no hay enlaces.\nÚnete a un planeta para ver la selección.", + "stationCategoryEmpty": "Aún no hay enlaces en esta categoría.", + "stationSuggestLater": "Las sugerencias se abrirán al conectar el servidor.", + "commonSubmit": "Enviar", + "commonName": "Nombre", + "commonNameRequired": "Escribe un nombre.", + "commonUrl": "URL", + "commonUrlRequired": "Escribe una URL.", + "commonUrlHttps": "Escribe una URL que empiece por https.", + "commonCategory": "Categoría", + "stationReason": "Por qué lo recomiendas (opcional)", + "stationSuggestNote": "Los enlaces sugeridos se añaden tras una revisión. Te avisaremos del resultado.", + "heroGiftUnavailable": "Este regalo no se puede recibir ahora.", + "heroGiftAlready": "Ya participaste en este regalo. Revisa tu historial de POP.", + "heroGiftRelogin": "Vuelve a iniciar sesión para participar.", + "heroGiftNoPermission": "No tienes permiso para participar en este regalo.", + "heroGiftHowTo": "Agita el móvil o toca el botón para unirte al regalo de energía de luz.", + "heroGiftPopNote": "El POP que recibes depende del regalo restante y del límite por persona.", + "heroGiftReceived": "{total} POP recibidos", + "heroGiftUnknown": "No se pudo confirmar el pago. Vuelve a comprobarlo.", + "heroGiftNone": "No hay regalos disponibles ahora.", + "heroGiftConfirmed": "Se confirmó la recepción del regalo.", + "heroGiftChecking": "Confirmando pago…", + "heroGiftRetry": "Volver a comprobar el regalo", + "heroGiftTap": "Participar con el botón", + "rankMyPlanet": "Ranking de {metric} de mi planeta", + "rankPlanetEmpty": "Aún no hay ranking de planetas", + "rankScopeLive": "En vivo", + "rankScopeCountry": "Por país", + "rankMine": "Mi ranking de {metric}", + "rankMyCountry": "Ranking de {metric} de mi país", + "rankEmpty": "Aún no hay ranking", + "rankScope": "Ranking {scope}", + "rankLoadFailed": "No se pudo cargar el ranking", + "rankMeSuffix": "{name} (yo)", + "reportDefamation": "Difamación o insultos al artista", + "reportInappropriate": "Contenido inapropiado", + "reportSpam": "Spam o publicidad comercial", + "reportPrivacy": "Exposición de datos personales o suplantación", + "reportTitle": "Denunciar", + "reportSubmit": "Denunciar", + "reportAuthor": "Autor", + "reportReasonLabel": "Motivo", + "reportConfirm": "¿Denunciar esta publicación?\nNo se puede deshacer.", + "blockMemberConfirm": "¿Bloquear todas las publicaciones de este miembro?", + "loungeMembersOnly": "Solo los miembros del fandom de este planeta pueden publicar, comentar o dar me gusta.", + "loungeFailed": "No se pudo completar. Inténtalo más tarde.", + "loungeTitle": "Lounge del fandom", + "likeCancel": "Quitar me gusta", + "like": "Me gusta", + "commentDeleteConfirm": "¿Eliminar este comentario?", + "commonShare": "Compartir", + "commonEditAction": "Editar", + "commonDeleteAction": "Eliminar", + "commonBlockAction": "Bloquear", + "commentFirst": "Sé el primero en comentar.", + "postDeleteConfirm": "¿Eliminar esta publicación?", + "reportReceived": "Denuncia recibida.", + "commonDelete": "Eliminar", + "commentHint": "Escribe un comentario.", + "commonSend": "Enviar", + "postWrite": "Escribir", + "postEdit": "Editar publicación", + "loungeHint": "Escribe libremente en el lounge del fandom.", + "photoRemove": "Quitar foto {n}", + "camera": "Cámara", + "album": "Álbum", + "historyTitle": "PASSPORT · Historial", + "historyMine": "Mi actividad", + "historyEvolution": "Evolución Stage {from} → {to}", + "historyHibernation": "Planeta en reposo", + "historyWakeUp": "Planeta despertó", + "historyZap": "Contribución ZAP", + "historyQuest": "Recompensa de Quest", + "historyAdBonus": "Recompensa extra por anuncio", + "historyFunding": "Contribución a Campaign", + "historyGrowthPop": "{amount} POP sumados al crecimiento", + "historyJoined": "Participaste en esta evolución", + "historyLoadFailed": "No se pudo cargar el historial.", + "historyMineEmpty": "Aún no hay actividad.", + "historyFandomEmpty": "Aún no hay historia del fandom.", + "historyMore": "Ver historial anterior", + "countryName": "{code, select, KR{Corea del Sur} JP{Japón} US{Estados Unidos} CN{China} TW{Taiwán} HK{Hong Kong} TH{Tailandia} VN{Vietnam} ID{Indonesia} PH{Filipinas} MY{Malasia} SG{Singapur} IN{India} GB{Reino Unido} FR{Francia} DE{Alemania} ES{España} BR{Brasil} MX{México} CA{Canadá} AU{Australia} ZZ{Sin especificar} other{-}}", + "loungeMaxPhotos": "Puedes subir hasta {count} fotos.", + "commonLoadFailed": "No se pudo cargar", + "planetPassportHistory": "PASSPORT · Historia del fandom", + "planetNamed": "Planeta {name}", + "blockNebulaConfirm": "¿Bloquear esta nebulosa?", + "blockPlanetConfirm": "¿Bloquear este planeta?", + "blocked": "Bloqueado.", + "planetLastUpdated": "Última actualización {time}", + "planetContribution": "Contribuciones", + "planetNebulaGrowth": "Crecimiento de la nebulosa", + "planetLiveRanking": "Ranking de contribución en vivo", + "planetNoContributors": "Aún no hay contribuyentes", + "planetContributeCta": "¡Contribuye y entra en el ranking!", + "planetOverallRank": "Puesto general {rank}", + "planetMyContribution": "Mi aporte", + "planetTallying": "Cálculo pendiente", + "planetTopPercent": "Top {percent}%", + "planetHideMember": "Ocultar todas las publicaciones de este miembro", + "reportReasonUnknown": "No se pudo verificar el motivo.", + "reportAlready": "Ya denunciaste esta publicación.", + "blockUnavailable": "No se puede bloquear a este autor.", + "commonMe": "Yo", + "planetSizeMedium": "Fandom mediano", + "planetPerHead": "Poder por miembro: {value}", + "planetJoined": "Unido", + "planetJoin": "Unirse", + "questDetails": "Detalles", + "questInProgress": "En curso", + "questTypeAttend": "Asistencia", + "questTypeSteps": "Caminar", + "questTypePlace": "Ubicación", + "questTypeStreaming": "Streaming", + "questTypeUnverifiable": "Aún no se ha definido cómo verificar este tipo de misión.", + "questTabMine": "Mis misiones", + "placePickPrompt": "Elige dónde\ncompletar la misión.", + "placeSearch": "Buscar lugar", + "commonNoResults": "Sin resultados.", + "commonSelectDone": "Listo", + "adMoreChance": "Consigue más oportunidades de contribuir.", + "adWatchPrompt": "¡Mira un anuncio para\nconseguir una recompensa extra!", + "adBonusPlaceholder": "+ POP de contribución", + "adPartyPack": "Pack de fiesta", + "adPickProduct": "Elegir producto", + "adWatchBonus": "Ver anuncio y recibir extra", + "adBasicReward": "Recibir recompensa básica", + "focusMode": "Modo Focus sin anuncios", + "pricePerMonth": "{price}/mes", + "adWarpBefore": "Warping... Buscando el anuncio ideal para que aportes más a tu fandom y artista.", + "adWarpAfter": "Warping... Preparando la recompensa por tu contribución.", + "adSkip": "Omitir", + "questPreviewHint": "Elige opciones y revisa la vista previa", + "questNoCategory": "Aún no hay categoría seleccionada", + "questNew": "Nueva misión", + "questCategory": "Categoría", + "questName": "Nombre de la misión", + "questAction": "Acción detallada", + "questPickPlace": "Elige la ubicación objetivo", + "questGoalValue": "Meta", + "questParticipants": "Participantes", + "questRewardPop": "POP de recompensa", + "questCreateConfirm": "¿Crear con estos datos?", + "questCreateSummary": "{title} · Meta {goal} · Recompensa {reward} POP", + "commonKeepEditing": "Seguir editando", + "questCreate": "Crear", + "questNamed": "Misión {title}", + "questDailyHelp": "Ayuda a tu planeta cada día", + "questTodayMust": "Misiones para hoy", + "questTabEmpty": "Aún no hay misiones en esta pestaña.", + "questCreateNotReady": "La creación de misiones se abrirá junto con los fondos de recompensa. Próximamente.", + "questNoBaseReward": "No se encontró la recompensa básica, así que no hay extra por anuncio.", + "questNoSteps": "Aún no hay pasos registrados. Camina un poco y vuelve a intentarlo.", + "questNoPlace": "Esta misión no tiene una ubicación registrada.", + "questGoal": "Meta", + "questReward": "Recompensa", + "questPopGrant": "Recompensa de POP", + "commonChecking": "Comprobando…", + "questMine": "Es tu misión. Participantes: {count}.", + "questStopConfirm": "¿Detener esta misión?", + "questStopBody": "Tu progreso se reiniciará desde el principio.", + "questStop": "Detener", + "adNotFinished": "No terminaste el anuncio, así que no hay extra. Recibes la recompensa básica.", + "openSettings": "Abrir ajustes", + "permStepsDenied": "El permiso de pasos está desactivado. Permite \"Actividad física\" en Ajustes.", + "permStepsNeeded": "Se necesita el permiso de actividad física para contar pasos.", + "permLocationOff": "La ubicación del dispositivo está desactivada. Actívala e inténtalo de nuevo.", + "permLocationNeeded": "Se necesita el permiso de ubicación para verificar tu visita.", + "permLocationDenied": "El permiso de ubicación está desactivado. Permite el acceso en Ajustes.", + "questRewardReceived": " ¡recibidos!", + "questAdBonus": "Extra por anuncio +{pop} POP", + "questParticipantsCount": "Participantes: {count}", + "questParticipantsCap": "Participantes: {count} / {cap}", + "nebulaCreateFailed": "No se pudo crear la nebulosa. Inténtalo más tarde.", + "nebulaAlreadyToday": "Ya creaste una nebulosa hoy.", + "nebulaCreatedKeepPrimary": "Nebulosa creada. Tu planeta principal no cambia: solo puede cambiarse 30 días después del último cambio.", + "fandomFounderLeaveBlocked": "Podrás salir cuando nazca el planeta.", + "sortPopular": "Populares", + "campaignSortContributing": "Contribuyendo", + "campaignStatusReview": "Esta campaña está en revisión.", + "campaignStatusRejected": "Esta campaña fue rechazada en la revisión. Te hemos devuelto la tarifa de registro.", + "campaignStatusWithdrawn": "El creador retiró esta campaña.", + "campaignStatusCancelled": "Se canceló por motivos operativos. Te hemos devuelto la tarifa de registro y los POP aportados.", + "rankWindowDaily": "Diario", + "rankWindowWeekly": "Semanal", + "campaignNotFound": "No se encontró la campaña.", + "campaignMediaMain": "Imagen principal", + "campaignLoadFailed": "No se pudo cargar la campaña.", + "campaignTabInfo": "Información", + "campaignTabBoard": "Apoyos {count}", + "campaignWithdraw": "Retirar campaña", + "campaignContributeAction": "Contribuir", + "campaignFundingStatus": "Estado de la financiación", + "campaignSupporting": "Apoyando", + "campaignTimeLeft": "¡Restante!", + "campaignReport": "Informe de resultados", + "campaignReportPhotos": "Fotos de ejecución", + "campaignBudget": "Desglose del presupuesto", + "campaignTotal": "Total", + "campaignReceipts": "Recibos", + "campaignDaysLeft": "{days} d {time}", + "campaignWithdrawTitle": "¿Retirar esta campaña?", + "campaignWithdrawRefund": "Como aún no es pública, te devolveremos la tarifa de registro de {fee} POP tal como la pagaste.", + "campaignWithdrawNoRefund": "La campaña ya es pública, por lo que la tarifa de registro de {fee} POP no se devolverá.", + "campaignWithdrawConfirm": "Retirar", + "shareCopied": "Texto para compartir copiado.", + "campaignContributePop": "Elige los POP a aportar", + "campaignEmptyContributing": "Aún no has contribuido a ninguna campaña.", + "campaignEmpty": "No hay campañas.", + "campaignNew": "Nueva campaña", + "campaignMyRanking": "Mi ranking de contribución", + "campaignWindowRanking": "Ranking {window}", + "campaignSearchHint": "Buscar campañas", + "campaignNoRecentSearches": "No hay búsquedas recientes", + "searchRemoveTerm": "Eliminar {term}", + "campaignNoResults": "Sin resultados", + "shareTopicCampaign": "Campaña", + "sharePlanetLine": "{brand} · Planeta {name}", + "campaignGaugeLabel": "Progreso de la campaña", + "campaignPeriodTitle": "Elige el periodo de la campaña", + "campaignPeriodMax": "Puedes elegir hasta un año", + "campaignYearMonth": "{month}/{year}", + "campaignWeekdays": "D,L,M,X,J,V,S", + "campaignPeriodRange": "{startDay}/{startMonth} ~ {endDay}/{endMonth}", + "campaignPeriodSelect": "Seleccionar periodo", + "calendarPrevMonth": "Mes anterior", + "calendarNextMonth": "Mes siguiente", + "campaignShareTitle": "Compartir campaña", + "campaignPickMedia": "Elegir imagen/vídeo", + "campaignTitleHint": "Escribe el título de la campaña.", + "campaignBodyHint": "Describe tu campaña.", + "campaignReviewNote": "Tras enviarla, la financiación se abre después de la revisión.", + "campaignSupportType": "Elige el tipo de apoyo", + "campaignPeriod": "Periodo de la campaña", + "campaignDateSelect": "Seleccionar fechas", + "campaignGoalPop": "POP objetivo", + "campaignSubmitTitle": "¿Enviar esta campaña?", + "campaignSubmitBody": "{title} · Objetivo {goal} POP · {days} días\nSe descontará la tarifa de registro de {fee} POP y la financiación se abrirá tras la revisión.", + "campaignSubmit": "Enviar", + "campaignCreateFailed": "No se pudo enviar la campaña.", + "campaignNeedsQuests": "Misiones completadas {done}/{need}: completa {left} más para abrir una campaña.", + "campaignPendingTitle": "Pendiente de revisión", + "campaignPendingBody": "Cuando termine la revisión, se abrirá la financiación y aparecerá en la lista.", + "insideRefundPending": "El plazo de aceptación venció. Se reembolsará la recompensa.", + "insideRefundDone": "El plazo de aceptación venció. Se reembolsó la recompensa.", + "insideRefundChecking": "El plazo de aceptación venció. Estamos revisando el reembolso de la recompensa.", + "insideErrPopShort": "No tienes POP suficientes para la recompensa.", + "insideErrSelfAccept": "No puedes aceptar tu propia respuesta.", + "insideErrAlreadyAccepted": "Ya se aceptó una respuesta y no se puede cambiar.", + "insideErrNotOwner": "Solo quien preguntó puede aceptar una respuesta.", + "commonCheckConnection": "Revisa tu conexión e inténtalo de nuevo.", + "insideAnswerLater": "Los comentarios se abrirán cuando se conecte el servidor.", + "insideAcceptConfirm": "¿Aceptar este comentario?\nSe entregará la recompensa de {pop} POP y no se podrá deshacer.", + "insideAccept": "Aceptar", + "insideMore": "Más", + "insideFirstAnswer": "Sé el primero en comentar.", + "insideAcceptDone": "Aceptada", + "insideAcceptAction": "Aceptar", + "insideAnswerHint": "Escribe un comentario.", + "insideLoadFailed": "No se pudo cargar INSIDE", + "insideEmpty": "Aún no hay preguntas.", + "insideWrite": "Escribir INSIDE", + "insideSearchHint": "Título o contenido de INSIDE", + "insideBodyHint": "Pregunta y responde libremente con otros fans.", + "insideTitleRequired": "Escribe un título.", + "insideBodyRequired": "Escribe el contenido.", + "insideBountySet": "Fijar recompensa", + "insideBountyHint": "Fija {min} o más.", + "insideAttachLater": "Los adjuntos de imagen se abrirán cuando se conecte el servidor.", + "insideBountyRequired": "Indica una recompensa.", + "insideNumberOnly": "Introduce un número.", + "insideBountyMin": "La recompensa debe ser de al menos {min} POP.", + "insideBountyOverBalance": "No puede superar tus POP disponibles.", + "questTypeTimeAttack": "Contrarreloj relámpago", + "questTimeLeft": "Tiempo restante", + "questTimeAttackClosed": "Cerrado", + "questTimeAttackLeft": "Quedan {time}", + "popReasonQuestTimeAttack": "Contrarreloj relámpago", + "briefingShareCopied": "Texto y enlace copiados.", + "shareCopyText": "Copiar texto", + "briefingShareTextCopied": "Texto copiado.", + "questAlreadyRewarded": "Recompensa ya recibida (+{pop} POP).", + "questCatHealth": "Salud", + "questCatPlanet": "Actividad del planeta", + "questCatCommunity": "Comunidad", + "questActShake": "Shake", + "questTypeVote": "Votar", + "questTypeSns": "Redes sociales", + "questActivity": "Actividad", + "questNoCreatable": "Ahora no hay actividades disponibles para crear.", + "questDescriptionOptional": "Descripción (opcional)", + "questLinkUrl": "URL del enlace", + "questLinkCheck": "Comprobar enlace", + "questLinkInvalid": "El enlace no es válido. Revisa la dirección.", + "questLinkPreviewFailed": "No se pudo cargar el enlace. Escribe un título.", + "questLinkTitle": "Título del enlace", + "questEndDate": "Fecha de fin", + "questEndDefault": "24 horas tras publicar", + "questEndDateValue": "Termina el {date} 23:59:59", + "questPoolHelper": "Depósito {pool} POP · Comisión {fee} POP", + "questCreateCost": "{title}\nRecompensa {reward} POP × {count} = {pool} POP\nComisión {fee} POP (se quema al publicar)\nSe descontarán {total} POP en total.", + "questNoReward": "Esta misión no tiene recompensa.", + "questDeadline": "Fecha límite", + "questRemainingHours": "Quedan {hours} h", + "questEnded": "Finalizada", + "questAuthorNote": "Nota del creador", + "questRepeatOnce": "Una participación por persona", + "questRepeatEvery": "Cada {hours} h · hasta {count} veces", + "questSubmit": "Enviar prueba", + "questResubmit": "Reenviar", + "questEvidenceUrl": "Enlace de prueba (opcional)", + "questEvidenceText": "Detalles de la prueba", + "questSubmitted": "Enviado. La recompensa se paga tras la revisión.", + "questReviewPending": "En revisión. La recompensa se paga al aprobarse.", + "questReviewDelayed": "La revisión se está retrasando. Nuestro equipo lo está revisando.", + "questApprovedPaying": "Aprobado. Pagando tu recompensa.", + "questRejected": "Rechazado: {reason}", + "questResubmitAllowed": "Puedes corregir y reenviar.", + "questResubmitNotAllowed": "Esta entrada no se puede reenviar.", + "questReviewButton": "En revisión", + "questRejectedButton": "Rechazado", + "questReviewQueue": "{count} pendientes de revisión", + "questApprove": "Aprobar", + "questReject": "Rechazar", + "questRejectReason": "Motivo", + "questAllowResubmit": "Permitir reenvío", + "questEdit": "Editar título y descripción", + "questEditNotice": "Tras publicar solo puedes editar el título y la descripción, y se guarda el historial.", + "popReasonQuestUgc": "Recompensa de misión", + "popReasonQuestFee": "Comisión por crear misión", + "questFreeDailyCap": "Puedes crear hasta {cap} misiones sin recompensa al día.", + "popReasonQuestPoolDeposit": "Depósito de recompensa", + "popReasonQuestPoolReturn": "Devolución de recompensa", + "campaignCommentNeedNickname": "Primero elige un apodo", + "campaignCommentSetNickname": "Elegir apodo" +} diff --git a/app/lib/l10n/app_ja.arb b/app/lib/l10n/app_ja.arb new file mode 100644 index 0000000..fbe8e63 --- /dev/null +++ b/app/lib/l10n/app_ja.arb @@ -0,0 +1,791 @@ +{ + "@@locale": "ja", + "commonConfirm": "確認", + "commonCancel": "キャンセル", + "commonClose": "閉じる", + "commonSelect": "選択", + "commonRetry": "再試行", + "commonView": "見る", + "commonPrev": "戻る", + "commonDone": "完了", + "commonStart": "はじめる", + "commonNotYet": "{where}画面は第2次実装確認(2026-09-10)で追加されます。", + "languageSettingsTitle": "言語設定", + "authLogin": "ログイン", + "authSignup": "新規登録", + "authFirstTime": "FANUZUは初めてですか?", + "authAlreadyMember": "すでに登録済みですか?", + "authLoginHeadline": "いちばん簡単な方法で\nFANUZUにログイン!", + "authSignupHeadline": "いちばん簡単な方法で\nFANUZUに新規登録!", + "authProviderLogin": "{provider, select, apple{Appleでログイン} google{Googleでログイン} kakao{Kakaoでログイン} line{LINEでログイン} other{メールでログイン}}", + "authProviderSignup": "{provider, select, apple{Appleで登録} google{Googleで登録} kakao{Kakaoで登録} line{LINEで登録} other{メールで登録}}", + "authNotConfiguredTitle": "{name}ログイン準備中", + "authNotConfiguredBody": "{name}ログインは発注元のキー提供後に利用できます。\n\n必要な項目\n· {items}", + "authNotConfiguredNotice": "{name}ログインはまだ準備中です。\n必要な項目: {items}", + "authResetNeedEmail": "再設定メールを受け取るメールアドレスを先に入力してください。", + "authResetSent": "入力されたアドレスに再設定の案内を送信しました。", + "termsTitle": "利用規約", + "termsAgreeAll": "すべて同意", + "termsService": "サービス利用規約(必須)", + "termsLocation": "位置情報サービス利用規約(必須)", + "termsAge": "14歳以上です。(必須)", + "termsMarketing": "マーケティング情報・イベント通知の受信に同意(任意)", + "signupDoneTitle": "新規登録が完了しました", + "signupDoneBody": "ファンダム活動を始められます。\nさまざまな活動でファンダム惑星を育てましょう。", + "homeItemsTooltip": "アイテム", + "homeNotificationsTooltip": "通知", + "homeFandomHistory": "ファンダムの歴史", + "homePopularInside": "人気 K-INSIDE", + "homeWhereBriefing": "AIブリーフィング", + "homeWhereInsideList": "INSIDE一覧", + "homeWhereInsideDetail": "INSIDE詳細", + "homeLoadFailed": "ホームを読み込めませんでした", + "homeRetryLater": "しばらくしてから再度お試しください。", + "myProfileCardTooltip": "プロフィールカード", + "myTabPlanets": "惑星/星雲", + "myTabActivity": "アクティビティ", + "myInside": "インサイド", + "myCampaign": "キャンペーン", + "myShareLater": "{target}への共有は09-10回で接続します。", + "myEditPhoto": "プロフィール写真を編集", + "myEditProfile": "プロフィールを編集", + "settingsTitle": "設定", + "settingsAccount": "アカウント", + "settingsNickname": "ニックネーム", + "settingsEmail": "メール", + "settingsSns": "SNS連携アカウント", + "settingsPopHistory": "POP履歴", + "settingsPayments": "決済履歴", + "settingsServicePayments": "サービス決済履歴", + "settingsService": "サービス設定", + "settingsPush": "プッシュ通知設定", + "settingsBlocked": "ブロック設定", + "settingsInfo": "サービス情報", + "settingsNotice": "お知らせ", + "settingsInquiry": "1:1お問い合わせ", + "settingsAppVersion": "アプリバージョン", + "settingsLatest": "最新バージョン", + "settingsLogout": "ログアウト", + "settingsLogoutConfirm": "ログアウトしますか?", + "settingsWithdraw": "退会", + "settingsWithdrawConfirm": "退会しますか?", + "settingsWithdrawBody": "保有しているPOPは消滅し、元に戻せません。", + "settingsWithdrawLater": "退会処理は09-10回で接続します。", + "settingsUnlinkConfirm": "連携中のアカウントを解除しますか?", + "settingsUnlinkBody": "連携したSNSのログイン手段のみ削除され、アカウント情報は削除されません。", + "settingsUnlink": "連携解除", + "settingsSameEmailOnly": "アカウントのメールと同じ\nソーシャルアカウントのみ連携できます。", + "settingsLinkNotReady": "{name}連携はまだ準備中です。必要な項目: {items}", + "settingsConnect": "連携", + "settingsDisconnect": "解除", + "settingsEmailProvider": "メール", + "pushDeviceOff": "端末のプッシュ通知がオフになっています。", + "pushDeviceOffHint": "通知を受け取るにはシステム設定で通知を許可してください。", + "pushRequesting": "リクエスト中…", + "pushGoToSettings": "設定へ移動", + "pushOpeningDeviceSettings": "端末の設定に移動します。", + "pushEnableInDevice": "端末の設定でFANUZUの通知をオンにしてください。", + "pushDeviceSection": "端末の通知設定", + "pushSoundVibration": "サウンド・バイブ設定", + "pushServiceSection": "サービス通知設定", + "pushTopicEssential": "サービス必須", + "pushTopicFandomEvent": "ファンダムイベント", + "pushTopicBriefing": "AI Briefing", + "pushTopicCommunity": "コミュニティ", + "pushTopicMarketing": "マーケティング・広報", + "linkTitle": "{name}連携", + "linkNotLinked": "未連携", + "linkLinked": "連携済み", + "linkSameEmailOnly": "アカウントのメールと同じソーシャルアカウントのみ連携できます。", + "linkUnlink": "連携解除", + "linkLink": "連携する", + "noticeBadge": "お知らせ", + "noticeTitle": "お知らせ", + "noticeEmpty": "お知らせはありません。", + "noticeDetailTitle": "お知らせ詳細", + "translationMark": "翻訳済み", + "translationReplyTo": "{name}さんのコメントを翻訳", + "translationWrite": "翻訳コメントを書く", + "translationHint": "翻訳を入力してください", + "translationValid": "有効", + "translationInvalid": "無効", + "translationSelfMarkBlocked": "自分の翻訳はマークできません。", + "translationSubmitted": "翻訳を登録しました。検証を通過すると24時間後にPOPが付与されます。", + "translationMarked": "マークしました。", + "translationAlreadyMarked": "すでにマークした翻訳です。", + "translationDuplicate": "この投稿にはすでに同じ言語で翻訳を登録しています。", + "translationFailed": "処理できませんでした。しばらくしてから再度お試しください。", + "translationNotEligible": "登録から24時間以上経過したこのファンダムのメンバーのみマークできます。", + "translationDailyCap": "本日はこれ以上翻訳を登録できません。", + "translationInvalidTarget": "このコンテンツは翻訳できません。", + "commonRequestFailed": "リクエストを処理できませんでした。", + "commonFan": "ファン", + "adLoadFailed": "広告を読み込めませんでした。基本報酬のみ受け取ります。", + "storeNotReady": "ストア商品準備中", + "storePending": "決済の承認を待っています。承認されると自動で反映されます。", + "storeFailed": "決済を完了できませんでした。", + "storePurchased": "購入が反映されました。", + "heroParticipate": "ZAP+ · ギフトに参加", + "heroRemainingSemantics": "HERO TIME 残り{minutes}分{seconds}秒", + "heroRemaining": "HERO TIME · 残り{time}", + "heroClaims": "ファンのギフト受取 {count}件", + "heroInProgress": "{name} · HERO TIME 開催中 →", + "stageNames": "星雲,始まり,成長,共に,同行,光,永遠", + "planetStageUnknown": "惑星 · ステージ情報なし", + "planetHibernatingSuffix": " · 休眠中", + "planetEclipseSuffix": " · Eclipse進行中", + "planetHeroSuffix": " · HERO TIME開催中", + "evoPlanetGrowth": "惑星の成長", + "evoHeroEnd": "HERO TIME 終了", + "evoAwakening": "惑星が目覚めます", + "evoAnomaly": "異常反応", + "evoEnergy": "エネルギー集中", + "evoTransition": "転換", + "evoComplete": "惑星の進化完了", + "evoOpenHistory": "PASSPORTの記録を見る", + "playNow": "今すぐプレイ", + "playHeadline": "さあ、ファンダムに会いに行こう!", + "playBody": "惑星を作ってファンダムと交流しながら楽しいファン活動...", + "timeJustNow": "たった今", + "timeMinutesAgo": "{n}分前", + "timeHoursAgo": "{n}時間前", + "timeDaysAgo": "{n}日前", + "commonMore": "もっと見る", + "authProviderStart": "{provider, select, email{メールではじめる} kakao{Kakaoではじめる} line{LINEではじめる} apple{Appleではじめる} other{Googleではじめる}}", + "authProviderName": "{provider, select, email{メール} kakao{Kakao} line{LINE} apple{Apple} other{Google}}", + "authLastProvider": "最後のログイン手段は連携解除できません。", + "authLoginRequired": "ログインが必要です。", + "authDifferentEmail": "登録したメールアドレスと異なるアカウントです。", + "authLinkFailed": "アカウントの連携に失敗しました。", + "authNotLinked": "連携されていないアカウントです。", + "authEmailUnavailable": "メールログインは利用できません。", + "authCheckCredentials": "メールアドレスまたはパスワードをご確認ください。", + "authSocialFailed": "ソーシャルログインに失敗しました。", + "authAppleUnavailable": "この端末ではAppleでログインを利用できません。", + "authEmailInUse": "すでに登録されているメールアドレスです。", + "authWeakPassword": "パスワードが短すぎます。8文字以上で設定してください。", + "authInvalidEmail": "メールアドレスの形式が正しくありません。", + "commonTryLater": "しばらくしてから再度お試しください。", + "authCredentialInUse": "別のFANUZUアカウントにすでに連携されているソーシャルアカウントです。", + "authAlreadyLinked": "すでに連携済みのアカウントです。", + "authMethodUnavailable": "このログイン手段はまだ利用できません。", + "authRecentLogin": "セキュリティのため、再度ログインしてからお試しください。", + "authOtherProviderGuide": "{email} アカウントは別のログイン手段で登録されています。最初に登録した手段でログインし、MY > 設定 > SNS連携でこの手段を連携してください。", + "authThisAccount": "この", + "eclipseCapped": "参加上限に達したため、一部は集計されませんでした。", + "eclipseResultUnknown": "参加結果を確認できませんでした。同じリクエストで再度ご確認ください。", + "eclipseChecking": "参加を確認中", + "eclipseRetrySame": "同じ参加を再確認", + "eclipseEnded": "イクリプスはすでに終了したか、防衛に成功しました。", + "eclipseTooFast": "参加が速すぎます。しばらくしてから再度お試しください。", + "eclipseNone": "進行中のイクリプスはありません", + "eclipseSuccessTitle": "隕石の衝突を\n見事に防ぎました!", + "eclipseQualified": "参加認定完了", + "eclipseFailTitle": "惜しくも惑星の防衛に\n失敗しました", + "eclipseFailBody": "今回の防衛目標を達成できませんでした。", + "eclipseCancelTitle": "イクリプス中止", + "eclipseCancelBody": "集計エラーのため中止されました。FPIは差し引かれていません。", + "eclipsePendingTitle": "防衛結果を確認中", + "eclipsePendingBody": "参加時間が終了しました。サーバーの判定を待っています。", + "eclipseShakeTimed": "秒間スマホを振って\n隕石の衝突を防ごう!", + "eclipseShake": "スマホを振って\n隕石の衝突を防ごう!", + "eclipseMyCount": "自分の参加 {count}回", + "eclipsePendingSuffix": " · +{count} 送信待ち", + "eclipseSendingSuffix": " · 送信中", + "eclipseNoPopNote": "POPは消費されません。有効Shakeを{count}回以上振って防衛に参加すると参加者として認定されます。", + "eclipseTapToJoin": "振るのが難しければタップで参加", + "eclipseTimeLeft": "イクリプス残り時間:", + "eclipseSeconds": " {seconds}秒", + "eclipseBannerDefended": "防衛成功 — {name} FPI減少なし", + "eclipseBannerActive": "イクリプス発動 — {name} 侵食 {rate}%", + "eclipseBannerDefense": "有効参加 {count} / {target}人", + "timeHoursMinutesLeft": "残り{hours}時間{minutes}分", + "timeMinutesLeft": "残り{minutes}分", + "liveEclipseDefense": "[LIVE] {name}さんがイクリプス防衛に参加しました!(+{count})", + "liveHotTime": "[LIVE] {name}さんのアーティストホットタイムが始まりました!", + "liveHeroGift": "[LIVE] {name}さんの英雄のギフトが届きました!", + "liveCampaign": "[LIVE] {name}さんがキャンペーンを支援しました!", + "galaxySetPrimaryConfirm": "選択した惑星をメイン惑星に変更しますか?", + "galaxyLoadFailed": "ギャラクシーを読み込めませんでした", + "commonSearch": "検索", + "galaxyMyPlanets": "マイ惑星", + "galaxyMap": "ギャラクシーマップ", + "nebulaCreate": "星雲を作成", + "commonRecheck": "再確認", + "zapApplying": "貢献を反映中", + "zapNeedsCheck": "貢献結果の確認が必要", + "zapNoPlanet": "所属惑星がありません", + "commonContribute": "貢献", + "commonItem": "アイテム", + "galaxyDefended": "防衛成功 · FPI減少なし", + "galaxyErosion": "侵食 {rate}% · 防衛 {progress}%", + "galaxyOnlineMembers": "接続 {online}人 · メンバー {members}人", + "galaxySearchHint": "ファンダム名で検索", + "commonRecentSearches": "最近の検索", + "commonClearAll": "すべて削除", + "commonNoRecentSearches": "最近の検索はありません。", + "galaxyNoHit": "該当するファンダムがありません。\n名前をもう一度ご確認ください。", + "galaxyMembers": "メンバー {count}人", + "galaxyPlanetRank": "{name} 惑星 {rank}位", + "galaxyMapOpen": "ギャラクシーマップを開く", + "zapNotEnoughPop": "POPが足りません", + "zapFailNoPop": "POPが足りず貢献できませんでした。", + "zapFailLedger": "残高と獲得履歴が一致しないため、貢献を停止しました。", + "zapFailRetry": "接続を確認して、同じ貢献を再度ご確認ください。", + "zapDone": "貢献完了", + "zapDonePop": "{pop} POP 貢献完了", + "growthFounders": "創立メンバー", + "growthNebulaGoalPassed": "目標期間が過ぎました · 条件を満たすと惑星が誕生します", + "growthFinalStage": "最終ステージ", + "growthPromotionPending": "昇格確定待ち", + "growthCheckConditions": "成長条件を確認", + "growthConditionChecking": "{name} 条件を確認中", + "growthConditionMet": "{name} 条件達成", + "growthConditionUnmet": "{name} 条件未達成", + "nebulaHeadline": "ファンダム惑星の名前を決めよう。", + "nebulaBody": "創立メンバーが{days}日以内に目標POPを達成すると\n正式な惑星として誕生します!\n進化に参加したメンバーには記念バッジが贈られます。", + "nebulaNameTaken": "すでに使用されている名前です。", + "nebulaNameHint": "ファンダム名", + "nebulaGoal": "目標 ", + "nebulaFounders": " / 創立メンバー", + "nebulaGoalSemantics": "目標 {pop} POP · 創立メンバー {members}人 · {days}日固定", + "commonMyPop": "保有POP:", + "commonChange": "変更", + "planetShape": "形 {n}", + "planetBgColor": "背景色を選択", + "planetColorSelect": "惑星の色を選択", + "planetTypeSelect": "惑星タイプを選択", + "commonNext": "次へ", + "planetCreateFailed": "惑星を作成できませんでした。しばらくしてから再度お試しください。", + "planetCreateConfirm": "選択したオプションで\n惑星を作成しますか?", + "commonCreate": "作成", + "briefingOpenFailed": "原文を開けません。", + "campaignAchieved": "{percent}% 達成", + "campaignView": "キャンペーンを見る", + "growthFinalReached": "最終ステージに到達しました", + "zapRetrySame": "同じ貢献を再確認", + "heroOwnerSuffix": "さんの", + "heroStartPrompt": "惑星を作成または参加して\nファン宇宙を始めよう!", + "heroNebulaPrompt": "活動で獲得したPOPで\n星雲に貢献しよう!", + "heroRecentContribution": "{pop} POPの貢献で\n着実に成長しています。", + "heroGrowingTogether": "みんなで育てるファンダム惑星", + "heroPopProgress": "POP進捗率", + "heroNebula": "星雲", + "heroNebulaTap": "星雲をタップして\n惑星を誕生させよう!", + "heroCreateNebula": "惑星を作成(星雲)", + "heroJoinPlanet": "惑星に参加", + "heroCreateOrJoin": "惑星を作成または参加", + "insideAnswers": "回答 {count}", + "itemJoinFirst": "惑星に参加すると英雄のギフトを送れます。", + "itemBuyNotReady": "アイテム購入は準備中です。", + "itemUseNotReady": "アイテムの使用は準備中です。", + "payOpenFailed": "決済画面を開けませんでした。", + "itemBuy": "アイテム購入", + "itemSelect": "アイテム選択", + "commonComingSoon": "準備中です。", + "itemSendPop": "{pop} POP 送信", + "itemBuyerPop": "購入者 +{pop} POP", + "itemOwned": "保有:{count}", + "itemOwnedCount": "保有:{count}個", + "growthOwnerOf": "さんの ", + "growthSteadySuffix": " の貢献で着実に成長しています。", + "questAttended": "出席完了", + "questInProgressSpaced": "進行中", + "questRewardPaid": "報酬支給完了", + "questClaim": "報酬を受け取る", + "questStart": "クエストを始める", + "questRewardDone": "報酬受取済み", + "popKindGeneral": "一般POP", + "popKindContribution": "貢献POP", + "popTxGrant": "支給", + "popTxSpend": "差引", + "popTxRefund": "返金", + "popTxVoid": "無効", + "popTypeQuest": "クエスト", + "popTypeReward": "報酬", + "popTypePayment": "決済", + "popTypeSubscription": "サブスク", + "popTypeAdmin": "運営", + "popTypeFee": "手数料", + "commonEtc": "その他", + "popReasonQuestSteps": "歩数計クエスト", + "popReasonQuestMusicShake": "ミュージックシェイククエスト", + "popReasonQuestPlace": "スポット訪問クエスト", + "popReasonQuestAttend": "出席クエスト", + "popReasonAdReward": "広告報酬", + "popReasonHeroDrop": "英雄のギフト", + "popReasonHeroDropTrigger": "英雄のギフト発動", + "popReasonTranslation": "参加型翻訳", + "popReasonIapPurchase": "商品購入", + "popReasonFocusDaily": "Focusデイリー報酬", + "popReasonPreregBonus": "事前予約報酬", + "popReasonReferral": "友達招待", + "popTypeReferral": "リファラル", + "popReasonAdminGrant": "運営支給", + "popReasonAdminClawback": "運営回収", + "popReasonCampaignFund": "キャンペーン支援", + "popReasonCampaignRefund": "キャンペーン返金", + "popReasonCampaignFeeRefund": "キャンペーン開設手数料の返金", + "popReasonCampaignFee": "キャンペーン登録費", + "popReasonQnaBounty": "インサイド懸賞金", + "popReasonQnaRefund": "懸賞金返金", + "popReasonQnaReward": "インサイド採択報酬", + "popReasonEclipseDefense": "Zap貢献", + "popReasonOther": "POP変動", + "popRepeat": "{title} {count}回", + "payStatePaid": "決済完了", + "payStateRefundRequested": "返金申請", + "payStateRefundApproved": "返金承認", + "payStateRefunded": "返金完了", + "payStateRefundRejected": "返金却下", + "pushTopicEssentialDesc": "アカウント・決済・メンテナンスのお知らせです。オフにできません。", + "pushTopicFandomEventDesc": "イクリプス・キャンペーン・クエスト・アーティストホットタイムのお知らせです。", + "pushTopicBriefingDesc": "惑星の一日をまとめたブリーフィングカードをお届けします。", + "pushTopicCommunityDesc": "自分の投稿へのコメント・いいね・回答の通知です。", + "pushTopicMarketingDesc": "イベント・特典情報を受け取ります。同意した場合のみオンになります。", + "inquiryPending": "受付", + "inquiryInProgress": "対応中", + "inquiryAnswered": "回答済み", + "photoBadType": "JPG・PNG・WEBP・GIF画像のみアップロードできます。", + "photoTooBig": "写真の容量が10MBを超えています。もっと小さい写真を選んでください。", + "photoUnreadable": "写真を読み込めませんでした。もう一度選んでください。", + "myListInside": "マイインサイド", + "myListInsideEmpty": "まだ投稿したインサイドはありません。", + "myListCampaign": "マイキャンペーン", + "myListCampaignEmpty": "まだ作成したキャンペーンはありません。", + "sortLatest": "新着順", + "sortPop": "POP順", + "sortSolved": "採択済み", + "sortAchieved": "達成済み", + "payNotFound": "決済履歴が見つかりません。", + "payItemFallback": "決済商品", + "payMethodInApp": "アプリ内購入", + "unknownUser": "不明なユーザー", + "blockSelf": "自分をブロックすることはできません。", + "inquiryFallback": "お問い合わせ", + "photoRejectedServer": "写真をアップロードできません。形式と容量をご確認ください。", + "photoUploadFailed": "写真のアップロードに失敗しました。しばらくしてから再度お試しください。", + "focusClaimed": "今日のFocus報酬 +{pop} 貢献POPを受け取りました。", + "focusActive": "広告なし集中モード利用中", + "focusUntil": "{date}まで · 今日の分は今日のみ受け取れます", + "focusClaimedToday": "本日受取済み", + "focusClaimToday": "今日の報酬を受け取る", + "storeConnectFailed": "ストアに接続できませんでした。しばらくしてから再度お試しください。", + "payRestore": "購入を復元", + "payEmpty": "決済履歴はありません。", + "payRefundGuide": "消費型商品:購入後7日以内かつ未使用の商品は返金できます。一部でも使用した商品は返金対象外です。\nサブスク商品:決済後24時間以内に特典(追加報酬など)を未使用の場合、返金できます。\n返金承認後7〜14日以内に返金が完了します。", + "payDetailTitle": "決済履歴の詳細", + "payNumber": "決済番号", + "payDate": "決済日時", + "payMethod": "決済方法", + "payStatus": "決済状況", + "payAmount": "決済金額", + "payRefundInfo": "返金のご案内", + "payRefundApply": "返金を申請", + "payRefundUnavailable": "返金不可", + "refundReasonMistake": "誤って決済しました", + "refundReasonExpectation": "サービスが期待と違いました", + "refundReasonError": "エラーで利用できませんでした", + "refundReasonDuplicate": "二重に決済しました", + "refundTitle": "返金", + "refundSubmit": "申請", + "refundReason": "返金理由", + "refundConfirm": "本当に返金を申請しますか?", + "myPop": "マイPOP", + "commonNoHistory": "履歴はありません。", + "nicknameEdit": "ニックネーム変更", + "nicknameReserved": "運営者と誤解される名前は使用できません。", + "nicknameTaken": "すでに使われているニックネームです。", + "albumItems": "アルバム項目", + "takePhoto": "写真を撮影", + "pickFromAlbum": "アルバムから選択", + "tierGuide": "ティア案内", + "statSteps": "累計歩数", + "statContribution": "累計貢献POP", + "statQuests": "完了クエスト", + "shareInstagram": "Instagram", + "shareTiktok": "TikTok", + "shareCopyLink": "リンクをコピー", + "inquiryListTitle": "1:1 お問い合わせ", + "inquiryEmpty": "お問い合わせはありません。", + "inquiryWrite": "お問い合わせを作成", + "inquiryDetail": "お問い合わせ詳細", + "commonEdit": "編集", + "inquiryAnswer": "回答", + "inquiryNew": "新規お問い合わせ", + "inquiryEdit": "お問い合わせを編集", + "commonTitle": "タイトル", + "commonTitleHint": "タイトルを入力してください。", + "inquiryBody": "お問い合わせ内容", + "inquiryBodyHint": "お問い合わせ内容を入力してください。", + "inquiryBodyHelper": "10文字以上書いていただくと、より早く対応できます。", + "inquirySubmitFailed": "お問い合わせを受け付けられませんでした。{reason}", + "inquiryEditFailed": "お問い合わせを修正できませんでした。{reason}", + "inquirySubmitted": "お問い合わせを受け付けました。", + "inquiryEdited": "お問い合わせを修正しました。", + "blockedEmpty": "ブロックしたアカウントはありません。", + "unblock": "ブロック解除", + "unblockFailed": "ブロックを解除できませんでした。{reason}", + "tierInfoTitle": "TIER情報", + "tierPromptPrefix": "惑星・星雲への貢献で\n", + "tierPromptRank": "ランク", + "tierPromptSuffix": "を上げよう", + "tierPassportNote": "Passport Tierはレベルとは別のカード外観の等級で、下がることはありません。", + "tierTotalContribution": "累計貢献", + "notifKindEclipse": "イクリプス", + "notifKindItemUsed": "アイテム使用", + "notifKindLike": "いいね", + "notifKindComment": "コメント", + "notifKindCampaign": "キャンペーン達成", + "notifKindQuest": "クエスト完了", + "notifTargetPost": "投稿", + "notifTargetCampaign": "キャンペーン", + "notifTargetQuest": "クエスト詳細", + "dayToday": "今日", + "dayYesterday": "昨日", + "notifEmpty": "通知はありません。", + "notifGoTo": "{target}へ移動します。", + "stationOfficial": "公式", + "stationShop": "販売店", + "stationTicket": "チケット", + "stationMedia": "メディア", + "stationLoadFailed": "STATIONを読み込めませんでした", + "commonAll": "すべて", + "stationSuggest": "販売店を提案", + "stationEmpty": "まだ登録されたリンクはありません。\n惑星に参加するとキュレーションが表示されます。", + "stationCategoryEmpty": "この分類にはまだリンクがありません。", + "stationSuggestLater": "提案の受付はサーバー連携後に開始します。", + "commonSubmit": "提出", + "commonName": "名前", + "commonNameRequired": "名前を入力してください。", + "commonUrl": "URL", + "commonUrlRequired": "URLを入力してください。", + "commonUrlHttps": "httpsで始まるURLを入力してください。", + "commonCategory": "分類", + "stationReason": "おすすめ理由(任意)", + "stationSuggestNote": "ご提案のリンクは運営の審査を経て登録されます。審査結果は通知でお知らせします。", + "heroGiftUnavailable": "このギフトは現在受け取れません。", + "heroGiftAlready": "すでに参加したギフトです。POP履歴をご確認ください。", + "heroGiftRelogin": "再度ログインしてから参加してください。", + "heroGiftNoPermission": "このギフトに参加する権限がありません。", + "heroGiftHowTo": "スマホを振るか下のボタンを押して、光エネルギーのギフトに参加しましょう。", + "heroGiftPopNote": "受け取れるPOPは残りのギフトと1人あたりの上限で決まります。", + "heroGiftReceived": "{total} POP 受取確認", + "heroGiftUnknown": "支給結果を確認できませんでした。再度ご確認ください。", + "heroGiftNone": "今参加できるギフトはありません。", + "heroGiftConfirmed": "参加したギフトの受取を確認しました。", + "heroGiftChecking": "支給を確認中…", + "heroGiftRetry": "同じギフトを再確認", + "heroGiftTap": "振る代わりにボタンで参加", + "rankMyPlanet": "マイ惑星の{metric}ランキング", + "rankPlanetEmpty": "まだ集計された惑星ランキングはありません", + "rankScopeLive": "リアルタイム", + "rankScopeCountry": "国別", + "rankMine": "私の{metric}ランキング", + "rankMyCountry": "自国の{metric}ランキング", + "rankEmpty": "まだ集計されたランキングはありません", + "rankScope": "{scope}ランキング", + "rankLoadFailed": "ランキングを読み込めませんでした", + "rankMeSuffix": "{name}(自分)", + "reportDefamation": "アーティストへの誹謗・名誉毀損", + "reportInappropriate": "不適切なコンテンツ", + "reportSpam": "スパム・営利目的の広告", + "reportPrivacy": "個人情報の露出・なりすまし", + "reportTitle": "通報する", + "reportSubmit": "通報", + "reportAuthor": "投稿者", + "reportReasonLabel": "通報理由", + "reportConfirm": "この投稿を通報しますか?\n受付後は取り消せません。", + "blockMemberConfirm": "この会員のすべての投稿をブロックしますか?", + "loungeMembersOnly": "この惑星のファンダム会員のみ投稿・コメント・いいねができます。", + "loungeFailed": "処理できませんでした。しばらくしてから再度お試しください。", + "loungeTitle": "ファンダムラウンジ", + "likeCancel": "いいね取り消し", + "like": "いいね", + "commentDeleteConfirm": "コメントを削除しますか?", + "commonShare": "共有", + "commonEditAction": "編集する", + "commonDeleteAction": "削除する", + "commonBlockAction": "ブロックする", + "commentFirst": "最初のコメントを残してください。", + "postDeleteConfirm": "この投稿を削除しますか?", + "reportReceived": "通報を受け付けました。", + "commonDelete": "削除", + "commentHint": "コメントを入力してください。", + "commonSend": "送信", + "postWrite": "投稿する", + "postEdit": "投稿を編集", + "loungeHint": "ファンダムラウンジに自由に投稿してみましょう。", + "photoRemove": "写真{n}を削除", + "camera": "カメラ", + "album": "アルバム", + "historyTitle": "PASSPORT · 記録", + "historyMine": "自分の活動", + "historyEvolution": "Stage {from} → {to} 進化", + "historyHibernation": "惑星休眠", + "historyWakeUp": "惑星が目覚めた", + "historyZap": "ZAP貢献", + "historyQuest": "Quest報酬", + "historyAdBonus": "広告追加報酬", + "historyFunding": "Campaign貢献", + "historyGrowthPop": "成長に{amount} POP反映", + "historyJoined": "この進化に参加しました", + "historyLoadFailed": "記録を読み込めませんでした。", + "historyMineEmpty": "まだ記録された活動はありません。", + "historyFandomEmpty": "まだ記録されたファンダムの歴史はありません。", + "historyMore": "以前の記録をもっと見る", + "countryName": "{code, select, KR{韓国} JP{日本} US{アメリカ} CN{中国} TW{台湾} HK{香港} TH{タイ} VN{ベトナム} ID{インドネシア} PH{フィリピン} MY{マレーシア} SG{シンガポール} IN{インド} GB{イギリス} FR{フランス} DE{ドイツ} ES{スペイン} BR{ブラジル} MX{メキシコ} CA{カナダ} AU{オーストラリア} ZZ{未指定} other{-}}", + "loungeMaxPhotos": "写真は{count}枚までアップロードできます。", + "commonLoadFailed": "読み込めませんでした", + "planetPassportHistory": "PASSPORT · ファンダムの歴史", + "planetNamed": "{name} 惑星", + "blockNebulaConfirm": "この星雲をブロックしますか?", + "blockPlanetConfirm": "この惑星をブロックしますか?", + "blocked": "ブロックしました。", + "planetLastUpdated": "最終更新 {time}", + "planetContribution": "貢献状況", + "planetNebulaGrowth": "星雲成長率", + "planetLiveRanking": "リアルタイム貢献ランキング", + "planetNoContributors": "まだ貢献者がいません", + "planetContributeCta": "貢献してランキングに入ろう!", + "planetOverallRank": "全体{rank}位", + "planetMyContribution": "自分の貢献度", + "planetTallying": "集計準備中", + "planetTopPercent": "上位{percent}%", + "planetHideMember": "この会員の投稿をすべて非表示", + "reportReasonUnknown": "通報理由を確認できません。", + "reportAlready": "すでに通報した投稿です。", + "blockUnavailable": "ブロックできない投稿者です。", + "commonMe": "自分", + "planetSizeMedium": "中規模ファンダム", + "planetPerHead": "1人あたり火力:{value}", + "planetJoined": "参加中", + "planetJoin": "参加する", + "questDetails": "詳細", + "questInProgress": "進行中", + "questTypeAttend": "出席", + "questTypeSteps": "ウォーキング", + "questTypePlace": "特定の場所", + "questTypeStreaming": "ストリーミング", + "questTypeUnverifiable": "このクエストタイプの検証方法はまだ決まっていません。", + "questTabMine": "マイクエスト", + "placePickPrompt": "クエストを完了する\n場所を選択してください。", + "placeSearch": "場所を検索", + "commonNoResults": "検索結果がありません。", + "commonSelectDone": "選択完了", + "adMoreChance": "もっと貢献のチャンスを手に入れよう。", + "adWatchPrompt": "広告を視聴して追加報酬を\n受け取れます!", + "adBonusPlaceholder": "+ 貢献POP", + "adPartyPack": "パーティーパック", + "adPickProduct": "商品を選ぶ", + "adWatchBonus": "広告を見て追加報酬を受け取る", + "adBasicReward": "基本報酬を受け取る", + "focusMode": "広告なし集中モード", + "pricePerMonth": "{price}/月", + "adWarpBefore": "Warping...ファンダムとアーティストにもっと貢献できるよう、ぴったりの広告を探しています。", + "adWarpAfter": "Warping...あなたの貢献への報酬を準備しています。", + "adSkip": "スキップ", + "questPreviewHint": "オプションを選んでプレビューを確認してください", + "questNoCategory": "まだカテゴリが選択されていません", + "questNew": "新規クエスト", + "questCategory": "カテゴリ", + "questName": "クエスト名", + "questAction": "詳細アクション", + "questPickPlace": "目標の場所を選んでください", + "questGoalValue": "目標値", + "questParticipants": "参加人数", + "questRewardPop": "完了報酬POP", + "questCreateConfirm": "この内容で作成しますか?", + "questCreateSummary": "{title} · 目標 {goal} · 報酬 {reward} POP", + "commonKeepEditing": "さらに編集", + "questCreate": "作成", + "questNamed": "{title} クエスト", + "questDailyHelp": "毎日惑星をサポート", + "questTodayMust": "今日やるべきクエストです", + "questTabEmpty": "このタブにはまだクエストがありません。", + "questCreateNotReady": "クエスト作成は報酬原資の預け入れと同時に開始する機能です。準備中です。", + "questNoBaseReward": "基本報酬の記録が見つからないため、広告追加報酬を受け取れません。", + "questNoSteps": "歩数がまだ取得されていません。少し歩いてから再度お試しください。", + "questNoPlace": "このクエストに認証する場所が登録されていません。", + "questGoal": "目標", + "questReward": "報酬", + "questPopGrant": "POP支給", + "commonChecking": "確認中…", + "questMine": "自分が作成したクエストです。参加者は{count}人です。", + "questStopConfirm": "クエストを中断しますか?", + "questStopBody": "これまでの進行状況がリセットされ、最初の状態に戻ります。", + "questStop": "中断", + "adNotFinished": "広告を最後まで見なかったため追加報酬はありません。基本報酬はそのまま受け取れます。", + "openSettings": "設定を開く", + "permStepsDenied": "歩数の権限がオフです。設定で「身体活動」を許可してください。", + "permStepsNeeded": "歩数を数えるには身体活動の権限が必要です。", + "permLocationOff": "端末の位置情報がオフです。オンにして再度お試しください。", + "permLocationNeeded": "訪問を確認するには位置情報の権限が必要です。", + "permLocationDenied": "位置情報の権限がオフです。設定で位置情報へのアクセスを許可してください。", + "questRewardReceived": " 受け取りました!", + "questAdBonus": "広告追加報酬 +{pop} POP", + "questParticipantsCount": "参加人数:{count}", + "questParticipantsCap": "参加人数:{count} / {cap}", + "nebulaCreateFailed": "星雲を作成できませんでした。しばらくしてから再度お試しください。", + "nebulaAlreadyToday": "今日はすでに星雲を作成しました。", + "nebulaCreatedKeepPrimary": "星雲を作成しました。メイン惑星は前回の変更から30日経過後に変更できるため、そのままです。", + "fandomFounderLeaveBlocked": "惑星が誕生した後に脱退できます。", + "sortPopular": "人気順", + "campaignSortContributing": "貢献中", + "campaignStatusReview": "運営が審査中のキャンペーンです。", + "campaignStatusRejected": "審査で差し戻されました。登録料は返還しました。", + "campaignStatusWithdrawn": "作成者が取り下げたキャンペーンです。", + "campaignStatusCancelled": "運営上の理由でキャンセルされました。登録料と支援POPを返還しました。", + "rankWindowDaily": "日間", + "rankWindowWeekly": "週間", + "campaignNotFound": "キャンペーンが見つかりません。", + "campaignMediaMain": "メイン画像", + "campaignLoadFailed": "キャンペーンを読み込めませんでした。", + "campaignTabInfo": "キャンペーン情報", + "campaignTabBoard": "応援掲示板 {count}", + "campaignWithdraw": "キャンペーン取り下げ", + "campaignContributeAction": "貢献する", + "campaignFundingStatus": "現在の支援状況", + "campaignSupporting": "応援中", + "campaignTimeLeft": "残り!", + "campaignReport": "結果報告", + "campaignReportPhotos": "実施写真", + "campaignBudget": "予算の使用内訳", + "campaignTotal": "合計", + "campaignReceipts": "領収書", + "campaignDaysLeft": "{days}日 {time}", + "campaignWithdrawTitle": "キャンペーンを取り下げますか?", + "campaignWithdrawRefund": "公開前の取り下げのため、登録料 {fee} POP を支払った通貨のまま返還します。", + "campaignWithdrawNoRefund": "すでに公開されたキャンペーンのため、登録料 {fee} POP は返還されません。", + "campaignWithdrawConfirm": "取り下げる", + "shareCopied": "共有テキストをコピーしました。", + "campaignContributePop": "貢献POPの設定", + "campaignEmptyContributing": "まだ貢献したキャンペーンはありません。", + "campaignEmpty": "登録されたキャンペーンはありません。", + "campaignNew": "新しいキャンペーン", + "campaignMyRanking": "私の貢献ランキング", + "campaignWindowRanking": "{window}ランキング", + "campaignSearchHint": "キャンペーン検索", + "campaignNoRecentSearches": "最近の検索はありません", + "searchRemoveTerm": "{term}を削除", + "campaignNoResults": "検索結果がありません", + "shareTopicCampaign": "キャンペーン", + "sharePlanetLine": "{brand} · {name} 惑星", + "campaignGaugeLabel": "キャンペーン貢献率", + "campaignPeriodTitle": "キャンペーン期間を選択してください", + "campaignPeriodMax": "期間は最大1年まで選択できます", + "campaignYearMonth": "{year}年{month}月", + "campaignWeekdays": "日,月,火,水,木,金,土", + "campaignPeriodRange": "{startMonth}月{startDay}日 ~ {endMonth}月{endDay}日", + "campaignPeriodSelect": "期間を選択", + "calendarPrevMonth": "前の月", + "calendarNextMonth": "次の月", + "campaignShareTitle": "キャンペーンを共有", + "campaignPickMedia": "画像/動画を選択", + "campaignTitleHint": "キャンペーンのタイトルを入力してください。", + "campaignBodyHint": "キャンペーンの説明を入力してください。", + "campaignReviewNote": "投稿後、運営の審査を経て支援が始まります。", + "campaignSupportType": "支援の種類を選択", + "campaignPeriod": "キャンペーン期間", + "campaignDateSelect": "日付を選択", + "campaignGoalPop": "目標貢献POP", + "campaignSubmitTitle": "この内容で投稿しますか?", + "campaignSubmitBody": "{title} · 目標 {goal} POP · {days}日\n登録料 {fee} POP が差し引かれ、運営の審査後に支援が始まります。", + "campaignSubmit": "投稿する", + "campaignCreateFailed": "キャンペーンを投稿できませんでした。", + "campaignNeedsQuests": "クエスト完了 {done}/{need} — あと{left}回完了するとキャンペーンを開けます。", + "campaignPendingTitle": "審査待ちです", + "campaignPendingBody": "運営の審査が終わると支援が始まり、一覧に表示されます。", + "insideRefundPending": "採択期限が過ぎたため、懸賞金は返金される予定です。", + "insideRefundDone": "採択期限が過ぎたため、懸賞金は返金されました。", + "insideRefundChecking": "採択期限が過ぎたため、懸賞金の返金を確認しています。", + "insideErrPopShort": "POPが足りず懸賞金を設定できませんでした。", + "insideErrSelfAccept": "自分の回答は採択できません。", + "insideErrAlreadyAccepted": "すでに採択済みの質問です。採択は変更できません。", + "insideErrNotOwner": "質問者のみ採択できます。", + "commonCheckConnection": "接続を確認して、もう一度お試しください。", + "insideAnswerLater": "コメントはサーバー連携後に利用できます。", + "insideAcceptConfirm": "このコメントを採択しますか?\n懸賞金 {pop} POP が渡され、採択は取り消せません。", + "insideAccept": "採択", + "insideMore": "もっと見る", + "insideFirstAnswer": "最初のコメントを残してください。", + "insideAcceptDone": "採択済み", + "insideAcceptAction": "採択する", + "insideAnswerHint": "コメントを入力してください。", + "insideLoadFailed": "INSIDEを読み込めませんでした", + "insideEmpty": "まだ質問はありません。", + "insideWrite": "INSIDEを作成", + "insideSearchHint": "INSIDEのタイトル・内容", + "insideBodyHint": "ファンダムと自由に質問をやり取りしてみましょう。", + "insideTitleRequired": "タイトルを入力してください。", + "insideBodyRequired": "内容を入力してください。", + "insideBountySet": "懸賞金の設定", + "insideBountyHint": "{min}以上に設定してください。", + "insideAttachLater": "画像の添付はサーバー連携後に利用できます。", + "insideBountyRequired": "懸賞金を入力してください。", + "insideNumberOnly": "数字で入力してください。", + "insideBountyMin": "懸賞金は {min} POP 以上である必要があります。", + "insideBountyOverBalance": "保有POPを超えることはできません。", + "questTypeTimeAttack": "緊急タイムアタック", + "questTimeLeft": "残り時間", + "questTimeAttackClosed": "締め切り", + "questTimeAttackLeft": "残り {time}", + "popReasonQuestTimeAttack": "緊急タイムアタック", + "briefingShareCopied": "共有テキストとリンクをコピーしました。", + "shareCopyText": "テキストをコピー", + "briefingShareTextCopied": "共有テキストをコピーしました。", + "questAlreadyRewarded": "受け取り済みの報酬です(+{pop} POP)。", + "questCatHealth": "健康", + "questCatPlanet": "惑星アクティビティ", + "questCatCommunity": "コミュニティ", + "questActShake": "シェイク", + "questTypeVote": "投票参加", + "questTypeSns": "SNS", + "questActivity": "アクティビティ", + "questNoCreatable": "現在作成できるクエスト活動はありません。", + "questDescriptionOptional": "説明(任意)", + "questLinkUrl": "リンクURL", + "questLinkCheck": "リンクを確認", + "questLinkInvalid": "正しいリンクではありません。URLを確認してください。", + "questLinkPreviewFailed": "リンク情報を取得できませんでした。タイトルを入力してください。", + "questLinkTitle": "リンクのタイトル", + "questEndDate": "終了日", + "questEndDefault": "公開から24時間", + "questEndDateValue": "{date} 23:59:59 締切", + "questPoolHelper": "合計 {pool} POP 預け入れ・手数料 {fee} POP", + "questCreateCost": "{title}\n報酬 {reward} POP × {count}人 = {pool} POP\n手数料 {fee} POP(公開時に消滅)\n合計 {total} POP が差し引かれます。", + "questNoReward": "報酬のない参加クエストです。", + "questDeadline": "締切", + "questRemainingHours": "残り{hours}時間", + "questEnded": "終了", + "questAuthorNote": "作成者の説明", + "questRepeatOnce": "1人1回参加", + "questRepeatEvery": "{hours}時間おき・最大{count}回", + "questSubmit": "認証を提出", + "questResubmit": "補完して再提出", + "questEvidenceUrl": "認証リンク(任意)", + "questEvidenceText": "認証内容", + "questSubmitted": "提出しました。審査後に報酬が支払われます。", + "questReviewPending": "審査中です。承認されると報酬が支払われます。", + "questReviewDelayed": "審査が遅れています。運営チームが確認中です。", + "questApprovedPaying": "承認されました。報酬を支払っています。", + "questRejected": "差し戻し: {reason}", + "questResubmitAllowed": "補完して再提出できます。", + "questResubmitNotAllowed": "この提出は再提出できません。", + "questReviewButton": "審査中", + "questRejectedButton": "差し戻し済み", + "questReviewQueue": "審査待ち {count}件", + "questApprove": "承認", + "questReject": "差し戻す", + "questRejectReason": "差し戻し理由", + "questAllowResubmit": "再提出を許可", + "questEdit": "タイトル・説明を編集", + "questEditNotice": "公開後はタイトルと説明のみ編集でき、履歴が残ります。", + "popReasonQuestUgc": "クエスト報酬", + "popReasonQuestFee": "クエスト作成手数料", + "questFreeDailyCap": "報酬なしのクエストは1日{cap}件まで作成できます。", + "popReasonQuestPoolDeposit": "クエスト報酬の預け入れ", + "popReasonQuestPoolReturn": "クエスト報酬の返還", + "campaignCommentNeedNickname": "先にニックネームを決めてください", + "campaignCommentSetNickname": "ニックネームを決める" +} diff --git a/app/lib/l10n/app_ko.arb b/app/lib/l10n/app_ko.arb new file mode 100644 index 0000000..96b8e56 --- /dev/null +++ b/app/lib/l10n/app_ko.arb @@ -0,0 +1,1634 @@ +{ + "@@locale": "ko", + "commonConfirm": "확인", + "commonCancel": "취소", + "commonClose": "닫기", + "commonSelect": "선택", + "commonRetry": "다시 시도", + "commonView": "보기", + "commonPrev": "이전", + "commonDone": "완료", + "commonStart": "시작", + "commonNotYet": "{where} 화면은 구현 확인물 2차(2026-09-10)에 들어갑니다.", + "@commonNotYet": { + "placeholders": { + "where": { + "type": "String" + } + } + }, + "languageSettingsTitle": "언어 설정", + "authLogin": "로그인", + "authSignup": "회원가입", + "authFirstTime": "팬우주는 처음이세요?", + "authAlreadyMember": "이미 가입하셨나요?", + "authLoginHeadline": "가장 편한 방법으로\n팬우주에 로그인하세요!", + "authSignupHeadline": "가장 편한 방법으로\n팬우주에 회원가입 하세요!", + "authProviderLogin": "{provider, select, apple{Apple로 로그인} google{Google로 로그인} kakao{Kakao로 로그인} line{LINE으로 로그인} other{이메일로 로그인}}", + "@authProviderLogin": { + "placeholders": { + "provider": { + "type": "String" + } + } + }, + "authProviderSignup": "{provider, select, apple{Apple로 가입하기} google{Google로 가입하기} kakao{Kakao로 가입하기} line{LINE으로 가입하기} other{이메일로 가입하기}}", + "@authProviderSignup": { + "placeholders": { + "provider": { + "type": "String" + } + } + }, + "authNotConfiguredTitle": "{name} 로그인 준비 중", + "@authNotConfiguredTitle": { + "placeholders": { + "name": { + "type": "String" + } + } + }, + "authNotConfiguredBody": "{name} 로그인은 발주사 키가 들어오면 열립니다.\n\n필요한 항목\n· {items}", + "@authNotConfiguredBody": { + "placeholders": { + "name": { + "type": "String" + }, + "items": { + "type": "String" + } + } + }, + "authNotConfiguredNotice": "{name} 로그인은 아직 준비 중입니다.\n필요한 항목: {items}", + "@authNotConfiguredNotice": { + "placeholders": { + "name": { + "type": "String" + }, + "items": { + "type": "String" + } + } + }, + "authResetNeedEmail": "재설정 메일을 받으실 이메일을 먼저 입력해 주세요.", + "authResetSent": "입력하신 주소로 재설정 안내를 보냈습니다.", + "termsTitle": "이용 약관", + "termsAgreeAll": "전체 동의", + "termsService": "서비스 이용약관 (필수)", + "termsLocation": "위치기반 서비스 이용약관 (필수)", + "termsAge": "만 14세 이상입니다. (필수)", + "termsMarketing": "마케팅 정보 및 이벤트 알림 수신 동의 (선택)", + "signupDoneTitle": "회원가입이 완료되었습니다", + "signupDoneBody": "팬덤 활동을 시작할 수 있습니다.\n다양한 활동을 통해 내 팬덤 행성을 키워보세요.", + "homeItemsTooltip": "아이템", + "homeNotificationsTooltip": "알림", + "homeFandomHistory": "팬덤 역사", + "homePopularInside": "인기 K-INSIDE", + "homeWhereBriefing": "AI 브리핑", + "homeWhereInsideList": "INSIDE 목록", + "homeWhereInsideDetail": "INSIDE 상세", + "homeLoadFailed": "홈을 불러오지 못했습니다", + "homeRetryLater": "잠시 뒤 다시 시도해 주세요.", + "myProfileCardTooltip": "프로필 카드", + "myTabPlanets": "행성/성운", + "myTabActivity": "활동", + "myInside": "인사이드", + "myCampaign": "캠페인", + "myShareLater": "{target} 공유는 09-10 회차에 연결합니다.", + "@myShareLater": { + "placeholders": { + "target": { + "type": "String" + } + } + }, + "myEditPhoto": "프로필 사진 수정", + "myEditProfile": "프로필 수정", + "settingsTitle": "설정", + "settingsAccount": "계정", + "settingsNickname": "닉네임", + "settingsEmail": "이메일", + "settingsSns": "SNS 연동 계정", + "settingsPopHistory": "POP 내역", + "settingsPayments": "결제 내역", + "settingsServicePayments": "서비스 결제 내역", + "settingsService": "서비스 설정", + "settingsPush": "푸시 알림 설정", + "settingsBlocked": "차단 설정", + "settingsInfo": "서비스 정보", + "settingsNotice": "공지 사항", + "settingsInquiry": "1:1 문의접수", + "settingsAppVersion": "앱 버전", + "settingsLatest": "최신 버전", + "settingsLogout": "로그아웃", + "settingsLogoutConfirm": "로그아웃 하시겠습니까?", + "settingsWithdraw": "서비스 탈퇴", + "settingsWithdrawConfirm": "서비스 탈퇴를 하시겠습니까?", + "settingsWithdrawBody": "보유하신 POP 이 사라지고 되돌릴 수 없습니다.", + "settingsWithdrawLater": "서비스 탈퇴 처리는 09-10 회차에 연결합니다.", + "settingsUnlinkConfirm": "연결된 계정을 끊으시겠습니까?", + "settingsUnlinkBody": "연결한 SNS 계정 로그인 수단만 제거되며, 계정 정보는 삭제되지 않습니다.", + "settingsUnlink": "연결 해제", + "settingsSameEmailOnly": "계정 이메일과 동일한 소셜 계정만\n연결 할 수 있습니다.", + "settingsLinkNotReady": "{name} 연동은 아직 준비 중입니다. 필요한 항목: {items}", + "@settingsLinkNotReady": { + "placeholders": { + "name": { + "type": "String" + }, + "items": { + "type": "String" + } + } + }, + "settingsConnect": "연결", + "settingsDisconnect": "연결해제", + "settingsEmailProvider": "이메일", + "pushDeviceOff": "기기의 푸시 알림 설정이 꺼진 상태입니다.", + "pushDeviceOffHint": "알림 수신을 위해 시스템 설정에서 알림을 허용해주세요.", + "pushRequesting": "요청 중…", + "pushGoToSettings": "설정하러 가기", + "pushOpeningDeviceSettings": "기기 설정으로 이동합니다.", + "pushEnableInDevice": "기기 설정에서 FANUZU 알림을 켜 주셔야 받으실 수 있습니다.", + "pushDeviceSection": "기기 알림 설정", + "pushSoundVibration": "소리, 진동 설정", + "pushServiceSection": "서비스 알림 설정", + "pushTopicEssential": "서비스 필수", + "pushTopicFandomEvent": "팬덤 이벤트", + "pushTopicBriefing": "AI Briefing", + "pushTopicCommunity": "커뮤니티", + "pushTopicMarketing": "마케팅·홍보", + "linkTitle": "{name} 연동", + "@linkTitle": { + "placeholders": { + "name": { + "type": "String" + } + } + }, + "linkNotLinked": "연동되지 않음", + "linkLinked": "연동됨", + "linkSameEmailOnly": "계정 이메일과 동일한 소셜 계정만 연결 할 수 있습니다.", + "linkUnlink": "연동 해제", + "linkLink": "연동하기", + "noticeBadge": "공지", + "noticeTitle": "공지사항", + "noticeEmpty": "공지사항이 없습니다.", + "noticeDetailTitle": "공지사항 상세", + "translationMark": "번역함", + "translationReplyTo": "{name} 님 댓글 번역", + "@translationReplyTo": { + "placeholders": { + "name": { + "type": "String" + } + } + }, + "translationWrite": "번역 댓글 작성", + "translationHint": "번역을 입력하세요", + "translationValid": "유효", + "translationInvalid": "무효", + "translationSelfMarkBlocked": "본인 번역은 마킹할 수 없습니다.", + "translationSubmitted": "번역을 등록했습니다. 검증을 통과하면 24시간 뒤 POP 이 지급됩니다.", + "translationMarked": "마킹했습니다.", + "translationAlreadyMarked": "이미 마킹한 번역입니다.", + "translationDuplicate": "이미 이 글에 같은 언어로 번역을 등록했습니다.", + "translationFailed": "처리하지 못했습니다. 잠시 뒤 다시 시도해 주세요.", + "translationNotEligible": "가입 후 24시간이 지난 이 팬덤의 멤버만 마킹할 수 있습니다.", + "translationDailyCap": "오늘은 번역을 더 등록할 수 없습니다.", + "translationInvalidTarget": "번역할 수 없는 콘텐츠입니다.", + "commonRequestFailed": "요청을 처리하지 못했습니다.", + "commonFan": "팬", + "adLoadFailed": "광고를 불러오지 못했어요. 기본 보상만 받습니다.", + "storeNotReady": "스토어 상품 준비 중", + "storePending": "결제 승인을 기다리고 있어요. 승인되면 자동으로 반영됩니다.", + "storeFailed": "결제를 완료하지 못했어요.", + "storePurchased": "구매가 반영되었습니다.", + "heroParticipate": "ZAP+ · 선물 참여", + "heroRemainingSemantics": "HERO TIME 남은 시간 {minutes}분 {seconds}초", + "@heroRemainingSemantics": { + "placeholders": { + "minutes": { + "type": "String" + }, + "seconds": { + "type": "String" + } + } + }, + "heroRemaining": "HERO TIME · {time} 남음", + "@heroRemaining": { + "placeholders": { + "time": { + "type": "String" + } + } + }, + "heroClaims": "팬들의 선물 수령 {count}건", + "@heroClaims": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "heroInProgress": "{name} · HERO TIME 진행 중 →", + "@heroInProgress": { + "placeholders": { + "name": { + "type": "String" + } + } + }, + "stageNames": "성운,시작,성장,함께,동행,빛,영원", + "planetStageUnknown": "행성 · 단계 정보 없음", + "planetHibernatingSuffix": " · 휴면 중", + "planetEclipseSuffix": " · Eclipse 진행 중", + "planetHeroSuffix": " · HERO TIME 진행 중", + "evoPlanetGrowth": "행성 성장", + "evoHeroEnd": "HERO TIME 종료", + "evoAwakening": "행성이 깨어납니다", + "evoAnomaly": "이상 반응", + "evoEnergy": "에너지 집중", + "evoTransition": "전환", + "evoComplete": "행성 진화 완료", + "evoOpenHistory": "PASSPORT 기록 보기", + "playNow": "지금 바로 플레이", + "playHeadline": "자, 팬덤을 만나러 가요!", + "playBody": "행성을 만들고 팬덤들과 소통하면서 즐거운 팬덤 활...", + "timeJustNow": "방금 전", + "timeMinutesAgo": "{n}분 전", + "@timeMinutesAgo": { + "placeholders": { + "n": { + "type": "String" + } + } + }, + "timeHoursAgo": "{n}시간 전", + "@timeHoursAgo": { + "placeholders": { + "n": { + "type": "String" + } + } + }, + "timeDaysAgo": "{n}일 전", + "@timeDaysAgo": { + "placeholders": { + "n": { + "type": "String" + } + } + }, + "commonMore": "더 보기", + "authProviderStart": "{provider, select, email{이메일로 시작하기} kakao{카카오로 시작하기} line{LINE으로 시작하기} apple{Apple로 시작하기} other{Google로 시작하기}}", + "@authProviderStart": { + "placeholders": { + "provider": { + "type": "String" + } + } + }, + "authProviderName": "{provider, select, email{이메일} kakao{카카오} line{LINE} apple{Apple} other{Google}}", + "@authProviderName": { + "placeholders": { + "provider": { + "type": "String" + } + } + }, + "authLastProvider": "마지막 로그인 수단은 연결을 해제할 수 없습니다.", + "authLoginRequired": "로그인이 필요합니다.", + "authDifferentEmail": "가입하신 이메일과 다른 계정입니다.", + "authLinkFailed": "계정 연결에 실패했습니다.", + "authNotLinked": "연결되어 있지 않은 계정입니다.", + "authEmailUnavailable": "이메일 로그인을 쓸 수 없습니다.", + "authCheckCredentials": "이메일 또는 비밀번호를 확인해 주세요.", + "authSocialFailed": "소셜 로그인에 실패했습니다.", + "authAppleUnavailable": "이 기기에서는 Apple 로그인을 쓸 수 없습니다.", + "authEmailInUse": "이미 가입된 이메일입니다.", + "authWeakPassword": "비밀번호가 너무 짧습니다. 8자 이상으로 정해 주세요.", + "authInvalidEmail": "이메일 형식이 올바르지 않습니다.", + "commonTryLater": "잠시 후 다시 시도해 주세요.", + "authCredentialInUse": "다른 FANUZU 계정에 이미 연결된 소셜 계정입니다.", + "authAlreadyLinked": "이미 연결된 계정입니다.", + "authMethodUnavailable": "이 로그인 수단은 아직 사용할 수 없습니다.", + "authRecentLogin": "보안을 위해 다시 로그인한 뒤 시도해 주세요.", + "authOtherProviderGuide": "{email} 계정은 다른 로그인 수단으로 가입돼 있습니다. 처음 가입에 쓰신 수단으로 로그인한 뒤 MY > 설정 > SNS 연동에서 이 수단을 연결해 주세요.", + "@authOtherProviderGuide": { + "placeholders": { + "email": { + "type": "String" + } + } + }, + "authThisAccount": "이", + "eclipseCapped": "참여 상한에 닿아 일부는 집계되지 않았습니다.", + "eclipseResultUnknown": "참여 결과를 확인하지 못했습니다. 같은 요청으로 다시 확인해 주세요.", + "eclipseChecking": "참여 확인 중", + "eclipseRetrySame": "같은 참여 다시 확인", + "eclipseEnded": "이클립스가 이미 끝났거나 방어에 성공했습니다.", + "eclipseTooFast": "너무 빠르게 참여했습니다. 잠시 뒤 다시 시도해 주십시오.", + "eclipseNone": "진행 중인 이클립스가 없습니다", + "eclipseSuccessTitle": "운석 충돌을\n성공적으로 막았어요!", + "eclipseQualified": "참여 인정 완료", + "eclipseFailTitle": "아쉽게 행성 방어를\n실패했어요", + "eclipseFailBody": "이번 회차의 방어 목표를 달성하지 못했습니다.", + "eclipseCancelTitle": "이클립스 취소", + "eclipseCancelBody": "집계 오류로 취소되었습니다. FPI는 차감되지 않았습니다.", + "eclipsePendingTitle": "방어 결과 확인 중", + "eclipsePendingBody": "참여 시간이 끝났습니다. 서버 판정을 기다리고 있습니다.", + "eclipseShakeTimed": "초간 휴대폰을 흔들어\n행성 운석 충돌을 막으세요!", + "eclipseShake": "휴대폰을 흔들어\n행성 운석 충돌을 막으세요!", + "eclipseMyCount": "내 참여 {count}회", + "@eclipseMyCount": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "eclipsePendingSuffix": " · +{count} 전송 대기", + "@eclipsePendingSuffix": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "eclipseSendingSuffix": " · 전송 중", + "eclipseNoPopNote": "POP은 소모되지 않습니다. 유효 Shake {count}회 이상 흔들어 방어에 참여하면 참여자로 인정됩니다.", + "@eclipseNoPopNote": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "eclipseTapToJoin": "흔들기 어려우면 눌러서 참여", + "eclipseTimeLeft": "이클립스 발생 남은 시간 :", + "eclipseSeconds": " {seconds}초", + "@eclipseSeconds": { + "placeholders": { + "seconds": { + "type": "String" + } + } + }, + "eclipseBannerDefended": "방어 성공 — {name} FPI 감소 없음", + "@eclipseBannerDefended": { + "placeholders": { + "name": { + "type": "String" + } + } + }, + "eclipseBannerActive": "이클립스 발동 — {name} 침식 {rate}%", + "@eclipseBannerActive": { + "placeholders": { + "name": { + "type": "String" + }, + "rate": { + "type": "String" + } + } + }, + "eclipseBannerDefense": "유효 참여 {count} / {target}명", + "@eclipseBannerDefense": { + "placeholders": { + "count": { + "type": "String" + }, + "target": { + "type": "String" + } + } + }, + "timeHoursMinutesLeft": "{hours}시간 {minutes}분 남음", + "@timeHoursMinutesLeft": { + "placeholders": { + "hours": { + "type": "String" + }, + "minutes": { + "type": "String" + } + } + }, + "timeMinutesLeft": "{minutes}분 남음", + "@timeMinutesLeft": { + "placeholders": { + "minutes": { + "type": "String" + } + } + }, + "liveEclipseDefense": "[LIVE] {name}님이 이클립스 방어에 참여했습니다! (+{count})", + "@liveEclipseDefense": { + "placeholders": { + "name": { + "type": "String" + }, + "count": { + "type": "String" + } + } + }, + "liveHotTime": "[LIVE] {name}님의 아티스트 핫타임이 시작되었습니다!", + "@liveHotTime": { + "placeholders": { + "name": { + "type": "String" + } + } + }, + "liveHeroGift": "[LIVE] {name}님의 영웅의 선물이 도착했습니다!", + "@liveHeroGift": { + "placeholders": { + "name": { + "type": "String" + } + } + }, + "liveCampaign": "[LIVE] {name}님이 캠페인에 후원했습니다!", + "@liveCampaign": { + "placeholders": { + "name": { + "type": "String" + } + } + }, + "galaxySetPrimaryConfirm": "선택한 행성을 기본 행성으로 변경할까요?", + "galaxyLoadFailed": "갤럭시를 불러오지 못했습니다", + "commonSearch": "검색", + "galaxyMyPlanets": "내 행성", + "galaxyMap": "갤럭시 맵", + "nebulaCreate": "성운 생성", + "commonRecheck": "다시 확인", + "zapApplying": "기여 반영 중", + "zapNeedsCheck": "기여 결과 확인 필요", + "zapNoPlanet": "소속 행성이 없습니다", + "commonContribute": "기여", + "commonItem": "아이템", + "galaxyDefended": "방어 성공 · FPI 감소 없음", + "galaxyErosion": "침식 {rate}% · 방어 {progress}%", + "@galaxyErosion": { + "placeholders": { + "rate": { + "type": "String" + }, + "progress": { + "type": "String" + } + } + }, + "galaxyOnlineMembers": "접속 {online}명 · 멤버 {members}명", + "@galaxyOnlineMembers": { + "placeholders": { + "online": { + "type": "String" + }, + "members": { + "type": "String" + } + } + }, + "galaxySearchHint": "팬덤 이름으로 검색", + "commonRecentSearches": "최근 검색어", + "commonClearAll": "전체 삭제", + "commonNoRecentSearches": "최근 검색어가 없습니다.", + "galaxyNoHit": "찾는 팬덤이 없습니다.\n이름을 다시 확인해 주세요.", + "galaxyMembers": "멤버 {count}명", + "@galaxyMembers": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "galaxyPlanetRank": "{name} 행성 {rank}위", + "@galaxyPlanetRank": { + "placeholders": { + "name": { + "type": "String" + }, + "rank": { + "type": "String" + } + } + }, + "galaxyMapOpen": "갤럭시 맵 열기", + "zapNotEnoughPop": "POP이 모자랍니다", + "zapFailNoPop": "POP 이 모자라 기여하지 못했습니다.", + "zapFailLedger": "잔액과 획득 내역이 맞지 않아 기여를 멈췄습니다.", + "zapFailRetry": "연결을 확인하고 같은 기여를 다시 확인해 주세요.", + "zapDone": "기여 완료", + "zapDonePop": "{pop} POP 기여 완료", + "@zapDonePop": { + "placeholders": { + "pop": { + "type": "String" + } + } + }, + "growthFounders": "창립 멤버", + "growthNebulaGoalPassed": "목표 기간이 지났어요 · 조건을 채우면 행성으로 탄생해요", + "growthFinalStage": "최종 Stage", + "growthPromotionPending": "승급 확정 대기", + "growthCheckConditions": "성장 조건 확인", + "growthConditionChecking": "{name} 조건 확인 중", + "@growthConditionChecking": { + "placeholders": { + "name": { + "type": "String" + } + } + }, + "growthConditionMet": "{name} 조건 충족", + "@growthConditionMet": { + "placeholders": { + "name": { + "type": "String" + } + } + }, + "growthConditionUnmet": "{name} 조건 미충족", + "@growthConditionUnmet": { + "placeholders": { + "name": { + "type": "String" + } + } + }, + "nebulaHeadline": "우리 팬덤 행성 이름을 만들어요.", + "nebulaBody": "창립맴버가 목표 POP을 {days}일내 달성하면 \n정식 행성으로 탄생합니다!\n진화에 참여한 맴버들에게는 기념 배지가 지급됩니다.", + "@nebulaBody": { + "placeholders": { + "days": { + "type": "String" + } + } + }, + "nebulaNameTaken": "이미 사용중인 이름입니다.", + "nebulaNameHint": "팬덤 이름", + "nebulaGoal": "목표 ", + "nebulaFounders": " / 창립맴버", + "nebulaGoalSemantics": "목표 {pop} POP · 창립 멤버 {members}명 · {days}일 고정", + "@nebulaGoalSemantics": { + "placeholders": { + "pop": { + "type": "String" + }, + "members": { + "type": "String" + }, + "days": { + "type": "String" + } + } + }, + "commonMyPop": "나의 보유 POP : ", + "commonChange": "변경", + "planetShape": "형태 {n}", + "@planetShape": { + "placeholders": { + "n": { + "type": "String" + } + } + }, + "planetBgColor": "배경색 선택", + "planetColorSelect": "행성 색 선택", + "planetTypeSelect": "행성 타입 선택", + "commonNext": "다음", + "planetCreateFailed": "행성을 만들지 못했습니다. 잠시 후 다시 시도해 주세요.", + "planetCreateConfirm": "선택한 옵션으로\n행성을 생성할까요?", + "commonCreate": "생성", + "briefingOpenFailed": "원문을 열 수 없습니다.", + "campaignAchieved": "{percent}% 달성", + "@campaignAchieved": { + "placeholders": { + "percent": { + "type": "String" + } + } + }, + "campaignView": "캠페인 보기", + "growthFinalReached": "최종 Stage에 도달했어요", + "zapRetrySame": "같은 기여 다시 확인", + "heroOwnerSuffix": "님의", + "heroStartPrompt": "행성을 생성 또는 가입하고\n팬우주를 시작해 보세요!", + "heroNebulaPrompt": "활동을 통해 얻은 POP을 통해\n성운에 기여해 보세요!", + "heroRecentContribution": "{pop} POP 기여로\n꾸준하게 성장하고 있어요.", + "@heroRecentContribution": { + "placeholders": { + "pop": { + "type": "String" + } + } + }, + "heroGrowingTogether": "함께 키워가는 팬덤 행성", + "heroPopProgress": "POP 진행률", + "heroNebula": "성운", + "heroNebulaTap": "성운을 클릭해서\n행성으로 탄생 시켜보세요!", + "heroCreateNebula": "행성 생성 (성운)", + "heroJoinPlanet": "행성 가입", + "heroCreateOrJoin": "행성 생성 또는 가입", + "insideAnswers": "답변 {count}", + "@insideAnswers": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "itemJoinFirst": "행성에 가입한 뒤 영웅의 선물을 보낼 수 있어요.", + "itemBuyNotReady": "아이템 구입은 준비 중입니다.", + "itemUseNotReady": "아이템 사용은 준비 중입니다.", + "payOpenFailed": "결제 창을 열지 못했어요.", + "itemBuy": "아이템 구입", + "itemSelect": "아이템 선택", + "commonComingSoon": "준비 중입니다.", + "itemSendPop": "{pop} POP 전송", + "@itemSendPop": { + "placeholders": { + "pop": { + "type": "String" + } + } + }, + "itemBuyerPop": "구매자 +{pop} POP", + "@itemBuyerPop": { + "placeholders": { + "pop": { + "type": "String" + } + } + }, + "itemOwned": "보유 : {count}", + "@itemOwned": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "itemOwnedCount": "보유 : {count}개", + "@itemOwnedCount": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "growthOwnerOf": "님의 ", + "growthSteadySuffix": " 기여로 꾸준하게 성장하고 있어요.", + "questAttended": "출석 완료", + "questInProgressSpaced": "진행 중", + "questRewardPaid": "보상 지급 완료", + "questClaim": "보상받기", + "questStart": "퀘스트 시작하기", + "questRewardDone": "보상 완료", + "popKindGeneral": "일반 POP", + "popKindContribution": "기여 POP", + "popTxGrant": "지급", + "popTxSpend": "차감", + "popTxRefund": "환불", + "popTxVoid": "무효", + "popTypeQuest": "퀘스트", + "popTypeReward": "보상", + "popTypePayment": "결제", + "popTypeSubscription": "구독", + "popTypeAdmin": "운영", + "popTypeFee": "수수료", + "commonEtc": "기타", + "popReasonQuestSteps": "만보기 퀘스트", + "popReasonQuestMusicShake": "뮤직 셰이크 퀘스트", + "popReasonQuestPlace": "장소 방문 퀘스트", + "popReasonQuestAttend": "출석 퀘스트", + "popReasonAdReward": "광고 보상", + "popReasonHeroDrop": "영웅의 선물", + "popReasonHeroDropTrigger": "영웅의 선물 발동", + "popReasonTranslation": "참여형 번역", + "popReasonIapPurchase": "상품 구매", + "popReasonFocusDaily": "Focus 일일 보상", + "popReasonPreregBonus": "사전예약 보상", + "popReasonReferral": "친구 초대", + "popTypeReferral": "레퍼럴", + "popReasonAdminGrant": "운영 지급", + "popReasonAdminClawback": "운영 회수", + "popReasonCampaignFund": "캠페인 후원", + "popReasonCampaignRefund": "캠페인 환불", + "popReasonCampaignFeeRefund": "캠페인 개설 수수료 환불", + "popReasonCampaignFee": "캠페인 등록비", + "popReasonQnaBounty": "인사이드 현상금", + "popReasonQnaRefund": "현상금 환불", + "popReasonQnaReward": "인사이드 채택 보상", + "popReasonEclipseDefense": "Zap 기여", + "popReasonOther": "POP 변동", + "popRepeat": "{title} {count}회", + "@popRepeat": { + "placeholders": { + "title": { + "type": "String" + }, + "count": { + "type": "String" + } + } + }, + "payStatePaid": "결제 완료", + "payStateRefundRequested": "환불 요청", + "payStateRefundApproved": "환불 승인", + "payStateRefunded": "환불 완료", + "payStateRefundRejected": "환불 반려", + "pushTopicEssentialDesc": "계정·결제·점검 안내입니다. 끄실 수 없습니다.", + "pushTopicFandomEventDesc": "이클립스 · 캠페인 · 퀘스트 · 아티스트 핫타임 소식입니다.", + "pushTopicBriefingDesc": "행성의 하루를 정리한 브리핑 카드를 알려 드립니다.", + "pushTopicCommunityDesc": "내 글의 댓글 · 좋아요 · 답변 알림입니다.", + "pushTopicMarketingDesc": "이벤트 · 혜택 정보를 받습니다. 동의하실 때만 켜집니다.", + "inquiryPending": "접수", + "inquiryInProgress": "처리중", + "inquiryAnswered": "답변 완료", + "photoBadType": "JPG · PNG · WEBP · GIF 이미지만 올릴 수 있습니다.", + "photoTooBig": "사진 용량이 10MB 를 넘습니다. 더 작은 사진을 골라 주세요.", + "photoUnreadable": "사진을 읽지 못했습니다. 다시 골라 주세요.", + "myListInside": "내 인사이드", + "myListInsideEmpty": "아직 올리신 인사이드가 없습니다.", + "myListCampaign": "내 캠페인", + "myListCampaignEmpty": "아직 만드신 캠페인이 없습니다.", + "sortLatest": "최신순", + "sortPop": "POP순", + "sortSolved": "채택 완료", + "sortAchieved": "달성 완료", + "payNotFound": "결제 내역을 찾을 수 없습니다.", + "payItemFallback": "결제 상품", + "payMethodInApp": "앱 내 구입", + "unknownUser": "알 수 없는 사용자", + "blockSelf": "자신은 차단할 수 없습니다.", + "inquiryFallback": "문의", + "photoRejectedServer": "사진을 올릴 수 없습니다. 형식과 용량을 확인해 주세요.", + "photoUploadFailed": "사진 업로드가 실패했습니다. 잠시 후 다시 시도해 주세요.", + "focusClaimed": "오늘의 Focus 보상 +{pop} 기여 POP 을 받았어요.", + "@focusClaimed": { + "placeholders": { + "pop": { + "type": "String" + } + } + }, + "focusActive": "광고 없는 집중모드 이용 중", + "focusUntil": "{date} 까지 · 오늘 몫은 오늘만 받을 수 있어요", + "@focusUntil": { + "placeholders": { + "date": { + "type": "String" + } + } + }, + "focusClaimedToday": "오늘 받음", + "focusClaimToday": "오늘 보상 받기", + "storeConnectFailed": "스토어에 연결하지 못했어요. 잠시 후 다시 시도해 주세요.", + "payRestore": "구매 복원", + "payEmpty": "결제 내역이 없습니다.", + "payRefundGuide": "소비형 상품 : 구매 후 7일 이내이며 사용하지 않은 상품은 환불 가능합니다. 일부라도 사용된 상품은 환불 대상에서 제외됩니다.\n구독형 상품 : 결제 후 24시간 이내 혜택 미사용(추가 보상 등) 시 환불 가능합니다.\n환불 승인 후 7-14 이내 환불 완료 처리가 됩니다.", + "payDetailTitle": "결제 내역 상세", + "payNumber": "결제 번호", + "payDate": "결제 일시", + "payMethod": "결제 방식", + "payStatus": "결제 상태", + "payAmount": "결제 금액", + "payRefundInfo": "환불 안내", + "payRefundApply": "환불 신청", + "payRefundUnavailable": "환불 불가", + "refundReasonMistake": "실수로 결제했습니다", + "refundReasonExpectation": "서비스가 기대와 다릅니다", + "refundReasonError": "오류로 사용하지 못했습니다", + "refundReasonDuplicate": "중복 결제했습니다", + "refundTitle": "환불", + "refundSubmit": "요청", + "refundReason": "환불 사유", + "refundConfirm": "정말 환불 신청을 할까요?", + "myPop": "내 POP", + "commonNoHistory": "내역이 없습니다.", + "nicknameEdit": "닉네임 수정", + "nicknameReserved": "운영자로 오해될 수 있는 이름은 쓸 수 없습니다.", + "nicknameTaken": "이미 쓰고 있는 닉네임입니다.", + "albumItems": "앨범 항목", + "takePhoto": "사진 촬영", + "pickFromAlbum": "앨범에서 고르기", + "tierGuide": "티어 안내", + "statSteps": "누적 걸음", + "statContribution": "누적 기여 POP", + "statQuests": "완료 퀘스트", + "shareInstagram": "인스타그램", + "shareTiktok": "틱톡", + "shareCopyLink": "링크 복사", + "inquiryListTitle": "1:1 문의 접수", + "inquiryEmpty": "접수하신 문의가 없습니다.", + "inquiryWrite": "문의 작성", + "inquiryDetail": "문의 상세", + "commonEdit": "수정", + "inquiryAnswer": "답변", + "inquiryNew": "새 문의 작성", + "inquiryEdit": "문의 수정", + "commonTitle": "제목", + "commonTitleHint": "제목을 입력해주세요.", + "inquiryBody": "문의 내용", + "inquiryBodyHint": "문의할 내용을 작성해 주세요.", + "inquiryBodyHelper": "열 글자 이상 적어 주시면 더 빨리 도와드릴 수 있습니다.", + "inquirySubmitFailed": "문의를 접수하지 못했습니다. {reason}", + "@inquirySubmitFailed": { + "placeholders": { + "reason": { + "type": "String" + } + } + }, + "inquiryEditFailed": "문의를 수정하지 못했습니다. {reason}", + "@inquiryEditFailed": { + "placeholders": { + "reason": { + "type": "String" + } + } + }, + "inquirySubmitted": "문의를 접수했습니다.", + "inquiryEdited": "문의를 수정했습니다.", + "blockedEmpty": "차단한 계정이 없습니다.", + "unblock": "차단 해제", + "unblockFailed": "차단을 해제하지 못했습니다. {reason}", + "@unblockFailed": { + "placeholders": { + "reason": { + "type": "String" + } + } + }, + "tierInfoTitle": "TIER 정보", + "tierPromptPrefix": "행성/성운의 기여를 통해\n", + "tierPromptRank": "랭크", + "tierPromptSuffix": "를 올려보세요", + "tierPassportNote": "Passport Tier 는 레벨과 별개인 카드 외형 등급이며 내려가지 않습니다.", + "tierTotalContribution": "누적 기여", + "notifKindEclipse": "이클립스", + "notifKindItemUsed": "아이템 사용", + "notifKindLike": "좋아요", + "notifKindComment": "댓글", + "notifKindCampaign": "캠페인 달성", + "notifKindQuest": "퀘스트 완료", + "notifTargetPost": "게시물", + "notifTargetCampaign": "캠페인", + "notifTargetQuest": "퀘스트 상세", + "dayToday": "오늘", + "dayYesterday": "어제", + "notifEmpty": "알림이 없습니다.", + "notifGoTo": "{target} 로 이동합니다.", + "@notifGoTo": { + "placeholders": { + "target": { + "type": "String" + } + } + }, + "stationOfficial": "공식", + "stationShop": "판매처", + "stationTicket": "예매", + "stationMedia": "미디어", + "stationLoadFailed": "STATION 을 불러오지 못했습니다", + "commonAll": "전체", + "stationSuggest": "판매처 제안", + "stationEmpty": "아직 등록된 링크가 없습니다.\n행성에 가입하면 큐레이션이 보입니다.", + "stationCategoryEmpty": "이 분류에는 아직 링크가 없습니다.", + "stationSuggestLater": "제안 접수는 서버 연동 후 열립니다.", + "commonSubmit": "제출", + "commonName": "이름", + "commonNameRequired": "이름을 입력해 주세요.", + "commonUrl": "주소", + "commonUrlRequired": "주소를 입력해 주세요.", + "commonUrlHttps": "https 로 시작하는 주소를 입력해 주세요.", + "commonCategory": "분류", + "stationReason": "추천 이유 (선택)", + "stationSuggestNote": "제안하신 링크는 운영 검토를 거쳐 등록됩니다. 검토 결과는 알림으로 알려 드립니다.", + "heroGiftUnavailable": "이 선물은 현재 수령할 수 없습니다.", + "heroGiftAlready": "이미 참여한 선물입니다. POP 내역을 확인해 주세요.", + "heroGiftRelogin": "다시 로그인한 뒤 참여해 주세요.", + "heroGiftNoPermission": "이 선물에 참여할 수 있는 권한이 없습니다.", + "heroGiftHowTo": "폰을 흔들거나 아래 버튼을 눌러 빛 에너지 선물에 참여하세요.", + "heroGiftPopNote": "받을 수 있는 POP은 남은 선물과 1인 상한에 따라 결정됩니다.", + "heroGiftReceived": "{total} POP 수령 확인", + "@heroGiftReceived": { + "placeholders": { + "total": { + "type": "String" + } + } + }, + "heroGiftUnknown": "지급 결과를 확인하지 못했습니다. 다시 확인해 주세요.", + "heroGiftNone": "지금 참여할 수 있는 선물이 없습니다.", + "heroGiftConfirmed": "참여한 선물의 수령을 확인했습니다.", + "heroGiftChecking": "지급 확인 중…", + "heroGiftRetry": "같은 선물 다시 확인", + "heroGiftTap": "흔들기 대신 버튼으로 참여", + "rankMyPlanet": "내 행성의 {metric} 랭킹", + "@rankMyPlanet": { + "placeholders": { + "metric": { + "type": "String" + } + } + }, + "rankPlanetEmpty": "아직 집계된 행성 랭킹이 없습니다", + "rankScopeLive": "실시간", + "rankScopeCountry": "국가별", + "rankMine": "나의 {metric} 랭킹", + "@rankMine": { + "placeholders": { + "metric": { + "type": "String" + } + } + }, + "rankMyCountry": "내 국가의 {metric} 랭킹", + "@rankMyCountry": { + "placeholders": { + "metric": { + "type": "String" + } + } + }, + "rankEmpty": "아직 집계된 랭킹이 없습니다", + "rankScope": "{scope} 랭킹", + "@rankScope": { + "placeholders": { + "scope": { + "type": "String" + } + } + }, + "rankLoadFailed": "랭킹을 불러오지 못했습니다", + "rankMeSuffix": "{name} (나)", + "@rankMeSuffix": { + "placeholders": { + "name": { + "type": "String" + } + } + }, + "reportDefamation": "아티스트 비방 및 명예훼손", + "reportInappropriate": "부적절한 콘텐츠", + "reportSpam": "스팸 및 영리 목적 광고", + "reportPrivacy": "개인정보 노출 및 사칭", + "reportTitle": "신고하기", + "reportSubmit": "신고", + "reportAuthor": "작성자", + "reportReasonLabel": "신고 사유", + "reportConfirm": "이 게시물을 신고할까요?\n접수 후에는 취소할 수 없습니다.", + "blockMemberConfirm": "이 회원의 모든 게시물을 차단할까요?", + "loungeMembersOnly": "이 행성 팬덤 회원만 글 · 댓글 · 좋아요를 남길 수 있습니다.", + "loungeFailed": "처리하지 못했습니다. 잠시 후 다시 시도해 주세요.", + "loungeTitle": "팬덤 라운지", + "likeCancel": "좋아요 취소", + "like": "좋아요", + "commentDeleteConfirm": "댓글을 삭제할까요?", + "commonShare": "공유", + "commonEditAction": "수정하기", + "commonDeleteAction": "삭제하기", + "commonBlockAction": "차단하기", + "commentFirst": "첫 댓글을 남겨주세요.", + "postDeleteConfirm": "이 글을 삭제할까요?", + "reportReceived": "신고를 접수했습니다.", + "commonDelete": "삭제", + "commentHint": "댓글을 입력해 보세요.", + "commonSend": "보내기", + "postWrite": "글 쓰기", + "postEdit": "글 수정", + "loungeHint": "팬덤 라운지에 자유롭게 글을 작성해 보세요.", + "photoRemove": "사진 {n} 빼기", + "@photoRemove": { + "placeholders": { + "n": { + "type": "String" + } + } + }, + "camera": "카메라", + "album": "앨범", + "historyTitle": "PASSPORT · 기록", + "historyMine": "내 활동", + "historyEvolution": "Stage {from} → {to} 진화", + "@historyEvolution": { + "placeholders": { + "from": { + "type": "String" + }, + "to": { + "type": "String" + } + } + }, + "historyHibernation": "행성 휴면", + "historyWakeUp": "행성 깨어남", + "historyZap": "ZAP 기여", + "historyQuest": "Quest 보상", + "historyAdBonus": "광고 추가 보상", + "historyFunding": "Campaign 기여", + "historyGrowthPop": "성장에 {amount} POP 반영", + "@historyGrowthPop": { + "placeholders": { + "amount": { + "type": "String" + } + } + }, + "historyJoined": "이 진화에 함께했습니다", + "historyLoadFailed": "기록을 불러오지 못했습니다.", + "historyMineEmpty": "아직 기록된 활동이 없습니다.", + "historyFandomEmpty": "아직 기록된 팬덤 역사가 없습니다.", + "historyMore": "이전 기록 더 보기", + "countryName": "{code, select, KR{대한민국} JP{일본} US{미국} CN{중국} TW{대만} HK{홍콩} TH{태국} VN{베트남} ID{인도네시아} PH{필리핀} MY{말레이시아} SG{싱가포르} IN{인도} GB{영국} FR{프랑스} DE{독일} ES{스페인} BR{브라질} MX{멕시코} CA{캐나다} AU{호주} ZZ{미지정} other{-}}", + "@countryName": { + "placeholders": { + "code": { + "type": "String" + } + } + }, + "loungeMaxPhotos": "사진은 {count}장까지 올릴 수 있습니다.", + "@loungeMaxPhotos": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "commonLoadFailed": "불러오지 못했습니다", + "planetPassportHistory": "PASSPORT · 팬덤 역사", + "planetNamed": "{name} 행성", + "@planetNamed": { + "placeholders": { + "name": { + "type": "String" + } + } + }, + "blockNebulaConfirm": "이 성운을 차단할까요?", + "blockPlanetConfirm": "이 행성을 차단할까요?", + "blocked": "차단했습니다.", + "planetLastUpdated": "마지막 갱신 {time}", + "@planetLastUpdated": { + "placeholders": { + "time": { + "type": "String" + } + } + }, + "planetContribution": "기여 현황", + "planetNebulaGrowth": "성운 성장률", + "planetLiveRanking": "실시간 기여 랭킹", + "planetNoContributors": "아직 기여자가 없어요", + "planetContributeCta": "기여하고 랭킹에 등극해보세요!", + "planetOverallRank": "전체 {rank}위", + "@planetOverallRank": { + "placeholders": { + "rank": { + "type": "String" + } + } + }, + "planetMyContribution": "내 기여도", + "planetTallying": "집계 준비 중", + "planetTopPercent": "상위 {percent}%", + "@planetTopPercent": { + "placeholders": { + "percent": { + "type": "String" + } + } + }, + "planetHideMember": "이 회원의 글 모두 숨기기", + "reportReasonUnknown": "신고 사유를 확인할 수 없습니다.", + "reportAlready": "이미 신고한 게시물입니다.", + "blockUnavailable": "차단할 수 없는 작성자입니다.", + "commonMe": "나", + "planetSizeMedium": "중형 팬덤", + "planetPerHead": "1인당 화력 : {value}", + "@planetPerHead": { + "placeholders": { + "value": { + "type": "String" + } + } + }, + "planetJoined": "가입중", + "planetJoin": "가입하기", + "questDetails": "자세히", + "questInProgress": "진행중", + "questTypeAttend": "출석", + "questTypeSteps": "걷기", + "questTypePlace": "특정 위치", + "questTypeStreaming": "스트리밍", + "questTypeUnverifiable": "이 퀘스트 유형의 검증 방식이 아직 정해지지 않았습니다.", + "questTabMine": "내 퀘스트", + "placePickPrompt": "퀘스트를 완료할\n장소를 선택해주세요.", + "placeSearch": "장소 검색", + "commonNoResults": "검색 결과가 없습니다.", + "commonSelectDone": "선택 완료", + "adMoreChance": "더 많은 기여의 기회를 얻으세요.", + "adWatchPrompt": "광고를 시청하고 추가 보상을\n받을 수 있어요!", + "adBonusPlaceholder": "+ 기여 POP", + "adPartyPack": "파티 팩", + "adPickProduct": "상품 선택하기", + "adWatchBonus": "광고 보고 추가 보상 받기", + "adBasicReward": "기본 보상 받기", + "focusMode": "광고 없는 집중모드", + "pricePerMonth": "{price}/월", + "@pricePerMonth": { + "placeholders": { + "price": { + "type": "String" + } + } + }, + "adWarpBefore": "Warping...우리 팬덤과 아티스트에 더 많은 기여를 위해 적합한 광고를 열심히 찾고 있어요.", + "adWarpAfter": "Warping...당신의 기여에 대한 보상을 준비하고 있어요.", + "adSkip": "건너뛰기", + "questPreviewHint": "옵션을 선택, 미리 보기를 확인하세요", + "questNoCategory": "아직 선택된 카테고리가 없습니다", + "questNew": "새 퀘스트", + "questCategory": "카테고리", + "questName": "퀘스트명", + "questAction": "상세 액션", + "questPickPlace": "목표 위치를 골라 주세요", + "questGoalValue": "목표 수치", + "questParticipants": "참여 인원", + "questRewardPop": "완료 보상 POP", + "questCreateConfirm": "이 내용으로 만들까요?", + "questCreateSummary": "{title} · 목표 {goal} · 보상 {reward} POP", + "@questCreateSummary": { + "placeholders": { + "title": { + "type": "String" + }, + "goal": { + "type": "String" + }, + "reward": { + "type": "String" + } + } + }, + "commonKeepEditing": "더 고치기", + "questCreate": "만들기", + "questNamed": "{title} 퀘스트", + "@questNamed": { + "placeholders": { + "title": { + "type": "String" + } + } + }, + "questDailyHelp": "매일매일 행성에 도움을", + "questTodayMust": "오늘 꼭 해야하는 퀘스트에요", + "questTabEmpty": "이 탭에는 아직 퀘스트가 없습니다.", + "questCreateNotReady": "퀘스트 생성은 보상 재원 예치와 함께 여는 기능입니다. 준비 중입니다.", + "questNoBaseReward": "기본 보상 기록을 찾지 못해 광고 추가 보상을 받을 수 없어요.", + "questNoSteps": "걸음 수가 아직 수집되지 않았습니다. 조금 걸은 뒤 다시 시도해 주세요.", + "questNoPlace": "이 퀘스트에 인증할 장소가 등록되어 있지 않습니다.", + "questGoal": "목표", + "questReward": "보상", + "questPopGrant": "POP 지급", + "commonChecking": "확인 중…", + "questMine": "내가 만든 퀘스트입니다. 참여자는 {count}명입니다.", + "@questMine": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "questStopConfirm": "퀘스트를 중단할까요?", + "questStopBody": "지금까지의 진행이 초기화되고 처음 상태로 돌아갑니다.", + "questStop": "중단", + "adNotFinished": "광고를 끝까지 보지 않아 추가 보상은 없어요. 기본 보상은 그대로 받습니다.", + "openSettings": "설정 열기", + "permStepsDenied": "걸음 수 권한이 꺼져 있습니다. 설정에서 「신체 활동」을 허용해 주세요.", + "permStepsNeeded": "걸음 수를 세려면 신체 활동 권한이 필요합니다.", + "permLocationOff": "기기의 위치 기능이 꺼져 있습니다. 켜고 다시 시도해 주세요.", + "permLocationNeeded": "방문을 확인하려면 위치 권한이 필요합니다.", + "permLocationDenied": "위치 권한이 꺼져 있습니다. 설정에서 위치 접근을 허용해 주세요.", + "questRewardReceived": " 받았어요!", + "questAdBonus": "광고 추가 보상 +{pop} POP", + "@questAdBonus": { + "placeholders": { + "pop": { + "type": "String" + } + } + }, + "questParticipantsCount": "참여 인원 : {count}", + "@questParticipantsCount": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "questParticipantsCap": "참여 인원 : {count} / {cap}", + "@questParticipantsCap": { + "placeholders": { + "count": { + "type": "String" + }, + "cap": { + "type": "String" + } + } + }, + "nebulaCreateFailed": "성운을 만들지 못했습니다. 잠시 후 다시 시도해 주세요.", + "nebulaAlreadyToday": "이미 오늘 성운을 생성하셨습니다.", + "nebulaCreatedKeepPrimary": "성운을 만들었습니다. 기본 행성은 최근 변경 후 30일이 지나야 바꿀 수 있어 그대로 유지됩니다.", + "fandomFounderLeaveBlocked": "행성이 탄생한 뒤에 탈퇴할 수 있어요.", + "sortPopular": "인기순", + "campaignSortContributing": "기여중", + "campaignStatusReview": "운영 검수 중인 캠페인입니다.", + "campaignStatusRejected": "운영 검수에서 반려되었습니다. 등록비는 돌려드렸습니다.", + "campaignStatusWithdrawn": "개설자가 철회한 캠페인입니다.", + "campaignStatusCancelled": "운영 사유로 취소되었습니다. 등록비와 후원 POP 을 돌려드렸습니다.", + "rankWindowDaily": "일간", + "rankWindowWeekly": "주간", + "campaignNotFound": "캠페인을 찾을 수 없습니다.", + "campaignMediaMain": "대표 이미지", + "campaignLoadFailed": "캠페인을 불러오지 못했습니다.", + "campaignTabInfo": "캠페인 정보", + "campaignTabBoard": "응원 게시판 {count}", + "@campaignTabBoard": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "campaignWithdraw": "캠페인 철회", + "campaignContributeAction": "기여하기", + "campaignFundingStatus": "현재 펀딩 현황", + "campaignSupporting": "응원 중", + "campaignTimeLeft": "남았어요!", + "campaignReport": "결과보고", + "campaignReportPhotos": "집행 사진", + "campaignBudget": "예산 사용 내역", + "campaignTotal": "합계", + "campaignReceipts": "영수증", + "campaignDaysLeft": "{days}일 {time}", + "@campaignDaysLeft": { + "placeholders": { + "days": { + "type": "String" + }, + "time": { + "type": "String" + } + } + }, + "campaignWithdrawTitle": "캠페인을 철회할까요?", + "campaignWithdrawRefund": "공개 전 철회라 등록비 {fee} POP 을 낸 재화 그대로 돌려드립니다.", + "@campaignWithdrawRefund": { + "placeholders": { + "fee": { + "type": "String" + } + } + }, + "campaignWithdrawNoRefund": "이미 공개된 캠페인이라 등록비 {fee} POP 은 돌려드리지 않습니다.", + "@campaignWithdrawNoRefund": { + "placeholders": { + "fee": { + "type": "String" + } + } + }, + "campaignWithdrawConfirm": "철회하기", + "shareCopied": "공유 문구를 복사했습니다.", + "campaignContributePop": "기여 POP 설정", + "campaignEmptyContributing": "아직 기여하신 캠페인이 없습니다.", + "campaignEmpty": "등록된 캠페인이 없습니다.", + "campaignNew": "새 캠페인", + "campaignMyRanking": "나의 기여 랭킹", + "campaignWindowRanking": "{window} 랭킹", + "@campaignWindowRanking": { + "placeholders": { + "window": { + "type": "String" + } + } + }, + "campaignSearchHint": "캠페인 검색", + "campaignNoRecentSearches": "최신 검색어가 없습니다", + "searchRemoveTerm": "{term} 삭제", + "@searchRemoveTerm": { + "placeholders": { + "term": { + "type": "String" + } + } + }, + "campaignNoResults": "검색 결과가 없습니다", + "shareTopicCampaign": "캠페인", + "sharePlanetLine": "{brand} · {name} 행성", + "@sharePlanetLine": { + "placeholders": { + "brand": { + "type": "String" + }, + "name": { + "type": "String" + } + } + }, + "campaignGaugeLabel": "캠페인 기여률", + "campaignPeriodTitle": "캠페인 진행 기간을 선택하세요", + "campaignPeriodMax": "기간은 최대 1년까지 선택할 수 있어요", + "campaignYearMonth": "{year}년 {month}월", + "@campaignYearMonth": { + "placeholders": { + "year": { + "type": "String" + }, + "month": { + "type": "String" + } + } + }, + "campaignWeekdays": "일,월,화,수,목,금,토", + "campaignPeriodRange": "{startMonth}월 {startDay}일 ~ {endMonth}월 {endDay}일", + "@campaignPeriodRange": { + "placeholders": { + "startMonth": { + "type": "String" + }, + "startDay": { + "type": "String" + }, + "endMonth": { + "type": "String" + }, + "endDay": { + "type": "String" + } + } + }, + "campaignPeriodSelect": "기간 선택", + "calendarPrevMonth": "이전 달", + "calendarNextMonth": "다음 달", + "campaignShareTitle": "캠페인 공유", + "campaignPickMedia": "이미지/영상 선택", + "campaignTitleHint": "캠페인 제목을 입력해주세요.", + "campaignBodyHint": "캠페인 설명을 작성해주세요.", + "campaignReviewNote": "올리시면 운영 검수를 지난 뒤 모금이 열립니다.", + "campaignSupportType": "지원 종류 선택", + "campaignPeriod": "캠페인 진행 기간", + "campaignDateSelect": "날짜 선택", + "campaignGoalPop": "목표 기여 POP", + "campaignSubmitTitle": "이 내용으로 올릴까요?", + "campaignSubmitBody": "{title} · 목표 {goal} POP · {days}일\n등록비 {fee} POP 이 빠지고 운영 검수 뒤 모금이 열립니다.", + "@campaignSubmitBody": { + "placeholders": { + "title": { + "type": "String" + }, + "goal": { + "type": "String" + }, + "days": { + "type": "String" + }, + "fee": { + "type": "String" + } + } + }, + "campaignSubmit": "올리기", + "campaignCreateFailed": "캠페인을 올리지 못했습니다.", + "campaignNeedsQuests": "퀘스트 완료 {done}/{need} — 퀘스트를 {left}번 더 완료하면 캠페인을 열 수 있습니다.", + "@campaignNeedsQuests": { + "placeholders": { + "done": { + "type": "String" + }, + "need": { + "type": "String" + }, + "left": { + "type": "String" + } + } + }, + "campaignPendingTitle": "검수 대기 중입니다", + "campaignPendingBody": "운영 검수를 마치면 모금이 열리고 목록에 보입니다.", + "insideRefundPending": "채택 기한이 지나 현상금이 환불될 예정입니다.", + "insideRefundDone": "채택 기한이 지나 현상금이 환불되었습니다.", + "insideRefundChecking": "채택 기한이 지나 현상금 환불을 확인하고 있습니다.", + "insideErrPopShort": "POP 이 모자라 현상금을 걸지 못했습니다.", + "insideErrSelfAccept": "자기 답변은 채택할 수 없습니다.", + "insideErrAlreadyAccepted": "이미 채택한 질문입니다. 채택은 바꿀 수 없습니다.", + "insideErrNotOwner": "질문을 올린 분만 채택할 수 있습니다.", + "commonCheckConnection": "연결을 확인하고 다시 시도해 주세요.", + "insideAnswerLater": "댓글 등록은 서버 연동 후 열립니다.", + "insideAcceptConfirm": "이 댓글을 채택할까요?\n현상금 {pop} POP 이 전달되며 채택은 되돌릴 수 없습니다.", + "@insideAcceptConfirm": { + "placeholders": { + "pop": { + "type": "String" + } + } + }, + "insideAccept": "채택", + "insideMore": "더보기", + "insideFirstAnswer": "첫 댓글을 남겨주세요.", + "insideAcceptDone": "채택 완료", + "insideAcceptAction": "채택하기", + "insideAnswerHint": "댓글을 입력해 보세요.", + "insideLoadFailed": "인사이드를 불러오지 못했습니다", + "insideEmpty": "아직 올라온 질문이 없습니다.", + "insideWrite": "인사이드 작성", + "insideSearchHint": "인사이드 제목, 내용", + "insideBodyHint": "팬덤들과 자유롭게 질문을 주고받아 보세요.", + "insideTitleRequired": "제목을 입력해 주세요.", + "insideBodyRequired": "내용을 입력해 주세요.", + "insideBountySet": "현상금 설정", + "insideBountyHint": "{min} 이상 설정해 주세요.", + "@insideBountyHint": { + "placeholders": { + "min": { + "type": "String" + } + } + }, + "insideAttachLater": "이미지 첨부는 서버 연동 후 열립니다.", + "insideBountyRequired": "현상금을 입력해 주세요.", + "insideNumberOnly": "숫자로 입력해 주세요.", + "insideBountyMin": "현상금은 {min} POP 이상이어야 합니다.", + "@insideBountyMin": { + "placeholders": { + "min": { + "type": "String" + } + } + }, + "insideBountyOverBalance": "보유 POP 을 넘을 수 없습니다.", + "questTypeTimeAttack": "긴급 타임어택", + "questTimeLeft": "남은 시간", + "questTimeAttackClosed": "마감", + "questTimeAttackLeft": "{time} 남음", + "@questTimeAttackLeft": { + "placeholders": { + "time": { + "type": "String" + } + } + }, + "popReasonQuestTimeAttack": "긴급 돌발 타임어택", + "briefingShareCopied": "공유 문구와 링크를 복사했습니다.", + "shareCopyText": "문구 복사", + "briefingShareTextCopied": "공유 문구를 복사했습니다.", + "questAlreadyRewarded": "이미 받은 보상입니다(+{pop} POP).", + "@questAlreadyRewarded": { + "placeholders": { + "pop": { + "type": "String" + } + } + }, + "questCatHealth": "건강", + "questCatPlanet": "행성 활동", + "questCatCommunity": "커뮤니티", + "questActShake": "쉐이크", + "questTypeVote": "투표 참여", + "questTypeSns": "SNS", + "questActivity": "활동", + "questNoCreatable": "지금 만들 수 있는 퀘스트 활동이 없습니다.", + "questDescriptionOptional": "설명 (선택)", + "questLinkUrl": "링크 주소", + "questLinkCheck": "링크 확인", + "questLinkInvalid": "올바른 링크가 아닙니다. 주소를 확인해 주세요.", + "questLinkPreviewFailed": "링크 정보를 불러오지 못했어요. 제목을 직접 입력해 주세요.", + "questLinkTitle": "링크 제목", + "questEndDate": "종료일", + "questEndDefault": "공개 후 24시간", + "questEndDateValue": "{date} 23:59:59 마감", + "@questEndDateValue": { + "placeholders": { + "date": { + "type": "String" + } + } + }, + "questPoolHelper": "총 {pool} POP 예치 · 수수료 {fee} POP", + "@questPoolHelper": { + "placeholders": { + "pool": { + "type": "String" + }, + "fee": { + "type": "String" + } + } + }, + "questCreateCost": "{title}\n보상 {reward} POP × {count}명 = {pool} POP\n수수료 {fee} POP (공개 시 소멸)\n총 {total} POP 이 차감됩니다.", + "@questCreateCost": { + "placeholders": { + "title": { + "type": "String" + }, + "reward": { + "type": "String" + }, + "count": { + "type": "String" + }, + "pool": { + "type": "String" + }, + "fee": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "questNoReward": "무보상 참여 퀘스트예요.", + "questDeadline": "마감", + "questRemainingHours": "{hours}시간 남음", + "@questRemainingHours": { + "placeholders": { + "hours": { + "type": "String" + } + } + }, + "questEnded": "마감됨", + "questAuthorNote": "작성자 설명", + "questRepeatOnce": "1인 1회 참여", + "questRepeatEvery": "{hours}시간 간격 · 최대 {count}회", + "@questRepeatEvery": { + "placeholders": { + "hours": { + "type": "String" + }, + "count": { + "type": "String" + } + } + }, + "questSubmit": "인증 제출", + "questResubmit": "보완 제출", + "questEvidenceUrl": "인증 링크 (선택)", + "questEvidenceText": "인증 내용", + "questSubmitted": "제출했어요. 검토 후 보상이 지급돼요.", + "questReviewPending": "검토 중이에요. 승인되면 보상이 지급돼요.", + "questReviewDelayed": "검토가 늦어지고 있어요. 운영팀이 확인하고 있습니다.", + "questApprovedPaying": "승인되었어요. 보상을 지급하고 있어요.", + "questRejected": "반려: {reason}", + "@questRejected": { + "placeholders": { + "reason": { + "type": "String" + } + } + }, + "questResubmitAllowed": "보완해서 다시 제출할 수 있어요.", + "questResubmitNotAllowed": "이 제출 건은 다시 제출할 수 없어요.", + "questReviewButton": "검토 중", + "questRejectedButton": "반려됨", + "questReviewQueue": "검토 대기 {count}건", + "@questReviewQueue": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "questApprove": "승인", + "questReject": "반려", + "questRejectReason": "반려 사유", + "questAllowResubmit": "보완 재제출 허용", + "questEdit": "제목 · 설명 수정", + "questEditNotice": "공개 후에는 제목과 설명만 고칠 수 있고 수정 이력이 남아요.", + "popReasonQuestUgc": "퀘스트 보상", + "popReasonQuestFee": "퀘스트 개설 수수료", + "questFreeDailyCap": "보상 없는 퀘스트는 하루 {cap}개까지 만들 수 있어요.", + "@questFreeDailyCap": { + "placeholders": { + "cap": { + "type": "String" + } + } + }, + "popReasonQuestPoolDeposit": "퀘스트 보상 예치", + "popReasonQuestPoolReturn": "퀘스트 보상 반환", + "campaignCommentNeedNickname": "닉네임을 먼저 정해 주세요", + "campaignCommentSetNickname": "닉네임 정하기" +} diff --git a/app/lib/l10n/app_localizations.dart b/app/lib/l10n/app_localizations.dart new file mode 100644 index 0000000..fc604ac --- /dev/null +++ b/app/lib/l10n/app_localizations.dart @@ -0,0 +1,4886 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:intl/intl.dart' as intl; + +import 'app_localizations_en.dart'; +import 'app_localizations_es.dart'; +import 'app_localizations_ja.dart'; +import 'app_localizations_ko.dart'; +import 'app_localizations_zh.dart'; + +// ignore_for_file: type=lint + +/// Callers can lookup localized strings with an instance of AppLocalizations +/// returned by `AppLocalizations.of(context)`. +/// +/// Applications need to include `AppLocalizations.delegate()` in their app's +/// `localizationDelegates` list, and the locales they support in the app's +/// `supportedLocales` list. For example: +/// +/// ```dart +/// import 'l10n/app_localizations.dart'; +/// +/// return MaterialApp( +/// localizationsDelegates: AppLocalizations.localizationsDelegates, +/// supportedLocales: AppLocalizations.supportedLocales, +/// home: MyApplicationHome(), +/// ); +/// ``` +/// +/// ## Update pubspec.yaml +/// +/// Please make sure to update your pubspec.yaml to include the following +/// packages: +/// +/// ```yaml +/// dependencies: +/// # Internationalization support. +/// flutter_localizations: +/// sdk: flutter +/// intl: any # Use the pinned version from flutter_localizations +/// +/// # Rest of dependencies +/// ``` +/// +/// ## iOS Applications +/// +/// iOS applications define key application metadata, including supported +/// locales, in an Info.plist file that is built into the application bundle. +/// To configure the locales supported by your app, you’ll need to edit this +/// file. +/// +/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file. +/// Then, in the Project Navigator, open the Info.plist file under the Runner +/// project’s Runner folder. +/// +/// Next, select the Information Property List item, select Add Item from the +/// Editor menu, then select Localizations from the pop-up menu. +/// +/// Select and expand the newly-created Localizations item then, for each +/// locale your application supports, add a new item and select the locale +/// you wish to add from the pop-up menu in the Value field. This list should +/// be consistent with the languages listed in the AppLocalizations.supportedLocales +/// property. +abstract class AppLocalizations { + AppLocalizations(String locale) + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + + final String localeName; + + static AppLocalizations? of(BuildContext context) { + return Localizations.of(context, AppLocalizations); + } + + static const LocalizationsDelegate delegate = + _AppLocalizationsDelegate(); + + /// A list of this localizations delegate along with the default localizations + /// delegates. + /// + /// Returns a list of localizations delegates containing this delegate along with + /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, + /// and GlobalWidgetsLocalizations.delegate. + /// + /// Additional delegates can be added by appending to this list in + /// MaterialApp. This list does not have to be used at all if a custom list + /// of delegates is preferred or required. + static const List> localizationsDelegates = + >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; + + /// A list of this localizations delegate's supported locales. + static const List supportedLocales = [ + Locale('en'), + Locale('es'), + Locale('ja'), + Locale('ko'), + Locale('zh'), + ]; + + /// No description provided for @commonConfirm. + /// + /// In ko, this message translates to: + /// **'확인'** + String get commonConfirm; + + /// No description provided for @commonCancel. + /// + /// In ko, this message translates to: + /// **'취소'** + String get commonCancel; + + /// No description provided for @commonClose. + /// + /// In ko, this message translates to: + /// **'닫기'** + String get commonClose; + + /// No description provided for @commonSelect. + /// + /// In ko, this message translates to: + /// **'선택'** + String get commonSelect; + + /// No description provided for @commonRetry. + /// + /// In ko, this message translates to: + /// **'다시 시도'** + String get commonRetry; + + /// No description provided for @commonView. + /// + /// In ko, this message translates to: + /// **'보기'** + String get commonView; + + /// No description provided for @commonPrev. + /// + /// In ko, this message translates to: + /// **'이전'** + String get commonPrev; + + /// No description provided for @commonDone. + /// + /// In ko, this message translates to: + /// **'완료'** + String get commonDone; + + /// No description provided for @commonStart. + /// + /// In ko, this message translates to: + /// **'시작'** + String get commonStart; + + /// No description provided for @commonNotYet. + /// + /// In ko, this message translates to: + /// **'{where} 화면은 구현 확인물 2차(2026-09-10)에 들어갑니다.'** + String commonNotYet(String where); + + /// No description provided for @languageSettingsTitle. + /// + /// In ko, this message translates to: + /// **'언어 설정'** + String get languageSettingsTitle; + + /// No description provided for @authLogin. + /// + /// In ko, this message translates to: + /// **'로그인'** + String get authLogin; + + /// No description provided for @authSignup. + /// + /// In ko, this message translates to: + /// **'회원가입'** + String get authSignup; + + /// No description provided for @authFirstTime. + /// + /// In ko, this message translates to: + /// **'팬우주는 처음이세요?'** + String get authFirstTime; + + /// No description provided for @authAlreadyMember. + /// + /// In ko, this message translates to: + /// **'이미 가입하셨나요?'** + String get authAlreadyMember; + + /// No description provided for @authLoginHeadline. + /// + /// In ko, this message translates to: + /// **'가장 편한 방법으로\n팬우주에 로그인하세요!'** + String get authLoginHeadline; + + /// No description provided for @authSignupHeadline. + /// + /// In ko, this message translates to: + /// **'가장 편한 방법으로\n팬우주에 회원가입 하세요!'** + String get authSignupHeadline; + + /// No description provided for @authProviderLogin. + /// + /// In ko, this message translates to: + /// **'{provider, select, apple{Apple로 로그인} google{Google로 로그인} kakao{Kakao로 로그인} line{LINE으로 로그인} other{이메일로 로그인}}'** + String authProviderLogin(String provider); + + /// No description provided for @authProviderSignup. + /// + /// In ko, this message translates to: + /// **'{provider, select, apple{Apple로 가입하기} google{Google로 가입하기} kakao{Kakao로 가입하기} line{LINE으로 가입하기} other{이메일로 가입하기}}'** + String authProviderSignup(String provider); + + /// No description provided for @authNotConfiguredTitle. + /// + /// In ko, this message translates to: + /// **'{name} 로그인 준비 중'** + String authNotConfiguredTitle(String name); + + /// No description provided for @authNotConfiguredBody. + /// + /// In ko, this message translates to: + /// **'{name} 로그인은 발주사 키가 들어오면 열립니다.\n\n필요한 항목\n· {items}'** + String authNotConfiguredBody(String name, String items); + + /// No description provided for @authNotConfiguredNotice. + /// + /// In ko, this message translates to: + /// **'{name} 로그인은 아직 준비 중입니다.\n필요한 항목: {items}'** + String authNotConfiguredNotice(String name, String items); + + /// No description provided for @authResetNeedEmail. + /// + /// In ko, this message translates to: + /// **'재설정 메일을 받으실 이메일을 먼저 입력해 주세요.'** + String get authResetNeedEmail; + + /// No description provided for @authResetSent. + /// + /// In ko, this message translates to: + /// **'입력하신 주소로 재설정 안내를 보냈습니다.'** + String get authResetSent; + + /// No description provided for @termsTitle. + /// + /// In ko, this message translates to: + /// **'이용 약관'** + String get termsTitle; + + /// No description provided for @termsAgreeAll. + /// + /// In ko, this message translates to: + /// **'전체 동의'** + String get termsAgreeAll; + + /// No description provided for @termsService. + /// + /// In ko, this message translates to: + /// **'서비스 이용약관 (필수)'** + String get termsService; + + /// No description provided for @termsLocation. + /// + /// In ko, this message translates to: + /// **'위치기반 서비스 이용약관 (필수)'** + String get termsLocation; + + /// No description provided for @termsAge. + /// + /// In ko, this message translates to: + /// **'만 14세 이상입니다. (필수)'** + String get termsAge; + + /// No description provided for @termsMarketing. + /// + /// In ko, this message translates to: + /// **'마케팅 정보 및 이벤트 알림 수신 동의 (선택)'** + String get termsMarketing; + + /// No description provided for @signupDoneTitle. + /// + /// In ko, this message translates to: + /// **'회원가입이 완료되었습니다'** + String get signupDoneTitle; + + /// No description provided for @signupDoneBody. + /// + /// In ko, this message translates to: + /// **'팬덤 활동을 시작할 수 있습니다.\n다양한 활동을 통해 내 팬덤 행성을 키워보세요.'** + String get signupDoneBody; + + /// No description provided for @homeItemsTooltip. + /// + /// In ko, this message translates to: + /// **'아이템'** + String get homeItemsTooltip; + + /// No description provided for @homeNotificationsTooltip. + /// + /// In ko, this message translates to: + /// **'알림'** + String get homeNotificationsTooltip; + + /// No description provided for @homeFandomHistory. + /// + /// In ko, this message translates to: + /// **'팬덤 역사'** + String get homeFandomHistory; + + /// No description provided for @homePopularInside. + /// + /// In ko, this message translates to: + /// **'인기 K-INSIDE'** + String get homePopularInside; + + /// No description provided for @homeWhereBriefing. + /// + /// In ko, this message translates to: + /// **'AI 브리핑'** + String get homeWhereBriefing; + + /// No description provided for @homeWhereInsideList. + /// + /// In ko, this message translates to: + /// **'INSIDE 목록'** + String get homeWhereInsideList; + + /// No description provided for @homeWhereInsideDetail. + /// + /// In ko, this message translates to: + /// **'INSIDE 상세'** + String get homeWhereInsideDetail; + + /// No description provided for @homeLoadFailed. + /// + /// In ko, this message translates to: + /// **'홈을 불러오지 못했습니다'** + String get homeLoadFailed; + + /// No description provided for @homeRetryLater. + /// + /// In ko, this message translates to: + /// **'잠시 뒤 다시 시도해 주세요.'** + String get homeRetryLater; + + /// No description provided for @myProfileCardTooltip. + /// + /// In ko, this message translates to: + /// **'프로필 카드'** + String get myProfileCardTooltip; + + /// No description provided for @myTabPlanets. + /// + /// In ko, this message translates to: + /// **'행성/성운'** + String get myTabPlanets; + + /// No description provided for @myTabActivity. + /// + /// In ko, this message translates to: + /// **'활동'** + String get myTabActivity; + + /// No description provided for @myInside. + /// + /// In ko, this message translates to: + /// **'인사이드'** + String get myInside; + + /// No description provided for @myCampaign. + /// + /// In ko, this message translates to: + /// **'캠페인'** + String get myCampaign; + + /// No description provided for @myShareLater. + /// + /// In ko, this message translates to: + /// **'{target} 공유는 09-10 회차에 연결합니다.'** + String myShareLater(String target); + + /// No description provided for @myEditPhoto. + /// + /// In ko, this message translates to: + /// **'프로필 사진 수정'** + String get myEditPhoto; + + /// No description provided for @myEditProfile. + /// + /// In ko, this message translates to: + /// **'프로필 수정'** + String get myEditProfile; + + /// No description provided for @settingsTitle. + /// + /// In ko, this message translates to: + /// **'설정'** + String get settingsTitle; + + /// No description provided for @settingsAccount. + /// + /// In ko, this message translates to: + /// **'계정'** + String get settingsAccount; + + /// No description provided for @settingsNickname. + /// + /// In ko, this message translates to: + /// **'닉네임'** + String get settingsNickname; + + /// No description provided for @settingsEmail. + /// + /// In ko, this message translates to: + /// **'이메일'** + String get settingsEmail; + + /// No description provided for @settingsSns. + /// + /// In ko, this message translates to: + /// **'SNS 연동 계정'** + String get settingsSns; + + /// No description provided for @settingsPopHistory. + /// + /// In ko, this message translates to: + /// **'POP 내역'** + String get settingsPopHistory; + + /// No description provided for @settingsPayments. + /// + /// In ko, this message translates to: + /// **'결제 내역'** + String get settingsPayments; + + /// No description provided for @settingsServicePayments. + /// + /// In ko, this message translates to: + /// **'서비스 결제 내역'** + String get settingsServicePayments; + + /// No description provided for @settingsService. + /// + /// In ko, this message translates to: + /// **'서비스 설정'** + String get settingsService; + + /// No description provided for @settingsPush. + /// + /// In ko, this message translates to: + /// **'푸시 알림 설정'** + String get settingsPush; + + /// No description provided for @settingsBlocked. + /// + /// In ko, this message translates to: + /// **'차단 설정'** + String get settingsBlocked; + + /// No description provided for @settingsInfo. + /// + /// In ko, this message translates to: + /// **'서비스 정보'** + String get settingsInfo; + + /// No description provided for @settingsNotice. + /// + /// In ko, this message translates to: + /// **'공지 사항'** + String get settingsNotice; + + /// No description provided for @settingsInquiry. + /// + /// In ko, this message translates to: + /// **'1:1 문의접수'** + String get settingsInquiry; + + /// No description provided for @settingsAppVersion. + /// + /// In ko, this message translates to: + /// **'앱 버전'** + String get settingsAppVersion; + + /// No description provided for @settingsLatest. + /// + /// In ko, this message translates to: + /// **'최신 버전'** + String get settingsLatest; + + /// No description provided for @settingsLogout. + /// + /// In ko, this message translates to: + /// **'로그아웃'** + String get settingsLogout; + + /// No description provided for @settingsLogoutConfirm. + /// + /// In ko, this message translates to: + /// **'로그아웃 하시겠습니까?'** + String get settingsLogoutConfirm; + + /// No description provided for @settingsWithdraw. + /// + /// In ko, this message translates to: + /// **'서비스 탈퇴'** + String get settingsWithdraw; + + /// No description provided for @settingsWithdrawConfirm. + /// + /// In ko, this message translates to: + /// **'서비스 탈퇴를 하시겠습니까?'** + String get settingsWithdrawConfirm; + + /// No description provided for @settingsWithdrawBody. + /// + /// In ko, this message translates to: + /// **'보유하신 POP 이 사라지고 되돌릴 수 없습니다.'** + String get settingsWithdrawBody; + + /// No description provided for @settingsWithdrawLater. + /// + /// In ko, this message translates to: + /// **'서비스 탈퇴 처리는 09-10 회차에 연결합니다.'** + String get settingsWithdrawLater; + + /// No description provided for @settingsUnlinkConfirm. + /// + /// In ko, this message translates to: + /// **'연결된 계정을 끊으시겠습니까?'** + String get settingsUnlinkConfirm; + + /// No description provided for @settingsUnlinkBody. + /// + /// In ko, this message translates to: + /// **'연결한 SNS 계정 로그인 수단만 제거되며, 계정 정보는 삭제되지 않습니다.'** + String get settingsUnlinkBody; + + /// No description provided for @settingsUnlink. + /// + /// In ko, this message translates to: + /// **'연결 해제'** + String get settingsUnlink; + + /// No description provided for @settingsSameEmailOnly. + /// + /// In ko, this message translates to: + /// **'계정 이메일과 동일한 소셜 계정만\n연결 할 수 있습니다.'** + String get settingsSameEmailOnly; + + /// No description provided for @settingsLinkNotReady. + /// + /// In ko, this message translates to: + /// **'{name} 연동은 아직 준비 중입니다. 필요한 항목: {items}'** + String settingsLinkNotReady(String name, String items); + + /// No description provided for @settingsConnect. + /// + /// In ko, this message translates to: + /// **'연결'** + String get settingsConnect; + + /// No description provided for @settingsDisconnect. + /// + /// In ko, this message translates to: + /// **'연결해제'** + String get settingsDisconnect; + + /// No description provided for @settingsEmailProvider. + /// + /// In ko, this message translates to: + /// **'이메일'** + String get settingsEmailProvider; + + /// No description provided for @pushDeviceOff. + /// + /// In ko, this message translates to: + /// **'기기의 푸시 알림 설정이 꺼진 상태입니다.'** + String get pushDeviceOff; + + /// No description provided for @pushDeviceOffHint. + /// + /// In ko, this message translates to: + /// **'알림 수신을 위해 시스템 설정에서 알림을 허용해주세요.'** + String get pushDeviceOffHint; + + /// No description provided for @pushRequesting. + /// + /// In ko, this message translates to: + /// **'요청 중…'** + String get pushRequesting; + + /// No description provided for @pushGoToSettings. + /// + /// In ko, this message translates to: + /// **'설정하러 가기'** + String get pushGoToSettings; + + /// No description provided for @pushOpeningDeviceSettings. + /// + /// In ko, this message translates to: + /// **'기기 설정으로 이동합니다.'** + String get pushOpeningDeviceSettings; + + /// No description provided for @pushEnableInDevice. + /// + /// In ko, this message translates to: + /// **'기기 설정에서 FANUZU 알림을 켜 주셔야 받으실 수 있습니다.'** + String get pushEnableInDevice; + + /// No description provided for @pushDeviceSection. + /// + /// In ko, this message translates to: + /// **'기기 알림 설정'** + String get pushDeviceSection; + + /// No description provided for @pushSoundVibration. + /// + /// In ko, this message translates to: + /// **'소리, 진동 설정'** + String get pushSoundVibration; + + /// No description provided for @pushServiceSection. + /// + /// In ko, this message translates to: + /// **'서비스 알림 설정'** + String get pushServiceSection; + + /// No description provided for @pushTopicEssential. + /// + /// In ko, this message translates to: + /// **'서비스 필수'** + String get pushTopicEssential; + + /// No description provided for @pushTopicFandomEvent. + /// + /// In ko, this message translates to: + /// **'팬덤 이벤트'** + String get pushTopicFandomEvent; + + /// No description provided for @pushTopicBriefing. + /// + /// In ko, this message translates to: + /// **'AI Briefing'** + String get pushTopicBriefing; + + /// No description provided for @pushTopicCommunity. + /// + /// In ko, this message translates to: + /// **'커뮤니티'** + String get pushTopicCommunity; + + /// No description provided for @pushTopicMarketing. + /// + /// In ko, this message translates to: + /// **'마케팅·홍보'** + String get pushTopicMarketing; + + /// No description provided for @linkTitle. + /// + /// In ko, this message translates to: + /// **'{name} 연동'** + String linkTitle(String name); + + /// No description provided for @linkNotLinked. + /// + /// In ko, this message translates to: + /// **'연동되지 않음'** + String get linkNotLinked; + + /// No description provided for @linkLinked. + /// + /// In ko, this message translates to: + /// **'연동됨'** + String get linkLinked; + + /// No description provided for @linkSameEmailOnly. + /// + /// In ko, this message translates to: + /// **'계정 이메일과 동일한 소셜 계정만 연결 할 수 있습니다.'** + String get linkSameEmailOnly; + + /// No description provided for @linkUnlink. + /// + /// In ko, this message translates to: + /// **'연동 해제'** + String get linkUnlink; + + /// No description provided for @linkLink. + /// + /// In ko, this message translates to: + /// **'연동하기'** + String get linkLink; + + /// No description provided for @noticeBadge. + /// + /// In ko, this message translates to: + /// **'공지'** + String get noticeBadge; + + /// No description provided for @noticeTitle. + /// + /// In ko, this message translates to: + /// **'공지사항'** + String get noticeTitle; + + /// No description provided for @noticeEmpty. + /// + /// In ko, this message translates to: + /// **'공지사항이 없습니다.'** + String get noticeEmpty; + + /// No description provided for @noticeDetailTitle. + /// + /// In ko, this message translates to: + /// **'공지사항 상세'** + String get noticeDetailTitle; + + /// No description provided for @translationMark. + /// + /// In ko, this message translates to: + /// **'번역함'** + String get translationMark; + + /// No description provided for @translationReplyTo. + /// + /// In ko, this message translates to: + /// **'{name} 님 댓글 번역'** + String translationReplyTo(String name); + + /// No description provided for @translationWrite. + /// + /// In ko, this message translates to: + /// **'번역 댓글 작성'** + String get translationWrite; + + /// No description provided for @translationHint. + /// + /// In ko, this message translates to: + /// **'번역을 입력하세요'** + String get translationHint; + + /// No description provided for @translationValid. + /// + /// In ko, this message translates to: + /// **'유효'** + String get translationValid; + + /// No description provided for @translationInvalid. + /// + /// In ko, this message translates to: + /// **'무효'** + String get translationInvalid; + + /// No description provided for @translationSelfMarkBlocked. + /// + /// In ko, this message translates to: + /// **'본인 번역은 마킹할 수 없습니다.'** + String get translationSelfMarkBlocked; + + /// No description provided for @translationSubmitted. + /// + /// In ko, this message translates to: + /// **'번역을 등록했습니다. 검증을 통과하면 24시간 뒤 POP 이 지급됩니다.'** + String get translationSubmitted; + + /// No description provided for @translationMarked. + /// + /// In ko, this message translates to: + /// **'마킹했습니다.'** + String get translationMarked; + + /// No description provided for @translationAlreadyMarked. + /// + /// In ko, this message translates to: + /// **'이미 마킹한 번역입니다.'** + String get translationAlreadyMarked; + + /// No description provided for @translationDuplicate. + /// + /// In ko, this message translates to: + /// **'이미 이 글에 같은 언어로 번역을 등록했습니다.'** + String get translationDuplicate; + + /// No description provided for @translationFailed. + /// + /// In ko, this message translates to: + /// **'처리하지 못했습니다. 잠시 뒤 다시 시도해 주세요.'** + String get translationFailed; + + /// No description provided for @translationNotEligible. + /// + /// In ko, this message translates to: + /// **'가입 후 24시간이 지난 이 팬덤의 멤버만 마킹할 수 있습니다.'** + String get translationNotEligible; + + /// No description provided for @translationDailyCap. + /// + /// In ko, this message translates to: + /// **'오늘은 번역을 더 등록할 수 없습니다.'** + String get translationDailyCap; + + /// No description provided for @translationInvalidTarget. + /// + /// In ko, this message translates to: + /// **'번역할 수 없는 콘텐츠입니다.'** + String get translationInvalidTarget; + + /// No description provided for @commonRequestFailed. + /// + /// In ko, this message translates to: + /// **'요청을 처리하지 못했습니다.'** + String get commonRequestFailed; + + /// No description provided for @commonFan. + /// + /// In ko, this message translates to: + /// **'팬'** + String get commonFan; + + /// No description provided for @adLoadFailed. + /// + /// In ko, this message translates to: + /// **'광고를 불러오지 못했어요. 기본 보상만 받습니다.'** + String get adLoadFailed; + + /// No description provided for @storeNotReady. + /// + /// In ko, this message translates to: + /// **'스토어 상품 준비 중'** + String get storeNotReady; + + /// No description provided for @storePending. + /// + /// In ko, this message translates to: + /// **'결제 승인을 기다리고 있어요. 승인되면 자동으로 반영됩니다.'** + String get storePending; + + /// No description provided for @storeFailed. + /// + /// In ko, this message translates to: + /// **'결제를 완료하지 못했어요.'** + String get storeFailed; + + /// No description provided for @storePurchased. + /// + /// In ko, this message translates to: + /// **'구매가 반영되었습니다.'** + String get storePurchased; + + /// No description provided for @heroParticipate. + /// + /// In ko, this message translates to: + /// **'ZAP+ · 선물 참여'** + String get heroParticipate; + + /// No description provided for @heroRemainingSemantics. + /// + /// In ko, this message translates to: + /// **'HERO TIME 남은 시간 {minutes}분 {seconds}초'** + String heroRemainingSemantics(String minutes, String seconds); + + /// No description provided for @heroRemaining. + /// + /// In ko, this message translates to: + /// **'HERO TIME · {time} 남음'** + String heroRemaining(String time); + + /// No description provided for @heroClaims. + /// + /// In ko, this message translates to: + /// **'팬들의 선물 수령 {count}건'** + String heroClaims(String count); + + /// No description provided for @heroInProgress. + /// + /// In ko, this message translates to: + /// **'{name} · HERO TIME 진행 중 →'** + String heroInProgress(String name); + + /// No description provided for @stageNames. + /// + /// In ko, this message translates to: + /// **'성운,시작,성장,함께,동행,빛,영원'** + String get stageNames; + + /// No description provided for @planetStageUnknown. + /// + /// In ko, this message translates to: + /// **'행성 · 단계 정보 없음'** + String get planetStageUnknown; + + /// No description provided for @planetHibernatingSuffix. + /// + /// In ko, this message translates to: + /// **' · 휴면 중'** + String get planetHibernatingSuffix; + + /// No description provided for @planetEclipseSuffix. + /// + /// In ko, this message translates to: + /// **' · Eclipse 진행 중'** + String get planetEclipseSuffix; + + /// No description provided for @planetHeroSuffix. + /// + /// In ko, this message translates to: + /// **' · HERO TIME 진행 중'** + String get planetHeroSuffix; + + /// No description provided for @evoPlanetGrowth. + /// + /// In ko, this message translates to: + /// **'행성 성장'** + String get evoPlanetGrowth; + + /// No description provided for @evoHeroEnd. + /// + /// In ko, this message translates to: + /// **'HERO TIME 종료'** + String get evoHeroEnd; + + /// No description provided for @evoAwakening. + /// + /// In ko, this message translates to: + /// **'행성이 깨어납니다'** + String get evoAwakening; + + /// No description provided for @evoAnomaly. + /// + /// In ko, this message translates to: + /// **'이상 반응'** + String get evoAnomaly; + + /// No description provided for @evoEnergy. + /// + /// In ko, this message translates to: + /// **'에너지 집중'** + String get evoEnergy; + + /// No description provided for @evoTransition. + /// + /// In ko, this message translates to: + /// **'전환'** + String get evoTransition; + + /// No description provided for @evoComplete. + /// + /// In ko, this message translates to: + /// **'행성 진화 완료'** + String get evoComplete; + + /// No description provided for @evoOpenHistory. + /// + /// In ko, this message translates to: + /// **'PASSPORT 기록 보기'** + String get evoOpenHistory; + + /// No description provided for @playNow. + /// + /// In ko, this message translates to: + /// **'지금 바로 플레이'** + String get playNow; + + /// No description provided for @playHeadline. + /// + /// In ko, this message translates to: + /// **'자, 팬덤을 만나러 가요!'** + String get playHeadline; + + /// No description provided for @playBody. + /// + /// In ko, this message translates to: + /// **'행성을 만들고 팬덤들과 소통하면서 즐거운 팬덤 활...'** + String get playBody; + + /// No description provided for @timeJustNow. + /// + /// In ko, this message translates to: + /// **'방금 전'** + String get timeJustNow; + + /// No description provided for @timeMinutesAgo. + /// + /// In ko, this message translates to: + /// **'{n}분 전'** + String timeMinutesAgo(String n); + + /// No description provided for @timeHoursAgo. + /// + /// In ko, this message translates to: + /// **'{n}시간 전'** + String timeHoursAgo(String n); + + /// No description provided for @timeDaysAgo. + /// + /// In ko, this message translates to: + /// **'{n}일 전'** + String timeDaysAgo(String n); + + /// No description provided for @commonMore. + /// + /// In ko, this message translates to: + /// **'더 보기'** + String get commonMore; + + /// No description provided for @authProviderStart. + /// + /// In ko, this message translates to: + /// **'{provider, select, email{이메일로 시작하기} kakao{카카오로 시작하기} line{LINE으로 시작하기} apple{Apple로 시작하기} other{Google로 시작하기}}'** + String authProviderStart(String provider); + + /// No description provided for @authProviderName. + /// + /// In ko, this message translates to: + /// **'{provider, select, email{이메일} kakao{카카오} line{LINE} apple{Apple} other{Google}}'** + String authProviderName(String provider); + + /// No description provided for @authLastProvider. + /// + /// In ko, this message translates to: + /// **'마지막 로그인 수단은 연결을 해제할 수 없습니다.'** + String get authLastProvider; + + /// No description provided for @authLoginRequired. + /// + /// In ko, this message translates to: + /// **'로그인이 필요합니다.'** + String get authLoginRequired; + + /// No description provided for @authDifferentEmail. + /// + /// In ko, this message translates to: + /// **'가입하신 이메일과 다른 계정입니다.'** + String get authDifferentEmail; + + /// No description provided for @authLinkFailed. + /// + /// In ko, this message translates to: + /// **'계정 연결에 실패했습니다.'** + String get authLinkFailed; + + /// No description provided for @authNotLinked. + /// + /// In ko, this message translates to: + /// **'연결되어 있지 않은 계정입니다.'** + String get authNotLinked; + + /// No description provided for @authEmailUnavailable. + /// + /// In ko, this message translates to: + /// **'이메일 로그인을 쓸 수 없습니다.'** + String get authEmailUnavailable; + + /// No description provided for @authCheckCredentials. + /// + /// In ko, this message translates to: + /// **'이메일 또는 비밀번호를 확인해 주세요.'** + String get authCheckCredentials; + + /// No description provided for @authSocialFailed. + /// + /// In ko, this message translates to: + /// **'소셜 로그인에 실패했습니다.'** + String get authSocialFailed; + + /// No description provided for @authAppleUnavailable. + /// + /// In ko, this message translates to: + /// **'이 기기에서는 Apple 로그인을 쓸 수 없습니다.'** + String get authAppleUnavailable; + + /// No description provided for @authEmailInUse. + /// + /// In ko, this message translates to: + /// **'이미 가입된 이메일입니다.'** + String get authEmailInUse; + + /// No description provided for @authWeakPassword. + /// + /// In ko, this message translates to: + /// **'비밀번호가 너무 짧습니다. 8자 이상으로 정해 주세요.'** + String get authWeakPassword; + + /// No description provided for @authInvalidEmail. + /// + /// In ko, this message translates to: + /// **'이메일 형식이 올바르지 않습니다.'** + String get authInvalidEmail; + + /// No description provided for @commonTryLater. + /// + /// In ko, this message translates to: + /// **'잠시 후 다시 시도해 주세요.'** + String get commonTryLater; + + /// No description provided for @authCredentialInUse. + /// + /// In ko, this message translates to: + /// **'다른 FANUZU 계정에 이미 연결된 소셜 계정입니다.'** + String get authCredentialInUse; + + /// No description provided for @authAlreadyLinked. + /// + /// In ko, this message translates to: + /// **'이미 연결된 계정입니다.'** + String get authAlreadyLinked; + + /// No description provided for @authMethodUnavailable. + /// + /// In ko, this message translates to: + /// **'이 로그인 수단은 아직 사용할 수 없습니다.'** + String get authMethodUnavailable; + + /// No description provided for @authRecentLogin. + /// + /// In ko, this message translates to: + /// **'보안을 위해 다시 로그인한 뒤 시도해 주세요.'** + String get authRecentLogin; + + /// No description provided for @authOtherProviderGuide. + /// + /// In ko, this message translates to: + /// **'{email} 계정은 다른 로그인 수단으로 가입돼 있습니다. 처음 가입에 쓰신 수단으로 로그인한 뒤 MY > 설정 > SNS 연동에서 이 수단을 연결해 주세요.'** + String authOtherProviderGuide(String email); + + /// No description provided for @authThisAccount. + /// + /// In ko, this message translates to: + /// **'이'** + String get authThisAccount; + + /// No description provided for @eclipseCapped. + /// + /// In ko, this message translates to: + /// **'참여 상한에 닿아 일부는 집계되지 않았습니다.'** + String get eclipseCapped; + + /// No description provided for @eclipseResultUnknown. + /// + /// In ko, this message translates to: + /// **'참여 결과를 확인하지 못했습니다. 같은 요청으로 다시 확인해 주세요.'** + String get eclipseResultUnknown; + + /// No description provided for @eclipseChecking. + /// + /// In ko, this message translates to: + /// **'참여 확인 중'** + String get eclipseChecking; + + /// No description provided for @eclipseRetrySame. + /// + /// In ko, this message translates to: + /// **'같은 참여 다시 확인'** + String get eclipseRetrySame; + + /// No description provided for @eclipseEnded. + /// + /// In ko, this message translates to: + /// **'이클립스가 이미 끝났거나 방어에 성공했습니다.'** + String get eclipseEnded; + + /// No description provided for @eclipseTooFast. + /// + /// In ko, this message translates to: + /// **'너무 빠르게 참여했습니다. 잠시 뒤 다시 시도해 주십시오.'** + String get eclipseTooFast; + + /// No description provided for @eclipseNone. + /// + /// In ko, this message translates to: + /// **'진행 중인 이클립스가 없습니다'** + String get eclipseNone; + + /// No description provided for @eclipseSuccessTitle. + /// + /// In ko, this message translates to: + /// **'운석 충돌을\n성공적으로 막았어요!'** + String get eclipseSuccessTitle; + + /// No description provided for @eclipseQualified. + /// + /// In ko, this message translates to: + /// **'참여 인정 완료'** + String get eclipseQualified; + + /// No description provided for @eclipseFailTitle. + /// + /// In ko, this message translates to: + /// **'아쉽게 행성 방어를\n실패했어요'** + String get eclipseFailTitle; + + /// No description provided for @eclipseFailBody. + /// + /// In ko, this message translates to: + /// **'이번 회차의 방어 목표를 달성하지 못했습니다.'** + String get eclipseFailBody; + + /// No description provided for @eclipseCancelTitle. + /// + /// In ko, this message translates to: + /// **'이클립스 취소'** + String get eclipseCancelTitle; + + /// No description provided for @eclipseCancelBody. + /// + /// In ko, this message translates to: + /// **'집계 오류로 취소되었습니다. FPI는 차감되지 않았습니다.'** + String get eclipseCancelBody; + + /// No description provided for @eclipsePendingTitle. + /// + /// In ko, this message translates to: + /// **'방어 결과 확인 중'** + String get eclipsePendingTitle; + + /// No description provided for @eclipsePendingBody. + /// + /// In ko, this message translates to: + /// **'참여 시간이 끝났습니다. 서버 판정을 기다리고 있습니다.'** + String get eclipsePendingBody; + + /// No description provided for @eclipseShakeTimed. + /// + /// In ko, this message translates to: + /// **'초간 휴대폰을 흔들어\n행성 운석 충돌을 막으세요!'** + String get eclipseShakeTimed; + + /// No description provided for @eclipseShake. + /// + /// In ko, this message translates to: + /// **'휴대폰을 흔들어\n행성 운석 충돌을 막으세요!'** + String get eclipseShake; + + /// No description provided for @eclipseMyCount. + /// + /// In ko, this message translates to: + /// **'내 참여 {count}회'** + String eclipseMyCount(String count); + + /// No description provided for @eclipsePendingSuffix. + /// + /// In ko, this message translates to: + /// **' · +{count} 전송 대기'** + String eclipsePendingSuffix(String count); + + /// No description provided for @eclipseSendingSuffix. + /// + /// In ko, this message translates to: + /// **' · 전송 중'** + String get eclipseSendingSuffix; + + /// No description provided for @eclipseNoPopNote. + /// + /// In ko, this message translates to: + /// **'POP은 소모되지 않습니다. 유효 Shake {count}회 이상 흔들어 방어에 참여하면 참여자로 인정됩니다.'** + String eclipseNoPopNote(String count); + + /// No description provided for @eclipseTapToJoin. + /// + /// In ko, this message translates to: + /// **'흔들기 어려우면 눌러서 참여'** + String get eclipseTapToJoin; + + /// No description provided for @eclipseTimeLeft. + /// + /// In ko, this message translates to: + /// **'이클립스 발생 남은 시간 :'** + String get eclipseTimeLeft; + + /// No description provided for @eclipseSeconds. + /// + /// In ko, this message translates to: + /// **' {seconds}초'** + String eclipseSeconds(String seconds); + + /// No description provided for @eclipseBannerDefended. + /// + /// In ko, this message translates to: + /// **'방어 성공 — {name} FPI 감소 없음'** + String eclipseBannerDefended(String name); + + /// No description provided for @eclipseBannerActive. + /// + /// In ko, this message translates to: + /// **'이클립스 발동 — {name} 침식 {rate}%'** + String eclipseBannerActive(String name, String rate); + + /// No description provided for @eclipseBannerDefense. + /// + /// In ko, this message translates to: + /// **'유효 참여 {count} / {target}명'** + String eclipseBannerDefense(String count, String target); + + /// No description provided for @timeHoursMinutesLeft. + /// + /// In ko, this message translates to: + /// **'{hours}시간 {minutes}분 남음'** + String timeHoursMinutesLeft(String hours, String minutes); + + /// No description provided for @timeMinutesLeft. + /// + /// In ko, this message translates to: + /// **'{minutes}분 남음'** + String timeMinutesLeft(String minutes); + + /// No description provided for @liveEclipseDefense. + /// + /// In ko, this message translates to: + /// **'[LIVE] {name}님이 이클립스 방어에 참여했습니다! (+{count})'** + String liveEclipseDefense(String name, String count); + + /// No description provided for @liveHotTime. + /// + /// In ko, this message translates to: + /// **'[LIVE] {name}님의 아티스트 핫타임이 시작되었습니다!'** + String liveHotTime(String name); + + /// No description provided for @liveHeroGift. + /// + /// In ko, this message translates to: + /// **'[LIVE] {name}님의 영웅의 선물이 도착했습니다!'** + String liveHeroGift(String name); + + /// No description provided for @liveCampaign. + /// + /// In ko, this message translates to: + /// **'[LIVE] {name}님이 캠페인에 후원했습니다!'** + String liveCampaign(String name); + + /// No description provided for @galaxySetPrimaryConfirm. + /// + /// In ko, this message translates to: + /// **'선택한 행성을 기본 행성으로 변경할까요?'** + String get galaxySetPrimaryConfirm; + + /// No description provided for @galaxyLoadFailed. + /// + /// In ko, this message translates to: + /// **'갤럭시를 불러오지 못했습니다'** + String get galaxyLoadFailed; + + /// No description provided for @commonSearch. + /// + /// In ko, this message translates to: + /// **'검색'** + String get commonSearch; + + /// No description provided for @galaxyMyPlanets. + /// + /// In ko, this message translates to: + /// **'내 행성'** + String get galaxyMyPlanets; + + /// No description provided for @galaxyMap. + /// + /// In ko, this message translates to: + /// **'갤럭시 맵'** + String get galaxyMap; + + /// No description provided for @nebulaCreate. + /// + /// In ko, this message translates to: + /// **'성운 생성'** + String get nebulaCreate; + + /// No description provided for @commonRecheck. + /// + /// In ko, this message translates to: + /// **'다시 확인'** + String get commonRecheck; + + /// No description provided for @zapApplying. + /// + /// In ko, this message translates to: + /// **'기여 반영 중'** + String get zapApplying; + + /// No description provided for @zapNeedsCheck. + /// + /// In ko, this message translates to: + /// **'기여 결과 확인 필요'** + String get zapNeedsCheck; + + /// No description provided for @zapNoPlanet. + /// + /// In ko, this message translates to: + /// **'소속 행성이 없습니다'** + String get zapNoPlanet; + + /// No description provided for @commonContribute. + /// + /// In ko, this message translates to: + /// **'기여'** + String get commonContribute; + + /// No description provided for @commonItem. + /// + /// In ko, this message translates to: + /// **'아이템'** + String get commonItem; + + /// No description provided for @galaxyDefended. + /// + /// In ko, this message translates to: + /// **'방어 성공 · FPI 감소 없음'** + String get galaxyDefended; + + /// No description provided for @galaxyErosion. + /// + /// In ko, this message translates to: + /// **'침식 {rate}% · 방어 {progress}%'** + String galaxyErosion(String rate, String progress); + + /// No description provided for @galaxyOnlineMembers. + /// + /// In ko, this message translates to: + /// **'접속 {online}명 · 멤버 {members}명'** + String galaxyOnlineMembers(String online, String members); + + /// No description provided for @galaxySearchHint. + /// + /// In ko, this message translates to: + /// **'팬덤 이름으로 검색'** + String get galaxySearchHint; + + /// No description provided for @commonRecentSearches. + /// + /// In ko, this message translates to: + /// **'최근 검색어'** + String get commonRecentSearches; + + /// No description provided for @commonClearAll. + /// + /// In ko, this message translates to: + /// **'전체 삭제'** + String get commonClearAll; + + /// No description provided for @commonNoRecentSearches. + /// + /// In ko, this message translates to: + /// **'최근 검색어가 없습니다.'** + String get commonNoRecentSearches; + + /// No description provided for @galaxyNoHit. + /// + /// In ko, this message translates to: + /// **'찾는 팬덤이 없습니다.\n이름을 다시 확인해 주세요.'** + String get galaxyNoHit; + + /// No description provided for @galaxyMembers. + /// + /// In ko, this message translates to: + /// **'멤버 {count}명'** + String galaxyMembers(String count); + + /// No description provided for @galaxyPlanetRank. + /// + /// In ko, this message translates to: + /// **'{name} 행성 {rank}위'** + String galaxyPlanetRank(String name, String rank); + + /// No description provided for @galaxyMapOpen. + /// + /// In ko, this message translates to: + /// **'갤럭시 맵 열기'** + String get galaxyMapOpen; + + /// No description provided for @zapNotEnoughPop. + /// + /// In ko, this message translates to: + /// **'POP이 모자랍니다'** + String get zapNotEnoughPop; + + /// No description provided for @zapFailNoPop. + /// + /// In ko, this message translates to: + /// **'POP 이 모자라 기여하지 못했습니다.'** + String get zapFailNoPop; + + /// No description provided for @zapFailLedger. + /// + /// In ko, this message translates to: + /// **'잔액과 획득 내역이 맞지 않아 기여를 멈췄습니다.'** + String get zapFailLedger; + + /// No description provided for @zapFailRetry. + /// + /// In ko, this message translates to: + /// **'연결을 확인하고 같은 기여를 다시 확인해 주세요.'** + String get zapFailRetry; + + /// No description provided for @zapDone. + /// + /// In ko, this message translates to: + /// **'기여 완료'** + String get zapDone; + + /// No description provided for @zapDonePop. + /// + /// In ko, this message translates to: + /// **'{pop} POP 기여 완료'** + String zapDonePop(String pop); + + /// No description provided for @growthFounders. + /// + /// In ko, this message translates to: + /// **'창립 멤버'** + String get growthFounders; + + /// No description provided for @growthNebulaGoalPassed. + /// + /// In ko, this message translates to: + /// **'목표 기간이 지났어요 · 조건을 채우면 행성으로 탄생해요'** + String get growthNebulaGoalPassed; + + /// No description provided for @growthFinalStage. + /// + /// In ko, this message translates to: + /// **'최종 Stage'** + String get growthFinalStage; + + /// No description provided for @growthPromotionPending. + /// + /// In ko, this message translates to: + /// **'승급 확정 대기'** + String get growthPromotionPending; + + /// No description provided for @growthCheckConditions. + /// + /// In ko, this message translates to: + /// **'성장 조건 확인'** + String get growthCheckConditions; + + /// No description provided for @growthConditionChecking. + /// + /// In ko, this message translates to: + /// **'{name} 조건 확인 중'** + String growthConditionChecking(String name); + + /// No description provided for @growthConditionMet. + /// + /// In ko, this message translates to: + /// **'{name} 조건 충족'** + String growthConditionMet(String name); + + /// No description provided for @growthConditionUnmet. + /// + /// In ko, this message translates to: + /// **'{name} 조건 미충족'** + String growthConditionUnmet(String name); + + /// No description provided for @nebulaHeadline. + /// + /// In ko, this message translates to: + /// **'우리 팬덤 행성 이름을 만들어요.'** + String get nebulaHeadline; + + /// No description provided for @nebulaBody. + /// + /// In ko, this message translates to: + /// **'창립맴버가 목표 POP을 {days}일내 달성하면 \n정식 행성으로 탄생합니다!\n진화에 참여한 맴버들에게는 기념 배지가 지급됩니다.'** + String nebulaBody(String days); + + /// No description provided for @nebulaNameTaken. + /// + /// In ko, this message translates to: + /// **'이미 사용중인 이름입니다.'** + String get nebulaNameTaken; + + /// No description provided for @nebulaNameHint. + /// + /// In ko, this message translates to: + /// **'팬덤 이름'** + String get nebulaNameHint; + + /// No description provided for @nebulaGoal. + /// + /// In ko, this message translates to: + /// **'목표 '** + String get nebulaGoal; + + /// No description provided for @nebulaFounders. + /// + /// In ko, this message translates to: + /// **' / 창립맴버'** + String get nebulaFounders; + + /// No description provided for @nebulaGoalSemantics. + /// + /// In ko, this message translates to: + /// **'목표 {pop} POP · 창립 멤버 {members}명 · {days}일 고정'** + String nebulaGoalSemantics(String pop, String members, String days); + + /// No description provided for @commonMyPop. + /// + /// In ko, this message translates to: + /// **'나의 보유 POP : '** + String get commonMyPop; + + /// No description provided for @commonChange. + /// + /// In ko, this message translates to: + /// **'변경'** + String get commonChange; + + /// No description provided for @planetShape. + /// + /// In ko, this message translates to: + /// **'형태 {n}'** + String planetShape(String n); + + /// No description provided for @planetBgColor. + /// + /// In ko, this message translates to: + /// **'배경색 선택'** + String get planetBgColor; + + /// No description provided for @planetColorSelect. + /// + /// In ko, this message translates to: + /// **'행성 색 선택'** + String get planetColorSelect; + + /// No description provided for @planetTypeSelect. + /// + /// In ko, this message translates to: + /// **'행성 타입 선택'** + String get planetTypeSelect; + + /// No description provided for @commonNext. + /// + /// In ko, this message translates to: + /// **'다음'** + String get commonNext; + + /// No description provided for @planetCreateFailed. + /// + /// In ko, this message translates to: + /// **'행성을 만들지 못했습니다. 잠시 후 다시 시도해 주세요.'** + String get planetCreateFailed; + + /// No description provided for @planetCreateConfirm. + /// + /// In ko, this message translates to: + /// **'선택한 옵션으로\n행성을 생성할까요?'** + String get planetCreateConfirm; + + /// No description provided for @commonCreate. + /// + /// In ko, this message translates to: + /// **'생성'** + String get commonCreate; + + /// No description provided for @briefingOpenFailed. + /// + /// In ko, this message translates to: + /// **'원문을 열 수 없습니다.'** + String get briefingOpenFailed; + + /// No description provided for @campaignAchieved. + /// + /// In ko, this message translates to: + /// **'{percent}% 달성'** + String campaignAchieved(String percent); + + /// No description provided for @campaignView. + /// + /// In ko, this message translates to: + /// **'캠페인 보기'** + String get campaignView; + + /// No description provided for @growthFinalReached. + /// + /// In ko, this message translates to: + /// **'최종 Stage에 도달했어요'** + String get growthFinalReached; + + /// No description provided for @zapRetrySame. + /// + /// In ko, this message translates to: + /// **'같은 기여 다시 확인'** + String get zapRetrySame; + + /// No description provided for @heroOwnerSuffix. + /// + /// In ko, this message translates to: + /// **'님의'** + String get heroOwnerSuffix; + + /// No description provided for @heroStartPrompt. + /// + /// In ko, this message translates to: + /// **'행성을 생성 또는 가입하고\n팬우주를 시작해 보세요!'** + String get heroStartPrompt; + + /// No description provided for @heroNebulaPrompt. + /// + /// In ko, this message translates to: + /// **'활동을 통해 얻은 POP을 통해\n성운에 기여해 보세요!'** + String get heroNebulaPrompt; + + /// No description provided for @heroRecentContribution. + /// + /// In ko, this message translates to: + /// **'{pop} POP 기여로\n꾸준하게 성장하고 있어요.'** + String heroRecentContribution(String pop); + + /// No description provided for @heroGrowingTogether. + /// + /// In ko, this message translates to: + /// **'함께 키워가는 팬덤 행성'** + String get heroGrowingTogether; + + /// No description provided for @heroPopProgress. + /// + /// In ko, this message translates to: + /// **'POP 진행률'** + String get heroPopProgress; + + /// No description provided for @heroNebula. + /// + /// In ko, this message translates to: + /// **'성운'** + String get heroNebula; + + /// No description provided for @heroNebulaTap. + /// + /// In ko, this message translates to: + /// **'성운을 클릭해서\n행성으로 탄생 시켜보세요!'** + String get heroNebulaTap; + + /// No description provided for @heroCreateNebula. + /// + /// In ko, this message translates to: + /// **'행성 생성 (성운)'** + String get heroCreateNebula; + + /// No description provided for @heroJoinPlanet. + /// + /// In ko, this message translates to: + /// **'행성 가입'** + String get heroJoinPlanet; + + /// No description provided for @heroCreateOrJoin. + /// + /// In ko, this message translates to: + /// **'행성 생성 또는 가입'** + String get heroCreateOrJoin; + + /// No description provided for @insideAnswers. + /// + /// In ko, this message translates to: + /// **'답변 {count}'** + String insideAnswers(String count); + + /// No description provided for @itemJoinFirst. + /// + /// In ko, this message translates to: + /// **'행성에 가입한 뒤 영웅의 선물을 보낼 수 있어요.'** + String get itemJoinFirst; + + /// No description provided for @itemBuyNotReady. + /// + /// In ko, this message translates to: + /// **'아이템 구입은 준비 중입니다.'** + String get itemBuyNotReady; + + /// No description provided for @itemUseNotReady. + /// + /// In ko, this message translates to: + /// **'아이템 사용은 준비 중입니다.'** + String get itemUseNotReady; + + /// No description provided for @payOpenFailed. + /// + /// In ko, this message translates to: + /// **'결제 창을 열지 못했어요.'** + String get payOpenFailed; + + /// No description provided for @itemBuy. + /// + /// In ko, this message translates to: + /// **'아이템 구입'** + String get itemBuy; + + /// No description provided for @itemSelect. + /// + /// In ko, this message translates to: + /// **'아이템 선택'** + String get itemSelect; + + /// No description provided for @commonComingSoon. + /// + /// In ko, this message translates to: + /// **'준비 중입니다.'** + String get commonComingSoon; + + /// No description provided for @itemSendPop. + /// + /// In ko, this message translates to: + /// **'{pop} POP 전송'** + String itemSendPop(String pop); + + /// No description provided for @itemBuyerPop. + /// + /// In ko, this message translates to: + /// **'구매자 +{pop} POP'** + String itemBuyerPop(String pop); + + /// No description provided for @itemOwned. + /// + /// In ko, this message translates to: + /// **'보유 : {count}'** + String itemOwned(String count); + + /// No description provided for @itemOwnedCount. + /// + /// In ko, this message translates to: + /// **'보유 : {count}개'** + String itemOwnedCount(String count); + + /// No description provided for @growthOwnerOf. + /// + /// In ko, this message translates to: + /// **'님의 '** + String get growthOwnerOf; + + /// No description provided for @growthSteadySuffix. + /// + /// In ko, this message translates to: + /// **' 기여로 꾸준하게 성장하고 있어요.'** + String get growthSteadySuffix; + + /// No description provided for @questAttended. + /// + /// In ko, this message translates to: + /// **'출석 완료'** + String get questAttended; + + /// No description provided for @questInProgressSpaced. + /// + /// In ko, this message translates to: + /// **'진행 중'** + String get questInProgressSpaced; + + /// No description provided for @questRewardPaid. + /// + /// In ko, this message translates to: + /// **'보상 지급 완료'** + String get questRewardPaid; + + /// No description provided for @questClaim. + /// + /// In ko, this message translates to: + /// **'보상받기'** + String get questClaim; + + /// No description provided for @questStart. + /// + /// In ko, this message translates to: + /// **'퀘스트 시작하기'** + String get questStart; + + /// No description provided for @questRewardDone. + /// + /// In ko, this message translates to: + /// **'보상 완료'** + String get questRewardDone; + + /// No description provided for @popKindGeneral. + /// + /// In ko, this message translates to: + /// **'일반 POP'** + String get popKindGeneral; + + /// No description provided for @popKindContribution. + /// + /// In ko, this message translates to: + /// **'기여 POP'** + String get popKindContribution; + + /// No description provided for @popTxGrant. + /// + /// In ko, this message translates to: + /// **'지급'** + String get popTxGrant; + + /// No description provided for @popTxSpend. + /// + /// In ko, this message translates to: + /// **'차감'** + String get popTxSpend; + + /// No description provided for @popTxRefund. + /// + /// In ko, this message translates to: + /// **'환불'** + String get popTxRefund; + + /// No description provided for @popTxVoid. + /// + /// In ko, this message translates to: + /// **'무효'** + String get popTxVoid; + + /// No description provided for @popTypeQuest. + /// + /// In ko, this message translates to: + /// **'퀘스트'** + String get popTypeQuest; + + /// No description provided for @popTypeReward. + /// + /// In ko, this message translates to: + /// **'보상'** + String get popTypeReward; + + /// No description provided for @popTypePayment. + /// + /// In ko, this message translates to: + /// **'결제'** + String get popTypePayment; + + /// No description provided for @popTypeSubscription. + /// + /// In ko, this message translates to: + /// **'구독'** + String get popTypeSubscription; + + /// No description provided for @popTypeAdmin. + /// + /// In ko, this message translates to: + /// **'운영'** + String get popTypeAdmin; + + /// No description provided for @popTypeFee. + /// + /// In ko, this message translates to: + /// **'수수료'** + String get popTypeFee; + + /// No description provided for @commonEtc. + /// + /// In ko, this message translates to: + /// **'기타'** + String get commonEtc; + + /// No description provided for @popReasonQuestSteps. + /// + /// In ko, this message translates to: + /// **'만보기 퀘스트'** + String get popReasonQuestSteps; + + /// No description provided for @popReasonQuestMusicShake. + /// + /// In ko, this message translates to: + /// **'뮤직 셰이크 퀘스트'** + String get popReasonQuestMusicShake; + + /// No description provided for @popReasonQuestPlace. + /// + /// In ko, this message translates to: + /// **'장소 방문 퀘스트'** + String get popReasonQuestPlace; + + /// No description provided for @popReasonQuestAttend. + /// + /// In ko, this message translates to: + /// **'출석 퀘스트'** + String get popReasonQuestAttend; + + /// No description provided for @popReasonAdReward. + /// + /// In ko, this message translates to: + /// **'광고 보상'** + String get popReasonAdReward; + + /// No description provided for @popReasonHeroDrop. + /// + /// In ko, this message translates to: + /// **'영웅의 선물'** + String get popReasonHeroDrop; + + /// No description provided for @popReasonHeroDropTrigger. + /// + /// In ko, this message translates to: + /// **'영웅의 선물 발동'** + String get popReasonHeroDropTrigger; + + /// No description provided for @popReasonTranslation. + /// + /// In ko, this message translates to: + /// **'참여형 번역'** + String get popReasonTranslation; + + /// No description provided for @popReasonIapPurchase. + /// + /// In ko, this message translates to: + /// **'상품 구매'** + String get popReasonIapPurchase; + + /// No description provided for @popReasonFocusDaily. + /// + /// In ko, this message translates to: + /// **'Focus 일일 보상'** + String get popReasonFocusDaily; + + /// No description provided for @popReasonPreregBonus. + /// + /// In ko, this message translates to: + /// **'사전예약 보상'** + String get popReasonPreregBonus; + + /// No description provided for @popReasonReferral. + /// + /// In ko, this message translates to: + /// **'친구 초대'** + String get popReasonReferral; + + /// No description provided for @popTypeReferral. + /// + /// In ko, this message translates to: + /// **'레퍼럴'** + String get popTypeReferral; + + /// No description provided for @popReasonAdminGrant. + /// + /// In ko, this message translates to: + /// **'운영 지급'** + String get popReasonAdminGrant; + + /// No description provided for @popReasonAdminClawback. + /// + /// In ko, this message translates to: + /// **'운영 회수'** + String get popReasonAdminClawback; + + /// No description provided for @popReasonCampaignFund. + /// + /// In ko, this message translates to: + /// **'캠페인 후원'** + String get popReasonCampaignFund; + + /// No description provided for @popReasonCampaignRefund. + /// + /// In ko, this message translates to: + /// **'캠페인 환불'** + String get popReasonCampaignRefund; + + /// No description provided for @popReasonCampaignFeeRefund. + /// + /// In ko, this message translates to: + /// **'캠페인 개설 수수료 환불'** + String get popReasonCampaignFeeRefund; + + /// No description provided for @popReasonCampaignFee. + /// + /// In ko, this message translates to: + /// **'캠페인 등록비'** + String get popReasonCampaignFee; + + /// No description provided for @popReasonQnaBounty. + /// + /// In ko, this message translates to: + /// **'인사이드 현상금'** + String get popReasonQnaBounty; + + /// No description provided for @popReasonQnaRefund. + /// + /// In ko, this message translates to: + /// **'현상금 환불'** + String get popReasonQnaRefund; + + /// No description provided for @popReasonQnaReward. + /// + /// In ko, this message translates to: + /// **'인사이드 채택 보상'** + String get popReasonQnaReward; + + /// No description provided for @popReasonEclipseDefense. + /// + /// In ko, this message translates to: + /// **'Zap 기여'** + String get popReasonEclipseDefense; + + /// No description provided for @popReasonOther. + /// + /// In ko, this message translates to: + /// **'POP 변동'** + String get popReasonOther; + + /// No description provided for @popRepeat. + /// + /// In ko, this message translates to: + /// **'{title} {count}회'** + String popRepeat(String title, String count); + + /// No description provided for @payStatePaid. + /// + /// In ko, this message translates to: + /// **'결제 완료'** + String get payStatePaid; + + /// No description provided for @payStateRefundRequested. + /// + /// In ko, this message translates to: + /// **'환불 요청'** + String get payStateRefundRequested; + + /// No description provided for @payStateRefundApproved. + /// + /// In ko, this message translates to: + /// **'환불 승인'** + String get payStateRefundApproved; + + /// No description provided for @payStateRefunded. + /// + /// In ko, this message translates to: + /// **'환불 완료'** + String get payStateRefunded; + + /// No description provided for @payStateRefundRejected. + /// + /// In ko, this message translates to: + /// **'환불 반려'** + String get payStateRefundRejected; + + /// No description provided for @pushTopicEssentialDesc. + /// + /// In ko, this message translates to: + /// **'계정·결제·점검 안내입니다. 끄실 수 없습니다.'** + String get pushTopicEssentialDesc; + + /// No description provided for @pushTopicFandomEventDesc. + /// + /// In ko, this message translates to: + /// **'이클립스 · 캠페인 · 퀘스트 · 아티스트 핫타임 소식입니다.'** + String get pushTopicFandomEventDesc; + + /// No description provided for @pushTopicBriefingDesc. + /// + /// In ko, this message translates to: + /// **'행성의 하루를 정리한 브리핑 카드를 알려 드립니다.'** + String get pushTopicBriefingDesc; + + /// No description provided for @pushTopicCommunityDesc. + /// + /// In ko, this message translates to: + /// **'내 글의 댓글 · 좋아요 · 답변 알림입니다.'** + String get pushTopicCommunityDesc; + + /// No description provided for @pushTopicMarketingDesc. + /// + /// In ko, this message translates to: + /// **'이벤트 · 혜택 정보를 받습니다. 동의하실 때만 켜집니다.'** + String get pushTopicMarketingDesc; + + /// No description provided for @inquiryPending. + /// + /// In ko, this message translates to: + /// **'접수'** + String get inquiryPending; + + /// No description provided for @inquiryInProgress. + /// + /// In ko, this message translates to: + /// **'처리중'** + String get inquiryInProgress; + + /// No description provided for @inquiryAnswered. + /// + /// In ko, this message translates to: + /// **'답변 완료'** + String get inquiryAnswered; + + /// No description provided for @photoBadType. + /// + /// In ko, this message translates to: + /// **'JPG · PNG · WEBP · GIF 이미지만 올릴 수 있습니다.'** + String get photoBadType; + + /// No description provided for @photoTooBig. + /// + /// In ko, this message translates to: + /// **'사진 용량이 10MB 를 넘습니다. 더 작은 사진을 골라 주세요.'** + String get photoTooBig; + + /// No description provided for @photoUnreadable. + /// + /// In ko, this message translates to: + /// **'사진을 읽지 못했습니다. 다시 골라 주세요.'** + String get photoUnreadable; + + /// No description provided for @myListInside. + /// + /// In ko, this message translates to: + /// **'내 인사이드'** + String get myListInside; + + /// No description provided for @myListInsideEmpty. + /// + /// In ko, this message translates to: + /// **'아직 올리신 인사이드가 없습니다.'** + String get myListInsideEmpty; + + /// No description provided for @myListCampaign. + /// + /// In ko, this message translates to: + /// **'내 캠페인'** + String get myListCampaign; + + /// No description provided for @myListCampaignEmpty. + /// + /// In ko, this message translates to: + /// **'아직 만드신 캠페인이 없습니다.'** + String get myListCampaignEmpty; + + /// No description provided for @sortLatest. + /// + /// In ko, this message translates to: + /// **'최신순'** + String get sortLatest; + + /// No description provided for @sortPop. + /// + /// In ko, this message translates to: + /// **'POP순'** + String get sortPop; + + /// No description provided for @sortSolved. + /// + /// In ko, this message translates to: + /// **'채택 완료'** + String get sortSolved; + + /// No description provided for @sortAchieved. + /// + /// In ko, this message translates to: + /// **'달성 완료'** + String get sortAchieved; + + /// No description provided for @payNotFound. + /// + /// In ko, this message translates to: + /// **'결제 내역을 찾을 수 없습니다.'** + String get payNotFound; + + /// No description provided for @payItemFallback. + /// + /// In ko, this message translates to: + /// **'결제 상품'** + String get payItemFallback; + + /// No description provided for @payMethodInApp. + /// + /// In ko, this message translates to: + /// **'앱 내 구입'** + String get payMethodInApp; + + /// No description provided for @unknownUser. + /// + /// In ko, this message translates to: + /// **'알 수 없는 사용자'** + String get unknownUser; + + /// No description provided for @blockSelf. + /// + /// In ko, this message translates to: + /// **'자신은 차단할 수 없습니다.'** + String get blockSelf; + + /// No description provided for @inquiryFallback. + /// + /// In ko, this message translates to: + /// **'문의'** + String get inquiryFallback; + + /// No description provided for @photoRejectedServer. + /// + /// In ko, this message translates to: + /// **'사진을 올릴 수 없습니다. 형식과 용량을 확인해 주세요.'** + String get photoRejectedServer; + + /// No description provided for @photoUploadFailed. + /// + /// In ko, this message translates to: + /// **'사진 업로드가 실패했습니다. 잠시 후 다시 시도해 주세요.'** + String get photoUploadFailed; + + /// No description provided for @focusClaimed. + /// + /// In ko, this message translates to: + /// **'오늘의 Focus 보상 +{pop} 기여 POP 을 받았어요.'** + String focusClaimed(String pop); + + /// No description provided for @focusActive. + /// + /// In ko, this message translates to: + /// **'광고 없는 집중모드 이용 중'** + String get focusActive; + + /// No description provided for @focusUntil. + /// + /// In ko, this message translates to: + /// **'{date} 까지 · 오늘 몫은 오늘만 받을 수 있어요'** + String focusUntil(String date); + + /// No description provided for @focusClaimedToday. + /// + /// In ko, this message translates to: + /// **'오늘 받음'** + String get focusClaimedToday; + + /// No description provided for @focusClaimToday. + /// + /// In ko, this message translates to: + /// **'오늘 보상 받기'** + String get focusClaimToday; + + /// No description provided for @storeConnectFailed. + /// + /// In ko, this message translates to: + /// **'스토어에 연결하지 못했어요. 잠시 후 다시 시도해 주세요.'** + String get storeConnectFailed; + + /// No description provided for @payRestore. + /// + /// In ko, this message translates to: + /// **'구매 복원'** + String get payRestore; + + /// No description provided for @payEmpty. + /// + /// In ko, this message translates to: + /// **'결제 내역이 없습니다.'** + String get payEmpty; + + /// No description provided for @payRefundGuide. + /// + /// In ko, this message translates to: + /// **'소비형 상품 : 구매 후 7일 이내이며 사용하지 않은 상품은 환불 가능합니다. 일부라도 사용된 상품은 환불 대상에서 제외됩니다.\n구독형 상품 : 결제 후 24시간 이내 혜택 미사용(추가 보상 등) 시 환불 가능합니다.\n환불 승인 후 7-14 이내 환불 완료 처리가 됩니다.'** + String get payRefundGuide; + + /// No description provided for @payDetailTitle. + /// + /// In ko, this message translates to: + /// **'결제 내역 상세'** + String get payDetailTitle; + + /// No description provided for @payNumber. + /// + /// In ko, this message translates to: + /// **'결제 번호'** + String get payNumber; + + /// No description provided for @payDate. + /// + /// In ko, this message translates to: + /// **'결제 일시'** + String get payDate; + + /// No description provided for @payMethod. + /// + /// In ko, this message translates to: + /// **'결제 방식'** + String get payMethod; + + /// No description provided for @payStatus. + /// + /// In ko, this message translates to: + /// **'결제 상태'** + String get payStatus; + + /// No description provided for @payAmount. + /// + /// In ko, this message translates to: + /// **'결제 금액'** + String get payAmount; + + /// No description provided for @payRefundInfo. + /// + /// In ko, this message translates to: + /// **'환불 안내'** + String get payRefundInfo; + + /// No description provided for @payRefundApply. + /// + /// In ko, this message translates to: + /// **'환불 신청'** + String get payRefundApply; + + /// No description provided for @payRefundUnavailable. + /// + /// In ko, this message translates to: + /// **'환불 불가'** + String get payRefundUnavailable; + + /// No description provided for @refundReasonMistake. + /// + /// In ko, this message translates to: + /// **'실수로 결제했습니다'** + String get refundReasonMistake; + + /// No description provided for @refundReasonExpectation. + /// + /// In ko, this message translates to: + /// **'서비스가 기대와 다릅니다'** + String get refundReasonExpectation; + + /// No description provided for @refundReasonError. + /// + /// In ko, this message translates to: + /// **'오류로 사용하지 못했습니다'** + String get refundReasonError; + + /// No description provided for @refundReasonDuplicate. + /// + /// In ko, this message translates to: + /// **'중복 결제했습니다'** + String get refundReasonDuplicate; + + /// No description provided for @refundTitle. + /// + /// In ko, this message translates to: + /// **'환불'** + String get refundTitle; + + /// No description provided for @refundSubmit. + /// + /// In ko, this message translates to: + /// **'요청'** + String get refundSubmit; + + /// No description provided for @refundReason. + /// + /// In ko, this message translates to: + /// **'환불 사유'** + String get refundReason; + + /// No description provided for @refundConfirm. + /// + /// In ko, this message translates to: + /// **'정말 환불 신청을 할까요?'** + String get refundConfirm; + + /// No description provided for @myPop. + /// + /// In ko, this message translates to: + /// **'내 POP'** + String get myPop; + + /// No description provided for @commonNoHistory. + /// + /// In ko, this message translates to: + /// **'내역이 없습니다.'** + String get commonNoHistory; + + /// No description provided for @nicknameEdit. + /// + /// In ko, this message translates to: + /// **'닉네임 수정'** + String get nicknameEdit; + + /// No description provided for @nicknameReserved. + /// + /// In ko, this message translates to: + /// **'운영자로 오해될 수 있는 이름은 쓸 수 없습니다.'** + String get nicknameReserved; + + /// No description provided for @nicknameTaken. + /// + /// In ko, this message translates to: + /// **'이미 쓰고 있는 닉네임입니다.'** + String get nicknameTaken; + + /// No description provided for @albumItems. + /// + /// In ko, this message translates to: + /// **'앨범 항목'** + String get albumItems; + + /// No description provided for @takePhoto. + /// + /// In ko, this message translates to: + /// **'사진 촬영'** + String get takePhoto; + + /// No description provided for @pickFromAlbum. + /// + /// In ko, this message translates to: + /// **'앨범에서 고르기'** + String get pickFromAlbum; + + /// No description provided for @tierGuide. + /// + /// In ko, this message translates to: + /// **'티어 안내'** + String get tierGuide; + + /// No description provided for @statSteps. + /// + /// In ko, this message translates to: + /// **'누적 걸음'** + String get statSteps; + + /// No description provided for @statContribution. + /// + /// In ko, this message translates to: + /// **'누적 기여 POP'** + String get statContribution; + + /// No description provided for @statQuests. + /// + /// In ko, this message translates to: + /// **'완료 퀘스트'** + String get statQuests; + + /// No description provided for @shareInstagram. + /// + /// In ko, this message translates to: + /// **'인스타그램'** + String get shareInstagram; + + /// No description provided for @shareTiktok. + /// + /// In ko, this message translates to: + /// **'틱톡'** + String get shareTiktok; + + /// No description provided for @shareCopyLink. + /// + /// In ko, this message translates to: + /// **'링크 복사'** + String get shareCopyLink; + + /// No description provided for @inquiryListTitle. + /// + /// In ko, this message translates to: + /// **'1:1 문의 접수'** + String get inquiryListTitle; + + /// No description provided for @inquiryEmpty. + /// + /// In ko, this message translates to: + /// **'접수하신 문의가 없습니다.'** + String get inquiryEmpty; + + /// No description provided for @inquiryWrite. + /// + /// In ko, this message translates to: + /// **'문의 작성'** + String get inquiryWrite; + + /// No description provided for @inquiryDetail. + /// + /// In ko, this message translates to: + /// **'문의 상세'** + String get inquiryDetail; + + /// No description provided for @commonEdit. + /// + /// In ko, this message translates to: + /// **'수정'** + String get commonEdit; + + /// No description provided for @inquiryAnswer. + /// + /// In ko, this message translates to: + /// **'답변'** + String get inquiryAnswer; + + /// No description provided for @inquiryNew. + /// + /// In ko, this message translates to: + /// **'새 문의 작성'** + String get inquiryNew; + + /// No description provided for @inquiryEdit. + /// + /// In ko, this message translates to: + /// **'문의 수정'** + String get inquiryEdit; + + /// No description provided for @commonTitle. + /// + /// In ko, this message translates to: + /// **'제목'** + String get commonTitle; + + /// No description provided for @commonTitleHint. + /// + /// In ko, this message translates to: + /// **'제목을 입력해주세요.'** + String get commonTitleHint; + + /// No description provided for @inquiryBody. + /// + /// In ko, this message translates to: + /// **'문의 내용'** + String get inquiryBody; + + /// No description provided for @inquiryBodyHint. + /// + /// In ko, this message translates to: + /// **'문의할 내용을 작성해 주세요.'** + String get inquiryBodyHint; + + /// No description provided for @inquiryBodyHelper. + /// + /// In ko, this message translates to: + /// **'열 글자 이상 적어 주시면 더 빨리 도와드릴 수 있습니다.'** + String get inquiryBodyHelper; + + /// No description provided for @inquirySubmitFailed. + /// + /// In ko, this message translates to: + /// **'문의를 접수하지 못했습니다. {reason}'** + String inquirySubmitFailed(String reason); + + /// No description provided for @inquiryEditFailed. + /// + /// In ko, this message translates to: + /// **'문의를 수정하지 못했습니다. {reason}'** + String inquiryEditFailed(String reason); + + /// No description provided for @inquirySubmitted. + /// + /// In ko, this message translates to: + /// **'문의를 접수했습니다.'** + String get inquirySubmitted; + + /// No description provided for @inquiryEdited. + /// + /// In ko, this message translates to: + /// **'문의를 수정했습니다.'** + String get inquiryEdited; + + /// No description provided for @blockedEmpty. + /// + /// In ko, this message translates to: + /// **'차단한 계정이 없습니다.'** + String get blockedEmpty; + + /// No description provided for @unblock. + /// + /// In ko, this message translates to: + /// **'차단 해제'** + String get unblock; + + /// No description provided for @unblockFailed. + /// + /// In ko, this message translates to: + /// **'차단을 해제하지 못했습니다. {reason}'** + String unblockFailed(String reason); + + /// No description provided for @tierInfoTitle. + /// + /// In ko, this message translates to: + /// **'TIER 정보'** + String get tierInfoTitle; + + /// No description provided for @tierPromptPrefix. + /// + /// In ko, this message translates to: + /// **'행성/성운의 기여를 통해\n'** + String get tierPromptPrefix; + + /// No description provided for @tierPromptRank. + /// + /// In ko, this message translates to: + /// **'랭크'** + String get tierPromptRank; + + /// No description provided for @tierPromptSuffix. + /// + /// In ko, this message translates to: + /// **'를 올려보세요'** + String get tierPromptSuffix; + + /// No description provided for @tierPassportNote. + /// + /// In ko, this message translates to: + /// **'Passport Tier 는 레벨과 별개인 카드 외형 등급이며 내려가지 않습니다.'** + String get tierPassportNote; + + /// No description provided for @tierTotalContribution. + /// + /// In ko, this message translates to: + /// **'누적 기여'** + String get tierTotalContribution; + + /// No description provided for @notifKindEclipse. + /// + /// In ko, this message translates to: + /// **'이클립스'** + String get notifKindEclipse; + + /// No description provided for @notifKindItemUsed. + /// + /// In ko, this message translates to: + /// **'아이템 사용'** + String get notifKindItemUsed; + + /// No description provided for @notifKindLike. + /// + /// In ko, this message translates to: + /// **'좋아요'** + String get notifKindLike; + + /// No description provided for @notifKindComment. + /// + /// In ko, this message translates to: + /// **'댓글'** + String get notifKindComment; + + /// No description provided for @notifKindCampaign. + /// + /// In ko, this message translates to: + /// **'캠페인 달성'** + String get notifKindCampaign; + + /// No description provided for @notifKindQuest. + /// + /// In ko, this message translates to: + /// **'퀘스트 완료'** + String get notifKindQuest; + + /// No description provided for @notifTargetPost. + /// + /// In ko, this message translates to: + /// **'게시물'** + String get notifTargetPost; + + /// No description provided for @notifTargetCampaign. + /// + /// In ko, this message translates to: + /// **'캠페인'** + String get notifTargetCampaign; + + /// No description provided for @notifTargetQuest. + /// + /// In ko, this message translates to: + /// **'퀘스트 상세'** + String get notifTargetQuest; + + /// No description provided for @dayToday. + /// + /// In ko, this message translates to: + /// **'오늘'** + String get dayToday; + + /// No description provided for @dayYesterday. + /// + /// In ko, this message translates to: + /// **'어제'** + String get dayYesterday; + + /// No description provided for @notifEmpty. + /// + /// In ko, this message translates to: + /// **'알림이 없습니다.'** + String get notifEmpty; + + /// No description provided for @notifGoTo. + /// + /// In ko, this message translates to: + /// **'{target} 로 이동합니다.'** + String notifGoTo(String target); + + /// No description provided for @stationOfficial. + /// + /// In ko, this message translates to: + /// **'공식'** + String get stationOfficial; + + /// No description provided for @stationShop. + /// + /// In ko, this message translates to: + /// **'판매처'** + String get stationShop; + + /// No description provided for @stationTicket. + /// + /// In ko, this message translates to: + /// **'예매'** + String get stationTicket; + + /// No description provided for @stationMedia. + /// + /// In ko, this message translates to: + /// **'미디어'** + String get stationMedia; + + /// No description provided for @stationLoadFailed. + /// + /// In ko, this message translates to: + /// **'STATION 을 불러오지 못했습니다'** + String get stationLoadFailed; + + /// No description provided for @commonAll. + /// + /// In ko, this message translates to: + /// **'전체'** + String get commonAll; + + /// No description provided for @stationSuggest. + /// + /// In ko, this message translates to: + /// **'판매처 제안'** + String get stationSuggest; + + /// No description provided for @stationEmpty. + /// + /// In ko, this message translates to: + /// **'아직 등록된 링크가 없습니다.\n행성에 가입하면 큐레이션이 보입니다.'** + String get stationEmpty; + + /// No description provided for @stationCategoryEmpty. + /// + /// In ko, this message translates to: + /// **'이 분류에는 아직 링크가 없습니다.'** + String get stationCategoryEmpty; + + /// No description provided for @stationSuggestLater. + /// + /// In ko, this message translates to: + /// **'제안 접수는 서버 연동 후 열립니다.'** + String get stationSuggestLater; + + /// No description provided for @commonSubmit. + /// + /// In ko, this message translates to: + /// **'제출'** + String get commonSubmit; + + /// No description provided for @commonName. + /// + /// In ko, this message translates to: + /// **'이름'** + String get commonName; + + /// No description provided for @commonNameRequired. + /// + /// In ko, this message translates to: + /// **'이름을 입력해 주세요.'** + String get commonNameRequired; + + /// No description provided for @commonUrl. + /// + /// In ko, this message translates to: + /// **'주소'** + String get commonUrl; + + /// No description provided for @commonUrlRequired. + /// + /// In ko, this message translates to: + /// **'주소를 입력해 주세요.'** + String get commonUrlRequired; + + /// No description provided for @commonUrlHttps. + /// + /// In ko, this message translates to: + /// **'https 로 시작하는 주소를 입력해 주세요.'** + String get commonUrlHttps; + + /// No description provided for @commonCategory. + /// + /// In ko, this message translates to: + /// **'분류'** + String get commonCategory; + + /// No description provided for @stationReason. + /// + /// In ko, this message translates to: + /// **'추천 이유 (선택)'** + String get stationReason; + + /// No description provided for @stationSuggestNote. + /// + /// In ko, this message translates to: + /// **'제안하신 링크는 운영 검토를 거쳐 등록됩니다. 검토 결과는 알림으로 알려 드립니다.'** + String get stationSuggestNote; + + /// No description provided for @heroGiftUnavailable. + /// + /// In ko, this message translates to: + /// **'이 선물은 현재 수령할 수 없습니다.'** + String get heroGiftUnavailable; + + /// No description provided for @heroGiftAlready. + /// + /// In ko, this message translates to: + /// **'이미 참여한 선물입니다. POP 내역을 확인해 주세요.'** + String get heroGiftAlready; + + /// No description provided for @heroGiftRelogin. + /// + /// In ko, this message translates to: + /// **'다시 로그인한 뒤 참여해 주세요.'** + String get heroGiftRelogin; + + /// No description provided for @heroGiftNoPermission. + /// + /// In ko, this message translates to: + /// **'이 선물에 참여할 수 있는 권한이 없습니다.'** + String get heroGiftNoPermission; + + /// No description provided for @heroGiftHowTo. + /// + /// In ko, this message translates to: + /// **'폰을 흔들거나 아래 버튼을 눌러 빛 에너지 선물에 참여하세요.'** + String get heroGiftHowTo; + + /// No description provided for @heroGiftPopNote. + /// + /// In ko, this message translates to: + /// **'받을 수 있는 POP은 남은 선물과 1인 상한에 따라 결정됩니다.'** + String get heroGiftPopNote; + + /// No description provided for @heroGiftReceived. + /// + /// In ko, this message translates to: + /// **'{total} POP 수령 확인'** + String heroGiftReceived(String total); + + /// No description provided for @heroGiftUnknown. + /// + /// In ko, this message translates to: + /// **'지급 결과를 확인하지 못했습니다. 다시 확인해 주세요.'** + String get heroGiftUnknown; + + /// No description provided for @heroGiftNone. + /// + /// In ko, this message translates to: + /// **'지금 참여할 수 있는 선물이 없습니다.'** + String get heroGiftNone; + + /// No description provided for @heroGiftConfirmed. + /// + /// In ko, this message translates to: + /// **'참여한 선물의 수령을 확인했습니다.'** + String get heroGiftConfirmed; + + /// No description provided for @heroGiftChecking. + /// + /// In ko, this message translates to: + /// **'지급 확인 중…'** + String get heroGiftChecking; + + /// No description provided for @heroGiftRetry. + /// + /// In ko, this message translates to: + /// **'같은 선물 다시 확인'** + String get heroGiftRetry; + + /// No description provided for @heroGiftTap. + /// + /// In ko, this message translates to: + /// **'흔들기 대신 버튼으로 참여'** + String get heroGiftTap; + + /// No description provided for @rankMyPlanet. + /// + /// In ko, this message translates to: + /// **'내 행성의 {metric} 랭킹'** + String rankMyPlanet(String metric); + + /// No description provided for @rankPlanetEmpty. + /// + /// In ko, this message translates to: + /// **'아직 집계된 행성 랭킹이 없습니다'** + String get rankPlanetEmpty; + + /// No description provided for @rankScopeLive. + /// + /// In ko, this message translates to: + /// **'실시간'** + String get rankScopeLive; + + /// No description provided for @rankScopeCountry. + /// + /// In ko, this message translates to: + /// **'국가별'** + String get rankScopeCountry; + + /// No description provided for @rankMine. + /// + /// In ko, this message translates to: + /// **'나의 {metric} 랭킹'** + String rankMine(String metric); + + /// No description provided for @rankMyCountry. + /// + /// In ko, this message translates to: + /// **'내 국가의 {metric} 랭킹'** + String rankMyCountry(String metric); + + /// No description provided for @rankEmpty. + /// + /// In ko, this message translates to: + /// **'아직 집계된 랭킹이 없습니다'** + String get rankEmpty; + + /// No description provided for @rankScope. + /// + /// In ko, this message translates to: + /// **'{scope} 랭킹'** + String rankScope(String scope); + + /// No description provided for @rankLoadFailed. + /// + /// In ko, this message translates to: + /// **'랭킹을 불러오지 못했습니다'** + String get rankLoadFailed; + + /// No description provided for @rankMeSuffix. + /// + /// In ko, this message translates to: + /// **'{name} (나)'** + String rankMeSuffix(String name); + + /// No description provided for @reportDefamation. + /// + /// In ko, this message translates to: + /// **'아티스트 비방 및 명예훼손'** + String get reportDefamation; + + /// No description provided for @reportInappropriate. + /// + /// In ko, this message translates to: + /// **'부적절한 콘텐츠'** + String get reportInappropriate; + + /// No description provided for @reportSpam. + /// + /// In ko, this message translates to: + /// **'스팸 및 영리 목적 광고'** + String get reportSpam; + + /// No description provided for @reportPrivacy. + /// + /// In ko, this message translates to: + /// **'개인정보 노출 및 사칭'** + String get reportPrivacy; + + /// No description provided for @reportTitle. + /// + /// In ko, this message translates to: + /// **'신고하기'** + String get reportTitle; + + /// No description provided for @reportSubmit. + /// + /// In ko, this message translates to: + /// **'신고'** + String get reportSubmit; + + /// No description provided for @reportAuthor. + /// + /// In ko, this message translates to: + /// **'작성자'** + String get reportAuthor; + + /// No description provided for @reportReasonLabel. + /// + /// In ko, this message translates to: + /// **'신고 사유'** + String get reportReasonLabel; + + /// No description provided for @reportConfirm. + /// + /// In ko, this message translates to: + /// **'이 게시물을 신고할까요?\n접수 후에는 취소할 수 없습니다.'** + String get reportConfirm; + + /// No description provided for @blockMemberConfirm. + /// + /// In ko, this message translates to: + /// **'이 회원의 모든 게시물을 차단할까요?'** + String get blockMemberConfirm; + + /// No description provided for @loungeMembersOnly. + /// + /// In ko, this message translates to: + /// **'이 행성 팬덤 회원만 글 · 댓글 · 좋아요를 남길 수 있습니다.'** + String get loungeMembersOnly; + + /// No description provided for @loungeFailed. + /// + /// In ko, this message translates to: + /// **'처리하지 못했습니다. 잠시 후 다시 시도해 주세요.'** + String get loungeFailed; + + /// No description provided for @loungeTitle. + /// + /// In ko, this message translates to: + /// **'팬덤 라운지'** + String get loungeTitle; + + /// No description provided for @likeCancel. + /// + /// In ko, this message translates to: + /// **'좋아요 취소'** + String get likeCancel; + + /// No description provided for @like. + /// + /// In ko, this message translates to: + /// **'좋아요'** + String get like; + + /// No description provided for @commentDeleteConfirm. + /// + /// In ko, this message translates to: + /// **'댓글을 삭제할까요?'** + String get commentDeleteConfirm; + + /// No description provided for @commonShare. + /// + /// In ko, this message translates to: + /// **'공유'** + String get commonShare; + + /// No description provided for @commonEditAction. + /// + /// In ko, this message translates to: + /// **'수정하기'** + String get commonEditAction; + + /// No description provided for @commonDeleteAction. + /// + /// In ko, this message translates to: + /// **'삭제하기'** + String get commonDeleteAction; + + /// No description provided for @commonBlockAction. + /// + /// In ko, this message translates to: + /// **'차단하기'** + String get commonBlockAction; + + /// No description provided for @commentFirst. + /// + /// In ko, this message translates to: + /// **'첫 댓글을 남겨주세요.'** + String get commentFirst; + + /// No description provided for @postDeleteConfirm. + /// + /// In ko, this message translates to: + /// **'이 글을 삭제할까요?'** + String get postDeleteConfirm; + + /// No description provided for @reportReceived. + /// + /// In ko, this message translates to: + /// **'신고를 접수했습니다.'** + String get reportReceived; + + /// No description provided for @commonDelete. + /// + /// In ko, this message translates to: + /// **'삭제'** + String get commonDelete; + + /// No description provided for @commentHint. + /// + /// In ko, this message translates to: + /// **'댓글을 입력해 보세요.'** + String get commentHint; + + /// No description provided for @commonSend. + /// + /// In ko, this message translates to: + /// **'보내기'** + String get commonSend; + + /// No description provided for @postWrite. + /// + /// In ko, this message translates to: + /// **'글 쓰기'** + String get postWrite; + + /// No description provided for @postEdit. + /// + /// In ko, this message translates to: + /// **'글 수정'** + String get postEdit; + + /// No description provided for @loungeHint. + /// + /// In ko, this message translates to: + /// **'팬덤 라운지에 자유롭게 글을 작성해 보세요.'** + String get loungeHint; + + /// No description provided for @photoRemove. + /// + /// In ko, this message translates to: + /// **'사진 {n} 빼기'** + String photoRemove(String n); + + /// No description provided for @camera. + /// + /// In ko, this message translates to: + /// **'카메라'** + String get camera; + + /// No description provided for @album. + /// + /// In ko, this message translates to: + /// **'앨범'** + String get album; + + /// No description provided for @historyTitle. + /// + /// In ko, this message translates to: + /// **'PASSPORT · 기록'** + String get historyTitle; + + /// No description provided for @historyMine. + /// + /// In ko, this message translates to: + /// **'내 활동'** + String get historyMine; + + /// No description provided for @historyEvolution. + /// + /// In ko, this message translates to: + /// **'Stage {from} → {to} 진화'** + String historyEvolution(String from, String to); + + /// No description provided for @historyHibernation. + /// + /// In ko, this message translates to: + /// **'행성 휴면'** + String get historyHibernation; + + /// No description provided for @historyWakeUp. + /// + /// In ko, this message translates to: + /// **'행성 깨어남'** + String get historyWakeUp; + + /// No description provided for @historyZap. + /// + /// In ko, this message translates to: + /// **'ZAP 기여'** + String get historyZap; + + /// No description provided for @historyQuest. + /// + /// In ko, this message translates to: + /// **'Quest 보상'** + String get historyQuest; + + /// No description provided for @historyAdBonus. + /// + /// In ko, this message translates to: + /// **'광고 추가 보상'** + String get historyAdBonus; + + /// No description provided for @historyFunding. + /// + /// In ko, this message translates to: + /// **'Campaign 기여'** + String get historyFunding; + + /// No description provided for @historyGrowthPop. + /// + /// In ko, this message translates to: + /// **'성장에 {amount} POP 반영'** + String historyGrowthPop(String amount); + + /// No description provided for @historyJoined. + /// + /// In ko, this message translates to: + /// **'이 진화에 함께했습니다'** + String get historyJoined; + + /// No description provided for @historyLoadFailed. + /// + /// In ko, this message translates to: + /// **'기록을 불러오지 못했습니다.'** + String get historyLoadFailed; + + /// No description provided for @historyMineEmpty. + /// + /// In ko, this message translates to: + /// **'아직 기록된 활동이 없습니다.'** + String get historyMineEmpty; + + /// No description provided for @historyFandomEmpty. + /// + /// In ko, this message translates to: + /// **'아직 기록된 팬덤 역사가 없습니다.'** + String get historyFandomEmpty; + + /// No description provided for @historyMore. + /// + /// In ko, this message translates to: + /// **'이전 기록 더 보기'** + String get historyMore; + + /// No description provided for @countryName. + /// + /// In ko, this message translates to: + /// **'{code, select, KR{대한민국} JP{일본} US{미국} CN{중국} TW{대만} HK{홍콩} TH{태국} VN{베트남} ID{인도네시아} PH{필리핀} MY{말레이시아} SG{싱가포르} IN{인도} GB{영국} FR{프랑스} DE{독일} ES{스페인} BR{브라질} MX{멕시코} CA{캐나다} AU{호주} ZZ{미지정} other{-}}'** + String countryName(String code); + + /// No description provided for @loungeMaxPhotos. + /// + /// In ko, this message translates to: + /// **'사진은 {count}장까지 올릴 수 있습니다.'** + String loungeMaxPhotos(String count); + + /// No description provided for @commonLoadFailed. + /// + /// In ko, this message translates to: + /// **'불러오지 못했습니다'** + String get commonLoadFailed; + + /// No description provided for @planetPassportHistory. + /// + /// In ko, this message translates to: + /// **'PASSPORT · 팬덤 역사'** + String get planetPassportHistory; + + /// No description provided for @planetNamed. + /// + /// In ko, this message translates to: + /// **'{name} 행성'** + String planetNamed(String name); + + /// No description provided for @blockNebulaConfirm. + /// + /// In ko, this message translates to: + /// **'이 성운을 차단할까요?'** + String get blockNebulaConfirm; + + /// No description provided for @blockPlanetConfirm. + /// + /// In ko, this message translates to: + /// **'이 행성을 차단할까요?'** + String get blockPlanetConfirm; + + /// No description provided for @blocked. + /// + /// In ko, this message translates to: + /// **'차단했습니다.'** + String get blocked; + + /// No description provided for @planetLastUpdated. + /// + /// In ko, this message translates to: + /// **'마지막 갱신 {time}'** + String planetLastUpdated(String time); + + /// No description provided for @planetContribution. + /// + /// In ko, this message translates to: + /// **'기여 현황'** + String get planetContribution; + + /// No description provided for @planetNebulaGrowth. + /// + /// In ko, this message translates to: + /// **'성운 성장률'** + String get planetNebulaGrowth; + + /// No description provided for @planetLiveRanking. + /// + /// In ko, this message translates to: + /// **'실시간 기여 랭킹'** + String get planetLiveRanking; + + /// No description provided for @planetNoContributors. + /// + /// In ko, this message translates to: + /// **'아직 기여자가 없어요'** + String get planetNoContributors; + + /// No description provided for @planetContributeCta. + /// + /// In ko, this message translates to: + /// **'기여하고 랭킹에 등극해보세요!'** + String get planetContributeCta; + + /// No description provided for @planetOverallRank. + /// + /// In ko, this message translates to: + /// **'전체 {rank}위'** + String planetOverallRank(String rank); + + /// No description provided for @planetMyContribution. + /// + /// In ko, this message translates to: + /// **'내 기여도'** + String get planetMyContribution; + + /// No description provided for @planetTallying. + /// + /// In ko, this message translates to: + /// **'집계 준비 중'** + String get planetTallying; + + /// No description provided for @planetTopPercent. + /// + /// In ko, this message translates to: + /// **'상위 {percent}%'** + String planetTopPercent(String percent); + + /// No description provided for @planetHideMember. + /// + /// In ko, this message translates to: + /// **'이 회원의 글 모두 숨기기'** + String get planetHideMember; + + /// No description provided for @reportReasonUnknown. + /// + /// In ko, this message translates to: + /// **'신고 사유를 확인할 수 없습니다.'** + String get reportReasonUnknown; + + /// No description provided for @reportAlready. + /// + /// In ko, this message translates to: + /// **'이미 신고한 게시물입니다.'** + String get reportAlready; + + /// No description provided for @blockUnavailable. + /// + /// In ko, this message translates to: + /// **'차단할 수 없는 작성자입니다.'** + String get blockUnavailable; + + /// No description provided for @commonMe. + /// + /// In ko, this message translates to: + /// **'나'** + String get commonMe; + + /// No description provided for @planetSizeMedium. + /// + /// In ko, this message translates to: + /// **'중형 팬덤'** + String get planetSizeMedium; + + /// No description provided for @planetPerHead. + /// + /// In ko, this message translates to: + /// **'1인당 화력 : {value}'** + String planetPerHead(String value); + + /// No description provided for @planetJoined. + /// + /// In ko, this message translates to: + /// **'가입중'** + String get planetJoined; + + /// No description provided for @planetJoin. + /// + /// In ko, this message translates to: + /// **'가입하기'** + String get planetJoin; + + /// No description provided for @questDetails. + /// + /// In ko, this message translates to: + /// **'자세히'** + String get questDetails; + + /// No description provided for @questInProgress. + /// + /// In ko, this message translates to: + /// **'진행중'** + String get questInProgress; + + /// No description provided for @questTypeAttend. + /// + /// In ko, this message translates to: + /// **'출석'** + String get questTypeAttend; + + /// No description provided for @questTypeSteps. + /// + /// In ko, this message translates to: + /// **'걷기'** + String get questTypeSteps; + + /// No description provided for @questTypePlace. + /// + /// In ko, this message translates to: + /// **'특정 위치'** + String get questTypePlace; + + /// No description provided for @questTypeStreaming. + /// + /// In ko, this message translates to: + /// **'스트리밍'** + String get questTypeStreaming; + + /// No description provided for @questTypeUnverifiable. + /// + /// In ko, this message translates to: + /// **'이 퀘스트 유형의 검증 방식이 아직 정해지지 않았습니다.'** + String get questTypeUnverifiable; + + /// No description provided for @questTabMine. + /// + /// In ko, this message translates to: + /// **'내 퀘스트'** + String get questTabMine; + + /// No description provided for @placePickPrompt. + /// + /// In ko, this message translates to: + /// **'퀘스트를 완료할\n장소를 선택해주세요.'** + String get placePickPrompt; + + /// No description provided for @placeSearch. + /// + /// In ko, this message translates to: + /// **'장소 검색'** + String get placeSearch; + + /// No description provided for @commonNoResults. + /// + /// In ko, this message translates to: + /// **'검색 결과가 없습니다.'** + String get commonNoResults; + + /// No description provided for @commonSelectDone. + /// + /// In ko, this message translates to: + /// **'선택 완료'** + String get commonSelectDone; + + /// No description provided for @adMoreChance. + /// + /// In ko, this message translates to: + /// **'더 많은 기여의 기회를 얻으세요.'** + String get adMoreChance; + + /// No description provided for @adWatchPrompt. + /// + /// In ko, this message translates to: + /// **'광고를 시청하고 추가 보상을\n받을 수 있어요!'** + String get adWatchPrompt; + + /// No description provided for @adBonusPlaceholder. + /// + /// In ko, this message translates to: + /// **'+ 기여 POP'** + String get adBonusPlaceholder; + + /// No description provided for @adPartyPack. + /// + /// In ko, this message translates to: + /// **'파티 팩'** + String get adPartyPack; + + /// No description provided for @adPickProduct. + /// + /// In ko, this message translates to: + /// **'상품 선택하기'** + String get adPickProduct; + + /// No description provided for @adWatchBonus. + /// + /// In ko, this message translates to: + /// **'광고 보고 추가 보상 받기'** + String get adWatchBonus; + + /// No description provided for @adBasicReward. + /// + /// In ko, this message translates to: + /// **'기본 보상 받기'** + String get adBasicReward; + + /// No description provided for @focusMode. + /// + /// In ko, this message translates to: + /// **'광고 없는 집중모드'** + String get focusMode; + + /// No description provided for @pricePerMonth. + /// + /// In ko, this message translates to: + /// **'{price}/월'** + String pricePerMonth(String price); + + /// No description provided for @adWarpBefore. + /// + /// In ko, this message translates to: + /// **'Warping...우리 팬덤과 아티스트에 더 많은 기여를 위해 적합한 광고를 열심히 찾고 있어요.'** + String get adWarpBefore; + + /// No description provided for @adWarpAfter. + /// + /// In ko, this message translates to: + /// **'Warping...당신의 기여에 대한 보상을 준비하고 있어요.'** + String get adWarpAfter; + + /// No description provided for @adSkip. + /// + /// In ko, this message translates to: + /// **'건너뛰기'** + String get adSkip; + + /// No description provided for @questPreviewHint. + /// + /// In ko, this message translates to: + /// **'옵션을 선택, 미리 보기를 확인하세요'** + String get questPreviewHint; + + /// No description provided for @questNoCategory. + /// + /// In ko, this message translates to: + /// **'아직 선택된 카테고리가 없습니다'** + String get questNoCategory; + + /// No description provided for @questNew. + /// + /// In ko, this message translates to: + /// **'새 퀘스트'** + String get questNew; + + /// No description provided for @questCategory. + /// + /// In ko, this message translates to: + /// **'카테고리'** + String get questCategory; + + /// No description provided for @questName. + /// + /// In ko, this message translates to: + /// **'퀘스트명'** + String get questName; + + /// No description provided for @questAction. + /// + /// In ko, this message translates to: + /// **'상세 액션'** + String get questAction; + + /// No description provided for @questPickPlace. + /// + /// In ko, this message translates to: + /// **'목표 위치를 골라 주세요'** + String get questPickPlace; + + /// No description provided for @questGoalValue. + /// + /// In ko, this message translates to: + /// **'목표 수치'** + String get questGoalValue; + + /// No description provided for @questParticipants. + /// + /// In ko, this message translates to: + /// **'참여 인원'** + String get questParticipants; + + /// No description provided for @questRewardPop. + /// + /// In ko, this message translates to: + /// **'완료 보상 POP'** + String get questRewardPop; + + /// No description provided for @questCreateConfirm. + /// + /// In ko, this message translates to: + /// **'이 내용으로 만들까요?'** + String get questCreateConfirm; + + /// No description provided for @questCreateSummary. + /// + /// In ko, this message translates to: + /// **'{title} · 목표 {goal} · 보상 {reward} POP'** + String questCreateSummary(String title, String goal, String reward); + + /// No description provided for @commonKeepEditing. + /// + /// In ko, this message translates to: + /// **'더 고치기'** + String get commonKeepEditing; + + /// No description provided for @questCreate. + /// + /// In ko, this message translates to: + /// **'만들기'** + String get questCreate; + + /// No description provided for @questNamed. + /// + /// In ko, this message translates to: + /// **'{title} 퀘스트'** + String questNamed(String title); + + /// No description provided for @questDailyHelp. + /// + /// In ko, this message translates to: + /// **'매일매일 행성에 도움을'** + String get questDailyHelp; + + /// No description provided for @questTodayMust. + /// + /// In ko, this message translates to: + /// **'오늘 꼭 해야하는 퀘스트에요'** + String get questTodayMust; + + /// No description provided for @questTabEmpty. + /// + /// In ko, this message translates to: + /// **'이 탭에는 아직 퀘스트가 없습니다.'** + String get questTabEmpty; + + /// No description provided for @questCreateNotReady. + /// + /// In ko, this message translates to: + /// **'퀘스트 생성은 보상 재원 예치와 함께 여는 기능입니다. 준비 중입니다.'** + String get questCreateNotReady; + + /// No description provided for @questNoBaseReward. + /// + /// In ko, this message translates to: + /// **'기본 보상 기록을 찾지 못해 광고 추가 보상을 받을 수 없어요.'** + String get questNoBaseReward; + + /// No description provided for @questNoSteps. + /// + /// In ko, this message translates to: + /// **'걸음 수가 아직 수집되지 않았습니다. 조금 걸은 뒤 다시 시도해 주세요.'** + String get questNoSteps; + + /// No description provided for @questNoPlace. + /// + /// In ko, this message translates to: + /// **'이 퀘스트에 인증할 장소가 등록되어 있지 않습니다.'** + String get questNoPlace; + + /// No description provided for @questGoal. + /// + /// In ko, this message translates to: + /// **'목표'** + String get questGoal; + + /// No description provided for @questReward. + /// + /// In ko, this message translates to: + /// **'보상'** + String get questReward; + + /// No description provided for @questPopGrant. + /// + /// In ko, this message translates to: + /// **'POP 지급'** + String get questPopGrant; + + /// No description provided for @commonChecking. + /// + /// In ko, this message translates to: + /// **'확인 중…'** + String get commonChecking; + + /// No description provided for @questMine. + /// + /// In ko, this message translates to: + /// **'내가 만든 퀘스트입니다. 참여자는 {count}명입니다.'** + String questMine(String count); + + /// No description provided for @questStopConfirm. + /// + /// In ko, this message translates to: + /// **'퀘스트를 중단할까요?'** + String get questStopConfirm; + + /// No description provided for @questStopBody. + /// + /// In ko, this message translates to: + /// **'지금까지의 진행이 초기화되고 처음 상태로 돌아갑니다.'** + String get questStopBody; + + /// No description provided for @questStop. + /// + /// In ko, this message translates to: + /// **'중단'** + String get questStop; + + /// No description provided for @adNotFinished. + /// + /// In ko, this message translates to: + /// **'광고를 끝까지 보지 않아 추가 보상은 없어요. 기본 보상은 그대로 받습니다.'** + String get adNotFinished; + + /// No description provided for @openSettings. + /// + /// In ko, this message translates to: + /// **'설정 열기'** + String get openSettings; + + /// No description provided for @permStepsDenied. + /// + /// In ko, this message translates to: + /// **'걸음 수 권한이 꺼져 있습니다. 설정에서 「신체 활동」을 허용해 주세요.'** + String get permStepsDenied; + + /// No description provided for @permStepsNeeded. + /// + /// In ko, this message translates to: + /// **'걸음 수를 세려면 신체 활동 권한이 필요합니다.'** + String get permStepsNeeded; + + /// No description provided for @permLocationOff. + /// + /// In ko, this message translates to: + /// **'기기의 위치 기능이 꺼져 있습니다. 켜고 다시 시도해 주세요.'** + String get permLocationOff; + + /// No description provided for @permLocationNeeded. + /// + /// In ko, this message translates to: + /// **'방문을 확인하려면 위치 권한이 필요합니다.'** + String get permLocationNeeded; + + /// No description provided for @permLocationDenied. + /// + /// In ko, this message translates to: + /// **'위치 권한이 꺼져 있습니다. 설정에서 위치 접근을 허용해 주세요.'** + String get permLocationDenied; + + /// No description provided for @questRewardReceived. + /// + /// In ko, this message translates to: + /// **' 받았어요!'** + String get questRewardReceived; + + /// No description provided for @questAdBonus. + /// + /// In ko, this message translates to: + /// **'광고 추가 보상 +{pop} POP'** + String questAdBonus(String pop); + + /// No description provided for @questParticipantsCount. + /// + /// In ko, this message translates to: + /// **'참여 인원 : {count}'** + String questParticipantsCount(String count); + + /// No description provided for @questParticipantsCap. + /// + /// In ko, this message translates to: + /// **'참여 인원 : {count} / {cap}'** + String questParticipantsCap(String count, String cap); + + /// No description provided for @nebulaCreateFailed. + /// + /// In ko, this message translates to: + /// **'성운을 만들지 못했습니다. 잠시 후 다시 시도해 주세요.'** + String get nebulaCreateFailed; + + /// No description provided for @nebulaAlreadyToday. + /// + /// In ko, this message translates to: + /// **'이미 오늘 성운을 생성하셨습니다.'** + String get nebulaAlreadyToday; + + /// No description provided for @nebulaCreatedKeepPrimary. + /// + /// In ko, this message translates to: + /// **'성운을 만들었습니다. 기본 행성은 최근 변경 후 30일이 지나야 바꿀 수 있어 그대로 유지됩니다.'** + String get nebulaCreatedKeepPrimary; + + /// No description provided for @fandomFounderLeaveBlocked. + /// + /// In ko, this message translates to: + /// **'행성이 탄생한 뒤에 탈퇴할 수 있어요.'** + String get fandomFounderLeaveBlocked; + + /// No description provided for @sortPopular. + /// + /// In ko, this message translates to: + /// **'인기순'** + String get sortPopular; + + /// No description provided for @campaignSortContributing. + /// + /// In ko, this message translates to: + /// **'기여중'** + String get campaignSortContributing; + + /// No description provided for @campaignStatusReview. + /// + /// In ko, this message translates to: + /// **'운영 검수 중인 캠페인입니다.'** + String get campaignStatusReview; + + /// No description provided for @campaignStatusRejected. + /// + /// In ko, this message translates to: + /// **'운영 검수에서 반려되었습니다. 등록비는 돌려드렸습니다.'** + String get campaignStatusRejected; + + /// No description provided for @campaignStatusWithdrawn. + /// + /// In ko, this message translates to: + /// **'개설자가 철회한 캠페인입니다.'** + String get campaignStatusWithdrawn; + + /// No description provided for @campaignStatusCancelled. + /// + /// In ko, this message translates to: + /// **'운영 사유로 취소되었습니다. 등록비와 후원 POP 을 돌려드렸습니다.'** + String get campaignStatusCancelled; + + /// No description provided for @rankWindowDaily. + /// + /// In ko, this message translates to: + /// **'일간'** + String get rankWindowDaily; + + /// No description provided for @rankWindowWeekly. + /// + /// In ko, this message translates to: + /// **'주간'** + String get rankWindowWeekly; + + /// No description provided for @campaignNotFound. + /// + /// In ko, this message translates to: + /// **'캠페인을 찾을 수 없습니다.'** + String get campaignNotFound; + + /// No description provided for @campaignMediaMain. + /// + /// In ko, this message translates to: + /// **'대표 이미지'** + String get campaignMediaMain; + + /// No description provided for @campaignLoadFailed. + /// + /// In ko, this message translates to: + /// **'캠페인을 불러오지 못했습니다.'** + String get campaignLoadFailed; + + /// No description provided for @campaignTabInfo. + /// + /// In ko, this message translates to: + /// **'캠페인 정보'** + String get campaignTabInfo; + + /// No description provided for @campaignTabBoard. + /// + /// In ko, this message translates to: + /// **'응원 게시판 {count}'** + String campaignTabBoard(String count); + + /// No description provided for @campaignWithdraw. + /// + /// In ko, this message translates to: + /// **'캠페인 철회'** + String get campaignWithdraw; + + /// No description provided for @campaignContributeAction. + /// + /// In ko, this message translates to: + /// **'기여하기'** + String get campaignContributeAction; + + /// No description provided for @campaignFundingStatus. + /// + /// In ko, this message translates to: + /// **'현재 펀딩 현황'** + String get campaignFundingStatus; + + /// No description provided for @campaignSupporting. + /// + /// In ko, this message translates to: + /// **'응원 중'** + String get campaignSupporting; + + /// No description provided for @campaignTimeLeft. + /// + /// In ko, this message translates to: + /// **'남았어요!'** + String get campaignTimeLeft; + + /// No description provided for @campaignReport. + /// + /// In ko, this message translates to: + /// **'결과보고'** + String get campaignReport; + + /// No description provided for @campaignReportPhotos. + /// + /// In ko, this message translates to: + /// **'집행 사진'** + String get campaignReportPhotos; + + /// No description provided for @campaignBudget. + /// + /// In ko, this message translates to: + /// **'예산 사용 내역'** + String get campaignBudget; + + /// No description provided for @campaignTotal. + /// + /// In ko, this message translates to: + /// **'합계'** + String get campaignTotal; + + /// No description provided for @campaignReceipts. + /// + /// In ko, this message translates to: + /// **'영수증'** + String get campaignReceipts; + + /// No description provided for @campaignDaysLeft. + /// + /// In ko, this message translates to: + /// **'{days}일 {time}'** + String campaignDaysLeft(String days, String time); + + /// No description provided for @campaignWithdrawTitle. + /// + /// In ko, this message translates to: + /// **'캠페인을 철회할까요?'** + String get campaignWithdrawTitle; + + /// No description provided for @campaignWithdrawRefund. + /// + /// In ko, this message translates to: + /// **'공개 전 철회라 등록비 {fee} POP 을 낸 재화 그대로 돌려드립니다.'** + String campaignWithdrawRefund(String fee); + + /// No description provided for @campaignWithdrawNoRefund. + /// + /// In ko, this message translates to: + /// **'이미 공개된 캠페인이라 등록비 {fee} POP 은 돌려드리지 않습니다.'** + String campaignWithdrawNoRefund(String fee); + + /// No description provided for @campaignWithdrawConfirm. + /// + /// In ko, this message translates to: + /// **'철회하기'** + String get campaignWithdrawConfirm; + + /// No description provided for @shareCopied. + /// + /// In ko, this message translates to: + /// **'공유 문구를 복사했습니다.'** + String get shareCopied; + + /// No description provided for @campaignContributePop. + /// + /// In ko, this message translates to: + /// **'기여 POP 설정'** + String get campaignContributePop; + + /// No description provided for @campaignEmptyContributing. + /// + /// In ko, this message translates to: + /// **'아직 기여하신 캠페인이 없습니다.'** + String get campaignEmptyContributing; + + /// No description provided for @campaignEmpty. + /// + /// In ko, this message translates to: + /// **'등록된 캠페인이 없습니다.'** + String get campaignEmpty; + + /// No description provided for @campaignNew. + /// + /// In ko, this message translates to: + /// **'새 캠페인'** + String get campaignNew; + + /// No description provided for @campaignMyRanking. + /// + /// In ko, this message translates to: + /// **'나의 기여 랭킹'** + String get campaignMyRanking; + + /// No description provided for @campaignWindowRanking. + /// + /// In ko, this message translates to: + /// **'{window} 랭킹'** + String campaignWindowRanking(String window); + + /// No description provided for @campaignSearchHint. + /// + /// In ko, this message translates to: + /// **'캠페인 검색'** + String get campaignSearchHint; + + /// No description provided for @campaignNoRecentSearches. + /// + /// In ko, this message translates to: + /// **'최신 검색어가 없습니다'** + String get campaignNoRecentSearches; + + /// No description provided for @searchRemoveTerm. + /// + /// In ko, this message translates to: + /// **'{term} 삭제'** + String searchRemoveTerm(String term); + + /// No description provided for @campaignNoResults. + /// + /// In ko, this message translates to: + /// **'검색 결과가 없습니다'** + String get campaignNoResults; + + /// No description provided for @shareTopicCampaign. + /// + /// In ko, this message translates to: + /// **'캠페인'** + String get shareTopicCampaign; + + /// No description provided for @sharePlanetLine. + /// + /// In ko, this message translates to: + /// **'{brand} · {name} 행성'** + String sharePlanetLine(String brand, String name); + + /// No description provided for @campaignGaugeLabel. + /// + /// In ko, this message translates to: + /// **'캠페인 기여률'** + String get campaignGaugeLabel; + + /// No description provided for @campaignPeriodTitle. + /// + /// In ko, this message translates to: + /// **'캠페인 진행 기간을 선택하세요'** + String get campaignPeriodTitle; + + /// No description provided for @campaignPeriodMax. + /// + /// In ko, this message translates to: + /// **'기간은 최대 1년까지 선택할 수 있어요'** + String get campaignPeriodMax; + + /// No description provided for @campaignYearMonth. + /// + /// In ko, this message translates to: + /// **'{year}년 {month}월'** + String campaignYearMonth(String year, String month); + + /// No description provided for @campaignWeekdays. + /// + /// In ko, this message translates to: + /// **'일,월,화,수,목,금,토'** + String get campaignWeekdays; + + /// No description provided for @campaignPeriodRange. + /// + /// In ko, this message translates to: + /// **'{startMonth}월 {startDay}일 ~ {endMonth}월 {endDay}일'** + String campaignPeriodRange( + String startMonth, + String startDay, + String endMonth, + String endDay, + ); + + /// No description provided for @campaignPeriodSelect. + /// + /// In ko, this message translates to: + /// **'기간 선택'** + String get campaignPeriodSelect; + + /// No description provided for @calendarPrevMonth. + /// + /// In ko, this message translates to: + /// **'이전 달'** + String get calendarPrevMonth; + + /// No description provided for @calendarNextMonth. + /// + /// In ko, this message translates to: + /// **'다음 달'** + String get calendarNextMonth; + + /// No description provided for @campaignShareTitle. + /// + /// In ko, this message translates to: + /// **'캠페인 공유'** + String get campaignShareTitle; + + /// No description provided for @campaignPickMedia. + /// + /// In ko, this message translates to: + /// **'이미지/영상 선택'** + String get campaignPickMedia; + + /// No description provided for @campaignTitleHint. + /// + /// In ko, this message translates to: + /// **'캠페인 제목을 입력해주세요.'** + String get campaignTitleHint; + + /// No description provided for @campaignBodyHint. + /// + /// In ko, this message translates to: + /// **'캠페인 설명을 작성해주세요.'** + String get campaignBodyHint; + + /// No description provided for @campaignReviewNote. + /// + /// In ko, this message translates to: + /// **'올리시면 운영 검수를 지난 뒤 모금이 열립니다.'** + String get campaignReviewNote; + + /// No description provided for @campaignSupportType. + /// + /// In ko, this message translates to: + /// **'지원 종류 선택'** + String get campaignSupportType; + + /// No description provided for @campaignPeriod. + /// + /// In ko, this message translates to: + /// **'캠페인 진행 기간'** + String get campaignPeriod; + + /// No description provided for @campaignDateSelect. + /// + /// In ko, this message translates to: + /// **'날짜 선택'** + String get campaignDateSelect; + + /// No description provided for @campaignGoalPop. + /// + /// In ko, this message translates to: + /// **'목표 기여 POP'** + String get campaignGoalPop; + + /// No description provided for @campaignSubmitTitle. + /// + /// In ko, this message translates to: + /// **'이 내용으로 올릴까요?'** + String get campaignSubmitTitle; + + /// No description provided for @campaignSubmitBody. + /// + /// In ko, this message translates to: + /// **'{title} · 목표 {goal} POP · {days}일\n등록비 {fee} POP 이 빠지고 운영 검수 뒤 모금이 열립니다.'** + String campaignSubmitBody(String title, String goal, String days, String fee); + + /// No description provided for @campaignSubmit. + /// + /// In ko, this message translates to: + /// **'올리기'** + String get campaignSubmit; + + /// No description provided for @campaignCreateFailed. + /// + /// In ko, this message translates to: + /// **'캠페인을 올리지 못했습니다.'** + String get campaignCreateFailed; + + /// No description provided for @campaignNeedsQuests. + /// + /// In ko, this message translates to: + /// **'퀘스트 완료 {done}/{need} — 퀘스트를 {left}번 더 완료하면 캠페인을 열 수 있습니다.'** + String campaignNeedsQuests(String done, String need, String left); + + /// No description provided for @campaignPendingTitle. + /// + /// In ko, this message translates to: + /// **'검수 대기 중입니다'** + String get campaignPendingTitle; + + /// No description provided for @campaignPendingBody. + /// + /// In ko, this message translates to: + /// **'운영 검수를 마치면 모금이 열리고 목록에 보입니다.'** + String get campaignPendingBody; + + /// No description provided for @insideRefundPending. + /// + /// In ko, this message translates to: + /// **'채택 기한이 지나 현상금이 환불될 예정입니다.'** + String get insideRefundPending; + + /// No description provided for @insideRefundDone. + /// + /// In ko, this message translates to: + /// **'채택 기한이 지나 현상금이 환불되었습니다.'** + String get insideRefundDone; + + /// No description provided for @insideRefundChecking. + /// + /// In ko, this message translates to: + /// **'채택 기한이 지나 현상금 환불을 확인하고 있습니다.'** + String get insideRefundChecking; + + /// No description provided for @insideErrPopShort. + /// + /// In ko, this message translates to: + /// **'POP 이 모자라 현상금을 걸지 못했습니다.'** + String get insideErrPopShort; + + /// No description provided for @insideErrSelfAccept. + /// + /// In ko, this message translates to: + /// **'자기 답변은 채택할 수 없습니다.'** + String get insideErrSelfAccept; + + /// No description provided for @insideErrAlreadyAccepted. + /// + /// In ko, this message translates to: + /// **'이미 채택한 질문입니다. 채택은 바꿀 수 없습니다.'** + String get insideErrAlreadyAccepted; + + /// No description provided for @insideErrNotOwner. + /// + /// In ko, this message translates to: + /// **'질문을 올린 분만 채택할 수 있습니다.'** + String get insideErrNotOwner; + + /// No description provided for @commonCheckConnection. + /// + /// In ko, this message translates to: + /// **'연결을 확인하고 다시 시도해 주세요.'** + String get commonCheckConnection; + + /// No description provided for @insideAnswerLater. + /// + /// In ko, this message translates to: + /// **'댓글 등록은 서버 연동 후 열립니다.'** + String get insideAnswerLater; + + /// No description provided for @insideAcceptConfirm. + /// + /// In ko, this message translates to: + /// **'이 댓글을 채택할까요?\n현상금 {pop} POP 이 전달되며 채택은 되돌릴 수 없습니다.'** + String insideAcceptConfirm(String pop); + + /// No description provided for @insideAccept. + /// + /// In ko, this message translates to: + /// **'채택'** + String get insideAccept; + + /// No description provided for @insideMore. + /// + /// In ko, this message translates to: + /// **'더보기'** + String get insideMore; + + /// No description provided for @insideFirstAnswer. + /// + /// In ko, this message translates to: + /// **'첫 댓글을 남겨주세요.'** + String get insideFirstAnswer; + + /// No description provided for @insideAcceptDone. + /// + /// In ko, this message translates to: + /// **'채택 완료'** + String get insideAcceptDone; + + /// No description provided for @insideAcceptAction. + /// + /// In ko, this message translates to: + /// **'채택하기'** + String get insideAcceptAction; + + /// No description provided for @insideAnswerHint. + /// + /// In ko, this message translates to: + /// **'댓글을 입력해 보세요.'** + String get insideAnswerHint; + + /// No description provided for @insideLoadFailed. + /// + /// In ko, this message translates to: + /// **'인사이드를 불러오지 못했습니다'** + String get insideLoadFailed; + + /// No description provided for @insideEmpty. + /// + /// In ko, this message translates to: + /// **'아직 올라온 질문이 없습니다.'** + String get insideEmpty; + + /// No description provided for @insideWrite. + /// + /// In ko, this message translates to: + /// **'인사이드 작성'** + String get insideWrite; + + /// No description provided for @insideSearchHint. + /// + /// In ko, this message translates to: + /// **'인사이드 제목, 내용'** + String get insideSearchHint; + + /// No description provided for @insideBodyHint. + /// + /// In ko, this message translates to: + /// **'팬덤들과 자유롭게 질문을 주고받아 보세요.'** + String get insideBodyHint; + + /// No description provided for @insideTitleRequired. + /// + /// In ko, this message translates to: + /// **'제목을 입력해 주세요.'** + String get insideTitleRequired; + + /// No description provided for @insideBodyRequired. + /// + /// In ko, this message translates to: + /// **'내용을 입력해 주세요.'** + String get insideBodyRequired; + + /// No description provided for @insideBountySet. + /// + /// In ko, this message translates to: + /// **'현상금 설정'** + String get insideBountySet; + + /// No description provided for @insideBountyHint. + /// + /// In ko, this message translates to: + /// **'{min} 이상 설정해 주세요.'** + String insideBountyHint(String min); + + /// No description provided for @insideAttachLater. + /// + /// In ko, this message translates to: + /// **'이미지 첨부는 서버 연동 후 열립니다.'** + String get insideAttachLater; + + /// No description provided for @insideBountyRequired. + /// + /// In ko, this message translates to: + /// **'현상금을 입력해 주세요.'** + String get insideBountyRequired; + + /// No description provided for @insideNumberOnly. + /// + /// In ko, this message translates to: + /// **'숫자로 입력해 주세요.'** + String get insideNumberOnly; + + /// No description provided for @insideBountyMin. + /// + /// In ko, this message translates to: + /// **'현상금은 {min} POP 이상이어야 합니다.'** + String insideBountyMin(String min); + + /// No description provided for @insideBountyOverBalance. + /// + /// In ko, this message translates to: + /// **'보유 POP 을 넘을 수 없습니다.'** + String get insideBountyOverBalance; + + /// No description provided for @questTypeTimeAttack. + /// + /// In ko, this message translates to: + /// **'긴급 타임어택'** + String get questTypeTimeAttack; + + /// No description provided for @questTimeLeft. + /// + /// In ko, this message translates to: + /// **'남은 시간'** + String get questTimeLeft; + + /// No description provided for @questTimeAttackClosed. + /// + /// In ko, this message translates to: + /// **'마감'** + String get questTimeAttackClosed; + + /// No description provided for @questTimeAttackLeft. + /// + /// In ko, this message translates to: + /// **'{time} 남음'** + String questTimeAttackLeft(String time); + + /// No description provided for @popReasonQuestTimeAttack. + /// + /// In ko, this message translates to: + /// **'긴급 돌발 타임어택'** + String get popReasonQuestTimeAttack; + + /// No description provided for @briefingShareCopied. + /// + /// In ko, this message translates to: + /// **'공유 문구와 링크를 복사했습니다.'** + String get briefingShareCopied; + + /// No description provided for @shareCopyText. + /// + /// In ko, this message translates to: + /// **'문구 복사'** + String get shareCopyText; + + /// No description provided for @briefingShareTextCopied. + /// + /// In ko, this message translates to: + /// **'공유 문구를 복사했습니다.'** + String get briefingShareTextCopied; + + /// No description provided for @questAlreadyRewarded. + /// + /// In ko, this message translates to: + /// **'이미 받은 보상입니다(+{pop} POP).'** + String questAlreadyRewarded(String pop); + + /// No description provided for @questCatHealth. + /// + /// In ko, this message translates to: + /// **'건강'** + String get questCatHealth; + + /// No description provided for @questCatPlanet. + /// + /// In ko, this message translates to: + /// **'행성 활동'** + String get questCatPlanet; + + /// No description provided for @questCatCommunity. + /// + /// In ko, this message translates to: + /// **'커뮤니티'** + String get questCatCommunity; + + /// No description provided for @questActShake. + /// + /// In ko, this message translates to: + /// **'쉐이크'** + String get questActShake; + + /// No description provided for @questTypeVote. + /// + /// In ko, this message translates to: + /// **'투표 참여'** + String get questTypeVote; + + /// No description provided for @questTypeSns. + /// + /// In ko, this message translates to: + /// **'SNS'** + String get questTypeSns; + + /// No description provided for @questActivity. + /// + /// In ko, this message translates to: + /// **'활동'** + String get questActivity; + + /// No description provided for @questNoCreatable. + /// + /// In ko, this message translates to: + /// **'지금 만들 수 있는 퀘스트 활동이 없습니다.'** + String get questNoCreatable; + + /// No description provided for @questDescriptionOptional. + /// + /// In ko, this message translates to: + /// **'설명 (선택)'** + String get questDescriptionOptional; + + /// No description provided for @questLinkUrl. + /// + /// In ko, this message translates to: + /// **'링크 주소'** + String get questLinkUrl; + + /// No description provided for @questLinkCheck. + /// + /// In ko, this message translates to: + /// **'링크 확인'** + String get questLinkCheck; + + /// No description provided for @questLinkInvalid. + /// + /// In ko, this message translates to: + /// **'올바른 링크가 아닙니다. 주소를 확인해 주세요.'** + String get questLinkInvalid; + + /// No description provided for @questLinkPreviewFailed. + /// + /// In ko, this message translates to: + /// **'링크 정보를 불러오지 못했어요. 제목을 직접 입력해 주세요.'** + String get questLinkPreviewFailed; + + /// No description provided for @questLinkTitle. + /// + /// In ko, this message translates to: + /// **'링크 제목'** + String get questLinkTitle; + + /// No description provided for @questEndDate. + /// + /// In ko, this message translates to: + /// **'종료일'** + String get questEndDate; + + /// No description provided for @questEndDefault. + /// + /// In ko, this message translates to: + /// **'공개 후 24시간'** + String get questEndDefault; + + /// No description provided for @questEndDateValue. + /// + /// In ko, this message translates to: + /// **'{date} 23:59:59 마감'** + String questEndDateValue(String date); + + /// No description provided for @questPoolHelper. + /// + /// In ko, this message translates to: + /// **'총 {pool} POP 예치 · 수수료 {fee} POP'** + String questPoolHelper(String pool, String fee); + + /// No description provided for @questCreateCost. + /// + /// In ko, this message translates to: + /// **'{title}\n보상 {reward} POP × {count}명 = {pool} POP\n수수료 {fee} POP (공개 시 소멸)\n총 {total} POP 이 차감됩니다.'** + String questCreateCost( + String title, + String reward, + String count, + String pool, + String fee, + String total, + ); + + /// No description provided for @questNoReward. + /// + /// In ko, this message translates to: + /// **'무보상 참여 퀘스트예요.'** + String get questNoReward; + + /// No description provided for @questDeadline. + /// + /// In ko, this message translates to: + /// **'마감'** + String get questDeadline; + + /// No description provided for @questRemainingHours. + /// + /// In ko, this message translates to: + /// **'{hours}시간 남음'** + String questRemainingHours(String hours); + + /// No description provided for @questEnded. + /// + /// In ko, this message translates to: + /// **'마감됨'** + String get questEnded; + + /// No description provided for @questAuthorNote. + /// + /// In ko, this message translates to: + /// **'작성자 설명'** + String get questAuthorNote; + + /// No description provided for @questRepeatOnce. + /// + /// In ko, this message translates to: + /// **'1인 1회 참여'** + String get questRepeatOnce; + + /// No description provided for @questRepeatEvery. + /// + /// In ko, this message translates to: + /// **'{hours}시간 간격 · 최대 {count}회'** + String questRepeatEvery(String hours, String count); + + /// No description provided for @questSubmit. + /// + /// In ko, this message translates to: + /// **'인증 제출'** + String get questSubmit; + + /// No description provided for @questResubmit. + /// + /// In ko, this message translates to: + /// **'보완 제출'** + String get questResubmit; + + /// No description provided for @questEvidenceUrl. + /// + /// In ko, this message translates to: + /// **'인증 링크 (선택)'** + String get questEvidenceUrl; + + /// No description provided for @questEvidenceText. + /// + /// In ko, this message translates to: + /// **'인증 내용'** + String get questEvidenceText; + + /// No description provided for @questSubmitted. + /// + /// In ko, this message translates to: + /// **'제출했어요. 검토 후 보상이 지급돼요.'** + String get questSubmitted; + + /// No description provided for @questReviewPending. + /// + /// In ko, this message translates to: + /// **'검토 중이에요. 승인되면 보상이 지급돼요.'** + String get questReviewPending; + + /// No description provided for @questReviewDelayed. + /// + /// In ko, this message translates to: + /// **'검토가 늦어지고 있어요. 운영팀이 확인하고 있습니다.'** + String get questReviewDelayed; + + /// No description provided for @questApprovedPaying. + /// + /// In ko, this message translates to: + /// **'승인되었어요. 보상을 지급하고 있어요.'** + String get questApprovedPaying; + + /// No description provided for @questRejected. + /// + /// In ko, this message translates to: + /// **'반려: {reason}'** + String questRejected(String reason); + + /// No description provided for @questResubmitAllowed. + /// + /// In ko, this message translates to: + /// **'보완해서 다시 제출할 수 있어요.'** + String get questResubmitAllowed; + + /// No description provided for @questResubmitNotAllowed. + /// + /// In ko, this message translates to: + /// **'이 제출 건은 다시 제출할 수 없어요.'** + String get questResubmitNotAllowed; + + /// No description provided for @questReviewButton. + /// + /// In ko, this message translates to: + /// **'검토 중'** + String get questReviewButton; + + /// No description provided for @questRejectedButton. + /// + /// In ko, this message translates to: + /// **'반려됨'** + String get questRejectedButton; + + /// No description provided for @questReviewQueue. + /// + /// In ko, this message translates to: + /// **'검토 대기 {count}건'** + String questReviewQueue(String count); + + /// No description provided for @questApprove. + /// + /// In ko, this message translates to: + /// **'승인'** + String get questApprove; + + /// No description provided for @questReject. + /// + /// In ko, this message translates to: + /// **'반려'** + String get questReject; + + /// No description provided for @questRejectReason. + /// + /// In ko, this message translates to: + /// **'반려 사유'** + String get questRejectReason; + + /// No description provided for @questAllowResubmit. + /// + /// In ko, this message translates to: + /// **'보완 재제출 허용'** + String get questAllowResubmit; + + /// No description provided for @questEdit. + /// + /// In ko, this message translates to: + /// **'제목 · 설명 수정'** + String get questEdit; + + /// No description provided for @questEditNotice. + /// + /// In ko, this message translates to: + /// **'공개 후에는 제목과 설명만 고칠 수 있고 수정 이력이 남아요.'** + String get questEditNotice; + + /// No description provided for @popReasonQuestUgc. + /// + /// In ko, this message translates to: + /// **'퀘스트 보상'** + String get popReasonQuestUgc; + + /// No description provided for @popReasonQuestFee. + /// + /// In ko, this message translates to: + /// **'퀘스트 개설 수수료'** + String get popReasonQuestFee; + + /// No description provided for @questFreeDailyCap. + /// + /// In ko, this message translates to: + /// **'보상 없는 퀘스트는 하루 {cap}개까지 만들 수 있어요.'** + String questFreeDailyCap(String cap); + + /// No description provided for @popReasonQuestPoolDeposit. + /// + /// In ko, this message translates to: + /// **'퀘스트 보상 예치'** + String get popReasonQuestPoolDeposit; + + /// No description provided for @popReasonQuestPoolReturn. + /// + /// In ko, this message translates to: + /// **'퀘스트 보상 반환'** + String get popReasonQuestPoolReturn; + + /// No description provided for @campaignCommentNeedNickname. + /// + /// In ko, this message translates to: + /// **'닉네임을 먼저 정해 주세요'** + String get campaignCommentNeedNickname; + + /// No description provided for @campaignCommentSetNickname. + /// + /// In ko, this message translates to: + /// **'닉네임 정하기'** + String get campaignCommentSetNickname; +} + +class _AppLocalizationsDelegate + extends LocalizationsDelegate { + const _AppLocalizationsDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture(lookupAppLocalizations(locale)); + } + + @override + bool isSupported(Locale locale) => + ['en', 'es', 'ja', 'ko', 'zh'].contains(locale.languageCode); + + @override + bool shouldReload(_AppLocalizationsDelegate old) => false; +} + +AppLocalizations lookupAppLocalizations(Locale locale) { + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'en': + return AppLocalizationsEn(); + case 'es': + return AppLocalizationsEs(); + case 'ja': + return AppLocalizationsJa(); + case 'ko': + return AppLocalizationsKo(); + case 'zh': + return AppLocalizationsZh(); + } + + throw FlutterError( + 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.', + ); +} diff --git a/app/lib/l10n/app_localizations_en.dart b/app/lib/l10n/app_localizations_en.dart new file mode 100644 index 0000000..2005786 --- /dev/null +++ b/app/lib/l10n/app_localizations_en.dart @@ -0,0 +1,2750 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for English (`en`). +class AppLocalizationsEn extends AppLocalizations { + AppLocalizationsEn([String locale = 'en']) : super(locale); + + @override + String get commonConfirm => 'OK'; + + @override + String get commonCancel => 'Cancel'; + + @override + String get commonClose => 'Close'; + + @override + String get commonSelect => 'Select'; + + @override + String get commonRetry => 'Try again'; + + @override + String get commonView => 'View'; + + @override + String get commonPrev => 'Back'; + + @override + String get commonDone => 'Done'; + + @override + String get commonStart => 'Start'; + + @override + String commonNotYet(String where) { + return 'The $where screen arrives in the second build review (2026-09-10).'; + } + + @override + String get languageSettingsTitle => 'Language'; + + @override + String get authLogin => 'Log in'; + + @override + String get authSignup => 'Sign up'; + + @override + String get authFirstTime => 'New to FANUZU?'; + + @override + String get authAlreadyMember => 'Already have an account?'; + + @override + String get authLoginHeadline => 'Log in to FANUZU\nthe easiest way!'; + + @override + String get authSignupHeadline => 'Join FANUZU\nthe easiest way!'; + + @override + String authProviderLogin(String provider) { + String _temp0 = intl.Intl.selectLogic(provider, { + 'apple': 'Log in with Apple', + 'google': 'Log in with Google', + 'kakao': 'Log in with Kakao', + 'line': 'Log in with LINE', + 'other': 'Log in with email', + }); + return '$_temp0'; + } + + @override + String authProviderSignup(String provider) { + String _temp0 = intl.Intl.selectLogic(provider, { + 'apple': 'Sign up with Apple', + 'google': 'Sign up with Google', + 'kakao': 'Sign up with Kakao', + 'line': 'Sign up with LINE', + 'other': 'Sign up with email', + }); + return '$_temp0'; + } + + @override + String authNotConfiguredTitle(String name) { + return '$name login coming soon'; + } + + @override + String authNotConfiguredBody(String name, String items) { + return '$name login opens once the client keys are provided.\n\nRequired\n· $items'; + } + + @override + String authNotConfiguredNotice(String name, String items) { + return '$name login is not ready yet.\nRequired: $items'; + } + + @override + String get authResetNeedEmail => + 'Enter the email address for the reset link first.'; + + @override + String get authResetSent => 'We sent reset instructions to that address.'; + + @override + String get termsTitle => 'Terms of use'; + + @override + String get termsAgreeAll => 'Agree to all'; + + @override + String get termsService => 'Terms of Service (required)'; + + @override + String get termsLocation => 'Location-based Service Terms (required)'; + + @override + String get termsAge => 'I am 14 or older. (required)'; + + @override + String get termsMarketing => + 'Receive marketing and event notifications (optional)'; + + @override + String get signupDoneTitle => 'You\'re all signed up'; + + @override + String get signupDoneBody => + 'You can start your fandom activities.\nGrow your fandom planet through all kinds of activities.'; + + @override + String get homeItemsTooltip => 'Items'; + + @override + String get homeNotificationsTooltip => 'Notifications'; + + @override + String get homeFandomHistory => 'Fandom history'; + + @override + String get homePopularInside => 'Popular K-INSIDE'; + + @override + String get homeWhereBriefing => 'AI Briefing'; + + @override + String get homeWhereInsideList => 'INSIDE list'; + + @override + String get homeWhereInsideDetail => 'INSIDE detail'; + + @override + String get homeLoadFailed => 'Couldn\'t load Home'; + + @override + String get homeRetryLater => 'Please try again in a moment.'; + + @override + String get myProfileCardTooltip => 'Profile card'; + + @override + String get myTabPlanets => 'Planets/Nebulae'; + + @override + String get myTabActivity => 'Activity'; + + @override + String get myInside => 'Inside'; + + @override + String get myCampaign => 'Campaigns'; + + @override + String myShareLater(String target) { + return '$target sharing will be connected in the 09-10 build.'; + } + + @override + String get myEditPhoto => 'Edit profile photo'; + + @override + String get myEditProfile => 'Edit profile'; + + @override + String get settingsTitle => 'Settings'; + + @override + String get settingsAccount => 'Account'; + + @override + String get settingsNickname => 'Nickname'; + + @override + String get settingsEmail => 'Email'; + + @override + String get settingsSns => 'Linked social accounts'; + + @override + String get settingsPopHistory => 'POP history'; + + @override + String get settingsPayments => 'Payments'; + + @override + String get settingsServicePayments => 'Service payment history'; + + @override + String get settingsService => 'Service settings'; + + @override + String get settingsPush => 'Push notifications'; + + @override + String get settingsBlocked => 'Blocked users'; + + @override + String get settingsInfo => 'About'; + + @override + String get settingsNotice => 'Notices'; + + @override + String get settingsInquiry => '1:1 inquiry'; + + @override + String get settingsAppVersion => 'App version'; + + @override + String get settingsLatest => 'Latest'; + + @override + String get settingsLogout => 'Log out'; + + @override + String get settingsLogoutConfirm => 'Do you want to log out?'; + + @override + String get settingsWithdraw => 'Delete account'; + + @override + String get settingsWithdrawConfirm => 'Do you want to delete your account?'; + + @override + String get settingsWithdrawBody => + 'Your POP will be lost and this cannot be undone.'; + + @override + String get settingsWithdrawLater => + 'Account deletion will be connected in the 09-10 build.'; + + @override + String get settingsUnlinkConfirm => 'Unlink this account?'; + + @override + String get settingsUnlinkBody => + 'Only this social login method is removed. Your account data is not deleted.'; + + @override + String get settingsUnlink => 'Unlink'; + + @override + String get settingsSameEmailOnly => + 'Only social accounts with the same\nemail as your account can be linked.'; + + @override + String settingsLinkNotReady(String name, String items) { + return '$name linking is not ready yet. Required: $items'; + } + + @override + String get settingsConnect => 'Link'; + + @override + String get settingsDisconnect => 'Unlink'; + + @override + String get settingsEmailProvider => 'Email'; + + @override + String get pushDeviceOff => + 'Push notifications are turned off on this device.'; + + @override + String get pushDeviceOffHint => + 'Allow notifications in system settings to receive them.'; + + @override + String get pushRequesting => 'Requesting…'; + + @override + String get pushGoToSettings => 'Open settings'; + + @override + String get pushOpeningDeviceSettings => 'Opening device settings.'; + + @override + String get pushEnableInDevice => + 'Turn on FANUZU notifications in device settings to receive them.'; + + @override + String get pushDeviceSection => 'Device notifications'; + + @override + String get pushSoundVibration => 'Sound & vibration'; + + @override + String get pushServiceSection => 'Service notifications'; + + @override + String get pushTopicEssential => 'Essential'; + + @override + String get pushTopicFandomEvent => 'Fandom events'; + + @override + String get pushTopicBriefing => 'AI Briefing'; + + @override + String get pushTopicCommunity => 'Community'; + + @override + String get pushTopicMarketing => 'Marketing'; + + @override + String linkTitle(String name) { + return '$name link'; + } + + @override + String get linkNotLinked => 'Not linked'; + + @override + String get linkLinked => 'Linked'; + + @override + String get linkSameEmailOnly => + 'Only social accounts with the same email as your account can be linked.'; + + @override + String get linkUnlink => 'Unlink'; + + @override + String get linkLink => 'Link'; + + @override + String get noticeBadge => 'Notice'; + + @override + String get noticeTitle => 'Notices'; + + @override + String get noticeEmpty => 'No notices yet.'; + + @override + String get noticeDetailTitle => 'Notice'; + + @override + String get translationMark => 'Translated'; + + @override + String translationReplyTo(String name) { + return 'Translating $name\'s comment'; + } + + @override + String get translationWrite => 'Write a translation'; + + @override + String get translationHint => 'Enter your translation'; + + @override + String get translationValid => 'Valid'; + + @override + String get translationInvalid => 'Invalid'; + + @override + String get translationSelfMarkBlocked => + 'You can\'t mark your own translation.'; + + @override + String get translationSubmitted => + 'Translation posted. If it passes review, POP is paid after 24 hours.'; + + @override + String get translationMarked => 'Marked.'; + + @override + String get translationAlreadyMarked => 'You already marked this translation.'; + + @override + String get translationDuplicate => + 'You already posted a translation in this language.'; + + @override + String get translationFailed => + 'Something went wrong. Please try again shortly.'; + + @override + String get translationNotEligible => + 'Only members of this fandom whose account is at least 24 hours old can mark.'; + + @override + String get translationDailyCap => 'You can\'t post more translations today.'; + + @override + String get translationInvalidTarget => 'This content can\'t be translated.'; + + @override + String get commonRequestFailed => 'Couldn\'t process the request.'; + + @override + String get commonFan => 'Fan'; + + @override + String get adLoadFailed => + 'Couldn\'t load the ad. You\'ll get the basic reward only.'; + + @override + String get storeNotReady => 'Store items coming soon'; + + @override + String get storePending => + 'Waiting for payment approval. It will apply automatically once approved.'; + + @override + String get storeFailed => 'Couldn\'t complete the payment.'; + + @override + String get storePurchased => 'Your purchase has been applied.'; + + @override + String get heroParticipate => 'ZAP+ · Join the gift'; + + @override + String heroRemainingSemantics(String minutes, String seconds) { + return 'HERO TIME remaining: $minutes min $seconds sec'; + } + + @override + String heroRemaining(String time) { + return 'HERO TIME · $time left'; + } + + @override + String heroClaims(String count) { + return 'Gifts claimed by fans: $count'; + } + + @override + String heroInProgress(String name) { + return '$name · HERO TIME in progress →'; + } + + @override + String get stageNames => + 'Nebula,Start,Growth,Together,Journey,Light,Eternity'; + + @override + String get planetStageUnknown => 'Planet · No stage info'; + + @override + String get planetHibernatingSuffix => ' · Dormant'; + + @override + String get planetEclipseSuffix => ' · Eclipse in progress'; + + @override + String get planetHeroSuffix => ' · HERO TIME in progress'; + + @override + String get evoPlanetGrowth => 'Planet growth'; + + @override + String get evoHeroEnd => 'HERO TIME ended'; + + @override + String get evoAwakening => 'The planet is awakening'; + + @override + String get evoAnomaly => 'Anomaly detected'; + + @override + String get evoEnergy => 'Gathering energy'; + + @override + String get evoTransition => 'Transforming'; + + @override + String get evoComplete => 'Planet evolution complete'; + + @override + String get evoOpenHistory => 'View PASSPORT history'; + + @override + String get playNow => 'Play now'; + + @override + String get playHeadline => 'Let\'s go meet your fandom!'; + + @override + String get playBody => + 'Create a planet, connect with fandoms, and enjoy fan activities...'; + + @override + String get timeJustNow => 'Just now'; + + @override + String timeMinutesAgo(String n) { + return '${n}m ago'; + } + + @override + String timeHoursAgo(String n) { + return '${n}h ago'; + } + + @override + String timeDaysAgo(String n) { + return '${n}d ago'; + } + + @override + String get commonMore => 'More'; + + @override + String authProviderStart(String provider) { + String _temp0 = intl.Intl.selectLogic(provider, { + 'email': 'Continue with email', + 'kakao': 'Continue with Kakao', + 'line': 'Continue with LINE', + 'apple': 'Continue with Apple', + 'other': 'Continue with Google', + }); + return '$_temp0'; + } + + @override + String authProviderName(String provider) { + String _temp0 = intl.Intl.selectLogic(provider, { + 'email': 'Email', + 'kakao': 'Kakao', + 'line': 'LINE', + 'apple': 'Apple', + 'other': 'Google', + }); + return '$_temp0'; + } + + @override + String get authLastProvider => 'You can\'t unlink your last sign-in method.'; + + @override + String get authLoginRequired => 'Please sign in.'; + + @override + String get authDifferentEmail => + 'This account doesn\'t match your sign-up email.'; + + @override + String get authLinkFailed => 'Couldn\'t link the account.'; + + @override + String get authNotLinked => 'This account isn\'t linked.'; + + @override + String get authEmailUnavailable => 'Email sign-in is unavailable.'; + + @override + String get authCheckCredentials => 'Please check your email or password.'; + + @override + String get authSocialFailed => 'Social sign-in failed.'; + + @override + String get authAppleUnavailable => + 'Sign in with Apple isn\'t available on this device.'; + + @override + String get authEmailInUse => 'This email is already registered.'; + + @override + String get authWeakPassword => + 'Password is too short. Use at least 8 characters.'; + + @override + String get authInvalidEmail => 'Invalid email format.'; + + @override + String get commonTryLater => 'Please try again later.'; + + @override + String get authCredentialInUse => + 'This social account is already linked to another FANUZU account.'; + + @override + String get authAlreadyLinked => 'This account is already linked.'; + + @override + String get authMethodUnavailable => + 'This sign-in method isn\'t available yet.'; + + @override + String get authRecentLogin => 'For security, please sign in again and retry.'; + + @override + String authOtherProviderGuide(String email) { + return 'The $email account was registered with a different sign-in method. Sign in with the method you first used, then link this one in MY > Settings > Linked accounts.'; + } + + @override + String get authThisAccount => 'This'; + + @override + String get eclipseCapped => + 'You reached the participation cap, so some shakes weren\'t counted.'; + + @override + String get eclipseResultUnknown => + 'Couldn\'t confirm your participation. Please check again with the same request.'; + + @override + String get eclipseChecking => 'Checking participation'; + + @override + String get eclipseRetrySame => 'Recheck same participation'; + + @override + String get eclipseEnded => + 'The eclipse has already ended or was successfully defended.'; + + @override + String get eclipseTooFast => + 'You\'re participating too fast. Please try again shortly.'; + + @override + String get eclipseNone => 'No eclipse in progress'; + + @override + String get eclipseSuccessTitle => 'You stopped\nthe meteor impact!'; + + @override + String get eclipseQualified => 'Participation confirmed'; + + @override + String get eclipseFailTitle => 'The planet defense\nfailed this time'; + + @override + String get eclipseFailBody => 'This round\'s defense goal wasn\'t reached.'; + + @override + String get eclipseCancelTitle => 'Eclipse cancelled'; + + @override + String get eclipseCancelBody => + 'Cancelled due to a tally error. No FPI was deducted.'; + + @override + String get eclipsePendingTitle => 'Checking defense result'; + + @override + String get eclipsePendingBody => + 'Participation time is over. Waiting for the server\'s decision.'; + + @override + String get eclipseShakeTimed => + 's — shake your phone\nto stop the meteor impact!'; + + @override + String get eclipseShake => 'Shake your phone\nto stop the meteor impact!'; + + @override + String eclipseMyCount(String count) { + return 'My shakes: $count'; + } + + @override + String eclipsePendingSuffix(String count) { + return ' · +$count pending'; + } + + @override + String get eclipseSendingSuffix => ' · Sending'; + + @override + String eclipseNoPopNote(String count) { + return 'No POP is spent. Make at least $count valid shakes to count as a participant.'; + } + + @override + String get eclipseTapToJoin => 'Can\'t shake? Tap to join'; + + @override + String get eclipseTimeLeft => 'Eclipse time left:'; + + @override + String eclipseSeconds(String seconds) { + return ' ${seconds}s'; + } + + @override + String eclipseBannerDefended(String name) { + return 'Defended — no FPI loss for $name'; + } + + @override + String eclipseBannerActive(String name, String rate) { + return 'Eclipse triggered — $name erosion $rate%'; + } + + @override + String eclipseBannerDefense(String count, String target) { + return 'Valid participants $count / $target'; + } + + @override + String timeHoursMinutesLeft(String hours, String minutes) { + return '${hours}h ${minutes}m left'; + } + + @override + String timeMinutesLeft(String minutes) { + return '${minutes}m left'; + } + + @override + String liveEclipseDefense(String name, String count) { + return '[LIVE] $name joined the eclipse defense! (+$count)'; + } + + @override + String liveHotTime(String name) { + return '[LIVE] $name\'s Artist Hot Time has started!'; + } + + @override + String liveHeroGift(String name) { + return '[LIVE] A Hero\'s Gift from $name has arrived!'; + } + + @override + String liveCampaign(String name) { + return '[LIVE] $name backed a campaign!'; + } + + @override + String get galaxySetPrimaryConfirm => + 'Set the selected planet as your main planet?'; + + @override + String get galaxyLoadFailed => 'Couldn\'t load the galaxy'; + + @override + String get commonSearch => 'Search'; + + @override + String get galaxyMyPlanets => 'My planets'; + + @override + String get galaxyMap => 'Galaxy map'; + + @override + String get nebulaCreate => 'Create nebula'; + + @override + String get commonRecheck => 'Check again'; + + @override + String get zapApplying => 'Applying contribution'; + + @override + String get zapNeedsCheck => 'Contribution result needs checking'; + + @override + String get zapNoPlanet => 'You don\'t belong to a planet'; + + @override + String get commonContribute => 'Contribute'; + + @override + String get commonItem => 'Items'; + + @override + String get galaxyDefended => 'Defended · No FPI loss'; + + @override + String galaxyErosion(String rate, String progress) { + return 'Erosion $rate% · Defense $progress%'; + } + + @override + String galaxyOnlineMembers(String online, String members) { + return '$online online · $members members'; + } + + @override + String get galaxySearchHint => 'Search by fandom name'; + + @override + String get commonRecentSearches => 'Recent searches'; + + @override + String get commonClearAll => 'Clear all'; + + @override + String get commonNoRecentSearches => 'No recent searches.'; + + @override + String get galaxyNoHit => 'No fandom found.\nPlease check the name.'; + + @override + String galaxyMembers(String count) { + return '$count members'; + } + + @override + String galaxyPlanetRank(String name, String rank) { + return '$name planet, rank $rank'; + } + + @override + String get galaxyMapOpen => 'Open galaxy map'; + + @override + String get zapNotEnoughPop => 'Not enough POP'; + + @override + String get zapFailNoPop => 'Not enough POP to contribute.'; + + @override + String get zapFailLedger => + 'Contribution stopped: balance and earnings history don\'t match.'; + + @override + String get zapFailRetry => + 'Check your connection and recheck the same contribution.'; + + @override + String get zapDone => 'Contributed'; + + @override + String zapDonePop(String pop) { + return 'Contributed $pop POP'; + } + + @override + String get growthFounders => 'Founding members'; + + @override + String get growthNebulaGoalPassed => + 'The goal period has passed · Meet the conditions and the planet will be born'; + + @override + String get growthFinalStage => 'Final stage'; + + @override + String get growthPromotionPending => 'Awaiting promotion'; + + @override + String get growthCheckConditions => 'Check growth conditions'; + + @override + String growthConditionChecking(String name) { + return 'Checking $name condition'; + } + + @override + String growthConditionMet(String name) { + return '$name condition met'; + } + + @override + String growthConditionUnmet(String name) { + return '$name condition not met'; + } + + @override + String get nebulaHeadline => 'Name your fandom planet.'; + + @override + String nebulaBody(String days) { + return 'If founding members reach the POP goal within $days days,\nit\'s born as an official planet!\nMembers who joined the evolution get a commemorative badge.'; + } + + @override + String get nebulaNameTaken => 'This name is already taken.'; + + @override + String get nebulaNameHint => 'Fandom name'; + + @override + String get nebulaGoal => 'Goal '; + + @override + String get nebulaFounders => ' / Founders'; + + @override + String nebulaGoalSemantics(String pop, String members, String days) { + return 'Goal $pop POP · $members founding members · fixed $days days'; + } + + @override + String get commonMyPop => 'My POP: '; + + @override + String get commonChange => 'Change'; + + @override + String planetShape(String n) { + return 'Shape $n'; + } + + @override + String get planetBgColor => 'Choose background color'; + + @override + String get planetColorSelect => 'Choose planet color'; + + @override + String get planetTypeSelect => 'Choose planet type'; + + @override + String get commonNext => 'Next'; + + @override + String get planetCreateFailed => + 'Couldn\'t create the planet. Please try again later.'; + + @override + String get planetCreateConfirm => 'Create a planet\nwith these options?'; + + @override + String get commonCreate => 'Create'; + + @override + String get briefingOpenFailed => 'Couldn\'t open the original.'; + + @override + String campaignAchieved(String percent) { + return '$percent% reached'; + } + + @override + String get campaignView => 'View campaign'; + + @override + String get growthFinalReached => 'You\'ve reached the final stage'; + + @override + String get zapRetrySame => 'Recheck same contribution'; + + @override + String get heroOwnerSuffix => '\'s'; + + @override + String get heroStartPrompt => + 'Create or join a planet\nand start your FANUZU journey!'; + + @override + String get heroNebulaPrompt => + 'Use the POP you earn from activities\nto contribute to the nebula!'; + + @override + String heroRecentContribution(String pop) { + return 'Growing steadily with\n$pop POP contributed.'; + } + + @override + String get heroGrowingTogether => 'A fandom planet we grow together'; + + @override + String get heroPopProgress => 'POP progress'; + + @override + String get heroNebula => 'Nebula'; + + @override + String get heroNebulaTap => 'Tap the nebula\nto give birth to a planet!'; + + @override + String get heroCreateNebula => 'Create planet (nebula)'; + + @override + String get heroJoinPlanet => 'Join a planet'; + + @override + String get heroCreateOrJoin => 'Create or join a planet'; + + @override + String insideAnswers(String count) { + return 'Answers $count'; + } + + @override + String get itemJoinFirst => 'Join a planet to send a Hero\'s Gift.'; + + @override + String get itemBuyNotReady => 'Item purchase is coming soon.'; + + @override + String get itemUseNotReady => 'Item use is coming soon.'; + + @override + String get payOpenFailed => 'Couldn\'t open the payment window.'; + + @override + String get itemBuy => 'Buy items'; + + @override + String get itemSelect => 'Choose an item'; + + @override + String get commonComingSoon => 'Coming soon.'; + + @override + String itemSendPop(String pop) { + return 'Send $pop POP'; + } + + @override + String itemBuyerPop(String pop) { + return 'Buyer +$pop POP'; + } + + @override + String itemOwned(String count) { + return 'Owned: $count'; + } + + @override + String itemOwnedCount(String count) { + return 'Owned: $count'; + } + + @override + String get growthOwnerOf => '\'s '; + + @override + String get growthSteadySuffix => ' contributed — growing steadily.'; + + @override + String get questAttended => 'Checked in'; + + @override + String get questInProgressSpaced => 'In progress'; + + @override + String get questRewardPaid => 'Reward paid'; + + @override + String get questClaim => 'Claim reward'; + + @override + String get questStart => 'Start quest'; + + @override + String get questRewardDone => 'Reward claimed'; + + @override + String get popKindGeneral => 'Regular POP'; + + @override + String get popKindContribution => 'Contribution POP'; + + @override + String get popTxGrant => 'Granted'; + + @override + String get popTxSpend => 'Spent'; + + @override + String get popTxRefund => 'Refund'; + + @override + String get popTxVoid => 'Voided'; + + @override + String get popTypeQuest => 'Quest'; + + @override + String get popTypeReward => 'Reward'; + + @override + String get popTypePayment => 'Payment'; + + @override + String get popTypeSubscription => 'Subscription'; + + @override + String get popTypeAdmin => 'Admin'; + + @override + String get popTypeFee => 'Fee'; + + @override + String get commonEtc => 'Other'; + + @override + String get popReasonQuestSteps => 'Step counter quest'; + + @override + String get popReasonQuestMusicShake => 'Music Shake quest'; + + @override + String get popReasonQuestPlace => 'Place visit quest'; + + @override + String get popReasonQuestAttend => 'Check-in quest'; + + @override + String get popReasonAdReward => 'Ad reward'; + + @override + String get popReasonHeroDrop => 'Hero\'s Gift'; + + @override + String get popReasonHeroDropTrigger => 'Hero\'s Gift sent'; + + @override + String get popReasonTranslation => 'Community translation'; + + @override + String get popReasonIapPurchase => 'Purchase'; + + @override + String get popReasonFocusDaily => 'Focus daily reward'; + + @override + String get popReasonPreregBonus => 'Pre-registration reward'; + + @override + String get popReasonReferral => 'Friend invite'; + + @override + String get popTypeReferral => 'Referral'; + + @override + String get popReasonAdminGrant => 'Admin grant'; + + @override + String get popReasonAdminClawback => 'Admin clawback'; + + @override + String get popReasonCampaignFund => 'Campaign support'; + + @override + String get popReasonCampaignRefund => 'Campaign refund'; + + @override + String get popReasonCampaignFeeRefund => 'Campaign fee refund'; + + @override + String get popReasonCampaignFee => 'Campaign fee'; + + @override + String get popReasonQnaBounty => 'INSIDE bounty'; + + @override + String get popReasonQnaRefund => 'Bounty refund'; + + @override + String get popReasonQnaReward => 'INSIDE accepted-answer reward'; + + @override + String get popReasonEclipseDefense => 'Zap contribution'; + + @override + String get popReasonOther => 'POP change'; + + @override + String popRepeat(String title, String count) { + return '$title ×$count'; + } + + @override + String get payStatePaid => 'Paid'; + + @override + String get payStateRefundRequested => 'Refund requested'; + + @override + String get payStateRefundApproved => 'Refund approved'; + + @override + String get payStateRefunded => 'Refunded'; + + @override + String get payStateRefundRejected => 'Refund rejected'; + + @override + String get pushTopicEssentialDesc => + 'Account, payment, and maintenance notices. These can\'t be turned off.'; + + @override + String get pushTopicFandomEventDesc => + 'Eclipse, campaign, quest, and Artist Hot Time news.'; + + @override + String get pushTopicBriefingDesc => + 'Daily briefing cards summarizing your planet\'s day.'; + + @override + String get pushTopicCommunityDesc => + 'Comments, likes, and answers on your posts.'; + + @override + String get pushTopicMarketingDesc => + 'Get event and benefit info. Only on if you agree.'; + + @override + String get inquiryPending => 'Received'; + + @override + String get inquiryInProgress => 'In progress'; + + @override + String get inquiryAnswered => 'Answered'; + + @override + String get photoBadType => + 'Only JPG, PNG, WEBP, or GIF images can be uploaded.'; + + @override + String get photoTooBig => + 'The photo is over 10MB. Please choose a smaller one.'; + + @override + String get photoUnreadable => + 'Couldn\'t read the photo. Please choose again.'; + + @override + String get myListInside => 'My INSIDE'; + + @override + String get myListInsideEmpty => 'You haven\'t posted to INSIDE yet.'; + + @override + String get myListCampaign => 'My campaigns'; + + @override + String get myListCampaignEmpty => 'You haven\'t created a campaign yet.'; + + @override + String get sortLatest => 'Latest'; + + @override + String get sortPop => 'By POP'; + + @override + String get sortSolved => 'Accepted'; + + @override + String get sortAchieved => 'Achieved'; + + @override + String get payNotFound => 'Payment not found.'; + + @override + String get payItemFallback => 'Purchased item'; + + @override + String get payMethodInApp => 'In-app purchase'; + + @override + String get unknownUser => 'Unknown user'; + + @override + String get blockSelf => 'You can\'t block yourself.'; + + @override + String get inquiryFallback => 'Inquiry'; + + @override + String get photoRejectedServer => + 'Couldn\'t upload the photo. Check its format and size.'; + + @override + String get photoUploadFailed => + 'Photo upload failed. Please try again later.'; + + @override + String focusClaimed(String pop) { + return 'You got today\'s Focus reward: +$pop Contribution POP.'; + } + + @override + String get focusActive => 'Ad-free Focus mode active'; + + @override + String focusUntil(String date) { + return 'Until $date · Today\'s reward can only be claimed today'; + } + + @override + String get focusClaimedToday => 'Claimed today'; + + @override + String get focusClaimToday => 'Claim today\'s reward'; + + @override + String get storeConnectFailed => + 'Couldn\'t connect to the store. Please try again later.'; + + @override + String get payRestore => 'Restore purchases'; + + @override + String get payEmpty => 'No payment history.'; + + @override + String get payRefundGuide => + 'Consumables: refundable within 7 days of purchase if unused. Items that have been partly used are not refundable.\nSubscriptions: refundable within 24 hours of payment if no benefits (e.g., bonus rewards) were used.\nRefunds are completed within 7–14 days after approval.'; + + @override + String get payDetailTitle => 'Payment details'; + + @override + String get payNumber => 'Payment no.'; + + @override + String get payDate => 'Payment date'; + + @override + String get payMethod => 'Payment method'; + + @override + String get payStatus => 'Payment status'; + + @override + String get payAmount => 'Amount'; + + @override + String get payRefundInfo => 'Refund policy'; + + @override + String get payRefundApply => 'Request refund'; + + @override + String get payRefundUnavailable => 'Not refundable'; + + @override + String get refundReasonMistake => 'I paid by mistake'; + + @override + String get refundReasonExpectation => 'Not what I expected'; + + @override + String get refundReasonError => 'Couldn\'t use it due to an error'; + + @override + String get refundReasonDuplicate => 'Duplicate payment'; + + @override + String get refundTitle => 'Refund'; + + @override + String get refundSubmit => 'Submit'; + + @override + String get refundReason => 'Reason for refund'; + + @override + String get refundConfirm => 'Request a refund?'; + + @override + String get myPop => 'My POP'; + + @override + String get commonNoHistory => 'No history yet.'; + + @override + String get nicknameEdit => 'Edit nickname'; + + @override + String get nicknameReserved => + 'You can\'t use a name that could be mistaken for staff.'; + + @override + String get nicknameTaken => 'This nickname is already in use.'; + + @override + String get albumItems => 'Photo options'; + + @override + String get takePhoto => 'Take photo'; + + @override + String get pickFromAlbum => 'Choose from album'; + + @override + String get tierGuide => 'Tier guide'; + + @override + String get statSteps => 'Total steps'; + + @override + String get statContribution => 'Total contribution POP'; + + @override + String get statQuests => 'Completed quests'; + + @override + String get shareInstagram => 'Instagram'; + + @override + String get shareTiktok => 'TikTok'; + + @override + String get shareCopyLink => 'Copy link'; + + @override + String get inquiryListTitle => '1:1 inquiries'; + + @override + String get inquiryEmpty => 'You have no inquiries.'; + + @override + String get inquiryWrite => 'Write inquiry'; + + @override + String get inquiryDetail => 'Inquiry details'; + + @override + String get commonEdit => 'Edit'; + + @override + String get inquiryAnswer => 'Answer'; + + @override + String get inquiryNew => 'New inquiry'; + + @override + String get inquiryEdit => 'Edit inquiry'; + + @override + String get commonTitle => 'Title'; + + @override + String get commonTitleHint => 'Enter a title.'; + + @override + String get inquiryBody => 'Details'; + + @override + String get inquiryBodyHint => 'Describe your inquiry.'; + + @override + String get inquiryBodyHelper => + 'Writing 10+ characters helps us assist you faster.'; + + @override + String inquirySubmitFailed(String reason) { + return 'Couldn\'t submit your inquiry. $reason'; + } + + @override + String inquiryEditFailed(String reason) { + return 'Couldn\'t update your inquiry. $reason'; + } + + @override + String get inquirySubmitted => 'Inquiry submitted.'; + + @override + String get inquiryEdited => 'Inquiry updated.'; + + @override + String get blockedEmpty => 'No blocked accounts.'; + + @override + String get unblock => 'Unblock'; + + @override + String unblockFailed(String reason) { + return 'Couldn\'t unblock. $reason'; + } + + @override + String get tierInfoTitle => 'TIER info'; + + @override + String get tierPromptPrefix => 'Raise your '; + + @override + String get tierPromptRank => 'rank'; + + @override + String get tierPromptSuffix => '\nby contributing to planets and nebulae'; + + @override + String get tierPassportNote => + 'Passport Tier is a card appearance grade separate from your level, and it never goes down.'; + + @override + String get tierTotalContribution => 'Total contribution'; + + @override + String get notifKindEclipse => 'Eclipse'; + + @override + String get notifKindItemUsed => 'Item used'; + + @override + String get notifKindLike => 'Likes'; + + @override + String get notifKindComment => 'Comments'; + + @override + String get notifKindCampaign => 'Campaign goal'; + + @override + String get notifKindQuest => 'Quest complete'; + + @override + String get notifTargetPost => 'post'; + + @override + String get notifTargetCampaign => 'campaign'; + + @override + String get notifTargetQuest => 'quest details'; + + @override + String get dayToday => 'Today'; + + @override + String get dayYesterday => 'Yesterday'; + + @override + String get notifEmpty => 'No notifications.'; + + @override + String notifGoTo(String target) { + return 'Going to $target.'; + } + + @override + String get stationOfficial => 'Official'; + + @override + String get stationShop => 'Shops'; + + @override + String get stationTicket => 'Tickets'; + + @override + String get stationMedia => 'Media'; + + @override + String get stationLoadFailed => 'Couldn\'t load STATION'; + + @override + String get commonAll => 'All'; + + @override + String get stationSuggest => 'Suggest a shop'; + + @override + String get stationEmpty => + 'No links yet.\nJoin a planet to see curated links.'; + + @override + String get stationCategoryEmpty => 'No links in this category yet.'; + + @override + String get stationSuggestLater => + 'Suggestions open once the server is connected.'; + + @override + String get commonSubmit => 'Submit'; + + @override + String get commonName => 'Name'; + + @override + String get commonNameRequired => 'Please enter a name.'; + + @override + String get commonUrl => 'URL'; + + @override + String get commonUrlRequired => 'Please enter a URL.'; + + @override + String get commonUrlHttps => 'Please enter a URL starting with https.'; + + @override + String get commonCategory => 'Category'; + + @override + String get stationReason => 'Why you recommend it (optional)'; + + @override + String get stationSuggestNote => + 'Suggested links are added after review. We\'ll notify you of the result.'; + + @override + String get heroGiftUnavailable => 'This gift can\'t be claimed right now.'; + + @override + String get heroGiftAlready => + 'You\'ve already joined this gift. Check your POP history.'; + + @override + String get heroGiftRelogin => 'Please sign in again to join.'; + + @override + String get heroGiftNoPermission => + 'You don\'t have permission to join this gift.'; + + @override + String get heroGiftHowTo => + 'Shake your phone or tap the button below to join the light energy gift.'; + + @override + String get heroGiftPopNote => + 'POP you can receive depends on the remaining gift and the per-person cap.'; + + @override + String heroGiftReceived(String total) { + return '$total POP received'; + } + + @override + String get heroGiftUnknown => + 'Couldn\'t confirm the payout. Please check again.'; + + @override + String get heroGiftNone => 'No gifts to join right now.'; + + @override + String get heroGiftConfirmed => 'Your gift claim is confirmed.'; + + @override + String get heroGiftChecking => 'Confirming payout…'; + + @override + String get heroGiftRetry => 'Recheck same gift'; + + @override + String get heroGiftTap => 'Join with a button instead'; + + @override + String rankMyPlanet(String metric) { + return 'My planet\'s $metric ranking'; + } + + @override + String get rankPlanetEmpty => 'No planet rankings yet'; + + @override + String get rankScopeLive => 'Live'; + + @override + String get rankScopeCountry => 'By country'; + + @override + String rankMine(String metric) { + return 'My $metric ranking'; + } + + @override + String rankMyCountry(String metric) { + return 'My country\'s $metric ranking'; + } + + @override + String get rankEmpty => 'No rankings yet'; + + @override + String rankScope(String scope) { + return '$scope ranking'; + } + + @override + String get rankLoadFailed => 'Couldn\'t load the ranking'; + + @override + String rankMeSuffix(String name) { + return '$name (me)'; + } + + @override + String get reportDefamation => 'Artist slander or defamation'; + + @override + String get reportInappropriate => 'Inappropriate content'; + + @override + String get reportSpam => 'Spam or commercial ads'; + + @override + String get reportPrivacy => 'Personal info exposure or impersonation'; + + @override + String get reportTitle => 'Report'; + + @override + String get reportSubmit => 'Report'; + + @override + String get reportAuthor => 'Author'; + + @override + String get reportReasonLabel => 'Reason'; + + @override + String get reportConfirm => + 'Report this post?\nThis can\'t be undone once submitted.'; + + @override + String get blockMemberConfirm => 'Block all posts from this member?'; + + @override + String get loungeMembersOnly => + 'Only members of this planet\'s fandom can post, comment, or like.'; + + @override + String get loungeFailed => 'Couldn\'t complete that. Please try again later.'; + + @override + String get loungeTitle => 'Fandom Lounge'; + + @override + String get likeCancel => 'Unlike'; + + @override + String get like => 'Like'; + + @override + String get commentDeleteConfirm => 'Delete this comment?'; + + @override + String get commonShare => 'Share'; + + @override + String get commonEditAction => 'Edit'; + + @override + String get commonDeleteAction => 'Delete'; + + @override + String get commonBlockAction => 'Block'; + + @override + String get commentFirst => 'Be the first to comment.'; + + @override + String get postDeleteConfirm => 'Delete this post?'; + + @override + String get reportReceived => 'Report received.'; + + @override + String get commonDelete => 'Delete'; + + @override + String get commentHint => 'Write a comment.'; + + @override + String get commonSend => 'Send'; + + @override + String get postWrite => 'Write post'; + + @override + String get postEdit => 'Edit post'; + + @override + String get loungeHint => 'Write freely in the Fandom Lounge.'; + + @override + String photoRemove(String n) { + return 'Remove photo $n'; + } + + @override + String get camera => 'Camera'; + + @override + String get album => 'Album'; + + @override + String get historyTitle => 'PASSPORT · History'; + + @override + String get historyMine => 'My activity'; + + @override + String historyEvolution(String from, String to) { + return 'Evolved Stage $from → $to'; + } + + @override + String get historyHibernation => 'Planet dormant'; + + @override + String get historyWakeUp => 'Planet awakened'; + + @override + String get historyZap => 'ZAP contribution'; + + @override + String get historyQuest => 'Quest reward'; + + @override + String get historyAdBonus => 'Ad bonus reward'; + + @override + String get historyFunding => 'Campaign contribution'; + + @override + String historyGrowthPop(String amount) { + return '$amount POP added to growth'; + } + + @override + String get historyJoined => 'You were part of this evolution'; + + @override + String get historyLoadFailed => 'Couldn\'t load history.'; + + @override + String get historyMineEmpty => 'No activity yet.'; + + @override + String get historyFandomEmpty => 'No fandom history yet.'; + + @override + String get historyMore => 'Load earlier history'; + + @override + String countryName(String code) { + String _temp0 = intl.Intl.selectLogic(code, { + 'KR': 'South Korea', + 'JP': 'Japan', + 'US': 'United States', + 'CN': 'China', + 'TW': 'Taiwan', + 'HK': 'Hong Kong', + 'TH': 'Thailand', + 'VN': 'Vietnam', + 'ID': 'Indonesia', + 'PH': 'Philippines', + 'MY': 'Malaysia', + 'SG': 'Singapore', + 'IN': 'India', + 'GB': 'United Kingdom', + 'FR': 'France', + 'DE': 'Germany', + 'ES': 'Spain', + 'BR': 'Brazil', + 'MX': 'Mexico', + 'CA': 'Canada', + 'AU': 'Australia', + 'ZZ': 'Unspecified', + 'other': '-', + }); + return '$_temp0'; + } + + @override + String loungeMaxPhotos(String count) { + return 'You can upload up to $count photos.'; + } + + @override + String get commonLoadFailed => 'Couldn\'t load'; + + @override + String get planetPassportHistory => 'PASSPORT · Fandom history'; + + @override + String planetNamed(String name) { + return 'Planet $name'; + } + + @override + String get blockNebulaConfirm => 'Block this nebula?'; + + @override + String get blockPlanetConfirm => 'Block this planet?'; + + @override + String get blocked => 'Blocked.'; + + @override + String planetLastUpdated(String time) { + return 'Last updated $time'; + } + + @override + String get planetContribution => 'Contributions'; + + @override + String get planetNebulaGrowth => 'Nebula growth'; + + @override + String get planetLiveRanking => 'Live contribution ranking'; + + @override + String get planetNoContributors => 'No contributors yet'; + + @override + String get planetContributeCta => 'Contribute and climb the ranking!'; + + @override + String planetOverallRank(String rank) { + return 'Overall #$rank'; + } + + @override + String get planetMyContribution => 'My share'; + + @override + String get planetTallying => 'Tallying soon'; + + @override + String planetTopPercent(String percent) { + return 'Top $percent%'; + } + + @override + String get planetHideMember => 'Hide all posts from this member'; + + @override + String get reportReasonUnknown => 'Couldn\'t verify the report reason.'; + + @override + String get reportAlready => 'You\'ve already reported this post.'; + + @override + String get blockUnavailable => 'This author can\'t be blocked.'; + + @override + String get commonMe => 'Me'; + + @override + String get planetSizeMedium => 'Mid-size fandom'; + + @override + String planetPerHead(String value) { + return 'Power per member: $value'; + } + + @override + String get planetJoined => 'Joined'; + + @override + String get planetJoin => 'Join'; + + @override + String get questDetails => 'Details'; + + @override + String get questInProgress => 'In progress'; + + @override + String get questTypeAttend => 'Check-in'; + + @override + String get questTypeSteps => 'Walking'; + + @override + String get questTypePlace => 'Location'; + + @override + String get questTypeStreaming => 'Streaming'; + + @override + String get questTypeUnverifiable => + 'The verification method for this quest type isn\'t set yet.'; + + @override + String get questTabMine => 'My quests'; + + @override + String get placePickPrompt => 'Choose where to\ncomplete the quest.'; + + @override + String get placeSearch => 'Search places'; + + @override + String get commonNoResults => 'No results.'; + + @override + String get commonSelectDone => 'Done'; + + @override + String get adMoreChance => 'Get more chances to contribute.'; + + @override + String get adWatchPrompt => 'Watch an ad to get\na bonus reward!'; + + @override + String get adBonusPlaceholder => '+ Contribution POP'; + + @override + String get adPartyPack => 'Party Pack'; + + @override + String get adPickProduct => 'Choose item'; + + @override + String get adWatchBonus => 'Watch ad for bonus reward'; + + @override + String get adBasicReward => 'Get basic reward'; + + @override + String get focusMode => 'Ad-free Focus mode'; + + @override + String pricePerMonth(String price) { + return '$price/mo'; + } + + @override + String get adWarpBefore => + 'Warping... Finding the right ad so you can contribute more to your fandom and artist.'; + + @override + String get adWarpAfter => + 'Warping... Preparing the reward for your contribution.'; + + @override + String get adSkip => 'Skip'; + + @override + String get questPreviewHint => 'Pick options and check the preview'; + + @override + String get questNoCategory => 'No category selected yet'; + + @override + String get questNew => 'New quest'; + + @override + String get questCategory => 'Category'; + + @override + String get questName => 'Quest name'; + + @override + String get questAction => 'Action details'; + + @override + String get questPickPlace => 'Choose a target location'; + + @override + String get questGoalValue => 'Goal'; + + @override + String get questParticipants => 'Participants'; + + @override + String get questRewardPop => 'Completion reward POP'; + + @override + String get questCreateConfirm => 'Create with these details?'; + + @override + String questCreateSummary(String title, String goal, String reward) { + return '$title · Goal $goal · Reward $reward POP'; + } + + @override + String get commonKeepEditing => 'Keep editing'; + + @override + String get questCreate => 'Create'; + + @override + String questNamed(String title) { + return '$title quest'; + } + + @override + String get questDailyHelp => 'Help your planet every day'; + + @override + String get questTodayMust => 'Quests to do today'; + + @override + String get questTabEmpty => 'No quests in this tab yet.'; + + @override + String get questCreateNotReady => + 'Quest creation opens together with reward funding. Coming soon.'; + + @override + String get questNoBaseReward => + 'Couldn\'t find the basic reward record, so the ad bonus isn\'t available.'; + + @override + String get questNoSteps => 'No steps recorded yet. Walk a bit and try again.'; + + @override + String get questNoPlace => 'No location is registered for this quest.'; + + @override + String get questGoal => 'Goal'; + + @override + String get questReward => 'Reward'; + + @override + String get questPopGrant => 'POP reward'; + + @override + String get commonChecking => 'Checking…'; + + @override + String questMine(String count) { + return 'This is your quest. $count participants.'; + } + + @override + String get questStopConfirm => 'Stop this quest?'; + + @override + String get questStopBody => 'Your progress will be reset to the beginning.'; + + @override + String get questStop => 'Stop'; + + @override + String get adNotFinished => + 'You didn\'t finish the ad, so there\'s no bonus. You still get the basic reward.'; + + @override + String get openSettings => 'Open settings'; + + @override + String get permStepsDenied => + 'Step permission is off. Allow \"Physical activity\" in Settings.'; + + @override + String get permStepsNeeded => + 'Physical activity permission is needed to count steps.'; + + @override + String get permLocationOff => + 'Location services are off. Turn them on and try again.'; + + @override + String get permLocationNeeded => + 'Location permission is needed to verify your visit.'; + + @override + String get permLocationDenied => + 'Location permission is off. Allow location access in Settings.'; + + @override + String get questRewardReceived => ' received!'; + + @override + String questAdBonus(String pop) { + return 'Ad bonus +$pop POP'; + } + + @override + String questParticipantsCount(String count) { + return 'Participants: $count'; + } + + @override + String questParticipantsCap(String count, String cap) { + return 'Participants: $count / $cap'; + } + + @override + String get nebulaCreateFailed => + 'Couldn\'t create the nebula. Please try again later.'; + + @override + String get nebulaAlreadyToday => 'You\'ve already created a nebula today.'; + + @override + String get nebulaCreatedKeepPrimary => + 'Nebula created. Your main planet stays the same because it can only be changed 30 days after the last change.'; + + @override + String get fandomFounderLeaveBlocked => + 'You can leave after the planet is born.'; + + @override + String get sortPopular => 'Popular'; + + @override + String get campaignSortContributing => 'Contributing'; + + @override + String get campaignStatusReview => 'This campaign is under review.'; + + @override + String get campaignStatusRejected => + 'This campaign was rejected in review. Your registration fee has been returned.'; + + @override + String get campaignStatusWithdrawn => + 'This campaign was withdrawn by its creator.'; + + @override + String get campaignStatusCancelled => + 'This campaign was cancelled for operational reasons. The registration fee and supported POP have been returned.'; + + @override + String get rankWindowDaily => 'Daily'; + + @override + String get rankWindowWeekly => 'Weekly'; + + @override + String get campaignNotFound => 'Campaign not found.'; + + @override + String get campaignMediaMain => 'Main image'; + + @override + String get campaignLoadFailed => 'Could not load the campaign.'; + + @override + String get campaignTabInfo => 'Campaign info'; + + @override + String campaignTabBoard(String count) { + return 'Cheers $count'; + } + + @override + String get campaignWithdraw => 'Withdraw campaign'; + + @override + String get campaignContributeAction => 'Contribute'; + + @override + String get campaignFundingStatus => 'Funding status'; + + @override + String get campaignSupporting => 'Supporters'; + + @override + String get campaignTimeLeft => 'Left!'; + + @override + String get campaignReport => 'Result report'; + + @override + String get campaignReportPhotos => 'Execution photos'; + + @override + String get campaignBudget => 'Budget breakdown'; + + @override + String get campaignTotal => 'Total'; + + @override + String get campaignReceipts => 'Receipts'; + + @override + String campaignDaysLeft(String days, String time) { + return '${days}d $time'; + } + + @override + String get campaignWithdrawTitle => 'Withdraw this campaign?'; + + @override + String campaignWithdrawRefund(String fee) { + return 'Since the campaign is not public yet, the $fee POP registration fee will be returned in the currency you paid.'; + } + + @override + String campaignWithdrawNoRefund(String fee) { + return 'This campaign is already public, so the $fee POP registration fee will not be returned.'; + } + + @override + String get campaignWithdrawConfirm => 'Withdraw'; + + @override + String get shareCopied => 'Share text copied.'; + + @override + String get campaignContributePop => 'Set POP to contribute'; + + @override + String get campaignEmptyContributing => + 'You haven\'t contributed to any campaigns yet.'; + + @override + String get campaignEmpty => 'No campaigns yet.'; + + @override + String get campaignNew => 'New campaign'; + + @override + String get campaignMyRanking => 'My contribution rank'; + + @override + String campaignWindowRanking(String window) { + return '$window ranking'; + } + + @override + String get campaignSearchHint => 'Search campaigns'; + + @override + String get campaignNoRecentSearches => 'No recent searches'; + + @override + String searchRemoveTerm(String term) { + return 'Remove $term'; + } + + @override + String get campaignNoResults => 'No results'; + + @override + String get shareTopicCampaign => 'Campaign'; + + @override + String sharePlanetLine(String brand, String name) { + return '$brand · $name Planet'; + } + + @override + String get campaignGaugeLabel => 'Campaign progress'; + + @override + String get campaignPeriodTitle => 'Choose the campaign period'; + + @override + String get campaignPeriodMax => 'You can choose up to one year'; + + @override + String campaignYearMonth(String year, String month) { + return '$month/$year'; + } + + @override + String get campaignWeekdays => 'S,M,T,W,T,F,S'; + + @override + String campaignPeriodRange( + String startMonth, + String startDay, + String endMonth, + String endDay, + ) { + return '$startMonth/$startDay ~ $endMonth/$endDay'; + } + + @override + String get campaignPeriodSelect => 'Select period'; + + @override + String get calendarPrevMonth => 'Previous month'; + + @override + String get calendarNextMonth => 'Next month'; + + @override + String get campaignShareTitle => 'Share campaign'; + + @override + String get campaignPickMedia => 'Choose image/video'; + + @override + String get campaignTitleHint => 'Enter a campaign title.'; + + @override + String get campaignBodyHint => 'Describe your campaign.'; + + @override + String get campaignReviewNote => + 'Once submitted, funding opens after review.'; + + @override + String get campaignSupportType => 'Choose support type'; + + @override + String get campaignPeriod => 'Campaign period'; + + @override + String get campaignDateSelect => 'Select dates'; + + @override + String get campaignGoalPop => 'Goal POP'; + + @override + String get campaignSubmitTitle => 'Submit this campaign?'; + + @override + String campaignSubmitBody( + String title, + String goal, + String days, + String fee, + ) { + return '$title · Goal $goal POP · $days days\nThe $fee POP registration fee will be deducted, and funding opens after review.'; + } + + @override + String get campaignSubmit => 'Submit'; + + @override + String get campaignCreateFailed => 'Could not submit the campaign.'; + + @override + String campaignNeedsQuests(String done, String need, String left) { + return 'Quests completed $done/$need — complete $left more to open a campaign.'; + } + + @override + String get campaignPendingTitle => 'Awaiting review'; + + @override + String get campaignPendingBody => + 'Once review is complete, funding opens and the campaign appears in the list.'; + + @override + String get insideRefundPending => + 'The acceptance deadline has passed. The bounty will be refunded.'; + + @override + String get insideRefundDone => + 'The acceptance deadline has passed. The bounty has been refunded.'; + + @override + String get insideRefundChecking => + 'The acceptance deadline has passed. We are checking the bounty refund.'; + + @override + String get insideErrPopShort => 'Not enough POP to set the bounty.'; + + @override + String get insideErrSelfAccept => 'You can\'t accept your own answer.'; + + @override + String get insideErrAlreadyAccepted => + 'An answer has already been accepted and cannot be changed.'; + + @override + String get insideErrNotOwner => + 'Only the person who asked can accept an answer.'; + + @override + String get commonCheckConnection => 'Check your connection and try again.'; + + @override + String get insideAnswerLater => 'Comments open once the server is connected.'; + + @override + String insideAcceptConfirm(String pop) { + return 'Accept this comment?\nThe $pop POP bounty will be given and this cannot be undone.'; + } + + @override + String get insideAccept => 'Accept'; + + @override + String get insideMore => 'More'; + + @override + String get insideFirstAnswer => 'Be the first to comment.'; + + @override + String get insideAcceptDone => 'Accepted'; + + @override + String get insideAcceptAction => 'Accept'; + + @override + String get insideAnswerHint => 'Write a comment.'; + + @override + String get insideLoadFailed => 'Could not load INSIDE'; + + @override + String get insideEmpty => 'No questions yet.'; + + @override + String get insideWrite => 'Write INSIDE'; + + @override + String get insideSearchHint => 'INSIDE title, content'; + + @override + String get insideBodyHint => 'Ask and answer freely with other fans.'; + + @override + String get insideTitleRequired => 'Please enter a title.'; + + @override + String get insideBodyRequired => 'Please enter the content.'; + + @override + String get insideBountySet => 'Set bounty'; + + @override + String insideBountyHint(String min) { + return 'Set $min or more.'; + } + + @override + String get insideAttachLater => + 'Image attachments open once the server is connected.'; + + @override + String get insideBountyRequired => 'Please enter a bounty.'; + + @override + String get insideNumberOnly => 'Please enter a number.'; + + @override + String insideBountyMin(String min) { + return 'The bounty must be at least $min POP.'; + } + + @override + String get insideBountyOverBalance => 'Can\'t exceed your POP balance.'; + + @override + String get questTypeTimeAttack => 'Flash Time Attack'; + + @override + String get questTimeLeft => 'Time left'; + + @override + String get questTimeAttackClosed => 'Closed'; + + @override + String questTimeAttackLeft(String time) { + return '$time left'; + } + + @override + String get popReasonQuestTimeAttack => 'Flash time attack'; + + @override + String get briefingShareCopied => 'Share text and link copied.'; + + @override + String get shareCopyText => 'Copy text'; + + @override + String get briefingShareTextCopied => 'Share text copied.'; + + @override + String questAlreadyRewarded(String pop) { + return 'Reward already received (+$pop POP).'; + } + + @override + String get questCatHealth => 'Health'; + + @override + String get questCatPlanet => 'Planet activity'; + + @override + String get questCatCommunity => 'Community'; + + @override + String get questActShake => 'Shake'; + + @override + String get questTypeVote => 'Vote'; + + @override + String get questTypeSns => 'Social media'; + + @override + String get questActivity => 'Activity'; + + @override + String get questNoCreatable => + 'No quest activities are open for creation right now.'; + + @override + String get questDescriptionOptional => 'Description (optional)'; + + @override + String get questLinkUrl => 'Link URL'; + + @override + String get questLinkCheck => 'Check link'; + + @override + String get questLinkInvalid => + 'This link isn\'t valid. Please check the address.'; + + @override + String get questLinkPreviewFailed => + 'Couldn\'t load the link info. Please enter a title.'; + + @override + String get questLinkTitle => 'Link title'; + + @override + String get questEndDate => 'End date'; + + @override + String get questEndDefault => '24 hours after publishing'; + + @override + String questEndDateValue(String date) { + return 'Ends $date 23:59:59'; + } + + @override + String questPoolHelper(String pool, String fee) { + return 'Deposit $pool POP · Fee $fee POP'; + } + + @override + String questCreateCost( + String title, + String reward, + String count, + String pool, + String fee, + String total, + ) { + return '$title\nReward $reward POP × $count = $pool POP\nFee $fee POP (burned on publishing)\n$total POP in total will be deducted.'; + } + + @override + String get questNoReward => 'This quest has no reward.'; + + @override + String get questDeadline => 'Deadline'; + + @override + String questRemainingHours(String hours) { + return '${hours}h left'; + } + + @override + String get questEnded => 'Ended'; + + @override + String get questAuthorNote => 'Creator\'s note'; + + @override + String get questRepeatOnce => 'One entry per person'; + + @override + String questRepeatEvery(String hours, String count) { + return 'Every ${hours}h · up to $count times'; + } + + @override + String get questSubmit => 'Submit proof'; + + @override + String get questResubmit => 'Resubmit'; + + @override + String get questEvidenceUrl => 'Proof link (optional)'; + + @override + String get questEvidenceText => 'Proof details'; + + @override + String get questSubmitted => 'Submitted. The reward is paid after review.'; + + @override + String get questReviewPending => + 'Under review. The reward is paid once approved.'; + + @override + String get questReviewDelayed => + 'Review is taking longer than usual. Our team is checking.'; + + @override + String get questApprovedPaying => 'Approved. Paying your reward.'; + + @override + String questRejected(String reason) { + return 'Rejected: $reason'; + } + + @override + String get questResubmitAllowed => 'You can revise and resubmit.'; + + @override + String get questResubmitNotAllowed => 'This entry can\'t be resubmitted.'; + + @override + String get questReviewButton => 'In review'; + + @override + String get questRejectedButton => 'Rejected'; + + @override + String questReviewQueue(String count) { + return '$count awaiting review'; + } + + @override + String get questApprove => 'Approve'; + + @override + String get questReject => 'Reject'; + + @override + String get questRejectReason => 'Reason'; + + @override + String get questAllowResubmit => 'Allow resubmission'; + + @override + String get questEdit => 'Edit title and description'; + + @override + String get questEditNotice => + 'After publishing, only the title and description can be edited, and edits are logged.'; + + @override + String get popReasonQuestUgc => 'Quest reward'; + + @override + String get popReasonQuestFee => 'Quest creation fee'; + + @override + String questFreeDailyCap(String cap) { + return 'You can create up to $cap no-reward quests a day.'; + } + + @override + String get popReasonQuestPoolDeposit => 'Quest reward deposit'; + + @override + String get popReasonQuestPoolReturn => 'Quest reward returned'; + + @override + String get campaignCommentNeedNickname => 'Please set a nickname first'; + + @override + String get campaignCommentSetNickname => 'Set nickname'; +} diff --git a/app/lib/l10n/app_localizations_es.dart b/app/lib/l10n/app_localizations_es.dart new file mode 100644 index 0000000..a81a284 --- /dev/null +++ b/app/lib/l10n/app_localizations_es.dart @@ -0,0 +1,2761 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Spanish Castilian (`es`). +class AppLocalizationsEs extends AppLocalizations { + AppLocalizationsEs([String locale = 'es']) : super(locale); + + @override + String get commonConfirm => 'Aceptar'; + + @override + String get commonCancel => 'Cancelar'; + + @override + String get commonClose => 'Cerrar'; + + @override + String get commonSelect => 'Seleccionar'; + + @override + String get commonRetry => 'Reintentar'; + + @override + String get commonView => 'Ver'; + + @override + String get commonPrev => 'Atrás'; + + @override + String get commonDone => 'Listo'; + + @override + String get commonStart => 'Empezar'; + + @override + String commonNotYet(String where) { + return 'La pantalla $where llega en la segunda revisión (2026-09-10).'; + } + + @override + String get languageSettingsTitle => 'Idioma'; + + @override + String get authLogin => 'Iniciar sesión'; + + @override + String get authSignup => 'Registrarse'; + + @override + String get authFirstTime => '¿Nuevo en FANUZU?'; + + @override + String get authAlreadyMember => '¿Ya tienes una cuenta?'; + + @override + String get authLoginHeadline => + '¡Inicia sesión en FANUZU\nde la forma más fácil!'; + + @override + String get authSignupHeadline => '¡Únete a FANUZU\nde la forma más fácil!'; + + @override + String authProviderLogin(String provider) { + String _temp0 = intl.Intl.selectLogic(provider, { + 'apple': 'Iniciar sesión con Apple', + 'google': 'Iniciar sesión con Google', + 'kakao': 'Iniciar sesión con Kakao', + 'line': 'Iniciar sesión con LINE', + 'other': 'Iniciar sesión con correo', + }); + return '$_temp0'; + } + + @override + String authProviderSignup(String provider) { + String _temp0 = intl.Intl.selectLogic(provider, { + 'apple': 'Registrarse con Apple', + 'google': 'Registrarse con Google', + 'kakao': 'Registrarse con Kakao', + 'line': 'Registrarse con LINE', + 'other': 'Registrarse con correo', + }); + return '$_temp0'; + } + + @override + String authNotConfiguredTitle(String name) { + return 'Inicio con $name próximamente'; + } + + @override + String authNotConfiguredBody(String name, String items) { + return 'El inicio con $name se habilitará cuando se entreguen las claves.\n\nRequerido\n· $items'; + } + + @override + String authNotConfiguredNotice(String name, String items) { + return 'El inicio con $name aún no está listo.\nRequerido: $items'; + } + + @override + String get authResetNeedEmail => + 'Primero introduce el correo para recibir el enlace.'; + + @override + String get authResetSent => 'Enviamos las instrucciones a esa dirección.'; + + @override + String get termsTitle => 'Términos de uso'; + + @override + String get termsAgreeAll => 'Aceptar todo'; + + @override + String get termsService => 'Términos del servicio (obligatorio)'; + + @override + String get termsLocation => + 'Términos de servicios de ubicación (obligatorio)'; + + @override + String get termsAge => 'Tengo 14 años o más. (obligatorio)'; + + @override + String get termsMarketing => + 'Recibir información de marketing y eventos (opcional)'; + + @override + String get signupDoneTitle => 'Registro completado'; + + @override + String get signupDoneBody => + 'Ya puedes empezar tus actividades de fandom.\nHaz crecer tu planeta con muchas actividades.'; + + @override + String get homeItemsTooltip => 'Objetos'; + + @override + String get homeNotificationsTooltip => 'Notificaciones'; + + @override + String get homeFandomHistory => 'Historia del fandom'; + + @override + String get homePopularInside => 'K-INSIDE popular'; + + @override + String get homeWhereBriefing => 'Resumen IA'; + + @override + String get homeWhereInsideList => 'Lista de INSIDE'; + + @override + String get homeWhereInsideDetail => 'Detalle de INSIDE'; + + @override + String get homeLoadFailed => 'No se pudo cargar el inicio'; + + @override + String get homeRetryLater => 'Inténtalo de nuevo en un momento.'; + + @override + String get myProfileCardTooltip => 'Tarjeta de perfil'; + + @override + String get myTabPlanets => 'Planetas/Nebulosas'; + + @override + String get myTabActivity => 'Actividad'; + + @override + String get myInside => 'Inside'; + + @override + String get myCampaign => 'Campañas'; + + @override + String myShareLater(String target) { + return 'Compartir en $target se conectará en la versión 09-10.'; + } + + @override + String get myEditPhoto => 'Editar foto de perfil'; + + @override + String get myEditProfile => 'Editar perfil'; + + @override + String get settingsTitle => 'Ajustes'; + + @override + String get settingsAccount => 'Cuenta'; + + @override + String get settingsNickname => 'Apodo'; + + @override + String get settingsEmail => 'Correo'; + + @override + String get settingsSns => 'Cuentas sociales vinculadas'; + + @override + String get settingsPopHistory => 'Historial de POP'; + + @override + String get settingsPayments => 'Pagos'; + + @override + String get settingsServicePayments => 'Historial de pagos del servicio'; + + @override + String get settingsService => 'Ajustes del servicio'; + + @override + String get settingsPush => 'Notificaciones push'; + + @override + String get settingsBlocked => 'Usuarios bloqueados'; + + @override + String get settingsInfo => 'Información'; + + @override + String get settingsNotice => 'Avisos'; + + @override + String get settingsInquiry => 'Consulta 1:1'; + + @override + String get settingsAppVersion => 'Versión de la app'; + + @override + String get settingsLatest => 'Última versión'; + + @override + String get settingsLogout => 'Cerrar sesión'; + + @override + String get settingsLogoutConfirm => '¿Quieres cerrar sesión?'; + + @override + String get settingsWithdraw => 'Eliminar cuenta'; + + @override + String get settingsWithdrawConfirm => '¿Quieres eliminar tu cuenta?'; + + @override + String get settingsWithdrawBody => 'Perderás tus POP y no se puede deshacer.'; + + @override + String get settingsWithdrawLater => + 'La eliminación de cuenta se conectará en la versión 09-10.'; + + @override + String get settingsUnlinkConfirm => '¿Desvincular esta cuenta?'; + + @override + String get settingsUnlinkBody => + 'Solo se quita este método de inicio social; los datos de tu cuenta no se eliminan.'; + + @override + String get settingsUnlink => 'Desvincular'; + + @override + String get settingsSameEmailOnly => + 'Solo se pueden vincular cuentas sociales\ncon el mismo correo.'; + + @override + String settingsLinkNotReady(String name, String items) { + return 'La vinculación con $name aún no está lista. Requerido: $items'; + } + + @override + String get settingsConnect => 'Vincular'; + + @override + String get settingsDisconnect => 'Desvincular'; + + @override + String get settingsEmailProvider => 'Correo'; + + @override + String get pushDeviceOff => + 'Las notificaciones push están desactivadas en este dispositivo.'; + + @override + String get pushDeviceOffHint => + 'Permite las notificaciones en los ajustes del sistema.'; + + @override + String get pushRequesting => 'Solicitando…'; + + @override + String get pushGoToSettings => 'Ir a ajustes'; + + @override + String get pushOpeningDeviceSettings => + 'Abriendo los ajustes del dispositivo.'; + + @override + String get pushEnableInDevice => + 'Activa las notificaciones de FANUZU en los ajustes del dispositivo.'; + + @override + String get pushDeviceSection => 'Notificaciones del dispositivo'; + + @override + String get pushSoundVibration => 'Sonido y vibración'; + + @override + String get pushServiceSection => 'Notificaciones del servicio'; + + @override + String get pushTopicEssential => 'Esenciales'; + + @override + String get pushTopicFandomEvent => 'Eventos del fandom'; + + @override + String get pushTopicBriefing => 'AI Briefing'; + + @override + String get pushTopicCommunity => 'Comunidad'; + + @override + String get pushTopicMarketing => 'Marketing'; + + @override + String linkTitle(String name) { + return 'Vincular $name'; + } + + @override + String get linkNotLinked => 'No vinculada'; + + @override + String get linkLinked => 'Vinculada'; + + @override + String get linkSameEmailOnly => + 'Solo se pueden vincular cuentas sociales con el mismo correo.'; + + @override + String get linkUnlink => 'Desvincular'; + + @override + String get linkLink => 'Vincular'; + + @override + String get noticeBadge => 'Aviso'; + + @override + String get noticeTitle => 'Avisos'; + + @override + String get noticeEmpty => 'No hay avisos.'; + + @override + String get noticeDetailTitle => 'Detalle del aviso'; + + @override + String get translationMark => 'Traducido'; + + @override + String translationReplyTo(String name) { + return 'Traduciendo el comentario de $name'; + } + + @override + String get translationWrite => 'Escribir traducción'; + + @override + String get translationHint => 'Escribe tu traducción'; + + @override + String get translationValid => 'Válida'; + + @override + String get translationInvalid => 'No válida'; + + @override + String get translationSelfMarkBlocked => + 'No puedes marcar tu propia traducción.'; + + @override + String get translationSubmitted => + 'Traducción publicada. Si se valida, recibirás POP en 24 horas.'; + + @override + String get translationMarked => 'Marcada.'; + + @override + String get translationAlreadyMarked => 'Ya marcaste esta traducción.'; + + @override + String get translationDuplicate => + 'Ya publicaste una traducción en este idioma.'; + + @override + String get translationFailed => + 'No se pudo procesar. Inténtalo de nuevo en un momento.'; + + @override + String get translationNotEligible => + 'Solo los miembros de este fandom con más de 24 horas de antigüedad pueden marcar.'; + + @override + String get translationDailyCap => + 'Hoy ya no puedes publicar más traducciones.'; + + @override + String get translationInvalidTarget => 'Este contenido no se puede traducir.'; + + @override + String get commonRequestFailed => 'No se pudo procesar la solicitud.'; + + @override + String get commonFan => 'Fan'; + + @override + String get adLoadFailed => + 'No se pudo cargar el anuncio. Solo recibirás la recompensa básica.'; + + @override + String get storeNotReady => 'Productos de la tienda en preparación'; + + @override + String get storePending => + 'Esperando la aprobación del pago. Se aplicará automáticamente al aprobarse.'; + + @override + String get storeFailed => 'No se pudo completar el pago.'; + + @override + String get storePurchased => 'Tu compra se ha aplicado.'; + + @override + String get heroParticipate => 'ZAP+ · Unirse al regalo'; + + @override + String heroRemainingSemantics(String minutes, String seconds) { + return 'HERO TIME restante: $minutes min $seconds s'; + } + + @override + String heroRemaining(String time) { + return 'HERO TIME · quedan $time'; + } + + @override + String heroClaims(String count) { + return 'Regalos recibidos por fans: $count'; + } + + @override + String heroInProgress(String name) { + return '$name · HERO TIME en curso →'; + } + + @override + String get stageNames => + 'Nebulosa,Inicio,Crecimiento,Juntos,Compañía,Luz,Eternidad'; + + @override + String get planetStageUnknown => 'Planeta · Sin información de etapa'; + + @override + String get planetHibernatingSuffix => ' · En reposo'; + + @override + String get planetEclipseSuffix => ' · Eclipse en curso'; + + @override + String get planetHeroSuffix => ' · HERO TIME en curso'; + + @override + String get evoPlanetGrowth => 'Crecimiento del planeta'; + + @override + String get evoHeroEnd => 'HERO TIME terminado'; + + @override + String get evoAwakening => 'El planeta despierta'; + + @override + String get evoAnomaly => 'Reacción anómala'; + + @override + String get evoEnergy => 'Concentrando energía'; + + @override + String get evoTransition => 'Transición'; + + @override + String get evoComplete => 'Evolución del planeta completa'; + + @override + String get evoOpenHistory => 'Ver historial de PASSPORT'; + + @override + String get playNow => 'Juega ahora'; + + @override + String get playHeadline => '¡Vamos a conocer a tu fandom!'; + + @override + String get playBody => + 'Crea un planeta, conecta con fandoms y disfruta de la actividad fan...'; + + @override + String get timeJustNow => 'Ahora'; + + @override + String timeMinutesAgo(String n) { + return 'hace $n min'; + } + + @override + String timeHoursAgo(String n) { + return 'hace $n h'; + } + + @override + String timeDaysAgo(String n) { + return 'hace $n d'; + } + + @override + String get commonMore => 'Ver más'; + + @override + String authProviderStart(String provider) { + String _temp0 = intl.Intl.selectLogic(provider, { + 'email': 'Continuar con correo', + 'kakao': 'Continuar con Kakao', + 'line': 'Continuar con LINE', + 'apple': 'Continuar con Apple', + 'other': 'Continuar con Google', + }); + return '$_temp0'; + } + + @override + String authProviderName(String provider) { + String _temp0 = intl.Intl.selectLogic(provider, { + 'email': 'Correo', + 'kakao': 'Kakao', + 'line': 'LINE', + 'apple': 'Apple', + 'other': 'Google', + }); + return '$_temp0'; + } + + @override + String get authLastProvider => + 'No puedes desvincular tu último método de inicio de sesión.'; + + @override + String get authLoginRequired => 'Debes iniciar sesión.'; + + @override + String get authDifferentEmail => + 'Esta cuenta no coincide con tu correo de registro.'; + + @override + String get authLinkFailed => 'No se pudo vincular la cuenta.'; + + @override + String get authNotLinked => 'Esta cuenta no está vinculada.'; + + @override + String get authEmailUnavailable => + 'El inicio de sesión con correo no está disponible.'; + + @override + String get authCheckCredentials => 'Revisa tu correo o contraseña.'; + + @override + String get authSocialFailed => 'Error al iniciar sesión con la red social.'; + + @override + String get authAppleUnavailable => + 'Iniciar sesión con Apple no está disponible en este dispositivo.'; + + @override + String get authEmailInUse => 'Este correo ya está registrado.'; + + @override + String get authWeakPassword => + 'La contraseña es muy corta. Usa al menos 8 caracteres.'; + + @override + String get authInvalidEmail => 'El formato del correo no es válido.'; + + @override + String get commonTryLater => 'Inténtalo de nuevo más tarde.'; + + @override + String get authCredentialInUse => + 'Esta cuenta social ya está vinculada a otra cuenta de FANUZU.'; + + @override + String get authAlreadyLinked => 'Esta cuenta ya está vinculada.'; + + @override + String get authMethodUnavailable => + 'Este método de inicio de sesión aún no está disponible.'; + + @override + String get authRecentLogin => + 'Por seguridad, vuelve a iniciar sesión e inténtalo de nuevo.'; + + @override + String authOtherProviderGuide(String email) { + return 'La cuenta $email se registró con otro método de inicio de sesión. Inicia sesión con el método que usaste primero y vincula este en MY > Ajustes > Cuentas vinculadas.'; + } + + @override + String get authThisAccount => 'Esta'; + + @override + String get eclipseCapped => + 'Alcanzaste el límite de participación; algunas no se contaron.'; + + @override + String get eclipseResultUnknown => + 'No se pudo confirmar tu participación. Vuelve a comprobarlo con la misma solicitud.'; + + @override + String get eclipseChecking => 'Comprobando participación'; + + @override + String get eclipseRetrySame => 'Volver a comprobar'; + + @override + String get eclipseEnded => 'El eclipse ya terminó o se defendió con éxito.'; + + @override + String get eclipseTooFast => + 'Estás participando demasiado rápido. Inténtalo en un momento.'; + + @override + String get eclipseNone => 'No hay ningún eclipse en curso'; + + @override + String get eclipseSuccessTitle => '¡Detuviste\nel impacto del meteorito!'; + + @override + String get eclipseQualified => 'Participación confirmada'; + + @override + String get eclipseFailTitle => + 'Lamentablemente,\nla defensa del planeta falló'; + + @override + String get eclipseFailBody => + 'No se alcanzó el objetivo de defensa de esta ronda.'; + + @override + String get eclipseCancelTitle => 'Eclipse cancelado'; + + @override + String get eclipseCancelBody => + 'Se canceló por un error de recuento. No se descontó FPI.'; + + @override + String get eclipsePendingTitle => 'Comprobando resultado de la defensa'; + + @override + String get eclipsePendingBody => + 'El tiempo de participación terminó. Esperando la decisión del servidor.'; + + @override + String get eclipseShakeTimed => + 's: ¡agita tu móvil\npara detener el meteorito!'; + + @override + String get eclipseShake => '¡Agita tu móvil\npara detener el meteorito!'; + + @override + String eclipseMyCount(String count) { + return 'Mis participaciones: $count'; + } + + @override + String eclipsePendingSuffix(String count) { + return ' · +$count pendientes'; + } + + @override + String get eclipseSendingSuffix => ' · Enviando'; + + @override + String eclipseNoPopNote(String count) { + return 'No se gasta POP. Haz al menos $count sacudidas válidas para contar como participante.'; + } + + @override + String get eclipseTapToJoin => '¿No puedes agitar? Toca para participar'; + + @override + String get eclipseTimeLeft => 'Tiempo restante del eclipse:'; + + @override + String eclipseSeconds(String seconds) { + return ' $seconds s'; + } + + @override + String eclipseBannerDefended(String name) { + return 'Defensa exitosa: $name sin pérdida de FPI'; + } + + @override + String eclipseBannerActive(String name, String rate) { + return 'Eclipse activado: $name erosión $rate%'; + } + + @override + String eclipseBannerDefense(String count, String target) { + return 'Participantes válidos $count / $target'; + } + + @override + String timeHoursMinutesLeft(String hours, String minutes) { + return 'Quedan $hours h $minutes min'; + } + + @override + String timeMinutesLeft(String minutes) { + return 'Quedan $minutes min'; + } + + @override + String liveEclipseDefense(String name, String count) { + return '[LIVE] ¡$name se unió a la defensa del eclipse! (+$count)'; + } + + @override + String liveHotTime(String name) { + return '[LIVE] ¡Comenzó el Artist Hot Time de $name!'; + } + + @override + String liveHeroGift(String name) { + return '[LIVE] ¡Llegó el Regalo del Héroe de $name!'; + } + + @override + String liveCampaign(String name) { + return '[LIVE] ¡$name apoyó una campaña!'; + } + + @override + String get galaxySetPrimaryConfirm => + '¿Establecer el planeta seleccionado como principal?'; + + @override + String get galaxyLoadFailed => 'No se pudo cargar la galaxia'; + + @override + String get commonSearch => 'Buscar'; + + @override + String get galaxyMyPlanets => 'Mis planetas'; + + @override + String get galaxyMap => 'Mapa de la galaxia'; + + @override + String get nebulaCreate => 'Crear nebulosa'; + + @override + String get commonRecheck => 'Volver a comprobar'; + + @override + String get zapApplying => 'Aplicando contribución'; + + @override + String get zapNeedsCheck => 'Hay que comprobar la contribución'; + + @override + String get zapNoPlanet => 'No perteneces a ningún planeta'; + + @override + String get commonContribute => 'Contribuir'; + + @override + String get commonItem => 'Objetos'; + + @override + String get galaxyDefended => 'Defensa exitosa · Sin pérdida de FPI'; + + @override + String galaxyErosion(String rate, String progress) { + return 'Erosión $rate% · Defensa $progress%'; + } + + @override + String galaxyOnlineMembers(String online, String members) { + return '$online en línea · $members miembros'; + } + + @override + String get galaxySearchHint => 'Buscar por nombre de fandom'; + + @override + String get commonRecentSearches => 'Búsquedas recientes'; + + @override + String get commonClearAll => 'Borrar todo'; + + @override + String get commonNoRecentSearches => 'No hay búsquedas recientes.'; + + @override + String get galaxyNoHit => 'No se encontró el fandom.\nRevisa el nombre.'; + + @override + String galaxyMembers(String count) { + return '$count miembros'; + } + + @override + String galaxyPlanetRank(String name, String rank) { + return 'Planeta $name, puesto $rank'; + } + + @override + String get galaxyMapOpen => 'Abrir mapa de la galaxia'; + + @override + String get zapNotEnoughPop => 'POP insuficiente'; + + @override + String get zapFailNoPop => 'No tienes POP suficiente para contribuir.'; + + @override + String get zapFailLedger => + 'Se detuvo la contribución: el saldo no coincide con el historial.'; + + @override + String get zapFailRetry => + 'Revisa tu conexión y vuelve a comprobar la contribución.'; + + @override + String get zapDone => 'Contribución completada'; + + @override + String zapDonePop(String pop) { + return '$pop POP contribuidos'; + } + + @override + String get growthFounders => 'Miembros fundadores'; + + @override + String get growthNebulaGoalPassed => + 'El plazo objetivo ha pasado · Cumple las condiciones y nacerá el planeta'; + + @override + String get growthFinalStage => 'Etapa final'; + + @override + String get growthPromotionPending => 'Ascenso pendiente'; + + @override + String get growthCheckConditions => 'Ver condiciones de crecimiento'; + + @override + String growthConditionChecking(String name) { + return 'Comprobando condición de $name'; + } + + @override + String growthConditionMet(String name) { + return 'Condición de $name cumplida'; + } + + @override + String growthConditionUnmet(String name) { + return 'Condición de $name no cumplida'; + } + + @override + String get nebulaHeadline => 'Ponle nombre al planeta de tu fandom.'; + + @override + String nebulaBody(String days) { + return 'Si los miembros fundadores alcanzan la meta de POP en $days días,\n¡nacerá como planeta oficial!\nQuienes participen en la evolución recibirán una insignia.'; + } + + @override + String get nebulaNameTaken => 'Este nombre ya está en uso.'; + + @override + String get nebulaNameHint => 'Nombre del fandom'; + + @override + String get nebulaGoal => 'Meta '; + + @override + String get nebulaFounders => ' / Fundadores'; + + @override + String nebulaGoalSemantics(String pop, String members, String days) { + return 'Meta $pop POP · $members fundadores · $days días fijos'; + } + + @override + String get commonMyPop => 'Mi POP: '; + + @override + String get commonChange => 'Cambiar'; + + @override + String planetShape(String n) { + return 'Forma $n'; + } + + @override + String get planetBgColor => 'Elige el color de fondo'; + + @override + String get planetColorSelect => 'Elige el color del planeta'; + + @override + String get planetTypeSelect => 'Elige el tipo de planeta'; + + @override + String get commonNext => 'Siguiente'; + + @override + String get planetCreateFailed => + 'No se pudo crear el planeta. Inténtalo más tarde.'; + + @override + String get planetCreateConfirm => '¿Crear el planeta\ncon estas opciones?'; + + @override + String get commonCreate => 'Crear'; + + @override + String get briefingOpenFailed => 'No se puede abrir el original.'; + + @override + String campaignAchieved(String percent) { + return '$percent% alcanzado'; + } + + @override + String get campaignView => 'Ver campaña'; + + @override + String get growthFinalReached => 'Has llegado a la etapa final'; + + @override + String get zapRetrySame => 'Volver a comprobar la contribución'; + + @override + String get heroOwnerSuffix => 'aporta:'; + + @override + String get heroStartPrompt => + '¡Crea o únete a un planeta\ny empieza en FANUZU!'; + + @override + String get heroNebulaPrompt => + '¡Usa el POP que ganas con actividades\npara contribuir a la nebulosa!'; + + @override + String heroRecentContribution(String pop) { + return 'Creciendo sin parar con\n$pop POP aportados.'; + } + + @override + String get heroGrowingTogether => 'Un planeta fan que crecemos juntos'; + + @override + String get heroPopProgress => 'Progreso de POP'; + + @override + String get heroNebula => 'Nebulosa'; + + @override + String get heroNebulaTap => '¡Toca la nebulosa\npara que nazca un planeta!'; + + @override + String get heroCreateNebula => 'Crear planeta (nebulosa)'; + + @override + String get heroJoinPlanet => 'Unirse a un planeta'; + + @override + String get heroCreateOrJoin => 'Crear o unirse a un planeta'; + + @override + String insideAnswers(String count) { + return 'Respuestas $count'; + } + + @override + String get itemJoinFirst => + 'Únete a un planeta para enviar un Regalo del Héroe.'; + + @override + String get itemBuyNotReady => 'La compra de objetos llegará pronto.'; + + @override + String get itemUseNotReady => 'El uso de objetos llegará pronto.'; + + @override + String get payOpenFailed => 'No se pudo abrir la ventana de pago.'; + + @override + String get itemBuy => 'Comprar objetos'; + + @override + String get itemSelect => 'Elegir objeto'; + + @override + String get commonComingSoon => 'Próximamente.'; + + @override + String itemSendPop(String pop) { + return 'Enviar $pop POP'; + } + + @override + String itemBuyerPop(String pop) { + return 'Comprador +$pop POP'; + } + + @override + String itemOwned(String count) { + return 'Tienes: $count'; + } + + @override + String itemOwnedCount(String count) { + return 'Tienes: $count'; + } + + @override + String get growthOwnerOf => ' aportó '; + + @override + String get growthSteadySuffix => ' y seguimos creciendo.'; + + @override + String get questAttended => 'Asistencia registrada'; + + @override + String get questInProgressSpaced => 'En curso'; + + @override + String get questRewardPaid => 'Recompensa entregada'; + + @override + String get questClaim => 'Reclamar'; + + @override + String get questStart => 'Iniciar misión'; + + @override + String get questRewardDone => 'Recompensa recibida'; + + @override + String get popKindGeneral => 'POP normal'; + + @override + String get popKindContribution => 'POP de contribución'; + + @override + String get popTxGrant => 'Otorgado'; + + @override + String get popTxSpend => 'Gastado'; + + @override + String get popTxRefund => 'Reembolso'; + + @override + String get popTxVoid => 'Anulado'; + + @override + String get popTypeQuest => 'Misión'; + + @override + String get popTypeReward => 'Recompensa'; + + @override + String get popTypePayment => 'Pago'; + + @override + String get popTypeSubscription => 'Suscripción'; + + @override + String get popTypeAdmin => 'Equipo'; + + @override + String get popTypeFee => 'Tarifa'; + + @override + String get commonEtc => 'Otros'; + + @override + String get popReasonQuestSteps => 'Misión de pasos'; + + @override + String get popReasonQuestMusicShake => 'Misión Music Shake'; + + @override + String get popReasonQuestPlace => 'Misión de visita'; + + @override + String get popReasonQuestAttend => 'Misión de asistencia'; + + @override + String get popReasonAdReward => 'Recompensa por anuncio'; + + @override + String get popReasonHeroDrop => 'Regalo del Héroe'; + + @override + String get popReasonHeroDropTrigger => 'Regalo del Héroe activado'; + + @override + String get popReasonTranslation => 'Traducción colaborativa'; + + @override + String get popReasonIapPurchase => 'Compra'; + + @override + String get popReasonFocusDaily => 'Recompensa diaria Focus'; + + @override + String get popReasonPreregBonus => 'Recompensa de prerregistro'; + + @override + String get popReasonReferral => 'Invitación de amigos'; + + @override + String get popTypeReferral => 'Referido'; + + @override + String get popReasonAdminGrant => 'Otorgado por el equipo'; + + @override + String get popReasonAdminClawback => 'Retirado por el equipo'; + + @override + String get popReasonCampaignFund => 'Apoyo a campaña'; + + @override + String get popReasonCampaignRefund => 'Reembolso de campaña'; + + @override + String get popReasonCampaignFeeRefund => 'Reembolso de la tarifa de campaña'; + + @override + String get popReasonCampaignFee => 'Tarifa de campaña'; + + @override + String get popReasonQnaBounty => 'Recompensa de INSIDE'; + + @override + String get popReasonQnaRefund => 'Reembolso de recompensa'; + + @override + String get popReasonQnaReward => 'Recompensa por respuesta aceptada'; + + @override + String get popReasonEclipseDefense => 'Contribución Zap'; + + @override + String get popReasonOther => 'Cambio de POP'; + + @override + String popRepeat(String title, String count) { + return '$title ×$count'; + } + + @override + String get payStatePaid => 'Pagado'; + + @override + String get payStateRefundRequested => 'Reembolso solicitado'; + + @override + String get payStateRefundApproved => 'Reembolso aprobado'; + + @override + String get payStateRefunded => 'Reembolsado'; + + @override + String get payStateRefundRejected => 'Reembolso rechazado'; + + @override + String get pushTopicEssentialDesc => + 'Avisos de cuenta, pagos y mantenimiento. No se pueden desactivar.'; + + @override + String get pushTopicFandomEventDesc => + 'Novedades de eclipses, campañas, misiones y Artist Hot Time.'; + + @override + String get pushTopicBriefingDesc => + 'Tarjetas de resumen diario de tu planeta.'; + + @override + String get pushTopicCommunityDesc => + 'Comentarios, me gusta y respuestas en tus publicaciones.'; + + @override + String get pushTopicMarketingDesc => + 'Recibe eventos y beneficios. Solo se activa si aceptas.'; + + @override + String get inquiryPending => 'Recibida'; + + @override + String get inquiryInProgress => 'En proceso'; + + @override + String get inquiryAnswered => 'Respondida'; + + @override + String get photoBadType => 'Solo puedes subir imágenes JPG, PNG, WEBP o GIF.'; + + @override + String get photoTooBig => 'La foto supera los 10 MB. Elige una más pequeña.'; + + @override + String get photoUnreadable => 'No se pudo leer la foto. Elige otra vez.'; + + @override + String get myListInside => 'Mi INSIDE'; + + @override + String get myListInsideEmpty => 'Aún no has publicado en INSIDE.'; + + @override + String get myListCampaign => 'Mis campañas'; + + @override + String get myListCampaignEmpty => 'Aún no has creado campañas.'; + + @override + String get sortLatest => 'Recientes'; + + @override + String get sortPop => 'Por POP'; + + @override + String get sortSolved => 'Aceptadas'; + + @override + String get sortAchieved => 'Logradas'; + + @override + String get payNotFound => 'No se encontró el pago.'; + + @override + String get payItemFallback => 'Artículo comprado'; + + @override + String get payMethodInApp => 'Compra en la app'; + + @override + String get unknownUser => 'Usuario desconocido'; + + @override + String get blockSelf => 'No puedes bloquearte a ti mismo.'; + + @override + String get inquiryFallback => 'Consulta'; + + @override + String get photoRejectedServer => + 'No se pudo subir la foto. Revisa el formato y el tamaño.'; + + @override + String get photoUploadFailed => + 'Error al subir la foto. Inténtalo más tarde.'; + + @override + String focusClaimed(String pop) { + return 'Recibiste la recompensa Focus de hoy: +$pop POP de contribución.'; + } + + @override + String get focusActive => 'Modo Focus sin anuncios activo'; + + @override + String focusUntil(String date) { + return 'Hasta el $date · La recompensa de hoy solo se reclama hoy'; + } + + @override + String get focusClaimedToday => 'Recibida hoy'; + + @override + String get focusClaimToday => 'Reclamar hoy'; + + @override + String get storeConnectFailed => + 'No se pudo conectar con la tienda. Inténtalo más tarde.'; + + @override + String get payRestore => 'Restaurar compras'; + + @override + String get payEmpty => 'No hay historial de pagos.'; + + @override + String get payRefundGuide => + 'Consumibles: reembolsables en 7 días desde la compra si no se usaron. Los artículos usados parcialmente no son reembolsables.\nSuscripciones: reembolsables en 24 horas desde el pago si no se usaron beneficios (p. ej., recompensas extra).\nEl reembolso se completa en 7–14 días tras la aprobación.'; + + @override + String get payDetailTitle => 'Detalle del pago'; + + @override + String get payNumber => 'N.º de pago'; + + @override + String get payDate => 'Fecha de pago'; + + @override + String get payMethod => 'Método de pago'; + + @override + String get payStatus => 'Estado del pago'; + + @override + String get payAmount => 'Importe'; + + @override + String get payRefundInfo => 'Política de reembolso'; + + @override + String get payRefundApply => 'Solicitar reembolso'; + + @override + String get payRefundUnavailable => 'No reembolsable'; + + @override + String get refundReasonMistake => 'Pagué por error'; + + @override + String get refundReasonExpectation => 'No es lo que esperaba'; + + @override + String get refundReasonError => 'No pude usarlo por un error'; + + @override + String get refundReasonDuplicate => 'Pago duplicado'; + + @override + String get refundTitle => 'Reembolso'; + + @override + String get refundSubmit => 'Enviar'; + + @override + String get refundReason => 'Motivo del reembolso'; + + @override + String get refundConfirm => '¿Seguro que quieres solicitar el reembolso?'; + + @override + String get myPop => 'Mi POP'; + + @override + String get commonNoHistory => 'Sin historial.'; + + @override + String get nicknameEdit => 'Editar apodo'; + + @override + String get nicknameReserved => + 'No puedes usar un nombre que parezca del equipo.'; + + @override + String get nicknameTaken => 'Este apodo ya está en uso.'; + + @override + String get albumItems => 'Opciones de foto'; + + @override + String get takePhoto => 'Tomar foto'; + + @override + String get pickFromAlbum => 'Elegir del álbum'; + + @override + String get tierGuide => 'Guía de niveles'; + + @override + String get statSteps => 'Pasos totales'; + + @override + String get statContribution => 'POP aportado total'; + + @override + String get statQuests => 'Misiones completadas'; + + @override + String get shareInstagram => 'Instagram'; + + @override + String get shareTiktok => 'TikTok'; + + @override + String get shareCopyLink => 'Copiar enlace'; + + @override + String get inquiryListTitle => 'Consultas 1:1'; + + @override + String get inquiryEmpty => 'No tienes consultas.'; + + @override + String get inquiryWrite => 'Escribir consulta'; + + @override + String get inquiryDetail => 'Detalle de la consulta'; + + @override + String get commonEdit => 'Editar'; + + @override + String get inquiryAnswer => 'Respuesta'; + + @override + String get inquiryNew => 'Nueva consulta'; + + @override + String get inquiryEdit => 'Editar consulta'; + + @override + String get commonTitle => 'Título'; + + @override + String get commonTitleHint => 'Escribe un título.'; + + @override + String get inquiryBody => 'Detalles'; + + @override + String get inquiryBodyHint => 'Describe tu consulta.'; + + @override + String get inquiryBodyHelper => + 'Con 10 caracteres o más podremos ayudarte antes.'; + + @override + String inquirySubmitFailed(String reason) { + return 'No se pudo enviar la consulta. $reason'; + } + + @override + String inquiryEditFailed(String reason) { + return 'No se pudo editar la consulta. $reason'; + } + + @override + String get inquirySubmitted => 'Consulta enviada.'; + + @override + String get inquiryEdited => 'Consulta actualizada.'; + + @override + String get blockedEmpty => 'No hay cuentas bloqueadas.'; + + @override + String get unblock => 'Desbloquear'; + + @override + String unblockFailed(String reason) { + return 'No se pudo desbloquear. $reason'; + } + + @override + String get tierInfoTitle => 'Info de TIER'; + + @override + String get tierPromptPrefix => 'Sube tu '; + + @override + String get tierPromptRank => 'rango'; + + @override + String get tierPromptSuffix => '\ncontribuyendo a planetas y nebulosas'; + + @override + String get tierPassportNote => + 'Passport Tier es un grado de apariencia de tarjeta, independiente del nivel, y nunca baja.'; + + @override + String get tierTotalContribution => 'Contribución total'; + + @override + String get notifKindEclipse => 'Eclipse'; + + @override + String get notifKindItemUsed => 'Objeto usado'; + + @override + String get notifKindLike => 'Me gusta'; + + @override + String get notifKindComment => 'Comentarios'; + + @override + String get notifKindCampaign => 'Meta de campaña'; + + @override + String get notifKindQuest => 'Misión completada'; + + @override + String get notifTargetPost => 'la publicación'; + + @override + String get notifTargetCampaign => 'la campaña'; + + @override + String get notifTargetQuest => 'el detalle de la misión'; + + @override + String get dayToday => 'Hoy'; + + @override + String get dayYesterday => 'Ayer'; + + @override + String get notifEmpty => 'No hay notificaciones.'; + + @override + String notifGoTo(String target) { + return 'Yendo a $target.'; + } + + @override + String get stationOfficial => 'Oficial'; + + @override + String get stationShop => 'Tiendas'; + + @override + String get stationTicket => 'Entradas'; + + @override + String get stationMedia => 'Medios'; + + @override + String get stationLoadFailed => 'No se pudo cargar STATION'; + + @override + String get commonAll => 'Todo'; + + @override + String get stationSuggest => 'Sugerir tienda'; + + @override + String get stationEmpty => + 'Aún no hay enlaces.\nÚnete a un planeta para ver la selección.'; + + @override + String get stationCategoryEmpty => 'Aún no hay enlaces en esta categoría.'; + + @override + String get stationSuggestLater => + 'Las sugerencias se abrirán al conectar el servidor.'; + + @override + String get commonSubmit => 'Enviar'; + + @override + String get commonName => 'Nombre'; + + @override + String get commonNameRequired => 'Escribe un nombre.'; + + @override + String get commonUrl => 'URL'; + + @override + String get commonUrlRequired => 'Escribe una URL.'; + + @override + String get commonUrlHttps => 'Escribe una URL que empiece por https.'; + + @override + String get commonCategory => 'Categoría'; + + @override + String get stationReason => 'Por qué lo recomiendas (opcional)'; + + @override + String get stationSuggestNote => + 'Los enlaces sugeridos se añaden tras una revisión. Te avisaremos del resultado.'; + + @override + String get heroGiftUnavailable => 'Este regalo no se puede recibir ahora.'; + + @override + String get heroGiftAlready => + 'Ya participaste en este regalo. Revisa tu historial de POP.'; + + @override + String get heroGiftRelogin => 'Vuelve a iniciar sesión para participar.'; + + @override + String get heroGiftNoPermission => + 'No tienes permiso para participar en este regalo.'; + + @override + String get heroGiftHowTo => + 'Agita el móvil o toca el botón para unirte al regalo de energía de luz.'; + + @override + String get heroGiftPopNote => + 'El POP que recibes depende del regalo restante y del límite por persona.'; + + @override + String heroGiftReceived(String total) { + return '$total POP recibidos'; + } + + @override + String get heroGiftUnknown => + 'No se pudo confirmar el pago. Vuelve a comprobarlo.'; + + @override + String get heroGiftNone => 'No hay regalos disponibles ahora.'; + + @override + String get heroGiftConfirmed => 'Se confirmó la recepción del regalo.'; + + @override + String get heroGiftChecking => 'Confirmando pago…'; + + @override + String get heroGiftRetry => 'Volver a comprobar el regalo'; + + @override + String get heroGiftTap => 'Participar con el botón'; + + @override + String rankMyPlanet(String metric) { + return 'Ranking de $metric de mi planeta'; + } + + @override + String get rankPlanetEmpty => 'Aún no hay ranking de planetas'; + + @override + String get rankScopeLive => 'En vivo'; + + @override + String get rankScopeCountry => 'Por país'; + + @override + String rankMine(String metric) { + return 'Mi ranking de $metric'; + } + + @override + String rankMyCountry(String metric) { + return 'Ranking de $metric de mi país'; + } + + @override + String get rankEmpty => 'Aún no hay ranking'; + + @override + String rankScope(String scope) { + return 'Ranking $scope'; + } + + @override + String get rankLoadFailed => 'No se pudo cargar el ranking'; + + @override + String rankMeSuffix(String name) { + return '$name (yo)'; + } + + @override + String get reportDefamation => 'Difamación o insultos al artista'; + + @override + String get reportInappropriate => 'Contenido inapropiado'; + + @override + String get reportSpam => 'Spam o publicidad comercial'; + + @override + String get reportPrivacy => 'Exposición de datos personales o suplantación'; + + @override + String get reportTitle => 'Denunciar'; + + @override + String get reportSubmit => 'Denunciar'; + + @override + String get reportAuthor => 'Autor'; + + @override + String get reportReasonLabel => 'Motivo'; + + @override + String get reportConfirm => + '¿Denunciar esta publicación?\nNo se puede deshacer.'; + + @override + String get blockMemberConfirm => + '¿Bloquear todas las publicaciones de este miembro?'; + + @override + String get loungeMembersOnly => + 'Solo los miembros del fandom de este planeta pueden publicar, comentar o dar me gusta.'; + + @override + String get loungeFailed => 'No se pudo completar. Inténtalo más tarde.'; + + @override + String get loungeTitle => 'Lounge del fandom'; + + @override + String get likeCancel => 'Quitar me gusta'; + + @override + String get like => 'Me gusta'; + + @override + String get commentDeleteConfirm => '¿Eliminar este comentario?'; + + @override + String get commonShare => 'Compartir'; + + @override + String get commonEditAction => 'Editar'; + + @override + String get commonDeleteAction => 'Eliminar'; + + @override + String get commonBlockAction => 'Bloquear'; + + @override + String get commentFirst => 'Sé el primero en comentar.'; + + @override + String get postDeleteConfirm => '¿Eliminar esta publicación?'; + + @override + String get reportReceived => 'Denuncia recibida.'; + + @override + String get commonDelete => 'Eliminar'; + + @override + String get commentHint => 'Escribe un comentario.'; + + @override + String get commonSend => 'Enviar'; + + @override + String get postWrite => 'Escribir'; + + @override + String get postEdit => 'Editar publicación'; + + @override + String get loungeHint => 'Escribe libremente en el lounge del fandom.'; + + @override + String photoRemove(String n) { + return 'Quitar foto $n'; + } + + @override + String get camera => 'Cámara'; + + @override + String get album => 'Álbum'; + + @override + String get historyTitle => 'PASSPORT · Historial'; + + @override + String get historyMine => 'Mi actividad'; + + @override + String historyEvolution(String from, String to) { + return 'Evolución Stage $from → $to'; + } + + @override + String get historyHibernation => 'Planeta en reposo'; + + @override + String get historyWakeUp => 'Planeta despertó'; + + @override + String get historyZap => 'Contribución ZAP'; + + @override + String get historyQuest => 'Recompensa de Quest'; + + @override + String get historyAdBonus => 'Recompensa extra por anuncio'; + + @override + String get historyFunding => 'Contribución a Campaign'; + + @override + String historyGrowthPop(String amount) { + return '$amount POP sumados al crecimiento'; + } + + @override + String get historyJoined => 'Participaste en esta evolución'; + + @override + String get historyLoadFailed => 'No se pudo cargar el historial.'; + + @override + String get historyMineEmpty => 'Aún no hay actividad.'; + + @override + String get historyFandomEmpty => 'Aún no hay historia del fandom.'; + + @override + String get historyMore => 'Ver historial anterior'; + + @override + String countryName(String code) { + String _temp0 = intl.Intl.selectLogic(code, { + 'KR': 'Corea del Sur', + 'JP': 'Japón', + 'US': 'Estados Unidos', + 'CN': 'China', + 'TW': 'Taiwán', + 'HK': 'Hong Kong', + 'TH': 'Tailandia', + 'VN': 'Vietnam', + 'ID': 'Indonesia', + 'PH': 'Filipinas', + 'MY': 'Malasia', + 'SG': 'Singapur', + 'IN': 'India', + 'GB': 'Reino Unido', + 'FR': 'Francia', + 'DE': 'Alemania', + 'ES': 'España', + 'BR': 'Brasil', + 'MX': 'México', + 'CA': 'Canadá', + 'AU': 'Australia', + 'ZZ': 'Sin especificar', + 'other': '-', + }); + return '$_temp0'; + } + + @override + String loungeMaxPhotos(String count) { + return 'Puedes subir hasta $count fotos.'; + } + + @override + String get commonLoadFailed => 'No se pudo cargar'; + + @override + String get planetPassportHistory => 'PASSPORT · Historia del fandom'; + + @override + String planetNamed(String name) { + return 'Planeta $name'; + } + + @override + String get blockNebulaConfirm => '¿Bloquear esta nebulosa?'; + + @override + String get blockPlanetConfirm => '¿Bloquear este planeta?'; + + @override + String get blocked => 'Bloqueado.'; + + @override + String planetLastUpdated(String time) { + return 'Última actualización $time'; + } + + @override + String get planetContribution => 'Contribuciones'; + + @override + String get planetNebulaGrowth => 'Crecimiento de la nebulosa'; + + @override + String get planetLiveRanking => 'Ranking de contribución en vivo'; + + @override + String get planetNoContributors => 'Aún no hay contribuyentes'; + + @override + String get planetContributeCta => '¡Contribuye y entra en el ranking!'; + + @override + String planetOverallRank(String rank) { + return 'Puesto general $rank'; + } + + @override + String get planetMyContribution => 'Mi aporte'; + + @override + String get planetTallying => 'Cálculo pendiente'; + + @override + String planetTopPercent(String percent) { + return 'Top $percent%'; + } + + @override + String get planetHideMember => + 'Ocultar todas las publicaciones de este miembro'; + + @override + String get reportReasonUnknown => 'No se pudo verificar el motivo.'; + + @override + String get reportAlready => 'Ya denunciaste esta publicación.'; + + @override + String get blockUnavailable => 'No se puede bloquear a este autor.'; + + @override + String get commonMe => 'Yo'; + + @override + String get planetSizeMedium => 'Fandom mediano'; + + @override + String planetPerHead(String value) { + return 'Poder por miembro: $value'; + } + + @override + String get planetJoined => 'Unido'; + + @override + String get planetJoin => 'Unirse'; + + @override + String get questDetails => 'Detalles'; + + @override + String get questInProgress => 'En curso'; + + @override + String get questTypeAttend => 'Asistencia'; + + @override + String get questTypeSteps => 'Caminar'; + + @override + String get questTypePlace => 'Ubicación'; + + @override + String get questTypeStreaming => 'Streaming'; + + @override + String get questTypeUnverifiable => + 'Aún no se ha definido cómo verificar este tipo de misión.'; + + @override + String get questTabMine => 'Mis misiones'; + + @override + String get placePickPrompt => 'Elige dónde\ncompletar la misión.'; + + @override + String get placeSearch => 'Buscar lugar'; + + @override + String get commonNoResults => 'Sin resultados.'; + + @override + String get commonSelectDone => 'Listo'; + + @override + String get adMoreChance => 'Consigue más oportunidades de contribuir.'; + + @override + String get adWatchPrompt => + '¡Mira un anuncio para\nconseguir una recompensa extra!'; + + @override + String get adBonusPlaceholder => '+ POP de contribución'; + + @override + String get adPartyPack => 'Pack de fiesta'; + + @override + String get adPickProduct => 'Elegir producto'; + + @override + String get adWatchBonus => 'Ver anuncio y recibir extra'; + + @override + String get adBasicReward => 'Recibir recompensa básica'; + + @override + String get focusMode => 'Modo Focus sin anuncios'; + + @override + String pricePerMonth(String price) { + return '$price/mes'; + } + + @override + String get adWarpBefore => + 'Warping... Buscando el anuncio ideal para que aportes más a tu fandom y artista.'; + + @override + String get adWarpAfter => + 'Warping... Preparando la recompensa por tu contribución.'; + + @override + String get adSkip => 'Omitir'; + + @override + String get questPreviewHint => 'Elige opciones y revisa la vista previa'; + + @override + String get questNoCategory => 'Aún no hay categoría seleccionada'; + + @override + String get questNew => 'Nueva misión'; + + @override + String get questCategory => 'Categoría'; + + @override + String get questName => 'Nombre de la misión'; + + @override + String get questAction => 'Acción detallada'; + + @override + String get questPickPlace => 'Elige la ubicación objetivo'; + + @override + String get questGoalValue => 'Meta'; + + @override + String get questParticipants => 'Participantes'; + + @override + String get questRewardPop => 'POP de recompensa'; + + @override + String get questCreateConfirm => '¿Crear con estos datos?'; + + @override + String questCreateSummary(String title, String goal, String reward) { + return '$title · Meta $goal · Recompensa $reward POP'; + } + + @override + String get commonKeepEditing => 'Seguir editando'; + + @override + String get questCreate => 'Crear'; + + @override + String questNamed(String title) { + return 'Misión $title'; + } + + @override + String get questDailyHelp => 'Ayuda a tu planeta cada día'; + + @override + String get questTodayMust => 'Misiones para hoy'; + + @override + String get questTabEmpty => 'Aún no hay misiones en esta pestaña.'; + + @override + String get questCreateNotReady => + 'La creación de misiones se abrirá junto con los fondos de recompensa. Próximamente.'; + + @override + String get questNoBaseReward => + 'No se encontró la recompensa básica, así que no hay extra por anuncio.'; + + @override + String get questNoSteps => + 'Aún no hay pasos registrados. Camina un poco y vuelve a intentarlo.'; + + @override + String get questNoPlace => 'Esta misión no tiene una ubicación registrada.'; + + @override + String get questGoal => 'Meta'; + + @override + String get questReward => 'Recompensa'; + + @override + String get questPopGrant => 'Recompensa de POP'; + + @override + String get commonChecking => 'Comprobando…'; + + @override + String questMine(String count) { + return 'Es tu misión. Participantes: $count.'; + } + + @override + String get questStopConfirm => '¿Detener esta misión?'; + + @override + String get questStopBody => 'Tu progreso se reiniciará desde el principio.'; + + @override + String get questStop => 'Detener'; + + @override + String get adNotFinished => + 'No terminaste el anuncio, así que no hay extra. Recibes la recompensa básica.'; + + @override + String get openSettings => 'Abrir ajustes'; + + @override + String get permStepsDenied => + 'El permiso de pasos está desactivado. Permite \"Actividad física\" en Ajustes.'; + + @override + String get permStepsNeeded => + 'Se necesita el permiso de actividad física para contar pasos.'; + + @override + String get permLocationOff => + 'La ubicación del dispositivo está desactivada. Actívala e inténtalo de nuevo.'; + + @override + String get permLocationNeeded => + 'Se necesita el permiso de ubicación para verificar tu visita.'; + + @override + String get permLocationDenied => + 'El permiso de ubicación está desactivado. Permite el acceso en Ajustes.'; + + @override + String get questRewardReceived => ' ¡recibidos!'; + + @override + String questAdBonus(String pop) { + return 'Extra por anuncio +$pop POP'; + } + + @override + String questParticipantsCount(String count) { + return 'Participantes: $count'; + } + + @override + String questParticipantsCap(String count, String cap) { + return 'Participantes: $count / $cap'; + } + + @override + String get nebulaCreateFailed => + 'No se pudo crear la nebulosa. Inténtalo más tarde.'; + + @override + String get nebulaAlreadyToday => 'Ya creaste una nebulosa hoy.'; + + @override + String get nebulaCreatedKeepPrimary => + 'Nebulosa creada. Tu planeta principal no cambia: solo puede cambiarse 30 días después del último cambio.'; + + @override + String get fandomFounderLeaveBlocked => + 'Podrás salir cuando nazca el planeta.'; + + @override + String get sortPopular => 'Populares'; + + @override + String get campaignSortContributing => 'Contribuyendo'; + + @override + String get campaignStatusReview => 'Esta campaña está en revisión.'; + + @override + String get campaignStatusRejected => + 'Esta campaña fue rechazada en la revisión. Te hemos devuelto la tarifa de registro.'; + + @override + String get campaignStatusWithdrawn => 'El creador retiró esta campaña.'; + + @override + String get campaignStatusCancelled => + 'Se canceló por motivos operativos. Te hemos devuelto la tarifa de registro y los POP aportados.'; + + @override + String get rankWindowDaily => 'Diario'; + + @override + String get rankWindowWeekly => 'Semanal'; + + @override + String get campaignNotFound => 'No se encontró la campaña.'; + + @override + String get campaignMediaMain => 'Imagen principal'; + + @override + String get campaignLoadFailed => 'No se pudo cargar la campaña.'; + + @override + String get campaignTabInfo => 'Información'; + + @override + String campaignTabBoard(String count) { + return 'Apoyos $count'; + } + + @override + String get campaignWithdraw => 'Retirar campaña'; + + @override + String get campaignContributeAction => 'Contribuir'; + + @override + String get campaignFundingStatus => 'Estado de la financiación'; + + @override + String get campaignSupporting => 'Apoyando'; + + @override + String get campaignTimeLeft => '¡Restante!'; + + @override + String get campaignReport => 'Informe de resultados'; + + @override + String get campaignReportPhotos => 'Fotos de ejecución'; + + @override + String get campaignBudget => 'Desglose del presupuesto'; + + @override + String get campaignTotal => 'Total'; + + @override + String get campaignReceipts => 'Recibos'; + + @override + String campaignDaysLeft(String days, String time) { + return '$days d $time'; + } + + @override + String get campaignWithdrawTitle => '¿Retirar esta campaña?'; + + @override + String campaignWithdrawRefund(String fee) { + return 'Como aún no es pública, te devolveremos la tarifa de registro de $fee POP tal como la pagaste.'; + } + + @override + String campaignWithdrawNoRefund(String fee) { + return 'La campaña ya es pública, por lo que la tarifa de registro de $fee POP no se devolverá.'; + } + + @override + String get campaignWithdrawConfirm => 'Retirar'; + + @override + String get shareCopied => 'Texto para compartir copiado.'; + + @override + String get campaignContributePop => 'Elige los POP a aportar'; + + @override + String get campaignEmptyContributing => + 'Aún no has contribuido a ninguna campaña.'; + + @override + String get campaignEmpty => 'No hay campañas.'; + + @override + String get campaignNew => 'Nueva campaña'; + + @override + String get campaignMyRanking => 'Mi ranking de contribución'; + + @override + String campaignWindowRanking(String window) { + return 'Ranking $window'; + } + + @override + String get campaignSearchHint => 'Buscar campañas'; + + @override + String get campaignNoRecentSearches => 'No hay búsquedas recientes'; + + @override + String searchRemoveTerm(String term) { + return 'Eliminar $term'; + } + + @override + String get campaignNoResults => 'Sin resultados'; + + @override + String get shareTopicCampaign => 'Campaña'; + + @override + String sharePlanetLine(String brand, String name) { + return '$brand · Planeta $name'; + } + + @override + String get campaignGaugeLabel => 'Progreso de la campaña'; + + @override + String get campaignPeriodTitle => 'Elige el periodo de la campaña'; + + @override + String get campaignPeriodMax => 'Puedes elegir hasta un año'; + + @override + String campaignYearMonth(String year, String month) { + return '$month/$year'; + } + + @override + String get campaignWeekdays => 'D,L,M,X,J,V,S'; + + @override + String campaignPeriodRange( + String startMonth, + String startDay, + String endMonth, + String endDay, + ) { + return '$startDay/$startMonth ~ $endDay/$endMonth'; + } + + @override + String get campaignPeriodSelect => 'Seleccionar periodo'; + + @override + String get calendarPrevMonth => 'Mes anterior'; + + @override + String get calendarNextMonth => 'Mes siguiente'; + + @override + String get campaignShareTitle => 'Compartir campaña'; + + @override + String get campaignPickMedia => 'Elegir imagen/vídeo'; + + @override + String get campaignTitleHint => 'Escribe el título de la campaña.'; + + @override + String get campaignBodyHint => 'Describe tu campaña.'; + + @override + String get campaignReviewNote => + 'Tras enviarla, la financiación se abre después de la revisión.'; + + @override + String get campaignSupportType => 'Elige el tipo de apoyo'; + + @override + String get campaignPeriod => 'Periodo de la campaña'; + + @override + String get campaignDateSelect => 'Seleccionar fechas'; + + @override + String get campaignGoalPop => 'POP objetivo'; + + @override + String get campaignSubmitTitle => '¿Enviar esta campaña?'; + + @override + String campaignSubmitBody( + String title, + String goal, + String days, + String fee, + ) { + return '$title · Objetivo $goal POP · $days días\nSe descontará la tarifa de registro de $fee POP y la financiación se abrirá tras la revisión.'; + } + + @override + String get campaignSubmit => 'Enviar'; + + @override + String get campaignCreateFailed => 'No se pudo enviar la campaña.'; + + @override + String campaignNeedsQuests(String done, String need, String left) { + return 'Misiones completadas $done/$need: completa $left más para abrir una campaña.'; + } + + @override + String get campaignPendingTitle => 'Pendiente de revisión'; + + @override + String get campaignPendingBody => + 'Cuando termine la revisión, se abrirá la financiación y aparecerá en la lista.'; + + @override + String get insideRefundPending => + 'El plazo de aceptación venció. Se reembolsará la recompensa.'; + + @override + String get insideRefundDone => + 'El plazo de aceptación venció. Se reembolsó la recompensa.'; + + @override + String get insideRefundChecking => + 'El plazo de aceptación venció. Estamos revisando el reembolso de la recompensa.'; + + @override + String get insideErrPopShort => + 'No tienes POP suficientes para la recompensa.'; + + @override + String get insideErrSelfAccept => 'No puedes aceptar tu propia respuesta.'; + + @override + String get insideErrAlreadyAccepted => + 'Ya se aceptó una respuesta y no se puede cambiar.'; + + @override + String get insideErrNotOwner => + 'Solo quien preguntó puede aceptar una respuesta.'; + + @override + String get commonCheckConnection => + 'Revisa tu conexión e inténtalo de nuevo.'; + + @override + String get insideAnswerLater => + 'Los comentarios se abrirán cuando se conecte el servidor.'; + + @override + String insideAcceptConfirm(String pop) { + return '¿Aceptar este comentario?\nSe entregará la recompensa de $pop POP y no se podrá deshacer.'; + } + + @override + String get insideAccept => 'Aceptar'; + + @override + String get insideMore => 'Más'; + + @override + String get insideFirstAnswer => 'Sé el primero en comentar.'; + + @override + String get insideAcceptDone => 'Aceptada'; + + @override + String get insideAcceptAction => 'Aceptar'; + + @override + String get insideAnswerHint => 'Escribe un comentario.'; + + @override + String get insideLoadFailed => 'No se pudo cargar INSIDE'; + + @override + String get insideEmpty => 'Aún no hay preguntas.'; + + @override + String get insideWrite => 'Escribir INSIDE'; + + @override + String get insideSearchHint => 'Título o contenido de INSIDE'; + + @override + String get insideBodyHint => 'Pregunta y responde libremente con otros fans.'; + + @override + String get insideTitleRequired => 'Escribe un título.'; + + @override + String get insideBodyRequired => 'Escribe el contenido.'; + + @override + String get insideBountySet => 'Fijar recompensa'; + + @override + String insideBountyHint(String min) { + return 'Fija $min o más.'; + } + + @override + String get insideAttachLater => + 'Los adjuntos de imagen se abrirán cuando se conecte el servidor.'; + + @override + String get insideBountyRequired => 'Indica una recompensa.'; + + @override + String get insideNumberOnly => 'Introduce un número.'; + + @override + String insideBountyMin(String min) { + return 'La recompensa debe ser de al menos $min POP.'; + } + + @override + String get insideBountyOverBalance => 'No puede superar tus POP disponibles.'; + + @override + String get questTypeTimeAttack => 'Contrarreloj relámpago'; + + @override + String get questTimeLeft => 'Tiempo restante'; + + @override + String get questTimeAttackClosed => 'Cerrado'; + + @override + String questTimeAttackLeft(String time) { + return 'Quedan $time'; + } + + @override + String get popReasonQuestTimeAttack => 'Contrarreloj relámpago'; + + @override + String get briefingShareCopied => 'Texto y enlace copiados.'; + + @override + String get shareCopyText => 'Copiar texto'; + + @override + String get briefingShareTextCopied => 'Texto copiado.'; + + @override + String questAlreadyRewarded(String pop) { + return 'Recompensa ya recibida (+$pop POP).'; + } + + @override + String get questCatHealth => 'Salud'; + + @override + String get questCatPlanet => 'Actividad del planeta'; + + @override + String get questCatCommunity => 'Comunidad'; + + @override + String get questActShake => 'Shake'; + + @override + String get questTypeVote => 'Votar'; + + @override + String get questTypeSns => 'Redes sociales'; + + @override + String get questActivity => 'Actividad'; + + @override + String get questNoCreatable => + 'Ahora no hay actividades disponibles para crear.'; + + @override + String get questDescriptionOptional => 'Descripción (opcional)'; + + @override + String get questLinkUrl => 'URL del enlace'; + + @override + String get questLinkCheck => 'Comprobar enlace'; + + @override + String get questLinkInvalid => 'El enlace no es válido. Revisa la dirección.'; + + @override + String get questLinkPreviewFailed => + 'No se pudo cargar el enlace. Escribe un título.'; + + @override + String get questLinkTitle => 'Título del enlace'; + + @override + String get questEndDate => 'Fecha de fin'; + + @override + String get questEndDefault => '24 horas tras publicar'; + + @override + String questEndDateValue(String date) { + return 'Termina el $date 23:59:59'; + } + + @override + String questPoolHelper(String pool, String fee) { + return 'Depósito $pool POP · Comisión $fee POP'; + } + + @override + String questCreateCost( + String title, + String reward, + String count, + String pool, + String fee, + String total, + ) { + return '$title\nRecompensa $reward POP × $count = $pool POP\nComisión $fee POP (se quema al publicar)\nSe descontarán $total POP en total.'; + } + + @override + String get questNoReward => 'Esta misión no tiene recompensa.'; + + @override + String get questDeadline => 'Fecha límite'; + + @override + String questRemainingHours(String hours) { + return 'Quedan $hours h'; + } + + @override + String get questEnded => 'Finalizada'; + + @override + String get questAuthorNote => 'Nota del creador'; + + @override + String get questRepeatOnce => 'Una participación por persona'; + + @override + String questRepeatEvery(String hours, String count) { + return 'Cada $hours h · hasta $count veces'; + } + + @override + String get questSubmit => 'Enviar prueba'; + + @override + String get questResubmit => 'Reenviar'; + + @override + String get questEvidenceUrl => 'Enlace de prueba (opcional)'; + + @override + String get questEvidenceText => 'Detalles de la prueba'; + + @override + String get questSubmitted => + 'Enviado. La recompensa se paga tras la revisión.'; + + @override + String get questReviewPending => + 'En revisión. La recompensa se paga al aprobarse.'; + + @override + String get questReviewDelayed => + 'La revisión se está retrasando. Nuestro equipo lo está revisando.'; + + @override + String get questApprovedPaying => 'Aprobado. Pagando tu recompensa.'; + + @override + String questRejected(String reason) { + return 'Rechazado: $reason'; + } + + @override + String get questResubmitAllowed => 'Puedes corregir y reenviar.'; + + @override + String get questResubmitNotAllowed => 'Esta entrada no se puede reenviar.'; + + @override + String get questReviewButton => 'En revisión'; + + @override + String get questRejectedButton => 'Rechazado'; + + @override + String questReviewQueue(String count) { + return '$count pendientes de revisión'; + } + + @override + String get questApprove => 'Aprobar'; + + @override + String get questReject => 'Rechazar'; + + @override + String get questRejectReason => 'Motivo'; + + @override + String get questAllowResubmit => 'Permitir reenvío'; + + @override + String get questEdit => 'Editar título y descripción'; + + @override + String get questEditNotice => + 'Tras publicar solo puedes editar el título y la descripción, y se guarda el historial.'; + + @override + String get popReasonQuestUgc => 'Recompensa de misión'; + + @override + String get popReasonQuestFee => 'Comisión por crear misión'; + + @override + String questFreeDailyCap(String cap) { + return 'Puedes crear hasta $cap misiones sin recompensa al día.'; + } + + @override + String get popReasonQuestPoolDeposit => 'Depósito de recompensa'; + + @override + String get popReasonQuestPoolReturn => 'Devolución de recompensa'; + + @override + String get campaignCommentNeedNickname => 'Primero elige un apodo'; + + @override + String get campaignCommentSetNickname => 'Elegir apodo'; +} diff --git a/app/lib/l10n/app_localizations_ja.dart b/app/lib/l10n/app_localizations_ja.dart new file mode 100644 index 0000000..8ea87f3 --- /dev/null +++ b/app/lib/l10n/app_localizations_ja.dart @@ -0,0 +1,2658 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Japanese (`ja`). +class AppLocalizationsJa extends AppLocalizations { + AppLocalizationsJa([String locale = 'ja']) : super(locale); + + @override + String get commonConfirm => '確認'; + + @override + String get commonCancel => 'キャンセル'; + + @override + String get commonClose => '閉じる'; + + @override + String get commonSelect => '選択'; + + @override + String get commonRetry => '再試行'; + + @override + String get commonView => '見る'; + + @override + String get commonPrev => '戻る'; + + @override + String get commonDone => '完了'; + + @override + String get commonStart => 'はじめる'; + + @override + String commonNotYet(String where) { + return '$where画面は第2次実装確認(2026-09-10)で追加されます。'; + } + + @override + String get languageSettingsTitle => '言語設定'; + + @override + String get authLogin => 'ログイン'; + + @override + String get authSignup => '新規登録'; + + @override + String get authFirstTime => 'FANUZUは初めてですか?'; + + @override + String get authAlreadyMember => 'すでに登録済みですか?'; + + @override + String get authLoginHeadline => 'いちばん簡単な方法で\nFANUZUにログイン!'; + + @override + String get authSignupHeadline => 'いちばん簡単な方法で\nFANUZUに新規登録!'; + + @override + String authProviderLogin(String provider) { + String _temp0 = intl.Intl.selectLogic(provider, { + 'apple': 'Appleでログイン', + 'google': 'Googleでログイン', + 'kakao': 'Kakaoでログイン', + 'line': 'LINEでログイン', + 'other': 'メールでログイン', + }); + return '$_temp0'; + } + + @override + String authProviderSignup(String provider) { + String _temp0 = intl.Intl.selectLogic(provider, { + 'apple': 'Appleで登録', + 'google': 'Googleで登録', + 'kakao': 'Kakaoで登録', + 'line': 'LINEで登録', + 'other': 'メールで登録', + }); + return '$_temp0'; + } + + @override + String authNotConfiguredTitle(String name) { + return '$nameログイン準備中'; + } + + @override + String authNotConfiguredBody(String name, String items) { + return '$nameログインは発注元のキー提供後に利用できます。\n\n必要な項目\n· $items'; + } + + @override + String authNotConfiguredNotice(String name, String items) { + return '$nameログインはまだ準備中です。\n必要な項目: $items'; + } + + @override + String get authResetNeedEmail => '再設定メールを受け取るメールアドレスを先に入力してください。'; + + @override + String get authResetSent => '入力されたアドレスに再設定の案内を送信しました。'; + + @override + String get termsTitle => '利用規約'; + + @override + String get termsAgreeAll => 'すべて同意'; + + @override + String get termsService => 'サービス利用規約(必須)'; + + @override + String get termsLocation => '位置情報サービス利用規約(必須)'; + + @override + String get termsAge => '14歳以上です。(必須)'; + + @override + String get termsMarketing => 'マーケティング情報・イベント通知の受信に同意(任意)'; + + @override + String get signupDoneTitle => '新規登録が完了しました'; + + @override + String get signupDoneBody => 'ファンダム活動を始められます。\nさまざまな活動でファンダム惑星を育てましょう。'; + + @override + String get homeItemsTooltip => 'アイテム'; + + @override + String get homeNotificationsTooltip => '通知'; + + @override + String get homeFandomHistory => 'ファンダムの歴史'; + + @override + String get homePopularInside => '人気 K-INSIDE'; + + @override + String get homeWhereBriefing => 'AIブリーフィング'; + + @override + String get homeWhereInsideList => 'INSIDE一覧'; + + @override + String get homeWhereInsideDetail => 'INSIDE詳細'; + + @override + String get homeLoadFailed => 'ホームを読み込めませんでした'; + + @override + String get homeRetryLater => 'しばらくしてから再度お試しください。'; + + @override + String get myProfileCardTooltip => 'プロフィールカード'; + + @override + String get myTabPlanets => '惑星/星雲'; + + @override + String get myTabActivity => 'アクティビティ'; + + @override + String get myInside => 'インサイド'; + + @override + String get myCampaign => 'キャンペーン'; + + @override + String myShareLater(String target) { + return '$targetへの共有は09-10回で接続します。'; + } + + @override + String get myEditPhoto => 'プロフィール写真を編集'; + + @override + String get myEditProfile => 'プロフィールを編集'; + + @override + String get settingsTitle => '設定'; + + @override + String get settingsAccount => 'アカウント'; + + @override + String get settingsNickname => 'ニックネーム'; + + @override + String get settingsEmail => 'メール'; + + @override + String get settingsSns => 'SNS連携アカウント'; + + @override + String get settingsPopHistory => 'POP履歴'; + + @override + String get settingsPayments => '決済履歴'; + + @override + String get settingsServicePayments => 'サービス決済履歴'; + + @override + String get settingsService => 'サービス設定'; + + @override + String get settingsPush => 'プッシュ通知設定'; + + @override + String get settingsBlocked => 'ブロック設定'; + + @override + String get settingsInfo => 'サービス情報'; + + @override + String get settingsNotice => 'お知らせ'; + + @override + String get settingsInquiry => '1:1お問い合わせ'; + + @override + String get settingsAppVersion => 'アプリバージョン'; + + @override + String get settingsLatest => '最新バージョン'; + + @override + String get settingsLogout => 'ログアウト'; + + @override + String get settingsLogoutConfirm => 'ログアウトしますか?'; + + @override + String get settingsWithdraw => '退会'; + + @override + String get settingsWithdrawConfirm => '退会しますか?'; + + @override + String get settingsWithdrawBody => '保有しているPOPは消滅し、元に戻せません。'; + + @override + String get settingsWithdrawLater => '退会処理は09-10回で接続します。'; + + @override + String get settingsUnlinkConfirm => '連携中のアカウントを解除しますか?'; + + @override + String get settingsUnlinkBody => '連携したSNSのログイン手段のみ削除され、アカウント情報は削除されません。'; + + @override + String get settingsUnlink => '連携解除'; + + @override + String get settingsSameEmailOnly => 'アカウントのメールと同じ\nソーシャルアカウントのみ連携できます。'; + + @override + String settingsLinkNotReady(String name, String items) { + return '$name連携はまだ準備中です。必要な項目: $items'; + } + + @override + String get settingsConnect => '連携'; + + @override + String get settingsDisconnect => '解除'; + + @override + String get settingsEmailProvider => 'メール'; + + @override + String get pushDeviceOff => '端末のプッシュ通知がオフになっています。'; + + @override + String get pushDeviceOffHint => '通知を受け取るにはシステム設定で通知を許可してください。'; + + @override + String get pushRequesting => 'リクエスト中…'; + + @override + String get pushGoToSettings => '設定へ移動'; + + @override + String get pushOpeningDeviceSettings => '端末の設定に移動します。'; + + @override + String get pushEnableInDevice => '端末の設定でFANUZUの通知をオンにしてください。'; + + @override + String get pushDeviceSection => '端末の通知設定'; + + @override + String get pushSoundVibration => 'サウンド・バイブ設定'; + + @override + String get pushServiceSection => 'サービス通知設定'; + + @override + String get pushTopicEssential => 'サービス必須'; + + @override + String get pushTopicFandomEvent => 'ファンダムイベント'; + + @override + String get pushTopicBriefing => 'AI Briefing'; + + @override + String get pushTopicCommunity => 'コミュニティ'; + + @override + String get pushTopicMarketing => 'マーケティング・広報'; + + @override + String linkTitle(String name) { + return '$name連携'; + } + + @override + String get linkNotLinked => '未連携'; + + @override + String get linkLinked => '連携済み'; + + @override + String get linkSameEmailOnly => 'アカウントのメールと同じソーシャルアカウントのみ連携できます。'; + + @override + String get linkUnlink => '連携解除'; + + @override + String get linkLink => '連携する'; + + @override + String get noticeBadge => 'お知らせ'; + + @override + String get noticeTitle => 'お知らせ'; + + @override + String get noticeEmpty => 'お知らせはありません。'; + + @override + String get noticeDetailTitle => 'お知らせ詳細'; + + @override + String get translationMark => '翻訳済み'; + + @override + String translationReplyTo(String name) { + return '$nameさんのコメントを翻訳'; + } + + @override + String get translationWrite => '翻訳コメントを書く'; + + @override + String get translationHint => '翻訳を入力してください'; + + @override + String get translationValid => '有効'; + + @override + String get translationInvalid => '無効'; + + @override + String get translationSelfMarkBlocked => '自分の翻訳はマークできません。'; + + @override + String get translationSubmitted => '翻訳を登録しました。検証を通過すると24時間後にPOPが付与されます。'; + + @override + String get translationMarked => 'マークしました。'; + + @override + String get translationAlreadyMarked => 'すでにマークした翻訳です。'; + + @override + String get translationDuplicate => 'この投稿にはすでに同じ言語で翻訳を登録しています。'; + + @override + String get translationFailed => '処理できませんでした。しばらくしてから再度お試しください。'; + + @override + String get translationNotEligible => '登録から24時間以上経過したこのファンダムのメンバーのみマークできます。'; + + @override + String get translationDailyCap => '本日はこれ以上翻訳を登録できません。'; + + @override + String get translationInvalidTarget => 'このコンテンツは翻訳できません。'; + + @override + String get commonRequestFailed => 'リクエストを処理できませんでした。'; + + @override + String get commonFan => 'ファン'; + + @override + String get adLoadFailed => '広告を読み込めませんでした。基本報酬のみ受け取ります。'; + + @override + String get storeNotReady => 'ストア商品準備中'; + + @override + String get storePending => '決済の承認を待っています。承認されると自動で反映されます。'; + + @override + String get storeFailed => '決済を完了できませんでした。'; + + @override + String get storePurchased => '購入が反映されました。'; + + @override + String get heroParticipate => 'ZAP+ · ギフトに参加'; + + @override + String heroRemainingSemantics(String minutes, String seconds) { + return 'HERO TIME 残り$minutes分$seconds秒'; + } + + @override + String heroRemaining(String time) { + return 'HERO TIME · 残り$time'; + } + + @override + String heroClaims(String count) { + return 'ファンのギフト受取 $count件'; + } + + @override + String heroInProgress(String name) { + return '$name · HERO TIME 開催中 →'; + } + + @override + String get stageNames => '星雲,始まり,成長,共に,同行,光,永遠'; + + @override + String get planetStageUnknown => '惑星 · ステージ情報なし'; + + @override + String get planetHibernatingSuffix => ' · 休眠中'; + + @override + String get planetEclipseSuffix => ' · Eclipse進行中'; + + @override + String get planetHeroSuffix => ' · HERO TIME開催中'; + + @override + String get evoPlanetGrowth => '惑星の成長'; + + @override + String get evoHeroEnd => 'HERO TIME 終了'; + + @override + String get evoAwakening => '惑星が目覚めます'; + + @override + String get evoAnomaly => '異常反応'; + + @override + String get evoEnergy => 'エネルギー集中'; + + @override + String get evoTransition => '転換'; + + @override + String get evoComplete => '惑星の進化完了'; + + @override + String get evoOpenHistory => 'PASSPORTの記録を見る'; + + @override + String get playNow => '今すぐプレイ'; + + @override + String get playHeadline => 'さあ、ファンダムに会いに行こう!'; + + @override + String get playBody => '惑星を作ってファンダムと交流しながら楽しいファン活動...'; + + @override + String get timeJustNow => 'たった今'; + + @override + String timeMinutesAgo(String n) { + return '$n分前'; + } + + @override + String timeHoursAgo(String n) { + return '$n時間前'; + } + + @override + String timeDaysAgo(String n) { + return '$n日前'; + } + + @override + String get commonMore => 'もっと見る'; + + @override + String authProviderStart(String provider) { + String _temp0 = intl.Intl.selectLogic(provider, { + 'email': 'メールではじめる', + 'kakao': 'Kakaoではじめる', + 'line': 'LINEではじめる', + 'apple': 'Appleではじめる', + 'other': 'Googleではじめる', + }); + return '$_temp0'; + } + + @override + String authProviderName(String provider) { + String _temp0 = intl.Intl.selectLogic(provider, { + 'email': 'メール', + 'kakao': 'Kakao', + 'line': 'LINE', + 'apple': 'Apple', + 'other': 'Google', + }); + return '$_temp0'; + } + + @override + String get authLastProvider => '最後のログイン手段は連携解除できません。'; + + @override + String get authLoginRequired => 'ログインが必要です。'; + + @override + String get authDifferentEmail => '登録したメールアドレスと異なるアカウントです。'; + + @override + String get authLinkFailed => 'アカウントの連携に失敗しました。'; + + @override + String get authNotLinked => '連携されていないアカウントです。'; + + @override + String get authEmailUnavailable => 'メールログインは利用できません。'; + + @override + String get authCheckCredentials => 'メールアドレスまたはパスワードをご確認ください。'; + + @override + String get authSocialFailed => 'ソーシャルログインに失敗しました。'; + + @override + String get authAppleUnavailable => 'この端末ではAppleでログインを利用できません。'; + + @override + String get authEmailInUse => 'すでに登録されているメールアドレスです。'; + + @override + String get authWeakPassword => 'パスワードが短すぎます。8文字以上で設定してください。'; + + @override + String get authInvalidEmail => 'メールアドレスの形式が正しくありません。'; + + @override + String get commonTryLater => 'しばらくしてから再度お試しください。'; + + @override + String get authCredentialInUse => '別のFANUZUアカウントにすでに連携されているソーシャルアカウントです。'; + + @override + String get authAlreadyLinked => 'すでに連携済みのアカウントです。'; + + @override + String get authMethodUnavailable => 'このログイン手段はまだ利用できません。'; + + @override + String get authRecentLogin => 'セキュリティのため、再度ログインしてからお試しください。'; + + @override + String authOtherProviderGuide(String email) { + return '$email アカウントは別のログイン手段で登録されています。最初に登録した手段でログインし、MY > 設定 > SNS連携でこの手段を連携してください。'; + } + + @override + String get authThisAccount => 'この'; + + @override + String get eclipseCapped => '参加上限に達したため、一部は集計されませんでした。'; + + @override + String get eclipseResultUnknown => '参加結果を確認できませんでした。同じリクエストで再度ご確認ください。'; + + @override + String get eclipseChecking => '参加を確認中'; + + @override + String get eclipseRetrySame => '同じ参加を再確認'; + + @override + String get eclipseEnded => 'イクリプスはすでに終了したか、防衛に成功しました。'; + + @override + String get eclipseTooFast => '参加が速すぎます。しばらくしてから再度お試しください。'; + + @override + String get eclipseNone => '進行中のイクリプスはありません'; + + @override + String get eclipseSuccessTitle => '隕石の衝突を\n見事に防ぎました!'; + + @override + String get eclipseQualified => '参加認定完了'; + + @override + String get eclipseFailTitle => '惜しくも惑星の防衛に\n失敗しました'; + + @override + String get eclipseFailBody => '今回の防衛目標を達成できませんでした。'; + + @override + String get eclipseCancelTitle => 'イクリプス中止'; + + @override + String get eclipseCancelBody => '集計エラーのため中止されました。FPIは差し引かれていません。'; + + @override + String get eclipsePendingTitle => '防衛結果を確認中'; + + @override + String get eclipsePendingBody => '参加時間が終了しました。サーバーの判定を待っています。'; + + @override + String get eclipseShakeTimed => '秒間スマホを振って\n隕石の衝突を防ごう!'; + + @override + String get eclipseShake => 'スマホを振って\n隕石の衝突を防ごう!'; + + @override + String eclipseMyCount(String count) { + return '自分の参加 $count回'; + } + + @override + String eclipsePendingSuffix(String count) { + return ' · +$count 送信待ち'; + } + + @override + String get eclipseSendingSuffix => ' · 送信中'; + + @override + String eclipseNoPopNote(String count) { + return 'POPは消費されません。有効Shakeを$count回以上振って防衛に参加すると参加者として認定されます。'; + } + + @override + String get eclipseTapToJoin => '振るのが難しければタップで参加'; + + @override + String get eclipseTimeLeft => 'イクリプス残り時間:'; + + @override + String eclipseSeconds(String seconds) { + return ' $seconds秒'; + } + + @override + String eclipseBannerDefended(String name) { + return '防衛成功 — $name FPI減少なし'; + } + + @override + String eclipseBannerActive(String name, String rate) { + return 'イクリプス発動 — $name 侵食 $rate%'; + } + + @override + String eclipseBannerDefense(String count, String target) { + return '有効参加 $count / $target人'; + } + + @override + String timeHoursMinutesLeft(String hours, String minutes) { + return '残り$hours時間$minutes分'; + } + + @override + String timeMinutesLeft(String minutes) { + return '残り$minutes分'; + } + + @override + String liveEclipseDefense(String name, String count) { + return '[LIVE] $nameさんがイクリプス防衛に参加しました!(+$count)'; + } + + @override + String liveHotTime(String name) { + return '[LIVE] $nameさんのアーティストホットタイムが始まりました!'; + } + + @override + String liveHeroGift(String name) { + return '[LIVE] $nameさんの英雄のギフトが届きました!'; + } + + @override + String liveCampaign(String name) { + return '[LIVE] $nameさんがキャンペーンを支援しました!'; + } + + @override + String get galaxySetPrimaryConfirm => '選択した惑星をメイン惑星に変更しますか?'; + + @override + String get galaxyLoadFailed => 'ギャラクシーを読み込めませんでした'; + + @override + String get commonSearch => '検索'; + + @override + String get galaxyMyPlanets => 'マイ惑星'; + + @override + String get galaxyMap => 'ギャラクシーマップ'; + + @override + String get nebulaCreate => '星雲を作成'; + + @override + String get commonRecheck => '再確認'; + + @override + String get zapApplying => '貢献を反映中'; + + @override + String get zapNeedsCheck => '貢献結果の確認が必要'; + + @override + String get zapNoPlanet => '所属惑星がありません'; + + @override + String get commonContribute => '貢献'; + + @override + String get commonItem => 'アイテム'; + + @override + String get galaxyDefended => '防衛成功 · FPI減少なし'; + + @override + String galaxyErosion(String rate, String progress) { + return '侵食 $rate% · 防衛 $progress%'; + } + + @override + String galaxyOnlineMembers(String online, String members) { + return '接続 $online人 · メンバー $members人'; + } + + @override + String get galaxySearchHint => 'ファンダム名で検索'; + + @override + String get commonRecentSearches => '最近の検索'; + + @override + String get commonClearAll => 'すべて削除'; + + @override + String get commonNoRecentSearches => '最近の検索はありません。'; + + @override + String get galaxyNoHit => '該当するファンダムがありません。\n名前をもう一度ご確認ください。'; + + @override + String galaxyMembers(String count) { + return 'メンバー $count人'; + } + + @override + String galaxyPlanetRank(String name, String rank) { + return '$name 惑星 $rank位'; + } + + @override + String get galaxyMapOpen => 'ギャラクシーマップを開く'; + + @override + String get zapNotEnoughPop => 'POPが足りません'; + + @override + String get zapFailNoPop => 'POPが足りず貢献できませんでした。'; + + @override + String get zapFailLedger => '残高と獲得履歴が一致しないため、貢献を停止しました。'; + + @override + String get zapFailRetry => '接続を確認して、同じ貢献を再度ご確認ください。'; + + @override + String get zapDone => '貢献完了'; + + @override + String zapDonePop(String pop) { + return '$pop POP 貢献完了'; + } + + @override + String get growthFounders => '創立メンバー'; + + @override + String get growthNebulaGoalPassed => '目標期間が過ぎました · 条件を満たすと惑星が誕生します'; + + @override + String get growthFinalStage => '最終ステージ'; + + @override + String get growthPromotionPending => '昇格確定待ち'; + + @override + String get growthCheckConditions => '成長条件を確認'; + + @override + String growthConditionChecking(String name) { + return '$name 条件を確認中'; + } + + @override + String growthConditionMet(String name) { + return '$name 条件達成'; + } + + @override + String growthConditionUnmet(String name) { + return '$name 条件未達成'; + } + + @override + String get nebulaHeadline => 'ファンダム惑星の名前を決めよう。'; + + @override + String nebulaBody(String days) { + return '創立メンバーが$days日以内に目標POPを達成すると\n正式な惑星として誕生します!\n進化に参加したメンバーには記念バッジが贈られます。'; + } + + @override + String get nebulaNameTaken => 'すでに使用されている名前です。'; + + @override + String get nebulaNameHint => 'ファンダム名'; + + @override + String get nebulaGoal => '目標 '; + + @override + String get nebulaFounders => ' / 創立メンバー'; + + @override + String nebulaGoalSemantics(String pop, String members, String days) { + return '目標 $pop POP · 創立メンバー $members人 · $days日固定'; + } + + @override + String get commonMyPop => '保有POP:'; + + @override + String get commonChange => '変更'; + + @override + String planetShape(String n) { + return '形 $n'; + } + + @override + String get planetBgColor => '背景色を選択'; + + @override + String get planetColorSelect => '惑星の色を選択'; + + @override + String get planetTypeSelect => '惑星タイプを選択'; + + @override + String get commonNext => '次へ'; + + @override + String get planetCreateFailed => '惑星を作成できませんでした。しばらくしてから再度お試しください。'; + + @override + String get planetCreateConfirm => '選択したオプションで\n惑星を作成しますか?'; + + @override + String get commonCreate => '作成'; + + @override + String get briefingOpenFailed => '原文を開けません。'; + + @override + String campaignAchieved(String percent) { + return '$percent% 達成'; + } + + @override + String get campaignView => 'キャンペーンを見る'; + + @override + String get growthFinalReached => '最終ステージに到達しました'; + + @override + String get zapRetrySame => '同じ貢献を再確認'; + + @override + String get heroOwnerSuffix => 'さんの'; + + @override + String get heroStartPrompt => '惑星を作成または参加して\nファン宇宙を始めよう!'; + + @override + String get heroNebulaPrompt => '活動で獲得したPOPで\n星雲に貢献しよう!'; + + @override + String heroRecentContribution(String pop) { + return '$pop POPの貢献で\n着実に成長しています。'; + } + + @override + String get heroGrowingTogether => 'みんなで育てるファンダム惑星'; + + @override + String get heroPopProgress => 'POP進捗率'; + + @override + String get heroNebula => '星雲'; + + @override + String get heroNebulaTap => '星雲をタップして\n惑星を誕生させよう!'; + + @override + String get heroCreateNebula => '惑星を作成(星雲)'; + + @override + String get heroJoinPlanet => '惑星に参加'; + + @override + String get heroCreateOrJoin => '惑星を作成または参加'; + + @override + String insideAnswers(String count) { + return '回答 $count'; + } + + @override + String get itemJoinFirst => '惑星に参加すると英雄のギフトを送れます。'; + + @override + String get itemBuyNotReady => 'アイテム購入は準備中です。'; + + @override + String get itemUseNotReady => 'アイテムの使用は準備中です。'; + + @override + String get payOpenFailed => '決済画面を開けませんでした。'; + + @override + String get itemBuy => 'アイテム購入'; + + @override + String get itemSelect => 'アイテム選択'; + + @override + String get commonComingSoon => '準備中です。'; + + @override + String itemSendPop(String pop) { + return '$pop POP 送信'; + } + + @override + String itemBuyerPop(String pop) { + return '購入者 +$pop POP'; + } + + @override + String itemOwned(String count) { + return '保有:$count'; + } + + @override + String itemOwnedCount(String count) { + return '保有:$count個'; + } + + @override + String get growthOwnerOf => 'さんの '; + + @override + String get growthSteadySuffix => ' の貢献で着実に成長しています。'; + + @override + String get questAttended => '出席完了'; + + @override + String get questInProgressSpaced => '進行中'; + + @override + String get questRewardPaid => '報酬支給完了'; + + @override + String get questClaim => '報酬を受け取る'; + + @override + String get questStart => 'クエストを始める'; + + @override + String get questRewardDone => '報酬受取済み'; + + @override + String get popKindGeneral => '一般POP'; + + @override + String get popKindContribution => '貢献POP'; + + @override + String get popTxGrant => '支給'; + + @override + String get popTxSpend => '差引'; + + @override + String get popTxRefund => '返金'; + + @override + String get popTxVoid => '無効'; + + @override + String get popTypeQuest => 'クエスト'; + + @override + String get popTypeReward => '報酬'; + + @override + String get popTypePayment => '決済'; + + @override + String get popTypeSubscription => 'サブスク'; + + @override + String get popTypeAdmin => '運営'; + + @override + String get popTypeFee => '手数料'; + + @override + String get commonEtc => 'その他'; + + @override + String get popReasonQuestSteps => '歩数計クエスト'; + + @override + String get popReasonQuestMusicShake => 'ミュージックシェイククエスト'; + + @override + String get popReasonQuestPlace => 'スポット訪問クエスト'; + + @override + String get popReasonQuestAttend => '出席クエスト'; + + @override + String get popReasonAdReward => '広告報酬'; + + @override + String get popReasonHeroDrop => '英雄のギフト'; + + @override + String get popReasonHeroDropTrigger => '英雄のギフト発動'; + + @override + String get popReasonTranslation => '参加型翻訳'; + + @override + String get popReasonIapPurchase => '商品購入'; + + @override + String get popReasonFocusDaily => 'Focusデイリー報酬'; + + @override + String get popReasonPreregBonus => '事前予約報酬'; + + @override + String get popReasonReferral => '友達招待'; + + @override + String get popTypeReferral => 'リファラル'; + + @override + String get popReasonAdminGrant => '運営支給'; + + @override + String get popReasonAdminClawback => '運営回収'; + + @override + String get popReasonCampaignFund => 'キャンペーン支援'; + + @override + String get popReasonCampaignRefund => 'キャンペーン返金'; + + @override + String get popReasonCampaignFeeRefund => 'キャンペーン開設手数料の返金'; + + @override + String get popReasonCampaignFee => 'キャンペーン登録費'; + + @override + String get popReasonQnaBounty => 'インサイド懸賞金'; + + @override + String get popReasonQnaRefund => '懸賞金返金'; + + @override + String get popReasonQnaReward => 'インサイド採択報酬'; + + @override + String get popReasonEclipseDefense => 'Zap貢献'; + + @override + String get popReasonOther => 'POP変動'; + + @override + String popRepeat(String title, String count) { + return '$title $count回'; + } + + @override + String get payStatePaid => '決済完了'; + + @override + String get payStateRefundRequested => '返金申請'; + + @override + String get payStateRefundApproved => '返金承認'; + + @override + String get payStateRefunded => '返金完了'; + + @override + String get payStateRefundRejected => '返金却下'; + + @override + String get pushTopicEssentialDesc => 'アカウント・決済・メンテナンスのお知らせです。オフにできません。'; + + @override + String get pushTopicFandomEventDesc => + 'イクリプス・キャンペーン・クエスト・アーティストホットタイムのお知らせです。'; + + @override + String get pushTopicBriefingDesc => '惑星の一日をまとめたブリーフィングカードをお届けします。'; + + @override + String get pushTopicCommunityDesc => '自分の投稿へのコメント・いいね・回答の通知です。'; + + @override + String get pushTopicMarketingDesc => 'イベント・特典情報を受け取ります。同意した場合のみオンになります。'; + + @override + String get inquiryPending => '受付'; + + @override + String get inquiryInProgress => '対応中'; + + @override + String get inquiryAnswered => '回答済み'; + + @override + String get photoBadType => 'JPG・PNG・WEBP・GIF画像のみアップロードできます。'; + + @override + String get photoTooBig => '写真の容量が10MBを超えています。もっと小さい写真を選んでください。'; + + @override + String get photoUnreadable => '写真を読み込めませんでした。もう一度選んでください。'; + + @override + String get myListInside => 'マイインサイド'; + + @override + String get myListInsideEmpty => 'まだ投稿したインサイドはありません。'; + + @override + String get myListCampaign => 'マイキャンペーン'; + + @override + String get myListCampaignEmpty => 'まだ作成したキャンペーンはありません。'; + + @override + String get sortLatest => '新着順'; + + @override + String get sortPop => 'POP順'; + + @override + String get sortSolved => '採択済み'; + + @override + String get sortAchieved => '達成済み'; + + @override + String get payNotFound => '決済履歴が見つかりません。'; + + @override + String get payItemFallback => '決済商品'; + + @override + String get payMethodInApp => 'アプリ内購入'; + + @override + String get unknownUser => '不明なユーザー'; + + @override + String get blockSelf => '自分をブロックすることはできません。'; + + @override + String get inquiryFallback => 'お問い合わせ'; + + @override + String get photoRejectedServer => '写真をアップロードできません。形式と容量をご確認ください。'; + + @override + String get photoUploadFailed => '写真のアップロードに失敗しました。しばらくしてから再度お試しください。'; + + @override + String focusClaimed(String pop) { + return '今日のFocus報酬 +$pop 貢献POPを受け取りました。'; + } + + @override + String get focusActive => '広告なし集中モード利用中'; + + @override + String focusUntil(String date) { + return '$dateまで · 今日の分は今日のみ受け取れます'; + } + + @override + String get focusClaimedToday => '本日受取済み'; + + @override + String get focusClaimToday => '今日の報酬を受け取る'; + + @override + String get storeConnectFailed => 'ストアに接続できませんでした。しばらくしてから再度お試しください。'; + + @override + String get payRestore => '購入を復元'; + + @override + String get payEmpty => '決済履歴はありません。'; + + @override + String get payRefundGuide => + '消費型商品:購入後7日以内かつ未使用の商品は返金できます。一部でも使用した商品は返金対象外です。\nサブスク商品:決済後24時間以内に特典(追加報酬など)を未使用の場合、返金できます。\n返金承認後7〜14日以内に返金が完了します。'; + + @override + String get payDetailTitle => '決済履歴の詳細'; + + @override + String get payNumber => '決済番号'; + + @override + String get payDate => '決済日時'; + + @override + String get payMethod => '決済方法'; + + @override + String get payStatus => '決済状況'; + + @override + String get payAmount => '決済金額'; + + @override + String get payRefundInfo => '返金のご案内'; + + @override + String get payRefundApply => '返金を申請'; + + @override + String get payRefundUnavailable => '返金不可'; + + @override + String get refundReasonMistake => '誤って決済しました'; + + @override + String get refundReasonExpectation => 'サービスが期待と違いました'; + + @override + String get refundReasonError => 'エラーで利用できませんでした'; + + @override + String get refundReasonDuplicate => '二重に決済しました'; + + @override + String get refundTitle => '返金'; + + @override + String get refundSubmit => '申請'; + + @override + String get refundReason => '返金理由'; + + @override + String get refundConfirm => '本当に返金を申請しますか?'; + + @override + String get myPop => 'マイPOP'; + + @override + String get commonNoHistory => '履歴はありません。'; + + @override + String get nicknameEdit => 'ニックネーム変更'; + + @override + String get nicknameReserved => '運営者と誤解される名前は使用できません。'; + + @override + String get nicknameTaken => 'すでに使われているニックネームです。'; + + @override + String get albumItems => 'アルバム項目'; + + @override + String get takePhoto => '写真を撮影'; + + @override + String get pickFromAlbum => 'アルバムから選択'; + + @override + String get tierGuide => 'ティア案内'; + + @override + String get statSteps => '累計歩数'; + + @override + String get statContribution => '累計貢献POP'; + + @override + String get statQuests => '完了クエスト'; + + @override + String get shareInstagram => 'Instagram'; + + @override + String get shareTiktok => 'TikTok'; + + @override + String get shareCopyLink => 'リンクをコピー'; + + @override + String get inquiryListTitle => '1:1 お問い合わせ'; + + @override + String get inquiryEmpty => 'お問い合わせはありません。'; + + @override + String get inquiryWrite => 'お問い合わせを作成'; + + @override + String get inquiryDetail => 'お問い合わせ詳細'; + + @override + String get commonEdit => '編集'; + + @override + String get inquiryAnswer => '回答'; + + @override + String get inquiryNew => '新規お問い合わせ'; + + @override + String get inquiryEdit => 'お問い合わせを編集'; + + @override + String get commonTitle => 'タイトル'; + + @override + String get commonTitleHint => 'タイトルを入力してください。'; + + @override + String get inquiryBody => 'お問い合わせ内容'; + + @override + String get inquiryBodyHint => 'お問い合わせ内容を入力してください。'; + + @override + String get inquiryBodyHelper => '10文字以上書いていただくと、より早く対応できます。'; + + @override + String inquirySubmitFailed(String reason) { + return 'お問い合わせを受け付けられませんでした。$reason'; + } + + @override + String inquiryEditFailed(String reason) { + return 'お問い合わせを修正できませんでした。$reason'; + } + + @override + String get inquirySubmitted => 'お問い合わせを受け付けました。'; + + @override + String get inquiryEdited => 'お問い合わせを修正しました。'; + + @override + String get blockedEmpty => 'ブロックしたアカウントはありません。'; + + @override + String get unblock => 'ブロック解除'; + + @override + String unblockFailed(String reason) { + return 'ブロックを解除できませんでした。$reason'; + } + + @override + String get tierInfoTitle => 'TIER情報'; + + @override + String get tierPromptPrefix => '惑星・星雲への貢献で\n'; + + @override + String get tierPromptRank => 'ランク'; + + @override + String get tierPromptSuffix => 'を上げよう'; + + @override + String get tierPassportNote => 'Passport Tierはレベルとは別のカード外観の等級で、下がることはありません。'; + + @override + String get tierTotalContribution => '累計貢献'; + + @override + String get notifKindEclipse => 'イクリプス'; + + @override + String get notifKindItemUsed => 'アイテム使用'; + + @override + String get notifKindLike => 'いいね'; + + @override + String get notifKindComment => 'コメント'; + + @override + String get notifKindCampaign => 'キャンペーン達成'; + + @override + String get notifKindQuest => 'クエスト完了'; + + @override + String get notifTargetPost => '投稿'; + + @override + String get notifTargetCampaign => 'キャンペーン'; + + @override + String get notifTargetQuest => 'クエスト詳細'; + + @override + String get dayToday => '今日'; + + @override + String get dayYesterday => '昨日'; + + @override + String get notifEmpty => '通知はありません。'; + + @override + String notifGoTo(String target) { + return '$targetへ移動します。'; + } + + @override + String get stationOfficial => '公式'; + + @override + String get stationShop => '販売店'; + + @override + String get stationTicket => 'チケット'; + + @override + String get stationMedia => 'メディア'; + + @override + String get stationLoadFailed => 'STATIONを読み込めませんでした'; + + @override + String get commonAll => 'すべて'; + + @override + String get stationSuggest => '販売店を提案'; + + @override + String get stationEmpty => 'まだ登録されたリンクはありません。\n惑星に参加するとキュレーションが表示されます。'; + + @override + String get stationCategoryEmpty => 'この分類にはまだリンクがありません。'; + + @override + String get stationSuggestLater => '提案の受付はサーバー連携後に開始します。'; + + @override + String get commonSubmit => '提出'; + + @override + String get commonName => '名前'; + + @override + String get commonNameRequired => '名前を入力してください。'; + + @override + String get commonUrl => 'URL'; + + @override + String get commonUrlRequired => 'URLを入力してください。'; + + @override + String get commonUrlHttps => 'httpsで始まるURLを入力してください。'; + + @override + String get commonCategory => '分類'; + + @override + String get stationReason => 'おすすめ理由(任意)'; + + @override + String get stationSuggestNote => 'ご提案のリンクは運営の審査を経て登録されます。審査結果は通知でお知らせします。'; + + @override + String get heroGiftUnavailable => 'このギフトは現在受け取れません。'; + + @override + String get heroGiftAlready => 'すでに参加したギフトです。POP履歴をご確認ください。'; + + @override + String get heroGiftRelogin => '再度ログインしてから参加してください。'; + + @override + String get heroGiftNoPermission => 'このギフトに参加する権限がありません。'; + + @override + String get heroGiftHowTo => 'スマホを振るか下のボタンを押して、光エネルギーのギフトに参加しましょう。'; + + @override + String get heroGiftPopNote => '受け取れるPOPは残りのギフトと1人あたりの上限で決まります。'; + + @override + String heroGiftReceived(String total) { + return '$total POP 受取確認'; + } + + @override + String get heroGiftUnknown => '支給結果を確認できませんでした。再度ご確認ください。'; + + @override + String get heroGiftNone => '今参加できるギフトはありません。'; + + @override + String get heroGiftConfirmed => '参加したギフトの受取を確認しました。'; + + @override + String get heroGiftChecking => '支給を確認中…'; + + @override + String get heroGiftRetry => '同じギフトを再確認'; + + @override + String get heroGiftTap => '振る代わりにボタンで参加'; + + @override + String rankMyPlanet(String metric) { + return 'マイ惑星の$metricランキング'; + } + + @override + String get rankPlanetEmpty => 'まだ集計された惑星ランキングはありません'; + + @override + String get rankScopeLive => 'リアルタイム'; + + @override + String get rankScopeCountry => '国別'; + + @override + String rankMine(String metric) { + return '私の$metricランキング'; + } + + @override + String rankMyCountry(String metric) { + return '自国の$metricランキング'; + } + + @override + String get rankEmpty => 'まだ集計されたランキングはありません'; + + @override + String rankScope(String scope) { + return '$scopeランキング'; + } + + @override + String get rankLoadFailed => 'ランキングを読み込めませんでした'; + + @override + String rankMeSuffix(String name) { + return '$name(自分)'; + } + + @override + String get reportDefamation => 'アーティストへの誹謗・名誉毀損'; + + @override + String get reportInappropriate => '不適切なコンテンツ'; + + @override + String get reportSpam => 'スパム・営利目的の広告'; + + @override + String get reportPrivacy => '個人情報の露出・なりすまし'; + + @override + String get reportTitle => '通報する'; + + @override + String get reportSubmit => '通報'; + + @override + String get reportAuthor => '投稿者'; + + @override + String get reportReasonLabel => '通報理由'; + + @override + String get reportConfirm => 'この投稿を通報しますか?\n受付後は取り消せません。'; + + @override + String get blockMemberConfirm => 'この会員のすべての投稿をブロックしますか?'; + + @override + String get loungeMembersOnly => 'この惑星のファンダム会員のみ投稿・コメント・いいねができます。'; + + @override + String get loungeFailed => '処理できませんでした。しばらくしてから再度お試しください。'; + + @override + String get loungeTitle => 'ファンダムラウンジ'; + + @override + String get likeCancel => 'いいね取り消し'; + + @override + String get like => 'いいね'; + + @override + String get commentDeleteConfirm => 'コメントを削除しますか?'; + + @override + String get commonShare => '共有'; + + @override + String get commonEditAction => '編集する'; + + @override + String get commonDeleteAction => '削除する'; + + @override + String get commonBlockAction => 'ブロックする'; + + @override + String get commentFirst => '最初のコメントを残してください。'; + + @override + String get postDeleteConfirm => 'この投稿を削除しますか?'; + + @override + String get reportReceived => '通報を受け付けました。'; + + @override + String get commonDelete => '削除'; + + @override + String get commentHint => 'コメントを入力してください。'; + + @override + String get commonSend => '送信'; + + @override + String get postWrite => '投稿する'; + + @override + String get postEdit => '投稿を編集'; + + @override + String get loungeHint => 'ファンダムラウンジに自由に投稿してみましょう。'; + + @override + String photoRemove(String n) { + return '写真$nを削除'; + } + + @override + String get camera => 'カメラ'; + + @override + String get album => 'アルバム'; + + @override + String get historyTitle => 'PASSPORT · 記録'; + + @override + String get historyMine => '自分の活動'; + + @override + String historyEvolution(String from, String to) { + return 'Stage $from → $to 進化'; + } + + @override + String get historyHibernation => '惑星休眠'; + + @override + String get historyWakeUp => '惑星が目覚めた'; + + @override + String get historyZap => 'ZAP貢献'; + + @override + String get historyQuest => 'Quest報酬'; + + @override + String get historyAdBonus => '広告追加報酬'; + + @override + String get historyFunding => 'Campaign貢献'; + + @override + String historyGrowthPop(String amount) { + return '成長に$amount POP反映'; + } + + @override + String get historyJoined => 'この進化に参加しました'; + + @override + String get historyLoadFailed => '記録を読み込めませんでした。'; + + @override + String get historyMineEmpty => 'まだ記録された活動はありません。'; + + @override + String get historyFandomEmpty => 'まだ記録されたファンダムの歴史はありません。'; + + @override + String get historyMore => '以前の記録をもっと見る'; + + @override + String countryName(String code) { + String _temp0 = intl.Intl.selectLogic(code, { + 'KR': '韓国', + 'JP': '日本', + 'US': 'アメリカ', + 'CN': '中国', + 'TW': '台湾', + 'HK': '香港', + 'TH': 'タイ', + 'VN': 'ベトナム', + 'ID': 'インドネシア', + 'PH': 'フィリピン', + 'MY': 'マレーシア', + 'SG': 'シンガポール', + 'IN': 'インド', + 'GB': 'イギリス', + 'FR': 'フランス', + 'DE': 'ドイツ', + 'ES': 'スペイン', + 'BR': 'ブラジル', + 'MX': 'メキシコ', + 'CA': 'カナダ', + 'AU': 'オーストラリア', + 'ZZ': '未指定', + 'other': '-', + }); + return '$_temp0'; + } + + @override + String loungeMaxPhotos(String count) { + return '写真は$count枚までアップロードできます。'; + } + + @override + String get commonLoadFailed => '読み込めませんでした'; + + @override + String get planetPassportHistory => 'PASSPORT · ファンダムの歴史'; + + @override + String planetNamed(String name) { + return '$name 惑星'; + } + + @override + String get blockNebulaConfirm => 'この星雲をブロックしますか?'; + + @override + String get blockPlanetConfirm => 'この惑星をブロックしますか?'; + + @override + String get blocked => 'ブロックしました。'; + + @override + String planetLastUpdated(String time) { + return '最終更新 $time'; + } + + @override + String get planetContribution => '貢献状況'; + + @override + String get planetNebulaGrowth => '星雲成長率'; + + @override + String get planetLiveRanking => 'リアルタイム貢献ランキング'; + + @override + String get planetNoContributors => 'まだ貢献者がいません'; + + @override + String get planetContributeCta => '貢献してランキングに入ろう!'; + + @override + String planetOverallRank(String rank) { + return '全体$rank位'; + } + + @override + String get planetMyContribution => '自分の貢献度'; + + @override + String get planetTallying => '集計準備中'; + + @override + String planetTopPercent(String percent) { + return '上位$percent%'; + } + + @override + String get planetHideMember => 'この会員の投稿をすべて非表示'; + + @override + String get reportReasonUnknown => '通報理由を確認できません。'; + + @override + String get reportAlready => 'すでに通報した投稿です。'; + + @override + String get blockUnavailable => 'ブロックできない投稿者です。'; + + @override + String get commonMe => '自分'; + + @override + String get planetSizeMedium => '中規模ファンダム'; + + @override + String planetPerHead(String value) { + return '1人あたり火力:$value'; + } + + @override + String get planetJoined => '参加中'; + + @override + String get planetJoin => '参加する'; + + @override + String get questDetails => '詳細'; + + @override + String get questInProgress => '進行中'; + + @override + String get questTypeAttend => '出席'; + + @override + String get questTypeSteps => 'ウォーキング'; + + @override + String get questTypePlace => '特定の場所'; + + @override + String get questTypeStreaming => 'ストリーミング'; + + @override + String get questTypeUnverifiable => 'このクエストタイプの検証方法はまだ決まっていません。'; + + @override + String get questTabMine => 'マイクエスト'; + + @override + String get placePickPrompt => 'クエストを完了する\n場所を選択してください。'; + + @override + String get placeSearch => '場所を検索'; + + @override + String get commonNoResults => '検索結果がありません。'; + + @override + String get commonSelectDone => '選択完了'; + + @override + String get adMoreChance => 'もっと貢献のチャンスを手に入れよう。'; + + @override + String get adWatchPrompt => '広告を視聴して追加報酬を\n受け取れます!'; + + @override + String get adBonusPlaceholder => '+ 貢献POP'; + + @override + String get adPartyPack => 'パーティーパック'; + + @override + String get adPickProduct => '商品を選ぶ'; + + @override + String get adWatchBonus => '広告を見て追加報酬を受け取る'; + + @override + String get adBasicReward => '基本報酬を受け取る'; + + @override + String get focusMode => '広告なし集中モード'; + + @override + String pricePerMonth(String price) { + return '$price/月'; + } + + @override + String get adWarpBefore => + 'Warping...ファンダムとアーティストにもっと貢献できるよう、ぴったりの広告を探しています。'; + + @override + String get adWarpAfter => 'Warping...あなたの貢献への報酬を準備しています。'; + + @override + String get adSkip => 'スキップ'; + + @override + String get questPreviewHint => 'オプションを選んでプレビューを確認してください'; + + @override + String get questNoCategory => 'まだカテゴリが選択されていません'; + + @override + String get questNew => '新規クエスト'; + + @override + String get questCategory => 'カテゴリ'; + + @override + String get questName => 'クエスト名'; + + @override + String get questAction => '詳細アクション'; + + @override + String get questPickPlace => '目標の場所を選んでください'; + + @override + String get questGoalValue => '目標値'; + + @override + String get questParticipants => '参加人数'; + + @override + String get questRewardPop => '完了報酬POP'; + + @override + String get questCreateConfirm => 'この内容で作成しますか?'; + + @override + String questCreateSummary(String title, String goal, String reward) { + return '$title · 目標 $goal · 報酬 $reward POP'; + } + + @override + String get commonKeepEditing => 'さらに編集'; + + @override + String get questCreate => '作成'; + + @override + String questNamed(String title) { + return '$title クエスト'; + } + + @override + String get questDailyHelp => '毎日惑星をサポート'; + + @override + String get questTodayMust => '今日やるべきクエストです'; + + @override + String get questTabEmpty => 'このタブにはまだクエストがありません。'; + + @override + String get questCreateNotReady => 'クエスト作成は報酬原資の預け入れと同時に開始する機能です。準備中です。'; + + @override + String get questNoBaseReward => '基本報酬の記録が見つからないため、広告追加報酬を受け取れません。'; + + @override + String get questNoSteps => '歩数がまだ取得されていません。少し歩いてから再度お試しください。'; + + @override + String get questNoPlace => 'このクエストに認証する場所が登録されていません。'; + + @override + String get questGoal => '目標'; + + @override + String get questReward => '報酬'; + + @override + String get questPopGrant => 'POP支給'; + + @override + String get commonChecking => '確認中…'; + + @override + String questMine(String count) { + return '自分が作成したクエストです。参加者は$count人です。'; + } + + @override + String get questStopConfirm => 'クエストを中断しますか?'; + + @override + String get questStopBody => 'これまでの進行状況がリセットされ、最初の状態に戻ります。'; + + @override + String get questStop => '中断'; + + @override + String get adNotFinished => '広告を最後まで見なかったため追加報酬はありません。基本報酬はそのまま受け取れます。'; + + @override + String get openSettings => '設定を開く'; + + @override + String get permStepsDenied => '歩数の権限がオフです。設定で「身体活動」を許可してください。'; + + @override + String get permStepsNeeded => '歩数を数えるには身体活動の権限が必要です。'; + + @override + String get permLocationOff => '端末の位置情報がオフです。オンにして再度お試しください。'; + + @override + String get permLocationNeeded => '訪問を確認するには位置情報の権限が必要です。'; + + @override + String get permLocationDenied => '位置情報の権限がオフです。設定で位置情報へのアクセスを許可してください。'; + + @override + String get questRewardReceived => ' 受け取りました!'; + + @override + String questAdBonus(String pop) { + return '広告追加報酬 +$pop POP'; + } + + @override + String questParticipantsCount(String count) { + return '参加人数:$count'; + } + + @override + String questParticipantsCap(String count, String cap) { + return '参加人数:$count / $cap'; + } + + @override + String get nebulaCreateFailed => '星雲を作成できませんでした。しばらくしてから再度お試しください。'; + + @override + String get nebulaAlreadyToday => '今日はすでに星雲を作成しました。'; + + @override + String get nebulaCreatedKeepPrimary => + '星雲を作成しました。メイン惑星は前回の変更から30日経過後に変更できるため、そのままです。'; + + @override + String get fandomFounderLeaveBlocked => '惑星が誕生した後に脱退できます。'; + + @override + String get sortPopular => '人気順'; + + @override + String get campaignSortContributing => '貢献中'; + + @override + String get campaignStatusReview => '運営が審査中のキャンペーンです。'; + + @override + String get campaignStatusRejected => '審査で差し戻されました。登録料は返還しました。'; + + @override + String get campaignStatusWithdrawn => '作成者が取り下げたキャンペーンです。'; + + @override + String get campaignStatusCancelled => '運営上の理由でキャンセルされました。登録料と支援POPを返還しました。'; + + @override + String get rankWindowDaily => '日間'; + + @override + String get rankWindowWeekly => '週間'; + + @override + String get campaignNotFound => 'キャンペーンが見つかりません。'; + + @override + String get campaignMediaMain => 'メイン画像'; + + @override + String get campaignLoadFailed => 'キャンペーンを読み込めませんでした。'; + + @override + String get campaignTabInfo => 'キャンペーン情報'; + + @override + String campaignTabBoard(String count) { + return '応援掲示板 $count'; + } + + @override + String get campaignWithdraw => 'キャンペーン取り下げ'; + + @override + String get campaignContributeAction => '貢献する'; + + @override + String get campaignFundingStatus => '現在の支援状況'; + + @override + String get campaignSupporting => '応援中'; + + @override + String get campaignTimeLeft => '残り!'; + + @override + String get campaignReport => '結果報告'; + + @override + String get campaignReportPhotos => '実施写真'; + + @override + String get campaignBudget => '予算の使用内訳'; + + @override + String get campaignTotal => '合計'; + + @override + String get campaignReceipts => '領収書'; + + @override + String campaignDaysLeft(String days, String time) { + return '$days日 $time'; + } + + @override + String get campaignWithdrawTitle => 'キャンペーンを取り下げますか?'; + + @override + String campaignWithdrawRefund(String fee) { + return '公開前の取り下げのため、登録料 $fee POP を支払った通貨のまま返還します。'; + } + + @override + String campaignWithdrawNoRefund(String fee) { + return 'すでに公開されたキャンペーンのため、登録料 $fee POP は返還されません。'; + } + + @override + String get campaignWithdrawConfirm => '取り下げる'; + + @override + String get shareCopied => '共有テキストをコピーしました。'; + + @override + String get campaignContributePop => '貢献POPの設定'; + + @override + String get campaignEmptyContributing => 'まだ貢献したキャンペーンはありません。'; + + @override + String get campaignEmpty => '登録されたキャンペーンはありません。'; + + @override + String get campaignNew => '新しいキャンペーン'; + + @override + String get campaignMyRanking => '私の貢献ランキング'; + + @override + String campaignWindowRanking(String window) { + return '$windowランキング'; + } + + @override + String get campaignSearchHint => 'キャンペーン検索'; + + @override + String get campaignNoRecentSearches => '最近の検索はありません'; + + @override + String searchRemoveTerm(String term) { + return '$termを削除'; + } + + @override + String get campaignNoResults => '検索結果がありません'; + + @override + String get shareTopicCampaign => 'キャンペーン'; + + @override + String sharePlanetLine(String brand, String name) { + return '$brand · $name 惑星'; + } + + @override + String get campaignGaugeLabel => 'キャンペーン貢献率'; + + @override + String get campaignPeriodTitle => 'キャンペーン期間を選択してください'; + + @override + String get campaignPeriodMax => '期間は最大1年まで選択できます'; + + @override + String campaignYearMonth(String year, String month) { + return '$year年$month月'; + } + + @override + String get campaignWeekdays => '日,月,火,水,木,金,土'; + + @override + String campaignPeriodRange( + String startMonth, + String startDay, + String endMonth, + String endDay, + ) { + return '$startMonth月$startDay日 ~ $endMonth月$endDay日'; + } + + @override + String get campaignPeriodSelect => '期間を選択'; + + @override + String get calendarPrevMonth => '前の月'; + + @override + String get calendarNextMonth => '次の月'; + + @override + String get campaignShareTitle => 'キャンペーンを共有'; + + @override + String get campaignPickMedia => '画像/動画を選択'; + + @override + String get campaignTitleHint => 'キャンペーンのタイトルを入力してください。'; + + @override + String get campaignBodyHint => 'キャンペーンの説明を入力してください。'; + + @override + String get campaignReviewNote => '投稿後、運営の審査を経て支援が始まります。'; + + @override + String get campaignSupportType => '支援の種類を選択'; + + @override + String get campaignPeriod => 'キャンペーン期間'; + + @override + String get campaignDateSelect => '日付を選択'; + + @override + String get campaignGoalPop => '目標貢献POP'; + + @override + String get campaignSubmitTitle => 'この内容で投稿しますか?'; + + @override + String campaignSubmitBody( + String title, + String goal, + String days, + String fee, + ) { + return '$title · 目標 $goal POP · $days日\n登録料 $fee POP が差し引かれ、運営の審査後に支援が始まります。'; + } + + @override + String get campaignSubmit => '投稿する'; + + @override + String get campaignCreateFailed => 'キャンペーンを投稿できませんでした。'; + + @override + String campaignNeedsQuests(String done, String need, String left) { + return 'クエスト完了 $done/$need — あと$left回完了するとキャンペーンを開けます。'; + } + + @override + String get campaignPendingTitle => '審査待ちです'; + + @override + String get campaignPendingBody => '運営の審査が終わると支援が始まり、一覧に表示されます。'; + + @override + String get insideRefundPending => '採択期限が過ぎたため、懸賞金は返金される予定です。'; + + @override + String get insideRefundDone => '採択期限が過ぎたため、懸賞金は返金されました。'; + + @override + String get insideRefundChecking => '採択期限が過ぎたため、懸賞金の返金を確認しています。'; + + @override + String get insideErrPopShort => 'POPが足りず懸賞金を設定できませんでした。'; + + @override + String get insideErrSelfAccept => '自分の回答は採択できません。'; + + @override + String get insideErrAlreadyAccepted => 'すでに採択済みの質問です。採択は変更できません。'; + + @override + String get insideErrNotOwner => '質問者のみ採択できます。'; + + @override + String get commonCheckConnection => '接続を確認して、もう一度お試しください。'; + + @override + String get insideAnswerLater => 'コメントはサーバー連携後に利用できます。'; + + @override + String insideAcceptConfirm(String pop) { + return 'このコメントを採択しますか?\n懸賞金 $pop POP が渡され、採択は取り消せません。'; + } + + @override + String get insideAccept => '採択'; + + @override + String get insideMore => 'もっと見る'; + + @override + String get insideFirstAnswer => '最初のコメントを残してください。'; + + @override + String get insideAcceptDone => '採択済み'; + + @override + String get insideAcceptAction => '採択する'; + + @override + String get insideAnswerHint => 'コメントを入力してください。'; + + @override + String get insideLoadFailed => 'INSIDEを読み込めませんでした'; + + @override + String get insideEmpty => 'まだ質問はありません。'; + + @override + String get insideWrite => 'INSIDEを作成'; + + @override + String get insideSearchHint => 'INSIDEのタイトル・内容'; + + @override + String get insideBodyHint => 'ファンダムと自由に質問をやり取りしてみましょう。'; + + @override + String get insideTitleRequired => 'タイトルを入力してください。'; + + @override + String get insideBodyRequired => '内容を入力してください。'; + + @override + String get insideBountySet => '懸賞金の設定'; + + @override + String insideBountyHint(String min) { + return '$min以上に設定してください。'; + } + + @override + String get insideAttachLater => '画像の添付はサーバー連携後に利用できます。'; + + @override + String get insideBountyRequired => '懸賞金を入力してください。'; + + @override + String get insideNumberOnly => '数字で入力してください。'; + + @override + String insideBountyMin(String min) { + return '懸賞金は $min POP 以上である必要があります。'; + } + + @override + String get insideBountyOverBalance => '保有POPを超えることはできません。'; + + @override + String get questTypeTimeAttack => '緊急タイムアタック'; + + @override + String get questTimeLeft => '残り時間'; + + @override + String get questTimeAttackClosed => '締め切り'; + + @override + String questTimeAttackLeft(String time) { + return '残り $time'; + } + + @override + String get popReasonQuestTimeAttack => '緊急タイムアタック'; + + @override + String get briefingShareCopied => '共有テキストとリンクをコピーしました。'; + + @override + String get shareCopyText => 'テキストをコピー'; + + @override + String get briefingShareTextCopied => '共有テキストをコピーしました。'; + + @override + String questAlreadyRewarded(String pop) { + return '受け取り済みの報酬です(+$pop POP)。'; + } + + @override + String get questCatHealth => '健康'; + + @override + String get questCatPlanet => '惑星アクティビティ'; + + @override + String get questCatCommunity => 'コミュニティ'; + + @override + String get questActShake => 'シェイク'; + + @override + String get questTypeVote => '投票参加'; + + @override + String get questTypeSns => 'SNS'; + + @override + String get questActivity => 'アクティビティ'; + + @override + String get questNoCreatable => '現在作成できるクエスト活動はありません。'; + + @override + String get questDescriptionOptional => '説明(任意)'; + + @override + String get questLinkUrl => 'リンクURL'; + + @override + String get questLinkCheck => 'リンクを確認'; + + @override + String get questLinkInvalid => '正しいリンクではありません。URLを確認してください。'; + + @override + String get questLinkPreviewFailed => 'リンク情報を取得できませんでした。タイトルを入力してください。'; + + @override + String get questLinkTitle => 'リンクのタイトル'; + + @override + String get questEndDate => '終了日'; + + @override + String get questEndDefault => '公開から24時間'; + + @override + String questEndDateValue(String date) { + return '$date 23:59:59 締切'; + } + + @override + String questPoolHelper(String pool, String fee) { + return '合計 $pool POP 預け入れ・手数料 $fee POP'; + } + + @override + String questCreateCost( + String title, + String reward, + String count, + String pool, + String fee, + String total, + ) { + return '$title\n報酬 $reward POP × $count人 = $pool POP\n手数料 $fee POP(公開時に消滅)\n合計 $total POP が差し引かれます。'; + } + + @override + String get questNoReward => '報酬のない参加クエストです。'; + + @override + String get questDeadline => '締切'; + + @override + String questRemainingHours(String hours) { + return '残り$hours時間'; + } + + @override + String get questEnded => '終了'; + + @override + String get questAuthorNote => '作成者の説明'; + + @override + String get questRepeatOnce => '1人1回参加'; + + @override + String questRepeatEvery(String hours, String count) { + return '$hours時間おき・最大$count回'; + } + + @override + String get questSubmit => '認証を提出'; + + @override + String get questResubmit => '補完して再提出'; + + @override + String get questEvidenceUrl => '認証リンク(任意)'; + + @override + String get questEvidenceText => '認証内容'; + + @override + String get questSubmitted => '提出しました。審査後に報酬が支払われます。'; + + @override + String get questReviewPending => '審査中です。承認されると報酬が支払われます。'; + + @override + String get questReviewDelayed => '審査が遅れています。運営チームが確認中です。'; + + @override + String get questApprovedPaying => '承認されました。報酬を支払っています。'; + + @override + String questRejected(String reason) { + return '差し戻し: $reason'; + } + + @override + String get questResubmitAllowed => '補完して再提出できます。'; + + @override + String get questResubmitNotAllowed => 'この提出は再提出できません。'; + + @override + String get questReviewButton => '審査中'; + + @override + String get questRejectedButton => '差し戻し済み'; + + @override + String questReviewQueue(String count) { + return '審査待ち $count件'; + } + + @override + String get questApprove => '承認'; + + @override + String get questReject => '差し戻す'; + + @override + String get questRejectReason => '差し戻し理由'; + + @override + String get questAllowResubmit => '再提出を許可'; + + @override + String get questEdit => 'タイトル・説明を編集'; + + @override + String get questEditNotice => '公開後はタイトルと説明のみ編集でき、履歴が残ります。'; + + @override + String get popReasonQuestUgc => 'クエスト報酬'; + + @override + String get popReasonQuestFee => 'クエスト作成手数料'; + + @override + String questFreeDailyCap(String cap) { + return '報酬なしのクエストは1日$cap件まで作成できます。'; + } + + @override + String get popReasonQuestPoolDeposit => 'クエスト報酬の預け入れ'; + + @override + String get popReasonQuestPoolReturn => 'クエスト報酬の返還'; + + @override + String get campaignCommentNeedNickname => '先にニックネームを決めてください'; + + @override + String get campaignCommentSetNickname => 'ニックネームを決める'; +} diff --git a/app/lib/l10n/app_localizations_ko.dart b/app/lib/l10n/app_localizations_ko.dart new file mode 100644 index 0000000..d2071ed --- /dev/null +++ b/app/lib/l10n/app_localizations_ko.dart @@ -0,0 +1,2660 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Korean (`ko`). +class AppLocalizationsKo extends AppLocalizations { + AppLocalizationsKo([String locale = 'ko']) : super(locale); + + @override + String get commonConfirm => '확인'; + + @override + String get commonCancel => '취소'; + + @override + String get commonClose => '닫기'; + + @override + String get commonSelect => '선택'; + + @override + String get commonRetry => '다시 시도'; + + @override + String get commonView => '보기'; + + @override + String get commonPrev => '이전'; + + @override + String get commonDone => '완료'; + + @override + String get commonStart => '시작'; + + @override + String commonNotYet(String where) { + return '$where 화면은 구현 확인물 2차(2026-09-10)에 들어갑니다.'; + } + + @override + String get languageSettingsTitle => '언어 설정'; + + @override + String get authLogin => '로그인'; + + @override + String get authSignup => '회원가입'; + + @override + String get authFirstTime => '팬우주는 처음이세요?'; + + @override + String get authAlreadyMember => '이미 가입하셨나요?'; + + @override + String get authLoginHeadline => '가장 편한 방법으로\n팬우주에 로그인하세요!'; + + @override + String get authSignupHeadline => '가장 편한 방법으로\n팬우주에 회원가입 하세요!'; + + @override + String authProviderLogin(String provider) { + String _temp0 = intl.Intl.selectLogic(provider, { + 'apple': 'Apple로 로그인', + 'google': 'Google로 로그인', + 'kakao': 'Kakao로 로그인', + 'line': 'LINE으로 로그인', + 'other': '이메일로 로그인', + }); + return '$_temp0'; + } + + @override + String authProviderSignup(String provider) { + String _temp0 = intl.Intl.selectLogic(provider, { + 'apple': 'Apple로 가입하기', + 'google': 'Google로 가입하기', + 'kakao': 'Kakao로 가입하기', + 'line': 'LINE으로 가입하기', + 'other': '이메일로 가입하기', + }); + return '$_temp0'; + } + + @override + String authNotConfiguredTitle(String name) { + return '$name 로그인 준비 중'; + } + + @override + String authNotConfiguredBody(String name, String items) { + return '$name 로그인은 발주사 키가 들어오면 열립니다.\n\n필요한 항목\n· $items'; + } + + @override + String authNotConfiguredNotice(String name, String items) { + return '$name 로그인은 아직 준비 중입니다.\n필요한 항목: $items'; + } + + @override + String get authResetNeedEmail => '재설정 메일을 받으실 이메일을 먼저 입력해 주세요.'; + + @override + String get authResetSent => '입력하신 주소로 재설정 안내를 보냈습니다.'; + + @override + String get termsTitle => '이용 약관'; + + @override + String get termsAgreeAll => '전체 동의'; + + @override + String get termsService => '서비스 이용약관 (필수)'; + + @override + String get termsLocation => '위치기반 서비스 이용약관 (필수)'; + + @override + String get termsAge => '만 14세 이상입니다. (필수)'; + + @override + String get termsMarketing => '마케팅 정보 및 이벤트 알림 수신 동의 (선택)'; + + @override + String get signupDoneTitle => '회원가입이 완료되었습니다'; + + @override + String get signupDoneBody => '팬덤 활동을 시작할 수 있습니다.\n다양한 활동을 통해 내 팬덤 행성을 키워보세요.'; + + @override + String get homeItemsTooltip => '아이템'; + + @override + String get homeNotificationsTooltip => '알림'; + + @override + String get homeFandomHistory => '팬덤 역사'; + + @override + String get homePopularInside => '인기 K-INSIDE'; + + @override + String get homeWhereBriefing => 'AI 브리핑'; + + @override + String get homeWhereInsideList => 'INSIDE 목록'; + + @override + String get homeWhereInsideDetail => 'INSIDE 상세'; + + @override + String get homeLoadFailed => '홈을 불러오지 못했습니다'; + + @override + String get homeRetryLater => '잠시 뒤 다시 시도해 주세요.'; + + @override + String get myProfileCardTooltip => '프로필 카드'; + + @override + String get myTabPlanets => '행성/성운'; + + @override + String get myTabActivity => '활동'; + + @override + String get myInside => '인사이드'; + + @override + String get myCampaign => '캠페인'; + + @override + String myShareLater(String target) { + return '$target 공유는 09-10 회차에 연결합니다.'; + } + + @override + String get myEditPhoto => '프로필 사진 수정'; + + @override + String get myEditProfile => '프로필 수정'; + + @override + String get settingsTitle => '설정'; + + @override + String get settingsAccount => '계정'; + + @override + String get settingsNickname => '닉네임'; + + @override + String get settingsEmail => '이메일'; + + @override + String get settingsSns => 'SNS 연동 계정'; + + @override + String get settingsPopHistory => 'POP 내역'; + + @override + String get settingsPayments => '결제 내역'; + + @override + String get settingsServicePayments => '서비스 결제 내역'; + + @override + String get settingsService => '서비스 설정'; + + @override + String get settingsPush => '푸시 알림 설정'; + + @override + String get settingsBlocked => '차단 설정'; + + @override + String get settingsInfo => '서비스 정보'; + + @override + String get settingsNotice => '공지 사항'; + + @override + String get settingsInquiry => '1:1 문의접수'; + + @override + String get settingsAppVersion => '앱 버전'; + + @override + String get settingsLatest => '최신 버전'; + + @override + String get settingsLogout => '로그아웃'; + + @override + String get settingsLogoutConfirm => '로그아웃 하시겠습니까?'; + + @override + String get settingsWithdraw => '서비스 탈퇴'; + + @override + String get settingsWithdrawConfirm => '서비스 탈퇴를 하시겠습니까?'; + + @override + String get settingsWithdrawBody => '보유하신 POP 이 사라지고 되돌릴 수 없습니다.'; + + @override + String get settingsWithdrawLater => '서비스 탈퇴 처리는 09-10 회차에 연결합니다.'; + + @override + String get settingsUnlinkConfirm => '연결된 계정을 끊으시겠습니까?'; + + @override + String get settingsUnlinkBody => '연결한 SNS 계정 로그인 수단만 제거되며, 계정 정보는 삭제되지 않습니다.'; + + @override + String get settingsUnlink => '연결 해제'; + + @override + String get settingsSameEmailOnly => '계정 이메일과 동일한 소셜 계정만\n연결 할 수 있습니다.'; + + @override + String settingsLinkNotReady(String name, String items) { + return '$name 연동은 아직 준비 중입니다. 필요한 항목: $items'; + } + + @override + String get settingsConnect => '연결'; + + @override + String get settingsDisconnect => '연결해제'; + + @override + String get settingsEmailProvider => '이메일'; + + @override + String get pushDeviceOff => '기기의 푸시 알림 설정이 꺼진 상태입니다.'; + + @override + String get pushDeviceOffHint => '알림 수신을 위해 시스템 설정에서 알림을 허용해주세요.'; + + @override + String get pushRequesting => '요청 중…'; + + @override + String get pushGoToSettings => '설정하러 가기'; + + @override + String get pushOpeningDeviceSettings => '기기 설정으로 이동합니다.'; + + @override + String get pushEnableInDevice => '기기 설정에서 FANUZU 알림을 켜 주셔야 받으실 수 있습니다.'; + + @override + String get pushDeviceSection => '기기 알림 설정'; + + @override + String get pushSoundVibration => '소리, 진동 설정'; + + @override + String get pushServiceSection => '서비스 알림 설정'; + + @override + String get pushTopicEssential => '서비스 필수'; + + @override + String get pushTopicFandomEvent => '팬덤 이벤트'; + + @override + String get pushTopicBriefing => 'AI Briefing'; + + @override + String get pushTopicCommunity => '커뮤니티'; + + @override + String get pushTopicMarketing => '마케팅·홍보'; + + @override + String linkTitle(String name) { + return '$name 연동'; + } + + @override + String get linkNotLinked => '연동되지 않음'; + + @override + String get linkLinked => '연동됨'; + + @override + String get linkSameEmailOnly => '계정 이메일과 동일한 소셜 계정만 연결 할 수 있습니다.'; + + @override + String get linkUnlink => '연동 해제'; + + @override + String get linkLink => '연동하기'; + + @override + String get noticeBadge => '공지'; + + @override + String get noticeTitle => '공지사항'; + + @override + String get noticeEmpty => '공지사항이 없습니다.'; + + @override + String get noticeDetailTitle => '공지사항 상세'; + + @override + String get translationMark => '번역함'; + + @override + String translationReplyTo(String name) { + return '$name 님 댓글 번역'; + } + + @override + String get translationWrite => '번역 댓글 작성'; + + @override + String get translationHint => '번역을 입력하세요'; + + @override + String get translationValid => '유효'; + + @override + String get translationInvalid => '무효'; + + @override + String get translationSelfMarkBlocked => '본인 번역은 마킹할 수 없습니다.'; + + @override + String get translationSubmitted => '번역을 등록했습니다. 검증을 통과하면 24시간 뒤 POP 이 지급됩니다.'; + + @override + String get translationMarked => '마킹했습니다.'; + + @override + String get translationAlreadyMarked => '이미 마킹한 번역입니다.'; + + @override + String get translationDuplicate => '이미 이 글에 같은 언어로 번역을 등록했습니다.'; + + @override + String get translationFailed => '처리하지 못했습니다. 잠시 뒤 다시 시도해 주세요.'; + + @override + String get translationNotEligible => '가입 후 24시간이 지난 이 팬덤의 멤버만 마킹할 수 있습니다.'; + + @override + String get translationDailyCap => '오늘은 번역을 더 등록할 수 없습니다.'; + + @override + String get translationInvalidTarget => '번역할 수 없는 콘텐츠입니다.'; + + @override + String get commonRequestFailed => '요청을 처리하지 못했습니다.'; + + @override + String get commonFan => '팬'; + + @override + String get adLoadFailed => '광고를 불러오지 못했어요. 기본 보상만 받습니다.'; + + @override + String get storeNotReady => '스토어 상품 준비 중'; + + @override + String get storePending => '결제 승인을 기다리고 있어요. 승인되면 자동으로 반영됩니다.'; + + @override + String get storeFailed => '결제를 완료하지 못했어요.'; + + @override + String get storePurchased => '구매가 반영되었습니다.'; + + @override + String get heroParticipate => 'ZAP+ · 선물 참여'; + + @override + String heroRemainingSemantics(String minutes, String seconds) { + return 'HERO TIME 남은 시간 $minutes분 $seconds초'; + } + + @override + String heroRemaining(String time) { + return 'HERO TIME · $time 남음'; + } + + @override + String heroClaims(String count) { + return '팬들의 선물 수령 $count건'; + } + + @override + String heroInProgress(String name) { + return '$name · HERO TIME 진행 중 →'; + } + + @override + String get stageNames => '성운,시작,성장,함께,동행,빛,영원'; + + @override + String get planetStageUnknown => '행성 · 단계 정보 없음'; + + @override + String get planetHibernatingSuffix => ' · 휴면 중'; + + @override + String get planetEclipseSuffix => ' · Eclipse 진행 중'; + + @override + String get planetHeroSuffix => ' · HERO TIME 진행 중'; + + @override + String get evoPlanetGrowth => '행성 성장'; + + @override + String get evoHeroEnd => 'HERO TIME 종료'; + + @override + String get evoAwakening => '행성이 깨어납니다'; + + @override + String get evoAnomaly => '이상 반응'; + + @override + String get evoEnergy => '에너지 집중'; + + @override + String get evoTransition => '전환'; + + @override + String get evoComplete => '행성 진화 완료'; + + @override + String get evoOpenHistory => 'PASSPORT 기록 보기'; + + @override + String get playNow => '지금 바로 플레이'; + + @override + String get playHeadline => '자, 팬덤을 만나러 가요!'; + + @override + String get playBody => '행성을 만들고 팬덤들과 소통하면서 즐거운 팬덤 활...'; + + @override + String get timeJustNow => '방금 전'; + + @override + String timeMinutesAgo(String n) { + return '$n분 전'; + } + + @override + String timeHoursAgo(String n) { + return '$n시간 전'; + } + + @override + String timeDaysAgo(String n) { + return '$n일 전'; + } + + @override + String get commonMore => '더 보기'; + + @override + String authProviderStart(String provider) { + String _temp0 = intl.Intl.selectLogic(provider, { + 'email': '이메일로 시작하기', + 'kakao': '카카오로 시작하기', + 'line': 'LINE으로 시작하기', + 'apple': 'Apple로 시작하기', + 'other': 'Google로 시작하기', + }); + return '$_temp0'; + } + + @override + String authProviderName(String provider) { + String _temp0 = intl.Intl.selectLogic(provider, { + 'email': '이메일', + 'kakao': '카카오', + 'line': 'LINE', + 'apple': 'Apple', + 'other': 'Google', + }); + return '$_temp0'; + } + + @override + String get authLastProvider => '마지막 로그인 수단은 연결을 해제할 수 없습니다.'; + + @override + String get authLoginRequired => '로그인이 필요합니다.'; + + @override + String get authDifferentEmail => '가입하신 이메일과 다른 계정입니다.'; + + @override + String get authLinkFailed => '계정 연결에 실패했습니다.'; + + @override + String get authNotLinked => '연결되어 있지 않은 계정입니다.'; + + @override + String get authEmailUnavailable => '이메일 로그인을 쓸 수 없습니다.'; + + @override + String get authCheckCredentials => '이메일 또는 비밀번호를 확인해 주세요.'; + + @override + String get authSocialFailed => '소셜 로그인에 실패했습니다.'; + + @override + String get authAppleUnavailable => '이 기기에서는 Apple 로그인을 쓸 수 없습니다.'; + + @override + String get authEmailInUse => '이미 가입된 이메일입니다.'; + + @override + String get authWeakPassword => '비밀번호가 너무 짧습니다. 8자 이상으로 정해 주세요.'; + + @override + String get authInvalidEmail => '이메일 형식이 올바르지 않습니다.'; + + @override + String get commonTryLater => '잠시 후 다시 시도해 주세요.'; + + @override + String get authCredentialInUse => '다른 FANUZU 계정에 이미 연결된 소셜 계정입니다.'; + + @override + String get authAlreadyLinked => '이미 연결된 계정입니다.'; + + @override + String get authMethodUnavailable => '이 로그인 수단은 아직 사용할 수 없습니다.'; + + @override + String get authRecentLogin => '보안을 위해 다시 로그인한 뒤 시도해 주세요.'; + + @override + String authOtherProviderGuide(String email) { + return '$email 계정은 다른 로그인 수단으로 가입돼 있습니다. 처음 가입에 쓰신 수단으로 로그인한 뒤 MY > 설정 > SNS 연동에서 이 수단을 연결해 주세요.'; + } + + @override + String get authThisAccount => '이'; + + @override + String get eclipseCapped => '참여 상한에 닿아 일부는 집계되지 않았습니다.'; + + @override + String get eclipseResultUnknown => '참여 결과를 확인하지 못했습니다. 같은 요청으로 다시 확인해 주세요.'; + + @override + String get eclipseChecking => '참여 확인 중'; + + @override + String get eclipseRetrySame => '같은 참여 다시 확인'; + + @override + String get eclipseEnded => '이클립스가 이미 끝났거나 방어에 성공했습니다.'; + + @override + String get eclipseTooFast => '너무 빠르게 참여했습니다. 잠시 뒤 다시 시도해 주십시오.'; + + @override + String get eclipseNone => '진행 중인 이클립스가 없습니다'; + + @override + String get eclipseSuccessTitle => '운석 충돌을\n성공적으로 막았어요!'; + + @override + String get eclipseQualified => '참여 인정 완료'; + + @override + String get eclipseFailTitle => '아쉽게 행성 방어를\n실패했어요'; + + @override + String get eclipseFailBody => '이번 회차의 방어 목표를 달성하지 못했습니다.'; + + @override + String get eclipseCancelTitle => '이클립스 취소'; + + @override + String get eclipseCancelBody => '집계 오류로 취소되었습니다. FPI는 차감되지 않았습니다.'; + + @override + String get eclipsePendingTitle => '방어 결과 확인 중'; + + @override + String get eclipsePendingBody => '참여 시간이 끝났습니다. 서버 판정을 기다리고 있습니다.'; + + @override + String get eclipseShakeTimed => '초간 휴대폰을 흔들어\n행성 운석 충돌을 막으세요!'; + + @override + String get eclipseShake => '휴대폰을 흔들어\n행성 운석 충돌을 막으세요!'; + + @override + String eclipseMyCount(String count) { + return '내 참여 $count회'; + } + + @override + String eclipsePendingSuffix(String count) { + return ' · +$count 전송 대기'; + } + + @override + String get eclipseSendingSuffix => ' · 전송 중'; + + @override + String eclipseNoPopNote(String count) { + return 'POP은 소모되지 않습니다. 유효 Shake $count회 이상 흔들어 방어에 참여하면 참여자로 인정됩니다.'; + } + + @override + String get eclipseTapToJoin => '흔들기 어려우면 눌러서 참여'; + + @override + String get eclipseTimeLeft => '이클립스 발생 남은 시간 :'; + + @override + String eclipseSeconds(String seconds) { + return ' $seconds초'; + } + + @override + String eclipseBannerDefended(String name) { + return '방어 성공 — $name FPI 감소 없음'; + } + + @override + String eclipseBannerActive(String name, String rate) { + return '이클립스 발동 — $name 침식 $rate%'; + } + + @override + String eclipseBannerDefense(String count, String target) { + return '유효 참여 $count / $target명'; + } + + @override + String timeHoursMinutesLeft(String hours, String minutes) { + return '$hours시간 $minutes분 남음'; + } + + @override + String timeMinutesLeft(String minutes) { + return '$minutes분 남음'; + } + + @override + String liveEclipseDefense(String name, String count) { + return '[LIVE] $name님이 이클립스 방어에 참여했습니다! (+$count)'; + } + + @override + String liveHotTime(String name) { + return '[LIVE] $name님의 아티스트 핫타임이 시작되었습니다!'; + } + + @override + String liveHeroGift(String name) { + return '[LIVE] $name님의 영웅의 선물이 도착했습니다!'; + } + + @override + String liveCampaign(String name) { + return '[LIVE] $name님이 캠페인에 후원했습니다!'; + } + + @override + String get galaxySetPrimaryConfirm => '선택한 행성을 기본 행성으로 변경할까요?'; + + @override + String get galaxyLoadFailed => '갤럭시를 불러오지 못했습니다'; + + @override + String get commonSearch => '검색'; + + @override + String get galaxyMyPlanets => '내 행성'; + + @override + String get galaxyMap => '갤럭시 맵'; + + @override + String get nebulaCreate => '성운 생성'; + + @override + String get commonRecheck => '다시 확인'; + + @override + String get zapApplying => '기여 반영 중'; + + @override + String get zapNeedsCheck => '기여 결과 확인 필요'; + + @override + String get zapNoPlanet => '소속 행성이 없습니다'; + + @override + String get commonContribute => '기여'; + + @override + String get commonItem => '아이템'; + + @override + String get galaxyDefended => '방어 성공 · FPI 감소 없음'; + + @override + String galaxyErosion(String rate, String progress) { + return '침식 $rate% · 방어 $progress%'; + } + + @override + String galaxyOnlineMembers(String online, String members) { + return '접속 $online명 · 멤버 $members명'; + } + + @override + String get galaxySearchHint => '팬덤 이름으로 검색'; + + @override + String get commonRecentSearches => '최근 검색어'; + + @override + String get commonClearAll => '전체 삭제'; + + @override + String get commonNoRecentSearches => '최근 검색어가 없습니다.'; + + @override + String get galaxyNoHit => '찾는 팬덤이 없습니다.\n이름을 다시 확인해 주세요.'; + + @override + String galaxyMembers(String count) { + return '멤버 $count명'; + } + + @override + String galaxyPlanetRank(String name, String rank) { + return '$name 행성 $rank위'; + } + + @override + String get galaxyMapOpen => '갤럭시 맵 열기'; + + @override + String get zapNotEnoughPop => 'POP이 모자랍니다'; + + @override + String get zapFailNoPop => 'POP 이 모자라 기여하지 못했습니다.'; + + @override + String get zapFailLedger => '잔액과 획득 내역이 맞지 않아 기여를 멈췄습니다.'; + + @override + String get zapFailRetry => '연결을 확인하고 같은 기여를 다시 확인해 주세요.'; + + @override + String get zapDone => '기여 완료'; + + @override + String zapDonePop(String pop) { + return '$pop POP 기여 완료'; + } + + @override + String get growthFounders => '창립 멤버'; + + @override + String get growthNebulaGoalPassed => '목표 기간이 지났어요 · 조건을 채우면 행성으로 탄생해요'; + + @override + String get growthFinalStage => '최종 Stage'; + + @override + String get growthPromotionPending => '승급 확정 대기'; + + @override + String get growthCheckConditions => '성장 조건 확인'; + + @override + String growthConditionChecking(String name) { + return '$name 조건 확인 중'; + } + + @override + String growthConditionMet(String name) { + return '$name 조건 충족'; + } + + @override + String growthConditionUnmet(String name) { + return '$name 조건 미충족'; + } + + @override + String get nebulaHeadline => '우리 팬덤 행성 이름을 만들어요.'; + + @override + String nebulaBody(String days) { + return '창립맴버가 목표 POP을 $days일내 달성하면 \n정식 행성으로 탄생합니다!\n진화에 참여한 맴버들에게는 기념 배지가 지급됩니다.'; + } + + @override + String get nebulaNameTaken => '이미 사용중인 이름입니다.'; + + @override + String get nebulaNameHint => '팬덤 이름'; + + @override + String get nebulaGoal => '목표 '; + + @override + String get nebulaFounders => ' / 창립맴버'; + + @override + String nebulaGoalSemantics(String pop, String members, String days) { + return '목표 $pop POP · 창립 멤버 $members명 · $days일 고정'; + } + + @override + String get commonMyPop => '나의 보유 POP : '; + + @override + String get commonChange => '변경'; + + @override + String planetShape(String n) { + return '형태 $n'; + } + + @override + String get planetBgColor => '배경색 선택'; + + @override + String get planetColorSelect => '행성 색 선택'; + + @override + String get planetTypeSelect => '행성 타입 선택'; + + @override + String get commonNext => '다음'; + + @override + String get planetCreateFailed => '행성을 만들지 못했습니다. 잠시 후 다시 시도해 주세요.'; + + @override + String get planetCreateConfirm => '선택한 옵션으로\n행성을 생성할까요?'; + + @override + String get commonCreate => '생성'; + + @override + String get briefingOpenFailed => '원문을 열 수 없습니다.'; + + @override + String campaignAchieved(String percent) { + return '$percent% 달성'; + } + + @override + String get campaignView => '캠페인 보기'; + + @override + String get growthFinalReached => '최종 Stage에 도달했어요'; + + @override + String get zapRetrySame => '같은 기여 다시 확인'; + + @override + String get heroOwnerSuffix => '님의'; + + @override + String get heroStartPrompt => '행성을 생성 또는 가입하고\n팬우주를 시작해 보세요!'; + + @override + String get heroNebulaPrompt => '활동을 통해 얻은 POP을 통해\n성운에 기여해 보세요!'; + + @override + String heroRecentContribution(String pop) { + return '$pop POP 기여로\n꾸준하게 성장하고 있어요.'; + } + + @override + String get heroGrowingTogether => '함께 키워가는 팬덤 행성'; + + @override + String get heroPopProgress => 'POP 진행률'; + + @override + String get heroNebula => '성운'; + + @override + String get heroNebulaTap => '성운을 클릭해서\n행성으로 탄생 시켜보세요!'; + + @override + String get heroCreateNebula => '행성 생성 (성운)'; + + @override + String get heroJoinPlanet => '행성 가입'; + + @override + String get heroCreateOrJoin => '행성 생성 또는 가입'; + + @override + String insideAnswers(String count) { + return '답변 $count'; + } + + @override + String get itemJoinFirst => '행성에 가입한 뒤 영웅의 선물을 보낼 수 있어요.'; + + @override + String get itemBuyNotReady => '아이템 구입은 준비 중입니다.'; + + @override + String get itemUseNotReady => '아이템 사용은 준비 중입니다.'; + + @override + String get payOpenFailed => '결제 창을 열지 못했어요.'; + + @override + String get itemBuy => '아이템 구입'; + + @override + String get itemSelect => '아이템 선택'; + + @override + String get commonComingSoon => '준비 중입니다.'; + + @override + String itemSendPop(String pop) { + return '$pop POP 전송'; + } + + @override + String itemBuyerPop(String pop) { + return '구매자 +$pop POP'; + } + + @override + String itemOwned(String count) { + return '보유 : $count'; + } + + @override + String itemOwnedCount(String count) { + return '보유 : $count개'; + } + + @override + String get growthOwnerOf => '님의 '; + + @override + String get growthSteadySuffix => ' 기여로 꾸준하게 성장하고 있어요.'; + + @override + String get questAttended => '출석 완료'; + + @override + String get questInProgressSpaced => '진행 중'; + + @override + String get questRewardPaid => '보상 지급 완료'; + + @override + String get questClaim => '보상받기'; + + @override + String get questStart => '퀘스트 시작하기'; + + @override + String get questRewardDone => '보상 완료'; + + @override + String get popKindGeneral => '일반 POP'; + + @override + String get popKindContribution => '기여 POP'; + + @override + String get popTxGrant => '지급'; + + @override + String get popTxSpend => '차감'; + + @override + String get popTxRefund => '환불'; + + @override + String get popTxVoid => '무효'; + + @override + String get popTypeQuest => '퀘스트'; + + @override + String get popTypeReward => '보상'; + + @override + String get popTypePayment => '결제'; + + @override + String get popTypeSubscription => '구독'; + + @override + String get popTypeAdmin => '운영'; + + @override + String get popTypeFee => '수수료'; + + @override + String get commonEtc => '기타'; + + @override + String get popReasonQuestSteps => '만보기 퀘스트'; + + @override + String get popReasonQuestMusicShake => '뮤직 셰이크 퀘스트'; + + @override + String get popReasonQuestPlace => '장소 방문 퀘스트'; + + @override + String get popReasonQuestAttend => '출석 퀘스트'; + + @override + String get popReasonAdReward => '광고 보상'; + + @override + String get popReasonHeroDrop => '영웅의 선물'; + + @override + String get popReasonHeroDropTrigger => '영웅의 선물 발동'; + + @override + String get popReasonTranslation => '참여형 번역'; + + @override + String get popReasonIapPurchase => '상품 구매'; + + @override + String get popReasonFocusDaily => 'Focus 일일 보상'; + + @override + String get popReasonPreregBonus => '사전예약 보상'; + + @override + String get popReasonReferral => '친구 초대'; + + @override + String get popTypeReferral => '레퍼럴'; + + @override + String get popReasonAdminGrant => '운영 지급'; + + @override + String get popReasonAdminClawback => '운영 회수'; + + @override + String get popReasonCampaignFund => '캠페인 후원'; + + @override + String get popReasonCampaignRefund => '캠페인 환불'; + + @override + String get popReasonCampaignFeeRefund => '캠페인 개설 수수료 환불'; + + @override + String get popReasonCampaignFee => '캠페인 등록비'; + + @override + String get popReasonQnaBounty => '인사이드 현상금'; + + @override + String get popReasonQnaRefund => '현상금 환불'; + + @override + String get popReasonQnaReward => '인사이드 채택 보상'; + + @override + String get popReasonEclipseDefense => 'Zap 기여'; + + @override + String get popReasonOther => 'POP 변동'; + + @override + String popRepeat(String title, String count) { + return '$title $count회'; + } + + @override + String get payStatePaid => '결제 완료'; + + @override + String get payStateRefundRequested => '환불 요청'; + + @override + String get payStateRefundApproved => '환불 승인'; + + @override + String get payStateRefunded => '환불 완료'; + + @override + String get payStateRefundRejected => '환불 반려'; + + @override + String get pushTopicEssentialDesc => '계정·결제·점검 안내입니다. 끄실 수 없습니다.'; + + @override + String get pushTopicFandomEventDesc => '이클립스 · 캠페인 · 퀘스트 · 아티스트 핫타임 소식입니다.'; + + @override + String get pushTopicBriefingDesc => '행성의 하루를 정리한 브리핑 카드를 알려 드립니다.'; + + @override + String get pushTopicCommunityDesc => '내 글의 댓글 · 좋아요 · 답변 알림입니다.'; + + @override + String get pushTopicMarketingDesc => '이벤트 · 혜택 정보를 받습니다. 동의하실 때만 켜집니다.'; + + @override + String get inquiryPending => '접수'; + + @override + String get inquiryInProgress => '처리중'; + + @override + String get inquiryAnswered => '답변 완료'; + + @override + String get photoBadType => 'JPG · PNG · WEBP · GIF 이미지만 올릴 수 있습니다.'; + + @override + String get photoTooBig => '사진 용량이 10MB 를 넘습니다. 더 작은 사진을 골라 주세요.'; + + @override + String get photoUnreadable => '사진을 읽지 못했습니다. 다시 골라 주세요.'; + + @override + String get myListInside => '내 인사이드'; + + @override + String get myListInsideEmpty => '아직 올리신 인사이드가 없습니다.'; + + @override + String get myListCampaign => '내 캠페인'; + + @override + String get myListCampaignEmpty => '아직 만드신 캠페인이 없습니다.'; + + @override + String get sortLatest => '최신순'; + + @override + String get sortPop => 'POP순'; + + @override + String get sortSolved => '채택 완료'; + + @override + String get sortAchieved => '달성 완료'; + + @override + String get payNotFound => '결제 내역을 찾을 수 없습니다.'; + + @override + String get payItemFallback => '결제 상품'; + + @override + String get payMethodInApp => '앱 내 구입'; + + @override + String get unknownUser => '알 수 없는 사용자'; + + @override + String get blockSelf => '자신은 차단할 수 없습니다.'; + + @override + String get inquiryFallback => '문의'; + + @override + String get photoRejectedServer => '사진을 올릴 수 없습니다. 형식과 용량을 확인해 주세요.'; + + @override + String get photoUploadFailed => '사진 업로드가 실패했습니다. 잠시 후 다시 시도해 주세요.'; + + @override + String focusClaimed(String pop) { + return '오늘의 Focus 보상 +$pop 기여 POP 을 받았어요.'; + } + + @override + String get focusActive => '광고 없는 집중모드 이용 중'; + + @override + String focusUntil(String date) { + return '$date 까지 · 오늘 몫은 오늘만 받을 수 있어요'; + } + + @override + String get focusClaimedToday => '오늘 받음'; + + @override + String get focusClaimToday => '오늘 보상 받기'; + + @override + String get storeConnectFailed => '스토어에 연결하지 못했어요. 잠시 후 다시 시도해 주세요.'; + + @override + String get payRestore => '구매 복원'; + + @override + String get payEmpty => '결제 내역이 없습니다.'; + + @override + String get payRefundGuide => + '소비형 상품 : 구매 후 7일 이내이며 사용하지 않은 상품은 환불 가능합니다. 일부라도 사용된 상품은 환불 대상에서 제외됩니다.\n구독형 상품 : 결제 후 24시간 이내 혜택 미사용(추가 보상 등) 시 환불 가능합니다.\n환불 승인 후 7-14 이내 환불 완료 처리가 됩니다.'; + + @override + String get payDetailTitle => '결제 내역 상세'; + + @override + String get payNumber => '결제 번호'; + + @override + String get payDate => '결제 일시'; + + @override + String get payMethod => '결제 방식'; + + @override + String get payStatus => '결제 상태'; + + @override + String get payAmount => '결제 금액'; + + @override + String get payRefundInfo => '환불 안내'; + + @override + String get payRefundApply => '환불 신청'; + + @override + String get payRefundUnavailable => '환불 불가'; + + @override + String get refundReasonMistake => '실수로 결제했습니다'; + + @override + String get refundReasonExpectation => '서비스가 기대와 다릅니다'; + + @override + String get refundReasonError => '오류로 사용하지 못했습니다'; + + @override + String get refundReasonDuplicate => '중복 결제했습니다'; + + @override + String get refundTitle => '환불'; + + @override + String get refundSubmit => '요청'; + + @override + String get refundReason => '환불 사유'; + + @override + String get refundConfirm => '정말 환불 신청을 할까요?'; + + @override + String get myPop => '내 POP'; + + @override + String get commonNoHistory => '내역이 없습니다.'; + + @override + String get nicknameEdit => '닉네임 수정'; + + @override + String get nicknameReserved => '운영자로 오해될 수 있는 이름은 쓸 수 없습니다.'; + + @override + String get nicknameTaken => '이미 쓰고 있는 닉네임입니다.'; + + @override + String get albumItems => '앨범 항목'; + + @override + String get takePhoto => '사진 촬영'; + + @override + String get pickFromAlbum => '앨범에서 고르기'; + + @override + String get tierGuide => '티어 안내'; + + @override + String get statSteps => '누적 걸음'; + + @override + String get statContribution => '누적 기여 POP'; + + @override + String get statQuests => '완료 퀘스트'; + + @override + String get shareInstagram => '인스타그램'; + + @override + String get shareTiktok => '틱톡'; + + @override + String get shareCopyLink => '링크 복사'; + + @override + String get inquiryListTitle => '1:1 문의 접수'; + + @override + String get inquiryEmpty => '접수하신 문의가 없습니다.'; + + @override + String get inquiryWrite => '문의 작성'; + + @override + String get inquiryDetail => '문의 상세'; + + @override + String get commonEdit => '수정'; + + @override + String get inquiryAnswer => '답변'; + + @override + String get inquiryNew => '새 문의 작성'; + + @override + String get inquiryEdit => '문의 수정'; + + @override + String get commonTitle => '제목'; + + @override + String get commonTitleHint => '제목을 입력해주세요.'; + + @override + String get inquiryBody => '문의 내용'; + + @override + String get inquiryBodyHint => '문의할 내용을 작성해 주세요.'; + + @override + String get inquiryBodyHelper => '열 글자 이상 적어 주시면 더 빨리 도와드릴 수 있습니다.'; + + @override + String inquirySubmitFailed(String reason) { + return '문의를 접수하지 못했습니다. $reason'; + } + + @override + String inquiryEditFailed(String reason) { + return '문의를 수정하지 못했습니다. $reason'; + } + + @override + String get inquirySubmitted => '문의를 접수했습니다.'; + + @override + String get inquiryEdited => '문의를 수정했습니다.'; + + @override + String get blockedEmpty => '차단한 계정이 없습니다.'; + + @override + String get unblock => '차단 해제'; + + @override + String unblockFailed(String reason) { + return '차단을 해제하지 못했습니다. $reason'; + } + + @override + String get tierInfoTitle => 'TIER 정보'; + + @override + String get tierPromptPrefix => '행성/성운의 기여를 통해\n'; + + @override + String get tierPromptRank => '랭크'; + + @override + String get tierPromptSuffix => '를 올려보세요'; + + @override + String get tierPassportNote => + 'Passport Tier 는 레벨과 별개인 카드 외형 등급이며 내려가지 않습니다.'; + + @override + String get tierTotalContribution => '누적 기여'; + + @override + String get notifKindEclipse => '이클립스'; + + @override + String get notifKindItemUsed => '아이템 사용'; + + @override + String get notifKindLike => '좋아요'; + + @override + String get notifKindComment => '댓글'; + + @override + String get notifKindCampaign => '캠페인 달성'; + + @override + String get notifKindQuest => '퀘스트 완료'; + + @override + String get notifTargetPost => '게시물'; + + @override + String get notifTargetCampaign => '캠페인'; + + @override + String get notifTargetQuest => '퀘스트 상세'; + + @override + String get dayToday => '오늘'; + + @override + String get dayYesterday => '어제'; + + @override + String get notifEmpty => '알림이 없습니다.'; + + @override + String notifGoTo(String target) { + return '$target 로 이동합니다.'; + } + + @override + String get stationOfficial => '공식'; + + @override + String get stationShop => '판매처'; + + @override + String get stationTicket => '예매'; + + @override + String get stationMedia => '미디어'; + + @override + String get stationLoadFailed => 'STATION 을 불러오지 못했습니다'; + + @override + String get commonAll => '전체'; + + @override + String get stationSuggest => '판매처 제안'; + + @override + String get stationEmpty => '아직 등록된 링크가 없습니다.\n행성에 가입하면 큐레이션이 보입니다.'; + + @override + String get stationCategoryEmpty => '이 분류에는 아직 링크가 없습니다.'; + + @override + String get stationSuggestLater => '제안 접수는 서버 연동 후 열립니다.'; + + @override + String get commonSubmit => '제출'; + + @override + String get commonName => '이름'; + + @override + String get commonNameRequired => '이름을 입력해 주세요.'; + + @override + String get commonUrl => '주소'; + + @override + String get commonUrlRequired => '주소를 입력해 주세요.'; + + @override + String get commonUrlHttps => 'https 로 시작하는 주소를 입력해 주세요.'; + + @override + String get commonCategory => '분류'; + + @override + String get stationReason => '추천 이유 (선택)'; + + @override + String get stationSuggestNote => + '제안하신 링크는 운영 검토를 거쳐 등록됩니다. 검토 결과는 알림으로 알려 드립니다.'; + + @override + String get heroGiftUnavailable => '이 선물은 현재 수령할 수 없습니다.'; + + @override + String get heroGiftAlready => '이미 참여한 선물입니다. POP 내역을 확인해 주세요.'; + + @override + String get heroGiftRelogin => '다시 로그인한 뒤 참여해 주세요.'; + + @override + String get heroGiftNoPermission => '이 선물에 참여할 수 있는 권한이 없습니다.'; + + @override + String get heroGiftHowTo => '폰을 흔들거나 아래 버튼을 눌러 빛 에너지 선물에 참여하세요.'; + + @override + String get heroGiftPopNote => '받을 수 있는 POP은 남은 선물과 1인 상한에 따라 결정됩니다.'; + + @override + String heroGiftReceived(String total) { + return '$total POP 수령 확인'; + } + + @override + String get heroGiftUnknown => '지급 결과를 확인하지 못했습니다. 다시 확인해 주세요.'; + + @override + String get heroGiftNone => '지금 참여할 수 있는 선물이 없습니다.'; + + @override + String get heroGiftConfirmed => '참여한 선물의 수령을 확인했습니다.'; + + @override + String get heroGiftChecking => '지급 확인 중…'; + + @override + String get heroGiftRetry => '같은 선물 다시 확인'; + + @override + String get heroGiftTap => '흔들기 대신 버튼으로 참여'; + + @override + String rankMyPlanet(String metric) { + return '내 행성의 $metric 랭킹'; + } + + @override + String get rankPlanetEmpty => '아직 집계된 행성 랭킹이 없습니다'; + + @override + String get rankScopeLive => '실시간'; + + @override + String get rankScopeCountry => '국가별'; + + @override + String rankMine(String metric) { + return '나의 $metric 랭킹'; + } + + @override + String rankMyCountry(String metric) { + return '내 국가의 $metric 랭킹'; + } + + @override + String get rankEmpty => '아직 집계된 랭킹이 없습니다'; + + @override + String rankScope(String scope) { + return '$scope 랭킹'; + } + + @override + String get rankLoadFailed => '랭킹을 불러오지 못했습니다'; + + @override + String rankMeSuffix(String name) { + return '$name (나)'; + } + + @override + String get reportDefamation => '아티스트 비방 및 명예훼손'; + + @override + String get reportInappropriate => '부적절한 콘텐츠'; + + @override + String get reportSpam => '스팸 및 영리 목적 광고'; + + @override + String get reportPrivacy => '개인정보 노출 및 사칭'; + + @override + String get reportTitle => '신고하기'; + + @override + String get reportSubmit => '신고'; + + @override + String get reportAuthor => '작성자'; + + @override + String get reportReasonLabel => '신고 사유'; + + @override + String get reportConfirm => '이 게시물을 신고할까요?\n접수 후에는 취소할 수 없습니다.'; + + @override + String get blockMemberConfirm => '이 회원의 모든 게시물을 차단할까요?'; + + @override + String get loungeMembersOnly => '이 행성 팬덤 회원만 글 · 댓글 · 좋아요를 남길 수 있습니다.'; + + @override + String get loungeFailed => '처리하지 못했습니다. 잠시 후 다시 시도해 주세요.'; + + @override + String get loungeTitle => '팬덤 라운지'; + + @override + String get likeCancel => '좋아요 취소'; + + @override + String get like => '좋아요'; + + @override + String get commentDeleteConfirm => '댓글을 삭제할까요?'; + + @override + String get commonShare => '공유'; + + @override + String get commonEditAction => '수정하기'; + + @override + String get commonDeleteAction => '삭제하기'; + + @override + String get commonBlockAction => '차단하기'; + + @override + String get commentFirst => '첫 댓글을 남겨주세요.'; + + @override + String get postDeleteConfirm => '이 글을 삭제할까요?'; + + @override + String get reportReceived => '신고를 접수했습니다.'; + + @override + String get commonDelete => '삭제'; + + @override + String get commentHint => '댓글을 입력해 보세요.'; + + @override + String get commonSend => '보내기'; + + @override + String get postWrite => '글 쓰기'; + + @override + String get postEdit => '글 수정'; + + @override + String get loungeHint => '팬덤 라운지에 자유롭게 글을 작성해 보세요.'; + + @override + String photoRemove(String n) { + return '사진 $n 빼기'; + } + + @override + String get camera => '카메라'; + + @override + String get album => '앨범'; + + @override + String get historyTitle => 'PASSPORT · 기록'; + + @override + String get historyMine => '내 활동'; + + @override + String historyEvolution(String from, String to) { + return 'Stage $from → $to 진화'; + } + + @override + String get historyHibernation => '행성 휴면'; + + @override + String get historyWakeUp => '행성 깨어남'; + + @override + String get historyZap => 'ZAP 기여'; + + @override + String get historyQuest => 'Quest 보상'; + + @override + String get historyAdBonus => '광고 추가 보상'; + + @override + String get historyFunding => 'Campaign 기여'; + + @override + String historyGrowthPop(String amount) { + return '성장에 $amount POP 반영'; + } + + @override + String get historyJoined => '이 진화에 함께했습니다'; + + @override + String get historyLoadFailed => '기록을 불러오지 못했습니다.'; + + @override + String get historyMineEmpty => '아직 기록된 활동이 없습니다.'; + + @override + String get historyFandomEmpty => '아직 기록된 팬덤 역사가 없습니다.'; + + @override + String get historyMore => '이전 기록 더 보기'; + + @override + String countryName(String code) { + String _temp0 = intl.Intl.selectLogic(code, { + 'KR': '대한민국', + 'JP': '일본', + 'US': '미국', + 'CN': '중국', + 'TW': '대만', + 'HK': '홍콩', + 'TH': '태국', + 'VN': '베트남', + 'ID': '인도네시아', + 'PH': '필리핀', + 'MY': '말레이시아', + 'SG': '싱가포르', + 'IN': '인도', + 'GB': '영국', + 'FR': '프랑스', + 'DE': '독일', + 'ES': '스페인', + 'BR': '브라질', + 'MX': '멕시코', + 'CA': '캐나다', + 'AU': '호주', + 'ZZ': '미지정', + 'other': '-', + }); + return '$_temp0'; + } + + @override + String loungeMaxPhotos(String count) { + return '사진은 $count장까지 올릴 수 있습니다.'; + } + + @override + String get commonLoadFailed => '불러오지 못했습니다'; + + @override + String get planetPassportHistory => 'PASSPORT · 팬덤 역사'; + + @override + String planetNamed(String name) { + return '$name 행성'; + } + + @override + String get blockNebulaConfirm => '이 성운을 차단할까요?'; + + @override + String get blockPlanetConfirm => '이 행성을 차단할까요?'; + + @override + String get blocked => '차단했습니다.'; + + @override + String planetLastUpdated(String time) { + return '마지막 갱신 $time'; + } + + @override + String get planetContribution => '기여 현황'; + + @override + String get planetNebulaGrowth => '성운 성장률'; + + @override + String get planetLiveRanking => '실시간 기여 랭킹'; + + @override + String get planetNoContributors => '아직 기여자가 없어요'; + + @override + String get planetContributeCta => '기여하고 랭킹에 등극해보세요!'; + + @override + String planetOverallRank(String rank) { + return '전체 $rank위'; + } + + @override + String get planetMyContribution => '내 기여도'; + + @override + String get planetTallying => '집계 준비 중'; + + @override + String planetTopPercent(String percent) { + return '상위 $percent%'; + } + + @override + String get planetHideMember => '이 회원의 글 모두 숨기기'; + + @override + String get reportReasonUnknown => '신고 사유를 확인할 수 없습니다.'; + + @override + String get reportAlready => '이미 신고한 게시물입니다.'; + + @override + String get blockUnavailable => '차단할 수 없는 작성자입니다.'; + + @override + String get commonMe => '나'; + + @override + String get planetSizeMedium => '중형 팬덤'; + + @override + String planetPerHead(String value) { + return '1인당 화력 : $value'; + } + + @override + String get planetJoined => '가입중'; + + @override + String get planetJoin => '가입하기'; + + @override + String get questDetails => '자세히'; + + @override + String get questInProgress => '진행중'; + + @override + String get questTypeAttend => '출석'; + + @override + String get questTypeSteps => '걷기'; + + @override + String get questTypePlace => '특정 위치'; + + @override + String get questTypeStreaming => '스트리밍'; + + @override + String get questTypeUnverifiable => '이 퀘스트 유형의 검증 방식이 아직 정해지지 않았습니다.'; + + @override + String get questTabMine => '내 퀘스트'; + + @override + String get placePickPrompt => '퀘스트를 완료할\n장소를 선택해주세요.'; + + @override + String get placeSearch => '장소 검색'; + + @override + String get commonNoResults => '검색 결과가 없습니다.'; + + @override + String get commonSelectDone => '선택 완료'; + + @override + String get adMoreChance => '더 많은 기여의 기회를 얻으세요.'; + + @override + String get adWatchPrompt => '광고를 시청하고 추가 보상을\n받을 수 있어요!'; + + @override + String get adBonusPlaceholder => '+ 기여 POP'; + + @override + String get adPartyPack => '파티 팩'; + + @override + String get adPickProduct => '상품 선택하기'; + + @override + String get adWatchBonus => '광고 보고 추가 보상 받기'; + + @override + String get adBasicReward => '기본 보상 받기'; + + @override + String get focusMode => '광고 없는 집중모드'; + + @override + String pricePerMonth(String price) { + return '$price/월'; + } + + @override + String get adWarpBefore => + 'Warping...우리 팬덤과 아티스트에 더 많은 기여를 위해 적합한 광고를 열심히 찾고 있어요.'; + + @override + String get adWarpAfter => 'Warping...당신의 기여에 대한 보상을 준비하고 있어요.'; + + @override + String get adSkip => '건너뛰기'; + + @override + String get questPreviewHint => '옵션을 선택, 미리 보기를 확인하세요'; + + @override + String get questNoCategory => '아직 선택된 카테고리가 없습니다'; + + @override + String get questNew => '새 퀘스트'; + + @override + String get questCategory => '카테고리'; + + @override + String get questName => '퀘스트명'; + + @override + String get questAction => '상세 액션'; + + @override + String get questPickPlace => '목표 위치를 골라 주세요'; + + @override + String get questGoalValue => '목표 수치'; + + @override + String get questParticipants => '참여 인원'; + + @override + String get questRewardPop => '완료 보상 POP'; + + @override + String get questCreateConfirm => '이 내용으로 만들까요?'; + + @override + String questCreateSummary(String title, String goal, String reward) { + return '$title · 목표 $goal · 보상 $reward POP'; + } + + @override + String get commonKeepEditing => '더 고치기'; + + @override + String get questCreate => '만들기'; + + @override + String questNamed(String title) { + return '$title 퀘스트'; + } + + @override + String get questDailyHelp => '매일매일 행성에 도움을'; + + @override + String get questTodayMust => '오늘 꼭 해야하는 퀘스트에요'; + + @override + String get questTabEmpty => '이 탭에는 아직 퀘스트가 없습니다.'; + + @override + String get questCreateNotReady => '퀘스트 생성은 보상 재원 예치와 함께 여는 기능입니다. 준비 중입니다.'; + + @override + String get questNoBaseReward => '기본 보상 기록을 찾지 못해 광고 추가 보상을 받을 수 없어요.'; + + @override + String get questNoSteps => '걸음 수가 아직 수집되지 않았습니다. 조금 걸은 뒤 다시 시도해 주세요.'; + + @override + String get questNoPlace => '이 퀘스트에 인증할 장소가 등록되어 있지 않습니다.'; + + @override + String get questGoal => '목표'; + + @override + String get questReward => '보상'; + + @override + String get questPopGrant => 'POP 지급'; + + @override + String get commonChecking => '확인 중…'; + + @override + String questMine(String count) { + return '내가 만든 퀘스트입니다. 참여자는 $count명입니다.'; + } + + @override + String get questStopConfirm => '퀘스트를 중단할까요?'; + + @override + String get questStopBody => '지금까지의 진행이 초기화되고 처음 상태로 돌아갑니다.'; + + @override + String get questStop => '중단'; + + @override + String get adNotFinished => '광고를 끝까지 보지 않아 추가 보상은 없어요. 기본 보상은 그대로 받습니다.'; + + @override + String get openSettings => '설정 열기'; + + @override + String get permStepsDenied => '걸음 수 권한이 꺼져 있습니다. 설정에서 「신체 활동」을 허용해 주세요.'; + + @override + String get permStepsNeeded => '걸음 수를 세려면 신체 활동 권한이 필요합니다.'; + + @override + String get permLocationOff => '기기의 위치 기능이 꺼져 있습니다. 켜고 다시 시도해 주세요.'; + + @override + String get permLocationNeeded => '방문을 확인하려면 위치 권한이 필요합니다.'; + + @override + String get permLocationDenied => '위치 권한이 꺼져 있습니다. 설정에서 위치 접근을 허용해 주세요.'; + + @override + String get questRewardReceived => ' 받았어요!'; + + @override + String questAdBonus(String pop) { + return '광고 추가 보상 +$pop POP'; + } + + @override + String questParticipantsCount(String count) { + return '참여 인원 : $count'; + } + + @override + String questParticipantsCap(String count, String cap) { + return '참여 인원 : $count / $cap'; + } + + @override + String get nebulaCreateFailed => '성운을 만들지 못했습니다. 잠시 후 다시 시도해 주세요.'; + + @override + String get nebulaAlreadyToday => '이미 오늘 성운을 생성하셨습니다.'; + + @override + String get nebulaCreatedKeepPrimary => + '성운을 만들었습니다. 기본 행성은 최근 변경 후 30일이 지나야 바꿀 수 있어 그대로 유지됩니다.'; + + @override + String get fandomFounderLeaveBlocked => '행성이 탄생한 뒤에 탈퇴할 수 있어요.'; + + @override + String get sortPopular => '인기순'; + + @override + String get campaignSortContributing => '기여중'; + + @override + String get campaignStatusReview => '운영 검수 중인 캠페인입니다.'; + + @override + String get campaignStatusRejected => '운영 검수에서 반려되었습니다. 등록비는 돌려드렸습니다.'; + + @override + String get campaignStatusWithdrawn => '개설자가 철회한 캠페인입니다.'; + + @override + String get campaignStatusCancelled => + '운영 사유로 취소되었습니다. 등록비와 후원 POP 을 돌려드렸습니다.'; + + @override + String get rankWindowDaily => '일간'; + + @override + String get rankWindowWeekly => '주간'; + + @override + String get campaignNotFound => '캠페인을 찾을 수 없습니다.'; + + @override + String get campaignMediaMain => '대표 이미지'; + + @override + String get campaignLoadFailed => '캠페인을 불러오지 못했습니다.'; + + @override + String get campaignTabInfo => '캠페인 정보'; + + @override + String campaignTabBoard(String count) { + return '응원 게시판 $count'; + } + + @override + String get campaignWithdraw => '캠페인 철회'; + + @override + String get campaignContributeAction => '기여하기'; + + @override + String get campaignFundingStatus => '현재 펀딩 현황'; + + @override + String get campaignSupporting => '응원 중'; + + @override + String get campaignTimeLeft => '남았어요!'; + + @override + String get campaignReport => '결과보고'; + + @override + String get campaignReportPhotos => '집행 사진'; + + @override + String get campaignBudget => '예산 사용 내역'; + + @override + String get campaignTotal => '합계'; + + @override + String get campaignReceipts => '영수증'; + + @override + String campaignDaysLeft(String days, String time) { + return '$days일 $time'; + } + + @override + String get campaignWithdrawTitle => '캠페인을 철회할까요?'; + + @override + String campaignWithdrawRefund(String fee) { + return '공개 전 철회라 등록비 $fee POP 을 낸 재화 그대로 돌려드립니다.'; + } + + @override + String campaignWithdrawNoRefund(String fee) { + return '이미 공개된 캠페인이라 등록비 $fee POP 은 돌려드리지 않습니다.'; + } + + @override + String get campaignWithdrawConfirm => '철회하기'; + + @override + String get shareCopied => '공유 문구를 복사했습니다.'; + + @override + String get campaignContributePop => '기여 POP 설정'; + + @override + String get campaignEmptyContributing => '아직 기여하신 캠페인이 없습니다.'; + + @override + String get campaignEmpty => '등록된 캠페인이 없습니다.'; + + @override + String get campaignNew => '새 캠페인'; + + @override + String get campaignMyRanking => '나의 기여 랭킹'; + + @override + String campaignWindowRanking(String window) { + return '$window 랭킹'; + } + + @override + String get campaignSearchHint => '캠페인 검색'; + + @override + String get campaignNoRecentSearches => '최신 검색어가 없습니다'; + + @override + String searchRemoveTerm(String term) { + return '$term 삭제'; + } + + @override + String get campaignNoResults => '검색 결과가 없습니다'; + + @override + String get shareTopicCampaign => '캠페인'; + + @override + String sharePlanetLine(String brand, String name) { + return '$brand · $name 행성'; + } + + @override + String get campaignGaugeLabel => '캠페인 기여률'; + + @override + String get campaignPeriodTitle => '캠페인 진행 기간을 선택하세요'; + + @override + String get campaignPeriodMax => '기간은 최대 1년까지 선택할 수 있어요'; + + @override + String campaignYearMonth(String year, String month) { + return '$year년 $month월'; + } + + @override + String get campaignWeekdays => '일,월,화,수,목,금,토'; + + @override + String campaignPeriodRange( + String startMonth, + String startDay, + String endMonth, + String endDay, + ) { + return '$startMonth월 $startDay일 ~ $endMonth월 $endDay일'; + } + + @override + String get campaignPeriodSelect => '기간 선택'; + + @override + String get calendarPrevMonth => '이전 달'; + + @override + String get calendarNextMonth => '다음 달'; + + @override + String get campaignShareTitle => '캠페인 공유'; + + @override + String get campaignPickMedia => '이미지/영상 선택'; + + @override + String get campaignTitleHint => '캠페인 제목을 입력해주세요.'; + + @override + String get campaignBodyHint => '캠페인 설명을 작성해주세요.'; + + @override + String get campaignReviewNote => '올리시면 운영 검수를 지난 뒤 모금이 열립니다.'; + + @override + String get campaignSupportType => '지원 종류 선택'; + + @override + String get campaignPeriod => '캠페인 진행 기간'; + + @override + String get campaignDateSelect => '날짜 선택'; + + @override + String get campaignGoalPop => '목표 기여 POP'; + + @override + String get campaignSubmitTitle => '이 내용으로 올릴까요?'; + + @override + String campaignSubmitBody( + String title, + String goal, + String days, + String fee, + ) { + return '$title · 목표 $goal POP · $days일\n등록비 $fee POP 이 빠지고 운영 검수 뒤 모금이 열립니다.'; + } + + @override + String get campaignSubmit => '올리기'; + + @override + String get campaignCreateFailed => '캠페인을 올리지 못했습니다.'; + + @override + String campaignNeedsQuests(String done, String need, String left) { + return '퀘스트 완료 $done/$need — 퀘스트를 $left번 더 완료하면 캠페인을 열 수 있습니다.'; + } + + @override + String get campaignPendingTitle => '검수 대기 중입니다'; + + @override + String get campaignPendingBody => '운영 검수를 마치면 모금이 열리고 목록에 보입니다.'; + + @override + String get insideRefundPending => '채택 기한이 지나 현상금이 환불될 예정입니다.'; + + @override + String get insideRefundDone => '채택 기한이 지나 현상금이 환불되었습니다.'; + + @override + String get insideRefundChecking => '채택 기한이 지나 현상금 환불을 확인하고 있습니다.'; + + @override + String get insideErrPopShort => 'POP 이 모자라 현상금을 걸지 못했습니다.'; + + @override + String get insideErrSelfAccept => '자기 답변은 채택할 수 없습니다.'; + + @override + String get insideErrAlreadyAccepted => '이미 채택한 질문입니다. 채택은 바꿀 수 없습니다.'; + + @override + String get insideErrNotOwner => '질문을 올린 분만 채택할 수 있습니다.'; + + @override + String get commonCheckConnection => '연결을 확인하고 다시 시도해 주세요.'; + + @override + String get insideAnswerLater => '댓글 등록은 서버 연동 후 열립니다.'; + + @override + String insideAcceptConfirm(String pop) { + return '이 댓글을 채택할까요?\n현상금 $pop POP 이 전달되며 채택은 되돌릴 수 없습니다.'; + } + + @override + String get insideAccept => '채택'; + + @override + String get insideMore => '더보기'; + + @override + String get insideFirstAnswer => '첫 댓글을 남겨주세요.'; + + @override + String get insideAcceptDone => '채택 완료'; + + @override + String get insideAcceptAction => '채택하기'; + + @override + String get insideAnswerHint => '댓글을 입력해 보세요.'; + + @override + String get insideLoadFailed => '인사이드를 불러오지 못했습니다'; + + @override + String get insideEmpty => '아직 올라온 질문이 없습니다.'; + + @override + String get insideWrite => '인사이드 작성'; + + @override + String get insideSearchHint => '인사이드 제목, 내용'; + + @override + String get insideBodyHint => '팬덤들과 자유롭게 질문을 주고받아 보세요.'; + + @override + String get insideTitleRequired => '제목을 입력해 주세요.'; + + @override + String get insideBodyRequired => '내용을 입력해 주세요.'; + + @override + String get insideBountySet => '현상금 설정'; + + @override + String insideBountyHint(String min) { + return '$min 이상 설정해 주세요.'; + } + + @override + String get insideAttachLater => '이미지 첨부는 서버 연동 후 열립니다.'; + + @override + String get insideBountyRequired => '현상금을 입력해 주세요.'; + + @override + String get insideNumberOnly => '숫자로 입력해 주세요.'; + + @override + String insideBountyMin(String min) { + return '현상금은 $min POP 이상이어야 합니다.'; + } + + @override + String get insideBountyOverBalance => '보유 POP 을 넘을 수 없습니다.'; + + @override + String get questTypeTimeAttack => '긴급 타임어택'; + + @override + String get questTimeLeft => '남은 시간'; + + @override + String get questTimeAttackClosed => '마감'; + + @override + String questTimeAttackLeft(String time) { + return '$time 남음'; + } + + @override + String get popReasonQuestTimeAttack => '긴급 돌발 타임어택'; + + @override + String get briefingShareCopied => '공유 문구와 링크를 복사했습니다.'; + + @override + String get shareCopyText => '문구 복사'; + + @override + String get briefingShareTextCopied => '공유 문구를 복사했습니다.'; + + @override + String questAlreadyRewarded(String pop) { + return '이미 받은 보상입니다(+$pop POP).'; + } + + @override + String get questCatHealth => '건강'; + + @override + String get questCatPlanet => '행성 활동'; + + @override + String get questCatCommunity => '커뮤니티'; + + @override + String get questActShake => '쉐이크'; + + @override + String get questTypeVote => '투표 참여'; + + @override + String get questTypeSns => 'SNS'; + + @override + String get questActivity => '활동'; + + @override + String get questNoCreatable => '지금 만들 수 있는 퀘스트 활동이 없습니다.'; + + @override + String get questDescriptionOptional => '설명 (선택)'; + + @override + String get questLinkUrl => '링크 주소'; + + @override + String get questLinkCheck => '링크 확인'; + + @override + String get questLinkInvalid => '올바른 링크가 아닙니다. 주소를 확인해 주세요.'; + + @override + String get questLinkPreviewFailed => '링크 정보를 불러오지 못했어요. 제목을 직접 입력해 주세요.'; + + @override + String get questLinkTitle => '링크 제목'; + + @override + String get questEndDate => '종료일'; + + @override + String get questEndDefault => '공개 후 24시간'; + + @override + String questEndDateValue(String date) { + return '$date 23:59:59 마감'; + } + + @override + String questPoolHelper(String pool, String fee) { + return '총 $pool POP 예치 · 수수료 $fee POP'; + } + + @override + String questCreateCost( + String title, + String reward, + String count, + String pool, + String fee, + String total, + ) { + return '$title\n보상 $reward POP × $count명 = $pool POP\n수수료 $fee POP (공개 시 소멸)\n총 $total POP 이 차감됩니다.'; + } + + @override + String get questNoReward => '무보상 참여 퀘스트예요.'; + + @override + String get questDeadline => '마감'; + + @override + String questRemainingHours(String hours) { + return '$hours시간 남음'; + } + + @override + String get questEnded => '마감됨'; + + @override + String get questAuthorNote => '작성자 설명'; + + @override + String get questRepeatOnce => '1인 1회 참여'; + + @override + String questRepeatEvery(String hours, String count) { + return '$hours시간 간격 · 최대 $count회'; + } + + @override + String get questSubmit => '인증 제출'; + + @override + String get questResubmit => '보완 제출'; + + @override + String get questEvidenceUrl => '인증 링크 (선택)'; + + @override + String get questEvidenceText => '인증 내용'; + + @override + String get questSubmitted => '제출했어요. 검토 후 보상이 지급돼요.'; + + @override + String get questReviewPending => '검토 중이에요. 승인되면 보상이 지급돼요.'; + + @override + String get questReviewDelayed => '검토가 늦어지고 있어요. 운영팀이 확인하고 있습니다.'; + + @override + String get questApprovedPaying => '승인되었어요. 보상을 지급하고 있어요.'; + + @override + String questRejected(String reason) { + return '반려: $reason'; + } + + @override + String get questResubmitAllowed => '보완해서 다시 제출할 수 있어요.'; + + @override + String get questResubmitNotAllowed => '이 제출 건은 다시 제출할 수 없어요.'; + + @override + String get questReviewButton => '검토 중'; + + @override + String get questRejectedButton => '반려됨'; + + @override + String questReviewQueue(String count) { + return '검토 대기 $count건'; + } + + @override + String get questApprove => '승인'; + + @override + String get questReject => '반려'; + + @override + String get questRejectReason => '반려 사유'; + + @override + String get questAllowResubmit => '보완 재제출 허용'; + + @override + String get questEdit => '제목 · 설명 수정'; + + @override + String get questEditNotice => '공개 후에는 제목과 설명만 고칠 수 있고 수정 이력이 남아요.'; + + @override + String get popReasonQuestUgc => '퀘스트 보상'; + + @override + String get popReasonQuestFee => '퀘스트 개설 수수료'; + + @override + String questFreeDailyCap(String cap) { + return '보상 없는 퀘스트는 하루 $cap개까지 만들 수 있어요.'; + } + + @override + String get popReasonQuestPoolDeposit => '퀘스트 보상 예치'; + + @override + String get popReasonQuestPoolReturn => '퀘스트 보상 반환'; + + @override + String get campaignCommentNeedNickname => '닉네임을 먼저 정해 주세요'; + + @override + String get campaignCommentSetNickname => '닉네임 정하기'; +} diff --git a/app/lib/l10n/app_localizations_zh.dart b/app/lib/l10n/app_localizations_zh.dart new file mode 100644 index 0000000..d580b50 --- /dev/null +++ b/app/lib/l10n/app_localizations_zh.dart @@ -0,0 +1,2655 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Chinese (`zh`). +class AppLocalizationsZh extends AppLocalizations { + AppLocalizationsZh([String locale = 'zh']) : super(locale); + + @override + String get commonConfirm => '确认'; + + @override + String get commonCancel => '取消'; + + @override + String get commonClose => '关闭'; + + @override + String get commonSelect => '选择'; + + @override + String get commonRetry => '重试'; + + @override + String get commonView => '查看'; + + @override + String get commonPrev => '上一步'; + + @override + String get commonDone => '完成'; + + @override + String get commonStart => '开始'; + + @override + String commonNotYet(String where) { + return '$where页面将在第二次实现确认(2026-09-10)中加入。'; + } + + @override + String get languageSettingsTitle => '语言设置'; + + @override + String get authLogin => '登录'; + + @override + String get authSignup => '注册'; + + @override + String get authFirstTime => '第一次使用FANUZU?'; + + @override + String get authAlreadyMember => '已经注册了吗?'; + + @override + String get authLoginHeadline => '用最方便的方式\n登录FANUZU!'; + + @override + String get authSignupHeadline => '用最方便的方式\n注册FANUZU!'; + + @override + String authProviderLogin(String provider) { + String _temp0 = intl.Intl.selectLogic(provider, { + 'apple': '通过Apple登录', + 'google': '通过Google登录', + 'kakao': '通过Kakao登录', + 'line': '通过LINE登录', + 'other': '通过邮箱登录', + }); + return '$_temp0'; + } + + @override + String authProviderSignup(String provider) { + String _temp0 = intl.Intl.selectLogic(provider, { + 'apple': '通过Apple注册', + 'google': '通过Google注册', + 'kakao': '通过Kakao注册', + 'line': '通过LINE注册', + 'other': '通过邮箱注册', + }); + return '$_temp0'; + } + + @override + String authNotConfiguredTitle(String name) { + return '$name登录准备中'; + } + + @override + String authNotConfiguredBody(String name, String items) { + return '$name登录将在客户提供密钥后开放。\n\n所需项目\n· $items'; + } + + @override + String authNotConfiguredNotice(String name, String items) { + return '$name登录尚在准备中。\n所需项目: $items'; + } + + @override + String get authResetNeedEmail => '请先输入接收重置邮件的邮箱。'; + + @override + String get authResetSent => '已向该地址发送重置说明。'; + + @override + String get termsTitle => '使用条款'; + + @override + String get termsAgreeAll => '全部同意'; + + @override + String get termsService => '服务条款(必选)'; + + @override + String get termsLocation => '位置服务条款(必选)'; + + @override + String get termsAge => '本人已满14周岁。(必选)'; + + @override + String get termsMarketing => '同意接收营销信息与活动通知(可选)'; + + @override + String get signupDoneTitle => '注册完成'; + + @override + String get signupDoneBody => '现在可以开始粉丝活动了。\n通过各种活动培养你的粉丝星球吧。'; + + @override + String get homeItemsTooltip => '道具'; + + @override + String get homeNotificationsTooltip => '通知'; + + @override + String get homeFandomHistory => '粉丝历史'; + + @override + String get homePopularInside => '热门 K-INSIDE'; + + @override + String get homeWhereBriefing => 'AI简报'; + + @override + String get homeWhereInsideList => 'INSIDE列表'; + + @override + String get homeWhereInsideDetail => 'INSIDE详情'; + + @override + String get homeLoadFailed => '无法加载首页'; + + @override + String get homeRetryLater => '请稍后再试。'; + + @override + String get myProfileCardTooltip => '个人名片'; + + @override + String get myTabPlanets => '星球/星云'; + + @override + String get myTabActivity => '活动'; + + @override + String get myInside => 'Inside'; + + @override + String get myCampaign => '应援活动'; + + @override + String myShareLater(String target) { + return '$target分享将在09-10版本中接入。'; + } + + @override + String get myEditPhoto => '修改头像'; + + @override + String get myEditProfile => '编辑资料'; + + @override + String get settingsTitle => '设置'; + + @override + String get settingsAccount => '账号'; + + @override + String get settingsNickname => '昵称'; + + @override + String get settingsEmail => '邮箱'; + + @override + String get settingsSns => '已关联社交账号'; + + @override + String get settingsPopHistory => 'POP记录'; + + @override + String get settingsPayments => '支付记录'; + + @override + String get settingsServicePayments => '服务支付记录'; + + @override + String get settingsService => '服务设置'; + + @override + String get settingsPush => '推送通知设置'; + + @override + String get settingsBlocked => '屏蔽设置'; + + @override + String get settingsInfo => '服务信息'; + + @override + String get settingsNotice => '公告'; + + @override + String get settingsInquiry => '1:1咨询'; + + @override + String get settingsAppVersion => '应用版本'; + + @override + String get settingsLatest => '最新版本'; + + @override + String get settingsLogout => '退出登录'; + + @override + String get settingsLogoutConfirm => '确定要退出登录吗?'; + + @override + String get settingsWithdraw => '注销账号'; + + @override + String get settingsWithdrawConfirm => '确定要注销账号吗?'; + + @override + String get settingsWithdrawBody => '您持有的POP将会清零且无法恢复。'; + + @override + String get settingsWithdrawLater => '注销处理将在09-10版本中接入。'; + + @override + String get settingsUnlinkConfirm => '确定要解除关联账号吗?'; + + @override + String get settingsUnlinkBody => '仅移除该社交登录方式,账号信息不会被删除。'; + + @override + String get settingsUnlink => '解除关联'; + + @override + String get settingsSameEmailOnly => '只能关联与账号邮箱\n相同的社交账号。'; + + @override + String settingsLinkNotReady(String name, String items) { + return '$name关联尚在准备中。所需项目: $items'; + } + + @override + String get settingsConnect => '关联'; + + @override + String get settingsDisconnect => '解除'; + + @override + String get settingsEmailProvider => '邮箱'; + + @override + String get pushDeviceOff => '设备的推送通知已关闭。'; + + @override + String get pushDeviceOffHint => '请在系统设置中允许通知以接收消息。'; + + @override + String get pushRequesting => '请求中…'; + + @override + String get pushGoToSettings => '前往设置'; + + @override + String get pushOpeningDeviceSettings => '正在前往设备设置。'; + + @override + String get pushEnableInDevice => '请在设备设置中开启FANUZU通知。'; + + @override + String get pushDeviceSection => '设备通知设置'; + + @override + String get pushSoundVibration => '声音、振动设置'; + + @override + String get pushServiceSection => '服务通知设置'; + + @override + String get pushTopicEssential => '服务必需'; + + @override + String get pushTopicFandomEvent => '粉丝活动'; + + @override + String get pushTopicBriefing => 'AI Briefing'; + + @override + String get pushTopicCommunity => '社区'; + + @override + String get pushTopicMarketing => '营销推广'; + + @override + String linkTitle(String name) { + return '$name关联'; + } + + @override + String get linkNotLinked => '未关联'; + + @override + String get linkLinked => '已关联'; + + @override + String get linkSameEmailOnly => '只能关联与账号邮箱相同的社交账号。'; + + @override + String get linkUnlink => '解除关联'; + + @override + String get linkLink => '关联'; + + @override + String get noticeBadge => '公告'; + + @override + String get noticeTitle => '公告'; + + @override + String get noticeEmpty => '暂无公告。'; + + @override + String get noticeDetailTitle => '公告详情'; + + @override + String get translationMark => '已翻译'; + + @override + String translationReplyTo(String name) { + return '翻译 $name 的评论'; + } + + @override + String get translationWrite => '撰写翻译评论'; + + @override + String get translationHint => '请输入翻译'; + + @override + String get translationValid => '有效'; + + @override + String get translationInvalid => '无效'; + + @override + String get translationSelfMarkBlocked => '不能标记自己的翻译。'; + + @override + String get translationSubmitted => '翻译已提交。通过验证后将在24小时后发放POP。'; + + @override + String get translationMarked => '已标记。'; + + @override + String get translationAlreadyMarked => '您已标记过该翻译。'; + + @override + String get translationDuplicate => '您已为该内容提交过同一语言的翻译。'; + + @override + String get translationFailed => '处理失败,请稍后再试。'; + + @override + String get translationNotEligible => '仅注册满24小时的本粉丝团成员可以标记。'; + + @override + String get translationDailyCap => '今天无法再提交翻译。'; + + @override + String get translationInvalidTarget => '该内容无法翻译。'; + + @override + String get commonRequestFailed => '无法处理请求。'; + + @override + String get commonFan => '粉丝'; + + @override + String get adLoadFailed => '广告加载失败,仅发放基础奖励。'; + + @override + String get storeNotReady => '商店商品准备中'; + + @override + String get storePending => '正在等待支付确认,确认后将自动生效。'; + + @override + String get storeFailed => '支付未完成。'; + + @override + String get storePurchased => '购买已生效。'; + + @override + String get heroParticipate => 'ZAP+ · 参与礼物'; + + @override + String heroRemainingSemantics(String minutes, String seconds) { + return 'HERO TIME 剩余 $minutes 分 $seconds 秒'; + } + + @override + String heroRemaining(String time) { + return 'HERO TIME · 剩余 $time'; + } + + @override + String heroClaims(String count) { + return '粉丝已领取礼物 $count 次'; + } + + @override + String heroInProgress(String name) { + return '$name · HERO TIME 进行中 →'; + } + + @override + String get stageNames => '星云,起始,成长,同心,同行,光芒,永恒'; + + @override + String get planetStageUnknown => '星球 · 无阶段信息'; + + @override + String get planetHibernatingSuffix => ' · 休眠中'; + + @override + String get planetEclipseSuffix => ' · Eclipse 进行中'; + + @override + String get planetHeroSuffix => ' · HERO TIME 进行中'; + + @override + String get evoPlanetGrowth => '星球成长'; + + @override + String get evoHeroEnd => 'HERO TIME 结束'; + + @override + String get evoAwakening => '星球正在苏醒'; + + @override + String get evoAnomaly => '异常反应'; + + @override + String get evoEnergy => '能量汇聚'; + + @override + String get evoTransition => '转换'; + + @override + String get evoComplete => '星球进化完成'; + + @override + String get evoOpenHistory => '查看 PASSPORT 记录'; + + @override + String get playNow => '立即开玩'; + + @override + String get playHeadline => '来吧,去见你的粉丝圈!'; + + @override + String get playBody => '创建星球,与粉丝们交流,享受愉快的追星活动...'; + + @override + String get timeJustNow => '刚刚'; + + @override + String timeMinutesAgo(String n) { + return '$n分钟前'; + } + + @override + String timeHoursAgo(String n) { + return '$n小时前'; + } + + @override + String timeDaysAgo(String n) { + return '$n天前'; + } + + @override + String get commonMore => '更多'; + + @override + String authProviderStart(String provider) { + String _temp0 = intl.Intl.selectLogic(provider, { + 'email': '使用邮箱开始', + 'kakao': '使用 Kakao 开始', + 'line': '使用 LINE 开始', + 'apple': '使用 Apple 开始', + 'other': '使用 Google 开始', + }); + return '$_temp0'; + } + + @override + String authProviderName(String provider) { + String _temp0 = intl.Intl.selectLogic(provider, { + 'email': '邮箱', + 'kakao': 'Kakao', + 'line': 'LINE', + 'apple': 'Apple', + 'other': 'Google', + }); + return '$_temp0'; + } + + @override + String get authLastProvider => '无法解除最后一个登录方式的关联。'; + + @override + String get authLoginRequired => '请先登录。'; + + @override + String get authDifferentEmail => '该账号与注册邮箱不一致。'; + + @override + String get authLinkFailed => '账号关联失败。'; + + @override + String get authNotLinked => '该账号未关联。'; + + @override + String get authEmailUnavailable => '无法使用邮箱登录。'; + + @override + String get authCheckCredentials => '请检查邮箱或密码。'; + + @override + String get authSocialFailed => '社交账号登录失败。'; + + @override + String get authAppleUnavailable => '此设备无法使用 Apple 登录。'; + + @override + String get authEmailInUse => '该邮箱已注册。'; + + @override + String get authWeakPassword => '密码太短,请至少设置 8 个字符。'; + + @override + String get authInvalidEmail => '邮箱格式不正确。'; + + @override + String get commonTryLater => '请稍后再试。'; + + @override + String get authCredentialInUse => '该社交账号已关联其他 FANUZU 账号。'; + + @override + String get authAlreadyLinked => '该账号已关联。'; + + @override + String get authMethodUnavailable => '此登录方式暂不可用。'; + + @override + String get authRecentLogin => '为了安全,请重新登录后再试。'; + + @override + String authOtherProviderGuide(String email) { + return '$email 账号已通过其他登录方式注册。请使用首次注册时的方式登录,然后在 MY > 设置 > SNS 关联中关联此方式。'; + } + + @override + String get authThisAccount => '此'; + + @override + String get eclipseCapped => '已达参与上限,部分未计入。'; + + @override + String get eclipseResultUnknown => '无法确认参与结果,请以相同请求再次确认。'; + + @override + String get eclipseChecking => '正在确认参与'; + + @override + String get eclipseRetrySame => '重新确认本次参与'; + + @override + String get eclipseEnded => '日蚀已结束或已防御成功。'; + + @override + String get eclipseTooFast => '参与过快,请稍后再试。'; + + @override + String get eclipseNone => '当前没有进行中的日蚀'; + + @override + String get eclipseSuccessTitle => '成功阻止了\n陨石撞击!'; + + @override + String get eclipseQualified => '参与已认定'; + + @override + String get eclipseFailTitle => '很遗憾,\n星球防御失败了'; + + @override + String get eclipseFailBody => '未达成本轮防御目标。'; + + @override + String get eclipseCancelTitle => '日蚀已取消'; + + @override + String get eclipseCancelBody => '因统计错误已取消,FPI 未扣除。'; + + @override + String get eclipsePendingTitle => '正在确认防御结果'; + + @override + String get eclipsePendingBody => '参与时间已结束,正在等待服务器判定。'; + + @override + String get eclipseShakeTimed => '秒内摇动手机\n阻止陨石撞击星球!'; + + @override + String get eclipseShake => '摇动手机\n阻止陨石撞击星球!'; + + @override + String eclipseMyCount(String count) { + return '我的参与 $count 次'; + } + + @override + String eclipsePendingSuffix(String count) { + return ' · +$count 待发送'; + } + + @override + String get eclipseSendingSuffix => ' · 发送中'; + + @override + String eclipseNoPopNote(String count) { + return '不会消耗 POP。有效摇动 $count 次以上即可认定为参与者。'; + } + + @override + String get eclipseTapToJoin => '不方便摇动?点击参与'; + + @override + String get eclipseTimeLeft => '日蚀剩余时间:'; + + @override + String eclipseSeconds(String seconds) { + return ' $seconds 秒'; + } + + @override + String eclipseBannerDefended(String name) { + return '防御成功 — $name FPI 未减少'; + } + + @override + String eclipseBannerActive(String name, String rate) { + return '日蚀发动 — $name 侵蚀 $rate%'; + } + + @override + String eclipseBannerDefense(String count, String target) { + return '有效参与 $count / $target 人'; + } + + @override + String timeHoursMinutesLeft(String hours, String minutes) { + return '剩余 $hours 小时 $minutes 分'; + } + + @override + String timeMinutesLeft(String minutes) { + return '剩余 $minutes 分'; + } + + @override + String liveEclipseDefense(String name, String count) { + return '[LIVE] $name 参与了日蚀防御!(+$count)'; + } + + @override + String liveHotTime(String name) { + return '[LIVE] $name 的艺人热力时刻开始了!'; + } + + @override + String liveHeroGift(String name) { + return '[LIVE] $name 的英雄礼物到了!'; + } + + @override + String liveCampaign(String name) { + return '[LIVE] $name 支持了一个活动!'; + } + + @override + String get galaxySetPrimaryConfirm => '要将所选星球设为默认星球吗?'; + + @override + String get galaxyLoadFailed => '无法加载银河'; + + @override + String get commonSearch => '搜索'; + + @override + String get galaxyMyPlanets => '我的星球'; + + @override + String get galaxyMap => '银河地图'; + + @override + String get nebulaCreate => '创建星云'; + + @override + String get commonRecheck => '重新确认'; + + @override + String get zapApplying => '正在计入贡献'; + + @override + String get zapNeedsCheck => '需确认贡献结果'; + + @override + String get zapNoPlanet => '你还没有所属星球'; + + @override + String get commonContribute => '贡献'; + + @override + String get commonItem => '道具'; + + @override + String get galaxyDefended => '防御成功 · FPI 未减少'; + + @override + String galaxyErosion(String rate, String progress) { + return '侵蚀 $rate% · 防御 $progress%'; + } + + @override + String galaxyOnlineMembers(String online, String members) { + return '在线 $online 人 · 成员 $members 人'; + } + + @override + String get galaxySearchHint => '按粉丝圈名称搜索'; + + @override + String get commonRecentSearches => '最近搜索'; + + @override + String get commonClearAll => '全部删除'; + + @override + String get commonNoRecentSearches => '没有最近搜索。'; + + @override + String get galaxyNoHit => '未找到该粉丝圈。\n请再次确认名称。'; + + @override + String galaxyMembers(String count) { + return '成员 $count 人'; + } + + @override + String galaxyPlanetRank(String name, String rank) { + return '$name 星球 第 $rank 名'; + } + + @override + String get galaxyMapOpen => '打开银河地图'; + + @override + String get zapNotEnoughPop => 'POP 不足'; + + @override + String get zapFailNoPop => 'POP 不足,无法贡献。'; + + @override + String get zapFailLedger => '余额与获取记录不符,已暂停贡献。'; + + @override + String get zapFailRetry => '请检查网络并重新确认本次贡献。'; + + @override + String get zapDone => '贡献完成'; + + @override + String zapDonePop(String pop) { + return '已贡献 $pop POP'; + } + + @override + String get growthFounders => '创始成员'; + + @override + String get growthNebulaGoalPassed => '目标期限已过 · 满足条件即可诞生行星'; + + @override + String get growthFinalStage => '最终阶段'; + + @override + String get growthPromotionPending => '等待晋级确认'; + + @override + String get growthCheckConditions => '查看成长条件'; + + @override + String growthConditionChecking(String name) { + return '正在确认$name条件'; + } + + @override + String growthConditionMet(String name) { + return '已满足$name条件'; + } + + @override + String growthConditionUnmet(String name) { + return '未满足$name条件'; + } + + @override + String get nebulaHeadline => '为我们的粉丝星球取个名字吧。'; + + @override + String nebulaBody(String days) { + return '创始成员在 $days 天内达成目标 POP,\n即可诞生为正式星球!\n参与进化的成员将获得纪念徽章。'; + } + + @override + String get nebulaNameTaken => '该名称已被使用。'; + + @override + String get nebulaNameHint => '粉丝圈名称'; + + @override + String get nebulaGoal => '目标 '; + + @override + String get nebulaFounders => ' / 创始成员'; + + @override + String nebulaGoalSemantics(String pop, String members, String days) { + return '目标 $pop POP · 创始成员 $members 人 · 固定 $days 天'; + } + + @override + String get commonMyPop => '我的 POP:'; + + @override + String get commonChange => '更改'; + + @override + String planetShape(String n) { + return '形态 $n'; + } + + @override + String get planetBgColor => '选择背景色'; + + @override + String get planetColorSelect => '选择星球颜色'; + + @override + String get planetTypeSelect => '选择星球类型'; + + @override + String get commonNext => '下一步'; + + @override + String get planetCreateFailed => '无法创建星球,请稍后再试。'; + + @override + String get planetCreateConfirm => '要用所选选项\n创建星球吗?'; + + @override + String get commonCreate => '创建'; + + @override + String get briefingOpenFailed => '无法打开原文。'; + + @override + String campaignAchieved(String percent) { + return '已达成 $percent%'; + } + + @override + String get campaignView => '查看活动'; + + @override + String get growthFinalReached => '已到达最终阶段'; + + @override + String get zapRetrySame => '重新确认本次贡献'; + + @override + String get heroOwnerSuffix => '的'; + + @override + String get heroStartPrompt => '创建或加入星球,\n开启粉丝宇宙吧!'; + + @override + String get heroNebulaPrompt => '用活动获得的 POP\n为星云做贡献吧!'; + + @override + String heroRecentContribution(String pop) { + return '凭借 $pop POP 的贡献\n正在稳步成长。'; + } + + @override + String get heroGrowingTogether => '一起培育的粉丝星球'; + + @override + String get heroPopProgress => 'POP 进度'; + + @override + String get heroNebula => '星云'; + + @override + String get heroNebulaTap => '点击星云,\n让它诞生为星球吧!'; + + @override + String get heroCreateNebula => '创建星球(星云)'; + + @override + String get heroJoinPlanet => '加入星球'; + + @override + String get heroCreateOrJoin => '创建或加入星球'; + + @override + String insideAnswers(String count) { + return '回答 $count'; + } + + @override + String get itemJoinFirst => '加入星球后才能发送英雄礼物。'; + + @override + String get itemBuyNotReady => '道具购买功能准备中。'; + + @override + String get itemUseNotReady => '道具使用功能准备中。'; + + @override + String get payOpenFailed => '无法打开支付窗口。'; + + @override + String get itemBuy => '购买道具'; + + @override + String get itemSelect => '选择道具'; + + @override + String get commonComingSoon => '准备中。'; + + @override + String itemSendPop(String pop) { + return '发送 $pop POP'; + } + + @override + String itemBuyerPop(String pop) { + return '购买者 +$pop POP'; + } + + @override + String itemOwned(String count) { + return '持有:$count'; + } + + @override + String itemOwnedCount(String count) { + return '持有:$count 个'; + } + + @override + String get growthOwnerOf => '的 '; + + @override + String get growthSteadySuffix => ' 贡献,正在稳步成长。'; + + @override + String get questAttended => '已签到'; + + @override + String get questInProgressSpaced => '进行中'; + + @override + String get questRewardPaid => '奖励已发放'; + + @override + String get questClaim => '领取奖励'; + + @override + String get questStart => '开始任务'; + + @override + String get questRewardDone => '已领取奖励'; + + @override + String get popKindGeneral => '普通 POP'; + + @override + String get popKindContribution => '贡献 POP'; + + @override + String get popTxGrant => '发放'; + + @override + String get popTxSpend => '扣除'; + + @override + String get popTxRefund => '退款'; + + @override + String get popTxVoid => '作废'; + + @override + String get popTypeQuest => '任务'; + + @override + String get popTypeReward => '奖励'; + + @override + String get popTypePayment => '支付'; + + @override + String get popTypeSubscription => '订阅'; + + @override + String get popTypeAdmin => '运营'; + + @override + String get popTypeFee => '手续费'; + + @override + String get commonEtc => '其他'; + + @override + String get popReasonQuestSteps => '计步任务'; + + @override + String get popReasonQuestMusicShake => '音乐摇摇任务'; + + @override + String get popReasonQuestPlace => '地点打卡任务'; + + @override + String get popReasonQuestAttend => '签到任务'; + + @override + String get popReasonAdReward => '广告奖励'; + + @override + String get popReasonHeroDrop => '英雄礼物'; + + @override + String get popReasonHeroDropTrigger => '英雄礼物发动'; + + @override + String get popReasonTranslation => '参与式翻译'; + + @override + String get popReasonIapPurchase => '购买商品'; + + @override + String get popReasonFocusDaily => 'Focus 每日奖励'; + + @override + String get popReasonPreregBonus => '预约奖励'; + + @override + String get popReasonReferral => '邀请好友'; + + @override + String get popTypeReferral => '推荐'; + + @override + String get popReasonAdminGrant => '运营发放'; + + @override + String get popReasonAdminClawback => '运营回收'; + + @override + String get popReasonCampaignFund => '活动支持'; + + @override + String get popReasonCampaignRefund => '活动退款'; + + @override + String get popReasonCampaignFeeRefund => '活动开设手续费退款'; + + @override + String get popReasonCampaignFee => '活动登记费'; + + @override + String get popReasonQnaBounty => 'INSIDE 悬赏'; + + @override + String get popReasonQnaRefund => '悬赏退款'; + + @override + String get popReasonQnaReward => 'INSIDE 采纳奖励'; + + @override + String get popReasonEclipseDefense => 'Zap 贡献'; + + @override + String get popReasonOther => 'POP 变动'; + + @override + String popRepeat(String title, String count) { + return '$title $count 次'; + } + + @override + String get payStatePaid => '支付完成'; + + @override + String get payStateRefundRequested => '已申请退款'; + + @override + String get payStateRefundApproved => '退款已批准'; + + @override + String get payStateRefunded => '退款完成'; + + @override + String get payStateRefundRejected => '退款被驳回'; + + @override + String get pushTopicEssentialDesc => '账号、支付、维护通知,无法关闭。'; + + @override + String get pushTopicFandomEventDesc => '日蚀、活动、任务、艺人热力时刻消息。'; + + @override + String get pushTopicBriefingDesc => '为你推送星球每日简报卡片。'; + + @override + String get pushTopicCommunityDesc => '我的帖子的评论、点赞、回答通知。'; + + @override + String get pushTopicMarketingDesc => '接收活动与优惠信息,仅在同意后开启。'; + + @override + String get inquiryPending => '已受理'; + + @override + String get inquiryInProgress => '处理中'; + + @override + String get inquiryAnswered => '已回复'; + + @override + String get photoBadType => '仅可上传 JPG、PNG、WEBP、GIF 图片。'; + + @override + String get photoTooBig => '照片超过 10MB,请选择更小的照片。'; + + @override + String get photoUnreadable => '无法读取照片,请重新选择。'; + + @override + String get myListInside => '我的 INSIDE'; + + @override + String get myListInsideEmpty => '你还没有发布 INSIDE。'; + + @override + String get myListCampaign => '我的活动'; + + @override + String get myListCampaignEmpty => '你还没有创建活动。'; + + @override + String get sortLatest => '最新'; + + @override + String get sortPop => '按 POP'; + + @override + String get sortSolved => '已采纳'; + + @override + String get sortAchieved => '已达成'; + + @override + String get payNotFound => '找不到支付记录。'; + + @override + String get payItemFallback => '支付商品'; + + @override + String get payMethodInApp => '应用内购买'; + + @override + String get unknownUser => '未知用户'; + + @override + String get blockSelf => '不能屏蔽自己。'; + + @override + String get inquiryFallback => '咨询'; + + @override + String get photoRejectedServer => '无法上传照片,请检查格式和大小。'; + + @override + String get photoUploadFailed => '照片上传失败,请稍后再试。'; + + @override + String focusClaimed(String pop) { + return '已领取今日 Focus 奖励 +$pop 贡献 POP。'; + } + + @override + String get focusActive => '正在使用无广告专注模式'; + + @override + String focusUntil(String date) { + return '有效期至 $date · 今日奖励仅限今日领取'; + } + + @override + String get focusClaimedToday => '今日已领取'; + + @override + String get focusClaimToday => '领取今日奖励'; + + @override + String get storeConnectFailed => '无法连接商店,请稍后再试。'; + + @override + String get payRestore => '恢复购买'; + + @override + String get payEmpty => '暂无支付记录。'; + + @override + String get payRefundGuide => + '消耗型商品:购买后 7 天内且未使用可退款,已部分使用的商品不予退款。\n订阅型商品:支付后 24 小时内未使用权益(如额外奖励)可退款。\n退款批准后 7–14 天内完成退款。'; + + @override + String get payDetailTitle => '支付详情'; + + @override + String get payNumber => '支付编号'; + + @override + String get payDate => '支付时间'; + + @override + String get payMethod => '支付方式'; + + @override + String get payStatus => '支付状态'; + + @override + String get payAmount => '支付金额'; + + @override + String get payRefundInfo => '退款说明'; + + @override + String get payRefundApply => '申请退款'; + + @override + String get payRefundUnavailable => '不可退款'; + + @override + String get refundReasonMistake => '误操作付款'; + + @override + String get refundReasonExpectation => '服务与预期不符'; + + @override + String get refundReasonError => '因错误无法使用'; + + @override + String get refundReasonDuplicate => '重复付款'; + + @override + String get refundTitle => '退款'; + + @override + String get refundSubmit => '提交'; + + @override + String get refundReason => '退款原因'; + + @override + String get refundConfirm => '确定要申请退款吗?'; + + @override + String get myPop => '我的 POP'; + + @override + String get commonNoHistory => '暂无记录。'; + + @override + String get nicknameEdit => '修改昵称'; + + @override + String get nicknameReserved => '不能使用可能被误认为运营方的名称。'; + + @override + String get nicknameTaken => '该昵称已被使用。'; + + @override + String get albumItems => '相册选项'; + + @override + String get takePhoto => '拍照'; + + @override + String get pickFromAlbum => '从相册选择'; + + @override + String get tierGuide => '等级说明'; + + @override + String get statSteps => '累计步数'; + + @override + String get statContribution => '累计贡献 POP'; + + @override + String get statQuests => '已完成任务'; + + @override + String get shareInstagram => 'Instagram'; + + @override + String get shareTiktok => 'TikTok'; + + @override + String get shareCopyLink => '复制链接'; + + @override + String get inquiryListTitle => '1:1 咨询'; + + @override + String get inquiryEmpty => '暂无咨询。'; + + @override + String get inquiryWrite => '撰写咨询'; + + @override + String get inquiryDetail => '咨询详情'; + + @override + String get commonEdit => '编辑'; + + @override + String get inquiryAnswer => '回复'; + + @override + String get inquiryNew => '新建咨询'; + + @override + String get inquiryEdit => '编辑咨询'; + + @override + String get commonTitle => '标题'; + + @override + String get commonTitleHint => '请输入标题。'; + + @override + String get inquiryBody => '咨询内容'; + + @override + String get inquiryBodyHint => '请填写咨询内容。'; + + @override + String get inquiryBodyHelper => '填写 10 个字以上,我们能更快为你处理。'; + + @override + String inquirySubmitFailed(String reason) { + return '咨询提交失败。$reason'; + } + + @override + String inquiryEditFailed(String reason) { + return '咨询修改失败。$reason'; + } + + @override + String get inquirySubmitted => '咨询已提交。'; + + @override + String get inquiryEdited => '咨询已修改。'; + + @override + String get blockedEmpty => '没有已屏蔽的账号。'; + + @override + String get unblock => '取消屏蔽'; + + @override + String unblockFailed(String reason) { + return '取消屏蔽失败。$reason'; + } + + @override + String get tierInfoTitle => 'TIER 信息'; + + @override + String get tierPromptPrefix => '通过为星球/星云做贡献\n'; + + @override + String get tierPromptRank => '等级'; + + @override + String get tierPromptSuffix => '来提升吧'; + + @override + String get tierPassportNote => 'Passport Tier 是与等级无关的卡片外观等级,不会降低。'; + + @override + String get tierTotalContribution => '累计贡献'; + + @override + String get notifKindEclipse => '日蚀'; + + @override + String get notifKindItemUsed => '道具使用'; + + @override + String get notifKindLike => '点赞'; + + @override + String get notifKindComment => '评论'; + + @override + String get notifKindCampaign => '活动达成'; + + @override + String get notifKindQuest => '任务完成'; + + @override + String get notifTargetPost => '帖子'; + + @override + String get notifTargetCampaign => '活动'; + + @override + String get notifTargetQuest => '任务详情'; + + @override + String get dayToday => '今天'; + + @override + String get dayYesterday => '昨天'; + + @override + String get notifEmpty => '暂无通知。'; + + @override + String notifGoTo(String target) { + return '即将前往$target。'; + } + + @override + String get stationOfficial => '官方'; + + @override + String get stationShop => '商店'; + + @override + String get stationTicket => '购票'; + + @override + String get stationMedia => '媒体'; + + @override + String get stationLoadFailed => '无法加载 STATION'; + + @override + String get commonAll => '全部'; + + @override + String get stationSuggest => '推荐商店'; + + @override + String get stationEmpty => '暂无链接。\n加入星球后即可看到精选内容。'; + + @override + String get stationCategoryEmpty => '此分类暂无链接。'; + + @override + String get stationSuggestLater => '服务器连接后即可提交推荐。'; + + @override + String get commonSubmit => '提交'; + + @override + String get commonName => '名称'; + + @override + String get commonNameRequired => '请输入名称。'; + + @override + String get commonUrl => '网址'; + + @override + String get commonUrlRequired => '请输入网址。'; + + @override + String get commonUrlHttps => '请输入以 https 开头的网址。'; + + @override + String get commonCategory => '分类'; + + @override + String get stationReason => '推荐理由(选填)'; + + @override + String get stationSuggestNote => '推荐的链接经运营审核后登记,审核结果将通过通知告知。'; + + @override + String get heroGiftUnavailable => '该礼物目前无法领取。'; + + @override + String get heroGiftAlready => '你已参与过该礼物,请查看 POP 记录。'; + + @override + String get heroGiftRelogin => '请重新登录后参与。'; + + @override + String get heroGiftNoPermission => '你没有参与该礼物的权限。'; + + @override + String get heroGiftHowTo => '摇动手机或点击下方按钮,参与光能礼物。'; + + @override + String get heroGiftPopNote => '可领取的 POP 取决于剩余礼物和每人上限。'; + + @override + String heroGiftReceived(String total) { + return '已确认领取 $total POP'; + } + + @override + String get heroGiftUnknown => '无法确认发放结果,请重新确认。'; + + @override + String get heroGiftNone => '目前没有可参与的礼物。'; + + @override + String get heroGiftConfirmed => '已确认领取所参与的礼物。'; + + @override + String get heroGiftChecking => '正在确认发放…'; + + @override + String get heroGiftRetry => '重新确认该礼物'; + + @override + String get heroGiftTap => '用按钮代替摇动参与'; + + @override + String rankMyPlanet(String metric) { + return '我的星球 $metric 排名'; + } + + @override + String get rankPlanetEmpty => '暂无星球排名数据'; + + @override + String get rankScopeLive => '实时'; + + @override + String get rankScopeCountry => '按国家'; + + @override + String rankMine(String metric) { + return '我的 $metric 排名'; + } + + @override + String rankMyCountry(String metric) { + return '我所在国家的 $metric 排名'; + } + + @override + String get rankEmpty => '暂无排名数据'; + + @override + String rankScope(String scope) { + return '$scope排名'; + } + + @override + String get rankLoadFailed => '无法加载排名'; + + @override + String rankMeSuffix(String name) { + return '$name(我)'; + } + + @override + String get reportDefamation => '诽谤艺人及损害名誉'; + + @override + String get reportInappropriate => '不当内容'; + + @override + String get reportSpam => '垃圾信息及商业广告'; + + @override + String get reportPrivacy => '泄露个人信息及冒充他人'; + + @override + String get reportTitle => '举报'; + + @override + String get reportSubmit => '举报'; + + @override + String get reportAuthor => '作者'; + + @override + String get reportReasonLabel => '举报原因'; + + @override + String get reportConfirm => '要举报这条帖子吗?\n提交后无法撤销。'; + + @override + String get blockMemberConfirm => '要屏蔽该会员的所有帖子吗?'; + + @override + String get loungeMembersOnly => '仅该星球粉丝圈成员可发帖、评论和点赞。'; + + @override + String get loungeFailed => '处理失败,请稍后再试。'; + + @override + String get loungeTitle => '粉丝休息室'; + + @override + String get likeCancel => '取消点赞'; + + @override + String get like => '点赞'; + + @override + String get commentDeleteConfirm => '要删除评论吗?'; + + @override + String get commonShare => '分享'; + + @override + String get commonEditAction => '编辑'; + + @override + String get commonDeleteAction => '删除'; + + @override + String get commonBlockAction => '屏蔽'; + + @override + String get commentFirst => '来发表第一条评论吧。'; + + @override + String get postDeleteConfirm => '要删除这篇帖子吗?'; + + @override + String get reportReceived => '举报已受理。'; + + @override + String get commonDelete => '删除'; + + @override + String get commentHint => '写下评论吧。'; + + @override + String get commonSend => '发送'; + + @override + String get postWrite => '发帖'; + + @override + String get postEdit => '编辑帖子'; + + @override + String get loungeHint => '在粉丝休息室自由发帖吧。'; + + @override + String photoRemove(String n) { + return '移除照片 $n'; + } + + @override + String get camera => '相机'; + + @override + String get album => '相册'; + + @override + String get historyTitle => 'PASSPORT · 记录'; + + @override + String get historyMine => '我的活动'; + + @override + String historyEvolution(String from, String to) { + return 'Stage $from → $to 进化'; + } + + @override + String get historyHibernation => '星球休眠'; + + @override + String get historyWakeUp => '星球苏醒'; + + @override + String get historyZap => 'ZAP 贡献'; + + @override + String get historyQuest => 'Quest 奖励'; + + @override + String get historyAdBonus => '广告额外奖励'; + + @override + String get historyFunding => 'Campaign 贡献'; + + @override + String historyGrowthPop(String amount) { + return '$amount POP 计入成长'; + } + + @override + String get historyJoined => '你参与了这次进化'; + + @override + String get historyLoadFailed => '无法加载记录。'; + + @override + String get historyMineEmpty => '暂无活动记录。'; + + @override + String get historyFandomEmpty => '暂无粉丝圈历史。'; + + @override + String get historyMore => '查看更早记录'; + + @override + String countryName(String code) { + String _temp0 = intl.Intl.selectLogic(code, { + 'KR': '韩国', + 'JP': '日本', + 'US': '美国', + 'CN': '中国', + 'TW': '台湾', + 'HK': '香港', + 'TH': '泰国', + 'VN': '越南', + 'ID': '印度尼西亚', + 'PH': '菲律宾', + 'MY': '马来西亚', + 'SG': '新加坡', + 'IN': '印度', + 'GB': '英国', + 'FR': '法国', + 'DE': '德国', + 'ES': '西班牙', + 'BR': '巴西', + 'MX': '墨西哥', + 'CA': '加拿大', + 'AU': '澳大利亚', + 'ZZ': '未指定', + 'other': '-', + }); + return '$_temp0'; + } + + @override + String loungeMaxPhotos(String count) { + return '最多可上传 $count 张照片。'; + } + + @override + String get commonLoadFailed => '加载失败'; + + @override + String get planetPassportHistory => 'PASSPORT · 粉丝圈历史'; + + @override + String planetNamed(String name) { + return '$name 星球'; + } + + @override + String get blockNebulaConfirm => '要屏蔽这个星云吗?'; + + @override + String get blockPlanetConfirm => '要屏蔽这个星球吗?'; + + @override + String get blocked => '已屏蔽。'; + + @override + String planetLastUpdated(String time) { + return '最后更新 $time'; + } + + @override + String get planetContribution => '贡献情况'; + + @override + String get planetNebulaGrowth => '星云成长率'; + + @override + String get planetLiveRanking => '实时贡献排名'; + + @override + String get planetNoContributors => '还没有贡献者'; + + @override + String get planetContributeCta => '贡献一下,登上排行榜吧!'; + + @override + String planetOverallRank(String rank) { + return '总排名第 $rank'; + } + + @override + String get planetMyContribution => '我的贡献度'; + + @override + String get planetTallying => '统计准备中'; + + @override + String planetTopPercent(String percent) { + return '前 $percent%'; + } + + @override + String get planetHideMember => '隐藏该会员的所有帖子'; + + @override + String get reportReasonUnknown => '无法确认举报原因。'; + + @override + String get reportAlready => '你已举报过该帖子。'; + + @override + String get blockUnavailable => '无法屏蔽该作者。'; + + @override + String get commonMe => '我'; + + @override + String get planetSizeMedium => '中型粉丝圈'; + + @override + String planetPerHead(String value) { + return '人均火力:$value'; + } + + @override + String get planetJoined => '已加入'; + + @override + String get planetJoin => '加入'; + + @override + String get questDetails => '详情'; + + @override + String get questInProgress => '进行中'; + + @override + String get questTypeAttend => '签到'; + + @override + String get questTypeSteps => '步行'; + + @override + String get questTypePlace => '指定地点'; + + @override + String get questTypeStreaming => '流媒体'; + + @override + String get questTypeUnverifiable => '该任务类型的验证方式尚未确定。'; + + @override + String get questTabMine => '我的任务'; + + @override + String get placePickPrompt => '请选择\n完成任务的地点。'; + + @override + String get placeSearch => '搜索地点'; + + @override + String get commonNoResults => '没有搜索结果。'; + + @override + String get commonSelectDone => '选择完成'; + + @override + String get adMoreChance => '获得更多贡献机会。'; + + @override + String get adWatchPrompt => '观看广告即可\n获得额外奖励!'; + + @override + String get adBonusPlaceholder => '+ 贡献 POP'; + + @override + String get adPartyPack => '派对礼包'; + + @override + String get adPickProduct => '选择商品'; + + @override + String get adWatchBonus => '看广告领取额外奖励'; + + @override + String get adBasicReward => '领取基础奖励'; + + @override + String get focusMode => '无广告专注模式'; + + @override + String pricePerMonth(String price) { + return '$price/月'; + } + + @override + String get adWarpBefore => 'Warping...正在努力寻找合适的广告,帮你为粉丝圈和艺人做更多贡献。'; + + @override + String get adWarpAfter => 'Warping...正在为你的贡献准备奖励。'; + + @override + String get adSkip => '跳过'; + + @override + String get questPreviewHint => '选择选项并查看预览'; + + @override + String get questNoCategory => '尚未选择分类'; + + @override + String get questNew => '新任务'; + + @override + String get questCategory => '分类'; + + @override + String get questName => '任务名称'; + + @override + String get questAction => '具体行动'; + + @override + String get questPickPlace => '请选择目标地点'; + + @override + String get questGoalValue => '目标数值'; + + @override + String get questParticipants => '参与人数'; + + @override + String get questRewardPop => '完成奖励 POP'; + + @override + String get questCreateConfirm => '按此内容创建吗?'; + + @override + String questCreateSummary(String title, String goal, String reward) { + return '$title · 目标 $goal · 奖励 $reward POP'; + } + + @override + String get commonKeepEditing => '继续修改'; + + @override + String get questCreate => '创建'; + + @override + String questNamed(String title) { + return '$title 任务'; + } + + @override + String get questDailyHelp => '每天为星球助力'; + + @override + String get questTodayMust => '今天必做的任务'; + + @override + String get questTabEmpty => '此标签下暂无任务。'; + + @override + String get questCreateNotReady => '任务创建将与奖励资金托管一同开放,敬请期待。'; + + @override + String get questNoBaseReward => '找不到基础奖励记录,无法领取广告额外奖励。'; + + @override + String get questNoSteps => '尚未收集到步数,请走一会儿后再试。'; + + @override + String get questNoPlace => '该任务未登记验证地点。'; + + @override + String get questGoal => '目标'; + + @override + String get questReward => '奖励'; + + @override + String get questPopGrant => '发放 POP'; + + @override + String get commonChecking => '确认中…'; + + @override + String questMine(String count) { + return '这是你创建的任务,参与者 $count 人。'; + } + + @override + String get questStopConfirm => '要中止任务吗?'; + + @override + String get questStopBody => '当前进度将被重置并恢复初始状态。'; + + @override + String get questStop => '中止'; + + @override + String get adNotFinished => '未看完广告,无额外奖励,基础奖励照常发放。'; + + @override + String get openSettings => '打开设置'; + + @override + String get permStepsDenied => '步数权限已关闭,请在设置中允许“身体活动”。'; + + @override + String get permStepsNeeded => '计步需要身体活动权限。'; + + @override + String get permLocationOff => '设备定位已关闭,请开启后重试。'; + + @override + String get permLocationNeeded => '需要定位权限以确认到访。'; + + @override + String get permLocationDenied => '定位权限已关闭,请在设置中允许访问位置。'; + + @override + String get questRewardReceived => ' 已领取!'; + + @override + String questAdBonus(String pop) { + return '广告额外奖励 +$pop POP'; + } + + @override + String questParticipantsCount(String count) { + return '参与人数:$count'; + } + + @override + String questParticipantsCap(String count, String cap) { + return '参与人数:$count / $cap'; + } + + @override + String get nebulaCreateFailed => '无法创建星云,请稍后再试。'; + + @override + String get nebulaAlreadyToday => '你今天已创建过星云。'; + + @override + String get nebulaCreatedKeepPrimary => '星云已创建。默认星球需在上次更改 30 天后才能更换,因此保持不变。'; + + @override + String get fandomFounderLeaveBlocked => '行星诞生后才能退出。'; + + @override + String get sortPopular => '热门'; + + @override + String get campaignSortContributing => '贡献中'; + + @override + String get campaignStatusReview => '该活动正在接受运营审核。'; + + @override + String get campaignStatusRejected => '活动未通过审核。登记费已退还。'; + + @override + String get campaignStatusWithdrawn => '该活动已被发起人撤回。'; + + @override + String get campaignStatusCancelled => '因运营原因已取消。登记费和支援的 POP 已退还。'; + + @override + String get rankWindowDaily => '日榜'; + + @override + String get rankWindowWeekly => '周榜'; + + @override + String get campaignNotFound => '找不到该活动。'; + + @override + String get campaignMediaMain => '主图'; + + @override + String get campaignLoadFailed => '无法加载活动。'; + + @override + String get campaignTabInfo => '活动信息'; + + @override + String campaignTabBoard(String count) { + return '应援留言 $count'; + } + + @override + String get campaignWithdraw => '撤回活动'; + + @override + String get campaignContributeAction => '贡献'; + + @override + String get campaignFundingStatus => '当前筹集情况'; + + @override + String get campaignSupporting => '应援中'; + + @override + String get campaignTimeLeft => '剩余!'; + + @override + String get campaignReport => '结果报告'; + + @override + String get campaignReportPhotos => '执行照片'; + + @override + String get campaignBudget => '预算使用明细'; + + @override + String get campaignTotal => '合计'; + + @override + String get campaignReceipts => '收据'; + + @override + String campaignDaysLeft(String days, String time) { + return '$days天 $time'; + } + + @override + String get campaignWithdrawTitle => '要撤回该活动吗?'; + + @override + String campaignWithdrawRefund(String fee) { + return '由于在公开前撤回,$fee POP 登记费将按原支付方式退还。'; + } + + @override + String campaignWithdrawNoRefund(String fee) { + return '该活动已公开,$fee POP 登记费不予退还。'; + } + + @override + String get campaignWithdrawConfirm => '撤回'; + + @override + String get shareCopied => '已复制分享文案。'; + + @override + String get campaignContributePop => '设置贡献 POP'; + + @override + String get campaignEmptyContributing => '您还没有贡献过任何活动。'; + + @override + String get campaignEmpty => '暂无活动。'; + + @override + String get campaignNew => '新活动'; + + @override + String get campaignMyRanking => '我的贡献排名'; + + @override + String campaignWindowRanking(String window) { + return '$window排名'; + } + + @override + String get campaignSearchHint => '搜索活动'; + + @override + String get campaignNoRecentSearches => '没有最近搜索'; + + @override + String searchRemoveTerm(String term) { + return '删除 $term'; + } + + @override + String get campaignNoResults => '没有搜索结果'; + + @override + String get shareTopicCampaign => '活动'; + + @override + String sharePlanetLine(String brand, String name) { + return '$brand · $name 行星'; + } + + @override + String get campaignGaugeLabel => '活动贡献率'; + + @override + String get campaignPeriodTitle => '请选择活动期间'; + + @override + String get campaignPeriodMax => '最长可选择一年'; + + @override + String campaignYearMonth(String year, String month) { + return '$year年$month月'; + } + + @override + String get campaignWeekdays => '日,一,二,三,四,五,六'; + + @override + String campaignPeriodRange( + String startMonth, + String startDay, + String endMonth, + String endDay, + ) { + return '$startMonth月$startDay日 ~ $endMonth月$endDay日'; + } + + @override + String get campaignPeriodSelect => '选择期间'; + + @override + String get calendarPrevMonth => '上个月'; + + @override + String get calendarNextMonth => '下个月'; + + @override + String get campaignShareTitle => '分享活动'; + + @override + String get campaignPickMedia => '选择图片/视频'; + + @override + String get campaignTitleHint => '请输入活动标题。'; + + @override + String get campaignBodyHint => '请填写活动说明。'; + + @override + String get campaignReviewNote => '提交后,经运营审核即开放筹集。'; + + @override + String get campaignSupportType => '选择支援类型'; + + @override + String get campaignPeriod => '活动期间'; + + @override + String get campaignDateSelect => '选择日期'; + + @override + String get campaignGoalPop => '目标贡献 POP'; + + @override + String get campaignSubmitTitle => '按此内容提交吗?'; + + @override + String campaignSubmitBody( + String title, + String goal, + String days, + String fee, + ) { + return '$title · 目标 $goal POP · $days天\n将扣除 $fee POP 登记费,运营审核后开放筹集。'; + } + + @override + String get campaignSubmit => '提交'; + + @override + String get campaignCreateFailed => '无法提交活动。'; + + @override + String campaignNeedsQuests(String done, String need, String left) { + return '已完成任务 $done/$need — 再完成 $left 次即可发起活动。'; + } + + @override + String get campaignPendingTitle => '等待审核'; + + @override + String get campaignPendingBody => '运营审核完成后将开放筹集并显示在列表中。'; + + @override + String get insideRefundPending => '采纳期限已过,悬赏将被退还。'; + + @override + String get insideRefundDone => '采纳期限已过,悬赏已退还。'; + + @override + String get insideRefundChecking => '采纳期限已过,正在确认悬赏退还。'; + + @override + String get insideErrPopShort => 'POP 不足,无法设置悬赏。'; + + @override + String get insideErrSelfAccept => '不能采纳自己的回答。'; + + @override + String get insideErrAlreadyAccepted => '该问题已采纳,无法更改。'; + + @override + String get insideErrNotOwner => '只有提问者可以采纳。'; + + @override + String get commonCheckConnection => '请检查网络连接后重试。'; + + @override + String get insideAnswerLater => '服务器连接后即可发表评论。'; + + @override + String insideAcceptConfirm(String pop) { + return '要采纳这条评论吗?\n将发放 $pop POP 悬赏,采纳后无法撤销。'; + } + + @override + String get insideAccept => '采纳'; + + @override + String get insideMore => '更多'; + + @override + String get insideFirstAnswer => '来发表第一条评论吧。'; + + @override + String get insideAcceptDone => '已采纳'; + + @override + String get insideAcceptAction => '采纳'; + + @override + String get insideAnswerHint => '写下评论吧。'; + + @override + String get insideLoadFailed => '无法加载 INSIDE'; + + @override + String get insideEmpty => '暂无提问。'; + + @override + String get insideWrite => '发布 INSIDE'; + + @override + String get insideSearchHint => 'INSIDE 标题、内容'; + + @override + String get insideBodyHint => '和粉丝们自由地提问交流吧。'; + + @override + String get insideTitleRequired => '请输入标题。'; + + @override + String get insideBodyRequired => '请输入内容。'; + + @override + String get insideBountySet => '设置悬赏'; + + @override + String insideBountyHint(String min) { + return '请设置 $min 以上。'; + } + + @override + String get insideAttachLater => '服务器连接后即可附加图片。'; + + @override + String get insideBountyRequired => '请输入悬赏。'; + + @override + String get insideNumberOnly => '请输入数字。'; + + @override + String insideBountyMin(String min) { + return '悬赏须至少为 $min POP。'; + } + + @override + String get insideBountyOverBalance => '不能超过持有的 POP。'; + + @override + String get questTypeTimeAttack => '紧急限时挑战'; + + @override + String get questTimeLeft => '剩余时间'; + + @override + String get questTimeAttackClosed => '已截止'; + + @override + String questTimeAttackLeft(String time) { + return '剩余 $time'; + } + + @override + String get popReasonQuestTimeAttack => '紧急限时挑战'; + + @override + String get briefingShareCopied => '已复制分享文字和链接。'; + + @override + String get shareCopyText => '复制文字'; + + @override + String get briefingShareTextCopied => '已复制分享文字。'; + + @override + String questAlreadyRewarded(String pop) { + return '已领取该奖励(+$pop POP)。'; + } + + @override + String get questCatHealth => '健康'; + + @override + String get questCatPlanet => '星球活动'; + + @override + String get questCatCommunity => '社区'; + + @override + String get questActShake => '摇一摇'; + + @override + String get questTypeVote => '参与投票'; + + @override + String get questTypeSns => '社交媒体'; + + @override + String get questActivity => '活动'; + + @override + String get questNoCreatable => '目前没有可创建的任务活动。'; + + @override + String get questDescriptionOptional => '说明(可选)'; + + @override + String get questLinkUrl => '链接地址'; + + @override + String get questLinkCheck => '检查链接'; + + @override + String get questLinkInvalid => '链接无效,请检查地址。'; + + @override + String get questLinkPreviewFailed => '无法获取链接信息,请手动输入标题。'; + + @override + String get questLinkTitle => '链接标题'; + + @override + String get questEndDate => '结束日期'; + + @override + String get questEndDefault => '发布后24小时'; + + @override + String questEndDateValue(String date) { + return '$date 23:59:59 截止'; + } + + @override + String questPoolHelper(String pool, String fee) { + return '共存入 $pool POP · 手续费 $fee POP'; + } + + @override + String questCreateCost( + String title, + String reward, + String count, + String pool, + String fee, + String total, + ) { + return '$title\n奖励 $reward POP × $count人 = $pool POP\n手续费 $fee POP(发布时销毁)\n共扣除 $total POP。'; + } + + @override + String get questNoReward => '这是无奖励的参与任务。'; + + @override + String get questDeadline => '截止'; + + @override + String questRemainingHours(String hours) { + return '剩余$hours小时'; + } + + @override + String get questEnded => '已截止'; + + @override + String get questAuthorNote => '创建者说明'; + + @override + String get questRepeatOnce => '每人限参与1次'; + + @override + String questRepeatEvery(String hours, String count) { + return '间隔$hours小时 · 最多$count次'; + } + + @override + String get questSubmit => '提交认证'; + + @override + String get questResubmit => '补充提交'; + + @override + String get questEvidenceUrl => '认证链接(可选)'; + + @override + String get questEvidenceText => '认证内容'; + + @override + String get questSubmitted => '已提交,审核后发放奖励。'; + + @override + String get questReviewPending => '审核中,批准后将发放奖励。'; + + @override + String get questReviewDelayed => '审核有所延迟,运营团队正在确认。'; + + @override + String get questApprovedPaying => '已批准,正在发放奖励。'; + + @override + String questRejected(String reason) { + return '已驳回:$reason'; + } + + @override + String get questResubmitAllowed => '可补充后重新提交。'; + + @override + String get questResubmitNotAllowed => '此提交无法重新提交。'; + + @override + String get questReviewButton => '审核中'; + + @override + String get questRejectedButton => '已驳回'; + + @override + String questReviewQueue(String count) { + return '待审核 $count 件'; + } + + @override + String get questApprove => '批准'; + + @override + String get questReject => '驳回'; + + @override + String get questRejectReason => '驳回理由'; + + @override + String get questAllowResubmit => '允许补充重新提交'; + + @override + String get questEdit => '编辑标题和说明'; + + @override + String get questEditNotice => '发布后仅可编辑标题和说明,并会保留修改记录。'; + + @override + String get popReasonQuestUgc => '任务奖励'; + + @override + String get popReasonQuestFee => '任务创建手续费'; + + @override + String questFreeDailyCap(String cap) { + return '无奖励任务每天最多可创建 $cap 个。'; + } + + @override + String get popReasonQuestPoolDeposit => '任务奖励存入'; + + @override + String get popReasonQuestPoolReturn => '任务奖励退回'; + + @override + String get campaignCommentNeedNickname => '请先设置昵称'; + + @override + String get campaignCommentSetNickname => '设置昵称'; +} diff --git a/app/lib/l10n/app_zh.arb b/app/lib/l10n/app_zh.arb new file mode 100644 index 0000000..78642d1 --- /dev/null +++ b/app/lib/l10n/app_zh.arb @@ -0,0 +1,791 @@ +{ + "@@locale": "zh", + "commonConfirm": "确认", + "commonCancel": "取消", + "commonClose": "关闭", + "commonSelect": "选择", + "commonRetry": "重试", + "commonView": "查看", + "commonPrev": "上一步", + "commonDone": "完成", + "commonStart": "开始", + "commonNotYet": "{where}页面将在第二次实现确认(2026-09-10)中加入。", + "languageSettingsTitle": "语言设置", + "authLogin": "登录", + "authSignup": "注册", + "authFirstTime": "第一次使用FANUZU?", + "authAlreadyMember": "已经注册了吗?", + "authLoginHeadline": "用最方便的方式\n登录FANUZU!", + "authSignupHeadline": "用最方便的方式\n注册FANUZU!", + "authProviderLogin": "{provider, select, apple{通过Apple登录} google{通过Google登录} kakao{通过Kakao登录} line{通过LINE登录} other{通过邮箱登录}}", + "authProviderSignup": "{provider, select, apple{通过Apple注册} google{通过Google注册} kakao{通过Kakao注册} line{通过LINE注册} other{通过邮箱注册}}", + "authNotConfiguredTitle": "{name}登录准备中", + "authNotConfiguredBody": "{name}登录将在客户提供密钥后开放。\n\n所需项目\n· {items}", + "authNotConfiguredNotice": "{name}登录尚在准备中。\n所需项目: {items}", + "authResetNeedEmail": "请先输入接收重置邮件的邮箱。", + "authResetSent": "已向该地址发送重置说明。", + "termsTitle": "使用条款", + "termsAgreeAll": "全部同意", + "termsService": "服务条款(必选)", + "termsLocation": "位置服务条款(必选)", + "termsAge": "本人已满14周岁。(必选)", + "termsMarketing": "同意接收营销信息与活动通知(可选)", + "signupDoneTitle": "注册完成", + "signupDoneBody": "现在可以开始粉丝活动了。\n通过各种活动培养你的粉丝星球吧。", + "homeItemsTooltip": "道具", + "homeNotificationsTooltip": "通知", + "homeFandomHistory": "粉丝历史", + "homePopularInside": "热门 K-INSIDE", + "homeWhereBriefing": "AI简报", + "homeWhereInsideList": "INSIDE列表", + "homeWhereInsideDetail": "INSIDE详情", + "homeLoadFailed": "无法加载首页", + "homeRetryLater": "请稍后再试。", + "myProfileCardTooltip": "个人名片", + "myTabPlanets": "星球/星云", + "myTabActivity": "活动", + "myInside": "Inside", + "myCampaign": "应援活动", + "myShareLater": "{target}分享将在09-10版本中接入。", + "myEditPhoto": "修改头像", + "myEditProfile": "编辑资料", + "settingsTitle": "设置", + "settingsAccount": "账号", + "settingsNickname": "昵称", + "settingsEmail": "邮箱", + "settingsSns": "已关联社交账号", + "settingsPopHistory": "POP记录", + "settingsPayments": "支付记录", + "settingsServicePayments": "服务支付记录", + "settingsService": "服务设置", + "settingsPush": "推送通知设置", + "settingsBlocked": "屏蔽设置", + "settingsInfo": "服务信息", + "settingsNotice": "公告", + "settingsInquiry": "1:1咨询", + "settingsAppVersion": "应用版本", + "settingsLatest": "最新版本", + "settingsLogout": "退出登录", + "settingsLogoutConfirm": "确定要退出登录吗?", + "settingsWithdraw": "注销账号", + "settingsWithdrawConfirm": "确定要注销账号吗?", + "settingsWithdrawBody": "您持有的POP将会清零且无法恢复。", + "settingsWithdrawLater": "注销处理将在09-10版本中接入。", + "settingsUnlinkConfirm": "确定要解除关联账号吗?", + "settingsUnlinkBody": "仅移除该社交登录方式,账号信息不会被删除。", + "settingsUnlink": "解除关联", + "settingsSameEmailOnly": "只能关联与账号邮箱\n相同的社交账号。", + "settingsLinkNotReady": "{name}关联尚在准备中。所需项目: {items}", + "settingsConnect": "关联", + "settingsDisconnect": "解除", + "settingsEmailProvider": "邮箱", + "pushDeviceOff": "设备的推送通知已关闭。", + "pushDeviceOffHint": "请在系统设置中允许通知以接收消息。", + "pushRequesting": "请求中…", + "pushGoToSettings": "前往设置", + "pushOpeningDeviceSettings": "正在前往设备设置。", + "pushEnableInDevice": "请在设备设置中开启FANUZU通知。", + "pushDeviceSection": "设备通知设置", + "pushSoundVibration": "声音、振动设置", + "pushServiceSection": "服务通知设置", + "pushTopicEssential": "服务必需", + "pushTopicFandomEvent": "粉丝活动", + "pushTopicBriefing": "AI Briefing", + "pushTopicCommunity": "社区", + "pushTopicMarketing": "营销推广", + "linkTitle": "{name}关联", + "linkNotLinked": "未关联", + "linkLinked": "已关联", + "linkSameEmailOnly": "只能关联与账号邮箱相同的社交账号。", + "linkUnlink": "解除关联", + "linkLink": "关联", + "noticeBadge": "公告", + "noticeTitle": "公告", + "noticeEmpty": "暂无公告。", + "noticeDetailTitle": "公告详情", + "translationMark": "已翻译", + "translationReplyTo": "翻译 {name} 的评论", + "translationWrite": "撰写翻译评论", + "translationHint": "请输入翻译", + "translationValid": "有效", + "translationInvalid": "无效", + "translationSelfMarkBlocked": "不能标记自己的翻译。", + "translationSubmitted": "翻译已提交。通过验证后将在24小时后发放POP。", + "translationMarked": "已标记。", + "translationAlreadyMarked": "您已标记过该翻译。", + "translationDuplicate": "您已为该内容提交过同一语言的翻译。", + "translationFailed": "处理失败,请稍后再试。", + "translationNotEligible": "仅注册满24小时的本粉丝团成员可以标记。", + "translationDailyCap": "今天无法再提交翻译。", + "translationInvalidTarget": "该内容无法翻译。", + "commonRequestFailed": "无法处理请求。", + "commonFan": "粉丝", + "adLoadFailed": "广告加载失败,仅发放基础奖励。", + "storeNotReady": "商店商品准备中", + "storePending": "正在等待支付确认,确认后将自动生效。", + "storeFailed": "支付未完成。", + "storePurchased": "购买已生效。", + "heroParticipate": "ZAP+ · 参与礼物", + "heroRemainingSemantics": "HERO TIME 剩余 {minutes} 分 {seconds} 秒", + "heroRemaining": "HERO TIME · 剩余 {time}", + "heroClaims": "粉丝已领取礼物 {count} 次", + "heroInProgress": "{name} · HERO TIME 进行中 →", + "stageNames": "星云,起始,成长,同心,同行,光芒,永恒", + "planetStageUnknown": "星球 · 无阶段信息", + "planetHibernatingSuffix": " · 休眠中", + "planetEclipseSuffix": " · Eclipse 进行中", + "planetHeroSuffix": " · HERO TIME 进行中", + "evoPlanetGrowth": "星球成长", + "evoHeroEnd": "HERO TIME 结束", + "evoAwakening": "星球正在苏醒", + "evoAnomaly": "异常反应", + "evoEnergy": "能量汇聚", + "evoTransition": "转换", + "evoComplete": "星球进化完成", + "evoOpenHistory": "查看 PASSPORT 记录", + "playNow": "立即开玩", + "playHeadline": "来吧,去见你的粉丝圈!", + "playBody": "创建星球,与粉丝们交流,享受愉快的追星活动...", + "timeJustNow": "刚刚", + "timeMinutesAgo": "{n}分钟前", + "timeHoursAgo": "{n}小时前", + "timeDaysAgo": "{n}天前", + "commonMore": "更多", + "authProviderStart": "{provider, select, email{使用邮箱开始} kakao{使用 Kakao 开始} line{使用 LINE 开始} apple{使用 Apple 开始} other{使用 Google 开始}}", + "authProviderName": "{provider, select, email{邮箱} kakao{Kakao} line{LINE} apple{Apple} other{Google}}", + "authLastProvider": "无法解除最后一个登录方式的关联。", + "authLoginRequired": "请先登录。", + "authDifferentEmail": "该账号与注册邮箱不一致。", + "authLinkFailed": "账号关联失败。", + "authNotLinked": "该账号未关联。", + "authEmailUnavailable": "无法使用邮箱登录。", + "authCheckCredentials": "请检查邮箱或密码。", + "authSocialFailed": "社交账号登录失败。", + "authAppleUnavailable": "此设备无法使用 Apple 登录。", + "authEmailInUse": "该邮箱已注册。", + "authWeakPassword": "密码太短,请至少设置 8 个字符。", + "authInvalidEmail": "邮箱格式不正确。", + "commonTryLater": "请稍后再试。", + "authCredentialInUse": "该社交账号已关联其他 FANUZU 账号。", + "authAlreadyLinked": "该账号已关联。", + "authMethodUnavailable": "此登录方式暂不可用。", + "authRecentLogin": "为了安全,请重新登录后再试。", + "authOtherProviderGuide": "{email} 账号已通过其他登录方式注册。请使用首次注册时的方式登录,然后在 MY > 设置 > SNS 关联中关联此方式。", + "authThisAccount": "此", + "eclipseCapped": "已达参与上限,部分未计入。", + "eclipseResultUnknown": "无法确认参与结果,请以相同请求再次确认。", + "eclipseChecking": "正在确认参与", + "eclipseRetrySame": "重新确认本次参与", + "eclipseEnded": "日蚀已结束或已防御成功。", + "eclipseTooFast": "参与过快,请稍后再试。", + "eclipseNone": "当前没有进行中的日蚀", + "eclipseSuccessTitle": "成功阻止了\n陨石撞击!", + "eclipseQualified": "参与已认定", + "eclipseFailTitle": "很遗憾,\n星球防御失败了", + "eclipseFailBody": "未达成本轮防御目标。", + "eclipseCancelTitle": "日蚀已取消", + "eclipseCancelBody": "因统计错误已取消,FPI 未扣除。", + "eclipsePendingTitle": "正在确认防御结果", + "eclipsePendingBody": "参与时间已结束,正在等待服务器判定。", + "eclipseShakeTimed": "秒内摇动手机\n阻止陨石撞击星球!", + "eclipseShake": "摇动手机\n阻止陨石撞击星球!", + "eclipseMyCount": "我的参与 {count} 次", + "eclipsePendingSuffix": " · +{count} 待发送", + "eclipseSendingSuffix": " · 发送中", + "eclipseNoPopNote": "不会消耗 POP。有效摇动 {count} 次以上即可认定为参与者。", + "eclipseTapToJoin": "不方便摇动?点击参与", + "eclipseTimeLeft": "日蚀剩余时间:", + "eclipseSeconds": " {seconds} 秒", + "eclipseBannerDefended": "防御成功 — {name} FPI 未减少", + "eclipseBannerActive": "日蚀发动 — {name} 侵蚀 {rate}%", + "eclipseBannerDefense": "有效参与 {count} / {target} 人", + "timeHoursMinutesLeft": "剩余 {hours} 小时 {minutes} 分", + "timeMinutesLeft": "剩余 {minutes} 分", + "liveEclipseDefense": "[LIVE] {name} 参与了日蚀防御!(+{count})", + "liveHotTime": "[LIVE] {name} 的艺人热力时刻开始了!", + "liveHeroGift": "[LIVE] {name} 的英雄礼物到了!", + "liveCampaign": "[LIVE] {name} 支持了一个活动!", + "galaxySetPrimaryConfirm": "要将所选星球设为默认星球吗?", + "galaxyLoadFailed": "无法加载银河", + "commonSearch": "搜索", + "galaxyMyPlanets": "我的星球", + "galaxyMap": "银河地图", + "nebulaCreate": "创建星云", + "commonRecheck": "重新确认", + "zapApplying": "正在计入贡献", + "zapNeedsCheck": "需确认贡献结果", + "zapNoPlanet": "你还没有所属星球", + "commonContribute": "贡献", + "commonItem": "道具", + "galaxyDefended": "防御成功 · FPI 未减少", + "galaxyErosion": "侵蚀 {rate}% · 防御 {progress}%", + "galaxyOnlineMembers": "在线 {online} 人 · 成员 {members} 人", + "galaxySearchHint": "按粉丝圈名称搜索", + "commonRecentSearches": "最近搜索", + "commonClearAll": "全部删除", + "commonNoRecentSearches": "没有最近搜索。", + "galaxyNoHit": "未找到该粉丝圈。\n请再次确认名称。", + "galaxyMembers": "成员 {count} 人", + "galaxyPlanetRank": "{name} 星球 第 {rank} 名", + "galaxyMapOpen": "打开银河地图", + "zapNotEnoughPop": "POP 不足", + "zapFailNoPop": "POP 不足,无法贡献。", + "zapFailLedger": "余额与获取记录不符,已暂停贡献。", + "zapFailRetry": "请检查网络并重新确认本次贡献。", + "zapDone": "贡献完成", + "zapDonePop": "已贡献 {pop} POP", + "growthFounders": "创始成员", + "growthNebulaGoalPassed": "目标期限已过 · 满足条件即可诞生行星", + "growthFinalStage": "最终阶段", + "growthPromotionPending": "等待晋级确认", + "growthCheckConditions": "查看成长条件", + "growthConditionChecking": "正在确认{name}条件", + "growthConditionMet": "已满足{name}条件", + "growthConditionUnmet": "未满足{name}条件", + "nebulaHeadline": "为我们的粉丝星球取个名字吧。", + "nebulaBody": "创始成员在 {days} 天内达成目标 POP,\n即可诞生为正式星球!\n参与进化的成员将获得纪念徽章。", + "nebulaNameTaken": "该名称已被使用。", + "nebulaNameHint": "粉丝圈名称", + "nebulaGoal": "目标 ", + "nebulaFounders": " / 创始成员", + "nebulaGoalSemantics": "目标 {pop} POP · 创始成员 {members} 人 · 固定 {days} 天", + "commonMyPop": "我的 POP:", + "commonChange": "更改", + "planetShape": "形态 {n}", + "planetBgColor": "选择背景色", + "planetColorSelect": "选择星球颜色", + "planetTypeSelect": "选择星球类型", + "commonNext": "下一步", + "planetCreateFailed": "无法创建星球,请稍后再试。", + "planetCreateConfirm": "要用所选选项\n创建星球吗?", + "commonCreate": "创建", + "briefingOpenFailed": "无法打开原文。", + "campaignAchieved": "已达成 {percent}%", + "campaignView": "查看活动", + "growthFinalReached": "已到达最终阶段", + "zapRetrySame": "重新确认本次贡献", + "heroOwnerSuffix": "的", + "heroStartPrompt": "创建或加入星球,\n开启粉丝宇宙吧!", + "heroNebulaPrompt": "用活动获得的 POP\n为星云做贡献吧!", + "heroRecentContribution": "凭借 {pop} POP 的贡献\n正在稳步成长。", + "heroGrowingTogether": "一起培育的粉丝星球", + "heroPopProgress": "POP 进度", + "heroNebula": "星云", + "heroNebulaTap": "点击星云,\n让它诞生为星球吧!", + "heroCreateNebula": "创建星球(星云)", + "heroJoinPlanet": "加入星球", + "heroCreateOrJoin": "创建或加入星球", + "insideAnswers": "回答 {count}", + "itemJoinFirst": "加入星球后才能发送英雄礼物。", + "itemBuyNotReady": "道具购买功能准备中。", + "itemUseNotReady": "道具使用功能准备中。", + "payOpenFailed": "无法打开支付窗口。", + "itemBuy": "购买道具", + "itemSelect": "选择道具", + "commonComingSoon": "准备中。", + "itemSendPop": "发送 {pop} POP", + "itemBuyerPop": "购买者 +{pop} POP", + "itemOwned": "持有:{count}", + "itemOwnedCount": "持有:{count} 个", + "growthOwnerOf": "的 ", + "growthSteadySuffix": " 贡献,正在稳步成长。", + "questAttended": "已签到", + "questInProgressSpaced": "进行中", + "questRewardPaid": "奖励已发放", + "questClaim": "领取奖励", + "questStart": "开始任务", + "questRewardDone": "已领取奖励", + "popKindGeneral": "普通 POP", + "popKindContribution": "贡献 POP", + "popTxGrant": "发放", + "popTxSpend": "扣除", + "popTxRefund": "退款", + "popTxVoid": "作废", + "popTypeQuest": "任务", + "popTypeReward": "奖励", + "popTypePayment": "支付", + "popTypeSubscription": "订阅", + "popTypeAdmin": "运营", + "popTypeFee": "手续费", + "commonEtc": "其他", + "popReasonQuestSteps": "计步任务", + "popReasonQuestMusicShake": "音乐摇摇任务", + "popReasonQuestPlace": "地点打卡任务", + "popReasonQuestAttend": "签到任务", + "popReasonAdReward": "广告奖励", + "popReasonHeroDrop": "英雄礼物", + "popReasonHeroDropTrigger": "英雄礼物发动", + "popReasonTranslation": "参与式翻译", + "popReasonIapPurchase": "购买商品", + "popReasonFocusDaily": "Focus 每日奖励", + "popReasonPreregBonus": "预约奖励", + "popReasonReferral": "邀请好友", + "popTypeReferral": "推荐", + "popReasonAdminGrant": "运营发放", + "popReasonAdminClawback": "运营回收", + "popReasonCampaignFund": "活动支持", + "popReasonCampaignRefund": "活动退款", + "popReasonCampaignFeeRefund": "活动开设手续费退款", + "popReasonCampaignFee": "活动登记费", + "popReasonQnaBounty": "INSIDE 悬赏", + "popReasonQnaRefund": "悬赏退款", + "popReasonQnaReward": "INSIDE 采纳奖励", + "popReasonEclipseDefense": "Zap 贡献", + "popReasonOther": "POP 变动", + "popRepeat": "{title} {count} 次", + "payStatePaid": "支付完成", + "payStateRefundRequested": "已申请退款", + "payStateRefundApproved": "退款已批准", + "payStateRefunded": "退款完成", + "payStateRefundRejected": "退款被驳回", + "pushTopicEssentialDesc": "账号、支付、维护通知,无法关闭。", + "pushTopicFandomEventDesc": "日蚀、活动、任务、艺人热力时刻消息。", + "pushTopicBriefingDesc": "为你推送星球每日简报卡片。", + "pushTopicCommunityDesc": "我的帖子的评论、点赞、回答通知。", + "pushTopicMarketingDesc": "接收活动与优惠信息,仅在同意后开启。", + "inquiryPending": "已受理", + "inquiryInProgress": "处理中", + "inquiryAnswered": "已回复", + "photoBadType": "仅可上传 JPG、PNG、WEBP、GIF 图片。", + "photoTooBig": "照片超过 10MB,请选择更小的照片。", + "photoUnreadable": "无法读取照片,请重新选择。", + "myListInside": "我的 INSIDE", + "myListInsideEmpty": "你还没有发布 INSIDE。", + "myListCampaign": "我的活动", + "myListCampaignEmpty": "你还没有创建活动。", + "sortLatest": "最新", + "sortPop": "按 POP", + "sortSolved": "已采纳", + "sortAchieved": "已达成", + "payNotFound": "找不到支付记录。", + "payItemFallback": "支付商品", + "payMethodInApp": "应用内购买", + "unknownUser": "未知用户", + "blockSelf": "不能屏蔽自己。", + "inquiryFallback": "咨询", + "photoRejectedServer": "无法上传照片,请检查格式和大小。", + "photoUploadFailed": "照片上传失败,请稍后再试。", + "focusClaimed": "已领取今日 Focus 奖励 +{pop} 贡献 POP。", + "focusActive": "正在使用无广告专注模式", + "focusUntil": "有效期至 {date} · 今日奖励仅限今日领取", + "focusClaimedToday": "今日已领取", + "focusClaimToday": "领取今日奖励", + "storeConnectFailed": "无法连接商店,请稍后再试。", + "payRestore": "恢复购买", + "payEmpty": "暂无支付记录。", + "payRefundGuide": "消耗型商品:购买后 7 天内且未使用可退款,已部分使用的商品不予退款。\n订阅型商品:支付后 24 小时内未使用权益(如额外奖励)可退款。\n退款批准后 7–14 天内完成退款。", + "payDetailTitle": "支付详情", + "payNumber": "支付编号", + "payDate": "支付时间", + "payMethod": "支付方式", + "payStatus": "支付状态", + "payAmount": "支付金额", + "payRefundInfo": "退款说明", + "payRefundApply": "申请退款", + "payRefundUnavailable": "不可退款", + "refundReasonMistake": "误操作付款", + "refundReasonExpectation": "服务与预期不符", + "refundReasonError": "因错误无法使用", + "refundReasonDuplicate": "重复付款", + "refundTitle": "退款", + "refundSubmit": "提交", + "refundReason": "退款原因", + "refundConfirm": "确定要申请退款吗?", + "myPop": "我的 POP", + "commonNoHistory": "暂无记录。", + "nicknameEdit": "修改昵称", + "nicknameReserved": "不能使用可能被误认为运营方的名称。", + "nicknameTaken": "该昵称已被使用。", + "albumItems": "相册选项", + "takePhoto": "拍照", + "pickFromAlbum": "从相册选择", + "tierGuide": "等级说明", + "statSteps": "累计步数", + "statContribution": "累计贡献 POP", + "statQuests": "已完成任务", + "shareInstagram": "Instagram", + "shareTiktok": "TikTok", + "shareCopyLink": "复制链接", + "inquiryListTitle": "1:1 咨询", + "inquiryEmpty": "暂无咨询。", + "inquiryWrite": "撰写咨询", + "inquiryDetail": "咨询详情", + "commonEdit": "编辑", + "inquiryAnswer": "回复", + "inquiryNew": "新建咨询", + "inquiryEdit": "编辑咨询", + "commonTitle": "标题", + "commonTitleHint": "请输入标题。", + "inquiryBody": "咨询内容", + "inquiryBodyHint": "请填写咨询内容。", + "inquiryBodyHelper": "填写 10 个字以上,我们能更快为你处理。", + "inquirySubmitFailed": "咨询提交失败。{reason}", + "inquiryEditFailed": "咨询修改失败。{reason}", + "inquirySubmitted": "咨询已提交。", + "inquiryEdited": "咨询已修改。", + "blockedEmpty": "没有已屏蔽的账号。", + "unblock": "取消屏蔽", + "unblockFailed": "取消屏蔽失败。{reason}", + "tierInfoTitle": "TIER 信息", + "tierPromptPrefix": "通过为星球/星云做贡献\n", + "tierPromptRank": "等级", + "tierPromptSuffix": "来提升吧", + "tierPassportNote": "Passport Tier 是与等级无关的卡片外观等级,不会降低。", + "tierTotalContribution": "累计贡献", + "notifKindEclipse": "日蚀", + "notifKindItemUsed": "道具使用", + "notifKindLike": "点赞", + "notifKindComment": "评论", + "notifKindCampaign": "活动达成", + "notifKindQuest": "任务完成", + "notifTargetPost": "帖子", + "notifTargetCampaign": "活动", + "notifTargetQuest": "任务详情", + "dayToday": "今天", + "dayYesterday": "昨天", + "notifEmpty": "暂无通知。", + "notifGoTo": "即将前往{target}。", + "stationOfficial": "官方", + "stationShop": "商店", + "stationTicket": "购票", + "stationMedia": "媒体", + "stationLoadFailed": "无法加载 STATION", + "commonAll": "全部", + "stationSuggest": "推荐商店", + "stationEmpty": "暂无链接。\n加入星球后即可看到精选内容。", + "stationCategoryEmpty": "此分类暂无链接。", + "stationSuggestLater": "服务器连接后即可提交推荐。", + "commonSubmit": "提交", + "commonName": "名称", + "commonNameRequired": "请输入名称。", + "commonUrl": "网址", + "commonUrlRequired": "请输入网址。", + "commonUrlHttps": "请输入以 https 开头的网址。", + "commonCategory": "分类", + "stationReason": "推荐理由(选填)", + "stationSuggestNote": "推荐的链接经运营审核后登记,审核结果将通过通知告知。", + "heroGiftUnavailable": "该礼物目前无法领取。", + "heroGiftAlready": "你已参与过该礼物,请查看 POP 记录。", + "heroGiftRelogin": "请重新登录后参与。", + "heroGiftNoPermission": "你没有参与该礼物的权限。", + "heroGiftHowTo": "摇动手机或点击下方按钮,参与光能礼物。", + "heroGiftPopNote": "可领取的 POP 取决于剩余礼物和每人上限。", + "heroGiftReceived": "已确认领取 {total} POP", + "heroGiftUnknown": "无法确认发放结果,请重新确认。", + "heroGiftNone": "目前没有可参与的礼物。", + "heroGiftConfirmed": "已确认领取所参与的礼物。", + "heroGiftChecking": "正在确认发放…", + "heroGiftRetry": "重新确认该礼物", + "heroGiftTap": "用按钮代替摇动参与", + "rankMyPlanet": "我的星球 {metric} 排名", + "rankPlanetEmpty": "暂无星球排名数据", + "rankScopeLive": "实时", + "rankScopeCountry": "按国家", + "rankMine": "我的 {metric} 排名", + "rankMyCountry": "我所在国家的 {metric} 排名", + "rankEmpty": "暂无排名数据", + "rankScope": "{scope}排名", + "rankLoadFailed": "无法加载排名", + "rankMeSuffix": "{name}(我)", + "reportDefamation": "诽谤艺人及损害名誉", + "reportInappropriate": "不当内容", + "reportSpam": "垃圾信息及商业广告", + "reportPrivacy": "泄露个人信息及冒充他人", + "reportTitle": "举报", + "reportSubmit": "举报", + "reportAuthor": "作者", + "reportReasonLabel": "举报原因", + "reportConfirm": "要举报这条帖子吗?\n提交后无法撤销。", + "blockMemberConfirm": "要屏蔽该会员的所有帖子吗?", + "loungeMembersOnly": "仅该星球粉丝圈成员可发帖、评论和点赞。", + "loungeFailed": "处理失败,请稍后再试。", + "loungeTitle": "粉丝休息室", + "likeCancel": "取消点赞", + "like": "点赞", + "commentDeleteConfirm": "要删除评论吗?", + "commonShare": "分享", + "commonEditAction": "编辑", + "commonDeleteAction": "删除", + "commonBlockAction": "屏蔽", + "commentFirst": "来发表第一条评论吧。", + "postDeleteConfirm": "要删除这篇帖子吗?", + "reportReceived": "举报已受理。", + "commonDelete": "删除", + "commentHint": "写下评论吧。", + "commonSend": "发送", + "postWrite": "发帖", + "postEdit": "编辑帖子", + "loungeHint": "在粉丝休息室自由发帖吧。", + "photoRemove": "移除照片 {n}", + "camera": "相机", + "album": "相册", + "historyTitle": "PASSPORT · 记录", + "historyMine": "我的活动", + "historyEvolution": "Stage {from} → {to} 进化", + "historyHibernation": "星球休眠", + "historyWakeUp": "星球苏醒", + "historyZap": "ZAP 贡献", + "historyQuest": "Quest 奖励", + "historyAdBonus": "广告额外奖励", + "historyFunding": "Campaign 贡献", + "historyGrowthPop": "{amount} POP 计入成长", + "historyJoined": "你参与了这次进化", + "historyLoadFailed": "无法加载记录。", + "historyMineEmpty": "暂无活动记录。", + "historyFandomEmpty": "暂无粉丝圈历史。", + "historyMore": "查看更早记录", + "countryName": "{code, select, KR{韩国} JP{日本} US{美国} CN{中国} TW{台湾} HK{香港} TH{泰国} VN{越南} ID{印度尼西亚} PH{菲律宾} MY{马来西亚} SG{新加坡} IN{印度} GB{英国} FR{法国} DE{德国} ES{西班牙} BR{巴西} MX{墨西哥} CA{加拿大} AU{澳大利亚} ZZ{未指定} other{-}}", + "loungeMaxPhotos": "最多可上传 {count} 张照片。", + "commonLoadFailed": "加载失败", + "planetPassportHistory": "PASSPORT · 粉丝圈历史", + "planetNamed": "{name} 星球", + "blockNebulaConfirm": "要屏蔽这个星云吗?", + "blockPlanetConfirm": "要屏蔽这个星球吗?", + "blocked": "已屏蔽。", + "planetLastUpdated": "最后更新 {time}", + "planetContribution": "贡献情况", + "planetNebulaGrowth": "星云成长率", + "planetLiveRanking": "实时贡献排名", + "planetNoContributors": "还没有贡献者", + "planetContributeCta": "贡献一下,登上排行榜吧!", + "planetOverallRank": "总排名第 {rank}", + "planetMyContribution": "我的贡献度", + "planetTallying": "统计准备中", + "planetTopPercent": "前 {percent}%", + "planetHideMember": "隐藏该会员的所有帖子", + "reportReasonUnknown": "无法确认举报原因。", + "reportAlready": "你已举报过该帖子。", + "blockUnavailable": "无法屏蔽该作者。", + "commonMe": "我", + "planetSizeMedium": "中型粉丝圈", + "planetPerHead": "人均火力:{value}", + "planetJoined": "已加入", + "planetJoin": "加入", + "questDetails": "详情", + "questInProgress": "进行中", + "questTypeAttend": "签到", + "questTypeSteps": "步行", + "questTypePlace": "指定地点", + "questTypeStreaming": "流媒体", + "questTypeUnverifiable": "该任务类型的验证方式尚未确定。", + "questTabMine": "我的任务", + "placePickPrompt": "请选择\n完成任务的地点。", + "placeSearch": "搜索地点", + "commonNoResults": "没有搜索结果。", + "commonSelectDone": "选择完成", + "adMoreChance": "获得更多贡献机会。", + "adWatchPrompt": "观看广告即可\n获得额外奖励!", + "adBonusPlaceholder": "+ 贡献 POP", + "adPartyPack": "派对礼包", + "adPickProduct": "选择商品", + "adWatchBonus": "看广告领取额外奖励", + "adBasicReward": "领取基础奖励", + "focusMode": "无广告专注模式", + "pricePerMonth": "{price}/月", + "adWarpBefore": "Warping...正在努力寻找合适的广告,帮你为粉丝圈和艺人做更多贡献。", + "adWarpAfter": "Warping...正在为你的贡献准备奖励。", + "adSkip": "跳过", + "questPreviewHint": "选择选项并查看预览", + "questNoCategory": "尚未选择分类", + "questNew": "新任务", + "questCategory": "分类", + "questName": "任务名称", + "questAction": "具体行动", + "questPickPlace": "请选择目标地点", + "questGoalValue": "目标数值", + "questParticipants": "参与人数", + "questRewardPop": "完成奖励 POP", + "questCreateConfirm": "按此内容创建吗?", + "questCreateSummary": "{title} · 目标 {goal} · 奖励 {reward} POP", + "commonKeepEditing": "继续修改", + "questCreate": "创建", + "questNamed": "{title} 任务", + "questDailyHelp": "每天为星球助力", + "questTodayMust": "今天必做的任务", + "questTabEmpty": "此标签下暂无任务。", + "questCreateNotReady": "任务创建将与奖励资金托管一同开放,敬请期待。", + "questNoBaseReward": "找不到基础奖励记录,无法领取广告额外奖励。", + "questNoSteps": "尚未收集到步数,请走一会儿后再试。", + "questNoPlace": "该任务未登记验证地点。", + "questGoal": "目标", + "questReward": "奖励", + "questPopGrant": "发放 POP", + "commonChecking": "确认中…", + "questMine": "这是你创建的任务,参与者 {count} 人。", + "questStopConfirm": "要中止任务吗?", + "questStopBody": "当前进度将被重置并恢复初始状态。", + "questStop": "中止", + "adNotFinished": "未看完广告,无额外奖励,基础奖励照常发放。", + "openSettings": "打开设置", + "permStepsDenied": "步数权限已关闭,请在设置中允许“身体活动”。", + "permStepsNeeded": "计步需要身体活动权限。", + "permLocationOff": "设备定位已关闭,请开启后重试。", + "permLocationNeeded": "需要定位权限以确认到访。", + "permLocationDenied": "定位权限已关闭,请在设置中允许访问位置。", + "questRewardReceived": " 已领取!", + "questAdBonus": "广告额外奖励 +{pop} POP", + "questParticipantsCount": "参与人数:{count}", + "questParticipantsCap": "参与人数:{count} / {cap}", + "nebulaCreateFailed": "无法创建星云,请稍后再试。", + "nebulaAlreadyToday": "你今天已创建过星云。", + "nebulaCreatedKeepPrimary": "星云已创建。默认星球需在上次更改 30 天后才能更换,因此保持不变。", + "fandomFounderLeaveBlocked": "行星诞生后才能退出。", + "sortPopular": "热门", + "campaignSortContributing": "贡献中", + "campaignStatusReview": "该活动正在接受运营审核。", + "campaignStatusRejected": "活动未通过审核。登记费已退还。", + "campaignStatusWithdrawn": "该活动已被发起人撤回。", + "campaignStatusCancelled": "因运营原因已取消。登记费和支援的 POP 已退还。", + "rankWindowDaily": "日榜", + "rankWindowWeekly": "周榜", + "campaignNotFound": "找不到该活动。", + "campaignMediaMain": "主图", + "campaignLoadFailed": "无法加载活动。", + "campaignTabInfo": "活动信息", + "campaignTabBoard": "应援留言 {count}", + "campaignWithdraw": "撤回活动", + "campaignContributeAction": "贡献", + "campaignFundingStatus": "当前筹集情况", + "campaignSupporting": "应援中", + "campaignTimeLeft": "剩余!", + "campaignReport": "结果报告", + "campaignReportPhotos": "执行照片", + "campaignBudget": "预算使用明细", + "campaignTotal": "合计", + "campaignReceipts": "收据", + "campaignDaysLeft": "{days}天 {time}", + "campaignWithdrawTitle": "要撤回该活动吗?", + "campaignWithdrawRefund": "由于在公开前撤回,{fee} POP 登记费将按原支付方式退还。", + "campaignWithdrawNoRefund": "该活动已公开,{fee} POP 登记费不予退还。", + "campaignWithdrawConfirm": "撤回", + "shareCopied": "已复制分享文案。", + "campaignContributePop": "设置贡献 POP", + "campaignEmptyContributing": "您还没有贡献过任何活动。", + "campaignEmpty": "暂无活动。", + "campaignNew": "新活动", + "campaignMyRanking": "我的贡献排名", + "campaignWindowRanking": "{window}排名", + "campaignSearchHint": "搜索活动", + "campaignNoRecentSearches": "没有最近搜索", + "searchRemoveTerm": "删除 {term}", + "campaignNoResults": "没有搜索结果", + "shareTopicCampaign": "活动", + "sharePlanetLine": "{brand} · {name} 行星", + "campaignGaugeLabel": "活动贡献率", + "campaignPeriodTitle": "请选择活动期间", + "campaignPeriodMax": "最长可选择一年", + "campaignYearMonth": "{year}年{month}月", + "campaignWeekdays": "日,一,二,三,四,五,六", + "campaignPeriodRange": "{startMonth}月{startDay}日 ~ {endMonth}月{endDay}日", + "campaignPeriodSelect": "选择期间", + "calendarPrevMonth": "上个月", + "calendarNextMonth": "下个月", + "campaignShareTitle": "分享活动", + "campaignPickMedia": "选择图片/视频", + "campaignTitleHint": "请输入活动标题。", + "campaignBodyHint": "请填写活动说明。", + "campaignReviewNote": "提交后,经运营审核即开放筹集。", + "campaignSupportType": "选择支援类型", + "campaignPeriod": "活动期间", + "campaignDateSelect": "选择日期", + "campaignGoalPop": "目标贡献 POP", + "campaignSubmitTitle": "按此内容提交吗?", + "campaignSubmitBody": "{title} · 目标 {goal} POP · {days}天\n将扣除 {fee} POP 登记费,运营审核后开放筹集。", + "campaignSubmit": "提交", + "campaignCreateFailed": "无法提交活动。", + "campaignNeedsQuests": "已完成任务 {done}/{need} — 再完成 {left} 次即可发起活动。", + "campaignPendingTitle": "等待审核", + "campaignPendingBody": "运营审核完成后将开放筹集并显示在列表中。", + "insideRefundPending": "采纳期限已过,悬赏将被退还。", + "insideRefundDone": "采纳期限已过,悬赏已退还。", + "insideRefundChecking": "采纳期限已过,正在确认悬赏退还。", + "insideErrPopShort": "POP 不足,无法设置悬赏。", + "insideErrSelfAccept": "不能采纳自己的回答。", + "insideErrAlreadyAccepted": "该问题已采纳,无法更改。", + "insideErrNotOwner": "只有提问者可以采纳。", + "commonCheckConnection": "请检查网络连接后重试。", + "insideAnswerLater": "服务器连接后即可发表评论。", + "insideAcceptConfirm": "要采纳这条评论吗?\n将发放 {pop} POP 悬赏,采纳后无法撤销。", + "insideAccept": "采纳", + "insideMore": "更多", + "insideFirstAnswer": "来发表第一条评论吧。", + "insideAcceptDone": "已采纳", + "insideAcceptAction": "采纳", + "insideAnswerHint": "写下评论吧。", + "insideLoadFailed": "无法加载 INSIDE", + "insideEmpty": "暂无提问。", + "insideWrite": "发布 INSIDE", + "insideSearchHint": "INSIDE 标题、内容", + "insideBodyHint": "和粉丝们自由地提问交流吧。", + "insideTitleRequired": "请输入标题。", + "insideBodyRequired": "请输入内容。", + "insideBountySet": "设置悬赏", + "insideBountyHint": "请设置 {min} 以上。", + "insideAttachLater": "服务器连接后即可附加图片。", + "insideBountyRequired": "请输入悬赏。", + "insideNumberOnly": "请输入数字。", + "insideBountyMin": "悬赏须至少为 {min} POP。", + "insideBountyOverBalance": "不能超过持有的 POP。", + "questTypeTimeAttack": "紧急限时挑战", + "questTimeLeft": "剩余时间", + "questTimeAttackClosed": "已截止", + "questTimeAttackLeft": "剩余 {time}", + "popReasonQuestTimeAttack": "紧急限时挑战", + "briefingShareCopied": "已复制分享文字和链接。", + "shareCopyText": "复制文字", + "briefingShareTextCopied": "已复制分享文字。", + "questAlreadyRewarded": "已领取该奖励(+{pop} POP)。", + "questCatHealth": "健康", + "questCatPlanet": "星球活动", + "questCatCommunity": "社区", + "questActShake": "摇一摇", + "questTypeVote": "参与投票", + "questTypeSns": "社交媒体", + "questActivity": "活动", + "questNoCreatable": "目前没有可创建的任务活动。", + "questDescriptionOptional": "说明(可选)", + "questLinkUrl": "链接地址", + "questLinkCheck": "检查链接", + "questLinkInvalid": "链接无效,请检查地址。", + "questLinkPreviewFailed": "无法获取链接信息,请手动输入标题。", + "questLinkTitle": "链接标题", + "questEndDate": "结束日期", + "questEndDefault": "发布后24小时", + "questEndDateValue": "{date} 23:59:59 截止", + "questPoolHelper": "共存入 {pool} POP · 手续费 {fee} POP", + "questCreateCost": "{title}\n奖励 {reward} POP × {count}人 = {pool} POP\n手续费 {fee} POP(发布时销毁)\n共扣除 {total} POP。", + "questNoReward": "这是无奖励的参与任务。", + "questDeadline": "截止", + "questRemainingHours": "剩余{hours}小时", + "questEnded": "已截止", + "questAuthorNote": "创建者说明", + "questRepeatOnce": "每人限参与1次", + "questRepeatEvery": "间隔{hours}小时 · 最多{count}次", + "questSubmit": "提交认证", + "questResubmit": "补充提交", + "questEvidenceUrl": "认证链接(可选)", + "questEvidenceText": "认证内容", + "questSubmitted": "已提交,审核后发放奖励。", + "questReviewPending": "审核中,批准后将发放奖励。", + "questReviewDelayed": "审核有所延迟,运营团队正在确认。", + "questApprovedPaying": "已批准,正在发放奖励。", + "questRejected": "已驳回:{reason}", + "questResubmitAllowed": "可补充后重新提交。", + "questResubmitNotAllowed": "此提交无法重新提交。", + "questReviewButton": "审核中", + "questRejectedButton": "已驳回", + "questReviewQueue": "待审核 {count} 件", + "questApprove": "批准", + "questReject": "驳回", + "questRejectReason": "驳回理由", + "questAllowResubmit": "允许补充重新提交", + "questEdit": "编辑标题和说明", + "questEditNotice": "发布后仅可编辑标题和说明,并会保留修改记录。", + "popReasonQuestUgc": "任务奖励", + "popReasonQuestFee": "任务创建手续费", + "questFreeDailyCap": "无奖励任务每天最多可创建 {cap} 个。", + "popReasonQuestPoolDeposit": "任务奖励存入", + "popReasonQuestPoolReturn": "任务奖励退回", + "campaignCommentNeedNickname": "请先设置昵称", + "campaignCommentSetNickname": "设置昵称" +} diff --git a/app/lib/l10n/remote_app_localizations.dart b/app/lib/l10n/remote_app_localizations.dart new file mode 100644 index 0000000..7d5a78c --- /dev/null +++ b/app/lib/l10n/remote_app_localizations.dart @@ -0,0 +1,2382 @@ +// GENERATED by tool/gen_l10n_remote.dart — 직접 고치지 말 것. +import 'app_localizations.dart'; + +/// 번들 문자열 위에 원격 문자열(`i18n/{locale}` 문서)을 덮는다. +/// 원격에 없는 키는 번들 → 번들에도 없으면 ko(template) 순으로 폴백한다. +class RemoteAppLocalizations extends AppLocalizations { + RemoteAppLocalizations(this._base, this._remote) : super(_base.localeName); + + final AppLocalizations _base; + final Map _remote; + + /// 자리표시자를 한 번에 치환한다 — 인자 값 안의 `{name}` 은 다시 바꾸지 않는다. + String? _fill(String key, Map args) => + _remote[key]?.replaceAllMapped( + RegExp(r'\{(\w+)\}'), + (m) => args[m[1]] ?? m[0]!, + ); + + @override + String get adBasicReward => _remote['adBasicReward'] ?? _base.adBasicReward; + + @override + String get adBonusPlaceholder => _remote['adBonusPlaceholder'] ?? _base.adBonusPlaceholder; + + @override + String get adLoadFailed => _remote['adLoadFailed'] ?? _base.adLoadFailed; + + @override + String get adMoreChance => _remote['adMoreChance'] ?? _base.adMoreChance; + + @override + String get adNotFinished => _remote['adNotFinished'] ?? _base.adNotFinished; + + @override + String get adPartyPack => _remote['adPartyPack'] ?? _base.adPartyPack; + + @override + String get adPickProduct => _remote['adPickProduct'] ?? _base.adPickProduct; + + @override + String get adSkip => _remote['adSkip'] ?? _base.adSkip; + + @override + String get adWarpAfter => _remote['adWarpAfter'] ?? _base.adWarpAfter; + + @override + String get adWarpBefore => _remote['adWarpBefore'] ?? _base.adWarpBefore; + + @override + String get adWatchBonus => _remote['adWatchBonus'] ?? _base.adWatchBonus; + + @override + String get adWatchPrompt => _remote['adWatchPrompt'] ?? _base.adWatchPrompt; + + @override + String get album => _remote['album'] ?? _base.album; + + @override + String get albumItems => _remote['albumItems'] ?? _base.albumItems; + + @override + String get authAlreadyLinked => _remote['authAlreadyLinked'] ?? _base.authAlreadyLinked; + + @override + String get authAlreadyMember => _remote['authAlreadyMember'] ?? _base.authAlreadyMember; + + @override + String get authAppleUnavailable => _remote['authAppleUnavailable'] ?? _base.authAppleUnavailable; + + @override + String get authCheckCredentials => _remote['authCheckCredentials'] ?? _base.authCheckCredentials; + + @override + String get authCredentialInUse => _remote['authCredentialInUse'] ?? _base.authCredentialInUse; + + @override + String get authDifferentEmail => _remote['authDifferentEmail'] ?? _base.authDifferentEmail; + + @override + String get authEmailInUse => _remote['authEmailInUse'] ?? _base.authEmailInUse; + + @override + String get authEmailUnavailable => _remote['authEmailUnavailable'] ?? _base.authEmailUnavailable; + + @override + String get authFirstTime => _remote['authFirstTime'] ?? _base.authFirstTime; + + @override + String get authInvalidEmail => _remote['authInvalidEmail'] ?? _base.authInvalidEmail; + + @override + String get authLastProvider => _remote['authLastProvider'] ?? _base.authLastProvider; + + @override + String get authLinkFailed => _remote['authLinkFailed'] ?? _base.authLinkFailed; + + @override + String get authLogin => _remote['authLogin'] ?? _base.authLogin; + + @override + String get authLoginHeadline => _remote['authLoginHeadline'] ?? _base.authLoginHeadline; + + @override + String get authLoginRequired => _remote['authLoginRequired'] ?? _base.authLoginRequired; + + @override + String get authMethodUnavailable => _remote['authMethodUnavailable'] ?? _base.authMethodUnavailable; + + @override + String authNotConfiguredBody(String name, String items) => _fill('authNotConfiguredBody', {'name': name, 'items': items}) ?? _base.authNotConfiguredBody(name, items); + + @override + String authNotConfiguredNotice(String name, String items) => _fill('authNotConfiguredNotice', {'name': name, 'items': items}) ?? _base.authNotConfiguredNotice(name, items); + + @override + String authNotConfiguredTitle(String name) => _fill('authNotConfiguredTitle', {'name': name}) ?? _base.authNotConfiguredTitle(name); + + @override + String get authNotLinked => _remote['authNotLinked'] ?? _base.authNotLinked; + + @override + String authOtherProviderGuide(String email) => _fill('authOtherProviderGuide', {'email': email}) ?? _base.authOtherProviderGuide(email); + + @override + String authProviderLogin(String provider) => _base.authProviderLogin(provider); + + @override + String authProviderName(String provider) => _base.authProviderName(provider); + + @override + String authProviderSignup(String provider) => _base.authProviderSignup(provider); + + @override + String authProviderStart(String provider) => _base.authProviderStart(provider); + + @override + String get authRecentLogin => _remote['authRecentLogin'] ?? _base.authRecentLogin; + + @override + String get authResetNeedEmail => _remote['authResetNeedEmail'] ?? _base.authResetNeedEmail; + + @override + String get authResetSent => _remote['authResetSent'] ?? _base.authResetSent; + + @override + String get authSignup => _remote['authSignup'] ?? _base.authSignup; + + @override + String get authSignupHeadline => _remote['authSignupHeadline'] ?? _base.authSignupHeadline; + + @override + String get authSocialFailed => _remote['authSocialFailed'] ?? _base.authSocialFailed; + + @override + String get authThisAccount => _remote['authThisAccount'] ?? _base.authThisAccount; + + @override + String get authWeakPassword => _remote['authWeakPassword'] ?? _base.authWeakPassword; + + @override + String get blockMemberConfirm => _remote['blockMemberConfirm'] ?? _base.blockMemberConfirm; + + @override + String get blockNebulaConfirm => _remote['blockNebulaConfirm'] ?? _base.blockNebulaConfirm; + + @override + String get blockPlanetConfirm => _remote['blockPlanetConfirm'] ?? _base.blockPlanetConfirm; + + @override + String get blockSelf => _remote['blockSelf'] ?? _base.blockSelf; + + @override + String get blockUnavailable => _remote['blockUnavailable'] ?? _base.blockUnavailable; + + @override + String get blocked => _remote['blocked'] ?? _base.blocked; + + @override + String get blockedEmpty => _remote['blockedEmpty'] ?? _base.blockedEmpty; + + @override + String get briefingOpenFailed => _remote['briefingOpenFailed'] ?? _base.briefingOpenFailed; + + @override + String get briefingShareCopied => _remote['briefingShareCopied'] ?? _base.briefingShareCopied; + + @override + String get briefingShareTextCopied => _remote['briefingShareTextCopied'] ?? _base.briefingShareTextCopied; + + @override + String get calendarNextMonth => _remote['calendarNextMonth'] ?? _base.calendarNextMonth; + + @override + String get calendarPrevMonth => _remote['calendarPrevMonth'] ?? _base.calendarPrevMonth; + + @override + String get camera => _remote['camera'] ?? _base.camera; + + @override + String campaignAchieved(String percent) => _fill('campaignAchieved', {'percent': percent}) ?? _base.campaignAchieved(percent); + + @override + String get campaignBodyHint => _remote['campaignBodyHint'] ?? _base.campaignBodyHint; + + @override + String get campaignBudget => _remote['campaignBudget'] ?? _base.campaignBudget; + + @override + String get campaignCommentNeedNickname => _remote['campaignCommentNeedNickname'] ?? _base.campaignCommentNeedNickname; + + @override + String get campaignCommentSetNickname => _remote['campaignCommentSetNickname'] ?? _base.campaignCommentSetNickname; + + @override + String get campaignContributeAction => _remote['campaignContributeAction'] ?? _base.campaignContributeAction; + + @override + String get campaignContributePop => _remote['campaignContributePop'] ?? _base.campaignContributePop; + + @override + String get campaignCreateFailed => _remote['campaignCreateFailed'] ?? _base.campaignCreateFailed; + + @override + String get campaignDateSelect => _remote['campaignDateSelect'] ?? _base.campaignDateSelect; + + @override + String campaignDaysLeft(String days, String time) => _fill('campaignDaysLeft', {'days': days, 'time': time}) ?? _base.campaignDaysLeft(days, time); + + @override + String get campaignEmpty => _remote['campaignEmpty'] ?? _base.campaignEmpty; + + @override + String get campaignEmptyContributing => _remote['campaignEmptyContributing'] ?? _base.campaignEmptyContributing; + + @override + String get campaignFundingStatus => _remote['campaignFundingStatus'] ?? _base.campaignFundingStatus; + + @override + String get campaignGaugeLabel => _remote['campaignGaugeLabel'] ?? _base.campaignGaugeLabel; + + @override + String get campaignGoalPop => _remote['campaignGoalPop'] ?? _base.campaignGoalPop; + + @override + String get campaignLoadFailed => _remote['campaignLoadFailed'] ?? _base.campaignLoadFailed; + + @override + String get campaignMediaMain => _remote['campaignMediaMain'] ?? _base.campaignMediaMain; + + @override + String get campaignMyRanking => _remote['campaignMyRanking'] ?? _base.campaignMyRanking; + + @override + String campaignNeedsQuests(String done, String need, String left) => _fill('campaignNeedsQuests', {'done': done, 'need': need, 'left': left}) ?? _base.campaignNeedsQuests(done, need, left); + + @override + String get campaignNew => _remote['campaignNew'] ?? _base.campaignNew; + + @override + String get campaignNoRecentSearches => _remote['campaignNoRecentSearches'] ?? _base.campaignNoRecentSearches; + + @override + String get campaignNoResults => _remote['campaignNoResults'] ?? _base.campaignNoResults; + + @override + String get campaignNotFound => _remote['campaignNotFound'] ?? _base.campaignNotFound; + + @override + String get campaignPendingBody => _remote['campaignPendingBody'] ?? _base.campaignPendingBody; + + @override + String get campaignPendingTitle => _remote['campaignPendingTitle'] ?? _base.campaignPendingTitle; + + @override + String get campaignPeriod => _remote['campaignPeriod'] ?? _base.campaignPeriod; + + @override + String get campaignPeriodMax => _remote['campaignPeriodMax'] ?? _base.campaignPeriodMax; + + @override + String campaignPeriodRange(String startMonth, String startDay, String endMonth, String endDay) => _fill('campaignPeriodRange', {'startMonth': startMonth, 'startDay': startDay, 'endMonth': endMonth, 'endDay': endDay}) ?? _base.campaignPeriodRange(startMonth, startDay, endMonth, endDay); + + @override + String get campaignPeriodSelect => _remote['campaignPeriodSelect'] ?? _base.campaignPeriodSelect; + + @override + String get campaignPeriodTitle => _remote['campaignPeriodTitle'] ?? _base.campaignPeriodTitle; + + @override + String get campaignPickMedia => _remote['campaignPickMedia'] ?? _base.campaignPickMedia; + + @override + String get campaignReceipts => _remote['campaignReceipts'] ?? _base.campaignReceipts; + + @override + String get campaignReport => _remote['campaignReport'] ?? _base.campaignReport; + + @override + String get campaignReportPhotos => _remote['campaignReportPhotos'] ?? _base.campaignReportPhotos; + + @override + String get campaignReviewNote => _remote['campaignReviewNote'] ?? _base.campaignReviewNote; + + @override + String get campaignSearchHint => _remote['campaignSearchHint'] ?? _base.campaignSearchHint; + + @override + String get campaignShareTitle => _remote['campaignShareTitle'] ?? _base.campaignShareTitle; + + @override + String get campaignSortContributing => _remote['campaignSortContributing'] ?? _base.campaignSortContributing; + + @override + String get campaignStatusCancelled => _remote['campaignStatusCancelled'] ?? _base.campaignStatusCancelled; + + @override + String get campaignStatusRejected => _remote['campaignStatusRejected'] ?? _base.campaignStatusRejected; + + @override + String get campaignStatusReview => _remote['campaignStatusReview'] ?? _base.campaignStatusReview; + + @override + String get campaignStatusWithdrawn => _remote['campaignStatusWithdrawn'] ?? _base.campaignStatusWithdrawn; + + @override + String get campaignSubmit => _remote['campaignSubmit'] ?? _base.campaignSubmit; + + @override + String campaignSubmitBody(String title, String goal, String days, String fee) => _fill('campaignSubmitBody', {'title': title, 'goal': goal, 'days': days, 'fee': fee}) ?? _base.campaignSubmitBody(title, goal, days, fee); + + @override + String get campaignSubmitTitle => _remote['campaignSubmitTitle'] ?? _base.campaignSubmitTitle; + + @override + String get campaignSupportType => _remote['campaignSupportType'] ?? _base.campaignSupportType; + + @override + String get campaignSupporting => _remote['campaignSupporting'] ?? _base.campaignSupporting; + + @override + String campaignTabBoard(String count) => _fill('campaignTabBoard', {'count': count}) ?? _base.campaignTabBoard(count); + + @override + String get campaignTabInfo => _remote['campaignTabInfo'] ?? _base.campaignTabInfo; + + @override + String get campaignTimeLeft => _remote['campaignTimeLeft'] ?? _base.campaignTimeLeft; + + @override + String get campaignTitleHint => _remote['campaignTitleHint'] ?? _base.campaignTitleHint; + + @override + String get campaignTotal => _remote['campaignTotal'] ?? _base.campaignTotal; + + @override + String get campaignView => _remote['campaignView'] ?? _base.campaignView; + + @override + String get campaignWeekdays => _remote['campaignWeekdays'] ?? _base.campaignWeekdays; + + @override + String campaignWindowRanking(String window) => _fill('campaignWindowRanking', {'window': window}) ?? _base.campaignWindowRanking(window); + + @override + String get campaignWithdraw => _remote['campaignWithdraw'] ?? _base.campaignWithdraw; + + @override + String get campaignWithdrawConfirm => _remote['campaignWithdrawConfirm'] ?? _base.campaignWithdrawConfirm; + + @override + String campaignWithdrawNoRefund(String fee) => _fill('campaignWithdrawNoRefund', {'fee': fee}) ?? _base.campaignWithdrawNoRefund(fee); + + @override + String campaignWithdrawRefund(String fee) => _fill('campaignWithdrawRefund', {'fee': fee}) ?? _base.campaignWithdrawRefund(fee); + + @override + String get campaignWithdrawTitle => _remote['campaignWithdrawTitle'] ?? _base.campaignWithdrawTitle; + + @override + String campaignYearMonth(String year, String month) => _fill('campaignYearMonth', {'year': year, 'month': month}) ?? _base.campaignYearMonth(year, month); + + @override + String get commentDeleteConfirm => _remote['commentDeleteConfirm'] ?? _base.commentDeleteConfirm; + + @override + String get commentFirst => _remote['commentFirst'] ?? _base.commentFirst; + + @override + String get commentHint => _remote['commentHint'] ?? _base.commentHint; + + @override + String get commonAll => _remote['commonAll'] ?? _base.commonAll; + + @override + String get commonBlockAction => _remote['commonBlockAction'] ?? _base.commonBlockAction; + + @override + String get commonCancel => _remote['commonCancel'] ?? _base.commonCancel; + + @override + String get commonCategory => _remote['commonCategory'] ?? _base.commonCategory; + + @override + String get commonChange => _remote['commonChange'] ?? _base.commonChange; + + @override + String get commonCheckConnection => _remote['commonCheckConnection'] ?? _base.commonCheckConnection; + + @override + String get commonChecking => _remote['commonChecking'] ?? _base.commonChecking; + + @override + String get commonClearAll => _remote['commonClearAll'] ?? _base.commonClearAll; + + @override + String get commonClose => _remote['commonClose'] ?? _base.commonClose; + + @override + String get commonComingSoon => _remote['commonComingSoon'] ?? _base.commonComingSoon; + + @override + String get commonConfirm => _remote['commonConfirm'] ?? _base.commonConfirm; + + @override + String get commonContribute => _remote['commonContribute'] ?? _base.commonContribute; + + @override + String get commonCreate => _remote['commonCreate'] ?? _base.commonCreate; + + @override + String get commonDelete => _remote['commonDelete'] ?? _base.commonDelete; + + @override + String get commonDeleteAction => _remote['commonDeleteAction'] ?? _base.commonDeleteAction; + + @override + String get commonDone => _remote['commonDone'] ?? _base.commonDone; + + @override + String get commonEdit => _remote['commonEdit'] ?? _base.commonEdit; + + @override + String get commonEditAction => _remote['commonEditAction'] ?? _base.commonEditAction; + + @override + String get commonEtc => _remote['commonEtc'] ?? _base.commonEtc; + + @override + String get commonFan => _remote['commonFan'] ?? _base.commonFan; + + @override + String get commonItem => _remote['commonItem'] ?? _base.commonItem; + + @override + String get commonKeepEditing => _remote['commonKeepEditing'] ?? _base.commonKeepEditing; + + @override + String get commonLoadFailed => _remote['commonLoadFailed'] ?? _base.commonLoadFailed; + + @override + String get commonMe => _remote['commonMe'] ?? _base.commonMe; + + @override + String get commonMore => _remote['commonMore'] ?? _base.commonMore; + + @override + String get commonMyPop => _remote['commonMyPop'] ?? _base.commonMyPop; + + @override + String get commonName => _remote['commonName'] ?? _base.commonName; + + @override + String get commonNameRequired => _remote['commonNameRequired'] ?? _base.commonNameRequired; + + @override + String get commonNext => _remote['commonNext'] ?? _base.commonNext; + + @override + String get commonNoHistory => _remote['commonNoHistory'] ?? _base.commonNoHistory; + + @override + String get commonNoRecentSearches => _remote['commonNoRecentSearches'] ?? _base.commonNoRecentSearches; + + @override + String get commonNoResults => _remote['commonNoResults'] ?? _base.commonNoResults; + + @override + String commonNotYet(String where) => _fill('commonNotYet', {'where': where}) ?? _base.commonNotYet(where); + + @override + String get commonPrev => _remote['commonPrev'] ?? _base.commonPrev; + + @override + String get commonRecentSearches => _remote['commonRecentSearches'] ?? _base.commonRecentSearches; + + @override + String get commonRecheck => _remote['commonRecheck'] ?? _base.commonRecheck; + + @override + String get commonRequestFailed => _remote['commonRequestFailed'] ?? _base.commonRequestFailed; + + @override + String get commonRetry => _remote['commonRetry'] ?? _base.commonRetry; + + @override + String get commonSearch => _remote['commonSearch'] ?? _base.commonSearch; + + @override + String get commonSelect => _remote['commonSelect'] ?? _base.commonSelect; + + @override + String get commonSelectDone => _remote['commonSelectDone'] ?? _base.commonSelectDone; + + @override + String get commonSend => _remote['commonSend'] ?? _base.commonSend; + + @override + String get commonShare => _remote['commonShare'] ?? _base.commonShare; + + @override + String get commonStart => _remote['commonStart'] ?? _base.commonStart; + + @override + String get commonSubmit => _remote['commonSubmit'] ?? _base.commonSubmit; + + @override + String get commonTitle => _remote['commonTitle'] ?? _base.commonTitle; + + @override + String get commonTitleHint => _remote['commonTitleHint'] ?? _base.commonTitleHint; + + @override + String get commonTryLater => _remote['commonTryLater'] ?? _base.commonTryLater; + + @override + String get commonUrl => _remote['commonUrl'] ?? _base.commonUrl; + + @override + String get commonUrlHttps => _remote['commonUrlHttps'] ?? _base.commonUrlHttps; + + @override + String get commonUrlRequired => _remote['commonUrlRequired'] ?? _base.commonUrlRequired; + + @override + String get commonView => _remote['commonView'] ?? _base.commonView; + + @override + String countryName(String code) => _base.countryName(code); + + @override + String get dayToday => _remote['dayToday'] ?? _base.dayToday; + + @override + String get dayYesterday => _remote['dayYesterday'] ?? _base.dayYesterday; + + @override + String eclipseBannerActive(String name, String rate) => _fill('eclipseBannerActive', {'name': name, 'rate': rate}) ?? _base.eclipseBannerActive(name, rate); + + @override + String eclipseBannerDefended(String name) => _fill('eclipseBannerDefended', {'name': name}) ?? _base.eclipseBannerDefended(name); + + @override + String eclipseBannerDefense(String count, String target) => _fill('eclipseBannerDefense', {'count': count, 'target': target}) ?? _base.eclipseBannerDefense(count, target); + + @override + String get eclipseCancelBody => _remote['eclipseCancelBody'] ?? _base.eclipseCancelBody; + + @override + String get eclipseCancelTitle => _remote['eclipseCancelTitle'] ?? _base.eclipseCancelTitle; + + @override + String get eclipseCapped => _remote['eclipseCapped'] ?? _base.eclipseCapped; + + @override + String get eclipseChecking => _remote['eclipseChecking'] ?? _base.eclipseChecking; + + @override + String get eclipseEnded => _remote['eclipseEnded'] ?? _base.eclipseEnded; + + @override + String get eclipseFailBody => _remote['eclipseFailBody'] ?? _base.eclipseFailBody; + + @override + String get eclipseFailTitle => _remote['eclipseFailTitle'] ?? _base.eclipseFailTitle; + + @override + String eclipseMyCount(String count) => _fill('eclipseMyCount', {'count': count}) ?? _base.eclipseMyCount(count); + + @override + String eclipseNoPopNote(String count) => _fill('eclipseNoPopNote', {'count': count}) ?? _base.eclipseNoPopNote(count); + + @override + String get eclipseNone => _remote['eclipseNone'] ?? _base.eclipseNone; + + @override + String get eclipsePendingBody => _remote['eclipsePendingBody'] ?? _base.eclipsePendingBody; + + @override + String eclipsePendingSuffix(String count) => _fill('eclipsePendingSuffix', {'count': count}) ?? _base.eclipsePendingSuffix(count); + + @override + String get eclipsePendingTitle => _remote['eclipsePendingTitle'] ?? _base.eclipsePendingTitle; + + @override + String get eclipseQualified => _remote['eclipseQualified'] ?? _base.eclipseQualified; + + @override + String get eclipseResultUnknown => _remote['eclipseResultUnknown'] ?? _base.eclipseResultUnknown; + + @override + String get eclipseRetrySame => _remote['eclipseRetrySame'] ?? _base.eclipseRetrySame; + + @override + String eclipseSeconds(String seconds) => _fill('eclipseSeconds', {'seconds': seconds}) ?? _base.eclipseSeconds(seconds); + + @override + String get eclipseSendingSuffix => _remote['eclipseSendingSuffix'] ?? _base.eclipseSendingSuffix; + + @override + String get eclipseShake => _remote['eclipseShake'] ?? _base.eclipseShake; + + @override + String get eclipseShakeTimed => _remote['eclipseShakeTimed'] ?? _base.eclipseShakeTimed; + + @override + String get eclipseSuccessTitle => _remote['eclipseSuccessTitle'] ?? _base.eclipseSuccessTitle; + + @override + String get eclipseTapToJoin => _remote['eclipseTapToJoin'] ?? _base.eclipseTapToJoin; + + @override + String get eclipseTimeLeft => _remote['eclipseTimeLeft'] ?? _base.eclipseTimeLeft; + + @override + String get eclipseTooFast => _remote['eclipseTooFast'] ?? _base.eclipseTooFast; + + @override + String get evoAnomaly => _remote['evoAnomaly'] ?? _base.evoAnomaly; + + @override + String get evoAwakening => _remote['evoAwakening'] ?? _base.evoAwakening; + + @override + String get evoComplete => _remote['evoComplete'] ?? _base.evoComplete; + + @override + String get evoEnergy => _remote['evoEnergy'] ?? _base.evoEnergy; + + @override + String get evoHeroEnd => _remote['evoHeroEnd'] ?? _base.evoHeroEnd; + + @override + String get evoOpenHistory => _remote['evoOpenHistory'] ?? _base.evoOpenHistory; + + @override + String get evoPlanetGrowth => _remote['evoPlanetGrowth'] ?? _base.evoPlanetGrowth; + + @override + String get evoTransition => _remote['evoTransition'] ?? _base.evoTransition; + + @override + String get fandomFounderLeaveBlocked => _remote['fandomFounderLeaveBlocked'] ?? _base.fandomFounderLeaveBlocked; + + @override + String get focusActive => _remote['focusActive'] ?? _base.focusActive; + + @override + String get focusClaimToday => _remote['focusClaimToday'] ?? _base.focusClaimToday; + + @override + String focusClaimed(String pop) => _fill('focusClaimed', {'pop': pop}) ?? _base.focusClaimed(pop); + + @override + String get focusClaimedToday => _remote['focusClaimedToday'] ?? _base.focusClaimedToday; + + @override + String get focusMode => _remote['focusMode'] ?? _base.focusMode; + + @override + String focusUntil(String date) => _fill('focusUntil', {'date': date}) ?? _base.focusUntil(date); + + @override + String get galaxyDefended => _remote['galaxyDefended'] ?? _base.galaxyDefended; + + @override + String galaxyErosion(String rate, String progress) => _fill('galaxyErosion', {'rate': rate, 'progress': progress}) ?? _base.galaxyErosion(rate, progress); + + @override + String get galaxyLoadFailed => _remote['galaxyLoadFailed'] ?? _base.galaxyLoadFailed; + + @override + String get galaxyMap => _remote['galaxyMap'] ?? _base.galaxyMap; + + @override + String get galaxyMapOpen => _remote['galaxyMapOpen'] ?? _base.galaxyMapOpen; + + @override + String galaxyMembers(String count) => _fill('galaxyMembers', {'count': count}) ?? _base.galaxyMembers(count); + + @override + String get galaxyMyPlanets => _remote['galaxyMyPlanets'] ?? _base.galaxyMyPlanets; + + @override + String get galaxyNoHit => _remote['galaxyNoHit'] ?? _base.galaxyNoHit; + + @override + String galaxyOnlineMembers(String online, String members) => _fill('galaxyOnlineMembers', {'online': online, 'members': members}) ?? _base.galaxyOnlineMembers(online, members); + + @override + String galaxyPlanetRank(String name, String rank) => _fill('galaxyPlanetRank', {'name': name, 'rank': rank}) ?? _base.galaxyPlanetRank(name, rank); + + @override + String get galaxySearchHint => _remote['galaxySearchHint'] ?? _base.galaxySearchHint; + + @override + String get galaxySetPrimaryConfirm => _remote['galaxySetPrimaryConfirm'] ?? _base.galaxySetPrimaryConfirm; + + @override + String get growthCheckConditions => _remote['growthCheckConditions'] ?? _base.growthCheckConditions; + + @override + String growthConditionChecking(String name) => _fill('growthConditionChecking', {'name': name}) ?? _base.growthConditionChecking(name); + + @override + String growthConditionMet(String name) => _fill('growthConditionMet', {'name': name}) ?? _base.growthConditionMet(name); + + @override + String growthConditionUnmet(String name) => _fill('growthConditionUnmet', {'name': name}) ?? _base.growthConditionUnmet(name); + + @override + String get growthFinalReached => _remote['growthFinalReached'] ?? _base.growthFinalReached; + + @override + String get growthFinalStage => _remote['growthFinalStage'] ?? _base.growthFinalStage; + + @override + String get growthFounders => _remote['growthFounders'] ?? _base.growthFounders; + + @override + String get growthNebulaGoalPassed => _remote['growthNebulaGoalPassed'] ?? _base.growthNebulaGoalPassed; + + @override + String get growthOwnerOf => _remote['growthOwnerOf'] ?? _base.growthOwnerOf; + + @override + String get growthPromotionPending => _remote['growthPromotionPending'] ?? _base.growthPromotionPending; + + @override + String get growthSteadySuffix => _remote['growthSteadySuffix'] ?? _base.growthSteadySuffix; + + @override + String heroClaims(String count) => _fill('heroClaims', {'count': count}) ?? _base.heroClaims(count); + + @override + String get heroCreateNebula => _remote['heroCreateNebula'] ?? _base.heroCreateNebula; + + @override + String get heroCreateOrJoin => _remote['heroCreateOrJoin'] ?? _base.heroCreateOrJoin; + + @override + String get heroGiftAlready => _remote['heroGiftAlready'] ?? _base.heroGiftAlready; + + @override + String get heroGiftChecking => _remote['heroGiftChecking'] ?? _base.heroGiftChecking; + + @override + String get heroGiftConfirmed => _remote['heroGiftConfirmed'] ?? _base.heroGiftConfirmed; + + @override + String get heroGiftHowTo => _remote['heroGiftHowTo'] ?? _base.heroGiftHowTo; + + @override + String get heroGiftNoPermission => _remote['heroGiftNoPermission'] ?? _base.heroGiftNoPermission; + + @override + String get heroGiftNone => _remote['heroGiftNone'] ?? _base.heroGiftNone; + + @override + String get heroGiftPopNote => _remote['heroGiftPopNote'] ?? _base.heroGiftPopNote; + + @override + String heroGiftReceived(String total) => _fill('heroGiftReceived', {'total': total}) ?? _base.heroGiftReceived(total); + + @override + String get heroGiftRelogin => _remote['heroGiftRelogin'] ?? _base.heroGiftRelogin; + + @override + String get heroGiftRetry => _remote['heroGiftRetry'] ?? _base.heroGiftRetry; + + @override + String get heroGiftTap => _remote['heroGiftTap'] ?? _base.heroGiftTap; + + @override + String get heroGiftUnavailable => _remote['heroGiftUnavailable'] ?? _base.heroGiftUnavailable; + + @override + String get heroGiftUnknown => _remote['heroGiftUnknown'] ?? _base.heroGiftUnknown; + + @override + String get heroGrowingTogether => _remote['heroGrowingTogether'] ?? _base.heroGrowingTogether; + + @override + String heroInProgress(String name) => _fill('heroInProgress', {'name': name}) ?? _base.heroInProgress(name); + + @override + String get heroJoinPlanet => _remote['heroJoinPlanet'] ?? _base.heroJoinPlanet; + + @override + String get heroNebula => _remote['heroNebula'] ?? _base.heroNebula; + + @override + String get heroNebulaPrompt => _remote['heroNebulaPrompt'] ?? _base.heroNebulaPrompt; + + @override + String get heroNebulaTap => _remote['heroNebulaTap'] ?? _base.heroNebulaTap; + + @override + String get heroOwnerSuffix => _remote['heroOwnerSuffix'] ?? _base.heroOwnerSuffix; + + @override + String get heroParticipate => _remote['heroParticipate'] ?? _base.heroParticipate; + + @override + String get heroPopProgress => _remote['heroPopProgress'] ?? _base.heroPopProgress; + + @override + String heroRecentContribution(String pop) => _fill('heroRecentContribution', {'pop': pop}) ?? _base.heroRecentContribution(pop); + + @override + String heroRemaining(String time) => _fill('heroRemaining', {'time': time}) ?? _base.heroRemaining(time); + + @override + String heroRemainingSemantics(String minutes, String seconds) => _fill('heroRemainingSemantics', {'minutes': minutes, 'seconds': seconds}) ?? _base.heroRemainingSemantics(minutes, seconds); + + @override + String get heroStartPrompt => _remote['heroStartPrompt'] ?? _base.heroStartPrompt; + + @override + String get historyAdBonus => _remote['historyAdBonus'] ?? _base.historyAdBonus; + + @override + String historyEvolution(String from, String to) => _fill('historyEvolution', {'from': from, 'to': to}) ?? _base.historyEvolution(from, to); + + @override + String get historyFandomEmpty => _remote['historyFandomEmpty'] ?? _base.historyFandomEmpty; + + @override + String get historyFunding => _remote['historyFunding'] ?? _base.historyFunding; + + @override + String historyGrowthPop(String amount) => _fill('historyGrowthPop', {'amount': amount}) ?? _base.historyGrowthPop(amount); + + @override + String get historyHibernation => _remote['historyHibernation'] ?? _base.historyHibernation; + + @override + String get historyJoined => _remote['historyJoined'] ?? _base.historyJoined; + + @override + String get historyLoadFailed => _remote['historyLoadFailed'] ?? _base.historyLoadFailed; + + @override + String get historyMine => _remote['historyMine'] ?? _base.historyMine; + + @override + String get historyMineEmpty => _remote['historyMineEmpty'] ?? _base.historyMineEmpty; + + @override + String get historyMore => _remote['historyMore'] ?? _base.historyMore; + + @override + String get historyQuest => _remote['historyQuest'] ?? _base.historyQuest; + + @override + String get historyTitle => _remote['historyTitle'] ?? _base.historyTitle; + + @override + String get historyWakeUp => _remote['historyWakeUp'] ?? _base.historyWakeUp; + + @override + String get historyZap => _remote['historyZap'] ?? _base.historyZap; + + @override + String get homeFandomHistory => _remote['homeFandomHistory'] ?? _base.homeFandomHistory; + + @override + String get homeItemsTooltip => _remote['homeItemsTooltip'] ?? _base.homeItemsTooltip; + + @override + String get homeLoadFailed => _remote['homeLoadFailed'] ?? _base.homeLoadFailed; + + @override + String get homeNotificationsTooltip => _remote['homeNotificationsTooltip'] ?? _base.homeNotificationsTooltip; + + @override + String get homePopularInside => _remote['homePopularInside'] ?? _base.homePopularInside; + + @override + String get homeRetryLater => _remote['homeRetryLater'] ?? _base.homeRetryLater; + + @override + String get homeWhereBriefing => _remote['homeWhereBriefing'] ?? _base.homeWhereBriefing; + + @override + String get homeWhereInsideDetail => _remote['homeWhereInsideDetail'] ?? _base.homeWhereInsideDetail; + + @override + String get homeWhereInsideList => _remote['homeWhereInsideList'] ?? _base.homeWhereInsideList; + + @override + String get inquiryAnswer => _remote['inquiryAnswer'] ?? _base.inquiryAnswer; + + @override + String get inquiryAnswered => _remote['inquiryAnswered'] ?? _base.inquiryAnswered; + + @override + String get inquiryBody => _remote['inquiryBody'] ?? _base.inquiryBody; + + @override + String get inquiryBodyHelper => _remote['inquiryBodyHelper'] ?? _base.inquiryBodyHelper; + + @override + String get inquiryBodyHint => _remote['inquiryBodyHint'] ?? _base.inquiryBodyHint; + + @override + String get inquiryDetail => _remote['inquiryDetail'] ?? _base.inquiryDetail; + + @override + String get inquiryEdit => _remote['inquiryEdit'] ?? _base.inquiryEdit; + + @override + String inquiryEditFailed(String reason) => _fill('inquiryEditFailed', {'reason': reason}) ?? _base.inquiryEditFailed(reason); + + @override + String get inquiryEdited => _remote['inquiryEdited'] ?? _base.inquiryEdited; + + @override + String get inquiryEmpty => _remote['inquiryEmpty'] ?? _base.inquiryEmpty; + + @override + String get inquiryFallback => _remote['inquiryFallback'] ?? _base.inquiryFallback; + + @override + String get inquiryInProgress => _remote['inquiryInProgress'] ?? _base.inquiryInProgress; + + @override + String get inquiryListTitle => _remote['inquiryListTitle'] ?? _base.inquiryListTitle; + + @override + String get inquiryNew => _remote['inquiryNew'] ?? _base.inquiryNew; + + @override + String get inquiryPending => _remote['inquiryPending'] ?? _base.inquiryPending; + + @override + String inquirySubmitFailed(String reason) => _fill('inquirySubmitFailed', {'reason': reason}) ?? _base.inquirySubmitFailed(reason); + + @override + String get inquirySubmitted => _remote['inquirySubmitted'] ?? _base.inquirySubmitted; + + @override + String get inquiryWrite => _remote['inquiryWrite'] ?? _base.inquiryWrite; + + @override + String get insideAccept => _remote['insideAccept'] ?? _base.insideAccept; + + @override + String get insideAcceptAction => _remote['insideAcceptAction'] ?? _base.insideAcceptAction; + + @override + String insideAcceptConfirm(String pop) => _fill('insideAcceptConfirm', {'pop': pop}) ?? _base.insideAcceptConfirm(pop); + + @override + String get insideAcceptDone => _remote['insideAcceptDone'] ?? _base.insideAcceptDone; + + @override + String get insideAnswerHint => _remote['insideAnswerHint'] ?? _base.insideAnswerHint; + + @override + String get insideAnswerLater => _remote['insideAnswerLater'] ?? _base.insideAnswerLater; + + @override + String insideAnswers(String count) => _fill('insideAnswers', {'count': count}) ?? _base.insideAnswers(count); + + @override + String get insideAttachLater => _remote['insideAttachLater'] ?? _base.insideAttachLater; + + @override + String get insideBodyHint => _remote['insideBodyHint'] ?? _base.insideBodyHint; + + @override + String get insideBodyRequired => _remote['insideBodyRequired'] ?? _base.insideBodyRequired; + + @override + String insideBountyHint(String min) => _fill('insideBountyHint', {'min': min}) ?? _base.insideBountyHint(min); + + @override + String insideBountyMin(String min) => _fill('insideBountyMin', {'min': min}) ?? _base.insideBountyMin(min); + + @override + String get insideBountyOverBalance => _remote['insideBountyOverBalance'] ?? _base.insideBountyOverBalance; + + @override + String get insideBountyRequired => _remote['insideBountyRequired'] ?? _base.insideBountyRequired; + + @override + String get insideBountySet => _remote['insideBountySet'] ?? _base.insideBountySet; + + @override + String get insideEmpty => _remote['insideEmpty'] ?? _base.insideEmpty; + + @override + String get insideErrAlreadyAccepted => _remote['insideErrAlreadyAccepted'] ?? _base.insideErrAlreadyAccepted; + + @override + String get insideErrNotOwner => _remote['insideErrNotOwner'] ?? _base.insideErrNotOwner; + + @override + String get insideErrPopShort => _remote['insideErrPopShort'] ?? _base.insideErrPopShort; + + @override + String get insideErrSelfAccept => _remote['insideErrSelfAccept'] ?? _base.insideErrSelfAccept; + + @override + String get insideFirstAnswer => _remote['insideFirstAnswer'] ?? _base.insideFirstAnswer; + + @override + String get insideLoadFailed => _remote['insideLoadFailed'] ?? _base.insideLoadFailed; + + @override + String get insideMore => _remote['insideMore'] ?? _base.insideMore; + + @override + String get insideNumberOnly => _remote['insideNumberOnly'] ?? _base.insideNumberOnly; + + @override + String get insideRefundChecking => _remote['insideRefundChecking'] ?? _base.insideRefundChecking; + + @override + String get insideRefundDone => _remote['insideRefundDone'] ?? _base.insideRefundDone; + + @override + String get insideRefundPending => _remote['insideRefundPending'] ?? _base.insideRefundPending; + + @override + String get insideSearchHint => _remote['insideSearchHint'] ?? _base.insideSearchHint; + + @override + String get insideTitleRequired => _remote['insideTitleRequired'] ?? _base.insideTitleRequired; + + @override + String get insideWrite => _remote['insideWrite'] ?? _base.insideWrite; + + @override + String get itemBuy => _remote['itemBuy'] ?? _base.itemBuy; + + @override + String get itemBuyNotReady => _remote['itemBuyNotReady'] ?? _base.itemBuyNotReady; + + @override + String itemBuyerPop(String pop) => _fill('itemBuyerPop', {'pop': pop}) ?? _base.itemBuyerPop(pop); + + @override + String get itemJoinFirst => _remote['itemJoinFirst'] ?? _base.itemJoinFirst; + + @override + String itemOwned(String count) => _fill('itemOwned', {'count': count}) ?? _base.itemOwned(count); + + @override + String itemOwnedCount(String count) => _fill('itemOwnedCount', {'count': count}) ?? _base.itemOwnedCount(count); + + @override + String get itemSelect => _remote['itemSelect'] ?? _base.itemSelect; + + @override + String itemSendPop(String pop) => _fill('itemSendPop', {'pop': pop}) ?? _base.itemSendPop(pop); + + @override + String get itemUseNotReady => _remote['itemUseNotReady'] ?? _base.itemUseNotReady; + + @override + String get languageSettingsTitle => _remote['languageSettingsTitle'] ?? _base.languageSettingsTitle; + + @override + String get like => _remote['like'] ?? _base.like; + + @override + String get likeCancel => _remote['likeCancel'] ?? _base.likeCancel; + + @override + String get linkLink => _remote['linkLink'] ?? _base.linkLink; + + @override + String get linkLinked => _remote['linkLinked'] ?? _base.linkLinked; + + @override + String get linkNotLinked => _remote['linkNotLinked'] ?? _base.linkNotLinked; + + @override + String get linkSameEmailOnly => _remote['linkSameEmailOnly'] ?? _base.linkSameEmailOnly; + + @override + String linkTitle(String name) => _fill('linkTitle', {'name': name}) ?? _base.linkTitle(name); + + @override + String get linkUnlink => _remote['linkUnlink'] ?? _base.linkUnlink; + + @override + String liveCampaign(String name) => _fill('liveCampaign', {'name': name}) ?? _base.liveCampaign(name); + + @override + String liveEclipseDefense(String name, String count) => _fill('liveEclipseDefense', {'name': name, 'count': count}) ?? _base.liveEclipseDefense(name, count); + + @override + String liveHeroGift(String name) => _fill('liveHeroGift', {'name': name}) ?? _base.liveHeroGift(name); + + @override + String liveHotTime(String name) => _fill('liveHotTime', {'name': name}) ?? _base.liveHotTime(name); + + @override + String get loungeFailed => _remote['loungeFailed'] ?? _base.loungeFailed; + + @override + String get loungeHint => _remote['loungeHint'] ?? _base.loungeHint; + + @override + String loungeMaxPhotos(String count) => _fill('loungeMaxPhotos', {'count': count}) ?? _base.loungeMaxPhotos(count); + + @override + String get loungeMembersOnly => _remote['loungeMembersOnly'] ?? _base.loungeMembersOnly; + + @override + String get loungeTitle => _remote['loungeTitle'] ?? _base.loungeTitle; + + @override + String get myCampaign => _remote['myCampaign'] ?? _base.myCampaign; + + @override + String get myEditPhoto => _remote['myEditPhoto'] ?? _base.myEditPhoto; + + @override + String get myEditProfile => _remote['myEditProfile'] ?? _base.myEditProfile; + + @override + String get myInside => _remote['myInside'] ?? _base.myInside; + + @override + String get myListCampaign => _remote['myListCampaign'] ?? _base.myListCampaign; + + @override + String get myListCampaignEmpty => _remote['myListCampaignEmpty'] ?? _base.myListCampaignEmpty; + + @override + String get myListInside => _remote['myListInside'] ?? _base.myListInside; + + @override + String get myListInsideEmpty => _remote['myListInsideEmpty'] ?? _base.myListInsideEmpty; + + @override + String get myPop => _remote['myPop'] ?? _base.myPop; + + @override + String get myProfileCardTooltip => _remote['myProfileCardTooltip'] ?? _base.myProfileCardTooltip; + + @override + String myShareLater(String target) => _fill('myShareLater', {'target': target}) ?? _base.myShareLater(target); + + @override + String get myTabActivity => _remote['myTabActivity'] ?? _base.myTabActivity; + + @override + String get myTabPlanets => _remote['myTabPlanets'] ?? _base.myTabPlanets; + + @override + String get nebulaAlreadyToday => _remote['nebulaAlreadyToday'] ?? _base.nebulaAlreadyToday; + + @override + String nebulaBody(String days) => _fill('nebulaBody', {'days': days}) ?? _base.nebulaBody(days); + + @override + String get nebulaCreate => _remote['nebulaCreate'] ?? _base.nebulaCreate; + + @override + String get nebulaCreateFailed => _remote['nebulaCreateFailed'] ?? _base.nebulaCreateFailed; + + @override + String get nebulaCreatedKeepPrimary => _remote['nebulaCreatedKeepPrimary'] ?? _base.nebulaCreatedKeepPrimary; + + @override + String get nebulaFounders => _remote['nebulaFounders'] ?? _base.nebulaFounders; + + @override + String get nebulaGoal => _remote['nebulaGoal'] ?? _base.nebulaGoal; + + @override + String nebulaGoalSemantics(String pop, String members, String days) => _fill('nebulaGoalSemantics', {'pop': pop, 'members': members, 'days': days}) ?? _base.nebulaGoalSemantics(pop, members, days); + + @override + String get nebulaHeadline => _remote['nebulaHeadline'] ?? _base.nebulaHeadline; + + @override + String get nebulaNameHint => _remote['nebulaNameHint'] ?? _base.nebulaNameHint; + + @override + String get nebulaNameTaken => _remote['nebulaNameTaken'] ?? _base.nebulaNameTaken; + + @override + String get nicknameEdit => _remote['nicknameEdit'] ?? _base.nicknameEdit; + + @override + String get nicknameReserved => _remote['nicknameReserved'] ?? _base.nicknameReserved; + + @override + String get nicknameTaken => _remote['nicknameTaken'] ?? _base.nicknameTaken; + + @override + String get noticeBadge => _remote['noticeBadge'] ?? _base.noticeBadge; + + @override + String get noticeDetailTitle => _remote['noticeDetailTitle'] ?? _base.noticeDetailTitle; + + @override + String get noticeEmpty => _remote['noticeEmpty'] ?? _base.noticeEmpty; + + @override + String get noticeTitle => _remote['noticeTitle'] ?? _base.noticeTitle; + + @override + String get notifEmpty => _remote['notifEmpty'] ?? _base.notifEmpty; + + @override + String notifGoTo(String target) => _fill('notifGoTo', {'target': target}) ?? _base.notifGoTo(target); + + @override + String get notifKindCampaign => _remote['notifKindCampaign'] ?? _base.notifKindCampaign; + + @override + String get notifKindComment => _remote['notifKindComment'] ?? _base.notifKindComment; + + @override + String get notifKindEclipse => _remote['notifKindEclipse'] ?? _base.notifKindEclipse; + + @override + String get notifKindItemUsed => _remote['notifKindItemUsed'] ?? _base.notifKindItemUsed; + + @override + String get notifKindLike => _remote['notifKindLike'] ?? _base.notifKindLike; + + @override + String get notifKindQuest => _remote['notifKindQuest'] ?? _base.notifKindQuest; + + @override + String get notifTargetCampaign => _remote['notifTargetCampaign'] ?? _base.notifTargetCampaign; + + @override + String get notifTargetPost => _remote['notifTargetPost'] ?? _base.notifTargetPost; + + @override + String get notifTargetQuest => _remote['notifTargetQuest'] ?? _base.notifTargetQuest; + + @override + String get openSettings => _remote['openSettings'] ?? _base.openSettings; + + @override + String get payAmount => _remote['payAmount'] ?? _base.payAmount; + + @override + String get payDate => _remote['payDate'] ?? _base.payDate; + + @override + String get payDetailTitle => _remote['payDetailTitle'] ?? _base.payDetailTitle; + + @override + String get payEmpty => _remote['payEmpty'] ?? _base.payEmpty; + + @override + String get payItemFallback => _remote['payItemFallback'] ?? _base.payItemFallback; + + @override + String get payMethod => _remote['payMethod'] ?? _base.payMethod; + + @override + String get payMethodInApp => _remote['payMethodInApp'] ?? _base.payMethodInApp; + + @override + String get payNotFound => _remote['payNotFound'] ?? _base.payNotFound; + + @override + String get payNumber => _remote['payNumber'] ?? _base.payNumber; + + @override + String get payOpenFailed => _remote['payOpenFailed'] ?? _base.payOpenFailed; + + @override + String get payRefundApply => _remote['payRefundApply'] ?? _base.payRefundApply; + + @override + String get payRefundGuide => _remote['payRefundGuide'] ?? _base.payRefundGuide; + + @override + String get payRefundInfo => _remote['payRefundInfo'] ?? _base.payRefundInfo; + + @override + String get payRefundUnavailable => _remote['payRefundUnavailable'] ?? _base.payRefundUnavailable; + + @override + String get payRestore => _remote['payRestore'] ?? _base.payRestore; + + @override + String get payStatePaid => _remote['payStatePaid'] ?? _base.payStatePaid; + + @override + String get payStateRefundApproved => _remote['payStateRefundApproved'] ?? _base.payStateRefundApproved; + + @override + String get payStateRefundRejected => _remote['payStateRefundRejected'] ?? _base.payStateRefundRejected; + + @override + String get payStateRefundRequested => _remote['payStateRefundRequested'] ?? _base.payStateRefundRequested; + + @override + String get payStateRefunded => _remote['payStateRefunded'] ?? _base.payStateRefunded; + + @override + String get payStatus => _remote['payStatus'] ?? _base.payStatus; + + @override + String get permLocationDenied => _remote['permLocationDenied'] ?? _base.permLocationDenied; + + @override + String get permLocationNeeded => _remote['permLocationNeeded'] ?? _base.permLocationNeeded; + + @override + String get permLocationOff => _remote['permLocationOff'] ?? _base.permLocationOff; + + @override + String get permStepsDenied => _remote['permStepsDenied'] ?? _base.permStepsDenied; + + @override + String get permStepsNeeded => _remote['permStepsNeeded'] ?? _base.permStepsNeeded; + + @override + String get photoBadType => _remote['photoBadType'] ?? _base.photoBadType; + + @override + String get photoRejectedServer => _remote['photoRejectedServer'] ?? _base.photoRejectedServer; + + @override + String photoRemove(String n) => _fill('photoRemove', {'n': n}) ?? _base.photoRemove(n); + + @override + String get photoTooBig => _remote['photoTooBig'] ?? _base.photoTooBig; + + @override + String get photoUnreadable => _remote['photoUnreadable'] ?? _base.photoUnreadable; + + @override + String get photoUploadFailed => _remote['photoUploadFailed'] ?? _base.photoUploadFailed; + + @override + String get pickFromAlbum => _remote['pickFromAlbum'] ?? _base.pickFromAlbum; + + @override + String get placePickPrompt => _remote['placePickPrompt'] ?? _base.placePickPrompt; + + @override + String get placeSearch => _remote['placeSearch'] ?? _base.placeSearch; + + @override + String get planetBgColor => _remote['planetBgColor'] ?? _base.planetBgColor; + + @override + String get planetColorSelect => _remote['planetColorSelect'] ?? _base.planetColorSelect; + + @override + String get planetContributeCta => _remote['planetContributeCta'] ?? _base.planetContributeCta; + + @override + String get planetContribution => _remote['planetContribution'] ?? _base.planetContribution; + + @override + String get planetCreateConfirm => _remote['planetCreateConfirm'] ?? _base.planetCreateConfirm; + + @override + String get planetCreateFailed => _remote['planetCreateFailed'] ?? _base.planetCreateFailed; + + @override + String get planetEclipseSuffix => _remote['planetEclipseSuffix'] ?? _base.planetEclipseSuffix; + + @override + String get planetHeroSuffix => _remote['planetHeroSuffix'] ?? _base.planetHeroSuffix; + + @override + String get planetHibernatingSuffix => _remote['planetHibernatingSuffix'] ?? _base.planetHibernatingSuffix; + + @override + String get planetHideMember => _remote['planetHideMember'] ?? _base.planetHideMember; + + @override + String get planetJoin => _remote['planetJoin'] ?? _base.planetJoin; + + @override + String get planetJoined => _remote['planetJoined'] ?? _base.planetJoined; + + @override + String planetLastUpdated(String time) => _fill('planetLastUpdated', {'time': time}) ?? _base.planetLastUpdated(time); + + @override + String get planetLiveRanking => _remote['planetLiveRanking'] ?? _base.planetLiveRanking; + + @override + String get planetMyContribution => _remote['planetMyContribution'] ?? _base.planetMyContribution; + + @override + String planetNamed(String name) => _fill('planetNamed', {'name': name}) ?? _base.planetNamed(name); + + @override + String get planetNebulaGrowth => _remote['planetNebulaGrowth'] ?? _base.planetNebulaGrowth; + + @override + String get planetNoContributors => _remote['planetNoContributors'] ?? _base.planetNoContributors; + + @override + String planetOverallRank(String rank) => _fill('planetOverallRank', {'rank': rank}) ?? _base.planetOverallRank(rank); + + @override + String get planetPassportHistory => _remote['planetPassportHistory'] ?? _base.planetPassportHistory; + + @override + String planetPerHead(String value) => _fill('planetPerHead', {'value': value}) ?? _base.planetPerHead(value); + + @override + String planetShape(String n) => _fill('planetShape', {'n': n}) ?? _base.planetShape(n); + + @override + String get planetSizeMedium => _remote['planetSizeMedium'] ?? _base.planetSizeMedium; + + @override + String get planetStageUnknown => _remote['planetStageUnknown'] ?? _base.planetStageUnknown; + + @override + String get planetTallying => _remote['planetTallying'] ?? _base.planetTallying; + + @override + String planetTopPercent(String percent) => _fill('planetTopPercent', {'percent': percent}) ?? _base.planetTopPercent(percent); + + @override + String get planetTypeSelect => _remote['planetTypeSelect'] ?? _base.planetTypeSelect; + + @override + String get playBody => _remote['playBody'] ?? _base.playBody; + + @override + String get playHeadline => _remote['playHeadline'] ?? _base.playHeadline; + + @override + String get playNow => _remote['playNow'] ?? _base.playNow; + + @override + String get popKindContribution => _remote['popKindContribution'] ?? _base.popKindContribution; + + @override + String get popKindGeneral => _remote['popKindGeneral'] ?? _base.popKindGeneral; + + @override + String get popReasonAdReward => _remote['popReasonAdReward'] ?? _base.popReasonAdReward; + + @override + String get popReasonAdminClawback => _remote['popReasonAdminClawback'] ?? _base.popReasonAdminClawback; + + @override + String get popReasonAdminGrant => _remote['popReasonAdminGrant'] ?? _base.popReasonAdminGrant; + + @override + String get popReasonCampaignFee => _remote['popReasonCampaignFee'] ?? _base.popReasonCampaignFee; + + @override + String get popReasonCampaignFeeRefund => _remote['popReasonCampaignFeeRefund'] ?? _base.popReasonCampaignFeeRefund; + + @override + String get popReasonCampaignFund => _remote['popReasonCampaignFund'] ?? _base.popReasonCampaignFund; + + @override + String get popReasonCampaignRefund => _remote['popReasonCampaignRefund'] ?? _base.popReasonCampaignRefund; + + @override + String get popReasonEclipseDefense => _remote['popReasonEclipseDefense'] ?? _base.popReasonEclipseDefense; + + @override + String get popReasonFocusDaily => _remote['popReasonFocusDaily'] ?? _base.popReasonFocusDaily; + + @override + String get popReasonHeroDrop => _remote['popReasonHeroDrop'] ?? _base.popReasonHeroDrop; + + @override + String get popReasonHeroDropTrigger => _remote['popReasonHeroDropTrigger'] ?? _base.popReasonHeroDropTrigger; + + @override + String get popReasonIapPurchase => _remote['popReasonIapPurchase'] ?? _base.popReasonIapPurchase; + + @override + String get popReasonOther => _remote['popReasonOther'] ?? _base.popReasonOther; + + @override + String get popReasonPreregBonus => _remote['popReasonPreregBonus'] ?? _base.popReasonPreregBonus; + + @override + String get popReasonQnaBounty => _remote['popReasonQnaBounty'] ?? _base.popReasonQnaBounty; + + @override + String get popReasonQnaRefund => _remote['popReasonQnaRefund'] ?? _base.popReasonQnaRefund; + + @override + String get popReasonQnaReward => _remote['popReasonQnaReward'] ?? _base.popReasonQnaReward; + + @override + String get popReasonQuestAttend => _remote['popReasonQuestAttend'] ?? _base.popReasonQuestAttend; + + @override + String get popReasonQuestFee => _remote['popReasonQuestFee'] ?? _base.popReasonQuestFee; + + @override + String get popReasonQuestMusicShake => _remote['popReasonQuestMusicShake'] ?? _base.popReasonQuestMusicShake; + + @override + String get popReasonQuestPlace => _remote['popReasonQuestPlace'] ?? _base.popReasonQuestPlace; + + @override + String get popReasonQuestPoolDeposit => _remote['popReasonQuestPoolDeposit'] ?? _base.popReasonQuestPoolDeposit; + + @override + String get popReasonQuestPoolReturn => _remote['popReasonQuestPoolReturn'] ?? _base.popReasonQuestPoolReturn; + + @override + String get popReasonQuestSteps => _remote['popReasonQuestSteps'] ?? _base.popReasonQuestSteps; + + @override + String get popReasonQuestTimeAttack => _remote['popReasonQuestTimeAttack'] ?? _base.popReasonQuestTimeAttack; + + @override + String get popReasonQuestUgc => _remote['popReasonQuestUgc'] ?? _base.popReasonQuestUgc; + + @override + String get popReasonReferral => _remote['popReasonReferral'] ?? _base.popReasonReferral; + + @override + String get popReasonTranslation => _remote['popReasonTranslation'] ?? _base.popReasonTranslation; + + @override + String popRepeat(String title, String count) => _fill('popRepeat', {'title': title, 'count': count}) ?? _base.popRepeat(title, count); + + @override + String get popTxGrant => _remote['popTxGrant'] ?? _base.popTxGrant; + + @override + String get popTxRefund => _remote['popTxRefund'] ?? _base.popTxRefund; + + @override + String get popTxSpend => _remote['popTxSpend'] ?? _base.popTxSpend; + + @override + String get popTxVoid => _remote['popTxVoid'] ?? _base.popTxVoid; + + @override + String get popTypeAdmin => _remote['popTypeAdmin'] ?? _base.popTypeAdmin; + + @override + String get popTypeFee => _remote['popTypeFee'] ?? _base.popTypeFee; + + @override + String get popTypePayment => _remote['popTypePayment'] ?? _base.popTypePayment; + + @override + String get popTypeQuest => _remote['popTypeQuest'] ?? _base.popTypeQuest; + + @override + String get popTypeReferral => _remote['popTypeReferral'] ?? _base.popTypeReferral; + + @override + String get popTypeReward => _remote['popTypeReward'] ?? _base.popTypeReward; + + @override + String get popTypeSubscription => _remote['popTypeSubscription'] ?? _base.popTypeSubscription; + + @override + String get postDeleteConfirm => _remote['postDeleteConfirm'] ?? _base.postDeleteConfirm; + + @override + String get postEdit => _remote['postEdit'] ?? _base.postEdit; + + @override + String get postWrite => _remote['postWrite'] ?? _base.postWrite; + + @override + String pricePerMonth(String price) => _fill('pricePerMonth', {'price': price}) ?? _base.pricePerMonth(price); + + @override + String get pushDeviceOff => _remote['pushDeviceOff'] ?? _base.pushDeviceOff; + + @override + String get pushDeviceOffHint => _remote['pushDeviceOffHint'] ?? _base.pushDeviceOffHint; + + @override + String get pushDeviceSection => _remote['pushDeviceSection'] ?? _base.pushDeviceSection; + + @override + String get pushEnableInDevice => _remote['pushEnableInDevice'] ?? _base.pushEnableInDevice; + + @override + String get pushGoToSettings => _remote['pushGoToSettings'] ?? _base.pushGoToSettings; + + @override + String get pushOpeningDeviceSettings => _remote['pushOpeningDeviceSettings'] ?? _base.pushOpeningDeviceSettings; + + @override + String get pushRequesting => _remote['pushRequesting'] ?? _base.pushRequesting; + + @override + String get pushServiceSection => _remote['pushServiceSection'] ?? _base.pushServiceSection; + + @override + String get pushSoundVibration => _remote['pushSoundVibration'] ?? _base.pushSoundVibration; + + @override + String get pushTopicBriefing => _remote['pushTopicBriefing'] ?? _base.pushTopicBriefing; + + @override + String get pushTopicBriefingDesc => _remote['pushTopicBriefingDesc'] ?? _base.pushTopicBriefingDesc; + + @override + String get pushTopicCommunity => _remote['pushTopicCommunity'] ?? _base.pushTopicCommunity; + + @override + String get pushTopicCommunityDesc => _remote['pushTopicCommunityDesc'] ?? _base.pushTopicCommunityDesc; + + @override + String get pushTopicEssential => _remote['pushTopicEssential'] ?? _base.pushTopicEssential; + + @override + String get pushTopicEssentialDesc => _remote['pushTopicEssentialDesc'] ?? _base.pushTopicEssentialDesc; + + @override + String get pushTopicFandomEvent => _remote['pushTopicFandomEvent'] ?? _base.pushTopicFandomEvent; + + @override + String get pushTopicFandomEventDesc => _remote['pushTopicFandomEventDesc'] ?? _base.pushTopicFandomEventDesc; + + @override + String get pushTopicMarketing => _remote['pushTopicMarketing'] ?? _base.pushTopicMarketing; + + @override + String get pushTopicMarketingDesc => _remote['pushTopicMarketingDesc'] ?? _base.pushTopicMarketingDesc; + + @override + String get questActShake => _remote['questActShake'] ?? _base.questActShake; + + @override + String get questAction => _remote['questAction'] ?? _base.questAction; + + @override + String get questActivity => _remote['questActivity'] ?? _base.questActivity; + + @override + String questAdBonus(String pop) => _fill('questAdBonus', {'pop': pop}) ?? _base.questAdBonus(pop); + + @override + String get questAllowResubmit => _remote['questAllowResubmit'] ?? _base.questAllowResubmit; + + @override + String questAlreadyRewarded(String pop) => _fill('questAlreadyRewarded', {'pop': pop}) ?? _base.questAlreadyRewarded(pop); + + @override + String get questApprove => _remote['questApprove'] ?? _base.questApprove; + + @override + String get questApprovedPaying => _remote['questApprovedPaying'] ?? _base.questApprovedPaying; + + @override + String get questAttended => _remote['questAttended'] ?? _base.questAttended; + + @override + String get questAuthorNote => _remote['questAuthorNote'] ?? _base.questAuthorNote; + + @override + String get questCatCommunity => _remote['questCatCommunity'] ?? _base.questCatCommunity; + + @override + String get questCatHealth => _remote['questCatHealth'] ?? _base.questCatHealth; + + @override + String get questCatPlanet => _remote['questCatPlanet'] ?? _base.questCatPlanet; + + @override + String get questCategory => _remote['questCategory'] ?? _base.questCategory; + + @override + String get questClaim => _remote['questClaim'] ?? _base.questClaim; + + @override + String get questCreate => _remote['questCreate'] ?? _base.questCreate; + + @override + String get questCreateConfirm => _remote['questCreateConfirm'] ?? _base.questCreateConfirm; + + @override + String questCreateCost(String title, String reward, String count, String pool, String fee, String total) => _fill('questCreateCost', {'title': title, 'reward': reward, 'count': count, 'pool': pool, 'fee': fee, 'total': total}) ?? _base.questCreateCost(title, reward, count, pool, fee, total); + + @override + String get questCreateNotReady => _remote['questCreateNotReady'] ?? _base.questCreateNotReady; + + @override + String questCreateSummary(String title, String goal, String reward) => _fill('questCreateSummary', {'title': title, 'goal': goal, 'reward': reward}) ?? _base.questCreateSummary(title, goal, reward); + + @override + String get questDailyHelp => _remote['questDailyHelp'] ?? _base.questDailyHelp; + + @override + String get questDeadline => _remote['questDeadline'] ?? _base.questDeadline; + + @override + String get questDescriptionOptional => _remote['questDescriptionOptional'] ?? _base.questDescriptionOptional; + + @override + String get questDetails => _remote['questDetails'] ?? _base.questDetails; + + @override + String get questEdit => _remote['questEdit'] ?? _base.questEdit; + + @override + String get questEditNotice => _remote['questEditNotice'] ?? _base.questEditNotice; + + @override + String get questEndDate => _remote['questEndDate'] ?? _base.questEndDate; + + @override + String questEndDateValue(String date) => _fill('questEndDateValue', {'date': date}) ?? _base.questEndDateValue(date); + + @override + String get questEndDefault => _remote['questEndDefault'] ?? _base.questEndDefault; + + @override + String get questEnded => _remote['questEnded'] ?? _base.questEnded; + + @override + String get questEvidenceText => _remote['questEvidenceText'] ?? _base.questEvidenceText; + + @override + String get questEvidenceUrl => _remote['questEvidenceUrl'] ?? _base.questEvidenceUrl; + + @override + String questFreeDailyCap(String cap) => _fill('questFreeDailyCap', {'cap': cap}) ?? _base.questFreeDailyCap(cap); + + @override + String get questGoal => _remote['questGoal'] ?? _base.questGoal; + + @override + String get questGoalValue => _remote['questGoalValue'] ?? _base.questGoalValue; + + @override + String get questInProgress => _remote['questInProgress'] ?? _base.questInProgress; + + @override + String get questInProgressSpaced => _remote['questInProgressSpaced'] ?? _base.questInProgressSpaced; + + @override + String get questLinkCheck => _remote['questLinkCheck'] ?? _base.questLinkCheck; + + @override + String get questLinkInvalid => _remote['questLinkInvalid'] ?? _base.questLinkInvalid; + + @override + String get questLinkPreviewFailed => _remote['questLinkPreviewFailed'] ?? _base.questLinkPreviewFailed; + + @override + String get questLinkTitle => _remote['questLinkTitle'] ?? _base.questLinkTitle; + + @override + String get questLinkUrl => _remote['questLinkUrl'] ?? _base.questLinkUrl; + + @override + String questMine(String count) => _fill('questMine', {'count': count}) ?? _base.questMine(count); + + @override + String get questName => _remote['questName'] ?? _base.questName; + + @override + String questNamed(String title) => _fill('questNamed', {'title': title}) ?? _base.questNamed(title); + + @override + String get questNew => _remote['questNew'] ?? _base.questNew; + + @override + String get questNoBaseReward => _remote['questNoBaseReward'] ?? _base.questNoBaseReward; + + @override + String get questNoCategory => _remote['questNoCategory'] ?? _base.questNoCategory; + + @override + String get questNoCreatable => _remote['questNoCreatable'] ?? _base.questNoCreatable; + + @override + String get questNoPlace => _remote['questNoPlace'] ?? _base.questNoPlace; + + @override + String get questNoReward => _remote['questNoReward'] ?? _base.questNoReward; + + @override + String get questNoSteps => _remote['questNoSteps'] ?? _base.questNoSteps; + + @override + String get questParticipants => _remote['questParticipants'] ?? _base.questParticipants; + + @override + String questParticipantsCap(String count, String cap) => _fill('questParticipantsCap', {'count': count, 'cap': cap}) ?? _base.questParticipantsCap(count, cap); + + @override + String questParticipantsCount(String count) => _fill('questParticipantsCount', {'count': count}) ?? _base.questParticipantsCount(count); + + @override + String get questPickPlace => _remote['questPickPlace'] ?? _base.questPickPlace; + + @override + String questPoolHelper(String pool, String fee) => _fill('questPoolHelper', {'pool': pool, 'fee': fee}) ?? _base.questPoolHelper(pool, fee); + + @override + String get questPopGrant => _remote['questPopGrant'] ?? _base.questPopGrant; + + @override + String get questPreviewHint => _remote['questPreviewHint'] ?? _base.questPreviewHint; + + @override + String get questReject => _remote['questReject'] ?? _base.questReject; + + @override + String get questRejectReason => _remote['questRejectReason'] ?? _base.questRejectReason; + + @override + String questRejected(String reason) => _fill('questRejected', {'reason': reason}) ?? _base.questRejected(reason); + + @override + String get questRejectedButton => _remote['questRejectedButton'] ?? _base.questRejectedButton; + + @override + String questRemainingHours(String hours) => _fill('questRemainingHours', {'hours': hours}) ?? _base.questRemainingHours(hours); + + @override + String questRepeatEvery(String hours, String count) => _fill('questRepeatEvery', {'hours': hours, 'count': count}) ?? _base.questRepeatEvery(hours, count); + + @override + String get questRepeatOnce => _remote['questRepeatOnce'] ?? _base.questRepeatOnce; + + @override + String get questResubmit => _remote['questResubmit'] ?? _base.questResubmit; + + @override + String get questResubmitAllowed => _remote['questResubmitAllowed'] ?? _base.questResubmitAllowed; + + @override + String get questResubmitNotAllowed => _remote['questResubmitNotAllowed'] ?? _base.questResubmitNotAllowed; + + @override + String get questReviewButton => _remote['questReviewButton'] ?? _base.questReviewButton; + + @override + String get questReviewDelayed => _remote['questReviewDelayed'] ?? _base.questReviewDelayed; + + @override + String get questReviewPending => _remote['questReviewPending'] ?? _base.questReviewPending; + + @override + String questReviewQueue(String count) => _fill('questReviewQueue', {'count': count}) ?? _base.questReviewQueue(count); + + @override + String get questReward => _remote['questReward'] ?? _base.questReward; + + @override + String get questRewardDone => _remote['questRewardDone'] ?? _base.questRewardDone; + + @override + String get questRewardPaid => _remote['questRewardPaid'] ?? _base.questRewardPaid; + + @override + String get questRewardPop => _remote['questRewardPop'] ?? _base.questRewardPop; + + @override + String get questRewardReceived => _remote['questRewardReceived'] ?? _base.questRewardReceived; + + @override + String get questStart => _remote['questStart'] ?? _base.questStart; + + @override + String get questStop => _remote['questStop'] ?? _base.questStop; + + @override + String get questStopBody => _remote['questStopBody'] ?? _base.questStopBody; + + @override + String get questStopConfirm => _remote['questStopConfirm'] ?? _base.questStopConfirm; + + @override + String get questSubmit => _remote['questSubmit'] ?? _base.questSubmit; + + @override + String get questSubmitted => _remote['questSubmitted'] ?? _base.questSubmitted; + + @override + String get questTabEmpty => _remote['questTabEmpty'] ?? _base.questTabEmpty; + + @override + String get questTabMine => _remote['questTabMine'] ?? _base.questTabMine; + + @override + String get questTimeAttackClosed => _remote['questTimeAttackClosed'] ?? _base.questTimeAttackClosed; + + @override + String questTimeAttackLeft(String time) => _fill('questTimeAttackLeft', {'time': time}) ?? _base.questTimeAttackLeft(time); + + @override + String get questTimeLeft => _remote['questTimeLeft'] ?? _base.questTimeLeft; + + @override + String get questTodayMust => _remote['questTodayMust'] ?? _base.questTodayMust; + + @override + String get questTypeAttend => _remote['questTypeAttend'] ?? _base.questTypeAttend; + + @override + String get questTypePlace => _remote['questTypePlace'] ?? _base.questTypePlace; + + @override + String get questTypeSns => _remote['questTypeSns'] ?? _base.questTypeSns; + + @override + String get questTypeSteps => _remote['questTypeSteps'] ?? _base.questTypeSteps; + + @override + String get questTypeStreaming => _remote['questTypeStreaming'] ?? _base.questTypeStreaming; + + @override + String get questTypeTimeAttack => _remote['questTypeTimeAttack'] ?? _base.questTypeTimeAttack; + + @override + String get questTypeUnverifiable => _remote['questTypeUnverifiable'] ?? _base.questTypeUnverifiable; + + @override + String get questTypeVote => _remote['questTypeVote'] ?? _base.questTypeVote; + + @override + String get rankEmpty => _remote['rankEmpty'] ?? _base.rankEmpty; + + @override + String get rankLoadFailed => _remote['rankLoadFailed'] ?? _base.rankLoadFailed; + + @override + String rankMeSuffix(String name) => _fill('rankMeSuffix', {'name': name}) ?? _base.rankMeSuffix(name); + + @override + String rankMine(String metric) => _fill('rankMine', {'metric': metric}) ?? _base.rankMine(metric); + + @override + String rankMyCountry(String metric) => _fill('rankMyCountry', {'metric': metric}) ?? _base.rankMyCountry(metric); + + @override + String rankMyPlanet(String metric) => _fill('rankMyPlanet', {'metric': metric}) ?? _base.rankMyPlanet(metric); + + @override + String get rankPlanetEmpty => _remote['rankPlanetEmpty'] ?? _base.rankPlanetEmpty; + + @override + String rankScope(String scope) => _fill('rankScope', {'scope': scope}) ?? _base.rankScope(scope); + + @override + String get rankScopeCountry => _remote['rankScopeCountry'] ?? _base.rankScopeCountry; + + @override + String get rankScopeLive => _remote['rankScopeLive'] ?? _base.rankScopeLive; + + @override + String get rankWindowDaily => _remote['rankWindowDaily'] ?? _base.rankWindowDaily; + + @override + String get rankWindowWeekly => _remote['rankWindowWeekly'] ?? _base.rankWindowWeekly; + + @override + String get refundConfirm => _remote['refundConfirm'] ?? _base.refundConfirm; + + @override + String get refundReason => _remote['refundReason'] ?? _base.refundReason; + + @override + String get refundReasonDuplicate => _remote['refundReasonDuplicate'] ?? _base.refundReasonDuplicate; + + @override + String get refundReasonError => _remote['refundReasonError'] ?? _base.refundReasonError; + + @override + String get refundReasonExpectation => _remote['refundReasonExpectation'] ?? _base.refundReasonExpectation; + + @override + String get refundReasonMistake => _remote['refundReasonMistake'] ?? _base.refundReasonMistake; + + @override + String get refundSubmit => _remote['refundSubmit'] ?? _base.refundSubmit; + + @override + String get refundTitle => _remote['refundTitle'] ?? _base.refundTitle; + + @override + String get reportAlready => _remote['reportAlready'] ?? _base.reportAlready; + + @override + String get reportAuthor => _remote['reportAuthor'] ?? _base.reportAuthor; + + @override + String get reportConfirm => _remote['reportConfirm'] ?? _base.reportConfirm; + + @override + String get reportDefamation => _remote['reportDefamation'] ?? _base.reportDefamation; + + @override + String get reportInappropriate => _remote['reportInappropriate'] ?? _base.reportInappropriate; + + @override + String get reportPrivacy => _remote['reportPrivacy'] ?? _base.reportPrivacy; + + @override + String get reportReasonLabel => _remote['reportReasonLabel'] ?? _base.reportReasonLabel; + + @override + String get reportReasonUnknown => _remote['reportReasonUnknown'] ?? _base.reportReasonUnknown; + + @override + String get reportReceived => _remote['reportReceived'] ?? _base.reportReceived; + + @override + String get reportSpam => _remote['reportSpam'] ?? _base.reportSpam; + + @override + String get reportSubmit => _remote['reportSubmit'] ?? _base.reportSubmit; + + @override + String get reportTitle => _remote['reportTitle'] ?? _base.reportTitle; + + @override + String searchRemoveTerm(String term) => _fill('searchRemoveTerm', {'term': term}) ?? _base.searchRemoveTerm(term); + + @override + String get settingsAccount => _remote['settingsAccount'] ?? _base.settingsAccount; + + @override + String get settingsAppVersion => _remote['settingsAppVersion'] ?? _base.settingsAppVersion; + + @override + String get settingsBlocked => _remote['settingsBlocked'] ?? _base.settingsBlocked; + + @override + String get settingsConnect => _remote['settingsConnect'] ?? _base.settingsConnect; + + @override + String get settingsDisconnect => _remote['settingsDisconnect'] ?? _base.settingsDisconnect; + + @override + String get settingsEmail => _remote['settingsEmail'] ?? _base.settingsEmail; + + @override + String get settingsEmailProvider => _remote['settingsEmailProvider'] ?? _base.settingsEmailProvider; + + @override + String get settingsInfo => _remote['settingsInfo'] ?? _base.settingsInfo; + + @override + String get settingsInquiry => _remote['settingsInquiry'] ?? _base.settingsInquiry; + + @override + String get settingsLatest => _remote['settingsLatest'] ?? _base.settingsLatest; + + @override + String settingsLinkNotReady(String name, String items) => _fill('settingsLinkNotReady', {'name': name, 'items': items}) ?? _base.settingsLinkNotReady(name, items); + + @override + String get settingsLogout => _remote['settingsLogout'] ?? _base.settingsLogout; + + @override + String get settingsLogoutConfirm => _remote['settingsLogoutConfirm'] ?? _base.settingsLogoutConfirm; + + @override + String get settingsNickname => _remote['settingsNickname'] ?? _base.settingsNickname; + + @override + String get settingsNotice => _remote['settingsNotice'] ?? _base.settingsNotice; + + @override + String get settingsPayments => _remote['settingsPayments'] ?? _base.settingsPayments; + + @override + String get settingsPopHistory => _remote['settingsPopHistory'] ?? _base.settingsPopHistory; + + @override + String get settingsPush => _remote['settingsPush'] ?? _base.settingsPush; + + @override + String get settingsSameEmailOnly => _remote['settingsSameEmailOnly'] ?? _base.settingsSameEmailOnly; + + @override + String get settingsService => _remote['settingsService'] ?? _base.settingsService; + + @override + String get settingsServicePayments => _remote['settingsServicePayments'] ?? _base.settingsServicePayments; + + @override + String get settingsSns => _remote['settingsSns'] ?? _base.settingsSns; + + @override + String get settingsTitle => _remote['settingsTitle'] ?? _base.settingsTitle; + + @override + String get settingsUnlink => _remote['settingsUnlink'] ?? _base.settingsUnlink; + + @override + String get settingsUnlinkBody => _remote['settingsUnlinkBody'] ?? _base.settingsUnlinkBody; + + @override + String get settingsUnlinkConfirm => _remote['settingsUnlinkConfirm'] ?? _base.settingsUnlinkConfirm; + + @override + String get settingsWithdraw => _remote['settingsWithdraw'] ?? _base.settingsWithdraw; + + @override + String get settingsWithdrawBody => _remote['settingsWithdrawBody'] ?? _base.settingsWithdrawBody; + + @override + String get settingsWithdrawConfirm => _remote['settingsWithdrawConfirm'] ?? _base.settingsWithdrawConfirm; + + @override + String get settingsWithdrawLater => _remote['settingsWithdrawLater'] ?? _base.settingsWithdrawLater; + + @override + String get shareCopied => _remote['shareCopied'] ?? _base.shareCopied; + + @override + String get shareCopyLink => _remote['shareCopyLink'] ?? _base.shareCopyLink; + + @override + String get shareCopyText => _remote['shareCopyText'] ?? _base.shareCopyText; + + @override + String get shareInstagram => _remote['shareInstagram'] ?? _base.shareInstagram; + + @override + String sharePlanetLine(String brand, String name) => _fill('sharePlanetLine', {'brand': brand, 'name': name}) ?? _base.sharePlanetLine(brand, name); + + @override + String get shareTiktok => _remote['shareTiktok'] ?? _base.shareTiktok; + + @override + String get shareTopicCampaign => _remote['shareTopicCampaign'] ?? _base.shareTopicCampaign; + + @override + String get signupDoneBody => _remote['signupDoneBody'] ?? _base.signupDoneBody; + + @override + String get signupDoneTitle => _remote['signupDoneTitle'] ?? _base.signupDoneTitle; + + @override + String get sortAchieved => _remote['sortAchieved'] ?? _base.sortAchieved; + + @override + String get sortLatest => _remote['sortLatest'] ?? _base.sortLatest; + + @override + String get sortPop => _remote['sortPop'] ?? _base.sortPop; + + @override + String get sortPopular => _remote['sortPopular'] ?? _base.sortPopular; + + @override + String get sortSolved => _remote['sortSolved'] ?? _base.sortSolved; + + @override + String get stageNames => _remote['stageNames'] ?? _base.stageNames; + + @override + String get statContribution => _remote['statContribution'] ?? _base.statContribution; + + @override + String get statQuests => _remote['statQuests'] ?? _base.statQuests; + + @override + String get statSteps => _remote['statSteps'] ?? _base.statSteps; + + @override + String get stationCategoryEmpty => _remote['stationCategoryEmpty'] ?? _base.stationCategoryEmpty; + + @override + String get stationEmpty => _remote['stationEmpty'] ?? _base.stationEmpty; + + @override + String get stationLoadFailed => _remote['stationLoadFailed'] ?? _base.stationLoadFailed; + + @override + String get stationMedia => _remote['stationMedia'] ?? _base.stationMedia; + + @override + String get stationOfficial => _remote['stationOfficial'] ?? _base.stationOfficial; + + @override + String get stationReason => _remote['stationReason'] ?? _base.stationReason; + + @override + String get stationShop => _remote['stationShop'] ?? _base.stationShop; + + @override + String get stationSuggest => _remote['stationSuggest'] ?? _base.stationSuggest; + + @override + String get stationSuggestLater => _remote['stationSuggestLater'] ?? _base.stationSuggestLater; + + @override + String get stationSuggestNote => _remote['stationSuggestNote'] ?? _base.stationSuggestNote; + + @override + String get stationTicket => _remote['stationTicket'] ?? _base.stationTicket; + + @override + String get storeConnectFailed => _remote['storeConnectFailed'] ?? _base.storeConnectFailed; + + @override + String get storeFailed => _remote['storeFailed'] ?? _base.storeFailed; + + @override + String get storeNotReady => _remote['storeNotReady'] ?? _base.storeNotReady; + + @override + String get storePending => _remote['storePending'] ?? _base.storePending; + + @override + String get storePurchased => _remote['storePurchased'] ?? _base.storePurchased; + + @override + String get takePhoto => _remote['takePhoto'] ?? _base.takePhoto; + + @override + String get termsAge => _remote['termsAge'] ?? _base.termsAge; + + @override + String get termsAgreeAll => _remote['termsAgreeAll'] ?? _base.termsAgreeAll; + + @override + String get termsLocation => _remote['termsLocation'] ?? _base.termsLocation; + + @override + String get termsMarketing => _remote['termsMarketing'] ?? _base.termsMarketing; + + @override + String get termsService => _remote['termsService'] ?? _base.termsService; + + @override + String get termsTitle => _remote['termsTitle'] ?? _base.termsTitle; + + @override + String get tierGuide => _remote['tierGuide'] ?? _base.tierGuide; + + @override + String get tierInfoTitle => _remote['tierInfoTitle'] ?? _base.tierInfoTitle; + + @override + String get tierPassportNote => _remote['tierPassportNote'] ?? _base.tierPassportNote; + + @override + String get tierPromptPrefix => _remote['tierPromptPrefix'] ?? _base.tierPromptPrefix; + + @override + String get tierPromptRank => _remote['tierPromptRank'] ?? _base.tierPromptRank; + + @override + String get tierPromptSuffix => _remote['tierPromptSuffix'] ?? _base.tierPromptSuffix; + + @override + String get tierTotalContribution => _remote['tierTotalContribution'] ?? _base.tierTotalContribution; + + @override + String timeDaysAgo(String n) => _fill('timeDaysAgo', {'n': n}) ?? _base.timeDaysAgo(n); + + @override + String timeHoursAgo(String n) => _fill('timeHoursAgo', {'n': n}) ?? _base.timeHoursAgo(n); + + @override + String timeHoursMinutesLeft(String hours, String minutes) => _fill('timeHoursMinutesLeft', {'hours': hours, 'minutes': minutes}) ?? _base.timeHoursMinutesLeft(hours, minutes); + + @override + String get timeJustNow => _remote['timeJustNow'] ?? _base.timeJustNow; + + @override + String timeMinutesAgo(String n) => _fill('timeMinutesAgo', {'n': n}) ?? _base.timeMinutesAgo(n); + + @override + String timeMinutesLeft(String minutes) => _fill('timeMinutesLeft', {'minutes': minutes}) ?? _base.timeMinutesLeft(minutes); + + @override + String get translationAlreadyMarked => _remote['translationAlreadyMarked'] ?? _base.translationAlreadyMarked; + + @override + String get translationDailyCap => _remote['translationDailyCap'] ?? _base.translationDailyCap; + + @override + String get translationDuplicate => _remote['translationDuplicate'] ?? _base.translationDuplicate; + + @override + String get translationFailed => _remote['translationFailed'] ?? _base.translationFailed; + + @override + String get translationHint => _remote['translationHint'] ?? _base.translationHint; + + @override + String get translationInvalid => _remote['translationInvalid'] ?? _base.translationInvalid; + + @override + String get translationInvalidTarget => _remote['translationInvalidTarget'] ?? _base.translationInvalidTarget; + + @override + String get translationMark => _remote['translationMark'] ?? _base.translationMark; + + @override + String get translationMarked => _remote['translationMarked'] ?? _base.translationMarked; + + @override + String get translationNotEligible => _remote['translationNotEligible'] ?? _base.translationNotEligible; + + @override + String translationReplyTo(String name) => _fill('translationReplyTo', {'name': name}) ?? _base.translationReplyTo(name); + + @override + String get translationSelfMarkBlocked => _remote['translationSelfMarkBlocked'] ?? _base.translationSelfMarkBlocked; + + @override + String get translationSubmitted => _remote['translationSubmitted'] ?? _base.translationSubmitted; + + @override + String get translationValid => _remote['translationValid'] ?? _base.translationValid; + + @override + String get translationWrite => _remote['translationWrite'] ?? _base.translationWrite; + + @override + String get unblock => _remote['unblock'] ?? _base.unblock; + + @override + String unblockFailed(String reason) => _fill('unblockFailed', {'reason': reason}) ?? _base.unblockFailed(reason); + + @override + String get unknownUser => _remote['unknownUser'] ?? _base.unknownUser; + + @override + String get zapApplying => _remote['zapApplying'] ?? _base.zapApplying; + + @override + String get zapDone => _remote['zapDone'] ?? _base.zapDone; + + @override + String zapDonePop(String pop) => _fill('zapDonePop', {'pop': pop}) ?? _base.zapDonePop(pop); + + @override + String get zapFailLedger => _remote['zapFailLedger'] ?? _base.zapFailLedger; + + @override + String get zapFailNoPop => _remote['zapFailNoPop'] ?? _base.zapFailNoPop; + + @override + String get zapFailRetry => _remote['zapFailRetry'] ?? _base.zapFailRetry; + + @override + String get zapNeedsCheck => _remote['zapNeedsCheck'] ?? _base.zapNeedsCheck; + + @override + String get zapNoPlanet => _remote['zapNoPlanet'] ?? _base.zapNoPlanet; + + @override + String get zapNotEnoughPop => _remote['zapNotEnoughPop'] ?? _base.zapNotEnoughPop; + + @override + String get zapRetrySame => _remote['zapRetrySame'] ?? _base.zapRetrySame; +} diff --git a/app/pubspec.lock b/app/pubspec.lock index a8e2be4..83d3b02 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: cel - sha256: "72776d4cacd526f9846522e7a3ce8584c2071974d747e2ea695645faa2dcea1b" + sha256: "51d77e16424d41b5fdb0a239be4c8a0550d4dd3f952801d35375ddd90cfb49da" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.5.4+1" characters: dependency: transitive description: @@ -137,6 +137,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.1.12" + code_assets: + dependency: transitive + description: + name: code_assets + sha256: cfd4f5f575a49c5f10ca856e9846073f1e6c3ee94912377eea5f6cefc5272941 + url: "https://pub.dev" + source: hosted + version: "2.0.0" collection: dependency: transitive description: @@ -153,6 +161,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.2" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: f141ea4f277af142a0356955707f6556f37b03947d39d55585981a06ca437bd6 + url: "https://pub.dev" + source: hosted + version: "0.3.5+5" crypto: dependency: "direct main" description: @@ -189,18 +205,18 @@ packages: dependency: "direct dev" description: name: fake_cloud_firestore - sha256: "4276eadc5e2c36663fe393e98d14d4cc413ebe8ce0d02edd8cda57ef7a5a8369" + sha256: c2ce1f828e5840c2f212584d496dbae0cfc94848daa84112e51a0722358aa24a url: "https://pub.dev" source: hosted - version: "4.3.0" + version: "4.2.0" fake_firebase_security_rules: dependency: transitive description: name: fake_firebase_security_rules - sha256: "983a1b3aec751af3122c1cfa79ebac0336a1589226a9683548b30457a5268720" + sha256: "6af54bedfd6985451a9735f2cfac91ffe0128bfc92bf15e8544cd56c6941d6cc" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.5.4" ffi: dependency: transitive description: @@ -217,6 +233,38 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.1" + file_selector_linux: + dependency: transitive + description: + name: file_selector_linux + sha256: da76400e7872ce7637ffdce12749ec24169c25f6195c28372208e65a24bcd2ab + url: "https://pub.dev" + source: hosted + version: "0.9.4+1" + file_selector_macos: + dependency: transitive + description: + name: file_selector_macos + sha256: d57c62362766b5e7ae739448650b66c6aab7a68ba7ecc65e04018652645ae0f4 + url: "https://pub.dev" + source: hosted + version: "0.9.5+1" + file_selector_platform_interface: + dependency: transitive + description: + name: file_selector_platform_interface + sha256: "35e0bd61ebcdb91a3505813b055b09b79dfdc7d0aee9c09a7ba59ae4bb13dc85" + url: "https://pub.dev" + source: hosted + version: "2.7.0" + file_selector_windows: + dependency: transitive + description: + name: file_selector_windows + sha256: fbefc5fb92c6d3cbe8d284a2cd971b593bb07d2cd6da8557b81a862250b4acec + url: "https://pub.dev" + source: hosted + version: "0.9.3+6" firebase_auth: dependency: "direct main" description: @@ -273,6 +321,54 @@ packages: url: "https://pub.dev" source: hosted version: "3.10.0" + firebase_messaging: + dependency: "direct main" + description: + name: firebase_messaging + sha256: "55cf1000dd72d229ebff3ce6a399bd35705a7675275478b31308afd90ac85751" + url: "https://pub.dev" + source: hosted + version: "16.5.0" + firebase_messaging_platform_interface: + dependency: transitive + description: + name: firebase_messaging_platform_interface + sha256: e5fbb62bd23a27c983825277877ac91fdfdb6bfb31c6e5399700963ae586e339 + url: "https://pub.dev" + source: hosted + version: "4.9.3" + firebase_messaging_web: + dependency: transitive + description: + name: firebase_messaging_web + sha256: "663d320e90a41fe57d651ec79df2bc831bf29e051bc0c2cd41faf3106eb412d9" + url: "https://pub.dev" + source: hosted + version: "4.2.4" + firebase_storage: + dependency: "direct main" + description: + name: firebase_storage + sha256: "6089b73ab5d4da77099eda5b8462a449f2915ecebbcc9ddd523039231ad17634" + url: "https://pub.dev" + source: hosted + version: "13.4.6" + firebase_storage_platform_interface: + dependency: transitive + description: + name: firebase_storage_platform_interface + sha256: fadbd48f9ff7bde8040ad898f68113f37873310eed68c84ff179821e618b8579 + url: "https://pub.dev" + source: hosted + version: "6.0.6" + firebase_storage_web: + dependency: transitive + description: + name: firebase_storage_web + sha256: "869c00fd45fad6da5dcfc0d24ce83d245884fb427088af48111cfa62e278e7e1" + url: "https://pub.dev" + source: hosted + version: "3.11.12" fixnum: dependency: transitive description: @@ -307,6 +403,19 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.0" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "3854fe5e3bff0b113c658f260b90c95dea17c92db0f2addeac2e343dd9969785" + url: "https://pub.dev" + source: hosted + version: "2.0.35" flutter_test: dependency: "direct dev" description: flutter @@ -322,6 +431,54 @@ packages: description: flutter source: sdk version: "0.0.0" + geolocator: + dependency: "direct main" + description: + name: geolocator + sha256: f62bcd90459e63210bbf9c35deb6a51c521f992a78de19a1fe5c11704f9530e2 + url: "https://pub.dev" + source: hosted + version: "13.0.4" + geolocator_android: + dependency: transitive + description: + name: geolocator_android + sha256: fcb1760a50d7500deca37c9a666785c047139b5f9ee15aa5469fae7dbbe3170d + url: "https://pub.dev" + source: hosted + version: "4.6.2" + geolocator_apple: + dependency: transitive + description: + name: geolocator_apple + sha256: "853803d6bb1713c094e935b4a5ae5f19c0308acf81da13fa9ff84fb4c70c0b73" + url: "https://pub.dev" + source: hosted + version: "2.3.14" + geolocator_platform_interface: + dependency: transitive + description: + name: geolocator_platform_interface + sha256: "94db8255dc183d268765df682580440617ca35877fc82cacb5420ad03b86198d" + url: "https://pub.dev" + source: hosted + version: "4.3.0" + geolocator_web: + dependency: transitive + description: + name: geolocator_web + sha256: "19e485a0f8d6a88abcf9c53cba3a4105e14b7435ed8ac1c108c067b938fe8429" + url: "https://pub.dev" + source: hosted + version: "4.1.4" + geolocator_windows: + dependency: transitive + description: + name: geolocator_windows + sha256: "175435404d20278ffd220de83c2ca293b73db95eafbdc8131fe8609be1421eb6" + url: "https://pub.dev" + source: hosted + version: "0.2.5" go_router: dependency: "direct main" description: @@ -338,6 +495,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.3+1" + google_mobile_ads: + dependency: "direct main" + description: + name: google_mobile_ads + sha256: "8094e1ace8b0da33fe79027ca6959763c96a28855025cb7c8ec60838e1d56ed8" + url: "https://pub.dev" + source: hosted + version: "9.1.0" google_sign_in: dependency: "direct main" description: @@ -378,6 +543,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.3" + hooks: + dependency: transitive + description: + name: hooks + sha256: eaac480a35ec0814146c2c48d96aaa829e0e44a7662c88ae84c9edf4bc35651f + url: "https://pub.dev" + source: hosted + version: "2.2.0" http: dependency: transitive description: @@ -402,11 +575,139 @@ packages: url: "https://pub.dev" source: hosted version: "4.9.1" + image_picker: + dependency: "direct main" + description: + name: image_picker + sha256: d8402284df184bc05f4a2210c6c23983b0720f4cd87cbd05c5390a78af602667 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + sha256: "0a55d645a670d6ae11efa948f955ae904afa472f1344fc15a7ddacc20c1e7219" + url: "https://pub.dev" + source: hosted + version: "0.8.13+23" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + sha256: "66257a3191ab360d23a55c8241c91a6e329d31e94efa7be9cf7a212e65850214" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + sha256: ee3885b6fcd71958fbc79770dd194c63371439d536d69c47b279171a486482ae + url: "https://pub.dev" + source: hosted + version: "0.8.13+7" + image_picker_linux: + dependency: transitive + description: + name: image_picker_linux + sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4" + url: "https://pub.dev" + source: hosted + version: "0.2.2" + image_picker_macos: + dependency: transitive + description: + name: image_picker_macos + sha256: "86f0f15a309de7e1a552c12df9ce5b59fe927e71385329355aec4776c6a8ec91" + url: "https://pub.dev" + source: hosted + version: "0.2.2+1" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + sha256: "567e056716333a1647c64bb6bd873cff7622233a5c3f694be28a583d4715690c" + url: "https://pub.dev" + source: hosted + version: "2.11.1" + image_picker_windows: + dependency: transitive + description: + name: image_picker_windows + sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae + url: "https://pub.dev" + source: hosted + version: "0.2.2" + in_app_purchase: + dependency: "direct main" + description: + name: in_app_purchase + sha256: d8317ec2ed6fe33199957d67ee67e08a4fd4871ef93b91d7611d8ee560892afc + url: "https://pub.dev" + source: hosted + version: "3.3.1" + in_app_purchase_android: + dependency: "direct main" + description: + name: in_app_purchase_android + sha256: f7327b5fd70d8dc1b419fb4cd5c17520175c4059441afd985e8a9c3fa2cefed9 + url: "https://pub.dev" + source: hosted + version: "0.5.3" + in_app_purchase_platform_interface: + dependency: transitive + description: + name: in_app_purchase_platform_interface + sha256: "0b0076cac8ce4fa7048f01e76af8b123aeb6a7c4e0dea2a5206d6664454f3e36" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + in_app_purchase_storekit: + dependency: transitive + description: + name: in_app_purchase_storekit + sha256: bb87b9003fd77da6f23f82c09b1891b28f7bf2520cfa1881a266a9601e675bf0 + url: "https://pub.dev" + source: hosted + version: "0.4.13" integration_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + intl: + dependency: "direct main" + description: + name: intl + sha256: "1ca20c894b1717686a2319b8548763d812bc0aabdac580420a44c5178c57a867" + url: "https://pub.dev" + source: hosted + version: "0.20.3" + jni: + dependency: transitive + description: + name: jni + sha256: f038e58b4dc2c9037f50e233175086337e0b305e356d28211bf55f21c504cbd3 + url: "https://pub.dev" + source: hosted + version: "1.0.3" + jni_flutter: + dependency: transitive + description: + name: jni_flutter + sha256: b2310cdd4c18c65c081ab141a41efa94aa26c65431803703ece51996f174f351 + url: "https://pub.dev" + source: hosted + version: "1.0.3" + jni_util: + dependency: transitive + description: + name: jni_util + sha256: "1ba86da04a5f2bf18fde2edb235587e70c5b0fc5bd4ba955f46b00942c3fc35f" + url: "https://pub.dev" + source: hosted + version: "1.0.0" json_annotation: dependency: transitive description: @@ -451,10 +752,10 @@ packages: dependency: transitive description: name: logger - sha256: "2a0dc097e7b01d942475bdd552356db2d0f768b05540bd4b2b53f1840f2239a7" + sha256: "25aee487596a6257655a1e091ec2ae66bc30e7af663592cc3a27e6591e05035c" url: "https://pub.dev" source: hosted - version: "2.8.0" + version: "2.7.0" logging: dependency: transitive description: @@ -487,6 +788,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.19.0" + mime: + dependency: transitive + description: + name: mime + sha256: bd47de35f07e27267e69c8c8b22edf9473bfee170a60d60fcc93730c5144b7f6 + url: "https://pub.dev" + source: hosted + version: "2.1.0" mock_exceptions: dependency: transitive description: @@ -503,6 +812,22 @@ packages: url: "https://pub.dev" source: hosted version: "4.7.0" + objective_c: + dependency: transitive + description: + name: objective_c + sha256: ad56fd53a78ff6b1472fa59ff2a4e8b8ccabafc586fc263a1dfad0b99b5553e3 + url: "https://pub.dev" + source: hosted + version: "9.6.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: ffcf4cf3d6c0b74ac43708d9f56625506e8a68aa935abe9d267a7330f320eb5d + url: "https://pub.dev" + source: hosted + version: "3.0.0" path: dependency: transitive description: @@ -511,6 +836,110 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: a7f4874f987173da295a61c181b8ee71dab59b332a486b391babf26a1b884825 + url: "https://pub.dev" + source: hosted + version: "2.1.6" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699" + url: "https://pub.dev" + source: hosted + version: "2.6.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: "58c2005f147315b11e9b4a7bc889cd5203e250cba8e3f012dae259b4972b5c16" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "484838772624c3a4b94f1e44a3e19897fee738f2d5c4ce448443b0417f7c9dda" + url: "https://pub.dev" + source: hosted + version: "2.1.3" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + pedometer: + dependency: "direct main" + description: + name: pedometer + sha256: "146d9663d4af6041328ac15cd96cdcd426b866d277d72f540f701193a61a193d" + url: "https://pub.dev" + source: hosted + version: "4.2.0" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + sha256: fe54465bcc62a4564c6e4db337bbaded6c0c0fa6e10487414436d163114784f6 + url: "https://pub.dev" + source: hosted + version: "12.0.3" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: "1e3bc410ca1bf84662104b100eb126e066cb55791b7451307f9708d4007350e6" + url: "https://pub.dev" + source: hosted + version: "13.0.1" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: f49cb15a064ea9d974fc7fbb302099353b7b170d07284e86e264561579e5bcf8 + url: "https://pub.dev" + source: hosted + version: "9.6.1" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: "6ea98b3f17f60d3b527f2647ed2ab4dc0f6bfe25b22cb1c363f5d8f62252f6ac" + url: "https://pub.dev" + source: hosted + version: "0.1.4+1" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: ed86a61c190258fdd65de395ea0632822e3415c1faec38eae0c31b479c28a531 + url: "https://pub.dev" + source: hosted + version: "4.4.1" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: caeae01858a0a7d2df67a445ac98e1ad95e55a0e77c73044f4e9b1c8c2289cbd + url: "https://pub.dev" + source: hosted + version: "0.2.2" petitparser: dependency: transitive description: @@ -523,10 +952,10 @@ packages: dependency: transitive description: name: platform - sha256: a36d119c13416516a7b5913fbe8af8531e11633d784c550b2125f76c758524ec + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.1.6" plugin_platform_interface: dependency: transitive description: @@ -559,6 +988,22 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.6" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "261236774e8b1d69cfc6b9eabbc96c40f25e7a2d6b171f3385d4f65d5734fb24" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + record_use: + dependency: transitive + description: + name: record_use + sha256: "1cb8564af8d43b464294411db9217f5ec04891c6f22ee2c32d73ae05e88a6bd2" + url: "https://pub.dev" + source: hosted + version: "1.1.1" rx: dependency: transitive description: @@ -575,6 +1020,94 @@ packages: url: "https://pub.dev" source: hosted version: "0.28.0" + sensors_plus: + dependency: "direct main" + description: + name: sensors_plus + sha256: "31182c626623de79d3f7e1f9b43028f940821ee012c092f68f165fe55b867822" + url: "https://pub.dev" + source: hosted + version: "7.1.0" + sensors_plus_platform_interface: + dependency: transitive + description: + name: sensors_plus_platform_interface + sha256: "9e12a92569bf14ae04b2f086c3e087177a47e527b7666ae1bbe5f70f8450ab19" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + share_plus: + dependency: "direct main" + description: + name: share_plus + sha256: "223873d106614442ea6f20db5a038685cc5b32a2fba81cdecaefbbae0523f7fa" + url: "https://pub.dev" + source: hosted + version: "12.0.2" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a" + url: "https://pub.dev" + source: hosted + version: "6.1.0" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf + url: "https://pub.dev" + source: hosted + version: "2.5.5" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "1e12aafe408aa50da80edfd679a2a6bf63ba7ab37c7fa98286da459a757b3399" + url: "https://pub.dev" + source: hosted + version: "2.4.28" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "2ec3934efa51e46117f23031cc141b8fc878e8525b94ec1ea4f7f586cf1b47ea" + url: "https://pub.dev" + source: hosted + version: "2.5.7" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" sign_in_with_apple: dependency: "direct main" description: @@ -668,6 +1201,70 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 + url: "https://pub.dev" + source: hosted + version: "6.3.2" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "611e87fb320b70d1dd721dc46af89c98aceccea9b31fde49e084591414e0c610" + url: "https://pub.dev" + source: hosted + version: "6.3.33" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: "8faa1aab294f1ab4040b43660c887b0418d5fa4f0cffef76a484e6aa1092eb4a" + url: "https://pub.dev" + source: hosted + version: "6.4.2" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: "10f86fef4c2c43563fa6c211ff9cf757adf4d3ab762c56bd430664a947d70cd0" + url: "https://pub.dev" + source: hosted + version: "3.2.3" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "5e835a3b869c2d70325349c81c5a45c28e20791265b67b2669da6b08c5cd5201" + url: "https://pub.dev" + source: hosted + version: "3.2.6" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34" + url: "https://pub.dev" + source: hosted + version: "2.4.3" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "6c5ad3f22cd4c38e089b81963b3cd7bb83b111b2df5dce008bb066162f42e429" + url: "https://pub.dev" + source: hosted + version: "3.1.6" uuid: dependency: transitive description: @@ -680,10 +1277,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "92b9910f66ed1057fd4da7b040ae7c74cafacf885bdc81be496928d5049b032d" + sha256: f36f9f3be64c6198714492bb455c11056e33e2f85d9a0b676a48301e44fdcf47 url: "https://pub.dev" source: hosted - version: "2.4.3" + version: "2.4.2" vm_service: dependency: transitive description: @@ -704,10 +1301,58 @@ packages: dependency: transitive description: name: webdriver - sha256: "28b82ec894fed45dd71c23ba62d1af973ed97dd59a4f5790a4d38b0b13e5657e" + sha256: "2f3a14ca026957870cfd9c635b83507e0e51d8091568e90129fbf805aba7cade" url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.1.0" + webview_flutter: + dependency: transitive + description: + name: webview_flutter + sha256: d53e1ccf5516f25017e3c9d44c39034db352d20fa34fe200674270242c2c5111 + url: "https://pub.dev" + source: hosted + version: "4.14.1" + webview_flutter_android: + dependency: transitive + description: + name: webview_flutter_android + sha256: "4de8b3d1ff4ebe1bdb42e68a5e4f809194a3cb0117a8f495f590004f00da3964" + url: "https://pub.dev" + source: hosted + version: "4.14.1" + webview_flutter_platform_interface: + dependency: transitive + description: + name: webview_flutter_platform_interface + sha256: "1221c1b12f5278791042f2ec2841743784cf25c5a644e23d6680e5d718824f04" + url: "https://pub.dev" + source: hosted + version: "2.15.1" + webview_flutter_wkwebview: + dependency: transitive + description: + name: webview_flutter_wkwebview + sha256: fe359c7fac1002124b5b9e2ba3a41906bbb9b2d029ccb4a0067404d8f3704730 + url: "https://pub.dev" + source: hosted + version: "3.26.1" + win32: + dependency: transitive + description: + name: win32 + sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e + url: "https://pub.dev" + source: hosted + version: "5.15.0" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" xml: dependency: transitive description: @@ -725,5 +1370,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.11.5 <4.0.0" - flutter: ">=3.41.0" + dart: ">=3.12.0 <4.0.0" + flutter: ">=3.44.0" diff --git a/app/pubspec.yaml b/app/pubspec.yaml index ebf0a12..5ad5779 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -7,18 +7,28 @@ environment: sdk: ^3.11.5 # ⚠️ 아래 의존성을 추가·변경·업그레이드하면 최소 지원 OS 하한을 재계산할 것. -# 절차: 2주차 플러그인 선검증 리포트 §3.5 · 배경은 README §2 +# 절차: 2주차 플러그인 선검증 리포트 §3.5 · 배경은 README 4절 # 최소 지원 OS 는 별첨1 v1.0 에 계약값으로 들어간다 — 값이 틀리면 계약이 틀린다. dependencies: flutter: sdk: flutter + # 다국어 — 정책 원장 B-76 (ko·en·ja·zh-CN·es). SDK 패키지라 최소 지원 OS 를 움직이지 않는다. + flutter_localizations: + sdk: flutter + intl: any # 5탭 셸(StatefulShellRoute) + 온보딩 라우트. flutter.dev 팀이 관리하는 공식 라우터다. go_router: ^17.4.0 # Firebase — 설정 파일(google-services.json / GoogleService-Info.plist)이 아직 없다. # 없으면 FirebaseBoot 이 목(mock) 모드로 뜬다. 파일 투입 절차는 README 「미투입 항목」 참고. - firebase_core: ^4.13.0 + # **firebase_core 를 정확 버전으로 못 박았다.** 4.14.0 은 Android 쪽 + # `FlutterFirebaseCorePlugin.customAuthDomain` 을 없앴고, firebase_auth 6.5.7 이 + # 그 심볼을 참조해 `:firebase_auth:compileDebugJavaWithJavac` 에서 죽는다. + # 캐럿(^)으로 두면 새 워크스페이스에서 pub 이 4.14.0 을 잡아 Android 빌드가 + # 깨지는데, 오류가 플러그인 내부에서 나 원인이 의존성 해석이라는 것이 안 보인다. + # firebase_auth 를 4.14.0 호환 버전으로 올릴 때 이 핀도 함께 푼다. + firebase_core: 4.13.0 firebase_auth: ^6.5.7 # 서버 연동 — 읽기는 Firestore 직결, 재화가 오가는 쓰기는 전부 Cloud Functions 를 지난다. @@ -26,18 +36,79 @@ dependencies: cloud_firestore: ^6.3.0 cloud_functions: ^6.1.0 + # 프로필 사진 원본 저장. 용량·형식은 클라이언트 1차 검증 + Storage 규칙 + # 서버 검증 2단으로 본다(정책 원장 B-44). + firebase_storage: ^13.1.0 + + # 프로필 사진 고르기. 기기 사진 선택기를 띄우므로 앱이 앨범 전체 접근 권한을 + # 들고 있지 않아도 된다. iOS 권한 문구는 Info.plist 에 이미 있다. + image_picker: ^1.2.0 + + # 캠페인 공유. OS 공유 시트를 띄운다. 하한은 Android minSdk 19 · iOS 12.0 으로 + # 앱 하한(Android 29 · iOS 15.0)보다 낮아 최소 지원 OS 를 움직이지 않는다. + share_plus: ^12.0.0 + + # 푸시 수신 — 정책 원장 B-77 5 개 구분. 서버는 구분마다 토픽을 따로 두고 + # 앱이 설정대로 구독·해지해 「OFF 카테고리 발송 0건」을 구조로 지킨다. + # + # **최소 지원 OS 하한은 바뀌지 않는다**(선검증 §3.5 절차대로 재계산했다). + # Android: 플러그인이 `project.ext.minSdk` 를 그대로 받아 **앱 하한 29 유지**. + # Android 13+ `POST_NOTIFICATIONS` 는 플러그인 매니페스트가 선언한다. + # iOS: 플러그인 `deployment_target = 15.0` — 현재 MinimumOSVersion 15.0 과 같다. + # iOS 실동작은 APNs 키(별첨 A-1 계정 이전) 대기라 지금은 코드 경로만이다. + firebase_messaging: ^16.5.0 + # Apple Sign-In nonce를 SHA-256으로 해시해 replay 공격을 막는다. crypto: ^3.0.7 # 소셜 로그인 — Google·Apple 만 실 코드 경로. 카카오·네이버는 앱 키가 없어 스텁이다(Q3). google_sign_in: ^7.2.0 sign_in_with_apple: ^8.1.0 + sensors_plus: ^7.1.0 + + # 만보기 퀘스트(S-H-WALK) — 걸음 센서. 2주차 플러그인 선검증리포트 §3.5 가 잰 값 그대로 + # 4.2.0 을 쓴다. minSdk 요구가 16 이라 현재 하한(24)을 올리지 않는다. + pedometer: ^4.2.0 + + # 위치 퀘스트(S-H-PLACE) — 좌표와 mock location 판정. 선검증리포트 §2.2 가 채택한 + # **13.x 하향**을 지킨다. 14.x 는 win32 6.x 를 끌어와 health 13.3.1 과 공존하지 못한다. + geolocator: ^13.0.4 + + # 활동 인식·위치 권한 요청. 선검증리포트 §2.3 이 13.0.0 에서 하향한 **12.x** 를 고정한다. + permission_handler: ^12.0.1 + + # 걸음 기준선을 하루 단위로 남긴다. 걸음 센서는 **부팅 이후 누적**만 주므로 기준선이 + # 없으면 앱을 껐다 켤 때마다 오늘치가 0 으로 돌아간다(정책 원장 B-26 은 일 단위 구간이다). + # flutter.dev 팀이 관리하며 최소 지원 OS 를 올리지 않는다. + shared_preferences: ^2.5.3 + + # 브리핑 「원문보기」 — 정책 원장 B-75 5 요소 중 하나. 외부 기사 URL 을 기본 + # 브라우저로 넘긴다. 인앱 WebView(`webview_flutter`)를 쓰지 않은 이유는 링크 하나를 + # 여는 데 렌더러를 하나 더 들이는 값이 없기 때문이다. + # + # **최소 지원 OS 하한은 바뀌지 않는다**(선검증 §3.5 절차대로 재계산했다). + # Android: url_launcher_android minSdk 24 — 앱 하한 29 아래라 영향 없음. + # iOS: url_launcher_ios deployment_target 13.0 — 앱 하한 15.0 아래라 영향 없음. + url_launcher: ^6.3.2 + + # 보상형 광고(정책 원장 B-03 · C-02) · 인앱 결제(B-02 · B-04 · B-84). 별첨 1 ⑥ 9주차 샌드박스. + # 광고 단위·앱 ID 는 Google 공식 **테스트 값**이다 — 실 값은 lib/core/monetization/rewarded_ad.dart 의 AdUnits + # 한 곳과 AndroidManifest · Info.plist 의 앱 ID 만 바꾼다. + # + # **최소 지원 OS 하한은 바뀌지 않는다**(선검증 §3.5 절차대로 재계산했다). + # Android: google_mobile_ads minSdk 24 · in_app_purchase_android minSdk 21 — 앱 하한 29 아래. + # iOS: google_mobile_ads · in_app_purchase_storekit deployment_target 13.0 — 앱 하한 15.0 아래. + # 추가해도 pubspec.lock 의 firebase_core 는 4.13.0 그대로다(위 핀 참고). + google_mobile_ads: ^9.1.0 + in_app_purchase: ^3.3.1 + # Android 소비(consume)를 서버 검증 뒤로 미루려고 직접 부른다(자동 소비 끔). + in_app_purchase_android: ^0.5.3 dev_dependencies: flutter_test: sdk: flutter - # 기기 위에서 앱을 띄워 에뮬레이터와 실제 왕복을 돈다. 위젯 테스트는 네트워크를 - # 타지 않으므로 「로그인 → 계정 문서 생성」은 여기서만 확인할 수 있다. + # 기기 위에서 프레임 타이밍을 잰다. 위젯 테스트는 실제 래스터화를 하지 않으므로 + # 「맵이 끊기지 않는가」는 여기서만 확인할 수 있다. integration_test: sdk: flutter flutter_lints: ^6.0.0 @@ -63,6 +134,8 @@ flutter_launcher_icons: flutter: uses-material-design: true + # lib/l10n/*.arb → AppLocalizations (l10n.yaml). + generate: true # 발주사 Figma 실측 폰트 (2026-09-02). 42dot Sans 가 본문·라벨 전량, ABeeZee 가 숫자·영문 표시. # 둘 다 Google Fonts OFL 이라 동봉한다. design/design-tokens.md 의 font-* 와 짝이다. @@ -73,7 +146,16 @@ flutter: - family: ABeeZee fonts: - asset: assets/fonts/ABeeZee-Regular.ttf + # 홈 헤더 워드마크(Figma `252:159361` Pretendard Black 20). 라틴 기본 글자만 남긴 부분집합(OFL). + - family: Pretendard + fonts: + - asset: assets/fonts/Pretendard-Black-Latin.otf + weight: 900 + # 발주사 Figma 03_Wireframe 에서 내보낸 시각 자산 (2026-09-02, figma-cli). + # 원본 노드: 우주 배경 252:159356 · 행성 252:159384 · 캠페인 252:159447 assets: + - assets/figma/ + - assets/fx/ # Figma Iconset 아이콘(lib/core/ui/app_icons.dart). - assets/icons/ diff --git a/app/tool/emulator_quest_roundtrip.sh b/app/tool/emulator_quest_roundtrip.sh new file mode 100755 index 0000000..087c054 --- /dev/null +++ b/app/tool/emulator_quest_roundtrip.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# 퀘스트 참여 실왕복 — 에뮬레이터 세 개를 물고 기기에서 한 바퀴 돈다. +# +# `firebase emulators:exec` 가 이 스크립트를 실행한다. 먼저 시드를 넣고(보안 규칙이 +# `quests` 쓰기를 마스터로 막아 앱이 만들 수 없다) 통합 테스트를 돌린다. +# +# cd firebase +# JAVA_HOME=... test/node_modules/.bin/firebase emulators:exec \ +# --only auth,firestore,functions --project demo-fanuzu \ +# "bash ../tool/emulator_quest_roundtrip.sh" +set -euo pipefail + +DEVICE="${DEVICE:-emulator-5554}" +HOST="${FANUZU_EMULATOR_HOST:-127.0.0.1}" +# 앱이 쓰는 프로젝트 ID. 설정 파일이 들어간 빌드는 `[DEFAULT]` 앱을 그대로 쓰므로 +# 실 프로젝트 ID 로 붙는다. 에뮬레이터는 프로젝트별로 칸이 나뉘어 시드도 같은 ID 에 넣는다. +PROJECT="${FANUZU_EMULATOR_PROJECT:-fanuzu-dev-b020d}" +BASE="http://127.0.0.1:8080/v1/projects/${PROJECT}/databases/(default)/documents" +ROOT="$(cd "$(dirname "$0")/.." && pwd)" + +echo "▸ 시드 — 퀘스트 정의와 운영값 (${PROJECT})" +# 에뮬레이터는 `Bearer owner` 토큰을 관리자로 본다. 이 헤더가 없으면 보안 규칙이 +# 그대로 적용돼 `quests` 쓰기가 막히고(마스터 전용) 시드가 조용히 사라진다. +AUTH=(-H 'Authorization: Bearer owner' -H 'Content-Type: application/json') +curl -sf -X PATCH "${BASE}/quests/daily-checkin" "${AUTH[@]}" -d '{ + "fields": { + "type": {"stringValue": "attend"}, + "title": {"stringValue": "출석 체크"}, + "rewardPop": {"integerValue": "10"}, + "dailyLimit": {"integerValue": "1"}, + "active": {"booleanValue": true} + } +}' > /dev/null +curl -sf -X PATCH "${BASE}/config/policy" "${AUTH[@]}" -d '{ + "fields": { + "attendPop": {"integerValue": "10"}, + "stepTiers": {"arrayValue": {"values": [ + {"mapValue": {"fields": {"steps": {"integerValue": "1000"}, "pop": {"integerValue": "5"}}}}, + {"mapValue": {"fields": {"steps": {"integerValue": "5000"}, "pop": {"integerValue": "6"}}}}, + {"mapValue": {"fields": {"steps": {"integerValue": "10000"}, "pop": {"integerValue": "7"}}}}, + {"mapValue": {"fields": {"steps": {"integerValue": "15000"}, "pop": {"integerValue": "8"}}}} + ]}} + } +}' > /dev/null +# 시드가 실제로 섰는지 본다. 없으면 여기서 끊는다 — 목록이 빈 채로 테스트가 돌면 +# 「참여 버튼이 없다」로만 보여 원인을 찾는 데 시간이 든다. +curl -sf "${BASE}/quests/daily-checkin" "${AUTH[@]}" | grep -q '"attend"' \ + || { echo "✗ 시드 실패 — quests/daily-checkin 이 없습니다"; exit 1; } +echo "✓ 시드 확인" + +echo "▸ 기기에서 왕복 (${DEVICE} · host ${HOST})" +cd "$ROOT" +exec fvm flutter test integration_test/quest_callable_emulator_test.dart \ + -d "$DEVICE" --dart-define="FANUZU_EMULATOR_HOST=${HOST}" diff --git a/app/tool/gen_l10n_remote.dart b/app/tool/gen_l10n_remote.dart new file mode 100644 index 0000000..d6f2d9e --- /dev/null +++ b/app/tool/gen_l10n_remote.dart @@ -0,0 +1,70 @@ +// 원격 배포 다국어 덮어쓰기 층 생성기 — 요구사항 FR-I18N-01 「언어 리소스를 원격 배포 방식으로 갱신」. +// +// `flutter gen-l10n` 이 만드는 AppLocalizations 는 문자열이 컴파일 상수라, 재배포 없이 +// 문구를 고칠 길이 없다. 여기서 그 클래스를 감싸는 [RemoteAppLocalizations] 를 만든다. +// 게터마다 Firestore `i18n/{locale}` 문서의 같은 키를 먼저 보고, 없으면 번들 문자열을 쓴다. +// +// 실행: `dart run tool/gen_l10n_remote.dart` (app/ 에서). arb 키를 더하거나 바꾸면 다시 돌린다. +// 생성물이 arb 와 어긋나면 test/i18n_test.dart 가 실패한다. +import 'dart:convert'; +import 'dart:io'; + +const templatePath = 'lib/l10n/app_ko.arb'; +const outputPath = 'lib/l10n/remote_app_localizations.dart'; + +void main() { + final arb = + jsonDecode(File(templatePath).readAsStringSync()) as Map; + File(outputPath).writeAsStringSync(generateRemoteL10n(arb)); + stdout.writeln('wrote $outputPath'); +} + +String generateRemoteL10n(Map arb) { + final out = StringBuffer() + ..writeln('// GENERATED by tool/gen_l10n_remote.dart — 직접 고치지 말 것.') + ..writeln("import 'app_localizations.dart';") + ..writeln() + ..writeln('/// 번들 문자열 위에 원격 문자열(`i18n/{locale}` 문서)을 덮는다.') + ..writeln('/// 원격에 없는 키는 번들 → 번들에도 없으면 ko(template) 순으로 폴백한다.') + ..writeln('class RemoteAppLocalizations extends AppLocalizations {') + ..writeln( + ' RemoteAppLocalizations(this._base, this._remote) : super(_base.localeName);', + ) + ..writeln() + ..writeln(' final AppLocalizations _base;') + ..writeln(' final Map _remote;') + ..writeln() + ..writeln(' /// 자리표시자를 한 번에 치환한다 — 인자 값 안의 `{name}` 은 다시 바꾸지 않는다.') + ..writeln(' String? _fill(String key, Map args) =>') + ..writeln(' _remote[key]?.replaceAllMapped(') + ..writeln(" RegExp(r'\\{(\\w+)\\}'),") + ..writeln(' (m) => args[m[1]] ?? m[0]!,') + ..writeln(' );'); + for (final key + in arb.keys.where((k) => !k.startsWith('@')).toList()..sort()) { + final message = arb[key] as String; + final meta = arb['@$key'] as Map?; + final names = ((meta?['placeholders'] as Map?) ?? const {}) + .keys + .toList(); + out.writeln(); + out.writeln(' @override'); + if (names.isEmpty) { + out.writeln(" String get $key => _remote['$key'] ?? _base.$key;"); + continue; + } + final params = names.map((n) => 'String $n').join(', '); + final args = names.join(', '); + // ICU select·plural 은 원격 문자열로 해석하지 않는다 — 번들만 쓴다. + if (message.contains(', select,') || message.contains(', plural,')) { + out.writeln(' String $key($params) => _base.$key($args);'); + continue; + } + final map = names.map((n) => "'$n': $n").join(', '); + out.writeln( + " String $key($params) => _fill('$key', {$map}) ?? _base.$key($args);", + ); + } + out.writeln('}'); + return out.toString(); +}