diff --git a/apps/extension/src/pages/content/elements/action.component.tsx b/apps/extension/src/pages/content/elements/action.component.tsx index ea6ec012..109520b2 100644 --- a/apps/extension/src/pages/content/elements/action.component.tsx +++ b/apps/extension/src/pages/content/elements/action.component.tsx @@ -102,7 +102,7 @@ export const ActionComponent: FC<{ return (
-
+
{modal && ( +
{t('post_not_found', 'Post not found')}
); @@ -119,7 +119,7 @@ export default async function Auth({ src={post[0].integration.picture} />
-
+
{post[0].integration.providerIdentifier} -
+
-
+
-
+
-
+
diff --git a/apps/frontend/src/app/(app)/layout.tsx b/apps/frontend/src/app/(app)/layout.tsx index 5f5eaf07..3f8176a0 100644 --- a/apps/frontend/src/app/(app)/layout.tsx +++ b/apps/frontend/src/app/(app)/layout.tsx @@ -16,6 +16,8 @@ import { ToltScript } from '@gitroom/frontend/components/layout/tolt.script'; import { FacebookComponent } from '@gitroom/frontend/components/layout/facebook.component'; import { headers } from 'next/headers'; import { headerName } from '@gitroom/react/translation/i18n.config'; +import { HtmlComponent } from '@gitroom/frontend/components/layout/html.component'; + const chakra = Chakra_Petch({ weight: '400', subsets: ['latin'], @@ -26,7 +28,7 @@ export default async function AppLayout({ children }: { children: ReactNode }) { ? PlausibleProvider : Fragment; return ( - + @@ -70,6 +72,6 @@ export default async function AppLayout({ children }: { children: ReactNode }) { - + ); } diff --git a/apps/frontend/src/app/global.scss b/apps/frontend/src/app/global.scss index 008f32dc..0fb9cc84 100644 --- a/apps/frontend/src/app/global.scss +++ b/apps/frontend/src/app/global.scss @@ -466,3 +466,7 @@ div div .set-font-family { .hideCopilot .copilotKitPopup { display: none !important; } + +html[dir='rtl'] [dir='ltr'] { + direction: rtl !important; +} diff --git a/apps/frontend/src/components/analytics/stars.and.forks.tsx b/apps/frontend/src/components/analytics/stars.and.forks.tsx index 5c76aec5..97950bc0 100644 --- a/apps/frontend/src/components/analytics/stars.and.forks.tsx +++ b/apps/frontend/src/components/analytics/stars.and.forks.tsx @@ -41,7 +41,7 @@ export const StarsAndForks: FC = (props) => {
-
+
{item.stars.length ? ( ) : ( @@ -84,7 +84,7 @@ export const StarsAndForks: FC = (props) => {
-
+
{item.forks.length ? ( ) : ( @@ -143,7 +143,7 @@ export const StarsAndForks: FC = (props) => {
-
+
-

+

{t('activate_your_account', 'Activate your account')}

diff --git a/apps/frontend/src/components/auth/forgot-return.tsx b/apps/frontend/src/components/auth/forgot-return.tsx index e8db7833..b6108ea0 100644 --- a/apps/frontend/src/components/auth/forgot-return.tsx +++ b/apps/frontend/src/components/auth/forgot-return.tsx @@ -52,7 +52,7 @@ export function ForgotReturn({ token }: { token: string }) {
-

+

{t('forgot_password_1', 'Forgot Password')}

@@ -87,7 +87,7 @@ export function ForgotReturn({ token }: { token: string }) { ) : ( <> -
+
{t( 'we_successfully_reset_your_password_you_can_now_login_with_your', 'We successfully reset your password. You can now login with your' diff --git a/apps/frontend/src/components/auth/forgot.tsx b/apps/frontend/src/components/auth/forgot.tsx index d37fd333..57300f63 100644 --- a/apps/frontend/src/components/auth/forgot.tsx +++ b/apps/frontend/src/components/auth/forgot.tsx @@ -39,7 +39,7 @@ export function Forgot() {
-

+

{t('forgot_password_1', 'Forgot Password')}

@@ -68,7 +68,7 @@ export function Forgot() { ) : ( <> -
+
{t( 'we_have_send_you_an_email_with_a_link_to_reset_your_password', 'We have send you an email with a link to reset your password.' diff --git a/apps/frontend/src/components/auth/login.tsx b/apps/frontend/src/components/auth/login.tsx index cc9122fa..79a293cc 100644 --- a/apps/frontend/src/components/auth/login.tsx +++ b/apps/frontend/src/components/auth/login.tsx @@ -58,7 +58,7 @@ export function Login() {
-

+

{t('sign_in', 'Sign In')}

@@ -76,7 +76,7 @@ export function Login() {
{t('or', 'OR')}
@@ -85,12 +85,14 @@ export function Login() {
-

+

{t('sign_up', 'Sign Up')}

@@ -169,7 +169,7 @@ export function RegisterAfter({
{t('or', 'OR')}
diff --git a/apps/frontend/src/components/autopost/autopost.tsx b/apps/frontend/src/components/autopost/autopost.tsx index f07e5698..629ce190 100644 --- a/apps/frontend/src/components/autopost/autopost.tsx +++ b/apps/frontend/src/components/autopost/autopost.tsx @@ -41,7 +41,15 @@ export const Autopost: FC = () => { ); const deleteHook = useCallback( (data: any) => async () => { - if (await deleteDialog(`Are you sure you want to delete ${data.name}?`)) { + if ( + await deleteDialog( + t( + 'are_you_sure_you_want_to_delete', + `Are you sure you want to delete ${data.name}?`, + { name: data.name } + ) + ) + ) { await fetch(`/autopost/${data.id}`, { method: 'DELETE', }); @@ -285,7 +293,7 @@ export const AddOrEditWebhook: FC<{
- - + + value === 'true' || value === true, })} @@ -384,6 +404,7 @@ export const AddOrEditWebhook: FC<{ value={allIntegrations.value} name="integrations" label="Integrations" + translationKey="label_integrations" disableForm={true} onChange={changeIntegration} > diff --git a/apps/frontend/src/components/billing/faq.component.tsx b/apps/frontend/src/components/billing/faq.component.tsx index 7387db29..ff53c498 100644 --- a/apps/frontend/src/components/billing/faq.component.tsx +++ b/apps/frontend/src/components/billing/faq.component.tsx @@ -9,39 +9,60 @@ import { useT } from '@gitroom/react/translation/get.transation.service.client'; const useFaqList = () => { const { isGeneral } = useVariables(); const user = useUser(); + const t = useT(); return [ ...(user?.allowTrial ? [ { - title: 'Am I going to be charged by Postiz?', - description: - 'To confirm credit card information Postiz will hold $2 and release it immediately', + title: t( + 'faq_am_i_going_to_be_charged_by_postiz', + 'Am I going to be charged by Postiz?' + ), + description: t( + 'faq_to_confirm_credit_card_information_postiz_will_hold', + 'To confirm credit card information Postiz will hold $2 and release it immediately' + ), }, ] : []), { - title: `Can I trust ${isGeneral ? 'Postiz' : 'Gitroom'}?`, - description: `${ - isGeneral ? 'Postiz' : 'Gitroom' - } is proudly open-source! We believe in an ethical and transparent culture, meaning that ${ - isGeneral ? 'Postiz' : 'Gitroom' - } will live forever. You can check out the entire code or use it for personal projects. To view the open-source repository, click here.`, + title: t( + 'faq_can_i_trust_postiz_gitroom', + `Can I trust ${isGeneral ? 'Postiz' : 'Gitroom'}?` + ), + description: t( + 'faq_postiz_gitroom_is_proudly_open_source', + `${ + isGeneral ? 'Postiz' : 'Gitroom' + } is proudly open-source! We believe in an ethical and transparent culture, meaning that ${ + isGeneral ? 'Postiz' : 'Gitroom' + } will live forever. You can check out the entire code or use it for personal projects. To view the open-source repository, click here.` + ), }, { - title: 'What are channels?', - description: `${ - isGeneral ? 'Postiz' : 'Gitroom' - } allows you to schedule your posts between different channels. + title: t('faq_what_are_channels', 'What are channels?'), + description: t( + 'faq_postiz_gitroom_allows_you_to_schedule_posts', + `${ + isGeneral ? 'Postiz' : 'Gitroom' + } allows you to schedule your posts between different channels. A channel is a publishing platform where you can schedule your posts. -For example, you can schedule your posts on X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads and Pinterest.`, +For example, you can schedule your posts on X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads and Pinterest.` + ), }, { - title: 'What are team members?', - description: `If you have a team with multiple members, you can invite them to your workspace to collaborate on your posts and add their personal channels`, + title: t('faq_what_are_team_members', 'What are team members?'), + description: t( + 'faq_if_you_have_a_team_with_multiple_members', + 'If you have a team with multiple members, you can invite them to your workspace to collaborate on your posts and add their personal channels' + ), }, { - title: 'What is AI auto-complete?', - description: `We automate ChatGPT to help you write your social posts and articles`, + title: t('faq_what_is_ai_auto_complete', 'What is AI auto-complete?'), + description: t( + 'faq_we_automate_chatgpt_to_help_you_write', + 'We automate ChatGPT to help you write your social posts and articles' + ), }, ]; }; diff --git a/apps/frontend/src/components/billing/lifetime.deal.tsx b/apps/frontend/src/components/billing/lifetime.deal.tsx index 0968d219..0f006c6d 100644 --- a/apps/frontend/src/components/billing/lifetime.deal.tsx +++ b/apps/frontend/src/components/billing/lifetime.deal.tsx @@ -175,6 +175,7 @@ export const LifetimeDeal = () => {
-
+
{!canSendForPublication - ? 'Not matching order' + ? t('not_matching_order', 'Not matching order') : postFor - ? 'Submit for order' + ? t('submit_for_order', 'Submit for order') : !existingData.integration ? selectedIntegrations.length === 0 - ? `Select channels from the circles above` - : 'Add to calendar' + ? t( + 'select_channels_from_circles', + 'Select channels from the circles above' + ) + : t('add_to_calendar', 'Add to calendar') : // @ts-ignore existingData?.posts?.[0]?.state === 'DRAFT' - ? 'Schedule' - : 'Update'} + ? t('schedule', 'Schedule') + : t('update', 'Update')}
{!postFor && (
@@ -858,7 +864,7 @@ Here are the things you can do:
setTags(e.target.value)} /> diff --git a/apps/frontend/src/components/launches/add.provider.component.tsx b/apps/frontend/src/components/launches/add.provider.component.tsx index 933d63ca..3dba9ef5 100644 --- a/apps/frontend/src/components/launches/add.provider.component.tsx +++ b/apps/frontend/src/components/launches/add.provider.component.tsx @@ -59,7 +59,7 @@ export const AddProviderButton: FC<{ />
-
{t('add_channel', 'Add Channel')}
+
{t('add_channel', 'Add Channel')}
); }; @@ -115,7 +115,7 @@ export const ApiModal: FC<{ {value.length >= 30 && !loading && ( -
+
    {list.map((p) => (
  • diff --git a/apps/frontend/src/components/launches/bot.picture.tsx b/apps/frontend/src/components/launches/bot.picture.tsx index 92d639da..3eb169e5 100644 --- a/apps/frontend/src/components/launches/bot.picture.tsx +++ b/apps/frontend/src/components/launches/bot.picture.tsx @@ -47,7 +47,7 @@ export const BotPicture: FC<{
    { const { currentYear, currentWeek } = useCalendar(); const t = useT(); + // Use dayjs to get localized day names + const localizedDays = useMemo(() => { + const currentLanguage = i18next.resolvedLanguage || 'en'; + dayjs.locale(currentLanguage); + + const days = []; + // Starting from Monday (1) to Sunday (7) + for (let i = 1; i <= 7; i++) { + days.push(dayjs().day(i).format('dddd')); + } + return days; + }, [i18next.resolvedLanguage]); + return (
    - {days.map((day, index) => ( + {localizedDays.map((day, index) => (
    -
    {t(day.toLowerCase(), day)}
    +
    {day}
    ))} {hours.map((hour) => ( -
    - {/* {hour.toString().padStart(2, '0')}:00 */} +
    {convertTimeFormatBasedOnLocality(hour)}
    {days.map((day, indexDay) => ( @@ -186,6 +225,19 @@ export const MonthView = () => { const { currentYear, currentMonth } = useCalendar(); const t = useT(); + // Use dayjs to get localized day names + const localizedDays = useMemo(() => { + const currentLanguage = i18next.resolvedLanguage || 'en'; + dayjs.locale(currentLanguage); + + const days = []; + // Starting from Monday (1) to Sunday (7) + for (let i = 1; i <= 7; i++) { + days.push(dayjs().day(i).format('dddd')); + } + return days; + }, [i18next.resolvedLanguage]); + const calendarDays = useMemo(() => { const startOfMonth = dayjs(new Date(currentYear, currentMonth, 1)); @@ -213,16 +265,17 @@ export const MonthView = () => { } return calendarDays; }, [currentYear, currentMonth]); + return (
    - {days.map((day) => ( + {localizedDays.map((day) => (
    -
    {t(day.toLowerCase(), day)}
    +
    {day}
    ))} {calendarDays.map((date, index) => ( @@ -618,7 +671,7 @@ export const CalendarColumn: FC<{ display === ('month' as any) ? 'flex-1 min-h-[40px] w-full' : !postList.length - ? 'h-full w-full absolute left-0 top-0 p-[5px]' + ? 'h-full w-full absolute start-0 top-0 p-[5px]' : 'min-h-[40px] w-full', 'flex items-center justify-center cursor-pointer pb-[2.5px]' )} @@ -656,13 +709,13 @@ export const CalendarColumn: FC<{ {selectedIntegrations.identifier === 'youtube' ? ( ) : ( {selectedIntegrations.identifier}
    -
    +
    {state === 'DRAFT' ? t('draft', 'Draft') + ': ' : ''}
    -
    +
    {removeMd(post.content).replace(/\n/g, ' ')}
    diff --git a/apps/frontend/src/components/launches/comments/comment.component.tsx b/apps/frontend/src/components/launches/comments/comment.component.tsx index e0f6d660..10ce1ea9 100644 --- a/apps/frontend/src/components/launches/comments/comment.component.tsx +++ b/apps/frontend/src/components/launches/comments/comment.component.tsx @@ -271,7 +271,7 @@ export const CommentComponent: FC<{
    -
    +
    { @@ -105,7 +105,7 @@ export const Editor = forwardRef< props?.onChange?.(e.target.value); }} onPaste={props.onPaste} - placeholder="Write your reply..." + placeholder={t('write_your_reply', 'Write your reply...')} autosuggestionsConfig={{ textareaPurpose: `Assist me in writing social media posts.`, chatApiConfigs: {}, diff --git a/apps/frontend/src/components/launches/filters.tsx b/apps/frontend/src/components/launches/filters.tsx index aa4c587a..5454a185 100644 --- a/apps/frontend/src/components/launches/filters.tsx +++ b/apps/frontend/src/components/launches/filters.tsx @@ -7,39 +7,46 @@ import { useCallback } from 'react'; import { isUSCitizen } from './helpers/isuscitizen.utils'; import { SelectCustomer } from '@gitroom/frontend/components/launches/select.customer'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import i18next from 'i18next'; + export const Filters = () => { const week = useCalendar(); const t = useT(); + + // Set dayjs locale based on current language + const currentLanguage = i18next.resolvedLanguage || 'en'; + dayjs.locale(currentLanguage); + const betweenDates = week.display === 'day' ? dayjs() .year(week.currentYear) .isoWeek(week.currentWeek) .day(week.currentDay) - .format(isUSCitizen() ? 'MM/DD/YYYY' : 'DD/MM/YYYY') + .format('L') : week.display === 'week' ? dayjs() .year(week.currentYear) .isoWeek(week.currentWeek) .startOf('isoWeek') - .format(isUSCitizen() ? 'MM/DD/YYYY' : 'DD/MM/YYYY') + + .format('L') + ' - ' + dayjs() .year(week.currentYear) .isoWeek(week.currentWeek) .endOf('isoWeek') - .format(isUSCitizen() ? 'MM/DD/YYYY' : 'DD/MM/YYYY') + .format('L') : dayjs() .year(week.currentYear) .month(week.currentMonth) .startOf('month') - .format(isUSCitizen() ? 'MM/DD/YYYY' : 'DD/MM/YYYY') + + .format('L') + ' - ' + dayjs() .year(week.currentYear) .month(week.currentMonth) .endOf('month') - .format(isUSCitizen() ? 'MM/DD/YYYY' : 'DD/MM/YYYY'); + .format('L'); const setDay = useCallback(() => { week.setFilters({ currentDay: +dayjs().day() as 0 | 1 | 2 | 3 | 4 | 5 | 6, @@ -162,7 +169,7 @@ export const Filters = () => { return (
    -
    +
    { .day(week.currentDay) .format('dddd')}` : week.display === 'week' - ? `Week ${week.currentWeek}` - : `${dayjs().month(week.currentMonth).format('MMMM')}`} + ? t('week_number', 'Week {{number}}', { number: week.currentWeek }) + : dayjs().month(week.currentMonth).format('MMMM')}
    -
    +
    {integration?.name}
    -
    +
    -
    +
    {integration?.display || '@username'}
    diff --git a/apps/frontend/src/components/launches/generator/generator.tsx b/apps/frontend/src/components/launches/generator/generator.tsx index 7c3eb452..3afe7b97 100644 --- a/apps/frontend/src/components/launches/generator/generator.tsx +++ b/apps/frontend/src/components/launches/generator/generator.tsx @@ -60,34 +60,48 @@ const FirstStep: FC = (props) => { const data = JSON.parse(chunk); switch (data.name) { case 'agent': - setShowStep('Agent starting'); + setShowStep(t('agent_starting', 'Agent starting')); break; case 'research': - setShowStep('Researching your content...'); + setShowStep( + t('researching_your_content', 'Researching your content...') + ); break; case 'find-category': - setShowStep('Understanding the category...'); + setShowStep( + t( + 'understanding_the_category', + 'Understanding the category...' + ) + ); break; case 'find-topic': - setShowStep('Finding the topic...'); + setShowStep(t('finding_the_topic', 'Finding the topic...')); break; case 'find-popular-posts': - setShowStep('Finding popular posts to match with...'); + setShowStep( + t( + 'finding_popular_posts_to_match_with', + 'Finding popular posts to match with...' + ) + ); break; case 'generate-hook': - setShowStep('Generating hook...'); + setShowStep(t('generating_hook', 'Generating hook...')); break; case 'generate-content': - setShowStep('Generating content...'); + setShowStep(t('generating_content', 'Generating content...')); break; case 'generate-picture': - setShowStep('Generating pictures...'); + setShowStep(t('generating_pictures', 'Generating pictures...')); break; case 'upload-pictures': - setShowStep('Uploading pictures...'); + setShowStep(t('uploading_pictures', 'Uploading pictures...')); break; case 'post-time': - setShowStep('Finding time to post...'); + setShowStep( + t('finding_time_to_post', 'Finding time to post...') + ); break; } lastResponse = data; @@ -97,7 +111,7 @@ const FirstStep: FC = (props) => { } } }, - [] + [t] ); const onSubmit: SubmitHandler<{ research: string; @@ -182,12 +196,18 @@ const FirstStep: FC = (props) => {
    )}