Skip to content

Commit f1baaff

Browse files
ericyangpanclaude
andcommitted
refactor(i18n): migrate pages to hierarchical namespace
Refactor all page components to use hierarchical translation namespaces instead of flat structure. This improves code maintainability and follows the i18n architecture rules. - List pages: useTranslations('pages.{type}') - Detail pages: useTranslations('pages.{type}Detail') - Client components: useTranslations('pages.{type}') - Image generators: useTranslations('pages.{type}') Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 810f230 commit f1baaff

34 files changed

+1940
-139
lines changed

src/app/[locale]/ai-coding-stack/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type { LocalePageProps } from '@/types/locale'
99

1010
export async function generateMetadata({ params }: LocalePageProps) {
1111
const { locale } = await params
12-
const tPage = await getTranslations({ locale, namespace: 'pages.stacks.overview' })
12+
const tPage = await getTranslations({ locale, namespace: 'pages.stacksOverview' })
1313

1414
const title = buildTitle({ title: tPage('title') })
1515
const description = tPage('subtitle')
@@ -27,7 +27,7 @@ export async function generateMetadata({ params }: LocalePageProps) {
2727

2828
export default async function AICodingStackPage({ params }: LocalePageProps) {
2929
const { locale } = await params
30-
const tPage = await getTranslations({ locale, namespace: 'pages.stacks.overview' })
30+
const tPage = await getTranslations({ locale, namespace: 'pages.stacksOverview' })
3131

3232
return (
3333
<>

src/app/[locale]/articles/[slug]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export default async function ArticlePage({ params }: Props) {
9090

9191
<Breadcrumb
9292
items={[
93-
{ name: tShared('header.articles'), href: '/articles' },
93+
{ name: tShared('terms.articles'), href: '/articles' },
9494
{ name: article.title, href: `/articles/${slug}` },
9595
]}
9696
/>

src/app/[locale]/clis/[slug]/page.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export async function generateMetadata({
3737
return { title: 'CLI Not Found | AI Coding Stack' }
3838
}
3939

40-
const tShared = await getTranslations({ locale })
40+
const tShared = await getTranslations({ locale, namespace: 'shared' })
4141
const licenseStr = cli.license ? translateLicenseText(cli.license, tShared) : ''
4242

4343
return await generateSoftwareDetailMetadata({
@@ -68,8 +68,8 @@ export default async function CLIPage({
6868
notFound()
6969
}
7070

71-
const tPage = await getTranslations({ locale, namespace: 'pages.stacks.cliDetail' })
72-
const tShared = await getTranslations({ locale })
71+
const tPage = await getTranslations({ locale, namespace: 'pages.cliDetail' })
72+
const tShared = await getTranslations({ locale, namespace: 'shared' })
7373

7474
// Transform URLs for component props
7575
const websiteUrl = cli.websiteUrl || cli.resourceUrls?.download || undefined
@@ -99,8 +99,8 @@ export default async function CLIPage({
9999

100100
// Breadcrumb items
101101
const breadcrumbItems = [
102-
{ name: tShared('shared.terms.aiCodingStack'), href: '/ai-coding-stack' },
103-
{ name: tShared('shared.categories.plural.clis'), href: '/clis' },
102+
{ name: tShared('terms.aiCodingStack'), href: '/ai-coding-stack' },
103+
{ name: tShared('categories.plural.clis'), href: '/clis' },
104104
{ name: cli.name, href: `clis/${cli.id}` },
105105
]
106106

src/app/[locale]/clis/opengraph-image.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ interface Props {
1111

1212
export default async function Image({ params }: Props) {
1313
const { locale } = await params
14-
const tPage = await getTranslations({ locale, namespace: 'pages.stacks.clis' })
14+
const tPage = await getTranslations({ locale, namespace: 'pages.clis' })
1515

1616
return new ImageResponse(
1717
<OGImageTemplate

src/app/[locale]/clis/page.client.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ type Props = {
1919
}
2020

2121
export default function CLIsPageClient({ locale }: Props) {
22-
const tPage = useTranslations('pages.stacks')
23-
const tGlobal = useTranslations()
22+
const tPage = useTranslations('pages.clis')
23+
const tShared = useTranslations('shared')
2424
const [sortOrder, setSortOrder] = useState<'default' | 'name-asc' | 'name-desc'>('default')
2525
const [licenseFilters, setLicenseFilters] = useState<string[]>([])
2626
const [platformFilters, setPlatformFilters] = useState<string[]>([])
@@ -101,14 +101,14 @@ export default function CLIsPageClient({ locale }: Props) {
101101
{/* Main Content */}
102102
<main className="w-full">
103103
<PageHeader
104-
title={tPage('clis.title')}
105-
subtitle={tPage('clis.subtitle')}
104+
title={tPage('title')}
105+
subtitle={tPage('subtitle')}
106106
action={
107107
<Link
108108
href="/clis/comparison"
109109
className="text-sm px-[var(--spacing-md)] py-[var(--spacing-xs)] border border-[var(--color-border)] hover:border-[var(--color-border-strong)] transition-colors"
110110
>
111-
{tPage('clis.compareAll')}
111+
{tPage('compareAll')}
112112
</Link>
113113
}
114114
/>
@@ -128,7 +128,7 @@ export default function CLIsPageClient({ locale }: Props) {
128128

129129
{filteredAndSortedClis.length === 0 ? (
130130
<div className="text-center py-[var(--spacing-xl)] text-[var(--color-text-secondary)]">
131-
{tPage('clis.noMatches')}
131+
{tPage('noMatches')}
132132
</div>
133133
) : (
134134
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-[var(--spacing-md)]">
@@ -153,7 +153,7 @@ export default function CLIsPageClient({ locale }: Props) {
153153
<div className="flex items-center gap-[var(--spacing-xs)] text-xs text-[var(--color-text-muted)] mt-auto">
154154
<span>{cli.vendor}</span>
155155
<span className="text-[var(--color-border)]"></span>
156-
<span>{translateLicenseText(cli.license, tGlobal)}</span>
156+
<span>{translateLicenseText(cli.license, tShared)}</span>
157157
</div>
158158
</Link>
159159
))}

src/app/[locale]/clis/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s
1010
return await generateListPageMetadata({
1111
locale: locale as Locale,
1212
category: 'clis',
13-
translationNamespace: 'pages.stacks.clis',
13+
translationNamespace: 'pages.clis',
1414
additionalKeywords: ['Gemini CLI', 'GitHub Copilot CLI', 'command line AI tools'],
1515
})
1616
}

src/app/[locale]/extensions/[slug]/page.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export async function generateMetadata({
3636
return { title: 'Extension Not Found | AI Coding Stack' }
3737
}
3838

39-
const tShared = await getTranslations({ locale })
39+
const tShared = await getTranslations({ locale, namespace: 'shared' })
4040
const licenseStr = extension.license ? translateLicenseText(extension.license, tShared) : ''
4141

4242
// Convert supportedIdes to platforms format for metadata generation
@@ -72,8 +72,8 @@ export default async function ExtensionPage({
7272
notFound()
7373
}
7474

75-
const tPage = await getTranslations({ locale, namespace: 'pages.stacks.extensionDetail' })
76-
const tShared = await getTranslations({ locale })
75+
const tPage = await getTranslations({ locale, namespace: 'pages.extensionDetail' })
76+
const tShared = await getTranslations({ locale, namespace: 'shared' })
7777

7878
// Transform URLs for component props
7979
const websiteUrl = extension.websiteUrl || extension.resourceUrls?.download || undefined
@@ -119,8 +119,8 @@ export default async function ExtensionPage({
119119

120120
// Breadcrumb items
121121
const breadcrumbItems = [
122-
{ name: tShared('shared.terms.aiCodingStack'), href: '/ai-coding-stack' },
123-
{ name: tShared('shared.categories.plural.extensions'), href: '/extensions' },
122+
{ name: tShared('terms.aiCodingStack'), href: '/ai-coding-stack' },
123+
{ name: tShared('categories.plural.extensions'), href: '/extensions' },
124124
{ name: extension.name, href: `extensions/${extension.id}` },
125125
]
126126

src/app/[locale]/extensions/opengraph-image.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ interface Props {
1111

1212
export default async function Image({ params }: Props) {
1313
const { locale } = await params
14-
const tPage = await getTranslations({ locale, namespace: 'pages.stacks.extensions' })
14+
const tPage = await getTranslations({ locale, namespace: 'pages.extensions' })
1515

1616
return new ImageResponse(
1717
<OGImageTemplate

src/app/[locale]/extensions/page.client.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ type Props = {
1919
}
2020

2121
export default function ExtensionsPageClient({ locale }: Props) {
22-
const tPage = useTranslations('pages.stacks')
23-
const tGlobal = useTranslations()
22+
const tPage = useTranslations('pages.extensions')
23+
const tShared = useTranslations('shared')
2424
const [sortOrder, setSortOrder] = useState<'default' | 'name-asc' | 'name-desc'>('default')
2525
const [licenseFilters, setLicenseFilters] = useState<string[]>([])
2626
const [platformFilters, setPlatformFilters] = useState<string[]>([])
@@ -104,14 +104,14 @@ export default function ExtensionsPageClient({ locale }: Props) {
104104
{/* Main Content */}
105105
<main className="w-full">
106106
<PageHeader
107-
title={tPage('extensions.title')}
108-
subtitle={tPage('extensions.subtitle')}
107+
title={tPage('title')}
108+
subtitle={tPage('subtitle')}
109109
action={
110110
<Link
111111
href="/extensions/comparison"
112112
className="text-sm px-[var(--spacing-md)] py-[var(--spacing-xs)] border border-[var(--color-border)] hover:border-[var(--color-border-strong)] transition-colors"
113113
>
114-
{tPage('extensions.compareAll')}
114+
{tPage('compareAll')}
115115
</Link>
116116
}
117117
/>
@@ -133,7 +133,7 @@ export default function ExtensionsPageClient({ locale }: Props) {
133133

134134
{filteredAndSortedExtensions.length === 0 ? (
135135
<div className="text-center py-[var(--spacing-xl)] text-[var(--color-text-secondary)]">
136-
{tPage('extensions.noMatches')}
136+
{tPage('noMatches')}
137137
</div>
138138
) : (
139139
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-[var(--spacing-md)]">
@@ -160,7 +160,7 @@ export default function ExtensionsPageClient({ locale }: Props) {
160160
{extension.license && (
161161
<>
162162
<span className="text-[var(--color-border)]"></span>
163-
<span>{translateLicenseText(extension.license, tGlobal)}</span>
163+
<span>{translateLicenseText(extension.license, tShared)}</span>
164164
</>
165165
)}
166166
</div>

src/app/[locale]/extensions/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s
1010
return await generateListPageMetadata({
1111
locale: locale as Locale,
1212
category: 'extensions',
13-
translationNamespace: 'pages.stacks.extensions',
13+
translationNamespace: 'pages.extensions',
1414
additionalKeywords: ['AI code completion', 'VS Code extensions', 'JetBrains AI'],
1515
})
1616
}

0 commit comments

Comments
 (0)