Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
b7e377e
v0.5.91: docs i18n, turborepo upgrade
waleedlatif1 Feb 16, 2026
da46a38
v0.5.92: shortlinks, copilot scrolling stickiness, pagination
waleedlatif1 Feb 17, 2026
fdca736
v0.5.93: NextJS config changes, MCP and Blocks whitelisting, copilot …
waleedlatif1 Feb 18, 2026
15ace5e
v0.5.94: vercel integration, folder insertion, migrated tracking redi…
waleedlatif1 Feb 19, 2026
67aa4bb
v0.5.95: gemini 3.1 pro, cloudflare, dataverse, revenuecat, redis, up…
waleedlatif1 Feb 20, 2026
34d92fa
v0.5.96: sim oauth provider, slack ephemeral message tool and blockki…
waleedlatif1 Feb 21, 2026
115f04e
v0.5.97: oidc discovery for copilot mcp
waleedlatif1 Feb 21, 2026
0d86ea0
v0.5.98: change detection improvements, rate limit and code execution…
waleedlatif1 Feb 22, 2026
af59234
v0.5.99: local dev improvements, live workflow logs in terminal
waleedlatif1 Feb 23, 2026
67f8a68
v0.5.100: multiple credentials, 40% speedup, gong, attio, audit log i…
waleedlatif1 Feb 25, 2026
b07dfc4
feat: setup next-intl and signUp translations
Kallebe23 Feb 26, 2026
01e7a5b
feat: sign_up translations
Kallebe23 Mar 2, 2026
80d4023
fix: syncing translations
Kallebe23 Mar 2, 2026
397058e
fix: json format
Kallebe23 Mar 2, 2026
2e964ab
fix: automated translation
Kallebe23 Mar 2, 2026
86ec8f3
feat: settings translations
Kallebe23 Mar 2, 2026
3413135
feat: blocks translations
Kallebe23 Mar 2, 2026
a96b289
feat: translating sub_blocks
Kallebe23 Mar 3, 2026
31ce911
feat: editor subblocks translations
Kallebe23 Mar 3, 2026
21adb7c
feat: logs + terminal translations
Kallebe23 Mar 3, 2026
833896a
fix: translating deploy components
Kallebe23 Mar 3, 2026
98f4f5d
fix: build errors
Kallebe23 Mar 3, 2026
79b19b6
feat: input-format subblock translations
Kallebe23 Mar 3, 2026
1a175be
feat: input-format subblock default translations
Kallebe23 Mar 3, 2026
3b2414e
feat: table and skill-input subblocks translations
Kallebe23 Mar 4, 2026
6aa2374
feat: knowledge base translations
Kallebe23 Mar 4, 2026
123a90e
feat: logs translations
Kallebe23 Mar 9, 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
90 changes: 90 additions & 0 deletions .claude/rules/sim-translations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
---
paths:
- "apps/sim/**/*.tsx"
- "apps/sim/**/*.ts"
- "apps/sim/**/*.json"
description: Automatically refactor React/Next.js components to use next-intl without namespaces
---

# Next-Intl Auto I18n Refactoring

## Overview

You will receive a React or Next.js component.

Your job is to:

1. Convert the component to use next-intl
2. Replace all user-visible and accessibility text with t() calls
3. Use no namespaces

Do not include explanations.

---
## Rules
1. No duplicate keys in translation files
2. Don't translate logger info or error messages that are not user-facing.
3. Do not translate error messages or similars as keys to be rendered as translations later. For example, if there is an error message like "Invalid email address", do not create a key like "errors.invalid_email" with the value "Invalid email address". If possible, just create a new hook that calls useTranslation inside it and returns the same object or function with translated texts and keep the code in the same file.
4. Detect Component Type (Client vs Server) and use the appropriate next-intl functions (useTranslations for Client, getTranslations for Server).
5. No namespaces in keys
6. You MUST replace every user-visible or accessibility string.
7. If a component is rendering rich text like <strong> or <em>, you must use the rich text formatting capabilities of next-intl.
eg.
```
{
"message": "Please refer to <guidelines>the guidelines</guidelines>."
}
// Returns `<>Please refer to <a href="/guidelines">the guidelines</a>.</>`
t.rich('message', {
guidelines: (chunks) => <a href="/guidelines">{chunks}</a>
});
```
8. All the translations must be applied in en.json, es.json and pt.json in the translations folder. Find the right place to change the translations file to reuse existing translations and avoid duplicates. If there is no right place, create a new key following the Key Naming Rules.

## What Must NOT Be Translated

Do NOT translate:

- className
- variable names
- function names
- API field names
- route paths (/login, /workspace)
- console logs (unless rendered in UI)
- error codes
- environment variable names
- regex patterns
- non-user-facing constants

## Key Naming Rules

All keys must be:

- snake_case
- descriptive
- stable

Keys must be grouped semantically:

- title
- subtitle
- description
- labels.*
- placeholders.*
- buttons.*
- links.*
- aria.*
- errors.*
- helper_text.*
- loading.*

Examples:

t('title')
t('labels.email')
t('placeholders.password')
t('buttons.create_account')
t('buttons.loading')
t('aria.show_password')
t('errors.invalid_email')

3 changes: 2 additions & 1 deletion apps/sim/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,5 @@ next-env.d.ts
# Uploads
/uploads

