diff --git a/LOCADEX.md b/LOCADEX.md new file mode 100644 index 000000000..54494e055 --- /dev/null +++ b/LOCADEX.md @@ -0,0 +1,25 @@ +# 🌐 Locadex i18n + +This repository is configured to use Locadex for automated internationalization. + +## Configuration: + +- **Working Directory**: `.` +- **Branch Prefix**: `locadex/` +- **Configured Locales**: `en-US`, `fr` +- **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) diff --git a/app/_hooks/[section]/layout.tsx b/app/_hooks/[section]/layout.tsx index 7ae121bc7..63ffaa83d 100644 --- a/app/_hooks/[section]/layout.tsx +++ b/app/_hooks/[section]/layout.tsx @@ -3,6 +3,7 @@ import db from '#/lib/db'; import { ClickCounter } from '#/ui/click-counter'; import { Tabs } from '#/ui/tabs'; import { notFound } from 'next/navigation'; +import { getGT } from 'gt-next/server'; export default async function Layout({ children, @@ -18,6 +19,7 @@ export default async function Layout({ } const categories = db.category.findMany({ where: { section: section.id } }); + const t = await getGT(); return (
@@ -25,7 +27,7 @@ export default async function Layout({ ({ text: x.name, slug: x.slug })), ]} /> diff --git a/app/_hooks/[section]/page.tsx b/app/_hooks/[section]/page.tsx index 0215186a0..5cabc81e1 100644 --- a/app/_hooks/[section]/page.tsx +++ b/app/_hooks/[section]/page.tsx @@ -1,6 +1,7 @@ import { HooksClient } from '#/app/_hooks/_components/router-context'; import db from '#/lib/db'; import { notFound } from 'next/navigation'; +import { T, Var } from 'gt-next'; export default async function Page({ params, @@ -15,9 +16,11 @@ export default async function Page({ return (
-

- All {section.name} -

+ +

+ All {section.name} +

+
diff --git a/app/_hooks/_components/router-context-layout.tsx b/app/_hooks/_components/router-context-layout.tsx index ceb755059..ad2a04c7b 100644 --- a/app/_hooks/_components/router-context-layout.tsx +++ b/app/_hooks/_components/router-context-layout.tsx @@ -5,13 +5,14 @@ import { useSelectedLayoutSegment, useSelectedLayoutSegments, } from 'next/navigation'; +import { T } from 'gt-next'; export function LayoutHooks() { const selectedLayoutSegment = useSelectedLayoutSegment(); const selectedLayoutSegments = useSelectedLayoutSegments(); return selectedLayoutSegment ? ( - + Client Component Hooks]} size="small">
           {JSON.stringify(
diff --git a/app/_hooks/_components/router-context.tsx b/app/_hooks/_components/router-context.tsx
index 3db938496..4dcf3bad7 100644
--- a/app/_hooks/_components/router-context.tsx
+++ b/app/_hooks/_components/router-context.tsx
@@ -8,6 +8,7 @@ import {
   useSelectedLayoutSegment,
   useSelectedLayoutSegments,
 } from 'next/navigation';
+import { useGT } from 'gt-next';
 
 export function HooksClient() {
   const pathname = usePathname();
@@ -15,9 +16,10 @@ export function HooksClient() {
   const selectedLayoutSegment = useSelectedLayoutSegment();
   const selectedLayoutSegments = useSelectedLayoutSegments();
   const searchParams = useSearchParams();
+  const t = useGT();
 
   return (
-    
+    
       
           {JSON.stringify(
diff --git a/app/_hooks/layout.tsx b/app/_hooks/layout.tsx
index 9c0beaaba..4eaa6888e 100644
--- a/app/_hooks/layout.tsx
+++ b/app/_hooks/layout.tsx
@@ -3,6 +3,7 @@ import db from '#/lib/db';
 import { ClickCounter } from '#/ui/click-counter';
 import { Tabs } from '#/ui/tabs';
 import React from 'react';
+import { getGT } from 'gt-next/server';
 
 // export function generateMetadata() {
 //   const demo = db.demo.find({ where: { slug: 'hooks' } });
@@ -21,6 +22,7 @@ export default async function Layout({
 }: {
   children: React.ReactNode;
 }) {
+  const t = await getGT();
   const sections = db.section.findMany();
 
   return (
@@ -29,7 +31,7 @@ export default async function Layout({
          ({ text: x.name, slug: x.slug })),
           ]}
         />
diff --git a/app/_internal/_data.ts b/app/_internal/_data.ts
index 0d5c8c786..b0fade4bd 100644
--- a/app/_internal/_data.ts
+++ b/app/_internal/_data.ts
@@ -34,148 +34,152 @@ export type Demo = {
 
 export type DemoCategory = { name: string; items: Demo[] };
 
-const sections: Section[] = [
-  { id: '1', name: 'Clothing', slug: 'clothing', categories: ['1', '2', '3'] },
+const getSections = (t: (content: string) => string): Section[] => [
+  { id: '1', name: t('Clothing'), slug: 'clothing', categories: ['1', '2', '3'] },
   {
     id: '2',
-    name: 'Electronics',
+    name: t('Electronics'),
     slug: 'electronics',
     categories: ['4', '5', '6'],
   },
-  { id: '3', name: 'Sports', slug: 'sports', categories: ['7', '8', '9'] },
+  { id: '3', name: t('Sports'), slug: 'sports', categories: ['7', '8', '9'] },
 ];
 
-const categories: Category[] = [
-  { id: '1', name: 'Tops', slug: 'tops', section: '1', products: ['1'] },
-  { id: '2', name: 'Shorts', slug: 'shorts', section: '1', products: ['2'] },
-  { id: '3', name: 'Shoes', slug: 'shoes', section: '1', products: ['3'] },
-  { id: '4', name: 'Phones', slug: 'phones', section: '2', products: ['4'] },
-  { id: '5', name: 'Laptops', slug: 'laptops', section: '2', products: ['5'] },
-  { id: '6', name: 'Tablets', slug: 'tablets', section: '2', products: ['6'] },
-  { id: '7', name: 'Balls', slug: 'balls', section: '3', products: ['7'] },
+const getCategories = (t: (content: string) => string): Category[] => [
+  { id: '1', name: t('Tops'), slug: 'tops', section: '1', products: ['1'] },
+  { id: '2', name: t('Shorts'), slug: 'shorts', section: '1', products: ['2'] },
+  { id: '3', name: t('Shoes'), slug: 'shoes', section: '1', products: ['3'] },
+  { id: '4', name: t('Phones'), slug: 'phones', section: '2', products: ['4'] },
+  { id: '5', name: t('Laptops'), slug: 'laptops', section: '2', products: ['5'] },
+  { id: '6', name: t('Tablets'), slug: 'tablets', section: '2', products: ['6'] },
+  { id: '7', name: t('Balls'), slug: 'balls', section: '3', products: ['7'] },
   {
     id: '8',
-    name: 'Equipment',
+    name: t('Equipment'),
     slug: 'equipment',
     section: '3',
     products: ['8'],
   },
   {
     id: '9',
-    name: 'Accessories',
+    name: t('Accessories'),
     slug: 'accessories',
     section: '3',
     products: ['9'],
   },
 ];
 
-const products: Product[] = [
-  { id: '1', name: 'Top', image: 'top.png', category: '1' },
-  { id: '2', name: 'Shorts', image: 'shorts.png', category: '2' },
-  { id: '3', name: 'Shoes', image: 'shoes.png', category: '3' },
+const getProducts = (t: (content: string) => string): Product[] => [
+  { id: '1', name: t('Top'), image: 'top.png', category: '1' },
+  { id: '2', name: t('Shorts'), image: 'shorts.png', category: '2' },
+  { id: '3', name: t('Shoes'), image: 'shoes.png', category: '3' },
 
-  { id: '4', name: 'Phone', image: 'phone.png', category: '4' },
-  { id: '5', name: 'Laptop', image: 'laptop.png', category: '5' },
-  { id: '6', name: 'Tablet', image: 'tablet.png', category: '6' },
-  { id: '7', name: 'Basketball', image: 'balls.png', category: '7' },
-  { id: '8', name: 'Weights', image: 'weights.png', category: '8' },
-  { id: '9', name: 'Gloves', image: 'gloves.png', category: '9' },
+  { id: '4', name: t('Phone'), image: 'phone.png', category: '4' },
+  { id: '5', name: t('Laptop'), image: 'laptop.png', category: '5' },
+  { id: '6', name: t('Tablet'), image: 'tablet.png', category: '6' },
+  { id: '7', name: t('Basketball'), image: 'balls.png', category: '7' },
+  { id: '8', name: t('Weights'), image: 'weights.png', category: '8' },
+  { id: '9', name: t('Gloves'), image: 'gloves.png', category: '9' },
 ];
 
-const demos = [
+const getDemos = (t: (content: string) => string) => [
   {
-    name: 'Layouts',
+    name: t('Layouts'),
     items: [
       {
         slug: 'layouts',
-        name: 'Nested Layouts',
-        description: 'Create UI that is shared across routes',
+        name: t('Nested Layouts'),
+        description: t('Create UI that is shared across routes'),
       },
       {
         slug: 'route-groups',
-        name: 'Route Groups',
-        description: 'Organize routes without affecting URL paths',
+        name: t('Route Groups'),
+        description: t('Organize routes without affecting URL paths'),
       },
       {
         slug: 'parallel-routes',
-        name: 'Parallel Routes',
-        description: 'Render multiple pages in the same layout',
+        name: t('Parallel Routes'),
+        description: t('Render multiple pages in the same layout'),
       },
     ],
   },
   {
-    name: 'File Conventions',
+    name: t('File Conventions'),
     items: [
       {
         slug: 'loading',
-        name: 'Loading',
-        description:
-          'Create meaningful Loading UI for specific parts of an app',
+        name: t('Loading'),
+        description: t('Create meaningful Loading UI for specific parts of an app'),
       },
       {
         slug: 'error',
-        name: 'Error',
-        description: 'Create Error UI for specific parts of an app',
+        name: t('Error'),
+        description: t('Create Error UI for specific parts of an app'),
       },
       {
         slug: 'not-found',
-        name: 'Not Found',
-        description: 'Create Not Found UI for specific parts of an app',
+        name: t('Not Found'),
+        description: t('Create Not Found UI for specific parts of an app'),
       },
     ],
   },
   {
-    name: 'Caching',
+    name: t('Caching'),
     items: [
       {
         slug: 'cached-routes',
-        name: 'Cached Route Segments',
-        nav_title: 'Cached Routes',
-        description: 'Cache the rendered output of a route segment',
+        name: t('Cached Route Segments'),
+        nav_title: t('Cached Routes'),
+        description: t('Cache the rendered output of a route segment'),
       },
       {
         slug: 'cached-components',
-        name: 'Cached React Server Components',
-        nav_title: 'Cached Components',
-        description:
-          'Cache the rendered output of an individual React Server Component',
+        name: t('Cached React Server Components'),
+        nav_title: t('Cached Components'),
+        description: t('Cache the rendered output of an individual React Server Component'),
       },
       {
         slug: 'cached-functions',
-        name: 'Cached Functions',
-        description: 'Cache the computed result of a regular function',
+        name: t('Cached Functions'),
+        description: t('Cache the computed result of a regular function'),
       },
     ],
   },
   {
-    name: 'APIs',
+    name: t('APIs'),
     items: [
       {
         slug: 'use-link-status',
-        name: 'useLinkStatus',
-        description: 'Create inline visual feedback for link interactions',
+        name: t('useLinkStatus'),
+        description: t('Create inline visual feedback for link interactions'),
       },
     ],
   },
   {
-    name: 'Misc',
+    name: t('Misc'),
     items: [
       {
         slug: 'view-transitions',
-        name: 'View Transitions',
-        description:
-          'Use animations to help users understand the relationship between the two views',
+        name: t('View Transitions'),
+        description: t('Use animations to help users understand the relationship between the two views'),
       },
       {
         slug: 'context',
-        name: 'Client Context',
-        description:
-          'Pass context between Client Components that cross Server/Client Component boundary',
+        name: t('Client Context'),
+        description: t('Pass context between Client Components that cross Server/Client Component boundary'),
       },
     ],
   },
 ] as const satisfies DemoCategory[];
 
+const demos = getDemos(() => '');
 export type DemoSlug = (typeof demos)[number]['items'][number]['slug'];
 
-export const data = { sections, categories, products, demos };
+export const getData = (t: (content: string) => string) => ({ 
+  sections: getSections(t), 
+  categories: getCategories(t), 
+  products: getProducts(t), 
+  demos: getDemos(t) 
+});
+
+export const data = { sections: getSections(() => ''), categories: getCategories(() => ''), products: getProducts(() => ''), demos };
diff --git a/app/_patterns/active-links/community/page.tsx b/app/_patterns/active-links/community/page.tsx
index c068b02d3..c779a4a36 100644
--- a/app/_patterns/active-links/community/page.tsx
+++ b/app/_patterns/active-links/community/page.tsx
@@ -1,3 +1,9 @@
+import { T } from 'gt-next';
+
 export default function Page() {
-  return 

Community

; + return ( + +

Community

+
+ ); } diff --git a/app/_patterns/active-links/layout.tsx b/app/_patterns/active-links/layout.tsx index eba40dc2d..ac032d7ae 100644 --- a/app/_patterns/active-links/layout.tsx +++ b/app/_patterns/active-links/layout.tsx @@ -1,14 +1,16 @@ import { NavLinks } from '#/app/_patterns/active-links/_components/nav-links'; import Image from 'next/image'; import Link from 'next/link'; +import { getGT } from 'gt-next/server'; -export default function Layout({ children }: { children: React.ReactNode }) { +export default async function Layout({ children }: { children: React.ReactNode }) { + const t = await getGT(); // Hardcoded links or fetched from db const links = [ - { href: '/patterns/active-links', name: 'Home' }, - { href: '/patterns/active-links/profile', name: 'Profile' }, - { href: '/patterns/active-links/community', name: 'Community' }, - { href: '/patterns/active-links/settings', name: 'Settings' }, + { href: '/patterns/active-links', name: t('Home') }, + { href: '/patterns/active-links/profile', name: t('Profile') }, + { href: '/patterns/active-links/community', name: t('Community') }, + { href: '/patterns/active-links/settings', name: t('Settings') }, ]; return ( @@ -21,7 +23,7 @@ export default function Layout({ children }: { children: React.ReactNode }) { className="rounded-full" width={40} height={40} - alt="User" + alt={t('User')} />
diff --git a/app/_patterns/active-links/page.tsx b/app/_patterns/active-links/page.tsx index 17c847eeb..324f10d0b 100644 --- a/app/_patterns/active-links/page.tsx +++ b/app/_patterns/active-links/page.tsx @@ -1,3 +1,9 @@ +import { T } from 'gt-next'; + export default function Page() { - return

Home

; + return ( + +

Home

+
+ ); } diff --git a/app/_patterns/active-links/profile/page.tsx b/app/_patterns/active-links/profile/page.tsx index d22888e45..65203c047 100644 --- a/app/_patterns/active-links/profile/page.tsx +++ b/app/_patterns/active-links/profile/page.tsx @@ -1,3 +1,9 @@ +import { T } from 'gt-next'; + export default function Page() { - return

Profile

; + return ( + +

Profile

+
+ ); } diff --git a/app/_patterns/active-links/settings/page.tsx b/app/_patterns/active-links/settings/page.tsx index ab769269f..748480208 100644 --- a/app/_patterns/active-links/settings/page.tsx +++ b/app/_patterns/active-links/settings/page.tsx @@ -1,3 +1,9 @@ +import { T } from 'gt-next'; + export default function Page() { - return

Settings

; + return ( + +

Settings

+
+ ); } diff --git a/app/_patterns/breadcrumbs/@breadcrumbs/page.tsx b/app/_patterns/breadcrumbs/@breadcrumbs/page.tsx index aed52e8ca..8e73f5ad5 100644 --- a/app/_patterns/breadcrumbs/@breadcrumbs/page.tsx +++ b/app/_patterns/breadcrumbs/@breadcrumbs/page.tsx @@ -1,11 +1,13 @@ import { Breadcrumbs } from '#/app/_patterns/breadcrumbs/_components/breadcrumbs'; +import { getGT } from 'gt-next/server'; // Note: Next.js doesn't currently support optional catchAll segments in parallel routes. // In the mean time, this file will match the "/breadcrumb" route. -export default function Page() { +export default async function Page() { + const t = await getGT(); const items = [ - { text: 'Patterns', href: '/patterns' }, - { text: 'Breadcrumbs', href: '/patterns/breadcrumbs' }, + { text: t('Patterns'), href: '/patterns' }, + { text: t('Breadcrumbs'), href: '/patterns/breadcrumbs' }, ]; return ; } diff --git a/app/_patterns/breadcrumbs/[section]/[category]/page.tsx b/app/_patterns/breadcrumbs/[section]/[category]/page.tsx index a20e0f21e..7ed4e7371 100644 --- a/app/_patterns/breadcrumbs/[section]/[category]/page.tsx +++ b/app/_patterns/breadcrumbs/[section]/[category]/page.tsx @@ -1,6 +1,7 @@ import db from '#/lib/db'; import { SkeletonCard } from '#/ui/skeleton-card'; import { notFound } from 'next/navigation'; +import { T, Var } from 'gt-next'; export default async function Page({ params, @@ -15,7 +16,9 @@ export default async function Page({ return (
-

{category.name}

+ +

{category.name}

+
{Array.from({ length: 10 }).map((_, i) => ( diff --git a/app/_patterns/breadcrumbs/[section]/layout.tsx b/app/_patterns/breadcrumbs/[section]/layout.tsx index 54698e137..32955bab3 100644 --- a/app/_patterns/breadcrumbs/[section]/layout.tsx +++ b/app/_patterns/breadcrumbs/[section]/layout.tsx @@ -1,6 +1,7 @@ import db from '#/lib/db'; import { Tabs } from '#/ui/tabs'; import { notFound } from 'next/navigation'; +import { T } from 'gt-next'; export default async function Layout({ children, @@ -23,7 +24,7 @@ export default async function Layout({ All }, ...categories.map((x) => ({ text: x.name, slug: x.slug })), ]} /> diff --git a/app/_patterns/breadcrumbs/[section]/page.tsx b/app/_patterns/breadcrumbs/[section]/page.tsx index 489775e63..f705bf706 100644 --- a/app/_patterns/breadcrumbs/[section]/page.tsx +++ b/app/_patterns/breadcrumbs/[section]/page.tsx @@ -1,6 +1,7 @@ import db from '#/lib/db'; import { SkeletonCard } from '#/ui/skeleton-card'; import { notFound } from 'next/navigation'; +import { T, Var } from 'gt-next'; export default async function Page({ params, @@ -15,9 +16,11 @@ export default async function Page({ return (
-

- All {section.name} -

+ +

+ All {section.name} +

+
{Array.from({ length: 9 }).map((_, i) => ( diff --git a/app/_patterns/breadcrumbs/layout.tsx b/app/_patterns/breadcrumbs/layout.tsx index 8b2a77519..7dce70fdb 100644 --- a/app/_patterns/breadcrumbs/layout.tsx +++ b/app/_patterns/breadcrumbs/layout.tsx @@ -2,6 +2,7 @@ import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { Tabs } from '#/ui/tabs'; import React from 'react'; +import { getGT } from 'gt-next/server'; // export function generateMetadata() { // const demo = db.demo.find({ where: { slug: 'breadcrumbs' } }); @@ -22,16 +23,17 @@ export default async function Layout({ breadcrumbs: React.ReactNode; }) { const categories = db.category.findMany(); + const t = await getGT(); return (
- {breadcrumbs} + {breadcrumbs}
({ text: x.name, slug: x.slug })), ]} /> diff --git a/app/_patterns/page.tsx b/app/_patterns/page.tsx index 7c8443c25..0306a499b 100644 --- a/app/_patterns/page.tsx +++ b/app/_patterns/page.tsx @@ -1,28 +1,33 @@ import { ExternalLink } from '#/ui/external-link'; import Link from 'next/link'; +import { T } from 'gt-next'; +import { getGT } from 'gt-next/server'; -const items = [ - { - name: 'Active links', - slug: 'active-links', - description: 'Update the style of the current active link', - }, - { - name: 'Breadcrumbs', - slug: 'breadcrumbs', - description: 'Shared server-side Breadcrumb UI using Parallel Routes', - }, - { - name: 'Updating URL search params', - slug: 'search-params', - description: 'Update searchParams using `useRouter` and ``', - }, -]; - -export default function Page() { +export default async function Page() { + const t = await getGT(); + + const items = [ + { + name: t('Active links'), + slug: 'active-links', + description: t('Update the style of the current active link'), + }, + { + name: t('Breadcrumbs'), + slug: 'breadcrumbs', + description: t('Shared server-side Breadcrumb UI using Parallel Routes'), + }, + { + name: t('Updating URL search params'), + slug: 'search-params', + description: t('Update searchParams using `useRouter` and ``'), + }, + ]; return (
-

Patterns

+ +

Patterns

+
{items.map((item) => { @@ -48,7 +53,7 @@ export default function Page() {
- Code + Code
diff --git a/app/_patterns/search-params/page.tsx b/app/_patterns/search-params/page.tsx index 08f874b17..a22599a12 100644 --- a/app/_patterns/search-params/page.tsx +++ b/app/_patterns/search-params/page.tsx @@ -1,38 +1,48 @@ import { Boundary } from '#/ui/boundary'; import { ExternalLink } from '#/ui/external-link'; import { Suspense } from 'react'; +import { T, getGT } from 'gt-next/server'; import ActiveLink from './active-link'; import Client from './client'; -const options = [ - { name: 'Sort', value: 'sort', items: ['asc', 'desc'] }, - { name: 'Page', value: 'page', items: ['1', '2', '3'] }, - { name: 'Items Per Page', value: 'perPage', items: ['10', '25', '100'] }, -]; export const dynamic = 'force-dynamic'; export default async function Page(props: { searchParams: Promise }) { const searchParams = await props.searchParams; + const t = await getGT(); + + const options = [ + { name: t('Sort'), value: 'sort', items: ['asc', 'desc'] }, + { name: t('Page'), value: 'page', items: ['1', '2', '3'] }, + { name: t('Items Per Page'), value: 'perPage', items: ['10', '25', '100'] }, + ]; + return (
-

- Updating searchParams -

-

- The useSearchParams hook returns a read only version of{' '} - URLSearchParams. You can use{' '} - useRouter() or <Link> to set new{' '} - searchParams. After a navigation is performed, the current{' '} - page.js will receive an updated searchParams{' '} - prop. -

+ +

+ Updating searchParams +

+
+ +

+ The useSearchParams hook returns a read only version of{' '} + URLSearchParams. You can use{' '} + useRouter() or <Link> to set new{' '} + searchParams. After a navigation is performed, the current{' '} + page.js will receive an updated searchParams{' '} + prop. +

+
-

- Using useRouter() -

+ +

+ Using useRouter() +

+
@@ -40,15 +50,17 @@ export default async function Page(props: { searchParams: Promise }) {
- Docs + Docs
-

- Using <Link> -

+ +

+ Using <Link> +

+
{options.map((option) => { @@ -85,7 +97,7 @@ export default async function Page(props: { searchParams: Promise }) { - Docs + Docs
diff --git a/app/api/og/route.tsx b/app/api/og/route.tsx index d44ca64d2..e80e0d1da 100644 --- a/app/api/og/route.tsx +++ b/app/api/og/route.tsx @@ -3,15 +3,17 @@ import { ImageResponse } from 'next/og'; import type { ReactElement } from 'react'; import { join } from 'path'; import { readFile } from 'fs/promises'; +import { getGT } from 'gt-next/server'; export async function GET(req: NextRequest): Promise { try { const { searchParams } = new URL(req.url); const isLight = req.headers.get('Sec-CH-Prefers-Color-Scheme') === 'light'; + const t = await getGT(); const title = searchParams.has('title') ? searchParams.get('title') - : 'App Router Playground'; + : t('App Router Playground'); const file = await readFile(join(process.cwd(), './Inter-SemiBold.ttf')); const font = Uint8Array.from(file).buffer; @@ -60,7 +62,7 @@ export async function GET(req: NextRequest): Promise { // eslint-disable-next-line no-console console.log(e.message); - return new Response(`Failed to generate the image`, { status: 500 }); + return new Response(t('Failed to generate the image'), { status: 500 }); } } diff --git a/app/cached-components/layout.tsx b/app/cached-components/layout.tsx index a469982ab..c23dcaa6c 100644 --- a/app/cached-components/layout.tsx +++ b/app/cached-components/layout.tsx @@ -3,6 +3,7 @@ import { Boundary } from '#/ui/boundary'; import { Mdx } from '#/ui/codehike'; import React from 'react'; import readme from './readme.mdx'; +import { T } from 'gt-next'; export function generateMetadata() { const demo = db.demo.find({ where: { slug: 'cached-components' } }); @@ -20,12 +21,12 @@ export default async function Layout({ }) { return ( <> - + Demo} kind="solid" animateRerendering={false}> layout.tsx (statically inferred)} kind="solid" animateRerendering={false} > diff --git a/app/cached-components/page.tsx b/app/cached-components/page.tsx index 29fe9ee2d..6d2b714d7 100644 --- a/app/cached-components/page.tsx +++ b/app/cached-components/page.tsx @@ -1,10 +1,11 @@ import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { ProductCard } from '#/ui/product-card'; +import { T, Var } from 'gt-next'; export default async function Page() { return ( - + page.tsx (statically inferred)}> ); @@ -19,14 +20,16 @@ async function ProductList() { const products = db.product.findMany({ limit: 9 }); return ( - + <ProductList> (Cacheable)} size="small">
-

- All{' '} - - ({products.length}) - -

+ +

+ All{' '} + + ({products.length}) + +

+
{products.map((product) => (
-

- All{' '} - - ({products.length}) - -

+ +

+ All{' '} + + ({products.length}) + +

+
{products.map((product) => (
-

- All{' '} - - ({products.length}) - -

+ +

+ All{' '} + + ({products.length}) + +

+
{products.map((product) => ( { const [count, setCount] = useCounter(); @@ -18,7 +19,7 @@ const ContextClickCounter = () => { onClick={() => setCount(count + 1)} className="rounded-lg bg-gray-700 px-3 py-1 text-sm font-medium text-gray-100 tabular-nums hover:bg-gray-500 hover:text-white" > - {count} Clicks + {count} Clicks ); @@ -35,7 +36,7 @@ export const Counter = () => { animateRerendering={false} >
- {count} Clicks + {count} Clicks
); diff --git a/app/error/[section]/[category]/page.tsx b/app/error/[section]/[category]/page.tsx index 860776fe1..22a83dd70 100644 --- a/app/error/[section]/[category]/page.tsx +++ b/app/error/[section]/[category]/page.tsx @@ -5,6 +5,7 @@ import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { ProductCard } from '#/ui/product-card'; import BuggyButton from '#/app/error/_ui/buggy-button'; +import { T, Num } from 'gt-next'; export async function generateStaticParams() { const categories = db.category.findMany(); @@ -28,12 +29,14 @@ export default async function Page({
-

- All{' '} - - ({products.length}) - -

+ +

+ All{' '} + + ({products.length}) + +

+
diff --git a/app/error/[section]/error.tsx b/app/error/[section]/error.tsx index 0260c34a1..ed4bbe476 100644 --- a/app/error/[section]/error.tsx +++ b/app/error/[section]/error.tsx @@ -2,9 +2,12 @@ import { Boundary } from '#/ui/boundary'; import Button from '#/ui/button'; +import { T, useGT } from 'gt-next'; import React from 'react'; export default function Error({ error, reset }: any) { + const t = useGT(); + React.useEffect(() => { console.log('logging error:', error); }, [error]); @@ -12,10 +15,14 @@ export default function Error({ error, reset }: any) { return (
-

Error

+ +

Error

+
{error?.message}
- +
diff --git a/app/error/[section]/layout.tsx b/app/error/[section]/layout.tsx index b70a750e5..6ef626ac5 100644 --- a/app/error/[section]/layout.tsx +++ b/app/error/[section]/layout.tsx @@ -3,6 +3,7 @@ import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { Tabs } from '#/ui/tabs'; import { Metadata } from 'next'; +import { getGT } from 'gt-next/server'; export async function generateMetadata(): Promise { const demo = db.demo.find({ where: { slug: 'error' } }); @@ -28,13 +29,14 @@ export default async function Layout({ const demo = db.demo.find({ where: { slug: 'error' } }); const categories = db.category.findMany({ where: { section: section.id } }); + const t = await getGT(); return ( ({ text: x.name, slug: x.slug })), ]} /> diff --git a/app/error/[section]/page.tsx b/app/error/[section]/page.tsx index 7896fc821..9a78de133 100644 --- a/app/error/[section]/page.tsx +++ b/app/error/[section]/page.tsx @@ -4,6 +4,7 @@ import { notFound } from 'next/navigation'; import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { ProductCard } from '#/ui/product-card'; +import { T, Num, getGT } from 'gt-next/server'; import BuggyButton from '#/app/error/_ui/buggy-button'; export async function generateStaticParams() { @@ -23,17 +24,20 @@ export default async function Page({ } const products = db.product.findMany({ where: { section: section.id } }); + const t = await getGT(); return (
-

- All{' '} - - ({products.length}) - -

+ +

+ All{' '} + + ({products.length}) + +

+
diff --git a/app/error/_ui/buggy-button.tsx b/app/error/_ui/buggy-button.tsx index b2190b2a2..0ab62a82f 100644 --- a/app/error/_ui/buggy-button.tsx +++ b/app/error/_ui/buggy-button.tsx @@ -2,6 +2,7 @@ import Button from '#/ui/button'; import React from 'react'; +import { T } from 'gt-next'; export default function BuggyButton() { const [clicked, setClicked] = React.useState(false); @@ -17,7 +18,7 @@ export default function BuggyButton() { setClicked(true); }} > - Trigger Error + Trigger Error ); } diff --git a/app/error/error.tsx b/app/error/error.tsx index 7e61c2dde..343d269ae 100644 --- a/app/error/error.tsx +++ b/app/error/error.tsx @@ -3,6 +3,7 @@ import { Boundary } from '#/ui/boundary'; import Button from '#/ui/button'; import React from 'react'; +import { T } from 'gt-next'; export default function Error({ error, reset }: any) { React.useEffect(() => { @@ -12,10 +13,10 @@ export default function Error({ error, reset }: any) { return (
-

Error

+

Error

{error?.message}
- +
diff --git a/app/error/page.tsx b/app/error/page.tsx index a797e0974..d7f2490f7 100644 --- a/app/error/page.tsx +++ b/app/error/page.tsx @@ -4,6 +4,7 @@ import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { ProductCard } from '#/ui/product-card'; import BuggyButton from '#/app/error/_ui/buggy-button'; +import { T, Var } from 'gt-next'; export default async function Page() { const products = db.product.findMany({ limit: 9 }); @@ -12,12 +13,14 @@ export default async function Page() {
-

- All{' '} - - ({products.length}) - -

+ +

+ All{' '} + + ({products.length}) + +

+
diff --git a/app/layout.tsx b/app/layout.tsx index a71156b41..979db62fd 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,43 +1,46 @@ import '#/styles/globals.css'; -import db from '#/lib/db'; +import { createDb } from '#/lib/db'; import Byline from '#/ui/byline'; import { GlobalNav } from '#/ui/global-nav'; import { Metadata } from 'next'; import { Geist, Geist_Mono } from 'next/font/google'; +import { getLocale, getGT } from "gt-next/server"; +import { GTProvider } from "gt-next"; const geistSans = Geist({ variable: '--font-geist-sans', subsets: ['latin'] }); const geistMono = Geist_Mono({ variable: '--font-geist-mono', - subsets: ['latin'], + subsets: ['latin'] }); -export const metadata: Metadata = { - title: { default: 'Next.js Playground', template: '%s | Next.js Playground' }, - metadataBase: new URL('https://app-router.vercel.app'), - description: - 'A playground to explore Next.js features such as nested layouts, instant loading states, streaming, and component level data fetching.', - openGraph: { - title: 'Next.js Playground', - description: - 'A playground to explore Next.js features such as nested layouts, instant loading states, streaming, and component level data fetching.', - images: [`/api/og?title=Next.js Playground`], - }, - twitter: { card: 'summary_large_image' }, -}; - -export default function RootLayout({ - children, -}: { - children: React.ReactNode; -}) { +export async function generateMetadata(): Promise { + const t = await getGT(); + return { + title: { default: t('Next.js Playground'), template: `%s | ${t('Next.js Playground')}` }, + metadataBase: new URL('https://app-router.vercel.app'), + description: t('A playground to explore Next.js features such as nested layouts, instant loading states, streaming, and component level data fetching.'), + openGraph: { + title: t('Next.js Playground'), + description: t('A playground to explore Next.js features such as nested layouts, instant loading states, streaming, and component level data fetching.'), + images: [`/api/og?title=${t('Next.js Playground')}`] + }, + twitter: { card: 'summary_large_image' } + }; +} + +export default async function RootLayout({ + children +}: {children: React.ReactNode;}) { + const t = await getGT(); + const db = createDb(t); const demos = db.demo.findMany(); return ( - + + className={`overflow-y-scroll bg-gray-950 font-sans ${geistSans.variable} ${geistMono.variable} antialiased`}> +
@@ -49,7 +52,7 @@ export default function RootLayout({
- + ); -} +} \ No newline at end of file diff --git a/app/layouts/[section]/[category]/page.tsx b/app/layouts/[section]/[category]/page.tsx index 94b375fc0..ed79f2a36 100644 --- a/app/layouts/[section]/[category]/page.tsx +++ b/app/layouts/[section]/[category]/page.tsx @@ -4,6 +4,7 @@ import { notFound } from 'next/navigation'; import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { ProductCard } from '#/ui/product-card'; +import { T, Num } from 'gt-next'; export async function generateStaticParams() { const categories = db.category.findMany(); @@ -26,12 +27,14 @@ export default async function Page({ return (
-

- All{' '} - - ({products.length}) - -

+ +

+ All{' '} + + ({products.length}) + +

+
{products.map((product) => ( diff --git a/app/layouts/[section]/layout.tsx b/app/layouts/[section]/layout.tsx index d1ce93907..1579b8289 100644 --- a/app/layouts/[section]/layout.tsx +++ b/app/layouts/[section]/layout.tsx @@ -4,6 +4,7 @@ import { notFound } from 'next/navigation'; import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { Tabs } from '#/ui/tabs'; +import { getGT } from 'gt-next/server'; export default async function Layout({ params, @@ -20,13 +21,14 @@ export default async function Layout({ const demo = db.demo.find({ where: { slug: 'layouts' } }); const categories = db.category.findMany({ where: { section: section?.id } }); + const t = await getGT(); return ( ({ text: x.name, slug: x.slug })), ]} /> diff --git a/app/layouts/[section]/page.tsx b/app/layouts/[section]/page.tsx index b2acf2204..bbff4f87e 100644 --- a/app/layouts/[section]/page.tsx +++ b/app/layouts/[section]/page.tsx @@ -4,6 +4,7 @@ import { notFound } from 'next/navigation'; import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { ProductCard } from '#/ui/product-card'; +import { T, Num, getGT } from 'gt-next/server'; export async function generateStaticParams() { const sections = db.section.findMany(); @@ -22,16 +23,19 @@ export default async function Page({ } const products = db.product.findMany({ where: { section: section.id } }); + const t = await getGT(); return (
-

- All{' '} - - ({products.length}) - -

+ +

+ All{' '} + + ({products.length}) + +

+
{products.map((product) => ( diff --git a/app/layouts/layout.tsx b/app/layouts/layout.tsx index 9ce48a1a3..16b4b70de 100644 --- a/app/layouts/layout.tsx +++ b/app/layouts/layout.tsx @@ -8,6 +8,7 @@ import { Tabs } from '#/ui/tabs'; import { type Metadata } from 'next'; import { Mdx } from '#/ui/codehike'; import readme from './readme.mdx'; +import { getGT } from 'gt-next/server'; export async function generateMetadata(): Promise { const demo = db.demo.find({ where: { slug: 'layouts' } }); @@ -25,6 +26,7 @@ export default async function Layout({ }) { const demo = db.demo.find({ where: { slug: 'layouts' } }); const sections = db.section.findMany(); + const t = await getGT(); return ( <> @@ -41,7 +43,7 @@ export default async function Layout({ ({ text: x.name, slug: x.slug })), ]} /> diff --git a/app/layouts/page.tsx b/app/layouts/page.tsx index 087d6e192..8b836a9fc 100644 --- a/app/layouts/page.tsx +++ b/app/layouts/page.tsx @@ -3,6 +3,7 @@ import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { ProductCard } from '#/ui/product-card'; +import { T, Var } from 'gt-next'; export default async function Page() { const products = db.product.findMany({ limit: 9 }); @@ -10,12 +11,14 @@ export default async function Page() { return (
-

- All{' '} - - ({products.length}) - -

+ +

+ All{' '} + + ({products.length}) + +

+
{products.map((product) => ( diff --git a/app/loading/[section]/[category]/page.tsx b/app/loading/[section]/[category]/page.tsx index 9e8ed91ff..ddf576130 100644 --- a/app/loading/[section]/[category]/page.tsx +++ b/app/loading/[section]/[category]/page.tsx @@ -3,6 +3,7 @@ import { connection } from 'next/server'; import { Boundary } from '#/ui/boundary'; import { ProductCard } from '#/ui/product-card'; import db from '#/lib/db'; +import { T, Num } from 'gt-next'; export default async function Page({ params, @@ -29,12 +30,14 @@ export default async function Page({ return (
-

- All{' '} - - ({products.length}) - -

+ +

+ All{' '} + + ({products.length}) + +

+
{products.map((product) => ( diff --git a/app/loading/[section]/layout.tsx b/app/loading/[section]/layout.tsx index 2ea492e11..f107eb4ca 100644 --- a/app/loading/[section]/layout.tsx +++ b/app/loading/[section]/layout.tsx @@ -4,6 +4,7 @@ import { notFound } from 'next/navigation'; import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { Tabs } from '#/ui/tabs'; +import { getGT } from 'gt-next/server'; export default async function Layout({ params, @@ -20,13 +21,14 @@ export default async function Layout({ const demo = db.demo.find({ where: { slug: 'loading' } }); const categories = db.category.findMany({ where: { section: section?.id } }); + const t = await getGT(); return ( ({ text: x.name, slug: x.slug })), ]} /> diff --git a/app/loading/[section]/loading.tsx b/app/loading/[section]/loading.tsx index 52e40a678..ffb52cd65 100644 --- a/app/loading/[section]/loading.tsx +++ b/app/loading/[section]/loading.tsx @@ -1,11 +1,14 @@ import { Boundary } from '#/ui/boundary'; import { ProductCardSkeleton } from '#/ui/product-card'; +import { T } from 'gt-next'; export default function Loading() { return (
-

All

+ +

All

+
diff --git a/app/loading/[section]/page.tsx b/app/loading/[section]/page.tsx index 81dc9ad60..03cb25487 100644 --- a/app/loading/[section]/page.tsx +++ b/app/loading/[section]/page.tsx @@ -3,6 +3,7 @@ import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { ProductCard } from '#/ui/product-card'; import { connection } from 'next/server'; +import { T, Var } from 'gt-next'; export default async function Page({ params, @@ -28,12 +29,14 @@ export default async function Page({ return (
-

- All{' '} - - ({products.length}) - -

+ +

+ All{' '} + + ({products.length}) + +

+
{products.map((product) => ( diff --git a/app/loading/layout.tsx b/app/loading/layout.tsx index dcff04069..68ba4354e 100644 --- a/app/loading/layout.tsx +++ b/app/loading/layout.tsx @@ -6,6 +6,7 @@ import { Mdx } from '#/ui/codehike'; import { Tabs } from '#/ui/tabs'; import { type Metadata } from 'next'; import Readme from './readme.mdx'; +import { getGT } from 'gt-next/server'; export async function generateMetadata(): Promise { const demo = db.demo.find({ where: { slug: 'loading' } }); @@ -23,6 +24,7 @@ export default async function Layout({ }) { const demo = db.demo.find({ where: { slug: 'loading' } }); const sections = db.section.findMany(); + const t = await getGT(); return ( <> @@ -39,7 +41,7 @@ export default async function Layout({ ({ text: x.name, slug: x.slug })), ]} /> diff --git a/app/loading/loading.tsx b/app/loading/loading.tsx index 52e40a678..ffb52cd65 100644 --- a/app/loading/loading.tsx +++ b/app/loading/loading.tsx @@ -1,11 +1,14 @@ import { Boundary } from '#/ui/boundary'; import { ProductCardSkeleton } from '#/ui/product-card'; +import { T } from 'gt-next'; export default function Loading() { return (
-

All

+ +

All

+
diff --git a/app/loading/page.tsx b/app/loading/page.tsx index ba0ee38a2..23da6b35a 100644 --- a/app/loading/page.tsx +++ b/app/loading/page.tsx @@ -2,6 +2,7 @@ import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { ProductCard } from '#/ui/product-card'; import { connection } from 'next/server'; +import { T, Var } from 'gt-next'; export default async function Page() { // DEMO: @@ -17,12 +18,14 @@ export default async function Page() { return (
-

- All{' '} - - ({products.length}) - -

+ +

+ All{' '} + + ({products.length}) + +

+
{products.map((product) => ( -
-

Not Found

-

Could not find requested resource

-
+ +
+

Not Found

+

Could not find requested resource

+
+
); } diff --git a/app/not-found/[section]/[category]/page.tsx b/app/not-found/[section]/[category]/page.tsx index 94b375fc0..ed79f2a36 100644 --- a/app/not-found/[section]/[category]/page.tsx +++ b/app/not-found/[section]/[category]/page.tsx @@ -4,6 +4,7 @@ import { notFound } from 'next/navigation'; import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { ProductCard } from '#/ui/product-card'; +import { T, Num } from 'gt-next'; export async function generateStaticParams() { const categories = db.category.findMany(); @@ -26,12 +27,14 @@ export default async function Page({ return (
-

- All{' '} - - ({products.length}) - -

+ +

+ All{' '} + + ({products.length}) + +

+
{products.map((product) => ( diff --git a/app/not-found/[section]/layout.tsx b/app/not-found/[section]/layout.tsx index 09acd0085..783b266cf 100644 --- a/app/not-found/[section]/layout.tsx +++ b/app/not-found/[section]/layout.tsx @@ -4,6 +4,7 @@ import { notFound } from 'next/navigation'; import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { Tabs } from '#/ui/tabs'; +import { T } from 'gt-next'; export default async function Layout({ params, @@ -26,9 +27,9 @@ export default async function Layout({ All }, ...categories.map((x) => ({ text: x.name, slug: x.slug })), - { text: 'Does Not Exist', slug: 'does-not-exist' }, + { text: Does Not Exist, slug: 'does-not-exist' }, ]} /> diff --git a/app/not-found/[section]/not-found.tsx b/app/not-found/[section]/not-found.tsx index eb695845a..2b20d97ee 100644 --- a/app/not-found/[section]/not-found.tsx +++ b/app/not-found/[section]/not-found.tsx @@ -1,6 +1,7 @@ import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { Tab } from '#/ui/tabs'; +import { T } from 'gt-next'; export default function NotFound() { const demo = db.demo.find({ where: { slug: 'not-found' } }); @@ -8,14 +9,18 @@ export default function NotFound() { return (
-

Not Found

-
- Sorry, the requested resource could not be found -
+ +

Not Found

+
+ +
+ Sorry, the requested resource could not be found +
+
- + Home, slug: demo.slug }} />
); diff --git a/app/not-found/[section]/page.tsx b/app/not-found/[section]/page.tsx index b2acf2204..ee5c76f5f 100644 --- a/app/not-found/[section]/page.tsx +++ b/app/not-found/[section]/page.tsx @@ -4,6 +4,7 @@ import { notFound } from 'next/navigation'; import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { ProductCard } from '#/ui/product-card'; +import { T, Var } from 'gt-next'; export async function generateStaticParams() { const sections = db.section.findMany(); @@ -26,12 +27,14 @@ export default async function Page({ return (
-

- All{' '} - - ({products.length}) - -

+ +

+ All{' '} + + ({products.length}) + +

+
{products.map((product) => ( diff --git a/app/not-found/layout.tsx b/app/not-found/layout.tsx index 22b3c5a6b..9f6038941 100644 --- a/app/not-found/layout.tsx +++ b/app/not-found/layout.tsx @@ -7,6 +7,7 @@ import { type Metadata } from 'next'; import React from 'react'; import Readme from './readme.mdx'; import { Mdx } from '#/ui/codehike'; +import { getGT } from 'gt-next/server'; export async function generateMetadata(): Promise { const demo = db.demo.find({ where: { slug: 'not-found' } }); @@ -24,6 +25,7 @@ export default async function Layout({ }) { const demo = db.demo.find({ where: { slug: 'not-found' } }); const sections = db.section.findMany({ limit: 1 }); + const t = await getGT(); return ( <> @@ -40,9 +42,9 @@ export default async function Layout({ ({ text: x.name, slug: x.slug })), - { text: 'Does Not Exist', slug: 'does-not-exist' }, + { text: t('Does Not Exist'), slug: 'does-not-exist' }, ]} /> diff --git a/app/not-found/not-found.tsx b/app/not-found/not-found.tsx index 6dd2ba257..2d1be5483 100644 --- a/app/not-found/not-found.tsx +++ b/app/not-found/not-found.tsx @@ -1,21 +1,28 @@ import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { Tab } from '#/ui/tabs'; +import { T } from 'gt-next'; +import { getGT } from 'gt-next/server'; -export default function NotFound() { +export default async function NotFound() { const demo = db.demo.find({ where: { slug: 'not-found' } }); + const t = await getGT(); return (
-

Not Found

-
- Sorry, the requested resource could not be found -
+ +

Not Found

+
+ +
+ Sorry, the requested resource could not be found +
+
- +
); diff --git a/app/not-found/page.tsx b/app/not-found/page.tsx index 34044f8be..431959b6f 100644 --- a/app/not-found/page.tsx +++ b/app/not-found/page.tsx @@ -3,6 +3,7 @@ import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { ProductCard } from '#/ui/product-card'; +import { T, Var } from 'gt-next'; export default async function Page() { const products = db.product.findMany({ limit: 9 }); @@ -10,12 +11,14 @@ export default async function Page() { return (
-

- All{' '} - - ({products.length}) - -

+ +

+ All{' '} + + ({products.length}) + +

+
{products.map((product) => ( diff --git a/app/page.tsx b/app/page.tsx index ea7a114da..52c30f4a5 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,13 +1,15 @@ import db from '#/lib/db'; +import { getGT } from 'gt-next/server'; import { Boundary } from '#/ui/boundary'; import { LinkStatus } from '#/ui/link-status'; import Link from 'next/link'; -export default function Page() { +export default async function Page() { const demos = db.demo.findMany(); + const t = await getGT(); return ( -

Default

+ +

Default

+
@@ -19,7 +22,7 @@ export default function Default() {
- + Home, slug: demo.slug }} />
); diff --git a/app/parallel-routes/@audience/demographics/page.tsx b/app/parallel-routes/@audience/demographics/page.tsx index 299803e20..66d36f8c9 100644 --- a/app/parallel-routes/@audience/demographics/page.tsx +++ b/app/parallel-routes/@audience/demographics/page.tsx @@ -1,4 +1,5 @@ import { Boundary } from '#/ui/boundary'; +import { T } from 'gt-next'; export default function Page() { return ( @@ -7,9 +8,11 @@ export default function Page() { size="small" className="flex flex-col gap-4" > -

- Audience demographics stats -

+ +

+ Audience demographics stats +

+
diff --git a/app/parallel-routes/@audience/layout.tsx b/app/parallel-routes/@audience/layout.tsx index 751591cbe..c3c0e915c 100644 --- a/app/parallel-routes/@audience/layout.tsx +++ b/app/parallel-routes/@audience/layout.tsx @@ -1,6 +1,7 @@ import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { Tabs } from '#/ui/tabs'; +import { T } from 'gt-next'; export default function Layout({ children }: { children: React.ReactNode }) { const demo = db.demo.find({ where: { slug: 'parallel-routes' } }); @@ -14,9 +15,9 @@ export default function Layout({ children }: { children: React.ReactNode }) { Home }, + { text: Demographics, slug: 'demographics' }, + { text: Subscribers, slug: 'subscribers' }, ]} /> diff --git a/app/parallel-routes/@audience/page.tsx b/app/parallel-routes/@audience/page.tsx index dced8ce7e..17df27ef4 100644 --- a/app/parallel-routes/@audience/page.tsx +++ b/app/parallel-routes/@audience/page.tsx @@ -1,4 +1,5 @@ import { Boundary } from '#/ui/boundary'; +import { T } from 'gt-next'; export default function Page() { return ( @@ -7,7 +8,9 @@ export default function Page() { size="small" className="flex flex-col gap-4" > -

Audience stats

+ +

Audience stats

+
diff --git a/app/parallel-routes/@audience/subscribers/page.tsx b/app/parallel-routes/@audience/subscribers/page.tsx index 854c75a58..412d360a8 100644 --- a/app/parallel-routes/@audience/subscribers/page.tsx +++ b/app/parallel-routes/@audience/subscribers/page.tsx @@ -1,4 +1,5 @@ import { Boundary } from '#/ui/boundary'; +import { T } from 'gt-next'; export default function Page() { return ( @@ -7,7 +8,9 @@ export default function Page() { size="small" className="flex flex-col gap-4" > -

Audience subscriber stats

+ +

Audience subscriber stats

+
diff --git a/app/parallel-routes/@views/default.tsx b/app/parallel-routes/@views/default.tsx index c61232e9f..da83f2b3a 100644 --- a/app/parallel-routes/@views/default.tsx +++ b/app/parallel-routes/@views/default.tsx @@ -1,6 +1,7 @@ import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { Tab } from '#/ui/tabs'; +import { T } from 'gt-next'; export default function Default() { const demo = db.demo.find({ where: { slug: 'parallel-routes' } }); @@ -11,7 +12,9 @@ export default function Default() { size="small" className="flex flex-col gap-4" > -

Default

+ +

Default

+
@@ -19,7 +22,7 @@ export default function Default() {
- + Home, slug: demo.slug }} />
); diff --git a/app/parallel-routes/@views/impressions/page.tsx b/app/parallel-routes/@views/impressions/page.tsx index fbbc9558a..195f66c65 100644 --- a/app/parallel-routes/@views/impressions/page.tsx +++ b/app/parallel-routes/@views/impressions/page.tsx @@ -1,4 +1,5 @@ import { Boundary } from '#/ui/boundary'; +import { T } from 'gt-next'; export default function Page() { return ( @@ -7,7 +8,9 @@ export default function Page() { size="small" className="flex flex-col gap-4" > -

View impression stats

+ +

View impression stats

+
diff --git a/app/parallel-routes/@views/layout.tsx b/app/parallel-routes/@views/layout.tsx index 36d692b0a..17970b7e6 100644 --- a/app/parallel-routes/@views/layout.tsx +++ b/app/parallel-routes/@views/layout.tsx @@ -1,6 +1,7 @@ import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { Tabs } from '#/ui/tabs'; +import { T } from 'gt-next'; export default function Layout({ children }: { children: React.ReactNode }) { const demo = db.demo.find({ where: { slug: 'parallel-routes' } }); @@ -14,9 +15,9 @@ export default function Layout({ children }: { children: React.ReactNode }) { Home }, + { text: Impressions, slug: 'impressions' }, + { text: View Duration, slug: 'view-duration' }, ]} /> {children} diff --git a/app/parallel-routes/@views/page.tsx b/app/parallel-routes/@views/page.tsx index ee8a2b515..f0072c94b 100644 --- a/app/parallel-routes/@views/page.tsx +++ b/app/parallel-routes/@views/page.tsx @@ -1,4 +1,5 @@ import { Boundary } from '#/ui/boundary'; +import { T } from 'gt-next'; export default function Page() { return ( @@ -7,7 +8,9 @@ export default function Page() { size="small" className="flex flex-col gap-4" > -

View stats

+ +

View stats

+
diff --git a/app/parallel-routes/@views/view-duration/page.tsx b/app/parallel-routes/@views/view-duration/page.tsx index deba2c53e..eebff976e 100644 --- a/app/parallel-routes/@views/view-duration/page.tsx +++ b/app/parallel-routes/@views/view-duration/page.tsx @@ -1,4 +1,5 @@ import { Boundary } from '#/ui/boundary'; +import { T } from 'gt-next'; export default function Page() { return ( @@ -7,7 +8,9 @@ export default function Page() { size="small" className="flex flex-col gap-4" > -

View duration stats

+ +

View duration stats

+
diff --git a/app/parallel-routes/default.tsx b/app/parallel-routes/default.tsx index a5f27d96c..d8fcc750a 100644 --- a/app/parallel-routes/default.tsx +++ b/app/parallel-routes/default.tsx @@ -1,13 +1,17 @@ import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { Tab } from '#/ui/tabs'; +import { T, useGT } from 'gt-next'; export default function Default() { const demo = db.demo.find({ where: { slug: 'parallel-routes' } }); + const t = useGT(); return ( -

Default

+ +

Default

+
@@ -15,7 +19,7 @@ export default function Default() {
- +
); diff --git a/app/parallel-routes/page.tsx b/app/parallel-routes/page.tsx index 685d453a8..04edb2db4 100644 --- a/app/parallel-routes/page.tsx +++ b/app/parallel-routes/page.tsx @@ -1,9 +1,12 @@ import { Boundary } from '#/ui/boundary'; +import { T } from 'gt-next'; export default function Page() { return ( -

Channel analytics

+ +

Channel analytics

+
diff --git a/app/route-groups/(checkout)/checkout/page.tsx b/app/route-groups/(checkout)/checkout/page.tsx index ebd201405..348212f16 100644 --- a/app/route-groups/(checkout)/checkout/page.tsx +++ b/app/route-groups/(checkout)/checkout/page.tsx @@ -1,17 +1,21 @@ import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { Tab } from '#/ui/tabs'; +import { T, useGT } from 'gt-next'; export default function Page() { + const t = useGT(); const demo = db.demo.find({ where: { slug: 'route-groups' } }); return (
- +
-

Checkout

+ +

Checkout

+
diff --git a/app/route-groups/(main)/(marketing)/blog/page.tsx b/app/route-groups/(main)/(marketing)/blog/page.tsx index db87cd5ec..d81f36aeb 100644 --- a/app/route-groups/(main)/(marketing)/blog/page.tsx +++ b/app/route-groups/(main)/(marketing)/blog/page.tsx @@ -1,4 +1,5 @@ import { Boundary } from '#/ui/boundary'; +import { T } from 'gt-next'; export default function Page() { return ( @@ -6,7 +7,9 @@ export default function Page() { label="(main)/(marketing)/blog/page.tsx" className="flex flex-col gap-4" > -

Blog

+ +

Blog

+
diff --git a/app/route-groups/(main)/(shop)/[section]/[category]/page.tsx b/app/route-groups/(main)/(shop)/[section]/[category]/page.tsx index 7fda03ea6..056e40132 100644 --- a/app/route-groups/(main)/(shop)/[section]/[category]/page.tsx +++ b/app/route-groups/(main)/(shop)/[section]/[category]/page.tsx @@ -1,4 +1,5 @@ import { notFound } from 'next/navigation'; +import { T } from 'gt-next'; import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { ProductCard } from '#/ui/product-card'; @@ -27,7 +28,7 @@ export default async function Page({

- All{' '} + All{' '} ({products.length}) diff --git a/app/route-groups/(main)/(shop)/[section]/layout.tsx b/app/route-groups/(main)/(shop)/[section]/layout.tsx index 0a8269829..ce08c441e 100644 --- a/app/route-groups/(main)/(shop)/[section]/layout.tsx +++ b/app/route-groups/(main)/(shop)/[section]/layout.tsx @@ -2,6 +2,7 @@ import { notFound } from 'next/navigation'; import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { Tabs } from '#/ui/tabs'; +import { getGT } from 'gt-next/server'; export default async function Layout({ params, @@ -10,6 +11,7 @@ export default async function Layout({ children: React.ReactNode; params: Promise<{ section: string }>; }) { + const t = await getGT(); const { section: sectionSlug } = await params; const section = db.section.find({ where: { slug: sectionSlug } }); if (!section) { @@ -27,7 +29,7 @@ export default async function Layout({ ({ text: x.name, slug: x.slug })), ]} /> diff --git a/app/route-groups/(main)/(shop)/[section]/page.tsx b/app/route-groups/(main)/(shop)/[section]/page.tsx index 038bd6bfa..88365862d 100644 --- a/app/route-groups/(main)/(shop)/[section]/page.tsx +++ b/app/route-groups/(main)/(shop)/[section]/page.tsx @@ -2,6 +2,7 @@ import { notFound } from 'next/navigation'; import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { ProductCard } from '#/ui/product-card'; +import { T, Var } from 'gt-next'; export async function generateStaticParams() { const sections = db.section.findMany(); @@ -26,12 +27,14 @@ export default async function Page({ return (
-

- All{' '} - - ({products.length}) - -

+ +

+ All{' '} + + ({products.length}) + +

+
{products.map((product) => ( diff --git a/app/route-groups/(main)/(shop)/page.tsx b/app/route-groups/(main)/(shop)/page.tsx index f83fcc9be..594789987 100644 --- a/app/route-groups/(main)/(shop)/page.tsx +++ b/app/route-groups/(main)/(shop)/page.tsx @@ -1,6 +1,7 @@ import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { ProductCard } from '#/ui/product-card'; +import { T, Var } from 'gt-next'; export default async function Page() { const products = db.product.findMany({ limit: 9 }); @@ -8,12 +9,14 @@ export default async function Page() { return (
-

- All{' '} - - ({products.length}) - -

+ +

+ All{' '} + + ({products.length}) + +

+
{products.map((product) => ( diff --git a/app/route-groups/(main)/layout.tsx b/app/route-groups/(main)/layout.tsx index c13991d2c..f51834cf1 100644 --- a/app/route-groups/(main)/layout.tsx +++ b/app/route-groups/(main)/layout.tsx @@ -2,12 +2,14 @@ import React from 'react'; import { Boundary } from '#/ui/boundary'; import { Tabs } from '#/ui/tabs'; import db from '#/lib/db'; +import { getGT } from 'gt-next/server'; export default async function Layout({ children, }: { children: React.ReactNode; }) { + const t = await getGT(); const demo = db.demo.find({ where: { slug: 'route-groups' } }); const sections = db.section.findMany({ limit: 1 }); @@ -16,10 +18,10 @@ export default async function Layout({ ({ text: x.name, slug: x.slug })), - { text: 'Checkout', slug: 'checkout' }, - { text: 'Blog', slug: 'blog' }, + { text: t('Checkout'), slug: 'checkout' }, + { text: t('Blog'), slug: 'blog' }, ]} /> diff --git a/app/use-link-status/[section]/[category]/page.tsx b/app/use-link-status/[section]/[category]/page.tsx index a0efe31d6..ea61b50c0 100644 --- a/app/use-link-status/[section]/[category]/page.tsx +++ b/app/use-link-status/[section]/[category]/page.tsx @@ -3,6 +3,7 @@ import { connection } from 'next/server'; import { Boundary } from '#/ui/boundary'; import { ProductCard } from '#/ui/product-card'; import db from '#/lib/db'; +import { T, Var } from 'gt-next'; export default async function Page({ params, @@ -28,12 +29,14 @@ export default async function Page({ return (
-

- All{' '} - - ({products.length}) - -

+ +

+ All{' '} + + ({products.length}) + +

+
{products.map((product) => ( diff --git a/app/use-link-status/[section]/layout.tsx b/app/use-link-status/[section]/layout.tsx index 3aac91797..39ceb08a5 100644 --- a/app/use-link-status/[section]/layout.tsx +++ b/app/use-link-status/[section]/layout.tsx @@ -4,6 +4,7 @@ import { notFound } from 'next/navigation'; import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { Tabs } from '#/ui/tabs'; +import { getGT } from 'gt-next/server'; export default async function Layout({ params, @@ -12,6 +13,7 @@ export default async function Layout({ children: React.ReactNode; params: Promise<{ section: string }>; }) { + const t = await getGT(); const { section: sectionSlug } = await params; const section = db.section.find({ where: { slug: sectionSlug } }); if (!section) { @@ -26,7 +28,7 @@ export default async function Layout({ ({ text: x.name, slug: x.slug })), ]} /> diff --git a/app/use-link-status/[section]/page.tsx b/app/use-link-status/[section]/page.tsx index 983668e2f..ea56e8a02 100644 --- a/app/use-link-status/[section]/page.tsx +++ b/app/use-link-status/[section]/page.tsx @@ -3,6 +3,7 @@ import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { ProductCard } from '#/ui/product-card'; import { connection } from 'next/server'; +import { T, Num } from 'gt-next'; export default async function Page({ params, @@ -26,12 +27,14 @@ export default async function Page({ return (
-

- All{' '} - - ({products.length}) - -

+ +

+ All{' '} + + ({products.length}) + +

+
{products.map((product) => ( diff --git a/app/use-link-status/layout.tsx b/app/use-link-status/layout.tsx index 640b17773..09f76646f 100644 --- a/app/use-link-status/layout.tsx +++ b/app/use-link-status/layout.tsx @@ -7,6 +7,7 @@ import { type Metadata } from 'next'; import React from 'react'; import Readme from './readme.mdx'; import { Mdx } from '#/ui/codehike'; +import { getGT } from 'gt-next/server'; export async function generateMetadata(): Promise { const demo = db.demo.find({ where: { slug: 'use-link-status' } }); @@ -23,6 +24,7 @@ export default async function Layout({ }) { const demo = db.demo.find({ where: { slug: 'use-link-status' } }); const sections = db.section.findMany(); + const t = await getGT(); return ( <> @@ -39,7 +41,7 @@ export default async function Layout({ ({ text: x.name, slug: x.slug })), ]} /> diff --git a/app/use-link-status/loading.tsx b/app/use-link-status/loading.tsx index 52e40a678..ffb52cd65 100644 --- a/app/use-link-status/loading.tsx +++ b/app/use-link-status/loading.tsx @@ -1,11 +1,14 @@ import { Boundary } from '#/ui/boundary'; import { ProductCardSkeleton } from '#/ui/product-card'; +import { T } from 'gt-next'; export default function Loading() { return (
-

All

+ +

All

+
diff --git a/app/use-link-status/page.tsx b/app/use-link-status/page.tsx index d5a1f1d8c..c70b528c5 100644 --- a/app/use-link-status/page.tsx +++ b/app/use-link-status/page.tsx @@ -2,6 +2,7 @@ import db from '#/lib/db'; import { Boundary } from '#/ui/boundary'; import { ProductCard } from '#/ui/product-card'; import { connection } from 'next/server'; +import { T, Var } from 'gt-next'; export default async function Page() { // DEMO: @@ -15,12 +16,14 @@ export default async function Page() { return (
-

- All{' '} - - ({products.length}) - -

+ +

+ All{' '} + + ({products.length}) + +

+
{products.map((product) => ( diff --git a/app/view-transitions/page.tsx b/app/view-transitions/page.tsx index 47e83f4d0..e1a244a12 100644 --- a/app/view-transitions/page.tsx +++ b/app/view-transitions/page.tsx @@ -8,6 +8,7 @@ import { } from '#/app/view-transitions/_ui/transitions'; import { Boundary } from '#/ui/boundary'; import Image from 'next/image'; +import { T } from 'gt-next'; export default async function Page() { const products = db.product.findMany(); @@ -29,7 +30,9 @@ export default async function Page() {
-

Shop

+ +

Shop

+
({products.length}) diff --git a/app/view-transitions/posts/[id]/page.tsx b/app/view-transitions/posts/[id]/page.tsx index 27a277ad9..d80abdf76 100644 --- a/app/view-transitions/posts/[id]/page.tsx +++ b/app/view-transitions/posts/[id]/page.tsx @@ -12,6 +12,7 @@ import { SkeletonText } from '#/ui/skeleton'; import { ChevronLeftIcon } from '@heroicons/react/24/solid'; import Image from 'next/image'; import { notFound } from 'next/navigation'; +import { T } from 'gt-next'; export async function generateStaticParams() { const products = db.product.findMany(); @@ -77,7 +78,9 @@ export default async function Page({ 'transition-to-detail': 'animate-morph', }} > -
Shop
+ +
Shop
+
@@ -102,13 +105,13 @@ export default async function Page({ href={prevProduct} type="transition-backwards" > - Previous + Previous - Next + Next
diff --git a/gt.config.json b/gt.config.json new file mode 100644 index 000000000..92eb660ff --- /dev/null +++ b/gt.config.json @@ -0,0 +1,15 @@ +{ + "projectId": "prj_j0swtvhnvdh7zptfh657xas6", + "locales": [ + "en-US", + "fr" + ], + "defaultLocale": "en-US", + "files": { + "gt": { + "output": "public/_gt/[locale].json" + } + }, + "framework": "next-app", + "_versionId": "5bcc07d03c316df327ac9448a19c9f0b268089b249755b1926a67ee53ffc36e1" +} \ No newline at end of file diff --git a/lib/db.ts b/lib/db.ts index d8fdaff5b..2f1d82c06 100644 --- a/lib/db.ts +++ b/lib/db.ts @@ -5,6 +5,7 @@ import 'server-only'; import { data, + getData, type Category, type Demo, type DemoCategory, @@ -29,15 +30,18 @@ type DemoWhere = { slug?: DemoSlug }; type DemoFindOptions = { where?: DemoWhere }; -const db = { - product: { +const createDb = (t?: (content: string) => string) => { + const currentData = t ? getData(t) : data; + + return { + product: { find: (options: ProductFindOptions) => { let product: Product | undefined; if (options.where?.id !== undefined) { - product = data.products.find((p) => p.id === options.where?.id); + product = currentData.products.find((p) => p.id === options.where?.id); } else if (options.where?.category !== undefined) { - product = data.products.find( + product = currentData.products.find( (p) => p.category === options.where?.category, ); } @@ -46,19 +50,19 @@ const db = { let next: string | undefined = undefined; if (product) { - const ids = data.products.map((p) => Number(p.id)); + const ids = currentData.products.map((p) => Number(p.id)); const currentIndex = ids.indexOf(Number(product.id)); const prevIndex = (currentIndex - 1 + ids.length) % ids.length; const nextIndex = (currentIndex + 1) % ids.length; - prev = data.products[prevIndex]?.id; - next = data.products[nextIndex]?.id; + prev = currentData.products[prevIndex]?.id; + next = currentData.products[nextIndex]?.id; } return product ? { ...product, prev, next } : null; }, findMany: (options: ProductFindOptions = {}) => { - let result = data.products; + let result = currentData.products; if (options.where?.category) { result = result.filter( @@ -67,7 +71,7 @@ const db = { } if (options.where?.section) { - const sectionCategories = data.categories + const sectionCategories = currentData.categories .filter((category) => category.section === options.where!.section) .map((category) => category.id); result = result.filter((product) => @@ -87,15 +91,15 @@ const db = { let section: Section | undefined; if (options.where?.id !== undefined) { - section = data.sections.find((s) => s.id === options.where?.id); + section = currentData.sections.find((s) => s.id === options.where?.id); } else if (options.where?.slug !== undefined) { - section = data.sections.find((s) => s.slug === options.where?.slug); + section = currentData.sections.find((s) => s.slug === options.where?.slug); } return section || null; }, findMany: (options: SectionFindOptions = {}) => { - let result = data.sections; + let result = currentData.sections; if (options.where?.id) { result = result.filter((section) => section.id === options.where!.id); @@ -119,11 +123,11 @@ const db = { let category: Category | undefined; if (options.where?.id !== undefined) { - category = data.categories.find((c) => c.id === options.where?.id); + category = currentData.categories.find((c) => c.id === options.where?.id); } else if (options.where?.slug !== undefined) { - category = data.categories.find((c) => c.slug === options.where?.slug); + category = currentData.categories.find((c) => c.slug === options.where?.slug); } else if (options.where?.section !== undefined) { - category = data.categories.find( + category = currentData.categories.find( (c) => c.section === options.where?.section, ); } @@ -131,7 +135,7 @@ const db = { return category || null; }, findMany: (options: CategoryFindOptions = {}) => { - let result = data.categories; + let result = currentData.categories; if (options.where?.id) { result = result.filter((category) => category.id === options.where!.id); @@ -157,7 +161,7 @@ const db = { let demo: Demo | undefined; if (options.where?.slug !== undefined) { - for (const category of data.demos) { + for (const category of currentData.demos) { const found = category.items.find( (d) => d.slug === options.where?.slug, ); @@ -177,11 +181,15 @@ const db = { return demo; }, findMany: () => { - return data.demos; + return currentData.demos; }, }, + }; }; +const db = createDb(); + export default db; +export { createDb }; export type { Demo, Product, Section, Category, DemoCategory }; diff --git a/loadTranslations.js b/loadTranslations.js new file mode 100644 index 000000000..c1e5b4478 --- /dev/null +++ b/loadTranslations.js @@ -0,0 +1,12 @@ + +export default async function loadTranslations(locale) { + try { + // Load translations from public/_gt directory + // This matches the GT config files.gt.output path + const t = await import(`./public/_gt/${locale}.json`); + return t.default; + } catch (error) { + console.warn(`Failed to load translations for locale ${locale}:`, error); + return {}; + } +} diff --git a/next.config.ts b/next.config.ts index 476263e08..1aae06402 100755 --- a/next.config.ts +++ b/next.config.ts @@ -1,3 +1,4 @@ +import { withGTConfig } from "gt-next/config"; import type { NextConfig } from 'next'; import createMDX from '@next/mdx'; import { type CodeHikeConfig } from 'codehike/mdx'; @@ -10,19 +11,19 @@ const nextConfig = { clientSegmentCache: true, viewTransition: true, prerenderEarlyExit: false, - routerBFCache: true, - }, + routerBFCache: true + } } satisfies NextConfig; const codeHikeConfig = { - components: { code: 'MyCode', inlineCode: 'MyInlineCode' }, + components: { code: 'MyCode', inlineCode: 'MyInlineCode' } } satisfies CodeHikeConfig; const withMDX = createMDX({ options: { remarkPlugins: [['remark-codehike', codeHikeConfig]], - recmaPlugins: [['recma-codehike', codeHikeConfig]], - }, + recmaPlugins: [['recma-codehike', codeHikeConfig]] + } }); -export default withMDX(nextConfig); +export default withGTConfig(withMDX(nextConfig), {}); \ No newline at end of file diff --git a/package.json b/package.json index dee223bfd..3edfa1512 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,10 @@ "private": true, "scripts": { "dev": "next dev --turbopack", - "build": "next build", + "build": "pnpm translate && next build", "start": "next start", - "prettier": "prettier --write --ignore-unknown ." + "prettier": "prettier --write --ignore-unknown .", + "translate": "locadex translate" }, "dependencies": { "@heroicons/react": "2.2.0", @@ -16,6 +17,7 @@ "codehike": "1.0.7", "date-fns": "4.1.0", "dinero.js": "2.0.0-alpha.10", + "gt-next": "6.0.5", "ms": "3.0.0-canary.1", "next": "15.4.0-canary.70", "react": "19.1.0", @@ -41,4 +43,4 @@ "tailwindcss": "4.1.4", "typescript": "5.7.3" } -} +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94f556a10..a4df0d1a7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: dinero.js: specifier: 2.0.0-alpha.10 version: 2.0.0-alpha.10 + gt-next: + specifier: 6.0.5 + version: 6.0.5(next@15.4.0-canary.70(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) ms: specifier: 3.0.0-canary.1 version: 3.0.0-canary.1 @@ -133,6 +136,24 @@ packages: '@emotion/unitless@0.8.1': resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==} + '@formatjs/ecma402-abstract@2.3.4': + resolution: {integrity: sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA==} + + '@formatjs/fast-memoize@2.2.7': + resolution: {integrity: sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==} + + '@formatjs/icu-messageformat-parser@2.11.2': + resolution: {integrity: sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA==} + + '@formatjs/icu-skeleton-parser@1.8.14': + resolution: {integrity: sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ==} + + '@formatjs/intl-localematcher@0.6.1': + resolution: {integrity: sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==} + + '@generaltranslation/supported-locales@2.0.12': + resolution: {integrity: sha512-dey6ehO1r2e1SL3Wsi2qkmplE3oOC3fGolyKQLIT/yL5uBe8laTlZuk6d1lnYLshhyFvFtzjweVIgHCyCeWv9A==} + '@heroicons/react@2.2.0': resolution: {integrity: sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==} peerDependencies: @@ -543,6 +564,9 @@ packages: comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + css-color-keywords@1.0.0: resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==} engines: {node: '>=4'} @@ -570,6 +594,9 @@ packages: supports-color: optional: true + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + decode-named-character-reference@1.1.0: resolution: {integrity: sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==} @@ -625,9 +652,28 @@ packages: extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + generaltranslation@7.0.1: + resolution: {integrity: sha512-G1aPfq9pHZFToW/1tNGZ4wTN1LpCmxE9Y5DmTk+wJZ2OUFM5X0nBQPEEwe8C2VR2vP1cFxMf4GBcU9DRpcv7ng==} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + gt-next@6.0.5: + resolution: {integrity: sha512-Z8TypXfQO0DvZS4XfVPolZatT2UoSiHvjFruUsKatOTUIwqaVATUl50UZMxCiYcqsV84wfShk+HWjxHcqRDlNg==} + peerDependencies: + next: '>=13.0.0 <15.2.1 || >15.2.2' + react: '>=16.8.0 <20.0.0' + react-dom: '>=16.8.0 <20.0.0' + + gt-react@10.0.3: + resolution: {integrity: sha512-+V6h8W/N8cit2sxwYcAIEmQL3jHH510qUeJjp0fVUEDedOgWwPEbdMknR2T2tJvDDuXOwZeDtTS/ev/avtnjZg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + hast-util-to-estree@3.1.3: resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==} @@ -640,6 +686,9 @@ packages: inline-style-parser@0.2.4: resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} + intl-messageformat@10.7.16: + resolution: {integrity: sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==} + is-alphabetical@2.0.1: resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} @@ -1186,6 +1235,36 @@ snapshots: '@emotion/unitless@0.8.1': {} + '@formatjs/ecma402-abstract@2.3.4': + dependencies: + '@formatjs/fast-memoize': 2.2.7 + '@formatjs/intl-localematcher': 0.6.1 + decimal.js: 10.6.0 + tslib: 2.8.1 + + '@formatjs/fast-memoize@2.2.7': + dependencies: + tslib: 2.8.1 + + '@formatjs/icu-messageformat-parser@2.11.2': + dependencies: + '@formatjs/ecma402-abstract': 2.3.4 + '@formatjs/icu-skeleton-parser': 1.8.14 + tslib: 2.8.1 + + '@formatjs/icu-skeleton-parser@1.8.14': + dependencies: + '@formatjs/ecma402-abstract': 2.3.4 + tslib: 2.8.1 + + '@formatjs/intl-localematcher@0.6.1': + dependencies: + tslib: 2.8.1 + + '@generaltranslation/supported-locales@2.0.12': + dependencies: + generaltranslation: 7.0.1 + '@heroicons/react@2.2.0(react@19.1.0)': dependencies: react: 19.1.0 @@ -1534,6 +1613,8 @@ snapshots: comma-separated-tokens@2.0.3: {} + crypto-js@4.2.0: {} + css-color-keywords@1.0.0: {} css-to-react-native@3.2.0: @@ -1552,6 +1633,8 @@ snapshots: dependencies: ms: 2.1.3 + decimal.js@10.6.0: {} + decode-named-character-reference@1.1.0: dependencies: character-entities: 2.0.2 @@ -1626,8 +1709,32 @@ snapshots: extend@3.0.2: {} + fast-json-stable-stringify@2.1.0: {} + + generaltranslation@7.0.1: + dependencies: + crypto-js: 4.2.0 + fast-json-stable-stringify: 2.1.0 + intl-messageformat: 10.7.16 + graceful-fs@4.2.11: {} + gt-next@6.0.5(next@15.4.0-canary.70(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + '@generaltranslation/supported-locales': 2.0.12 + generaltranslation: 7.0.1 + gt-react: 10.0.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.4.0-canary.70(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + + gt-react@10.0.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + '@generaltranslation/supported-locales': 2.0.12 + generaltranslation: 7.0.1 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + hast-util-to-estree@3.1.3: dependencies: '@types/estree': 1.0.7 @@ -1675,6 +1782,13 @@ snapshots: inline-style-parser@0.2.4: {} + intl-messageformat@10.7.16: + dependencies: + '@formatjs/ecma402-abstract': 2.3.4 + '@formatjs/fast-memoize': 2.2.7 + '@formatjs/icu-messageformat-parser': 2.11.2 + tslib: 2.8.1 + is-alphabetical@2.0.1: {} is-alphanumerical@2.0.1: diff --git a/public/_gt/fr.json b/public/_gt/fr.json new file mode 100644 index 000000000..28ae791bc --- /dev/null +++ b/public/_gt/fr.json @@ -0,0 +1,291 @@ +{ + "0f442932c6435aad": { + "t": "h1", + "i": 1, + "c": { + "i": 2, + "k": "_gt_value_2", + "v": "v" + } + }, + "bd47cac6d21b1ef1": { + "i": 1, + "c": "Erreur" + }, + "b0bba1915917949b": "@breadcrumbs", + "0c18b24308a37b92": "Page", + "f1a1d5bbe0c78854": "Code", + "278bc40eb92d03b3": [ + { + "i": 1, + "k": "_gt_value_1", + "v": "v" + }, + " Clics" + ], + "67f7439638ec898d": "CommunautĂ©", + "0bb02a2d56746f0b": { + "i": 1, + "c": "Boutique" + }, + "b4acaa914e07881a": { + "i": 1, + "c": [ + "Tout ", + { + "i": 2, + "k": "_gt_value_2", + "v": "v" + } + ] + }, + "d5cfefaf7011c288": "RĂ©essayer", + "f01773d2c6a3a360": { + "i": 1, + "c": "Page non trouvĂ©e" + }, + "67508b5a9a7db096": "Profil", + "84bee82a2fe6343b": { + "i": 1, + "c": "Profil" + }, + "ca749b12449e9284": "Accueil", + "8585b2ab678dbb20": "Hooks de Composant Client", + "ce5951a137949761": "Utilisateur", + "9588289bb956e0d6": "Documentation", + "6eb3c2310bd6e593": "ÉlĂ©ments par page", + "d894f6cb29f7a3ea": "Retour", + "a05d5e8274b18097": "N'existe pas", + "d382c93806d820ed": { + "i": 1, + "c": "DĂ©solĂ©, la ressource demandĂ©e n'a pas pu ĂȘtre trouvĂ©e" + }, + "a274ad8a0a8abcb6": "N'existe pas", + "2ecf88ef9e8e4259": "Accueil", + "b3c416ad21771939": [ + { + "i": 1, + "c": { + "i": 2, + "k": "_gt_value_2", + "v": "v" + } + }, + " Clics" + ], + "224b6eecfd42b515": "Blog", + "045741aa62df51f8": "Tout", + "0a75e464d833bca8": "ParamĂštres", + "b9c7e67b2b1ecf28": { + "i": 1, + "c": "Statistiques d'audience" + }, + "b84165d70370ef55": "DĂ©couvrir", + "98d347dec30ba3d5": { + "i": 1, + "c": "ModĂšles" + }, + "3572bfbee16de5ee": " (Mise en cache possible)", + "753bc9a22f4f30bf": { + "i": 1, + "c": "CommunautĂ©" + }, + "7a55cd9a63169cab": "Mise Ă  jour des paramĂštres de recherche d'URL", + "c55fdf419b6fac07": { + "i": 1, + "c": "Tout" + }, + "e6693514f3518433": { + "i": 1, + "c": "Par dĂ©faut" + }, + "aaa699c33de18415": "DĂ©mographie", + "7458cc239ccaf99d": { + "i": 1, + "c": "Accueil" + }, + "aa39c8d4b0caedfb": "PrĂ©cĂ©dent", + "ec443fb55db39677": { + "i": 1, + "c": "Voir les statistiques" + }, + "f4074400df60f73f": "ModĂšles", + "0a635625a1609120": "layout.tsx (infĂ©rĂ© statiquement)", + "3a78c731c9528266": { + "i": 1, + "c": "Commande" + }, + "f3b0a7888cecce8a": "DĂ©clencher une erreur", + "21612a4cefb2cd4f": "Hooks de Composant Client", + "7f3f1617bd786b1a": { + "i": 1, + "c": "ParamĂštres" + }, + "29eead877ddd0ea8": "DurĂ©e de visualisation", + "0762eeda1d93ef14": "Fil d'Ariane", + "884e0a31fb295198": "Mettre Ă  jour searchParams en utilisant `useRouter` et ``", + "9d34099df6d34061": { + "i": 1, + "c": "Voir les statistiques de durĂ©e" + }, + "591038bdbc97df62": "DĂ©mo", + "8d2f555849119794": "Exemples", + "9e2ae47f909c99bc": "Terrain de jeu Next.js", + "8da4638c0ec49785": "Impressions", + "957ea99ea1ad9543": { + "i": 1, + "c": [ + "Tout ", + { + "i": 2, + "c": [ + "(", + { + "i": 3, + "k": "_gt_value_3", + "v": "v" + }, + ")" + ] + } + ] + }, + "c0640d09dc8523c2": "page.tsx (infĂ©rĂ© statiquement)", + "fe3b96f6bb08e06e": { + "i": 1, + "c": [ + "Utilisation de ", + { + "i": 2, + "c": "" + } + ] + }, + "a0b8e460c5a59ec7": { + "i": 1, + "c": [ + "Utilisation de ", + { + "i": 2, + "c": "useRouter()" + } + ] + }, + "b3dac8653f529f09": { + "i": 1, + "c": "Voir les statistiques d'impression" + }, + "a65ad03dda72be9a": "Tout", + "77b5f98f1f9a4ca7": "Next", + "351320b28bb49fd9": { + "i": 1, + "c": "Analyses de canal" + }, + "f59bb5d91c06a3a5": { + "i": 1, + "c": [ + "Mise Ă  jour de ", + { + "i": 2, + "c": "searchParams" + } + ] + }, + "6486c2110656e680": { + "i": 1, + "c": "Blog" + }, + "45377d1b4218062e": "Mettre Ă  jour le style du lien actuellement actif", + "3bca3b2756dc6f28": "Interface utilisateur de fil d'Ariane cĂŽtĂ© serveur partagĂ©e utilisant les Routes ParallĂšles", + "d319f71097cee01c": { + "i": 1, + "c": [ + { + "i": 2, + "c": "Non trouvĂ©" + }, + { + "i": 3, + "c": "Impossible de trouver la ressource demandĂ©e" + } + ] + }, + "eecf08a9d64e3ad1": "Terrain de jeu App Router", + "35048090d61ee72e": "Un terrain de jeu pour explorer les fonctionnalitĂ©s de Next.js telles que les mises en page imbriquĂ©es, les Ă©tats de chargement instantanĂ©, le streaming et la rĂ©cupĂ©ration de donnĂ©es au niveau des composants.", + "937020f29c4f0a65": "Trier", + "4b40e480a2e8f2d4": "AbonnĂ©s", + "c7bb9d5070f33d0d": { + "i": 1, + "c": "Statistiques des abonnĂ©s de l'audience" + }, + "acee3ae066c2039e": { + "i": 1, + "c": "Statistiques dĂ©mographiques de l'audience" + }, + "e37aab66aba787e2": { + "i": 1, + "c": [ + "Tout ", + { + "i": 2, + "c": [ + "(", + { + "i": 3, + "k": "_gt_n_3", + "v": "n" + }, + ")" + ] + } + ] + }, + "a723cf3d89ec6b17": { + "i": 1, + "c": [ + "Le hook ", + { + "i": 2, + "c": "useSearchParams" + }, + " retourne une version en lecture seule de ", + { + "i": 3, + "c": "URLSearchParams", + "t": "code" + }, + ". Vous pouvez utiliser ", + { + "i": 4, + "c": "useRouter()", + "t": "code" + }, + " ou ", + { + "i": 5, + "c": "", + "t": "code" + }, + " pour dĂ©finir de nouveaux ", + { + "i": 6, + "c": "searchParams", + "t": "code" + }, + ". AprĂšs qu'une navigation soit effectuĂ©e, le ", + { + "i": 7, + "c": "page.js", + "t": "code" + }, + " actuel recevra une prop ", + { + "i": 8, + "c": "searchParams", + "t": "code" + }, + " mise Ă  jour." + ] + }, + "57a0b74151c7cb42": "Liens actifs" +} \ No newline at end of file diff --git a/ui/byline.tsx b/ui/byline.tsx index c72bbfe6a..9b20dca11 100644 --- a/ui/byline.tsx +++ b/ui/byline.tsx @@ -1,6 +1,8 @@ import { Boundary } from '#/ui/boundary'; +import { T, useGT } from 'gt-next'; export default function Byline() { + const t = useGT(); return (
@@ -10,7 +12,7 @@ export default function Byline() { target="_blank" rel="noreferrer" > - Source code + Source code / - Docs + Docs / - Deploy on + Deploy on setCount(count + 1)} className="rounded-md bg-gray-700 px-3 py-1 text-sm font-semibold whitespace-nowrap text-gray-100 tabular-nums hover:bg-gray-500 hover:text-white" > - {count} Clicks + + {count} Clicks + ); } diff --git a/ui/global-nav.tsx b/ui/global-nav.tsx index ee9be21c6..68ebebe19 100644 --- a/ui/global-nav.tsx +++ b/ui/global-nav.tsx @@ -8,6 +8,7 @@ import clsx from 'clsx'; import Link from 'next/link'; import { useSelectedLayoutSegment } from 'next/navigation'; import { Suspense, useState } from 'react'; +import { LocaleSelector, T } from 'gt-next'; export function GlobalNav({ items }: { items: DemoCategory[] }) { const [isOpen, setIsOpen] = useState(false); @@ -26,7 +27,7 @@ export function GlobalNav({ items }: { items: DemoCategory[] }) {

- Playground + Playground

@@ -36,7 +37,7 @@ export function GlobalNav({ items }: { items: DemoCategory[] }) { onClick={() => setIsOpen(!isOpen)} >
- Menu + Menu
{isOpen ? ( @@ -75,6 +76,12 @@ export function GlobalNav({ items }: { items: DemoCategory[] }) {
); })} +
+
+ Language +
+ +
diff --git a/ui/prose.tsx b/ui/prose.tsx index 5f0e4e9a3..3f0aae83c 100644 --- a/ui/prose.tsx +++ b/ui/prose.tsx @@ -2,6 +2,7 @@ import clsx from 'clsx'; import React from 'react'; +import { T, Branch } from 'gt-next'; export function Prose({ children, @@ -39,7 +40,11 @@ export function Prose({ aria-expanded={!isCollapsed} className="mt-4 rounded-sm bg-gray-800 px-1.5 py-1 text-xs leading-none font-semibold whitespace-nowrap text-gray-300 tabular-nums hover:bg-gray-500 hover:text-white" > - {isCollapsed ? 'More' : 'Less'} + + + Toggle + + )}