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 (
{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 ( -+ 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{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 ( ++ + ); } 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')} />Community
+Home
; + return ( ++ + ); } 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() { - returnHome
+Profile
; + return ( ++ + ); } 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() { - returnProfile
+Settings
; + return ( ++ + ); } 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' }, ]; returnSettings
+; } 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() {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- Code + Code }) { 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 updatedsearchParams
{' '} - 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 updatedsearchParams
{' '} + prop. +- - Using
+useRouter()
-+ + Using
+useRouter()
+@@ -40,15 +50,17 @@ 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- - Using
+<Link>
-+ + Using
+<Link>
+{options.map((option) => { @@ -85,7 +97,7 @@ export default async function Page(props: { searchParams: Promise}) { - Docs + Docs { 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 +); 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({{count} Clicks -- 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 ( 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 -Error
++ Error
+{error?.message}- +{ 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 (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() { -Error
+Error
{error?.message}- + -- + ); -} +} \ 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}) + +
+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({ -- 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
-+ ); } 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 (++Not Found
+Could not find requested resource
+ -- 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 (); 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 ( -Not Found
-- Sorry, the requested resource could not be found -++ +Not Found
++ + Sorry, the requested resource could not be found ++-+ Home, slug: demo.slug }} /> -- 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 ( ); 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 ( -Not Found
-- Sorry, the requested resource could not be found -++ +Not Found
++ + Sorry, the requested resource could not be found ++-+ -- 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 (- ); 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" > -Default
++ Default
+@@ -19,7 +22,7 @@ export default function Default() {-+ Home, slug: demo.slug }} /> - 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() {-); 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" > -+ Home, slug: demo.slug }} /> 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 (- ); 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 (Default
++ Default
+@@ -15,7 +19,7 @@ export default function Default() {-+ - 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 ( -> 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'} +- 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() { @@ -36,7 +37,7 @@ export function GlobalNav({ items }: { items: DemoCategory[] }) { onClick={() => setIsOpen(!isOpen)} >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": "- ({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
+Shop++ @@ -102,13 +105,13 @@ export default async function Page({ href={prevProduct} type="transition-backwards" > - Previous +Shop+Previous - Next + Next (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 - Playground +
Playground - Menu +{isOpen ? (Menu @@ -75,6 +76,12 @@ export function GlobalNav({ items }: { items: DemoCategory[] }) { +++Language ++ + )}+ Toggle + +