.trigger
.trigger
certificates
33 changes: 19 additions & 14 deletions apps/sim/app/(auth)/components/social-login-buttons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { GithubIcon, GoogleIcon } from '@/components/icons'
import { Button } from '@/components/ui/button'
import { client } from '@/lib/auth/auth-client'
import { inter } from '@/app/_styles/fonts/inter/inter'
import { useTranslations } from 'next-intl'

interface SocialLoginButtonsProps {
githubAvailable: boolean
Expand All @@ -21,16 +22,16 @@ export function SocialLoginButtons({
isProduction,
children,
}: SocialLoginButtonsProps) {
const t = useTranslations('social_login')

const [isGithubLoading, setIsGithubLoading] = useState(false)
const [isGoogleLoading, setIsGoogleLoading] = useState(false)
const [mounted, setMounted] = useState(false)

// Set mounted state to true on client-side
useEffect(() => {
setMounted(true)
}, [])

// Only render on the client side to avoid hydration errors
if (!mounted) return null

async function signInWithGithub() {
Expand All @@ -40,17 +41,19 @@ export function SocialLoginButtons({
try {
await client.signIn.social({ provider: 'github', callbackURL })
} catch (err: any) {
let errorMessage = 'Failed to sign in with GitHub'
let errorMessage = t('errors.github.default')

if (err.message?.includes('account exists')) {
errorMessage = 'An account with this email already exists. Please sign in instead.'
errorMessage = t('errors.account_exists')
} else if (err.message?.includes('cancelled')) {
errorMessage = 'GitHub sign in was cancelled. Please try again.'
errorMessage = t('errors.github.cancelled')
} else if (err.message?.includes('network')) {
errorMessage = 'Network error. Please check your connection and try again.'
errorMessage = t('errors.network')
} else if (err.message?.includes('rate limit')) {
errorMessage = 'Too many attempts. Please try again later.'
errorMessage = t('errors.rate_limit')
}

console.error(errorMessage)
} finally {
setIsGithubLoading(false)
}
Expand All @@ -63,17 +66,19 @@ export function SocialLoginButtons({
try {
await client.signIn.social({ provider: 'google', callbackURL })
} catch (err: any) {
let errorMessage = 'Failed to sign in with Google'
let errorMessage = t('errors.google.default')

if (err.message?.includes('account exists')) {
errorMessage = 'An account with this email already exists. Please sign in instead.'
errorMessage = t('errors.account_exists')
} else if (err.message?.includes('cancelled')) {
errorMessage = 'Google sign in was cancelled. Please try again.'
errorMessage = t('errors.google.cancelled')
} else if (err.message?.includes('network')) {
errorMessage = 'Network error. Please check your connection and try again.'
errorMessage = t('errors.network')
} else if (err.message?.includes('rate limit')) {
errorMessage = 'Too many attempts. Please try again later.'
errorMessage = t('errors.rate_limit')
}

console.error(errorMessage)
} finally {
setIsGoogleLoading(false)
}
Expand All @@ -87,7 +92,7 @@ export function SocialLoginButtons({
onClick={signInWithGithub}
>
<GithubIcon className='!h-[18px] !w-[18px] mr-1' />
{isGithubLoading ? 'Connecting...' : 'GitHub'}
{isGithubLoading ? t('buttons.connecting') : t('buttons.github')}
</Button>
)

Expand All @@ -99,7 +104,7 @@ export function SocialLoginButtons({
onClick={signInWithGoogle}
>
<GoogleIcon className='!h-[18px] !w-[18px] mr-1' />
{isGoogleLoading ? 'Connecting...' : 'Google'}
{isGoogleLoading ? t('buttons.connecting') : t('buttons.google')}
</Button>
)

Expand Down
4 changes: 3 additions & 1 deletion apps/sim/app/(auth)/components/sso-login-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useRouter } from 'next/navigation'
import { Button } from '@/components/ui/button'
import { getEnv, isTruthy } from '@/lib/core/config/env'
import { cn } from '@/lib/core/utils/cn'
import { useTranslations } from 'next-intl'

interface SSOLoginButtonProps {
callbackURL?: string
Expand All @@ -23,6 +24,7 @@ export function SSOLoginButton({
primaryClassName,
}: SSOLoginButtonProps) {
const router = useRouter()
const t = useTranslations()

if (!isTruthy(getEnv('NEXT_PUBLIC_SSO_ENABLED'))) {
return null
Expand All @@ -47,7 +49,7 @@ export function SSOLoginButton({
variant={variant === 'outline' ? 'outline' : undefined}
className={cn(variant === 'outline' ? outlineBtnClasses : primaryBtnClasses, className)}
>
Sign in with SSO
{t('sign_in_with_sso')}
</Button>
)
}
6 changes: 4 additions & 2 deletions apps/sim/app/(auth)/components/support-footer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client'

import { useTranslations } from 'next-intl'
import { inter } from '@/app/_styles/fonts/inter/inter'
import { useBrandConfig } from '@/ee/whitelabeling'

Expand All @@ -23,17 +24,18 @@ export interface SupportFooterProps {
*/
export function SupportFooter({ position = 'fixed' }: SupportFooterProps) {
const brandConfig = useBrandConfig()
const t = useTranslations()

return (
<div
className={`${inter.className} auth-text-muted right-0 bottom-0 left-0 z-50 pb-8 text-center font-[340] text-[13px] leading-relaxed ${position}`}
>
Need help?{' '}
{t('helper_text.need_help')}{' '}
<a
href={`mailto:${brandConfig.supportEmail}`}
className='auth-link underline-offset-4 transition hover:underline'
>
Contact support
{t('links.contact_support')}
</a>
</div>
)
Expand Down
Loading