diff --git a/AGENTS.md b/AGENTS.md index 8bd0e39..d72061a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,3 +3,10 @@ This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices. + +# Translations and Locale Management Rule +- All translation files must be located in `src/translations/` (NOT `src/i18n/`). +- Inside `src/translations/locales/`, there are NO subdirectories. Each language has a single, flat key-value JSON file (`ar.json`, `az.json`, `bn.json`, `zh.json`, `da.json`, `de.json`, `en.json`, `es.json`, `fa.json`, `fr.json`, `gu.json`, `ha.json`, `he.json`, `hi.json`, `id.json`, `ks.json`, `pt.json`, `ru.json`, `sw.json`, `tg.json`, `tr.json`, `ul.json`, `ur.json`, `uz.json`). +- Registration form structure data is kept separately in `src/data/questions/`. +- CRITICAL AUTOMATION RULE: Whenever any new data, field, component label, key, or text item is added to the project, its default translation string MUST automatically be added to the `en.json` file (`src/translations/locales/en.json`) as key-value pairs. + diff --git a/src/app/[lang]/layout.tsx b/src/app/[lang]/layout.tsx index 366c543..c0ec596 100644 --- a/src/app/[lang]/layout.tsx +++ b/src/app/[lang]/layout.tsx @@ -1,7 +1,7 @@ import { notFound } from "next/navigation"; import LanguageSwitcher from "@/components/ui/language-switcher"; -import { isLocale, localeDirections } from "@/i18n/config"; -import { I18nProvider } from "@/i18n/provider"; +import { isLocale, localeDirections } from "@/translations/config"; +import { I18nProvider } from "@/translations/provider"; export function generateStaticParams() { return [{ lang: "en" }, { lang: "fa" }]; diff --git a/src/app/[lang]/questions-list/[slug]/page.tsx b/src/app/[lang]/questions-list/[slug]/page.tsx index 334af5f..f78c120 100644 --- a/src/app/[lang]/questions-list/[slug]/page.tsx +++ b/src/app/[lang]/questions-list/[slug]/page.tsx @@ -1,5 +1,5 @@ import { getQuestionListItems } from "@/data/question-data"; -import { locales } from "@/i18n/config"; +import { locales } from "@/translations/config"; import QuestionDetailPage from "@/app/questions-list/[slug]/page"; export function generateStaticParams() { diff --git a/src/app/candidate-contact/page.tsx b/src/app/candidate-contact/page.tsx index b0e0728..354d77b 100644 --- a/src/app/candidate-contact/page.tsx +++ b/src/app/candidate-contact/page.tsx @@ -6,7 +6,7 @@ import Button from "@/components/ui/button"; import CallResultSheet from "@/components/ui/call-result-sheet"; import DismissReasonSheet from "@/components/ui/dismiss-reason-sheet"; import NavigationButton from "@/components/ui/navigation-button"; -import { useI18n } from "@/i18n/provider"; +import { useI18n } from "@/translations/provider"; export default function CandidateContactPage() { const { dictionary: t } = useI18n(); diff --git a/src/app/finding-match/page.tsx b/src/app/finding-match/page.tsx index 06c07a9..ec14a99 100644 --- a/src/app/finding-match/page.tsx +++ b/src/app/finding-match/page.tsx @@ -7,8 +7,8 @@ import AdvisorActionsCard from "@/components/ui/advisor-actions-card"; import NavigationButton from "@/components/ui/navigation-button"; import { PageBackground } from "@/components/utils/page-background"; import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main"; -import { localizePath } from "@/i18n/config"; -import { useI18n } from "@/i18n/provider"; +import { localizePath } from "@/translations/config"; +import { useI18n } from "@/translations/provider"; const advisorAvatars = [ { id: "advisor-primary", src: "/assets/images/Avatar Image.png" }, diff --git a/src/app/intro/page.tsx b/src/app/intro/page.tsx index 1f3b845..a3d67b2 100644 --- a/src/app/intro/page.tsx +++ b/src/app/intro/page.tsx @@ -9,8 +9,8 @@ import ReportActionsSheet from "@/components/ui/report-actions-sheet"; import { authBridge } from "@/lib/auth-bridge"; import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main"; import type { MarriageProfileResponse } from "@/hooks/marriage/types"; -import { localizePath } from "@/i18n/config"; -import { useI18n } from "@/i18n/provider"; +import { localizePath } from "@/translations/config"; +import { useI18n } from "@/translations/provider"; const REDIRECT_SESSION_KEY = "redirect"; diff --git a/src/app/new-match/page.tsx b/src/app/new-match/page.tsx index 9cd135a..1a9e6c2 100644 --- a/src/app/new-match/page.tsx +++ b/src/app/new-match/page.tsx @@ -13,7 +13,7 @@ import type { MarriagePhoneFieldValue, } from "@/hooks/marriage/types"; import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main"; -import { useI18n } from "@/i18n/provider"; +import { useI18n } from "@/translations/provider"; import { useViewPaddings } from "@/hooks/use-view-paddings"; const advisorAvatars = [ diff --git a/src/app/new-match/profile/page.tsx b/src/app/new-match/profile/page.tsx index 82e39fa..65aae02 100644 --- a/src/app/new-match/profile/page.tsx +++ b/src/app/new-match/profile/page.tsx @@ -19,8 +19,8 @@ import type { } from "@/hooks/marriage/types"; import { useRespondToMarriageCaseMutation } from "@/hooks/marriage/use-case-respond"; import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main"; -import { localizePath } from "@/i18n/config"; -import { useI18n } from "@/i18n/provider"; +import { localizePath } from "@/translations/config"; +import { useI18n } from "@/translations/provider"; function formatFieldValue(value: MarriageFieldValue) { if (value === null || value === "") { diff --git a/src/app/questions-list/[slug]/page.tsx b/src/app/questions-list/[slug]/page.tsx index 3f5fb8d..1277d9d 100644 --- a/src/app/questions-list/[slug]/page.tsx +++ b/src/app/questions-list/[slug]/page.tsx @@ -4,8 +4,8 @@ import { getQuestionListItemBySlug, getQuestionListItems, } from "@/data/question-data"; -import { defaultLocale, isLocale, locales } from "@/i18n/config"; -import { getDictionary } from "@/i18n/dictionaries"; +import { defaultLocale, isLocale, locales } from "@/translations/config"; +import { getDictionary } from "@/translations/dictionaries"; import QuestionDetailClient from "./question-detail-client"; export function generateStaticParams() { diff --git a/src/app/questions-list/[slug]/question-detail-client.tsx b/src/app/questions-list/[slug]/question-detail-client.tsx index 0d2083d..3218d30 100644 --- a/src/app/questions-list/[slug]/question-detail-client.tsx +++ b/src/app/questions-list/[slug]/question-detail-client.tsx @@ -32,8 +32,8 @@ import { } from "@/data/question-data"; import type { MarriageGender } from "@/hooks/marriage/types"; import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main"; -import { defaultLocale, type Locale } from "@/i18n/config"; -import { useI18n } from "@/i18n/provider"; +import { defaultLocale, type Locale } from "@/translations/config"; +import { useI18n } from "@/translations/provider"; import { useCattellQuestionsQuery, useSubmitCattellAssessmentMutation, @@ -44,7 +44,6 @@ import { } from "@/hooks/marriage/use-glasser"; import { cattellFallbackQuestions } from "@/data/cattell-fallback"; import { glasserFallbackQuestions } from "@/data/glasser-fallback"; -import TestLoadingScreen from "@/components/questions/test-loading-screen"; import TestIntroPage from "@/components/questions/test-intro-page"; import TestQuestionsFlow, { type TestQuestion } from "@/components/questions/test-questions-flow"; import AnswerPaceSheet from "./answer-pace-sheet"; @@ -425,7 +424,9 @@ export default function QuestionDetailClient({ const rawOptions = q.options && q.options.length > 0 ? q.options - : ["بله", "به اندازه کافی واضح نیست", "نه"]; + : locale === "fa" + ? ["بله", "به اندازه کافی واضح نیست", "نه"] + : ["Yes", "Not clear enough", "No"]; const mappedOptions = rawOptions.map((optText, idx) => ({ label: optText, value: idx === 0 ? "A" : idx === 1 ? "B" : "C", @@ -503,7 +504,14 @@ export default function QuestionDetailClient({ }, [isProfileLoading, item, profileContext, questionsListHref, router]); if (isProfileLoading && item) { - return ; + return ( + <> + +
+
+
+ + ); } else if (!item || !isQuestionListItemVisibleForProfile(item, profileContext)) { return null; } @@ -518,14 +526,12 @@ export default function QuestionDetailClient({ if (isQuestionsLoading) { return ( - + <> + +
+
+
+ ); } diff --git a/src/app/questions-list/page.tsx b/src/app/questions-list/page.tsx index d9ef296..79de471 100644 --- a/src/app/questions-list/page.tsx +++ b/src/app/questions-list/page.tsx @@ -21,8 +21,8 @@ import { hasQuestionAnswerValue } from "@/components/questions/question-answer-s import { useStartMarriageMatchMutation } from "@/hooks/marriage/use-match-start"; import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main"; import { useMarriageSectionsQuery } from "@/hooks/marriage/use-sections"; -import { localizePath } from "@/i18n/config"; -import { useI18n } from "@/i18n/provider"; +import { localizePath } from "@/translations/config"; +import { useI18n } from "@/translations/provider"; import { toFrontendSlug } from "@/data/section-slug-map"; import SectionsRequest from "./sections-request"; import { getStoredAge, getLocalSectionProgress } from "@/components/questions/progress-helper"; diff --git a/src/app/request-accepted/page.tsx b/src/app/request-accepted/page.tsx index 475f2b8..ce55629 100644 --- a/src/app/request-accepted/page.tsx +++ b/src/app/request-accepted/page.tsx @@ -17,8 +17,8 @@ import { useHabcoinPaymentMutation, } from "@/hooks/marriage/use-habcoin-payment"; import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main"; -import { localizePath } from "@/i18n/config"; -import { useI18n } from "@/i18n/provider"; +import { localizePath } from "@/translations/config"; +import { useI18n } from "@/translations/provider"; type ContactInfoPhoneItem = { key: string; diff --git a/src/app/request-sent/page.tsx b/src/app/request-sent/page.tsx index f1ccdee..dff0c1b 100644 --- a/src/app/request-sent/page.tsx +++ b/src/app/request-sent/page.tsx @@ -4,7 +4,7 @@ import Image from "next/image"; import AdvisorActionsCard from "@/components/ui/advisor-actions-card"; import NavigationButton from "@/components/ui/navigation-button"; import { PageBackground } from "@/components/utils/page-background"; -import { useI18n } from "@/i18n/provider"; +import { useI18n } from "@/translations/provider"; import Link from "next/link"; const advisorAvatars = [ diff --git a/src/components/questions/question-card.tsx b/src/components/questions/question-card.tsx index 5ad2385..07222b4 100644 --- a/src/components/questions/question-card.tsx +++ b/src/components/questions/question-card.tsx @@ -9,8 +9,8 @@ import { IoSchool, } from "react-icons/io5"; import type { QuestionCardIcon, QuestionListItem } from "@/data/question-data"; -import { localizePath } from "@/i18n/config"; -import { useI18n } from "@/i18n/provider"; +import { localizePath } from "@/translations/config"; +import { useI18n } from "@/translations/provider"; import Image from "next/image"; type QuestionCardProps = { diff --git a/src/components/questions/question-date.tsx b/src/components/questions/question-date.tsx index 2181b99..646b17a 100644 --- a/src/components/questions/question-date.tsx +++ b/src/components/questions/question-date.tsx @@ -12,18 +12,18 @@ type QuestionDateProps = { }; const MONTHS = [ - { value: "01", label: "01 - ژانویه" }, - { value: "02", label: "02 - فوریه" }, - { value: "03", label: "03 - مارس" }, - { value: "04", label: "04 - آوریل" }, - { value: "05", label: "05 - می" }, - { value: "06", label: "06 - ژوئن" }, - { value: "07", label: "07 - ژوئیه" }, - { value: "08", label: "08 - آگوست" }, - { value: "09", label: "09 - سپتامبر" }, - { value: "10", label: "10 - اکتبر" }, - { value: "11", label: "11 - نوامبر" }, - { value: "12", label: "12 - دسامبر" }, + { value: "01", label: "01 - January" }, + { value: "02", label: "02 - February" }, + { value: "03", label: "03 - March" }, + { value: "04", label: "04 - April" }, + { value: "05", label: "05 - May" }, + { value: "06", label: "06 - June" }, + { value: "07", label: "07 - July" }, + { value: "08", label: "08 - August" }, + { value: "09", label: "09 - September" }, + { value: "10", label: "10 - October" }, + { value: "11", label: "11 - November" }, + { value: "12", label: "12 - December" }, ]; const DAYS = Array.from({ length: 31 }, (_, i) => { @@ -101,10 +101,10 @@ export function QuestionDate({ value={day} onChange={(e) => handleSelectChange(year, month, e.target.value)} disabled={disabled} - aria-label="روز" + aria-label="Day" className="h-[54px] w-full cursor-pointer rounded-[15px] border border-[#E7D8D5] bg-white px-3 group-12 text-[#181818] outline-none transition-all focus:border-[#6F6F6F]" > - + {DAYS.map((d) => (