export type SystemWordingLeaf = string; export type SystemWordingTree = { [key: string]: SystemWordingLeaf | SystemWordingTree }; export type SystemWordingMessages = Record; export const defaultLocale = 'en'; export const systemWordingMessages = { en: { common: { add: 'Add', admin: 'Admin', all: 'All', back: 'Back', backToList: 'Back to list', cancel: 'Cancel', close: 'Close', create: 'Create', delete: 'Delete', edit: 'Edit', details: 'Details', filters: 'Filters', loading: 'Loading', name: 'Name', new: 'New', no: 'No', none: 'None', save: 'Save', saving: 'Saving', search: 'Search', select: 'Select', selected: 'Selected', system: 'System', noRecords: 'No records', fieldForLanguage: '{field} ({language})', searchOrSelect: 'Search or select', noMatches: 'No matches', createNamed: 'Add "{name}"', creating: 'Adding', inDev: 'In-Dev', removeNamed: 'Remove {name}', quantity: 'Quantity', eventItem: 'Event item', required: 'Required' }, nav: { home: 'Home', pokedex: 'Pokedex', habitatDex: 'Habitat Dex', collections: 'Collections', mainGame: 'Main Game', event: 'Event', pokemon: 'Pokemon', eventPokemon: 'Event Pokemon', habitats: 'Habitats', eventHabitats: 'Event Habitats', items: 'Items', eventItems: 'Event Items', ancientArtifacts: 'Ancient Artifacts', recipes: 'Recipes', automation: 'Automation', dish: 'Dish', events: 'Events', actions: 'Actions', dreamIsland: 'Dream Island', clothes: 'Clothes', checklist: 'CheckList', life: 'Life', admin: 'Admin', main: 'Main navigation', openMenu: 'Open navigation', closeMenu: 'Close navigation', collapseSidebar: 'Collapse sidebar', expandSidebar: 'Expand sidebar', language: 'Language', profile: 'Profile', login: 'Log in', logout: 'Log out', register: 'Register' }, search: { label: 'Search Pokopia Wiki', placeholder: 'Search wiki', open: 'Open search', clear: 'Clear search', empty: 'No matching results', failed: 'Search is unavailable', groups: { pokemon: 'Pokemon', habitats: 'Habitats', items: 'Items', ancientArtifacts: 'Ancient Artifacts', recipes: 'Recipes', dailyChecklist: 'Daily CheckList', life: 'Life', users: 'Users' } }, notifications: { title: 'Notifications', open: 'Open notifications', unreadCount: '{count} unread', markAllRead: 'Mark all read', markRead: 'Mark as read', loadMore: 'Load more', emptyTitle: 'No notifications', emptyBody: 'Comments, reactions, and review results will appear here.', systemActor: 'Pokopia Wiki', targetLifePost: 'Life post', targetLifeComment: 'Life comment', targetDiscussionComment: 'discussion comment', targetProfile: 'profile', lifePostComment: '{actor} commented on your Life post', lifeCommentReply: '{actor} replied to your Life comment', discussionCommentReply: '{actor} replied to your discussion comment', lifePostReaction: '{actor} reacted {reaction} to your Life post', userFollow: '{actor} followed you', moderationApproved: 'Your {target} passed review', moderationRejected: 'Your {target} did not pass review', moderationFailed: 'Review failed for your {target}' }, legal: { footer: { copyright: 'Copyright {year} Tootaio Studio. All rights reserved.', linksLabel: 'Legal pages', privacy: 'Privacy Policy', terms: 'Terms of Service', disclaimers: 'Disclaimers', notice: 'Pokopia Wiki uses community contributions and third-party references, including PokeAPI data and image resources. Pokemon-related names, images, and marks belong to their respective rights holders.' } }, seo: { siteDescription: 'Browse Pokopia Wiki for Pokemon, Event Pokemon, habitats, Event Habitats, items, Event Items, Ancient Artifacts, recipes, daily tasks, and Life community posts for Pokemon Pokopia.', pokemonDetailDescription: 'Read {name} details in Pokopia Wiki, including habitat, types, specialities, favourites, stats, related items, discussions, and edit history.', itemDetailDescription: 'Browse {name} item details in Pokopia Wiki, including base price, category, usage, acquisition methods, customization, related recipes, habitats, and Pokemon drops.', ancientArtifactDetailDescription: 'Browse {name} Ancient Artifact details in Pokopia Wiki, including category, tags, description, discussions, and edit history.', habitatDetailDescription: 'View {name} habitat details in Pokopia Wiki, including recipes, possible Pokemon, maps, time, weather, discussions, and edit history.', recipeDetailDescription: 'View the {name} recipe in Pokopia Wiki, including the result item, acquisition methods, materials, discussions, and edit history.' }, auth: { accountAccess: 'Trainer Pass', email: 'Email', password: 'Password', currentPassword: 'Current password', newPassword: 'New password', confirmPassword: 'Confirm password', displayName: 'Display name', referralCode: 'Referral code', referralCodePlaceholder: 'Optional code', referralCodeHint: 'Use an invite code from another trainer.', loginTitle: 'Log in', loginSubtitle: 'Use a verified email to enter Pokopia Wiki.', loggingIn: 'Logging in', loginFailed: 'Login failed', rememberMe: 'Remember me', forgotPassword: 'Forgot password?', noAccount: 'No account yet?', registerTitle: 'Register', registerSubtitle: 'Verify your email after creating an account.', registerFailed: 'Registration failed', sending: 'Sending', sendVerification: 'Send verification email', hasAccount: 'Already have an account?', requestResetTitle: 'Reset password', requestResetSubtitle: 'Send a password reset link to your account email.', sendResetLink: 'Send reset link', requestResetFailed: 'Password reset request failed', resetTitle: 'Choose a new password', resetSubtitle: 'Use the reset link from your email to update your password.', resetPassword: 'Reset password', resetting: 'Resetting', resetFailed: 'Password reset failed', passwordMismatch: 'Passwords do not match', invalidPasswordReset: 'The password reset link is invalid or expired.', verifyTitle: 'Email verification', verifySubtitle: 'You can log in after verification is complete.', verifyingEmail: 'Verifying email', invalidVerification: 'The verification link is invalid or expired.', verifyFailed: 'Email verification failed', goLogin: 'Go to login' }, errors: { requestFailed: 'Request failed ({status})', operationFailed: 'Operation failed', loadFailed: 'Load failed', addFailed: 'Add failed', saveFailed: 'Save failed', completeEmailVerification: 'Please complete email verification first.', permissionDenied: 'You do not have permission to use this action.' }, pages: { home: { kicker: 'Community Wiki', title: 'Pokopia Wiki', subtitle: 'Browse Pokemon, Event Pokemon, habitats, Event Habitats, items, recipes, daily tasks, and Life posts for Pokemon Pokopia.', primaryActions: 'Primary home actions', browsePokemon: 'Browse Pokemon', openChecklist: 'Daily CheckList', openLife: 'Open Life', quickIndex: 'Quick wiki index', featuredPanel: 'Featured wiki entry panel', dexCode: 'POKOPIA-001', dexTitle: 'Community-maintained game records', dexBody: 'Start with the core Wiki libraries, then jump into daily tasks or community Life posts.', wikiKicker: 'Wiki Libraries', wikiTitle: 'Browse game records', communityKicker: 'Daily & Community', communityTitle: 'Follow daily tasks and community updates', projectUpdatesKicker: 'Project Updates', projectUpdatesTitle: 'Latest site changes', projectUpdatesRepo: 'Source repository', projectUpdatesUpdatedAt: 'Updated {date}', projectUpdatesCommits: 'Recent commits', projectUpdatesReleases: 'Releases', projectUpdatesViewCommit: 'View commit', projectUpdatesViewRelease: 'View release', projectUpdatesViewAll: 'View all', futureKicker: 'More Sections', futureTitle: 'Planned wiki areas', sections: { pokemon: { title: 'Pokemon', description: 'Search Pokemon and filter by specialities, ideal habitat, and favourites.' }, eventPokemon: { title: 'Event Pokemon', description: 'Browse limited Pokemon entries with their own Pokopia IDs and list order.' }, habitats: { title: 'Habitats', description: 'View recipes, maps, weather, time, and Pokemon that may appear.' }, eventHabitats: { title: 'Event Habitats', description: 'Browse limited habitats with event recipes and possible Pokemon appearances.' }, items: { title: 'Items', description: 'Browse categories, usage, acquisition methods, customization, and tags.' }, eventItems: { title: 'Event Items', description: 'Browse limited event items with shared item categories and custom ordering.' }, ancientArtifacts: { title: 'Ancient Artifacts', description: 'Browse Lost Relics and Fossils with tags, descriptions, and wiki history.' }, recipes: { title: 'Recipes', description: 'Find result items, materials, and acquisition details.' }, checklist: { title: 'Daily CheckList', description: 'Review tasks that can be completed each day.' }, life: { title: 'Life', description: 'Read community posts, tips, discoveries, and comments.' }, automation: { title: 'Automation', description: 'Factory and automation base guides are being prepared.' }, dish: { title: 'Dish', description: 'Browse cooked dishes by cookware, ingredients, flavor, and Mosslax effects.' }, events: { title: 'Events', description: 'Seasonal and limited-time activity records are being prepared.' }, actions: { title: 'Actions', description: 'Game shortcut actions and social gestures are being prepared.' }, dreamIsland: { title: 'Dream Island', description: 'Dream Island information is being organized.' }, clothes: { title: 'Clothes', description: 'Outfit and clothing references are being prepared.' } } }, projectUpdates: { kicker: 'Project Updates', title: 'Project Updates', subtitle: 'Follow public site changes from the Pokopia Wiki source repository.', sourceRepository: 'Source repository', updatedAt: 'Updated {date}', openRepository: 'Open repository', commits: 'Commits', releases: 'Releases', viewCommit: 'View commit', viewRelease: 'View release', expandMessage: 'Expand', collapseMessage: 'Collapse', commitMessage: 'Commit message', loading: 'Loading project updates', retry: 'Retry', empty: 'No commits yet' }, legal: { lastUpdated: 'Last updated: May 3, 2026', sourceLinks: 'Source and reference links', privacy: { kicker: 'Legal', title: 'Privacy Policy', subtitle: 'How Pokopia Wiki handles account, contribution, and browsing information.', sections: { overview: { title: 'Overview', bodyOne: 'Pokopia Wiki is operated by Tootaio Studio as a community-editable game wiki.', bodyTwo: 'This policy explains the information used to provide accounts, wiki editing, community features, and site security.' }, information: { title: 'Information we collect', bodyOne: 'When you register, Pokopia Wiki collects your email address, display name, password hash, email verification state, session state, and referral information when a referral code is used.', bodyTwo: 'When you use the wiki, Pokopia Wiki may store your edits, uploads, Life posts, comments, reactions, discussion activity, public profile activity, and audit records needed to maintain community content.' }, storage: { title: 'Cookies and local storage', bodyOne: 'Pokopia Wiki uses browser storage for the selected language, login session token, Remember me choice, and local Daily CheckList completion state.', bodyTwo: 'Server-side sessions, verification tokens, reset tokens, and passwords are stored as hashes where applicable. Token hashes and password hashes are not exposed through public API responses.' }, content: { title: 'Community content and edit history', bodyOne: 'Wiki edits, image uploads, discussions, Life posts, reactions, and edit history may be visible to other users together with your display name and public profile link.', bodyTwo: 'Do not submit private personal information in public content. Moderation and audit records may be retained when needed for safety, integrity, dispute handling, or legal obligations.' }, sharing: { title: 'Service providers and safety', bodyOne: 'Pokopia Wiki may use hosting, database, email delivery, logging, and AI moderation providers to operate the service.', bodyTwo: 'Tootaio Studio does not sell personal information. Information may be disclosed when required by law, to protect site security, or to enforce the Terms of Service.' }, choices: { title: 'Your choices', bodyOne: 'You can log out to clear the active browser token, and registered users can update their display name and password from their profile.', bodyTwo: 'Contact Tootaio Studio for privacy questions or requests. Some records may be retained when they are needed for security, audit history, content integrity, or legal compliance.' } } }, terms: { kicker: 'Legal', title: 'Terms of Service', subtitle: 'Rules for using and contributing to Pokopia Wiki.', sections: { acceptance: { title: 'Agreement', bodyOne: 'By accessing or using Pokopia Wiki, you agree to these Terms of Service and to any rules shown in the product for accounts, edits, discussions, and community features.', bodyTwo: 'If you do not agree to these terms, do not use the site or submit content to Pokopia Wiki.' }, accounts: { title: 'Accounts', bodyOne: 'You are responsible for the accuracy of your account information and for keeping your login credentials secure.', bodyTwo: 'Editing and community actions may require a registered account, email verification, and the permissions assigned to that account.' }, contributions: { title: 'Contributions', bodyOne: 'You are responsible for the content you submit, including wiki edits, images, comments, Life posts, reactions, and discussion replies.', bodyTwo: 'By submitting content, you grant Tootaio Studio and the Pokopia Wiki community permission to host, display, reproduce, adapt, moderate, and maintain that content as part of the wiki. Do not submit content you do not have the right to share.' }, acceptableUse: { title: 'Acceptable use', bodyOne: 'Do not use Pokopia Wiki for harassment, spam, illegal activity, malware, deceptive content, rights infringement, or attempts to bypass authentication, rate limits, moderation, or security controls.', bodyTwo: 'Tootaio Studio may remove content, restrict features, or suspend access when needed to protect the wiki, its users, or third-party rights.' }, availability: { title: 'Availability and changes', bodyOne: 'Pokopia Wiki is provided as a community resource and may change, pause, or become unavailable without prior notice.', bodyTwo: 'Features, routes, data, moderation behavior, and account access may be updated as the project grows or as security, legal, or operational needs change.' }, changes: { title: 'Updates to these terms', bodyOne: 'Tootaio Studio may update these Terms of Service from time to time. The latest version will be posted on this page.', bodyTwo: 'Continuing to use Pokopia Wiki after changes are posted means you accept the updated terms.' } } }, disclaimers: { kicker: 'Legal', title: 'Disclaimers', subtitle: 'Important source, affiliation, accuracy, and rights notices for Pokopia Wiki.', sections: { community: { title: 'Community-maintained information', bodyOne: 'Pokopia Wiki is a community-maintained reference for Pokemon Pokopia game information.', bodyTwo: 'Content may be incomplete, outdated, speculative, or edited by community members. It is provided for general reference only.' }, affiliation: { title: 'No official affiliation', bodyOne: 'Pokopia Wiki is not affiliated with, sponsored by, endorsed by, or approved by Nintendo, The Pokemon Company, Game Freak, Creatures, PokeAPI, or pokopiawiki.com.', bodyTwo: 'Pokemon-related names, images, artwork, marks, characters, and game materials belong to their respective rights holders.' }, pokeapi: { title: 'PokeAPI data and images', bodyOne: 'Some Pokemon data and image resources used by Pokopia Wiki are sourced from or checked against PokeAPI and related PokeAPI repositories.', bodyTwo: 'PokeAPI project data and sprite repository materials may include their own license notices, while Pokemon names, images, and related intellectual property remain owned by their respective rights holders.' }, references: { title: 'Reference sources', bodyOne: 'Pokopia Wiki may refer to pokopiawiki.com and other public references when organizing game information.', bodyTwo: 'References are used for research, comparison, and attribution. Referencing a source does not imply affiliation, endorsement, sponsorship, or approval.' }, accuracy: { title: 'Accuracy and reliance', bodyOne: 'Game information can change, and community-maintained records may contain mistakes.', bodyTwo: 'Tootaio Studio does not guarantee that Pokopia Wiki content is complete, current, accurate, or error-free.' }, rights: { title: 'Rights concerns', bodyOne: 'If you believe content on Pokopia Wiki infringes rights, misattributes a source, or should be corrected, contact Tootaio Studio with the relevant page and source details.', bodyTwo: 'Tootaio Studio may update, remove, or restrict content when needed to address source, accuracy, safety, or rights concerns.' } }, sources: { pokeapiDocs: 'PokeAPI documentation', pokeapiApiDataLicense: 'PokeAPI API data license', pokeapiSpritesLicense: 'PokeAPI sprites license', pokemonLegal: 'Pokemon legal information', pokopiaWikiReference: 'pokopiawiki.com reference' } } }, profile: { title: 'User profile', subtitle: 'Manage your account details, referral, and password.', loading: 'Loading profile', accountSummary: 'Account summary', profileDetails: 'Profile details', displayNameHint: 'Display name is shown on edits, discussions, and Life posts.', displayNameRequired: 'Display name is required.', emailVerified: 'Email verified', emailUnverified: 'Email unverified', saved: 'Profile saved', saveFailed: 'Profile save failed', referralTitle: 'Referral', referralCode: 'Referral code', referralUrl: 'Invite link', referralHint: 'Share this link with new editors. Invites count after email verification.', verifiedReferralCount: 'Verified invites', copyReferralLink: 'Copy link', referralCopied: 'Referral link copied', referralCopyFailed: 'Referral link copy failed', referralLoadFailed: 'Referral details failed to load', publicSubtitle: 'Review this member\'s Life posts, reactions, comments, and Wiki contributions.', publicKicker: 'Member Profile', tabsLabel: 'Profile sections', tabFeeds: 'Feeds', tabContributions: 'Contributions', tabReactions: 'Reactions', tabComments: 'Comments', tabAccount: 'Account', contributionFiltersLabel: 'Contribution categories', contributionConfig: 'Config', contributionsFilterEmpty: 'No contributions in this category', reactionFiltersLabel: 'Reaction categories', reactionsFilterEmpty: 'No reactions in this category', commentFiltersLabel: 'Comment categories', commentsFilterEmpty: 'No comments in this category', lifeCommentCategory: 'Life', discussionCommentCategory: 'Wiki', passwordTitle: 'Change password', passwordHint: 'Use at least 8 characters.', passwordSaved: 'Password updated', passwordSaveFailed: 'Password update failed', savePassword: 'Save password', follow: 'Follow', followBack: 'Follow back', following: 'Following', friend: 'Friend', followers: 'Followers', followingCount: 'Following', friends: 'Friends', followFailed: 'Follow action failed', joinedAt: 'Joined {date}', lifePosts: 'Life posts', lifeComments: 'Life comments', lifeReactions: 'Reactions', discussionComments: 'Wiki comments', commentsMade: 'Comments', wikiEdits: 'Wiki edits', wikiCreates: 'Creates', wikiUpdates: 'Updates', wikiDeletes: 'Deletes', imageUploads: 'Images', wikiContributionStats: 'Wiki contribution stats', communityStats: 'Community stats', contributionBreakdown: 'Contribution breakdown', total: 'Total', otherContributions: 'Other', feedsEmpty: 'No Life posts yet', contributionsEmpty: 'No Wiki contributions yet', reactionsEmpty: 'No reactions yet', commentsEmpty: 'No comments yet', loadMore: 'Load more', lifeComment: 'Life comment', discussionComment: 'Wiki discussion', lifePostTarget: 'Life post', lifePostBy: 'Life post by {name}' }, pokemon: { title: 'Pokemon', subtitle: 'Search Pokemon and filter by specialities, ideal habitat, and favourites.', listKicker: 'Pokédex', detailKicker: 'Pokédex Detail', editKicker: 'Pokédex Edit', editSubtitle: 'Maintain Pokemon profile, details, types, stats, specialities, and favourites.', editSections: 'Pokemon edit sections', editTabBasic: 'Basic', editTabAdvance: 'Advance', newTitle: 'New Pokemon', editTitle: 'Edit #{id} {name}', id: 'Pokopia ID', fetchData: 'Fetch data', fetchingData: 'Fetching', fetchIdentifier: 'Data identifier', fetchIdentifierPlaceholder: 'bulbasaur or 1', fetchIdentifierRequired: 'Enter a Pokemon identifier', fetchFailed: 'Pokemon data fetch failed', fetchIdMismatch: 'Fetched official ID #{id} does not match this editor.', fetchResults: 'Pokemon data results', fetchSearching: 'Searching data', fetchNoMatches: 'No matching Pokemon data', fetchSearchFailed: 'Pokemon data search failed', image: 'Image', fetchImages: 'Fetch images', fetchingImages: 'Fetching', imageFetchFailed: 'Pokemon image fetch failed', imageNoMatches: 'No available Pokemon images', loadingImages: 'Loading Pokemon images', selectedImage: 'Selected Pokemon image', imageOptions: 'Pokemon image options', clearImage: 'Clear image', imageEmpty: 'No Pokemon image selected', imageAlt: '{name} {variant} image', eventItem: 'Event Pokemon', loadingList: 'Loading Pokemon list', loadingDetail: 'Loading Pokemon detail', loadingEdit: 'Loading Pokemon editor', environmentPrefix: 'Ideal Habitat: {name}', details: 'Details', genus: 'Genus', height: 'Height', heightInput: 'Height (in)', heightImperial: 'ft / in', heightMetric: 'm', feet: 'ft', inches: 'in', meters: 'm', weight: 'Weight', weightInput: 'Weight (lb)', pounds: 'lb', kilograms: 'kg', measurements: 'Height & Weight', types: 'Types', typeOne: 'Type 1', typeTwo: 'Type 2', typesAndStats: 'Types & Base stats', statsTitle: 'Base stats', stats: { hp: 'HP', attack: 'Attack', defense: 'Defense', specialAttack: 'Special Attack', specialDefense: 'Special Defense', speed: 'Speed' }, environment: 'Ideal Habitat', skills: 'Specialities', skillMatchMode: 'Speciality match mode', any: 'Any', all: 'All', favoriteThings: 'Favourites', favoriteThingMatchMode: 'Favourites match mode', skillDrops: 'Speciality drops', skillDrop: '{name} drop', dropItem: 'Drop item', trading: 'Trading', tradingItems: 'Trading items', tradingLikes: 'Likes', tradingNeutral: 'Neutral', tradingPriceBonus: '1.5x price', tradingSelectedCount: '{count} selected', tradingModalSubtitle: 'Likes: 1.5x price · Neutral: no bonus', tradingAvailableItems: 'Available trading items', tradingSelectedItems: 'Selected trading items', tradingPreferenceFor: 'Trading preference for {name}', tradingDefaultGroup: 'Default add target', manageTrading: 'Manage trading', searchPokemon: 'Search Pokemon', relatedPokemon: 'Related Pokemon', relatedHabitat: 'Related Pokemon habitat', relatedItems: 'Related items', relatedItemCategory: 'Related item category', habitats: 'Habitats', namePlaceholder: 'Name', searchTypes: 'Search types', searchEnvironment: 'Search ideal habitats', searchSkills: 'Search specialities', searchFavoriteThings: 'Search favourites', searchItems: 'Search items' }, eventPokemon: { title: 'Event Pokemon', subtitle: 'Search Event Pokemon and filter by specialities, ideal habitat, and favourites.', kicker: 'Event Pokédex', detailKicker: 'Event Pokemon Detail', editSubtitle: 'Maintain Event Pokemon profile, Pokopia ID, official data identity, images, stats, specialities, and favourites.', newTitle: 'New Event Pokemon', editTitle: 'Edit Event #{id} {name}', loadingList: 'Loading Event Pokemon list' }, habitats: { title: 'Habitats', subtitle: 'View recipes and Pokemon that may appear.', listKicker: 'Habitats', detailKicker: 'Habitat Detail', detailSubtitle: 'Habitat detail', editSubtitle: 'Maintain habitat recipes and possible Pokemon appearances.', newTitle: 'New habitat', editTitle: 'Edit {name}', fallbackName: 'Habitat', loadingList: 'Loading habitat list', loadingDetail: 'Loading habitat detail', loadingEdit: 'Loading habitat editor', eventItem: 'Event Habitat', recipe: 'Recipe', recipeList: 'Recipe list', possiblePokemon: 'Possible Pokemon', addItem: 'Add item', addPokemon: 'Add Pokemon', maps: 'Maps', searchMaps: 'Search maps' }, eventHabitats: { title: 'Event Habitats', subtitle: 'View limited habitats, event recipes, and Pokemon that may appear.', kicker: 'Event Habitats', detailKicker: 'Event Habitat Detail', editSubtitle: 'Maintain Event Habitat recipes, possible Pokemon appearances, and image.', newTitle: 'New Event Habitat', editTitle: 'Edit Event Habitat {name}', loadingList: 'Loading Event Habitat list' }, items: { title: 'Items', subtitle: 'Browse items by category, usage, and tags.', kicker: 'Items', detailKicker: 'Item Detail', detailSubtitle: 'Item detail', editKicker: 'Item Edit', editSubtitle: 'Maintain item base price, category, usage, acquisition methods, customization, and tags.', newTitle: 'New item', editTitle: 'Edit {name}', fallbackName: 'Item', loadingList: 'Loading item list', loadingDetail: 'Loading item detail', loadingEdit: 'Loading item editor', description: 'Description', basePrice: 'Base Price', ancientArtifact: 'Ancient Artifact', category: 'Category', usage: 'Usage', tags: 'Tags', acquisitionMethods: 'Acquisition methods', customization: 'Customization', dyeable: 'Dyeable', dualDyeable: 'Dual dyeable', patternEditable: 'Pattern editable', noRecipe: 'No recipe', eventItem: 'Event item', recipeInfo: 'Recipe info', relatedRecipes: 'Related recipes', relatedHabitats: 'Related habitats', pokemonDrops: 'Pokemon drops', possibleTags: 'Possible Tags', highlyLikelyTags: 'Highly likely', possibleTagsPossible: 'Possible', excludedTags: 'Excluded', possibleTagsEvidence: 'Evidence', createRecipe: 'Create recipe', addItem: 'Add item', createDefaultsMenu: 'New item options', createDefaultsTitle: 'Session defaults', clearCreateDefaults: 'Clear defaults', itemActions: 'Item actions', insertBeforeItem: 'Insert before', insertAfterItem: 'Insert after', searchCategory: 'Search categories', searchUsage: 'Search usages', searchMethods: 'Search acquisition methods', searchTags: 'Search tags' }, eventItems: { title: 'Event Items', subtitle: 'Browse event items by category, usage, and tags.', kicker: 'Event Items', detailKicker: 'Event Item Detail', editSubtitle: 'Maintain event item base price, category, usage, acquisition methods, customization, and tags.', newTitle: 'New event item' }, ancientArtifacts: { title: 'Ancient Artifacts', subtitle: 'Browse Ancient Artifacts by relic, fossil category, and tags.', kicker: 'Ancient Artifacts', detailKicker: 'Ancient Artifact Detail', detailSubtitle: 'Ancient Artifact detail', editKicker: 'Ancient Artifact Edit', editSubtitle: 'Maintain the item details, base price, category, usage, Ancient Artifact classification, and tags.', newTitle: 'New Ancient Artifact', editTitle: 'Edit {name}', fallbackName: 'Ancient Artifact', loadingList: 'Loading Ancient Artifact list', loadingDetail: 'Loading Ancient Artifact detail', loadingEdit: 'Loading Ancient Artifact editor', description: 'Description', category: 'Category', tags: 'Tags', searchCategory: 'Search categories', searchTags: 'Search tags' }, recipes: { title: 'Recipes', subtitle: 'Browse recipes by category, usage, and tags.', detailKicker: 'Recipe Detail', detailSubtitle: 'Recipe detail', editKicker: 'Recipe Edit', editSubtitle: 'Maintain result item, acquisition methods, and materials.', newTitle: 'New recipe', editTitle: 'Edit {name}', fallbackName: 'Recipe', loadingList: 'Loading recipe list', loadingDetail: 'Loading recipe detail', loadingEdit: 'Loading recipe editor', item: 'Item', materials: 'Materials', addMaterial: 'Add material' }, dish: { kicker: 'Dish', title: 'Dish', subtitle: 'Browse cooked dishes by category, cookware, ingredients, flavor, and Mosslax effects.', loading: 'Loading Dish records', category: 'Category', categories: 'Categories', dishes: 'Dishes', cookware: 'Cookware', effect: 'Effect', totalMaterialQuantity: 'Total material count', dishItem: 'Dish item', flavor: 'Flavor', mainMaterial: 'Main material', secondaryMaterial: 'Secondary material', secondaryMaterials: 'Secondary materials', secondSecondaryMaterial: 'Second secondary material', pokemonSkill: 'Pokemon speciality', mosslaxEffect: 'Mosslax effect', newCategory: 'New category', editCategory: 'Edit category', newDish: 'New dish', editDish: 'Edit dish' }, comingSoon: { status: 'In development', heading: 'This wiki section is being prepared.', previewLabel: 'Section preview', sections: { automation: { kicker: 'Automation', title: 'Automation', subtitle: 'Factory and automation base guides will be shared here.', body: 'Automation pages will help players compare production setups, material outputs, required Pokemon, production order, and shared favourites.', preview: { one: 'Factory guides will focus on the base layout and production goal.', two: 'Material output, Pokemon needs, and production order will stay easy to scan.', three: 'Shared favourite items can help players plan compatible teams and workflows.' } }, dish: { kicker: 'Dish', title: 'Dish', subtitle: 'A future home for cooked dishes and food discoveries.', body: 'Dish pages are being shaped for clear browsing, source notes, and useful ingredient links.', preview: { one: 'Dish records will focus on names, effects, and discovery context.', two: 'Ingredient relationships will connect back to items and recipes where useful.', three: 'The page will stay browse-first so community edits can grow naturally.' } }, events: { kicker: 'Events', title: 'Events', subtitle: 'Seasonal and limited-time game activity records are coming later.', body: 'Events will collect timing, rewards, and participation details once the section is ready.', preview: { one: 'Event cards will make dates and active windows easy to scan.', two: 'Rewards and related items will sit close to the event summary.', three: 'Archived activities will remain readable after they end.' } }, actions: { kicker: 'Actions', title: 'Actions', subtitle: 'Game shortcut actions such as waving and dancing will be documented here.', body: 'Actions are being prepared as a quick reference for expressive in-game gestures and shortcuts.', preview: { one: 'Each action will describe the gesture or shortcut in player-facing language.', two: 'Common examples include waving, dancing, and other social actions.', three: 'Related unlock or usage details can be linked when the data model is ready.' } }, dreamIsland: { kicker: 'Dream Island', title: 'Dream Island', subtitle: 'Dream Island information is being organized for future browsing.', body: 'This area will present island details with a calm, destination-style layout when content is ready.', preview: { one: 'Island notes will prioritize location, availability, and notable discoveries.', two: 'Related Pokemon, items, or activities can be connected from the page.', three: 'The layout will support browsing without adding another management flow yet.' } }, clothes: { kicker: 'Clothes', title: 'Clothes', subtitle: 'Outfit and clothing references are being prepared.', body: 'Clothes pages will make it easy to compare appearance, acquisition, and customization details.', preview: { one: 'Clothing entries will focus on display names and visual categories.', two: 'Acquisition and customization details can be connected when available.', three: 'The page will keep item-like details readable without mixing them into the item list.' } } } }, checklist: { title: 'Daily checklist', subtitle: 'See what can be completed each day.', sectionTitle: 'Daily tasks', empty: 'No daily checklist', loading: 'Loading daily checklist', task: 'Task', newTask: 'New task', editTask: 'Edit task' }, life: { title: 'Life', subtitle: 'Share favourite thoughts, tips, and community finds.', kicker: 'Community Feed', detailTitle: 'Life Post', detailSubtitle: 'Read this community post and its discussion.', detailKicker: 'Life Detail', backToLife: 'Back to Life', viewPost: 'View post', composerTitle: 'Share something', composerPrompt: 'What would you like to share?', bodyLabel: 'Post', bodyPlaceholder: 'Share a thought, tip, or discovery...', newPost: 'New Post', category: 'Category', gameVersion: 'Game version', versionPlaceholder: 'No version', searchVersions: 'Search versions', languages: 'Languages', allLanguages: 'All languages', allCategories: 'All', feedScope: 'Feed scope', allFeed: 'All feed', followingFeed: 'Following', allVersions: 'All versions', versionFilter: 'Version', ratingFilter: 'Rating', allRatingModes: 'All posts', rateableOnly: 'Rateable only', notRateableOnly: 'Not rateable', sort: 'Sort', sortLatest: 'Latest', sortOldest: 'Oldest', sortTopRated: 'Top rated', sortMostLiked: 'Most liked', sortMostReplied: 'Most replied', categoryPlaceholder: 'Select category', searchCategories: 'Search categories', search: 'Search Life', searchPlaceholder: 'Search post content...', clearSearch: 'Clear search', searchEmpty: 'No posts match your search', searchEmptyHint: 'Try another keyword or clear the search.', comments: 'Comments', commentsCount: '{count} comments', comment: 'Comment', hideComments: 'Hide comments', react: 'Like', reactions: 'Reactions', reactionsCount: '{count} reactions', reactionCountLabel: '{reaction}: {count}', reactionLike: 'Like', reactionHelpful: 'Helpful', reactionFun: 'Fun', reactionThanks: 'Thanks', chooseReaction: 'Choose reaction', reactionMenu: 'Reaction menu', reactionUsersTitle: 'Reactions', reactionUsersSubtitle: 'People who reacted to this Life post.', reactionFiltersLabel: 'Reaction types', allReactions: 'All reactions', reactionUsersEmpty: 'No reactions yet', loadMoreReactions: 'Load more reactions', removeReaction: 'Remove reaction', reactionFailed: 'Reaction failed', postMeta: 'Post details', changeLog: 'ChangeLog', rating: 'Rating', setRating: 'Rate {count} stars', removeRating: 'Remove rating', ratingAverage: '{average} average from {count} ratings', noRatings: 'No ratings yet', ratingFailed: 'Rating failed', commentPlaceholder: 'Write a comment...', commentReplyPlaceholder: 'Write a reply...', postComment: 'Post comment', postingComment: 'Posting comment', reply: 'Reply', postReply: 'Post reply', postingReply: 'Posting reply', cancelReply: 'Cancel reply', noComments: 'No comments yet', loadingComments: 'Loading comments', loadMoreComments: 'Load more comments', deleteComment: 'Delete comment', deleteCommentConfirm: 'Delete this comment?', commentDeleted: 'Comment deleted', restoreComment: 'Undo', likeComment: 'Like comment', unlikeComment: 'Unlike comment', commentLikeCount: '{count} likes', commentLikeFailed: 'Like failed', commentRequired: 'Please enter a comment.', commentFailed: 'Comment failed', replyFailed: 'Reply failed', deleteCommentFailed: 'Delete comment failed', restoreCommentFailed: 'Undo failed', publish: 'Post', publishing: 'Posting', update: 'Update', updating: 'Updating', cancelEdit: 'Cancel edit', empty: 'No posts yet', emptyHint: 'Verified members can start the first Life post.', loading: 'Loading Life feed', retryFeed: 'Retry loading', loginPrompt: 'Log in with a verified email to post.', verifyPrompt: 'Complete email verification to post.', editPost: 'Edit post', deletePost: 'Delete post', saveEdit: 'Save edit', postFailed: 'Post failed', saveFailed: 'Save failed', deleteFailed: 'Delete failed', bodyRequired: 'Please enter a post.', categoryRequired: 'Please select a category.', byUnknown: 'Community member', edited: 'Edited', deleteConfirm: 'Delete this post?', moderationUnreviewed: 'Not reviewed', moderationReviewing: 'Reviewing', moderationApproved: 'Approved', moderationRejected: 'Rejected', moderationFailed: 'Review failed', moderationReason: 'Review detail', moderationRetry: 'Retry review', moderationRetrying: 'Retrying', moderationRetryFailed: 'Review retry failed', charactersLeft: '{count} characters left' }, admin: { title: 'Admin', subtitle: 'Manage Wiki content, configuration, localization, and access.', modules: 'Admin modules', contentGroup: 'Content', configurationGroup: 'Configuration', localizationGroup: 'Localization', accessGroup: 'Access', loading: 'Loading admin list', users: 'Users', roles: 'Roles', permissions: 'Permissions', config: 'System config', configType: 'System config type', checklist: 'CheckList', pokemonList: 'Pokemon list', itemList: 'Item list', ancientArtifactList: 'Ancient Artifact list', recipeList: 'Recipe list', dishList: 'Dish list', habitatList: 'Habitat list', dataTools: 'Data tools', dataToolRefresh: 'Refresh', dataToolExport: 'Export data', dataToolExportButton: 'Export JSON', dataToolImport: 'Import data', dataToolImportButton: 'Import', dataToolImportFile: 'Data bundle', dataToolImportMode: 'Import replaces the scopes included in the bundle.', dataToolItemsCsvFile: 'Items CSV', dataToolItemsCsvMode: 'CSV import adds Items only. Wipe Items first when replacing the list.', dataToolItemsCsvImported: 'Items CSV imported.', dataToolHabitatsCsvFile: 'Habitats CSV', dataToolHabitatsCsvMode: 'CSV import adds Habitats only. Wipe Habitats first when replacing the list.', dataToolHabitatsCsvImported: 'Habitats CSV imported.', dataToolWipe: 'Wipe data', dataToolWipeButton: 'Wipe', dataToolSelectScope: 'Select at least one data scope.', dataToolInvalidBundle: 'Data bundle is invalid.', dataToolImportConfirm: 'Import will replace: {scopes}.', dataToolWipeConfirm: 'Wipe will delete: {scopes}.', dataToolConfirmImport: 'Type IMPORT to confirm', dataToolConfirmWipe: 'Type WIPE to confirm', dataToolDependencyNote: 'Items include Recipes because recipes depend on items.', dataToolReplaceNote: 'Related records, translations, edit history, image history, and discussions are included.', dataToolUploadsNote: 'Uploaded files are not included in JSON exports.', dataToolScopePokemon: 'Pokemon', dataToolScopeHabitats: 'Habitats', dataToolScopeItems: 'Items', dataToolScopeArtifacts: 'Ancient Artifacts', dataToolScopeRecipes: 'Recipes', dataToolScopeChecklist: 'Daily CheckList', languages: 'Languages', newConfig: 'New {name}', editConfig: 'Edit {name}', hasItemDrop: 'Has item drop', hasTrading: 'Has trading', rateableCategory: 'Rateable', changeLog: 'ChangeLog', dragSort: 'Drag to reorder: {name}', dragSortTitle: 'Drag to reorder', languageCode: 'Code', languageName: 'Language name', enabled: 'Enabled', defaultLanguage: 'Default language', defaultCategory: 'Default category', sortOrder: 'Sort order', newLanguage: 'New language', editLanguage: 'Edit language', wordings: 'System wordings', aiModeration: 'AI moderation', aiModerationEnabled: 'Enabled', aiModerationFormat: 'API format', aiModerationFormatGemini: 'Gemini generateContent', aiModerationFormatOpenAi: 'OpenAI-compatible chat completions', aiModerationAuthMode: 'Auth mode', aiModerationAuthQueryKey: 'Query key', aiModerationAuthBearer: 'Bearer token', aiModerationEndpoint: 'End Point', aiModerationModel: 'Model', aiModerationRpm: 'Requests per minute', aiModerationApiKey: 'API Key', aiModerationApiKeyConfigured: 'API Key configured', aiModerationApiKeyMissing: 'API Key missing', aiModerationClearApiKey: 'Clear saved API Key', aiModerationSettings: 'AI moderation settings', rateLimits: 'Rate limits', rateLimitMaxRequests: 'Max requests', rateLimitWindowMinutes: 'Window minutes', rateLimitCooldownSeconds: 'Cooldown seconds', rateLimitAccountWrite: 'Account writes', rateLimitAdminWrite: 'Management writes', rateLimitWikiWrite: 'Wiki content writes', rateLimitCommunityWrite: 'Community writes', rateLimitCommunityReaction: 'Community reactions', rateLimitUpload: 'Uploads', rateLimitFetch: 'Pokemon fetch', wordingLocale: 'Locale', wordingModule: 'Module', wordingSurface: 'Surface', wordingMissingOnly: 'Missing only', wordingKey: 'Key', wordingValue: 'Wording', defaultValue: 'Default wording', placeholders: 'Placeholders', missingTranslation: 'Missing translation', allModules: 'All modules', allSurfaces: 'All surfaces', surfaceFrontend: 'Frontend', surfaceBackend: 'Backend', surfaceEmail: 'Email', editWording: 'Edit wording', userRoles: 'User roles', noRoles: 'No roles', newRole: 'New role', editRole: 'Edit role', roleKey: 'Role key', roleName: 'Role name', description: 'Description', level: 'Level', disabled: 'Disabled', systemRole: 'System role', roleLevel: 'Level {level}', permissionCount: '{count} permissions', rolePermissions: 'Role permissions', newPermission: 'New permission', editPermission: 'Edit permission', permissionKey: 'Permission key', permissionName: 'Permission name', category: 'Category', systemPermission: 'System permission' } }, config: { pokemonTypes: 'Pokemon Types', skills: 'Specialities', environments: 'Ideal Habitats', favoriteThings: 'Favourites / tags', acquisitionMethods: 'Acquisition methods', maps: 'Maps', lifeCategories: 'Life categories', gameVersions: 'Game versions', dishFlavors: 'Dish flavors' }, appearance: { time: 'Time', weather: 'Weather', rarity: 'Rarity', map: 'Map', maps: 'Maps', morning: 'Morning', noon: 'Noon', evening: 'Evening', night: 'Night', sunny: 'Sunny', cloudy: 'Cloudy', rainy: 'Rainy', stars: '{count} stars' }, history: { title: 'Contribution records', createdBy: 'Created by', lastEdited: 'Last edited', editHistory: 'Edit history', before: 'Before', after: 'After', author: 'Author', time: 'Time', action: 'Action', create: 'Create', update: 'Edit', delete: 'Delete', empty: 'No edit history' }, media: { image: 'Image', imageAlt: '{name} image', uploadImage: 'Upload image', uploading: 'Uploading', uploadedImage: 'Uploaded image', selectedImage: 'Selected image', imageHistory: 'Image history', imageOptions: 'Image options', clearImage: 'Clear image', imageEmpty: 'No image selected', imageHistoryEmpty: 'No uploaded images', uploadFailed: 'Image upload failed', selectImage: 'Select image' }, discussion: { title: 'Discussion', count: '{count} comments', comment: 'Comment', commentPlaceholder: 'Write a comment...', replyPlaceholder: 'Write a reply...', postComment: 'Post comment', postingComment: 'Posting comment', reply: 'Reply', postReply: 'Post reply', postingReply: 'Posting reply', cancelReply: 'Cancel reply', deleteComment: 'Delete comment', deleteConfirm: 'Delete this comment?', deletedComment: 'Comment deleted', commentRequired: 'Please enter a comment.', commentFailed: 'Comment failed', replyFailed: 'Reply failed', deleteFailed: 'Delete failed', languages: 'Languages', allLanguages: 'All languages', moderationUnreviewed: 'Not reviewed', moderationReviewing: 'Reviewing', moderationApproved: 'Approved', moderationRejected: 'Rejected', moderationFailed: 'Review failed', moderationReason: 'Review detail', moderationRetry: 'Retry review', moderationRetrying: 'Retrying', moderationRetryFailed: 'Review retry failed', loading: 'Loading discussion', loadMore: 'Load more comments', sort: 'Sort', sortOldest: 'Oldest', sortLatest: 'Latest', sortMostLiked: 'Most liked', sortMostReplied: 'Most replied', likeComment: 'Like comment', unlikeComment: 'Unlike comment', commentLikeCount: '{count} likes', commentLikeFailed: 'Like failed', empty: 'No discussion yet', emptyHint: 'Start a new discussion now.', loginPrompt: 'Log in with a verified email to comment.', verifyPrompt: 'Complete email verification to comment.', byUnknown: 'Community member', charactersLeft: '{count} characters left' }, server: { errors: { foreignKey: 'Referenced data does not exist or the record is currently in use', duplicate: 'A record with the same name or ID already exists', invalidField: 'Field value is invalid', serverError: 'Server error', loginRequired: 'Please log in first', verifyEmailFirst: 'Please complete email verification first', permissionDenied: 'Permission denied', notFound: 'Not found', rateLimited: 'Too many requests. Please try again later.' }, auth: { emailRequired: 'Email is required', invalidEmail: 'Email format is invalid', displayNameRequired: 'Display name is required', displayNameLength: 'Display name must be 1 to 40 characters', passwordLength: 'Password must be at least 8 characters', invalidToken: 'The verification link is invalid or expired', emailAlreadyRegistered: 'This email is already registered', checkVerificationEmail: 'Please check your verification email', emailVerified: 'Email verified', checkPasswordResetEmail: 'If an account uses this email, a password reset link will be sent.', passwordResetComplete: 'Password updated. You can log in with the new password.', passwordChanged: 'Password updated.', invalidCredentials: 'Email or password is incorrect', verifyEmailFirst: 'Please complete email verification first', invalidResetToken: 'The password reset link is invalid or expired', currentPasswordInvalid: 'Current password is incorrect', invalidReferralCode: 'Referral code is invalid', cannotFollowSelf: 'You cannot follow yourself', emailDeliveryUnavailable: 'Email delivery is temporarily unavailable. Please try again later.' }, validation: { nameRequired: 'Name is required', recordMissing: 'Record does not exist', languageCodeInvalid: 'Language code is invalid', languageNameRequired: 'Language name is required', defaultLanguageMustBeEnglish: 'Default language must be English', defaultLanguageMustBeEnabled: 'Default language must be enabled', languageNotFound: 'Language not found', defaultLanguageRequired: 'A default language is required', defaultLanguageCannotBeDeleted: 'Default language cannot be deleted', selectLanguage: 'Please select a language', languageDoesNotExist: 'Language does not exist', pokemonIdentifierRequired: 'Pokemon identifier is required', pokemonTypeDataUnavailable: 'Pokemon type data is unavailable', pokemonDataNotFound: 'Pokemon data was not found', pokemonDataIdMismatch: 'Official Pokemon data ID does not match this Pokemon', dataToolScopeRequired: 'Select at least one data scope', dataToolScopeInvalid: 'Data scope is invalid', dataToolBundleInvalid: 'Data bundle is invalid', dataToolItemsCsvInvalid: 'Items CSV is invalid', dataToolHabitatsCsvInvalid: 'Habitats CSV is invalid', pokemonImagePathInvalid: 'Pokemon image path is invalid', imagePathInvalid: 'Image path is invalid', imageUploadRequired: 'Please select an image', imageUploadTypeInvalid: 'Image type is not supported', imageUploadContentInvalid: 'Image file is invalid', imageUploadEntityNameRequired: 'Please enter a name before uploading an image', imageUploadFailed: 'Image upload failed', taskRequired: 'Please enter a task', selectTask: 'Please select a task', taskDoesNotExist: 'Task does not exist', postRequired: 'Please enter a post', postTooLong: 'Post is too long', lifeCategoryRequired: 'Please select a category', lifeCategoryInvalid: 'Category is invalid', gameVersionInvalid: 'Game version is invalid', commentRequired: 'Please enter a comment', commentTooLong: 'Comment is too long', reactionInvalid: 'Reaction is invalid', ratingInvalid: 'Rating is invalid', cursorInvalid: 'Cursor is invalid', tagInvalid: 'Tag is invalid', entityTypeInvalid: 'Entity type is invalid', recordInvalid: 'Record is invalid', commentInvalid: 'Comment is invalid', selectRecord: 'Please select a record', typeMin: 'Choose at least 1 type', typeMax: 'Choose at most 2 types', skillMax: 'Choose at most 2 specialities', favoriteMax: 'Choose at most 6 favourites', dropItemSelectedSkill: 'Drop items must be linked to selected specialities', pokemonIdRequired: 'Pokopia ID is required', pokemonNameRequired: 'Pokemon name is required', heightNonNegative: 'Height must be a non-negative number', weightNonNegative: 'Weight must be a non-negative number', environmentRequired: 'Ideal Habitat is required', skillNoDrop: 'This speciality cannot have a drop item', habitatNameRequired: 'Habitat name is required', usageRequired: 'Usage is required', itemNameRequired: 'Item name is required', categoryRequired: 'Category is required', artifactNameRequired: 'Ancient Artifact name is required', recipeFreeWithRecipe: 'An item with a recipe cannot be marked as recipe-free', itemRequired: 'Item is required', recipeFreeItem: 'This item is marked as recipe-free', statNonNegative: 'Base stat must be a non-negative integer', pokemonDataFileEmpty: 'Pokemon data file is empty', pokemonDataFileUnavailable: 'Pokemon data file is unavailable' }, wordings: { keyNotFound: 'System wording key was not found', localeRequired: 'Locale is required', valueRequired: 'Wording is required', placeholderMismatch: 'Placeholders must match the default wording' }, permissions: { nameRequired: 'Name is required', valueTooLong: 'Value is too long', invalidSelection: 'Selection is invalid', roleKeyInvalid: 'Role key is invalid', roleNotFound: 'Role not found', ownerRequired: 'At least one Owner is required', ownerRoleLocked: 'Owner role permissions cannot be edited', ownerRoleOperationDenied: 'Only Owners with Owner assignment permission can assign or remove the Owner role', roleLevelOperationDenied: 'You can only assign or remove roles below your highest role level', permissionKeyInvalid: 'Permission key is invalid', permissionNotFound: 'Permission not found', criticalPermissionRequired: 'Critical administration permissions must remain enabled', permissionManagerRequired: 'At least one verified user must be able to manage permissions', userNotFound: 'User not found' } }, email: { auth: { kicker: 'Account security', linkFallback: 'If the button does not work, copy and paste this link into your browser:', footer: 'You received this automated email because an account action was requested for Pokopia Wiki.', verificationSubject: 'Verify your Pokopia Wiki email', verificationActionLabel: 'Verify email', verificationHtml: '

Welcome to Pokopia Wiki. Confirm this email address to finish setting up your account and unlock verified editing.

Verify email

This secure link expires in {hours} hours.

', verificationText: 'Verify your Pokopia Wiki email: {url}\nThis secure link expires in {hours} hours.', passwordResetSubject: 'Reset your Pokopia Wiki password', passwordResetActionLabel: 'Reset password', passwordResetHtml: '

Use this secure link to choose a new password for your Pokopia Wiki account.

Reset password

This link expires in {hours} hours. If you did not request this, you can ignore this email.

', passwordResetText: 'Reset your Pokopia Wiki password: {url}\nThis link expires in {hours} hours. If you did not request this, you can ignore this email.' } }, }, 'zh-CN': { common: { add: '添加', admin: '管理', all: '全部', back: '返回', backToList: '返回列表', cancel: '取消', close: '关闭', create: '创建', delete: '删除', edit: '编辑', details: '详情', filters: '筛选', loading: '加载中', name: '名称', new: '新建', no: '否', none: '无', save: '保存', saving: '保存中', search: '搜索', select: '请选择', selected: '已选', system: '系统', noRecords: '暂无记录', fieldForLanguage: '{field}({language})', searchOrSelect: '搜索或选择', noMatches: '没有匹配项', createNamed: '添加「{name}」', creating: '添加中', inDev: '开发中', removeNamed: '移除{name}', quantity: '数量', eventItem: '活动物品', required: '必填' }, nav: { home: '首页', pokedex: 'Pokedex', habitatDex: 'Habitat Dex', collections: 'Collections', mainGame: 'Main Game', event: 'Event', pokemon: 'Pokemon', eventPokemon: 'Event Pokemon', habitats: '栖息地', eventHabitats: 'Event Habitats', items: '物品', eventItems: 'Event Items', ancientArtifacts: 'Ancient Artifacts', recipes: '材料单', automation: '自动化', dish: '料理', events: '活动', actions: '动作', dreamIsland: 'Dream Island', clothes: '服装', checklist: 'CheckList', life: 'Life', admin: '管理', main: '主导航', openMenu: '打开导航', closeMenu: '关闭导航', collapseSidebar: '收起侧边栏', expandSidebar: '展开侧边栏', language: '语言', profile: '个人资料', login: '登录', logout: '退出', register: '注册' }, search: { label: '搜索 Pokopia Wiki', placeholder: '搜索 Wiki', open: '打开搜索', clear: '清空搜索', empty: '没有匹配结果', failed: '搜索暂不可用', groups: { pokemon: 'Pokemon', habitats: '栖息地', items: '物品', ancientArtifacts: 'Ancient Artifacts', recipes: '材料单', dailyChecklist: '每日 CheckList', life: 'Life', users: '用户' } }, notifications: { title: '通知', open: '打开通知', unreadCount: '{count} 条未读', markAllRead: '全部已读', markRead: '标为已读', loadMore: '加载更多', emptyTitle: '暂无通知', emptyBody: '评论、Reaction 和审核结果会显示在这里。', systemActor: 'Pokopia Wiki', targetLifePost: 'Life 动态', targetLifeComment: 'Life 评论', targetDiscussionComment: '讨论评论', targetProfile: '个人主页', lifePostComment: '{actor} 评论了你的 Life 动态', lifeCommentReply: '{actor} 回复了你的 Life 评论', discussionCommentReply: '{actor} 回复了你的讨论评论', lifePostReaction: '{actor} 用 {reaction} Reaction 了你的 Life 动态', userFollow: '{actor} 关注了你', moderationApproved: '你的{target}已审核通过', moderationRejected: '你的{target}未通过审核', moderationFailed: '你的{target}审核失败' }, legal: { footer: { copyright: 'Copyright {year} Tootaio Studio. All rights reserved.', linksLabel: '法律页面', privacy: '隐私政策', terms: '服务条款', disclaimers: '免责声明', notice: 'Pokopia Wiki 使用社区贡献和第三方参考资料,包括 PokeAPI 数据与图片资源。Pokemon 相关名称、图片和标志归其各自权利人所有。' } }, seo: { siteDescription: '浏览 Pokopia Wiki 的 Pokemon、Event Pokemon、栖息地、Event Habitats、物品、Event Items、Ancient Artifacts、材料单、每日清单和 Life 社区动态。', pokemonDetailDescription: '查看 {name} 在 Pokopia Wiki 中的栖息地、属性、特长、喜欢的东西、六维、相关物品、讨论和编辑历史。', itemDetailDescription: '查看 {name} 在 Pokopia Wiki 中的基础价格、分类、用途、入手方式、自定义、相关材料单、栖息地和 Pokemon 掉落。', ancientArtifactDetailDescription: '查看 {name} 在 Pokopia Wiki 中的分类、标签、介绍、讨论和编辑历史。', habitatDetailDescription: '查看 {name} 在 Pokopia Wiki 中的配方、可能出现的 Pokemon、地图、时间、天气、讨论和编辑历史。', recipeDetailDescription: '查看 {name} 材料单在 Pokopia Wiki 中的结果物品、入手方式、需要材料、讨论和编辑历史。' }, auth: { accountAccess: 'Trainer Pass', email: '邮箱', password: '密码', currentPassword: '当前密码', newPassword: '新密码', confirmPassword: '确认密码', displayName: '显示名', referralCode: '邀请码', referralCodePlaceholder: '可选邀请码', referralCodeHint: '可填写其他训练师分享的邀请码。', loginTitle: '登录', loginSubtitle: '使用已验证邮箱进入 Pokopia Wiki', loggingIn: '登录中', loginFailed: '登录失败', rememberMe: '记住我', forgotPassword: '忘记密码?', noAccount: '还没有账号?', registerTitle: '注册', registerSubtitle: '创建账号后需要完成邮箱验证', registerFailed: '注册失败', sending: '发送中', sendVerification: '发送验证邮件', hasAccount: '已有账号?', requestResetTitle: '重置密码', requestResetSubtitle: '向账号邮箱发送密码重置链接。', sendResetLink: '发送重置链接', requestResetFailed: '密码重置请求失败', resetTitle: '设置新密码', resetSubtitle: '使用邮件中的重置链接更新密码。', resetPassword: '重置密码', resetting: '重置中', resetFailed: '密码重置失败', passwordMismatch: '两次输入的密码不一致', invalidPasswordReset: '密码重置链接无效或已过期', verifyTitle: '邮箱验证', verifySubtitle: '完成验证后即可登录', verifyingEmail: '正在验证邮箱', invalidVerification: '验证链接无效或已过期', verifyFailed: '邮箱验证失败', goLogin: '去登录' }, errors: { requestFailed: '请求失败({status})', operationFailed: '操作失败', loadFailed: '加载失败', addFailed: '添加失败', saveFailed: '保存失败', completeEmailVerification: '请先完成邮箱验证', permissionDenied: '你没有权限执行这个操作' }, pages: { home: { kicker: '社区 Wiki', title: 'Pokopia Wiki', subtitle: '浏览 Pokemon、Event Pokemon、栖息地、Event Habitats、物品、材料单、每日任务和 Pokemon Pokopia 的 Life 动态。', primaryActions: '首页主要操作', browsePokemon: '浏览 Pokemon', openChecklist: '每日 CheckList', openLife: '打开 Life', quickIndex: 'Wiki 快速入口', featuredPanel: '精选 Wiki 入口面板', dexCode: 'POKOPIA-001', dexTitle: '社区维护的游戏资料', dexBody: '从核心 Wiki 资料开始,也可以直接进入每日任务和社区 Life 动态。', wikiKicker: 'Wiki 资料', wikiTitle: '浏览游戏资料', communityKicker: '每日与社区', communityTitle: '查看每日任务和社区更新', projectUpdatesKicker: '项目更新', projectUpdatesTitle: '最近站点改动', projectUpdatesRepo: '源码仓库', projectUpdatesUpdatedAt: '更新于 {date}', projectUpdatesCommits: '最近提交', projectUpdatesReleases: '发布版本', projectUpdatesViewCommit: '查看提交', projectUpdatesViewRelease: '查看发布', projectUpdatesViewAll: '查看全部', futureKicker: '更多分区', futureTitle: '规划中的 Wiki 区域', sections: { pokemon: { title: 'Pokemon', description: '搜索 Pokemon,并按特长、喜欢的环境和喜欢的东西筛选。' }, eventPokemon: { title: 'Event Pokemon', description: '浏览限时 Pokemon 条目,并维护独立的 Pokopia ID 与排序。' }, habitats: { title: '栖息地', description: '查看配方、地图、天气、时间和可能出现的 Pokemon。' }, eventHabitats: { title: 'Event Habitats', description: '浏览限时栖息地、活动配方和可能出现的 Pokemon。' }, items: { title: '物品', description: '按分类、用途、入手方式、自定义和标签浏览物品。' }, eventItems: { title: 'Event Items', description: '浏览限时活动物品、共享分类与自定义排序。' }, ancientArtifacts: { title: 'Ancient Artifacts', description: '浏览 Lost Relics 和 Fossils 的标签、介绍与 Wiki 历史。' }, recipes: { title: '材料单', description: '查找结果物品、需要材料和入手方式。' }, checklist: { title: '每日 CheckList', description: '查看每天可以完成的任务。' }, life: { title: 'Life', description: '阅读社区动态、心得、发现和评论。' }, automation: { title: 'Automation', description: '工厂和自动化基地指南正在准备中。' }, dish: { title: 'Dish', description: '按厨具、材料、口味和苔藓卡比兽效果浏览料理。' }, events: { title: 'Events', description: '季节活动和限时活动记录正在准备中。' }, actions: { title: 'Actions', description: '游戏快捷动作和社交动作正在准备中。' }, dreamIsland: { title: 'Dream Island', description: 'Dream Island 信息正在整理中。' }, clothes: { title: 'Clothes', description: '服装和外观资料正在准备中。' } } }, projectUpdates: { kicker: '项目更新', title: '项目更新', subtitle: '查看 Pokopia Wiki 源码仓库中的公开站点改动。', sourceRepository: '源码仓库', updatedAt: '更新于 {date}', openRepository: '打开仓库', commits: '提交记录', releases: '发布版本', viewCommit: '查看提交', viewRelease: '查看发布', expandMessage: '展开', collapseMessage: '收起', commitMessage: 'Commit Message', loading: '正在加载项目更新', retry: '重试', empty: '暂无提交' }, legal: { lastUpdated: '最后更新:2026年5月3日', sourceLinks: '来源与参考链接', privacy: { kicker: '法律信息', title: '隐私政策', subtitle: '说明 Pokopia Wiki 如何处理账号、贡献和浏览相关信息。', sections: { overview: { title: '概览', bodyOne: 'Pokopia Wiki 由 Tootaio Studio 运营,是一个社区可编辑的游戏 Wiki。', bodyTwo: '本政策说明为了提供账号、Wiki 编辑、社区功能和站点安全而使用的信息。' }, information: { title: '我们收集的信息', bodyOne: '注册时,Pokopia Wiki 会收集邮箱地址、显示名、密码哈希、邮箱验证状态、会话状态,以及使用邀请码时的邀请信息。', bodyTwo: '使用 Wiki 时,Pokopia Wiki 可能保存你的编辑、上传、Life 动态、评论、互动、讨论活动、公开主页活动,以及维护社区内容所需的审计记录。' }, storage: { title: '浏览器存储', bodyOne: 'Pokopia Wiki 使用浏览器存储保存当前语言、登录会话 token、Remember me 选择和本地每日 CheckList 完成状态。', bodyTwo: '服务端会在适用场景中以哈希形式保存 session、验证 token、重置 token 和密码。Token 哈希和密码哈希不会通过公开 API 返回。' }, content: { title: '社区内容和编辑历史', bodyOne: 'Wiki 编辑、图片上传、讨论、Life 动态、互动和编辑历史可能会连同你的显示名和公开主页链接展示给其他用户。', bodyTwo: '请不要在公开内容中提交私人个人信息。出于安全、完整性、争议处理或法律义务需要,审核和审计记录可能会被保留。' }, sharing: { title: '服务提供方和安全', bodyOne: 'Pokopia Wiki 可能使用托管、数据库、邮件发送、日志和 AI 审核服务提供方来运行服务。', bodyTwo: 'Tootaio Studio 不出售个人信息。必要时,我们可能为了法律要求、站点安全或执行服务条款而披露相关信息。' }, choices: { title: '你的选择', bodyOne: '你可以退出登录以清除浏览器中的当前 token,注册用户也可以在个人资料中更新显示名和密码。', bodyTwo: '如有隐私问题或请求,请联系 Tootaio Studio。出于安全、审计历史、内容完整性或法律合规需要,部分记录可能会被保留。' } } }, terms: { kicker: '法律信息', title: '服务条款', subtitle: '使用和贡献 Pokopia Wiki 时需要遵守的规则。', sections: { acceptance: { title: '同意条款', bodyOne: '访问或使用 Pokopia Wiki 即表示你同意本服务条款,以及产品中针对账号、编辑、讨论和社区功能展示的规则。', bodyTwo: '如果你不同意这些条款,请不要使用本网站,也不要向 Pokopia Wiki 提交内容。' }, accounts: { title: '账号', bodyOne: '你需要对账号信息的准确性负责,并妥善保管登录凭据。', bodyTwo: '编辑和社区操作可能需要注册账号、完成邮箱验证,并拥有该账号被分配的权限。' }, contributions: { title: '贡献内容', bodyOne: '你需要对自己提交的内容负责,包括 Wiki 编辑、图片、评论、Life 动态、互动和讨论回复。', bodyTwo: '提交内容即表示你授权 Tootaio Studio 和 Pokopia Wiki 社区将该内容作为 Wiki 的一部分进行托管、展示、复制、改编、审核和维护。请不要提交你无权分享的内容。' }, acceptableUse: { title: '可接受使用', bodyOne: '不得将 Pokopia Wiki 用于骚扰、垃圾信息、违法活动、恶意软件、欺骗性内容、侵权行为,或试图绕过认证、限流、审核和安全控制。', bodyTwo: '为保护 Wiki、用户或第三方权利,Tootaio Studio 可能移除内容、限制功能或暂停访问。' }, availability: { title: '可用性和变更', bodyOne: 'Pokopia Wiki 作为社区资料服务提供,可能在不提前通知的情况下变更、暂停或不可用。', bodyTwo: '随着项目发展,或因安全、法律、运营需要,功能、路由、数据、审核行为和账号访问可能会更新。' }, changes: { title: '条款更新', bodyOne: 'Tootaio Studio 可能不定期更新本服务条款。最新版本会发布在本页面。', bodyTwo: '变更发布后继续使用 Pokopia Wiki,即表示你接受更新后的条款。' } } }, disclaimers: { kicker: '法律信息', title: '免责声明', subtitle: 'Pokopia Wiki 的来源、关联关系、准确性和权利声明。', sections: { community: { title: '社区维护信息', bodyOne: 'Pokopia Wiki 是面向 Pokemon Pokopia 游戏资料的社区维护参考网站。', bodyTwo: '内容可能不完整、过时、带有推测性,或由社区成员编辑;内容仅供一般参考。' }, affiliation: { title: '非官方关联', bodyOne: 'Pokopia Wiki 与 Nintendo、The Pokemon Company、Game Freak、Creatures、PokeAPI 或 pokopiawiki.com 均不存在从属、赞助、背书或官方认可关系。', bodyTwo: 'Pokemon 相关名称、图片、插画、标志、角色和游戏素材归其各自权利人所有。' }, pokeapi: { title: 'PokeAPI 数据和图片', bodyOne: 'Pokopia Wiki 使用的部分 Pokemon 数据和图片资源来自或参考了 PokeAPI 及相关 PokeAPI 仓库。', bodyTwo: 'PokeAPI 项目数据和 sprites 仓库材料可能包含其自身许可声明;Pokemon 名称、图片和相关知识产权仍归其各自权利人所有。' }, references: { title: '参考来源', bodyOne: '整理游戏资料时,Pokopia Wiki 可能参考 pokopiawiki.com 和其他公开资料。', bodyTwo: '参考来源用于研究、对照和署名;引用某个来源不代表从属、背书、赞助或官方认可。' }, accuracy: { title: '准确性和依赖', bodyOne: '游戏信息可能发生变化,社区维护记录也可能存在错误。', bodyTwo: 'Tootaio Studio 不保证 Pokopia Wiki 内容完整、最新、准确或没有错误。' }, rights: { title: '权利问题', bodyOne: '如果你认为 Pokopia Wiki 上的内容涉及侵权、来源标注不当或需要更正,请联系 Tootaio Studio,并提供相关页面和来源信息。', bodyTwo: '为处理来源、准确性、安全或权利问题,Tootaio Studio 可能更新、移除或限制相关内容。' } }, sources: { pokeapiDocs: 'PokeAPI 文档', pokeapiApiDataLicense: 'PokeAPI API 数据许可', pokeapiSpritesLicense: 'PokeAPI sprites 许可', pokemonLegal: 'Pokemon 法律信息', pokopiaWikiReference: 'pokopiawiki.com 参考来源' } } }, profile: { title: '个人资料', subtitle: '管理账号资料、邀请信息和密码。', loading: '正在加载个人资料', accountSummary: '账号概览', profileDetails: '资料详情', displayNameHint: '显示名会用于编辑署名、讨论和 Life 动态。', displayNameRequired: '请输入显示名。', emailVerified: '邮箱已验证', emailUnverified: '邮箱未验证', saved: '个人资料已保存', saveFailed: '个人资料保存失败', referralTitle: '邀请', referralCode: '邀请码', referralUrl: '邀请链接', referralHint: '分享给新编辑者,对方完成邮箱验证后会计入有效邀请。', verifiedReferralCount: '有效邀请', copyReferralLink: '复制链接', referralCopied: '邀请链接已复制', referralCopyFailed: '邀请链接复制失败', referralLoadFailed: '邀请信息加载失败', publicSubtitle: '查看该成员的 Life 动态、互动、评论和 Wiki 贡献。', publicKicker: '成员主页', tabsLabel: '个人主页分区', tabFeeds: 'Feeds', tabContributions: '贡献', tabReactions: '互动', tabComments: '评论', tabAccount: '账号', contributionFiltersLabel: '贡献分类', contributionConfig: '配置', contributionsFilterEmpty: '该分类暂无贡献', reactionFiltersLabel: '互动分类', reactionsFilterEmpty: '该分类暂无互动', commentFiltersLabel: '评论分类', commentsFilterEmpty: '该分类暂无评论', lifeCommentCategory: 'Life', discussionCommentCategory: 'Wiki', passwordTitle: '修改密码', passwordHint: '至少使用 8 个字符。', passwordSaved: '密码已更新', passwordSaveFailed: '密码更新失败', savePassword: '保存密码', follow: '关注', followBack: '回关', following: '已关注', friend: '好友', followers: '粉丝', followingCount: '关注', friends: '好友', followFailed: '关注操作失败', joinedAt: '加入于 {date}', lifePosts: 'Life 动态', lifeComments: 'Life 评论', lifeReactions: '互动', discussionComments: 'Wiki 评论', commentsMade: '评论', wikiEdits: 'Wiki 编辑', wikiCreates: '新增', wikiUpdates: '更新', wikiDeletes: '删除', imageUploads: '图片', wikiContributionStats: 'Wiki 贡献统计', communityStats: '社区统计', contributionBreakdown: '贡献分布', total: '总计', otherContributions: '其他', feedsEmpty: '暂无 Life 动态', contributionsEmpty: '暂无 Wiki 贡献', reactionsEmpty: '暂无互动', commentsEmpty: '暂无评论', loadMore: '加载更多', lifeComment: 'Life 评论', discussionComment: 'Wiki 讨论', lifePostTarget: 'Life 动态', lifePostBy: '{name} 的 Life 动态' }, pokemon: { title: 'Pokemon', subtitle: '搜索宝可梦,并按特长、环境、喜欢的东西筛选。', listKicker: 'Pokédex', detailKicker: 'Pokédex Detail', editKicker: 'Pokédex Edit', editSubtitle: '维护 Pokemon 介绍、属性、六维、特长和喜欢的东西。', editSections: 'Pokemon 编辑分区', editTabBasic: '基础', editTabAdvance: '进阶', newTitle: '新增 Pokemon', editTitle: '编辑 #{id} {name}', id: 'Pokopia ID', fetchData: '获取数据', fetchingData: '正在获取', fetchIdentifier: '数据标识', fetchIdentifierPlaceholder: 'bulbasaur 或 1', fetchIdentifierRequired: '请输入 Pokemon 数据标识', fetchFailed: 'Pokemon 数据获取失败', fetchIdMismatch: '获取到的官方 ID #{id} 与当前编辑内容不一致。', fetchResults: 'Pokemon 数据结果', fetchSearching: '正在搜索数据', fetchNoMatches: '没有匹配的 Pokemon 数据', fetchSearchFailed: 'Pokemon 数据搜索失败', image: '图片', fetchImages: '获取图片', fetchingImages: '正在获取', imageFetchFailed: 'Pokemon 图片获取失败', imageNoMatches: '没有可用的 Pokemon 图片', loadingImages: '正在加载 Pokemon 图片', selectedImage: '已选择的 Pokemon 图片', imageOptions: 'Pokemon 图片选项', clearImage: '清除图片', imageEmpty: '尚未选择 Pokemon 图片', imageAlt: '{name} {variant} 图片', eventItem: 'Event Pokemon', loadingList: '正在加载 Pokemon 列表', loadingDetail: '正在加载 Pokemon 详情', loadingEdit: '正在加载 Pokemon 编辑内容', environmentPrefix: '喜欢的环境:{name}', details: '介绍', genus: '分类', height: '身高', heightInput: '身高(in)', heightImperial: 'ft / in', heightMetric: 'm', feet: 'ft', inches: 'in', meters: 'm', weight: '体重', weightInput: '体重(lb)', pounds: 'lb', kilograms: 'kg', measurements: '身高与体重', types: '属性', typeOne: '属性 1', typeTwo: '属性 2', typesAndStats: '属性与六维', statsTitle: '六维', stats: { hp: 'HP', attack: '攻击', defense: '防御', specialAttack: '特攻', specialDefense: '特防', speed: '速度' }, environment: '喜欢的环境', skills: '特长', skillMatchMode: '特长匹配方式', any: '任意', all: '全部', favoriteThings: '喜欢的东西', favoriteThingMatchMode: '喜欢的东西匹配方式', skillDrops: '特长掉落物', skillDrop: '{name}掉落物', dropItem: '掉落物', trading: 'Trading', tradingItems: 'Trading 物品', tradingLikes: 'Likes', tradingNeutral: 'Neutral', tradingPriceBonus: '1.5x 价格', tradingSelectedCount: '已选择 {count} 个', tradingModalSubtitle: 'Likes:1.5x 价格 · Neutral:无加成', tradingAvailableItems: '可选 Trading 物品', tradingSelectedItems: '已选 Trading 物品', tradingPreferenceFor: '{name} 的 Trading 偏好', tradingDefaultGroup: '默认加入组', manageTrading: '管理 Trading', searchPokemon: '搜索 Pokemon', relatedPokemon: '相关 Pokemon', relatedHabitat: '相关 Pokemon 栖息地', relatedItems: '关联物品', relatedItemCategory: '关联物品分类', habitats: '栖息地', namePlaceholder: '名字', searchTypes: '搜索属性', searchEnvironment: '搜索喜欢的环境', searchSkills: '搜索特长', searchFavoriteThings: '搜索喜欢的东西', searchItems: '搜索物品' }, eventPokemon: { title: 'Event Pokemon', subtitle: '搜索 Event Pokemon,并按特长、环境、喜欢的东西筛选。', kicker: 'Event Pokédex', detailKicker: 'Event Pokemon Detail', editSubtitle: '维护 Event Pokemon 介绍、Pokopia ID、官方数据身份、图片、六维、特长和喜欢的东西。', newTitle: '新增 Event Pokemon', editTitle: '编辑 Event #{id} {name}', loadingList: '正在加载 Event Pokemon 列表' }, habitats: { title: '栖息地', subtitle: '查看配方和可能出现的宝可梦。', listKicker: 'Habitats', detailKicker: 'Habitat Detail', detailSubtitle: '栖息地详情', editSubtitle: '维护栖息地配方和可能出现的 Pokemon。', newTitle: '新增栖息地', editTitle: '编辑 {name}', fallbackName: '栖息地', loadingList: '正在加载栖息地列表', loadingDetail: '正在加载栖息地详情', loadingEdit: '正在加载栖息地编辑内容', eventItem: 'Event Habitat', recipe: '配方', recipeList: '配方列表', possiblePokemon: '可能出现的宝可梦', addItem: '添加物品', addPokemon: '添加 Pokemon', maps: '地图', searchMaps: '搜索地图' }, eventHabitats: { title: 'Event Habitats', subtitle: '查看限时栖息地、活动配方和可能出现的 Pokemon。', kicker: 'Event Habitats', detailKicker: 'Event Habitat Detail', editSubtitle: '维护 Event Habitat 配方、可能出现的 Pokemon 和图片。', newTitle: '新增 Event Habitat', editTitle: '编辑 Event Habitat {name}', loadingList: '正在加载 Event Habitat 列表' }, items: { title: '物品', subtitle: '按分类、用途、标签查看物品。', kicker: '物品', detailKicker: 'Item Detail', detailSubtitle: '物品详情', editKicker: 'Item Edit', editSubtitle: '维护物品基础价格、分类、用途、入手方式、自定义和标签。', newTitle: '新增物品', editTitle: '编辑 {name}', fallbackName: '物品', loadingList: '正在加载列表', loadingDetail: '正在加载物品详情', loadingEdit: '正在加载物品编辑内容', description: '介绍', basePrice: '基础价格', ancientArtifact: 'Ancient Artifact', category: '分类', usage: '用途', tags: '标签', acquisitionMethods: '入手方式', customization: '自定义', dyeable: '可染色', dualDyeable: '可双区染色', patternEditable: '可改花纹', noRecipe: '无材料单', eventItem: '活动物品', recipeInfo: '材料单信息', relatedRecipes: '相关材料单', relatedHabitats: '相关栖息地', pokemonDrops: 'Pokemon 掉落', possibleTags: 'Possible Tags', highlyLikelyTags: '高度可能', possibleTagsPossible: '可能', excludedTags: '已排除', possibleTagsEvidence: '证据', createRecipe: '创建材料单', addItem: '新增物品', createDefaultsMenu: '新增物品选项', createDefaultsTitle: '当前会话默认值', clearCreateDefaults: '清除默认值', itemActions: '物品操作', insertBeforeItem: '在前面插入', insertAfterItem: '在后面插入', searchCategory: '搜索分类', searchUsage: '搜索用途', searchMethods: '搜索入手方式', searchTags: '搜索标签' }, eventItems: { title: 'Event Items', subtitle: '按分类、用途、标签查看活动物品。', kicker: 'Event Items', detailKicker: 'Event Item Detail', editSubtitle: '维护 Event Item 基础价格、分类、用途、入手方式、自定义和标签。', newTitle: '新增 Event Item' }, ancientArtifacts: { title: 'Ancient Artifacts', subtitle: '按遗物、化石分类和标签查看 Ancient Artifacts。', kicker: 'Ancient Artifacts', detailKicker: 'Ancient Artifact Detail', detailSubtitle: 'Ancient Artifact 详情', editKicker: 'Ancient Artifact Edit', editSubtitle: '维护物品介绍、基础价格、分类、用途、Ancient Artifact 分类和标签。', newTitle: '新增 Ancient Artifact', editTitle: '编辑 {name}', fallbackName: 'Ancient Artifact', loadingList: '正在加载 Ancient Artifact 列表', loadingDetail: '正在加载 Ancient Artifact 详情', loadingEdit: '正在加载 Ancient Artifact 编辑内容', description: '介绍', category: '分类', tags: '标签', searchCategory: '搜索分类', searchTags: '搜索标签' }, recipes: { title: '材料单', subtitle: '按分类、用途、标签查看材料单。', detailKicker: 'Recipe Detail', detailSubtitle: '材料单详情', editKicker: 'Recipe Edit', editSubtitle: '维护材料单结果物品、入手方式和需要材料。', newTitle: '新增材料单', editTitle: '编辑 {name}', fallbackName: '材料单', loadingList: '正在加载材料单列表', loadingDetail: '正在加载材料单详情', loadingEdit: '正在加载材料单编辑内容', item: '物品', materials: '需要材料', addMaterial: '添加材料' }, dish: { kicker: 'Dish', title: '料理', subtitle: '按分类、厨具、材料、口味和苔藓卡比兽效果浏览料理。', loading: '正在加载料理记录', category: '分类', categories: '分类', dishes: '菜肴', cookware: '厨具', effect: '吃后效果', totalMaterialQuantity: '总数所需材料数量', dishItem: '菜肴物品', flavor: '口味', mainMaterial: '主材料', secondaryMaterial: '副材料', secondaryMaterials: '副材料', secondSecondaryMaterial: '第二副材料', pokemonSkill: 'Pokemon 特长', mosslaxEffect: 'Mosslax 效果', newCategory: '新增分类', editCategory: '编辑分类', newDish: '新增菜肴', editDish: '编辑菜肴' }, comingSoon: { status: '正在开发中', heading: '这个 Wiki 分区正在准备中。', previewLabel: '分区预览', sections: { automation: { kicker: 'Automation', title: '自动化', subtitle: '自动化基地和工厂方案会在这里分享。', body: '自动化分区会帮助玩家对比生产配置、材料产出、所需 Pokemon、生产顺序和共同喜好物品。', preview: { one: '工厂方案会围绕基地布局和生产目标整理。', two: '材料产出、Pokemon 需求和生产顺序会保持易读。', three: '共同喜好物品可用于规划更适合协作的队伍和流程。' } }, dish: { kicker: 'Dish', title: '料理', subtitle: '未来会用于整理料理和食物相关发现。', body: '料理页面会围绕清晰浏览、来源记录和材料关联来设计。', preview: { one: '料理记录会优先呈现名称、效果和发现方式。', two: '需要时会把材料关系连接回物品和材料单。', three: '页面会先保持浏览友好,后续再自然承接社区编辑内容。' } }, events: { kicker: 'Events', title: '活动', subtitle: '季节活动和限时内容资料会在这里整理。', body: '活动分区会在准备好后集中展示时间、奖励和参与信息。', preview: { one: '活动卡片会让日期和开放时间更容易浏览。', two: '奖励与关联物品会靠近活动摘要展示。', three: '活动结束后,历史记录也会保持可读。' } }, actions: { kicker: 'Actions', title: '动作', subtitle: '挥手、跳舞等游戏内快捷动作会记录在这里。', body: '动作分区会作为游戏内表情、社交动作和快捷动作的快速参考。', preview: { one: '每个动作会用面向玩家的语言说明动作或快捷方式。', two: '常见内容包括挥手、跳舞和其他社交动作。', three: '后续可在数据模型准备好后补充解锁或使用条件。' } }, dreamIsland: { kicker: 'Dream Island', title: 'Dream Island', subtitle: 'Dream Island 相关资料正在整理。', body: '这个区域未来会用更像目的地资料页的方式展示岛屿信息。', preview: { one: '岛屿记录会优先整理地点、开放状态和重要发现。', two: '可关联的 Pokemon、物品或活动会从页面中连接出来。', three: '目前先保持公开浏览入口,不额外增加管理流程。' } }, clothes: { kicker: 'Clothes', title: '服装', subtitle: '外观和服装资料正在准备。', body: '服装页面会用于对比外观、入手方式和自定义信息。', preview: { one: '服装条目会优先整理展示名称和视觉分类。', two: '入手方式与自定义信息会在资料可用后接入。', three: '页面会保持服装资料清晰,不和普通物品列表混在一起。' } } } }, checklist: { title: '每日清单', subtitle: '查看每天可以完成的事项。', sectionTitle: '每日做什么', empty: '暂无每日清单', loading: '正在加载每日清单', task: 'Task', newTask: '新增 Task', editTask: '编辑 Task' }, life: { title: 'Life', subtitle: '分享喜欢的心得、想法和社区发现。', kicker: '社区动态', detailTitle: 'Life 动态', detailSubtitle: '查看这条社区动态和相关讨论。', detailKicker: 'Life 详情', backToLife: '返回 Life', viewPost: '查看动态', composerTitle: '分享动态', composerPrompt: '想分享什么?', bodyLabel: '动态内容', bodyPlaceholder: '分享一段想法、心得或发现……', newPost: 'New Post', category: 'Category', gameVersion: '游戏版本', versionPlaceholder: '不选择版本', searchVersions: '搜索版本', languages: '语言区', allLanguages: '全部语言', allCategories: '全部', feedScope: '动态范围', allFeed: '全部动态', followingFeed: '关注动态', allVersions: '全部版本', versionFilter: '版本', ratingFilter: '评分', allRatingModes: '全部动态', rateableOnly: '仅可评分', notRateableOnly: '不可评分', sort: '排序', sortLatest: '最新', sortOldest: '最早', sortTopRated: '评分最高', sortMostLiked: '点赞最多', sortMostReplied: '回复最多', categoryPlaceholder: '选择 Category', searchCategories: '搜索 Category', search: '搜索动态', searchPlaceholder: '搜索动态内容……', clearSearch: '清除搜索', searchEmpty: '没有匹配的动态', searchEmptyHint: '换个关键词或清除搜索。', comments: '评论', commentsCount: '{count} 条评论', comment: '评论', hideComments: '收起评论', react: '点赞', reactions: '互动', reactionsCount: '{count} 次互动', reactionCountLabel: '{reaction}:{count}', reactionLike: '喜欢', reactionHelpful: '有帮助', reactionFun: '有趣', reactionThanks: '感谢', chooseReaction: '选择互动', reactionMenu: '互动菜单', reactionUsersTitle: '互动', reactionUsersSubtitle: '查看对这条 Life 动态做出互动的用户。', reactionFiltersLabel: '互动类型', allReactions: '全部互动', reactionUsersEmpty: '暂无互动', loadMoreReactions: '加载更多互动', removeReaction: '取消互动', reactionFailed: '互动失败', postMeta: '动态信息', changeLog: 'ChangeLog', rating: '评分', setRating: '评 {count} 星', removeRating: '取消评分', ratingAverage: '{average} 平均分,{count} 人评分', noRatings: '暂无评分', ratingFailed: '评分失败', commentPlaceholder: '写下评论……', commentReplyPlaceholder: '写下回复……', postComment: '发表评论', postingComment: '评论中', reply: '回复', postReply: '发布回复', postingReply: '回复中', cancelReply: '取消回复', noComments: '暂无评论', loadingComments: '正在加载评论', loadMoreComments: '加载更多评论', deleteComment: '删除评论', deleteCommentConfirm: '确认删除这条评论?', commentDeleted: '评论已删除', restoreComment: '撤销', likeComment: '点赞评论', unlikeComment: '取消点赞评论', commentLikeCount: '{count} 个赞', commentLikeFailed: '点赞失败', commentRequired: '请输入评论内容。', commentFailed: '评论失败', replyFailed: '回复失败', deleteCommentFailed: '删除评论失败', restoreCommentFailed: '撤销失败', publish: '发布', publishing: '发布中', update: '更新', updating: '更新中', cancelEdit: '取消编辑', empty: '暂无动态', emptyHint: '已验证成员可以发布第一条 Life 动态。', loading: '正在加载 Life 动态', retryFeed: '重试加载', loginPrompt: '使用已验证邮箱登录后即可发布。', verifyPrompt: '完成邮箱验证后即可发布。', editPost: '编辑动态', deletePost: '删除动态', saveEdit: '保存编辑', postFailed: '发布失败', saveFailed: '保存失败', deleteFailed: '删除失败', bodyRequired: '请输入动态内容。', categoryRequired: '请选择 Category。', byUnknown: '社区成员', edited: '已编辑', deleteConfirm: '确认删除这条动态?', moderationUnreviewed: '未审核', moderationReviewing: '审核中', moderationApproved: '审核通过', moderationRejected: '审核不通过', moderationFailed: '审核失败', moderationReason: '审核详情', moderationRetry: '重新审核', moderationRetrying: '重审中', moderationRetryFailed: '重新审核失败', charactersLeft: '还可以输入 {count} 个字符' }, admin: { title: '管理', subtitle: '管理 Wiki 内容、配置、本地化和访问权限。', modules: '管理模块', contentGroup: '内容', configurationGroup: '配置', localizationGroup: '本地化', accessGroup: '访问权限', loading: '正在加载管理列表', users: '用户', roles: '角色', permissions: '权限', config: '系统配置', configType: '系统配置类型', checklist: 'CheckList', pokemonList: 'Pokemon 列表', itemList: '物品列表', ancientArtifactList: 'Ancient Artifact 列表', recipeList: '材料单列表', dishList: '料理列表', habitatList: '栖息地列表', dataTools: '数据工具', dataToolRefresh: '刷新', dataToolExport: '导出数据', dataToolExportButton: '导出 JSON', dataToolImport: '导入数据', dataToolImportButton: '导入', dataToolImportFile: '数据包', dataToolImportMode: '导入会替换数据包内包含的范围。', dataToolItemsCsvFile: '物品 CSV', dataToolItemsCsvMode: 'CSV 导入只会新增物品。替换列表时请先清空物品。', dataToolItemsCsvImported: '物品 CSV 已导入。', dataToolHabitatsCsvFile: '栖息地 CSV', dataToolHabitatsCsvMode: 'CSV 导入只会新增栖息地。替换列表时请先清空栖息地。', dataToolHabitatsCsvImported: '栖息地 CSV 已导入。', dataToolWipe: '清空数据', dataToolWipeButton: '清空', dataToolSelectScope: '请至少选择一个数据范围。', dataToolInvalidBundle: '数据包不合法。', dataToolImportConfirm: '导入将替换:{scopes}。', dataToolWipeConfirm: '清空将删除:{scopes}。', dataToolConfirmImport: '输入 IMPORT 确认', dataToolConfirmWipe: '输入 WIPE 确认', dataToolDependencyNote: '物品会连同材料单一起处理,因为材料单依赖物品。', dataToolReplaceNote: '关联记录、翻译、编辑历史、图片历史和讨论会一并处理。', dataToolUploadsNote: 'JSON 导出不包含上传文件本身。', dataToolScopePokemon: 'Pokemon', dataToolScopeHabitats: '栖息地', dataToolScopeItems: '物品', dataToolScopeArtifacts: 'Ancient Artifacts', dataToolScopeRecipes: '材料单', dataToolScopeChecklist: '每日 CheckList', languages: '语言', newConfig: '新增{name}', editConfig: '编辑{name}', hasItemDrop: '有掉落物', hasTrading: '有 Trading', rateableCategory: '可评分', changeLog: 'ChangeLog', dragSort: '拖曳排序:{name}', dragSortTitle: '拖曳排序', languageCode: 'Code', languageName: '语言名称', enabled: '启用', defaultLanguage: '默认语言', defaultCategory: '默认 Category', sortOrder: '排序', newLanguage: '新增语言', editLanguage: '编辑语言', wordings: '系统文案', aiModeration: 'AI 审核', aiModerationEnabled: '启用', aiModerationFormat: 'API 格式', aiModerationFormatGemini: 'Gemini generateContent', aiModerationFormatOpenAi: 'OpenAI-compatible chat completions', aiModerationAuthMode: '鉴权方式', aiModerationAuthQueryKey: 'Query key', aiModerationAuthBearer: 'Bearer token', aiModerationEndpoint: 'End Point', aiModerationModel: '模型', aiModerationRpm: '每分钟请求数', aiModerationApiKey: 'API Key', aiModerationApiKeyConfigured: 'API Key 已配置', aiModerationApiKeyMissing: 'API Key 未配置', aiModerationClearApiKey: '清除已保存 API Key', aiModerationSettings: 'AI 审核设置', rateLimits: '限流', rateLimitMaxRequests: '最大请求数', rateLimitWindowMinutes: '窗口分钟数', rateLimitCooldownSeconds: '冷却秒数', rateLimitAccountWrite: '账号写入', rateLimitAdminWrite: '管理写入', rateLimitWikiWrite: 'Wiki 内容写入', rateLimitCommunityWrite: '社区写入', rateLimitCommunityReaction: '社区 Reaction', rateLimitUpload: '上传', rateLimitFetch: 'Pokemon Fetch', wordingLocale: '语言', wordingModule: '模块', wordingSurface: '端', wordingMissingOnly: '只看缺失', wordingKey: 'Key', wordingValue: '文案', defaultValue: '默认文案', placeholders: '占位符', missingTranslation: '缺少翻译', allModules: '全部模块', allSurfaces: '全部端', surfaceFrontend: '前端', surfaceBackend: '后端', surfaceEmail: '邮件', editWording: '编辑文案', userRoles: '用户角色', noRoles: '无角色', newRole: '新增角色', editRole: '编辑角色', roleKey: '角色 Key', roleName: '角色名称', description: '说明', level: '层级', disabled: '停用', systemRole: '系统角色', roleLevel: '层级 {level}', permissionCount: '{count} 个权限', rolePermissions: '角色权限', newPermission: '新增权限', editPermission: '编辑权限', permissionKey: '权限 Key', permissionName: '权限名称', category: '分类', systemPermission: '系统权限' } }, config: { pokemonTypes: 'Pokemon 属性', skills: '特长', environments: '喜欢的环境', favoriteThings: '喜欢的东西 / 标签', acquisitionMethods: '入手方式', maps: '地图', lifeCategories: 'Life Categories', gameVersions: '游戏版本', dishFlavors: '料理味道' }, appearance: { time: '时段', weather: '天气', rarity: '稀有度', map: '地图', maps: '出现地图', morning: '早晨', noon: '中午', evening: '傍晚', night: '晚上', sunny: '晴天', cloudy: '阴天', rainy: '雨天', stars: '{count} 星' }, history: { title: '贡献记录', createdBy: '由谁创建', lastEdited: '最后编辑', editHistory: '编辑历史', before: '修改前', after: '修改后', author: '作者', time: '时间', action: '动作', create: '创建', update: '编辑', delete: '删除', empty: '暂无编辑历史' }, media: { image: '图片', imageAlt: '{name}图片', uploadImage: '上传图片', uploading: '上传中', uploadedImage: '上传图片', selectedImage: '已选择图片', imageHistory: '图片历史', imageOptions: '图片选项', clearImage: '清除图片', imageEmpty: '尚未选择图片', imageHistoryEmpty: '暂无上传图片', uploadFailed: '图片上传失败', selectImage: '选择图片' }, discussion: { title: '讨论', count: '{count} 条评论', comment: '评论', commentPlaceholder: '写下评论……', replyPlaceholder: '写下回复……', postComment: '发表评论', postingComment: '评论中', reply: '回复', postReply: '发布回复', postingReply: '回复中', cancelReply: '取消回复', deleteComment: '删除评论', deleteConfirm: '确认删除这条评论?', deletedComment: '评论已删除', commentRequired: '请输入评论内容。', commentFailed: '评论失败', replyFailed: '回复失败', deleteFailed: '删除失败', languages: '语言区', allLanguages: '全部语言', moderationUnreviewed: '未审核', moderationReviewing: '审核中', moderationApproved: '审核通过', moderationRejected: '审核不通过', moderationFailed: '审核失败', moderationReason: '审核详情', moderationRetry: '重新审核', moderationRetrying: '重审中', moderationRetryFailed: '重新审核失败', loading: '正在加载讨论', loadMore: '加载更多评论', sort: '排序', sortOldest: '最早', sortLatest: '最新', sortMostLiked: '点赞最多', sortMostReplied: '回复最多', likeComment: '点赞评论', unlikeComment: '取消点赞评论', commentLikeCount: '{count} 个赞', commentLikeFailed: '点赞失败', empty: '暂无讨论', emptyHint: '现在发起新的讨论。', loginPrompt: '使用已验证邮箱登录后即可评论。', verifyPrompt: '完成邮箱验证后即可评论。', byUnknown: '社区成员', charactersLeft: '还可以输入 {count} 个字符' }, server: { errors: { foreignKey: '引用的数据不存在,或当前记录正在被使用', duplicate: '同名或相同 ID 的记录已存在', invalidField: '字段值不合法', serverError: '服务器错误', loginRequired: '请先登录', verifyEmailFirst: '请先完成邮箱验证', permissionDenied: '权限不足', notFound: '未找到记录', rateLimited: '请求过于频繁,请稍后再试。' }, auth: { emailRequired: '请输入邮箱', invalidEmail: '邮箱格式不正确', displayNameRequired: '请输入显示名', displayNameLength: '显示名长度需为 1 到 40 个字符', passwordLength: '密码至少需要 8 个字符', invalidToken: '验证链接无效或已过期', emailAlreadyRegistered: '该邮箱已注册', checkVerificationEmail: '请查收验证邮件', emailVerified: '邮箱已验证', checkPasswordResetEmail: '如果该邮箱已注册,系统会发送密码重置链接。', passwordResetComplete: '密码已更新,请使用新密码登录。', passwordChanged: '密码已更新。', invalidCredentials: '邮箱或密码不正确', verifyEmailFirst: '请先完成邮箱验证', invalidResetToken: '密码重置链接无效或已过期', currentPasswordInvalid: '当前密码不正确', invalidReferralCode: '邀请码无效', cannotFollowSelf: '不能关注自己', emailDeliveryUnavailable: '邮件发送暂时不可用,请稍后再试。' }, validation: { nameRequired: '请输入名称', recordMissing: '记录不存在', languageCodeInvalid: '语言 Code 不合法', languageNameRequired: '请输入语言名称', defaultLanguageMustBeEnglish: '默认语言必须是 English', defaultLanguageMustBeEnabled: '默认语言必须启用', languageNotFound: '语言不存在', defaultLanguageRequired: '必须保留一个默认语言', defaultLanguageCannotBeDeleted: '默认语言不能删除', selectLanguage: '请选择语言', languageDoesNotExist: '语言不存在', pokemonIdentifierRequired: '请输入 Pokemon 标识', pokemonTypeDataUnavailable: 'Pokemon 属性数据不可用', pokemonDataNotFound: '未找到 Pokemon 数据', pokemonDataIdMismatch: '官方 Pokemon 数据 ID 与当前 Pokemon 不一致', dataToolScopeRequired: '请至少选择一个数据范围', dataToolScopeInvalid: '数据范围不合法', dataToolBundleInvalid: '数据包不合法', dataToolItemsCsvInvalid: '物品 CSV 不合法', dataToolHabitatsCsvInvalid: '栖息地 CSV 不合法', pokemonImagePathInvalid: 'Pokemon 图片路径不合法', imagePathInvalid: '图片路径不合法', imageUploadRequired: '请选择图片', imageUploadTypeInvalid: '不支持这种图片类型', imageUploadContentInvalid: '图片文件不合法', imageUploadEntityNameRequired: '请先输入名称再上传图片', imageUploadFailed: '图片上传失败', taskRequired: '请输入任务', selectTask: '请选择任务', taskDoesNotExist: '任务不存在', postRequired: '请输入动态内容', postTooLong: '动态内容过长', lifeCategoryRequired: '请选择 Category', lifeCategoryInvalid: 'Category 不合法', gameVersionInvalid: '游戏版本不合法', commentRequired: '请输入评论内容', commentTooLong: '评论内容过长', reactionInvalid: '互动类型不合法', ratingInvalid: '评分不合法', cursorInvalid: '分页位置不合法', tagInvalid: '标签不合法', entityTypeInvalid: '实体类型不合法', recordInvalid: '记录不合法', commentInvalid: '评论不合法', selectRecord: '请选择记录', typeMin: '请至少选择 1 个属性', typeMax: '最多选择 2 个属性', skillMax: '最多选择 2 个特长', favoriteMax: '最多选择 6 个喜欢的东西', dropItemSelectedSkill: '掉落物必须关联到已选择的特长', pokemonIdRequired: '请输入 Pokopia ID', pokemonNameRequired: '请输入 Pokemon 名称', heightNonNegative: '身高必须是不小于 0 的数字', weightNonNegative: '体重必须是不小于 0 的数字', environmentRequired: '请选择喜欢的环境', skillNoDrop: '这个特长不能设置掉落物', habitatNameRequired: '请输入栖息地名称', usageRequired: '请选择用途', itemNameRequired: '请输入物品名称', categoryRequired: '请选择分类', artifactNameRequired: '请输入 Ancient Artifact 名称', recipeFreeWithRecipe: '已有材料单的物品不能标记为无材料单', itemRequired: '请选择物品', recipeFreeItem: '这个物品已标记为无材料单', statNonNegative: '六维必须是不小于 0 的整数', pokemonDataFileEmpty: 'Pokemon 数据文件为空', pokemonDataFileUnavailable: 'Pokemon 数据文件不可用' }, wordings: { keyNotFound: '系统文案 Key 不存在', localeRequired: '请选择语言', valueRequired: '请输入文案', placeholderMismatch: '占位符必须与默认文案一致' }, permissions: { nameRequired: '请输入名称', valueTooLong: '内容过长', invalidSelection: '选择项不合法', roleKeyInvalid: '角色 Key 不合法', roleNotFound: '角色不存在', ownerRequired: '必须至少保留一个 Owner', ownerRoleLocked: 'Owner 角色权限不能编辑', ownerRoleOperationDenied: '只有具备 Owner 分配权限的 Owner 可以分配或移除 Owner 角色', roleLevelOperationDenied: '只能分配或移除低于自己最高角色等级的角色', permissionKeyInvalid: '权限 Key 不合法', permissionNotFound: '权限不存在', criticalPermissionRequired: '关键管理权限必须保持启用', permissionManagerRequired: '必须至少保留一个可管理权限的已验证用户', userNotFound: '用户不存在' } }, email: { auth: { kicker: '账号安全', linkFallback: '如果按钮无法打开,请复制以下链接到浏览器:', footer: '这封自动邮件来自 Pokopia Wiki,用于处理你的账号操作。', verificationSubject: '验证你的 Pokopia Wiki 邮箱', verificationActionLabel: '验证邮箱', verificationHtml: '

欢迎来到 Pokopia Wiki。请确认这个邮箱地址,完成账号设置并解锁已验证编辑权限。

验证邮箱

安全链接将在 {hours} 小时后失效。

', verificationText: '请打开以下链接完成 Pokopia Wiki 邮箱验证:{url}\n安全链接将在 {hours} 小时后失效。', passwordResetSubject: '重置你的 Pokopia Wiki 密码', passwordResetActionLabel: '重置密码', passwordResetHtml: '

请使用这个安全链接为你的 Pokopia Wiki 账号设置新密码。

重置密码

链接将在 {hours} 小时后失效。如果这不是你本人操作,可以忽略这封邮件。

', passwordResetText: '请打开以下链接重置 Pokopia Wiki 密码:{url}\n链接将在 {hours} 小时后失效。如果这不是你本人操作,可以忽略这封邮件。' } } } } as const; export type SystemWordingSurface = 'frontend' | 'backend' | 'email'; export type SystemWordingCatalogEntry = { key: string; module: string; surface: SystemWordingSurface; description: string; placeholders: string[]; values: Record; }; const placeholderPattern = /\{([A-Za-z0-9_]+)\}/g; function isMessageTree(value: SystemWordingLeaf | SystemWordingTree): value is SystemWordingTree { return typeof value === 'object' && value !== null; } function collectPlaceholders(value: string): string[] { return [...new Set([...value.matchAll(placeholderPattern)].map((match) => match[1]))].sort(); } function mergePlaceholders(values: Record): string[] { return [...new Set(Object.values(values).flatMap(collectPlaceholders))].sort(); } function moduleForKey(key: string): string { const parts = key.split('.'); if ((parts[0] === 'pages' || parts[0] === 'server' || parts[0] === 'email') && parts[1]) { return `${parts[0]}.${parts[1]}`; } return parts[0] ?? 'system'; } function surfaceForKey(key: string): SystemWordingSurface { if (key.startsWith('email.')) return 'email'; if (key.startsWith('server.')) return 'backend'; return 'frontend'; } function flattenMessages(tree: SystemWordingTree, prefix = ''): Record { const entries: Record = {}; for (const [key, value] of Object.entries(tree)) { const nextKey = prefix ? `${prefix}.${key}` : key; if (isMessageTree(value)) { Object.assign(entries, flattenMessages(value, nextKey)); } else { entries[nextKey] = value; } } return entries; } export function flattenSystemWordingMessages(messages: SystemWordingMessages = systemWordingMessages): Record> { return Object.fromEntries(Object.entries(messages).map(([locale, tree]) => [locale, flattenMessages(tree)])); } export function systemWordingCatalogEntries(messages: SystemWordingMessages = systemWordingMessages): SystemWordingCatalogEntry[] { const flattened = flattenSystemWordingMessages(messages); const keys = Object.keys(flattened[defaultLocale] ?? {}).sort(); return keys.map((key) => { const values = Object.fromEntries( Object.entries(flattened) .map(([locale, localeMessages]) => [locale, localeMessages[key]]) .filter((entry): entry is [string, string] => typeof entry[1] === 'string' && entry[1].trim() !== '') ); return { key, module: moduleForKey(key), surface: surfaceForKey(key), description: '', placeholders: mergePlaceholders(values), values }; }); } export function systemWordingFallback(key: string, locale = defaultLocale): string | undefined { const flattened = flattenSystemWordingMessages(); return flattened[locale]?.[key] ?? flattened[defaultLocale]?.[key]; }