diff --git a/.github/crowdin-app.yml b/.github/crowdin-app.yml index 7f37da20966..f6cf01c678c 100644 --- a/.github/crowdin-app.yml +++ b/.github/crowdin-app.yml @@ -4,20 +4,19 @@ # See https://crowdin.github.io/crowdin-cli/configuration for more information # -"preserve_hierarchy": true -"base_path": ".." - -files: [ - { - # - # Source files filter - PO files for Lingui - # - "source": "**/en.po", +preserve_hierarchy: true +base_path: .. +files: + # + # Source files filter - PO files for Lingui + # + - source: packages/twenty-front/src/locales/en.po # # Translation files path # - "translation": "%original_path%/%locale%.po", - } -] - + translation: '%original_path%/%locale%.po' + - source: packages/twenty-server/src/engine/core-modules/i18n/locales/en.po + translation: '%original_path%/%locale%.po' + - source: packages/twenty-emails/src/locales/en.po + translation: '%original_path%/%locale%.po' diff --git a/.github/crowdin-website.yml b/.github/crowdin-website.yml new file mode 100644 index 00000000000..331c0e36717 --- /dev/null +++ b/.github/crowdin-website.yml @@ -0,0 +1,23 @@ +# +# Crowdin CLI configuration for Website translations (twenty-website-new) +# Project ID: 4 +# See https://crowdin.github.io/crowdin-cli/configuration for more information +# + +project_id: 4 +preserve_hierarchy: true +base_url: 'https://twenty.api.crowdin.com' +base_path: .. +languages_mapping: + locale: + fr: fr-FR + +files: + # + # Source file - PO file for Lingui + # + - source: packages/twenty-website-new/src/locales/en.po + # + # Translation files path + # + translation: '%original_path%/%locale%.po' diff --git a/.github/workflows/i18n-pull.yaml b/.github/workflows/i18n-pull.yaml index d88ff9e1cae..b078f5b7d48 100644 --- a/.github/workflows/i18n-pull.yaml +++ b/.github/workflows/i18n-pull.yaml @@ -58,7 +58,6 @@ jobs: npx nx run twenty-server:lingui:compile --strict npx nx run twenty-emails:lingui:compile --strict npx nx run twenty-front:lingui:compile --strict - npx nx run twenty-website-new:lingui:compile --strict continue-on-error: true - name: Stash any changes before pulling translations @@ -75,8 +74,6 @@ jobs: upload_sources: false upload_translations: false download_translations: true - source: '**/en.po' - translation: '%original_path%/%locale%.po' export_only_approved: false localization_branch_name: i18n base_url: 'https://twenty.api.crowdin.com' @@ -116,7 +113,6 @@ jobs: npx nx run twenty-server:lingui:compile npx nx run twenty-emails:lingui:compile npx nx run twenty-front:lingui:compile - npx nx run twenty-website-new:lingui:compile git status git add . if ! git diff --staged --quiet --exit-code; then diff --git a/.github/workflows/i18n-push.yaml b/.github/workflows/i18n-push.yaml index 73d402acbc3..2f7229674de 100644 --- a/.github/workflows/i18n-push.yaml +++ b/.github/workflows/i18n-push.yaml @@ -41,7 +41,6 @@ jobs: npx nx run twenty-server:lingui:extract npx nx run twenty-emails:lingui:extract npx nx run twenty-front:lingui:extract - npx nx run twenty-website-new:lingui:extract - name: Check and commit extracted files id: check_extract_changes @@ -61,7 +60,6 @@ jobs: npx nx run twenty-server:lingui:compile npx nx run twenty-emails:lingui:compile npx nx run twenty-front:lingui:compile - npx nx run twenty-website-new:lingui:compile - name: Check and commit compiled files id: check_compile_changes diff --git a/.github/workflows/website-i18n-pull.yaml b/.github/workflows/website-i18n-pull.yaml new file mode 100644 index 00000000000..516c23bd570 --- /dev/null +++ b/.github/workflows/website-i18n-pull.yaml @@ -0,0 +1,135 @@ +# Pull down website translations from Crowdin every two hours or when triggered manually. +# When force_pull input is true, translations will be pulled regardless of compilation status. + +name: 'Pull website translations from Crowdin' + +permissions: + contents: write + pull-requests: write + +on: + schedule: + - cron: '0 */2 * * *' # Every two hours. + workflow_dispatch: + inputs: + force_pull: + description: 'Force pull translations regardless of compilation status' + required: false + type: boolean + default: false + workflow_call: + inputs: + force_pull: + description: 'Force pull translations regardless of compilation status' + required: false + type: boolean + default: false + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + +jobs: + pull_website_translations: + name: Pull website translations + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + token: ${{ github.token }} + ref: ${{ github.head_ref || github.ref_name }} + + - name: Setup website i18n branch + run: | + git fetch origin i18n-website || true + git checkout -B i18n-website origin/i18n-website || git checkout -b i18n-website + + - name: Install dependencies + uses: ./.github/actions/yarn-install + + - name: Build twenty-shared + run: npx nx build twenty-shared + + # Strict mode fails if there are missing website translations. + - name: Compile website translations + id: compile_translations_strict + run: npx nx run twenty-website-new:lingui:compile --strict + continue-on-error: true + + - name: Stash any changes before pulling translations + run: | + git config --global user.name 'github-actions' + git config --global user.email 'github-actions@twenty.com' + git add . + git stash + + - name: Pull website translations from Crowdin + if: inputs.force_pull || steps.compile_translations_strict.outcome == 'failure' + uses: crowdin/github-action@v2 + with: + upload_sources: false + upload_translations: false + download_translations: true + source: 'packages/twenty-website-new/src/locales/en.po' + translation: 'packages/twenty-website-new/src/locales/%locale%.po' + export_only_approved: false + localization_branch_name: i18n-website + base_url: 'https://twenty.api.crowdin.com' + auto_approve_imported: false + import_eq_suggestions: false + download_sources: false + push_sources: false + skip_untranslated_strings: false + skip_untranslated_files: false + push_translations: false + create_pull_request: false + skip_ref_checkout: true + dryrun_action: false + config: '.github/crowdin-website.yml' + env: + GITHUB_TOKEN: ${{ github.token }} + # Website translations project + CROWDIN_PROJECT_ID: '4' + CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} + + # As the files are extracted from a Docker container, they belong to root:root. + # We need to fix this before the next steps. + - name: Fix file permissions + run: sudo chown -R runner:docker . + + - name: Compile website translations + id: compile_translations + run: | + npx nx run twenty-website-new:lingui:compile + git status + git add packages/twenty-website-new/src/locales + if ! git diff --staged --quiet --exit-code; then + git commit -m "chore: compile website translations" + echo "changes_detected=true" >> $GITHUB_OUTPUT + else + echo "changes_detected=false" >> $GITHUB_OUTPUT + fi + + - name: Push changes + if: steps.compile_translations.outputs.changes_detected == 'true' + run: git push origin HEAD:i18n-website + + - name: Create pull request + if: steps.compile_translations.outputs.changes_detected == 'true' + run: | + if git diff --name-only origin/main..HEAD | grep -q .; then + gh pr create -B main -H i18n-website --title 'i18n - website translations' --body 'Created by Github action' || true + else + echo "No file differences between branches, skipping PR creation" + fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Trigger i18n automerge + if: steps.compile_translations.outputs.changes_detected == 'true' + uses: peter-evans/repository-dispatch@v2 + with: + token: ${{ secrets.TWENTY_INFRA_TOKEN }} + repository: twentyhq/twenty-infra + event-type: i18n-pr-ready diff --git a/.github/workflows/website-i18n-push.yaml b/.github/workflows/website-i18n-push.yaml new file mode 100644 index 00000000000..a0e9802d49a --- /dev/null +++ b/.github/workflows/website-i18n-push.yaml @@ -0,0 +1,111 @@ +name: 'Push website translations to Crowdin' + +permissions: + contents: write + pull-requests: write + +on: + workflow_dispatch: + workflow_call: + push: + branches: ['main'] + paths: + - 'packages/twenty-website-new/**' + - '.github/crowdin-website.yml' + - '.github/workflows/website-i18n-push.yaml' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + +jobs: + extract_website_translations: + name: Extract and upload website translations + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + token: ${{ github.token }} + ref: main + + - name: Setup website i18n branch + run: | + git fetch origin i18n-website || true + git checkout -B i18n-website origin/i18n-website || git checkout -b i18n-website + + - name: Install dependencies + uses: ./.github/actions/yarn-install + + - name: Build dependencies + run: npx nx build twenty-shared + + - name: Extract website translations + run: npx nx run twenty-website-new:lingui:extract + + - name: Check and commit extracted files + id: check_extract_changes + run: | + git config --global user.name 'github-actions' + git config --global user.email 'github-actions@twenty.com' + git add packages/twenty-website-new/src/locales + if ! git diff --staged --quiet --exit-code; then + git commit -m "chore: extract website translations" + echo "changes_detected=true" >> $GITHUB_OUTPUT + else + echo "changes_detected=false" >> $GITHUB_OUTPUT + fi + + - name: Compile website translations + run: npx nx run twenty-website-new:lingui:compile + + - name: Check and commit compiled files + id: check_compile_changes + run: | + git config --global user.name 'github-actions' + git config --global user.email 'github-actions@twenty.com' + git add packages/twenty-website-new/src/locales/generated + if ! git diff --staged --quiet --exit-code; then + git commit -m "chore: compile website translations" + echo "changes_detected=true" >> $GITHUB_OUTPUT + else + echo "changes_detected=false" >> $GITHUB_OUTPUT + fi + + - name: Push changes and create remote branch if needed + if: steps.check_extract_changes.outputs.changes_detected == 'true' || steps.check_compile_changes.outputs.changes_detected == 'true' + run: git push origin HEAD:i18n-website + + - name: Upload missing website translations + if: steps.check_extract_changes.outputs.changes_detected == 'true' + uses: crowdin/github-action@v2 + with: + upload_sources: true + upload_translations: true + download_translations: false + localization_branch_name: i18n-website + base_url: 'https://twenty.api.crowdin.com' + config: '.github/crowdin-website.yml' + env: + # Website translations project + CROWDIN_PROJECT_ID: '4' + CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} + + - name: Create a pull request + if: steps.check_extract_changes.outputs.changes_detected == 'true' || steps.check_compile_changes.outputs.changes_detected == 'true' + run: | + if git diff --name-only origin/main..HEAD | grep -q .; then + gh pr create -B main -H i18n-website --title 'i18n - website translations' --body 'Created by Github action' || true + else + echo "No file differences between branches, skipping PR creation" + fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Trigger i18n automerge + if: steps.check_extract_changes.outputs.changes_detected == 'true' || steps.check_compile_changes.outputs.changes_detected == 'true' + uses: peter-evans/repository-dispatch@v2 + with: + token: ${{ secrets.TWENTY_INFRA_TOKEN }} + repository: twentyhq/twenty-infra + event-type: i18n-pr-ready diff --git a/packages/twenty-website-new/jest.config.mjs b/packages/twenty-website-new/jest.config.mjs index a96081bf015..f8068af1d34 100644 --- a/packages/twenty-website-new/jest.config.mjs +++ b/packages/twenty-website-new/jest.config.mjs @@ -21,6 +21,19 @@ const jestConfig = { { jsc: { parser: { syntax: 'typescript', tsx: true }, + experimental: { + plugins: [ + [ + '@lingui/swc-plugin', + { + runtimeModules: { + i18n: ['@lingui/core', 'i18n'], + trans: ['@lingui/react', 'Trans'], + }, + }, + ], + ], + }, transform: { react: { runtime: 'automatic' } }, }, }, diff --git a/packages/twenty-website-new/lingui.config.ts b/packages/twenty-website-new/lingui.config.ts index debcc9c7ac9..1125c1c997a 100644 --- a/packages/twenty-website-new/lingui.config.ts +++ b/packages/twenty-website-new/lingui.config.ts @@ -1,13 +1,13 @@ import { defineConfig } from '@lingui/conf'; import { formatter } from '@lingui/format-po'; -import { APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared/translations'; +import { SOURCE_LOCALE } from 'twenty-shared/translations'; + +import { WEBSITE_LOCALE_LIST } from './src/lib/i18n/website-locale-list'; export default defineConfig({ sourceLocale: SOURCE_LOCALE, - locales: Object.values(APP_LOCALES), - pseudoLocale: 'pseudo-en', + locales: [...WEBSITE_LOCALE_LIST], fallbackLocales: { - 'pseudo-en': 'en', default: SOURCE_LOCALE, }, catalogs: [ diff --git a/packages/twenty-website-new/scripts/check-boundaries.mjs b/packages/twenty-website-new/scripts/check-boundaries.mjs index f5e500b4caa..3b77818510a 100644 --- a/packages/twenty-website-new/scripts/check-boundaries.mjs +++ b/packages/twenty-website-new/scripts/check-boundaries.mjs @@ -35,7 +35,8 @@ const RULES = [ id: 'no-raw-animation-frame', description: '`requestAnimationFrame(...)` / `cancelAnimationFrame(...)` may only be used inside shared runtime primitives.', - pattern: /\b(?:window\.)?(?:requestAnimationFrame|cancelAnimationFrame)\s*\(/, + pattern: + /\b(?:window\.)?(?:requestAnimationFrame|cancelAnimationFrame)\s*\(/, appliesTo: (rel) => rel.startsWith('src/') && /\.(ts|tsx|mjs|js|jsx)$/.test(rel), exempt: (rel) => diff --git a/packages/twenty-website-new/src/app/[locale]/(home)/helped.data.ts b/packages/twenty-website-new/src/app/[locale]/(home)/helped.data.ts index e394c608bef..f1e83674edb 100644 --- a/packages/twenty-website-new/src/app/[locale]/(home)/helped.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/(home)/helped.data.ts @@ -1,46 +1,40 @@ +import { msg } from '@lingui/core/macro'; import type { HelpedDataType } from '@/sections/Helped/types/HelpedData'; export const HELPED_DATA: HelpedDataType = { eyebrow: { heading: { - text: 'In production.', + text: msg`In production.`, fontFamily: 'sans', }, }, - heading: [ - { - text: 'Dev teams power\ncompany-wide\n', - fontFamily: 'serif', - }, - { - text: 'change with Twenty', - fontFamily: 'sans', - }, - ], cards: [ { icon: 'w3villa', - heading: { text: 'Ship a product on Twenty', fontFamily: 'sans' }, + heading: { text: msg`Ship a product on Twenty`, fontFamily: 'sans' }, body: { - text: 'W3villa built W3Grads for AI mock interviews at scale, with Twenty as the operational backbone.', + text: msg`W3villa built W3Grads for AI mock interviews at scale, with Twenty as the operational backbone.`, }, illustration: 'target', href: '/customers/w3villa', }, { icon: 'act-education', - heading: { text: 'Own your CRM end to end', fontFamily: 'sans' }, + heading: { text: msg`Own your CRM end to end`, fontFamily: 'sans' }, body: { - text: 'AC&T replaced a shuttered vendor CRM with self-hosted Twenty and cut CRM costs by more than 90%.', + text: msg`AC&T replaced a shuttered vendor CRM with self-hosted Twenty and cut CRM costs by more than 90%.`, }, illustration: 'spaceship', href: '/customers/act-education', }, { icon: 'netzero', - heading: { text: 'Grow with a flexible foundation', fontFamily: 'sans' }, + heading: { + text: msg`Grow with a flexible foundation`, + fontFamily: 'sans', + }, body: { - text: 'NetZero runs a modular Twenty setup across carbon credits, ag products, and industrial systems.', + text: msg`NetZero runs a modular Twenty setup across carbon credits, ag products, and industrial systems.`, }, illustration: 'money', href: '/customers/netzero', diff --git a/packages/twenty-website-new/src/app/[locale]/(home)/hero.data.ts b/packages/twenty-website-new/src/app/[locale]/(home)/hero.data.ts index cb76db6bf8e..c1aced91628 100644 --- a/packages/twenty-website-new/src/app/[locale]/(home)/hero.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/(home)/hero.data.ts @@ -1,9 +1,11 @@ +import { msg } from '@lingui/core/macro'; + import type { HeroDashboardDataType, HeroDashboardPageDefinition, - HeroHomeDataType, HeroKanbanPageDefinition, HeroTablePageDefinition, + HeroVisualType, } from '@/sections/Hero/types'; import { SHARED_PEOPLE_AVATAR_URLS } from '@/content/site/asset-paths'; @@ -323,15 +325,11 @@ function createTablePage({ }; } -export const HERO_DATA: HeroHomeDataType = { - heading: [ - { text: 'Build', fontFamily: 'serif' }, - { text: ' your Enterprise CRM ', fontFamily: 'serif' }, - { text: 'at\u00A0AI\u00A0Speed', fontFamily: 'sans' }, - ], - body: { - text: 'Twenty gives technical teams the building blocks for a custom CRM that meets complex business needs and quickly adapts as the business evolves.', - }, +export const HERO_COPY = { + body: msg`Twenty gives technical teams the building blocks for a custom CRM that meets complex business needs and quickly adapts as the business evolves.`, +}; + +export const HERO_DATA: { visual: HeroVisualType } = { visual: { workspace: { icon: 'apple', name: 'Apple' }, tableWidth: 1700, diff --git a/packages/twenty-website-new/src/app/[locale]/(home)/home-stepper.data.ts b/packages/twenty-website-new/src/app/[locale]/(home)/home-stepper.data.ts index 21991c9d05f..348535eaddd 100644 --- a/packages/twenty-website-new/src/app/[locale]/(home)/home-stepper.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/(home)/home-stepper.data.ts @@ -1,3 +1,4 @@ +import { msg } from '@lingui/core/macro'; import type { HomeStepperDataType } from '@/sections/HomeStepper/types'; export const HOME_STEPPER_DATA: HomeStepperDataType = { @@ -5,46 +6,46 @@ export const HOME_STEPPER_DATA: HomeStepperDataType = { { heading: [ { - text: 'Begin with production-grade', + text: msg`Begin with production-grade`, fontFamily: 'serif', }, { - text: ' building blocks', + text: msg`building blocks`, fontFamily: 'sans', }, ], body: { - text: 'Compose your CRM and internal apps with a single extensibility toolkit. Data model, layout, and automation.', + text: msg`Compose your CRM and internal apps with a single extensibility toolkit. Data model, layout, and automation.`, }, }, { heading: [ { - text: 'Continue iteration', + text: msg`Continue iteration`, fontFamily: 'serif', }, { - text: ' without friction', + text: msg`without friction`, fontFamily: 'sans', }, ], body: { - text: 'Enjoy unlimited customization using the AI coding tools you already love. Adapt your CRM to fit the way your business grows and wins.', + text: msg`Enjoy unlimited customization using the AI coding tools you already love. Adapt your CRM to fit the way your business grows and wins.`, }, }, { heading: [ { - text: 'Stay in control with our', + text: msg`Stay in control with our`, fontFamily: 'serif', }, { - text: ' open-source software', + text: msg`open-source software`, fontFamily: 'sans', }, ], body: { - text: "Don't get locked into someone else's ecosystem. Twenty's developer experience looks like normal software, with local setup, real data, live testing, and no proprietary tooling.", + text: msg`Don't get locked into someone else's ecosystem. Twenty's developer experience looks like normal software, with local setup, real data, live testing, and no proprietary tooling.`, }, }, ], diff --git a/packages/twenty-website-new/src/app/[locale]/(home)/page.tsx b/packages/twenty-website-new/src/app/[locale]/(home)/page.tsx index 22fd93ffdb2..69db981d222 100644 --- a/packages/twenty-website-new/src/app/[locale]/(home)/page.tsx +++ b/packages/twenty-website-new/src/app/[locale]/(home)/page.tsx @@ -1,5 +1,6 @@ +import { msg } from '@lingui/core/macro'; import { HELPED_DATA } from '@/app/[locale]/(home)/helped.data'; -import { HERO_DATA } from '@/app/[locale]/(home)/hero.data'; +import { HERO_COPY, HERO_DATA } from '@/app/[locale]/(home)/hero.data'; import { HOME_STEPPER_DATA } from '@/app/[locale]/(home)/home-stepper.data'; import { PROBLEM_DATA } from '@/app/[locale]/(home)/problem.data'; import { TESTIMONIALS_DATA } from '@/app/[locale]/(home)/testimonials.data'; @@ -9,9 +10,20 @@ import { TalkToUsButton } from '@/lib/contact-cal'; import { FAQ_DATA } from '@/sections/Faq/data'; import { MENU_DATA } from '@/sections/Menu/data'; import { TRUSTED_BY_DATA } from '@/sections/TrustedBy/data'; -import { Body, Eyebrow, Heading, LinkButton } from '@/design-system/components'; -import { Pages } from '@/lib/pages'; +import { + Body, + Eyebrow, + Heading, + HeadingPart, + LinkButton, +} from '@/design-system/components'; import { fetchCommunityStats } from '@/lib/community/fetch-community-stats'; +import { createMessageDescriptorRenderer } from '@/lib/i18n/create-message-descriptor-renderer'; +import { + getRouteI18n, + type LocaleRouteParams, +} from '@/lib/i18n/get-route-i18n'; +import { Pages } from '@/lib/pages'; import { mergeSocialLinkLabels } from '@/lib/community/merge-social-link-labels'; import { Faq } from '@/sections/Faq/components'; import { Helped } from '@/sections/Helped/components'; @@ -85,8 +97,16 @@ const threeCardsIllustrationBodyClassName = css` } `; -export default async function HomePage() { - const stats = await fetchCommunityStats(); +type HomePageProps = { + params: Promise; +}; + +export default async function HomePage({ params }: HomePageProps) { + const [i18n, stats] = await Promise.all([ + getRouteI18n(params), + fetchCommunityStats(), + ]); + const renderText = createMessageDescriptorRenderer(i18n); const menuSocialLinks = mergeSocialLinkLabels(MENU_DATA.socialLinks, stats); return ( @@ -116,20 +136,31 @@ export default async function HomePage() { - - + + + {renderText(msg`Build your Enterprise CRM`)} + {' '} + + {renderText(msg`at\u00A0AI\u00A0Speed`)} + + + @@ -138,9 +169,15 @@ export default async function HomePage() { - + - + @@ -149,9 +186,26 @@ export default async function HomePage() { + + + {renderText(msg`A custom CRM gives your org an edge,`)} + {' '} + + {renderText(msg`but building one`)} + {' '} + + {renderText(msg`comes with`)} + {' '} + + {renderText(msg`tradeoffs`)} + + + - - @@ -162,19 +216,29 @@ export default async function HomePage() { + > + + {renderText(msg`Assemble, iterate and adapt a robust CRM,`)} + {' '} + + {renderText(msg`that's quick to flex`)} + + - + {THREE_CARDS_ILLUSTRATION_DATA.body && ( + + )} - + + + {renderText(msg`Make your GTM team happy`)} + +
+ + {renderText(msg`with`)} + {' '} + + {renderText(msg`a CRM they'll love`)} + +
- - + + + + {renderText(msg`Stop fighting custom.`)} + +
+ + {renderText(msg`Start building, with Twenty`)} + +
diff --git a/packages/twenty-website-new/src/app/[locale]/(home)/problem.data.ts b/packages/twenty-website-new/src/app/[locale]/(home)/problem.data.ts index 9c991c8d9ca..483f80bcaa7 100644 --- a/packages/twenty-website-new/src/app/[locale]/(home)/problem.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/(home)/problem.data.ts @@ -1,24 +1,19 @@ +import { msg } from '@lingui/core/macro'; import type { ProblemDataType } from '@/sections/Problem/types'; export const PROBLEM_DATA: ProblemDataType = { - eyebrow: { heading: { text: 'The Problem.', fontFamily: 'sans' } }, - heading: [ - { text: 'A custom CRM gives your org an edge, ', fontFamily: 'serif' }, - { text: 'but building one ', fontFamily: 'sans' }, - { text: 'comes with ', fontFamily: 'serif' }, - { text: 'tradeoffs', fontFamily: 'sans' }, - ], + eyebrow: { heading: { text: msg`The Problem.`, fontFamily: 'sans' } }, points: [ { - heading: { text: 'The Giant Monolith', fontFamily: 'sans' }, + heading: { text: msg`The Giant Monolith`, fontFamily: 'sans' }, body: { - text: 'Proprietary languages, slow deployment cycles, and "black box" logic.', + text: msg`Proprietary languages, slow deployment cycles, and "black box" logic.`, }, }, { - heading: { text: 'The In-house Burden', fontFamily: 'sans' }, + heading: { text: msg`The In-house Burden`, fontFamily: 'sans' }, body: { - text: "It's fragile. V1 ships quickly, but maintaining and making changes is a long term burden.", + text: msg`It's fragile. V1 ships quickly, but maintaining and making changes is a long term burden.`, }, }, ], diff --git a/packages/twenty-website-new/src/app/[locale]/(home)/testimonials.data.ts b/packages/twenty-website-new/src/app/[locale]/(home)/testimonials.data.ts index 627cbac2d73..ee091bb1949 100644 --- a/packages/twenty-website-new/src/app/[locale]/(home)/testimonials.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/(home)/testimonials.data.ts @@ -1,39 +1,40 @@ +import { msg } from '@lingui/core/macro'; import type { TestimonialsDataType } from '@/sections/Testimonials/types'; export const TESTIMONIALS_DATA: TestimonialsDataType = { eyebrow: { - heading: { text: 'They are the real sales', fontFamily: 'sans' }, + heading: { text: msg`They are the real sales`, fontFamily: 'sans' }, }, testimonials: [ { heading: { - text: 'The flexibility is really what made the difference. Our needs evolve very fast. I discover a new need and in two clicks I can address it. That is a real advantage when you are moving quickly.', + text: msg`The flexibility is really what made the difference. Our needs evolve very fast. I discover a new need and in two clicks I can address it. That is a real advantage when you are moving quickly.`, fontFamily: 'sans', }, author: { - name: { text: 'Olivier Reinaud' }, - designation: { text: 'Co-founder at NetZero' }, + name: { text: msg`Olivier Reinaud` }, + designation: { text: msg`Co-founder at NetZero` }, }, }, { heading: { - text: "We didn't want to patch over the problem. We wanted to build something institutions could rely on at scale, and that meant starting from a foundation solid enough to support the full complexity of what we had in mind.", + text: msg`We didn't want to patch over the problem. We wanted to build something institutions could rely on at scale, and that meant starting from a foundation solid enough to support the full complexity of what we had in mind.`, fontFamily: 'sans', }, author: { - name: { text: 'Amrendra Pratap Singh' }, - designation: { text: 'VP of Engineering at W3villa Technologies' }, + name: { text: msg`Amrendra Pratap Singh` }, + designation: { text: msg`VP of Engineering at W3villa Technologies` }, }, }, { heading: { - text: 'It is just such a nicer experience than dealing with a Salesforce or a HubSpot. My mission has been to get every tool API-accessible, so everything talks to each other. Twenty made that possible in a way older CRM platforms simply do not.', + text: msg`It is just such a nicer experience than dealing with a Salesforce or a HubSpot. My mission has been to get every tool API-accessible, so everything talks to each other. Twenty made that possible in a way older CRM platforms simply do not.`, fontFamily: 'sans', }, author: { - name: { text: 'Justin Beadle' }, + name: { text: msg`Justin Beadle` }, designation: { - text: 'Director of Digital and Information, Elevate Consulting', + text: msg`Director of Digital and Information, Elevate Consulting`, }, }, }, diff --git a/packages/twenty-website-new/src/app/[locale]/(home)/three-cards-feature.data.ts b/packages/twenty-website-new/src/app/[locale]/(home)/three-cards-feature.data.ts index d841503e88c..50b2af9e447 100644 --- a/packages/twenty-website-new/src/app/[locale]/(home)/three-cards-feature.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/(home)/three-cards-feature.data.ts @@ -1,22 +1,18 @@ +import { msg } from '@lingui/core/macro'; import type { ThreeCardsFeatureCardsDataType } from '@/sections/ThreeCards/types'; export const THREE_CARDS_FEATURE_DATA: ThreeCardsFeatureCardsDataType = { eyebrow: { heading: { - text: 'Skip the clunky UX that always comes with custom.', + text: msg`Skip the clunky UX that always comes with custom.`, fontFamily: 'sans', }, }, - heading: [ - { text: 'Make your GTM team happy', fontFamily: 'serif' }, - { text: 'with ', fontFamily: 'serif', lineBreakBefore: true }, - { text: "a CRM they'll love", fontFamily: 'sans' }, - ], featureCards: [ { - heading: { text: 'Familiar, modern interface', fontFamily: 'sans' }, + heading: { text: msg`Familiar, modern interface`, fontFamily: 'sans' }, body: { - text: "Twenty makes it simple. It's clean, intuitive, and built to feel like Notion.", + text: msg`Twenty makes it simple. It's clean, intuitive, and built to feel like Notion.`, }, backgroundImageSrc: '/images/home/three-cards-feature/familiar-interface-gradient.webp', @@ -24,9 +20,9 @@ export const THREE_CARDS_FEATURE_DATA: ThreeCardsFeatureCardsDataType = { illustration: 'familiar-interface', }, { - heading: { text: 'Live data and AI built', fontFamily: 'sans' }, + heading: { text: msg`Live data and AI built`, fontFamily: 'sans' }, body: { - text: 'Everything updates in real time, with AI chat always ready to help you move faster.', + text: msg`Everything updates in real time, with AI chat always ready to help you move faster.`, }, backgroundImageSrc: '/images/home/three-cards-feature/live-data-gradient.webp', @@ -34,9 +30,9 @@ export const THREE_CARDS_FEATURE_DATA: ThreeCardsFeatureCardsDataType = { illustration: 'live-data', }, { - heading: { text: 'Fast path to action', fontFamily: 'sans' }, + heading: { text: msg`Fast path to action`, fontFamily: 'sans' }, body: { - text: 'Smart patterns, shortcuts, and layouts make everyday tasks faster and easier to execute.', + text: msg`Smart patterns, shortcuts, and layouts make everyday tasks faster and easier to execute.`, }, backgroundImageSrc: '/images/home/three-cards-feature/fast-path-gradient.webp', diff --git a/packages/twenty-website-new/src/app/[locale]/(home)/three-cards-illustration.data.ts b/packages/twenty-website-new/src/app/[locale]/(home)/three-cards-illustration.data.ts index 94d1da69f4f..aeba6f3314a 100644 --- a/packages/twenty-website-new/src/app/[locale]/(home)/three-cards-illustration.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/(home)/three-cards-illustration.data.ts @@ -1,52 +1,49 @@ +import { msg } from '@lingui/core/macro'; import type { ThreeCardsIllustrationDataType } from '@/sections/ThreeCards/types'; export const THREE_CARDS_ILLUSTRATION_DATA: ThreeCardsIllustrationDataType = { eyebrow: { - heading: { text: 'Stop settling for trade-offs.', fontFamily: 'sans' }, + heading: { text: msg`Stop settling for trade-offs.`, fontFamily: 'sans' }, }, - heading: [ - { text: 'Assemble, iterate and adapt a robust CRM,', fontFamily: 'serif' }, - { text: " that's quick to flex", fontFamily: 'sans' }, - ], body: { - text: 'Compose your CRM and internal apps with a single extensibility toolkit.', + text: msg`Compose your CRM and internal apps with a single extensibility toolkit.`, }, illustrationCards: [ { - heading: { text: 'Production grade quality', fontFamily: 'sans' }, + heading: { text: msg`Production grade quality`, fontFamily: 'sans' }, body: { - text: 'W3villa used Twenty as a production-grade framework for the data model, permissions, authentication, and workflow engine they would otherwise have rebuilt themselves.', + text: msg`W3villa used Twenty as a production-grade framework for the data model, permissions, authentication, and workflow engine they would otherwise have rebuilt themselves.`, }, benefits: undefined, attribution: { - role: { text: 'VP of Engineering' }, - company: { text: 'W3villa Technologies' }, + role: { text: msg`VP of Engineering` }, + company: { text: msg`W3villa Technologies` }, }, illustration: 'diamond', caseStudySlug: 'w3villa', }, { - heading: { text: 'AI for rapid iterations', fontFamily: 'sans' }, + heading: { text: msg`AI for rapid iterations`, fontFamily: 'sans' }, body: { - text: 'Alternative Partners used agentic AI to compress what would typically be weeks of Salesforce migration work into something a single person could oversee.', + text: msg`Alternative Partners used agentic AI to compress what would typically be weeks of Salesforce migration work into something a single person could oversee.`, }, benefits: undefined, attribution: { - role: { text: 'Principal and Founder' }, - company: { text: 'Alternative Partners' }, + role: { text: msg`Principal and Founder` }, + company: { text: msg`Alternative Partners` }, }, illustration: 'flash', caseStudySlug: 'alternative-partners', }, { - heading: { text: 'Control without drag', fontFamily: 'sans' }, + heading: { text: msg`Control without drag`, fontFamily: 'sans' }, body: { - text: 'AC&T moved to a self-hosted Twenty instance with no vendor risk, no forced migration, and CRM costs reduced by more than 90%.', + text: msg`AC&T moved to a self-hosted Twenty instance with no vendor risk, no forced migration, and CRM costs reduced by more than 90%.`, }, benefits: undefined, attribution: { - role: { text: 'CRM Engineer' }, - company: { text: 'AC&T Education Migration' }, + role: { text: msg`CRM Engineer` }, + company: { text: msg`AC&T Education Migration` }, }, illustration: 'lock', caseStudySlug: 'act-education', diff --git a/packages/twenty-website-new/src/app/[locale]/customers/9dots/page.tsx b/packages/twenty-website-new/src/app/[locale]/customers/9dots/page.tsx index 55ed77a0d84..c2ca86b168d 100644 --- a/packages/twenty-website-new/src/app/[locale]/customers/9dots/page.tsx +++ b/packages/twenty-website-new/src/app/[locale]/customers/9dots/page.tsx @@ -1,7 +1,13 @@ +import { msg } from '@lingui/core/macro'; import { MENU_DATA } from '@/sections/Menu/data'; import { CustomersCaseStudySignoff } from '@/app/[locale]/customers/_components/CustomersCaseStudySignoff'; import { getCaseStudyPalette, type CaseStudyData } from '@/lib/customers'; import { fetchCommunityStats } from '@/lib/community/fetch-community-stats'; +import { createMessageDescriptorRenderer } from '@/lib/i18n/create-message-descriptor-renderer'; +import { + getRouteI18n, + type LocaleRouteParams, +} from '@/lib/i18n/get-route-i18n'; import { mergeSocialLinkLabels } from '@/lib/community/merge-social-link-labels'; import { CaseStudy } from '@/sections/CaseStudy/components'; import { Menu } from '@/sections/Menu/components'; @@ -13,88 +19,94 @@ const PLACEHOLDER_HERO = const CASE_STUDY: CaseStudyData = { meta: { - title: - 'Homeseller, WhatsApp, and a CRM built around the business | Nine Dots & Twenty', - description: - 'How Nine Dots Ventures rebuilt a Singapore real estate agency on Twenty with APIs, n8n, Grafana, and AI on top of 2,000+ WhatsApp messages a day.', + title: msg`Homeseller, WhatsApp, and a CRM built around the business | Nine Dots & Twenty`, + description: msg`How Nine Dots Ventures rebuilt a Singapore real estate agency on Twenty with APIs, n8n, Grafana, and AI on top of 2,000+ WhatsApp messages a day.`, }, hero: { readingTime: '9 min', title: [ - { text: 'A real estate agency on WhatsApp ', fontFamily: 'serif' }, - { text: 'built a ', fontFamily: 'serif' }, - { text: 'CRM', fontFamily: 'sans', newLine: true }, - { text: ' around it', fontFamily: 'serif' }, + { + text: msg`A real estate agency on WhatsApp`, + fontFamily: 'serif', + }, + { text: msg`built a`, fontFamily: 'serif' }, + { text: msg`CRM`, fontFamily: 'sans', newLine: true }, + { text: msg`around it`, fontFamily: 'serif' }, ], author: 'Mike Babiy & Azmat Parveen', authorAvatarSrc: '/images/partner/testimonials/mike-babiy.png', - authorRole: 'Founder, Nine Dots Ventures', + authorRole: msg`Founder, Nine Dots Ventures`, clientIcon: 'nine-dots', heroImageSrc: PLACEHOLDER_HERO, - industry: 'Real Estate', + industry: msg`Real Estate`, kpis: [ - { value: '150 hrs', label: 'Saved / month' }, - { value: '2,000+', label: 'Daily messages' }, - { value: 'Q1 2026', label: 'Record quarter' }, + { value: msg`150 hrs`, label: msg`Saved / month` }, + { value: msg`2,000+`, label: msg`Daily messages` }, + { value: msg`Q1 2026`, label: msg`Record quarter` }, ], quote: { - text: 'Twenty lets us build a CRM around the business and not the business around the CRM.', + text: msg`Twenty lets us build a CRM around the business and not the business around the CRM.`, author: 'Mike Babiy', - role: 'Founder, Nine Dots Ventures', + role: msg`Founder, Nine Dots Ventures`, }, }, sections: [ { type: 'text', - eyebrow: 'Homeseller', + eyebrow: msg`Homeseller`, heading: [ - { text: 'When the channel is ', fontFamily: 'serif' }, - { text: 'the business', fontFamily: 'sans' }, + { + text: msg`When the channel is`, + fontFamily: 'serif', + }, + { text: msg`the business`, fontFamily: 'sans' }, ], paragraphs: [ - "Homeseller is a high-volume real estate agency in Singapore, founded by one of the country's top-performing property agents. The whole operation runs on WhatsApp: no email, no calendars, just group chats, thousands of them, with clients, agents, and leads together.", - 'That works until you need to understand the business underneath. Which deals are stuck? Where are leads coming from? What is the close rate? With spreadsheets and a legacy custom CRM that could not keep up, those questions were nearly impossible to answer.', - 'Mike and Azmat from Nine Dots stepped in to fix that, not by changing how Homeseller works, but by building a system that finally fit around it.', + msg`Homeseller is a high-volume real estate agency in Singapore, founded by one of the country's top-performing property agents. The whole operation runs on WhatsApp: no email, no calendars, just group chats, thousands of them, with clients, agents, and leads together.`, + msg`That works until you need to understand the business underneath. Which deals are stuck? Where are leads coming from? What is the close rate? With spreadsheets and a legacy custom CRM that could not keep up, those questions were nearly impossible to answer.`, + msg`Mike and Azmat from Nine Dots stepped in to fix that, not by changing how Homeseller works, but by building a system that finally fit around it.`, ], callout: '"Twenty lets us build a CRM around the business and not the business around the CRM." - Mike Babiy, Founder, Nine Dots Ventures', }, { type: 'text', - eyebrow: 'Architecture', + eyebrow: msg`Architecture`, heading: [ - { text: 'The CRM as a ', fontFamily: 'serif' }, - { text: 'control hub', fontFamily: 'sans' }, + { text: msg`The CRM as a`, fontFamily: 'serif' }, + { text: msg`control hub`, fontFamily: 'sans' }, ], paragraphs: [ - "Nine Dots rebuilt Homeseller's operations on Twenty, with a custom data model shaped around their sales flow. Because Twenty is open and everything is accessible via API, they connected it to what the business actually needed: n8n for automated workflows (in-app workflows were not available at that time), Grafana for live dashboards fed from Twenty, and a custom AI layer to parse and extract structured insights from more than 2,000 WhatsApp messages a day.", - 'Homeseller kept their habits. WhatsApp stayed WhatsApp. What changed is that everything flowing through those conversations now lands in a structured system, tracked, classified, and visible in real time.', + msg`Nine Dots rebuilt Homeseller's operations on Twenty, with a custom data model shaped around their sales flow. Because Twenty is open and everything is accessible via API, they connected it to what the business actually needed: n8n for automated workflows (in-app workflows were not available at that time), Grafana for live dashboards fed from Twenty, and a custom AI layer to parse and extract structured insights from more than 2,000 WhatsApp messages a day.`, + msg`Homeseller kept their habits. WhatsApp stayed WhatsApp. What changed is that everything flowing through those conversations now lands in a structured system, tracked, classified, and visible in real time.`, ], callout: '"Twenty is the heart of the system. Everything branches from it." - Azmat Parveen, Nine Dots Ventures', }, { type: 'text', - eyebrow: 'The result', + eyebrow: msg`The result`, heading: [ - { text: '150 hours', fontFamily: 'sans' }, - { text: ' saved every month', fontFamily: 'serif' }, + { text: msg`150 hours`, fontFamily: 'sans' }, + { + text: msg`saved every month`, + fontFamily: 'serif', + }, ], paragraphs: [ - 'About 150 hours per month saved in manual operations. Real-time metrics for the business owner. Growth readiness without adding operational headcount. A team that can answer questions that used to take days to piece together.', - 'The full rollout landed in July 2025. Since then, Nine Dots built a Smart Assistant on top of the system, nudging agents with tasks, reminders, and on-demand market analysis. Some agents never open Twenty directly, yet they are powered by it, outperforming peers on manual processes alone. By Q1 2026, Homeseller had recorded its best sales quarter ever.', + msg`About 150 hours per month saved in manual operations. Real-time metrics for the business owner. Growth readiness without adding operational headcount. A team that can answer questions that used to take days to piece together.`, + msg`The full rollout landed in July 2025. Since then, Nine Dots built a Smart Assistant on top of the system, nudging agents with tasks, reminders, and on-demand market analysis. Some agents never open Twenty directly, yet they are powered by it, outperforming peers on manual processes alone. By Q1 2026, Homeseller had recorded its best sales quarter ever.`, ], }, ], tableOfContents: [ - 'When the channel is the business', - 'The CRM as a control hub', - 'The result', + msg`When the channel is the business`, + msg`The CRM as a control hub`, + msg`The result`, ], catalogCard: { - summary: - "Nine Dots put Twenty at the center of Homeseller's stack with APIs, automation, and AI on top of WhatsApp-heavy operations.", - date: 'Jul 2025', + summary: msg`Nine Dots put Twenty at the center of Homeseller's stack with APIs, automation, and AI on top of WhatsApp-heavy operations.`, + date: msg`Jul 2025`, }, }; @@ -104,8 +116,18 @@ export const generateMetadata = buildLocalizedMetadata({ description: CASE_STUDY.meta.description, }); -export default async function NineDotsCaseStudyPage() { - const stats = await fetchCommunityStats(); +type CaseStudyPageProps = { + params: Promise; +}; + +export default async function NineDotsCaseStudyPage({ + params, +}: CaseStudyPageProps) { + const [i18n, stats] = await Promise.all([ + getRouteI18n(params), + fetchCommunityStats(), + ]); + const renderText = createMessageDescriptorRenderer(i18n); const menuSocialLinks = mergeSocialLinkLabels(MENU_DATA.socialLinks, stats); const palette = getCaseStudyPalette('/customers/9dots'); @@ -119,6 +141,7 @@ export default async function NineDotsCaseStudyPage() { key={index} block={block} isLast={index === CASE_STUDY.sections.length - 1} + renderText={renderText} sectionId={sectionId} /> ); @@ -150,17 +173,19 @@ export default async function NineDotsCaseStudyPage() { dashColor={palette.dashColor} hero={CASE_STUDY.hero} hoverDashColor={palette.hoverDashColor} + renderText={renderText} /> {sectionBlocks} - + ); } diff --git a/packages/twenty-website-new/src/app/[locale]/customers/_components/CustomersCaseStudySignoff.tsx b/packages/twenty-website-new/src/app/[locale]/customers/_components/CustomersCaseStudySignoff.tsx index 4f344ededc5..f23e2c28bdc 100644 --- a/packages/twenty-website-new/src/app/[locale]/customers/_components/CustomersCaseStudySignoff.tsx +++ b/packages/twenty-website-new/src/app/[locale]/customers/_components/CustomersCaseStudySignoff.tsx @@ -1,38 +1,48 @@ +import { msg } from '@lingui/core/macro'; import { TalkToUsButton } from '@/lib/contact-cal'; -import { LinkButton } from '@/design-system/components'; +import { HeadingPart, LinkButton } from '@/design-system/components'; import { Pages } from '@/lib/pages'; import { Signoff } from '@/sections/Signoff/components'; import { theme } from '@/theme'; - -const SIGNOFF_HEADING = [ - { text: 'Ready to grow\nwith ', fontFamily: 'serif' as const }, - { text: 'Twenty?', fontFamily: 'sans' as const }, -]; +import type { MessageDescriptor } from '@lingui/core'; const SIGNOFF_BODY = { - text: 'Join the teams that chose to own their CRM.\nStart building with Twenty today.', + text: msg`Join the teams that chose to own their CRM.\nStart building with Twenty today.`, }; -export function CustomersCaseStudySignoff() { +type CustomersCaseStudySignoffProps = { + renderText: (descriptor: MessageDescriptor) => string; +}; + +export function CustomersCaseStudySignoff({ + renderText, +}: CustomersCaseStudySignoffProps) { return ( - - + + + {renderText(msg`Ready to grow\nwith Twenty?`)} + + + diff --git a/packages/twenty-website-new/src/app/[locale]/customers/act-education/page.tsx b/packages/twenty-website-new/src/app/[locale]/customers/act-education/page.tsx index f97eee69389..58d737cce9a 100644 --- a/packages/twenty-website-new/src/app/[locale]/customers/act-education/page.tsx +++ b/packages/twenty-website-new/src/app/[locale]/customers/act-education/page.tsx @@ -1,7 +1,13 @@ +import { msg } from '@lingui/core/macro'; import { MENU_DATA } from '@/sections/Menu/data'; import { CustomersCaseStudySignoff } from '@/app/[locale]/customers/_components/CustomersCaseStudySignoff'; import { getCaseStudyPalette, type CaseStudyData } from '@/lib/customers'; import { fetchCommunityStats } from '@/lib/community/fetch-community-stats'; +import { createMessageDescriptorRenderer } from '@/lib/i18n/create-message-descriptor-renderer'; +import { + getRouteI18n, + type LocaleRouteParams, +} from '@/lib/i18n/get-route-i18n'; import { mergeSocialLinkLabels } from '@/lib/community/merge-social-link-labels'; import { CaseStudy } from '@/sections/CaseStudy/components'; import { Menu } from '@/sections/Menu/components'; @@ -13,88 +19,91 @@ const PLACEHOLDER_HERO = const CASE_STUDY: CaseStudyData = { meta: { - title: - 'Burned by vendor lock-in, AC&T built a CRM they actually own | Twenty', - description: - 'How AC&T Education Migration and Flycoder replaced a shuttered vendor CRM with self-hosted Twenty, with 90%+ lower cost and full ownership.', + title: msg`Burned by vendor lock-in, AC&T built a CRM they actually own | Twenty`, + description: msg`How AC&T Education Migration and Flycoder replaced a shuttered vendor CRM with self-hosted Twenty, with 90%+ lower cost and full ownership.`, }, hero: { readingTime: '7 min', title: [ - { text: 'A CRM they ', fontFamily: 'serif' }, - { text: 'actually own', fontFamily: 'sans', newLine: true }, + { text: msg`A CRM they`, fontFamily: 'serif' }, + { text: msg`actually own`, fontFamily: 'sans', newLine: true }, ], author: 'Joseph Chiang', authorAvatarSrc: '/images/partner/testimonials/joseph-chiang.jpg', - authorRole: 'CRM Engineer, AC&T Education Migration', + authorRole: msg`CRM Engineer, AC&T Education Migration`, clientIcon: 'act-education', heroImageSrc: PLACEHOLDER_HERO, - industry: 'Education', - kpis: [{ value: '90%+', label: 'Lower CRM cost' }], + industry: msg`Education`, + kpis: [{ value: msg`90%+`, label: msg`Lower CRM cost` }], }, sections: [ { type: 'text', - eyebrow: 'AC&T Education Migration', + eyebrow: msg`AC&T Education Migration`, heading: [ - { text: 'When the vendor ', fontFamily: 'serif' }, - { text: 'pulled the plug', fontFamily: 'sans' }, + { text: msg`When the vendor`, fontFamily: 'serif' }, + { text: msg`pulled the plug`, fontFamily: 'sans' }, ], paragraphs: [ - 'AC&T Education Migration (actimmi.com) is an education agency in Australia. They help international students with applications to education providers and visas. They had been on a previous CRM until the vendor shut the system down, leaving nothing but a CSV export.', - 'Whatever came next had to be something they could own.', + msg`AC&T Education Migration (actimmi.com) is an education agency in Australia. They help international students with applications to education providers and visas. They had been on a previous CRM until the vendor shut the system down, leaving nothing but a CSV export.`, + msg`Whatever came next had to be something they could own.`, ], callout: '"They did not want to learn someone else\'s system. They wanted to keep working the way they already did and make it smoother." - Joseph Chiang, CRM Engineer, AC&T Education Migration', }, { type: 'text', - eyebrow: 'Implementation', + eyebrow: msg`Implementation`, heading: [ - { text: "No more renting someone else's ", fontFamily: 'serif' }, - { text: 'structure', fontFamily: 'sans' }, + { + text: msg`No more renting someone else's`, + fontFamily: 'serif', + }, + { text: msg`structure`, fontFamily: 'sans' }, ], paragraphs: [ - 'They evaluated Salesforce, Zoho, Pipedrive, and SuiteCRM. Each came with the same tradeoffs: too expensive, too rigid, or too generic, and none fixed the underlying problem. They were still renting a structure they did not control.', - 'Flycoder, a full-stack development partner, helped them set up Twenty as a self-hosted instance shaped around how AC&T actually operates. The data model centers on students, not a generic contact-and-deal pipeline. Statuses update automatically: a workflow runs nightly to keep enrollment records current. Automated email reminders cover important dates. Adding a new record takes under a minute.', - 'The result is a system that fits how AC&T already worked, instead of the other way around.', + msg`They evaluated Salesforce, Zoho, Pipedrive, and SuiteCRM. Each came with the same tradeoffs: too expensive, too rigid, or too generic, and none fixed the underlying problem. They were still renting a structure they did not control.`, + msg`Flycoder, a full-stack development partner, helped them set up Twenty as a self-hosted instance shaped around how AC&T actually operates. The data model centers on students, not a generic contact-and-deal pipeline. Statuses update automatically: a workflow runs nightly to keep enrollment records current. Automated email reminders cover important dates. Adding a new record takes under a minute.`, + msg`The result is a system that fits how AC&T already worked, instead of the other way around.`, ], }, { type: 'text', - eyebrow: 'Control', + eyebrow: msg`Control`, heading: [ - { text: 'Control without ', fontFamily: 'serif' }, - { text: 'the overhead', fontFamily: 'sans' }, + { text: msg`Control without`, fontFamily: 'serif' }, + { text: msg`the overhead`, fontFamily: 'sans' }, ], paragraphs: [ - 'Self-hosted means AC&T carries no vendor risk: no pricing model that can change, no platform that can disappear, no forced migration. The system is theirs.', - "Because everything is built on Twenty's open foundation, Flycoder could wire the exact logic AC&T needed without fighting the platform.", + msg`Self-hosted means AC&T carries no vendor risk: no pricing model that can change, no platform that can disappear, no forced migration. The system is theirs.`, + msg`Because everything is built on Twenty's open foundation, Flycoder could wire the exact logic AC&T needed without fighting the platform.`, ], }, { type: 'text', - eyebrow: 'The result', + eyebrow: msg`The result`, heading: [ - { text: 'Costs down more than ', fontFamily: 'serif' }, - { text: '90%', fontFamily: 'sans' }, + { + text: msg`Costs down more than`, + fontFamily: 'serif', + }, + { text: msg`90%`, fontFamily: 'sans' }, ], paragraphs: [ - 'CRM costs dropped by more than 90%. Manual overhead tied to the old system is gone. For the first time, AC&T has a CRM they will not lose again.', - 'They did not just replace a tool. They took back ownership of how their business runs.', + msg`CRM costs dropped by more than 90%. Manual overhead tied to the old system is gone. For the first time, AC&T has a CRM they will not lose again.`, + msg`They did not just replace a tool. They took back ownership of how their business runs.`, ], }, ], tableOfContents: [ - 'When the vendor pulled the plug', - "No more renting someone else's structure", - 'Control without the overhead', - 'The result', + msg`When the vendor pulled the plug`, + msg`No more renting someone else's structure`, + msg`Control without the overhead`, + msg`The result`, ], catalogCard: { - summary: - 'AC&T and Flycoder moved from a dead vendor export to self-hosted Twenty, with over 90% lower CRM cost and full control.', - date: '2025', + summary: msg`AC&T and Flycoder moved from a dead vendor export to self-hosted Twenty, with over 90% lower CRM cost and full control.`, + date: msg`2025`, }, }; @@ -104,8 +113,18 @@ export const generateMetadata = buildLocalizedMetadata({ description: CASE_STUDY.meta.description, }); -export default async function ActEducationCaseStudyPage() { - const stats = await fetchCommunityStats(); +type CaseStudyPageProps = { + params: Promise; +}; + +export default async function ActEducationCaseStudyPage({ + params, +}: CaseStudyPageProps) { + const [i18n, stats] = await Promise.all([ + getRouteI18n(params), + fetchCommunityStats(), + ]); + const renderText = createMessageDescriptorRenderer(i18n); const menuSocialLinks = mergeSocialLinkLabels(MENU_DATA.socialLinks, stats); const palette = getCaseStudyPalette('/customers/act-education'); @@ -119,6 +138,7 @@ export default async function ActEducationCaseStudyPage() { key={index} block={block} isLast={index === CASE_STUDY.sections.length - 1} + renderText={renderText} sectionId={sectionId} /> ); @@ -150,17 +170,19 @@ export default async function ActEducationCaseStudyPage() { dashColor={palette.dashColor} hero={CASE_STUDY.hero} hoverDashColor={palette.hoverDashColor} + renderText={renderText} /> {sectionBlocks} - + ); } diff --git a/packages/twenty-website-new/src/app/[locale]/customers/alternative-partners/page.tsx b/packages/twenty-website-new/src/app/[locale]/customers/alternative-partners/page.tsx index dbc185cb731..0230c936ac5 100644 --- a/packages/twenty-website-new/src/app/[locale]/customers/alternative-partners/page.tsx +++ b/packages/twenty-website-new/src/app/[locale]/customers/alternative-partners/page.tsx @@ -1,7 +1,13 @@ +import { msg } from '@lingui/core/macro'; import { MENU_DATA } from '@/sections/Menu/data'; import { CustomersCaseStudySignoff } from '@/app/[locale]/customers/_components/CustomersCaseStudySignoff'; import { getCaseStudyPalette, type CaseStudyData } from '@/lib/customers'; import { fetchCommunityStats } from '@/lib/community/fetch-community-stats'; +import { createMessageDescriptorRenderer } from '@/lib/i18n/create-message-descriptor-renderer'; +import { + getRouteI18n, + type LocaleRouteParams, +} from '@/lib/i18n/get-route-i18n'; import { mergeSocialLinkLabels } from '@/lib/community/merge-social-link-labels'; import { CaseStudy } from '@/sections/CaseStudy/components'; import { Menu } from '@/sections/Menu/components'; @@ -13,62 +19,59 @@ const PLACEHOLDER_HERO = const CASE_STUDY: CaseStudyData = { meta: { - title: - 'From Salesforce to self-hosted Twenty, powered by AI | Alternative Partners', - description: - 'How Alternative Partners migrated from Salesforce to self-hosted Twenty using agentic AI in the implementation loop: fast migration, durable ownership.', + title: msg`From Salesforce to self-hosted Twenty, powered by AI | Alternative Partners`, + description: msg`How Alternative Partners migrated from Salesforce to self-hosted Twenty using agentic AI in the implementation loop: fast migration, durable ownership.`, }, hero: { readingTime: '7 min', title: [ - { text: 'From Salesforce to ', fontFamily: 'serif' }, - { text: 'self-hosted Twenty', fontFamily: 'sans', newLine: true }, + { text: msg`From Salesforce to`, fontFamily: 'serif' }, + { text: msg`self-hosted Twenty`, fontFamily: 'sans', newLine: true }, ], author: 'Benjamin Reynolds', authorAvatarSrc: '/images/partner/testimonials/benjamin-reynolds.webp', - authorRole: 'Principal and Founder, Alternative Partners', + authorRole: msg`Principal and Founder, Alternative Partners`, clientIcon: 'alternative-partners', heroImageSrc: PLACEHOLDER_HERO, - industry: 'Consulting', + industry: msg`Consulting`, kpis: [ - { value: 'AI-assisted', label: 'Salesforce migration' }, - { value: 'Self-hosted', label: 'Full ownership' }, + { value: msg`AI-assisted`, label: msg`Salesforce migration` }, + { value: msg`Self-hosted`, label: msg`Full ownership` }, ], }, sections: [ { type: 'text', - eyebrow: 'Alternative Partners', + eyebrow: msg`Alternative Partners`, heading: [ - { text: 'AI in the ', fontFamily: 'serif' }, - { text: 'migration workflow', fontFamily: 'sans' }, + { text: msg`AI in the`, fontFamily: 'serif' }, + { text: msg`migration workflow`, fontFamily: 'sans' }, ], paragraphs: [ - "Alternative Partners is a consulting firm that moved from Salesforce to a self-hosted Twenty instance. Benjamin Reynolds led the migration. He had already become a Twenty expert implementing Twenty for one of Twenty's first cloud customers.", - 'His approach was unconventional. Instead of mapping fields manually, scripting transforms, and validating data step by step, he handed the job to agentic AI tools with a brief: where the data lives, the GitHub repo for the target platform, and the Railway deployment. Start, and only return if something breaks beyond a 70% confidence fix.', - 'It worked. This is AI-assisted iteration in practice: not AI as a product feature, but as part of implementation work, compressing what would typically be weeks into something one person can oversee without being the bottleneck.', + msg`Alternative Partners is a consulting firm that moved from Salesforce to a self-hosted Twenty instance. Benjamin Reynolds led the migration. He had already become a Twenty expert implementing Twenty for one of Twenty's first cloud customers.`, + msg`His approach was unconventional. Instead of mapping fields manually, scripting transforms, and validating data step by step, he handed the job to agentic AI tools with a brief: where the data lives, the GitHub repo for the target platform, and the Railway deployment. Start, and only return if something breaks beyond a 70% confidence fix.`, + msg`It worked. This is AI-assisted iteration in practice: not AI as a product feature, but as part of implementation work, compressing what would typically be weeks into something one person can oversee without being the bottleneck.`, ], }, { type: 'text', - eyebrow: 'Ownership', + eyebrow: msg`Ownership`, heading: [ - { text: 'Self-hosted ', fontFamily: 'serif' }, - { text: 'means control', fontFamily: 'sans' }, + { text: msg`Self-hosted`, fontFamily: 'serif' }, + { text: msg`means control`, fontFamily: 'sans' }, ], paragraphs: [ - 'The self-hosted setup means Alternative Partners owns the full stack: no vendor access to their data, no dependency on a SaaS pricing model, full control over how the system evolves. The migration was fast because of AI; the result is durable because the stack is open source.', + msg`The self-hosted setup means Alternative Partners owns the full stack: no vendor access to their data, no dependency on a SaaS pricing model, full control over how the system evolves. The migration was fast because of AI; the result is durable because the stack is open source.`, ], }, ], tableOfContents: [ - 'AI in the migration workflow', - 'Self-hosted means control', + msg`AI in the migration workflow`, + msg`Self-hosted means control`, ], catalogCard: { - summary: - 'Alternative Partners replaced Salesforce with self-hosted Twenty, using agentic AI to compress migration work.', - date: '2025', + summary: msg`Alternative Partners replaced Salesforce with self-hosted Twenty, using agentic AI to compress migration work.`, + date: msg`2025`, }, }; @@ -78,8 +81,18 @@ export const generateMetadata = buildLocalizedMetadata({ description: CASE_STUDY.meta.description, }); -export default async function AlternativePartnersCaseStudyPage() { - const stats = await fetchCommunityStats(); +type CaseStudyPageProps = { + params: Promise; +}; + +export default async function AlternativePartnersCaseStudyPage({ + params, +}: CaseStudyPageProps) { + const [i18n, stats] = await Promise.all([ + getRouteI18n(params), + fetchCommunityStats(), + ]); + const renderText = createMessageDescriptorRenderer(i18n); const menuSocialLinks = mergeSocialLinkLabels(MENU_DATA.socialLinks, stats); const palette = getCaseStudyPalette('/customers/alternative-partners'); @@ -93,6 +106,7 @@ export default async function AlternativePartnersCaseStudyPage() { key={index} block={block} isLast={index === CASE_STUDY.sections.length - 1} + renderText={renderText} sectionId={sectionId} /> ); @@ -124,17 +138,19 @@ export default async function AlternativePartnersCaseStudyPage() { dashColor={palette.dashColor} hero={CASE_STUDY.hero} hoverDashColor={palette.hoverDashColor} + renderText={renderText} /> {sectionBlocks} - + ); } diff --git a/packages/twenty-website-new/src/app/[locale]/customers/elevate-consulting/page.tsx b/packages/twenty-website-new/src/app/[locale]/customers/elevate-consulting/page.tsx index 6b5499cdc11..842c4ac0d7c 100644 --- a/packages/twenty-website-new/src/app/[locale]/customers/elevate-consulting/page.tsx +++ b/packages/twenty-website-new/src/app/[locale]/customers/elevate-consulting/page.tsx @@ -1,7 +1,13 @@ +import { msg } from '@lingui/core/macro'; import { MENU_DATA } from '@/sections/Menu/data'; import { CustomersCaseStudySignoff } from '@/app/[locale]/customers/_components/CustomersCaseStudySignoff'; import { getCaseStudyPalette, type CaseStudyData } from '@/lib/customers'; import { fetchCommunityStats } from '@/lib/community/fetch-community-stats'; +import { createMessageDescriptorRenderer } from '@/lib/i18n/create-message-descriptor-renderer'; +import { + getRouteI18n, + type LocaleRouteParams, +} from '@/lib/i18n/get-route-i18n'; import { mergeSocialLinkLabels } from '@/lib/community/merge-social-link-labels'; import { CaseStudy } from '@/sections/CaseStudy/components'; import { Menu } from '@/sections/Menu/components'; @@ -13,102 +19,102 @@ const PLACEHOLDER_HERO = const CASE_STUDY: CaseStudyData = { meta: { - title: - 'Twenty as the API backbone of a go-to-market stack | Elevate Consulting', - description: - 'How Elevate Consulting moved off documents and spreadsheets to Twenty as the API-connected CRM at the center of their stack.', + title: msg`Twenty as the API backbone of a go-to-market stack | Elevate Consulting`, + description: msg`How Elevate Consulting moved off documents and spreadsheets to Twenty as the API-connected CRM at the center of their stack.`, }, hero: { readingTime: '8 min', title: [ - { text: 'Twenty as the ', fontFamily: 'serif' }, - { text: 'API backbone', fontFamily: 'sans', newLine: true }, - { text: ' of a go-to-market stack', fontFamily: 'serif' }, + { text: msg`Twenty as the`, fontFamily: 'serif' }, + { text: msg`API backbone`, fontFamily: 'sans', newLine: true }, + { + text: msg`of a go-to-market stack`, + fontFamily: 'serif', + }, ], author: 'Justin Beadle', - authorRole: 'Director of Digital and Information, Elevate Consulting', + authorRole: msg`Director of Digital and Information, Elevate Consulting`, clientIcon: 'elevate-consulting', heroImageSrc: PLACEHOLDER_HERO, - industry: 'Management Consulting', + industry: msg`Management Consulting`, kpis: [ - { value: '1 click', label: 'Proposal automation' }, - { value: '4 tools', label: 'Connected via API' }, - { value: 'API-first', label: 'Tool integration' }, + { value: msg`1 click`, label: msg`Proposal automation` }, + { value: msg`4 tools`, label: msg`Connected via API` }, + { value: msg`API-first`, label: msg`Tool integration` }, ], quote: { - text: 'It is just such a nicer experience than dealing with a Salesforce or a HubSpot. My mission has been to get every tool API-accessible, so everything talks to each other.', + text: msg`It is just such a nicer experience than dealing with a Salesforce or a HubSpot. My mission has been to get every tool API-accessible, so everything talks to each other.`, author: 'Justin Beadle', - role: 'Director of Digital and Information, Elevate Consulting', + role: msg`Director of Digital and Information, Elevate Consulting`, }, }, sections: [ { type: 'text', - eyebrow: 'The situation', + eyebrow: msg`The situation`, heading: [ - { text: 'From documents to ', fontFamily: 'serif' }, - { text: 'open APIs', fontFamily: 'sans' }, + { text: msg`From documents to`, fontFamily: 'serif' }, + { text: msg`open APIs`, fontFamily: 'sans' }, ], paragraphs: [ - 'Elevate Consulting is a management consultancy based in Canada. When Justin Beadle, Director of Digital and Information, joined, the company ran entirely on Word documents, Excel spreadsheets, sticky notes, emails, and reliance on people. There was no CRM, no API-accessible tools, only a patchwork trying to stand in for a single source of truth.', - 'The CEO had resisted bringing in a CRM for years. The business development team had no experience using one, and the licensing costs of well-known CRMs like Salesforce or HubSpot were hard to justify without any guarantee of adoption: CRMs are only as good as the maintenance of the data inside them.', - 'In June 2025, Justin learned Twenty v1 had shipped. Within two or three days, the CEO asked him to look into setting up a CRM. The shift came from the potential of what could be built on top of fully open APIs. The timing was perfect.', + msg`Elevate Consulting is a management consultancy based in Canada. When Justin Beadle, Director of Digital and Information, joined, the company ran entirely on Word documents, Excel spreadsheets, sticky notes, emails, and reliance on people. There was no CRM, no API-accessible tools, only a patchwork trying to stand in for a single source of truth.`, + msg`The CEO had resisted bringing in a CRM for years. The business development team had no experience using one, and the licensing costs of well-known CRMs like Salesforce or HubSpot were hard to justify without any guarantee of adoption: CRMs are only as good as the maintenance of the data inside them.`, + msg`In June 2025, Justin learned Twenty v1 had shipped. Within two or three days, the CEO asked him to look into setting up a CRM. The shift came from the potential of what could be built on top of fully open APIs. The timing was perfect.`, ], callout: '"It is just such a nicer experience than dealing with a Salesforce or a HubSpot. My mission has been to get every tool API-accessible, so everything talks to each other. Twenty made that possible in a way older CRM platforms simply do not." - Justin Beadle, Director of Digital and Information, Elevate Consulting', }, { type: 'text', - eyebrow: 'Integration', + eyebrow: msg`Integration`, heading: [ - { text: 'One ', fontFamily: 'serif' }, - { text: 'API', fontFamily: 'sans' }, - { text: ' to rule them all', fontFamily: 'serif' }, + { text: msg`One`, fontFamily: 'serif' }, + { text: msg`API`, fontFamily: 'sans' }, + { text: msg`to rule them all`, fontFamily: 'serif' }, ], paragraphs: [ - "Justin's broader mission at Elevate has been to move the company off static documents and onto tools with API access. By the end of 2025, that was in place: time billing, resource planning, Microsoft Teams, and project management were all accessible via API, with Twenty at the center holding client and opportunity data. Team members could use that information strategically instead of re-keying it.", - 'That opened the door to something more powerful. Justin built a custom front end that pulls live data from those systems into a single view, tailored to each role. When a proposal is won, what used to require four separate people manually setting up instances across four different tools now happens in a single click, drawing on data collected in Twenty across the full opportunity lifecycle. It is another shift toward higher-value work for clients.', - 'Twenty is not only where CRM data lives. It is the API backbone that makes the rest of the stack possible.', + msg`Justin's broader mission at Elevate has been to move the company off static documents and onto tools with API access. By the end of 2025, that was in place: time billing, resource planning, Microsoft Teams, and project management were all accessible via API, with Twenty at the center holding client and opportunity data. Team members could use that information strategically instead of re-keying it.`, + msg`That opened the door to something more powerful. Justin built a custom front end that pulls live data from those systems into a single view, tailored to each role. When a proposal is won, what used to require four separate people manually setting up instances across four different tools now happens in a single click, drawing on data collected in Twenty across the full opportunity lifecycle. It is another shift toward higher-value work for clients.`, + msg`Twenty is not only where CRM data lives. It is the API backbone that makes the rest of the stack possible.`, ], callout: '"Because Twenty\'s API is fully open, I could connect it to every other tool in our stack. When a proposal is won, what used to take four people manually setting things up across four different tools now happens in a single click. That is the kind of time saving that only becomes possible when everything is connected." - Justin Beadle, Director of Digital and Information, Elevate Consulting', }, { type: 'text', - eyebrow: 'Adoption', + eyebrow: msg`Adoption`, heading: [ - { text: 'Workflows that ', fontFamily: 'serif' }, - { text: 'actually get used', fontFamily: 'sans' }, + { text: msg`Workflows that`, fontFamily: 'serif' }, + { text: msg`actually get used`, fontFamily: 'sans' }, ], paragraphs: [ - 'The business development team finally had the CRM they had been asking for. Adoption came naturally: their data was already there when they logged in.', - 'Justin built workflows for notifications across the team, alerting the right people in Teams when a prospect becomes a lead or when project milestones are reached. Forms in Twenty let the business development team log activity without leaving the tool. The impact is real for the organization. The tool has been adaptable from opportunity-level work at a client to executive-level decisions.', - 'The flexibility to wire this together, without outside help and without fighting the platform, is what made it possible for a single person to stand up and maintain a connected stack across an entire consultancy.', + msg`The business development team finally had the CRM they had been asking for. Adoption came naturally: their data was already there when they logged in.`, + msg`Justin built workflows for notifications across the team, alerting the right people in Teams when a prospect becomes a lead or when project milestones are reached. Forms in Twenty let the business development team log activity without leaving the tool. The impact is real for the organization. The tool has been adaptable from opportunity-level work at a client to executive-level decisions.`, + msg`The flexibility to wire this together, without outside help and without fighting the platform, is what made it possible for a single person to stand up and maintain a connected stack across an entire consultancy.`, ], }, { type: 'text', - eyebrow: 'What is next', + eyebrow: msg`What is next`, heading: [ - { text: 'Beyond ', fontFamily: 'serif' }, - { text: 'internal rollout', fontFamily: 'sans' }, + { text: msg`Beyond`, fontFamily: 'serif' }, + { text: msg`internal rollout`, fontFamily: 'sans' }, ], paragraphs: [ - "Elevate's CEO was so impressed with Twenty he started recommending it to clients before the internal setup was even complete. The team is exploring bringing Twenty to client projects as part of their consulting practice, including as the backend for custom-built products tailored to specific operational needs.", - 'For a firm that once ran on sticky notes, this is more than an upgrade. It is a complete transformation.', + msg`Elevate's CEO was so impressed with Twenty he started recommending it to clients before the internal setup was even complete. The team is exploring bringing Twenty to client projects as part of their consulting practice, including as the backend for custom-built products tailored to specific operational needs.`, + msg`For a firm that once ran on sticky notes, this is more than an upgrade. It is a complete transformation.`, ], }, ], tableOfContents: [ - 'From documents to open APIs', - 'One API to rule them all', - 'Workflows that actually get used', - 'What is next', + msg`From documents to open APIs`, + msg`One API to rule them all`, + msg`Workflows that actually get used`, + msg`What is next`, ], catalogCard: { - summary: - 'Elevate Consulting uses Twenty as the API backbone connecting billing, Teams, resourcing, and a custom front end around client and opportunity data.', - date: 'Jun 2025', + summary: msg`Elevate Consulting uses Twenty as the API backbone connecting billing, Teams, resourcing, and a custom front end around client and opportunity data.`, + date: msg`Jun 2025`, }, }; @@ -118,8 +124,18 @@ export const generateMetadata = buildLocalizedMetadata({ description: CASE_STUDY.meta.description, }); -export default async function ElevateConsultingCaseStudyPage() { - const stats = await fetchCommunityStats(); +type CaseStudyPageProps = { + params: Promise; +}; + +export default async function ElevateConsultingCaseStudyPage({ + params, +}: CaseStudyPageProps) { + const [i18n, stats] = await Promise.all([ + getRouteI18n(params), + fetchCommunityStats(), + ]); + const renderText = createMessageDescriptorRenderer(i18n); const menuSocialLinks = mergeSocialLinkLabels(MENU_DATA.socialLinks, stats); const palette = getCaseStudyPalette('/customers/elevate-consulting'); @@ -133,6 +149,7 @@ export default async function ElevateConsultingCaseStudyPage() { key={index} block={block} isLast={index === CASE_STUDY.sections.length - 1} + renderText={renderText} sectionId={sectionId} /> ); @@ -164,17 +181,19 @@ export default async function ElevateConsultingCaseStudyPage() { dashColor={palette.dashColor} hero={CASE_STUDY.hero} hoverDashColor={palette.hoverDashColor} + renderText={renderText} /> {sectionBlocks} - + ); } diff --git a/packages/twenty-website-new/src/app/[locale]/customers/netzero/page.tsx b/packages/twenty-website-new/src/app/[locale]/customers/netzero/page.tsx index f25f3903ac4..be61519d525 100644 --- a/packages/twenty-website-new/src/app/[locale]/customers/netzero/page.tsx +++ b/packages/twenty-website-new/src/app/[locale]/customers/netzero/page.tsx @@ -1,7 +1,13 @@ +import { msg } from '@lingui/core/macro'; import { MENU_DATA } from '@/sections/Menu/data'; import { CustomersCaseStudySignoff } from '@/app/[locale]/customers/_components/CustomersCaseStudySignoff'; import { getCaseStudyPalette, type CaseStudyData } from '@/lib/customers'; import { fetchCommunityStats } from '@/lib/community/fetch-community-stats'; +import { createMessageDescriptorRenderer } from '@/lib/i18n/create-message-descriptor-renderer'; +import { + getRouteI18n, + type LocaleRouteParams, +} from '@/lib/i18n/get-route-i18n'; import { mergeSocialLinkLabels } from '@/lib/community/merge-social-link-labels'; import { CaseStudy } from '@/sections/CaseStudy/components'; import { Menu } from '@/sections/Menu/components'; @@ -13,92 +19,93 @@ const PLACEHOLDER_HERO = const CASE_STUDY: CaseStudyData = { meta: { - title: 'A CRM that grows with you | NetZero & Twenty', - description: - 'How NetZero uses Twenty across carbon credits, agricultural products, and franchised industrial systems with a modular CRM and a roadmap toward AI-assisted workflows.', + title: msg`A CRM that grows with you | NetZero & Twenty`, + description: msg`How NetZero uses Twenty across carbon credits, agricultural products, and franchised industrial systems with a modular CRM and a roadmap toward AI-assisted workflows.`, }, hero: { readingTime: '8 min', title: [ - { text: 'A CRM that ', fontFamily: 'serif' }, - { text: 'grows', fontFamily: 'sans', newLine: true }, - { text: ' with you', fontFamily: 'serif' }, + { text: msg`A CRM that`, fontFamily: 'serif' }, + { text: msg`grows`, fontFamily: 'sans', newLine: true }, + { text: msg`with you`, fontFamily: 'serif' }, ], author: 'Olivier Reinaud', authorAvatarSrc: '/images/partner/testimonials/olivier-reinaud.jpg', - authorRole: 'Co-founder, NetZero', + authorRole: msg`Co-founder, NetZero`, clientIcon: 'netzero', heroImageSrc: PLACEHOLDER_HERO, - industry: 'Agribusiness', + industry: msg`Agribusiness`, kpis: [ - { value: '3 product lines', label: 'On a single CRM' }, - { value: 'No-code', label: 'Customizations' }, + { value: msg`3 product lines`, label: msg`On a single CRM` }, + { value: msg`No-code`, label: msg`Customizations` }, ], }, sections: [ { type: 'text', - eyebrow: 'NetZero', + eyebrow: msg`NetZero`, heading: [ - { text: 'The right ', fontFamily: 'serif' }, - { text: 'foundation', fontFamily: 'sans' }, + { text: msg`The right`, fontFamily: 'serif' }, + { text: msg`foundation`, fontFamily: 'sans' }, ], paragraphs: [ - 'NetZero works with the agro-industry, serving clients from multinationals to smallholder farmers. They sell carbon credits, agricultural products, and franchised industrial systems across three different product lines, multiple countries, and multiple company sizes. When Olivier Reinaud, co-founder of NetZero, started looking at CRMs in late 2024, he was not chasing the most feature-rich platform. He wanted the right foundation.', + msg`NetZero works with the agro-industry, serving clients from multinationals to smallholder farmers. They sell carbon credits, agricultural products, and franchised industrial systems across three different product lines, multiple countries, and multiple company sizes. When Olivier Reinaud, co-founder of NetZero, started looking at CRMs in late 2024, he was not chasing the most feature-rich platform. He wanted the right foundation.`, ], callout: '"Twenty delivers on what CRMs should have always been: fairly priced software with a fully modular and customizable model, a clean and modern UI, granular permissions, automations, enterprise features. A compelling solution with high potential to rightfully disrupt the CRM market." - Olivier Reinaud, co-founder of NetZero', }, { type: 'text', - eyebrow: 'Flexibility', + eyebrow: msg`Flexibility`, heading: [ - { text: 'A business that does not fit a ', fontFamily: 'serif' }, - { text: 'template', fontFamily: 'sans' }, + { + text: msg`A business that does not fit a`, + fontFamily: 'serif', + }, + { text: msg`template`, fontFamily: 'sans' }, ], paragraphs: [ - 'What convinced Olivier was the flexibility of the platform and where it was headed. Even when initial needs were basic record-keeping, he still needed a custom data model with granular permissions to manage the wide range of NetZero activities. He also needed a system that could adapt quickly to a fast-iteration company.', - 'With Twenty, when a new need appears, he can address it himself: no developer required, no support ticket.', + msg`What convinced Olivier was the flexibility of the platform and where it was headed. Even when initial needs were basic record-keeping, he still needed a custom data model with granular permissions to manage the wide range of NetZero activities. He also needed a system that could adapt quickly to a fast-iteration company.`, + msg`With Twenty, when a new need appears, he can address it himself: no developer required, no support ticket.`, ], callout: '"The flexibility is really what made the difference. Our needs evolve very fast. I discover a new need and in two clicks I can address it. That is a real advantage when you are moving quickly." - Olivier Reinaud, co-founder of NetZero', }, { type: 'text', - eyebrow: 'Roadmap', + eyebrow: msg`Roadmap`, heading: [ - { text: 'From simple to ', fontFamily: 'serif' }, - { text: 'advanced', fontFamily: 'sans' }, + { text: msg`From simple to`, fontFamily: 'serif' }, + { text: msg`advanced`, fontFamily: 'sans' }, ], paragraphs: [ - "Olivier recognizes that NetZero's current use of Twenty is still relatively simple: workflows and integrations are not yet as deep as he eventually wants, because he prioritized getting foundations right first.", - 'What is planned is significant. NetZero has a data lake, online forms, and multiple internal systems that he wants to connect to Twenty. The pipes are there; the next step is automations that tie them together.', - 'What is coming in April 2026 is what he has been waiting for: AI-assisted workflow creation, describing what he needs and iterating from there instead of building complex logic from scratch. For a founder who runs the CRM himself, that changes what is realistically possible.', + msg`Olivier recognizes that NetZero's current use of Twenty is still relatively simple: workflows and integrations are not yet as deep as he eventually wants, because he prioritized getting foundations right first.`, + msg`What is planned is significant. NetZero has a data lake, online forms, and multiple internal systems that he wants to connect to Twenty. The pipes are there; the next step is automations that tie them together.`, + msg`What is coming in April 2026 is what he has been waiting for: AI-assisted workflow creation, describing what he needs and iterating from there instead of building complex logic from scratch. For a founder who runs the CRM himself, that changes what is realistically possible.`, ], }, { type: 'text', - eyebrow: 'Results', + eyebrow: msg`Results`, heading: [ - { text: 'The bet is ', fontFamily: 'serif' }, - { text: 'paying off', fontFamily: 'sans' }, + { text: msg`The bet is`, fontFamily: 'serif' }, + { text: msg`paying off`, fontFamily: 'sans' }, ], paragraphs: [ - 'While NetZero still runs a second CRM in parallel for WhatsApp-heavy operations with farmers in Brazil, they expect to migrate all of it to Twenty as features and the ecosystem grow. Already, their structured, multinational pipeline is powered by Twenty.', - 'The early bet on the architecture is holding, and upcoming AI features are expected to make it even more relevant.', + msg`While NetZero still runs a second CRM in parallel for WhatsApp-heavy operations with farmers in Brazil, they expect to migrate all of it to Twenty as features and the ecosystem grow. Already, their structured, multinational pipeline is powered by Twenty.`, + msg`The early bet on the architecture is holding, and upcoming AI features are expected to make it even more relevant.`, ], }, ], tableOfContents: [ - 'The right foundation', - 'A business that does not fit a template', - 'From simple to advanced', - 'The bet is paying off', + msg`The right foundation`, + msg`A business that does not fit a template`, + msg`From simple to advanced`, + msg`The bet is paying off`, ], catalogCard: { - summary: - 'NetZero uses Twenty as a modular CRM across product lines and countries, with a roadmap into AI-assisted workflows.', - date: '2025', + summary: msg`NetZero uses Twenty as a modular CRM across product lines and countries, with a roadmap into AI-assisted workflows.`, + date: msg`2025`, }, }; @@ -108,8 +115,18 @@ export const generateMetadata = buildLocalizedMetadata({ description: CASE_STUDY.meta.description, }); -export default async function NetZeroCaseStudyPage() { - const stats = await fetchCommunityStats(); +type CaseStudyPageProps = { + params: Promise; +}; + +export default async function NetZeroCaseStudyPage({ + params, +}: CaseStudyPageProps) { + const [i18n, stats] = await Promise.all([ + getRouteI18n(params), + fetchCommunityStats(), + ]); + const renderText = createMessageDescriptorRenderer(i18n); const menuSocialLinks = mergeSocialLinkLabels(MENU_DATA.socialLinks, stats); const palette = getCaseStudyPalette('/customers/netzero'); @@ -123,6 +140,7 @@ export default async function NetZeroCaseStudyPage() { key={index} block={block} isLast={index === CASE_STUDY.sections.length - 1} + renderText={renderText} sectionId={sectionId} /> ); @@ -154,17 +172,19 @@ export default async function NetZeroCaseStudyPage() { dashColor={palette.dashColor} hero={CASE_STUDY.hero} hoverDashColor={palette.hoverDashColor} + renderText={renderText} /> {sectionBlocks} - + ); } diff --git a/packages/twenty-website-new/src/app/[locale]/customers/page.tsx b/packages/twenty-website-new/src/app/[locale]/customers/page.tsx index c2903a45e9f..14b668f1af2 100644 --- a/packages/twenty-website-new/src/app/[locale]/customers/page.tsx +++ b/packages/twenty-website-new/src/app/[locale]/customers/page.tsx @@ -1,11 +1,17 @@ +import { msg } from '@lingui/core/macro'; import { FAQ_DATA } from '@/sections/Faq/data'; import { MENU_DATA } from '@/sections/Menu/data'; import { TRUSTED_BY_DATA } from '@/sections/TrustedBy/data'; import { TalkToUsButton } from '@/lib/contact-cal'; import { CASE_STUDY_CATALOG_ENTRIES } from '@/lib/customers'; -import { Eyebrow, LinkButton } from '@/design-system/components'; -import { Pages } from '@/lib/pages'; +import { Eyebrow, HeadingPart, LinkButton } from '@/design-system/components'; import { fetchCommunityStats } from '@/lib/community/fetch-community-stats'; +import { createMessageDescriptorRenderer } from '@/lib/i18n/create-message-descriptor-renderer'; +import { + getRouteI18n, + type LocaleRouteParams, +} from '@/lib/i18n/get-route-i18n'; +import { Pages } from '@/lib/pages'; import { mergeSocialLinkLabels } from '@/lib/community/merge-social-link-labels'; import { CaseStudyCatalog } from '@/sections/CaseStudyCatalog/components'; import { Faq } from '@/sections/Faq/components'; @@ -19,23 +25,12 @@ import { css } from '@linaria/core'; export const generateMetadata = buildRouteMetadata('customers'); -const HERO_HEADING = [ - { text: 'See how teams ', fontFamily: 'serif' as const }, - { text: 'build ', fontFamily: 'serif' as const, newLine: true }, - { text: 'on Twenty', fontFamily: 'sans' as const }, -]; - const HERO_BODY = { - text: 'Real stories from real teams about how they shaped Twenty to fit their workflow and accelerated their growth.', + text: msg`Real stories from real teams about how they shaped Twenty to fit their workflow and accelerated their growth.`, }; -const SIGNOFF_HEADING = [ - { text: 'Ready to build\n', fontFamily: 'serif' as const }, - { text: 'your own story?', fontFamily: 'sans' as const }, -]; - const SIGNOFF_BODY = { - text: 'Join the teams that chose to own their CRM.\nStart building with Twenty today.', + text: msg`Join the teams that chose to own their CRM.\nStart building with Twenty today.`, }; const CUSTOMERS_TOP_BACKGROUND_COLOR = '#F4F4F4'; @@ -66,8 +61,18 @@ const pageRevealClassName = css` } `; -export default async function CaseStudiesCatalogPage() { - const stats = await fetchCommunityStats(); +type CaseStudiesCatalogPageProps = { + params: Promise; +}; + +export default async function CaseStudiesCatalogPage({ + params, +}: CaseStudiesCatalogPageProps) { + const [i18n, stats] = await Promise.all([ + getRouteI18n(params), + fetchCommunityStats(), + ]); + const renderText = createMessageDescriptorRenderer(i18n); const menuSocialLinks = mergeSocialLinkLabels(MENU_DATA.socialLinks, stats); return ( @@ -86,41 +91,75 @@ export default async function CaseStudiesCatalogPage() {
- - + + + {renderText(msg`See how teams`)} + +
+ + {renderText(msg`build`)} + {' '} + + {renderText(msg`on Twenty`)} + +
+
- +
- + - - + + + {renderText(msg`Ready to build`)} + +
+ + {renderText(msg`your own story?`)} + +
+ @@ -128,19 +167,30 @@ export default async function CaseStudiesCatalogPage() { - - + + + + {renderText(msg`Stop fighting custom.`)} + +
+ + {renderText(msg`Start building, with Twenty`)} + +
diff --git a/packages/twenty-website-new/src/app/[locale]/customers/w3villa/page.tsx b/packages/twenty-website-new/src/app/[locale]/customers/w3villa/page.tsx index eff21a97ba0..0314103f803 100644 --- a/packages/twenty-website-new/src/app/[locale]/customers/w3villa/page.tsx +++ b/packages/twenty-website-new/src/app/[locale]/customers/w3villa/page.tsx @@ -1,7 +1,13 @@ +import { msg } from '@lingui/core/macro'; import { MENU_DATA } from '@/sections/Menu/data'; import { CustomersCaseStudySignoff } from '@/app/[locale]/customers/_components/CustomersCaseStudySignoff'; import { getCaseStudyPalette, type CaseStudyData } from '@/lib/customers'; import { fetchCommunityStats } from '@/lib/community/fetch-community-stats'; +import { createMessageDescriptorRenderer } from '@/lib/i18n/create-message-descriptor-renderer'; +import { + getRouteI18n, + type LocaleRouteParams, +} from '@/lib/i18n/get-route-i18n'; import { mergeSocialLinkLabels } from '@/lib/community/merge-social-link-labels'; import { CaseStudy } from '@/sections/CaseStudy/components'; import { Menu } from '@/sections/Menu/components'; @@ -13,89 +19,92 @@ const PLACEHOLDER_HERO = const CASE_STUDY: CaseStudyData = { meta: { - title: - 'When your CRM is the product: W3Grads on Twenty | W3villa Technologies', - description: - 'How W3villa Technologies shipped W3Grads, an AI mock interview platform for institutions, on Twenty as the operational backbone.', + title: msg`When your CRM is the product: W3Grads on Twenty | W3villa Technologies`, + description: msg`How W3villa Technologies shipped W3Grads, an AI mock interview platform for institutions, on Twenty as the operational backbone.`, }, hero: { readingTime: '8 min', title: [ - { text: 'When your CRM is ', fontFamily: 'serif' }, - { text: 'the product', fontFamily: 'sans', newLine: true }, + { text: msg`When your CRM is`, fontFamily: 'serif' }, + { text: msg`the product`, fontFamily: 'sans', newLine: true }, ], author: 'Amrendra Pratap Singh', authorAvatarSrc: '/images/partner/testimonials/amrendra-singh.webp', - authorRole: 'VP of Engineering, W3villa Technologies', + authorRole: msg`VP of Engineering, W3villa Technologies`, clientIcon: 'w3villa', heroImageSrc: PLACEHOLDER_HERO, - industry: 'EdTech', - kpis: [{ value: 'Zero', label: 'Manual work at core' }], + industry: msg`EdTech`, + kpis: [{ value: msg`Zero`, label: msg`Manual work at core` }], }, sections: [ { type: 'text', - eyebrow: 'W3Grads', + eyebrow: msg`W3Grads`, heading: [ - { text: 'Scale without ', fontFamily: 'serif' }, - { text: 'breaking operations', fontFamily: 'sans' }, + { text: msg`Scale without`, fontFamily: 'serif' }, + { text: msg`breaking operations`, fontFamily: 'sans' }, ], paragraphs: [ - 'Running mock interview programs for hundreds of students sounds straightforward. In practice, universities and training institutes hit the same wall: registrations entered by hand, interview links sent one by one, faculty reviewing every session without scoring or classification. At real scale, it breaks.', - 'W3villa Technologies set out to solve it properly, not with a workaround, but with a product.', + msg`Running mock interview programs for hundreds of students sounds straightforward. In practice, universities and training institutes hit the same wall: registrations entered by hand, interview links sent one by one, faculty reviewing every session without scoring or classification. At real scale, it breaks.`, + msg`W3villa Technologies set out to solve it properly, not with a workaround, but with a product.`, ], callout: '"We did not want to patch over the problem. We wanted to build something institutions could rely on at scale, and that meant starting from a foundation solid enough to support the full complexity of what we had in mind." - Amrendra Pratap Singh, VP of Engineering, W3villa Technologies', }, { type: 'text', - eyebrow: 'Architecture', + eyebrow: msg`Architecture`, heading: [ - { text: 'Focus on the use case, not the ', fontFamily: 'serif' }, - { text: 'plumbing', fontFamily: 'sans' }, + { + text: msg`Focus on the use case, not the`, + fontFamily: 'serif', + }, + { text: msg`plumbing`, fontFamily: 'sans' }, ], paragraphs: [ - 'W3villa built W3Grads (w3grads.com), an AI-powered mock interview platform for universities and training institutes, using Twenty as its operational backbone.', - 'The key decision was not to build everything from scratch. Twenty covers the data model, permissions, authentication, and workflow engine, the parts that would have taken months to rebuild, so the team could focus on product-specific logic.', - 'When a student registers via QR at a campus event, the system assigns a plan, generates an interview session, and sends a link. The AI conducts the interview, scores the candidate, and classifies the result. Faculty see where each student stands without manually reviewing every session. Building and iterating on these workflows was faster with AI in the loop.', + msg`W3villa built W3Grads (w3grads.com), an AI-powered mock interview platform for universities and training institutes, using Twenty as its operational backbone.`, + msg`The key decision was not to build everything from scratch. Twenty covers the data model, permissions, authentication, and workflow engine, the parts that would have taken months to rebuild, so the team could focus on product-specific logic.`, + msg`When a student registers via QR at a campus event, the system assigns a plan, generates an interview session, and sends a link. The AI conducts the interview, scores the candidate, and classifies the result. Faculty see where each student stands without manually reviewing every session. Building and iterating on these workflows was faster with AI in the loop.`, ], }, { type: 'text', - eyebrow: 'Scale', + eyebrow: msg`Scale`, heading: [ - { text: 'A platform ready to ', fontFamily: 'serif' }, - { text: 'grow', fontFamily: 'sans' }, + { + text: msg`A platform ready to`, + fontFamily: 'serif', + }, + { text: msg`grow`, fontFamily: 'sans' }, ], paragraphs: [ - 'Because the foundation is solid, W3Grads is architected for what comes next, including a payment layer for future paid interview plans and nationwide scale without structural rewrites.', + msg`Because the foundation is solid, W3Grads is architected for what comes next, including a payment layer for future paid interview plans and nationwide scale without structural rewrites.`, ], callout: '"Twenty gave us the flexibility to model the entire interview lifecycle as custom objects and workflows. We could build something genuinely complex without fighting the platform to do it." - Piyush Khandelwal, Director, W3villa Technologies, Partner', }, { type: 'text', - eyebrow: 'The result', + eyebrow: msg`The result`, heading: [ - { text: 'Zero manual work', fontFamily: 'sans' }, - { text: ' at the core', fontFamily: 'serif' }, + { text: msg`Zero manual work`, fontFamily: 'sans' }, + { text: msg`at the core`, fontFamily: 'serif' }, ], paragraphs: [ - 'Programs that previously needed heavy manual coordination now run end-to-end with automation. Institutions get a scalable, intelligent system; students get faster preparation for interviews that matter; W3villa shipped a product institutions can build revenue around.', - 'Zero manual work at the core. Full automation. Built on Twenty.', + msg`Programs that previously needed heavy manual coordination now run end-to-end with automation. Institutions get a scalable, intelligent system; students get faster preparation for interviews that matter; W3villa shipped a product institutions can build revenue around.`, + msg`Zero manual work at the core. Full automation. Built on Twenty.`, ], }, ], tableOfContents: [ - 'Scale without breaking operations', - 'Focus on the use case, not the plumbing', - 'A platform ready to grow', - 'The result', + msg`Scale without breaking operations`, + msg`Focus on the use case, not the plumbing`, + msg`A platform ready to grow`, + msg`The result`, ], catalogCard: { - summary: - 'W3villa shipped W3Grads on Twenty for AI interviews, scoring, and institution-scale workflows without rebuilding CRM plumbing.', - date: '2025', + summary: msg`W3villa shipped W3Grads on Twenty for AI interviews, scoring, and institution-scale workflows without rebuilding CRM plumbing.`, + date: msg`2025`, }, }; @@ -105,8 +114,18 @@ export const generateMetadata = buildLocalizedMetadata({ description: CASE_STUDY.meta.description, }); -export default async function W3villaCaseStudyPage() { - const stats = await fetchCommunityStats(); +type CaseStudyPageProps = { + params: Promise; +}; + +export default async function W3villaCaseStudyPage({ + params, +}: CaseStudyPageProps) { + const [i18n, stats] = await Promise.all([ + getRouteI18n(params), + fetchCommunityStats(), + ]); + const renderText = createMessageDescriptorRenderer(i18n); const menuSocialLinks = mergeSocialLinkLabels(MENU_DATA.socialLinks, stats); const palette = getCaseStudyPalette('/customers/w3villa'); @@ -120,6 +139,7 @@ export default async function W3villaCaseStudyPage() { key={index} block={block} isLast={index === CASE_STUDY.sections.length - 1} + renderText={renderText} sectionId={sectionId} /> ); @@ -151,17 +171,19 @@ export default async function W3villaCaseStudyPage() { dashColor={palette.dashColor} hero={CASE_STUDY.hero} hoverDashColor={palette.hoverDashColor} + renderText={renderText} /> {sectionBlocks} - + ); } diff --git a/packages/twenty-website-new/src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx b/packages/twenty-website-new/src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx index f7a0c89a770..27ef2657920 100644 --- a/packages/twenty-website-new/src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +++ b/packages/twenty-website-new/src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx @@ -1,10 +1,12 @@ 'use client'; +import { msg } from '@lingui/core/macro'; import { BaseButton, buttonBaseStyles, } from '@/design-system/components/Button/BaseButton'; import { Body, Heading } from '@/design-system/components'; +import { useRenderMessage } from '@/lib/i18n/use-render-message'; import { useTimeoutRegistry } from '@/lib/react'; import { theme } from '@/theme'; import { css } from '@linaria/core'; @@ -46,6 +48,12 @@ const LicenseeRow = styled.div` gap: ${theme.spacing(1)}; `; +const LicenseeValue = styled.span` + font-family: ${theme.font.family.sans}; + font-size: ${theme.font.size(4)}; + line-height: 1.55; +`; + const KeySection = styled.div` display: flex; flex-direction: column; @@ -132,6 +140,7 @@ const nextStepItemClassName = css` `; export function EnterpriseActivateClient() { + const renderText = useRenderMessage(); const searchParams = useSearchParams(); const sessionId = searchParams.get('session_id'); const timeoutRegistry = useTimeoutRegistry(); @@ -214,7 +223,8 @@ export function EnterpriseActivateClient() { {loading && ( @@ -226,9 +236,10 @@ export function EnterpriseActivateClient() { <> @@ -236,24 +247,27 @@ export function EnterpriseActivateClient() { - + />{' '} + {result.licensee} @@ -270,7 +284,7 @@ export function EnterpriseActivateClient() { > @@ -281,28 +295,32 @@ export function EnterpriseActivateClient() {
  • diff --git a/packages/twenty-website-new/src/app/[locale]/enterprise/activate/page.tsx b/packages/twenty-website-new/src/app/[locale]/enterprise/activate/page.tsx index 058a7047f44..c53a9edad74 100644 --- a/packages/twenty-website-new/src/app/[locale]/enterprise/activate/page.tsx +++ b/packages/twenty-website-new/src/app/[locale]/enterprise/activate/page.tsx @@ -1,9 +1,19 @@ +import { msg } from '@lingui/core/macro'; import { MENU_DATA } from '@/sections/Menu/data'; import { EnterpriseActivateClient } from '@/app/[locale]/enterprise/activate/EnterpriseActivateClient'; -import { Body, Container, Eyebrow } from '@/design-system/components'; -import type { HeadingType } from '@/design-system/components/Heading'; -import { Pages } from '@/lib/pages'; +import { + Body, + Container, + Eyebrow, + HeadingPart, +} from '@/design-system/components'; import { fetchCommunityStats } from '@/lib/community/fetch-community-stats'; +import { createMessageDescriptorRenderer } from '@/lib/i18n/create-message-descriptor-renderer'; +import { + getRouteI18n, + type LocaleRouteParams, +} from '@/lib/i18n/get-route-i18n'; +import { Pages } from '@/lib/pages'; import { mergeSocialLinkLabels } from '@/lib/community/merge-social-link-labels'; import { Hero } from '@/sections/Hero/components'; import { Menu } from '@/sections/Menu/components'; @@ -14,13 +24,8 @@ import { styled } from '@linaria/react'; export const generateMetadata = buildRouteMetadata('enterpriseActivate'); -const ENTERPRISE_ACTIVATE_HEADING: HeadingType[] = [ - { text: 'Enterprise ', fontFamily: 'serif' }, - { text: 'activation', fontFamily: 'sans' }, -]; - const ENTERPRISE_ACTIVATE_BODY = { - text: 'Your checkout is complete. Follow the steps below to copy your license key into your Twenty instance.', + text: msg`Your checkout is complete. Follow the steps below to copy your license key into your Twenty instance.`, }; const ActivatePageContent = styled.section` @@ -38,18 +43,30 @@ const ActivateContentInner = styled.div` width: 100%; `; -function EnterpriseActivateFallback() { +type EnterpriseActivateFallbackProps = { + loadingLabel: string; +}; + +function EnterpriseActivateFallback({ + loadingLabel, +}: EnterpriseActivateFallbackProps) { return ( - + ); } -export default async function EnterpriseActivatePage() { - const stats = await fetchCommunityStats(); +type EnterpriseActivatePageProps = { + params: Promise; +}; + +export default async function EnterpriseActivatePage({ + params, +}: EnterpriseActivatePageProps) { + const [i18n, stats] = await Promise.all([ + getRouteI18n(params), + fetchCommunityStats(), + ]); + const renderText = createMessageDescriptorRenderer(i18n); const menuSocialLinks = mergeSocialLinkLabels(MENU_DATA.socialLinks, stats); return ( @@ -72,19 +89,34 @@ export default async function EnterpriseActivatePage() { > - + + {renderText(msg`Enterprise`)} + {' '} + + {renderText(msg`activation`)} + + + - - }> + + } + > diff --git a/packages/twenty-website-new/src/app/[locale]/layout.tsx b/packages/twenty-website-new/src/app/[locale]/layout.tsx index 5fddedb5759..016dbfc9bf8 100644 --- a/packages/twenty-website-new/src/app/[locale]/layout.tsx +++ b/packages/twenty-website-new/src/app/[locale]/layout.tsx @@ -11,12 +11,14 @@ import { type ReactNode } from 'react'; import { FooterVisibilityGate } from '@/app/_components/FooterVisibilityGate'; import { ScrollToTopOnRouteChange } from '@/app/_components/ScrollToTopOnRouteChange'; import { ContactCalModalRoot } from '@/lib/contact-cal'; +import { createMessageDescriptorRenderer } from '@/lib/i18n/create-message-descriptor-renderer'; import { I18nProvider, PUBLIC_APP_LOCALE_LIST, - getLocaleMessages, resolveLocaleParam, } from '@/lib/i18n'; +import { getLocaleMessages } from '@/lib/i18n/messages-by-locale'; +import { setServerI18n } from '@/lib/i18n/set-server-i18n'; import { PartnerApplicationModalRoot } from '@/lib/partner-application'; import { Footer } from '@/sections/Footer/components'; import { FOOTER_DATA } from '@/sections/Footer/data'; @@ -124,6 +126,8 @@ const LocaleLayout = async ({ }) => { const { locale: rawLocale } = await params; const locale = resolveLocaleParam(rawLocale); + const i18n = setServerI18n(locale); + const renderText = createMessageDescriptorRenderer(i18n); const messages = getLocaleMessages(locale); return ( @@ -147,10 +151,14 @@ const LocaleLayout = async ({ - + diff --git a/packages/twenty-website-new/src/app/[locale]/partners/components/PartnerApplication/BecomePartnerButton.tsx b/packages/twenty-website-new/src/app/[locale]/partners/components/PartnerApplication/BecomePartnerButton.tsx index a7b54b02ba6..c3d7c525db3 100644 --- a/packages/twenty-website-new/src/app/[locale]/partners/components/PartnerApplication/BecomePartnerButton.tsx +++ b/packages/twenty-website-new/src/app/[locale]/partners/components/PartnerApplication/BecomePartnerButton.tsx @@ -4,7 +4,10 @@ import { BaseButton, buttonBaseStyles, } from '@/design-system/components/Button/BaseButton'; +import { useRenderMessage } from '@/lib/i18n/use-render-message'; import { usePartnerApplicationModal } from '@/lib/partner-application'; +import type { MessageDescriptor } from '@lingui/core'; +import { msg } from '@lingui/core/macro'; import { styled } from '@linaria/react'; const StyledTrigger = styled.button` @@ -13,15 +16,16 @@ const StyledTrigger = styled.button` type BecomePartnerButtonProps = { color?: 'primary' | 'secondary'; - label?: string; + label?: MessageDescriptor; variant?: 'contained' | 'outlined'; }; export function BecomePartnerButton({ color = 'secondary', - label = 'Become a partner', + label = msg`Become a partner`, variant = 'contained', }: BecomePartnerButtonProps) { + const renderText = useRenderMessage(); const { openPartnerApplicationModal } = usePartnerApplicationModal(); return ( @@ -33,7 +37,7 @@ export function BecomePartnerButton({ openPartnerApplicationModal(); }} > - + ); } diff --git a/packages/twenty-website-new/src/app/[locale]/partners/components/PartnerApplication/PartnerHeroCtas.tsx b/packages/twenty-website-new/src/app/[locale]/partners/components/PartnerApplication/PartnerHeroCtas.tsx index 83a9a009b56..94b395327af 100644 --- a/packages/twenty-website-new/src/app/[locale]/partners/components/PartnerApplication/PartnerHeroCtas.tsx +++ b/packages/twenty-website-new/src/app/[locale]/partners/components/PartnerApplication/PartnerHeroCtas.tsx @@ -1,6 +1,7 @@ 'use client'; import { TalkToUsButton } from '@/lib/contact-cal'; +import { msg } from '@lingui/core/macro'; import { BecomePartnerButton } from './BecomePartnerButton'; @@ -10,7 +11,7 @@ export function PartnerHeroCtas() { diff --git a/packages/twenty-website-new/src/app/[locale]/partners/components/PartnerApplication/PartnerSignoffCtas.tsx b/packages/twenty-website-new/src/app/[locale]/partners/components/PartnerApplication/PartnerSignoffCtas.tsx index 9382601c2eb..b103637dfe2 100644 --- a/packages/twenty-website-new/src/app/[locale]/partners/components/PartnerApplication/PartnerSignoffCtas.tsx +++ b/packages/twenty-website-new/src/app/[locale]/partners/components/PartnerApplication/PartnerSignoffCtas.tsx @@ -1,6 +1,7 @@ 'use client'; import { TalkToUsButton } from '@/lib/contact-cal'; +import { msg } from '@lingui/core/macro'; import { BecomePartnerButton } from './BecomePartnerButton'; @@ -10,7 +11,7 @@ export function PartnerSignoffCtas() { diff --git a/packages/twenty-website-new/src/app/[locale]/partners/engagement-band.data.ts b/packages/twenty-website-new/src/app/[locale]/partners/engagement-band.data.ts deleted file mode 100644 index 7af2efc3e91..00000000000 --- a/packages/twenty-website-new/src/app/[locale]/partners/engagement-band.data.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { EngagementBandDataType } from '@/sections/EngagementBand/types'; - -export const ENGAGEMENT_BAND_DATA: EngagementBandDataType = { - heading: { - text: 'See what they built', - fontFamily: 'serif', - }, - body: { - text: 'Discover how our partners implement, customize, and scale Twenty in real-world deployments.', - }, -}; diff --git a/packages/twenty-website-new/src/app/[locale]/partners/hero.data.ts b/packages/twenty-website-new/src/app/[locale]/partners/hero.data.ts index 0d82b9f3177..c3b4c098e23 100644 --- a/packages/twenty-website-new/src/app/[locale]/partners/hero.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/partners/hero.data.ts @@ -1,11 +1,5 @@ -import type { HeroBaseDataType } from '@/sections/Hero/types'; +import { msg } from '@lingui/core/macro'; -export const HERO_DATA = { - heading: [ - { text: 'Become ', fontFamily: 'serif' }, - { text: 'our partner', fontFamily: 'sans', newLine: true }, - ], - body: { - text: "We're building the #1 open source CRM, but we can't do it alone. Join our partner ecosystem and grow with us.", - }, -} satisfies HeroBaseDataType; +export const HERO_COPY = { + body: msg`We're building the #1 open source CRM, but we can't do it alone. Join our partner ecosystem and grow with us.`, +}; diff --git a/packages/twenty-website-new/src/app/[locale]/partners/page.tsx b/packages/twenty-website-new/src/app/[locale]/partners/page.tsx index dfaaed3021d..d2e1c695a12 100644 --- a/packages/twenty-website-new/src/app/[locale]/partners/page.tsx +++ b/packages/twenty-website-new/src/app/[locale]/partners/page.tsx @@ -1,19 +1,31 @@ +import { msg } from '@lingui/core/macro'; import { FAQ_DATA } from '@/sections/Faq/data'; import { MENU_DATA } from '@/sections/Menu/data'; import { TRUSTED_BY_DATA } from '@/sections/TrustedBy/data'; import { TalkToUsButton } from '@/lib/contact-cal'; import { CASE_STUDY_CATALOG_ENTRIES } from '@/lib/customers'; import { THREE_CARDS_ILLUSTRATION_DATA } from '@/app/[locale]/partners/three-cards-illustration.data'; -import { HERO_DATA } from '@/app/[locale]/partners/hero.data'; -import { SIGNOFF_DATA } from '@/app/[locale]/partners/signoff.data'; +import { HERO_COPY } from '@/app/[locale]/partners/hero.data'; +import { SIGNOFF_COPY } from '@/app/[locale]/partners/signoff.data'; import { TESTIMONIALS_DATA } from '@/app/[locale]/partners/testimonials.data'; import { PartnerHeroCtas, PartnerSignoffCtas, } from '@/app/[locale]/partners/components/PartnerApplication'; -import { Body, Eyebrow, Heading, LinkButton } from '@/design-system/components'; -import { Pages } from '@/lib/pages'; +import { + Body, + Eyebrow, + Heading, + HeadingPart, + LinkButton, +} from '@/design-system/components'; import { fetchCommunityStats } from '@/lib/community/fetch-community-stats'; +import { createMessageDescriptorRenderer } from '@/lib/i18n/create-message-descriptor-renderer'; +import { + getRouteI18n, + type LocaleRouteParams, +} from '@/lib/i18n/get-route-i18n'; +import { Pages } from '@/lib/pages'; import { mergeSocialLinkLabels } from '@/lib/community/merge-social-link-labels'; import { CaseStudyCatalog } from '@/sections/CaseStudyCatalog/components'; import { Faq } from '@/sections/Faq/components'; @@ -47,8 +59,16 @@ const PromoSpacing = styled.div` export const generateMetadata = buildRouteMetadata('partners'); -export default async function PartnerPage() { - const stats = await fetchCommunityStats(); +type PartnerPageProps = { + params: Promise; +}; + +export default async function PartnerPage({ params }: PartnerPageProps) { + const [i18n, stats] = await Promise.all([ + getRouteI18n(params), + fetchCommunityStats(), + ]); + const renderText = createMessageDescriptorRenderer(i18n); const menuSocialLinks = mergeSocialLinkLabels(MENU_DATA.socialLinks, stats); return ( @@ -66,8 +86,20 @@ export default async function PartnerPage() { - - + + + {renderText(msg`Become`)} + +
    + + {renderText(msg`our partner`)} + +
    + @@ -78,15 +110,22 @@ export default async function PartnerPage() { backgroundColor={theme.colors.primary.background[100]} compactBottom > - + - + @@ -95,13 +134,23 @@ export default async function PartnerPage() { - - + + + {renderText(msg`Find the program that fits your business`)} + {' '} + + {renderText(msg`and unlock new opportunities with Twenty`)} + + + {THREE_CARDS_ILLUSTRATION_DATA.body && ( + + )} - + + {renderText(msg`Ready to grow`)} + +
    + + {renderText(msg`with Twenty?`)} + +
    + - @@ -140,19 +198,30 @@ export default async function PartnerPage() { - - + + + + {renderText(msg`Stop fighting custom.`)} + +
    + + {renderText(msg`Start building, with Twenty`)} + +
    diff --git a/packages/twenty-website-new/src/app/[locale]/partners/signoff.data.ts b/packages/twenty-website-new/src/app/[locale]/partners/signoff.data.ts index 206557a25e9..c35eebf9ca6 100644 --- a/packages/twenty-website-new/src/app/[locale]/partners/signoff.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/partners/signoff.data.ts @@ -1,11 +1,5 @@ -import type { SignoffDataType } from '@/sections/Signoff/types'; +import { msg } from '@lingui/core/macro'; -export const SIGNOFF_DATA: SignoffDataType = { - heading: [ - { text: 'Ready to grow\n', fontFamily: 'serif' }, - { text: 'with Twenty?', fontFamily: 'sans' }, - ], - body: { - text: 'Join our partner ecosystem and help businesses\ntake control of their CRM.', - }, +export const SIGNOFF_COPY = { + body: msg`Join our partner ecosystem and help businesses\ntake control of their CRM.`, }; diff --git a/packages/twenty-website-new/src/app/[locale]/partners/testimonials.data.ts b/packages/twenty-website-new/src/app/[locale]/partners/testimonials.data.ts index 8c53b9379cc..a0d7bd4a469 100644 --- a/packages/twenty-website-new/src/app/[locale]/partners/testimonials.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/partners/testimonials.data.ts @@ -1,18 +1,22 @@ +import { msg } from '@lingui/core/macro'; import type { TestimonialsDataType } from '@/sections/Testimonials/types'; export const TESTIMONIALS_DATA: TestimonialsDataType = { eyebrow: { - heading: { text: 'Join our growing partner ecosystem', fontFamily: 'sans' }, + heading: { + text: msg`Join our growing partner ecosystem`, + fontFamily: 'sans', + }, }, testimonials: [ { heading: { - text: "Twenty gives you the kind of flexibility that actually changes what you can offer your clients. The dev experience is clean, the APIs are open, and when something needs to be customized, you can just do it. There's no fighting the platform.", + text: msg`Twenty gives you the kind of flexibility that actually changes what you can offer your clients. The dev experience is clean, the APIs are open, and when something needs to be customized, you can just do it. There's no fighting the platform.`, fontFamily: 'sans', }, author: { - name: { text: 'Benjamin Reynolds' }, - designation: { text: 'Principal and Founder, Alternative Partners' }, + name: { text: msg`Benjamin Reynolds` }, + designation: { text: msg`Principal and Founder, Alternative Partners` }, avatar: { src: '/images/partner/testimonials/benjamin-reynolds.webp', alt: 'Portrait of Benjamin Reynolds', @@ -21,12 +25,12 @@ export const TESTIMONIALS_DATA: TestimonialsDataType = { }, { heading: { - text: "The flexibility is just amazing. Literally, there's nothing you cannot do. You can create objects, access everything through the API, pull notes and send them to the portal. Try doing that in HubSpot. No way. It's the true ability to build exactly what's actually needed.", + text: msg`The flexibility is just amazing. Literally, there's nothing you cannot do. You can create objects, access everything through the API, pull notes and send them to the portal. Try doing that in HubSpot. No way. It's the true ability to build exactly what's actually needed.`, fontFamily: 'sans', }, author: { - name: { text: 'Bertrams' }, - designation: { text: 'Founder, Wintactix' }, + name: { text: msg`Bertrams` }, + designation: { text: msg`Founder, Wintactix` }, avatar: { src: '/images/partner/testimonials/bertrams.jpeg', alt: 'Portrait of Bertrams', @@ -35,12 +39,12 @@ export const TESTIMONIALS_DATA: TestimonialsDataType = { }, { heading: { - text: "Twenty Apps opens the door to building products, not just implementations. For example, we're developing a WhatsApp Business integration that any Twenty’s client could get. That's a recurring revenue stream we wouldn't have if we were just configuring someone else's platform.", + text: msg`Twenty Apps opens the door to building products, not just implementations. For example, we're developing a WhatsApp Business integration that any Twenty’s client could get. That's a recurring revenue stream we wouldn't have if we were just configuring someone else's platform.`, fontFamily: 'sans', }, author: { - name: { text: 'Mike Babiy' }, - designation: { text: 'Founder, Nine Dots Ventures' }, + name: { text: msg`Mike Babiy` }, + designation: { text: msg`Founder, Nine Dots Ventures` }, avatar: { src: '/images/partner/testimonials/mike-babiy.png', alt: 'Photo featuring Mike Babiy', diff --git a/packages/twenty-website-new/src/app/[locale]/partners/three-cards-illustration.data.ts b/packages/twenty-website-new/src/app/[locale]/partners/three-cards-illustration.data.ts index 5a1c23b9d2c..651202b8767 100644 --- a/packages/twenty-website-new/src/app/[locale]/partners/three-cards-illustration.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/partners/three-cards-illustration.data.ts @@ -1,67 +1,66 @@ +import { msg } from '@lingui/core/macro'; import type { ThreeCardsIllustrationDataType } from '@/sections/ThreeCards/types'; export const THREE_CARDS_ILLUSTRATION_DATA: ThreeCardsIllustrationDataType = { eyebrow: { heading: { - text: 'Which partner program is right for you?', + text: msg`Which partner program is right for you?`, fontFamily: 'sans', }, }, - heading: [ - { text: 'Find the program that fits your business ', fontFamily: 'serif' }, - { text: 'and unlock new opportunities with Twenty', fontFamily: 'sans' }, - ], - body: { text: '' }, illustrationCards: [ { - heading: { text: 'Technology Partners', fontFamily: 'sans' }, + heading: { text: msg`Technology Partners`, fontFamily: 'sans' }, body: { - text: 'Build integrations that connect Twenty with the tools your customers already use. Help us expand the Twenty ecosystem.', + text: msg`Build integrations that connect Twenty with the tools your customers already use. Help us expand the Twenty ecosystem.`, }, benefits: [ - { text: 'Co-marketing opportunities', icon: 'users' }, - { text: 'Listing on Twenty integrations page', icon: 'search' }, - { text: 'Soon: earn revenue', icon: 'tag' }, + { text: msg`Co-marketing opportunities`, icon: 'users' }, + { text: msg`Listing on Twenty integrations page`, icon: 'search' }, + { text: msg`Soon: earn revenue`, icon: 'tag' }, ], action: { kind: 'partnerApplication', - label: 'Become a Technology Partner', + label: msg`Become a Technology Partner`, programId: 'technology', }, attribution: undefined, illustration: 'programming', }, { - heading: { text: 'Content & Community Partners', fontFamily: 'sans' }, + heading: { text: msg`Content & Community Partners`, fontFamily: 'sans' }, body: { - text: "Share Twenty with your audience and help shape the future of the #1 open source CRM. We're looking for creators, educators, and community builders who want to showcase great software.", + text: msg`Share Twenty with your audience and help shape the future of the #1 open source CRM. We're looking for creators, educators, and community builders who want to showcase great software.`, }, benefits: [ - { text: 'Revenue share for referred customers', icon: 'tag' }, - { text: 'Exclusive content collaboration opportunities', icon: 'edit' }, - { text: 'Marketing assets & brand resources', icon: 'book' }, + { text: msg`Revenue share for referred customers`, icon: 'tag' }, + { + text: msg`Exclusive content collaboration opportunities`, + icon: 'edit', + }, + { text: msg`Marketing assets & brand resources`, icon: 'book' }, ], action: { kind: 'partnerApplication', - label: 'Become a Content Partner', + label: msg`Become a Content Partner`, programId: 'content', }, attribution: undefined, illustration: 'connect', }, { - heading: { text: 'Solutions Partners', fontFamily: 'sans' }, + heading: { text: msg`Solutions Partners`, fontFamily: 'sans' }, body: { - text: 'Help customers implement, customize, and succeed with Twenty. Combine sales and services to grow your business.', + text: msg`Help customers implement, customize, and succeed with Twenty. Combine sales and services to grow your business.`, }, benefits: [ - { text: 'Resale discounts & revenue share', icon: 'tag' }, - { text: 'Marketplace listing', icon: 'search' }, - { text: 'Dedicated partner support', icon: 'users' }, + { text: msg`Resale discounts & revenue share`, icon: 'tag' }, + { text: msg`Marketplace listing`, icon: 'search' }, + { text: msg`Dedicated partner support`, icon: 'users' }, ], action: { kind: 'partnerApplication', - label: 'Become a Solution Partner', + label: msg`Become a Solution Partner`, programId: 'solutions', }, attribution: undefined, diff --git a/packages/twenty-website-new/src/app/[locale]/pricing/engagement-band.data.ts b/packages/twenty-website-new/src/app/[locale]/pricing/engagement-band.data.ts index d0a10ead894..b516b399f41 100644 --- a/packages/twenty-website-new/src/app/[locale]/pricing/engagement-band.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/pricing/engagement-band.data.ts @@ -1,11 +1,6 @@ -import type { EngagementBandDataType } from '@/sections/EngagementBand/types'; +import { msg } from '@lingui/core/macro'; -export const ENGAGEMENT_BAND_DATA: EngagementBandDataType = { - heading: { - text: 'Need help with customization?', - fontFamily: 'serif', - }, - body: { - text: 'Find the right partner to implement, customize, and tailor Twenty to your team.', - }, +export const ENGAGEMENT_BAND_COPY = { + body: msg`Find the right partner to implement, customize, and tailor Twenty to your team.`, + heading: msg`Need help with customization?`, }; diff --git a/packages/twenty-website-new/src/app/[locale]/pricing/hero.data.ts b/packages/twenty-website-new/src/app/[locale]/pricing/hero.data.ts index 2cee64b6ee1..be0371d4c8f 100644 --- a/packages/twenty-website-new/src/app/[locale]/pricing/hero.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/pricing/hero.data.ts @@ -1,13 +1,5 @@ -import type { HeroBaseDataType } from '@/sections/Hero/types'; +import { msg } from '@lingui/core/macro'; -const PRICING_HERO_SUBTAGLINE = { - text: 'Start your free trial today\nwithout credit card.', +export const HERO_COPY = { + body: msg`Start your free trial today\nwithout credit card.`, }; - -export const HERO_DATA = { - heading: [ - { text: 'Simple', fontFamily: 'serif' }, - { text: 'Pricing', fontFamily: 'sans', newLine: true }, - ], - body: PRICING_HERO_SUBTAGLINE, -} satisfies HeroBaseDataType; diff --git a/packages/twenty-website-new/src/app/[locale]/pricing/page.tsx b/packages/twenty-website-new/src/app/[locale]/pricing/page.tsx index 2670ee32593..b92096479a3 100644 --- a/packages/twenty-website-new/src/app/[locale]/pricing/page.tsx +++ b/packages/twenty-website-new/src/app/[locale]/pricing/page.tsx @@ -1,14 +1,20 @@ +import { msg } from '@lingui/core/macro'; import { FAQ_DATA } from '@/sections/Faq/data'; import { MENU_DATA } from '@/sections/Menu/data'; import { TalkToUsButton } from '@/lib/contact-cal'; import { BecomePartnerButton } from '@/app/[locale]/partners/components/PartnerApplication'; -import { ENGAGEMENT_BAND_DATA } from '@/app/[locale]/pricing/engagement-band.data'; -import { HERO_DATA } from '@/app/[locale]/pricing/hero.data'; +import { ENGAGEMENT_BAND_COPY } from '@/app/[locale]/pricing/engagement-band.data'; +import { HERO_COPY } from '@/app/[locale]/pricing/hero.data'; import { PLAN_TABLE_DATA } from '@/app/[locale]/pricing/plan-table.data'; import { SALESFORCE_DATA } from '@/app/[locale]/pricing/salesforce.data'; -import { Eyebrow, LinkButton } from '@/design-system/components'; -import { Pages } from '@/lib/pages'; +import { Eyebrow, HeadingPart, LinkButton } from '@/design-system/components'; import { fetchCommunityStats } from '@/lib/community/fetch-community-stats'; +import { createMessageDescriptorRenderer } from '@/lib/i18n/create-message-descriptor-renderer'; +import { + getRouteI18n, + type LocaleRouteParams, +} from '@/lib/i18n/get-route-i18n'; +import { Pages } from '@/lib/pages'; import { mergeSocialLinkLabels } from '@/lib/community/merge-social-link-labels'; import { EngagementBand } from '@/sections/EngagementBand/components'; import { Faq } from '@/sections/Faq/components'; @@ -36,8 +42,16 @@ const PricingBannerContainer = styled.div` export const generateMetadata = buildRouteMetadata('pricing'); -export default async function PricingPage() { - const stats = await fetchCommunityStats(); +type PricingPageProps = { + params: Promise; +}; + +export default async function PricingPage({ params }: PricingPageProps) { + const [i18n, stats] = await Promise.all([ + getRouteI18n(params), + fetchCommunityStats(), + ]); + const renderText = createMessageDescriptorRenderer(i18n); const menuSocialLinks = mergeSocialLinkLabels(MENU_DATA.socialLinks, stats); return ( @@ -55,11 +69,20 @@ export default async function PricingPage() { - + + + {renderText(msg`Simple`)} + +
    + + {renderText(msg`Pricing`)} + +
    @@ -81,14 +104,21 @@ export default async function PricingPage() { > + - @@ -106,25 +136,40 @@ export default async function PricingPage() { + > + + {renderText(msg`Trust the n°1 CRM,`)} + {' '} + {renderText(msg`or not !`)} + - - + + + + {renderText(msg`Stop fighting custom.`)} + +
    + + {renderText(msg`Start building, with Twenty`)} + +
    diff --git a/packages/twenty-website-new/src/app/[locale]/pricing/plan-table.data.ts b/packages/twenty-website-new/src/app/[locale]/pricing/plan-table.data.ts index 19287f9e010..f944671fa66 100644 --- a/packages/twenty-website-new/src/app/[locale]/pricing/plan-table.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/pricing/plan-table.data.ts @@ -1,402 +1,403 @@ +import { msg } from '@lingui/core/macro'; import type { PlanTableDataType } from '@/sections/PlanTable/types'; export const PLAN_TABLE_DATA: PlanTableDataType = { - featureColumnLabel: 'Name', + featureColumnLabel: msg`Name`, rows: [ { - featureLabel: 'Price', + featureLabel: msg`Price`, selfHostTiers: { - organization: { kind: 'text', text: '$19' }, - pro: { kind: 'text', text: '$0' }, + organization: { kind: 'text', text: msg`$19` }, + pro: { kind: 'text', text: msg`$0` }, }, tiers: { - organization: { kind: 'text', text: '$19' }, - pro: { kind: 'text', text: '$9' }, + organization: { kind: 'text', text: msg`$19` }, + pro: { kind: 'text', text: msg`$9` }, }, type: 'row', }, { - featureLabel: 'Seats limit', + featureLabel: msg`Seats limit`, tiers: { - organization: { kind: 'text', text: 'Unlimited' }, - pro: { kind: 'text', text: 'Unlimited' }, + organization: { kind: 'text', text: msg`Unlimited` }, + pro: { kind: 'text', text: msg`Unlimited` }, }, type: 'row', }, { - title: 'Workspace', + title: msg`Workspace`, type: 'category', }, { - featureLabel: 'Custom objects', + featureLabel: msg`Custom objects`, tiers: { - organization: { kind: 'text', text: 'Unlimited' }, - pro: { kind: 'text', text: 'Unlimited' }, + organization: { kind: 'text', text: msg`Unlimited` }, + pro: { kind: 'text', text: msg`Unlimited` }, }, type: 'row', }, { - featureLabel: 'Custom fields', + featureLabel: msg`Custom fields`, tiers: { - organization: { kind: 'text', text: 'Unlimited' }, - pro: { kind: 'text', text: 'Unlimited' }, + organization: { kind: 'text', text: msg`Unlimited` }, + pro: { kind: 'text', text: msg`Unlimited` }, }, type: 'row', }, { - featureLabel: 'Custom views', + featureLabel: msg`Custom views`, tiers: { - organization: { kind: 'text', text: 'Unlimited' }, - pro: { kind: 'text', text: 'Unlimited' }, + organization: { kind: 'text', text: msg`Unlimited` }, + pro: { kind: 'text', text: msg`Unlimited` }, }, type: 'row', }, { - featureLabel: 'View types', + featureLabel: msg`View types`, tiers: { - organization: { kind: 'text', text: 'Table, Kanban, Calendar' }, - pro: { kind: 'text', text: 'Table, Kanban, Calendar' }, + organization: { kind: 'text', text: msg`Table, Kanban, Calendar` }, + pro: { kind: 'text', text: msg`Table, Kanban, Calendar` }, }, type: 'row', }, { - featureLabel: 'Custom layout', + featureLabel: msg`Custom layout`, tiers: { - organization: { kind: 'text', text: 'Unlimited' }, - pro: { kind: 'text', text: 'Unlimited' }, + organization: { kind: 'text', text: msg`Unlimited` }, + pro: { kind: 'text', text: msg`Unlimited` }, }, type: 'row', }, { - featureLabel: 'Records', + featureLabel: msg`Records`, tiers: { - organization: { kind: 'text', text: 'Unlimited' }, - pro: { kind: 'text', text: 'Unlimited' }, + organization: { kind: 'text', text: msg`Unlimited` }, + pro: { kind: 'text', text: msg`Unlimited` }, }, type: 'row', }, { - featureLabel: 'CSV import & export', + featureLabel: msg`CSV import & export`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, - pro: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, + pro: { kind: 'yes', label: msg`Yes` }, }, type: 'row', }, { - featureLabel: 'Languages', + featureLabel: msg`Languages`, tiers: { - organization: { kind: 'text', text: '30+' }, - pro: { kind: 'text', text: '30+' }, + organization: { kind: 'text', text: msg`30+` }, + pro: { kind: 'text', text: msg`30+` }, }, type: 'row', }, { - title: 'Reports', + title: msg`Reports`, type: 'category', }, { - featureLabel: 'Number of dashboards', + featureLabel: msg`Number of dashboards`, tiers: { - organization: { kind: 'text', text: 'Unlimited' }, - pro: { kind: 'text', text: 'Unlimited' }, + organization: { kind: 'text', text: msg`Unlimited` }, + pro: { kind: 'text', text: msg`Unlimited` }, }, type: 'row', }, { - title: 'Emails & Calendar', + title: msg`Emails & Calendar`, type: 'category', }, { - featureLabel: 'Internet accounts per user', + featureLabel: msg`Internet accounts per user`, tiers: { - organization: { kind: 'text', text: 'Unlimited' }, - pro: { kind: 'text', text: 'Unlimited' }, + organization: { kind: 'text', text: msg`Unlimited` }, + pro: { kind: 'text', text: msg`Unlimited` }, }, type: 'row', }, { - featureLabel: 'Folder/Label import', + featureLabel: msg`Folder/Label import`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, - pro: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, + pro: { kind: 'yes', label: msg`Yes` }, }, type: 'row', }, { - featureLabel: 'Email sharing', + featureLabel: msg`Email sharing`, tiers: { - organization: { kind: 'text', text: 'Fully customizable' }, - pro: { kind: 'text', text: 'Fully customizable' }, + organization: { kind: 'text', text: msg`Fully customizable` }, + pro: { kind: 'text', text: msg`Fully customizable` }, }, type: 'row', }, { - title: 'AI & Automations', + title: msg`AI & Automations`, type: 'category', }, { - featureLabel: 'Workflows', + featureLabel: msg`Workflows`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, - pro: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, + pro: { kind: 'yes', label: msg`Yes` }, }, type: 'row', }, { - featureLabel: 'AI agents', + featureLabel: msg`AI agents`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, - pro: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, + pro: { kind: 'yes', label: msg`Yes` }, }, type: 'row', }, { - featureLabel: 'Custom AI models', + featureLabel: msg`Custom AI models`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, pro: { kind: 'dash' }, }, type: 'row', }, { - title: 'Security', + title: msg`Security`, type: 'category', }, { - featureLabel: 'Two-factor authentication', + featureLabel: msg`Two-factor authentication`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, - pro: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, + pro: { kind: 'yes', label: msg`Yes` }, }, type: 'row', }, { - featureLabel: 'User roles', + featureLabel: msg`User roles`, tiers: { - organization: { kind: 'text', text: 'Unlimited' }, - pro: { kind: 'text', text: 'Unlimited' }, + organization: { kind: 'text', text: msg`Unlimited` }, + pro: { kind: 'text', text: msg`Unlimited` }, }, type: 'row', }, { - featureLabel: 'Read/Edit/Delete permissions', + featureLabel: msg`Read/Edit/Delete permissions`, tiers: { - organization: { kind: 'text', text: 'Unlimited' }, - pro: { kind: 'text', text: 'Unlimited' }, + organization: { kind: 'text', text: msg`Unlimited` }, + pro: { kind: 'text', text: msg`Unlimited` }, }, type: 'row', }, { - featureLabel: 'Field-level permissions', + featureLabel: msg`Field-level permissions`, tiers: { - organization: { kind: 'text', text: 'Unlimited' }, - pro: { kind: 'text', text: 'Unlimited' }, + organization: { kind: 'text', text: msg`Unlimited` }, + pro: { kind: 'text', text: msg`Unlimited` }, }, type: 'row', }, { - featureLabel: 'Row-level permissions', + featureLabel: msg`Row-level permissions`, tiers: { - organization: { kind: 'text', text: 'Unlimited' }, + organization: { kind: 'text', text: msg`Unlimited` }, pro: { kind: 'dash' }, }, type: 'row', }, { - featureLabel: 'SSO', + featureLabel: msg`SSO`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, pro: { kind: 'dash' }, }, type: 'row', }, { - featureLabel: 'Advanced Encryption', + featureLabel: msg`Advanced Encryption`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, pro: { kind: 'dash' }, }, type: 'row', }, { - featureLabel: 'Audit logs', + featureLabel: msg`Audit logs`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, pro: { kind: 'dash' }, }, type: 'row', }, { - featureLabel: 'Environments', + featureLabel: msg`Environments`, tiers: { - organization: { kind: 'text', text: 'Local, Production' }, - pro: { kind: 'text', text: 'Local, Production' }, + organization: { kind: 'text', text: msg`Local, Production` }, + pro: { kind: 'text', text: msg`Local, Production` }, }, type: 'row', }, { - featureLabel: 'Impersonate users', + featureLabel: msg`Impersonate users`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, - pro: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, + pro: { kind: 'yes', label: msg`Yes` }, }, type: 'row', }, { - title: 'Support', + title: msg`Support`, type: 'category', }, { - featureLabel: 'Community', + featureLabel: msg`Community`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, - pro: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, + pro: { kind: 'yes', label: msg`Yes` }, }, type: 'row', }, { - featureLabel: 'Help center', + featureLabel: msg`Help center`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, - pro: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, + pro: { kind: 'yes', label: msg`Yes` }, }, type: 'row', }, { - featureLabel: 'Email and Chat', + featureLabel: msg`Email and Chat`, selfHostTiers: { - organization: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, pro: { kind: 'dash' }, }, tiers: { - organization: { kind: 'yes', label: 'Yes' }, - pro: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, + pro: { kind: 'yes', label: msg`Yes` }, }, type: 'row', }, { - featureLabel: 'Priority support', + featureLabel: msg`Priority support`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, pro: { kind: 'dash' }, }, type: 'row', }, { - featureLabel: 'Onboarding Packs', + featureLabel: msg`Onboarding Packs`, selfHostTiers: { - organization: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, pro: { kind: 'dash' }, }, tiers: { - organization: { kind: 'yes', label: 'Yes' }, - pro: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, + pro: { kind: 'yes', label: msg`Yes` }, }, type: 'row', }, { - featureLabel: 'Implementation partners', + featureLabel: msg`Implementation partners`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, pro: { kind: 'dash' }, }, type: 'row', }, { - title: 'Customization', + title: msg`Customization`, type: 'category', }, { - featureLabel: 'Custom apps', + featureLabel: msg`Custom apps`, tiers: { - organization: { kind: 'text', text: 'Unlimited' }, - pro: { kind: 'text', text: 'Unlimited' }, + organization: { kind: 'text', text: msg`Unlimited` }, + pro: { kind: 'text', text: msg`Unlimited` }, }, type: 'row', }, { appliesTo: 'cloud', - featureLabel: 'Subdomain (yourco.twenty.com)', + featureLabel: msg`Subdomain (yourco.twenty.com)`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, - pro: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, + pro: { kind: 'yes', label: msg`Yes` }, }, type: 'row', }, { appliesTo: 'cloud', - featureLabel: 'Custom domain (crm.yourco.com)', + featureLabel: msg`Custom domain (crm.yourco.com)`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, - pro: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, + pro: { kind: 'yes', label: msg`Yes` }, }, type: 'row', }, { - title: 'Developers', + title: msg`Developers`, type: 'category', }, { - featureLabel: 'REST & GraphQL API', + featureLabel: msg`REST & GraphQL API`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, - pro: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, + pro: { kind: 'yes', label: msg`Yes` }, }, type: 'row', }, { - featureLabel: 'Webhooks', + featureLabel: msg`Webhooks`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, - pro: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, + pro: { kind: 'yes', label: msg`Yes` }, }, type: 'row', }, { - featureLabel: 'MCP server', + featureLabel: msg`MCP server`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, - pro: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, + pro: { kind: 'yes', label: msg`Yes` }, }, type: 'row', }, { - featureLabel: 'Install shared tarball app', + featureLabel: msg`Install shared tarball app`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, pro: { kind: 'dash' }, }, type: 'row', }, { appliesTo: 'cloud', - featureLabel: 'API calls', + featureLabel: msg`API calls`, tiers: { - organization: { kind: 'text', text: '200 per minute' }, - pro: { kind: 'text', text: '100 per minute' }, + organization: { kind: 'text', text: msg`200 per minute` }, + pro: { kind: 'text', text: msg`100 per minute` }, }, type: 'row', }, { appliesTo: 'selfHost', - title: 'Self-hosting', + title: msg`Self-hosting`, type: 'category', }, { appliesTo: 'selfHost', - featureLabel: 'Source code access', + featureLabel: msg`Source code access`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, - pro: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, + pro: { kind: 'yes', label: msg`Yes` }, }, type: 'row', }, { appliesTo: 'selfHost', - featureLabel: 'Commercial license (no AGPL obligations)', + featureLabel: msg`Commercial license (no AGPL obligations)`, tiers: { - organization: { kind: 'yes', label: 'Yes' }, + organization: { kind: 'yes', label: msg`Yes` }, pro: { kind: 'dash' }, }, type: 'row', @@ -404,11 +405,11 @@ export const PLAN_TABLE_DATA: PlanTableDataType = { ], initialVisibleRowCount: 15, seeMoreFeaturesCta: { - collapseLabel: 'Show less', - expandLabel: 'See more features', + collapseLabel: msg`Show less`, + expandLabel: msg`See more features`, }, tierColumns: [ - { id: 'pro', label: 'Pro' }, - { id: 'organization', label: 'Organization' }, + { id: 'pro', label: msg`Pro` }, + { id: 'organization', label: msg`Organization` }, ], }; diff --git a/packages/twenty-website-new/src/app/[locale]/pricing/salesforce.data.ts b/packages/twenty-website-new/src/app/[locale]/pricing/salesforce.data.ts index 35c567845c3..0191b7c68fa 100644 --- a/packages/twenty-website-new/src/app/[locale]/pricing/salesforce.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/pricing/salesforce.data.ts @@ -1,51 +1,48 @@ +import { msg } from '@lingui/core/macro'; import type { SalesforceDataType } from '@/sections/Salesforce/types'; -const SALESFORCE_POPUP_TITLE = 'Good choice!'; +const SALESFORCE_POPUP_TITLE = msg`Good choice!`; export const SALESFORCE_DATA: SalesforceDataType = { body: { - text: "Some call this enterprise pricing. We prefer a CRM where API access, webhooks, and workflows don't show up as surprise add-ons.", + text: msg`Some call this enterprise pricing. We prefer a CRM where API access, webhooks, and workflows don't show up as surprise add-ons.`, }, - heading: [ - { text: 'Trust the n°1 CRM,', fontFamily: 'serif' }, - { text: ' or not !', fontFamily: 'sans' }, - ], pricing: { addons: [ { cost: 35, id: 'api-access', - label: 'API access', + label: msg`API access`, popup: { - body: 'APIs are extra. Simplicity has a price.', + body: msg`APIs are extra. Simplicity has a price.`, titleBar: SALESFORCE_POPUP_TITLE, }, - rightLabel: '+$35/user per month', + rightLabel: msg`+$35/user per month`, }, { cost: 0, fixedCost: 7000, id: 'webhooks', - label: 'Webhooks (Change Data Capture)', + label: msg`Webhooks (Change Data Capture)`, popup: { - body: 'Real-time changes? That will be a premium surprise.', + body: msg`Real-time changes? That will be a premium surprise.`, titleBar: SALESFORCE_POPUP_TITLE, }, - rightLabel: '+$7000/org per month', + rightLabel: msg`+$7000/org per month`, }, { cost: 0, disabled: true, id: 'live-updates', - label: 'Live updates', + label: msg`Live updates`, popup: { - body: 'Live updates are unavailable, which is almost more honest.', + body: msg`Live updates are unavailable, which is almost more honest.`, titleBar: SALESFORCE_POPUP_TITLE, }, - rightLabel: 'Unavailable', + rightLabel: msg`Unavailable`, tooltip: { - title: 'Unavailable', - body: 'Real-time is a state of mind, not a feature.', + title: msg`Unavailable`, + body: msg`Real-time is a state of mind, not a feature.`, }, }, { @@ -53,71 +50,71 @@ export const SALESFORCE_DATA: SalesforceDataType = { defaultChecked: true, disabled: true, id: 'ui-theme', - label: 'UI theme', + label: msg`UI theme`, popup: { - body: 'A retro theme as a paid add-on is somehow the most believable part.', + body: msg`A retro theme as a paid add-on is somehow the most believable part.`, titleBar: SALESFORCE_POPUP_TITLE, }, - rightLabel: 'Retro 2015', + rightLabel: msg`Retro 2015`, tooltip: { - title: 'Included!', - body: 'Better than Liquid Glass!', + title: msg`Included!`, + body: msg`Better than Liquid Glass!`, }, }, { cost: 5, id: 'sso', - label: 'SSO', + label: msg`SSO`, popup: { - body: 'Only $5 for SSO. Practically a charity program.', + body: msg`Only $5 for SSO. Practically a charity program.`, titleBar: SALESFORCE_POPUP_TITLE, }, - rightLabel: '+$5/user per month', + rightLabel: msg`+$5/user per month`, }, { cost: 75, id: 'permissions', - label: '11 permissions\ngroups', + label: msg`11 permissions\ngroups`, popup: { - body: 'Experience enterprise-grade granularity, starting with an 11th permission.', + body: msg`Experience enterprise-grade granularity, starting with an 11th permission.`, titleBar: SALESFORCE_POPUP_TITLE, }, - rightLabel: '+$75/user per month\nSwitch to enterprise!', + rightLabel: msg`+$75/user per month\nSwitch to enterprise!`, sharedCostKey: 'enterprise-plan', }, { cost: 105, id: 'maps', - label: 'Maps view', + label: msg`Maps view`, popup: { - body: 'Visualize your customers on a map!', + body: msg`Visualize your customers on a map!`, titleBar: SALESFORCE_POPUP_TITLE, }, - rightLabel: '+$105/user per month', + rightLabel: msg`+$105/user per month`, }, { cost: 75, id: 'workflows', - label: '6 workflows', + label: msg`6 workflows`, popup: { - body: 'Start automating at huge scale!', + body: msg`Start automating at huge scale!`, titleBar: SALESFORCE_POPUP_TITLE, }, - rightLabel: '+$75/user per month\nSwitch to enterprise!', + rightLabel: msg`+$75/user per month\nSwitch to enterprise!`, sharedCostKey: 'enterprise-plan', }, { cost: 0, id: 'lock-in', - label: 'Lock-in', + label: msg`Lock-in`, popup: { - body: 'They call it customer loyalty. We call it a very affectionate cage.', + body: msg`They call it customer loyalty. We call it a very affectionate cage.`, titleBar: SALESFORCE_POPUP_TITLE, }, - rightLabel: '3 2 years contract\n-33% off', + rightLabel: msg`3 2 years contract\n-33% off`, rightLabelParts: [ - [{ strike: true, text: '3' }, { text: ' 2 years contract' }], - [{ text: '-33% off' }], + [{ strike: true, text: msg`3` }, { text: msg`2 years contract` }], + [{ text: msg`-33% off` }], ], }, { @@ -125,30 +122,30 @@ export const SALESFORCE_DATA: SalesforceDataType = { defaultChecked: true, disabled: true, id: 'apex-tutorials', - label: 'APEX tutorials', + label: msg`APEX tutorials`, popup: { - body: 'Even the training material is a feature worth celebrating.', + body: msg`Even the training material is a feature worth celebrating.`, titleBar: SALESFORCE_POPUP_TITLE, }, - rightLabel: 'Free for you!', + rightLabel: msg`Free for you!`, tooltip: { - title: 'Included!', - body: 'Available on YouTube!', + title: msg`Included!`, + body: msg`Available on YouTube!`, }, }, { cost: 0, disabled: true, id: 'self-hosting', - label: 'Self-hosting', + label: msg`Self-hosting`, popup: { - body: 'Owning your stack remains mysteriously out of stock.', + body: msg`Owning your stack remains mysteriously out of stock.`, titleBar: SALESFORCE_POPUP_TITLE, }, - rightLabel: 'Out of stock', + rightLabel: msg`Out of stock`, tooltip: { - title: 'Out of stock', - body: 'Self-hosting, now for rent!', + title: msg`Out of stock`, + body: msg`Self-hosting, now for rent!`, }, }, { @@ -156,81 +153,79 @@ export const SALESFORCE_DATA: SalesforceDataType = { defaultChecked: true, disabled: true, id: 'salesforce-classic', - label: 'Salesforce Classic', + label: msg`Salesforce Classic`, popup: { - body: 'Classic never dies. It just gets extended one more time.', + body: msg`Classic never dies. It just gets extended one more time.`, titleBar: SALESFORCE_POPUP_TITLE, }, - rightLabel: 'Extended run!', + rightLabel: msg`Extended run!`, tooltip: { - title: 'Included!', - body: 'Outlived every redesign since 2004.', + title: msg`Included!`, + body: msg`Outlived every redesign since 2004.`, }, }, { cost: 75, id: 'flow-orchestration', - label: 'Flow\norchestration', + label: msg`Flow\norchestration`, popup: { - body: 'Because true orchestration means putting a dollar sign on every dramatic entrance.', + body: msg`Because true orchestration means putting a dollar sign on every dramatic entrance.`, titleBar: SALESFORCE_POPUP_TITLE, }, - rightLabel: - '$1/orchestration run/org\n+$75/user per month\nSwitch to enterprise!', + rightLabel: msg`$1/orchestration run/org\n+$75/user per month\nSwitch to enterprise!`, sharedCostKey: 'enterprise-plan', }, { cost: 0, disabled: true, id: 'infinite-scroll', - label: 'Infinite scroll', + label: msg`Infinite scroll`, popup: { - body: 'Infinite scroll is still coming soon, unlike the invoice.', + body: msg`Infinite scroll is still coming soon, unlike the invoice.`, titleBar: SALESFORCE_POPUP_TITLE, }, - rightLabel: 'Coming soon!', + rightLabel: msg`Coming soon!`, tooltip: { - title: 'Coming soon!', - body: 'Pagination builds character.', + title: msg`Coming soon!`, + body: msg`Pagination builds character.`, }, }, { cost: 75, id: 'ai-einstein', - label: 'AI (Einstein)', + label: msg`AI (Einstein)`, popup: { - body: 'become a genius!', + body: msg`become a genius!`, titleBar: SALESFORCE_POPUP_TITLE, }, - rightLabel: '+$75/user per month\nSwitch to enterprise!', + rightLabel: msg`+$75/user per month\nSwitch to enterprise!`, sharedCostKey: 'enterprise-plan', }, { cost: 75, id: 'encrypt-data', - label: 'Encrypt your data', + label: msg`Encrypt your data`, netSpendRate: 0.2, popup: { - body: 'Because apparently privacy feels more premium with a surcharge.', + body: msg`Because apparently privacy feels more premium with a surcharge.`, titleBar: SALESFORCE_POPUP_TITLE, }, - rightLabel: - '+20% of net spend\n+$75/user per month\nSwitch to enterprise!', + rightLabel: msg`+20% of net spend\n+$75/user per month\nSwitch to enterprise!`, sharedCostKey: 'enterprise-plan', }, ], basePriceAmount: 100, - promoTag: '1‑800‑YES‑SOFTWARE', - featureSectionHeading: 'Add-ons', + promoTag: msg`1‑800‑YES‑SOFTWARE`, + featureSectionHeading: msg`Add-ons`, productIconAlt: 'Retro help document icon', productIconSrc: '/images/pricing/salesforce/help-icon.webp', - priceSuffix: ' / seat / month - billed yearly', - productTitle: 'Salesfarce Pro', - secondaryCtaNote: 'More options available!', + priceSuffix: msg`/ seat / month - billed yearly`, + productTitle: msg`Salesfarce Pro`, + secondaryCtaNote: msg`More options available!`, secondaryCtaHref: 'https://www.salesforce.com/en-us/wp-content/uploads/sites/4/documents/pricing/all-add-ons.pdf', - secondaryCtaLabel: 'Check more add-ons', - totalPriceLabel: 'total per month with fixed cost', - windowTitle: 'Salesfarce Add-on Center', + secondaryCtaLabel: msg`Check more add-ons`, + totalPriceLabel: msg`total per month with fixed cost`, + windowTitle: msg`Salesfarce Add-on Center`, }, }; diff --git a/packages/twenty-website-new/src/app/[locale]/product/feature.data.ts b/packages/twenty-website-new/src/app/[locale]/product/feature.data.ts index 2439a44922c..b75f5077371 100644 --- a/packages/twenty-website-new/src/app/[locale]/product/feature.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/product/feature.data.ts @@ -1,86 +1,83 @@ +import { msg } from '@lingui/core/macro'; import type { FeatureDataType } from '@/sections/Feature/types'; export const FEATURE_DATA: FeatureDataType = { eyebrow: { heading: { - text: 'Core Features', + text: msg`Core Features`, fontFamily: 'sans', }, }, - heading: [ - { text: 'Everything you need,', fontFamily: 'serif' }, - { text: ' out of the box', fontFamily: 'sans' }, - ], mask: { src: '/images/product/feature/mask.webp', alt: '' }, tiles: [ { icon: 'check', image: { src: '/images/product/feature/dashboards.webp', alt: '' }, - heading: { text: 'Reports & Dashboards', fontFamily: 'sans' }, + heading: { text: msg`Reports & Dashboards`, fontFamily: 'sans' }, bullets: [ - { text: 'Build custom dashboards' }, - { text: 'Aggregate, bar, line, pie, and gauge widgets' }, - { text: 'Filtered metrics from live CRM data' }, + { text: msg`Build custom dashboards` }, + { text: msg`Aggregate, bar, line, pie, and gauge widgets` }, + { text: msg`Filtered metrics from live CRM data` }, ], }, { icon: 'check', image: { src: '/images/product/feature/tasks.webp', alt: '' }, - heading: { text: 'Tasks & Activities', fontFamily: 'sans' }, + heading: { text: msg`Tasks & Activities`, fontFamily: 'sans' }, bullets: [ - { text: 'Create tasks from records' }, - { text: 'Assign owners and due dates' }, - { text: 'Rich notes attached to records' }, + { text: msg`Create tasks from records` }, + { text: msg`Assign owners and due dates` }, + { text: msg`Rich notes attached to records` }, ], }, { icon: 'check', image: { src: '/images/product/feature/emails.webp', alt: '' }, - heading: { text: 'Email & Calendar', fontFamily: 'sans' }, + heading: { text: msg`Email & Calendar`, fontFamily: 'sans' }, bullets: [ - { text: 'Connect Google or Microsoft accounts' }, - { text: 'Emails and events linked to CRM records' }, - { text: 'Full communication history in one place' }, + { text: msg`Connect Google or Microsoft accounts` }, + { text: msg`Emails and events linked to CRM records` }, + { text: msg`Full communication history in one place` }, ], }, { icon: 'check', image: { src: '/images/product/feature/contacts.webp', alt: '' }, - heading: { text: 'Contacts & Companies', fontFamily: 'sans' }, + heading: { text: msg`Contacts & Companies`, fontFamily: 'sans' }, bullets: [ - { text: 'Custom fields and relationships' }, - { text: 'Unified timeline (emails, events, tasks, notes, files)' }, - { text: 'Email/calendar activity on each record' }, + { text: msg`Custom fields and relationships` }, + { text: msg`Unified timeline (emails, events, tasks, notes, files)` }, + { text: msg`Email/calendar activity on each record` }, ], }, { icon: 'check', image: { src: '/images/product/feature/pipeline.webp', alt: '' }, - heading: { text: 'Pipeline Management', fontFamily: 'sans' }, + heading: { text: msg`Pipeline Management`, fontFamily: 'sans' }, bullets: [ - { text: 'Custom deal stages for your process' }, - { text: 'Drag-and-drop deals between stages' }, - { text: 'Track amount and close date' }, + { text: msg`Custom deal stages for your process` }, + { text: msg`Drag-and-drop deals between stages` }, + { text: msg`Track amount and close date` }, ], }, { icon: 'check', image: { src: '/images/product/feature/files.webp', alt: '' }, - heading: { text: 'Files', fontFamily: 'sans' }, + heading: { text: msg`Files`, fontFamily: 'sans' }, bullets: [ - { text: 'Multi-file upload on records' }, - { text: 'Rename, download, and delete attachments' }, - { text: 'In-app preview for supported file types (when enabled)' }, + { text: msg`Multi-file upload on records` }, + { text: msg`Rename, download, and delete attachments` }, + { text: msg`In-app preview for supported file types (when enabled)` }, ], }, { icon: 'check', image: { src: '/images/product/feature/data.webp', alt: '' }, - heading: { text: 'Data import', fontFamily: 'sans' }, + heading: { text: msg`Data import`, fontFamily: 'sans' }, bullets: [ - { text: 'CSV import flow' }, - { text: 'Column-to-field mapping (including relations)' }, - { text: 'CSV export anytime' }, + { text: msg`CSV import flow` }, + { text: msg`Column-to-field mapping (including relations)` }, + { text: msg`CSV export anytime` }, ], }, ], diff --git a/packages/twenty-website-new/src/app/[locale]/product/hero.data.ts b/packages/twenty-website-new/src/app/[locale]/product/hero.data.ts index 6fdd3768e0c..1c2eeedcd17 100644 --- a/packages/twenty-website-new/src/app/[locale]/product/hero.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/product/hero.data.ts @@ -1,12 +1,5 @@ -import type { HeroBaseDataType } from '@/sections/Hero/types'; +import { msg } from '@lingui/core/macro'; -export const HERO_DATA = { - heading: [ - { text: 'A CRM for teams', fontFamily: 'serif' }, - { text: 'that ', fontFamily: 'serif', newLine: true }, - { text: 'moves fast', fontFamily: 'sans' }, - ], - body: { - text: 'Track relationships, manage pipelines, and take action quickly with a CRM that feels intuitive from day one.', - }, -} satisfies HeroBaseDataType; +export const HERO_COPY = { + body: msg`Track relationships, manage pipelines, and take action quickly with a CRM that feels intuitive from day one.`, +}; diff --git a/packages/twenty-website-new/src/app/[locale]/product/page.tsx b/packages/twenty-website-new/src/app/[locale]/product/page.tsx index 343d7619a9f..864850bf81f 100644 --- a/packages/twenty-website-new/src/app/[locale]/product/page.tsx +++ b/packages/twenty-website-new/src/app/[locale]/product/page.tsx @@ -1,15 +1,27 @@ +import { msg } from '@lingui/core/macro'; import { FAQ_DATA } from '@/sections/Faq/data'; import { MENU_DATA } from '@/sections/Menu/data'; import { TRUSTED_BY_DATA } from '@/sections/TrustedBy/data'; import { TalkToUsButton } from '@/lib/contact-cal'; import { FEATURE_DATA } from '@/app/[locale]/product/feature.data'; -import { HERO_DATA } from '@/app/[locale]/product/hero.data'; -import { SIGNOFF_DATA } from '@/app/[locale]/product/signoff.data'; +import { HERO_COPY } from '@/app/[locale]/product/hero.data'; +import { SIGNOFF_COPY } from '@/app/[locale]/product/signoff.data'; import { STEPPER_DATA } from '@/app/[locale]/product/stepper.data'; import { THREE_CARDS_ILLUSTRATION_DATA } from '@/app/[locale]/product/three-cards.data'; -import { Body, Eyebrow, Heading, LinkButton } from '@/design-system/components'; -import { Pages } from '@/lib/pages'; +import { + Body, + Eyebrow, + Heading, + HeadingPart, + LinkButton, +} from '@/design-system/components'; import { fetchCommunityStats } from '@/lib/community/fetch-community-stats'; +import { createMessageDescriptorRenderer } from '@/lib/i18n/create-message-descriptor-renderer'; +import { + getRouteI18n, + type LocaleRouteParams, +} from '@/lib/i18n/get-route-i18n'; +import { Pages } from '@/lib/pages'; import { mergeSocialLinkLabels } from '@/lib/community/merge-social-link-labels'; import { Faq } from '@/sections/Faq/components'; import { Feature } from '@/sections/Feature/components'; @@ -24,8 +36,16 @@ import { buildRouteMetadata } from '@/lib/seo'; export const generateMetadata = buildRouteMetadata('product'); -export default async function ProductPage() { - const stats = await fetchCommunityStats(); +type ProductPageProps = { + params: Promise; +}; + +export default async function ProductPage({ params }: ProductPageProps) { + const [i18n, stats] = await Promise.all([ + getRouteI18n(params), + fetchCommunityStats(), + ]); + const renderText = createMessageDescriptorRenderer(i18n); const menuSocialLinks = mergeSocialLinkLabels(MENU_DATA.socialLinks, stats); return ( @@ -53,14 +73,28 @@ export default async function ProductPage() { - - + + + {renderText(msg`A CRM for teams`)} + +
    + + {renderText(msg`that`)} + {' '} + + {renderText(msg`moves fast`)} + +
    + @@ -68,9 +102,15 @@ export default async function ProductPage() {
    - + - + @@ -78,10 +118,22 @@ export default async function ProductPage() { - + + + {renderText(msg`Everything you need,`)} + {' '} + + {renderText(msg`out of the box`)} + + - + @@ -89,13 +141,23 @@ export default async function ProductPage() { - - + + + {renderText(msg`A modern CRM with`)} + {' '} + + {renderText(msg`an intuitive interface`)} + + + {THREE_CARDS_ILLUSTRATION_DATA.body && ( + + )} + > + + {renderText(msg`Go the extra mile`)} + {' '} + + {renderText(msg`with no-code`)} + + - - + + + {renderText(msg`Ready to grow`)} + +
    + + {renderText(msg`with`)} + {' '} + + {renderText(msg`Twenty?`)} + +
    + @@ -134,19 +216,30 @@ export default async function ProductPage() { - - + + + + {renderText(msg`Stop fighting custom.`)} + +
    + + {renderText(msg`Start building, with Twenty`)} + +
    diff --git a/packages/twenty-website-new/src/app/[locale]/product/signoff.data.ts b/packages/twenty-website-new/src/app/[locale]/product/signoff.data.ts index f670876e9a7..390f828f570 100644 --- a/packages/twenty-website-new/src/app/[locale]/product/signoff.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/product/signoff.data.ts @@ -1,12 +1,5 @@ -import type { SignoffDataType } from '@/sections/Signoff/types'; +import { msg } from '@lingui/core/macro'; -export const SIGNOFF_DATA: SignoffDataType = { - heading: [ - { text: 'Ready to grow', fontFamily: 'serif' }, - { text: 'with ', fontFamily: 'serif', newLine: true }, - { text: 'Twenty?', fontFamily: 'sans' }, - ], - body: { - text: 'Join the teams that chose to own their CRM. Start building with Twenty today.', - }, +export const SIGNOFF_COPY = { + body: msg`Join the teams that chose to own their CRM. Start building with Twenty today.`, }; diff --git a/packages/twenty-website-new/src/app/[locale]/product/stepper.data.ts b/packages/twenty-website-new/src/app/[locale]/product/stepper.data.ts index 048e33f6ad7..cf282a3c2d0 100644 --- a/packages/twenty-website-new/src/app/[locale]/product/stepper.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/product/stepper.data.ts @@ -1,34 +1,25 @@ +import { msg } from '@lingui/core/macro'; import type { ProductStepperDataType } from '@/sections/ProductStepper/types'; export const STEPPER_DATA: ProductStepperDataType = { eyebrow: { heading: { - text: 'Customization', + text: msg`Customization`, fontFamily: 'sans', }, }, - heading: [ - { - text: 'Go the extra mile', - fontFamily: 'serif', - }, - { - text: ' with no-code', - fontFamily: 'sans', - }, - ], body: { - text: 'Need a quick change? Skip the engineering ticket. Customize your workspace in minutes.', + text: msg`Need a quick change? Skip the engineering ticket. Customize your workspace in minutes.`, }, steps: [ { icon: 'users', heading: { - text: 'Data model', + text: msg`Data model`, fontFamily: 'sans', }, body: { - text: 'Add objects and fields', + text: msg`Add objects and fields`, }, image: { src: '/images/product/stepper/step-one.webp', @@ -38,11 +29,11 @@ export const STEPPER_DATA: ProductStepperDataType = { { icon: 'check', heading: { - text: 'Automation', + text: msg`Automation`, fontFamily: 'sans', }, body: { - text: 'Create a workflow', + text: msg`Create a workflow`, }, image: { src: '/images/product/stepper/step-two.webp', @@ -52,11 +43,11 @@ export const STEPPER_DATA: ProductStepperDataType = { { icon: 'eye', heading: { - text: 'Layout', + text: msg`Layout`, fontFamily: 'sans', }, body: { - text: 'Tailor record pages, menus, and views', + text: msg`Tailor record pages, menus, and views`, }, image: { src: '/images/product/stepper/step-three.webp', diff --git a/packages/twenty-website-new/src/app/[locale]/product/three-cards.data.ts b/packages/twenty-website-new/src/app/[locale]/product/three-cards.data.ts index 046e0922a3f..d27dbf21147 100644 --- a/packages/twenty-website-new/src/app/[locale]/product/three-cards.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/product/three-cards.data.ts @@ -1,37 +1,33 @@ +import { msg } from '@lingui/core/macro'; import type { ThreeCardsIllustrationDataType } from '@/sections/ThreeCards/types'; export const THREE_CARDS_ILLUSTRATION_DATA: ThreeCardsIllustrationDataType = { eyebrow: { - heading: { text: 'Stop settling for trade-offs.', fontFamily: 'sans' }, + heading: { text: msg`Stop settling for trade-offs.`, fontFamily: 'sans' }, }, - heading: [ - { text: 'A modern CRM with ', fontFamily: 'serif' }, - { text: 'an intuitive interface', fontFamily: 'sans' }, - ], - body: { text: '' }, illustrationCards: [ { - heading: { text: 'Built for speed', fontFamily: 'sans' }, + heading: { text: msg`Built for speed`, fontFamily: 'sans' }, body: { - text: 'Fly through your workspace with shortcuts and short load times.', + text: msg`Fly through your workspace with shortcuts and short load times.`, }, benefits: undefined, attribution: undefined, illustration: 'speed', }, { - heading: { text: 'Real-time data', fontFamily: 'sans' }, + heading: { text: msg`Real-time data`, fontFamily: 'sans' }, body: { - text: 'See updates as they happen. Work with your team and agents seamlessly.', + text: msg`See updates as they happen. Work with your team and agents seamlessly.`, }, benefits: undefined, attribution: undefined, illustration: 'eye', }, { - heading: { text: 'Stay in Flow', fontFamily: 'sans' }, + heading: { text: msg`Stay in Flow`, fontFamily: 'sans' }, body: { - text: 'AI chat, settings, and records in a side panels for fast, single-screen access.', + text: msg`AI chat, settings, and records in a side panels for fast, single-screen access.`, }, benefits: undefined, attribution: undefined, diff --git a/packages/twenty-website-new/src/app/[locale]/releases/hero.data.ts b/packages/twenty-website-new/src/app/[locale]/releases/hero.data.ts index 3072c831133..a10c4231f4f 100644 --- a/packages/twenty-website-new/src/app/[locale]/releases/hero.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/releases/hero.data.ts @@ -1,11 +1,5 @@ -import type { BodyType } from '@/design-system/components/Body'; -import type { HeadingType } from '@/design-system/components/Heading'; +import { msg } from '@lingui/core/macro'; -export const RELEASE_NOTES_HERO_HEADING: HeadingType[] = [ - { fontFamily: 'serif', text: 'Latest ' }, - { fontFamily: 'sans', text: 'Releases', newLine: true }, -]; - -export const RELEASE_NOTES_HERO_BODY: BodyType = { - text: 'Discover the newest features and improvements in Twenty,\nthe #1 open source CRM.', +export const RELEASE_NOTES_HERO_COPY = { + body: msg`Discover the newest features and improvements in Twenty,\nthe #1 open source CRM.`, }; diff --git a/packages/twenty-website-new/src/app/[locale]/releases/page.tsx b/packages/twenty-website-new/src/app/[locale]/releases/page.tsx index 92481719561..ea0adf2d71c 100644 --- a/packages/twenty-website-new/src/app/[locale]/releases/page.tsx +++ b/packages/twenty-website-new/src/app/[locale]/releases/page.tsx @@ -1,12 +1,15 @@ +import { msg } from '@lingui/core/macro'; import { MENU_DATA } from '@/sections/Menu/data'; -import { - RELEASE_NOTES_HERO_BODY, - RELEASE_NOTES_HERO_HEADING, -} from '@/app/[locale]/releases/hero.data'; -import { LinkButton } from '@/design-system/components'; -import { Pages } from '@/lib/pages'; +import { RELEASE_NOTES_HERO_COPY } from '@/app/[locale]/releases/hero.data'; +import { HeadingPart, LinkButton } from '@/design-system/components'; import { GitHubIcon } from '@/icons'; import { fetchCommunityStats } from '@/lib/community/fetch-community-stats'; +import { createMessageDescriptorRenderer } from '@/lib/i18n/create-message-descriptor-renderer'; +import { + getRouteI18n, + type LocaleRouteParams, +} from '@/lib/i18n/get-route-i18n'; +import { Pages } from '@/lib/pages'; import { mergeSocialLinkLabels } from '@/lib/community/merge-social-link-labels'; import { fetchLatestGithubReleaseTag } from '@/lib/releases/fetch-latest-release-tag'; import { getVisibleReleaseNotes } from '@/lib/releases/get-visible-releases'; @@ -20,12 +23,18 @@ import { Fragment } from 'react'; export const generateMetadata = buildRouteMetadata('releases'); -export default async function ReleasesPage() { +type ReleasesPageProps = { + params: Promise; +}; + +export default async function ReleasesPage({ params }: ReleasesPageProps) { const allNotes = loadLocalReleaseNotes(); - const [latestTag, stats] = await Promise.all([ + const [i18n, latestTag, stats] = await Promise.all([ + getRouteI18n(params), fetchLatestGithubReleaseTag(), fetchCommunityStats(), ]); + const renderText = createMessageDescriptorRenderer(i18n); const menuSocialLinks = mergeSocialLinkLabels(MENU_DATA.socialLinks, stats); const visibleNotes = process.env.NODE_ENV === 'development' @@ -56,24 +65,27 @@ export default async function ReleasesPage() { - + + + {renderText(msg`Latest`)} + +
    + + {renderText(msg`Releases`)} + +
    } - type="anchor" variant="outlined" /> diff --git a/packages/twenty-website-new/src/app/[locale]/why-twenty/editorial-four.data.ts b/packages/twenty-website-new/src/app/[locale]/why-twenty/editorial-four.data.ts index 2de905bad41..b0fd2a50c02 100644 --- a/packages/twenty-website-new/src/app/[locale]/why-twenty/editorial-four.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/why-twenty/editorial-four.data.ts @@ -1,28 +1,19 @@ +import { msg } from '@lingui/core/macro'; import type { EditorialDataType } from '@/sections/Editorial/types/EditorialData'; export const EDITORIAL_FOUR: EditorialDataType = { eyebrow: { heading: { fontFamily: 'sans', - text: 'What this means', + text: msg`What this means`, }, }, - heading: [ - { - fontFamily: 'serif', - text: 'Differentiation now ', - }, - { - fontFamily: 'sans', - text: 'lives in the code you own.', - }, - ], body: [ { - text: "You don't buy your deployment pipeline off the shelf. You don't rent your data warehouse from a vendor who decides the schema. You build it, you own it, you iterate on it every week. CRM is going the same way. The teams that treat it as infrastructure they own will compound an advantage every quarter.", + text: msg`You don't buy your deployment pipeline off the shelf. You don't rent your data warehouse from a vendor who decides the schema. You build it, you own it, you iterate on it every week. CRM is going the same way. The teams that treat it as infrastructure they own will compound an advantage every quarter.`, }, { - text: 'Tuesday your team learns that deals with a technical champion close 3x faster. Wednesday you add the field, wire up the scoring, adjust the workflow. By Thursday your agents are acting on it. That feedback loop is the edge. And it only works if the CRM is yours.', + text: msg`Tuesday your team learns that deals with a technical champion close 3x faster. Wednesday you add the field, wire up the scoring, adjust the workflow. By Thursday your agents are acting on it. That feedback loop is the edge. And it only works if the CRM is yours.`, }, ], }; diff --git a/packages/twenty-website-new/src/app/[locale]/why-twenty/editorial-one.data.ts b/packages/twenty-website-new/src/app/[locale]/why-twenty/editorial-one.data.ts index d7c6276597f..b4f2d4b5b2c 100644 --- a/packages/twenty-website-new/src/app/[locale]/why-twenty/editorial-one.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/why-twenty/editorial-one.data.ts @@ -1,22 +1,19 @@ +import { msg } from '@lingui/core/macro'; import type { EditorialDataType } from '@/sections/Editorial/types/EditorialData'; export const EDITORIAL_ONE: EditorialDataType = { eyebrow: { heading: { fontFamily: 'sans', - text: 'The shift', + text: msg`The shift`, }, }, - heading: [ - { fontFamily: 'serif', text: 'CRM was a ledger.' }, - { fontFamily: 'sans', text: ' AI turned it into an operating system.' }, - ], body: [ { - text: "For twenty years, CRM meant the same thing: a place to log calls, track deals, and pull reports on Friday. The real work happened in people's heads, in Slack threads, in hallway conversations. The CRM kept score. Nobody expected more from it.", + text: msg`For twenty years, CRM meant the same thing: a place to log calls, track deals, and pull reports on Friday. The real work happened in people's heads, in Slack threads, in hallway conversations. The CRM kept score. Nobody expected more from it.`, }, { - text: 'AI agents are starting to draft outreach, score leads, research accounts, write follow-ups, update deal stages. Every one of these actions reads from and writes to the CRM. The scoreboard became the playbook. The database became the brain.', + text: msg`AI agents are starting to draft outreach, score leads, research accounts, write follow-ups, update deal stages. Every one of these actions reads from and writes to the CRM. The scoreboard became the playbook. The database became the brain.`, }, ], }; diff --git a/packages/twenty-website-new/src/app/[locale]/why-twenty/editorial-three.data.ts b/packages/twenty-website-new/src/app/[locale]/why-twenty/editorial-three.data.ts index 3f7a1917edd..7d287dd5201 100644 --- a/packages/twenty-website-new/src/app/[locale]/why-twenty/editorial-three.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/why-twenty/editorial-three.data.ts @@ -1,25 +1,16 @@ +import { msg } from '@lingui/core/macro'; import type { EditorialDataType } from '@/sections/Editorial/types/EditorialData'; export const EDITORIAL_THREE: EditorialDataType = { eyebrow: { - heading: { fontFamily: 'sans', text: 'The opportunity' }, + heading: { fontFamily: 'sans', text: msg`The opportunity` }, }, - heading: [ - { - fontFamily: 'serif', - text: 'Build it in an afternoon.', - }, - { - fontFamily: 'sans', - text: ' AI made the gap that small.', - }, - ], body: [ { - text: 'A year ago, customizing your CRM meant hiring a Salesforce consultant, learning Apex, waiting months. The gap between "I want this" and "it\'s live" was measured in quarters and invoices. So people settled. They bent their process to fit the tool and called it adoption.', + text: msg`A year ago, customizing your CRM meant hiring a Salesforce consultant, learning Apex, waiting months. The gap between "I want this" and "it\'s live" was measured in quarters and invoices. So people settled. They bent their process to fit the tool and called it adoption.`, }, { - text: "Now a developer can describe what they want to Claude Code and have a working app in an afternoon. A custom object, a scoring workflow, a new view, an integration. The bottleneck isn't building anymore. It's whether your platform lets you.", + text: msg`Now a developer can describe what they want to Claude Code and have a working app in an afternoon. A custom object, a scoring workflow, a new view, an integration. The bottleneck isn't building anymore. It's whether your platform lets you.`, }, ], }; diff --git a/packages/twenty-website-new/src/app/[locale]/why-twenty/hero.data.ts b/packages/twenty-website-new/src/app/[locale]/why-twenty/hero.data.ts index fbdda1834c1..4ca1f72b78b 100644 --- a/packages/twenty-website-new/src/app/[locale]/why-twenty/hero.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/why-twenty/hero.data.ts @@ -1,14 +1,5 @@ -import type { HeroWhyTwentyDataType } from '@/sections/Hero/types'; +import { msg } from '@lingui/core/macro'; -export const HERO_DATA: HeroWhyTwentyDataType = { - heading: [ - { text: 'The future of CRM is built,', fontFamily: 'serif' }, - { text: ' not bought.', fontFamily: 'sans' }, - ], - body: { - text: - 'CRM was a database you filled on Fridays. ' + - 'AI turned it into the system that runs your go-to-market. ' + - "To differentiate, you have to build what your competitors can't buy.", - }, +export const HERO_COPY = { + body: msg`CRM was a database you filled on Fridays. AI turned it into the system that runs your go-to-market. To differentiate, you have to build what your competitors can't buy.`, }; diff --git a/packages/twenty-website-new/src/app/[locale]/why-twenty/marquee.data.ts b/packages/twenty-website-new/src/app/[locale]/why-twenty/marquee.data.ts index f02ac704fcf..4f837794903 100644 --- a/packages/twenty-website-new/src/app/[locale]/why-twenty/marquee.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/why-twenty/marquee.data.ts @@ -1,9 +1,10 @@ +import { msg } from '@lingui/core/macro'; import type { MarqueeDataType } from '@/sections/Marquee/types'; export const MARQUEE_DATA: MarqueeDataType = { heading: [ - { fontFamily: 'serif', text: 'Same CRM' }, - { fontFamily: 'sans', text: 'Same output' }, - { fontFamily: 'serif', text: 'Same results' }, + { fontFamily: 'serif', text: msg`Same CRM` }, + { fontFamily: 'sans', text: msg`Same output` }, + { fontFamily: 'serif', text: msg`Same results` }, ], }; diff --git a/packages/twenty-website-new/src/app/[locale]/why-twenty/page.tsx b/packages/twenty-website-new/src/app/[locale]/why-twenty/page.tsx index 069a3e00e65..e46aaa4cd82 100644 --- a/packages/twenty-website-new/src/app/[locale]/why-twenty/page.tsx +++ b/packages/twenty-website-new/src/app/[locale]/why-twenty/page.tsx @@ -1,13 +1,19 @@ +import { msg } from '@lingui/core/macro'; import { MENU_DATA } from '@/sections/Menu/data'; import { EDITORIAL_FOUR } from '@/app/[locale]/why-twenty/editorial-four.data'; import { EDITORIAL_ONE } from '@/app/[locale]/why-twenty/editorial-one.data'; import { EDITORIAL_THREE } from '@/app/[locale]/why-twenty/editorial-three.data'; -import { HERO_DATA } from '@/app/[locale]/why-twenty/hero.data'; +import { HERO_COPY } from '@/app/[locale]/why-twenty/hero.data'; import { MARQUEE_DATA } from '@/app/[locale]/why-twenty/marquee.data'; -import { SIGNOFF_DATA } from '@/app/[locale]/why-twenty/signoff.data'; -import { LinkButton } from '@/design-system/components'; -import { Pages } from '@/lib/pages'; +import { SIGNOFF_COPY } from '@/app/[locale]/why-twenty/signoff.data'; +import { HeadingPart, LinkButton } from '@/design-system/components'; import { fetchCommunityStats } from '@/lib/community/fetch-community-stats'; +import { createMessageDescriptorRenderer } from '@/lib/i18n/create-message-descriptor-renderer'; +import { + getRouteI18n, + type LocaleRouteParams, +} from '@/lib/i18n/get-route-i18n'; +import { Pages } from '@/lib/pages'; import { mergeSocialLinkLabels } from '@/lib/community/merge-social-link-labels'; import { Editorial } from '@/sections/Editorial/components'; import { Hero } from '@/sections/Hero/components'; @@ -59,8 +65,16 @@ const sectionCrosshairRight = { export const generateMetadata = buildRouteMetadata('whyTwenty'); -export default async function WhyTwentyPage() { - const stats = await fetchCommunityStats(); +type WhyTwentyPageProps = { + params: Promise; +}; + +export default async function WhyTwentyPage({ params }: WhyTwentyPageProps) { + const [i18n, stats] = await Promise.all([ + getRouteI18n(params), + fetchCommunityStats(), + ]); + const renderText = createMessageDescriptorRenderer(i18n); const menuSocialLinks = mergeSocialLinkLabels(MENU_DATA.socialLinks, stats); return ( @@ -91,12 +105,19 @@ export default async function WhyTwentyPage() { backgroundColor={theme.colors.secondary.background[100]} colorScheme="secondary" > - + + {renderText(msg`The future of CRM is built,`)} + {' '} + + {renderText(msg`not bought.`)} + + + -
    @@ -109,13 +130,22 @@ export default async function WhyTwentyPage() { - + + + {renderText(msg`CRM was a ledger.`)} + {' '} + + {renderText(msg`AI turned it into an operating system.`)} + + @@ -128,6 +158,7 @@ export default async function WhyTwentyPage() { body={EDITORIAL_TWO.body} color={theme.colors.secondary.text[60]} layout="centered" + renderText={renderText} /> */} @@ -141,13 +172,22 @@ export default async function WhyTwentyPage() { - + + + {renderText(msg`Differentiation now`)} + {' '} + + {renderText(msg`lives in the code you own.`)} + + @@ -160,19 +200,29 @@ export default async function WhyTwentyPage() { - + + + {renderText(msg`Build it in an afternoon.`)} + {' '} + + {renderText(msg`AI made the gap that small.`)} + + @@ -182,17 +232,24 @@ export default async function WhyTwentyPage() { color={theme.colors.secondary.text[100]} page={Pages.WhyTwenty} > - + + {renderText(msg`Build a CRM your competitors`)} + {' '} + + {renderText(msg`can't buy.`)} + + + - diff --git a/packages/twenty-website-new/src/app/[locale]/why-twenty/signoff.data.ts b/packages/twenty-website-new/src/app/[locale]/why-twenty/signoff.data.ts index 052e68e7bc2..7f8efcb4c94 100644 --- a/packages/twenty-website-new/src/app/[locale]/why-twenty/signoff.data.ts +++ b/packages/twenty-website-new/src/app/[locale]/why-twenty/signoff.data.ts @@ -1,11 +1,5 @@ -import type { SignoffDataType } from '@/sections/Signoff/types'; +import { msg } from '@lingui/core/macro'; -export const SIGNOFF_DATA: SignoffDataType = { - heading: [ - { text: 'Build a CRM your competitors ', fontFamily: 'serif' }, - { text: "can't buy.", fontFamily: 'sans' }, - ], - body: { - text: 'Open-source, AI-ready, and yours to shape.', - }, +export const SIGNOFF_COPY = { + body: msg`Open-source, AI-ready, and yours to shape.`, }; diff --git a/packages/twenty-website-new/src/app/__tests__/sitemap.test.ts b/packages/twenty-website-new/src/app/__tests__/sitemap.test.ts index 9ef4b1ab2cd..168b767c370 100644 --- a/packages/twenty-website-new/src/app/__tests__/sitemap.test.ts +++ b/packages/twenty-website-new/src/app/__tests__/sitemap.test.ts @@ -9,8 +9,9 @@ describe('sitemap', () => { expect(pathnames).toContain('/'); expect(pathnames).toContain('/product'); + expect(pathnames).toContain('/fr-FR/product'); expect(pathnames).toContain('/customers/9dots'); - expect(pathnames).not.toContain('/fr-FR/product'); + expect(pathnames).not.toContain('/de-DE/product'); expect(pathnames).not.toContain('/halftone'); expect(pathnames).not.toContain('/enterprise/activate'); }); @@ -22,8 +23,9 @@ describe('sitemap', () => { expect(productEntry?.alternates?.languages).toMatchObject({ en: expect.stringMatching(/\/product$/), + 'fr-FR': expect.stringMatching(/\/fr-FR\/product$/), 'x-default': expect.stringMatching(/\/product$/), }); - expect(productEntry?.alternates?.languages).not.toHaveProperty('fr-FR'); + expect(productEntry?.alternates?.languages).not.toHaveProperty('de-DE'); }); }); diff --git a/packages/twenty-website-new/src/design-system/components/Body.tsx b/packages/twenty-website-new/src/design-system/components/Body.tsx index 8c9d1e2d009..110fae5208e 100644 --- a/packages/twenty-website-new/src/design-system/components/Body.tsx +++ b/packages/twenty-website-new/src/design-system/components/Body.tsx @@ -1,8 +1,9 @@ import { theme } from '@/theme'; import { css } from '@linaria/core'; +import type { ReactNode } from 'react'; -export type BodyType = { - text: string; +export type BodyType = { + text: TText; }; const bodyClassName = css` @@ -80,26 +81,33 @@ export type BodyWeight = 'light' | 'regular' | 'medium'; export type BodySize = 'md' | 'sm' | 'xs'; export type BodyVariant = 'default' | 'body-paragraph'; -export type BodyProps = { +type BodyTextRenderer = [TText] extends [ReactNode] + ? { renderText?: (text: TText) => ReactNode } + : { renderText: (text: TText) => ReactNode }; + +export type BodyProps = { as?: BodyAs; - body: BodyType; + body: BodyType; family?: BodyFamily; weight?: BodyWeight; size?: BodySize; variant?: BodyVariant; className?: string; -}; +} & BodyTextRenderer; -export function Body({ +export function Body({ as: Tag = 'p', body, family = 'sans', + renderText, weight = 'regular', size = 'md', variant = 'default', className, -}: BodyProps) { +}: BodyProps) { const rootClassName = [bodyClassName, className].filter(Boolean).join(' '); + const content = + renderText === undefined ? (body.text as ReactNode) : renderText(body.text); return ( - {body.text} + {content} ); } diff --git a/packages/twenty-website-new/src/design-system/components/Button/BaseButton.tsx b/packages/twenty-website-new/src/design-system/components/Button/BaseButton.tsx index 63788e21c64..9b399a1363a 100644 --- a/packages/twenty-website-new/src/design-system/components/Button/BaseButton.tsx +++ b/packages/twenty-website-new/src/design-system/components/Button/BaseButton.tsx @@ -92,7 +92,7 @@ const Label = styled.span` export type BaseButtonProps = { color: 'primary' | 'secondary'; - label: string; + label: ReactNode; leadingIcon?: ReactNode; size?: ButtonSize; variant: 'contained' | 'outlined'; diff --git a/packages/twenty-website-new/src/design-system/components/Button/LinkButton.tsx b/packages/twenty-website-new/src/design-system/components/Button/LinkButton.tsx index 66690cc0352..b63e33d8fe6 100644 --- a/packages/twenty-website-new/src/design-system/components/Button/LinkButton.tsx +++ b/packages/twenty-website-new/src/design-system/components/Button/LinkButton.tsx @@ -1,5 +1,4 @@ import type { LinkButtonType } from '@/design-system/components/Button/types/LinkButtonType'; -import { LocalizedLink } from '@/lib/i18n'; import { styled } from '@linaria/react'; import { BaseButton, @@ -11,14 +10,7 @@ const StyledButtonAnchor = styled.a` ${buttonBaseStyles} `; -const StyledButtonLink = styled(LocalizedLink)` - ${buttonBaseStyles} -`; - -export type LinkButtonPresentation = 'anchor' | 'link'; - -export type LinkButtonProps = Omit & - LinkButtonType & { type: LinkButtonPresentation }; +export type LinkButtonProps = Omit & LinkButtonType; export function LinkButton({ color, @@ -26,7 +18,6 @@ export function LinkButton({ label, leadingIcon, size = 'regular', - type, variant, }: LinkButtonProps) { const inner = ( @@ -39,29 +30,16 @@ export function LinkButton({ /> ); - if (type === 'anchor') { - return ( - - {inner} - - ); - } - return ( - {inner} - + ); } diff --git a/packages/twenty-website-new/src/design-system/components/Button/types/LinkButtonType.ts b/packages/twenty-website-new/src/design-system/components/Button/types/LinkButtonType.ts index e2d671cf605..1c2a409cd91 100644 --- a/packages/twenty-website-new/src/design-system/components/Button/types/LinkButtonType.ts +++ b/packages/twenty-website-new/src/design-system/components/Button/types/LinkButtonType.ts @@ -1 +1,3 @@ -export type LinkButtonType = { href: string; label: string }; +import type { ReactNode } from 'react'; + +export type LinkButtonType = { href: string; label: ReactNode }; diff --git a/packages/twenty-website-new/src/design-system/components/Button/types/SubmitButtonType.ts b/packages/twenty-website-new/src/design-system/components/Button/types/SubmitButtonType.ts index 4e98c8ecc2e..610819aeaff 100644 --- a/packages/twenty-website-new/src/design-system/components/Button/types/SubmitButtonType.ts +++ b/packages/twenty-website-new/src/design-system/components/Button/types/SubmitButtonType.ts @@ -1 +1,3 @@ -export type SubmitButtonType = { label: string }; +import type { ReactNode } from 'react'; + +export type SubmitButtonType = { label: ReactNode }; diff --git a/packages/twenty-website-new/src/design-system/components/Eyebrow.tsx b/packages/twenty-website-new/src/design-system/components/Eyebrow.tsx index 64e8dfbf902..a6672378415 100644 --- a/packages/twenty-website-new/src/design-system/components/Eyebrow.tsx +++ b/packages/twenty-website-new/src/design-system/components/Eyebrow.tsx @@ -1,10 +1,11 @@ import { Heading, type HeadingType } from '@/design-system/components/Heading'; import { RectangleFillIcon } from '@/icons'; - -export type EyebrowType = { heading: HeadingType }; import { theme } from '@/theme'; import { css } from '@linaria/core'; import { styled } from '@linaria/react'; +import type { ReactNode } from 'react'; + +export type EyebrowType = { heading: HeadingType }; const EyebrowRow = styled.div` align-items: center; @@ -37,22 +38,31 @@ const eyebrowLabelClassName = css` } `; -type EyebrowProps = { - heading: HeadingType; +type EyebrowTextRenderer = [TText] extends [ReactNode] + ? { renderText?: (text: TText) => ReactNode } + : { renderText: (text: TText) => ReactNode }; + +type EyebrowProps = { + heading: HeadingType; colorScheme: 'primary' | 'secondary'; markerHeight?: number; markerWidth?: number; -}; +} & EyebrowTextRenderer; -export function Eyebrow({ +export function Eyebrow({ heading, colorScheme, markerHeight, markerWidth, -}: EyebrowProps) { + renderText, +}: EyebrowProps) { const colorClassName = colorScheme === 'primary' ? eyebrowColorPrimary : eyebrowColorSecondary; const headingClassName = [eyebrowLabelClassName, colorClassName].join(' '); + const headingSegment = { + fontFamily: heading.fontFamily, + text: heading.text, + }; return ( @@ -64,13 +74,24 @@ export function Eyebrow({ width={markerWidth} /> - + {renderText === undefined ? ( + + ) : ( + + as="h3" + className={headingClassName} + renderText={renderText} + segments={headingSegment} + size="xs" + weight="medium" + /> + )} ); } diff --git a/packages/twenty-website-new/src/design-system/components/Heading.tsx b/packages/twenty-website-new/src/design-system/components/Heading.tsx index 70b6bfe9707..29b5177a121 100644 --- a/packages/twenty-website-new/src/design-system/components/Heading.tsx +++ b/packages/twenty-website-new/src/design-system/components/Heading.tsx @@ -1,11 +1,11 @@ import { theme } from '@/theme'; import { css } from '@linaria/core'; import { styled } from '@linaria/react'; -import { Fragment } from 'react'; +import { Fragment, type ReactNode } from 'react'; -export type HeadingType = { +export type HeadingType = { fontFamily: 'sans' | 'serif' | 'mono'; - text: string; + text: TText; fontWeight?: 'light' | 'regular' | 'medium'; newLine?: boolean; lineBreakBefore?: boolean; @@ -108,57 +108,91 @@ const StyledSpan = styled.span` } `; +type HeadingPartProps = { + children: ReactNode; + fontFamily: HeadingFamily; + fontWeight?: HeadingWeight; +}; + +export function HeadingPart({ + children, + fontFamily, + fontWeight, +}: HeadingPartProps) { + return ( + + {children} + + ); +} + export type HeadingAs = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; export type HeadingFamily = 'sans' | 'serif' | 'mono'; export type HeadingWeight = 'light' | 'regular' | 'medium'; export type HeadingSize = 'xl' | 'lg' | 'md' | 'sm' | 'xs'; -export type HeadingProps = { +type HeadingTextRenderer = [TText] extends [ReactNode] + ? { renderText?: (text: TText) => ReactNode } + : { renderText: (text: TText) => ReactNode }; + +export type HeadingProps = { as?: HeadingAs; - segments: HeadingType | HeadingType[]; + children?: ReactNode; + inlineSegmentSeparator?: ReactNode; + segments?: HeadingType | HeadingType[]; weight?: HeadingWeight; size?: HeadingSize; className?: string; -}; +} & HeadingTextRenderer; -export function Heading({ +export function Heading({ as: Tag = 'h1', + children, + inlineSegmentSeparator = ' ', + renderText, segments, weight = 'regular', size = 'md', className, -}: HeadingProps) { +}: HeadingProps) { const rootClassName = [headingRootClassName, className] .filter(Boolean) .join(' '); + const renderSegmentText = (text: TText) => + renderText === undefined ? (text as ReactNode) : renderText(text); return ( - {Array.isArray(segments) ? ( + {children !== undefined ? ( + children + ) : Array.isArray(segments) ? ( segments.map((segment, index) => { const lineBreakBefore = segment.newLine === true || segment.lineBreakBefore === true; + const joinWithPreviousInlineSegment = + index > 0 && lineBreakBefore === false; return ( {lineBreakBefore ?
    : null} + {joinWithPreviousInlineSegment ? inlineSegmentSeparator : null} - {segment.text} + {renderSegmentText(segment.text)}
    ); }) - ) : ( + ) : segments !== undefined ? ( - {segments.text} + {renderSegmentText(segments.text)} - )} + ) : null}
    ); } diff --git a/packages/twenty-website-new/src/design-system/components/IconButton.tsx b/packages/twenty-website-new/src/design-system/components/IconButton.tsx index 38590db7abd..18438593c75 100644 --- a/packages/twenty-website-new/src/design-system/components/IconButton.tsx +++ b/packages/twenty-website-new/src/design-system/components/IconButton.tsx @@ -1,4 +1,3 @@ -import { LocalizedLink } from '@/lib/i18n'; import { theme } from '@/theme'; import { styled } from '@linaria/react'; import type { ComponentType } from 'react'; @@ -15,7 +14,6 @@ interface IconButtonProps { iconSize: number; iconStrokeColor: string; size: number; - href?: string; onClick?: (e: React.MouseEvent) => void; ariaExpanded?: boolean; } @@ -59,14 +57,6 @@ const StyledButton = styled.button` width: ${({ $size }) => `${$size}px`}; `; -const StyledIconLink = styled(LocalizedLink)` - ${iconButtonSurfaceStyles} - border: 1px solid ${({ $borderColor }) => $borderColor}; - color: inherit; - height: ${({ $size }) => `${$size}px`}; - width: ${({ $size }) => `${$size}px`}; -`; - export function IconButton({ icon: Icon, ariaLabel, @@ -75,7 +65,6 @@ export function IconButton({ iconSize, iconStrokeColor, size, - href, onClick, ariaExpanded, }: IconButtonProps) { @@ -88,19 +77,6 @@ export function IconButton({ /> ); - if (href !== undefined && href !== '') { - return ( - - {icon} - - ); - } - return ( - + ); } diff --git a/packages/twenty-website-new/src/lib/customers/case-study-catalog.ts b/packages/twenty-website-new/src/lib/customers/case-study-catalog.ts index 8cb0ec145b5..83eac66d855 100644 --- a/packages/twenty-website-new/src/lib/customers/case-study-catalog.ts +++ b/packages/twenty-website-new/src/lib/customers/case-study-catalog.ts @@ -1,3 +1,4 @@ +import { msg } from '@lingui/core/macro'; import type { CaseStudyCatalogEntry } from '@/lib/customers/types'; import { theme } from '@/theme'; @@ -38,23 +39,23 @@ export function getCaseStudyPalette(href: string) { export const CASE_STUDY_CATALOG_ENTRIES: CaseStudyCatalogEntry[] = [ { href: '/customers/9dots', - industry: 'Real Estate', - authorRole: 'Founder, Nine Dots Ventures', + industry: msg`Real Estate`, + authorRole: msg`Founder, Nine Dots Ventures`, kpis: [ - { value: '150 hrs', label: 'Saved / month' }, - { value: '2,000+', label: 'Daily messages' }, - { value: 'Q1 2026', label: 'Record quarter' }, + { value: msg`150 hrs`, label: msg`Saved / month` }, + { value: msg`2,000+`, label: msg`Daily messages` }, + { value: msg`Q1 2026`, label: msg`Record quarter` }, ], quote: { - text: 'Twenty lets us build a CRM around the business and not the business around the CRM.', + text: msg`Twenty lets us build a CRM around the business and not the business around the CRM.`, author: 'Mike Babiy', - role: 'Founder, Nine Dots Ventures', + role: msg`Founder, Nine Dots Ventures`, }, hero: { readingTime: '9 min', title: [ - { text: 'A real estate agency on WhatsApp ', fontFamily: 'serif' }, - { text: 'built a CRM around it', fontFamily: 'sans' }, + { text: msg`A real estate agency on WhatsApp`, fontFamily: 'serif' }, + { text: msg`built a CRM around it`, fontFamily: 'sans' }, ], author: 'Mike Babiy & Azmat Parveen', authorAvatarSrc: '/images/partner/testimonials/mike-babiy.png', @@ -62,26 +63,25 @@ export const CASE_STUDY_CATALOG_ENTRIES: CaseStudyCatalogEntry[] = [ heroImageSrc: PLACEHOLDER_HERO, }, catalogCard: { - summary: - "Nine Dots put Twenty at the center of Homeseller's stack with APIs, automation, and AI on top of WhatsApp-heavy operations.", - date: 'Jul 2025', + summary: msg`Nine Dots put Twenty at the center of Homeseller's stack with APIs, automation, and AI on top of WhatsApp-heavy operations.`, + date: msg`Jul 2025`, coverImageSrc: 'https://images.unsplash.com/photo-1733244766159-f58f4184fd38?w=1600&q=80', }, }, { href: '/customers/alternative-partners', - industry: 'Consulting', - authorRole: 'Principal and Founder, Alternative Partners', + industry: msg`Consulting`, + authorRole: msg`Principal and Founder, Alternative Partners`, kpis: [ - { value: 'AI-assisted', label: 'Salesforce migration' }, - { value: 'Self-hosted', label: 'Full ownership' }, + { value: msg`AI-assisted`, label: msg`Salesforce migration` }, + { value: msg`Self-hosted`, label: msg`Full ownership` }, ], hero: { readingTime: '7 min', title: [ - { text: 'From Salesforce to ', fontFamily: 'serif' }, - { text: 'self-hosted Twenty', fontFamily: 'sans' }, + { text: msg`From Salesforce to`, fontFamily: 'serif' }, + { text: msg`self-hosted Twenty`, fontFamily: 'sans' }, ], author: 'Benjamin Reynolds', authorAvatarSrc: '/images/partner/testimonials/benjamin-reynolds.webp', @@ -89,26 +89,25 @@ export const CASE_STUDY_CATALOG_ENTRIES: CaseStudyCatalogEntry[] = [ heroImageSrc: PLACEHOLDER_HERO, }, catalogCard: { - summary: - 'Alternative Partners replaced Salesforce with self-hosted Twenty, using agentic AI to compress migration work.', - date: '2025', + summary: msg`Alternative Partners replaced Salesforce with self-hosted Twenty, using agentic AI to compress migration work.`, + date: msg`2025`, coverImageSrc: 'https://images.unsplash.com/photo-1702047149248-a6049168d2a8?w=1600&q=80', }, }, { href: '/customers/netzero', - industry: 'Agribusiness', - authorRole: 'Co-founder, NetZero', + industry: msg`Agribusiness`, + authorRole: msg`Co-founder, NetZero`, kpis: [ - { value: '3 product lines', label: 'On a single CRM' }, - { value: 'No-code', label: 'Customizations' }, + { value: msg`3 product lines`, label: msg`On a single CRM` }, + { value: msg`No-code`, label: msg`Customizations` }, ], hero: { readingTime: '8 min', title: [ - { text: 'A CRM that ', fontFamily: 'serif' }, - { text: 'grows with you', fontFamily: 'sans' }, + { text: msg`A CRM that`, fontFamily: 'serif' }, + { text: msg`grows with you`, fontFamily: 'sans' }, ], author: 'Olivier Reinaud', authorAvatarSrc: '/images/partner/testimonials/olivier-reinaud.jpg', @@ -116,23 +115,22 @@ export const CASE_STUDY_CATALOG_ENTRIES: CaseStudyCatalogEntry[] = [ heroImageSrc: PLACEHOLDER_HERO, }, catalogCard: { - summary: - 'NetZero uses Twenty as a modular CRM across product lines and countries, with a roadmap into AI-assisted workflows.', - date: '2025', + summary: msg`NetZero uses Twenty as a modular CRM across product lines and countries, with a roadmap into AI-assisted workflows.`, + date: msg`2025`, coverImageSrc: 'https://images.unsplash.com/photo-1744830343976-ce690ba2a67c?w=1600&q=80', }, }, { href: '/customers/act-education', - industry: 'Education', - authorRole: 'CRM Engineer, AC&T Education Migration', - kpis: [{ value: '90%+', label: 'Lower CRM cost' }], + industry: msg`Education`, + authorRole: msg`CRM Engineer, AC&T Education Migration`, + kpis: [{ value: msg`90%+`, label: msg`Lower CRM cost` }], hero: { readingTime: '7 min', title: [ - { text: 'A CRM they ', fontFamily: 'serif' }, - { text: 'actually own', fontFamily: 'sans' }, + { text: msg`A CRM they`, fontFamily: 'serif' }, + { text: msg`actually own`, fontFamily: 'sans' }, ], author: 'Joseph Chiang', authorAvatarSrc: '/images/partner/testimonials/joseph-chiang.jpg', @@ -140,23 +138,22 @@ export const CASE_STUDY_CATALOG_ENTRIES: CaseStudyCatalogEntry[] = [ heroImageSrc: PLACEHOLDER_HERO, }, catalogCard: { - summary: - 'AC&T and Flycoder moved from a dead vendor export to self-hosted Twenty, with over 90% lower CRM cost and full control.', - date: '2025', + summary: msg`AC&T and Flycoder moved from a dead vendor export to self-hosted Twenty, with over 90% lower CRM cost and full control.`, + date: msg`2025`, coverImageSrc: 'https://images.unsplash.com/photo-1687600154329-150952c73169?w=1600&q=80', }, }, { href: '/customers/w3villa', - industry: 'EdTech', - authorRole: 'VP of Engineering, W3villa Technologies', - kpis: [{ value: 'Zero', label: 'Manual work at core' }], + industry: msg`EdTech`, + authorRole: msg`VP of Engineering, W3villa Technologies`, + kpis: [{ value: msg`Zero`, label: msg`Manual work at core` }], hero: { readingTime: '8 min', title: [ - { text: 'When your CRM ', fontFamily: 'serif' }, - { text: 'is the product', fontFamily: 'sans' }, + { text: msg`When your CRM`, fontFamily: 'serif' }, + { text: msg`is the product`, fontFamily: 'sans' }, ], author: 'Amrendra Pratap Singh', authorAvatarSrc: '/images/partner/testimonials/amrendra-singh.webp', @@ -164,41 +161,39 @@ export const CASE_STUDY_CATALOG_ENTRIES: CaseStudyCatalogEntry[] = [ heroImageSrc: PLACEHOLDER_HERO, }, catalogCard: { - summary: - 'W3villa shipped W3Grads on Twenty for AI interviews, scoring, and institution-scale workflows without rebuilding CRM plumbing.', - date: '2025', + summary: msg`W3villa shipped W3Grads on Twenty for AI interviews, scoring, and institution-scale workflows without rebuilding CRM plumbing.`, + date: msg`2025`, coverImageSrc: 'https://images.unsplash.com/photo-1756830231350-3b501f63c5c1?w=1600&q=80', }, }, { href: '/customers/elevate-consulting', - industry: 'Management Consulting', - authorRole: 'Director of Digital and Information, Elevate Consulting', + industry: msg`Management Consulting`, + authorRole: msg`Director of Digital and Information, Elevate Consulting`, kpis: [ - { value: '1 click', label: 'Proposal automation' }, - { value: '4 tools', label: 'Connected via API' }, - { value: 'API-first', label: 'Tool integration' }, + { value: msg`1 click`, label: msg`Proposal automation` }, + { value: msg`4 tools`, label: msg`Connected via API` }, + { value: msg`API-first`, label: msg`Tool integration` }, ], quote: { - text: 'It is just such a nicer experience than dealing with a Salesforce or a HubSpot. My mission has been to get every tool API-accessible, so everything talks to each other.', + text: msg`It is just such a nicer experience than dealing with a Salesforce or a HubSpot. My mission has been to get every tool API-accessible, so everything talks to each other.`, author: 'Justin Beadle', - role: 'Director of Digital and Information, Elevate Consulting', + role: msg`Director of Digital and Information, Elevate Consulting`, }, hero: { readingTime: '8 min', title: [ - { text: 'Twenty as the API backbone ', fontFamily: 'serif' }, - { text: 'of a go-to-market stack', fontFamily: 'sans' }, + { text: msg`Twenty as the API backbone`, fontFamily: 'serif' }, + { text: msg`of a go-to-market stack`, fontFamily: 'sans' }, ], author: 'Justin Beadle', clientIcon: 'elevate-consulting', heroImageSrc: PLACEHOLDER_HERO, }, catalogCard: { - summary: - 'Elevate Consulting uses Twenty as the API backbone connecting billing, Teams, resourcing, and a custom front end around client and opportunity data.', - date: 'Jun 2025', + summary: msg`Elevate Consulting uses Twenty as the API backbone connecting billing, Teams, resourcing, and a custom front end around client and opportunity data.`, + date: msg`Jun 2025`, coverImageSrc: 'https://images.unsplash.com/photo-1758873269035-aae0e1fd3422?w=1600&q=80', }, diff --git a/packages/twenty-website-new/src/lib/customers/types.ts b/packages/twenty-website-new/src/lib/customers/types.ts index e102fc63e3e..5b71853a642 100644 --- a/packages/twenty-website-new/src/lib/customers/types.ts +++ b/packages/twenty-website-new/src/lib/customers/types.ts @@ -1,10 +1,11 @@ -import type { HeadingType } from '@/design-system/components/Heading'; +import type { MessageHeadingSegment } from '@/lib/i18n/message-heading-segment'; +import type { MessageDescriptor } from '@lingui/core'; export type CaseStudyTextBlock = { type: 'text'; - eyebrow?: string; - heading: HeadingType[]; - paragraphs: string[]; + eyebrow?: MessageDescriptor; + heading: MessageHeadingSegment[]; + paragraphs: MessageDescriptor[]; callout?: string; }; @@ -17,44 +18,44 @@ export type CaseStudyVisualBlock = { export type CaseStudyContentBlock = CaseStudyTextBlock | CaseStudyVisualBlock; export type CaseStudyKpi = { - value: string; - label: string; + value: MessageDescriptor; + label: MessageDescriptor; }; export type CaseStudyQuote = { - text: string; + text: MessageDescriptor; author: string; - role: string; + role: MessageDescriptor; }; export type CaseStudyData = { - meta: { title: string; description: string }; + meta: { title: MessageDescriptor; description: MessageDescriptor }; hero: { readingTime: string; - title: HeadingType[]; + title: MessageHeadingSegment[]; author: string; authorAvatarSrc?: string; clientIcon: string; heroImageSrc: string; - industry?: string; - authorRole?: string; + industry?: MessageDescriptor; + authorRole?: MessageDescriptor; kpis?: CaseStudyKpi[]; quote?: CaseStudyQuote; }; sections: CaseStudyContentBlock[]; - tableOfContents: string[]; + tableOfContents: MessageDescriptor[]; catalogCard: { - summary: string; - date: string; + summary: MessageDescriptor; + date: MessageDescriptor; coverImageSrc?: string; }; }; export type CaseStudyCatalogEntry = { href: string; - industry: string; + industry: MessageDescriptor; kpis: CaseStudyKpi[]; - authorRole: string; + authorRole: MessageDescriptor; quote?: CaseStudyQuote; hero: CaseStudyData['hero']; catalogCard: CaseStudyData['catalogCard']; diff --git a/packages/twenty-website-new/src/lib/i18n/LocalizedLinkButton.tsx b/packages/twenty-website-new/src/lib/i18n/LocalizedLinkButton.tsx new file mode 100644 index 00000000000..22f1753df95 --- /dev/null +++ b/packages/twenty-website-new/src/lib/i18n/LocalizedLinkButton.tsx @@ -0,0 +1,44 @@ +'use client'; + +import { + BaseButton, + type BaseButtonProps, + buttonBaseStyles, +} from '@/design-system/components/Button/BaseButton'; +import type { LinkButtonType } from '@/design-system/components/Button/types/LinkButtonType'; +import { styled } from '@linaria/react'; + +import { LocalizedLink } from './LocalizedLink'; + +const StyledButtonLink = styled(LocalizedLink)` + ${buttonBaseStyles} +`; + +export type LocalizedLinkButtonProps = Omit & + LinkButtonType; + +export function LocalizedLinkButton({ + color, + href, + label, + leadingIcon, + size = 'regular', + variant, +}: LocalizedLinkButtonProps) { + return ( + + + + ); +} diff --git a/packages/twenty-website-new/src/lib/i18n/__tests__/detect-locale.test.ts b/packages/twenty-website-new/src/lib/i18n/__tests__/detect-locale.test.ts index 0385ae71ad4..7224111afad 100644 --- a/packages/twenty-website-new/src/lib/i18n/__tests__/detect-locale.test.ts +++ b/packages/twenty-website-new/src/lib/i18n/__tests__/detect-locale.test.ts @@ -24,15 +24,15 @@ describe('detectLocale', () => { detectLocale({ acceptLanguageHeader: 'en;q=0.5,fr-FR;q=0.9,de;q=0.7', }), - ).toBe('en'); + ).toBe('fr-FR'); }); - it('falls back to the source locale when a regional language is not published', () => { + it('matches a regional language to a published locale with the same language subtag', () => { expect( detectLocale({ acceptLanguageHeader: 'fr-CA', }), - ).toBe('en'); + ).toBe('fr-FR'); }); it('falls back to the source locale when a bare language is not published', () => { @@ -68,6 +68,6 @@ describe('detectLocale', () => { detectLocale({ acceptLanguageHeader: 'en;q=0.1, fr-FR ; q=0.9, de-DE ; q=0.5', }), - ).toBe('en'); + ).toBe('fr-FR'); }); }); diff --git a/packages/twenty-website-new/src/lib/i18n/__tests__/localize-href.test.ts b/packages/twenty-website-new/src/lib/i18n/__tests__/localize-href.test.ts index 6ba2cf03477..a47cea35aa9 100644 --- a/packages/twenty-website-new/src/lib/i18n/__tests__/localize-href.test.ts +++ b/packages/twenty-website-new/src/lib/i18n/__tests__/localize-href.test.ts @@ -2,17 +2,21 @@ import { localizeHref, stripLocale } from '../localize-href'; describe('localizeHref', () => { it('does not emit locale prefixes for locales the website does not publish yet', () => { - expect(localizeHref('fr-FR', '/pricing')).toBe('/pricing'); expect(localizeHref('de-DE', '/pricing')).toBe('/pricing'); }); + it('emits locale prefixes for published non-default locales', () => { + expect(localizeHref('fr-FR', '/pricing')).toBe('/fr-FR/pricing'); + expect(localizeHref('fr-FR', '/')).toBe('/fr-FR'); + }); + it('returns paths unprefixed for the default locale (English at root)', () => { expect(localizeHref('en', '/pricing')).toBe('/pricing'); expect(localizeHref('en', '/')).toBe('/'); }); it('keeps the root path unprefixed for unpublished locales', () => { - expect(localizeHref('fr-FR', '/')).toBe('/'); + expect(localizeHref('de-DE', '/')).toBe('/'); }); it('preserves query strings and hash fragments', () => { @@ -25,8 +29,8 @@ describe('localizeHref', () => { }); it('strips legacy locale prefixes when targeting an unpublished locale', () => { - expect(localizeHref('fr-FR', '/de-DE/why-twenty')).toBe('/why-twenty'); - expect(localizeHref('fr-FR', '/fr-FR/pricing')).toBe('/pricing'); + expect(localizeHref('de-DE', '/fr-FR/why-twenty')).toBe('/why-twenty'); + expect(localizeHref('de-DE', '/de-DE/pricing')).toBe('/pricing'); }); it('strips a redundant /en prefix when targeting the default locale', () => { @@ -35,8 +39,8 @@ describe('localizeHref', () => { }); it('strips an /en-prefixed path when targeting an unpublished locale', () => { - expect(localizeHref('fr-FR', '/en/why-twenty')).toBe('/why-twenty'); - expect(localizeHref('fr-FR', '/en')).toBe('/'); + expect(localizeHref('de-DE', '/en/why-twenty')).toBe('/why-twenty'); + expect(localizeHref('de-DE', '/en')).toBe('/'); }); it('passes external https URLs through unchanged', () => { @@ -66,14 +70,14 @@ describe('localizeHref', () => { it('handles a locale segment immediately followed by a query string', () => { expect(localizeHref('en', '/en?ref=hero')).toBe('/?ref=hero'); - expect(localizeHref('fr-FR', '/en?ref=hero')).toBe('/?ref=hero'); - expect(localizeHref('fr-FR', '/de-DE?ref=hero')).toBe('/?ref=hero'); + expect(localizeHref('de-DE', '/en?ref=hero')).toBe('/?ref=hero'); + expect(localizeHref('de-DE', '/fr-FR?ref=hero')).toBe('/?ref=hero'); }); it('handles a locale segment immediately followed by a hash fragment', () => { expect(localizeHref('en', '/en#anchor')).toBe('/#anchor'); - expect(localizeHref('fr-FR', '/en#anchor')).toBe('/#anchor'); - expect(localizeHref('fr-FR', '/de-DE#anchor')).toBe('/#anchor'); + expect(localizeHref('de-DE', '/en#anchor')).toBe('/#anchor'); + expect(localizeHref('de-DE', '/fr-FR#anchor')).toBe('/#anchor'); }); }); diff --git a/packages/twenty-website-new/src/lib/i18n/app-locale-set.ts b/packages/twenty-website-new/src/lib/i18n/app-locale-set.ts index 7e858b07128..1aa554a97ce 100644 --- a/packages/twenty-website-new/src/lib/i18n/app-locale-set.ts +++ b/packages/twenty-website-new/src/lib/i18n/app-locale-set.ts @@ -1,8 +1,8 @@ -import { - APP_LOCALES, - SOURCE_LOCALE, - type AppLocale, -} from 'twenty-shared/translations'; +import { APP_LOCALES, type AppLocale } from 'twenty-shared/translations'; + +import { WEBSITE_LOCALE_LIST } from './website-locale-list'; + +export { WEBSITE_LOCALE_LIST } from './website-locale-list'; const APP_LOCALE_VALUES: readonly AppLocale[] = Object.values(APP_LOCALES); @@ -12,8 +12,6 @@ const isKnownPublicLocale = (locale: AppLocale): boolean => export const KNOWN_PUBLIC_APP_LOCALE_LIST: readonly AppLocale[] = APP_LOCALE_VALUES.filter(isKnownPublicLocale); -export const WEBSITE_LOCALE_LIST: readonly AppLocale[] = [SOURCE_LOCALE]; - const WEBSITE_LOCALE_SET: ReadonlySet = new Set(WEBSITE_LOCALE_LIST); export const isPublicAppLocale = (locale: AppLocale): boolean => diff --git a/packages/twenty-website-new/src/lib/i18n/create-message-descriptor-renderer.ts b/packages/twenty-website-new/src/lib/i18n/create-message-descriptor-renderer.ts new file mode 100644 index 00000000000..3f10838227c --- /dev/null +++ b/packages/twenty-website-new/src/lib/i18n/create-message-descriptor-renderer.ts @@ -0,0 +1,6 @@ +import type { I18n, MessageDescriptor } from '@lingui/core'; + +export const createMessageDescriptorRenderer = + (i18n: I18n) => + (descriptor: MessageDescriptor): string => + i18n._(descriptor); diff --git a/packages/twenty-website-new/src/lib/i18n/detect-locale.ts b/packages/twenty-website-new/src/lib/i18n/detect-locale.ts index df20fb13228..8ca333ea7b5 100644 --- a/packages/twenty-website-new/src/lib/i18n/detect-locale.ts +++ b/packages/twenty-website-new/src/lib/i18n/detect-locale.ts @@ -48,7 +48,8 @@ export const detectLocale = ({ } if (acceptLanguageHeader !== undefined && acceptLanguageHeader.length > 0) { - for (const { tag } of parseAcceptLanguage(acceptLanguageHeader)) { + for (const { tag, quality } of parseAcceptLanguage(acceptLanguageHeader)) { + if (quality <= 0) continue; const match = matchTag(tag); if (match !== undefined) return match; } diff --git a/packages/twenty-website-new/src/lib/i18n/get-message-descriptor-source.ts b/packages/twenty-website-new/src/lib/i18n/get-message-descriptor-source.ts new file mode 100644 index 00000000000..e800a65f799 --- /dev/null +++ b/packages/twenty-website-new/src/lib/i18n/get-message-descriptor-source.ts @@ -0,0 +1,5 @@ +import type { MessageDescriptor } from '@lingui/core'; + +export const getMessageDescriptorSource = ( + descriptor: MessageDescriptor, +): string => descriptor.message ?? descriptor.id; diff --git a/packages/twenty-website-new/src/lib/i18n/get-route-i18n.ts b/packages/twenty-website-new/src/lib/i18n/get-route-i18n.ts new file mode 100644 index 00000000000..cdbd68ce5f9 --- /dev/null +++ b/packages/twenty-website-new/src/lib/i18n/get-route-i18n.ts @@ -0,0 +1,17 @@ +import type { AppLocale } from 'twenty-shared/translations'; + +import { createI18nInstance } from './create-i18n-instance'; +import { resolveLocaleParam } from './resolve-locale-param'; + +export type LocaleRouteParams = { + locale: string; +}; + +export const getRouteI18n = async ( + params: Promise, +): Promise> => { + const { locale: rawLocale } = await params; + const locale: AppLocale = resolveLocaleParam(rawLocale); + + return createI18nInstance(locale); +}; diff --git a/packages/twenty-website-new/src/lib/i18n/index.ts b/packages/twenty-website-new/src/lib/i18n/index.ts index 7b34b19dd20..355f93fca2c 100644 --- a/packages/twenty-website-new/src/lib/i18n/index.ts +++ b/packages/twenty-website-new/src/lib/i18n/index.ts @@ -9,12 +9,15 @@ export { isPublicAppLocale, } from './app-locale-set'; export { createI18nInstance } from './create-i18n-instance'; +export { createMessageDescriptorRenderer } from './create-message-descriptor-renderer'; export { detectLocale, LOCALE_COOKIE_NAME } from './detect-locale'; +export { getRouteI18n, type LocaleRouteParams } from './get-route-i18n'; export { I18nProvider } from './I18nProvider'; export { LocaleContext } from './LocaleContext'; export { LocalizedLink } from './LocalizedLink'; +export { LocalizedLinkButton } from './LocalizedLinkButton'; export { localizeHref, stripLocale } from './localize-href'; -export { getLocaleMessages } from './messages-by-locale'; export { resolveLocaleParam } from './resolve-locale-param'; export { useLocale } from './use-locale'; +export { useRenderMessage } from './use-render-message'; export { useUnlocalizedPathname } from './use-unlocalized-pathname'; diff --git a/packages/twenty-website-new/src/lib/i18n/localize-href.ts b/packages/twenty-website-new/src/lib/i18n/localize-href.ts index 9928cc6e568..42365fd6f9e 100644 --- a/packages/twenty-website-new/src/lib/i18n/localize-href.ts +++ b/packages/twenty-website-new/src/lib/i18n/localize-href.ts @@ -32,9 +32,11 @@ export const localizeHref = (locale: AppLocale, href: string): string => { ? buildTailFromSegmentEnd(href, segmentEnd) : href; - return locale === SOURCE_LOCALE || !isPublicAppLocale(locale) - ? unprefixed - : `/${locale}${unprefixed}`; + if (locale === SOURCE_LOCALE || !isPublicAppLocale(locale)) { + return unprefixed; + } + + return unprefixed === '/' ? `/${locale}` : `/${locale}${unprefixed}`; }; export const stripLocale = (pathname: string): string => { diff --git a/packages/twenty-website-new/src/lib/i18n/message-body.ts b/packages/twenty-website-new/src/lib/i18n/message-body.ts new file mode 100644 index 00000000000..f85cbc18173 --- /dev/null +++ b/packages/twenty-website-new/src/lib/i18n/message-body.ts @@ -0,0 +1,5 @@ +import type { MessageDescriptor } from '@lingui/core'; + +export type MessageBody = { + text: MessageDescriptor; +}; diff --git a/packages/twenty-website-new/src/lib/i18n/message-eyebrow.ts b/packages/twenty-website-new/src/lib/i18n/message-eyebrow.ts new file mode 100644 index 00000000000..b21c7b546fc --- /dev/null +++ b/packages/twenty-website-new/src/lib/i18n/message-eyebrow.ts @@ -0,0 +1,5 @@ +import type { MessageHeadingSegment } from './message-heading-segment'; + +export type MessageEyebrow = { + heading: MessageHeadingSegment; +}; diff --git a/packages/twenty-website-new/src/lib/i18n/message-heading-segment.ts b/packages/twenty-website-new/src/lib/i18n/message-heading-segment.ts new file mode 100644 index 00000000000..670d4a3d706 --- /dev/null +++ b/packages/twenty-website-new/src/lib/i18n/message-heading-segment.ts @@ -0,0 +1,9 @@ +import type { MessageDescriptor } from '@lingui/core'; + +export type MessageHeadingSegment = { + fontFamily: 'sans' | 'serif' | 'mono'; + text: MessageDescriptor; + fontWeight?: 'light' | 'regular' | 'medium'; + newLine?: boolean; + lineBreakBefore?: boolean; +}; diff --git a/packages/twenty-website-new/src/lib/i18n/messages-by-locale.ts b/packages/twenty-website-new/src/lib/i18n/messages-by-locale.ts index fcd445babe1..9c99ccbc10d 100644 --- a/packages/twenty-website-new/src/lib/i18n/messages-by-locale.ts +++ b/packages/twenty-website-new/src/lib/i18n/messages-by-locale.ts @@ -2,9 +2,11 @@ import { type Messages } from '@lingui/core'; import { SOURCE_LOCALE, type AppLocale } from 'twenty-shared/translations'; import { messages as enMessages } from '@/locales/generated/en'; +import { messages as frMessages } from '@/locales/generated/fr-FR'; const MESSAGES_BY_LOCALE: Partial> = { en: enMessages, + 'fr-FR': frMessages, }; export const getLocaleMessages = (locale: AppLocale): Messages => diff --git a/packages/twenty-website-new/src/lib/i18n/set-server-i18n.ts b/packages/twenty-website-new/src/lib/i18n/set-server-i18n.ts new file mode 100644 index 00000000000..15040b89b8d --- /dev/null +++ b/packages/twenty-website-new/src/lib/i18n/set-server-i18n.ts @@ -0,0 +1,10 @@ +import { setI18n } from '@lingui/react/server'; +import type { AppLocale } from 'twenty-shared/translations'; + +import { createI18nInstance } from './create-i18n-instance'; + +export const setServerI18n = (locale: AppLocale) => { + const i18n = createI18nInstance(locale); + setI18n(i18n); + return i18n; +}; diff --git a/packages/twenty-website-new/src/lib/i18n/use-render-message.ts b/packages/twenty-website-new/src/lib/i18n/use-render-message.ts new file mode 100644 index 00000000000..a51c5de4242 --- /dev/null +++ b/packages/twenty-website-new/src/lib/i18n/use-render-message.ts @@ -0,0 +1,13 @@ +'use client'; + +import type { MessageDescriptor } from '@lingui/core'; +import { useLingui } from '@lingui/react'; +import { useCallback } from 'react'; + +export const useRenderMessage = () => { + const { i18n } = useLingui(); + return useCallback( + (descriptor: MessageDescriptor) => i18n._(descriptor), + [i18n], + ); +}; diff --git a/packages/twenty-website-new/src/lib/i18n/website-locale-list.ts b/packages/twenty-website-new/src/lib/i18n/website-locale-list.ts new file mode 100644 index 00000000000..f1e80c4fadb --- /dev/null +++ b/packages/twenty-website-new/src/lib/i18n/website-locale-list.ts @@ -0,0 +1,12 @@ +import { + APP_LOCALES, + SOURCE_LOCALE, + type AppLocale, +} from 'twenty-shared/translations'; + +// Website localization is intentionally rolled out separately from the app. +// Keep this allowlist small until Crowdin sync, SEO signals, and QA are proven. +export const WEBSITE_LOCALE_LIST = [ + SOURCE_LOCALE, + APP_LOCALES['fr-FR'], +] as const satisfies readonly AppLocale[]; diff --git a/packages/twenty-website-new/src/lib/partner-application/PartnerApplicationModal.tsx b/packages/twenty-website-new/src/lib/partner-application/PartnerApplicationModal.tsx index 9aa3caf492c..89f4ac74dfb 100644 --- a/packages/twenty-website-new/src/lib/partner-application/PartnerApplicationModal.tsx +++ b/packages/twenty-website-new/src/lib/partner-application/PartnerApplicationModal.tsx @@ -6,9 +6,11 @@ import { buttonBaseStyles, } from '@/design-system/components/Button/BaseButton'; import { ButtonShape } from '@/design-system/components/Button/ButtonShape'; +import { useRenderMessage } from '@/lib/i18n/use-render-message'; import { PARTNER_APPLICATION_MODAL_COPY, - PARTNER_PROGRAM_OPTIONS, + PARTNER_PROGRAM_IDS, + PARTNER_PROGRAM_LABELS, type PartnerProgramId, } from '@/lib/partner-application/partner-application-modal-data'; import { theme } from '@/theme'; @@ -266,6 +268,7 @@ export function PartnerApplicationModal({ onClose, initialProgramId = 'technology', }: PartnerApplicationModalProps) { + const renderText = useRenderMessage(); const formRef = useRef(null); const dropdownRef = useRef(null); const [programId, setProgramId] = @@ -273,6 +276,7 @@ export function PartnerApplicationModal({ const [dropdownOpen, setDropdownOpen] = useState(false); const [submitError, setSubmitError] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); + const copy = PARTNER_APPLICATION_MODAL_COPY; useEffect(() => { if (open) { @@ -336,12 +340,12 @@ export function PartnerApplicationModal({ setSubmitError(null); if (!name || !email || !company || !website || !message) { - setSubmitError(validationCopy.incompleteForm); + setSubmitError(renderText(validationCopy.incompleteForm)); return; } if (!emailLooksValid) { - setSubmitError(validationCopy.invalidEmail); + setSubmitError(renderText(validationCopy.invalidEmail)); return; } @@ -363,22 +367,20 @@ export function PartnerApplicationModal({ }); if (!response.ok) { - setSubmitError(validationCopy.submitFailed); + setSubmitError(renderText(validationCopy.submitFailed)); return; } onClose(); } catch { - setSubmitError(validationCopy.submitFailed); + setSubmitError(renderText(validationCopy.submitFailed)); } finally { setIsSubmitting(false); } }, - [isSubmitting, onClose, programId], + [isSubmitting, onClose, programId, renderText], ); - const copy = PARTNER_APPLICATION_MODAL_COPY; - return ( @@ -415,8 +418,16 @@ export function PartnerApplicationModal({ - - + + } /> @@ -424,19 +435,19 @@ export function PartnerApplicationModal({
    - - {PARTNER_PROGRAM_OPTIONS.map((option) => ( + + {PARTNER_PROGRAM_IDS.map((id) => ( { - setProgramId(option.id); + setProgramId(id); }} > - {option.label} + {renderText(PARTNER_PROGRAM_LABELS[id])} ))} @@ -446,20 +457,16 @@ export function PartnerApplicationModal({ { setDropdownOpen((previous) => !previous); }} > - {copy.selectLabel} + {renderText(copy.selectLabel)} - { - PARTNER_PROGRAM_OPTIONS.find( - (option) => option.id === programId, - )?.label - } + {renderText(PARTNER_PROGRAM_LABELS[programId])} @@ -468,20 +475,23 @@ export function PartnerApplicationModal({ {dropdownOpen && ( - - {PARTNER_PROGRAM_OPTIONS.map((option) => ( + + {PARTNER_PROGRAM_IDS.map((id) => ( { - setProgramId(option.id); + setProgramId(id); setDropdownOpen(false); }} > - {option.label} + {renderText(PARTNER_PROGRAM_LABELS[id])} ))} @@ -494,7 +504,7 @@ export function PartnerApplicationModal({ aria-required="true" autoComplete="off" name="name" - placeholder={copy.fields.name} + placeholder={renderText(copy.fields.name)} type="text" /> @@ -506,7 +516,7 @@ export function PartnerApplicationModal({ autoComplete="off" inputMode="email" name="email" - placeholder={copy.fields.email} + placeholder={renderText(copy.fields.email)} type="text" /> @@ -515,7 +525,7 @@ export function PartnerApplicationModal({ aria-required="true" autoComplete="off" name="company" - placeholder={copy.fields.company} + placeholder={renderText(copy.fields.company)} type="text" /> @@ -526,7 +536,7 @@ export function PartnerApplicationModal({ aria-required="true" autoComplete="off" name="website" - placeholder={copy.fields.website} + placeholder={renderText(copy.fields.website)} type="text" /> @@ -535,7 +545,7 @@ export function PartnerApplicationModal({ @@ -545,7 +555,7 @@ export function PartnerApplicationModal({ aria-required="true" autoComplete="off" name="message" - placeholder={`${copy.fields.messageLabel}\n\n${copy.fields.messageHint}`} + placeholder={`${renderText(copy.fields.messageLabel)}\n\n${renderText(copy.fields.messageHint)}`} /> @@ -564,7 +574,7 @@ export function PartnerApplicationModal({ strokeColor="none" /> - {isSubmitting ? copy.submitInFlight : copy.submit} + {renderText(isSubmitting ? copy.submitInFlight : copy.submit)} diff --git a/packages/twenty-website-new/src/lib/partner-application/index.ts b/packages/twenty-website-new/src/lib/partner-application/index.ts index 4f5599ef946..c586e267fe4 100644 --- a/packages/twenty-website-new/src/lib/partner-application/index.ts +++ b/packages/twenty-website-new/src/lib/partner-application/index.ts @@ -5,7 +5,8 @@ export { export { PARTNER_APPLICATION_MODAL_COPY, - PARTNER_PROGRAM_OPTIONS, + PARTNER_PROGRAM_IDS, + PARTNER_PROGRAM_LABELS, type PartnerProgramId, } from './partner-application-modal-data'; diff --git a/packages/twenty-website-new/src/lib/partner-application/partner-application-modal-data.ts b/packages/twenty-website-new/src/lib/partner-application/partner-application-modal-data.ts index df0ba99d855..3a9db4ec61b 100644 --- a/packages/twenty-website-new/src/lib/partner-application/partner-application-modal-data.ts +++ b/packages/twenty-website-new/src/lib/partner-application/partner-application-modal-data.ts @@ -1,37 +1,43 @@ -export type PartnerProgramId = 'technology' | 'content' | 'solutions'; +import type { MessageDescriptor } from '@lingui/core'; +import { msg } from '@lingui/core/macro'; -export const PARTNER_PROGRAM_OPTIONS: ReadonlyArray<{ - id: PartnerProgramId; - label: string; -}> = [ - { id: 'technology', label: 'Technology Partner' }, - { id: 'content', label: 'Content & Community Partner' }, - { id: 'solutions', label: 'Solutions Partner' }, -]; +export const PARTNER_PROGRAM_IDS = [ + 'technology', + 'content', + 'solutions', +] as const; + +export type PartnerProgramId = (typeof PARTNER_PROGRAM_IDS)[number]; + +export const PARTNER_PROGRAM_LABELS: Record< + PartnerProgramId, + MessageDescriptor +> = { + technology: msg`Technology Partner`, + content: msg`Content & Community Partner`, + solutions: msg`Solutions Partner`, +}; export const PARTNER_APPLICATION_MODAL_COPY = { - titleSerif: 'Apply to build', - titleSans: 'the future of CRM', - subtitleLine1: - 'Join our ecosystem and help businesses take control of their customer data with', - subtitleLine2: 'open-source primitives.', - selectLabel: 'Select your team', + titleSerif: msg`Apply to build`, + titleSans: msg`the future of CRM`, + subtitleLine1: msg`Join our ecosystem and help businesses take control of their customer data with`, + subtitleLine2: msg`open-source primitives.`, + selectLabel: msg`Select your team`, fields: { - name: 'Your name *', - email: 'Work email *', - company: 'Company or brand *', - website: 'Website or github link *', - opportunities: 'Estimated monthly opportunities (optional)', - messageLabel: 'How do you want to partner with Twenty? *', - messageHint: - 'Tell us about the custom solutions or integrations you plan to build.', + name: msg`Your name *`, + email: msg`Work email *`, + company: msg`Company or brand *`, + website: msg`Website or github link *`, + opportunities: msg`Estimated monthly opportunities (optional)`, + messageLabel: msg`How do you want to partner with Twenty? *`, + messageHint: msg`Tell us about the custom solutions or integrations you plan to build.`, }, - submit: 'Submit application', - submitInFlight: 'Submitting…', + submit: msg`Submit application`, + submitInFlight: msg`Submitting…`, validation: { - incompleteForm: 'Please complete all required fields before submitting.', - invalidEmail: 'Enter a valid email address.', - submitFailed: - 'We could not submit your application. Please try again in a moment.', + incompleteForm: msg`Please complete all required fields before submitting.`, + invalidEmail: msg`Enter a valid email address.`, + submitFailed: msg`We could not submit your application. Please try again in a moment.`, }, } as const; diff --git a/packages/twenty-website-new/src/lib/releases/get-latest-release-preview.ts b/packages/twenty-website-new/src/lib/releases/get-latest-release-preview.ts index 3432f38bdb1..b29ce328459 100644 --- a/packages/twenty-website-new/src/lib/releases/get-latest-release-preview.ts +++ b/packages/twenty-website-new/src/lib/releases/get-latest-release-preview.ts @@ -1,4 +1,6 @@ import { loadLocalReleaseNotes } from '@/lib/releases/load-local-release-notes'; +import type { MessageDescriptor } from '@lingui/core'; +import { msg } from '@lingui/core/macro'; const IMAGE_REGEX_GLOBAL = /!\[[^\]]*\]\(([^)]+)\)/g; const HEADING_REGEX_GLOBAL = /^#\s+(.+)$/gm; @@ -7,8 +9,8 @@ export type LatestReleasePreview = { image: string; imageAlt: string; imageScale?: number; - title: string; - description: string; + title: MessageDescriptor; + description: MessageDescriptor; }; export function getLatestReleasePreview(): LatestReleasePreview | null { @@ -37,8 +39,7 @@ export function getLatestReleasePreview(): LatestReleasePreview | null { image, imageAlt: `Twenty release ${latest.release}${featureTitle ? ` — ${featureTitle}` : ''}`, imageScale: 1.04, - title: `See what shipped in ${latest.release}`, - description: - 'Track every release with changelogs, highlights and demos of the newest features.', + title: msg`See what shipped in ${latest.release}`, + description: msg`Track every release with changelogs, highlights and demos of the newest features.`, }; } diff --git a/packages/twenty-website-new/src/lib/seo/__tests__/build-page-metadata.test.ts b/packages/twenty-website-new/src/lib/seo/__tests__/build-page-metadata.test.ts index 1d6e5e1c57a..c3149241b6a 100644 --- a/packages/twenty-website-new/src/lib/seo/__tests__/build-page-metadata.test.ts +++ b/packages/twenty-website-new/src/lib/seo/__tests__/build-page-metadata.test.ts @@ -2,6 +2,8 @@ import { buildPageMetadata } from '@/lib/seo'; const ORIGINAL_SITE_URL = process.env.NEXT_PUBLIC_WEBSITE_URL; +const descriptor = (message: string) => ({ id: message, message }); + beforeEach(() => { process.env.NEXT_PUBLIC_WEBSITE_URL = 'https://example.test'; }); @@ -15,8 +17,8 @@ describe('buildPageMetadata', () => { const metadata = buildPageMetadata({ locale: 'en', path: '/product', - title: 'Product | Twenty', - description: 'Product page description.', + title: descriptor('Product | Twenty'), + description: descriptor('Product page description.'), }); expect(metadata.title).toEqual({ absolute: 'Product | Twenty' }); @@ -38,29 +40,55 @@ describe('buildPageMetadata', () => { it('falls back to the source canonical for locales the website does not publish', () => { const metadata = buildPageMetadata({ - locale: 'fr-FR', + locale: 'de-DE', path: '/product', - title: 't', - description: 'd', + title: descriptor('t'), + description: descriptor('d'), }); expect(metadata.alternates).toMatchObject({ canonical: '/product' }); expect(metadata.openGraph).toMatchObject({ locale: 'en' }); }); + it('resolves Lingui message descriptors before emitting SEO fields', () => { + const metadata = buildPageMetadata({ + locale: 'en', + path: '/', + title: { + id: 'seo.test.title', + message: 'Twenty | #1 open source CRM', + }, + description: { + id: 'seo.test.description', + message: 'The #1 open source CRM for modern teams.', + }, + }); + + expect(metadata.title).toEqual({ + absolute: 'Twenty | #1 open source CRM', + }); + expect(metadata.description).toBe( + 'The #1 open source CRM for modern teams.', + ); + expect(metadata.openGraph).toMatchObject({ + title: 'Twenty | #1 open source CRM', + description: 'The #1 open source CRM for modern teams.', + }); + }); + it('emits hreflang alternates only for published website locales', () => { const metadata = buildPageMetadata({ locale: 'fr-FR', path: '/pricing', - title: 't', - description: 'd', + title: descriptor('t'), + description: descriptor('d'), }); const languages = metadata.alternates?.languages as | Record | undefined; expect(languages?.en).toBe('/pricing'); - expect(languages?.['fr-FR']).toBeUndefined(); + expect(languages?.['fr-FR']).toBe('/fr-FR/pricing'); expect(languages?.['zh-CN']).toBeUndefined(); expect(languages?.['x-default']).toBe('/pricing'); }); @@ -69,8 +97,8 @@ describe('buildPageMetadata', () => { const metadata = buildPageMetadata({ locale: 'en', path: '/pricing', - title: 't', - description: 'd', + title: descriptor('t'), + description: descriptor('d'), }); const languages = metadata.alternates?.languages as @@ -83,8 +111,8 @@ describe('buildPageMetadata', () => { const metadata = buildPageMetadata({ locale: 'de-DE', path: '/', - title: 't', - description: 'd', + title: descriptor('t'), + description: descriptor('d'), }); expect(metadata.alternates).toMatchObject({ canonical: '/' }); @@ -92,6 +120,7 @@ describe('buildPageMetadata', () => { | Record | undefined; expect(languages?.['de-DE']).toBeUndefined(); + expect(languages?.['fr-FR']).toBe('/fr-FR'); expect(languages?.en).toBe('/'); expect(languages?.['x-default']).toBe('/'); }); @@ -100,8 +129,8 @@ describe('buildPageMetadata', () => { const metadata = buildPageMetadata({ locale: 'en', path: '/x', - title: 't', - description: 'd', + title: descriptor('t'), + description: descriptor('d'), ogImage: '/og/x.png', }); @@ -115,8 +144,8 @@ describe('buildPageMetadata', () => { const metadata = buildPageMetadata({ locale: 'en', path: '/x', - title: 't', - description: 'd', + title: descriptor('t'), + description: descriptor('d'), ogImage: 'https://cdn.example/og.png', }); @@ -129,8 +158,8 @@ describe('buildPageMetadata', () => { const metadata = buildPageMetadata({ locale: 'en', path: '/x', - title: 'Default title', - description: 'Default description', + title: descriptor('Default title'), + description: descriptor('Default description'), ogImage: '/og/default.png', extend: { openGraph: { title: 'Override OG title' }, @@ -150,8 +179,8 @@ describe('buildPageMetadata', () => { const metadata = buildPageMetadata({ locale: 'en', path: '/x', - title: 't', - description: 'd', + title: descriptor('t'), + description: descriptor('d'), ogImage: '/og/x.png', extend: { twitter: { card: 'summary' }, @@ -171,8 +200,8 @@ describe('buildPageMetadata', () => { const metadata = buildPageMetadata({ locale: 'en', path: '/x', - title: 't', - description: 'd', + title: descriptor('t'), + description: descriptor('d'), extend: { alternates: { canonical: '/custom-canonical' }, }, @@ -191,8 +220,8 @@ describe('buildPageMetadata', () => { const metadata = buildPageMetadata({ locale: 'en', path: '/x', - title: 't', - description: 'd', + title: descriptor('t'), + description: descriptor('d'), extend: { robots: { index: false, follow: false }, }, diff --git a/packages/twenty-website-new/src/lib/seo/build-localized-metadata.ts b/packages/twenty-website-new/src/lib/seo/build-localized-metadata.ts index 2753ec437d7..67df59cb2b6 100644 --- a/packages/twenty-website-new/src/lib/seo/build-localized-metadata.ts +++ b/packages/twenty-website-new/src/lib/seo/build-localized-metadata.ts @@ -1,6 +1,7 @@ import type { Metadata } from 'next'; import { resolveLocaleParam } from '@/lib/i18n/resolve-locale-param'; +import { setServerI18n } from '@/lib/i18n/set-server-i18n'; import { buildPageMetadata, @@ -16,5 +17,6 @@ export const buildLocalizedMetadata = async ({ params }: LocalizedMetadataArgs): Promise => { const { locale: rawLocale } = await params; const locale = resolveLocaleParam(rawLocale); + setServerI18n(locale); return buildPageMetadata({ ...input, locale }); }; diff --git a/packages/twenty-website-new/src/lib/seo/build-page-metadata.ts b/packages/twenty-website-new/src/lib/seo/build-page-metadata.ts index 0b2070eb21c..4cb9416fcdd 100644 --- a/packages/twenty-website-new/src/lib/seo/build-page-metadata.ts +++ b/packages/twenty-website-new/src/lib/seo/build-page-metadata.ts @@ -5,6 +5,9 @@ import { PUBLIC_APP_LOCALE_LIST, isPublicAppLocale, } from '@/lib/i18n/app-locale-set'; +import { createI18nInstance } from '@/lib/i18n/create-i18n-instance'; +import { createMessageDescriptorRenderer } from '@/lib/i18n/create-message-descriptor-renderer'; +import type { MessageDescriptor } from '@lingui/core'; import { getSiteUrl } from './site-url'; @@ -14,8 +17,8 @@ const TWITTER_HANDLE = '@twentycrm'; export type BuildPageMetadataInput = { locale: AppLocale; path: string; - title: string; - description: string; + title: MessageDescriptor; + description: MessageDescriptor; ogImage?: string; type?: 'website' | 'article'; extend?: Metadata; @@ -55,6 +58,10 @@ export function buildPageMetadata({ const normalizedPath = normalizePath(path); const metadataLocale = isPublicAppLocale(locale) ? locale : SOURCE_LOCALE; const canonical = localizePath(metadataLocale, normalizedPath); + const i18n = createI18nInstance(metadataLocale); + const renderText = createMessageDescriptorRenderer(i18n); + const resolvedTitle = renderText(title); + const resolvedDescription = renderText(description); const ogImages = ogImage === undefined @@ -68,15 +75,15 @@ export function buildPageMetadata({ ]; const baseMetadata: Metadata = { - title: { absolute: title }, - description, + title: { absolute: resolvedTitle }, + description: resolvedDescription, alternates: { canonical, languages: buildLanguageAlternates(normalizedPath), }, openGraph: { - title, - description, + title: resolvedTitle, + description: resolvedDescription, url: canonical, siteName: SITE_NAME, locale: metadataLocale, @@ -85,8 +92,8 @@ export function buildPageMetadata({ }, twitter: { card: 'summary_large_image', - title, - description, + title: resolvedTitle, + description: resolvedDescription, site: TWITTER_HANDLE, creator: TWITTER_HANDLE, ...(ogImages && { images: ogImages.map((image) => image.url) }), diff --git a/packages/twenty-website-new/src/lib/website-routing/customer-story-routes.ts b/packages/twenty-website-new/src/lib/website-routing/customer-story-routes.ts index b74c0df060d..da21313f4b4 100644 --- a/packages/twenty-website-new/src/lib/website-routing/customer-story-routes.ts +++ b/packages/twenty-website-new/src/lib/website-routing/customer-story-routes.ts @@ -1,4 +1,5 @@ import { CASE_STUDY_CATALOG_ENTRIES } from '@/lib/customers'; +import { msg } from '@lingui/core/macro'; import type { WebsiteRoute } from './types'; @@ -6,7 +7,7 @@ export const CUSTOMER_STORY_ROUTES: readonly WebsiteRoute[] = CASE_STUDY_CATALOG_ENTRIES.map((entry) => ({ id: `customer:${entry.href.slice('/customers/'.length)}`, path: entry.href, - title: `${entry.hero.author} | Twenty Customer Story`, + title: msg`${entry.hero.author} | Twenty Customer Story`, description: entry.catalogCard.summary, changeFrequency: 'yearly', priority: 0.5, diff --git a/packages/twenty-website-new/src/lib/website-routing/static-website-routes.ts b/packages/twenty-website-new/src/lib/website-routing/static-website-routes.ts index cbfd516938c..f8bf9edc86c 100644 --- a/packages/twenty-website-new/src/lib/website-routing/static-website-routes.ts +++ b/packages/twenty-website-new/src/lib/website-routing/static-website-routes.ts @@ -1,12 +1,13 @@ +import { msg } from '@lingui/core/macro'; + import type { WebsiteRoute } from './types'; export const STATIC_WEBSITE_ROUTES = [ { id: 'home', path: '/', - title: 'Twenty | #1 open source CRM', - description: - 'The #1 open source CRM for modern teams. Modular, scalable, and built to fit your business.', + title: msg`Twenty | #1 open source CRM`, + description: msg`The #1 open source CRM for modern teams. Modular, scalable, and built to fit your business.`, changeFrequency: 'weekly', priority: 1, indexed: true, @@ -14,9 +15,8 @@ export const STATIC_WEBSITE_ROUTES = [ { id: 'whyTwenty', path: '/why-twenty', - title: 'Why Twenty | Twenty', - description: - 'Most packaged software makes companies more similar. Learn why the future of CRM is built, not bought.', + title: msg`Why Twenty | Twenty`, + description: msg`Most packaged software makes companies more similar. Learn why the future of CRM is built, not bought.`, changeFrequency: 'monthly', priority: 0.8, indexed: true, @@ -24,9 +24,8 @@ export const STATIC_WEBSITE_ROUTES = [ { id: 'product', path: '/product', - title: 'Product | Twenty', - description: - 'Track relationships, manage pipelines, and take action quickly with a CRM that feels intuitive from day one.', + title: msg`Product | Twenty`, + description: msg`Track relationships, manage pipelines, and take action quickly with a CRM that feels intuitive from day one.`, changeFrequency: 'monthly', priority: 0.8, indexed: true, @@ -34,9 +33,8 @@ export const STATIC_WEBSITE_ROUTES = [ { id: 'pricing', path: '/pricing', - title: 'Pricing | Twenty', - description: - 'Plans that scale with your team. Compare tiers of the #1 open source CRM.', + title: msg`Pricing | Twenty`, + description: msg`Plans that scale with your team. Compare tiers of the #1 open source CRM.`, changeFrequency: 'monthly', priority: 0.9, indexed: true, @@ -44,9 +42,8 @@ export const STATIC_WEBSITE_ROUTES = [ { id: 'partners', path: '/partners', - title: 'Partners | Twenty', - description: - 'Join our partner ecosystem and grow with us as we build the #1 open source CRM.', + title: msg`Partners | Twenty`, + description: msg`Join our partner ecosystem and grow with us as we build the #1 open source CRM.`, changeFrequency: 'monthly', priority: 0.7, indexed: true, @@ -54,9 +51,8 @@ export const STATIC_WEBSITE_ROUTES = [ { id: 'releases', path: '/releases', - title: 'Releases | Twenty', - description: - 'Discover the newest features and improvements in Twenty, the #1 open source CRM.', + title: msg`Releases | Twenty`, + description: msg`Discover the newest features and improvements in Twenty, the #1 open source CRM.`, changeFrequency: 'weekly', priority: 0.7, indexed: true, @@ -64,9 +60,8 @@ export const STATIC_WEBSITE_ROUTES = [ { id: 'customers', path: '/customers', - title: 'Customers | Twenty', - description: - 'Meet the teams running their business on Twenty. Real customer stories on how they shaped the CRM to fit their workflow.', + title: msg`Customers | Twenty`, + description: msg`Meet the teams running their business on Twenty. Real customer stories on how they shaped the CRM to fit their workflow.`, changeFrequency: 'monthly', priority: 0.7, indexed: true, @@ -74,9 +69,8 @@ export const STATIC_WEBSITE_ROUTES = [ { id: 'privacyPolicy', path: '/privacy-policy', - title: 'Privacy Policy | Twenty', - description: - 'How Twenty collects, uses, safeguards, and discloses information when you use Twenty.com and related services.', + title: msg`Privacy Policy | Twenty`, + description: msg`How Twenty collects, uses, safeguards, and discloses information when you use Twenty.com and related services.`, changeFrequency: 'yearly', priority: 0.3, indexed: true, @@ -84,9 +78,8 @@ export const STATIC_WEBSITE_ROUTES = [ { id: 'terms', path: '/terms', - title: 'Terms of Service | Twenty', - description: - 'Terms of Service for Twenty.com PBC, including use of Twenty.com, sub-domains, and related services.', + title: msg`Terms of Service | Twenty`, + description: msg`Terms of Service for Twenty.com PBC, including use of Twenty.com, sub-domains, and related services.`, changeFrequency: 'yearly', priority: 0.3, indexed: true, @@ -94,8 +87,8 @@ export const STATIC_WEBSITE_ROUTES = [ { id: 'halftone', path: '/halftone', - title: 'Halftone Generator | Twenty', - description: 'Interactive halftone generator exported from Twenty.', + title: msg`Halftone Generator | Twenty`, + description: msg`Interactive halftone generator exported from Twenty.`, changeFrequency: 'monthly', priority: 0, indexed: false, @@ -104,9 +97,8 @@ export const STATIC_WEBSITE_ROUTES = [ { id: 'enterpriseActivate', path: '/enterprise/activate', - title: 'Enterprise activation | Twenty', - description: - 'Complete activation for your Twenty self-hosted enterprise license.', + title: msg`Enterprise activation | Twenty`, + description: msg`Complete activation for your Twenty self-hosted enterprise license.`, changeFrequency: 'yearly', priority: 0, indexed: false, diff --git a/packages/twenty-website-new/src/lib/website-routing/types.ts b/packages/twenty-website-new/src/lib/website-routing/types.ts index 85b14355ef4..7e9e22fcd1c 100644 --- a/packages/twenty-website-new/src/lib/website-routing/types.ts +++ b/packages/twenty-website-new/src/lib/website-routing/types.ts @@ -1,5 +1,7 @@ import type { MetadataRoute } from 'next'; +import type { MessageDescriptor } from '@lingui/core'; + export type WebsiteRouteId = | 'home' | 'whyTwenty' @@ -16,11 +18,11 @@ export type WebsiteRouteId = export type WebsiteRoute = { changeFrequency: MetadataRoute.Sitemap[number]['changeFrequency']; - description: string; + description: MessageDescriptor; id: WebsiteRouteId; indexed: boolean; path: string; priority: number; robotsDisallow?: boolean; - title: string; + title: MessageDescriptor; }; diff --git a/packages/twenty-website-new/src/locales/af-ZA.po b/packages/twenty-website-new/src/locales/af-ZA.po deleted file mode 100644 index ae43b2703f6..00000000000 --- a/packages/twenty-website-new/src/locales/af-ZA.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: af-ZA\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/ar-SA.po b/packages/twenty-website-new/src/locales/ar-SA.po deleted file mode 100644 index 590256a9d60..00000000000 --- a/packages/twenty-website-new/src/locales/ar-SA.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: ar-SA\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/ca-ES.po b/packages/twenty-website-new/src/locales/ca-ES.po deleted file mode 100644 index 6d1c83cd64f..00000000000 --- a/packages/twenty-website-new/src/locales/ca-ES.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: ca-ES\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/cs-CZ.po b/packages/twenty-website-new/src/locales/cs-CZ.po deleted file mode 100644 index dbf7245bcde..00000000000 --- a/packages/twenty-website-new/src/locales/cs-CZ.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: cs-CZ\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/da-DK.po b/packages/twenty-website-new/src/locales/da-DK.po deleted file mode 100644 index 1e10ca0b382..00000000000 --- a/packages/twenty-website-new/src/locales/da-DK.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: da-DK\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/de-DE.po b/packages/twenty-website-new/src/locales/de-DE.po deleted file mode 100644 index dbe207ce0d6..00000000000 --- a/packages/twenty-website-new/src/locales/de-DE.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: de-DE\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/el-GR.po b/packages/twenty-website-new/src/locales/el-GR.po deleted file mode 100644 index 6df7663a158..00000000000 --- a/packages/twenty-website-new/src/locales/el-GR.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: el-GR\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/en.po b/packages/twenty-website-new/src/locales/en.po index 40a1285ea76..2ee317ec94d 100644 --- a/packages/twenty-website-new/src/locales/en.po +++ b/packages/twenty-website-new/src/locales/en.po @@ -12,3 +12,3652 @@ msgstr "" "Last-Translator: \n" "Language-Team: \n" "Plural-Forms: \n" + +#. js-lingui-id: BUIzFF +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "-33% off" +msgstr "-33% off" + +#. js-lingui-id: q0ur5+ +#. placeholder {0}: entry.hero.author +#: src/lib/website-routing/customer-story-routes.ts +msgid "{0} | Twenty Customer Story" +msgstr "{0} | Twenty Customer Story" + +#. js-lingui-id: icND7o +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "/ seat / month - billed yearly" +msgstr "/ seat / month - billed yearly" + +#. js-lingui-id: XUqxHW +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +msgid "/user/month" +msgstr "/user/month" + +#. js-lingui-id: ojimXJ +#: src/sections/Footer/data.ts +msgid "© 2026 – Twenty" +msgstr "© 2026 – Twenty" + +#. js-lingui-id: foGG8A +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "+$105/user per month" +msgstr "+$105/user per month" + +#. js-lingui-id: Bsq9GJ +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "+$35/user per month" +msgstr "+$35/user per month" + +#. js-lingui-id: mWSpW5 +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "+$5/user per month" +msgstr "+$5/user per month" + +#. js-lingui-id: JIwDAd +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "+$7000/org per month" +msgstr "+$7000/org per month" + +#. js-lingui-id: yVr18d +#: src/app/[locale]/pricing/salesforce.data.ts +#: src/app/[locale]/pricing/salesforce.data.ts +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "" +"+$75/user per month\n" +"Switch to enterprise!" +msgstr "" +"+$75/user per month\n" +"Switch to enterprise!" + +#. js-lingui-id: iK+ikF +#: src/sections/TrustedBy/data.ts +msgid "+10k others" +msgstr "+10k others" + +#. js-lingui-id: nRtVCK +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "" +"+20% of net spend\n" +"+$75/user per month\n" +"Switch to enterprise!" +msgstr "" +"+20% of net spend\n" +"+$75/user per month\n" +"Switch to enterprise!" + +#. js-lingui-id: be30i9 +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "$0" +msgstr "$0" + +#. js-lingui-id: 6inRXo +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "" +"$1/orchestration run/org\n" +"+$75/user per month\n" +"Switch to enterprise!" +msgstr "" +"$1/orchestration run/org\n" +"+$75/user per month\n" +"Switch to enterprise!" + +#. js-lingui-id: W8uNyP +#: src/sections/Plans/data.ts +msgid "$12" +msgstr "$12" + +#. js-lingui-id: EkIZkY +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "$19" +msgstr "$19" + +#. js-lingui-id: xrRqkE +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +msgid "$25" +msgstr "$25" + +#. js-lingui-id: 8r6DgO +#: src/sections/Plans/data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "$9" +msgstr "$9" + +#. js-lingui-id: EHNAf2 +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "1 click" +msgstr "1 click" + +#. js-lingui-id: qvNspm +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "1‑800‑YES‑SOFTWARE" +msgstr "1‑800‑YES‑SOFTWARE" + +#. js-lingui-id: 6WQdu8 +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "100 per minute" +msgstr "100 per minute" + +#. js-lingui-id: uuKUqZ +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "" +"11 permissions\n" +"groups" +msgstr "" +"11 permissions\n" +"groups" + +#. js-lingui-id: SaSafJ +#: src/app/[locale]/customers/9dots/page.tsx +msgid "150 hours" +msgstr "150 hours" + +#. js-lingui-id: 9ntyY8 +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "150 hrs" +msgstr "150 hrs" + +#. js-lingui-id: 2z1Znr +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "2 years contract" +msgstr "2 years contract" + +#. js-lingui-id: l6Vx4q +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "2,000+" +msgstr "2,000+" + +#. js-lingui-id: oadHWW +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "200 per minute" +msgstr "200 per minute" + +#. js-lingui-id: FKcV/y +#: src/lib/customers/case-study-catalog.ts +#: src/lib/customers/case-study-catalog.ts +#: src/lib/customers/case-study-catalog.ts +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/w3villa/page.tsx +#: src/app/[locale]/customers/netzero/page.tsx +#: src/app/[locale]/customers/alternative-partners/page.tsx +#: src/app/[locale]/customers/act-education/page.tsx +msgid "2025" +msgstr "2025" + +#. js-lingui-id: PJgfBS +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "3" +msgstr "3" + +#. js-lingui-id: BL47wE +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "" +"3 2 years contract\n" +"-33% off" +msgstr "" +"3 2 years contract\n" +"-33% off" + +#. js-lingui-id: /jgC+9 +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/netzero/page.tsx +msgid "3 product lines" +msgstr "3 product lines" + +#. js-lingui-id: 7SJhyh +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "30+" +msgstr "30+" + +#. js-lingui-id: Pdl2UE +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "4 tools" +msgstr "4 tools" + +#. js-lingui-id: tZvajW +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "6 workflows" +msgstr "6 workflows" + +#. js-lingui-id: AQPMc4 +#: src/app/[locale]/customers/act-education/page.tsx +msgid "90%" +msgstr "90%" + +#. js-lingui-id: fHxYPM +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/act-education/page.tsx +msgid "90%+" +msgstr "90%+" + +#. js-lingui-id: 1GAcp6 +#: src/app/[locale]/customers/netzero/page.tsx +msgid "A business that does not fit a" +msgstr "A business that does not fit a" + +#. js-lingui-id: NLSM5L +#: src/app/[locale]/customers/netzero/page.tsx +msgid "A business that does not fit a template" +msgstr "A business that does not fit a template" + +#. js-lingui-id: 9Z2FZQ +#: src/app/[locale]/product/page.tsx +msgid "A CRM for teams" +msgstr "A CRM for teams" + +#. js-lingui-id: GSM5RA +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/netzero/page.tsx +msgid "A CRM that" +msgstr "A CRM that" + +#. js-lingui-id: och6cp +#: src/app/[locale]/customers/netzero/page.tsx +msgid "A CRM that grows with you | NetZero & Twenty" +msgstr "A CRM that grows with you | NetZero & Twenty" + +#. js-lingui-id: EewhZt +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/act-education/page.tsx +msgid "A CRM they" +msgstr "A CRM they" + +#. js-lingui-id: sgzhYK +#: src/app/[locale]/(home)/page.tsx +msgid "a CRM they'll love" +msgstr "a CRM they'll love" + +#. js-lingui-id: xiVefD +#: src/app/[locale]/(home)/page.tsx +msgid "A custom CRM gives your org an edge," +msgstr "A custom CRM gives your org an edge," + +#. js-lingui-id: Qay3/Z +#: src/app/[locale]/product/page.tsx +msgid "A modern CRM with" +msgstr "A modern CRM with" + +#. js-lingui-id: GoyOzl +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "A platform ready to" +msgstr "A platform ready to" + +#. js-lingui-id: rxMguZ +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "A platform ready to grow" +msgstr "A platform ready to grow" + +#. js-lingui-id: R5hTKX +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "A real estate agency on WhatsApp" +msgstr "A real estate agency on WhatsApp" + +#. js-lingui-id: WjUlJr +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "A retro theme as a paid add-on is somehow the most believable part." +msgstr "A retro theme as a paid add-on is somehow the most believable part." + +#. js-lingui-id: r9Msiv +#: src/app/[locale]/why-twenty/editorial-three.data.ts +msgid "A year ago, customizing your CRM meant hiring a Salesforce consultant, learning Apex, waiting months. The gap between \"I want this\" and \"it's live\" was measured in quarters and invoices. So people settled. They bent their process to fit the tool and called it adoption." +msgstr "A year ago, customizing your CRM meant hiring a Salesforce consultant, learning Apex, waiting months. The gap between \"I want this\" and \"it's live\" was measured in quarters and invoices. So people settled. They bent their process to fit the tool and called it adoption." + +#. js-lingui-id: c0RR2h +#: src/app/[locale]/customers/9dots/page.tsx +msgid "About 150 hours per month saved in manual operations. Real-time metrics for the business owner. Growth readiness without adding operational headcount. A team that can answer questions that used to take days to piece together." +msgstr "About 150 hours per month saved in manual operations. Real-time metrics for the business owner. Growth readiness without adding operational headcount. A team that can answer questions that used to take days to piece together." + +#. js-lingui-id: 41o3VS +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/act-education/page.tsx +msgid "AC&T and Flycoder moved from a dead vendor export to self-hosted Twenty, with over 90% lower CRM cost and full control." +msgstr "AC&T and Flycoder moved from a dead vendor export to self-hosted Twenty, with over 90% lower CRM cost and full control." + +#. js-lingui-id: TuJiQF +#: src/app/[locale]/customers/act-education/page.tsx +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "AC&T Education Migration" +msgstr "AC&T Education Migration" + +#. js-lingui-id: yHUdh6 +#: src/app/[locale]/customers/act-education/page.tsx +msgid "AC&T Education Migration (actimmi.com) is an education agency in Australia. They help international students with applications to education providers and visas. They had been on a previous CRM until the vendor shut the system down, leaving nothing but a CSV export." +msgstr "AC&T Education Migration (actimmi.com) is an education agency in Australia. They help international students with applications to education providers and visas. They had been on a previous CRM until the vendor shut the system down, leaving nothing but a CSV export." + +#. js-lingui-id: MPzy5R +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "AC&T moved to a self-hosted Twenty instance with no vendor risk, no forced migration, and CRM costs reduced by more than 90%." +msgstr "AC&T moved to a self-hosted Twenty instance with no vendor risk, no forced migration, and CRM costs reduced by more than 90%." + +#. js-lingui-id: J+A83M +#: src/app/[locale]/(home)/helped.data.ts +msgid "AC&T replaced a shuttered vendor CRM with self-hosted Twenty and cut CRM costs by more than 90%." +msgstr "AC&T replaced a shuttered vendor CRM with self-hosted Twenty and cut CRM costs by more than 90%." + +#. js-lingui-id: muhZ+X +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Activating your enterprise license…" +msgstr "Activating your enterprise license…" + +#. js-lingui-id: y+9jZD +#: src/app/[locale]/enterprise/activate/page.tsx +msgid "activation" +msgstr "activation" + +#. js-lingui-id: TDMykO +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "actually get used" +msgstr "actually get used" + +#. js-lingui-id: +/fX0G +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/act-education/page.tsx +msgid "actually own" +msgstr "actually own" + +#. js-lingui-id: woOp+c +#: src/app/[locale]/product/stepper.data.ts +msgid "Add objects and fields" +msgstr "Add objects and fields" + +#. js-lingui-id: Y2K3rp +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Add-ons" +msgstr "Add-ons" + +#. js-lingui-id: Op7nVF +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Adoption" +msgstr "Adoption" + +#. js-lingui-id: TeMRC2 +#: src/app/[locale]/customers/netzero/page.tsx +msgid "advanced" +msgstr "advanced" + +#. js-lingui-id: cBdysG +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Advanced Encryption" +msgstr "Advanced Encryption" + +#. js-lingui-id: /C6Pwb +#: src/app/[locale]/product/feature.data.ts +msgid "Aggregate, bar, line, pie, and gauge widgets" +msgstr "Aggregate, bar, line, pie, and gauge widgets" + +#. js-lingui-id: rp2omj +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/netzero/page.tsx +msgid "Agribusiness" +msgstr "Agribusiness" + +#. js-lingui-id: jiGK6+ +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "AI (Einstein)" +msgstr "AI (Einstein)" + +#. js-lingui-id: aUn7Ps +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "AI & Automations" +msgstr "AI & Automations" + +#. js-lingui-id: HbXHSp +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "AI agents" +msgstr "AI agents" + +#. js-lingui-id: WGXwzu +#: src/app/[locale]/why-twenty/editorial-one.data.ts +msgid "AI agents are starting to draft outreach, score leads, research accounts, write follow-ups, update deal stages. Every one of these actions reads from and writes to the CRM. The scoreboard became the playbook. The database became the brain." +msgstr "AI agents are starting to draft outreach, score leads, research accounts, write follow-ups, update deal stages. Every one of these actions reads from and writes to the CRM. The scoreboard became the playbook. The database became the brain." + +#. js-lingui-id: Rsxdfy +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +msgid "AI Agents with custom skills" +msgstr "AI Agents with custom skills" + +#. js-lingui-id: TfV/y9 +#: src/app/[locale]/product/three-cards.data.ts +msgid "AI chat, settings, and records in a side panels for fast, single-screen access." +msgstr "AI chat, settings, and records in a side panels for fast, single-screen access." + +#. js-lingui-id: R+bwFB +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "AI for rapid iterations" +msgstr "AI for rapid iterations" + +#. js-lingui-id: +pgY/C +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "AI in the" +msgstr "AI in the" + +#. js-lingui-id: A6v5aI +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "AI in the migration workflow" +msgstr "AI in the migration workflow" + +#. js-lingui-id: 9VlH2i +#: src/app/[locale]/why-twenty/page.tsx +msgid "AI made the gap that small." +msgstr "AI made the gap that small." + +#. js-lingui-id: Tp1P7F +#: src/app/[locale]/why-twenty/page.tsx +msgid "AI turned it into an operating system." +msgstr "AI turned it into an operating system." + +#. js-lingui-id: t97j+F +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "AI-assisted" +msgstr "AI-assisted" + +#. js-lingui-id: LIGwFL +#: src/app/[locale]/customers/alternative-partners/page.tsx +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "Alternative Partners" +msgstr "Alternative Partners" + +#. js-lingui-id: GDVvyT +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "Alternative Partners is a consulting firm that moved from Salesforce to a self-hosted Twenty instance. Benjamin Reynolds led the migration. He had already become a Twenty expert implementing Twenty for one of Twenty's first cloud customers." +msgstr "Alternative Partners is a consulting firm that moved from Salesforce to a self-hosted Twenty instance. Benjamin Reynolds led the migration. He had already become a Twenty expert implementing Twenty for one of Twenty's first cloud customers." + +#. js-lingui-id: CCrRuT +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "Alternative Partners replaced Salesforce with self-hosted Twenty, using agentic AI to compress migration work." +msgstr "Alternative Partners replaced Salesforce with self-hosted Twenty, using agentic AI to compress migration work." + +#. js-lingui-id: UIu18Y +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "Alternative Partners used agentic AI to compress what would typically be weeks of Salesforce migration work into something a single person could oversee." +msgstr "Alternative Partners used agentic AI to compress what would typically be weeks of Salesforce migration work into something a single person could oversee." + +#. js-lingui-id: iFLhqi +#: src/app/[locale]/(home)/testimonials.data.ts +msgid "Amrendra Pratap Singh" +msgstr "Amrendra Pratap Singh" + +#. js-lingui-id: JDRPTH +#: src/app/[locale]/product/page.tsx +msgid "an intuitive interface" +msgstr "an intuitive interface" + +#. js-lingui-id: lTSix7 +#: src/app/[locale]/partners/page.tsx +msgid "and unlock new opportunities with Twenty" +msgstr "and unlock new opportunities with Twenty" + +#. js-lingui-id: aGCcXe +#: src/sections/Faq/data.ts +msgid "Any Questions?" +msgstr "Any Questions?" + +#. js-lingui-id: /AMVjF +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "APEX tutorials" +msgstr "APEX tutorials" + +#. js-lingui-id: OZtEcz +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "API" +msgstr "API" + +#. js-lingui-id: yKBF3l +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "API access" +msgstr "API access" + +#. js-lingui-id: GpYSXo +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "API backbone" +msgstr "API backbone" + +#. js-lingui-id: 30KnZB +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "API calls" +msgstr "API calls" + +#. js-lingui-id: 42FQ3y +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "API-first" +msgstr "API-first" + +#. js-lingui-id: azQMHP +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "APIs are extra. Simplicity has a price." +msgstr "APIs are extra. Simplicity has a price." + +#. js-lingui-id: MqJvvH +#: src/sections/Menu/data.ts +msgid "APIs, SDKs and webhooks to extend Twenty and ship apps on top of your CRM data." +msgstr "APIs, SDKs and webhooks to extend Twenty and ship apps on top of your CRM data." + +#. js-lingui-id: I1Z4AM +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Apply to build" +msgstr "Apply to build" + +#. js-lingui-id: uicv6Z +#: src/app/[locale]/customers/w3villa/page.tsx +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Architecture" +msgstr "Architecture" + +#. js-lingui-id: Sfa0xD +#: src/app/[locale]/customers/9dots/page.tsx +msgid "around it" +msgstr "around it" + +#. js-lingui-id: yYKNWp +#: src/app/[locale]/(home)/page.tsx +msgid "Assemble, iterate and adapt a robust CRM," +msgstr "Assemble, iterate and adapt a robust CRM," + +#. js-lingui-id: v7zTZl +#: src/app/[locale]/product/feature.data.ts +msgid "Assign owners and due dates" +msgstr "Assign owners and due dates" + +#. js-lingui-id: 9c5acG +#: src/app/[locale]/(home)/page.tsx +msgid "at AI Speed" +msgstr "at AI Speed" + +#. js-lingui-id: 4/b98B +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "at the core" +msgstr "at the core" + +#. js-lingui-id: y2W2Hg +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Audit logs" +msgstr "Audit logs" + +#. js-lingui-id: FFv0Vh +#: src/app/[locale]/product/stepper.data.ts +msgid "Automation" +msgstr "Automation" + +#. js-lingui-id: SJ7WSP +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Available on YouTube!" +msgstr "Available on YouTube!" + +#. js-lingui-id: iH8pgl +#: src/sections/CaseStudy/components/Hero.tsx +msgid "Back" +msgstr "Back" + +#. js-lingui-id: jQLfIW +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Because apparently privacy feels more premium with a surcharge." +msgstr "Because apparently privacy feels more premium with a surcharge." + +#. js-lingui-id: qEQv1F +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Because everything is built on Twenty's open foundation, Flycoder could wire the exact logic AC&T needed without fighting the platform." +msgstr "Because everything is built on Twenty's open foundation, Flycoder could wire the exact logic AC&T needed without fighting the platform." + +#. js-lingui-id: uIG0OH +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Because the foundation is solid, W3Grads is architected for what comes next, including a payment layer for future paid interview plans and nationwide scale without structural rewrites." +msgstr "Because the foundation is solid, W3Grads is architected for what comes next, including a payment layer for future paid interview plans and nationwide scale without structural rewrites." + +#. js-lingui-id: JGM+JO +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Because true orchestration means putting a dollar sign on every dramatic entrance." +msgstr "Because true orchestration means putting a dollar sign on every dramatic entrance." + +#. js-lingui-id: SoJDT2 +#: src/app/[locale]/partners/page.tsx +msgid "Become" +msgstr "Become" + +#. js-lingui-id: sx6hMS +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Become a Content Partner" +msgstr "Become a Content Partner" + +#. js-lingui-id: PKMHNe +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "become a genius!" +msgstr "become a genius!" + +#. js-lingui-id: 6ZCRfT +#: src/app/[locale]/partners/components/PartnerApplication/BecomePartnerButton.tsx +msgid "Become a partner" +msgstr "Become a partner" + +#. js-lingui-id: lvXL1I +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Become a Solution Partner" +msgstr "Become a Solution Partner" + +#. js-lingui-id: EwaFPn +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Become a Technology Partner" +msgstr "Become a Technology Partner" + +#. js-lingui-id: eqswCn +#: src/app/[locale]/(home)/home-stepper.data.ts +msgid "Begin with production-grade" +msgstr "Begin with production-grade" + +#. js-lingui-id: SYfuGP +#: src/app/[locale]/partners/testimonials.data.ts +msgid "Benjamin Reynolds" +msgstr "Benjamin Reynolds" + +#. js-lingui-id: YfjrMq +#: src/app/[locale]/partners/testimonials.data.ts +msgid "Bertrams" +msgstr "Bertrams" + +#. js-lingui-id: mzTjnf +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Better than Liquid Glass!" +msgstr "Better than Liquid Glass!" + +#. js-lingui-id: GvGk4g +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Beyond" +msgstr "Beyond" + +#. js-lingui-id: bxWnM1 +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "breaking operations" +msgstr "breaking operations" + +#. js-lingui-id: ONUC8n +#: src/app/[locale]/customers/page.tsx +msgid "build" +msgstr "build" + +#. js-lingui-id: WxyAko +#: src/app/[locale]/why-twenty/page.tsx +msgid "Build a CRM your competitors" +msgstr "Build a CRM your competitors" + +#. js-lingui-id: pF0PoL +#: src/app/[locale]/product/feature.data.ts +msgid "Build custom dashboards" +msgstr "Build custom dashboards" + +#. js-lingui-id: wWXG44 +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Build integrations that connect Twenty with the tools your customers already use. Help us expand the Twenty ecosystem." +msgstr "Build integrations that connect Twenty with the tools your customers already use. Help us expand the Twenty ecosystem." + +#. js-lingui-id: ZhC3tY +#: src/app/[locale]/why-twenty/page.tsx +msgid "Build it in an afternoon." +msgstr "Build it in an afternoon." + +#. js-lingui-id: e1u+Zh +#: src/sections/Menu/data.ts +msgid "Build on an open platform" +msgstr "Build on an open platform" + +#. js-lingui-id: oIpt0K +#: src/app/[locale]/(home)/page.tsx +msgid "Build your Enterprise CRM" +msgstr "Build your Enterprise CRM" + +#. js-lingui-id: J7i6zm +#: src/app/[locale]/(home)/home-stepper.data.ts +msgid "building blocks" +msgstr "building blocks" + +#. js-lingui-id: /ETeLn +#: src/app/[locale]/customers/9dots/page.tsx +msgid "built a" +msgstr "built a" + +#. js-lingui-id: 8hMULp +#: src/lib/customers/case-study-catalog.ts +msgid "built a CRM around it" +msgstr "built a CRM around it" + +#. js-lingui-id: SoAKmY +#: src/app/[locale]/product/three-cards.data.ts +msgid "Built for speed" +msgstr "Built for speed" + +#. js-lingui-id: hnSOXa +#: src/sections/CaseStudyCatalog/components/Promo.tsx +msgid "built with Twenty" +msgstr "built with Twenty" + +#. js-lingui-id: j8RGrG +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Burned by vendor lock-in, AC&T built a CRM they actually own | Twenty" +msgstr "Burned by vendor lock-in, AC&T built a CRM they actually own | Twenty" + +#. js-lingui-id: EjSajd +#: src/app/[locale]/(home)/page.tsx +msgid "but building one" +msgstr "but building one" + +#. js-lingui-id: wCALve +#: src/sections/Faq/data.ts +msgid "Can developers extend Twenty with code?" +msgstr "Can developers extend Twenty with code?" + +#. js-lingui-id: 6mytgb +#: src/sections/Faq/data.ts +msgid "Can I migrate from Salesforce or HubSpot?" +msgstr "Can I migrate from Salesforce or HubSpot?" + +#. js-lingui-id: +oHJTp +#: src/app/[locale]/why-twenty/page.tsx +msgid "can't buy." +msgstr "can't buy." + +#. js-lingui-id: u+wGUf +#: src/sections/Helped/components/Scene.tsx +msgid "change with Twenty" +msgstr "change with Twenty" + +#. js-lingui-id: 2D9CbR +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Check more add-ons" +msgstr "Check more add-ons" + +#. js-lingui-id: ZbCD7v +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Classic never dies. It just gets extended one more time." +msgstr "Classic never dies. It just gets extended one more time." + +#. js-lingui-id: L/LPQZ +#: src/sections/Faq/data.ts +msgid "Cloud Pro is $9/user/month (yearly). Organization is $19/user/month and unlocks SSO and row-level permissions for teams that need finer access control." +msgstr "Cloud Pro is $9/user/month (yearly). Organization is $19/user/month and unlocks SSO and row-level permissions for teams that need finer access control." + +#. js-lingui-id: 1YncLF +#: src/app/[locale]/(home)/testimonials.data.ts +msgid "Co-founder at NetZero" +msgstr "Co-founder at NetZero" + +#. js-lingui-id: vWaYWP +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/netzero/page.tsx +msgid "Co-founder, NetZero" +msgstr "Co-founder, NetZero" + +#. js-lingui-id: R8BDsa +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Co-marketing opportunities" +msgstr "Co-marketing opportunities" + +#. js-lingui-id: b5bnuN +#: src/app/[locale]/product/feature.data.ts +msgid "Column-to-field mapping (including relations)" +msgstr "Column-to-field mapping (including relations)" + +#. js-lingui-id: NHYWK4 +#: src/app/[locale]/(home)/page.tsx +msgid "comes with" +msgstr "comes with" + +#. js-lingui-id: ofoEsM +#: src/app/[locale]/pricing/salesforce.data.ts +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Coming soon!" +msgstr "Coming soon!" + +#. js-lingui-id: KGD9Kt +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Commercial license (no AGPL obligations)" +msgstr "Commercial license (no AGPL obligations)" + +#. js-lingui-id: chL5IG +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Community" +msgstr "Community" + +#. js-lingui-id: 8CLnho +#: src/sections/Plans/data.ts +msgid "Community support" +msgstr "Community support" + +#. js-lingui-id: SHjhDC +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Company or brand *" +msgstr "Company or brand *" + +#. js-lingui-id: O403zL +#: src/sections/Helped/components/Scene.tsx +msgid "company-wide" +msgstr "company-wide" + +#. js-lingui-id: P/f7ez +#: src/lib/website-routing/static-website-routes.ts +msgid "Complete activation for your Twenty self-hosted enterprise license." +msgstr "Complete activation for your Twenty self-hosted enterprise license." + +#. js-lingui-id: Fkb+LW +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "Compose your CRM and internal apps with a single extensibility toolkit." +msgstr "Compose your CRM and internal apps with a single extensibility toolkit." + +#. js-lingui-id: kD7ZGH +#: src/app/[locale]/(home)/home-stepper.data.ts +msgid "Compose your CRM and internal apps with a single extensibility toolkit. Data model, layout, and automation." +msgstr "Compose your CRM and internal apps with a single extensibility toolkit. Data model, layout, and automation." + +#. js-lingui-id: iSLIjg +#: src/sections/Footer/data.ts +msgid "Connect" +msgstr "Connect" + +#. js-lingui-id: 90KBr0 +#: src/app/[locale]/product/feature.data.ts +msgid "Connect Google or Microsoft accounts" +msgstr "Connect Google or Microsoft accounts" + +#. js-lingui-id: MVrQxI +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Connected via API" +msgstr "Connected via API" + +#. js-lingui-id: cfAG43 +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "Consulting" +msgstr "Consulting" + +#. js-lingui-id: Oii3vg +#: src/app/[locale]/product/feature.data.ts +msgid "Contacts & Companies" +msgstr "Contacts & Companies" + +#. js-lingui-id: fi9xsM +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Content & Community Partner" +msgstr "Content & Community Partner" + +#. js-lingui-id: vymMOT +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Content & Community Partners" +msgstr "Content & Community Partners" + +#. js-lingui-id: mutfS8 +#: src/app/[locale]/(home)/home-stepper.data.ts +msgid "Continue iteration" +msgstr "Continue iteration" + +#. js-lingui-id: +Uepfb +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Control" +msgstr "Control" + +#. js-lingui-id: VOpE1H +#: src/app/[locale]/customers/9dots/page.tsx +msgid "control hub" +msgstr "control hub" + +#. js-lingui-id: sTIhSj +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Control without" +msgstr "Control without" + +#. js-lingui-id: wH1xmY +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "Control without drag" +msgstr "Control without drag" + +#. js-lingui-id: Bj7igG +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Control without the overhead" +msgstr "Control without the overhead" + +#. js-lingui-id: PiH3UR +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Copied!" +msgstr "Copied!" + +#. js-lingui-id: he3ygx +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Copy" +msgstr "Copy" + +#. js-lingui-id: 7qTWwB +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Copy the enterprise key above." +msgstr "Copy the enterprise key above." + +#. js-lingui-id: PXnj76 +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Copy this key and paste it into your Twenty self-hosted instance settings." +msgstr "Copy this key and paste it into your Twenty self-hosted instance settings." + +#. js-lingui-id: FapWIq +#: src/app/[locale]/product/feature.data.ts +msgid "Core Features" +msgstr "Core Features" + +#. js-lingui-id: JRK+HF +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Costs down more than" +msgstr "Costs down more than" + +#. js-lingui-id: Xn2Nlq +#: src/app/[locale]/product/stepper.data.ts +msgid "Create a workflow" +msgstr "Create a workflow" + +#. js-lingui-id: B9nLrl +#: src/sections/Menu/data.ts +msgid "Create apps on Twenty" +msgstr "Create apps on Twenty" + +#. js-lingui-id: yAL7FY +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +msgid "Create custom apps" +msgstr "Create custom apps" + +#. js-lingui-id: oS1RiS +#: src/app/[locale]/product/feature.data.ts +msgid "Create tasks from records" +msgstr "Create tasks from records" + +#. js-lingui-id: MSlbwg +#: src/app/[locale]/customers/9dots/page.tsx +msgid "CRM" +msgstr "CRM" + +#. js-lingui-id: Y44Xsf +#: src/app/[locale]/customers/act-education/page.tsx +msgid "CRM costs dropped by more than 90%. Manual overhead tied to the old system is gone. For the first time, AC&T has a CRM they will not lose again." +msgstr "CRM costs dropped by more than 90%. Manual overhead tied to the old system is gone. For the first time, AC&T has a CRM they will not lose again." + +#. js-lingui-id: wDasLa +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "CRM Engineer" +msgstr "CRM Engineer" + +#. js-lingui-id: DqkzNV +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/act-education/page.tsx +msgid "CRM Engineer, AC&T Education Migration" +msgstr "CRM Engineer, AC&T Education Migration" + +#. js-lingui-id: b7vNti +#: src/app/[locale]/why-twenty/hero.data.ts +msgid "CRM was a database you filled on Fridays. AI turned it into the system that runs your go-to-market. To differentiate, you have to build what your competitors can't buy." +msgstr "CRM was a database you filled on Fridays. AI turned it into the system that runs your go-to-market. To differentiate, you have to build what your competitors can't buy." + +#. js-lingui-id: v5sHk3 +#: src/app/[locale]/why-twenty/page.tsx +msgid "CRM was a ledger." +msgstr "CRM was a ledger." + +#. js-lingui-id: I6FFSA +#: src/app/[locale]/product/feature.data.ts +msgid "CSV export anytime" +msgstr "CSV export anytime" + +#. js-lingui-id: 8RWevB +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "CSV import & export" +msgstr "CSV import & export" + +#. js-lingui-id: s+QiQi +#: src/app/[locale]/product/feature.data.ts +msgid "CSV import flow" +msgstr "CSV import flow" + +#. js-lingui-id: lh6aPn +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Custom AI models" +msgstr "Custom AI models" + +#. js-lingui-id: OaTurC +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Custom apps" +msgstr "Custom apps" + +#. js-lingui-id: yBSEtR +#: src/app/[locale]/product/feature.data.ts +msgid "Custom deal stages for your process" +msgstr "Custom deal stages for your process" + +#. js-lingui-id: Vz1Vq2 +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Custom domain (crm.yourco.com)" +msgstr "Custom domain (crm.yourco.com)" + +#. js-lingui-id: oPwQt4 +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Custom fields" +msgstr "Custom fields" + +#. js-lingui-id: U1RB/7 +#: src/app/[locale]/product/feature.data.ts +msgid "Custom fields and relationships" +msgstr "Custom fields and relationships" + +#. js-lingui-id: prIqWa +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Custom layout" +msgstr "Custom layout" + +#. js-lingui-id: 8skTDV +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Custom objects" +msgstr "Custom objects" + +#. js-lingui-id: AbyZbl +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Custom views" +msgstr "Custom views" + +#. js-lingui-id: YjXLAQ +#: src/sections/CaseStudyCatalog/components/Promo.tsx +msgid "Customer Stories" +msgstr "Customer Stories" + +#. js-lingui-id: NihQNk +#: src/sections/Menu/data.ts +msgid "Customers" +msgstr "Customers" + +#. js-lingui-id: 3mRQi4 +#: src/lib/website-routing/static-website-routes.ts +msgid "Customers | Twenty" +msgstr "Customers | Twenty" + +#. js-lingui-id: qqLY+j +#: src/app/[locale]/product/stepper.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Customization" +msgstr "Customization" + +#. js-lingui-id: GRfLAg +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/netzero/page.tsx +msgid "Customizations" +msgstr "Customizations" + +#. js-lingui-id: 4RLD4p +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Daily messages" +msgstr "Daily messages" + +#. js-lingui-id: V0kvgB +#: src/app/[locale]/product/feature.data.ts +msgid "Data import" +msgstr "Data import" + +#. js-lingui-id: 5cNMFz +#: src/app/[locale]/product/stepper.data.ts +msgid "Data model" +msgstr "Data model" + +#. js-lingui-id: FBZA6a +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Dedicated partner support" +msgstr "Dedicated partner support" + +#. js-lingui-id: KR5UiF +#: src/sections/Helped/components/Scene.tsx +msgid "Dev teams power" +msgstr "Dev teams power" + +#. js-lingui-id: n+SX4g +#: src/sections/Menu/data.ts +#: src/sections/Footer/data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Developers" +msgstr "Developers" + +#. js-lingui-id: vcsX5L +#: src/app/[locale]/why-twenty/page.tsx +msgid "Differentiation now" +msgstr "Differentiation now" + +#. js-lingui-id: Wuqvfz +#: src/lib/customers/case-study-catalog.ts +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +#: src/app/[locale]/customers/elevate-consulting/page.tsx +#: src/app/[locale]/(home)/testimonials.data.ts +msgid "Director of Digital and Information, Elevate Consulting" +msgstr "Director of Digital and Information, Elevate Consulting" + +#. js-lingui-id: +SvmQA +#: src/app/[locale]/releases/hero.data.ts +msgid "" +"Discover the newest features and improvements in Twenty,\n" +"the #1 open source CRM." +msgstr "" +"Discover the newest features and improvements in Twenty,\n" +"the #1 open source CRM." + +#. js-lingui-id: lsSE4P +#: src/lib/website-routing/static-website-routes.ts +msgid "Discover the newest features and improvements in Twenty, the #1 open source CRM." +msgstr "Discover the newest features and improvements in Twenty, the #1 open source CRM." + +#. js-lingui-id: W+b/DF +#: src/sections/Menu/data.ts +msgid "Discover what's new" +msgstr "Discover what's new" + +#. js-lingui-id: Zjjbne +#: src/sections/Faq/data.ts +msgid "Do I need a developer to customize Twenty?" +msgstr "Do I need a developer to customize Twenty?" + +#. js-lingui-id: Zx22Ih +#: src/sections/Faq/data.ts +msgid "Does Twenty work with Claude, ChatGPT, and Cursor?" +msgstr "Does Twenty work with Claude, ChatGPT, and Cursor?" + +#. js-lingui-id: pOLPPB +#: src/app/[locale]/(home)/home-stepper.data.ts +msgid "Don't get locked into someone else's ecosystem. Twenty's developer experience looks like normal software, with local setup, real data, live testing, and no proprietary tooling." +msgstr "Don't get locked into someone else's ecosystem. Twenty's developer experience looks like normal software, with local setup, real data, live testing, and no proprietary tooling." + +#. js-lingui-id: fPma12 +#: src/app/[locale]/product/feature.data.ts +msgid "Drag-and-drop deals between stages" +msgstr "Drag-and-drop deals between stages" + +#. js-lingui-id: 4CJ4xV +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "EdTech" +msgstr "EdTech" + +#. js-lingui-id: aqxYLv +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Education" +msgstr "Education" + +#. js-lingui-id: 1wTjWx +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Elevate Consulting is a management consultancy based in Canada. When Justin Beadle, Director of Digital and Information, joined, the company ran entirely on Word documents, Excel spreadsheets, sticky notes, emails, and reliance on people. There was no CRM, no API-accessible tools, only a patchwork trying to stand in for a single source of truth." +msgstr "Elevate Consulting is a management consultancy based in Canada. When Justin Beadle, Director of Digital and Information, joined, the company ran entirely on Word documents, Excel spreadsheets, sticky notes, emails, and reliance on people. There was no CRM, no API-accessible tools, only a patchwork trying to stand in for a single source of truth." + +#. js-lingui-id: 2THY70 +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Elevate Consulting uses Twenty as the API backbone connecting billing, Teams, resourcing, and a custom front end around client and opportunity data." +msgstr "Elevate Consulting uses Twenty as the API backbone connecting billing, Teams, resourcing, and a custom front end around client and opportunity data." + +#. js-lingui-id: wiTeYI +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Elevate's CEO was so impressed with Twenty he started recommending it to clients before the internal setup was even complete. The team is exploring bringing Twenty to client projects as part of their consulting practice, including as the backend for custom-built products tailored to specific operational needs." +msgstr "Elevate's CEO was so impressed with Twenty he started recommending it to clients before the internal setup was even complete. The team is exploring bringing Twenty to client projects as part of their consulting practice, including as the backend for custom-built products tailored to specific operational needs." + +#. js-lingui-id: FNyrb0 +#: src/app/[locale]/product/feature.data.ts +msgid "Email & Calendar" +msgstr "Email & Calendar" + +#. js-lingui-id: AoN898 +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Email and Chat" +msgstr "Email and Chat" + +#. js-lingui-id: ObCvfI +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Email sharing" +msgstr "Email sharing" + +#. js-lingui-id: hfqiVr +#: src/app/[locale]/product/feature.data.ts +msgid "Email/calendar activity on each record" +msgstr "Email/calendar activity on each record" + +#. js-lingui-id: XGd8Wo +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Emails & Calendar" +msgstr "Emails & Calendar" + +#. js-lingui-id: o4/9l1 +#: src/app/[locale]/product/feature.data.ts +msgid "Emails and events linked to CRM records" +msgstr "Emails and events linked to CRM records" + +#. js-lingui-id: Qpn/bX +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Encrypt your data" +msgstr "Encrypt your data" + +#. js-lingui-id: Ijh+h+ +#: src/app/[locale]/(home)/home-stepper.data.ts +msgid "Enjoy unlimited customization using the AI coding tools you already love. Adapt your CRM to fit the way your business grows and wins." +msgstr "Enjoy unlimited customization using the AI coding tools you already love. Adapt your CRM to fit the way your business grows and wins." + +#. js-lingui-id: xoqD/n +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Enter a valid email address." +msgstr "Enter a valid email address." + +#. js-lingui-id: GpB8YV +#: src/app/[locale]/enterprise/activate/page.tsx +msgid "Enterprise" +msgstr "Enterprise" + +#. js-lingui-id: 5l0LGU +#: src/lib/website-routing/static-website-routes.ts +msgid "Enterprise activation | Twenty" +msgstr "Enterprise activation | Twenty" + +#. js-lingui-id: 3dQ6zJ +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Environments" +msgstr "Environments" + +#. js-lingui-id: D1bFNS +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Estimated monthly opportunities (optional)" +msgstr "Estimated monthly opportunities (optional)" + +#. js-lingui-id: IhKZhp +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Even the training material is a feature worth celebrating." +msgstr "Even the training material is a feature worth celebrating." + +#. js-lingui-id: kz/8m8 +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +msgid "Everything in Pro" +msgstr "Everything in Pro" + +#. js-lingui-id: zoNk7e +#: src/app/[locale]/(home)/three-cards-feature.data.ts +msgid "Everything updates in real time, with AI chat always ready to help you move faster." +msgstr "Everything updates in real time, with AI chat always ready to help you move faster." + +#. js-lingui-id: bEEOy2 +#: src/app/[locale]/product/page.tsx +msgid "Everything you need," +msgstr "Everything you need," + +#. js-lingui-id: CIcRGB +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Exclusive content collaboration opportunities" +msgstr "Exclusive content collaboration opportunities" + +#. js-lingui-id: 57gG1f +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Experience enterprise-grade granularity, starting with an 11th permission." +msgstr "Experience enterprise-grade granularity, starting with an 11th permission." + +#. js-lingui-id: JTM5zS +#: src/sections/CaseStudyCatalog/components/Promo.tsx +msgid "Explore customer stories" +msgstr "Explore customer stories" + +#. js-lingui-id: weFouS +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Extended run!" +msgstr "Extended run!" + +#. js-lingui-id: akLvep +#: src/app/[locale]/(home)/three-cards-feature.data.ts +msgid "Familiar, modern interface" +msgstr "Familiar, modern interface" + +#. js-lingui-id: abO45l +#: src/app/[locale]/(home)/three-cards-feature.data.ts +msgid "Fast path to action" +msgstr "Fast path to action" + +#. js-lingui-id: e6BgMV +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Field-level permissions" +msgstr "Field-level permissions" + +#. js-lingui-id: sER+bs +#: src/app/[locale]/product/feature.data.ts +msgid "Files" +msgstr "Files" + +#. js-lingui-id: UWTNog +#: src/app/[locale]/product/feature.data.ts +msgid "Filtered metrics from live CRM data" +msgstr "Filtered metrics from live CRM data" + +#. js-lingui-id: TJ7HQl +#: src/app/[locale]/pricing/page.tsx +#: src/app/[locale]/partners/components/PartnerApplication/PartnerHeroCtas.tsx +msgid "Find a partner" +msgstr "Find a partner" + +#. js-lingui-id: MD032e +#: src/sections/Menu/data.ts +msgid "Find a Twenty partner" +msgstr "Find a Twenty partner" + +#. js-lingui-id: SZabbQ +#: src/app/[locale]/partners/page.tsx +msgid "Find the program that fits your business" +msgstr "Find the program that fits your business" + +#. js-lingui-id: wHyJkT +#: src/app/[locale]/pricing/engagement-band.data.ts +msgid "Find the right partner to implement, customize, and tailor Twenty to your team." +msgstr "Find the right partner to implement, customize, and tailor Twenty to your team." + +#. js-lingui-id: PHM5wp +#: src/app/[locale]/customers/netzero/page.tsx +msgid "Flexibility" +msgstr "Flexibility" + +#. js-lingui-id: HBJ0P5 +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "" +"Flow\n" +"orchestration" +msgstr "" +"Flow\n" +"orchestration" + +#. js-lingui-id: obrJBN +#: src/app/[locale]/product/three-cards.data.ts +msgid "Fly through your workspace with shortcuts and short load times." +msgstr "Fly through your workspace with shortcuts and short load times." + +#. js-lingui-id: HjFq2b +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Flycoder, a full-stack development partner, helped them set up Twenty as a self-hosted instance shaped around how AC&T actually operates. The data model centers on students, not a generic contact-and-deal pipeline. Statuses update automatically: a workflow runs nightly to keep enrollment records current. Automated email reminders cover important dates. Adding a new record takes under a minute." +msgstr "Flycoder, a full-stack development partner, helped them set up Twenty as a self-hosted instance shaped around how AC&T actually operates. The data model centers on students, not a generic contact-and-deal pipeline. Statuses update automatically: a workflow runs nightly to keep enrollment records current. Automated email reminders cover important dates. Adding a new record takes under a minute." + +#. js-lingui-id: 1SL9ZF +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Focus on the use case, not the" +msgstr "Focus on the use case, not the" + +#. js-lingui-id: P5E+kT +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Focus on the use case, not the plumbing" +msgstr "Focus on the use case, not the plumbing" + +#. js-lingui-id: wdVyxi +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Folder/Label import" +msgstr "Folder/Label import" + +#. js-lingui-id: CKQ0za +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "For a firm that once ran on sticky notes, this is more than an upgrade. It is a complete transformation." +msgstr "For a firm that once ran on sticky notes, this is more than an upgrade. It is a complete transformation." + +#. js-lingui-id: yAJC9c +#: src/app/[locale]/why-twenty/editorial-one.data.ts +msgid "For twenty years, CRM meant the same thing: a place to log calls, track deals, and pull reports on Friday. The real work happened in people's heads, in Slack threads, in hallway conversations. The CRM kept score. Nobody expected more from it." +msgstr "For twenty years, CRM meant the same thing: a place to log calls, track deals, and pull reports on Friday. The real work happened in people's heads, in Slack threads, in hallway conversations. The CRM kept score. Nobody expected more from it." + +#. js-lingui-id: wSoqhC +#: src/app/[locale]/customers/netzero/page.tsx +msgid "foundation" +msgstr "foundation" + +#. js-lingui-id: UCoxP5 +#: src/lib/customers/case-study-catalog.ts +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/partners/testimonials.data.ts +#: src/app/[locale]/customers/9dots/page.tsx +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Founder, Nine Dots Ventures" +msgstr "Founder, Nine Dots Ventures" + +#. js-lingui-id: IPaRlc +#: src/app/[locale]/partners/testimonials.data.ts +msgid "Founder, Wintactix" +msgstr "Founder, Wintactix" + +#. js-lingui-id: tUgSXd +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Free for you!" +msgstr "Free for you!" + +#. js-lingui-id: yDeZSV +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "From documents to" +msgstr "From documents to" + +#. js-lingui-id: emeKZ7 +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "From documents to open APIs" +msgstr "From documents to open APIs" + +#. js-lingui-id: CMqgta +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "From Salesforce to" +msgstr "From Salesforce to" + +#. js-lingui-id: syU/BD +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "From Salesforce to self-hosted Twenty, powered by AI | Alternative Partners" +msgstr "From Salesforce to self-hosted Twenty, powered by AI | Alternative Partners" + +#. js-lingui-id: TH5XjF +#: src/app/[locale]/customers/netzero/page.tsx +msgid "From simple to" +msgstr "From simple to" + +#. js-lingui-id: Lf7cb3 +#: src/app/[locale]/customers/netzero/page.tsx +msgid "From simple to advanced" +msgstr "From simple to advanced" + +#. js-lingui-id: m1I5TY +#: src/app/[locale]/product/feature.data.ts +msgid "Full communication history in one place" +msgstr "Full communication history in one place" + +#. js-lingui-id: wXgKOm +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +msgid "Full customization" +msgstr "Full customization" + +#. js-lingui-id: YZ7Q3Z +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "Full ownership" +msgstr "Full ownership" + +#. js-lingui-id: hdxwWi +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Fully customizable" +msgstr "Fully customizable" + +#. js-lingui-id: ZDIydz +#: src/sections/Menu/components/Root.tsx +#: src/sections/Menu/components/Cta.tsx +#: src/sections/Footer/data.ts +#: src/app/[locale]/why-twenty/page.tsx +#: src/app/[locale]/product/page.tsx +#: src/app/[locale]/product/page.tsx +#: src/app/[locale]/product/page.tsx +#: src/app/[locale]/pricing/page.tsx +#: src/app/[locale]/partners/page.tsx +#: src/app/[locale]/customers/page.tsx +#: src/app/[locale]/customers/page.tsx +#: src/app/[locale]/customers/_components/CustomersCaseStudySignoff.tsx +#: src/app/[locale]/(home)/page.tsx +#: src/app/[locale]/(home)/page.tsx +msgid "Get started" +msgstr "Get started" + +#. js-lingui-id: 3mUKNr +#: src/app/[locale]/product/page.tsx +msgid "Go the extra mile" +msgstr "Go the extra mile" + +#. js-lingui-id: Ay/vbT +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Good choice!" +msgstr "Good choice!" + +#. js-lingui-id: IsZ6P7 +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "grow" +msgstr "grow" + +#. js-lingui-id: MCLWq4 +#: src/app/[locale]/(home)/helped.data.ts +msgid "Grow with a flexible foundation" +msgstr "Grow with a flexible foundation" + +#. js-lingui-id: XpCing +#: src/app/[locale]/customers/netzero/page.tsx +msgid "grows" +msgstr "grows" + +#. js-lingui-id: ORQRms +#: src/lib/customers/case-study-catalog.ts +msgid "grows with you" +msgstr "grows with you" + +#. js-lingui-id: Xf5EJg +#: src/sections/Footer/data.ts +msgid "Halftone generator" +msgstr "Halftone generator" + +#. js-lingui-id: DHC85F +#: src/lib/website-routing/static-website-routes.ts +msgid "Halftone Generator | Twenty" +msgstr "Halftone Generator | Twenty" + +#. js-lingui-id: c3XJ18 +#: src/sections/Footer/data.ts +msgid "Help" +msgstr "Help" + +#. js-lingui-id: CRzGla +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Help center" +msgstr "Help center" + +#. js-lingui-id: R5aged +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Help customers implement, customize, and succeed with Twenty. Combine sales and services to grow your business." +msgstr "Help customers implement, customize, and succeed with Twenty. Combine sales and services to grow your business." + +#. js-lingui-id: qfSbUb +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "His approach was unconventional. Instead of mapping fields manually, scripting transforms, and validating data step by step, he handed the job to agentic AI tools with a brief: where the data lives, the GitHub repo for the target platform, and the Railway deployment. Start, and only return if something breaks beyond a 70% confidence fix." +msgstr "His approach was unconventional. Instead of mapping fields manually, scripting transforms, and validating data step by step, he handed the job to agentic AI tools with a brief: where the data lives, the GitHub repo for the target platform, and the Railway deployment. Start, and only return if something breaks beyond a 70% confidence fix." + +#. js-lingui-id: i0qMbr +#: src/sections/Footer/data.ts +msgid "Home" +msgstr "Home" + +#. js-lingui-id: CtU7yU +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Homeseller" +msgstr "Homeseller" + +#. js-lingui-id: KYnPnU +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Homeseller is a high-volume real estate agency in Singapore, founded by one of the country's top-performing property agents. The whole operation runs on WhatsApp: no email, no calendars, just group chats, thousands of them, with clients, agents, and leads together." +msgstr "Homeseller is a high-volume real estate agency in Singapore, founded by one of the country's top-performing property agents. The whole operation runs on WhatsApp: no email, no calendars, just group chats, thousands of them, with clients, agents, and leads together." + +#. js-lingui-id: v6cmMq +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Homeseller kept their habits. WhatsApp stayed WhatsApp. What changed is that everything flowing through those conversations now lands in a structured system, tracked, classified, and visible in real time." +msgstr "Homeseller kept their habits. WhatsApp stayed WhatsApp. What changed is that everything flowing through those conversations now lands in a structured system, tracked, classified, and visible in real time." + +#. js-lingui-id: xaIV9m +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Homeseller, WhatsApp, and a CRM built around the business | Nine Dots & Twenty" +msgstr "Homeseller, WhatsApp, and a CRM built around the business | Nine Dots & Twenty" + +#. js-lingui-id: XnFo3G +#: src/app/[locale]/customers/act-education/page.tsx +msgid "How AC&T Education Migration and Flycoder replaced a shuttered vendor CRM with self-hosted Twenty, with 90%+ lower cost and full ownership." +msgstr "How AC&T Education Migration and Flycoder replaced a shuttered vendor CRM with self-hosted Twenty, with 90%+ lower cost and full ownership." + +#. js-lingui-id: PYi0f6 +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "How Alternative Partners migrated from Salesforce to self-hosted Twenty using agentic AI in the implementation loop: fast migration, durable ownership." +msgstr "How Alternative Partners migrated from Salesforce to self-hosted Twenty using agentic AI in the implementation loop: fast migration, durable ownership." + +#. js-lingui-id: o+Kp2M +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "How do you want to partner with Twenty? *" +msgstr "How do you want to partner with Twenty? *" + +#. js-lingui-id: +MLOVo +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "How Elevate Consulting moved off documents and spreadsheets to Twenty as the API-connected CRM at the center of their stack." +msgstr "How Elevate Consulting moved off documents and spreadsheets to Twenty as the API-connected CRM at the center of their stack." + +#. js-lingui-id: OIwpKt +#: src/sections/Faq/data.ts +msgid "How long does it take to get started?" +msgstr "How long does it take to get started?" + +#. js-lingui-id: iRzy9e +#: src/app/[locale]/customers/netzero/page.tsx +msgid "How NetZero uses Twenty across carbon credits, agricultural products, and franchised industrial systems with a modular CRM and a roadmap toward AI-assisted workflows." +msgstr "How NetZero uses Twenty across carbon credits, agricultural products, and franchised industrial systems with a modular CRM and a roadmap toward AI-assisted workflows." + +#. js-lingui-id: NZYA4S +#: src/app/[locale]/customers/9dots/page.tsx +msgid "How Nine Dots Ventures rebuilt a Singapore real estate agency on Twenty with APIs, n8n, Grafana, and AI on top of 2,000+ WhatsApp messages a day." +msgstr "How Nine Dots Ventures rebuilt a Singapore real estate agency on Twenty with APIs, n8n, Grafana, and AI on top of 2,000+ WhatsApp messages a day." + +#. js-lingui-id: kqEcVc +#: src/sections/CaseStudyCatalog/components/Promo.tsx +msgid "How teams" +msgstr "How teams" + +#. js-lingui-id: bcln75 +#: src/lib/website-routing/static-website-routes.ts +msgid "How Twenty collects, uses, safeguards, and discloses information when you use Twenty.com and related services." +msgstr "How Twenty collects, uses, safeguards, and discloses information when you use Twenty.com and related services." + +#. js-lingui-id: URQPvK +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "How W3villa Technologies shipped W3Grads, an AI mock interview platform for institutions, on Twenty as the operational backbone." +msgstr "How W3villa Technologies shipped W3Grads, an AI mock interview platform for institutions, on Twenty as the operational backbone." + +#. js-lingui-id: l0JGUk +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Impersonate users" +msgstr "Impersonate users" + +#. js-lingui-id: N4OVNn +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Implementation" +msgstr "Implementation" + +#. js-lingui-id: yHueLx +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Implementation partners" +msgstr "Implementation partners" + +#. js-lingui-id: InyU0Z +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "In June 2025, Justin learned Twenty v1 had shipped. Within two or three days, the CEO asked him to look into setting up a CRM. The shift came from the potential of what could be built on top of fully open APIs. The timing was perfect." +msgstr "In June 2025, Justin learned Twenty v1 had shipped. Within two or three days, the CEO asked him to look into setting up a CRM. The shift came from the potential of what could be built on top of fully open APIs. The timing was perfect." + +#. js-lingui-id: d1Aru8 +#: src/app/[locale]/(home)/helped.data.ts +msgid "In production." +msgstr "In production." + +#. js-lingui-id: Sqb+jp +#: src/app/[locale]/product/feature.data.ts +msgid "In-app preview for supported file types (when enabled)" +msgstr "In-app preview for supported file types (when enabled)" + +#. js-lingui-id: 3XP8Lk +#: src/app/[locale]/pricing/salesforce.data.ts +#: src/app/[locale]/pricing/salesforce.data.ts +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Included!" +msgstr "Included!" + +#. js-lingui-id: S8gy7K +#: src/sections/CaseStudy/components/Highlights.tsx +msgid "Industry" +msgstr "Industry" + +#. js-lingui-id: MeL8SS +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Infinite scroll" +msgstr "Infinite scroll" + +#. js-lingui-id: +mOisw +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Infinite scroll is still coming soon, unlike the invoice." +msgstr "Infinite scroll is still coming soon, unlike the invoice." + +#. js-lingui-id: K8iwJx +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Install shared tarball app" +msgstr "Install shared tarball app" + +#. js-lingui-id: AHWM9N +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Integration" +msgstr "Integration" + +#. js-lingui-id: OChKCc +#: src/lib/website-routing/static-website-routes.ts +msgid "Interactive halftone generator exported from Twenty." +msgstr "Interactive halftone generator exported from Twenty." + +#. js-lingui-id: mdsMtj +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "internal rollout" +msgstr "internal rollout" + +#. js-lingui-id: a+PGuG +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Internet accounts per user" +msgstr "Internet accounts per user" + +#. js-lingui-id: DQenUJ +#: src/lib/customers/case-study-catalog.ts +msgid "is the product" +msgstr "is the product" + +#. js-lingui-id: /yQIKP +#: src/sections/Faq/data.ts +msgid "Is Twenty really open-source?" +msgstr "Is Twenty really open-source?" + +#. js-lingui-id: QjEULz +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "It is just such a nicer experience than dealing with a Salesforce or a HubSpot. My mission has been to get every tool API-accessible, so everything talks to each other." +msgstr "It is just such a nicer experience than dealing with a Salesforce or a HubSpot. My mission has been to get every tool API-accessible, so everything talks to each other." + +#. js-lingui-id: FJ51pP +#: src/app/[locale]/(home)/testimonials.data.ts +msgid "It is just such a nicer experience than dealing with a Salesforce or a HubSpot. My mission has been to get every tool API-accessible, so everything talks to each other. Twenty made that possible in a way older CRM platforms simply do not." +msgstr "It is just such a nicer experience than dealing with a Salesforce or a HubSpot. My mission has been to get every tool API-accessible, so everything talks to each other. Twenty made that possible in a way older CRM platforms simply do not." + +#. js-lingui-id: kqIAbN +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "It worked. This is AI-assisted iteration in practice: not AI as a product feature, but as part of implementation work, compressing what would typically be weeks into something one person can oversee without being the bottleneck." +msgstr "It worked. This is AI-assisted iteration in practice: not AI as a product feature, but as part of implementation work, compressing what would typically be weeks into something one person can oversee without being the bottleneck." + +#. js-lingui-id: ltNDOB +#: src/app/[locale]/(home)/problem.data.ts +msgid "It's fragile. V1 ships quickly, but maintaining and making changes is a long term burden." +msgstr "It's fragile. V1 ships quickly, but maintaining and making changes is a long term burden." + +#. js-lingui-id: kX2mij +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Join our ecosystem and help businesses take control of their customer data with" +msgstr "Join our ecosystem and help businesses take control of their customer data with" + +#. js-lingui-id: 1qiriB +#: src/app/[locale]/partners/testimonials.data.ts +msgid "Join our growing partner ecosystem" +msgstr "Join our growing partner ecosystem" + +#. js-lingui-id: ZFnW+/ +#: src/lib/website-routing/static-website-routes.ts +msgid "Join our partner ecosystem and grow with us as we build the #1 open source CRM." +msgstr "Join our partner ecosystem and grow with us as we build the #1 open source CRM." + +#. js-lingui-id: 4721j2 +#: src/app/[locale]/partners/signoff.data.ts +msgid "" +"Join our partner ecosystem and help businesses\n" +"take control of their CRM." +msgstr "" +"Join our partner ecosystem and help businesses\n" +"take control of their CRM." + +#. js-lingui-id: 4QqdtB +#: src/app/[locale]/customers/page.tsx +#: src/app/[locale]/customers/_components/CustomersCaseStudySignoff.tsx +msgid "" +"Join the teams that chose to own their CRM.\n" +"Start building with Twenty today." +msgstr "" +"Join the teams that chose to own their CRM.\n" +"Start building with Twenty today." + +#. js-lingui-id: m/pXki +#: src/app/[locale]/product/signoff.data.ts +msgid "Join the teams that chose to own their CRM. Start building with Twenty today." +msgstr "Join the teams that chose to own their CRM. Start building with Twenty today." + +#. js-lingui-id: pB76mP +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Jul 2025" +msgstr "Jul 2025" + +#. js-lingui-id: hvwlu3 +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Jun 2025" +msgstr "Jun 2025" + +#. js-lingui-id: qrYDGE +#: src/app/[locale]/(home)/testimonials.data.ts +msgid "Justin Beadle" +msgstr "Justin Beadle" + +#. js-lingui-id: vYXuJI +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Justin built workflows for notifications across the team, alerting the right people in Teams when a prospect becomes a lead or when project milestones are reached. Forms in Twenty let the business development team log activity without leaving the tool. The impact is real for the organization. The tool has been adaptable from opportunity-level work at a client to executive-level decisions." +msgstr "Justin built workflows for notifications across the team, alerting the right people in Teams when a prospect becomes a lead or when project milestones are reached. Forms in Twenty let the business development team log activity without leaving the tool. The impact is real for the organization. The tool has been adaptable from opportunity-level work at a client to executive-level decisions." + +#. js-lingui-id: E6Oohx +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Justin's broader mission at Elevate has been to move the company off static documents and onto tools with API access. By the end of 2025, that was in place: time billing, resource planning, Microsoft Teams, and project management were all accessible via API, with Twenty at the center holding client and opportunity data. Team members could use that information strategically instead of re-keying it." +msgstr "Justin's broader mission at Elevate has been to move the company off static documents and onto tools with API access. By the end of 2025, that was in place: time billing, resource planning, Microsoft Teams, and project management were all accessible via API, with Twenty at the center holding client and opportunity data. Team members could use that information strategically instead of re-keying it." + +#. js-lingui-id: GAmD3h +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Languages" +msgstr "Languages" + +#. js-lingui-id: wL3cK8 +#: src/app/[locale]/releases/page.tsx +msgid "Latest" +msgstr "Latest" + +#. js-lingui-id: rdU729 +#: src/app/[locale]/product/stepper.data.ts +msgid "Layout" +msgstr "Layout" + +#. js-lingui-id: +Ss/og +#: src/sections/Menu/data.ts +msgid "Learn how to use Twenty" +msgstr "Learn how to use Twenty" + +#. js-lingui-id: vifyyw +#: src/sections/Footer/data.ts +msgid "Legal" +msgstr "Legal" + +#. js-lingui-id: tHFTlp +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Licensee:" +msgstr "Licensee:" + +#. js-lingui-id: gggTBm +#: src/sections/Footer/data.ts +msgid "LinkedIn" +msgstr "LinkedIn" + +#. js-lingui-id: Jvz4g8 +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Listing on Twenty integrations page" +msgstr "Listing on Twenty integrations page" + +#. js-lingui-id: AHVzME +#: src/app/[locale]/(home)/three-cards-feature.data.ts +msgid "Live data and AI built" +msgstr "Live data and AI built" + +#. js-lingui-id: GObQuL +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Live updates" +msgstr "Live updates" + +#. js-lingui-id: zmRZwk +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Live updates are unavailable, which is almost more honest." +msgstr "Live updates are unavailable, which is almost more honest." + +#. js-lingui-id: M7lzaL +#: src/app/[locale]/why-twenty/page.tsx +msgid "lives in the code you own." +msgstr "lives in the code you own." + +#. js-lingui-id: ObPscj +#: src/app/[locale]/enterprise/activate/page.tsx +msgid "Loading activation…" +msgstr "Loading activation…" + +#. js-lingui-id: 7VBQ2j +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Local, Production" +msgstr "Local, Production" + +#. js-lingui-id: JO8Bdx +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Lock-in" +msgstr "Lock-in" + +#. js-lingui-id: sQia9P +#: src/sections/Menu/components/Drawer.tsx +#: src/sections/Menu/components/Cta.tsx +msgid "Log in" +msgstr "Log in" + +#. js-lingui-id: KPLAuI +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Lower CRM cost" +msgstr "Lower CRM cost" + +#. js-lingui-id: i3PI5l +#: src/app/[locale]/(home)/page.tsx +msgid "Make your GTM team happy" +msgstr "Make your GTM team happy" + +#. js-lingui-id: Si4WyF +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Management Consulting" +msgstr "Management Consulting" + +#. js-lingui-id: poX06f +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Manual work at core" +msgstr "Manual work at core" + +#. js-lingui-id: VJOKPB +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Maps view" +msgstr "Maps view" + +#. js-lingui-id: u0wogL +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Marketing assets & brand resources" +msgstr "Marketing assets & brand resources" + +#. js-lingui-id: LiMSZr +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Marketplace listing" +msgstr "Marketplace listing" + +#. js-lingui-id: bJNKJ1 +#: src/sections/Menu/data.ts +msgid "Master every corner of Twenty" +msgstr "Master every corner of Twenty" + +#. js-lingui-id: Zw+Zv+ +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "MCP server" +msgstr "MCP server" + +#. js-lingui-id: zxcfgg +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "means control" +msgstr "means control" + +#. js-lingui-id: bwCiCi +#: src/sections/Menu/data.ts +msgid "Meet the certified agencies and consultants implementing Twenty for teams worldwide." +msgstr "Meet the certified agencies and consultants implementing Twenty for teams worldwide." + +#. js-lingui-id: EhO+8s +#: src/lib/website-routing/static-website-routes.ts +msgid "Meet the teams running their business on Twenty. Real customer stories on how they shaped the CRM to fit their workflow." +msgstr "Meet the teams running their business on Twenty. Real customer stories on how they shaped the CRM to fit their workflow." + +#. js-lingui-id: ujYZ+f +#: src/sections/CaseStudyCatalog/components/Promo.tsx +msgid "Meet the teams who shaped Twenty into their own CRM with self-hosted deployments, AI-assisted workflows, and API-first product stacks." +msgstr "Meet the teams who shaped Twenty into their own CRM with self-hosted deployments, AI-assisted workflows, and API-first product stacks." + +#. js-lingui-id: ZdvtU2 +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "migration workflow" +msgstr "migration workflow" + +#. js-lingui-id: so04x6 +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Mike and Azmat from Nine Dots stepped in to fix that, not by changing how Homeseller works, but by building a system that finally fit around it." +msgstr "Mike and Azmat from Nine Dots stepped in to fix that, not by changing how Homeseller works, but by building a system that finally fit around it." + +#. js-lingui-id: izddzp +#: src/app/[locale]/partners/testimonials.data.ts +msgid "Mike Babiy" +msgstr "Mike Babiy" + +#. js-lingui-id: +8Nek/ +#: src/sections/Plans/components/BillingToggle.tsx +msgid "Monthly" +msgstr "Monthly" + +#. js-lingui-id: VqXuUv +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "More options available!" +msgstr "More options available!" + +#. js-lingui-id: XPXSwi +#: src/lib/website-routing/static-website-routes.ts +msgid "Most packaged software makes companies more similar. Learn why the future of CRM is built, not bought." +msgstr "Most packaged software makes companies more similar. Learn why the future of CRM is built, not bought." + +#. js-lingui-id: ZPjy5q +#: src/app/[locale]/product/page.tsx +msgid "moves fast" +msgstr "moves fast" + +#. js-lingui-id: dlKkNL +#: src/app/[locale]/product/feature.data.ts +msgid "Multi-file upload on records" +msgstr "Multi-file upload on records" + +#. js-lingui-id: 6YtxFj +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Name" +msgstr "Name" + +#. js-lingui-id: LfNsfa +#: src/app/[locale]/product/stepper.data.ts +msgid "Need a quick change? Skip the engineering ticket. Customize your workspace in minutes." +msgstr "Need a quick change? Skip the engineering ticket. Customize your workspace in minutes." + +#. js-lingui-id: 4sbgPq +#: src/app/[locale]/pricing/engagement-band.data.ts +msgid "Need help with customization?" +msgstr "Need help with customization?" + +#. js-lingui-id: 8Cp4Of +#: src/app/[locale]/customers/netzero/page.tsx +msgid "NetZero" +msgstr "NetZero" + +#. js-lingui-id: J8ZssL +#: src/app/[locale]/(home)/helped.data.ts +msgid "NetZero runs a modular Twenty setup across carbon credits, ag products, and industrial systems." +msgstr "NetZero runs a modular Twenty setup across carbon credits, ag products, and industrial systems." + +#. js-lingui-id: 7HVBV+ +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/netzero/page.tsx +msgid "NetZero uses Twenty as a modular CRM across product lines and countries, with a roadmap into AI-assisted workflows." +msgstr "NetZero uses Twenty as a modular CRM across product lines and countries, with a roadmap into AI-assisted workflows." + +#. js-lingui-id: 3PtYTO +#: src/app/[locale]/customers/netzero/page.tsx +msgid "NetZero works with the agro-industry, serving clients from multinationals to smallholder farmers. They sell carbon credits, agricultural products, and franchised industrial systems across three different product lines, multiple countries, and multiple company sizes. When Olivier Reinaud, co-founder of NetZero, started looking at CRMs in late 2024, he was not chasing the most feature-rich platform. He wanted the right foundation." +msgstr "NetZero works with the agro-industry, serving clients from multinationals to smallholder farmers. They sell carbon credits, agricultural products, and franchised industrial systems across three different product lines, multiple countries, and multiple company sizes. When Olivier Reinaud, co-founder of NetZero, started looking at CRMs in late 2024, he was not chasing the most feature-rich platform. He wanted the right foundation." + +#. js-lingui-id: KqojOL +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Next steps" +msgstr "Next steps" + +#. js-lingui-id: qZ+egR +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Nine Dots put Twenty at the center of Homeseller's stack with APIs, automation, and AI on top of WhatsApp-heavy operations." +msgstr "Nine Dots put Twenty at the center of Homeseller's stack with APIs, automation, and AI on top of WhatsApp-heavy operations." + +#. js-lingui-id: sGuHDu +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Nine Dots rebuilt Homeseller's operations on Twenty, with a custom data model shaped around their sales flow. Because Twenty is open and everything is accessible via API, they connected it to what the business actually needed: n8n for automated workflows (in-app workflows were not available at that time), Grafana for live dashboards fed from Twenty, and a custom AI layer to parse and extract structured insights from more than 2,000 WhatsApp messages a day." +msgstr "Nine Dots rebuilt Homeseller's operations on Twenty, with a custom data model shaped around their sales flow. Because Twenty is open and everything is accessible via API, they connected it to what the business actually needed: n8n for automated workflows (in-app workflows were not available at that time), Grafana for live dashboards fed from Twenty, and a custom AI layer to parse and extract structured insights from more than 2,000 WhatsApp messages a day." + +#. js-lingui-id: 1UzENP +#: src/sections/PlanTable/components/Content.tsx +msgid "No" +msgstr "No" + +#. js-lingui-id: o+/Ad5 +#: src/app/[locale]/customers/act-education/page.tsx +msgid "No more renting someone else's" +msgstr "No more renting someone else's" + +#. js-lingui-id: LmWdm6 +#: src/app/[locale]/customers/act-education/page.tsx +msgid "No more renting someone else's structure" +msgstr "No more renting someone else's structure" + +#. js-lingui-id: K1QTmg +#: src/sections/Plans/data.ts +msgid "No open-source distribution requirement" +msgstr "No open-source distribution requirement" + +#. js-lingui-id: q9f4Tp +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/netzero/page.tsx +msgid "No-code" +msgstr "No-code" + +#. js-lingui-id: SiZ/7E +#: src/sections/Faq/data.ts +msgid "No. Build custom objects, fields, views, and no-code workflows straight from Settings. Unlimited, no extra charge." +msgstr "No. Build custom objects, fields, views, and no-code workflows straight from Settings. Unlimited, no extra charge." + +#. js-lingui-id: 7ngHsA +#: src/app/[locale]/why-twenty/page.tsx +msgid "not bought." +msgstr "not bought." + +#. js-lingui-id: k3T2mM +#: src/app/[locale]/why-twenty/editorial-three.data.ts +msgid "Now a developer can describe what they want to Claude Code and have a working app in an afternoon. A custom object, a scoring workflow, a new view, an integration. The bottleneck isn't building anymore. It's whether your platform lets you." +msgstr "Now a developer can describe what they want to Claude Code and have a working app in an afternoon. A custom object, a scoring workflow, a new view, an integration. The bottleneck isn't building anymore. It's whether your platform lets you." + +#. js-lingui-id: CzeIij +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Number of dashboards" +msgstr "Number of dashboards" + +#. js-lingui-id: 0dVyEt +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "of a go-to-market stack" +msgstr "of a go-to-market stack" + +#. js-lingui-id: pkrZnO +#: src/app/[locale]/customers/netzero/page.tsx +msgid "Olivier recognizes that NetZero's current use of Twenty is still relatively simple: workflows and integrations are not yet as deep as he eventually wants, because he prioritized getting foundations right first." +msgstr "Olivier recognizes that NetZero's current use of Twenty is still relatively simple: workflows and integrations are not yet as deep as he eventually wants, because he prioritized getting foundations right first." + +#. js-lingui-id: l/1LQc +#: src/app/[locale]/(home)/testimonials.data.ts +msgid "Olivier Reinaud" +msgstr "Olivier Reinaud" + +#. js-lingui-id: D2b+1q +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/netzero/page.tsx +msgid "On a single CRM" +msgstr "On a single CRM" + +#. js-lingui-id: XhXmzW +#: src/sections/CaseStudy/components/CaseStudySectionNav.tsx +msgid "On this page" +msgstr "On this page" + +#. js-lingui-id: jeGPx0 +#: src/app/[locale]/customers/page.tsx +msgid "on Twenty" +msgstr "on Twenty" + +#. js-lingui-id: pbmZ7R +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Onboarding Packs" +msgstr "Onboarding Packs" + +#. js-lingui-id: /DFChQ +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "One" +msgstr "One" + +#. js-lingui-id: Zt1UZb +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "One API to rule them all" +msgstr "One API to rule them all" + +#. js-lingui-id: pShqss +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Only $5 for SSO. Practically a charity program." +msgstr "Only $5 for SSO. Practically a charity program." + +#. js-lingui-id: 3WErRa +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "open APIs" +msgstr "open APIs" + +#. js-lingui-id: pkXQA+ +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Open your Twenty self-hosted instance Settings → Enterprise." +msgstr "Open your Twenty self-hosted instance Settings → Enterprise." + +#. js-lingui-id: wtKPUT +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "open-source primitives." +msgstr "open-source primitives." + +#. js-lingui-id: 8THUfN +#: src/app/[locale]/(home)/home-stepper.data.ts +msgid "open-source software" +msgstr "open-source software" + +#. js-lingui-id: 5RkmVr +#: src/app/[locale]/why-twenty/signoff.data.ts +msgid "Open-source, AI-ready, and yours to shape." +msgstr "Open-source, AI-ready, and yours to shape." + +#. js-lingui-id: oYQN5j +#: src/app/[locale]/pricing/page.tsx +msgid "or not !" +msgstr "or not !" + +#. js-lingui-id: ucgZ0o +#: src/sections/Plans/data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Organization" +msgstr "Organization" + +#. js-lingui-id: M12WZl +#: src/app/[locale]/partners/page.tsx +msgid "our partner" +msgstr "our partner" + +#. js-lingui-id: dKGg8T +#: src/app/[locale]/pricing/salesforce.data.ts +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Out of stock" +msgstr "Out of stock" + +#. js-lingui-id: C8Ex6S +#: src/app/[locale]/product/page.tsx +msgid "out of the box" +msgstr "out of the box" + +#. js-lingui-id: PASC/7 +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Outlived every redesign since 2004." +msgstr "Outlived every redesign since 2004." + +#. js-lingui-id: Bwsi7B +#: src/app/[locale]/(home)/helped.data.ts +msgid "Own your CRM end to end" +msgstr "Own your CRM end to end" + +#. js-lingui-id: oDAEQq +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "Ownership" +msgstr "Ownership" + +#. js-lingui-id: NmF/Vo +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Owning your stack remains mysteriously out of stock." +msgstr "Owning your stack remains mysteriously out of stock." + +#. js-lingui-id: +AiiMt +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Pagination builds character." +msgstr "Pagination builds character." + +#. js-lingui-id: mPkInZ +#: src/sections/Menu/data.ts +#: src/sections/Footer/data.ts +msgid "Partners" +msgstr "Partners" + +#. js-lingui-id: itrQKv +#: src/lib/website-routing/static-website-routes.ts +msgid "Partners | Twenty" +msgstr "Partners | Twenty" + +#. js-lingui-id: 9yo8NN +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Paste the key and click Activate." +msgstr "Paste the key and click Activate." + +#. js-lingui-id: nxV7o2 +#: src/app/[locale]/customers/netzero/page.tsx +msgid "paying off" +msgstr "paying off" + +#. js-lingui-id: FFkjaT +#: src/app/[locale]/product/feature.data.ts +msgid "Pipeline Management" +msgstr "Pipeline Management" + +#. js-lingui-id: GwGdy2 +#: src/lib/website-routing/static-website-routes.ts +msgid "Plans that scale with your team. Compare tiers of the #1 open source CRM." +msgstr "Plans that scale with your team. Compare tiers of the #1 open source CRM." + +#. js-lingui-id: 0gBYFU +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Please complete all required fields before submitting." +msgstr "Please complete all required fields before submitting." + +#. js-lingui-id: iCh6/3 +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "plumbing" +msgstr "plumbing" + +#. js-lingui-id: a7u1N9 +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Price" +msgstr "Price" + +#. js-lingui-id: aHCEmh +#: src/sections/Menu/data.ts +#: src/sections/Footer/data.ts +#: src/app/[locale]/pricing/page.tsx +msgid "Pricing" +msgstr "Pricing" + +#. js-lingui-id: CboOX5 +#: src/lib/website-routing/static-website-routes.ts +msgid "Pricing | Twenty" +msgstr "Pricing | Twenty" + +#. js-lingui-id: J2IAmT +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "Principal and Founder" +msgstr "Principal and Founder" + +#. js-lingui-id: kXBQYM +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/partners/testimonials.data.ts +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "Principal and Founder, Alternative Partners" +msgstr "Principal and Founder, Alternative Partners" + +#. js-lingui-id: k62X/2 +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Priority support" +msgstr "Priority support" + +#. js-lingui-id: LcET2C +#: src/sections/Footer/data.ts +msgid "Privacy Policy" +msgstr "Privacy Policy" + +#. js-lingui-id: DJ3uVe +#: src/lib/website-routing/static-website-routes.ts +msgid "Privacy Policy | Twenty" +msgstr "Privacy Policy | Twenty" + +#. js-lingui-id: 3fPjUY +#: src/sections/Plans/data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Pro" +msgstr "Pro" + +#. js-lingui-id: 4gXof3 +#: src/lib/website-routing/static-website-routes.ts +msgid "Product | Twenty" +msgstr "Product | Twenty" + +#. js-lingui-id: kIGKva +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "Production grade quality" +msgstr "Production grade quality" + +#. js-lingui-id: CcK9cq +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Programs that previously needed heavy manual coordination now run end-to-end with automation. Institutions get a scalable, intelligent system; students get faster preparation for interviews that matter; W3villa shipped a product institutions can build revenue around." +msgstr "Programs that previously needed heavy manual coordination now run end-to-end with automation. Institutions get a scalable, intelligent system; students get faster preparation for interviews that matter; W3villa shipped a product institutions can build revenue around." + +#. js-lingui-id: 9NAqO4 +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Proposal automation" +msgstr "Proposal automation" + +#. js-lingui-id: a24q7E +#: src/app/[locale]/(home)/problem.data.ts +msgid "Proprietary languages, slow deployment cycles, and \"black box\" logic." +msgstr "Proprietary languages, slow deployment cycles, and \"black box\" logic." + +#. js-lingui-id: kS6G/9 +#: src/app/[locale]/customers/act-education/page.tsx +msgid "pulled the plug" +msgstr "pulled the plug" + +#. js-lingui-id: frfCYp +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Q1 2026" +msgstr "Q1 2026" + +#. js-lingui-id: gSQVmr +#: src/sections/Helped/components/Card.tsx +msgid "Read the case" +msgstr "Read the case" + +#. js-lingui-id: EqCbT9 +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Read/Edit/Delete permissions" +msgstr "Read/Edit/Delete permissions" + +#. js-lingui-id: SNf+4G +#: src/app/[locale]/customers/page.tsx +msgid "Ready to build" +msgstr "Ready to build" + +#. js-lingui-id: 9BL+5g +#: src/app/[locale]/product/page.tsx +#: src/app/[locale]/partners/page.tsx +msgid "Ready to grow" +msgstr "Ready to grow" + +#. js-lingui-id: 1d+b/h +#: src/app/[locale]/customers/_components/CustomersCaseStudySignoff.tsx +msgid "" +"Ready to grow\n" +"with Twenty?" +msgstr "" +"Ready to grow\n" +"with Twenty?" + +#. js-lingui-id: NIwjHh +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Real Estate" +msgstr "Real Estate" + +#. js-lingui-id: CH+Ona +#: src/app/[locale]/customers/page.tsx +msgid "Real stories from real teams about how they shaped Twenty to fit their workflow and accelerated their growth." +msgstr "Real stories from real teams about how they shaped Twenty to fit their workflow and accelerated their growth." + +#. js-lingui-id: 5O1eTm +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Real-time changes? That will be a premium surprise." +msgstr "Real-time changes? That will be a premium surprise." + +#. js-lingui-id: 4hc7hc +#: src/app/[locale]/product/three-cards.data.ts +msgid "Real-time data" +msgstr "Real-time data" + +#. js-lingui-id: dKQCrj +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Real-time is a state of mind, not a feature." +msgstr "Real-time is a state of mind, not a feature." + +#. js-lingui-id: Zhiuz9 +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Record quarter" +msgstr "Record quarter" + +#. js-lingui-id: PWlTvh +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Records" +msgstr "Records" + +#. js-lingui-id: rwWjWg +#: src/sections/Footer/data.ts +msgid "Release Notes" +msgstr "Release Notes" + +#. js-lingui-id: 5icoS1 +#: src/sections/Menu/data.ts +#: src/app/[locale]/releases/page.tsx +msgid "Releases" +msgstr "Releases" + +#. js-lingui-id: Bh0u1L +#: src/lib/website-routing/static-website-routes.ts +msgid "Releases | Twenty" +msgstr "Releases | Twenty" + +#. js-lingui-id: a/aGHe +#: src/app/[locale]/product/feature.data.ts +msgid "Rename, download, and delete attachments" +msgstr "Rename, download, and delete attachments" + +#. js-lingui-id: t9yxlZ +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Reports" +msgstr "Reports" + +#. js-lingui-id: SY/an2 +#: src/app/[locale]/product/feature.data.ts +msgid "Reports & Dashboards" +msgstr "Reports & Dashboards" + +#. js-lingui-id: 5Cs/CL +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Resale discounts & revenue share" +msgstr "Resale discounts & revenue share" + +#. js-lingui-id: s+MGs7 +#: src/sections/Menu/data.ts +msgid "Resources" +msgstr "Resources" + +#. js-lingui-id: oC2l7f +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "REST & GraphQL API" +msgstr "REST & GraphQL API" + +#. js-lingui-id: kx0s+n +#: src/app/[locale]/customers/netzero/page.tsx +msgid "Results" +msgstr "Results" + +#. js-lingui-id: 2itg0p +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Retro 2015" +msgstr "Retro 2015" + +#. js-lingui-id: OH7xLu +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Revenue share for referred customers" +msgstr "Revenue share for referred customers" + +#. js-lingui-id: frqduN +#: src/app/[locale]/product/feature.data.ts +msgid "Rich notes attached to records" +msgstr "Rich notes attached to records" + +#. js-lingui-id: /gaSVU +#: src/app/[locale]/customers/netzero/page.tsx +msgid "Roadmap" +msgstr "Roadmap" + +#. js-lingui-id: C77N5M +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +msgid "Roles & Permissions" +msgstr "Roles & Permissions" + +#. js-lingui-id: Wdh41P +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Row-level permissions" +msgstr "Row-level permissions" + +#. js-lingui-id: mRpuWW +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Running mock interview programs for hundreds of students sounds straightforward. In practice, universities and training institutes hit the same wall: registrations entered by hand, interview links sent one by one, faculty reviewing every session without scoring or classification. At real scale, it breaks." +msgstr "Running mock interview programs for hundreds of students sounds straightforward. In practice, universities and training institutes hit the same wall: registrations entered by hand, interview links sent one by one, faculty reviewing every session without scoring or classification. At real scale, it breaks." + +#. js-lingui-id: Burn4/ +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Salesfarce Add-on Center" +msgstr "Salesfarce Add-on Center" + +#. js-lingui-id: xTfEgV +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Salesfarce Pro" +msgstr "Salesfarce Pro" + +#. js-lingui-id: /UolBB +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Salesforce Classic" +msgstr "Salesforce Classic" + +#. js-lingui-id: l69E+u +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "Salesforce migration" +msgstr "Salesforce migration" + +#. js-lingui-id: CADlRK +#: src/app/[locale]/why-twenty/marquee.data.ts +msgid "Same CRM" +msgstr "Same CRM" + +#. js-lingui-id: 60i6S+ +#: src/app/[locale]/why-twenty/marquee.data.ts +msgid "Same output" +msgstr "Same output" + +#. js-lingui-id: xl2tJ9 +#: src/app/[locale]/why-twenty/marquee.data.ts +msgid "Same results" +msgstr "Same results" + +#. js-lingui-id: w9QWxz +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +msgid "SAML/OIDC SSO" +msgstr "SAML/OIDC SSO" + +#. js-lingui-id: elz5ka +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Saved / month" +msgstr "Saved / month" + +#. js-lingui-id: tigXYO +#: src/app/[locale]/customers/9dots/page.tsx +msgid "saved every month" +msgstr "saved every month" + +#. js-lingui-id: bifv6N +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Scale" +msgstr "Scale" + +#. js-lingui-id: rbgetd +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Scale without" +msgstr "Scale without" + +#. js-lingui-id: 842ybw +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Scale without breaking operations" +msgstr "Scale without breaking operations" + +#. js-lingui-id: HVOGoW +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Seats limit" +msgstr "Seats limit" + +#. js-lingui-id: a3LDKx +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Security" +msgstr "Security" + +#. js-lingui-id: q20Cda +#: src/app/[locale]/customers/page.tsx +msgid "See how teams" +msgstr "See how teams" + +#. js-lingui-id: aB+XpI +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "See more features" +msgstr "See more features" + +#. js-lingui-id: FY4ykg +#: src/sections/Menu/data.ts +msgid "See the latest release" +msgstr "See the latest release" + +#. js-lingui-id: JDp1bg +#: src/app/[locale]/product/three-cards.data.ts +msgid "See updates as they happen. Work with your team and agents seamlessly." +msgstr "See updates as they happen. Work with your team and agents seamlessly." + +#. js-lingui-id: 0kgOPB +#. placeholder {0}: latest.release +#: src/lib/releases/get-latest-release-preview.ts +msgid "See what shipped in {0}" +msgstr "See what shipped in {0}" + +#. js-lingui-id: wgNoIs +#: src/sections/Salesforce/components/PricingWindow.tsx +msgid "Select all" +msgstr "Select all" + +#. js-lingui-id: KP119R +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Select your team" +msgstr "Select your team" + +#. js-lingui-id: 6JhL+3 +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/alternative-partners/page.tsx +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "Self-hosted" +msgstr "Self-hosted" + +#. js-lingui-id: 5CWy3T +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Self-hosted means AC&T carries no vendor risk: no pricing model that can change, no platform that can disappear, no forced migration. The system is theirs." +msgstr "Self-hosted means AC&T carries no vendor risk: no pricing model that can change, no platform that can disappear, no forced migration. The system is theirs." + +#. js-lingui-id: 5WmQ5O +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "Self-hosted means control" +msgstr "Self-hosted means control" + +#. js-lingui-id: 6QPYP7 +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "self-hosted Twenty" +msgstr "self-hosted Twenty" + +#. js-lingui-id: GrBvv/ +#: src/app/[locale]/pricing/salesforce.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/enterprise/activate/page.tsx +msgid "Self-hosting" +msgstr "Self-hosting" + +#. js-lingui-id: YdhUoe +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Self-hosting, now for rent!" +msgstr "Self-hosting, now for rent!" + +#. js-lingui-id: NaHz2o +#: src/sections/Plans/components/SelfHostToggle.tsx +msgid "Selfhosting" +msgstr "Selfhosting" + +#. js-lingui-id: 2ZItPC +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Share Twenty with your audience and help shape the future of the #1 open source CRM. We're looking for creators, educators, and community builders who want to showcase great software." +msgstr "Share Twenty with your audience and help shape the future of the #1 open source CRM. We're looking for creators, educators, and community builders who want to showcase great software." + +#. js-lingui-id: 3VFzsh +#: src/app/[locale]/(home)/helped.data.ts +msgid "Ship a product on Twenty" +msgstr "Ship a product on Twenty" + +#. js-lingui-id: 6lGV3K +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Show less" +msgstr "Show less" + +#. js-lingui-id: mA6cL1 +#: src/sections/Faq/data.ts +msgid "Sign up for Cloud in under a minute and start your 30-day trial. For larger rollouts, our 4-hour Onboarding Packs or certified partners get you live in 1–2 weeks." +msgstr "Sign up for Cloud in under a minute and start your 30-day trial. For larger rollouts, our 4-hour Onboarding Packs or certified partners get you live in 1–2 weeks." + +#. js-lingui-id: AQK14J +#: src/app/[locale]/pricing/page.tsx +msgid "Simple" +msgstr "Simple" + +#. js-lingui-id: V9dFwD +#: src/sections/Footer/data.ts +msgid "Sitemap" +msgstr "Sitemap" + +#. js-lingui-id: t6ComU +#: src/app/[locale]/(home)/three-cards-feature.data.ts +msgid "Skip the clunky UX that always comes with custom." +msgstr "Skip the clunky UX that always comes with custom." + +#. js-lingui-id: bUmSwL +#: src/app/[locale]/(home)/three-cards-feature.data.ts +msgid "Smart patterns, shortcuts, and layouts make everyday tasks faster and easier to execute." +msgstr "Smart patterns, shortcuts, and layouts make everyday tasks faster and easier to execute." + +#. js-lingui-id: 7PyS12 +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Solutions Partner" +msgstr "Solutions Partner" + +#. js-lingui-id: e1+XW/ +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Solutions Partners" +msgstr "Solutions Partners" + +#. js-lingui-id: AVfp38 +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Some call this enterprise pricing. We prefer a CRM where API access, webhooks, and workflows don't show up as surprise add-ons." +msgstr "Some call this enterprise pricing. We prefer a CRM where API access, webhooks, and workflows don't show up as surprise add-ons." + +#. js-lingui-id: /UwJ/B +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Soon: earn revenue" +msgstr "Soon: earn revenue" + +#. js-lingui-id: B/mYo/ +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Source code access" +msgstr "Source code access" + +#. js-lingui-id: vnS6Rf +#: src/app/[locale]/pricing/salesforce.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "SSO" +msgstr "SSO" + +#. js-lingui-id: uQl22y +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +msgid "Standard support" +msgstr "Standard support" + +#. js-lingui-id: wxW2Sv +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Start automating at huge scale!" +msgstr "Start automating at huge scale!" + +#. js-lingui-id: F02wii +#: src/app/[locale]/product/page.tsx +#: src/app/[locale]/pricing/page.tsx +#: src/app/[locale]/partners/page.tsx +#: src/app/[locale]/customers/page.tsx +#: src/app/[locale]/(home)/page.tsx +msgid "Start building, with Twenty" +msgstr "Start building, with Twenty" + +#. js-lingui-id: XO628f +#: src/sections/Plans/components/Card.tsx +msgid "Start for free" +msgstr "Start for free" + +#. js-lingui-id: sBOFB0 +#: src/app/[locale]/pricing/hero.data.ts +msgid "" +"Start your free trial today\n" +"without credit card." +msgstr "" +"Start your free trial today\n" +"without credit card." + +#. js-lingui-id: FT+TW7 +#: src/app/[locale]/(home)/home-stepper.data.ts +msgid "Stay in control with our" +msgstr "Stay in control with our" + +#. js-lingui-id: j9c8rb +#: src/app/[locale]/product/three-cards.data.ts +msgid "Stay in Flow" +msgstr "Stay in Flow" + +#. js-lingui-id: bxgoJR +#: src/sections/Menu/data.ts +msgid "Step-by-step guides and playbooks to help your team get the most out of their workspace." +msgstr "Step-by-step guides and playbooks to help your team get the most out of their workspace." + +#. js-lingui-id: RMK92/ +#: src/app/[locale]/product/page.tsx +#: src/app/[locale]/pricing/page.tsx +#: src/app/[locale]/partners/page.tsx +#: src/app/[locale]/customers/page.tsx +#: src/app/[locale]/(home)/page.tsx +msgid "Stop fighting custom." +msgstr "Stop fighting custom." + +#. js-lingui-id: UCLArd +#: src/app/[locale]/product/three-cards.data.ts +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "Stop settling for trade-offs." +msgstr "Stop settling for trade-offs." + +#. js-lingui-id: lm4alq +#: src/app/[locale]/customers/act-education/page.tsx +msgid "structure" +msgstr "structure" + +#. js-lingui-id: Sv4BSp +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Subdomain (yourco.twenty.com)" +msgstr "Subdomain (yourco.twenty.com)" + +#. js-lingui-id: H5/rZQ +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Submit application" +msgstr "Submit application" + +#. js-lingui-id: MDqQmP +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Submitting…" +msgstr "Submitting…" + +#. js-lingui-id: XYLcNv +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Support" +msgstr "Support" + +#. js-lingui-id: 3w+Aox +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Table, Kanban, Calendar" +msgstr "Table, Kanban, Calendar" + +#. js-lingui-id: 9M02G5 +#: src/app/[locale]/product/stepper.data.ts +msgid "Tailor record pages, menus, and views" +msgstr "Tailor record pages, menus, and views" + +#. js-lingui-id: JAKtcG +#: src/sections/Footer/data.ts +#: src/app/[locale]/product/page.tsx +#: src/app/[locale]/product/page.tsx +#: src/app/[locale]/pricing/page.tsx +#: src/app/[locale]/partners/page.tsx +#: src/app/[locale]/partners/components/PartnerApplication/PartnerSignoffCtas.tsx +#: src/app/[locale]/customers/page.tsx +#: src/app/[locale]/customers/page.tsx +#: src/app/[locale]/customers/_components/CustomersCaseStudySignoff.tsx +#: src/app/[locale]/(home)/page.tsx +#: src/app/[locale]/(home)/page.tsx +msgid "Talk to us" +msgstr "Talk to us" + +#. js-lingui-id: GWMpL3 +#: src/app/[locale]/product/feature.data.ts +msgid "Tasks & Activities" +msgstr "Tasks & Activities" + +#. js-lingui-id: Ye3KKA +#: src/sections/Menu/data.ts +msgid "Team up with a Twenty expert" +msgstr "Team up with a Twenty expert" + +#. js-lingui-id: vsdUaL +#: src/app/[locale]/releases/page.tsx +msgid "Technical notes" +msgstr "Technical notes" + +#. js-lingui-id: 8dX1MJ +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Technology Partner" +msgstr "Technology Partner" + +#. js-lingui-id: EeZsnh +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Technology Partners" +msgstr "Technology Partners" + +#. js-lingui-id: gNW5s8 +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Tell us about the custom solutions or integrations you plan to build." +msgstr "Tell us about the custom solutions or integrations you plan to build." + +#. js-lingui-id: 1ZwmkP +#: src/app/[locale]/customers/netzero/page.tsx +msgid "template" +msgstr "template" + +#. js-lingui-id: mvP/25 +#: src/sections/Footer/data.ts +msgid "Terms and Conditions" +msgstr "Terms and Conditions" + +#. js-lingui-id: BEX1RA +#: src/lib/website-routing/static-website-routes.ts +msgid "Terms of Service | Twenty" +msgstr "Terms of Service | Twenty" + +#. js-lingui-id: iqG74V +#: src/lib/website-routing/static-website-routes.ts +msgid "Terms of Service for Twenty.com PBC, including use of Twenty.com, sub-domains, and related services." +msgstr "Terms of Service for Twenty.com PBC, including use of Twenty.com, sub-domains, and related services." + +#. js-lingui-id: dn9a7V +#: src/app/[locale]/product/page.tsx +msgid "that" +msgstr "that" + +#. js-lingui-id: FjkPYg +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "That opened the door to something more powerful. Justin built a custom front end that pulls live data from those systems into a single view, tailored to each role. When a proposal is won, what used to require four separate people manually setting up instances across four different tools now happens in a single click, drawing on data collected in Twenty across the full opportunity lifecycle. It is another shift toward higher-value work for clients." +msgstr "That opened the door to something more powerful. Justin built a custom front end that pulls live data from those systems into a single view, tailored to each role. When a proposal is won, what used to require four separate people manually setting up instances across four different tools now happens in a single click, drawing on data collected in Twenty across the full opportunity lifecycle. It is another shift toward higher-value work for clients." + +#. js-lingui-id: 8S4psU +#: src/app/[locale]/customers/9dots/page.tsx +msgid "That works until you need to understand the business underneath. Which deals are stuck? Where are leads coming from? What is the close rate? With spreadsheets and a legacy custom CRM that could not keep up, those questions were nearly impossible to answer." +msgstr "That works until you need to understand the business underneath. Which deals are stuck? Where are leads coming from? What is the close rate? With spreadsheets and a legacy custom CRM that could not keep up, those questions were nearly impossible to answer." + +#. js-lingui-id: yWOGs1 +#: src/app/[locale]/(home)/page.tsx +msgid "that's quick to flex" +msgstr "that's quick to flex" + +#. js-lingui-id: rTqABu +#: src/lib/website-routing/static-website-routes.ts +msgid "The #1 open source CRM for modern teams. Modular, scalable, and built to fit your business." +msgstr "The #1 open source CRM for modern teams. Modular, scalable, and built to fit your business." + +#. js-lingui-id: G2IgPj +#: src/app/[locale]/customers/netzero/page.tsx +msgid "The bet is" +msgstr "The bet is" + +#. js-lingui-id: NT2bSJ +#: src/app/[locale]/customers/netzero/page.tsx +msgid "The bet is paying off" +msgstr "The bet is paying off" + +#. js-lingui-id: Xmr/wH +#: src/app/[locale]/customers/9dots/page.tsx +msgid "the business" +msgstr "the business" + +#. js-lingui-id: Wie492 +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "The business development team finally had the CRM they had been asking for. Adoption came naturally: their data was already there when they logged in." +msgstr "The business development team finally had the CRM they had been asking for. Adoption came naturally: their data was already there when they logged in." + +#. js-lingui-id: 2kHqXc +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "The CEO had resisted bringing in a CRM for years. The business development team had no experience using one, and the licensing costs of well-known CRMs like Salesforce or HubSpot were hard to justify without any guarantee of adoption: CRMs are only as good as the maintenance of the data inside them." +msgstr "The CEO had resisted bringing in a CRM for years. The business development team had no experience using one, and the licensing costs of well-known CRMs like Salesforce or HubSpot were hard to justify without any guarantee of adoption: CRMs are only as good as the maintenance of the data inside them." + +#. js-lingui-id: 5DpEh7 +#: src/app/[locale]/customers/9dots/page.tsx +msgid "The CRM as a" +msgstr "The CRM as a" + +#. js-lingui-id: 2sfVd8 +#: src/app/[locale]/customers/9dots/page.tsx +msgid "The CRM as a control hub" +msgstr "The CRM as a control hub" + +#. js-lingui-id: XlFsoH +#: src/app/[locale]/customers/netzero/page.tsx +msgid "The early bet on the architecture is holding, and upcoming AI features are expected to make it even more relevant." +msgstr "The early bet on the architecture is holding, and upcoming AI features are expected to make it even more relevant." + +#. js-lingui-id: kCUcnD +#: src/app/[locale]/partners/testimonials.data.ts +msgid "The flexibility is just amazing. Literally, there's nothing you cannot do. You can create objects, access everything through the API, pull notes and send them to the portal. Try doing that in HubSpot. No way. It's the true ability to build exactly what's actually needed." +msgstr "The flexibility is just amazing. Literally, there's nothing you cannot do. You can create objects, access everything through the API, pull notes and send them to the portal. Try doing that in HubSpot. No way. It's the true ability to build exactly what's actually needed." + +#. js-lingui-id: MaHqAc +#: src/app/[locale]/(home)/testimonials.data.ts +msgid "The flexibility is really what made the difference. Our needs evolve very fast. I discover a new need and in two clicks I can address it. That is a real advantage when you are moving quickly." +msgstr "The flexibility is really what made the difference. Our needs evolve very fast. I discover a new need and in two clicks I can address it. That is a real advantage when you are moving quickly." + +#. js-lingui-id: Q5iZ9Q +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "The flexibility to wire this together, without outside help and without fighting the platform, is what made it possible for a single person to stand up and maintain a connected stack across an entire consultancy." +msgstr "The flexibility to wire this together, without outside help and without fighting the platform, is what made it possible for a single person to stand up and maintain a connected stack across an entire consultancy." + +#. js-lingui-id: l0eUFC +#: src/app/[locale]/customers/9dots/page.tsx +msgid "The full rollout landed in July 2025. Since then, Nine Dots built a Smart Assistant on top of the system, nudging agents with tasks, reminders, and on-demand market analysis. Some agents never open Twenty directly, yet they are powered by it, outperforming peers on manual processes alone. By Q1 2026, Homeseller had recorded its best sales quarter ever." +msgstr "The full rollout landed in July 2025. Since then, Nine Dots built a Smart Assistant on top of the system, nudging agents with tasks, reminders, and on-demand market analysis. Some agents never open Twenty directly, yet they are powered by it, outperforming peers on manual processes alone. By Q1 2026, Homeseller had recorded its best sales quarter ever." + +#. js-lingui-id: SiW1pC +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "the future of CRM" +msgstr "the future of CRM" + +#. js-lingui-id: Uslh7V +#: src/app/[locale]/why-twenty/page.tsx +msgid "The future of CRM is built," +msgstr "The future of CRM is built," + +#. js-lingui-id: sm10Rg +#: src/app/[locale]/(home)/problem.data.ts +msgid "The Giant Monolith" +msgstr "The Giant Monolith" + +#. js-lingui-id: nFB8I1 +#: src/app/[locale]/(home)/problem.data.ts +msgid "The In-house Burden" +msgstr "The In-house Burden" + +#. js-lingui-id: ZNzKXV +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "The key decision was not to build everything from scratch. Twenty covers the data model, permissions, authentication, and workflow engine, the parts that would have taken months to rebuild, so the team could focus on product-specific logic." +msgstr "The key decision was not to build everything from scratch. Twenty covers the data model, permissions, authentication, and workflow engine, the parts that would have taken months to rebuild, so the team could focus on product-specific logic." + +#. js-lingui-id: q1kKLq +#: src/app/[locale]/why-twenty/editorial-three.data.ts +msgid "The opportunity" +msgstr "The opportunity" + +#. js-lingui-id: ZgWQl4 +#: src/app/[locale]/customers/act-education/page.tsx +msgid "the overhead" +msgstr "the overhead" + +#. js-lingui-id: 2NJdyz +#: src/app/[locale]/(home)/problem.data.ts +msgid "The Problem." +msgstr "The Problem." + +#. js-lingui-id: Q8D9Lf +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "the product" +msgstr "the product" + +#. js-lingui-id: 3/wAFg +#: src/app/[locale]/customers/w3villa/page.tsx +#: src/app/[locale]/customers/w3villa/page.tsx +#: src/app/[locale]/customers/act-education/page.tsx +#: src/app/[locale]/customers/act-education/page.tsx +#: src/app/[locale]/customers/9dots/page.tsx +#: src/app/[locale]/customers/9dots/page.tsx +msgid "The result" +msgstr "The result" + +#. js-lingui-id: nAWeyi +#: src/app/[locale]/customers/act-education/page.tsx +msgid "The result is a system that fits how AC&T already worked, instead of the other way around." +msgstr "The result is a system that fits how AC&T already worked, instead of the other way around." + +#. js-lingui-id: tfG/xw +#: src/app/[locale]/customers/netzero/page.tsx +msgid "The right" +msgstr "The right" + +#. js-lingui-id: nbHA9N +#: src/app/[locale]/customers/netzero/page.tsx +msgid "The right foundation" +msgstr "The right foundation" + +#. js-lingui-id: omVXTg +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "The self-hosted setup means Alternative Partners owns the full stack: no vendor access to their data, no dependency on a SaaS pricing model, full control over how the system evolves. The migration was fast because of AI; the result is durable because the stack is open source." +msgstr "The self-hosted setup means Alternative Partners owns the full stack: no vendor access to their data, no dependency on a SaaS pricing model, full control over how the system evolves. The migration was fast because of AI; the result is durable because the stack is open source." + +#. js-lingui-id: 1tN3qf +#: src/app/[locale]/why-twenty/editorial-one.data.ts +msgid "The shift" +msgstr "The shift" + +#. js-lingui-id: AnGOwN +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "The situation" +msgstr "The situation" + +#. js-lingui-id: u0uFxg +#: src/app/[locale]/(home)/testimonials.data.ts +msgid "They are the real sales" +msgstr "They are the real sales" + +#. js-lingui-id: 53RXLB +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "They call it customer loyalty. We call it a very affectionate cage." +msgstr "They call it customer loyalty. We call it a very affectionate cage." + +#. js-lingui-id: RhmZMe +#: src/app/[locale]/customers/act-education/page.tsx +msgid "They did not just replace a tool. They took back ownership of how their business runs." +msgstr "They did not just replace a tool. They took back ownership of how their business runs." + +#. js-lingui-id: 5YzqyT +#: src/app/[locale]/customers/act-education/page.tsx +msgid "They evaluated Salesforce, Zoho, Pipedrive, and SuiteCRM. Each came with the same tradeoffs: too expensive, too rigid, or too generic, and none fixed the underlying problem. They were still renting a structure they did not control." +msgstr "They evaluated Salesforce, Zoho, Pipedrive, and SuiteCRM. Each came with the same tradeoffs: too expensive, too rigid, or too generic, and none fixed the underlying problem. They were still renting a structure they did not control." + +#. js-lingui-id: 2y6W+f +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "to rule them all" +msgstr "to rule them all" + +#. js-lingui-id: 5eFktS +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Tool integration" +msgstr "Tool integration" + +#. js-lingui-id: xv7HSr +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "total per month with fixed cost" +msgstr "total per month with fixed cost" + +#. js-lingui-id: hmu++i +#: src/app/[locale]/product/feature.data.ts +msgid "Track amount and close date" +msgstr "Track amount and close date" + +#. js-lingui-id: POzmFl +#: src/sections/Menu/data.ts +#: src/lib/releases/get-latest-release-preview.ts +msgid "Track every release with changelogs, highlights and demos of the newest features." +msgstr "Track every release with changelogs, highlights and demos of the newest features." + +#. js-lingui-id: yiYtW0 +#: src/lib/website-routing/static-website-routes.ts +#: src/app/[locale]/product/hero.data.ts +msgid "Track relationships, manage pipelines, and take action quickly with a CRM that feels intuitive from day one." +msgstr "Track relationships, manage pipelines, and take action quickly with a CRM that feels intuitive from day one." + +#. js-lingui-id: XzfkgA +#: src/app/[locale]/(home)/page.tsx +msgid "tradeoffs" +msgstr "tradeoffs" + +#. js-lingui-id: ccIZU2 +#: src/app/[locale]/pricing/page.tsx +msgid "Trust the n°1 CRM," +msgstr "Trust the n°1 CRM," + +#. js-lingui-id: KtyN0b +#: src/sections/TrustedBy/data.ts +msgid "trusted by" +msgstr "trusted by" + +#. js-lingui-id: D5Kf4t +#: src/app/[locale]/why-twenty/editorial-four.data.ts +msgid "Tuesday your team learns that deals with a technical champion close 3x faster. Wednesday you add the field, wire up the scoring, adjust the workflow. By Thursday your agents are acting on it. That feedback loop is the edge. And it only works if the CRM is yours." +msgstr "Tuesday your team learns that deals with a technical champion close 3x faster. Wednesday you add the field, wire up the scoring, adjust the workflow. By Thursday your agents are acting on it. That feedback loop is the edge. And it only works if the CRM is yours." + +#. js-lingui-id: Sl2dv/ +#: src/lib/website-routing/static-website-routes.ts +msgid "Twenty | #1 open source CRM" +msgstr "Twenty | #1 open source CRM" + +#. js-lingui-id: G4fKLP +#: src/app/[locale]/partners/testimonials.data.ts +msgid "Twenty Apps opens the door to building products, not just implementations. For example, we're developing a WhatsApp Business integration that any Twenty’s client could get. That's a recurring revenue stream we wouldn't have if we were just configuring someone else's platform." +msgstr "Twenty Apps opens the door to building products, not just implementations. For example, we're developing a WhatsApp Business integration that any Twenty’s client could get. That's a recurring revenue stream we wouldn't have if we were just configuring someone else's platform." + +#. js-lingui-id: mkTfZ1 +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Twenty as the" +msgstr "Twenty as the" + +#. js-lingui-id: 9gf3bc +#: src/lib/customers/case-study-catalog.ts +msgid "Twenty as the API backbone" +msgstr "Twenty as the API backbone" + +#. js-lingui-id: zj0CA+ +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Twenty as the API backbone of a go-to-market stack | Elevate Consulting" +msgstr "Twenty as the API backbone of a go-to-market stack | Elevate Consulting" + +#. js-lingui-id: 0qyMhk +#: src/app/[locale]/(home)/hero.data.ts +msgid "Twenty gives technical teams the building blocks for a custom CRM that meets complex business needs and quickly adapts as the business evolves." +msgstr "Twenty gives technical teams the building blocks for a custom CRM that meets complex business needs and quickly adapts as the business evolves." + +#. js-lingui-id: jVhq41 +#: src/app/[locale]/partners/testimonials.data.ts +msgid "Twenty gives you the kind of flexibility that actually changes what you can offer your clients. The dev experience is clean, the APIs are open, and when something needs to be customized, you can just do it. There's no fighting the platform." +msgstr "Twenty gives you the kind of flexibility that actually changes what you can offer your clients. The dev experience is clean, the APIs are open, and when something needs to be customized, you can just do it. There's no fighting the platform." + +#. js-lingui-id: x/I85y +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Twenty is not only where CRM data lives. It is the API backbone that makes the rest of the stack possible." +msgstr "Twenty is not only where CRM data lives. It is the API backbone that makes the rest of the stack possible." + +#. js-lingui-id: K+zzSQ +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Twenty lets us build a CRM around the business and not the business around the CRM." +msgstr "Twenty lets us build a CRM around the business and not the business around the CRM." + +#. js-lingui-id: AxT+g4 +#: src/app/[locale]/(home)/three-cards-feature.data.ts +msgid "Twenty makes it simple. It's clean, intuitive, and built to feel like Notion." +msgstr "Twenty makes it simple. It's clean, intuitive, and built to feel like Notion." + +#. js-lingui-id: V1lV7B +#: src/sections/Plans/data.ts +msgid "Twenty team support" +msgstr "Twenty team support" + +#. js-lingui-id: WN9ufk +#: src/app/[locale]/product/page.tsx +msgid "Twenty?" +msgstr "Twenty?" + +#. js-lingui-id: EIU345 +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Two-factor authentication" +msgstr "Two-factor authentication" + +#. js-lingui-id: uxuy4l +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "UI theme" +msgstr "UI theme" + +#. js-lingui-id: jqzUyM +#: src/app/[locale]/pricing/salesforce.data.ts +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Unavailable" +msgstr "Unavailable" + +#. js-lingui-id: Hix/m6 +#: src/app/[locale]/product/feature.data.ts +msgid "Unified timeline (emails, events, tasks, notes, files)" +msgstr "Unified timeline (emails, events, tasks, notes, files)" + +#. js-lingui-id: NIuIk1 +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Unlimited" +msgstr "Unlimited" + +#. js-lingui-id: LF0tzk +#: src/sections/Plans/data.ts +msgid "Up to 50M automation credits/year" +msgstr "Up to 50M automation credits/year" + +#. js-lingui-id: RDRePw +#: src/sections/Plans/data.ts +msgid "Up to 5M automation credits/month" +msgstr "Up to 5M automation credits/month" + +#. js-lingui-id: Roaswv +#: src/sections/Menu/data.ts +#: src/sections/Footer/data.ts +msgid "User Guide" +msgstr "User Guide" + +#. js-lingui-id: 3XIgKU +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "User roles" +msgstr "User roles" + +#. js-lingui-id: BAzC1v +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "View types" +msgstr "View types" + +#. js-lingui-id: mHtVst +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Visualize your customers on a map!" +msgstr "Visualize your customers on a map!" + +#. js-lingui-id: CsHuOr +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "VP of Engineering" +msgstr "VP of Engineering" + +#. js-lingui-id: uRh799 +#: src/app/[locale]/(home)/testimonials.data.ts +msgid "VP of Engineering at W3villa Technologies" +msgstr "VP of Engineering at W3villa Technologies" + +#. js-lingui-id: w2INie +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "VP of Engineering, W3villa Technologies" +msgstr "VP of Engineering, W3villa Technologies" + +#. js-lingui-id: s0tU5h +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "W3Grads" +msgstr "W3Grads" + +#. js-lingui-id: R8BueT +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "W3villa built W3Grads (w3grads.com), an AI-powered mock interview platform for universities and training institutes, using Twenty as its operational backbone." +msgstr "W3villa built W3Grads (w3grads.com), an AI-powered mock interview platform for universities and training institutes, using Twenty as its operational backbone." + +#. js-lingui-id: 7hHsoV +#: src/app/[locale]/(home)/helped.data.ts +msgid "W3villa built W3Grads for AI mock interviews at scale, with Twenty as the operational backbone." +msgstr "W3villa built W3Grads for AI mock interviews at scale, with Twenty as the operational backbone." + +#. js-lingui-id: QBOUnd +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "W3villa shipped W3Grads on Twenty for AI interviews, scoring, and institution-scale workflows without rebuilding CRM plumbing." +msgstr "W3villa shipped W3Grads on Twenty for AI interviews, scoring, and institution-scale workflows without rebuilding CRM plumbing." + +#. js-lingui-id: 7q5Vjn +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "W3villa Technologies" +msgstr "W3villa Technologies" + +#. js-lingui-id: ZabLEt +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "W3villa Technologies set out to solve it properly, not with a workaround, but with a product." +msgstr "W3villa Technologies set out to solve it properly, not with a workaround, but with a product." + +#. js-lingui-id: 3v2ipf +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "W3villa used Twenty as a production-grade framework for the data model, permissions, authentication, and workflow engine they would otherwise have rebuilt themselves." +msgstr "W3villa used Twenty as a production-grade framework for the data model, permissions, authentication, and workflow engine they would otherwise have rebuilt themselves." + +#. js-lingui-id: 8CHatv +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "We could not submit your application. Please try again in a moment." +msgstr "We could not submit your application. Please try again in a moment." + +#. js-lingui-id: MjWeUE +#: src/app/[locale]/(home)/testimonials.data.ts +msgid "We didn't want to patch over the problem. We wanted to build something institutions could rely on at scale, and that meant starting from a foundation solid enough to support the full complexity of what we had in mind." +msgstr "We didn't want to patch over the problem. We wanted to build something institutions could rely on at scale, and that meant starting from a foundation solid enough to support the full complexity of what we had in mind." + +#. js-lingui-id: gdnUik +#: src/app/[locale]/partners/hero.data.ts +msgid "We're building the #1 open source CRM, but we can't do it alone. Join our partner ecosystem and grow with us." +msgstr "We're building the #1 open source CRM, but we can't do it alone. Join our partner ecosystem and grow with us." + +#. js-lingui-id: v1kQyJ +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Webhooks" +msgstr "Webhooks" + +#. js-lingui-id: ZhnM/K +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Webhooks (Change Data Capture)" +msgstr "Webhooks (Change Data Capture)" + +#. js-lingui-id: jBX2tZ +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Website or github link *" +msgstr "Website or github link *" + +#. js-lingui-id: Z34FQq +#: src/app/[locale]/customers/netzero/page.tsx +msgid "What convinced Olivier was the flexibility of the platform and where it was headed. Even when initial needs were basic record-keeping, he still needed a custom data model with granular permissions to manage the wide range of NetZero activities. He also needed a system that could adapt quickly to a fast-iteration company." +msgstr "What convinced Olivier was the flexibility of the platform and where it was headed. Even when initial needs were basic record-keeping, he still needed a custom data model with granular permissions to manage the wide range of NetZero activities. He also needed a system that could adapt quickly to a fast-iteration company." + +#. js-lingui-id: phiBjc +#: src/sections/Faq/data.ts +msgid "What does Twenty cost?" +msgstr "What does Twenty cost?" + +#. js-lingui-id: WVQaGB +#: src/app/[locale]/customers/netzero/page.tsx +msgid "What is coming in April 2026 is what he has been waiting for: AI-assisted workflow creation, describing what he needs and iterating from there instead of building complex logic from scratch. For a founder who runs the CRM himself, that changes what is realistically possible." +msgstr "What is coming in April 2026 is what he has been waiting for: AI-assisted workflow creation, describing what he needs and iterating from there instead of building complex logic from scratch. For a founder who runs the CRM himself, that changes what is realistically possible." + +#. js-lingui-id: iOAq7n +#: src/app/[locale]/customers/elevate-consulting/page.tsx +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "What is next" +msgstr "What is next" + +#. js-lingui-id: tWd6MT +#: src/app/[locale]/customers/netzero/page.tsx +msgid "What is planned is significant. NetZero has a data lake, online forms, and multiple internal systems that he wants to connect to Twenty. The pipes are there; the next step is automations that tie them together." +msgstr "What is planned is significant. NetZero has a data lake, online forms, and multiple internal systems that he wants to connect to Twenty. The pipes are there; the next step is automations that tie them together." + +#. js-lingui-id: aYRqmz +#: src/app/[locale]/why-twenty/editorial-four.data.ts +msgid "What this means" +msgstr "What this means" + +#. js-lingui-id: 1kfjxX +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Whatever came next had to be something they could own." +msgstr "Whatever came next had to be something they could own." + +#. js-lingui-id: cs6VpJ +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "When a student registers via QR at a campus event, the system assigns a plan, generates an interview session, and sends a link. The AI conducts the interview, scores the candidate, and classifies the result. Faculty see where each student stands without manually reviewing every session. Building and iterating on these workflows was faster with AI in the loop." +msgstr "When a student registers via QR at a campus event, the system assigns a plan, generates an interview session, and sends a link. The AI conducts the interview, scores the candidate, and classifies the result. Faculty see where each student stands without manually reviewing every session. Building and iterating on these workflows was faster with AI in the loop." + +#. js-lingui-id: I/oPrY +#: src/app/[locale]/customers/9dots/page.tsx +msgid "When the channel is" +msgstr "When the channel is" + +#. js-lingui-id: JglRo/ +#: src/app/[locale]/customers/9dots/page.tsx +msgid "When the channel is the business" +msgstr "When the channel is the business" + +#. js-lingui-id: ee4xKk +#: src/app/[locale]/customers/act-education/page.tsx +msgid "When the vendor" +msgstr "When the vendor" + +#. js-lingui-id: DajeYD +#: src/app/[locale]/customers/act-education/page.tsx +msgid "When the vendor pulled the plug" +msgstr "When the vendor pulled the plug" + +#. js-lingui-id: 6LeUXo +#: src/lib/customers/case-study-catalog.ts +msgid "When your CRM" +msgstr "When your CRM" + +#. js-lingui-id: X1gdzB +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "When your CRM is" +msgstr "When your CRM is" + +#. js-lingui-id: Svuneu +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "When your CRM is the product: W3Grads on Twenty | W3villa Technologies" +msgstr "When your CRM is the product: W3Grads on Twenty | W3villa Technologies" + +#. js-lingui-id: KRKrw8 +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Which partner program is right for you?" +msgstr "Which partner program is right for you?" + +#. js-lingui-id: bGN8u+ +#: src/app/[locale]/customers/netzero/page.tsx +msgid "While NetZero still runs a second CRM in parallel for WhatsApp-heavy operations with farmers in Brazil, they expect to migrate all of it to Twenty as features and the ecosystem grow. Already, their structured, multinational pipeline is powered by Twenty." +msgstr "While NetZero still runs a second CRM in parallel for WhatsApp-heavy operations with farmers in Brazil, they expect to migrate all of it to Twenty as features and the ecosystem grow. Already, their structured, multinational pipeline is powered by Twenty." + +#. js-lingui-id: DJlSwA +#: src/sections/Menu/data.ts +msgid "Why" +msgstr "Why" + +#. js-lingui-id: 4sOVu+ +#: src/sections/Footer/data.ts +msgid "Why Twenty" +msgstr "Why Twenty" + +#. js-lingui-id: kwvfYA +#: src/lib/website-routing/static-website-routes.ts +msgid "Why Twenty | Twenty" +msgstr "Why Twenty | Twenty" + +#. js-lingui-id: aycITw +#: src/app/[locale]/product/page.tsx +#: src/app/[locale]/(home)/page.tsx +msgid "with" +msgstr "with" + +#. js-lingui-id: NI3KID +#: src/app/[locale]/product/page.tsx +msgid "with no-code" +msgstr "with no-code" + +#. js-lingui-id: OKVlnc +#: src/app/[locale]/customers/netzero/page.tsx +msgid "With Twenty, when a new need appears, he can address it himself: no developer required, no support ticket." +msgstr "With Twenty, when a new need appears, he can address it himself: no developer required, no support ticket." + +#. js-lingui-id: JRUWoH +#: src/app/[locale]/partners/page.tsx +msgid "with Twenty?" +msgstr "with Twenty?" + +#. js-lingui-id: KAB1uP +#: src/app/[locale]/customers/netzero/page.tsx +msgid "with you" +msgstr "with you" + +#. js-lingui-id: Lr53K3 +#: src/app/[locale]/(home)/home-stepper.data.ts +msgid "without friction" +msgstr "without friction" + +#. js-lingui-id: OX5bbs +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Work email *" +msgstr "Work email *" + +#. js-lingui-id: woYYQq +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Workflows" +msgstr "Workflows" + +#. js-lingui-id: 1ie9Dm +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Workflows that" +msgstr "Workflows that" + +#. js-lingui-id: AzqmMJ +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Workflows that actually get used" +msgstr "Workflows that actually get used" + +#. js-lingui-id: pmUArF +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Workspace" +msgstr "Workspace" + +#. js-lingui-id: zkWmBh +#: src/sections/Plans/components/BillingToggle.tsx +msgid "Yearly" +msgstr "Yearly" + +#. js-lingui-id: l75CjT +#: src/sections/PlanTable/components/Content.tsx +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Yes" +msgstr "Yes" + +#. js-lingui-id: bo6pUs +#: src/sections/Faq/data.ts +msgid "Yes, with our Apps framework. Scaffold an extension with `npx create-twenty-app` and ship custom objects, server-side logic functions, React components that render inside Twenty’s UI, AI skills and agents, views, and navigation, all in TypeScript, deployable to any workspace." +msgstr "Yes, with our Apps framework. Scaffold an extension with `npx create-twenty-app` and ship custom objects, server-side logic functions, React components that render inside Twenty’s UI, AI skills and agents, views, and navigation, all in TypeScript, deployable to any workspace." + +#. js-lingui-id: yIcBW6 +#: src/sections/Faq/data.ts +msgid "Yes. Every Cloud workspace ships with a native MCP server. Connect your AI assistant via OAuth and it can read and write your CRM data in natural language." +msgstr "Yes. Every Cloud workspace ships with a native MCP server. Connect your AI assistant via OAuth and it can read and write your CRM data in natural language." + +#. js-lingui-id: 8xYMYQ +#: src/sections/Faq/data.ts +msgid "Yes. Import your data via CSV, or use our API for 50,000+ records. Our partners can handle the full migration for you." +msgstr "Yes. Import your data via CSV, or use our API for 50,000+ records. Our partners can handle the full migration for you." + +#. js-lingui-id: bnuv9n +#: src/sections/Faq/data.ts +msgid "Yes. Twenty is the #1 open source CRM on GitHub. You can self-host to fully own your infrastructure, or run it on our managed cloud for a zero-ops setup." +msgstr "Yes. Twenty is the #1 open source CRM on GitHub. You can self-host to fully own your infrastructure, or run it on our managed cloud for a zero-ops setup." + +#. js-lingui-id: LJ3sTb +#: src/app/[locale]/why-twenty/editorial-four.data.ts +msgid "You don't buy your deployment pipeline off the shelf. You don't rent your data warehouse from a vendor who decides the schema. You build it, you own it, you iterate on it every week. CRM is going the same way. The teams that treat it as infrastructure they own will compound an advantage every quarter." +msgstr "You don't buy your deployment pipeline off the shelf. You don't rent your data warehouse from a vendor who decides the schema. You build it, you own it, you iterate on it every week. CRM is going the same way. The teams that treat it as infrastructure they own will compound an advantage every quarter." + +#. js-lingui-id: SfJVcK +#: src/app/[locale]/enterprise/activate/page.tsx +msgid "Your checkout is complete. Follow the steps below to copy your license key into your Twenty instance." +msgstr "Your checkout is complete. Follow the steps below to copy your license key into your Twenty instance." + +#. js-lingui-id: krHgj1 +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Your enterprise key" +msgstr "Your enterprise key" + +#. js-lingui-id: +dxjCu +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Your enterprise license has been activated successfully." +msgstr "Your enterprise license has been activated successfully." + +#. js-lingui-id: pEQiCY +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Your name *" +msgstr "Your name *" + +#. js-lingui-id: 9Apzz7 +#: src/app/[locale]/customers/page.tsx +msgid "your own story?" +msgstr "your own story?" + +#. js-lingui-id: wHjFOw +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Zero" +msgstr "Zero" + +#. js-lingui-id: xlFvlS +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Zero manual work" +msgstr "Zero manual work" + +#. js-lingui-id: ZvEoMq +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Zero manual work at the core. Full automation. Built on Twenty." +msgstr "Zero manual work at the core. Full automation. Built on Twenty." diff --git a/packages/twenty-website-new/src/locales/es-ES.po b/packages/twenty-website-new/src/locales/es-ES.po deleted file mode 100644 index 6c76cf4efb4..00000000000 --- a/packages/twenty-website-new/src/locales/es-ES.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: es-ES\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/fi-FI.po b/packages/twenty-website-new/src/locales/fi-FI.po deleted file mode 100644 index b627312a5a1..00000000000 --- a/packages/twenty-website-new/src/locales/fi-FI.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: fi-FI\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/fr-FR.po b/packages/twenty-website-new/src/locales/fr-FR.po index 28fb8044645..4737cdfb755 100644 --- a/packages/twenty-website-new/src/locales/fr-FR.po +++ b/packages/twenty-website-new/src/locales/fr-FR.po @@ -1,6 +1,6 @@ msgid "" msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" +"POT-Creation-Date: 2026-04-29 21:38+0500\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -12,3 +12,3628 @@ msgstr "" "Last-Translator: \n" "Language-Team: \n" "Plural-Forms: \n" + +#. js-lingui-id: BUIzFF +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "-33% off" +msgstr "" + +#. js-lingui-id: q0ur5+ +#. placeholder {0}: entry.hero.author +#: src/lib/website-routing/customer-story-routes.ts +msgid "{0} | Twenty Customer Story" +msgstr "" + +#. js-lingui-id: icND7o +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "/ seat / month - billed yearly" +msgstr "" + +#. js-lingui-id: XUqxHW +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +msgid "/user/month" +msgstr "" + +#. js-lingui-id: ojimXJ +#: src/sections/Footer/data.ts +msgid "© 2026 – Twenty" +msgstr "" + +#. js-lingui-id: foGG8A +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "+$105/user per month" +msgstr "" + +#. js-lingui-id: Bsq9GJ +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "+$35/user per month" +msgstr "" + +#. js-lingui-id: mWSpW5 +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "+$5/user per month" +msgstr "" + +#. js-lingui-id: JIwDAd +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "+$7000/org per month" +msgstr "" + +#. js-lingui-id: yVr18d +#: src/app/[locale]/pricing/salesforce.data.ts +#: src/app/[locale]/pricing/salesforce.data.ts +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "" +"+$75/user per month\n" +"Switch to enterprise!" +msgstr "" + +#. js-lingui-id: iK+ikF +#: src/sections/TrustedBy/data.ts +msgid "+10k others" +msgstr "" + +#. js-lingui-id: nRtVCK +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "" +"+20% of net spend\n" +"+$75/user per month\n" +"Switch to enterprise!" +msgstr "" + +#. js-lingui-id: be30i9 +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "$0" +msgstr "" + +#. js-lingui-id: 6inRXo +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "" +"$1/orchestration run/org\n" +"+$75/user per month\n" +"Switch to enterprise!" +msgstr "" + +#. js-lingui-id: W8uNyP +#: src/sections/Plans/data.ts +msgid "$12" +msgstr "" + +#. js-lingui-id: EkIZkY +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "$19" +msgstr "" + +#. js-lingui-id: xrRqkE +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +msgid "$25" +msgstr "" + +#. js-lingui-id: 8r6DgO +#: src/sections/Plans/data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "$9" +msgstr "" + +#. js-lingui-id: EHNAf2 +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "1 click" +msgstr "" + +#. js-lingui-id: qvNspm +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "1‑800‑YES‑SOFTWARE" +msgstr "" + +#. js-lingui-id: 6WQdu8 +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "100 per minute" +msgstr "" + +#. js-lingui-id: uuKUqZ +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "" +"11 permissions\n" +"groups" +msgstr "" + +#. js-lingui-id: SaSafJ +#: src/app/[locale]/customers/9dots/page.tsx +msgid "150 hours" +msgstr "" + +#. js-lingui-id: 9ntyY8 +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "150 hrs" +msgstr "" + +#. js-lingui-id: 2z1Znr +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "2 years contract" +msgstr "" + +#. js-lingui-id: l6Vx4q +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "2,000+" +msgstr "" + +#. js-lingui-id: oadHWW +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "200 per minute" +msgstr "" + +#. js-lingui-id: FKcV/y +#: src/lib/customers/case-study-catalog.ts +#: src/lib/customers/case-study-catalog.ts +#: src/lib/customers/case-study-catalog.ts +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/w3villa/page.tsx +#: src/app/[locale]/customers/netzero/page.tsx +#: src/app/[locale]/customers/alternative-partners/page.tsx +#: src/app/[locale]/customers/act-education/page.tsx +msgid "2025" +msgstr "" + +#. js-lingui-id: PJgfBS +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "3" +msgstr "" + +#. js-lingui-id: BL47wE +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "" +"3 2 years contract\n" +"-33% off" +msgstr "" + +#. js-lingui-id: /jgC+9 +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/netzero/page.tsx +msgid "3 product lines" +msgstr "" + +#. js-lingui-id: 7SJhyh +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "30+" +msgstr "" + +#. js-lingui-id: Pdl2UE +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "4 tools" +msgstr "" + +#. js-lingui-id: tZvajW +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "6 workflows" +msgstr "" + +#. js-lingui-id: AQPMc4 +#: src/app/[locale]/customers/act-education/page.tsx +msgid "90%" +msgstr "" + +#. js-lingui-id: fHxYPM +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/act-education/page.tsx +msgid "90%+" +msgstr "" + +#. js-lingui-id: 1GAcp6 +#: src/app/[locale]/customers/netzero/page.tsx +msgid "A business that does not fit a" +msgstr "" + +#. js-lingui-id: NLSM5L +#: src/app/[locale]/customers/netzero/page.tsx +msgid "A business that does not fit a template" +msgstr "" + +#. js-lingui-id: 9Z2FZQ +#: src/app/[locale]/product/page.tsx +msgid "A CRM for teams" +msgstr "" + +#. js-lingui-id: GSM5RA +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/netzero/page.tsx +msgid "A CRM that" +msgstr "" + +#. js-lingui-id: och6cp +#: src/app/[locale]/customers/netzero/page.tsx +msgid "A CRM that grows with you | NetZero & Twenty" +msgstr "" + +#. js-lingui-id: EewhZt +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/act-education/page.tsx +msgid "A CRM they" +msgstr "" + +#. js-lingui-id: sgzhYK +#: src/app/[locale]/(home)/page.tsx +msgid "a CRM they'll love" +msgstr "" + +#. js-lingui-id: xiVefD +#: src/app/[locale]/(home)/page.tsx +msgid "A custom CRM gives your org an edge," +msgstr "" + +#. js-lingui-id: Qay3/Z +#: src/app/[locale]/product/page.tsx +msgid "A modern CRM with" +msgstr "" + +#. js-lingui-id: GoyOzl +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "A platform ready to" +msgstr "" + +#. js-lingui-id: rxMguZ +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "A platform ready to grow" +msgstr "" + +#. js-lingui-id: R5hTKX +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "A real estate agency on WhatsApp" +msgstr "" + +#. js-lingui-id: WjUlJr +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "A retro theme as a paid add-on is somehow the most believable part." +msgstr "" + +#. js-lingui-id: r9Msiv +#: src/app/[locale]/why-twenty/editorial-three.data.ts +msgid "A year ago, customizing your CRM meant hiring a Salesforce consultant, learning Apex, waiting months. The gap between \"I want this\" and \"it's live\" was measured in quarters and invoices. So people settled. They bent their process to fit the tool and called it adoption." +msgstr "" + +#. js-lingui-id: c0RR2h +#: src/app/[locale]/customers/9dots/page.tsx +msgid "About 150 hours per month saved in manual operations. Real-time metrics for the business owner. Growth readiness without adding operational headcount. A team that can answer questions that used to take days to piece together." +msgstr "" + +#. js-lingui-id: 41o3VS +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/act-education/page.tsx +msgid "AC&T and Flycoder moved from a dead vendor export to self-hosted Twenty, with over 90% lower CRM cost and full control." +msgstr "" + +#. js-lingui-id: TuJiQF +#: src/app/[locale]/customers/act-education/page.tsx +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "AC&T Education Migration" +msgstr "" + +#. js-lingui-id: yHUdh6 +#: src/app/[locale]/customers/act-education/page.tsx +msgid "AC&T Education Migration (actimmi.com) is an education agency in Australia. They help international students with applications to education providers and visas. They had been on a previous CRM until the vendor shut the system down, leaving nothing but a CSV export." +msgstr "" + +#. js-lingui-id: MPzy5R +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "AC&T moved to a self-hosted Twenty instance with no vendor risk, no forced migration, and CRM costs reduced by more than 90%." +msgstr "" + +#. js-lingui-id: J+A83M +#: src/app/[locale]/(home)/helped.data.ts +msgid "AC&T replaced a shuttered vendor CRM with self-hosted Twenty and cut CRM costs by more than 90%." +msgstr "" + +#. js-lingui-id: muhZ+X +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Activating your enterprise license…" +msgstr "" + +#. js-lingui-id: y+9jZD +#: src/app/[locale]/enterprise/activate/page.tsx +msgid "activation" +msgstr "" + +#. js-lingui-id: TDMykO +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "actually get used" +msgstr "" + +#. js-lingui-id: +/fX0G +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/act-education/page.tsx +msgid "actually own" +msgstr "" + +#. js-lingui-id: woOp+c +#: src/app/[locale]/product/stepper.data.ts +msgid "Add objects and fields" +msgstr "" + +#. js-lingui-id: Y2K3rp +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Add-ons" +msgstr "" + +#. js-lingui-id: Op7nVF +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Adoption" +msgstr "" + +#. js-lingui-id: TeMRC2 +#: src/app/[locale]/customers/netzero/page.tsx +msgid "advanced" +msgstr "" + +#. js-lingui-id: cBdysG +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Advanced Encryption" +msgstr "" + +#. js-lingui-id: /C6Pwb +#: src/app/[locale]/product/feature.data.ts +msgid "Aggregate, bar, line, pie, and gauge widgets" +msgstr "" + +#. js-lingui-id: rp2omj +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/netzero/page.tsx +msgid "Agribusiness" +msgstr "" + +#. js-lingui-id: jiGK6+ +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "AI (Einstein)" +msgstr "" + +#. js-lingui-id: aUn7Ps +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "AI & Automations" +msgstr "" + +#. js-lingui-id: HbXHSp +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "AI agents" +msgstr "" + +#. js-lingui-id: WGXwzu +#: src/app/[locale]/why-twenty/editorial-one.data.ts +msgid "AI agents are starting to draft outreach, score leads, research accounts, write follow-ups, update deal stages. Every one of these actions reads from and writes to the CRM. The scoreboard became the playbook. The database became the brain." +msgstr "" + +#. js-lingui-id: Rsxdfy +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +msgid "AI Agents with custom skills" +msgstr "" + +#. js-lingui-id: TfV/y9 +#: src/app/[locale]/product/three-cards.data.ts +msgid "AI chat, settings, and records in a side panels for fast, single-screen access." +msgstr "" + +#. js-lingui-id: R+bwFB +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "AI for rapid iterations" +msgstr "" + +#. js-lingui-id: +pgY/C +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "AI in the" +msgstr "" + +#. js-lingui-id: A6v5aI +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "AI in the migration workflow" +msgstr "" + +#. js-lingui-id: 9VlH2i +#: src/app/[locale]/why-twenty/page.tsx +msgid "AI made the gap that small." +msgstr "" + +#. js-lingui-id: Tp1P7F +#: src/app/[locale]/why-twenty/page.tsx +msgid "AI turned it into an operating system." +msgstr "" + +#. js-lingui-id: t97j+F +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "AI-assisted" +msgstr "" + +#. js-lingui-id: LIGwFL +#: src/app/[locale]/customers/alternative-partners/page.tsx +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "Alternative Partners" +msgstr "" + +#. js-lingui-id: GDVvyT +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "Alternative Partners is a consulting firm that moved from Salesforce to a self-hosted Twenty instance. Benjamin Reynolds led the migration. He had already become a Twenty expert implementing Twenty for one of Twenty's first cloud customers." +msgstr "" + +#. js-lingui-id: CCrRuT +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "Alternative Partners replaced Salesforce with self-hosted Twenty, using agentic AI to compress migration work." +msgstr "" + +#. js-lingui-id: UIu18Y +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "Alternative Partners used agentic AI to compress what would typically be weeks of Salesforce migration work into something a single person could oversee." +msgstr "" + +#. js-lingui-id: iFLhqi +#: src/app/[locale]/(home)/testimonials.data.ts +msgid "Amrendra Pratap Singh" +msgstr "" + +#. js-lingui-id: JDRPTH +#: src/app/[locale]/product/page.tsx +msgid "an intuitive interface" +msgstr "" + +#. js-lingui-id: lTSix7 +#: src/app/[locale]/partners/page.tsx +msgid "and unlock new opportunities with Twenty" +msgstr "" + +#. js-lingui-id: aGCcXe +#: src/sections/Faq/data.ts +msgid "Any Questions?" +msgstr "" + +#. js-lingui-id: /AMVjF +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "APEX tutorials" +msgstr "" + +#. js-lingui-id: OZtEcz +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "API" +msgstr "" + +#. js-lingui-id: yKBF3l +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "API access" +msgstr "" + +#. js-lingui-id: GpYSXo +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "API backbone" +msgstr "" + +#. js-lingui-id: 30KnZB +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "API calls" +msgstr "" + +#. js-lingui-id: 42FQ3y +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "API-first" +msgstr "" + +#. js-lingui-id: azQMHP +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "APIs are extra. Simplicity has a price." +msgstr "" + +#. js-lingui-id: MqJvvH +#: src/sections/Menu/data.ts +msgid "APIs, SDKs and webhooks to extend Twenty and ship apps on top of your CRM data." +msgstr "" + +#. js-lingui-id: I1Z4AM +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Apply to build" +msgstr "" + +#. js-lingui-id: uicv6Z +#: src/app/[locale]/customers/w3villa/page.tsx +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Architecture" +msgstr "" + +#. js-lingui-id: Sfa0xD +#: src/app/[locale]/customers/9dots/page.tsx +msgid "around it" +msgstr "" + +#. js-lingui-id: yYKNWp +#: src/app/[locale]/(home)/page.tsx +msgid "Assemble, iterate and adapt a robust CRM," +msgstr "" + +#. js-lingui-id: v7zTZl +#: src/app/[locale]/product/feature.data.ts +msgid "Assign owners and due dates" +msgstr "" + +#. js-lingui-id: 9c5acG +#: src/app/[locale]/(home)/page.tsx +msgid "at AI Speed" +msgstr "" + +#. js-lingui-id: 4/b98B +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "at the core" +msgstr "" + +#. js-lingui-id: y2W2Hg +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Audit logs" +msgstr "" + +#. js-lingui-id: FFv0Vh +#: src/app/[locale]/product/stepper.data.ts +msgid "Automation" +msgstr "" + +#. js-lingui-id: SJ7WSP +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Available on YouTube!" +msgstr "" + +#. js-lingui-id: iH8pgl +#: src/sections/CaseStudy/components/Hero.tsx +msgid "Back" +msgstr "" + +#. js-lingui-id: jQLfIW +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Because apparently privacy feels more premium with a surcharge." +msgstr "" + +#. js-lingui-id: qEQv1F +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Because everything is built on Twenty's open foundation, Flycoder could wire the exact logic AC&T needed without fighting the platform." +msgstr "" + +#. js-lingui-id: uIG0OH +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Because the foundation is solid, W3Grads is architected for what comes next, including a payment layer for future paid interview plans and nationwide scale without structural rewrites." +msgstr "" + +#. js-lingui-id: JGM+JO +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Because true orchestration means putting a dollar sign on every dramatic entrance." +msgstr "" + +#. js-lingui-id: SoJDT2 +#: src/app/[locale]/partners/page.tsx +msgid "Become" +msgstr "" + +#. js-lingui-id: sx6hMS +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Become a Content Partner" +msgstr "" + +#. js-lingui-id: PKMHNe +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "become a genius!" +msgstr "" + +#. js-lingui-id: 6ZCRfT +#: src/app/[locale]/partners/components/PartnerApplication/BecomePartnerButton.tsx +msgid "Become a partner" +msgstr "" + +#. js-lingui-id: lvXL1I +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Become a Solution Partner" +msgstr "" + +#. js-lingui-id: EwaFPn +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Become a Technology Partner" +msgstr "" + +#. js-lingui-id: eqswCn +#: src/app/[locale]/(home)/home-stepper.data.ts +msgid "Begin with production-grade" +msgstr "" + +#. js-lingui-id: SYfuGP +#: src/app/[locale]/partners/testimonials.data.ts +msgid "Benjamin Reynolds" +msgstr "" + +#. js-lingui-id: YfjrMq +#: src/app/[locale]/partners/testimonials.data.ts +msgid "Bertrams" +msgstr "" + +#. js-lingui-id: mzTjnf +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Better than Liquid Glass!" +msgstr "" + +#. js-lingui-id: GvGk4g +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Beyond" +msgstr "" + +#. js-lingui-id: bxWnM1 +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "breaking operations" +msgstr "" + +#. js-lingui-id: ONUC8n +#: src/app/[locale]/customers/page.tsx +msgid "build" +msgstr "" + +#. js-lingui-id: WxyAko +#: src/app/[locale]/why-twenty/page.tsx +msgid "Build a CRM your competitors" +msgstr "" + +#. js-lingui-id: pF0PoL +#: src/app/[locale]/product/feature.data.ts +msgid "Build custom dashboards" +msgstr "" + +#. js-lingui-id: wWXG44 +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Build integrations that connect Twenty with the tools your customers already use. Help us expand the Twenty ecosystem." +msgstr "" + +#. js-lingui-id: ZhC3tY +#: src/app/[locale]/why-twenty/page.tsx +msgid "Build it in an afternoon." +msgstr "" + +#. js-lingui-id: e1u+Zh +#: src/sections/Menu/data.ts +msgid "Build on an open platform" +msgstr "" + +#. js-lingui-id: oIpt0K +#: src/app/[locale]/(home)/page.tsx +msgid "Build your Enterprise CRM" +msgstr "" + +#. js-lingui-id: J7i6zm +#: src/app/[locale]/(home)/home-stepper.data.ts +msgid "building blocks" +msgstr "" + +#. js-lingui-id: /ETeLn +#: src/app/[locale]/customers/9dots/page.tsx +msgid "built a" +msgstr "" + +#. js-lingui-id: 8hMULp +#: src/lib/customers/case-study-catalog.ts +msgid "built a CRM around it" +msgstr "" + +#. js-lingui-id: SoAKmY +#: src/app/[locale]/product/three-cards.data.ts +msgid "Built for speed" +msgstr "" + +#. js-lingui-id: hnSOXa +#: src/sections/CaseStudyCatalog/components/Promo.tsx +msgid "built with Twenty" +msgstr "" + +#. js-lingui-id: j8RGrG +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Burned by vendor lock-in, AC&T built a CRM they actually own | Twenty" +msgstr "" + +#. js-lingui-id: EjSajd +#: src/app/[locale]/(home)/page.tsx +msgid "but building one" +msgstr "" + +#. js-lingui-id: wCALve +#: src/sections/Faq/data.ts +msgid "Can developers extend Twenty with code?" +msgstr "" + +#. js-lingui-id: 6mytgb +#: src/sections/Faq/data.ts +msgid "Can I migrate from Salesforce or HubSpot?" +msgstr "" + +#. js-lingui-id: +oHJTp +#: src/app/[locale]/why-twenty/page.tsx +msgid "can't buy." +msgstr "" + +#. js-lingui-id: u+wGUf +#: src/sections/Helped/components/Scene.tsx +msgid "change with Twenty" +msgstr "" + +#. js-lingui-id: 2D9CbR +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Check more add-ons" +msgstr "" + +#. js-lingui-id: ZbCD7v +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Classic never dies. It just gets extended one more time." +msgstr "" + +#. js-lingui-id: L/LPQZ +#: src/sections/Faq/data.ts +msgid "Cloud Pro is $9/user/month (yearly). Organization is $19/user/month and unlocks SSO and row-level permissions for teams that need finer access control." +msgstr "" + +#. js-lingui-id: 1YncLF +#: src/app/[locale]/(home)/testimonials.data.ts +msgid "Co-founder at NetZero" +msgstr "" + +#. js-lingui-id: vWaYWP +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/netzero/page.tsx +msgid "Co-founder, NetZero" +msgstr "" + +#. js-lingui-id: R8BDsa +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Co-marketing opportunities" +msgstr "" + +#. js-lingui-id: b5bnuN +#: src/app/[locale]/product/feature.data.ts +msgid "Column-to-field mapping (including relations)" +msgstr "" + +#. js-lingui-id: NHYWK4 +#: src/app/[locale]/(home)/page.tsx +msgid "comes with" +msgstr "" + +#. js-lingui-id: ofoEsM +#: src/app/[locale]/pricing/salesforce.data.ts +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Coming soon!" +msgstr "" + +#. js-lingui-id: KGD9Kt +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Commercial license (no AGPL obligations)" +msgstr "" + +#. js-lingui-id: chL5IG +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Community" +msgstr "" + +#. js-lingui-id: 8CLnho +#: src/sections/Plans/data.ts +msgid "Community support" +msgstr "" + +#. js-lingui-id: SHjhDC +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Company or brand *" +msgstr "" + +#. js-lingui-id: O403zL +#: src/sections/Helped/components/Scene.tsx +msgid "company-wide" +msgstr "" + +#. js-lingui-id: P/f7ez +#: src/lib/website-routing/static-website-routes.ts +msgid "Complete activation for your Twenty self-hosted enterprise license." +msgstr "" + +#. js-lingui-id: Fkb+LW +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "Compose your CRM and internal apps with a single extensibility toolkit." +msgstr "" + +#. js-lingui-id: kD7ZGH +#: src/app/[locale]/(home)/home-stepper.data.ts +msgid "Compose your CRM and internal apps with a single extensibility toolkit. Data model, layout, and automation." +msgstr "" + +#. js-lingui-id: iSLIjg +#: src/sections/Footer/data.ts +msgid "Connect" +msgstr "" + +#. js-lingui-id: 90KBr0 +#: src/app/[locale]/product/feature.data.ts +msgid "Connect Google or Microsoft accounts" +msgstr "" + +#. js-lingui-id: MVrQxI +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Connected via API" +msgstr "" + +#. js-lingui-id: cfAG43 +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "Consulting" +msgstr "" + +#. js-lingui-id: Oii3vg +#: src/app/[locale]/product/feature.data.ts +msgid "Contacts & Companies" +msgstr "" + +#. js-lingui-id: fi9xsM +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Content & Community Partner" +msgstr "" + +#. js-lingui-id: vymMOT +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Content & Community Partners" +msgstr "" + +#. js-lingui-id: mutfS8 +#: src/app/[locale]/(home)/home-stepper.data.ts +msgid "Continue iteration" +msgstr "" + +#. js-lingui-id: +Uepfb +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Control" +msgstr "" + +#. js-lingui-id: VOpE1H +#: src/app/[locale]/customers/9dots/page.tsx +msgid "control hub" +msgstr "" + +#. js-lingui-id: sTIhSj +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Control without" +msgstr "" + +#. js-lingui-id: wH1xmY +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "Control without drag" +msgstr "" + +#. js-lingui-id: Bj7igG +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Control without the overhead" +msgstr "" + +#. js-lingui-id: PiH3UR +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Copied!" +msgstr "" + +#. js-lingui-id: he3ygx +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Copy" +msgstr "" + +#. js-lingui-id: 7qTWwB +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Copy the enterprise key above." +msgstr "" + +#. js-lingui-id: PXnj76 +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Copy this key and paste it into your Twenty self-hosted instance settings." +msgstr "" + +#. js-lingui-id: FapWIq +#: src/app/[locale]/product/feature.data.ts +msgid "Core Features" +msgstr "" + +#. js-lingui-id: JRK+HF +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Costs down more than" +msgstr "" + +#. js-lingui-id: Xn2Nlq +#: src/app/[locale]/product/stepper.data.ts +msgid "Create a workflow" +msgstr "" + +#. js-lingui-id: B9nLrl +#: src/sections/Menu/data.ts +msgid "Create apps on Twenty" +msgstr "" + +#. js-lingui-id: yAL7FY +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +msgid "Create custom apps" +msgstr "" + +#. js-lingui-id: oS1RiS +#: src/app/[locale]/product/feature.data.ts +msgid "Create tasks from records" +msgstr "" + +#. js-lingui-id: MSlbwg +#: src/app/[locale]/customers/9dots/page.tsx +msgid "CRM" +msgstr "" + +#. js-lingui-id: Y44Xsf +#: src/app/[locale]/customers/act-education/page.tsx +msgid "CRM costs dropped by more than 90%. Manual overhead tied to the old system is gone. For the first time, AC&T has a CRM they will not lose again." +msgstr "" + +#. js-lingui-id: wDasLa +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "CRM Engineer" +msgstr "" + +#. js-lingui-id: DqkzNV +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/act-education/page.tsx +msgid "CRM Engineer, AC&T Education Migration" +msgstr "" + +#. js-lingui-id: b7vNti +#: src/app/[locale]/why-twenty/hero.data.ts +msgid "CRM was a database you filled on Fridays. AI turned it into the system that runs your go-to-market. To differentiate, you have to build what your competitors can't buy." +msgstr "" + +#. js-lingui-id: v5sHk3 +#: src/app/[locale]/why-twenty/page.tsx +msgid "CRM was a ledger." +msgstr "" + +#. js-lingui-id: I6FFSA +#: src/app/[locale]/product/feature.data.ts +msgid "CSV export anytime" +msgstr "" + +#. js-lingui-id: 8RWevB +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "CSV import & export" +msgstr "" + +#. js-lingui-id: s+QiQi +#: src/app/[locale]/product/feature.data.ts +msgid "CSV import flow" +msgstr "" + +#. js-lingui-id: lh6aPn +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Custom AI models" +msgstr "" + +#. js-lingui-id: OaTurC +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Custom apps" +msgstr "" + +#. js-lingui-id: yBSEtR +#: src/app/[locale]/product/feature.data.ts +msgid "Custom deal stages for your process" +msgstr "" + +#. js-lingui-id: Vz1Vq2 +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Custom domain (crm.yourco.com)" +msgstr "" + +#. js-lingui-id: oPwQt4 +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Custom fields" +msgstr "" + +#. js-lingui-id: U1RB/7 +#: src/app/[locale]/product/feature.data.ts +msgid "Custom fields and relationships" +msgstr "" + +#. js-lingui-id: prIqWa +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Custom layout" +msgstr "" + +#. js-lingui-id: 8skTDV +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Custom objects" +msgstr "" + +#. js-lingui-id: AbyZbl +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Custom views" +msgstr "" + +#. js-lingui-id: YjXLAQ +#: src/sections/CaseStudyCatalog/components/Promo.tsx +msgid "Customer Stories" +msgstr "" + +#. js-lingui-id: NihQNk +#: src/sections/Menu/data.ts +msgid "Customers" +msgstr "" + +#. js-lingui-id: 3mRQi4 +#: src/lib/website-routing/static-website-routes.ts +msgid "Customers | Twenty" +msgstr "" + +#. js-lingui-id: qqLY+j +#: src/app/[locale]/product/stepper.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Customization" +msgstr "" + +#. js-lingui-id: GRfLAg +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/netzero/page.tsx +msgid "Customizations" +msgstr "" + +#. js-lingui-id: 4RLD4p +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Daily messages" +msgstr "" + +#. js-lingui-id: V0kvgB +#: src/app/[locale]/product/feature.data.ts +msgid "Data import" +msgstr "" + +#. js-lingui-id: 5cNMFz +#: src/app/[locale]/product/stepper.data.ts +msgid "Data model" +msgstr "" + +#. js-lingui-id: FBZA6a +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Dedicated partner support" +msgstr "" + +#. js-lingui-id: KR5UiF +#: src/sections/Helped/components/Scene.tsx +msgid "Dev teams power" +msgstr "" + +#. js-lingui-id: n+SX4g +#: src/sections/Menu/data.ts +#: src/sections/Footer/data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Developers" +msgstr "" + +#. js-lingui-id: vcsX5L +#: src/app/[locale]/why-twenty/page.tsx +msgid "Differentiation now" +msgstr "" + +#. js-lingui-id: Wuqvfz +#: src/lib/customers/case-study-catalog.ts +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +#: src/app/[locale]/customers/elevate-consulting/page.tsx +#: src/app/[locale]/(home)/testimonials.data.ts +msgid "Director of Digital and Information, Elevate Consulting" +msgstr "" + +#. js-lingui-id: +SvmQA +#: src/app/[locale]/releases/hero.data.ts +msgid "" +"Discover the newest features and improvements in Twenty,\n" +"the #1 open source CRM." +msgstr "" + +#. js-lingui-id: lsSE4P +#: src/lib/website-routing/static-website-routes.ts +msgid "Discover the newest features and improvements in Twenty, the #1 open source CRM." +msgstr "" + +#. js-lingui-id: W+b/DF +#: src/sections/Menu/data.ts +msgid "Discover what's new" +msgstr "" + +#. js-lingui-id: Zjjbne +#: src/sections/Faq/data.ts +msgid "Do I need a developer to customize Twenty?" +msgstr "" + +#. js-lingui-id: Zx22Ih +#: src/sections/Faq/data.ts +msgid "Does Twenty work with Claude, ChatGPT, and Cursor?" +msgstr "" + +#. js-lingui-id: pOLPPB +#: src/app/[locale]/(home)/home-stepper.data.ts +msgid "Don't get locked into someone else's ecosystem. Twenty's developer experience looks like normal software, with local setup, real data, live testing, and no proprietary tooling." +msgstr "" + +#. js-lingui-id: fPma12 +#: src/app/[locale]/product/feature.data.ts +msgid "Drag-and-drop deals between stages" +msgstr "" + +#. js-lingui-id: 4CJ4xV +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "EdTech" +msgstr "" + +#. js-lingui-id: aqxYLv +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Education" +msgstr "" + +#. js-lingui-id: 1wTjWx +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Elevate Consulting is a management consultancy based in Canada. When Justin Beadle, Director of Digital and Information, joined, the company ran entirely on Word documents, Excel spreadsheets, sticky notes, emails, and reliance on people. There was no CRM, no API-accessible tools, only a patchwork trying to stand in for a single source of truth." +msgstr "" + +#. js-lingui-id: 2THY70 +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Elevate Consulting uses Twenty as the API backbone connecting billing, Teams, resourcing, and a custom front end around client and opportunity data." +msgstr "" + +#. js-lingui-id: wiTeYI +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Elevate's CEO was so impressed with Twenty he started recommending it to clients before the internal setup was even complete. The team is exploring bringing Twenty to client projects as part of their consulting practice, including as the backend for custom-built products tailored to specific operational needs." +msgstr "" + +#. js-lingui-id: FNyrb0 +#: src/app/[locale]/product/feature.data.ts +msgid "Email & Calendar" +msgstr "" + +#. js-lingui-id: AoN898 +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Email and Chat" +msgstr "" + +#. js-lingui-id: ObCvfI +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Email sharing" +msgstr "" + +#. js-lingui-id: hfqiVr +#: src/app/[locale]/product/feature.data.ts +msgid "Email/calendar activity on each record" +msgstr "" + +#. js-lingui-id: XGd8Wo +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Emails & Calendar" +msgstr "" + +#. js-lingui-id: o4/9l1 +#: src/app/[locale]/product/feature.data.ts +msgid "Emails and events linked to CRM records" +msgstr "" + +#. js-lingui-id: Qpn/bX +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Encrypt your data" +msgstr "" + +#. js-lingui-id: Ijh+h+ +#: src/app/[locale]/(home)/home-stepper.data.ts +msgid "Enjoy unlimited customization using the AI coding tools you already love. Adapt your CRM to fit the way your business grows and wins." +msgstr "" + +#. js-lingui-id: xoqD/n +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Enter a valid email address." +msgstr "" + +#. js-lingui-id: GpB8YV +#: src/app/[locale]/enterprise/activate/page.tsx +msgid "Enterprise" +msgstr "" + +#. js-lingui-id: 5l0LGU +#: src/lib/website-routing/static-website-routes.ts +msgid "Enterprise activation | Twenty" +msgstr "" + +#. js-lingui-id: 3dQ6zJ +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Environments" +msgstr "" + +#. js-lingui-id: D1bFNS +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Estimated monthly opportunities (optional)" +msgstr "" + +#. js-lingui-id: IhKZhp +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Even the training material is a feature worth celebrating." +msgstr "" + +#. js-lingui-id: kz/8m8 +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +msgid "Everything in Pro" +msgstr "" + +#. js-lingui-id: zoNk7e +#: src/app/[locale]/(home)/three-cards-feature.data.ts +msgid "Everything updates in real time, with AI chat always ready to help you move faster." +msgstr "" + +#. js-lingui-id: bEEOy2 +#: src/app/[locale]/product/page.tsx +msgid "Everything you need," +msgstr "" + +#. js-lingui-id: CIcRGB +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Exclusive content collaboration opportunities" +msgstr "" + +#. js-lingui-id: 57gG1f +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Experience enterprise-grade granularity, starting with an 11th permission." +msgstr "" + +#. js-lingui-id: JTM5zS +#: src/sections/CaseStudyCatalog/components/Promo.tsx +msgid "Explore customer stories" +msgstr "" + +#. js-lingui-id: weFouS +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Extended run!" +msgstr "" + +#. js-lingui-id: akLvep +#: src/app/[locale]/(home)/three-cards-feature.data.ts +msgid "Familiar, modern interface" +msgstr "" + +#. js-lingui-id: abO45l +#: src/app/[locale]/(home)/three-cards-feature.data.ts +msgid "Fast path to action" +msgstr "" + +#. js-lingui-id: e6BgMV +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Field-level permissions" +msgstr "" + +#. js-lingui-id: sER+bs +#: src/app/[locale]/product/feature.data.ts +msgid "Files" +msgstr "" + +#. js-lingui-id: UWTNog +#: src/app/[locale]/product/feature.data.ts +msgid "Filtered metrics from live CRM data" +msgstr "" + +#. js-lingui-id: TJ7HQl +#: src/app/[locale]/pricing/page.tsx +#: src/app/[locale]/partners/components/PartnerApplication/PartnerHeroCtas.tsx +msgid "Find a partner" +msgstr "" + +#. js-lingui-id: MD032e +#: src/sections/Menu/data.ts +msgid "Find a Twenty partner" +msgstr "" + +#. js-lingui-id: SZabbQ +#: src/app/[locale]/partners/page.tsx +msgid "Find the program that fits your business" +msgstr "" + +#. js-lingui-id: wHyJkT +#: src/app/[locale]/pricing/engagement-band.data.ts +msgid "Find the right partner to implement, customize, and tailor Twenty to your team." +msgstr "" + +#. js-lingui-id: PHM5wp +#: src/app/[locale]/customers/netzero/page.tsx +msgid "Flexibility" +msgstr "" + +#. js-lingui-id: HBJ0P5 +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "" +"Flow\n" +"orchestration" +msgstr "" + +#. js-lingui-id: obrJBN +#: src/app/[locale]/product/three-cards.data.ts +msgid "Fly through your workspace with shortcuts and short load times." +msgstr "" + +#. js-lingui-id: HjFq2b +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Flycoder, a full-stack development partner, helped them set up Twenty as a self-hosted instance shaped around how AC&T actually operates. The data model centers on students, not a generic contact-and-deal pipeline. Statuses update automatically: a workflow runs nightly to keep enrollment records current. Automated email reminders cover important dates. Adding a new record takes under a minute." +msgstr "" + +#. js-lingui-id: 1SL9ZF +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Focus on the use case, not the" +msgstr "" + +#. js-lingui-id: P5E+kT +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Focus on the use case, not the plumbing" +msgstr "" + +#. js-lingui-id: wdVyxi +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Folder/Label import" +msgstr "" + +#. js-lingui-id: CKQ0za +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "For a firm that once ran on sticky notes, this is more than an upgrade. It is a complete transformation." +msgstr "" + +#. js-lingui-id: yAJC9c +#: src/app/[locale]/why-twenty/editorial-one.data.ts +msgid "For twenty years, CRM meant the same thing: a place to log calls, track deals, and pull reports on Friday. The real work happened in people's heads, in Slack threads, in hallway conversations. The CRM kept score. Nobody expected more from it." +msgstr "" + +#. js-lingui-id: wSoqhC +#: src/app/[locale]/customers/netzero/page.tsx +msgid "foundation" +msgstr "" + +#. js-lingui-id: UCoxP5 +#: src/lib/customers/case-study-catalog.ts +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/partners/testimonials.data.ts +#: src/app/[locale]/customers/9dots/page.tsx +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Founder, Nine Dots Ventures" +msgstr "" + +#. js-lingui-id: IPaRlc +#: src/app/[locale]/partners/testimonials.data.ts +msgid "Founder, Wintactix" +msgstr "" + +#. js-lingui-id: tUgSXd +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Free for you!" +msgstr "" + +#. js-lingui-id: yDeZSV +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "From documents to" +msgstr "" + +#. js-lingui-id: emeKZ7 +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "From documents to open APIs" +msgstr "" + +#. js-lingui-id: CMqgta +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "From Salesforce to" +msgstr "" + +#. js-lingui-id: syU/BD +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "From Salesforce to self-hosted Twenty, powered by AI | Alternative Partners" +msgstr "" + +#. js-lingui-id: TH5XjF +#: src/app/[locale]/customers/netzero/page.tsx +msgid "From simple to" +msgstr "" + +#. js-lingui-id: Lf7cb3 +#: src/app/[locale]/customers/netzero/page.tsx +msgid "From simple to advanced" +msgstr "" + +#. js-lingui-id: m1I5TY +#: src/app/[locale]/product/feature.data.ts +msgid "Full communication history in one place" +msgstr "" + +#. js-lingui-id: wXgKOm +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +msgid "Full customization" +msgstr "" + +#. js-lingui-id: YZ7Q3Z +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "Full ownership" +msgstr "" + +#. js-lingui-id: hdxwWi +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Fully customizable" +msgstr "" + +#. js-lingui-id: ZDIydz +#: src/sections/Menu/components/Root.tsx +#: src/sections/Menu/components/Cta.tsx +#: src/sections/Footer/data.ts +#: src/app/[locale]/why-twenty/page.tsx +#: src/app/[locale]/product/page.tsx +#: src/app/[locale]/product/page.tsx +#: src/app/[locale]/product/page.tsx +#: src/app/[locale]/pricing/page.tsx +#: src/app/[locale]/partners/page.tsx +#: src/app/[locale]/customers/page.tsx +#: src/app/[locale]/customers/page.tsx +#: src/app/[locale]/customers/_components/CustomersCaseStudySignoff.tsx +#: src/app/[locale]/(home)/page.tsx +#: src/app/[locale]/(home)/page.tsx +msgid "Get started" +msgstr "" + +#. js-lingui-id: 3mUKNr +#: src/app/[locale]/product/page.tsx +msgid "Go the extra mile" +msgstr "" + +#. js-lingui-id: Ay/vbT +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Good choice!" +msgstr "" + +#. js-lingui-id: IsZ6P7 +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "grow" +msgstr "" + +#. js-lingui-id: MCLWq4 +#: src/app/[locale]/(home)/helped.data.ts +msgid "Grow with a flexible foundation" +msgstr "" + +#. js-lingui-id: XpCing +#: src/app/[locale]/customers/netzero/page.tsx +msgid "grows" +msgstr "" + +#. js-lingui-id: ORQRms +#: src/lib/customers/case-study-catalog.ts +msgid "grows with you" +msgstr "" + +#. js-lingui-id: Xf5EJg +#: src/sections/Footer/data.ts +msgid "Halftone generator" +msgstr "" + +#. js-lingui-id: DHC85F +#: src/lib/website-routing/static-website-routes.ts +msgid "Halftone Generator | Twenty" +msgstr "" + +#. js-lingui-id: c3XJ18 +#: src/sections/Footer/data.ts +msgid "Help" +msgstr "" + +#. js-lingui-id: CRzGla +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Help center" +msgstr "" + +#. js-lingui-id: R5aged +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Help customers implement, customize, and succeed with Twenty. Combine sales and services to grow your business." +msgstr "" + +#. js-lingui-id: qfSbUb +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "His approach was unconventional. Instead of mapping fields manually, scripting transforms, and validating data step by step, he handed the job to agentic AI tools with a brief: where the data lives, the GitHub repo for the target platform, and the Railway deployment. Start, and only return if something breaks beyond a 70% confidence fix." +msgstr "" + +#. js-lingui-id: i0qMbr +#: src/sections/Footer/data.ts +msgid "Home" +msgstr "" + +#. js-lingui-id: CtU7yU +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Homeseller" +msgstr "" + +#. js-lingui-id: KYnPnU +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Homeseller is a high-volume real estate agency in Singapore, founded by one of the country's top-performing property agents. The whole operation runs on WhatsApp: no email, no calendars, just group chats, thousands of them, with clients, agents, and leads together." +msgstr "" + +#. js-lingui-id: v6cmMq +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Homeseller kept their habits. WhatsApp stayed WhatsApp. What changed is that everything flowing through those conversations now lands in a structured system, tracked, classified, and visible in real time." +msgstr "" + +#. js-lingui-id: xaIV9m +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Homeseller, WhatsApp, and a CRM built around the business | Nine Dots & Twenty" +msgstr "" + +#. js-lingui-id: XnFo3G +#: src/app/[locale]/customers/act-education/page.tsx +msgid "How AC&T Education Migration and Flycoder replaced a shuttered vendor CRM with self-hosted Twenty, with 90%+ lower cost and full ownership." +msgstr "" + +#. js-lingui-id: PYi0f6 +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "How Alternative Partners migrated from Salesforce to self-hosted Twenty using agentic AI in the implementation loop: fast migration, durable ownership." +msgstr "" + +#. js-lingui-id: o+Kp2M +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "How do you want to partner with Twenty? *" +msgstr "" + +#. js-lingui-id: +MLOVo +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "How Elevate Consulting moved off documents and spreadsheets to Twenty as the API-connected CRM at the center of their stack." +msgstr "" + +#. js-lingui-id: OIwpKt +#: src/sections/Faq/data.ts +msgid "How long does it take to get started?" +msgstr "" + +#. js-lingui-id: iRzy9e +#: src/app/[locale]/customers/netzero/page.tsx +msgid "How NetZero uses Twenty across carbon credits, agricultural products, and franchised industrial systems with a modular CRM and a roadmap toward AI-assisted workflows." +msgstr "" + +#. js-lingui-id: NZYA4S +#: src/app/[locale]/customers/9dots/page.tsx +msgid "How Nine Dots Ventures rebuilt a Singapore real estate agency on Twenty with APIs, n8n, Grafana, and AI on top of 2,000+ WhatsApp messages a day." +msgstr "" + +#. js-lingui-id: kqEcVc +#: src/sections/CaseStudyCatalog/components/Promo.tsx +msgid "How teams" +msgstr "" + +#. js-lingui-id: bcln75 +#: src/lib/website-routing/static-website-routes.ts +msgid "How Twenty collects, uses, safeguards, and discloses information when you use Twenty.com and related services." +msgstr "" + +#. js-lingui-id: URQPvK +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "How W3villa Technologies shipped W3Grads, an AI mock interview platform for institutions, on Twenty as the operational backbone." +msgstr "" + +#. js-lingui-id: l0JGUk +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Impersonate users" +msgstr "" + +#. js-lingui-id: N4OVNn +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Implementation" +msgstr "" + +#. js-lingui-id: yHueLx +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Implementation partners" +msgstr "" + +#. js-lingui-id: InyU0Z +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "In June 2025, Justin learned Twenty v1 had shipped. Within two or three days, the CEO asked him to look into setting up a CRM. The shift came from the potential of what could be built on top of fully open APIs. The timing was perfect." +msgstr "" + +#. js-lingui-id: d1Aru8 +#: src/app/[locale]/(home)/helped.data.ts +msgid "In production." +msgstr "" + +#. js-lingui-id: Sqb+jp +#: src/app/[locale]/product/feature.data.ts +msgid "In-app preview for supported file types (when enabled)" +msgstr "" + +#. js-lingui-id: 3XP8Lk +#: src/app/[locale]/pricing/salesforce.data.ts +#: src/app/[locale]/pricing/salesforce.data.ts +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Included!" +msgstr "" + +#. js-lingui-id: S8gy7K +#: src/sections/CaseStudy/components/Highlights.tsx +msgid "Industry" +msgstr "" + +#. js-lingui-id: MeL8SS +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Infinite scroll" +msgstr "" + +#. js-lingui-id: +mOisw +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Infinite scroll is still coming soon, unlike the invoice." +msgstr "" + +#. js-lingui-id: K8iwJx +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Install shared tarball app" +msgstr "" + +#. js-lingui-id: AHWM9N +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Integration" +msgstr "" + +#. js-lingui-id: OChKCc +#: src/lib/website-routing/static-website-routes.ts +msgid "Interactive halftone generator exported from Twenty." +msgstr "" + +#. js-lingui-id: mdsMtj +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "internal rollout" +msgstr "" + +#. js-lingui-id: a+PGuG +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Internet accounts per user" +msgstr "" + +#. js-lingui-id: DQenUJ +#: src/lib/customers/case-study-catalog.ts +msgid "is the product" +msgstr "" + +#. js-lingui-id: /yQIKP +#: src/sections/Faq/data.ts +msgid "Is Twenty really open-source?" +msgstr "" + +#. js-lingui-id: QjEULz +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "It is just such a nicer experience than dealing with a Salesforce or a HubSpot. My mission has been to get every tool API-accessible, so everything talks to each other." +msgstr "" + +#. js-lingui-id: FJ51pP +#: src/app/[locale]/(home)/testimonials.data.ts +msgid "It is just such a nicer experience than dealing with a Salesforce or a HubSpot. My mission has been to get every tool API-accessible, so everything talks to each other. Twenty made that possible in a way older CRM platforms simply do not." +msgstr "" + +#. js-lingui-id: kqIAbN +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "It worked. This is AI-assisted iteration in practice: not AI as a product feature, but as part of implementation work, compressing what would typically be weeks into something one person can oversee without being the bottleneck." +msgstr "" + +#. js-lingui-id: ltNDOB +#: src/app/[locale]/(home)/problem.data.ts +msgid "It's fragile. V1 ships quickly, but maintaining and making changes is a long term burden." +msgstr "" + +#. js-lingui-id: kX2mij +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Join our ecosystem and help businesses take control of their customer data with" +msgstr "" + +#. js-lingui-id: 1qiriB +#: src/app/[locale]/partners/testimonials.data.ts +msgid "Join our growing partner ecosystem" +msgstr "" + +#. js-lingui-id: ZFnW+/ +#: src/lib/website-routing/static-website-routes.ts +msgid "Join our partner ecosystem and grow with us as we build the #1 open source CRM." +msgstr "" + +#. js-lingui-id: 4721j2 +#: src/app/[locale]/partners/signoff.data.ts +msgid "" +"Join our partner ecosystem and help businesses\n" +"take control of their CRM." +msgstr "" + +#. js-lingui-id: 4QqdtB +#: src/app/[locale]/customers/page.tsx +#: src/app/[locale]/customers/_components/CustomersCaseStudySignoff.tsx +msgid "" +"Join the teams that chose to own their CRM.\n" +"Start building with Twenty today." +msgstr "" + +#. js-lingui-id: m/pXki +#: src/app/[locale]/product/signoff.data.ts +msgid "Join the teams that chose to own their CRM. Start building with Twenty today." +msgstr "" + +#. js-lingui-id: pB76mP +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Jul 2025" +msgstr "" + +#. js-lingui-id: hvwlu3 +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Jun 2025" +msgstr "" + +#. js-lingui-id: qrYDGE +#: src/app/[locale]/(home)/testimonials.data.ts +msgid "Justin Beadle" +msgstr "" + +#. js-lingui-id: vYXuJI +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Justin built workflows for notifications across the team, alerting the right people in Teams when a prospect becomes a lead or when project milestones are reached. Forms in Twenty let the business development team log activity without leaving the tool. The impact is real for the organization. The tool has been adaptable from opportunity-level work at a client to executive-level decisions." +msgstr "" + +#. js-lingui-id: E6Oohx +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Justin's broader mission at Elevate has been to move the company off static documents and onto tools with API access. By the end of 2025, that was in place: time billing, resource planning, Microsoft Teams, and project management were all accessible via API, with Twenty at the center holding client and opportunity data. Team members could use that information strategically instead of re-keying it." +msgstr "" + +#. js-lingui-id: GAmD3h +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Languages" +msgstr "" + +#. js-lingui-id: wL3cK8 +#: src/app/[locale]/releases/page.tsx +msgid "Latest" +msgstr "" + +#. js-lingui-id: rdU729 +#: src/app/[locale]/product/stepper.data.ts +msgid "Layout" +msgstr "" + +#. js-lingui-id: +Ss/og +#: src/sections/Menu/data.ts +msgid "Learn how to use Twenty" +msgstr "" + +#. js-lingui-id: vifyyw +#: src/sections/Footer/data.ts +msgid "Legal" +msgstr "" + +#. js-lingui-id: tHFTlp +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Licensee:" +msgstr "" + +#. js-lingui-id: gggTBm +#: src/sections/Footer/data.ts +msgid "LinkedIn" +msgstr "" + +#. js-lingui-id: Jvz4g8 +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Listing on Twenty integrations page" +msgstr "" + +#. js-lingui-id: AHVzME +#: src/app/[locale]/(home)/three-cards-feature.data.ts +msgid "Live data and AI built" +msgstr "" + +#. js-lingui-id: GObQuL +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Live updates" +msgstr "" + +#. js-lingui-id: zmRZwk +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Live updates are unavailable, which is almost more honest." +msgstr "" + +#. js-lingui-id: M7lzaL +#: src/app/[locale]/why-twenty/page.tsx +msgid "lives in the code you own." +msgstr "" + +#. js-lingui-id: ObPscj +#: src/app/[locale]/enterprise/activate/page.tsx +msgid "Loading activation…" +msgstr "" + +#. js-lingui-id: 7VBQ2j +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Local, Production" +msgstr "" + +#. js-lingui-id: JO8Bdx +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Lock-in" +msgstr "" + +#. js-lingui-id: sQia9P +#: src/sections/Menu/components/Drawer.tsx +#: src/sections/Menu/components/Cta.tsx +msgid "Log in" +msgstr "" + +#. js-lingui-id: KPLAuI +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Lower CRM cost" +msgstr "" + +#. js-lingui-id: i3PI5l +#: src/app/[locale]/(home)/page.tsx +msgid "Make your GTM team happy" +msgstr "" + +#. js-lingui-id: Si4WyF +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Management Consulting" +msgstr "" + +#. js-lingui-id: poX06f +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Manual work at core" +msgstr "" + +#. js-lingui-id: VJOKPB +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Maps view" +msgstr "" + +#. js-lingui-id: u0wogL +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Marketing assets & brand resources" +msgstr "" + +#. js-lingui-id: LiMSZr +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Marketplace listing" +msgstr "" + +#. js-lingui-id: bJNKJ1 +#: src/sections/Menu/data.ts +msgid "Master every corner of Twenty" +msgstr "" + +#. js-lingui-id: Zw+Zv+ +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "MCP server" +msgstr "" + +#. js-lingui-id: zxcfgg +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "means control" +msgstr "" + +#. js-lingui-id: bwCiCi +#: src/sections/Menu/data.ts +msgid "Meet the certified agencies and consultants implementing Twenty for teams worldwide." +msgstr "" + +#. js-lingui-id: EhO+8s +#: src/lib/website-routing/static-website-routes.ts +msgid "Meet the teams running their business on Twenty. Real customer stories on how they shaped the CRM to fit their workflow." +msgstr "" + +#. js-lingui-id: ujYZ+f +#: src/sections/CaseStudyCatalog/components/Promo.tsx +msgid "Meet the teams who shaped Twenty into their own CRM with self-hosted deployments, AI-assisted workflows, and API-first product stacks." +msgstr "" + +#. js-lingui-id: ZdvtU2 +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "migration workflow" +msgstr "" + +#. js-lingui-id: so04x6 +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Mike and Azmat from Nine Dots stepped in to fix that, not by changing how Homeseller works, but by building a system that finally fit around it." +msgstr "" + +#. js-lingui-id: izddzp +#: src/app/[locale]/partners/testimonials.data.ts +msgid "Mike Babiy" +msgstr "" + +#. js-lingui-id: +8Nek/ +#: src/sections/Plans/components/BillingToggle.tsx +msgid "Monthly" +msgstr "" + +#. js-lingui-id: VqXuUv +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "More options available!" +msgstr "" + +#. js-lingui-id: XPXSwi +#: src/lib/website-routing/static-website-routes.ts +msgid "Most packaged software makes companies more similar. Learn why the future of CRM is built, not bought." +msgstr "" + +#. js-lingui-id: ZPjy5q +#: src/app/[locale]/product/page.tsx +msgid "moves fast" +msgstr "" + +#. js-lingui-id: dlKkNL +#: src/app/[locale]/product/feature.data.ts +msgid "Multi-file upload on records" +msgstr "" + +#. js-lingui-id: 6YtxFj +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Name" +msgstr "" + +#. js-lingui-id: LfNsfa +#: src/app/[locale]/product/stepper.data.ts +msgid "Need a quick change? Skip the engineering ticket. Customize your workspace in minutes." +msgstr "" + +#. js-lingui-id: 4sbgPq +#: src/app/[locale]/pricing/engagement-band.data.ts +msgid "Need help with customization?" +msgstr "" + +#. js-lingui-id: 8Cp4Of +#: src/app/[locale]/customers/netzero/page.tsx +msgid "NetZero" +msgstr "" + +#. js-lingui-id: J8ZssL +#: src/app/[locale]/(home)/helped.data.ts +msgid "NetZero runs a modular Twenty setup across carbon credits, ag products, and industrial systems." +msgstr "" + +#. js-lingui-id: 7HVBV+ +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/netzero/page.tsx +msgid "NetZero uses Twenty as a modular CRM across product lines and countries, with a roadmap into AI-assisted workflows." +msgstr "" + +#. js-lingui-id: 3PtYTO +#: src/app/[locale]/customers/netzero/page.tsx +msgid "NetZero works with the agro-industry, serving clients from multinationals to smallholder farmers. They sell carbon credits, agricultural products, and franchised industrial systems across three different product lines, multiple countries, and multiple company sizes. When Olivier Reinaud, co-founder of NetZero, started looking at CRMs in late 2024, he was not chasing the most feature-rich platform. He wanted the right foundation." +msgstr "" + +#. js-lingui-id: KqojOL +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Next steps" +msgstr "" + +#. js-lingui-id: qZ+egR +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Nine Dots put Twenty at the center of Homeseller's stack with APIs, automation, and AI on top of WhatsApp-heavy operations." +msgstr "" + +#. js-lingui-id: sGuHDu +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Nine Dots rebuilt Homeseller's operations on Twenty, with a custom data model shaped around their sales flow. Because Twenty is open and everything is accessible via API, they connected it to what the business actually needed: n8n for automated workflows (in-app workflows were not available at that time), Grafana for live dashboards fed from Twenty, and a custom AI layer to parse and extract structured insights from more than 2,000 WhatsApp messages a day." +msgstr "" + +#. js-lingui-id: 1UzENP +#: src/sections/PlanTable/components/Content.tsx +msgid "No" +msgstr "" + +#. js-lingui-id: o+/Ad5 +#: src/app/[locale]/customers/act-education/page.tsx +msgid "No more renting someone else's" +msgstr "" + +#. js-lingui-id: LmWdm6 +#: src/app/[locale]/customers/act-education/page.tsx +msgid "No more renting someone else's structure" +msgstr "" + +#. js-lingui-id: K1QTmg +#: src/sections/Plans/data.ts +msgid "No open-source distribution requirement" +msgstr "" + +#. js-lingui-id: q9f4Tp +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/netzero/page.tsx +msgid "No-code" +msgstr "" + +#. js-lingui-id: SiZ/7E +#: src/sections/Faq/data.ts +msgid "No. Build custom objects, fields, views, and no-code workflows straight from Settings. Unlimited, no extra charge." +msgstr "" + +#. js-lingui-id: 7ngHsA +#: src/app/[locale]/why-twenty/page.tsx +msgid "not bought." +msgstr "" + +#. js-lingui-id: k3T2mM +#: src/app/[locale]/why-twenty/editorial-three.data.ts +msgid "Now a developer can describe what they want to Claude Code and have a working app in an afternoon. A custom object, a scoring workflow, a new view, an integration. The bottleneck isn't building anymore. It's whether your platform lets you." +msgstr "" + +#. js-lingui-id: CzeIij +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Number of dashboards" +msgstr "" + +#. js-lingui-id: 0dVyEt +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "of a go-to-market stack" +msgstr "" + +#. js-lingui-id: pkrZnO +#: src/app/[locale]/customers/netzero/page.tsx +msgid "Olivier recognizes that NetZero's current use of Twenty is still relatively simple: workflows and integrations are not yet as deep as he eventually wants, because he prioritized getting foundations right first." +msgstr "" + +#. js-lingui-id: l/1LQc +#: src/app/[locale]/(home)/testimonials.data.ts +msgid "Olivier Reinaud" +msgstr "" + +#. js-lingui-id: D2b+1q +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/netzero/page.tsx +msgid "On a single CRM" +msgstr "" + +#. js-lingui-id: XhXmzW +#: src/sections/CaseStudy/components/CaseStudySectionNav.tsx +msgid "On this page" +msgstr "" + +#. js-lingui-id: jeGPx0 +#: src/app/[locale]/customers/page.tsx +msgid "on Twenty" +msgstr "" + +#. js-lingui-id: pbmZ7R +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Onboarding Packs" +msgstr "" + +#. js-lingui-id: /DFChQ +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "One" +msgstr "" + +#. js-lingui-id: Zt1UZb +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "One API to rule them all" +msgstr "" + +#. js-lingui-id: pShqss +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Only $5 for SSO. Practically a charity program." +msgstr "" + +#. js-lingui-id: 3WErRa +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "open APIs" +msgstr "" + +#. js-lingui-id: pkXQA+ +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Open your Twenty self-hosted instance Settings → Enterprise." +msgstr "" + +#. js-lingui-id: wtKPUT +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "open-source primitives." +msgstr "" + +#. js-lingui-id: 8THUfN +#: src/app/[locale]/(home)/home-stepper.data.ts +msgid "open-source software" +msgstr "" + +#. js-lingui-id: 5RkmVr +#: src/app/[locale]/why-twenty/signoff.data.ts +msgid "Open-source, AI-ready, and yours to shape." +msgstr "" + +#. js-lingui-id: oYQN5j +#: src/app/[locale]/pricing/page.tsx +msgid "or not !" +msgstr "" + +#. js-lingui-id: ucgZ0o +#: src/sections/Plans/data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Organization" +msgstr "" + +#. js-lingui-id: M12WZl +#: src/app/[locale]/partners/page.tsx +msgid "our partner" +msgstr "" + +#. js-lingui-id: dKGg8T +#: src/app/[locale]/pricing/salesforce.data.ts +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Out of stock" +msgstr "" + +#. js-lingui-id: C8Ex6S +#: src/app/[locale]/product/page.tsx +msgid "out of the box" +msgstr "" + +#. js-lingui-id: PASC/7 +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Outlived every redesign since 2004." +msgstr "" + +#. js-lingui-id: Bwsi7B +#: src/app/[locale]/(home)/helped.data.ts +msgid "Own your CRM end to end" +msgstr "" + +#. js-lingui-id: oDAEQq +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "Ownership" +msgstr "" + +#. js-lingui-id: NmF/Vo +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Owning your stack remains mysteriously out of stock." +msgstr "" + +#. js-lingui-id: +AiiMt +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Pagination builds character." +msgstr "" + +#. js-lingui-id: mPkInZ +#: src/sections/Menu/data.ts +#: src/sections/Footer/data.ts +msgid "Partners" +msgstr "" + +#. js-lingui-id: itrQKv +#: src/lib/website-routing/static-website-routes.ts +msgid "Partners | Twenty" +msgstr "" + +#. js-lingui-id: 9yo8NN +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Paste the key and click Activate." +msgstr "" + +#. js-lingui-id: nxV7o2 +#: src/app/[locale]/customers/netzero/page.tsx +msgid "paying off" +msgstr "" + +#. js-lingui-id: FFkjaT +#: src/app/[locale]/product/feature.data.ts +msgid "Pipeline Management" +msgstr "" + +#. js-lingui-id: GwGdy2 +#: src/lib/website-routing/static-website-routes.ts +msgid "Plans that scale with your team. Compare tiers of the #1 open source CRM." +msgstr "" + +#. js-lingui-id: 0gBYFU +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Please complete all required fields before submitting." +msgstr "" + +#. js-lingui-id: iCh6/3 +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "plumbing" +msgstr "" + +#. js-lingui-id: a7u1N9 +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Price" +msgstr "" + +#. js-lingui-id: aHCEmh +#: src/sections/Menu/data.ts +#: src/sections/Footer/data.ts +#: src/app/[locale]/pricing/page.tsx +msgid "Pricing" +msgstr "" + +#. js-lingui-id: CboOX5 +#: src/lib/website-routing/static-website-routes.ts +msgid "Pricing | Twenty" +msgstr "" + +#. js-lingui-id: J2IAmT +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "Principal and Founder" +msgstr "" + +#. js-lingui-id: kXBQYM +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/partners/testimonials.data.ts +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "Principal and Founder, Alternative Partners" +msgstr "" + +#. js-lingui-id: k62X/2 +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Priority support" +msgstr "" + +#. js-lingui-id: LcET2C +#: src/sections/Footer/data.ts +msgid "Privacy Policy" +msgstr "" + +#. js-lingui-id: DJ3uVe +#: src/lib/website-routing/static-website-routes.ts +msgid "Privacy Policy | Twenty" +msgstr "" + +#. js-lingui-id: 3fPjUY +#: src/sections/Plans/data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Pro" +msgstr "" + +#. js-lingui-id: 4gXof3 +#: src/lib/website-routing/static-website-routes.ts +msgid "Product | Twenty" +msgstr "" + +#. js-lingui-id: kIGKva +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "Production grade quality" +msgstr "" + +#. js-lingui-id: CcK9cq +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Programs that previously needed heavy manual coordination now run end-to-end with automation. Institutions get a scalable, intelligent system; students get faster preparation for interviews that matter; W3villa shipped a product institutions can build revenue around." +msgstr "" + +#. js-lingui-id: 9NAqO4 +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Proposal automation" +msgstr "" + +#. js-lingui-id: a24q7E +#: src/app/[locale]/(home)/problem.data.ts +msgid "Proprietary languages, slow deployment cycles, and \"black box\" logic." +msgstr "" + +#. js-lingui-id: kS6G/9 +#: src/app/[locale]/customers/act-education/page.tsx +msgid "pulled the plug" +msgstr "" + +#. js-lingui-id: frfCYp +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Q1 2026" +msgstr "" + +#. js-lingui-id: gSQVmr +#: src/sections/Helped/components/Card.tsx +msgid "Read the case" +msgstr "" + +#. js-lingui-id: EqCbT9 +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Read/Edit/Delete permissions" +msgstr "" + +#. js-lingui-id: SNf+4G +#: src/app/[locale]/customers/page.tsx +msgid "Ready to build" +msgstr "" + +#. js-lingui-id: 9BL+5g +#: src/app/[locale]/product/page.tsx +#: src/app/[locale]/partners/page.tsx +msgid "Ready to grow" +msgstr "" + +#. js-lingui-id: 1d+b/h +#: src/app/[locale]/customers/_components/CustomersCaseStudySignoff.tsx +msgid "" +"Ready to grow\n" +"with Twenty?" +msgstr "" + +#. js-lingui-id: NIwjHh +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Real Estate" +msgstr "" + +#. js-lingui-id: CH+Ona +#: src/app/[locale]/customers/page.tsx +msgid "Real stories from real teams about how they shaped Twenty to fit their workflow and accelerated their growth." +msgstr "" + +#. js-lingui-id: 5O1eTm +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Real-time changes? That will be a premium surprise." +msgstr "" + +#. js-lingui-id: 4hc7hc +#: src/app/[locale]/product/three-cards.data.ts +msgid "Real-time data" +msgstr "" + +#. js-lingui-id: dKQCrj +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Real-time is a state of mind, not a feature." +msgstr "" + +#. js-lingui-id: Zhiuz9 +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Record quarter" +msgstr "" + +#. js-lingui-id: PWlTvh +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Records" +msgstr "" + +#. js-lingui-id: rwWjWg +#: src/sections/Footer/data.ts +msgid "Release Notes" +msgstr "" + +#. js-lingui-id: 5icoS1 +#: src/sections/Menu/data.ts +#: src/app/[locale]/releases/page.tsx +msgid "Releases" +msgstr "" + +#. js-lingui-id: Bh0u1L +#: src/lib/website-routing/static-website-routes.ts +msgid "Releases | Twenty" +msgstr "" + +#. js-lingui-id: a/aGHe +#: src/app/[locale]/product/feature.data.ts +msgid "Rename, download, and delete attachments" +msgstr "" + +#. js-lingui-id: t9yxlZ +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Reports" +msgstr "" + +#. js-lingui-id: SY/an2 +#: src/app/[locale]/product/feature.data.ts +msgid "Reports & Dashboards" +msgstr "" + +#. js-lingui-id: 5Cs/CL +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Resale discounts & revenue share" +msgstr "" + +#. js-lingui-id: s+MGs7 +#: src/sections/Menu/data.ts +msgid "Resources" +msgstr "" + +#. js-lingui-id: oC2l7f +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "REST & GraphQL API" +msgstr "" + +#. js-lingui-id: kx0s+n +#: src/app/[locale]/customers/netzero/page.tsx +msgid "Results" +msgstr "" + +#. js-lingui-id: 2itg0p +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Retro 2015" +msgstr "" + +#. js-lingui-id: OH7xLu +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Revenue share for referred customers" +msgstr "" + +#. js-lingui-id: frqduN +#: src/app/[locale]/product/feature.data.ts +msgid "Rich notes attached to records" +msgstr "" + +#. js-lingui-id: /gaSVU +#: src/app/[locale]/customers/netzero/page.tsx +msgid "Roadmap" +msgstr "" + +#. js-lingui-id: C77N5M +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +msgid "Roles & Permissions" +msgstr "" + +#. js-lingui-id: Wdh41P +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Row-level permissions" +msgstr "" + +#. js-lingui-id: mRpuWW +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Running mock interview programs for hundreds of students sounds straightforward. In practice, universities and training institutes hit the same wall: registrations entered by hand, interview links sent one by one, faculty reviewing every session without scoring or classification. At real scale, it breaks." +msgstr "" + +#. js-lingui-id: Burn4/ +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Salesfarce Add-on Center" +msgstr "" + +#. js-lingui-id: xTfEgV +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Salesfarce Pro" +msgstr "" + +#. js-lingui-id: /UolBB +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Salesforce Classic" +msgstr "" + +#. js-lingui-id: l69E+u +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "Salesforce migration" +msgstr "" + +#. js-lingui-id: CADlRK +#: src/app/[locale]/why-twenty/marquee.data.ts +msgid "Same CRM" +msgstr "" + +#. js-lingui-id: 60i6S+ +#: src/app/[locale]/why-twenty/marquee.data.ts +msgid "Same output" +msgstr "" + +#. js-lingui-id: xl2tJ9 +#: src/app/[locale]/why-twenty/marquee.data.ts +msgid "Same results" +msgstr "" + +#. js-lingui-id: w9QWxz +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +msgid "SAML/OIDC SSO" +msgstr "" + +#. js-lingui-id: elz5ka +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Saved / month" +msgstr "" + +#. js-lingui-id: tigXYO +#: src/app/[locale]/customers/9dots/page.tsx +msgid "saved every month" +msgstr "" + +#. js-lingui-id: bifv6N +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Scale" +msgstr "" + +#. js-lingui-id: rbgetd +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Scale without" +msgstr "" + +#. js-lingui-id: 842ybw +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Scale without breaking operations" +msgstr "" + +#. js-lingui-id: HVOGoW +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Seats limit" +msgstr "" + +#. js-lingui-id: a3LDKx +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Security" +msgstr "" + +#. js-lingui-id: q20Cda +#: src/app/[locale]/customers/page.tsx +msgid "See how teams" +msgstr "" + +#. js-lingui-id: aB+XpI +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "See more features" +msgstr "" + +#. js-lingui-id: FY4ykg +#: src/sections/Menu/data.ts +msgid "See the latest release" +msgstr "" + +#. js-lingui-id: JDp1bg +#: src/app/[locale]/product/three-cards.data.ts +msgid "See updates as they happen. Work with your team and agents seamlessly." +msgstr "" + +#. js-lingui-id: 0kgOPB +#. placeholder {0}: latest.release +#: src/lib/releases/get-latest-release-preview.ts +msgid "See what shipped in {0}" +msgstr "" + +#. js-lingui-id: wgNoIs +#: src/sections/Salesforce/components/PricingWindow.tsx +msgid "Select all" +msgstr "" + +#. js-lingui-id: KP119R +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Select your team" +msgstr "" + +#. js-lingui-id: 6JhL+3 +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/alternative-partners/page.tsx +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "Self-hosted" +msgstr "" + +#. js-lingui-id: 5CWy3T +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Self-hosted means AC&T carries no vendor risk: no pricing model that can change, no platform that can disappear, no forced migration. The system is theirs." +msgstr "" + +#. js-lingui-id: 5WmQ5O +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "Self-hosted means control" +msgstr "" + +#. js-lingui-id: 6QPYP7 +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "self-hosted Twenty" +msgstr "" + +#. js-lingui-id: GrBvv/ +#: src/app/[locale]/pricing/salesforce.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/enterprise/activate/page.tsx +msgid "Self-hosting" +msgstr "" + +#. js-lingui-id: YdhUoe +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Self-hosting, now for rent!" +msgstr "" + +#. js-lingui-id: NaHz2o +#: src/sections/Plans/components/SelfHostToggle.tsx +msgid "Selfhosting" +msgstr "" + +#. js-lingui-id: 2ZItPC +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Share Twenty with your audience and help shape the future of the #1 open source CRM. We're looking for creators, educators, and community builders who want to showcase great software." +msgstr "" + +#. js-lingui-id: 3VFzsh +#: src/app/[locale]/(home)/helped.data.ts +msgid "Ship a product on Twenty" +msgstr "" + +#. js-lingui-id: 6lGV3K +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Show less" +msgstr "" + +#. js-lingui-id: mA6cL1 +#: src/sections/Faq/data.ts +msgid "Sign up for Cloud in under a minute and start your 30-day trial. For larger rollouts, our 4-hour Onboarding Packs or certified partners get you live in 1–2 weeks." +msgstr "" + +#. js-lingui-id: AQK14J +#: src/app/[locale]/pricing/page.tsx +msgid "Simple" +msgstr "" + +#. js-lingui-id: V9dFwD +#: src/sections/Footer/data.ts +msgid "Sitemap" +msgstr "" + +#. js-lingui-id: t6ComU +#: src/app/[locale]/(home)/three-cards-feature.data.ts +msgid "Skip the clunky UX that always comes with custom." +msgstr "" + +#. js-lingui-id: bUmSwL +#: src/app/[locale]/(home)/three-cards-feature.data.ts +msgid "Smart patterns, shortcuts, and layouts make everyday tasks faster and easier to execute." +msgstr "" + +#. js-lingui-id: 7PyS12 +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Solutions Partner" +msgstr "" + +#. js-lingui-id: e1+XW/ +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Solutions Partners" +msgstr "" + +#. js-lingui-id: AVfp38 +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Some call this enterprise pricing. We prefer a CRM where API access, webhooks, and workflows don't show up as surprise add-ons." +msgstr "" + +#. js-lingui-id: /UwJ/B +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Soon: earn revenue" +msgstr "" + +#. js-lingui-id: B/mYo/ +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Source code access" +msgstr "" + +#. js-lingui-id: vnS6Rf +#: src/app/[locale]/pricing/salesforce.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "SSO" +msgstr "" + +#. js-lingui-id: uQl22y +#: src/sections/Plans/data.ts +#: src/sections/Plans/data.ts +msgid "Standard support" +msgstr "" + +#. js-lingui-id: wxW2Sv +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Start automating at huge scale!" +msgstr "" + +#. js-lingui-id: F02wii +#: src/app/[locale]/product/page.tsx +#: src/app/[locale]/pricing/page.tsx +#: src/app/[locale]/partners/page.tsx +#: src/app/[locale]/customers/page.tsx +#: src/app/[locale]/(home)/page.tsx +msgid "Start building, with Twenty" +msgstr "" + +#. js-lingui-id: XO628f +#: src/sections/Plans/components/Card.tsx +msgid "Start for free" +msgstr "" + +#. js-lingui-id: sBOFB0 +#: src/app/[locale]/pricing/hero.data.ts +msgid "" +"Start your free trial today\n" +"without credit card." +msgstr "" + +#. js-lingui-id: FT+TW7 +#: src/app/[locale]/(home)/home-stepper.data.ts +msgid "Stay in control with our" +msgstr "" + +#. js-lingui-id: j9c8rb +#: src/app/[locale]/product/three-cards.data.ts +msgid "Stay in Flow" +msgstr "" + +#. js-lingui-id: bxgoJR +#: src/sections/Menu/data.ts +msgid "Step-by-step guides and playbooks to help your team get the most out of their workspace." +msgstr "" + +#. js-lingui-id: RMK92/ +#: src/app/[locale]/product/page.tsx +#: src/app/[locale]/pricing/page.tsx +#: src/app/[locale]/partners/page.tsx +#: src/app/[locale]/customers/page.tsx +#: src/app/[locale]/(home)/page.tsx +msgid "Stop fighting custom." +msgstr "" + +#. js-lingui-id: UCLArd +#: src/app/[locale]/product/three-cards.data.ts +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "Stop settling for trade-offs." +msgstr "" + +#. js-lingui-id: lm4alq +#: src/app/[locale]/customers/act-education/page.tsx +msgid "structure" +msgstr "" + +#. js-lingui-id: Sv4BSp +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Subdomain (yourco.twenty.com)" +msgstr "" + +#. js-lingui-id: H5/rZQ +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Submit application" +msgstr "" + +#. js-lingui-id: MDqQmP +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Submitting…" +msgstr "" + +#. js-lingui-id: XYLcNv +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Support" +msgstr "" + +#. js-lingui-id: 3w+Aox +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Table, Kanban, Calendar" +msgstr "" + +#. js-lingui-id: 9M02G5 +#: src/app/[locale]/product/stepper.data.ts +msgid "Tailor record pages, menus, and views" +msgstr "" + +#. js-lingui-id: JAKtcG +#: src/sections/Footer/data.ts +#: src/app/[locale]/product/page.tsx +#: src/app/[locale]/product/page.tsx +#: src/app/[locale]/pricing/page.tsx +#: src/app/[locale]/partners/page.tsx +#: src/app/[locale]/partners/components/PartnerApplication/PartnerSignoffCtas.tsx +#: src/app/[locale]/customers/page.tsx +#: src/app/[locale]/customers/page.tsx +#: src/app/[locale]/customers/_components/CustomersCaseStudySignoff.tsx +#: src/app/[locale]/(home)/page.tsx +#: src/app/[locale]/(home)/page.tsx +msgid "Talk to us" +msgstr "" + +#. js-lingui-id: GWMpL3 +#: src/app/[locale]/product/feature.data.ts +msgid "Tasks & Activities" +msgstr "" + +#. js-lingui-id: Ye3KKA +#: src/sections/Menu/data.ts +msgid "Team up with a Twenty expert" +msgstr "" + +#. js-lingui-id: vsdUaL +#: src/app/[locale]/releases/page.tsx +msgid "Technical notes" +msgstr "" + +#. js-lingui-id: 8dX1MJ +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Technology Partner" +msgstr "" + +#. js-lingui-id: EeZsnh +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Technology Partners" +msgstr "" + +#. js-lingui-id: gNW5s8 +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Tell us about the custom solutions or integrations you plan to build." +msgstr "" + +#. js-lingui-id: 1ZwmkP +#: src/app/[locale]/customers/netzero/page.tsx +msgid "template" +msgstr "" + +#. js-lingui-id: mvP/25 +#: src/sections/Footer/data.ts +msgid "Terms and Conditions" +msgstr "" + +#. js-lingui-id: BEX1RA +#: src/lib/website-routing/static-website-routes.ts +msgid "Terms of Service | Twenty" +msgstr "" + +#. js-lingui-id: iqG74V +#: src/lib/website-routing/static-website-routes.ts +msgid "Terms of Service for Twenty.com PBC, including use of Twenty.com, sub-domains, and related services." +msgstr "" + +#. js-lingui-id: dn9a7V +#: src/app/[locale]/product/page.tsx +msgid "that" +msgstr "" + +#. js-lingui-id: FjkPYg +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "That opened the door to something more powerful. Justin built a custom front end that pulls live data from those systems into a single view, tailored to each role. When a proposal is won, what used to require four separate people manually setting up instances across four different tools now happens in a single click, drawing on data collected in Twenty across the full opportunity lifecycle. It is another shift toward higher-value work for clients." +msgstr "" + +#. js-lingui-id: 8S4psU +#: src/app/[locale]/customers/9dots/page.tsx +msgid "That works until you need to understand the business underneath. Which deals are stuck? Where are leads coming from? What is the close rate? With spreadsheets and a legacy custom CRM that could not keep up, those questions were nearly impossible to answer." +msgstr "" + +#. js-lingui-id: yWOGs1 +#: src/app/[locale]/(home)/page.tsx +msgid "that's quick to flex" +msgstr "" + +#. js-lingui-id: rTqABu +#: src/lib/website-routing/static-website-routes.ts +msgid "The #1 open source CRM for modern teams. Modular, scalable, and built to fit your business." +msgstr "" + +#. js-lingui-id: G2IgPj +#: src/app/[locale]/customers/netzero/page.tsx +msgid "The bet is" +msgstr "" + +#. js-lingui-id: NT2bSJ +#: src/app/[locale]/customers/netzero/page.tsx +msgid "The bet is paying off" +msgstr "" + +#. js-lingui-id: Xmr/wH +#: src/app/[locale]/customers/9dots/page.tsx +msgid "the business" +msgstr "" + +#. js-lingui-id: Wie492 +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "The business development team finally had the CRM they had been asking for. Adoption came naturally: their data was already there when they logged in." +msgstr "" + +#. js-lingui-id: 2kHqXc +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "The CEO had resisted bringing in a CRM for years. The business development team had no experience using one, and the licensing costs of well-known CRMs like Salesforce or HubSpot were hard to justify without any guarantee of adoption: CRMs are only as good as the maintenance of the data inside them." +msgstr "" + +#. js-lingui-id: 5DpEh7 +#: src/app/[locale]/customers/9dots/page.tsx +msgid "The CRM as a" +msgstr "" + +#. js-lingui-id: 2sfVd8 +#: src/app/[locale]/customers/9dots/page.tsx +msgid "The CRM as a control hub" +msgstr "" + +#. js-lingui-id: XlFsoH +#: src/app/[locale]/customers/netzero/page.tsx +msgid "The early bet on the architecture is holding, and upcoming AI features are expected to make it even more relevant." +msgstr "" + +#. js-lingui-id: kCUcnD +#: src/app/[locale]/partners/testimonials.data.ts +msgid "The flexibility is just amazing. Literally, there's nothing you cannot do. You can create objects, access everything through the API, pull notes and send them to the portal. Try doing that in HubSpot. No way. It's the true ability to build exactly what's actually needed." +msgstr "" + +#. js-lingui-id: MaHqAc +#: src/app/[locale]/(home)/testimonials.data.ts +msgid "The flexibility is really what made the difference. Our needs evolve very fast. I discover a new need and in two clicks I can address it. That is a real advantage when you are moving quickly." +msgstr "" + +#. js-lingui-id: Q5iZ9Q +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "The flexibility to wire this together, without outside help and without fighting the platform, is what made it possible for a single person to stand up and maintain a connected stack across an entire consultancy." +msgstr "" + +#. js-lingui-id: l0eUFC +#: src/app/[locale]/customers/9dots/page.tsx +msgid "The full rollout landed in July 2025. Since then, Nine Dots built a Smart Assistant on top of the system, nudging agents with tasks, reminders, and on-demand market analysis. Some agents never open Twenty directly, yet they are powered by it, outperforming peers on manual processes alone. By Q1 2026, Homeseller had recorded its best sales quarter ever." +msgstr "" + +#. js-lingui-id: SiW1pC +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "the future of CRM" +msgstr "" + +#. js-lingui-id: Uslh7V +#: src/app/[locale]/why-twenty/page.tsx +msgid "The future of CRM is built," +msgstr "" + +#. js-lingui-id: sm10Rg +#: src/app/[locale]/(home)/problem.data.ts +msgid "The Giant Monolith" +msgstr "" + +#. js-lingui-id: nFB8I1 +#: src/app/[locale]/(home)/problem.data.ts +msgid "The In-house Burden" +msgstr "" + +#. js-lingui-id: ZNzKXV +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "The key decision was not to build everything from scratch. Twenty covers the data model, permissions, authentication, and workflow engine, the parts that would have taken months to rebuild, so the team could focus on product-specific logic." +msgstr "" + +#. js-lingui-id: q1kKLq +#: src/app/[locale]/why-twenty/editorial-three.data.ts +msgid "The opportunity" +msgstr "" + +#. js-lingui-id: ZgWQl4 +#: src/app/[locale]/customers/act-education/page.tsx +msgid "the overhead" +msgstr "" + +#. js-lingui-id: 2NJdyz +#: src/app/[locale]/(home)/problem.data.ts +msgid "The Problem." +msgstr "" + +#. js-lingui-id: Q8D9Lf +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "the product" +msgstr "" + +#. js-lingui-id: 3/wAFg +#: src/app/[locale]/customers/w3villa/page.tsx +#: src/app/[locale]/customers/w3villa/page.tsx +#: src/app/[locale]/customers/act-education/page.tsx +#: src/app/[locale]/customers/act-education/page.tsx +#: src/app/[locale]/customers/9dots/page.tsx +#: src/app/[locale]/customers/9dots/page.tsx +msgid "The result" +msgstr "" + +#. js-lingui-id: nAWeyi +#: src/app/[locale]/customers/act-education/page.tsx +msgid "The result is a system that fits how AC&T already worked, instead of the other way around." +msgstr "" + +#. js-lingui-id: tfG/xw +#: src/app/[locale]/customers/netzero/page.tsx +msgid "The right" +msgstr "" + +#. js-lingui-id: nbHA9N +#: src/app/[locale]/customers/netzero/page.tsx +msgid "The right foundation" +msgstr "" + +#. js-lingui-id: omVXTg +#: src/app/[locale]/customers/alternative-partners/page.tsx +msgid "The self-hosted setup means Alternative Partners owns the full stack: no vendor access to their data, no dependency on a SaaS pricing model, full control over how the system evolves. The migration was fast because of AI; the result is durable because the stack is open source." +msgstr "" + +#. js-lingui-id: 1tN3qf +#: src/app/[locale]/why-twenty/editorial-one.data.ts +msgid "The shift" +msgstr "" + +#. js-lingui-id: AnGOwN +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "The situation" +msgstr "" + +#. js-lingui-id: u0uFxg +#: src/app/[locale]/(home)/testimonials.data.ts +msgid "They are the real sales" +msgstr "" + +#. js-lingui-id: 53RXLB +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "They call it customer loyalty. We call it a very affectionate cage." +msgstr "" + +#. js-lingui-id: RhmZMe +#: src/app/[locale]/customers/act-education/page.tsx +msgid "They did not just replace a tool. They took back ownership of how their business runs." +msgstr "" + +#. js-lingui-id: 5YzqyT +#: src/app/[locale]/customers/act-education/page.tsx +msgid "They evaluated Salesforce, Zoho, Pipedrive, and SuiteCRM. Each came with the same tradeoffs: too expensive, too rigid, or too generic, and none fixed the underlying problem. They were still renting a structure they did not control." +msgstr "" + +#. js-lingui-id: 2y6W+f +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "to rule them all" +msgstr "" + +#. js-lingui-id: 5eFktS +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Tool integration" +msgstr "" + +#. js-lingui-id: xv7HSr +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "total per month with fixed cost" +msgstr "" + +#. js-lingui-id: hmu++i +#: src/app/[locale]/product/feature.data.ts +msgid "Track amount and close date" +msgstr "" + +#. js-lingui-id: POzmFl +#: src/sections/Menu/data.ts +#: src/lib/releases/get-latest-release-preview.ts +msgid "Track every release with changelogs, highlights and demos of the newest features." +msgstr "" + +#. js-lingui-id: yiYtW0 +#: src/lib/website-routing/static-website-routes.ts +#: src/app/[locale]/product/hero.data.ts +msgid "Track relationships, manage pipelines, and take action quickly with a CRM that feels intuitive from day one." +msgstr "" + +#. js-lingui-id: XzfkgA +#: src/app/[locale]/(home)/page.tsx +msgid "tradeoffs" +msgstr "" + +#. js-lingui-id: ccIZU2 +#: src/app/[locale]/pricing/page.tsx +msgid "Trust the n°1 CRM," +msgstr "" + +#. js-lingui-id: KtyN0b +#: src/sections/TrustedBy/data.ts +msgid "trusted by" +msgstr "" + +#. js-lingui-id: D5Kf4t +#: src/app/[locale]/why-twenty/editorial-four.data.ts +msgid "Tuesday your team learns that deals with a technical champion close 3x faster. Wednesday you add the field, wire up the scoring, adjust the workflow. By Thursday your agents are acting on it. That feedback loop is the edge. And it only works if the CRM is yours." +msgstr "" + +#. js-lingui-id: Sl2dv/ +#: src/lib/website-routing/static-website-routes.ts +msgid "Twenty | #1 open source CRM" +msgstr "" + +#. js-lingui-id: G4fKLP +#: src/app/[locale]/partners/testimonials.data.ts +msgid "Twenty Apps opens the door to building products, not just implementations. For example, we're developing a WhatsApp Business integration that any Twenty’s client could get. That's a recurring revenue stream we wouldn't have if we were just configuring someone else's platform." +msgstr "" + +#. js-lingui-id: mkTfZ1 +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Twenty as the" +msgstr "" + +#. js-lingui-id: 9gf3bc +#: src/lib/customers/case-study-catalog.ts +msgid "Twenty as the API backbone" +msgstr "" + +#. js-lingui-id: zj0CA+ +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Twenty as the API backbone of a go-to-market stack | Elevate Consulting" +msgstr "" + +#. js-lingui-id: 0qyMhk +#: src/app/[locale]/(home)/hero.data.ts +msgid "Twenty gives technical teams the building blocks for a custom CRM that meets complex business needs and quickly adapts as the business evolves." +msgstr "" + +#. js-lingui-id: jVhq41 +#: src/app/[locale]/partners/testimonials.data.ts +msgid "Twenty gives you the kind of flexibility that actually changes what you can offer your clients. The dev experience is clean, the APIs are open, and when something needs to be customized, you can just do it. There's no fighting the platform." +msgstr "" + +#. js-lingui-id: x/I85y +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Twenty is not only where CRM data lives. It is the API backbone that makes the rest of the stack possible." +msgstr "" + +#. js-lingui-id: K+zzSQ +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/9dots/page.tsx +msgid "Twenty lets us build a CRM around the business and not the business around the CRM." +msgstr "" + +#. js-lingui-id: AxT+g4 +#: src/app/[locale]/(home)/three-cards-feature.data.ts +msgid "Twenty makes it simple. It's clean, intuitive, and built to feel like Notion." +msgstr "" + +#. js-lingui-id: V1lV7B +#: src/sections/Plans/data.ts +msgid "Twenty team support" +msgstr "" + +#. js-lingui-id: WN9ufk +#: src/app/[locale]/product/page.tsx +msgid "Twenty?" +msgstr "" + +#. js-lingui-id: EIU345 +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Two-factor authentication" +msgstr "" + +#. js-lingui-id: uxuy4l +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "UI theme" +msgstr "" + +#. js-lingui-id: jqzUyM +#: src/app/[locale]/pricing/salesforce.data.ts +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Unavailable" +msgstr "" + +#. js-lingui-id: Hix/m6 +#: src/app/[locale]/product/feature.data.ts +msgid "Unified timeline (emails, events, tasks, notes, files)" +msgstr "" + +#. js-lingui-id: NIuIk1 +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Unlimited" +msgstr "" + +#. js-lingui-id: LF0tzk +#: src/sections/Plans/data.ts +msgid "Up to 50M automation credits/year" +msgstr "" + +#. js-lingui-id: RDRePw +#: src/sections/Plans/data.ts +msgid "Up to 5M automation credits/month" +msgstr "" + +#. js-lingui-id: Roaswv +#: src/sections/Menu/data.ts +#: src/sections/Footer/data.ts +msgid "User Guide" +msgstr "" + +#. js-lingui-id: 3XIgKU +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "User roles" +msgstr "" + +#. js-lingui-id: BAzC1v +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "View types" +msgstr "" + +#. js-lingui-id: mHtVst +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Visualize your customers on a map!" +msgstr "" + +#. js-lingui-id: CsHuOr +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "VP of Engineering" +msgstr "" + +#. js-lingui-id: uRh799 +#: src/app/[locale]/(home)/testimonials.data.ts +msgid "VP of Engineering at W3villa Technologies" +msgstr "" + +#. js-lingui-id: w2INie +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "VP of Engineering, W3villa Technologies" +msgstr "" + +#. js-lingui-id: s0tU5h +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "W3Grads" +msgstr "" + +#. js-lingui-id: R8BueT +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "W3villa built W3Grads (w3grads.com), an AI-powered mock interview platform for universities and training institutes, using Twenty as its operational backbone." +msgstr "" + +#. js-lingui-id: 7hHsoV +#: src/app/[locale]/(home)/helped.data.ts +msgid "W3villa built W3Grads for AI mock interviews at scale, with Twenty as the operational backbone." +msgstr "" + +#. js-lingui-id: QBOUnd +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "W3villa shipped W3Grads on Twenty for AI interviews, scoring, and institution-scale workflows without rebuilding CRM plumbing." +msgstr "" + +#. js-lingui-id: 7q5Vjn +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "W3villa Technologies" +msgstr "" + +#. js-lingui-id: ZabLEt +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "W3villa Technologies set out to solve it properly, not with a workaround, but with a product." +msgstr "" + +#. js-lingui-id: 3v2ipf +#: src/app/[locale]/(home)/three-cards-illustration.data.ts +msgid "W3villa used Twenty as a production-grade framework for the data model, permissions, authentication, and workflow engine they would otherwise have rebuilt themselves." +msgstr "" + +#. js-lingui-id: 8CHatv +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "We could not submit your application. Please try again in a moment." +msgstr "" + +#. js-lingui-id: MjWeUE +#: src/app/[locale]/(home)/testimonials.data.ts +msgid "We didn't want to patch over the problem. We wanted to build something institutions could rely on at scale, and that meant starting from a foundation solid enough to support the full complexity of what we had in mind." +msgstr "" + +#. js-lingui-id: gdnUik +#: src/app/[locale]/partners/hero.data.ts +msgid "We're building the #1 open source CRM, but we can't do it alone. Join our partner ecosystem and grow with us." +msgstr "" + +#. js-lingui-id: v1kQyJ +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Webhooks" +msgstr "" + +#. js-lingui-id: ZhnM/K +#: src/app/[locale]/pricing/salesforce.data.ts +msgid "Webhooks (Change Data Capture)" +msgstr "" + +#. js-lingui-id: jBX2tZ +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Website or github link *" +msgstr "" + +#. js-lingui-id: Z34FQq +#: src/app/[locale]/customers/netzero/page.tsx +msgid "What convinced Olivier was the flexibility of the platform and where it was headed. Even when initial needs were basic record-keeping, he still needed a custom data model with granular permissions to manage the wide range of NetZero activities. He also needed a system that could adapt quickly to a fast-iteration company." +msgstr "" + +#. js-lingui-id: phiBjc +#: src/sections/Faq/data.ts +msgid "What does Twenty cost?" +msgstr "" + +#. js-lingui-id: WVQaGB +#: src/app/[locale]/customers/netzero/page.tsx +msgid "What is coming in April 2026 is what he has been waiting for: AI-assisted workflow creation, describing what he needs and iterating from there instead of building complex logic from scratch. For a founder who runs the CRM himself, that changes what is realistically possible." +msgstr "" + +#. js-lingui-id: iOAq7n +#: src/app/[locale]/customers/elevate-consulting/page.tsx +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "What is next" +msgstr "" + +#. js-lingui-id: tWd6MT +#: src/app/[locale]/customers/netzero/page.tsx +msgid "What is planned is significant. NetZero has a data lake, online forms, and multiple internal systems that he wants to connect to Twenty. The pipes are there; the next step is automations that tie them together." +msgstr "" + +#. js-lingui-id: aYRqmz +#: src/app/[locale]/why-twenty/editorial-four.data.ts +msgid "What this means" +msgstr "" + +#. js-lingui-id: 1kfjxX +#: src/app/[locale]/customers/act-education/page.tsx +msgid "Whatever came next had to be something they could own." +msgstr "" + +#. js-lingui-id: cs6VpJ +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "When a student registers via QR at a campus event, the system assigns a plan, generates an interview session, and sends a link. The AI conducts the interview, scores the candidate, and classifies the result. Faculty see where each student stands without manually reviewing every session. Building and iterating on these workflows was faster with AI in the loop." +msgstr "" + +#. js-lingui-id: I/oPrY +#: src/app/[locale]/customers/9dots/page.tsx +msgid "When the channel is" +msgstr "" + +#. js-lingui-id: JglRo/ +#: src/app/[locale]/customers/9dots/page.tsx +msgid "When the channel is the business" +msgstr "" + +#. js-lingui-id: ee4xKk +#: src/app/[locale]/customers/act-education/page.tsx +msgid "When the vendor" +msgstr "" + +#. js-lingui-id: DajeYD +#: src/app/[locale]/customers/act-education/page.tsx +msgid "When the vendor pulled the plug" +msgstr "" + +#. js-lingui-id: 6LeUXo +#: src/lib/customers/case-study-catalog.ts +msgid "When your CRM" +msgstr "" + +#. js-lingui-id: X1gdzB +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "When your CRM is" +msgstr "" + +#. js-lingui-id: Svuneu +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "When your CRM is the product: W3Grads on Twenty | W3villa Technologies" +msgstr "" + +#. js-lingui-id: KRKrw8 +#: src/app/[locale]/partners/three-cards-illustration.data.ts +msgid "Which partner program is right for you?" +msgstr "" + +#. js-lingui-id: bGN8u+ +#: src/app/[locale]/customers/netzero/page.tsx +msgid "While NetZero still runs a second CRM in parallel for WhatsApp-heavy operations with farmers in Brazil, they expect to migrate all of it to Twenty as features and the ecosystem grow. Already, their structured, multinational pipeline is powered by Twenty." +msgstr "" + +#. js-lingui-id: DJlSwA +#: src/sections/Menu/data.ts +msgid "Why" +msgstr "" + +#. js-lingui-id: 4sOVu+ +#: src/sections/Footer/data.ts +msgid "Why Twenty" +msgstr "" + +#. js-lingui-id: kwvfYA +#: src/lib/website-routing/static-website-routes.ts +msgid "Why Twenty | Twenty" +msgstr "" + +#. js-lingui-id: aycITw +#: src/app/[locale]/product/page.tsx +#: src/app/[locale]/(home)/page.tsx +msgid "with" +msgstr "" + +#. js-lingui-id: NI3KID +#: src/app/[locale]/product/page.tsx +msgid "with no-code" +msgstr "" + +#. js-lingui-id: OKVlnc +#: src/app/[locale]/customers/netzero/page.tsx +msgid "With Twenty, when a new need appears, he can address it himself: no developer required, no support ticket." +msgstr "" + +#. js-lingui-id: JRUWoH +#: src/app/[locale]/partners/page.tsx +msgid "with Twenty?" +msgstr "" + +#. js-lingui-id: KAB1uP +#: src/app/[locale]/customers/netzero/page.tsx +msgid "with you" +msgstr "" + +#. js-lingui-id: Lr53K3 +#: src/app/[locale]/(home)/home-stepper.data.ts +msgid "without friction" +msgstr "" + +#. js-lingui-id: OX5bbs +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Work email *" +msgstr "" + +#. js-lingui-id: woYYQq +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Workflows" +msgstr "" + +#. js-lingui-id: 1ie9Dm +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Workflows that" +msgstr "" + +#. js-lingui-id: AzqmMJ +#: src/app/[locale]/customers/elevate-consulting/page.tsx +msgid "Workflows that actually get used" +msgstr "" + +#. js-lingui-id: pmUArF +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Workspace" +msgstr "" + +#. js-lingui-id: zkWmBh +#: src/sections/Plans/components/BillingToggle.tsx +msgid "Yearly" +msgstr "" + +#. js-lingui-id: l75CjT +#: src/sections/PlanTable/components/Content.tsx +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +#: src/app/[locale]/pricing/plan-table.data.ts +msgid "Yes" +msgstr "" + +#. js-lingui-id: bo6pUs +#: src/sections/Faq/data.ts +msgid "Yes, with our Apps framework. Scaffold an extension with `npx create-twenty-app` and ship custom objects, server-side logic functions, React components that render inside Twenty’s UI, AI skills and agents, views, and navigation, all in TypeScript, deployable to any workspace." +msgstr "" + +#. js-lingui-id: yIcBW6 +#: src/sections/Faq/data.ts +msgid "Yes. Every Cloud workspace ships with a native MCP server. Connect your AI assistant via OAuth and it can read and write your CRM data in natural language." +msgstr "" + +#. js-lingui-id: 8xYMYQ +#: src/sections/Faq/data.ts +msgid "Yes. Import your data via CSV, or use our API for 50,000+ records. Our partners can handle the full migration for you." +msgstr "" + +#. js-lingui-id: bnuv9n +#: src/sections/Faq/data.ts +msgid "Yes. Twenty is the #1 open source CRM on GitHub. You can self-host to fully own your infrastructure, or run it on our managed cloud for a zero-ops setup." +msgstr "" + +#. js-lingui-id: LJ3sTb +#: src/app/[locale]/why-twenty/editorial-four.data.ts +msgid "You don't buy your deployment pipeline off the shelf. You don't rent your data warehouse from a vendor who decides the schema. You build it, you own it, you iterate on it every week. CRM is going the same way. The teams that treat it as infrastructure they own will compound an advantage every quarter." +msgstr "" + +#. js-lingui-id: SfJVcK +#: src/app/[locale]/enterprise/activate/page.tsx +msgid "Your checkout is complete. Follow the steps below to copy your license key into your Twenty instance." +msgstr "" + +#. js-lingui-id: krHgj1 +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Your enterprise key" +msgstr "" + +#. js-lingui-id: +dxjCu +#: src/app/[locale]/enterprise/activate/EnterpriseActivateClient.tsx +msgid "Your enterprise license has been activated successfully." +msgstr "" + +#. js-lingui-id: pEQiCY +#: src/lib/partner-application/partner-application-modal-data.ts +msgid "Your name *" +msgstr "" + +#. js-lingui-id: 9Apzz7 +#: src/app/[locale]/customers/page.tsx +msgid "your own story?" +msgstr "" + +#. js-lingui-id: wHjFOw +#: src/lib/customers/case-study-catalog.ts +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Zero" +msgstr "" + +#. js-lingui-id: xlFvlS +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Zero manual work" +msgstr "" + +#. js-lingui-id: ZvEoMq +#: src/app/[locale]/customers/w3villa/page.tsx +msgid "Zero manual work at the core. Full automation. Built on Twenty." +msgstr "" diff --git a/packages/twenty-website-new/src/locales/generated/af-ZA.ts b/packages/twenty-website-new/src/locales/generated/af-ZA.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/af-ZA.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/ar-SA.ts b/packages/twenty-website-new/src/locales/generated/ar-SA.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/ar-SA.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/ca-ES.ts b/packages/twenty-website-new/src/locales/generated/ca-ES.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/ca-ES.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/cs-CZ.ts b/packages/twenty-website-new/src/locales/generated/cs-CZ.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/cs-CZ.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/da-DK.ts b/packages/twenty-website-new/src/locales/generated/da-DK.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/da-DK.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/de-DE.ts b/packages/twenty-website-new/src/locales/generated/de-DE.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/de-DE.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/el-GR.ts b/packages/twenty-website-new/src/locales/generated/el-GR.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/el-GR.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/en.ts b/packages/twenty-website-new/src/locales/generated/en.ts index 76934b72de0..22f5cda7d45 100644 --- a/packages/twenty-website-new/src/locales/generated/en.ts +++ b/packages/twenty-website-new/src/locales/generated/en.ts @@ -1 +1 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file +/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"BUIzFF\":[\"-33% off\"],\"q0ur5+\":[[\"0\"],\" | Twenty Customer Story\"],\"icND7o\":[\"/ seat / month - billed yearly\"],\"XUqxHW\":[\"/user/month\"],\"ojimXJ\":[\"© 2026 – Twenty\"],\"foGG8A\":[\"+$105/user per month\"],\"Bsq9GJ\":[\"+$35/user per month\"],\"mWSpW5\":[\"+$5/user per month\"],\"JIwDAd\":[\"+$7000/org per month\"],\"yVr18d\":[\"+$75/user per month\\nSwitch to enterprise!\"],\"iK+ikF\":[\"+10k others\"],\"nRtVCK\":[\"+20% of net spend\\n+$75/user per month\\nSwitch to enterprise!\"],\"be30i9\":[\"$0\"],\"6inRXo\":[\"$1/orchestration run/org\\n+$75/user per month\\nSwitch to enterprise!\"],\"W8uNyP\":[\"$12\"],\"EkIZkY\":[\"$19\"],\"xrRqkE\":[\"$25\"],\"8r6DgO\":[\"$9\"],\"EHNAf2\":[\"1 click\"],\"qvNspm\":[\"1‑800‑YES‑SOFTWARE\"],\"6WQdu8\":[\"100 per minute\"],\"uuKUqZ\":[\"11 permissions\\ngroups\"],\"SaSafJ\":[\"150 hours\"],\"9ntyY8\":[\"150 hrs\"],\"2z1Znr\":[\"2 years contract\"],\"l6Vx4q\":[\"2,000+\"],\"oadHWW\":[\"200 per minute\"],\"FKcV/y\":[\"2025\"],\"PJgfBS\":[\"3\"],\"BL47wE\":[\"3 2 years contract\\n-33% off\"],\"/jgC+9\":[\"3 product lines\"],\"7SJhyh\":[\"30+\"],\"Pdl2UE\":[\"4 tools\"],\"tZvajW\":[\"6 workflows\"],\"AQPMc4\":[\"90%\"],\"fHxYPM\":[\"90%+\"],\"1GAcp6\":[\"A business that does not fit a\"],\"NLSM5L\":[\"A business that does not fit a template\"],\"9Z2FZQ\":[\"A CRM for teams\"],\"GSM5RA\":[\"A CRM that\"],\"och6cp\":[\"A CRM that grows with you | NetZero & Twenty\"],\"EewhZt\":[\"A CRM they\"],\"sgzhYK\":[\"a CRM they'll love\"],\"xiVefD\":[\"A custom CRM gives your org an edge,\"],\"Qay3/Z\":[\"A modern CRM with\"],\"GoyOzl\":[\"A platform ready to\"],\"rxMguZ\":[\"A platform ready to grow\"],\"R5hTKX\":[\"A real estate agency on WhatsApp\"],\"WjUlJr\":[\"A retro theme as a paid add-on is somehow the most believable part.\"],\"r9Msiv\":[\"A year ago, customizing your CRM meant hiring a Salesforce consultant, learning Apex, waiting months. The gap between \\\"I want this\\\" and \\\"it's live\\\" was measured in quarters and invoices. So people settled. They bent their process to fit the tool and called it adoption.\"],\"c0RR2h\":[\"About 150 hours per month saved in manual operations. Real-time metrics for the business owner. Growth readiness without adding operational headcount. A team that can answer questions that used to take days to piece together.\"],\"41o3VS\":[\"AC&T and Flycoder moved from a dead vendor export to self-hosted Twenty, with over 90% lower CRM cost and full control.\"],\"TuJiQF\":[\"AC&T Education Migration\"],\"yHUdh6\":[\"AC&T Education Migration (actimmi.com) is an education agency in Australia. They help international students with applications to education providers and visas. They had been on a previous CRM until the vendor shut the system down, leaving nothing but a CSV export.\"],\"MPzy5R\":[\"AC&T moved to a self-hosted Twenty instance with no vendor risk, no forced migration, and CRM costs reduced by more than 90%.\"],\"J+A83M\":[\"AC&T replaced a shuttered vendor CRM with self-hosted Twenty and cut CRM costs by more than 90%.\"],\"muhZ+X\":[\"Activating your enterprise license…\"],\"y+9jZD\":[\"activation\"],\"TDMykO\":[\"actually get used\"],\"+/fX0G\":[\"actually own\"],\"woOp+c\":[\"Add objects and fields\"],\"Y2K3rp\":[\"Add-ons\"],\"Op7nVF\":[\"Adoption\"],\"TeMRC2\":[\"advanced\"],\"cBdysG\":[\"Advanced Encryption\"],\"/C6Pwb\":[\"Aggregate, bar, line, pie, and gauge widgets\"],\"rp2omj\":[\"Agribusiness\"],\"jiGK6+\":[\"AI (Einstein)\"],\"aUn7Ps\":[\"AI & Automations\"],\"HbXHSp\":[\"AI agents\"],\"WGXwzu\":[\"AI agents are starting to draft outreach, score leads, research accounts, write follow-ups, update deal stages. Every one of these actions reads from and writes to the CRM. The scoreboard became the playbook. The database became the brain.\"],\"Rsxdfy\":[\"AI Agents with custom skills\"],\"TfV/y9\":[\"AI chat, settings, and records in a side panels for fast, single-screen access.\"],\"R+bwFB\":[\"AI for rapid iterations\"],\"+pgY/C\":[\"AI in the\"],\"A6v5aI\":[\"AI in the migration workflow\"],\"9VlH2i\":[\"AI made the gap that small.\"],\"Tp1P7F\":[\"AI turned it into an operating system.\"],\"t97j+F\":[\"AI-assisted\"],\"LIGwFL\":[\"Alternative Partners\"],\"GDVvyT\":[\"Alternative Partners is a consulting firm that moved from Salesforce to a self-hosted Twenty instance. Benjamin Reynolds led the migration. He had already become a Twenty expert implementing Twenty for one of Twenty's first cloud customers.\"],\"CCrRuT\":[\"Alternative Partners replaced Salesforce with self-hosted Twenty, using agentic AI to compress migration work.\"],\"UIu18Y\":[\"Alternative Partners used agentic AI to compress what would typically be weeks of Salesforce migration work into something a single person could oversee.\"],\"iFLhqi\":[\"Amrendra Pratap Singh\"],\"JDRPTH\":[\"an intuitive interface\"],\"lTSix7\":[\"and unlock new opportunities with Twenty\"],\"aGCcXe\":[\"Any Questions?\"],\"/AMVjF\":[\"APEX tutorials\"],\"OZtEcz\":[\"API\"],\"yKBF3l\":[\"API access\"],\"GpYSXo\":[\"API backbone\"],\"30KnZB\":[\"API calls\"],\"42FQ3y\":[\"API-first\"],\"azQMHP\":[\"APIs are extra. Simplicity has a price.\"],\"MqJvvH\":[\"APIs, SDKs and webhooks to extend Twenty and ship apps on top of your CRM data.\"],\"I1Z4AM\":[\"Apply to build\"],\"uicv6Z\":[\"Architecture\"],\"Sfa0xD\":[\"around it\"],\"yYKNWp\":[\"Assemble, iterate and adapt a robust CRM,\"],\"v7zTZl\":[\"Assign owners and due dates\"],\"9c5acG\":[\"at\xA0AI\xA0Speed\"],\"4/b98B\":[\"at the core\"],\"y2W2Hg\":[\"Audit logs\"],\"FFv0Vh\":[\"Automation\"],\"SJ7WSP\":[\"Available on YouTube!\"],\"iH8pgl\":[\"Back\"],\"jQLfIW\":[\"Because apparently privacy feels more premium with a surcharge.\"],\"qEQv1F\":[\"Because everything is built on Twenty's open foundation, Flycoder could wire the exact logic AC&T needed without fighting the platform.\"],\"uIG0OH\":[\"Because the foundation is solid, W3Grads is architected for what comes next, including a payment layer for future paid interview plans and nationwide scale without structural rewrites.\"],\"JGM+JO\":[\"Because true orchestration means putting a dollar sign on every dramatic entrance.\"],\"SoJDT2\":[\"Become\"],\"sx6hMS\":[\"Become a Content Partner\"],\"PKMHNe\":[\"become a genius!\"],\"6ZCRfT\":[\"Become a partner\"],\"lvXL1I\":[\"Become a Solution Partner\"],\"EwaFPn\":[\"Become a Technology Partner\"],\"eqswCn\":[\"Begin with production-grade\"],\"SYfuGP\":[\"Benjamin Reynolds\"],\"YfjrMq\":[\"Bertrams\"],\"mzTjnf\":[\"Better than Liquid Glass!\"],\"GvGk4g\":[\"Beyond\"],\"bxWnM1\":[\"breaking operations\"],\"ONUC8n\":[\"build\"],\"WxyAko\":[\"Build a CRM your competitors\"],\"pF0PoL\":[\"Build custom dashboards\"],\"wWXG44\":[\"Build integrations that connect Twenty with the tools your customers already use. Help us expand the Twenty ecosystem.\"],\"ZhC3tY\":[\"Build it in an afternoon.\"],\"e1u+Zh\":[\"Build on an open platform\"],\"oIpt0K\":[\"Build your Enterprise CRM\"],\"J7i6zm\":[\"building blocks\"],\"/ETeLn\":[\"built a\"],\"8hMULp\":[\"built a CRM around it\"],\"SoAKmY\":[\"Built for speed\"],\"hnSOXa\":[\"built with Twenty\"],\"j8RGrG\":[\"Burned by vendor lock-in, AC&T built a CRM they actually own | Twenty\"],\"EjSajd\":[\"but building one\"],\"wCALve\":[\"Can developers extend Twenty with code?\"],\"6mytgb\":[\"Can I migrate from Salesforce or HubSpot?\"],\"+oHJTp\":[\"can't buy.\"],\"u+wGUf\":[\"change with Twenty\"],\"2D9CbR\":[\"Check more add-ons\"],\"ZbCD7v\":[\"Classic never dies. It just gets extended one more time.\"],\"L/LPQZ\":[\"Cloud Pro is $9/user/month (yearly). Organization is $19/user/month and unlocks SSO and row-level permissions for teams that need finer access control.\"],\"1YncLF\":[\"Co-founder at NetZero\"],\"vWaYWP\":[\"Co-founder, NetZero\"],\"R8BDsa\":[\"Co-marketing opportunities\"],\"b5bnuN\":[\"Column-to-field mapping (including relations)\"],\"NHYWK4\":[\"comes with\"],\"ofoEsM\":[\"Coming soon!\"],\"KGD9Kt\":[\"Commercial license (no AGPL obligations)\"],\"chL5IG\":[\"Community\"],\"8CLnho\":[\"Community support\"],\"SHjhDC\":[\"Company or brand *\"],\"O403zL\":[\"company-wide\"],\"P/f7ez\":[\"Complete activation for your Twenty self-hosted enterprise license.\"],\"Fkb+LW\":[\"Compose your CRM and internal apps with a single extensibility toolkit.\"],\"kD7ZGH\":[\"Compose your CRM and internal apps with a single extensibility toolkit. Data model, layout, and automation.\"],\"iSLIjg\":[\"Connect\"],\"90KBr0\":[\"Connect Google or Microsoft accounts\"],\"MVrQxI\":[\"Connected via API\"],\"cfAG43\":[\"Consulting\"],\"Oii3vg\":[\"Contacts & Companies\"],\"fi9xsM\":[\"Content & Community Partner\"],\"vymMOT\":[\"Content & Community Partners\"],\"mutfS8\":[\"Continue iteration\"],\"+Uepfb\":[\"Control\"],\"VOpE1H\":[\"control hub\"],\"sTIhSj\":[\"Control without\"],\"wH1xmY\":[\"Control without drag\"],\"Bj7igG\":[\"Control without the overhead\"],\"PiH3UR\":[\"Copied!\"],\"he3ygx\":[\"Copy\"],\"7qTWwB\":[\"Copy the enterprise key above.\"],\"PXnj76\":[\"Copy this key and paste it into your Twenty self-hosted instance settings.\"],\"FapWIq\":[\"Core Features\"],\"JRK+HF\":[\"Costs down more than\"],\"Xn2Nlq\":[\"Create a workflow\"],\"B9nLrl\":[\"Create apps on Twenty\"],\"yAL7FY\":[\"Create custom apps\"],\"oS1RiS\":[\"Create tasks from records\"],\"MSlbwg\":[\"CRM\"],\"Y44Xsf\":[\"CRM costs dropped by more than 90%. Manual overhead tied to the old system is gone. For the first time, AC&T has a CRM they will not lose again.\"],\"wDasLa\":[\"CRM Engineer\"],\"DqkzNV\":[\"CRM Engineer, AC&T Education Migration\"],\"b7vNti\":[\"CRM was a database you filled on Fridays. AI turned it into the system that runs your go-to-market. To differentiate, you have to build what your competitors can't buy.\"],\"v5sHk3\":[\"CRM was a ledger.\"],\"I6FFSA\":[\"CSV export anytime\"],\"8RWevB\":[\"CSV import & export\"],\"s+QiQi\":[\"CSV import flow\"],\"lh6aPn\":[\"Custom AI models\"],\"OaTurC\":[\"Custom apps\"],\"yBSEtR\":[\"Custom deal stages for your process\"],\"Vz1Vq2\":[\"Custom domain (crm.yourco.com)\"],\"oPwQt4\":[\"Custom fields\"],\"U1RB/7\":[\"Custom fields and relationships\"],\"prIqWa\":[\"Custom layout\"],\"8skTDV\":[\"Custom objects\"],\"AbyZbl\":[\"Custom views\"],\"YjXLAQ\":[\"Customer Stories\"],\"NihQNk\":[\"Customers\"],\"3mRQi4\":[\"Customers | Twenty\"],\"qqLY+j\":[\"Customization\"],\"GRfLAg\":[\"Customizations\"],\"4RLD4p\":[\"Daily messages\"],\"V0kvgB\":[\"Data import\"],\"5cNMFz\":[\"Data model\"],\"FBZA6a\":[\"Dedicated partner support\"],\"KR5UiF\":[\"Dev teams power\"],\"n+SX4g\":[\"Developers\"],\"vcsX5L\":[\"Differentiation now\"],\"Wuqvfz\":[\"Director of Digital and Information, Elevate Consulting\"],\"+SvmQA\":[\"Discover the newest features and improvements in Twenty,\\nthe #1 open source CRM.\"],\"lsSE4P\":[\"Discover the newest features and improvements in Twenty, the #1 open source CRM.\"],\"W+b/DF\":[\"Discover what's new\"],\"Zjjbne\":[\"Do I need a developer to customize Twenty?\"],\"Zx22Ih\":[\"Does Twenty work with Claude, ChatGPT, and Cursor?\"],\"pOLPPB\":[\"Don't get locked into someone else's ecosystem. Twenty's developer experience looks like normal software, with local setup, real data, live testing, and no proprietary tooling.\"],\"fPma12\":[\"Drag-and-drop deals between stages\"],\"4CJ4xV\":[\"EdTech\"],\"aqxYLv\":[\"Education\"],\"1wTjWx\":[\"Elevate Consulting is a management consultancy based in Canada. When Justin Beadle, Director of Digital and Information, joined, the company ran entirely on Word documents, Excel spreadsheets, sticky notes, emails, and reliance on people. There was no CRM, no API-accessible tools, only a patchwork trying to stand in for a single source of truth.\"],\"2THY70\":[\"Elevate Consulting uses Twenty as the API backbone connecting billing, Teams, resourcing, and a custom front end around client and opportunity data.\"],\"wiTeYI\":[\"Elevate's CEO was so impressed with Twenty he started recommending it to clients before the internal setup was even complete. The team is exploring bringing Twenty to client projects as part of their consulting practice, including as the backend for custom-built products tailored to specific operational needs.\"],\"FNyrb0\":[\"Email & Calendar\"],\"AoN898\":[\"Email and Chat\"],\"ObCvfI\":[\"Email sharing\"],\"hfqiVr\":[\"Email/calendar activity on each record\"],\"XGd8Wo\":[\"Emails & Calendar\"],\"o4/9l1\":[\"Emails and events linked to CRM records\"],\"Qpn/bX\":[\"Encrypt your data\"],\"Ijh+h+\":[\"Enjoy unlimited customization using the AI coding tools you already love. Adapt your CRM to fit the way your business grows and wins.\"],\"xoqD/n\":[\"Enter a valid email address.\"],\"GpB8YV\":[\"Enterprise\"],\"5l0LGU\":[\"Enterprise activation | Twenty\"],\"3dQ6zJ\":[\"Environments\"],\"D1bFNS\":[\"Estimated monthly opportunities (optional)\"],\"IhKZhp\":[\"Even the training material is a feature worth celebrating.\"],\"kz/8m8\":[\"Everything in Pro\"],\"zoNk7e\":[\"Everything updates in real time, with AI chat always ready to help you move faster.\"],\"bEEOy2\":[\"Everything you need,\"],\"CIcRGB\":[\"Exclusive content collaboration opportunities\"],\"57gG1f\":[\"Experience enterprise-grade granularity, starting with an 11th permission.\"],\"JTM5zS\":[\"Explore customer stories\"],\"weFouS\":[\"Extended run!\"],\"akLvep\":[\"Familiar, modern interface\"],\"abO45l\":[\"Fast path to action\"],\"e6BgMV\":[\"Field-level permissions\"],\"sER+bs\":[\"Files\"],\"UWTNog\":[\"Filtered metrics from live CRM data\"],\"TJ7HQl\":[\"Find a partner\"],\"MD032e\":[\"Find a Twenty partner\"],\"SZabbQ\":[\"Find the program that fits your business\"],\"wHyJkT\":[\"Find the right partner to implement, customize, and tailor Twenty to your team.\"],\"PHM5wp\":[\"Flexibility\"],\"HBJ0P5\":[\"Flow\\norchestration\"],\"obrJBN\":[\"Fly through your workspace with shortcuts and short load times.\"],\"HjFq2b\":[\"Flycoder, a full-stack development partner, helped them set up Twenty as a self-hosted instance shaped around how AC&T actually operates. The data model centers on students, not a generic contact-and-deal pipeline. Statuses update automatically: a workflow runs nightly to keep enrollment records current. Automated email reminders cover important dates. Adding a new record takes under a minute.\"],\"1SL9ZF\":[\"Focus on the use case, not the\"],\"P5E+kT\":[\"Focus on the use case, not the plumbing\"],\"wdVyxi\":[\"Folder/Label import\"],\"CKQ0za\":[\"For a firm that once ran on sticky notes, this is more than an upgrade. It is a complete transformation.\"],\"yAJC9c\":[\"For twenty years, CRM meant the same thing: a place to log calls, track deals, and pull reports on Friday. The real work happened in people's heads, in Slack threads, in hallway conversations. The CRM kept score. Nobody expected more from it.\"],\"wSoqhC\":[\"foundation\"],\"UCoxP5\":[\"Founder, Nine Dots Ventures\"],\"IPaRlc\":[\"Founder, Wintactix\"],\"tUgSXd\":[\"Free for you!\"],\"yDeZSV\":[\"From documents to\"],\"emeKZ7\":[\"From documents to open APIs\"],\"CMqgta\":[\"From Salesforce to\"],\"syU/BD\":[\"From Salesforce to self-hosted Twenty, powered by AI | Alternative Partners\"],\"TH5XjF\":[\"From simple to\"],\"Lf7cb3\":[\"From simple to advanced\"],\"m1I5TY\":[\"Full communication history in one place\"],\"wXgKOm\":[\"Full customization\"],\"YZ7Q3Z\":[\"Full ownership\"],\"hdxwWi\":[\"Fully customizable\"],\"ZDIydz\":[\"Get started\"],\"3mUKNr\":[\"Go the extra mile\"],\"Ay/vbT\":[\"Good choice!\"],\"IsZ6P7\":[\"grow\"],\"MCLWq4\":[\"Grow with a flexible foundation\"],\"XpCing\":[\"grows\"],\"ORQRms\":[\"grows with you\"],\"Xf5EJg\":[\"Halftone generator\"],\"DHC85F\":[\"Halftone Generator | Twenty\"],\"c3XJ18\":[\"Help\"],\"CRzGla\":[\"Help center\"],\"R5aged\":[\"Help customers implement, customize, and succeed with Twenty. Combine sales and services to grow your business.\"],\"qfSbUb\":[\"His approach was unconventional. Instead of mapping fields manually, scripting transforms, and validating data step by step, he handed the job to agentic AI tools with a brief: where the data lives, the GitHub repo for the target platform, and the Railway deployment. Start, and only return if something breaks beyond a 70% confidence fix.\"],\"i0qMbr\":[\"Home\"],\"CtU7yU\":[\"Homeseller\"],\"KYnPnU\":[\"Homeseller is a high-volume real estate agency in Singapore, founded by one of the country's top-performing property agents. The whole operation runs on WhatsApp: no email, no calendars, just group chats, thousands of them, with clients, agents, and leads together.\"],\"v6cmMq\":[\"Homeseller kept their habits. WhatsApp stayed WhatsApp. What changed is that everything flowing through those conversations now lands in a structured system, tracked, classified, and visible in real time.\"],\"xaIV9m\":[\"Homeseller, WhatsApp, and a CRM built around the business | Nine Dots & Twenty\"],\"XnFo3G\":[\"How AC&T Education Migration and Flycoder replaced a shuttered vendor CRM with self-hosted Twenty, with 90%+ lower cost and full ownership.\"],\"PYi0f6\":[\"How Alternative Partners migrated from Salesforce to self-hosted Twenty using agentic AI in the implementation loop: fast migration, durable ownership.\"],\"o+Kp2M\":[\"How do you want to partner with Twenty? *\"],\"+MLOVo\":[\"How Elevate Consulting moved off documents and spreadsheets to Twenty as the API-connected CRM at the center of their stack.\"],\"OIwpKt\":[\"How long does it take to get started?\"],\"iRzy9e\":[\"How NetZero uses Twenty across carbon credits, agricultural products, and franchised industrial systems with a modular CRM and a roadmap toward AI-assisted workflows.\"],\"NZYA4S\":[\"How Nine Dots Ventures rebuilt a Singapore real estate agency on Twenty with APIs, n8n, Grafana, and AI on top of 2,000+ WhatsApp messages a day.\"],\"kqEcVc\":[\"How teams\"],\"bcln75\":[\"How Twenty collects, uses, safeguards, and discloses information when you use Twenty.com and related services.\"],\"URQPvK\":[\"How W3villa Technologies shipped W3Grads, an AI mock interview platform for institutions, on Twenty as the operational backbone.\"],\"l0JGUk\":[\"Impersonate users\"],\"N4OVNn\":[\"Implementation\"],\"yHueLx\":[\"Implementation partners\"],\"InyU0Z\":[\"In June 2025, Justin learned Twenty v1 had shipped. Within two or three days, the CEO asked him to look into setting up a CRM. The shift came from the potential of what could be built on top of fully open APIs. The timing was perfect.\"],\"d1Aru8\":[\"In production.\"],\"Sqb+jp\":[\"In-app preview for supported file types (when enabled)\"],\"3XP8Lk\":[\"Included!\"],\"S8gy7K\":[\"Industry\"],\"MeL8SS\":[\"Infinite scroll\"],\"+mOisw\":[\"Infinite scroll is still coming soon, unlike the invoice.\"],\"K8iwJx\":[\"Install shared tarball app\"],\"AHWM9N\":[\"Integration\"],\"OChKCc\":[\"Interactive halftone generator exported from Twenty.\"],\"mdsMtj\":[\"internal rollout\"],\"a+PGuG\":[\"Internet accounts per user\"],\"DQenUJ\":[\"is the product\"],\"/yQIKP\":[\"Is Twenty really open-source?\"],\"QjEULz\":[\"It is just such a nicer experience than dealing with a Salesforce or a HubSpot. My mission has been to get every tool API-accessible, so everything talks to each other.\"],\"FJ51pP\":[\"It is just such a nicer experience than dealing with a Salesforce or a HubSpot. My mission has been to get every tool API-accessible, so everything talks to each other. Twenty made that possible in a way older CRM platforms simply do not.\"],\"kqIAbN\":[\"It worked. This is AI-assisted iteration in practice: not AI as a product feature, but as part of implementation work, compressing what would typically be weeks into something one person can oversee without being the bottleneck.\"],\"ltNDOB\":[\"It's fragile. V1 ships quickly, but maintaining and making changes is a long term burden.\"],\"kX2mij\":[\"Join our ecosystem and help businesses take control of their customer data with\"],\"1qiriB\":[\"Join our growing partner ecosystem\"],\"ZFnW+/\":[\"Join our partner ecosystem and grow with us as we build the #1 open source CRM.\"],\"4721j2\":[\"Join our partner ecosystem and help businesses\\ntake control of their CRM.\"],\"4QqdtB\":[\"Join the teams that chose to own their CRM.\\nStart building with Twenty today.\"],\"m/pXki\":[\"Join the teams that chose to own their CRM. Start building with Twenty today.\"],\"pB76mP\":[\"Jul 2025\"],\"hvwlu3\":[\"Jun 2025\"],\"qrYDGE\":[\"Justin Beadle\"],\"vYXuJI\":[\"Justin built workflows for notifications across the team, alerting the right people in Teams when a prospect becomes a lead or when project milestones are reached. Forms in Twenty let the business development team log activity without leaving the tool. The impact is real for the organization. The tool has been adaptable from opportunity-level work at a client to executive-level decisions.\"],\"E6Oohx\":[\"Justin's broader mission at Elevate has been to move the company off static documents and onto tools with API access. By the end of 2025, that was in place: time billing, resource planning, Microsoft Teams, and project management were all accessible via API, with Twenty at the center holding client and opportunity data. Team members could use that information strategically instead of re-keying it.\"],\"GAmD3h\":[\"Languages\"],\"wL3cK8\":[\"Latest\"],\"rdU729\":[\"Layout\"],\"+Ss/og\":[\"Learn how to use Twenty\"],\"vifyyw\":[\"Legal\"],\"tHFTlp\":[\"Licensee:\"],\"gggTBm\":[\"LinkedIn\"],\"Jvz4g8\":[\"Listing on Twenty integrations page\"],\"AHVzME\":[\"Live data and AI built\"],\"GObQuL\":[\"Live updates\"],\"zmRZwk\":[\"Live updates are unavailable, which is almost more honest.\"],\"M7lzaL\":[\"lives in the code you own.\"],\"ObPscj\":[\"Loading activation…\"],\"7VBQ2j\":[\"Local, Production\"],\"JO8Bdx\":[\"Lock-in\"],\"sQia9P\":[\"Log in\"],\"KPLAuI\":[\"Lower CRM cost\"],\"i3PI5l\":[\"Make your GTM team happy\"],\"Si4WyF\":[\"Management Consulting\"],\"poX06f\":[\"Manual work at core\"],\"VJOKPB\":[\"Maps view\"],\"u0wogL\":[\"Marketing assets & brand resources\"],\"LiMSZr\":[\"Marketplace listing\"],\"bJNKJ1\":[\"Master every corner of Twenty\"],\"Zw+Zv+\":[\"MCP server\"],\"zxcfgg\":[\"means control\"],\"bwCiCi\":[\"Meet the certified agencies and consultants implementing Twenty for teams worldwide.\"],\"EhO+8s\":[\"Meet the teams running their business on Twenty. Real customer stories on how they shaped the CRM to fit their workflow.\"],\"ujYZ+f\":[\"Meet the teams who shaped Twenty into their own CRM with self-hosted deployments, AI-assisted workflows, and API-first product stacks.\"],\"ZdvtU2\":[\"migration workflow\"],\"so04x6\":[\"Mike and Azmat from Nine Dots stepped in to fix that, not by changing how Homeseller works, but by building a system that finally fit around it.\"],\"izddzp\":[\"Mike Babiy\"],\"+8Nek/\":[\"Monthly\"],\"VqXuUv\":[\"More options available!\"],\"XPXSwi\":[\"Most packaged software makes companies more similar. Learn why the future of CRM is built, not bought.\"],\"ZPjy5q\":[\"moves fast\"],\"dlKkNL\":[\"Multi-file upload on records\"],\"6YtxFj\":[\"Name\"],\"LfNsfa\":[\"Need a quick change? Skip the engineering ticket. Customize your workspace in minutes.\"],\"4sbgPq\":[\"Need help with customization?\"],\"8Cp4Of\":[\"NetZero\"],\"J8ZssL\":[\"NetZero runs a modular Twenty setup across carbon credits, ag products, and industrial systems.\"],\"7HVBV+\":[\"NetZero uses Twenty as a modular CRM across product lines and countries, with a roadmap into AI-assisted workflows.\"],\"3PtYTO\":[\"NetZero works with the agro-industry, serving clients from multinationals to smallholder farmers. They sell carbon credits, agricultural products, and franchised industrial systems across three different product lines, multiple countries, and multiple company sizes. When Olivier Reinaud, co-founder of NetZero, started looking at CRMs in late 2024, he was not chasing the most feature-rich platform. He wanted the right foundation.\"],\"KqojOL\":[\"Next steps\"],\"qZ+egR\":[\"Nine Dots put Twenty at the center of Homeseller's stack with APIs, automation, and AI on top of WhatsApp-heavy operations.\"],\"sGuHDu\":[\"Nine Dots rebuilt Homeseller's operations on Twenty, with a custom data model shaped around their sales flow. Because Twenty is open and everything is accessible via API, they connected it to what the business actually needed: n8n for automated workflows (in-app workflows were not available at that time), Grafana for live dashboards fed from Twenty, and a custom AI layer to parse and extract structured insights from more than 2,000 WhatsApp messages a day.\"],\"1UzENP\":[\"No\"],\"o+/Ad5\":[\"No more renting someone else's\"],\"LmWdm6\":[\"No more renting someone else's structure\"],\"K1QTmg\":[\"No open-source distribution requirement\"],\"q9f4Tp\":[\"No-code\"],\"SiZ/7E\":[\"No. Build custom objects, fields, views, and no-code workflows straight from Settings. Unlimited, no extra charge.\"],\"7ngHsA\":[\"not bought.\"],\"k3T2mM\":[\"Now a developer can describe what they want to Claude Code and have a working app in an afternoon. A custom object, a scoring workflow, a new view, an integration. The bottleneck isn't building anymore. It's whether your platform lets you.\"],\"CzeIij\":[\"Number of dashboards\"],\"0dVyEt\":[\"of a go-to-market stack\"],\"pkrZnO\":[\"Olivier recognizes that NetZero's current use of Twenty is still relatively simple: workflows and integrations are not yet as deep as he eventually wants, because he prioritized getting foundations right first.\"],\"l/1LQc\":[\"Olivier Reinaud\"],\"D2b+1q\":[\"On a single CRM\"],\"XhXmzW\":[\"On this page\"],\"jeGPx0\":[\"on Twenty\"],\"pbmZ7R\":[\"Onboarding Packs\"],\"/DFChQ\":[\"One\"],\"Zt1UZb\":[\"One API to rule them all\"],\"pShqss\":[\"Only $5 for SSO. Practically a charity program.\"],\"3WErRa\":[\"open APIs\"],\"pkXQA+\":[\"Open your Twenty self-hosted instance Settings → Enterprise.\"],\"wtKPUT\":[\"open-source primitives.\"],\"8THUfN\":[\"open-source software\"],\"5RkmVr\":[\"Open-source, AI-ready, and yours to shape.\"],\"oYQN5j\":[\"or not !\"],\"ucgZ0o\":[\"Organization\"],\"M12WZl\":[\"our partner\"],\"dKGg8T\":[\"Out of stock\"],\"C8Ex6S\":[\"out of the box\"],\"PASC/7\":[\"Outlived every redesign since 2004.\"],\"Bwsi7B\":[\"Own your CRM end to end\"],\"oDAEQq\":[\"Ownership\"],\"NmF/Vo\":[\"Owning your stack remains mysteriously out of stock.\"],\"+AiiMt\":[\"Pagination builds character.\"],\"mPkInZ\":[\"Partners\"],\"itrQKv\":[\"Partners | Twenty\"],\"9yo8NN\":[\"Paste the key and click Activate.\"],\"nxV7o2\":[\"paying off\"],\"FFkjaT\":[\"Pipeline Management\"],\"GwGdy2\":[\"Plans that scale with your team. Compare tiers of the #1 open source CRM.\"],\"0gBYFU\":[\"Please complete all required fields before submitting.\"],\"iCh6/3\":[\"plumbing\"],\"a7u1N9\":[\"Price\"],\"aHCEmh\":[\"Pricing\"],\"CboOX5\":[\"Pricing | Twenty\"],\"J2IAmT\":[\"Principal and Founder\"],\"kXBQYM\":[\"Principal and Founder, Alternative Partners\"],\"k62X/2\":[\"Priority support\"],\"LcET2C\":[\"Privacy Policy\"],\"DJ3uVe\":[\"Privacy Policy | Twenty\"],\"3fPjUY\":[\"Pro\"],\"4gXof3\":[\"Product | Twenty\"],\"kIGKva\":[\"Production grade quality\"],\"CcK9cq\":[\"Programs that previously needed heavy manual coordination now run end-to-end with automation. Institutions get a scalable, intelligent system; students get faster preparation for interviews that matter; W3villa shipped a product institutions can build revenue around.\"],\"9NAqO4\":[\"Proposal automation\"],\"a24q7E\":[\"Proprietary languages, slow deployment cycles, and \\\"black box\\\" logic.\"],\"kS6G/9\":[\"pulled the plug\"],\"frfCYp\":[\"Q1 2026\"],\"gSQVmr\":[\"Read the case\"],\"EqCbT9\":[\"Read/Edit/Delete permissions\"],\"SNf+4G\":[\"Ready to build\"],\"9BL+5g\":[\"Ready to grow\"],\"1d+b/h\":[\"Ready to grow\\nwith Twenty?\"],\"NIwjHh\":[\"Real Estate\"],\"CH+Ona\":[\"Real stories from real teams about how they shaped Twenty to fit their workflow and accelerated their growth.\"],\"5O1eTm\":[\"Real-time changes? That will be a premium surprise.\"],\"4hc7hc\":[\"Real-time data\"],\"dKQCrj\":[\"Real-time is a state of mind, not a feature.\"],\"Zhiuz9\":[\"Record quarter\"],\"PWlTvh\":[\"Records\"],\"rwWjWg\":[\"Release Notes\"],\"5icoS1\":[\"Releases\"],\"Bh0u1L\":[\"Releases | Twenty\"],\"a/aGHe\":[\"Rename, download, and delete attachments\"],\"t9yxlZ\":[\"Reports\"],\"SY/an2\":[\"Reports & Dashboards\"],\"5Cs/CL\":[\"Resale discounts & revenue share\"],\"s+MGs7\":[\"Resources\"],\"oC2l7f\":[\"REST & GraphQL API\"],\"kx0s+n\":[\"Results\"],\"2itg0p\":[\"Retro 2015\"],\"OH7xLu\":[\"Revenue share for referred customers\"],\"frqduN\":[\"Rich notes attached to records\"],\"/gaSVU\":[\"Roadmap\"],\"C77N5M\":[\"Roles & Permissions\"],\"Wdh41P\":[\"Row-level permissions\"],\"mRpuWW\":[\"Running mock interview programs for hundreds of students sounds straightforward. In practice, universities and training institutes hit the same wall: registrations entered by hand, interview links sent one by one, faculty reviewing every session without scoring or classification. At real scale, it breaks.\"],\"Burn4/\":[\"Salesfarce Add-on Center\"],\"xTfEgV\":[\"Salesfarce Pro\"],\"/UolBB\":[\"Salesforce Classic\"],\"l69E+u\":[\"Salesforce migration\"],\"CADlRK\":[\"Same CRM\"],\"60i6S+\":[\"Same output\"],\"xl2tJ9\":[\"Same results\"],\"w9QWxz\":[\"SAML/OIDC SSO\"],\"elz5ka\":[\"Saved / month\"],\"tigXYO\":[\"saved every month\"],\"bifv6N\":[\"Scale\"],\"rbgetd\":[\"Scale without\"],\"842ybw\":[\"Scale without breaking operations\"],\"HVOGoW\":[\"Seats limit\"],\"a3LDKx\":[\"Security\"],\"q20Cda\":[\"See how teams\"],\"aB+XpI\":[\"See more features\"],\"FY4ykg\":[\"See the latest release\"],\"JDp1bg\":[\"See updates as they happen. Work with your team and agents seamlessly.\"],\"0kgOPB\":[\"See what shipped in \",[\"0\"]],\"wgNoIs\":[\"Select all\"],\"KP119R\":[\"Select your team\"],\"6JhL+3\":[\"Self-hosted\"],\"5CWy3T\":[\"Self-hosted means AC&T carries no vendor risk: no pricing model that can change, no platform that can disappear, no forced migration. The system is theirs.\"],\"5WmQ5O\":[\"Self-hosted means control\"],\"6QPYP7\":[\"self-hosted Twenty\"],\"GrBvv/\":[\"Self-hosting\"],\"YdhUoe\":[\"Self-hosting, now for rent!\"],\"NaHz2o\":[\"Selfhosting\"],\"2ZItPC\":[\"Share Twenty with your audience and help shape the future of the #1 open source CRM. We're looking for creators, educators, and community builders who want to showcase great software.\"],\"3VFzsh\":[\"Ship a product on Twenty\"],\"6lGV3K\":[\"Show less\"],\"mA6cL1\":[\"Sign up for Cloud in under a minute and start your 30-day trial. For larger rollouts, our 4-hour Onboarding Packs or certified partners get you live in 1–2 weeks.\"],\"AQK14J\":[\"Simple\"],\"V9dFwD\":[\"Sitemap\"],\"t6ComU\":[\"Skip the clunky UX that always comes with custom.\"],\"bUmSwL\":[\"Smart patterns, shortcuts, and layouts make everyday tasks faster and easier to execute.\"],\"7PyS12\":[\"Solutions Partner\"],\"e1+XW/\":[\"Solutions Partners\"],\"AVfp38\":[\"Some call this enterprise pricing. We prefer a CRM where API access, webhooks, and workflows don't show up as surprise add-ons.\"],\"/UwJ/B\":[\"Soon: earn revenue\"],\"B/mYo/\":[\"Source code access\"],\"vnS6Rf\":[\"SSO\"],\"uQl22y\":[\"Standard support\"],\"wxW2Sv\":[\"Start automating at huge scale!\"],\"F02wii\":[\"Start building, with Twenty\"],\"XO628f\":[\"Start for free\"],\"sBOFB0\":[\"Start your free trial today\\nwithout credit card.\"],\"FT+TW7\":[\"Stay in control with our\"],\"j9c8rb\":[\"Stay in Flow\"],\"bxgoJR\":[\"Step-by-step guides and playbooks to help your team get the most out of their workspace.\"],\"RMK92/\":[\"Stop fighting custom.\"],\"UCLArd\":[\"Stop settling for trade-offs.\"],\"lm4alq\":[\"structure\"],\"Sv4BSp\":[\"Subdomain (yourco.twenty.com)\"],\"H5/rZQ\":[\"Submit application\"],\"MDqQmP\":[\"Submitting…\"],\"XYLcNv\":[\"Support\"],\"3w+Aox\":[\"Table, Kanban, Calendar\"],\"9M02G5\":[\"Tailor record pages, menus, and views\"],\"JAKtcG\":[\"Talk to us\"],\"GWMpL3\":[\"Tasks & Activities\"],\"Ye3KKA\":[\"Team up with a Twenty expert\"],\"vsdUaL\":[\"Technical notes\"],\"8dX1MJ\":[\"Technology Partner\"],\"EeZsnh\":[\"Technology Partners\"],\"gNW5s8\":[\"Tell us about the custom solutions or integrations you plan to build.\"],\"1ZwmkP\":[\"template\"],\"mvP/25\":[\"Terms and Conditions\"],\"BEX1RA\":[\"Terms of Service | Twenty\"],\"iqG74V\":[\"Terms of Service for Twenty.com PBC, including use of Twenty.com, sub-domains, and related services.\"],\"dn9a7V\":[\"that\"],\"FjkPYg\":[\"That opened the door to something more powerful. Justin built a custom front end that pulls live data from those systems into a single view, tailored to each role. When a proposal is won, what used to require four separate people manually setting up instances across four different tools now happens in a single click, drawing on data collected in Twenty across the full opportunity lifecycle. It is another shift toward higher-value work for clients.\"],\"8S4psU\":[\"That works until you need to understand the business underneath. Which deals are stuck? Where are leads coming from? What is the close rate? With spreadsheets and a legacy custom CRM that could not keep up, those questions were nearly impossible to answer.\"],\"yWOGs1\":[\"that's quick to flex\"],\"rTqABu\":[\"The #1 open source CRM for modern teams. Modular, scalable, and built to fit your business.\"],\"G2IgPj\":[\"The bet is\"],\"NT2bSJ\":[\"The bet is paying off\"],\"Xmr/wH\":[\"the business\"],\"Wie492\":[\"The business development team finally had the CRM they had been asking for. Adoption came naturally: their data was already there when they logged in.\"],\"2kHqXc\":[\"The CEO had resisted bringing in a CRM for years. The business development team had no experience using one, and the licensing costs of well-known CRMs like Salesforce or HubSpot were hard to justify without any guarantee of adoption: CRMs are only as good as the maintenance of the data inside them.\"],\"5DpEh7\":[\"The CRM as a\"],\"2sfVd8\":[\"The CRM as a control hub\"],\"XlFsoH\":[\"The early bet on the architecture is holding, and upcoming AI features are expected to make it even more relevant.\"],\"kCUcnD\":[\"The flexibility is just amazing. Literally, there's nothing you cannot do. You can create objects, access everything through the API, pull notes and send them to the portal. Try doing that in HubSpot. No way. It's the true ability to build exactly what's actually needed.\"],\"MaHqAc\":[\"The flexibility is really what made the difference. Our needs evolve very fast. I discover a new need and in two clicks I can address it. That is a real advantage when you are moving quickly.\"],\"Q5iZ9Q\":[\"The flexibility to wire this together, without outside help and without fighting the platform, is what made it possible for a single person to stand up and maintain a connected stack across an entire consultancy.\"],\"l0eUFC\":[\"The full rollout landed in July 2025. Since then, Nine Dots built a Smart Assistant on top of the system, nudging agents with tasks, reminders, and on-demand market analysis. Some agents never open Twenty directly, yet they are powered by it, outperforming peers on manual processes alone. By Q1 2026, Homeseller had recorded its best sales quarter ever.\"],\"SiW1pC\":[\"the future of CRM\"],\"Uslh7V\":[\"The future of CRM is built,\"],\"sm10Rg\":[\"The Giant Monolith\"],\"nFB8I1\":[\"The In-house Burden\"],\"ZNzKXV\":[\"The key decision was not to build everything from scratch. Twenty covers the data model, permissions, authentication, and workflow engine, the parts that would have taken months to rebuild, so the team could focus on product-specific logic.\"],\"q1kKLq\":[\"The opportunity\"],\"ZgWQl4\":[\"the overhead\"],\"2NJdyz\":[\"The Problem.\"],\"Q8D9Lf\":[\"the product\"],\"3/wAFg\":[\"The result\"],\"nAWeyi\":[\"The result is a system that fits how AC&T already worked, instead of the other way around.\"],\"tfG/xw\":[\"The right\"],\"nbHA9N\":[\"The right foundation\"],\"omVXTg\":[\"The self-hosted setup means Alternative Partners owns the full stack: no vendor access to their data, no dependency on a SaaS pricing model, full control over how the system evolves. The migration was fast because of AI; the result is durable because the stack is open source.\"],\"1tN3qf\":[\"The shift\"],\"AnGOwN\":[\"The situation\"],\"u0uFxg\":[\"They are the real sales\"],\"53RXLB\":[\"They call it customer loyalty. We call it a very affectionate cage.\"],\"RhmZMe\":[\"They did not just replace a tool. They took back ownership of how their business runs.\"],\"5YzqyT\":[\"They evaluated Salesforce, Zoho, Pipedrive, and SuiteCRM. Each came with the same tradeoffs: too expensive, too rigid, or too generic, and none fixed the underlying problem. They were still renting a structure they did not control.\"],\"2y6W+f\":[\"to rule them all\"],\"5eFktS\":[\"Tool integration\"],\"xv7HSr\":[\"total per month with fixed cost\"],\"hmu++i\":[\"Track amount and close date\"],\"POzmFl\":[\"Track every release with changelogs, highlights and demos of the newest features.\"],\"yiYtW0\":[\"Track relationships, manage pipelines, and take action quickly with a CRM that feels intuitive from day one.\"],\"XzfkgA\":[\"tradeoffs\"],\"ccIZU2\":[\"Trust the n°1 CRM,\"],\"KtyN0b\":[\"trusted by\"],\"D5Kf4t\":[\"Tuesday your team learns that deals with a technical champion close 3x faster. Wednesday you add the field, wire up the scoring, adjust the workflow. By Thursday your agents are acting on it. That feedback loop is the edge. And it only works if the CRM is yours.\"],\"Sl2dv/\":[\"Twenty | #1 open source CRM\"],\"G4fKLP\":[\"Twenty Apps opens the door to building products, not just implementations. For example, we're developing a WhatsApp Business integration that any Twenty’s client could get. That's a recurring revenue stream we wouldn't have if we were just configuring someone else's platform.\"],\"mkTfZ1\":[\"Twenty as the\"],\"9gf3bc\":[\"Twenty as the API backbone\"],\"zj0CA+\":[\"Twenty as the API backbone of a go-to-market stack | Elevate Consulting\"],\"0qyMhk\":[\"Twenty gives technical teams the building blocks for a custom CRM that meets complex business needs and quickly adapts as the business evolves.\"],\"jVhq41\":[\"Twenty gives you the kind of flexibility that actually changes what you can offer your clients. The dev experience is clean, the APIs are open, and when something needs to be customized, you can just do it. There's no fighting the platform.\"],\"x/I85y\":[\"Twenty is not only where CRM data lives. It is the API backbone that makes the rest of the stack possible.\"],\"K+zzSQ\":[\"Twenty lets us build a CRM around the business and not the business around the CRM.\"],\"AxT+g4\":[\"Twenty makes it simple. It's clean, intuitive, and built to feel like Notion.\"],\"V1lV7B\":[\"Twenty team support\"],\"WN9ufk\":[\"Twenty?\"],\"EIU345\":[\"Two-factor authentication\"],\"uxuy4l\":[\"UI theme\"],\"jqzUyM\":[\"Unavailable\"],\"Hix/m6\":[\"Unified timeline (emails, events, tasks, notes, files)\"],\"NIuIk1\":[\"Unlimited\"],\"LF0tzk\":[\"Up to 50M automation credits/year\"],\"RDRePw\":[\"Up to 5M automation credits/month\"],\"Roaswv\":[\"User Guide\"],\"3XIgKU\":[\"User roles\"],\"BAzC1v\":[\"View types\"],\"mHtVst\":[\"Visualize your customers on a map!\"],\"CsHuOr\":[\"VP of Engineering\"],\"uRh799\":[\"VP of Engineering at W3villa Technologies\"],\"w2INie\":[\"VP of Engineering, W3villa Technologies\"],\"s0tU5h\":[\"W3Grads\"],\"R8BueT\":[\"W3villa built W3Grads (w3grads.com), an AI-powered mock interview platform for universities and training institutes, using Twenty as its operational backbone.\"],\"7hHsoV\":[\"W3villa built W3Grads for AI mock interviews at scale, with Twenty as the operational backbone.\"],\"QBOUnd\":[\"W3villa shipped W3Grads on Twenty for AI interviews, scoring, and institution-scale workflows without rebuilding CRM plumbing.\"],\"7q5Vjn\":[\"W3villa Technologies\"],\"ZabLEt\":[\"W3villa Technologies set out to solve it properly, not with a workaround, but with a product.\"],\"3v2ipf\":[\"W3villa used Twenty as a production-grade framework for the data model, permissions, authentication, and workflow engine they would otherwise have rebuilt themselves.\"],\"8CHatv\":[\"We could not submit your application. Please try again in a moment.\"],\"MjWeUE\":[\"We didn't want to patch over the problem. We wanted to build something institutions could rely on at scale, and that meant starting from a foundation solid enough to support the full complexity of what we had in mind.\"],\"gdnUik\":[\"We're building the #1 open source CRM, but we can't do it alone. Join our partner ecosystem and grow with us.\"],\"v1kQyJ\":[\"Webhooks\"],\"ZhnM/K\":[\"Webhooks (Change Data Capture)\"],\"jBX2tZ\":[\"Website or github link *\"],\"Z34FQq\":[\"What convinced Olivier was the flexibility of the platform and where it was headed. Even when initial needs were basic record-keeping, he still needed a custom data model with granular permissions to manage the wide range of NetZero activities. He also needed a system that could adapt quickly to a fast-iteration company.\"],\"phiBjc\":[\"What does Twenty cost?\"],\"WVQaGB\":[\"What is coming in April 2026 is what he has been waiting for: AI-assisted workflow creation, describing what he needs and iterating from there instead of building complex logic from scratch. For a founder who runs the CRM himself, that changes what is realistically possible.\"],\"iOAq7n\":[\"What is next\"],\"tWd6MT\":[\"What is planned is significant. NetZero has a data lake, online forms, and multiple internal systems that he wants to connect to Twenty. The pipes are there; the next step is automations that tie them together.\"],\"aYRqmz\":[\"What this means\"],\"1kfjxX\":[\"Whatever came next had to be something they could own.\"],\"cs6VpJ\":[\"When a student registers via QR at a campus event, the system assigns a plan, generates an interview session, and sends a link. The AI conducts the interview, scores the candidate, and classifies the result. Faculty see where each student stands without manually reviewing every session. Building and iterating on these workflows was faster with AI in the loop.\"],\"I/oPrY\":[\"When the channel is\"],\"JglRo/\":[\"When the channel is the business\"],\"ee4xKk\":[\"When the vendor\"],\"DajeYD\":[\"When the vendor pulled the plug\"],\"6LeUXo\":[\"When your CRM\"],\"X1gdzB\":[\"When your CRM is\"],\"Svuneu\":[\"When your CRM is the product: W3Grads on Twenty | W3villa Technologies\"],\"KRKrw8\":[\"Which partner program is right for you?\"],\"bGN8u+\":[\"While NetZero still runs a second CRM in parallel for WhatsApp-heavy operations with farmers in Brazil, they expect to migrate all of it to Twenty as features and the ecosystem grow. Already, their structured, multinational pipeline is powered by Twenty.\"],\"DJlSwA\":[\"Why\"],\"4sOVu+\":[\"Why Twenty\"],\"kwvfYA\":[\"Why Twenty | Twenty\"],\"aycITw\":[\"with\"],\"NI3KID\":[\"with no-code\"],\"OKVlnc\":[\"With Twenty, when a new need appears, he can address it himself: no developer required, no support ticket.\"],\"JRUWoH\":[\"with Twenty?\"],\"KAB1uP\":[\"with you\"],\"Lr53K3\":[\"without friction\"],\"OX5bbs\":[\"Work email *\"],\"woYYQq\":[\"Workflows\"],\"1ie9Dm\":[\"Workflows that\"],\"AzqmMJ\":[\"Workflows that actually get used\"],\"pmUArF\":[\"Workspace\"],\"zkWmBh\":[\"Yearly\"],\"l75CjT\":[\"Yes\"],\"bo6pUs\":[\"Yes, with our Apps framework. Scaffold an extension with `npx create-twenty-app` and ship custom objects, server-side logic functions, React components that render inside Twenty’s UI, AI skills and agents, views, and navigation, all in TypeScript, deployable to any workspace.\"],\"yIcBW6\":[\"Yes. Every Cloud workspace ships with a native MCP server. Connect your AI assistant via OAuth and it can read and write your CRM data in natural language.\"],\"8xYMYQ\":[\"Yes. Import your data via CSV, or use our API for 50,000+ records. Our partners can handle the full migration for you.\"],\"bnuv9n\":[\"Yes. Twenty is the #1 open source CRM on GitHub. You can self-host to fully own your infrastructure, or run it on our managed cloud for a zero-ops setup.\"],\"LJ3sTb\":[\"You don't buy your deployment pipeline off the shelf. You don't rent your data warehouse from a vendor who decides the schema. You build it, you own it, you iterate on it every week. CRM is going the same way. The teams that treat it as infrastructure they own will compound an advantage every quarter.\"],\"SfJVcK\":[\"Your checkout is complete. Follow the steps below to copy your license key into your Twenty instance.\"],\"krHgj1\":[\"Your enterprise key\"],\"+dxjCu\":[\"Your enterprise license has been activated successfully.\"],\"pEQiCY\":[\"Your name *\"],\"9Apzz7\":[\"your own story?\"],\"wHjFOw\":[\"Zero\"],\"xlFvlS\":[\"Zero manual work\"],\"ZvEoMq\":[\"Zero manual work at the core. Full automation. Built on Twenty.\"]}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/es-ES.ts b/packages/twenty-website-new/src/locales/generated/es-ES.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/es-ES.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/fi-FI.ts b/packages/twenty-website-new/src/locales/generated/fi-FI.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/fi-FI.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/fr-FR.ts b/packages/twenty-website-new/src/locales/generated/fr-FR.ts index 76934b72de0..22f5cda7d45 100644 --- a/packages/twenty-website-new/src/locales/generated/fr-FR.ts +++ b/packages/twenty-website-new/src/locales/generated/fr-FR.ts @@ -1 +1 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file +/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"BUIzFF\":[\"-33% off\"],\"q0ur5+\":[[\"0\"],\" | Twenty Customer Story\"],\"icND7o\":[\"/ seat / month - billed yearly\"],\"XUqxHW\":[\"/user/month\"],\"ojimXJ\":[\"© 2026 – Twenty\"],\"foGG8A\":[\"+$105/user per month\"],\"Bsq9GJ\":[\"+$35/user per month\"],\"mWSpW5\":[\"+$5/user per month\"],\"JIwDAd\":[\"+$7000/org per month\"],\"yVr18d\":[\"+$75/user per month\\nSwitch to enterprise!\"],\"iK+ikF\":[\"+10k others\"],\"nRtVCK\":[\"+20% of net spend\\n+$75/user per month\\nSwitch to enterprise!\"],\"be30i9\":[\"$0\"],\"6inRXo\":[\"$1/orchestration run/org\\n+$75/user per month\\nSwitch to enterprise!\"],\"W8uNyP\":[\"$12\"],\"EkIZkY\":[\"$19\"],\"xrRqkE\":[\"$25\"],\"8r6DgO\":[\"$9\"],\"EHNAf2\":[\"1 click\"],\"qvNspm\":[\"1‑800‑YES‑SOFTWARE\"],\"6WQdu8\":[\"100 per minute\"],\"uuKUqZ\":[\"11 permissions\\ngroups\"],\"SaSafJ\":[\"150 hours\"],\"9ntyY8\":[\"150 hrs\"],\"2z1Znr\":[\"2 years contract\"],\"l6Vx4q\":[\"2,000+\"],\"oadHWW\":[\"200 per minute\"],\"FKcV/y\":[\"2025\"],\"PJgfBS\":[\"3\"],\"BL47wE\":[\"3 2 years contract\\n-33% off\"],\"/jgC+9\":[\"3 product lines\"],\"7SJhyh\":[\"30+\"],\"Pdl2UE\":[\"4 tools\"],\"tZvajW\":[\"6 workflows\"],\"AQPMc4\":[\"90%\"],\"fHxYPM\":[\"90%+\"],\"1GAcp6\":[\"A business that does not fit a\"],\"NLSM5L\":[\"A business that does not fit a template\"],\"9Z2FZQ\":[\"A CRM for teams\"],\"GSM5RA\":[\"A CRM that\"],\"och6cp\":[\"A CRM that grows with you | NetZero & Twenty\"],\"EewhZt\":[\"A CRM they\"],\"sgzhYK\":[\"a CRM they'll love\"],\"xiVefD\":[\"A custom CRM gives your org an edge,\"],\"Qay3/Z\":[\"A modern CRM with\"],\"GoyOzl\":[\"A platform ready to\"],\"rxMguZ\":[\"A platform ready to grow\"],\"R5hTKX\":[\"A real estate agency on WhatsApp\"],\"WjUlJr\":[\"A retro theme as a paid add-on is somehow the most believable part.\"],\"r9Msiv\":[\"A year ago, customizing your CRM meant hiring a Salesforce consultant, learning Apex, waiting months. The gap between \\\"I want this\\\" and \\\"it's live\\\" was measured in quarters and invoices. So people settled. They bent their process to fit the tool and called it adoption.\"],\"c0RR2h\":[\"About 150 hours per month saved in manual operations. Real-time metrics for the business owner. Growth readiness without adding operational headcount. A team that can answer questions that used to take days to piece together.\"],\"41o3VS\":[\"AC&T and Flycoder moved from a dead vendor export to self-hosted Twenty, with over 90% lower CRM cost and full control.\"],\"TuJiQF\":[\"AC&T Education Migration\"],\"yHUdh6\":[\"AC&T Education Migration (actimmi.com) is an education agency in Australia. They help international students with applications to education providers and visas. They had been on a previous CRM until the vendor shut the system down, leaving nothing but a CSV export.\"],\"MPzy5R\":[\"AC&T moved to a self-hosted Twenty instance with no vendor risk, no forced migration, and CRM costs reduced by more than 90%.\"],\"J+A83M\":[\"AC&T replaced a shuttered vendor CRM with self-hosted Twenty and cut CRM costs by more than 90%.\"],\"muhZ+X\":[\"Activating your enterprise license…\"],\"y+9jZD\":[\"activation\"],\"TDMykO\":[\"actually get used\"],\"+/fX0G\":[\"actually own\"],\"woOp+c\":[\"Add objects and fields\"],\"Y2K3rp\":[\"Add-ons\"],\"Op7nVF\":[\"Adoption\"],\"TeMRC2\":[\"advanced\"],\"cBdysG\":[\"Advanced Encryption\"],\"/C6Pwb\":[\"Aggregate, bar, line, pie, and gauge widgets\"],\"rp2omj\":[\"Agribusiness\"],\"jiGK6+\":[\"AI (Einstein)\"],\"aUn7Ps\":[\"AI & Automations\"],\"HbXHSp\":[\"AI agents\"],\"WGXwzu\":[\"AI agents are starting to draft outreach, score leads, research accounts, write follow-ups, update deal stages. Every one of these actions reads from and writes to the CRM. The scoreboard became the playbook. The database became the brain.\"],\"Rsxdfy\":[\"AI Agents with custom skills\"],\"TfV/y9\":[\"AI chat, settings, and records in a side panels for fast, single-screen access.\"],\"R+bwFB\":[\"AI for rapid iterations\"],\"+pgY/C\":[\"AI in the\"],\"A6v5aI\":[\"AI in the migration workflow\"],\"9VlH2i\":[\"AI made the gap that small.\"],\"Tp1P7F\":[\"AI turned it into an operating system.\"],\"t97j+F\":[\"AI-assisted\"],\"LIGwFL\":[\"Alternative Partners\"],\"GDVvyT\":[\"Alternative Partners is a consulting firm that moved from Salesforce to a self-hosted Twenty instance. Benjamin Reynolds led the migration. He had already become a Twenty expert implementing Twenty for one of Twenty's first cloud customers.\"],\"CCrRuT\":[\"Alternative Partners replaced Salesforce with self-hosted Twenty, using agentic AI to compress migration work.\"],\"UIu18Y\":[\"Alternative Partners used agentic AI to compress what would typically be weeks of Salesforce migration work into something a single person could oversee.\"],\"iFLhqi\":[\"Amrendra Pratap Singh\"],\"JDRPTH\":[\"an intuitive interface\"],\"lTSix7\":[\"and unlock new opportunities with Twenty\"],\"aGCcXe\":[\"Any Questions?\"],\"/AMVjF\":[\"APEX tutorials\"],\"OZtEcz\":[\"API\"],\"yKBF3l\":[\"API access\"],\"GpYSXo\":[\"API backbone\"],\"30KnZB\":[\"API calls\"],\"42FQ3y\":[\"API-first\"],\"azQMHP\":[\"APIs are extra. Simplicity has a price.\"],\"MqJvvH\":[\"APIs, SDKs and webhooks to extend Twenty and ship apps on top of your CRM data.\"],\"I1Z4AM\":[\"Apply to build\"],\"uicv6Z\":[\"Architecture\"],\"Sfa0xD\":[\"around it\"],\"yYKNWp\":[\"Assemble, iterate and adapt a robust CRM,\"],\"v7zTZl\":[\"Assign owners and due dates\"],\"9c5acG\":[\"at\xA0AI\xA0Speed\"],\"4/b98B\":[\"at the core\"],\"y2W2Hg\":[\"Audit logs\"],\"FFv0Vh\":[\"Automation\"],\"SJ7WSP\":[\"Available on YouTube!\"],\"iH8pgl\":[\"Back\"],\"jQLfIW\":[\"Because apparently privacy feels more premium with a surcharge.\"],\"qEQv1F\":[\"Because everything is built on Twenty's open foundation, Flycoder could wire the exact logic AC&T needed without fighting the platform.\"],\"uIG0OH\":[\"Because the foundation is solid, W3Grads is architected for what comes next, including a payment layer for future paid interview plans and nationwide scale without structural rewrites.\"],\"JGM+JO\":[\"Because true orchestration means putting a dollar sign on every dramatic entrance.\"],\"SoJDT2\":[\"Become\"],\"sx6hMS\":[\"Become a Content Partner\"],\"PKMHNe\":[\"become a genius!\"],\"6ZCRfT\":[\"Become a partner\"],\"lvXL1I\":[\"Become a Solution Partner\"],\"EwaFPn\":[\"Become a Technology Partner\"],\"eqswCn\":[\"Begin with production-grade\"],\"SYfuGP\":[\"Benjamin Reynolds\"],\"YfjrMq\":[\"Bertrams\"],\"mzTjnf\":[\"Better than Liquid Glass!\"],\"GvGk4g\":[\"Beyond\"],\"bxWnM1\":[\"breaking operations\"],\"ONUC8n\":[\"build\"],\"WxyAko\":[\"Build a CRM your competitors\"],\"pF0PoL\":[\"Build custom dashboards\"],\"wWXG44\":[\"Build integrations that connect Twenty with the tools your customers already use. Help us expand the Twenty ecosystem.\"],\"ZhC3tY\":[\"Build it in an afternoon.\"],\"e1u+Zh\":[\"Build on an open platform\"],\"oIpt0K\":[\"Build your Enterprise CRM\"],\"J7i6zm\":[\"building blocks\"],\"/ETeLn\":[\"built a\"],\"8hMULp\":[\"built a CRM around it\"],\"SoAKmY\":[\"Built for speed\"],\"hnSOXa\":[\"built with Twenty\"],\"j8RGrG\":[\"Burned by vendor lock-in, AC&T built a CRM they actually own | Twenty\"],\"EjSajd\":[\"but building one\"],\"wCALve\":[\"Can developers extend Twenty with code?\"],\"6mytgb\":[\"Can I migrate from Salesforce or HubSpot?\"],\"+oHJTp\":[\"can't buy.\"],\"u+wGUf\":[\"change with Twenty\"],\"2D9CbR\":[\"Check more add-ons\"],\"ZbCD7v\":[\"Classic never dies. It just gets extended one more time.\"],\"L/LPQZ\":[\"Cloud Pro is $9/user/month (yearly). Organization is $19/user/month and unlocks SSO and row-level permissions for teams that need finer access control.\"],\"1YncLF\":[\"Co-founder at NetZero\"],\"vWaYWP\":[\"Co-founder, NetZero\"],\"R8BDsa\":[\"Co-marketing opportunities\"],\"b5bnuN\":[\"Column-to-field mapping (including relations)\"],\"NHYWK4\":[\"comes with\"],\"ofoEsM\":[\"Coming soon!\"],\"KGD9Kt\":[\"Commercial license (no AGPL obligations)\"],\"chL5IG\":[\"Community\"],\"8CLnho\":[\"Community support\"],\"SHjhDC\":[\"Company or brand *\"],\"O403zL\":[\"company-wide\"],\"P/f7ez\":[\"Complete activation for your Twenty self-hosted enterprise license.\"],\"Fkb+LW\":[\"Compose your CRM and internal apps with a single extensibility toolkit.\"],\"kD7ZGH\":[\"Compose your CRM and internal apps with a single extensibility toolkit. Data model, layout, and automation.\"],\"iSLIjg\":[\"Connect\"],\"90KBr0\":[\"Connect Google or Microsoft accounts\"],\"MVrQxI\":[\"Connected via API\"],\"cfAG43\":[\"Consulting\"],\"Oii3vg\":[\"Contacts & Companies\"],\"fi9xsM\":[\"Content & Community Partner\"],\"vymMOT\":[\"Content & Community Partners\"],\"mutfS8\":[\"Continue iteration\"],\"+Uepfb\":[\"Control\"],\"VOpE1H\":[\"control hub\"],\"sTIhSj\":[\"Control without\"],\"wH1xmY\":[\"Control without drag\"],\"Bj7igG\":[\"Control without the overhead\"],\"PiH3UR\":[\"Copied!\"],\"he3ygx\":[\"Copy\"],\"7qTWwB\":[\"Copy the enterprise key above.\"],\"PXnj76\":[\"Copy this key and paste it into your Twenty self-hosted instance settings.\"],\"FapWIq\":[\"Core Features\"],\"JRK+HF\":[\"Costs down more than\"],\"Xn2Nlq\":[\"Create a workflow\"],\"B9nLrl\":[\"Create apps on Twenty\"],\"yAL7FY\":[\"Create custom apps\"],\"oS1RiS\":[\"Create tasks from records\"],\"MSlbwg\":[\"CRM\"],\"Y44Xsf\":[\"CRM costs dropped by more than 90%. Manual overhead tied to the old system is gone. For the first time, AC&T has a CRM they will not lose again.\"],\"wDasLa\":[\"CRM Engineer\"],\"DqkzNV\":[\"CRM Engineer, AC&T Education Migration\"],\"b7vNti\":[\"CRM was a database you filled on Fridays. AI turned it into the system that runs your go-to-market. To differentiate, you have to build what your competitors can't buy.\"],\"v5sHk3\":[\"CRM was a ledger.\"],\"I6FFSA\":[\"CSV export anytime\"],\"8RWevB\":[\"CSV import & export\"],\"s+QiQi\":[\"CSV import flow\"],\"lh6aPn\":[\"Custom AI models\"],\"OaTurC\":[\"Custom apps\"],\"yBSEtR\":[\"Custom deal stages for your process\"],\"Vz1Vq2\":[\"Custom domain (crm.yourco.com)\"],\"oPwQt4\":[\"Custom fields\"],\"U1RB/7\":[\"Custom fields and relationships\"],\"prIqWa\":[\"Custom layout\"],\"8skTDV\":[\"Custom objects\"],\"AbyZbl\":[\"Custom views\"],\"YjXLAQ\":[\"Customer Stories\"],\"NihQNk\":[\"Customers\"],\"3mRQi4\":[\"Customers | Twenty\"],\"qqLY+j\":[\"Customization\"],\"GRfLAg\":[\"Customizations\"],\"4RLD4p\":[\"Daily messages\"],\"V0kvgB\":[\"Data import\"],\"5cNMFz\":[\"Data model\"],\"FBZA6a\":[\"Dedicated partner support\"],\"KR5UiF\":[\"Dev teams power\"],\"n+SX4g\":[\"Developers\"],\"vcsX5L\":[\"Differentiation now\"],\"Wuqvfz\":[\"Director of Digital and Information, Elevate Consulting\"],\"+SvmQA\":[\"Discover the newest features and improvements in Twenty,\\nthe #1 open source CRM.\"],\"lsSE4P\":[\"Discover the newest features and improvements in Twenty, the #1 open source CRM.\"],\"W+b/DF\":[\"Discover what's new\"],\"Zjjbne\":[\"Do I need a developer to customize Twenty?\"],\"Zx22Ih\":[\"Does Twenty work with Claude, ChatGPT, and Cursor?\"],\"pOLPPB\":[\"Don't get locked into someone else's ecosystem. Twenty's developer experience looks like normal software, with local setup, real data, live testing, and no proprietary tooling.\"],\"fPma12\":[\"Drag-and-drop deals between stages\"],\"4CJ4xV\":[\"EdTech\"],\"aqxYLv\":[\"Education\"],\"1wTjWx\":[\"Elevate Consulting is a management consultancy based in Canada. When Justin Beadle, Director of Digital and Information, joined, the company ran entirely on Word documents, Excel spreadsheets, sticky notes, emails, and reliance on people. There was no CRM, no API-accessible tools, only a patchwork trying to stand in for a single source of truth.\"],\"2THY70\":[\"Elevate Consulting uses Twenty as the API backbone connecting billing, Teams, resourcing, and a custom front end around client and opportunity data.\"],\"wiTeYI\":[\"Elevate's CEO was so impressed with Twenty he started recommending it to clients before the internal setup was even complete. The team is exploring bringing Twenty to client projects as part of their consulting practice, including as the backend for custom-built products tailored to specific operational needs.\"],\"FNyrb0\":[\"Email & Calendar\"],\"AoN898\":[\"Email and Chat\"],\"ObCvfI\":[\"Email sharing\"],\"hfqiVr\":[\"Email/calendar activity on each record\"],\"XGd8Wo\":[\"Emails & Calendar\"],\"o4/9l1\":[\"Emails and events linked to CRM records\"],\"Qpn/bX\":[\"Encrypt your data\"],\"Ijh+h+\":[\"Enjoy unlimited customization using the AI coding tools you already love. Adapt your CRM to fit the way your business grows and wins.\"],\"xoqD/n\":[\"Enter a valid email address.\"],\"GpB8YV\":[\"Enterprise\"],\"5l0LGU\":[\"Enterprise activation | Twenty\"],\"3dQ6zJ\":[\"Environments\"],\"D1bFNS\":[\"Estimated monthly opportunities (optional)\"],\"IhKZhp\":[\"Even the training material is a feature worth celebrating.\"],\"kz/8m8\":[\"Everything in Pro\"],\"zoNk7e\":[\"Everything updates in real time, with AI chat always ready to help you move faster.\"],\"bEEOy2\":[\"Everything you need,\"],\"CIcRGB\":[\"Exclusive content collaboration opportunities\"],\"57gG1f\":[\"Experience enterprise-grade granularity, starting with an 11th permission.\"],\"JTM5zS\":[\"Explore customer stories\"],\"weFouS\":[\"Extended run!\"],\"akLvep\":[\"Familiar, modern interface\"],\"abO45l\":[\"Fast path to action\"],\"e6BgMV\":[\"Field-level permissions\"],\"sER+bs\":[\"Files\"],\"UWTNog\":[\"Filtered metrics from live CRM data\"],\"TJ7HQl\":[\"Find a partner\"],\"MD032e\":[\"Find a Twenty partner\"],\"SZabbQ\":[\"Find the program that fits your business\"],\"wHyJkT\":[\"Find the right partner to implement, customize, and tailor Twenty to your team.\"],\"PHM5wp\":[\"Flexibility\"],\"HBJ0P5\":[\"Flow\\norchestration\"],\"obrJBN\":[\"Fly through your workspace with shortcuts and short load times.\"],\"HjFq2b\":[\"Flycoder, a full-stack development partner, helped them set up Twenty as a self-hosted instance shaped around how AC&T actually operates. The data model centers on students, not a generic contact-and-deal pipeline. Statuses update automatically: a workflow runs nightly to keep enrollment records current. Automated email reminders cover important dates. Adding a new record takes under a minute.\"],\"1SL9ZF\":[\"Focus on the use case, not the\"],\"P5E+kT\":[\"Focus on the use case, not the plumbing\"],\"wdVyxi\":[\"Folder/Label import\"],\"CKQ0za\":[\"For a firm that once ran on sticky notes, this is more than an upgrade. It is a complete transformation.\"],\"yAJC9c\":[\"For twenty years, CRM meant the same thing: a place to log calls, track deals, and pull reports on Friday. The real work happened in people's heads, in Slack threads, in hallway conversations. The CRM kept score. Nobody expected more from it.\"],\"wSoqhC\":[\"foundation\"],\"UCoxP5\":[\"Founder, Nine Dots Ventures\"],\"IPaRlc\":[\"Founder, Wintactix\"],\"tUgSXd\":[\"Free for you!\"],\"yDeZSV\":[\"From documents to\"],\"emeKZ7\":[\"From documents to open APIs\"],\"CMqgta\":[\"From Salesforce to\"],\"syU/BD\":[\"From Salesforce to self-hosted Twenty, powered by AI | Alternative Partners\"],\"TH5XjF\":[\"From simple to\"],\"Lf7cb3\":[\"From simple to advanced\"],\"m1I5TY\":[\"Full communication history in one place\"],\"wXgKOm\":[\"Full customization\"],\"YZ7Q3Z\":[\"Full ownership\"],\"hdxwWi\":[\"Fully customizable\"],\"ZDIydz\":[\"Get started\"],\"3mUKNr\":[\"Go the extra mile\"],\"Ay/vbT\":[\"Good choice!\"],\"IsZ6P7\":[\"grow\"],\"MCLWq4\":[\"Grow with a flexible foundation\"],\"XpCing\":[\"grows\"],\"ORQRms\":[\"grows with you\"],\"Xf5EJg\":[\"Halftone generator\"],\"DHC85F\":[\"Halftone Generator | Twenty\"],\"c3XJ18\":[\"Help\"],\"CRzGla\":[\"Help center\"],\"R5aged\":[\"Help customers implement, customize, and succeed with Twenty. Combine sales and services to grow your business.\"],\"qfSbUb\":[\"His approach was unconventional. Instead of mapping fields manually, scripting transforms, and validating data step by step, he handed the job to agentic AI tools with a brief: where the data lives, the GitHub repo for the target platform, and the Railway deployment. Start, and only return if something breaks beyond a 70% confidence fix.\"],\"i0qMbr\":[\"Home\"],\"CtU7yU\":[\"Homeseller\"],\"KYnPnU\":[\"Homeseller is a high-volume real estate agency in Singapore, founded by one of the country's top-performing property agents. The whole operation runs on WhatsApp: no email, no calendars, just group chats, thousands of them, with clients, agents, and leads together.\"],\"v6cmMq\":[\"Homeseller kept their habits. WhatsApp stayed WhatsApp. What changed is that everything flowing through those conversations now lands in a structured system, tracked, classified, and visible in real time.\"],\"xaIV9m\":[\"Homeseller, WhatsApp, and a CRM built around the business | Nine Dots & Twenty\"],\"XnFo3G\":[\"How AC&T Education Migration and Flycoder replaced a shuttered vendor CRM with self-hosted Twenty, with 90%+ lower cost and full ownership.\"],\"PYi0f6\":[\"How Alternative Partners migrated from Salesforce to self-hosted Twenty using agentic AI in the implementation loop: fast migration, durable ownership.\"],\"o+Kp2M\":[\"How do you want to partner with Twenty? *\"],\"+MLOVo\":[\"How Elevate Consulting moved off documents and spreadsheets to Twenty as the API-connected CRM at the center of their stack.\"],\"OIwpKt\":[\"How long does it take to get started?\"],\"iRzy9e\":[\"How NetZero uses Twenty across carbon credits, agricultural products, and franchised industrial systems with a modular CRM and a roadmap toward AI-assisted workflows.\"],\"NZYA4S\":[\"How Nine Dots Ventures rebuilt a Singapore real estate agency on Twenty with APIs, n8n, Grafana, and AI on top of 2,000+ WhatsApp messages a day.\"],\"kqEcVc\":[\"How teams\"],\"bcln75\":[\"How Twenty collects, uses, safeguards, and discloses information when you use Twenty.com and related services.\"],\"URQPvK\":[\"How W3villa Technologies shipped W3Grads, an AI mock interview platform for institutions, on Twenty as the operational backbone.\"],\"l0JGUk\":[\"Impersonate users\"],\"N4OVNn\":[\"Implementation\"],\"yHueLx\":[\"Implementation partners\"],\"InyU0Z\":[\"In June 2025, Justin learned Twenty v1 had shipped. Within two or three days, the CEO asked him to look into setting up a CRM. The shift came from the potential of what could be built on top of fully open APIs. The timing was perfect.\"],\"d1Aru8\":[\"In production.\"],\"Sqb+jp\":[\"In-app preview for supported file types (when enabled)\"],\"3XP8Lk\":[\"Included!\"],\"S8gy7K\":[\"Industry\"],\"MeL8SS\":[\"Infinite scroll\"],\"+mOisw\":[\"Infinite scroll is still coming soon, unlike the invoice.\"],\"K8iwJx\":[\"Install shared tarball app\"],\"AHWM9N\":[\"Integration\"],\"OChKCc\":[\"Interactive halftone generator exported from Twenty.\"],\"mdsMtj\":[\"internal rollout\"],\"a+PGuG\":[\"Internet accounts per user\"],\"DQenUJ\":[\"is the product\"],\"/yQIKP\":[\"Is Twenty really open-source?\"],\"QjEULz\":[\"It is just such a nicer experience than dealing with a Salesforce or a HubSpot. My mission has been to get every tool API-accessible, so everything talks to each other.\"],\"FJ51pP\":[\"It is just such a nicer experience than dealing with a Salesforce or a HubSpot. My mission has been to get every tool API-accessible, so everything talks to each other. Twenty made that possible in a way older CRM platforms simply do not.\"],\"kqIAbN\":[\"It worked. This is AI-assisted iteration in practice: not AI as a product feature, but as part of implementation work, compressing what would typically be weeks into something one person can oversee without being the bottleneck.\"],\"ltNDOB\":[\"It's fragile. V1 ships quickly, but maintaining and making changes is a long term burden.\"],\"kX2mij\":[\"Join our ecosystem and help businesses take control of their customer data with\"],\"1qiriB\":[\"Join our growing partner ecosystem\"],\"ZFnW+/\":[\"Join our partner ecosystem and grow with us as we build the #1 open source CRM.\"],\"4721j2\":[\"Join our partner ecosystem and help businesses\\ntake control of their CRM.\"],\"4QqdtB\":[\"Join the teams that chose to own their CRM.\\nStart building with Twenty today.\"],\"m/pXki\":[\"Join the teams that chose to own their CRM. Start building with Twenty today.\"],\"pB76mP\":[\"Jul 2025\"],\"hvwlu3\":[\"Jun 2025\"],\"qrYDGE\":[\"Justin Beadle\"],\"vYXuJI\":[\"Justin built workflows for notifications across the team, alerting the right people in Teams when a prospect becomes a lead or when project milestones are reached. Forms in Twenty let the business development team log activity without leaving the tool. The impact is real for the organization. The tool has been adaptable from opportunity-level work at a client to executive-level decisions.\"],\"E6Oohx\":[\"Justin's broader mission at Elevate has been to move the company off static documents and onto tools with API access. By the end of 2025, that was in place: time billing, resource planning, Microsoft Teams, and project management were all accessible via API, with Twenty at the center holding client and opportunity data. Team members could use that information strategically instead of re-keying it.\"],\"GAmD3h\":[\"Languages\"],\"wL3cK8\":[\"Latest\"],\"rdU729\":[\"Layout\"],\"+Ss/og\":[\"Learn how to use Twenty\"],\"vifyyw\":[\"Legal\"],\"tHFTlp\":[\"Licensee:\"],\"gggTBm\":[\"LinkedIn\"],\"Jvz4g8\":[\"Listing on Twenty integrations page\"],\"AHVzME\":[\"Live data and AI built\"],\"GObQuL\":[\"Live updates\"],\"zmRZwk\":[\"Live updates are unavailable, which is almost more honest.\"],\"M7lzaL\":[\"lives in the code you own.\"],\"ObPscj\":[\"Loading activation…\"],\"7VBQ2j\":[\"Local, Production\"],\"JO8Bdx\":[\"Lock-in\"],\"sQia9P\":[\"Log in\"],\"KPLAuI\":[\"Lower CRM cost\"],\"i3PI5l\":[\"Make your GTM team happy\"],\"Si4WyF\":[\"Management Consulting\"],\"poX06f\":[\"Manual work at core\"],\"VJOKPB\":[\"Maps view\"],\"u0wogL\":[\"Marketing assets & brand resources\"],\"LiMSZr\":[\"Marketplace listing\"],\"bJNKJ1\":[\"Master every corner of Twenty\"],\"Zw+Zv+\":[\"MCP server\"],\"zxcfgg\":[\"means control\"],\"bwCiCi\":[\"Meet the certified agencies and consultants implementing Twenty for teams worldwide.\"],\"EhO+8s\":[\"Meet the teams running their business on Twenty. Real customer stories on how they shaped the CRM to fit their workflow.\"],\"ujYZ+f\":[\"Meet the teams who shaped Twenty into their own CRM with self-hosted deployments, AI-assisted workflows, and API-first product stacks.\"],\"ZdvtU2\":[\"migration workflow\"],\"so04x6\":[\"Mike and Azmat from Nine Dots stepped in to fix that, not by changing how Homeseller works, but by building a system that finally fit around it.\"],\"izddzp\":[\"Mike Babiy\"],\"+8Nek/\":[\"Monthly\"],\"VqXuUv\":[\"More options available!\"],\"XPXSwi\":[\"Most packaged software makes companies more similar. Learn why the future of CRM is built, not bought.\"],\"ZPjy5q\":[\"moves fast\"],\"dlKkNL\":[\"Multi-file upload on records\"],\"6YtxFj\":[\"Name\"],\"LfNsfa\":[\"Need a quick change? Skip the engineering ticket. Customize your workspace in minutes.\"],\"4sbgPq\":[\"Need help with customization?\"],\"8Cp4Of\":[\"NetZero\"],\"J8ZssL\":[\"NetZero runs a modular Twenty setup across carbon credits, ag products, and industrial systems.\"],\"7HVBV+\":[\"NetZero uses Twenty as a modular CRM across product lines and countries, with a roadmap into AI-assisted workflows.\"],\"3PtYTO\":[\"NetZero works with the agro-industry, serving clients from multinationals to smallholder farmers. They sell carbon credits, agricultural products, and franchised industrial systems across three different product lines, multiple countries, and multiple company sizes. When Olivier Reinaud, co-founder of NetZero, started looking at CRMs in late 2024, he was not chasing the most feature-rich platform. He wanted the right foundation.\"],\"KqojOL\":[\"Next steps\"],\"qZ+egR\":[\"Nine Dots put Twenty at the center of Homeseller's stack with APIs, automation, and AI on top of WhatsApp-heavy operations.\"],\"sGuHDu\":[\"Nine Dots rebuilt Homeseller's operations on Twenty, with a custom data model shaped around their sales flow. Because Twenty is open and everything is accessible via API, they connected it to what the business actually needed: n8n for automated workflows (in-app workflows were not available at that time), Grafana for live dashboards fed from Twenty, and a custom AI layer to parse and extract structured insights from more than 2,000 WhatsApp messages a day.\"],\"1UzENP\":[\"No\"],\"o+/Ad5\":[\"No more renting someone else's\"],\"LmWdm6\":[\"No more renting someone else's structure\"],\"K1QTmg\":[\"No open-source distribution requirement\"],\"q9f4Tp\":[\"No-code\"],\"SiZ/7E\":[\"No. Build custom objects, fields, views, and no-code workflows straight from Settings. Unlimited, no extra charge.\"],\"7ngHsA\":[\"not bought.\"],\"k3T2mM\":[\"Now a developer can describe what they want to Claude Code and have a working app in an afternoon. A custom object, a scoring workflow, a new view, an integration. The bottleneck isn't building anymore. It's whether your platform lets you.\"],\"CzeIij\":[\"Number of dashboards\"],\"0dVyEt\":[\"of a go-to-market stack\"],\"pkrZnO\":[\"Olivier recognizes that NetZero's current use of Twenty is still relatively simple: workflows and integrations are not yet as deep as he eventually wants, because he prioritized getting foundations right first.\"],\"l/1LQc\":[\"Olivier Reinaud\"],\"D2b+1q\":[\"On a single CRM\"],\"XhXmzW\":[\"On this page\"],\"jeGPx0\":[\"on Twenty\"],\"pbmZ7R\":[\"Onboarding Packs\"],\"/DFChQ\":[\"One\"],\"Zt1UZb\":[\"One API to rule them all\"],\"pShqss\":[\"Only $5 for SSO. Practically a charity program.\"],\"3WErRa\":[\"open APIs\"],\"pkXQA+\":[\"Open your Twenty self-hosted instance Settings → Enterprise.\"],\"wtKPUT\":[\"open-source primitives.\"],\"8THUfN\":[\"open-source software\"],\"5RkmVr\":[\"Open-source, AI-ready, and yours to shape.\"],\"oYQN5j\":[\"or not !\"],\"ucgZ0o\":[\"Organization\"],\"M12WZl\":[\"our partner\"],\"dKGg8T\":[\"Out of stock\"],\"C8Ex6S\":[\"out of the box\"],\"PASC/7\":[\"Outlived every redesign since 2004.\"],\"Bwsi7B\":[\"Own your CRM end to end\"],\"oDAEQq\":[\"Ownership\"],\"NmF/Vo\":[\"Owning your stack remains mysteriously out of stock.\"],\"+AiiMt\":[\"Pagination builds character.\"],\"mPkInZ\":[\"Partners\"],\"itrQKv\":[\"Partners | Twenty\"],\"9yo8NN\":[\"Paste the key and click Activate.\"],\"nxV7o2\":[\"paying off\"],\"FFkjaT\":[\"Pipeline Management\"],\"GwGdy2\":[\"Plans that scale with your team. Compare tiers of the #1 open source CRM.\"],\"0gBYFU\":[\"Please complete all required fields before submitting.\"],\"iCh6/3\":[\"plumbing\"],\"a7u1N9\":[\"Price\"],\"aHCEmh\":[\"Pricing\"],\"CboOX5\":[\"Pricing | Twenty\"],\"J2IAmT\":[\"Principal and Founder\"],\"kXBQYM\":[\"Principal and Founder, Alternative Partners\"],\"k62X/2\":[\"Priority support\"],\"LcET2C\":[\"Privacy Policy\"],\"DJ3uVe\":[\"Privacy Policy | Twenty\"],\"3fPjUY\":[\"Pro\"],\"4gXof3\":[\"Product | Twenty\"],\"kIGKva\":[\"Production grade quality\"],\"CcK9cq\":[\"Programs that previously needed heavy manual coordination now run end-to-end with automation. Institutions get a scalable, intelligent system; students get faster preparation for interviews that matter; W3villa shipped a product institutions can build revenue around.\"],\"9NAqO4\":[\"Proposal automation\"],\"a24q7E\":[\"Proprietary languages, slow deployment cycles, and \\\"black box\\\" logic.\"],\"kS6G/9\":[\"pulled the plug\"],\"frfCYp\":[\"Q1 2026\"],\"gSQVmr\":[\"Read the case\"],\"EqCbT9\":[\"Read/Edit/Delete permissions\"],\"SNf+4G\":[\"Ready to build\"],\"9BL+5g\":[\"Ready to grow\"],\"1d+b/h\":[\"Ready to grow\\nwith Twenty?\"],\"NIwjHh\":[\"Real Estate\"],\"CH+Ona\":[\"Real stories from real teams about how they shaped Twenty to fit their workflow and accelerated their growth.\"],\"5O1eTm\":[\"Real-time changes? That will be a premium surprise.\"],\"4hc7hc\":[\"Real-time data\"],\"dKQCrj\":[\"Real-time is a state of mind, not a feature.\"],\"Zhiuz9\":[\"Record quarter\"],\"PWlTvh\":[\"Records\"],\"rwWjWg\":[\"Release Notes\"],\"5icoS1\":[\"Releases\"],\"Bh0u1L\":[\"Releases | Twenty\"],\"a/aGHe\":[\"Rename, download, and delete attachments\"],\"t9yxlZ\":[\"Reports\"],\"SY/an2\":[\"Reports & Dashboards\"],\"5Cs/CL\":[\"Resale discounts & revenue share\"],\"s+MGs7\":[\"Resources\"],\"oC2l7f\":[\"REST & GraphQL API\"],\"kx0s+n\":[\"Results\"],\"2itg0p\":[\"Retro 2015\"],\"OH7xLu\":[\"Revenue share for referred customers\"],\"frqduN\":[\"Rich notes attached to records\"],\"/gaSVU\":[\"Roadmap\"],\"C77N5M\":[\"Roles & Permissions\"],\"Wdh41P\":[\"Row-level permissions\"],\"mRpuWW\":[\"Running mock interview programs for hundreds of students sounds straightforward. In practice, universities and training institutes hit the same wall: registrations entered by hand, interview links sent one by one, faculty reviewing every session without scoring or classification. At real scale, it breaks.\"],\"Burn4/\":[\"Salesfarce Add-on Center\"],\"xTfEgV\":[\"Salesfarce Pro\"],\"/UolBB\":[\"Salesforce Classic\"],\"l69E+u\":[\"Salesforce migration\"],\"CADlRK\":[\"Same CRM\"],\"60i6S+\":[\"Same output\"],\"xl2tJ9\":[\"Same results\"],\"w9QWxz\":[\"SAML/OIDC SSO\"],\"elz5ka\":[\"Saved / month\"],\"tigXYO\":[\"saved every month\"],\"bifv6N\":[\"Scale\"],\"rbgetd\":[\"Scale without\"],\"842ybw\":[\"Scale without breaking operations\"],\"HVOGoW\":[\"Seats limit\"],\"a3LDKx\":[\"Security\"],\"q20Cda\":[\"See how teams\"],\"aB+XpI\":[\"See more features\"],\"FY4ykg\":[\"See the latest release\"],\"JDp1bg\":[\"See updates as they happen. Work with your team and agents seamlessly.\"],\"0kgOPB\":[\"See what shipped in \",[\"0\"]],\"wgNoIs\":[\"Select all\"],\"KP119R\":[\"Select your team\"],\"6JhL+3\":[\"Self-hosted\"],\"5CWy3T\":[\"Self-hosted means AC&T carries no vendor risk: no pricing model that can change, no platform that can disappear, no forced migration. The system is theirs.\"],\"5WmQ5O\":[\"Self-hosted means control\"],\"6QPYP7\":[\"self-hosted Twenty\"],\"GrBvv/\":[\"Self-hosting\"],\"YdhUoe\":[\"Self-hosting, now for rent!\"],\"NaHz2o\":[\"Selfhosting\"],\"2ZItPC\":[\"Share Twenty with your audience and help shape the future of the #1 open source CRM. We're looking for creators, educators, and community builders who want to showcase great software.\"],\"3VFzsh\":[\"Ship a product on Twenty\"],\"6lGV3K\":[\"Show less\"],\"mA6cL1\":[\"Sign up for Cloud in under a minute and start your 30-day trial. For larger rollouts, our 4-hour Onboarding Packs or certified partners get you live in 1–2 weeks.\"],\"AQK14J\":[\"Simple\"],\"V9dFwD\":[\"Sitemap\"],\"t6ComU\":[\"Skip the clunky UX that always comes with custom.\"],\"bUmSwL\":[\"Smart patterns, shortcuts, and layouts make everyday tasks faster and easier to execute.\"],\"7PyS12\":[\"Solutions Partner\"],\"e1+XW/\":[\"Solutions Partners\"],\"AVfp38\":[\"Some call this enterprise pricing. We prefer a CRM where API access, webhooks, and workflows don't show up as surprise add-ons.\"],\"/UwJ/B\":[\"Soon: earn revenue\"],\"B/mYo/\":[\"Source code access\"],\"vnS6Rf\":[\"SSO\"],\"uQl22y\":[\"Standard support\"],\"wxW2Sv\":[\"Start automating at huge scale!\"],\"F02wii\":[\"Start building, with Twenty\"],\"XO628f\":[\"Start for free\"],\"sBOFB0\":[\"Start your free trial today\\nwithout credit card.\"],\"FT+TW7\":[\"Stay in control with our\"],\"j9c8rb\":[\"Stay in Flow\"],\"bxgoJR\":[\"Step-by-step guides and playbooks to help your team get the most out of their workspace.\"],\"RMK92/\":[\"Stop fighting custom.\"],\"UCLArd\":[\"Stop settling for trade-offs.\"],\"lm4alq\":[\"structure\"],\"Sv4BSp\":[\"Subdomain (yourco.twenty.com)\"],\"H5/rZQ\":[\"Submit application\"],\"MDqQmP\":[\"Submitting…\"],\"XYLcNv\":[\"Support\"],\"3w+Aox\":[\"Table, Kanban, Calendar\"],\"9M02G5\":[\"Tailor record pages, menus, and views\"],\"JAKtcG\":[\"Talk to us\"],\"GWMpL3\":[\"Tasks & Activities\"],\"Ye3KKA\":[\"Team up with a Twenty expert\"],\"vsdUaL\":[\"Technical notes\"],\"8dX1MJ\":[\"Technology Partner\"],\"EeZsnh\":[\"Technology Partners\"],\"gNW5s8\":[\"Tell us about the custom solutions or integrations you plan to build.\"],\"1ZwmkP\":[\"template\"],\"mvP/25\":[\"Terms and Conditions\"],\"BEX1RA\":[\"Terms of Service | Twenty\"],\"iqG74V\":[\"Terms of Service for Twenty.com PBC, including use of Twenty.com, sub-domains, and related services.\"],\"dn9a7V\":[\"that\"],\"FjkPYg\":[\"That opened the door to something more powerful. Justin built a custom front end that pulls live data from those systems into a single view, tailored to each role. When a proposal is won, what used to require four separate people manually setting up instances across four different tools now happens in a single click, drawing on data collected in Twenty across the full opportunity lifecycle. It is another shift toward higher-value work for clients.\"],\"8S4psU\":[\"That works until you need to understand the business underneath. Which deals are stuck? Where are leads coming from? What is the close rate? With spreadsheets and a legacy custom CRM that could not keep up, those questions were nearly impossible to answer.\"],\"yWOGs1\":[\"that's quick to flex\"],\"rTqABu\":[\"The #1 open source CRM for modern teams. Modular, scalable, and built to fit your business.\"],\"G2IgPj\":[\"The bet is\"],\"NT2bSJ\":[\"The bet is paying off\"],\"Xmr/wH\":[\"the business\"],\"Wie492\":[\"The business development team finally had the CRM they had been asking for. Adoption came naturally: their data was already there when they logged in.\"],\"2kHqXc\":[\"The CEO had resisted bringing in a CRM for years. The business development team had no experience using one, and the licensing costs of well-known CRMs like Salesforce or HubSpot were hard to justify without any guarantee of adoption: CRMs are only as good as the maintenance of the data inside them.\"],\"5DpEh7\":[\"The CRM as a\"],\"2sfVd8\":[\"The CRM as a control hub\"],\"XlFsoH\":[\"The early bet on the architecture is holding, and upcoming AI features are expected to make it even more relevant.\"],\"kCUcnD\":[\"The flexibility is just amazing. Literally, there's nothing you cannot do. You can create objects, access everything through the API, pull notes and send them to the portal. Try doing that in HubSpot. No way. It's the true ability to build exactly what's actually needed.\"],\"MaHqAc\":[\"The flexibility is really what made the difference. Our needs evolve very fast. I discover a new need and in two clicks I can address it. That is a real advantage when you are moving quickly.\"],\"Q5iZ9Q\":[\"The flexibility to wire this together, without outside help and without fighting the platform, is what made it possible for a single person to stand up and maintain a connected stack across an entire consultancy.\"],\"l0eUFC\":[\"The full rollout landed in July 2025. Since then, Nine Dots built a Smart Assistant on top of the system, nudging agents with tasks, reminders, and on-demand market analysis. Some agents never open Twenty directly, yet they are powered by it, outperforming peers on manual processes alone. By Q1 2026, Homeseller had recorded its best sales quarter ever.\"],\"SiW1pC\":[\"the future of CRM\"],\"Uslh7V\":[\"The future of CRM is built,\"],\"sm10Rg\":[\"The Giant Monolith\"],\"nFB8I1\":[\"The In-house Burden\"],\"ZNzKXV\":[\"The key decision was not to build everything from scratch. Twenty covers the data model, permissions, authentication, and workflow engine, the parts that would have taken months to rebuild, so the team could focus on product-specific logic.\"],\"q1kKLq\":[\"The opportunity\"],\"ZgWQl4\":[\"the overhead\"],\"2NJdyz\":[\"The Problem.\"],\"Q8D9Lf\":[\"the product\"],\"3/wAFg\":[\"The result\"],\"nAWeyi\":[\"The result is a system that fits how AC&T already worked, instead of the other way around.\"],\"tfG/xw\":[\"The right\"],\"nbHA9N\":[\"The right foundation\"],\"omVXTg\":[\"The self-hosted setup means Alternative Partners owns the full stack: no vendor access to their data, no dependency on a SaaS pricing model, full control over how the system evolves. The migration was fast because of AI; the result is durable because the stack is open source.\"],\"1tN3qf\":[\"The shift\"],\"AnGOwN\":[\"The situation\"],\"u0uFxg\":[\"They are the real sales\"],\"53RXLB\":[\"They call it customer loyalty. We call it a very affectionate cage.\"],\"RhmZMe\":[\"They did not just replace a tool. They took back ownership of how their business runs.\"],\"5YzqyT\":[\"They evaluated Salesforce, Zoho, Pipedrive, and SuiteCRM. Each came with the same tradeoffs: too expensive, too rigid, or too generic, and none fixed the underlying problem. They were still renting a structure they did not control.\"],\"2y6W+f\":[\"to rule them all\"],\"5eFktS\":[\"Tool integration\"],\"xv7HSr\":[\"total per month with fixed cost\"],\"hmu++i\":[\"Track amount and close date\"],\"POzmFl\":[\"Track every release with changelogs, highlights and demos of the newest features.\"],\"yiYtW0\":[\"Track relationships, manage pipelines, and take action quickly with a CRM that feels intuitive from day one.\"],\"XzfkgA\":[\"tradeoffs\"],\"ccIZU2\":[\"Trust the n°1 CRM,\"],\"KtyN0b\":[\"trusted by\"],\"D5Kf4t\":[\"Tuesday your team learns that deals with a technical champion close 3x faster. Wednesday you add the field, wire up the scoring, adjust the workflow. By Thursday your agents are acting on it. That feedback loop is the edge. And it only works if the CRM is yours.\"],\"Sl2dv/\":[\"Twenty | #1 open source CRM\"],\"G4fKLP\":[\"Twenty Apps opens the door to building products, not just implementations. For example, we're developing a WhatsApp Business integration that any Twenty’s client could get. That's a recurring revenue stream we wouldn't have if we were just configuring someone else's platform.\"],\"mkTfZ1\":[\"Twenty as the\"],\"9gf3bc\":[\"Twenty as the API backbone\"],\"zj0CA+\":[\"Twenty as the API backbone of a go-to-market stack | Elevate Consulting\"],\"0qyMhk\":[\"Twenty gives technical teams the building blocks for a custom CRM that meets complex business needs and quickly adapts as the business evolves.\"],\"jVhq41\":[\"Twenty gives you the kind of flexibility that actually changes what you can offer your clients. The dev experience is clean, the APIs are open, and when something needs to be customized, you can just do it. There's no fighting the platform.\"],\"x/I85y\":[\"Twenty is not only where CRM data lives. It is the API backbone that makes the rest of the stack possible.\"],\"K+zzSQ\":[\"Twenty lets us build a CRM around the business and not the business around the CRM.\"],\"AxT+g4\":[\"Twenty makes it simple. It's clean, intuitive, and built to feel like Notion.\"],\"V1lV7B\":[\"Twenty team support\"],\"WN9ufk\":[\"Twenty?\"],\"EIU345\":[\"Two-factor authentication\"],\"uxuy4l\":[\"UI theme\"],\"jqzUyM\":[\"Unavailable\"],\"Hix/m6\":[\"Unified timeline (emails, events, tasks, notes, files)\"],\"NIuIk1\":[\"Unlimited\"],\"LF0tzk\":[\"Up to 50M automation credits/year\"],\"RDRePw\":[\"Up to 5M automation credits/month\"],\"Roaswv\":[\"User Guide\"],\"3XIgKU\":[\"User roles\"],\"BAzC1v\":[\"View types\"],\"mHtVst\":[\"Visualize your customers on a map!\"],\"CsHuOr\":[\"VP of Engineering\"],\"uRh799\":[\"VP of Engineering at W3villa Technologies\"],\"w2INie\":[\"VP of Engineering, W3villa Technologies\"],\"s0tU5h\":[\"W3Grads\"],\"R8BueT\":[\"W3villa built W3Grads (w3grads.com), an AI-powered mock interview platform for universities and training institutes, using Twenty as its operational backbone.\"],\"7hHsoV\":[\"W3villa built W3Grads for AI mock interviews at scale, with Twenty as the operational backbone.\"],\"QBOUnd\":[\"W3villa shipped W3Grads on Twenty for AI interviews, scoring, and institution-scale workflows without rebuilding CRM plumbing.\"],\"7q5Vjn\":[\"W3villa Technologies\"],\"ZabLEt\":[\"W3villa Technologies set out to solve it properly, not with a workaround, but with a product.\"],\"3v2ipf\":[\"W3villa used Twenty as a production-grade framework for the data model, permissions, authentication, and workflow engine they would otherwise have rebuilt themselves.\"],\"8CHatv\":[\"We could not submit your application. Please try again in a moment.\"],\"MjWeUE\":[\"We didn't want to patch over the problem. We wanted to build something institutions could rely on at scale, and that meant starting from a foundation solid enough to support the full complexity of what we had in mind.\"],\"gdnUik\":[\"We're building the #1 open source CRM, but we can't do it alone. Join our partner ecosystem and grow with us.\"],\"v1kQyJ\":[\"Webhooks\"],\"ZhnM/K\":[\"Webhooks (Change Data Capture)\"],\"jBX2tZ\":[\"Website or github link *\"],\"Z34FQq\":[\"What convinced Olivier was the flexibility of the platform and where it was headed. Even when initial needs were basic record-keeping, he still needed a custom data model with granular permissions to manage the wide range of NetZero activities. He also needed a system that could adapt quickly to a fast-iteration company.\"],\"phiBjc\":[\"What does Twenty cost?\"],\"WVQaGB\":[\"What is coming in April 2026 is what he has been waiting for: AI-assisted workflow creation, describing what he needs and iterating from there instead of building complex logic from scratch. For a founder who runs the CRM himself, that changes what is realistically possible.\"],\"iOAq7n\":[\"What is next\"],\"tWd6MT\":[\"What is planned is significant. NetZero has a data lake, online forms, and multiple internal systems that he wants to connect to Twenty. The pipes are there; the next step is automations that tie them together.\"],\"aYRqmz\":[\"What this means\"],\"1kfjxX\":[\"Whatever came next had to be something they could own.\"],\"cs6VpJ\":[\"When a student registers via QR at a campus event, the system assigns a plan, generates an interview session, and sends a link. The AI conducts the interview, scores the candidate, and classifies the result. Faculty see where each student stands without manually reviewing every session. Building and iterating on these workflows was faster with AI in the loop.\"],\"I/oPrY\":[\"When the channel is\"],\"JglRo/\":[\"When the channel is the business\"],\"ee4xKk\":[\"When the vendor\"],\"DajeYD\":[\"When the vendor pulled the plug\"],\"6LeUXo\":[\"When your CRM\"],\"X1gdzB\":[\"When your CRM is\"],\"Svuneu\":[\"When your CRM is the product: W3Grads on Twenty | W3villa Technologies\"],\"KRKrw8\":[\"Which partner program is right for you?\"],\"bGN8u+\":[\"While NetZero still runs a second CRM in parallel for WhatsApp-heavy operations with farmers in Brazil, they expect to migrate all of it to Twenty as features and the ecosystem grow. Already, their structured, multinational pipeline is powered by Twenty.\"],\"DJlSwA\":[\"Why\"],\"4sOVu+\":[\"Why Twenty\"],\"kwvfYA\":[\"Why Twenty | Twenty\"],\"aycITw\":[\"with\"],\"NI3KID\":[\"with no-code\"],\"OKVlnc\":[\"With Twenty, when a new need appears, he can address it himself: no developer required, no support ticket.\"],\"JRUWoH\":[\"with Twenty?\"],\"KAB1uP\":[\"with you\"],\"Lr53K3\":[\"without friction\"],\"OX5bbs\":[\"Work email *\"],\"woYYQq\":[\"Workflows\"],\"1ie9Dm\":[\"Workflows that\"],\"AzqmMJ\":[\"Workflows that actually get used\"],\"pmUArF\":[\"Workspace\"],\"zkWmBh\":[\"Yearly\"],\"l75CjT\":[\"Yes\"],\"bo6pUs\":[\"Yes, with our Apps framework. Scaffold an extension with `npx create-twenty-app` and ship custom objects, server-side logic functions, React components that render inside Twenty’s UI, AI skills and agents, views, and navigation, all in TypeScript, deployable to any workspace.\"],\"yIcBW6\":[\"Yes. Every Cloud workspace ships with a native MCP server. Connect your AI assistant via OAuth and it can read and write your CRM data in natural language.\"],\"8xYMYQ\":[\"Yes. Import your data via CSV, or use our API for 50,000+ records. Our partners can handle the full migration for you.\"],\"bnuv9n\":[\"Yes. Twenty is the #1 open source CRM on GitHub. You can self-host to fully own your infrastructure, or run it on our managed cloud for a zero-ops setup.\"],\"LJ3sTb\":[\"You don't buy your deployment pipeline off the shelf. You don't rent your data warehouse from a vendor who decides the schema. You build it, you own it, you iterate on it every week. CRM is going the same way. The teams that treat it as infrastructure they own will compound an advantage every quarter.\"],\"SfJVcK\":[\"Your checkout is complete. Follow the steps below to copy your license key into your Twenty instance.\"],\"krHgj1\":[\"Your enterprise key\"],\"+dxjCu\":[\"Your enterprise license has been activated successfully.\"],\"pEQiCY\":[\"Your name *\"],\"9Apzz7\":[\"your own story?\"],\"wHjFOw\":[\"Zero\"],\"xlFvlS\":[\"Zero manual work\"],\"ZvEoMq\":[\"Zero manual work at the core. Full automation. Built on Twenty.\"]}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/he-IL.ts b/packages/twenty-website-new/src/locales/generated/he-IL.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/he-IL.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/hu-HU.ts b/packages/twenty-website-new/src/locales/generated/hu-HU.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/hu-HU.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/it-IT.ts b/packages/twenty-website-new/src/locales/generated/it-IT.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/it-IT.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/ja-JP.ts b/packages/twenty-website-new/src/locales/generated/ja-JP.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/ja-JP.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/ko-KR.ts b/packages/twenty-website-new/src/locales/generated/ko-KR.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/ko-KR.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/nl-NL.ts b/packages/twenty-website-new/src/locales/generated/nl-NL.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/nl-NL.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/no-NO.ts b/packages/twenty-website-new/src/locales/generated/no-NO.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/no-NO.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/pl-PL.ts b/packages/twenty-website-new/src/locales/generated/pl-PL.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/pl-PL.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/pseudo-en.ts b/packages/twenty-website-new/src/locales/generated/pseudo-en.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/pseudo-en.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/pt-BR.ts b/packages/twenty-website-new/src/locales/generated/pt-BR.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/pt-BR.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/pt-PT.ts b/packages/twenty-website-new/src/locales/generated/pt-PT.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/pt-PT.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/ro-RO.ts b/packages/twenty-website-new/src/locales/generated/ro-RO.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/ro-RO.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/ru-RU.ts b/packages/twenty-website-new/src/locales/generated/ru-RU.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/ru-RU.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/sr-Cyrl.ts b/packages/twenty-website-new/src/locales/generated/sr-Cyrl.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/sr-Cyrl.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/sv-SE.ts b/packages/twenty-website-new/src/locales/generated/sv-SE.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/sv-SE.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/tr-TR.ts b/packages/twenty-website-new/src/locales/generated/tr-TR.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/tr-TR.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/uk-UA.ts b/packages/twenty-website-new/src/locales/generated/uk-UA.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/uk-UA.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/vi-VN.ts b/packages/twenty-website-new/src/locales/generated/vi-VN.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/vi-VN.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/zh-CN.ts b/packages/twenty-website-new/src/locales/generated/zh-CN.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/zh-CN.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/generated/zh-TW.ts b/packages/twenty-website-new/src/locales/generated/zh-TW.ts deleted file mode 100644 index 76934b72de0..00000000000 --- a/packages/twenty-website-new/src/locales/generated/zh-TW.ts +++ /dev/null @@ -1 +0,0 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{}")as Messages; \ No newline at end of file diff --git a/packages/twenty-website-new/src/locales/he-IL.po b/packages/twenty-website-new/src/locales/he-IL.po deleted file mode 100644 index bd1e413f9b8..00000000000 --- a/packages/twenty-website-new/src/locales/he-IL.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: he-IL\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/hu-HU.po b/packages/twenty-website-new/src/locales/hu-HU.po deleted file mode 100644 index 826b2ea2ea9..00000000000 --- a/packages/twenty-website-new/src/locales/hu-HU.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: hu-HU\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/it-IT.po b/packages/twenty-website-new/src/locales/it-IT.po deleted file mode 100644 index 2b9bff1d527..00000000000 --- a/packages/twenty-website-new/src/locales/it-IT.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: it-IT\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/ja-JP.po b/packages/twenty-website-new/src/locales/ja-JP.po deleted file mode 100644 index 2d565d87419..00000000000 --- a/packages/twenty-website-new/src/locales/ja-JP.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: ja-JP\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/ko-KR.po b/packages/twenty-website-new/src/locales/ko-KR.po deleted file mode 100644 index 5906001873f..00000000000 --- a/packages/twenty-website-new/src/locales/ko-KR.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: ko-KR\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/nl-NL.po b/packages/twenty-website-new/src/locales/nl-NL.po deleted file mode 100644 index 0b76627356b..00000000000 --- a/packages/twenty-website-new/src/locales/nl-NL.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: nl-NL\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/no-NO.po b/packages/twenty-website-new/src/locales/no-NO.po deleted file mode 100644 index 2f59e767425..00000000000 --- a/packages/twenty-website-new/src/locales/no-NO.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: no-NO\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/pl-PL.po b/packages/twenty-website-new/src/locales/pl-PL.po deleted file mode 100644 index 5b70b938269..00000000000 --- a/packages/twenty-website-new/src/locales/pl-PL.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: pl-PL\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/pseudo-en.po b/packages/twenty-website-new/src/locales/pseudo-en.po deleted file mode 100644 index a444967f915..00000000000 --- a/packages/twenty-website-new/src/locales/pseudo-en.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: pseudo-en\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/pt-BR.po b/packages/twenty-website-new/src/locales/pt-BR.po deleted file mode 100644 index 8e6a2c499ad..00000000000 --- a/packages/twenty-website-new/src/locales/pt-BR.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: pt-BR\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/pt-PT.po b/packages/twenty-website-new/src/locales/pt-PT.po deleted file mode 100644 index c7553d71090..00000000000 --- a/packages/twenty-website-new/src/locales/pt-PT.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: pt-PT\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/ro-RO.po b/packages/twenty-website-new/src/locales/ro-RO.po deleted file mode 100644 index 0fe8e95c50c..00000000000 --- a/packages/twenty-website-new/src/locales/ro-RO.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: ro-RO\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/ru-RU.po b/packages/twenty-website-new/src/locales/ru-RU.po deleted file mode 100644 index 6bee6044588..00000000000 --- a/packages/twenty-website-new/src/locales/ru-RU.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: ru-RU\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/sr-Cyrl.po b/packages/twenty-website-new/src/locales/sr-Cyrl.po deleted file mode 100644 index 064c7ac5d20..00000000000 --- a/packages/twenty-website-new/src/locales/sr-Cyrl.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: sr-Cyrl\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/sv-SE.po b/packages/twenty-website-new/src/locales/sv-SE.po deleted file mode 100644 index cb4e88c59e9..00000000000 --- a/packages/twenty-website-new/src/locales/sv-SE.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: sv-SE\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/tr-TR.po b/packages/twenty-website-new/src/locales/tr-TR.po deleted file mode 100644 index d9a369e4700..00000000000 --- a/packages/twenty-website-new/src/locales/tr-TR.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: tr-TR\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/uk-UA.po b/packages/twenty-website-new/src/locales/uk-UA.po deleted file mode 100644 index d7aac0f7f2c..00000000000 --- a/packages/twenty-website-new/src/locales/uk-UA.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: uk-UA\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/vi-VN.po b/packages/twenty-website-new/src/locales/vi-VN.po deleted file mode 100644 index ea973a14cb1..00000000000 --- a/packages/twenty-website-new/src/locales/vi-VN.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: vi-VN\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/zh-CN.po b/packages/twenty-website-new/src/locales/zh-CN.po deleted file mode 100644 index 6a53fa38833..00000000000 --- a/packages/twenty-website-new/src/locales/zh-CN.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: zh-CN\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/locales/zh-TW.po b/packages/twenty-website-new/src/locales/zh-TW.po deleted file mode 100644 index 33f14eb4017..00000000000 --- a/packages/twenty-website-new/src/locales/zh-TW.po +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"POT-Creation-Date: 2026-04-27 14:31+0500\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: @lingui/cli\n" -"Language: zh-TW\n" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Plural-Forms: \n" diff --git a/packages/twenty-website-new/src/sections/CaseStudy/components/CaseStudySectionNav.tsx b/packages/twenty-website-new/src/sections/CaseStudy/components/CaseStudySectionNav.tsx index 9e94d03549e..0a3becf6fde 100644 --- a/packages/twenty-website-new/src/sections/CaseStudy/components/CaseStudySectionNav.tsx +++ b/packages/twenty-website-new/src/sections/CaseStudy/components/CaseStudySectionNav.tsx @@ -1,7 +1,10 @@ 'use client'; +import { useRenderMessage } from '@/lib/i18n/use-render-message'; import { useScheduledOnScroll } from '@/lib/scroll'; import { theme } from '@/theme'; +import type { MessageDescriptor } from '@lingui/core'; +import { msg } from '@lingui/core/macro'; import { styled } from '@linaria/react'; import { IconBrandDiscord, @@ -11,6 +14,8 @@ import { } from '@tabler/icons-react'; import { useCallback, useState, type MouseEvent } from 'react'; +const DEFAULT_LABEL = msg`On this page`; + const HERO_ANCHOR_ID = 'case-study-hero'; const SECTION_ID_PREFIX = 'case-study-section'; @@ -141,10 +146,18 @@ const SocialLink = styled.a` `; type CaseStudySectionNavProps = { - items: string[]; + items: MessageDescriptor[]; + label?: MessageDescriptor; }; -export function CaseStudySectionNav({ items }: CaseStudySectionNavProps) { +export function CaseStudySectionNav({ + items, + label = DEFAULT_LABEL, +}: CaseStudySectionNavProps) { + const renderText = useRenderMessage(); + const resolvedLabel = renderText(label); + const resolvedItems = items.map(renderText); + const [visible, setVisible] = useState(false); const [activeIndex, setActiveIndex] = useState(0); @@ -202,13 +215,13 @@ export function CaseStudySectionNav({ items }: CaseStudySectionNavProps) { return ( - On this page + {resolvedLabel} - - {items.map((item, index) => ( + + {resolvedItems.map((item, index) => ( string; }; -export function Hero({ hero, dashColor, hoverDashColor }: HeroProps) { +export function Hero({ + hero, + dashColor, + hoverDashColor, + renderText, +}: HeroProps) { const ClientIcon = CLIENT_ICONS[hero.clientIcon]; const authorInitials = hero.author .split(' ') @@ -255,7 +263,13 @@ export function Hero({ hero, dashColor, hoverDashColor }: HeroProps) { - + @@ -275,13 +289,13 @@ export function Hero({ hero, dashColor, hoverDashColor }: HeroProps) { - Back + {renderText(msg`Back`)} {hero.author} {hero.authorRole ? ( - {hero.authorRole} + {renderText(hero.authorRole)} ) : null} diff --git a/packages/twenty-website-new/src/sections/CaseStudy/components/Highlights.tsx b/packages/twenty-website-new/src/sections/CaseStudy/components/Highlights.tsx index 3b489c74046..56753666ae9 100644 --- a/packages/twenty-website-new/src/sections/CaseStudy/components/Highlights.tsx +++ b/packages/twenty-website-new/src/sections/CaseStudy/components/Highlights.tsx @@ -1,5 +1,7 @@ import { Container } from '@/design-system/components'; +import type { MessageDescriptor } from '@lingui/core'; import { theme } from '@/theme'; +import { msg } from '@lingui/core/macro'; import { styled } from '@linaria/react'; const Section = styled.section` @@ -111,14 +113,15 @@ const Label = styled.span` `; type HighlightsProps = { - industry?: string; - kpis?: { value: string; label: string }[]; + industry?: MessageDescriptor; + kpis?: { value: MessageDescriptor; label: MessageDescriptor }[]; + renderText: (descriptor: MessageDescriptor) => string; }; -export function Highlights({ industry, kpis }: HighlightsProps) { - const cells: { value: string; label: string }[] = []; +export function Highlights({ industry, kpis, renderText }: HighlightsProps) { + const cells: { value: MessageDescriptor; label: MessageDescriptor }[] = []; if (industry) { - cells.push({ value: industry, label: 'Industry' }); + cells.push({ value: industry, label: msg`Industry` }); } if (kpis) { for (const kpi of kpis) { @@ -136,9 +139,9 @@ export function Highlights({ industry, kpis }: HighlightsProps) { {cells.map((cell, index) => ( - - {cell.value} - + + {renderText(cell.value)} + ))} diff --git a/packages/twenty-website-new/src/sections/CaseStudy/components/TextBlock.tsx b/packages/twenty-website-new/src/sections/CaseStudy/components/TextBlock.tsx index b95348a2bd6..e3663f013ff 100644 --- a/packages/twenty-website-new/src/sections/CaseStudy/components/TextBlock.tsx +++ b/packages/twenty-website-new/src/sections/CaseStudy/components/TextBlock.tsx @@ -6,6 +6,7 @@ import { Heading, } from '@/design-system/components'; import { theme } from '@/theme'; +import type { MessageDescriptor } from '@lingui/core'; import { styled } from '@linaria/react'; const Section = styled.section` @@ -124,12 +125,14 @@ function parseCallout(raw: string): { type TextBlockProps = { block: CaseStudyTextBlock; isLast?: boolean; + renderText: (descriptor: MessageDescriptor) => string; sectionId?: string; }; export function TextBlock({ block, isLast = false, + renderText, sectionId, }: TextBlockProps) { return ( @@ -140,10 +143,17 @@ export function TextBlock({ )} - + {block.paragraphs.map((paragraph, index) => ( @@ -151,6 +161,7 @@ export function TextBlock({ key={index} body={{ text: paragraph }} family="sans" + renderText={renderText} size="md" variant="body-paragraph" weight="regular" diff --git a/packages/twenty-website-new/src/sections/CaseStudyCatalog/components/Card.tsx b/packages/twenty-website-new/src/sections/CaseStudyCatalog/components/Card.tsx index beb2bf84e8a..df2432707a2 100644 --- a/packages/twenty-website-new/src/sections/CaseStudyCatalog/components/Card.tsx +++ b/packages/twenty-website-new/src/sections/CaseStudyCatalog/components/Card.tsx @@ -3,29 +3,18 @@ import { ArrowRightIcon, CLIENT_ICONS } from '@/icons'; import { LocalizedLink } from '@/lib/i18n'; import { CustomerCasesCover } from '@/sections/CaseStudyCatalog/visuals/CustomerCasesCover'; import { theme } from '@/theme'; +import type { MessageDescriptor } from '@lingui/core'; import { styled } from '@linaria/react'; import Image from 'next/image'; -import { type ComponentProps } from 'react'; +import type { CSSProperties } from 'react'; type CardVariant = 'default' | 'large'; -type CardLinkBaseProps = ComponentProps & { - $cardIndex: number; - variant: CardVariant; +type CaseStudyCardStyle = CSSProperties & { + '--case-study-card-index': number; }; -function CardLinkBase({ - $cardIndex: _cardIndex, - variant: _variant, - ...linkProps -}: CardLinkBaseProps) { - return ; -} - -const CardLink = styled(CardLinkBase)<{ - variant: CardVariant; - $cardIndex: number; -}>` +const CardLink = styled(LocalizedLink)` @keyframes caseStudyCardEnter { from { opacity: 0; @@ -38,14 +27,13 @@ const CardLink = styled(CardLinkBase)<{ } animation: caseStudyCardEnter 700ms cubic-bezier(0.22, 1, 0.36, 1) both; - animation-delay: calc(${({ $cardIndex }) => $cardIndex} * 90ms + 180ms); + animation-delay: calc(var(--case-study-card-index) * 90ms + 180ms); background-color: ${theme.colors.primary.background[100]}; border: 1px solid ${theme.colors.primary.border[10]}; border-radius: ${theme.radius(2)}; color: inherit; display: flex; flex-direction: column; - grid-column: ${({ variant }) => (variant === 'large' ? '1 / -1' : 'auto')}; overflow: hidden; text-decoration: none; transition: @@ -54,9 +42,14 @@ const CardLink = styled(CardLinkBase)<{ transform 0.25s ease; will-change: transform; + &[data-card-variant='large'] { + grid-column: 1 / -1; + } + @media (min-width: ${theme.breakpoints.md}px) { - flex-direction: ${({ variant }) => - variant === 'large' ? 'row' : 'column'}; + &[data-card-variant='large'] { + flex-direction: row; + } } &:hover { @@ -427,22 +420,6 @@ const ReadIconButton = styled.span` } `; -const Dot = styled.span` - background-color: ${theme.colors.primary.text[40]}; - border-radius: 50%; - flex-shrink: 0; - height: 3px; - width: 3px; -`; - -const DateLabel = styled.span` - color: ${theme.colors.primary.text[40]}; - font-family: ${theme.font.family.sans}; - font-size: ${theme.font.size(3.5)}; - font-weight: ${theme.font.weight.regular}; - line-height: ${theme.lineHeight(4)}; -`; - const IndustryLabel = styled.span` color: ${theme.colors.primary.text[60]}; font-family: ${theme.font.family.mono}; @@ -470,7 +447,8 @@ const LARGE_LOGO_SCALE = 1.4; type CardProps = { entry: CaseStudyCatalogEntry; index?: number; - variant?: 'default' | 'large'; + renderText: (descriptor: MessageDescriptor) => string; + variant?: CardVariant; dashColor?: string; hoverDashColor?: string; }; @@ -478,6 +456,7 @@ type CardProps = { export function Card({ entry, index = 0, + renderText, variant = 'default', dashColor, hoverDashColor, @@ -495,9 +474,12 @@ export function Card({ const isLarge = variant === 'large'; const hasQuote = isLarge && entry.quote; const kpiCount = entry.kpis.length; + const cardStyle: CaseStudyCardStyle = { + '--case-study-card-index': index, + }; return ( - + {coverImageSrc ? ( @@ -520,27 +502,29 @@ export function Card({ - {entry.industry} + {renderText(entry.industry)} {entry.hero.title.map((segment, segmentIndex) => segment.fontFamily === 'sans' ? ( - <TitleSans key={segmentIndex}>{segment.text}</TitleSans> + <TitleSans key={segmentIndex}> + {renderText(segment.text)} + </TitleSans> ) : ( - <span key={segmentIndex}>{segment.text}</span> + <span key={segmentIndex}>{renderText(segment.text)}</span> ), )} {hasQuote && entry.quote ? ( - “{entry.quote.text}” + “{renderText(entry.quote.text)}” ) : !isLarge ? ( - {entry.catalogCard.summary} + {renderText(entry.catalogCard.summary)} ) : null} {isLarge && kpiCount > 0 ? ( {entry.kpis.map((kpi, kpiIndex) => ( - - {kpi.value} - {kpi.label} + + {renderText(kpi.value)} + {renderText(kpi.label)} ))} @@ -549,11 +533,11 @@ export function Card({ {!isLarge && kpiCount > 0 ? ( - {entry.kpis.map((kpi) => ( - - {kpi.value} + {entry.kpis.map((kpi, kpiIndex) => ( + + {renderText(kpi.value)} - {kpi.label} + {renderText(kpi.label)} ))} @@ -577,12 +561,16 @@ export function Card({ {hasQuote && entry.quote ? ( {entry.quote.author}{' '} - · {entry.quote.role} + + · {renderText(entry.quote.role)} + ) : ( {entry.hero.author}{' '} - · {entry.authorRole} + + · {renderText(entry.authorRole)} + )} diff --git a/packages/twenty-website-new/src/sections/CaseStudyCatalog/components/Grid.tsx b/packages/twenty-website-new/src/sections/CaseStudyCatalog/components/Grid.tsx index 395f18eed04..36e6c51ee7e 100644 --- a/packages/twenty-website-new/src/sections/CaseStudyCatalog/components/Grid.tsx +++ b/packages/twenty-website-new/src/sections/CaseStudyCatalog/components/Grid.tsx @@ -6,6 +6,7 @@ import { Container } from '@/design-system/components'; import { PlusIcon } from '@/icons'; import { Card } from '@/sections/CaseStudyCatalog/components/Card'; import { theme } from '@/theme'; +import type { MessageDescriptor } from '@lingui/core'; import { styled } from '@linaria/react'; import type { ReactNode } from 'react'; @@ -140,6 +141,7 @@ type GridProps = { compactTop?: boolean; entries: readonly CaseStudyCatalogEntry[]; intro?: ReactNode; + renderText: (descriptor: MessageDescriptor) => string; }; export function Grid({ @@ -147,6 +149,7 @@ export function Grid({ compactTop = false, entries, intro, + renderText, }: GridProps) { const lastIndex = entries.length - 1; @@ -200,6 +203,7 @@ export function Grid({ hoverDashColor={palette.hoverDashColor} index={index} key={entry.href} + renderText={renderText} variant={isLarge ? 'large' : 'default'} /> ); diff --git a/packages/twenty-website-new/src/sections/CaseStudyCatalog/components/Promo.tsx b/packages/twenty-website-new/src/sections/CaseStudyCatalog/components/Promo.tsx index 8c37a757b16..24f059b79ae 100644 --- a/packages/twenty-website-new/src/sections/CaseStudyCatalog/components/Promo.tsx +++ b/packages/twenty-website-new/src/sections/CaseStudyCatalog/components/Promo.tsx @@ -1,14 +1,11 @@ +import { msg } from '@lingui/core/macro'; import type { CaseStudyCatalogEntry } from '@/lib/customers'; -import { - Body, - Container, - Eyebrow, - Heading, - LinkButton, -} from '@/design-system/components'; +import { Body, Container, Eyebrow, Heading } from '@/design-system/components'; import { PlusIcon, UsersIcon } from '@/icons'; +import { LocalizedLinkButton } from '@/lib/i18n/LocalizedLinkButton'; import { PromoMic } from '@/sections/CaseStudyCatalog/visuals/PromoMic'; import { theme } from '@/theme'; +import type { MessageDescriptor } from '@lingui/core'; import { styled } from '@linaria/react'; const CORNER_SIZE = 14; @@ -19,17 +16,17 @@ const TRUSTED_BY_BOTTOM_PADDING = 12; const TRUSTED_BY_BOTTOM_PADDING_DESKTOP = 16; const EYEBROW = { - text: 'Customer Stories', + text: msg`Customer Stories`, fontFamily: 'mono' as const, }; const HEADING = [ - { text: 'How teams ', fontFamily: 'serif' as const }, - { text: 'built with Twenty', fontFamily: 'sans' as const, newLine: true }, + { text: msg`How teams`, fontFamily: 'serif' as const }, + { text: msg`built with Twenty`, fontFamily: 'sans' as const, newLine: true }, ]; const BODY = { - text: 'Meet the teams who shaped Twenty into their own CRM with self-hosted deployments, AI-assisted workflows, and API-first product stacks.', + text: msg`Meet the teams who shaped Twenty into their own CRM with self-hosted deployments, AI-assisted workflows, and API-first product stacks.`, }; const Section = styled.section` @@ -259,15 +256,17 @@ const CtaRow = styled.div` type PromoProps = { entries: readonly CaseStudyCatalogEntry[]; ctaHref?: string; - ctaLabel?: string; + ctaLabel?: MessageDescriptor; compactTop?: boolean; + renderText: (descriptor: MessageDescriptor) => string; }; export function Promo({ entries, ctaHref = '/customers', - ctaLabel = 'Explore customer stories', + ctaLabel = msg`Explore customer stories`, compactTop = false, + renderText, }: PromoProps) { return (
    @@ -337,15 +336,20 @@ export function Promo({ heading={EYEBROW} markerHeight={6} markerWidth={14} + renderText={renderText} /> - - + + - diff --git a/packages/twenty-website-new/src/sections/Editorial/components/Body.tsx b/packages/twenty-website-new/src/sections/Editorial/components/Body.tsx index 496c571ff1d..c38aeb5c6ff 100644 --- a/packages/twenty-website-new/src/sections/Editorial/components/Body.tsx +++ b/packages/twenty-website-new/src/sections/Editorial/components/Body.tsx @@ -1,7 +1,9 @@ import { Body as BaseBody } from '@/design-system/components'; -import type { BodyType } from '@/design-system/components/Body'; +import type { MessageBody } from '@/lib/i18n/message-body'; import { theme } from '@/theme'; +import type { MessageDescriptor } from '@lingui/core'; import { styled } from '@linaria/react'; +import type { ReactNode } from 'react'; const BodyParagraph = styled.div<{ $color: string }>` --body-paragraph-color: ${({ $color }) => $color}; @@ -44,7 +46,7 @@ const SingleColumnBody = styled.div` `; type EditorialBodyProps = { - body: BodyType | BodyType[]; + body: MessageBody | MessageBody[]; color: string; layout: | 'centered' @@ -52,9 +54,10 @@ type EditorialBodyProps = { | 'two-column' | 'two-column-left' | 'two-column-right'; + renderText: (descriptor: MessageDescriptor) => ReactNode; }; -export function Body({ body, color, layout }: EditorialBodyProps) { +export function Body({ body, color, layout, renderText }: EditorialBodyProps) { const paragraphs = Array.isArray(body) ? ( body.map((item, index) => ( @@ -62,6 +65,7 @@ export function Body({ body, color, layout }: EditorialBodyProps) { as="p" body={item} family="sans" + renderText={renderText} size="md" variant="body-paragraph" weight="regular" @@ -74,6 +78,7 @@ export function Body({ body, color, layout }: EditorialBodyProps) { as="p" body={body} family="sans" + renderText={renderText} size="md" variant="body-paragraph" weight="regular" diff --git a/packages/twenty-website-new/src/sections/Editorial/components/Eyebrow.tsx b/packages/twenty-website-new/src/sections/Editorial/components/Eyebrow.tsx index f6ea6cda4bb..545e5a9d55b 100644 --- a/packages/twenty-website-new/src/sections/Editorial/components/Eyebrow.tsx +++ b/packages/twenty-website-new/src/sections/Editorial/components/Eyebrow.tsx @@ -1,11 +1,24 @@ import { Eyebrow as BaseEyebrow } from '@/design-system/components'; -import type { EyebrowType } from '@/design-system/components/Eyebrow'; +import type { MessageEyebrow } from '@/lib/i18n/message-eyebrow'; +import type { MessageDescriptor } from '@lingui/core'; +import type { ReactNode } from 'react'; type EditorialEyebrowProps = { colorScheme: 'primary' | 'secondary'; - eyebrow: EyebrowType; + eyebrow: MessageEyebrow; + renderText: (descriptor: MessageDescriptor) => ReactNode; }; -export function Eyebrow({ colorScheme, eyebrow }: EditorialEyebrowProps) { - return ; +export function Eyebrow({ + colorScheme, + eyebrow, + renderText, +}: EditorialEyebrowProps) { + return ( + + ); } diff --git a/packages/twenty-website-new/src/sections/Editorial/components/Heading.tsx b/packages/twenty-website-new/src/sections/Editorial/components/Heading.tsx index 8cf5f92ea36..5757b64eef7 100644 --- a/packages/twenty-website-new/src/sections/Editorial/components/Heading.tsx +++ b/packages/twenty-website-new/src/sections/Editorial/components/Heading.tsx @@ -1,6 +1,6 @@ import { Heading as BaseHeading } from '@/design-system/components'; -import type { HeadingType } from '@/design-system/components/Heading'; import { styled } from '@linaria/react'; +import type { ReactNode } from 'react'; const HeadingWrap = styled.div` max-width: var(--editorial-heading-max-width, 617px); @@ -9,13 +9,15 @@ const HeadingWrap = styled.div` `; type EditorialHeadingProps = { - segments: HeadingType | HeadingType[]; + children: ReactNode; }; -export function Heading({ segments }: EditorialHeadingProps) { +export function Heading({ children }: EditorialHeadingProps) { return ( - + + {children} + ); } diff --git a/packages/twenty-website-new/src/sections/Editorial/types/EditorialData.ts b/packages/twenty-website-new/src/sections/Editorial/types/EditorialData.ts index 39491f58665..6a24cd11fdd 100644 --- a/packages/twenty-website-new/src/sections/Editorial/types/EditorialData.ts +++ b/packages/twenty-website-new/src/sections/Editorial/types/EditorialData.ts @@ -1,9 +1,7 @@ -import { BodyType } from '@/design-system/components/Body'; -import { EyebrowType } from '@/design-system/components/Eyebrow'; -import { HeadingType } from '@/design-system/components/Heading'; +import type { MessageBody } from '@/lib/i18n/message-body'; +import type { MessageEyebrow } from '@/lib/i18n/message-eyebrow'; export type EditorialDataType = { - eyebrow?: EyebrowType; - heading?: HeadingType | HeadingType[]; - body: BodyType | BodyType[]; + eyebrow?: MessageEyebrow; + body: MessageBody | MessageBody[]; }; diff --git a/packages/twenty-website-new/src/sections/EngagementBand/components/Body.tsx b/packages/twenty-website-new/src/sections/EngagementBand/components/Body.tsx index 2713b34d3d8..32e77a1670c 100644 --- a/packages/twenty-website-new/src/sections/EngagementBand/components/Body.tsx +++ b/packages/twenty-website-new/src/sections/EngagementBand/components/Body.tsx @@ -1,6 +1,8 @@ import { Body as BaseBody } from '@/design-system/components'; -import type { BodyType } from '@/design-system/components/Body'; +import type { MessageBody } from '@/lib/i18n/message-body'; +import type { MessageDescriptor } from '@lingui/core'; import { styled } from '@linaria/react'; +import type { ReactNode } from 'react'; const StyledBody = styled(BaseBody)` --body-sm-color: color-mix(in srgb, currentColor 90%, transparent); @@ -8,9 +10,17 @@ const StyledBody = styled(BaseBody)` `; type BodyProps = { - body: BodyType; + body: MessageBody; + renderText: (descriptor: MessageDescriptor) => ReactNode; }; -export function Body({ body }: BodyProps) { - return ; +export function Body({ body, renderText }: BodyProps) { + return ( + + ); } diff --git a/packages/twenty-website-new/src/sections/EngagementBand/components/Heading.tsx b/packages/twenty-website-new/src/sections/EngagementBand/components/Heading.tsx index f406ca23b35..a267944db8e 100644 --- a/packages/twenty-website-new/src/sections/EngagementBand/components/Heading.tsx +++ b/packages/twenty-website-new/src/sections/EngagementBand/components/Heading.tsx @@ -1,8 +1,10 @@ import { Heading as BaseHeading } from '@/design-system/components'; import type { HeadingSize } from '@/design-system/components/Heading'; -import type { HeadingType } from '@/design-system/components/Heading'; +import type { MessageHeadingSegment } from '@/lib/i18n/message-heading-segment'; import { theme } from '@/theme'; +import type { MessageDescriptor } from '@lingui/core'; import { styled } from '@linaria/react'; +import type { ReactNode } from 'react'; const StyledHeading = styled(BaseHeading)` white-space: pre-line; @@ -13,10 +15,18 @@ const StyledHeading = styled(BaseHeading)` `; type HeadingProps = { - segments: HeadingType; + segments: MessageHeadingSegment; + renderText: (descriptor: MessageDescriptor) => ReactNode; size?: HeadingSize; }; -export function Heading({ segments, size = 'sm' }: HeadingProps) { - return ; +export function Heading({ segments, renderText, size = 'sm' }: HeadingProps) { + return ( + + ); } diff --git a/packages/twenty-website-new/src/sections/EngagementBand/types/EngagementBandData.ts b/packages/twenty-website-new/src/sections/EngagementBand/types/EngagementBandData.ts deleted file mode 100644 index e11faf234b7..00000000000 --- a/packages/twenty-website-new/src/sections/EngagementBand/types/EngagementBandData.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { BodyType } from '@/design-system/components/Body'; -import type { HeadingType } from '@/design-system/components/Heading'; - -export type EngagementBandDataType = { - body: BodyType; - heading: HeadingType; -}; diff --git a/packages/twenty-website-new/src/sections/EngagementBand/types/index.ts b/packages/twenty-website-new/src/sections/EngagementBand/types/index.ts deleted file mode 100644 index 1c6d688860b..00000000000 --- a/packages/twenty-website-new/src/sections/EngagementBand/types/index.ts +++ /dev/null @@ -1 +0,0 @@ -export type { EngagementBandDataType } from './EngagementBandData'; diff --git a/packages/twenty-website-new/src/sections/Faq/components/Heading.tsx b/packages/twenty-website-new/src/sections/Faq/components/Heading.tsx index d8933a5a46c..1b585524de5 100644 --- a/packages/twenty-website-new/src/sections/Faq/components/Heading.tsx +++ b/packages/twenty-website-new/src/sections/Faq/components/Heading.tsx @@ -1,7 +1,7 @@ import { Heading as BaseHeading } from '@/design-system/components'; -import type { HeadingType } from '@/design-system/components/Heading'; import { theme } from '@/theme'; import { css } from '@linaria/core'; +import type { ReactNode } from 'react'; const faqHeadingClassName = css` white-space: pre-line; @@ -12,17 +12,18 @@ const faqHeadingClassName = css` `; type HeadingProps = { - segments: HeadingType[]; + children: ReactNode; }; -export function Heading({ segments }: HeadingProps) { +export function Heading({ children }: HeadingProps) { return ( + > + {children} + ); } diff --git a/packages/twenty-website-new/src/sections/Faq/components/Item.tsx b/packages/twenty-website-new/src/sections/Faq/components/Item.tsx index 2410cddcc90..000af100f8b 100644 --- a/packages/twenty-website-new/src/sections/Faq/components/Item.tsx +++ b/packages/twenty-website-new/src/sections/Faq/components/Item.tsx @@ -7,6 +7,7 @@ import { RectangleOutlineIcon, } from '@/icons'; import type { FaqQuestionType } from '@/sections/Faq/types/FaqQuestion'; +import { useRenderMessage } from '@/lib/i18n/use-render-message'; import { theme } from '@/theme'; import { Accordion as BaseAccordion } from '@base-ui/react/accordion'; import { styled } from '@linaria/react'; @@ -207,6 +208,8 @@ type ItemProps = { }; export function Item({ question, value }: ItemProps) { + const renderText = useRenderMessage(); + return ( }> }> @@ -226,7 +229,7 @@ export function Item({ question, value }: ItemProps) { - {question.question.text} + {renderText(question.question.text)} @@ -249,7 +252,7 @@ export function Item({ question, value }: ItemProps) { } keepMounted> - {question.answer.text} + {renderText(question.answer.text)} diff --git a/packages/twenty-website-new/src/sections/Faq/data.ts b/packages/twenty-website-new/src/sections/Faq/data.ts index bae1ce46586..c32a65f208f 100644 --- a/packages/twenty-website-new/src/sections/Faq/data.ts +++ b/packages/twenty-website-new/src/sections/Faq/data.ts @@ -1,84 +1,75 @@ +import { msg } from '@lingui/core/macro'; import type { FaqDataType } from '@/sections/Faq/types'; export const FAQ_DATA: FaqDataType = { eyebrow: { heading: { - text: 'Any Questions?', + text: msg`Any Questions?`, fontFamily: 'sans', }, }, - heading: [ - { - text: 'Stop fighting custom.\n', - fontFamily: 'serif', - }, - { - text: ' Start building, with Twenty', - fontFamily: 'sans', - }, - ], questions: [ { question: { - text: 'Is Twenty really open-source?', + text: msg`Is Twenty really open-source?`, fontFamily: 'sans', }, answer: { - text: 'Yes. Twenty is the #1 open source CRM on GitHub. You can self-host to fully own your infrastructure, or run it on our managed cloud for a zero-ops setup.', + text: msg`Yes. Twenty is the #1 open source CRM on GitHub. You can self-host to fully own your infrastructure, or run it on our managed cloud for a zero-ops setup.`, }, }, { question: { - text: 'How long does it take to get started?', + text: msg`How long does it take to get started?`, fontFamily: 'sans', }, answer: { - text: 'Sign up for Cloud in under a minute and start your 30-day trial. For larger rollouts, our 4-hour Onboarding Packs or certified partners get you live in 1–2 weeks.', + text: msg`Sign up for Cloud in under a minute and start your 30-day trial. For larger rollouts, our 4-hour Onboarding Packs or certified partners get you live in 1–2 weeks.`, }, }, { question: { - text: 'Can I migrate from Salesforce or HubSpot?', + text: msg`Can I migrate from Salesforce or HubSpot?`, fontFamily: 'sans', }, answer: { - text: 'Yes. Import your data via CSV, or use our API for 50,000+ records. Our partners can handle the full migration for you.', + text: msg`Yes. Import your data via CSV, or use our API for 50,000+ records. Our partners can handle the full migration for you.`, }, }, { question: { - text: 'Do I need a developer to customize Twenty?', + text: msg`Do I need a developer to customize Twenty?`, fontFamily: 'sans', }, answer: { - text: 'No. Build custom objects, fields, views, and no-code workflows straight from Settings. Unlimited, no extra charge.', + text: msg`No. Build custom objects, fields, views, and no-code workflows straight from Settings. Unlimited, no extra charge.`, }, }, { question: { - text: 'Can developers extend Twenty with code?', + text: msg`Can developers extend Twenty with code?`, fontFamily: 'sans', }, answer: { - text: 'Yes, with our Apps framework. Scaffold an extension with `npx create-twenty-app` and ship custom objects, server-side logic functions, React components that render inside Twenty\u2019s UI, AI skills and agents, views, and navigation, all in TypeScript, deployable to any workspace.', + text: msg`Yes, with our Apps framework. Scaffold an extension with \`npx create-twenty-app\` and ship custom objects, server-side logic functions, React components that render inside Twenty\u2019s UI, AI skills and agents, views, and navigation, all in TypeScript, deployable to any workspace.`, }, }, { question: { - text: 'Does Twenty work with Claude, ChatGPT, and Cursor?', + text: msg`Does Twenty work with Claude, ChatGPT, and Cursor?`, fontFamily: 'sans', }, answer: { - text: 'Yes. Every Cloud workspace ships with a native MCP server. Connect your AI assistant via OAuth and it can read and write your CRM data in natural language.', + text: msg`Yes. Every Cloud workspace ships with a native MCP server. Connect your AI assistant via OAuth and it can read and write your CRM data in natural language.`, }, }, { question: { - text: 'What does Twenty cost?', + text: msg`What does Twenty cost?`, fontFamily: 'sans', }, answer: { - text: 'Cloud Pro is $9/user/month (yearly). Organization is $19/user/month and unlocks SSO and row-level permissions for teams that need finer access control.', + text: msg`Cloud Pro is $9/user/month (yearly). Organization is $19/user/month and unlocks SSO and row-level permissions for teams that need finer access control.`, }, }, ], diff --git a/packages/twenty-website-new/src/sections/Faq/types/FaqData.ts b/packages/twenty-website-new/src/sections/Faq/types/FaqData.ts index eefe8fd54dc..5b2b1f2ccff 100644 --- a/packages/twenty-website-new/src/sections/Faq/types/FaqData.ts +++ b/packages/twenty-website-new/src/sections/Faq/types/FaqData.ts @@ -1,9 +1,7 @@ -import { type EyebrowType } from '@/design-system/components/Eyebrow'; -import { type HeadingType } from '@/design-system/components/Heading'; +import type { MessageEyebrow } from '@/lib/i18n/message-eyebrow'; import { type FaqQuestionType } from '@/sections/Faq/types/FaqQuestion'; export type FaqDataType = { - eyebrow: EyebrowType; - heading: HeadingType[]; + eyebrow: MessageEyebrow; questions: FaqQuestionType[]; }; diff --git a/packages/twenty-website-new/src/sections/Faq/types/FaqQuestion.ts b/packages/twenty-website-new/src/sections/Faq/types/FaqQuestion.ts index a7c7931d0b8..4bcf494bd05 100644 --- a/packages/twenty-website-new/src/sections/Faq/types/FaqQuestion.ts +++ b/packages/twenty-website-new/src/sections/Faq/types/FaqQuestion.ts @@ -1,7 +1,7 @@ -import { BodyType } from '@/design-system/components/Body'; -import { HeadingType } from '@/design-system/components/Heading'; +import type { MessageBody } from '@/lib/i18n/message-body'; +import type { MessageHeadingSegment } from '@/lib/i18n/message-heading-segment'; export type FaqQuestionType = { - question: HeadingType; - answer: BodyType; + question: MessageHeadingSegment; + answer: MessageBody; }; diff --git a/packages/twenty-website-new/src/sections/Feature/components/Tile/Tile.tsx b/packages/twenty-website-new/src/sections/Feature/components/Tile/Tile.tsx index 4cd06f50632..377ee7c3a85 100644 --- a/packages/twenty-website-new/src/sections/Feature/components/Tile/Tile.tsx +++ b/packages/twenty-website-new/src/sections/Feature/components/Tile/Tile.tsx @@ -3,6 +3,7 @@ import { styled } from '@linaria/react'; import type { ImageType } from '@/design-system/components/Image'; import type { FeatureTileType } from '@/sections/Feature/types/FeatureTile'; import { theme } from '@/theme'; +import type { MessageDescriptor } from '@lingui/core'; import { TileContent } from './TileContent'; import { TileVisual } from './TileVisual'; @@ -22,14 +23,15 @@ const TileRoot = styled.article` type TileProps = { index: number; mask: ImageType; + renderText: (descriptor: MessageDescriptor) => string; tile: FeatureTileType; }; -export function Tile({ index, mask, tile }: TileProps) { +export function Tile({ index, mask, renderText, tile }: TileProps) { return ( - + ); } diff --git a/packages/twenty-website-new/src/sections/Feature/components/Tile/TileContent.tsx b/packages/twenty-website-new/src/sections/Feature/components/Tile/TileContent.tsx index b0ba1a57005..f8e0bd8e588 100644 --- a/packages/twenty-website-new/src/sections/Feature/components/Tile/TileContent.tsx +++ b/packages/twenty-website-new/src/sections/Feature/components/Tile/TileContent.tsx @@ -4,6 +4,7 @@ import { Body, Heading } from '@/design-system/components'; import { INFORMATIVE_ICONS } from '@/icons'; import type { FeatureTileType } from '@/sections/Feature/types/FeatureTile'; import { theme } from '@/theme'; +import type { MessageDescriptor } from '@lingui/core'; const Content = styled.div` display: grid; @@ -52,10 +53,11 @@ const BulletItem = styled.li` `; type TileContentProps = { + renderText: (descriptor: MessageDescriptor) => string; tile: FeatureTileType; }; -export function TileContent({ tile }: TileContentProps) { +export function TileContent({ renderText, tile }: TileContentProps) { const Icon = INFORMATIVE_ICONS[tile.icon]; return ( @@ -67,13 +69,25 @@ export function TileContent({ tile }: TileContentProps) { ) : null} - + {tile.bullets.map((bullet, bulletIndex) => ( - + ))} diff --git a/packages/twenty-website-new/src/sections/Feature/components/Tiles.tsx b/packages/twenty-website-new/src/sections/Feature/components/Tiles.tsx index 1df99baa949..1693214d7e8 100644 --- a/packages/twenty-website-new/src/sections/Feature/components/Tiles.tsx +++ b/packages/twenty-website-new/src/sections/Feature/components/Tiles.tsx @@ -1,6 +1,7 @@ import type { ImageType } from '@/design-system/components/Image'; import type { FeatureTileType } from '@/sections/Feature/types/FeatureTile'; import { theme } from '@/theme'; +import type { MessageDescriptor } from '@lingui/core'; import { styled } from '@linaria/react'; import { Tile } from './Tile/Tile'; @@ -39,15 +40,16 @@ const TileCell = styled.div` type TilesProps = { mask: ImageType; + renderText: (descriptor: MessageDescriptor) => string; tiles: FeatureTileType[]; }; -export function Tiles({ mask, tiles }: TilesProps) { +export function Tiles({ mask, renderText, tiles }: TilesProps) { return ( {tiles.map((tile, index) => ( - + ))} diff --git a/packages/twenty-website-new/src/sections/Feature/types/FeatureData.ts b/packages/twenty-website-new/src/sections/Feature/types/FeatureData.ts index 9bf227b439a..4b36929ccee 100644 --- a/packages/twenty-website-new/src/sections/Feature/types/FeatureData.ts +++ b/packages/twenty-website-new/src/sections/Feature/types/FeatureData.ts @@ -1,11 +1,9 @@ -import type { EyebrowType } from '@/design-system/components/Eyebrow'; -import type { HeadingType } from '@/design-system/components/Heading'; +import type { MessageEyebrow } from '@/lib/i18n/message-eyebrow'; import type { ImageType } from '@/design-system/components/Image'; import type { FeatureTileType } from '@/sections/Feature/types/FeatureTile'; export type FeatureDataType = { - eyebrow: EyebrowType; - heading: HeadingType[]; + eyebrow: MessageEyebrow; mask: ImageType; tiles: FeatureTileType[]; }; diff --git a/packages/twenty-website-new/src/sections/Feature/types/FeatureTile.ts b/packages/twenty-website-new/src/sections/Feature/types/FeatureTile.ts index 8be5db25757..7426e809fe5 100644 --- a/packages/twenty-website-new/src/sections/Feature/types/FeatureTile.ts +++ b/packages/twenty-website-new/src/sections/Feature/types/FeatureTile.ts @@ -1,10 +1,10 @@ -import type { BodyType } from '@/design-system/components/Body'; -import type { HeadingType } from '@/design-system/components/Heading'; +import type { MessageBody } from '@/lib/i18n/message-body'; +import type { MessageHeadingSegment } from '@/lib/i18n/message-heading-segment'; import type { ImageType } from '@/design-system/components/Image'; export type FeatureTileType = { - bullets: BodyType[]; - heading: HeadingType; + bullets: MessageBody[]; + heading: MessageHeadingSegment; icon: string; image: ImageType; }; diff --git a/packages/twenty-website-new/src/sections/Footer/components/Bottom.tsx b/packages/twenty-website-new/src/sections/Footer/components/Bottom.tsx index e0c855178a4..a32da17bc52 100644 --- a/packages/twenty-website-new/src/sections/Footer/components/Bottom.tsx +++ b/packages/twenty-website-new/src/sections/Footer/components/Bottom.tsx @@ -1,4 +1,5 @@ import { ArrowRightUpIcon, SOCIAL_ICONS } from '@/icons'; +import type { MessageDescriptor } from '@lingui/core'; import type { FooterSocialLinkType } from '@/sections/Footer/types'; import { theme } from '@/theme'; import { Separator } from '@base-ui/react/separator'; @@ -74,14 +75,15 @@ const SocialLink = styled.a` `; type BottomProps = { - copyright: string; + copyright: MessageDescriptor; links: FooterSocialLinkType[]; + renderText: (descriptor: MessageDescriptor) => string; }; -export function Bottom({ copyright, links }: BottomProps) { +export function Bottom({ copyright, links, renderText }: BottomProps) { return ( - {copyright} + {renderText(copyright)} {links.map((link, index) => { const IconComponent = SOCIAL_ICONS[link.icon]; @@ -100,7 +102,7 @@ export function Bottom({ copyright, links }: BottomProps) { fillColor={theme.colors.secondary.background[100]} aria-hidden /> - {link.label} + {link.label ?? null} {link.label && ( string; }; -export function FooterNavCta({ cta }: FooterNavCtaProps) { +export function FooterNavCta({ cta, renderText }: FooterNavCtaProps) { if (cta.kind === 'contactModal') { return ( ); diff --git a/packages/twenty-website-new/src/sections/Footer/components/Nav.tsx b/packages/twenty-website-new/src/sections/Footer/components/Nav.tsx index 97e9e80e1e2..42d5cc07a46 100644 --- a/packages/twenty-website-new/src/sections/Footer/components/Nav.tsx +++ b/packages/twenty-website-new/src/sections/Footer/components/Nav.tsx @@ -4,6 +4,7 @@ import { LocalizedLink } from '@/lib/i18n'; import type { FooterNavGroupType } from '@/sections/Footer/types'; import { theme } from '@/theme'; import { NavigationMenu } from '@base-ui/react/navigation-menu'; +import type { MessageDescriptor } from '@lingui/core'; import { styled } from '@linaria/react'; import React from 'react'; @@ -125,9 +126,10 @@ const Actions = styled.div` type NavProps = { groups: FooterNavGroupType[]; + renderText: (descriptor: MessageDescriptor) => string; }; -export function Nav({ groups }: NavProps) { +export function Nav({ groups, renderText }: NavProps) { return ( }> {groups.map((group, index) => ( @@ -148,10 +150,12 @@ export function Nav({ groups }: NavProps) { )} - {group.title} + + {renderText(group.title)} + {group.links.map((link) => ( - + - {link.label} + {renderText(link.label)} ))} @@ -181,11 +185,10 @@ export function Nav({ groups }: NavProps) { {group.ctas.map((cta) => ( ))} diff --git a/packages/twenty-website-new/src/sections/Footer/data.ts b/packages/twenty-website-new/src/sections/Footer/data.ts index ffb526b3341..d0a998cdbf5 100644 --- a/packages/twenty-website-new/src/sections/Footer/data.ts +++ b/packages/twenty-website-new/src/sections/Footer/data.ts @@ -1,39 +1,40 @@ +import { msg } from '@lingui/core/macro'; import type { FooterDataType } from '@/sections/Footer/types'; export const FOOTER_DATA: FooterDataType = { bottom: { - copyright: '© 2026 – Twenty', + copyright: msg`© 2026 – Twenty`, }, navGroups: [ { id: 'footer-sitemap', - title: 'Sitemap', + title: msg`Sitemap`, ctas: [], links: [ - { label: 'Home', href: '/', external: false }, - { label: 'Pricing', href: '/pricing', external: false }, - { label: 'Partners', href: '/partners', external: false }, - { label: 'Why Twenty', href: '/why-twenty', external: false }, + { label: msg`Home`, href: '/', external: false }, + { label: msg`Pricing`, href: '/pricing', external: false }, + { label: msg`Partners`, href: '/partners', external: false }, + { label: msg`Why Twenty`, href: '/why-twenty', external: false }, ], }, { id: 'footer-help', - title: 'Help', + title: msg`Help`, ctas: [], links: [ { - label: 'Developers', + label: msg`Developers`, href: 'https://docs.twenty.com/developers/introduction', external: true, }, { - label: 'User Guide', + label: msg`User Guide`, href: 'https://docs.twenty.com/getting-started/introduction', external: true, }, - { label: 'Release Notes', href: '/releases', external: false }, + { label: msg`Release Notes`, href: '/releases', external: false }, { - label: 'Halftone generator', + label: msg`Halftone generator`, href: '/halftone', external: false, }, @@ -41,34 +42,38 @@ export const FOOTER_DATA: FooterDataType = { }, { id: 'footer-legal', - title: 'Legal', + title: msg`Legal`, ctas: [], links: [ - { label: 'Privacy Policy', href: '/privacy-policy', external: false }, - { label: 'Terms and Conditions', href: '/terms', external: false }, + { + label: msg`Privacy Policy`, + href: '/privacy-policy', + external: false, + }, + { label: msg`Terms and Conditions`, href: '/terms', external: false }, ], }, { id: 'footer-connect', - title: 'Connect', + title: msg`Connect`, ctas: [ { color: 'secondary', kind: 'contactModal', - label: 'Talk to us', + label: msg`Talk to us`, variant: 'contained', }, { color: 'secondary', href: 'https://app.twenty.com/welcome', kind: 'link', - label: 'Get started', + label: msg`Get started`, variant: 'outlined', }, ], links: [ { - label: 'LinkedIn', + label: msg`LinkedIn`, href: 'https://www.linkedin.com/company/twenty', external: true, }, diff --git a/packages/twenty-website-new/src/sections/Footer/types/FooterBottom.ts b/packages/twenty-website-new/src/sections/Footer/types/FooterBottom.ts index d453fbf685a..c86652756dd 100644 --- a/packages/twenty-website-new/src/sections/Footer/types/FooterBottom.ts +++ b/packages/twenty-website-new/src/sections/Footer/types/FooterBottom.ts @@ -1,3 +1,5 @@ +import type { MessageDescriptor } from '@lingui/core'; + export type FooterBottomType = { - copyright: string; + copyright: MessageDescriptor; }; diff --git a/packages/twenty-website-new/src/sections/Footer/types/FooterCta.ts b/packages/twenty-website-new/src/sections/Footer/types/FooterCta.ts index 4ccfd59da72..5b1ac4b643a 100644 --- a/packages/twenty-website-new/src/sections/Footer/types/FooterCta.ts +++ b/packages/twenty-website-new/src/sections/Footer/types/FooterCta.ts @@ -1,14 +1,16 @@ +import type { MessageDescriptor } from '@lingui/core'; + export type FooterCtaType = | { color: 'secondary'; href: string; kind: 'link'; - label: string; + label: MessageDescriptor; variant: 'contained' | 'outlined'; } | { color: 'secondary'; kind: 'contactModal'; - label: string; + label: MessageDescriptor; variant: 'contained' | 'outlined'; }; diff --git a/packages/twenty-website-new/src/sections/Footer/types/FooterNavGroup.ts b/packages/twenty-website-new/src/sections/Footer/types/FooterNavGroup.ts index de39f5dc9bf..8f79d6f0cbb 100644 --- a/packages/twenty-website-new/src/sections/Footer/types/FooterNavGroup.ts +++ b/packages/twenty-website-new/src/sections/Footer/types/FooterNavGroup.ts @@ -1,3 +1,4 @@ +import type { MessageDescriptor } from '@lingui/core'; import type { FooterCtaType } from './FooterCta'; import type { FooterNavLinkType } from './FooterNavLink'; @@ -5,5 +6,5 @@ export type FooterNavGroupType = { ctas: FooterCtaType[]; id: string; links: FooterNavLinkType[]; - title: string; + title: MessageDescriptor; }; diff --git a/packages/twenty-website-new/src/sections/Footer/types/FooterNavLink.ts b/packages/twenty-website-new/src/sections/Footer/types/FooterNavLink.ts index e90fa69d245..e82f7f36759 100644 --- a/packages/twenty-website-new/src/sections/Footer/types/FooterNavLink.ts +++ b/packages/twenty-website-new/src/sections/Footer/types/FooterNavLink.ts @@ -1,5 +1,7 @@ +import type { MessageDescriptor } from '@lingui/core'; + export type FooterNavLinkType = { external: boolean; href: string; - label: string; + label: MessageDescriptor; }; diff --git a/packages/twenty-website-new/src/sections/Helped/components/Card.tsx b/packages/twenty-website-new/src/sections/Helped/components/Card.tsx index 03a9a5a4178..7221dbc23cd 100644 --- a/packages/twenty-website-new/src/sections/Helped/components/Card.tsx +++ b/packages/twenty-website-new/src/sections/Helped/components/Card.tsx @@ -1,10 +1,13 @@ -import { Body, Heading, LinkButton } from '@/design-system/components'; +import { Body, Heading } from '@/design-system/components'; import { CLIENT_ICONS } from '@/icons'; +import { LocalizedLinkButton } from '@/lib/i18n/LocalizedLinkButton'; import { WebGlMount } from '@/lib/visual-runtime'; import { HelpedCardShape } from '@/sections/Helped/components/HelpedCardShape'; import type { HeadingCardType } from '@/sections/Helped/types/HeadingCard'; import { HELPED_VISUALS } from '@/sections/Helped/visuals'; import { theme } from '@/theme'; +import type { MessageDescriptor } from '@lingui/core'; +import { msg } from '@lingui/core/macro'; import { styled } from '@linaria/react'; const CardRoot = styled.article` @@ -71,9 +74,10 @@ const LOGO_FILL = theme.colors.secondary.text[100]; type CardProps = { card: HeadingCardType; + renderText: (descriptor: MessageDescriptor) => string; }; -export function Card({ card }: CardProps) { +export function Card({ card, renderText }: CardProps) { const IconComponent = CLIENT_ICONS[card.icon]; const Visual = HELPED_VISUALS[card.illustration]; const logoWidth = 104; @@ -98,18 +102,23 @@ export function Card({ card }: CardProps) {