Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
904e8ed
Cherry-pick packages/i18n
pieterbeulque Jan 21, 2026
8e0c2f0
Add locale resolver + translation hook
pieterbeulque Jan 21, 2026
f8e8170
Add type-safe string interpolation
pieterbeulque Jan 22, 2026
c8085b1
Interpolation & plurals
pieterbeulque Jan 22, 2026
fb5c2b1
Re-run `pnpm i`
pieterbeulque Feb 6, 2026
e81c745
Remove unused experiment props
pieterbeulque Feb 6, 2026
1a76330
Translate `CheckoutForm` completely
pieterbeulque Feb 6, 2026
4363d94
WIP derived themes
pieterbeulque Feb 6, 2026
b206f48
Properly pass locale
pieterbeulque Feb 6, 2026
23483f2
Formatting
pieterbeulque Feb 6, 2026
f9ff708
Translate intervals correctly
pieterbeulque Feb 6, 2026
d88430c
Translate State / Province / Country placeholders
pieterbeulque Feb 6, 2026
98d8786
Update some missing translations
pieterbeulque Feb 6, 2026
dcf4c80
Translate checkout confirmation
pieterbeulque Feb 6, 2026
e1da8ad
Fix date formatting
pieterbeulque Feb 9, 2026
9de55c7
Support BCP 47
pieterbeulque Feb 9, 2026
c1cd903
Fix types
pieterbeulque Feb 9, 2026
c313870
Use constant `DEFAULT_LOCALE`
pieterbeulque Feb 9, 2026
9e9d5cc
Resolve `pnpm-lock.yaml`
pieterbeulque Feb 9, 2026
7b9dc2d
Fix static analysis
pieterbeulque Feb 9, 2026
27f0502
Fix `.prettierignore`
pieterbeulque Feb 9, 2026
0ca420f
Use checkout/customer locale in checkout if available
pieterbeulque Feb 10, 2026
fdf968f
Store accepted locale on checkout upon confirmation
pieterbeulque Feb 10, 2026
682a5f2
Set customer locale from checkout locale
pieterbeulque Feb 10, 2026
5915951
Fix sudden Stripe type error
pieterbeulque Feb 10, 2026
69cb8c9
Pass `locale` to `CountryPicker`
pieterbeulque Feb 10, 2026
733fda1
Fix (unrelated) build error
pieterbeulque Feb 10, 2026
4d0b066
Minimal i18n package
pieterbeulque Feb 11, 2026
680ee42
Add checkout localization feature flag
pieterbeulque Feb 10, 2026
f605bb1
Force `en-US` locale when feature flag is not enabled
pieterbeulque Feb 11, 2026
b63138c
Add UI for feature flag
pieterbeulque Feb 11, 2026
e22593a
Import i18n automations
pieterbeulque Feb 11, 2026
c01871a
Fix typecheck
pieterbeulque Feb 11, 2026
b15724b
Add `_llmContext` support to the Typescript helpers
pieterbeulque Feb 11, 2026
ab26fdc
Add Spanish, French, and Swedish
pieterbeulque Feb 11, 2026
dc0feec
Tinker with languages
pieterbeulque Feb 11, 2026
43fe6ee
Improve street address labels
pieterbeulque Feb 11, 2026
757abb4
Update some Swedish
pieterbeulque Feb 11, 2026
bd45a15
Update Swedish feedback
pieterbeulque Feb 11, 2026
deafce8
Actually import locales
pieterbeulque Feb 11, 2026
de44fac
Fix types
pieterbeulque Feb 11, 2026
be6592c
Fix `.env` loading on CI/CD too
pieterbeulque Feb 11, 2026
5759c5a
Revert NL changes to reviewed file from main branch
pieterbeulque Feb 11, 2026
b72b7d3
Import i18n automations
pieterbeulque Feb 11, 2026
4df90c1
Fix typecheck
pieterbeulque Feb 11, 2026
02967c2
Add `_llmContext` support to the Typescript helpers
pieterbeulque Feb 11, 2026
c2e03da
Revert NL changes to reviewed file from main branch
pieterbeulque Feb 11, 2026
7c9e60c
Add German translations
pieterbeulque Feb 11, 2026
28ec721
Fix build
pieterbeulque Feb 11, 2026
d22047b
Process feedback
pieterbeulque Feb 11, 2026
49bb158
Cherry-pick automation changes from #9502
pieterbeulque Feb 12, 2026
af813cb
Merge branch 'pieter/automated-i18n' into pieter/add-other-languages
pieterbeulque Feb 12, 2026
2ab1e98
Merge branch 'pieter/bare-bone-i18n' into pieter/add-other-languages
pieterbeulque Feb 12, 2026
4adc9b4
Switch to Gemini 3 Pro
pieterbeulque Feb 12, 2026
382b189
Re-run NL
pieterbeulque Feb 12, 2026
12fc2f9
Merge branch 'pieter/bare-bone-i18n' into pieter/add-other-languages
pieterbeulque Feb 12, 2026
8b5683c
Process FR feedback
pieterbeulque Feb 12, 2026
5834d4f
Remove hardcoded dollar value
pieterbeulque Feb 12, 2026
efd91ad
chore(i18n): update translations
github-actions[bot] Feb 12, 2026
0264989
Remove nonexisting benefitType
pieterbeulque Feb 12, 2026
6097bc4
Fix Github Action
pieterbeulque Feb 12, 2026
f3b74bc
Switch to default exports
pieterbeulque Feb 12, 2026
8dc57cc
Merge branch 'pieter/bare-bone-i18n' into pieter/add-other-languages
pieterbeulque Feb 12, 2026
f2b94f5
Add Hungarian (#9511)
pieterbeulque Feb 12, 2026
ee59406
Merge branch 'main' into pieter/bare-bone-i18n
pieterbeulque Feb 12, 2026
9661db7
Update supported languages in the docs
pieterbeulque Feb 12, 2026
3b7d328
Import `hu`
pieterbeulque Feb 12, 2026
9c873ea
Fix formatting
pieterbeulque Feb 12, 2026
0c13734
Clean-up
pieterbeulque Feb 12, 2026
89b43a3
Move ordinal formatting
pieterbeulque Feb 12, 2026
6f8c4cc
Improve default locale fallback
pieterbeulque Feb 12, 2026
71faa2f
Clean up index.ts
pieterbeulque Feb 13, 2026
e06a1c2
Consider feature flag & querystring in checkout links too
pieterbeulque Feb 13, 2026
d0a996c
Manually handle ordinal translations
pieterbeulque Feb 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .github/workflows/i18n_translate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Generate Translations

on:
pull_request:
paths:
- "clients/packages/i18n/src/locales/en.ts"
- "clients/packages/i18n/src/config.ts"

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
translate:
runs-on: ubuntu-latest

defaults:
run:
working-directory: ${{ github.workspace }}/clients

permissions:
contents: write

steps:
- name: Checkout code
uses: actions/checkout@v6
with:
ref: ${{ github.head_ref }}
token: ${{ secrets.GITHUB_TOKEN }}

- uses: pnpm/action-setup@v4
with:
package_json_file: clients/package.json

- name: Setup Node.js environment
uses: actions/setup-node@v6
with:
node-version-file: clients/.node-version
cache: "pnpm"
cache-dependency-path: "clients/pnpm-lock.yaml"

- name: Install dependencies
run: pnpm install --ignore-scripts

- name: Generate translations
env:
GOOGLE_GENERATIVE_AI_API_KEY: ${{ secrets.GOOGLE_GENERATIVE_AI_API_KEY }}
run: pnpm --filter @polar-sh/i18n translate

- name: Commit translations
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add packages/i18n/
git diff --staged --quiet || git commit -m "chore(i18n): update translations"
git push
5 changes: 3 additions & 2 deletions clients/apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@
"@polar-sh/client": "workspace:*",
"@polar-sh/currency": "workspace:^",
"@polar-sh/customer-portal": "workspace:*",
"@polar-sh/i18n": "workspace:*",
"@polar-sh/mdx": "workspace:*",
"@polar-sh/sdk": "^0.42.5",
"@polar-sh/sdk": "^0.43.0",
"@polar-sh/ui": "workspace:*",
"@posthog/ai": "^7.5.4",
"@posthog/core": "^1.14.0",
Expand Down Expand Up @@ -115,7 +116,7 @@
"@types/mdx": "^2.0.13",
"@types/node": "^24.0.0",
"@types/qrcode": "^1.5.6",
"@types/react": "19.2.10",
"@types/react": "19.2.13",
"@types/react-dom": "19.2.3",
"@types/tinycolor2": "^1.4.6",
"@types/web": "^0.0.323",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@
import Checkout from '@/components/Checkout/Checkout'
import CheckoutLayout from '@/components/Checkout/CheckoutLayout'
import { useCheckout } from '@polar-sh/checkout/providers'
import { AcceptedLocale } from '@polar-sh/i18n'

const ClientPage = ({
embed,
theme,
locale,
}: {
embed: boolean
theme?: 'light' | 'dark'
locale: AcceptedLocale
}) => {
const { checkout } = useCheckout()

return (
<CheckoutLayout checkout={checkout} embed={embed} theme={theme}>
<Checkout embed={embed} theme={theme} />
<Checkout embed={embed} theme={theme} locale={locale} />
</CheckoutLayout>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { CheckoutConfirmation } from '@/components/Checkout/CheckoutConfirmation'
import CheckoutLayout from '@/components/Checkout/CheckoutLayout'
import { getServerURL } from '@/utils/api'
import { resolveLocale } from '@/utils/i18n'
import { PolarCore } from '@polar-sh/sdk/core'
import { checkoutsClientGet } from '@polar-sh/sdk/funcs/checkoutsClientGet'
import { ExpiredCheckoutError } from '@polar-sh/sdk/models/errors/expiredcheckouterror'
Expand All @@ -12,12 +13,13 @@ export default async function Page(props: {
searchParams: Promise<{
embed?: string
theme?: 'light' | 'dark'
locale?: string
customer_session_token?: string
}>
}) {
const searchParams = await props.searchParams

const { embed, theme, customer_session_token } = searchParams
const { embed, theme, locale: _locale, customer_session_token } = searchParams

const params = await props.params

Expand Down Expand Up @@ -61,12 +63,15 @@ export default async function Page(props: {
redirect(checkout.url)
}

const locale = await resolveLocale(_locale, checkout.locale)

return (
<CheckoutLayout checkout={checkout} embed={embed === 'true'} theme={theme}>
<CheckoutConfirmation
checkout={checkout}
embed={embed === 'true'}
theme={theme}
locale={locale}
customerSessionToken={customer_session_token}
/>
</CheckoutLayout>
Expand Down
15 changes: 11 additions & 4 deletions clients/apps/web/src/app/checkout/[clientSecret]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getPublicServerURL, getServerURL } from '@/utils/api'
import { resolveLocale } from '@/utils/i18n'
import {
CheckoutFormProvider,
CheckoutProvider,
Expand All @@ -12,11 +13,15 @@ import CheckoutPage from './CheckoutPage'

export default async function Page(props: {
params: Promise<{ clientSecret: string }>
searchParams: Promise<{ embed?: string; theme?: 'light' | 'dark' }>
searchParams: Promise<{
embed?: string
theme?: 'light' | 'dark'
locale?: string
}>
}) {
const searchParams = await props.searchParams

const { embed: _embed, theme } = searchParams
const { embed: _embed, theme, locale: _locale } = searchParams

const params = await props.params

Expand Down Expand Up @@ -75,13 +80,15 @@ export default async function Page(props: {
redirect(`/checkout/${checkout.clientSecret}/confirmation`)
}

const locale = await resolveLocale(_locale, checkout.locale)

return (
<CheckoutProvider
clientSecret={checkout.clientSecret}
serverURL={getPublicServerURL()}
>
<CheckoutFormProvider>
<CheckoutPage theme={theme} embed={embed} />
<CheckoutFormProvider locale={locale}>
<CheckoutPage theme={theme} embed={embed} locale={locale} />
</CheckoutFormProvider>
</CheckoutProvider>
)
Expand Down
55 changes: 42 additions & 13 deletions clients/apps/web/src/components/Benefit/BenefitGrant.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { useCustomerBenefitGrantUpdate } from '@/hooks/queries'
import { markdownOptions } from '@/utils/markdown'
import { Client, schemas } from '@polar-sh/client'
import {
DEFAULT_LOCALE,
useTranslations,
type AcceptedLocale,
} from '@polar-sh/i18n'
import Button from '@polar-sh/ui/components/atoms/Button'
import {
Select,
Expand All @@ -15,11 +20,12 @@ import { usePathname, useSearchParams } from 'next/navigation'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import DownloadablesBenefitGrant from './Downloadables/DownloadablesBenefitGrant'
import { LicenseKeyBenefitGrant } from './LicenseKeys/LicenseKeyBenefitGrant'
import { benefitsDisplayNames, resolveBenefitIcon } from './utils'
import { resolveBenefitIcon } from './utils'

interface BenefitGrantProps {
api: Client
benefitGrant: schemas['CustomerBenefitGrant']
locale?: AcceptedLocale
}

const BenefitGrantCustom = ({
Expand Down Expand Up @@ -48,6 +54,7 @@ const BenefitGrantOAuth = ({
api,
benefitGrant,
platform,
locale = DEFAULT_LOCALE,
connectButtonText,
openButtonUrl,
openButtonText,
Expand All @@ -58,11 +65,13 @@ const BenefitGrantOAuth = ({
| schemas['CustomerBenefitGrantGitHubRepository']
| schemas['CustomerBenefitGrantDiscord']
platform: 'github' | 'discord'
locale?: AcceptedLocale
openButtonText: string
openButtonUrl: string
connectButtonText: string
selectPlaceholder: string
}) => {
const t = useTranslations(locale)
const pathname = usePathname()
const searchParams = useSearchParams()
const {
Expand Down Expand Up @@ -199,7 +208,7 @@ const BenefitGrantOAuth = ({
variant="secondary"
className="grow"
>
Request new invite
{t('checkout.benefits.requestNewInvite')}
</Button>
</>
)}
Expand All @@ -214,7 +223,7 @@ const BenefitGrantOAuth = ({
disabled={retryCountdown > 0}
>
{retryCountdown > 0
? `Try again in ${retryCountdown} second${retryCountdown === 1 ? '' : 's'}`
? t('checkout.benefits.retryIn', { count: retryCountdown })
: connectButtonText}
</Button>
) : (
Expand All @@ -240,7 +249,9 @@ const BenefitGrantOAuth = ({
{account.account_username}
</SelectItem>
))}
<SelectItem value="add">Connect new account</SelectItem>
<SelectItem value="add">
{t('checkout.benefits.connectNewAccount')}
</SelectItem>
</SelectContent>
</Select>
<Button
Expand All @@ -249,7 +260,7 @@ const BenefitGrantOAuth = ({
fullWidth
variant="secondary"
>
Request my invite
{t('checkout.benefits.requestMyInvite')}
</Button>
</>
)}
Expand All @@ -268,10 +279,13 @@ const BenefitGrantOAuth = ({
const BenefitGrantGitHubRepository = ({
api,
benefitGrant,
locale = DEFAULT_LOCALE,
}: {
api: Client
benefitGrant: schemas['CustomerBenefitGrantGitHubRepository']
locale?: AcceptedLocale
}) => {
const t = useTranslations(locale)
const {
benefit: {
properties: { repository_owner, repository_name },
Expand All @@ -282,21 +296,27 @@ const BenefitGrantGitHubRepository = ({
api={api}
benefitGrant={benefitGrant}
platform="github"
connectButtonText="Connect GitHub account"
openButtonText={`Go to ${repository_owner}/${repository_name}`}
locale={locale}
connectButtonText={t('checkout.benefits.github.connect')}
openButtonText={t('checkout.benefits.github.goTo', {
repository: `${repository_owner}/${repository_name}`,
})}
openButtonUrl={`https://github.com/${repository_owner}/${repository_name}/invitations`}
selectPlaceholder="Select a GitHub account"
selectPlaceholder={t('checkout.benefits.github.selectAccount')}
/>
)
}

const BenefitGrantDiscord = ({
api,
benefitGrant,
locale = DEFAULT_LOCALE,
}: {
api: Client
benefitGrant: schemas['CustomerBenefitGrantDiscord']
locale?: AcceptedLocale
}) => {
const t = useTranslations(locale)
const {
benefit: {
properties: { guild_id },
Expand All @@ -307,15 +327,21 @@ const BenefitGrantDiscord = ({
api={api}
benefitGrant={benefitGrant}
platform="discord"
connectButtonText="Connect Discord account"
openButtonText="Open Discord"
locale={locale}
connectButtonText={t('checkout.benefits.discord.connect')}
openButtonText={t('checkout.benefits.discord.open')}
openButtonUrl={`https://www.discord.com/channels/${guild_id}`}
selectPlaceholder="Select a Discord account"
selectPlaceholder={t('checkout.benefits.discord.selectAccount')}
/>
)
}

export const BenefitGrant = ({ api, benefitGrant }: BenefitGrantProps) => {
export const BenefitGrant = ({
api,
benefitGrant,
locale = DEFAULT_LOCALE,
}: BenefitGrantProps) => {
const t = useTranslations(locale)
const { benefit } = benefitGrant

return (
Expand All @@ -329,7 +355,7 @@ export const BenefitGrant = ({ api, benefitGrant }: BenefitGrantProps) => {
<div className="flex flex-col">
<h3 className="text-sm font-medium">{benefit.description}</h3>
<p className="dark:text-polar-500 flex flex-row gap-x-1 truncate text-sm text-gray-500">
{benefitsDisplayNames[benefit.type]}
{t(`benefitTypes.${benefit.type}`)}
</p>
</div>
</div>
Expand All @@ -352,6 +378,7 @@ export const BenefitGrant = ({ api, benefitGrant }: BenefitGrantProps) => {
benefitGrant={
benefitGrant as schemas['CustomerBenefitGrantLicenseKeys']
}
locale={locale}
/>
)}
{benefit.type === 'github_repository' && (
Expand All @@ -360,12 +387,14 @@ export const BenefitGrant = ({ api, benefitGrant }: BenefitGrantProps) => {
benefitGrant={
benefitGrant as schemas['CustomerBenefitGrantGitHubRepository']
}
locale={locale}
/>
)}
{benefit.type === 'discord' && (
<BenefitGrantDiscord
api={api}
benefitGrant={benefitGrant as schemas['CustomerBenefitGrantDiscord']}
locale={locale}
/>
)}
</div>
Expand Down
Loading
Loading