Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
26 changes: 26 additions & 0 deletions LOCADEX.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# 🌐 Locadex i18n

This repository is configured to use Locadex for automated internationalization.

## Configuration:

- **Working Directory**: `./apps/snow-leopard`
- **Branch Prefix**: `locadex/`
- **Configured Locales**: `en-US`, `de`, `es`, `fr`, `it`, `ja`, `pt-BR`, `zh`
- **Local Translations**: Enabled

## How it works:

- Locadex will automatically analyze your code for translatable content every time you open a PR
- Locadex will modify your build command to automatically generate translations for your content in your configured locales
- Locadex will push its changes to your PR branch, which you can review and merge

## Next Steps:
1. **Get API Keys**: Visit [General Translation Dashboard](https://dash.generaltranslation.com) to generate API Keys
2. **Add API Keys**: Add a Production API Key and Project ID to your project CI workflow to keep your translations up to date
3. In development, using a Development API Key will allow you to hot-reload translations in your app as you make changes

---

Generated by [Locadex](https://generaltranslation.com) • [Documentation](https://generaltranslation.com/docs)
Timestamp: 8/18/2025
35 changes: 21 additions & 14 deletions apps/snow-leopard/app/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { T } from 'gt-next';
import { authClient } from '@/lib/auth-client';
import { toast } from '@/components/toast';
import { AuthForm } from '@/components/auth-form';
Expand Down Expand Up @@ -82,10 +83,14 @@ export default function LoginPage() {
<div className="flex h-dvh w-screen items-start pt-12 md:pt-0 md:items-center justify-center bg-background">
<div className="w-full max-w-md overflow-hidden rounded-2xl flex flex-col gap-12">
<div className="flex flex-col items-center justify-center gap-2 px-8 text-center">
<h3 className="text-xl font-semibold dark:text-zinc-50">Sign In</h3>
<p className="text-sm text-gray-500 dark:text-zinc-400">
Sign in with your email and password
</p>
<T>
<h3 className="text-xl font-semibold dark:text-zinc-50">Sign In</h3>
</T>
<T>
<p className="text-sm text-gray-500 dark:text-zinc-400">
Sign in with your email and password
</p>
</T>
</div>

<div className="px-8">
Expand All @@ -102,20 +107,22 @@ export default function LoginPage() {
<SubmitButton
isSuccessful={isSuccessful}
>
Sign In
<T>Sign In</T>
</SubmitButton>
</AuthForm>
</div>

<p className="text-center text-sm text-gray-600 dark:text-zinc-400">
{"Don't have an account? "}
<Link
href="/register"
className="font-semibold text-gray-800 hover:underline dark:text-zinc-200"
>
Sign up
</Link>
</p>
<T>
<p className="text-center text-sm text-gray-600 dark:text-zinc-400">
{"Don't have an account? "}
<Link
href="/register"
className="font-semibold text-gray-800 hover:underline dark:text-zinc-200"
>
Sign up
</Link>
</p>
</T>
</div>
</div>
);
Expand Down
37 changes: 22 additions & 15 deletions apps/snow-leopard/app/(auth)/register/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { T } from 'gt-next';
import { authClient } from '@/lib/auth-client';
import { toast } from '@/components/toast';
import { AuthForm } from '@/components/auth-form';
Expand Down Expand Up @@ -92,10 +93,14 @@ export default function RegisterPage() {
<div className="flex h-dvh w-screen items-start pt-12 md:pt-0 md:items-center justify-center bg-background">
<div className="w-full max-w-md overflow-hidden rounded-2xl flex flex-col gap-12">
<div className="flex flex-col items-center justify-center gap-2 px-8 text-center">
<h3 className="text-xl font-semibold dark:text-zinc-50">Sign Up</h3>
<p className="text-sm text-gray-500 dark:text-zinc-400">
Create your account with email and password
</p>
<T>
<h3 className="text-xl font-semibold dark:text-zinc-50">Sign Up</h3>
</T>
<T>
<p className="text-sm text-gray-500 dark:text-zinc-400">
Create your account with email and password
</p>
</T>
</div>

<div className="px-8 flex flex-col gap-6">
Expand All @@ -112,22 +117,24 @@ export default function RegisterPage() {
<SubmitButton
isSuccessful={isSuccessful}
>
Sign Up
<T>Sign Up</T>
</SubmitButton>
</AuthForm>
</div>

<div className="text-center">
<p className="text-sm text-gray-600 dark:text-zinc-400">
{'Already have an account? '}
<Link
href="/login"
className="font-semibold text-gray-800 hover:underline dark:text-zinc-200"
>
Sign in
</Link>
{' instead.'}
</p>
<T>
<p className="text-sm text-gray-600 dark:text-zinc-400">
{'Already have an account? '}
<Link
href="/login"
className="font-semibold text-gray-800 hover:underline dark:text-zinc-200"
>
Sign in
</Link>
{' instead.'}
</p>
</T>
</div>
</div>
</div>
Expand Down
3 changes: 2 additions & 1 deletion apps/snow-leopard/app/[author]/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { notFound } from 'next/navigation';
import { db } from '@snow-leopard/db';
import * as schema from '@snow-leopard/db';
import { eq, and } from 'drizzle-orm';
import { T } from 'gt-next';
import { Blog } from '@/components/blog';
import AIChatWidget from '@/components/ai-chat-widget';
import ThemeToggle from '@/components/theme-toggle';
Expand Down Expand Up @@ -36,7 +37,7 @@ export default async function Page({ params }: any) {
<ThemeToggle />
<Link href="/register">
<Button variant="outline" className="fixed top-4 right-4 z-50">
Sign up to Snow Leopard
<T>Sign up to Snow Leopard</T>
</Button>
</Link>
<Blog
Expand Down
1 change: 0 additions & 1 deletion apps/snow-leopard/app/api/chat/actions/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
deleteChatById,
saveChat,
saveMessages,
getMessagesByChatId,
getMessageById,
deleteMessagesByChatIdAfterTimestamp,
} from '@/lib/db/queries';
Expand Down
6 changes: 4 additions & 2 deletions apps/snow-leopard/app/api/chat/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {
streamText,
smoothStream,
} from 'ai';
import { systemPrompt } from '@/lib/ai/prompts';
import { getSystemPrompt } from '@/lib/ai/prompts';
import { getGT } from 'gt-next/server';
import {
deleteChatById,
getChatById,
Expand Down Expand Up @@ -52,8 +53,9 @@ async function createEnhancedSystemPrompt({
applyStyle?: boolean;
availableTools?: Array<'createDocument'|'streamingDocument'|'updateDocument'|'webSearch'>;
}) {
const t = await getGT();

let basePrompt = systemPrompt({ selectedChatModel, availableTools });
let basePrompt = getSystemPrompt({ selectedChatModel, availableTools, t });
let contextAdded = false;

if (customInstructions) {
Expand Down
10 changes: 6 additions & 4 deletions apps/snow-leopard/app/api/document/actions/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,28 @@ import { NextRequest, NextResponse } from 'next/server';
import { auth } from "@/lib/auth";
import { headers } from "next/headers";
import { updateDocumentPublishSettings, getActiveSubscriptionByUserId } from "@/lib/db/queries";
import { getGT } from 'gt-next/server';

export async function publishDocument(request: NextRequest, body: any): Promise<NextResponse> {
const t = await getGT();
const readonlyHeaders = await headers();
const requestHeaders = new Headers(readonlyHeaders);
const session = await auth.api.getSession({ headers: requestHeaders });
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
return NextResponse.json({ error: t('Unauthorized') }, { status: 401 });
}
const userId = session.user.id;

if (process.env.STRIPE_ENABLED === 'true') {
const subscription = await getActiveSubscriptionByUserId({ userId });
if (!subscription) {
return NextResponse.json({ error: 'Payment Required: publishing is pro-only' }, { status: 402 });
return NextResponse.json({ error: t('Payment Required: publishing is pro-only') }, { status: 402 });
}
}

const { id: documentId, visibility, author, style, slug } = body;
if (!documentId || !slug) {
return NextResponse.json({ error: 'Invalid parameters' }, { status: 400 });
return NextResponse.json({ error: t('Invalid parameters') }, { status: 400 });
}

try {
Expand All @@ -32,6 +34,6 @@ export async function publishDocument(request: NextRequest, body: any): Promise<
if (typeof error?.message === 'string' && error.message.toLowerCase().includes('already published')) {
return NextResponse.json({ error: error.message }, { status: 409 });
}
return NextResponse.json({ error: error.message || 'Failed to update publish settings' }, { status: 500 });
return NextResponse.json({ error: error.message || t('Failed to update publish settings') }, { status: 500 });
}
}
13 changes: 7 additions & 6 deletions apps/snow-leopard/app/api/document/actions/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
getCurrentDocumentByTitle,
getDocumentById
} from '@/lib/db/queries'; // Import Drizzle queries
import { tx } from 'gt-next/server';

/**
* Gets a file by path - attempts to match ID first, then title.
Expand All @@ -21,7 +22,7 @@ export async function getFileByPath(path: string) {
const session = await auth.api.getSession({ headers: requestHeaders });

if (!session?.user?.id) {
throw new Error('Unauthorized');
throw new Error(await tx('Unauthorized'));
}
const userId = session.user.id;

Expand Down Expand Up @@ -84,7 +85,7 @@ export async function searchDocuments({

if (!session?.user?.id) {
console.warn('[Document Search] Unauthorized search request');
throw new Error('Unauthorized');
throw new Error(await tx('Unauthorized'));
}
const userId = session.user.id;

Expand All @@ -99,11 +100,11 @@ export async function searchDocuments({
});

// Format results for the mention UI (same logic)
const results = documents?.map(doc => ({
const results = await Promise.all(documents?.map(async doc => ({
id: doc.id,
title: doc.title || 'Untitled Document',
title: doc.title || await tx('Untitled Document'),
type: 'document' // Assuming a type identifier is needed
})) || [];
})) || []);

return {
results,
Expand All @@ -114,7 +115,7 @@ export async function searchDocuments({
console.error('[Document Search] Error executing search:', error);
return {
results: [],
error: 'Failed to search documents'
error: await tx('Failed to search documents')
// Optionally include the original query in the error response
// query
};
Expand Down
6 changes: 4 additions & 2 deletions apps/snow-leopard/app/api/document/publish/route.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { NextRequest, NextResponse } from 'next/server';
import { publishDocument } from '../actions/publish';
import { getGT } from 'gt-next/server';

export async function POST(request: NextRequest) {
const t = await getGT();
let body: any;
try {
body = await request.json();
} catch (error: any) {
console.error('[API /document/publish] Invalid JSON:', error);
return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 });
return NextResponse.json({ error: t('Invalid JSON body') }, { status: 400 });
}
try {
return await publishDocument(request, body);
} catch (error: any) {
console.error('[API /document/publish] Error handling publish:', error);
return NextResponse.json({ error: error.message || 'Error publishing document' }, { status: 500 });
return NextResponse.json({ error: error.message || t('Error publishing document') }, { status: 500 });
}
}
11 changes: 6 additions & 5 deletions apps/snow-leopard/app/api/messages/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { NextResponse } from 'next/server';
import { auth } from "@/lib/auth";
import { headers } from 'next/headers';
import { getMessagesByChatId, getChatById } from '@/lib/db/queries';
import { tx } from 'gt-next/server';

export async function GET(request: Request) {
try {
Expand All @@ -11,23 +12,23 @@ export async function GET(request: Request) {

if (!session?.user?.id) {
console.error('Session error in /api/messages');
return NextResponse.json({ error: 'Authentication error' }, { status: 401 });
return NextResponse.json({ error: tx('Authentication error') }, { status: 401 });
}
const userId = session.user.id;

const { searchParams } = new URL(request.url);
const chatId = searchParams.get('chatId');

if (!chatId) {
return NextResponse.json({ error: 'Chat ID is required' }, { status: 400 });
return NextResponse.json({ error: tx('Chat ID is required') }, { status: 400 });
}

const chat = await getChatById({ id: chatId });
if (!chat) {
return NextResponse.json({ error: 'Chat not found' }, { status: 404 });
return NextResponse.json({ error: tx('Chat not found') }, { status: 404 });
}
if (chat.userId !== userId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 403 });
return NextResponse.json({ error: tx('Unauthorized') }, { status: 403 });
}

const messages = await getMessagesByChatId({ id: chatId });
Expand All @@ -43,6 +44,6 @@ export async function GET(request: Request) {

} catch (error) {
console.error('Error fetching messages:', error);
return NextResponse.json({ error: 'Error fetching messages' }, { status: 500 });
return NextResponse.json({ error: tx('Error fetching messages') }, { status: 500 });
}
}
Loading