From 394aa983145303aac17ddbda78bab51339f38af0 Mon Sep 17 00:00:00 2001 From: Admin Date: Sat, 11 Jul 2026 00:42:33 +0330 Subject: [PATCH] Remove unused build-messages.mjs i18n generator script. The app loads frontend/messages/*.json directly via next-intl; the script was never wired into npm scripts or CI and had drifted out of sync with the JSON files. Co-authored-by: Cursor --- frontend/scripts/build-messages.mjs | 636 ---------------------------- 1 file changed, 636 deletions(-) delete mode 100644 frontend/scripts/build-messages.mjs diff --git a/frontend/scripts/build-messages.mjs b/frontend/scripts/build-messages.mjs deleted file mode 100644 index c45e4cb..0000000 --- a/frontend/scripts/build-messages.mjs +++ /dev/null @@ -1,636 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import { fileURLToPath } from 'url'; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const messagesDir = path.join(__dirname, '..', 'messages'); - -const en = { - common: { - appName: 'DyoLink', - loading: 'Loading...', - loadingApp: 'Loading app...', - loadingWorkspace: 'Loading workspace...', - continue: 'Continue', - back: 'Back', - save: 'Save', - cancel: 'Cancel', - delete: 'Delete', - edit: 'Edit', - next: 'Next', - dismiss: 'Dismiss', - or: 'Or', - and: 'and', - close: 'Close', - redirecting: 'Redirecting…', - readOnlyAccess: 'Read-only access for this organization.', - errorGeneric: 'Something went wrong', - loadingEllipsis: 'Loading...', - search: 'Search', - action: 'Action', - status: 'Status', - name: 'Name', - email: 'Email', - date: 'Date', - organization: 'Organization', - backToApp: '← Back to app', - copied: 'Copied', - copyLink: 'Copy link', - none: 'None', - preview: 'Preview', - }, - language: { - label: 'Language', - selectLanguage: 'Select language', - en: 'English', - fa: 'Persian', - nl: 'Dutch', - }, - theme: { - switchToLight: 'Switch to light mode', - switchToDark: 'Switch to dark mode', - lightMode: 'Light mode', - darkMode: 'Dark mode', - }, - nav: { - dashboard: 'Dashboard', - staff: 'Staff', - patients: 'Patients', - appointment: 'Appointment', - treatment: 'Treatment', - billing: 'Billing', - reports: 'Reports', - clinics: 'Clinics', - labs: 'Labs', - }, - auth: { - login: 'Login', - signIn: 'Sign in', - signOut: 'Log out', - register: 'Register', - startTrial: 'Start Trial', - startFreeTrial: 'Start Free Trial', - dashboard: 'Dashboard', - signInTitle: 'Sign in to your account', - signInPrompt: 'Or {link}', - startTrialLink: 'start your free trial', - registerTitle: 'Start your 30-day free trial', - registerPrompt: 'Already have an account?', - signInLink: 'Sign in', - email: 'Email address', - password: 'Password', - confirmPassword: 'Confirm password', - fullName: 'Full name', - rememberMe: 'Remember me', - showPassword: 'Show password', - hidePassword: 'Hide password', - forgotPassword: 'Forgot your password?', - invalidCredentials: 'Invalid email or password', - loginFailed: 'Login failed', - registrationFailed: 'Registration failed. Please try again.', - startMyFreeTrial: 'Start my free trial', - trialIncludes: 'Your trial includes:', - trialTeamMembers: 'Up to 5 team members', - trialFullAccess: 'Full access to all features', - trialNoCard: '30 days free, no credit card required', - termsAgreement: 'By signing up, you agree to our {terms} and {privacy}', - termsOfService: 'Terms of Service', - privacyPolicy: 'Privacy Policy', - signedIn: 'Signed in', - switchOrganization: 'Switch organization', - subscriptions: 'Subscriptions', - account: 'Account', - emailPlaceholder: 'you@example.com', - passwordPlaceholder: '••••••••', - namePlaceholder: 'John Doe', - termsIntro: 'By signing up, you agree to our', - errorRegistrationFailed: 'Registration failed', - errorLoginFailed: 'Login failed', - errorCreateOrganization: 'Failed to create organization', - acceptInviteTitle: 'Accept invitation', - loadingInvitation: 'Loading invitation...', - invalidInvitationLink: 'Invalid invitation link', - invitationAlreadyAccepted: 'This invitation is already accepted. You can log in now.', - errorLoadInvitation: 'Could not load invitation', - organizationLabel: 'Organization:', - emailLabel: 'Email:', - nameRequired: 'Name is required', - passwordMinLength8: 'Password must be at least 8 characters', - passwordsDoNotMatch: 'Passwords do not match', - labelName: 'Name', - labelCreatePassword: 'Create password', - labelConfirmPassword: 'Confirm password', - activateAccount: 'Activate account', - invitationAcceptedRedirect: 'Invitation Accepted. Redirecting to login...', - errorAcceptInvitation: 'Could not accept invitation', - alreadyHaveAccess: 'Already have access?', - goToLogin: 'Go to login', - acceptOrganizationTitle: 'Accept organization invitation', - alreadyHaveAccount: 'Already have an account?', - invitedBy: 'Invited by:', - ownerEmail: 'Owner email', - activateOrganization: 'Activate organization', - organizationAcceptedRedirect: 'Invitation accepted. Redirecting to login...', - stepAccount: 'Account', - stepOrganization: 'Organization', - organizationName: 'Organization name', - organizationNamePlaceholder: 'Sunshine Dental Clinic', - organizationEmail: 'Organization email', - organizationEmailPlaceholder: 'contact@sunshineclinic.com', - organizationType: 'Organization type', - dentalClinic: 'Dental Clinic', - dentalLab: 'Dental Lab', - }, - landing: { - heroTitle: 'Connect Dental Clinics & Labs', - heroHighlight: 'Seamlessly', - heroSubtitle: - 'Streamline communication between dental professionals. Start with a 30-day free trial, no credit card required.', - featureClinicsTitle: 'For Clinics', - featureClinicsDescription: - 'Manage patients, appointments, and send cases to labs instantly.', - featureLabsTitle: 'For Labs', - featureLabsDescription: 'Receive cases, track progress, and communicate with clinics.', - featureTeamTitle: 'Team Management', - featureTeamDescription: 'Add up to 5 team members during trial. Scale as you grow.', - featureTrialTitle: '30-Day Trial', - featureTrialDescription: 'Full access to all features. No credit card required.', - featureRealtimeTitle: 'Real-time Updates', - featureRealtimeDescription: 'Get instant notifications on case status changes.', - featureSecurityTitle: 'Secure & Compliant', - featureSecurityDescription: 'HIPAA-compliant with enterprise-grade security.', - footerCopyright: '© 2026 DyoLink. All rights reserved.', - termsAndConditions: 'Terms & Conditions', - }, - accountMenu: { - noActiveSubscription: 'No active subscription — review Subscriptions', - trialEnded: 'Trial ended — review Subscriptions', - trialEndingSoon: 'Trial ending soon — review Subscriptions', - seatsLow: 'Seats running low — review Subscriptions', - reviewSubscriptions: 'Review Subscriptions', - }, - validation: { - emailInvalid: 'Please enter a valid email address', - passwordRequired: 'Password is required', - nameMinLength: 'Name must be at least 2 characters', - passwordMinLength: 'Password must be at least 8 characters', - passwordUppercase: 'Password must contain at least one uppercase letter', - passwordNumber: 'Password must contain at least one number', - organizationNameMinLength: 'Organization name must be at least 2 characters', - organizationEmailInvalid: 'Please enter a valid organization email', - organizationTypeRequired: 'Please select organization type', - passwordsDoNotMatch: "Passwords don't match", - }, - today: { - welcomeBack: 'Welcome back!!', - noSubscriptionNotice: 'This organization does not have an active subscription yet.', - choosePlanLink: 'Choose a plan', - noSubscriptionCta: 'to start the purchase process.', - noWidgets: 'No dashboard metrics are available for your current permissions.', - loadError: 'Could not load dashboard metrics.', - seatsUnlimited: 'Unlimited plan', - widgetAppointmentsToday: "Today's Appointments", - widgetPatientsToday: 'Patients Today', - widgetTreatmentsToday: 'Treatments Today', - widgetDraftTreatments: 'Draft Treatments', - widgetLabCasesPendingSend: 'Lab Cases Pending Send', - widgetCasesReceivedToday: 'Cases Received Today', - widgetTasksInProgress: 'Tasks In Progress', - widgetImportantTasks: 'Important Tasks', - widgetPendingConnections: 'Pending Connections', - widgetSeats: 'Seat Usage', - widgetPendingStaffInvites: 'Pending Staff Invites', - chartTreatmentMixTitle: 'Treatment Mix', - chartTreatmentMixSubtitle: 'Last 7 days', - chartTasksByStepTitle: 'Tasks by Workflow Step', - chartTasksByStepSubtitle: 'In progress now', - chartEmpty: 'No data for this period yet.', - }, - staff: { - redirecting: 'Redirecting…', - title: 'Staff Management', - subtitle: 'Invite teammates, set tab access, and stay within your plan seat limit.', - inviteMember: 'Invite member', - seatsLabel: 'Seats:', - unlimitedPlan: '(unlimited plan)', - seatLimitReached: 'Plan seat limit reached for this organization.', - noActivePlan: - 'No active plan selected for this organization. Choose a subscription plan to invite members.', - invitedPending: - 'Invitation is pending until they open the link, set a password, and log in.', - invitedAccepted: 'Invitation was accepted immediately.', - inviteLinkHeading: 'Invite link', - shareLinkHint: - 'Share this link manually via SMS or email. A new link is generated if the previous one expired or was lost.', - loadingTeam: 'Loading team…', - tableName: 'Name', - tableEmail: 'Email', - tableRole: 'Role', - tableStatus: 'Status', - tableAccess: 'Access', - tableAction: 'Action', - roleOwner: 'Owner', - roleStaff: 'Staff', - statusActive: 'Active', - statusPending: 'Pending', - statusDisabled: 'Disabled', - statusExpired: 'Expired', - allFeatures: 'All features', - inviteModalTitle: 'Invite team member', - stepOf: 'Step {step} of 2', - permissionView: 'View', - permissionEdit: 'Edit', - labelEmail: 'Email', - labelDisplayName: 'Display name', - tabAccess: 'Tab access', - sendInvite: 'Send invite', - skipForNow: 'Skip for now', - enableModalTitle: 'Enable team member', - enableConfirm: 'Enable {name} ({email})?', - enableBullet1: 'They can sign in to this organization again with their existing account.', - enableBullet2: 'No new invitation is sent and no data was removed while they were disabled.', - enableBullet3: 'Enabling uses one seat on your plan.', - noSeatsAvailable: - 'No seats are available. Disable another member or upgrade your plan before enabling this person.', - enableMemberButton: 'Enable member', - disableModalTitle: 'Disable team member', - disableConfirm: 'Disable {name} ({email})?', - disableBullet1: 'They will not be able to sign in to this organization.', - disableBullet2: 'No data will be removed.', - disableBullet3: 'Disabling frees one seat on your plan so you can invite someone else.', - disableMemberButton: 'Disable member', - editModalTitle: 'Edit member', - loadingWorkingHours: 'Loading working hours…', - errorLoadStaff: 'Failed to load staff.', - errorCopyInvite: 'Could not copy invitation link.', - errorSendInvite: 'Failed to send invitation.', - errorLoadWorkingHours: 'Failed to load working hours.', - successMemberUpdated: 'Member updated.', - errorUpdateMember: 'Failed to update member.', - errorDeleteNotImplemented: 'Delete is not implemented yet.', - successMemberDisabled: '{name} was disabled. A seat is now available.', - errorDisableMember: 'Failed to disable member.', - successMemberEnabled: '{name} was enabled and can sign in again.', - errorEnableMember: 'Failed to enable member.', - successInvited: '{name} ({email}) was invited.', - copyInviteLink: 'Copy invitation link', - copyInviteLinkTitle: 'Copy invitation link (generates a new link if needed)', - enableMemberAria: 'Enable member', - enableMemberTitle: 'Enable member (uses a seat)', - disableMemberAria: 'Disable member', - disableMemberTitle: 'Disable member (frees a seat)', - editMemberAria: 'Edit member', - deleteMemberAria: 'Delete member', - deleteMemberTitle: 'Delete member (not implemented)', - features: { - featureToday: 'Today', - featureStaff: 'Staff', - featureOrganizations: 'Organizations', - featureClinics: 'Clinics', - featureLabs: 'Labs', - featurePatients: 'Patients', - featureAppointment: 'Appointment', - featureTreatment: 'Treatment', - featureBilling: 'Billing', - featureReports: 'Reports', - noTabAccess: 'No tab access', - readOnlySuffix: '(Read only)', - }, - workingHours: { - recommendedTitle: 'Working hours recommended', - recommendedBody: - 'Staff with treatment edit access appear as provider columns in Appointments. Set their weekly hours so the schedule grid shows the right bookable times.', - intro: - 'Set weekly working hours for this provider. The appointments grid uses these hours to show bookable time slots.', - workingDay: 'Working day', - start: 'Start', - end: 'End', - removeShift: 'Remove shift', - addShift: 'Add shift', - autoRepeatWeekly: 'Repeat these hours at the start of each week (copy forward on Monday)', - weekdayMon: 'Mon', - weekdayTue: 'Tue', - weekdayWed: 'Wed', - weekdayThu: 'Thu', - weekdayFri: 'Fri', - weekdaySat: 'Sat', - weekdaySun: 'Sun', - validationNeedsShift: '{day} needs at least one shift or should be marked off.', - validationEndAfterStart: '{day} shift end time must be after start time.', - validationOverlap: '{day} shifts cannot overlap.', - }, - }, - patients: { - title: 'Patients', - newPatient: 'New Patient', - errorLoadPatients: 'Failed to load patients.', - successPatientSaved: 'Patient {firstName} {lastName} was saved successfully.', - errorSavePatient: 'Failed to save patient.', - firstName: 'First name', - lastName: 'Last name', - phone: 'Phone', - savePatient: 'Save Patient', - dialogTitle: 'New patient', - searchPlaceholder: 'Search patients by name, phone, email', - loadingPatients: 'Loading patients...', - noResults: 'No patients found for this search.', - noContact: 'No contact', - selectPatient: 'Select a patient to view details.', - phoneLabel: 'Phone:', - emailLabel: 'Email:', - statusLabel: 'Status:', - statusActive: 'Active', - statusInactive: 'Inactive', - emptyValue: '-', - }, - appointments: { - title: 'Appointments', - subtitle: 'Search a patient, pick a date, then click a time slot under a provider to book.', - loadingSchedule: 'Loading schedule…', - infoPastViewOnly: 'Past appointments are view-only.', - infoSelectPatient: 'Select a patient before booking.', - errorOutsideHours: - "This appointment falls outside the provider's current working hours and cannot be edited.", - successUpdated: 'Appointment updated.', - successSaved: 'Appointment saved.', - errorUpdate: 'Could not update appointment.', - errorSave: 'Could not save appointment.', - confirmRemove: 'Remove this appointment?', - successRemoved: 'Appointment removed.', - errorDelete: 'Could not delete appointment.', - errorLoadSchedule: 'Failed to load schedule.', - successPatientSaved: 'Patient {firstName} {lastName} was saved.', - searchPlaceholder: 'Search existing patients', - searching: 'Searching…', - searchHint: 'Type to search patients by name, phone, or email.', - noPermissionAdd: 'You do not have permission to add patients.', - editTitle: 'Edit appointment', - newTitle: 'New appointment', - providerLabel: 'Provider:', - patientLabel: 'Patient', - startLabel: 'Start', - endLabel: 'End', - purposeLabel: 'Purpose', - errorSelectPatient: 'Select a patient first.', - errorEndAfterStart: 'End time must be after start time.', - errorPastSchedule: 'Cannot schedule in the past.', - errorPastViewOnly: 'Past appointments are view-only.', - errorMissingDetails: 'Missing appointment details.', - noProviders: - 'No providers available. Add staff with treatment edit access to see columns here.', - noWorkingHours: - 'No working hours are configured for this day. Set provider working hours in Staff management.', - noHoursSet: 'No hours set', - offToday: 'Off today', - slotOffToday: 'Provider is off today', - slotHoursNotConfigured: 'Working hours not configured', - slotOutsideHours: 'Outside working hours', - slotCannotCreate: 'You cannot create appointments', - slotBookAt: 'Book {time}', - outsideHoursBlocked: 'Outside working hours — editing blocked', - overlappingChoose: '{count} overlapping — click to choose', - overlapping: '{count} overlapping', - legend: 'Legend', - overlappingTitle: 'Overlapping appointments ({count})', - purposeConsultation: 'Consultation', - purposeFilling: 'Filling', - purposeEndo: 'Endo', - purposeVisit: 'Visit', - purposeHygiene: 'Hygiene', - }, - treatment: { - loading: 'Loading…', - noPermissionTitle: 'Treatment workspace', - noPermissionBody: 'You do not have permission to view the Treatment tab for this organization.', - title: 'Treatment', - subtitleEdit: - 'Document cases for your appointments, save drafts, and send work to linked organizations.', - subtitleReadOnly: - 'View-only access — you can review appointments and treatment history but cannot edit.', - pastDayNotice: - 'Past days are view-only. You can review appointments and history, but treatment cases cannot be added or changed.', - selectedPatient: 'Selected patient', - purposeLabel: 'Purpose:', - loadingAppointments: 'Loading appointments…', - selectDayWithAppointment: 'Select a day with at least one appointment.', - confirmDiscard: 'You have unsaved changes. Discard them and continue?', - successDraftSaved: 'Treatment draft saved.', - errorChooseOrg: 'Choose at least one active organization to send this case.', - successCaseSent: 'Case sent to selected organizations.', - successFilesUploaded: '{count} file(s) uploaded successfully.', - errorLoadAppointments: 'Failed to load appointments.', - errorLoadOrgs: 'Failed to load linked organizations.', - errorLoadHistory: 'Failed to load treatment history.', - errorLoadDraft: 'Failed to load treatment draft.', - errorUpload: 'Failed to upload attachments.', - errorSaveDraft: 'Failed to save treatment draft.', - errorSendCase: 'Failed to send case.', - errorCaseMustSave: 'Case must be saved before sending.', - draftTitle: 'Draft · {patientName}', - hiddenMessage: 'Appointments are hidden.', - showAppointments: 'Show appointments', - appointmentsTitle: 'My appointments', - hideAppointments: 'Hide appointments', - emptyDay: 'No appointments assigned to you on this day.', - casesTitle: 'Treatment cases', - casesSubtitle: 'Each case has its own teeth, notes, attachments, and destinations for send.', - addCase: 'Add case', - caseLabel: 'Case {n}', - comments: 'Comments', - commentsPlaceholder: 'Write clinical notes for this case…', - treatmentType: 'Treatment type', - typeConsultation: 'consultation', - typeFilling: 'filling', - typeEndo: 'endo', - typeVisit: 'visit', - typeHygiene: 'hygiene', - attachments: 'Attachments', - attachFiles: 'Attach files for this treatment case', - chooseFiles: 'Choose files', - sendToOrgs: 'Send this case to linked organizations', - searchOrgsPlaceholder: 'Search active organizations...', - recent: 'Recent:', - noOrgMatch: 'No active organization matches your search.', - sendThisCase: 'Send this case', - saveDraft: 'Save treatment draft', - unsavedChanges: 'Unsaved changes', - draftSaved: 'Draft saved', - sendSavesFirst: 'Sending is per case and saves first automatically.', - historyTitle: 'Previous treatments', - historySubtitle: 'Completed treatments for this patient. Each case is listed separately.', - loadingHistory: 'Loading history…', - historyEmpty: 'No prior treatments for this patient.', - statusLabel: 'Status:', - historyCaseLabel: 'Case {n} · {type}', - teethLabel: 'Teeth:', - teethNone: 'None selected', - reviewDetails: 'Review details', - previewTitle: 'Treatment preview', - previewDraft: 'Preview current draft', - selectAppointment: 'Select an appointment to preview its draft.', - caseCount: '{n} case(s)', - attachmentCount: '{n} attachment(s)', - caseSummary: 'Case {n}: {type}', - teethPrefix: '· Teeth', - moreCases: '+ {n} more case(s)', - previewDialogTitle: 'Treatment preview', - previewDialogSubtitle: 'Review cases, attachments, and send destinations.', - noCases: 'No cases in this treatment.', - typeLabel: 'Type:', - commentsLabel: 'Comments:', - commentsEmpty: 'Comments: —', - attachFilesShort: 'Attach files', - sendCase: 'Send this case', - sendToLinkedOrgs: 'Send to linked organizations', - noActiveOrgs: 'No active linked organizations.', - confirmSend: 'Confirm send', - toothChartTitle: 'FDI tooth chart', - toothChartHint: 'Tap teeth to multi-select. Applies to the active case.', - selectedLabel: 'Selected:', - selectedEmpty: '—', - upperArch: 'Upper arch', - lowerArch: 'Lower arch', - toothAria: 'FDI tooth {fdi}', - toothSelectedSuffix: ', selected', - sentToAt: 'Sent to {orgName} at {datetime}', - fallbackOrgName: 'organization', - }, - organizations: { - loadingOrganization: 'Loading organization...', - subtitle: - 'Search organizations, send connection requests to existing accounts, or invitation links when they are not on DyoLink yet.', - invitationHistory: 'Invitation History', - searchPlaceholder: 'Search {counterpart} by name, email, or phone...', - backToList: 'Back to list', - tableOrganization: 'Organization', - tableOwnerEmail: 'Owner email', - tableDate: 'Date', - tableStatus: 'Status', - tableAction: 'Action', - emptyConnections: 'No connections yet. Search to send a connection request or an invitation link.', - statusInvitationPending: 'Invitation pending', - statusConnectionPending: 'Connection request pending', - statusConnected: 'Connected', - statusDeclined: 'Connection request declined', - statusFound: 'Found', - statusToday: 'Today', - acceptRequest: 'Accept connection request', - declineRequest: 'Decline connection request', - removeConnection: 'Remove connection', - sendRequest: 'Send connection request', - noDirectoryResults: 'No organization found in directory search.', - hideInvitationFields: 'Hide invitation fields', - sendInvitationLink: 'Send invitation link', - counterpartNameLabel: '{counterpart} name', - ownerEmailLabel: 'Owner email', - sendInvitation: 'Send invitation', - successConnectionSent: '{counterpart} connection request sent.', - successInviteCreated: 'Invitation link created for {email}', - successLinkCopied: 'Invitation link copied to clipboard.', - successAccepted: 'Connection request accepted.', - successDeclined: 'Connection request declined.', - successRemoved: 'Connection removed.', - historyTitle: 'Invitation History', - loadingHistory: 'Loading invitation history...', - historyEmpty: 'No invitations yet.', - tableInvitationLink: 'Invitation link', - statusPending: 'Invitation pending', - statusAccepted: 'Invitation accepted', - statusRejected: 'Invitation rejected', - statusExpired: 'Invitation expired', - copyInvitationLink: 'Copy invitation link', - copyInvitationLinkTitle: 'Copy invitation link (generates a new link if needed)', - selectorTitle: 'Organizations', - selectorSubtitleWithCreate: 'Select an organization to continue, or create a new one.', - selectorSubtitleSelectOnly: 'Select an organization to continue.', - createOrganization: 'Create Organization', - createAndContinue: 'Create and Continue', - emptyCanCreate: 'No organizations found. Create your first one to continue.', - emptyAskOwner: 'No organizations found. Ask an organization owner to invite you.', - continueArrow: 'Continue →', - planLabel: 'Plan: {name} • {maxUsers} users', - }, - settings: { - accountTitle: 'Account', - accountSubtitle: 'Profile and security settings for your login.', - accountPlaceholder: - 'Password change and profile editing will be wired here next (e.g. invite flow, reset password).', - subscriptionsTitle: 'Subscriptions', - subscriptionsSubtitle: - 'Your DyoLink workspace plan and seats for {orgName}. Clinic and lab income tracking stays under the sidebar Billing tab.', - noSubscriptionNotice: - 'This organization has no active subscription. Select a plan below to start the purchase process.', - currentPlan: 'Current plan', - planPrice: 'Plan price', - seatsUsed: 'Seats used', - seatsRemaining: 'Seats remaining', - daysRemaining: 'Days remaining', - unlimited: 'Unlimited', - unlimitedSeats: 'Unlimited seats', - seatsCount: '{n} seats', - pricePerMonth: '${price} / month', - noActiveSubscription: 'No active subscription for this organization.', - trialEnded: 'Trial period has ended. Choose a plan when checkout is available.', - trialEndsIn: 'Trial ends in {days} day(s).', - seatsLow: 'Seat usage is high for this organization.', - choosePlanIntro: - 'Choose a plan to continue. Purchase integration is not active yet, so this currently prepares the selection step only.', - planSolo: 'Solo', - planSmall: 'Small', - planMedium: 'Medium', - planLarge: 'Large', - planEnterprise: 'Enterprise', - startPurchase: 'Start purchase process', - purchaseNotice: - 'Purchase flow will be enabled soon. {plan} is selected and ready for checkout setup.', - }, - schedule: { - defaultLabel: 'Schedule date', - previousDay: 'Previous day', - nextDay: 'Next day', - chooseDate: 'Choose schedule date', - year: 'Year', - month: 'Month', - day: 'Day', - monthJanuary: 'January', - monthFebruary: 'February', - monthMarch: 'March', - monthApril: 'April', - monthMay: 'May', - monthJune: 'June', - monthJuly: 'July', - monthAugust: 'August', - monthSeptember: 'September', - monthOctober: 'October', - monthNovember: 'November', - monthDecember: 'December', - }, -}; - -function deepMerge(base, overlay) { - const result = { ...base }; - for (const key of Object.keys(base)) { - const baseVal = base[key]; - const overlayVal = overlay?.[key]; - if (baseVal && typeof baseVal === 'object' && !Array.isArray(baseVal)) { - result[key] = deepMerge(baseVal, overlayVal ?? {}); - } else if (overlayVal !== undefined) { - result[key] = overlayVal; - } - } - return result; -} - -function writeJson(file, data) { - fs.writeFileSync(file, `${JSON.stringify(data, null, 2)}\n`, 'utf8'); -} - -writeJson(path.join(messagesDir, 'en.json'), en); - -for (const locale of ['fa', 'nl']) { - const file = path.join(messagesDir, `${locale}.json`); - const existing = fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf8')) : {}; - writeJson(file, deepMerge(en, existing)); -} - -console.log('Messages built: en.json updated; fa.json and nl.json merged with existing translations.');