diff --git a/cspell.json b/cspell.json index d42b699e..b1cfa5f7 100644 --- a/cspell.json +++ b/cspell.json @@ -44,6 +44,18 @@ "ignorePaths": ["cloudflare-env.d.ts", "docs", ".claude"], "ignoreRegExpList": ["/\\d+-g[0-9a-f]{6,}/g"], "words": [ + "Kesiapan", + "Мультимодельная", + "мультимодельную", + "мультимодельные", + "мультимодельных", + "продакшену", + "confiabilidade", + "gözlemlenebilirliğini", + "modelnya", + "observabilitas", + "observabilidad", + "observabilidade", "Autorisierungsaufträgen", "Entwicklungsframework", "İçgörüleri", diff --git a/data/data-health.json b/data/data-health.json index a5912542..b4fb15dc 100644 --- a/data/data-health.json +++ b/data/data-health.json @@ -73,59 +73,59 @@ }, "translationsByLocale": { "de": { - "totalStrings": 453, + "totalStrings": 467, "matchingEnglish": 39, - "matchingEnglishPercent": 8.6 + "matchingEnglishPercent": 8.4 }, "es": { - "totalStrings": 453, + "totalStrings": 467, "matchingEnglish": 25, - "matchingEnglishPercent": 5.5 + "matchingEnglishPercent": 5.4 }, "fr": { - "totalStrings": 453, + "totalStrings": 467, "matchingEnglish": 36, - "matchingEnglishPercent": 7.9 + "matchingEnglishPercent": 7.7 }, "id": { - "totalStrings": 453, + "totalStrings": 467, "matchingEnglish": 30, - "matchingEnglishPercent": 6.6 + "matchingEnglishPercent": 6.4 }, "ja": { - "totalStrings": 453, + "totalStrings": 467, "matchingEnglish": 23, - "matchingEnglishPercent": 5.1 + "matchingEnglishPercent": 4.9 }, "ko": { - "totalStrings": 453, + "totalStrings": 467, "matchingEnglish": 23, - "matchingEnglishPercent": 5.1 + "matchingEnglishPercent": 4.9 }, "pt": { - "totalStrings": 453, + "totalStrings": 467, "matchingEnglish": 31, - "matchingEnglishPercent": 6.8 + "matchingEnglishPercent": 6.6 }, "ru": { - "totalStrings": 453, + "totalStrings": 467, "matchingEnglish": 23, - "matchingEnglishPercent": 5.1 + "matchingEnglishPercent": 4.9 }, "tr": { - "totalStrings": 453, + "totalStrings": 467, "matchingEnglish": 26, - "matchingEnglishPercent": 5.7 + "matchingEnglishPercent": 5.6 }, "zh-Hans": { - "totalStrings": 453, + "totalStrings": 467, "matchingEnglish": 22, - "matchingEnglishPercent": 4.9 + "matchingEnglishPercent": 4.7 }, "zh-Hant": { - "totalStrings": 453, + "totalStrings": 467, "matchingEnglish": 22, - "matchingEnglishPercent": 4.9 + "matchingEnglishPercent": 4.7 } }, "issues": [] diff --git a/docs/DATA-HEALTH.md b/docs/DATA-HEALTH.md index bd421531..f1855cfe 100644 --- a/docs/DATA-HEALTH.md +++ b/docs/DATA-HEALTH.md @@ -37,17 +37,17 @@ Exact English matches are a triage signal; product names and technical terms can | Locale | Comparable strings | Exact English matches | Match rate | | --- | ---: | ---: | ---: | -| de | 453 | 39 | 8.6% | -| es | 453 | 25 | 5.5% | -| fr | 453 | 36 | 7.9% | -| id | 453 | 30 | 6.6% | -| ja | 453 | 23 | 5.1% | -| ko | 453 | 23 | 5.1% | -| pt | 453 | 31 | 6.8% | -| ru | 453 | 23 | 5.1% | -| tr | 453 | 26 | 5.7% | -| zh-Hans | 453 | 22 | 4.9% | -| zh-Hant | 453 | 22 | 4.9% | +| de | 467 | 39 | 8.4% | +| es | 467 | 25 | 5.4% | +| fr | 467 | 36 | 7.7% | +| id | 467 | 30 | 6.4% | +| ja | 467 | 23 | 4.9% | +| ko | 467 | 23 | 4.9% | +| pt | 467 | 31 | 6.6% | +| ru | 467 | 23 | 4.9% | +| tr | 467 | 26 | 5.6% | +| zh-Hans | 467 | 22 | 4.7% | +| zh-Hant | 467 | 22 | 4.7% | ## Backlog by Issue Type diff --git a/docs/SEO-SEARCH-OPPORTUNITY-MAP.md b/docs/SEO-SEARCH-OPPORTUNITY-MAP.md index 2aaca42b..f553d2bc 100644 --- a/docs/SEO-SEARCH-OPPORTUNITY-MAP.md +++ b/docs/SEO-SEARCH-OPPORTUNITY-MAP.md @@ -67,7 +67,10 @@ AI Coding Stack has stronger structured catalog data than many editorial competi | P0 | Optimize `/models` for comparison intent | It is the strongest striking-distance page | Implemented in working tree | | P0 | Complete `/models/compare` metadata | The comparison route lacked canonical, hreflang, robots, and complete social metadata | Implemented in working tree | | P0 | Reposition homepage metadata around “AI coding tools directory and comparisons” | Aligns the broad landing page with non-brand discovery intent | Implemented in working tree | -| P1 | Expand `/model-providers` selection guidance | Average position 14.3 indicates near-term potential | Planned | +| P0 | Add the lowercase domain as a fallback WebSite name | Google indexed pages but exact brand and domain discovery remained weak; the WebSite schema lacked an `alternateName` fallback | Implemented in working tree | +| P0 | Correct `/model-providers` canonical and hreflang paths | Production pointed canonical and alternates to the non-public `/modelProviders` path while the sitemap used `/model-providers` | Implemented in working tree | +| P0 | Remove static meta-keyword lists and product examples | Google Search does not use the keywords meta tag for indexing or ranking; manually curated product names become stale and distract from visible, localized search-intent content | Implemented in working tree | +| P1 | Expand `/model-providers` selection guidance | Average position 14.3 indicates near-term potential | Implemented in working tree | | P1 | Expand `/clis/codex-cli` with a direct answer and setup coverage | Multiple queries rank between positions 14.9 and 16.0 | Planned | | P1 | Expand `/ides/vscode` for “VS Code AI” intent | Highest observed product-query impressions | Planned | | P1 | Expand `/ides/kiro` for AWS/Kiro entity coverage | Multiple related queries already receive impressions | Planned | diff --git a/src/app/[locale]/ai-coding-landscape/page.tsx b/src/app/[locale]/ai-coding-landscape/page.tsx index f3f52d68..ed47b94d 100644 --- a/src/app/[locale]/ai-coding-landscape/page.tsx +++ b/src/app/[locale]/ai-coding-landscape/page.tsx @@ -20,8 +20,6 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s basePath: 'ai-coding-landscape', title, description, - keywords: - 'AI coding ecosystem, AI development landscape, AI tools, coding tools visualization, vendor comparison, product matrix', ogType: 'website', }) } diff --git a/src/app/[locale]/articles/page.tsx b/src/app/[locale]/articles/page.tsx index a3474c7b..ae191e2a 100644 --- a/src/app/[locale]/articles/page.tsx +++ b/src/app/[locale]/articles/page.tsx @@ -22,7 +22,6 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s basePath: 'articles', title, description, - keywords: tPage('keywords'), ogType: 'website', }) } diff --git a/src/app/[locale]/clis/page.tsx b/src/app/[locale]/clis/page.tsx index 0c9ff7a1..f90e59da 100644 --- a/src/app/[locale]/clis/page.tsx +++ b/src/app/[locale]/clis/page.tsx @@ -11,7 +11,6 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s locale: locale as Locale, category: 'clis', translationNamespace: 'pages.clis', - additionalKeywords: ['Gemini CLI', 'GitHub Copilot CLI', 'command line AI tools'], }) } diff --git a/src/app/[locale]/curated-collections/page.tsx b/src/app/[locale]/curated-collections/page.tsx index cdea8f88..445084e5 100644 --- a/src/app/[locale]/curated-collections/page.tsx +++ b/src/app/[locale]/curated-collections/page.tsx @@ -23,8 +23,6 @@ export async function generateMetadata({ params }: LocalePageProps) { basePath: 'curated-collections', title, description, - keywords: - 'AI coding resources, MCP protocol, Agent2Agent, development standards, AI coding articles, semantic versioning, conventional commits', ogType: 'website', }) } diff --git a/src/app/[locale]/docs/page.tsx b/src/app/[locale]/docs/page.tsx index 236f94b8..dd04a3d3 100644 --- a/src/app/[locale]/docs/page.tsx +++ b/src/app/[locale]/docs/page.tsx @@ -23,7 +23,6 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s basePath: 'docs', title, description, - keywords: tPage('keywords'), ogType: 'website', }) } diff --git a/src/app/[locale]/extensions/page.tsx b/src/app/[locale]/extensions/page.tsx index 9f4d8c20..331d01a5 100644 --- a/src/app/[locale]/extensions/page.tsx +++ b/src/app/[locale]/extensions/page.tsx @@ -11,7 +11,6 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s locale: locale as Locale, category: 'extensions', translationNamespace: 'pages.extensions', - additionalKeywords: ['AI code completion', 'VS Code extensions', 'JetBrains AI'], }) } diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx index c5528a84..325f1620 100644 --- a/src/app/[locale]/layout.tsx +++ b/src/app/[locale]/layout.tsx @@ -61,16 +61,6 @@ export async function generateMetadata({ locale: locale as Locale, title, description, - keywords: [ - 'AI coding tools', - 'AI code editor', - 'AI IDE', - 'AI coding assistant', - 'AI CLI', - 'LLM models', - 'AI coding directory', - 'coding tools comparison', - ].join(', '), canonical: canonicalPath, languageAlternates: buildLanguageAlternates(''), openGraph: { diff --git a/src/app/[locale]/manifesto/page.tsx b/src/app/[locale]/manifesto/page.tsx index 8c73c3d9..41641723 100644 --- a/src/app/[locale]/manifesto/page.tsx +++ b/src/app/[locale]/manifesto/page.tsx @@ -18,7 +18,6 @@ export async function generateMetadata({ params }: LocalePageProps) { basePath: 'manifesto', title: buildTitle({ title: tShared('terms.manifesto') }), description: tPage('subtitle'), - keywords: 'AI Coding Manifesto, AI development philosophy, AI coding principles', ogType: 'website', }) } diff --git a/src/app/[locale]/model-intelligence-index/page.tsx b/src/app/[locale]/model-intelligence-index/page.tsx index 0ac936b6..337b07ec 100644 --- a/src/app/[locale]/model-intelligence-index/page.tsx +++ b/src/app/[locale]/model-intelligence-index/page.tsx @@ -23,7 +23,6 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s basePath: 'model-intelligence-index', title: buildTitle({ title: tPage('title') }), description: tPage('description'), - keywords: tPage('keywords'), ogType: 'website', }) } diff --git a/src/app/[locale]/model-price-intelligence-index/page.tsx b/src/app/[locale]/model-price-intelligence-index/page.tsx index 32ee6c9d..3b9cabd0 100644 --- a/src/app/[locale]/model-price-intelligence-index/page.tsx +++ b/src/app/[locale]/model-price-intelligence-index/page.tsx @@ -22,7 +22,6 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s basePath: 'model-price-intelligence-index', title: buildTitle({ title: tPage('title') }), description: tPage('description'), - keywords: tPage('keywords'), ogType: 'website', }) } diff --git a/src/app/[locale]/model-providers/page.client.tsx b/src/app/[locale]/model-providers/page.client.tsx index b2b3e919..942c14f5 100644 --- a/src/app/[locale]/model-providers/page.client.tsx +++ b/src/app/[locale]/model-providers/page.client.tsx @@ -62,6 +62,40 @@ export default function ModelProvidersPageClient({ locale }: Props) { p => p.type === 'foundation-model-provider' ) const modelServiceProviders = filteredProviders.filter(p => p.type === 'model-service-provider') + const selectionCriteria = [ + { + id: 'model-access', + title: tPage('guide.criteria.modelAccess.title'), + description: tPage('guide.criteria.modelAccess.description'), + }, + { + id: 'pricing-limits', + title: tPage('guide.criteria.pricingLimits.title'), + description: tPage('guide.criteria.pricingLimits.description'), + }, + { + id: 'production-fit', + title: tPage('guide.criteria.productionFit.title'), + description: tPage('guide.criteria.productionFit.description'), + }, + ] + const faqItems = [ + { + id: 'provider-types', + question: tPage('faq.providerTypes.question'), + answer: tPage('faq.providerTypes.answer'), + }, + { + id: 'selection', + question: tPage('faq.selection.question'), + answer: tPage('faq.selection.answer'), + }, + { + id: 'first-party', + question: tPage('faq.firstParty.question'), + answer: tPage('faq.firstParty.answer'), + }, + ] return ( <> @@ -74,6 +108,47 @@ export default function ModelProvidersPageClient({ locale }: Props) { +
+

+ {tPage('guide.title')} +

+

+ {tPage('guide.description')} +

+
+ {selectionCriteria.map(criterion => ( +
+

+ {criterion.title} +

+

+ {criterion.description} +

+
+ ))} +
+
+ + {tPage('guide.browseModels')} + + + {tPage('guide.compareModels')} + +
+
+ {/* Search Box */}
+ +
+

+ {tPage('faq.title')} +

+
+ {faqItems.map(item => ( +
+

{item.question}

+

+ {item.answer} +

+
+ ))} +
+
diff --git a/src/app/[locale]/model-providers/page.tsx b/src/app/[locale]/model-providers/page.tsx index cdc98523..b95b4d47 100644 --- a/src/app/[locale]/model-providers/page.tsx +++ b/src/app/[locale]/model-providers/page.tsx @@ -1,5 +1,8 @@ +import { getTranslations } from 'next-intl/server' +import { JsonLd } from '@/components/JsonLd' import type { Locale } from '@/i18n/config' import { generateListPageMetadata } from '@/lib/metadata' +import { generateFAQPageSchema } from '@/lib/metadata/schemas' import type { LocalePageProps } from '@/types/locale' import ModelProvidersPageClient from './page.client' @@ -12,11 +15,31 @@ export async function generateMetadata({ params }: LocalePageProps) { locale: locale as Locale, category: 'modelProviders', translationNamespace: 'pages.modelProviders', - additionalKeywords: ['OpenAI', 'Anthropic', 'model API', 'AI provider comparison'], }) } export default async function ModelProvidersPage({ params }: LocalePageProps) { const { locale } = await params - return + const tPage = await getTranslations({ locale, namespace: 'pages.modelProviders' }) + const faqSchema = await generateFAQPageSchema([ + { + question: tPage('faq.providerTypes.question'), + answer: tPage('faq.providerTypes.answer'), + }, + { + question: tPage('faq.selection.question'), + answer: tPage('faq.selection.answer'), + }, + { + question: tPage('faq.firstParty.question'), + answer: tPage('faq.firstParty.answer'), + }, + ]) + + return ( + <> + + + + ) } diff --git a/src/app/[locale]/models/compare/page.tsx b/src/app/[locale]/models/compare/page.tsx index d88bbd1c..5252bf2a 100644 --- a/src/app/[locale]/models/compare/page.tsx +++ b/src/app/[locale]/models/compare/page.tsx @@ -23,8 +23,6 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s basePath: 'models/compare', title: `${t('models.title')} ${new Date().getUTCFullYear()}`, description: t('models.subtitle'), - keywords: - 'AI coding model comparison, compare coding LLMs, LLM pricing comparison, context window comparison', ogType: 'website', }) } diff --git a/src/app/[locale]/models/page.tsx b/src/app/[locale]/models/page.tsx index fdbe952b..5f2c60fb 100644 --- a/src/app/[locale]/models/page.tsx +++ b/src/app/[locale]/models/page.tsx @@ -11,7 +11,6 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s locale: locale as Locale, category: 'models', translationNamespace: 'pages.models', - additionalKeywords: ['LLM for coding', 'Claude Sonnet', 'GPT-4', 'coding AI models'], }) } diff --git a/src/app/[locale]/page.tsx b/src/app/[locale]/page.tsx index a9d3036c..e0fcc900 100644 --- a/src/app/[locale]/page.tsx +++ b/src/app/[locale]/page.tsx @@ -24,8 +24,6 @@ export async function generateMetadata({ params }: LocalePageProps) { basePath: '', title, description, - keywords: - 'AI coding, AI IDE, AI CLI, AI extensions, LLM models, AI coding tools, Cursor, Claude Code, VS Code', ogType: 'website', pageType: 'home', }) diff --git a/src/app/[locale]/vendors/page.tsx b/src/app/[locale]/vendors/page.tsx index a3a06e2c..41368964 100644 --- a/src/app/[locale]/vendors/page.tsx +++ b/src/app/[locale]/vendors/page.tsx @@ -11,13 +11,6 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s locale: locale as Locale, category: 'vendors', translationNamespace: 'pages.vendors', - additionalKeywords: [ - 'LLM vendors', - 'Google', - 'Microsoft', - 'AI companies', - 'foundation model providers', - ], }) } diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts index 502ceedc..1886351c 100644 --- a/src/app/sitemap.ts +++ b/src/app/sitemap.ts @@ -11,7 +11,8 @@ import { } from '@/lib/generated' import { articles } from '@/lib/generated/articles' import { docSections } from '@/lib/generated/docs' -import { SITE_CONFIG } from '@/lib/metadata/config' +import { METADATA_CATEGORIES, SITE_CONFIG } from '@/lib/metadata/config' +import { getCategoryRoutePath } from '@/lib/metadata/helpers' type ManifestItem = { id: string @@ -40,20 +41,13 @@ function generateLocalizedPages( export default function sitemap(): MetadataRoute.Sitemap { const baseUrl = SITE_CONFIG.url + const categoryPaths = METADATA_CATEGORIES.map(category => `/${getCategoryRoutePath(category)}`) // Static pages - generate for all locales const staticPaths = [ '/', - '/ides', - '/clis', - '/desktops', - '/extensions', - '/models', - '/model-providers', - '/vendors', - '/articles', + ...categoryPaths, '/ai-coding-stack', - '/docs', '/curated-collections', '/manifesto', '/ai-coding-landscape', diff --git a/src/lib/metadata/config.ts b/src/lib/metadata/config.ts index a751c27f..04132710 100644 --- a/src/lib/metadata/config.ts +++ b/src/lib/metadata/config.ts @@ -1,6 +1,6 @@ /** * Metadata Configuration - * Centralized configuration for site-wide metadata constants and category-specific SEO keywords + * Centralized configuration for site-wide metadata constants and structural categories */ import { defaultLocale, locales } from '@/i18n/config' @@ -68,94 +68,21 @@ export const SEO_CONFIG = { } as const /** - * Category-specific SEO keywords - * These are appended to dynamic keywords for each category + * Category identifiers used by metadata generators and category routes. + * Search terms and product examples belong in localized page content or + * schema-validated catalog data, not in this structural registry. */ -export const CATEGORY_SEO_KEYWORDS = { - ides: [ - 'AI IDE', - 'AI code editor', - 'AI development environment', - 'code completion', - 'AI pair programming', - 'VS Code', - 'Cursor IDE', - 'TRAE', - ], - clis: [ - 'AI CLI', - 'AI coding assistant', - 'command line AI', - 'terminal AI', - 'CLI tools', - 'developer CLI', - 'Codex', - 'Claude Code', - ], - desktops: [ - 'desktop coding agent', - 'AI desktop app', - 'agent command center', - 'parallel coding agents', - 'local coding agent', - ], - extensions: [ - 'AI extension', - 'IDE extension', - 'code editor plugin', - 'developer tools', - 'VS Code extension', - ], - models: [ - 'coding LLM', - 'AI model', - 'code generation', - 'LLM for coding', - 'DeepSeek', - 'Kimi K2', - 'GLM', - 'Qwen3 Coder', - ], - modelProviders: [ - 'LLM API', - 'AI API provider', - 'model provider', - 'API service', - 'DeepSeek', - 'Moonshot', - 'SiliconFlow', - 'OpenRouter', - ], - vendors: ['AI vendor', 'tool vendor', 'software vendor', 'AI company'], - articles: ['AI coding', 'development guide', 'tutorial', 'tech article'], - docs: ['documentation', 'guide', 'how-to', 'reference'], -} as const - -/** - * Category display names for SEO titles - */ -export const CATEGORY_DISPLAY_NAMES = { - ides: 'AI-Powered IDEs', - clis: 'AI Coding Assistant CLIs', - desktops: 'Desktop Coding Agents', - extensions: 'IDE Extensions', - models: 'Coding LLM Models', - modelProviders: 'LLM API Providers', - vendors: 'Tool Vendors', -} as const - -/** - * Category example tools for SEO titles - */ -export const CATEGORY_EXAMPLES = { - ides: ['VS Code', 'Cursor', 'TRAE'], - clis: ['Codex', 'Claude Code'], - desktops: ['Codex App', 'Claude Code Desktop', 'OpenCode'], - extensions: ['Copilot', 'Tabnine', 'Codeium'], - models: ['DeepSeek V3.1', 'Kimi K2', 'GLM 4.5', 'Qwen3 Coder'], - modelProviders: ['DeepSeek', 'Moonshot', 'SiliconFlow', 'OpenRouter'], - vendors: [], -} as const +export const METADATA_CATEGORIES = [ + 'ides', + 'clis', + 'desktops', + 'extensions', + 'models', + 'modelProviders', + 'vendors', + 'articles', + 'docs', +] as const -export type Category = keyof typeof CATEGORY_SEO_KEYWORDS +export type Category = (typeof METADATA_CATEGORIES)[number] export type Locale = (typeof SITE_CONFIG.supportedLocales)[number] diff --git a/src/lib/metadata/generators.ts b/src/lib/metadata/generators.ts index 1813690d..66ce995b 100644 --- a/src/lib/metadata/generators.ts +++ b/src/lib/metadata/generators.ts @@ -13,23 +13,17 @@ import type { ManifestTokenPricing, ModelLifecycle, } from '@/types/manifests' -import { - CATEGORY_DISPLAY_NAMES, - CATEGORY_SEO_KEYWORDS, - type Category, - type Locale, - METADATA_DEFAULTS, -} from './config' +import { type Category, type Locale, METADATA_DEFAULTS } from './config' import { buildAlternates, buildDetailPageTitle, - buildKeywords, buildListPageTitle, buildOpenGraph, buildProductDescription, buildTwitterCard, formatPlatforms, formatPriceForDescription, + getCategoryRoutePath, } from './helpers' import { createPageMetadata, type PageType } from './templates' @@ -44,7 +38,6 @@ export interface ListPageMetadataParams { locale: Locale category: Category translationNamespace: string - additionalKeywords?: string[] } /** @@ -128,7 +121,6 @@ export interface StaticPageMetadataParams { basePath: string title: string description: string - keywords?: string ogType?: 'website' | 'article' pageType?: 'home' | 'static' | 'search' } @@ -148,7 +140,6 @@ interface CommonMetadataOptions { languageBasePath?: string title: string description: string - keywords?: string // OpenGraph specific ogTitle?: string ogDescription?: string @@ -197,7 +188,6 @@ function buildMetadataWithSocial(options: CommonMetadataOptions): Metadata { pageType: options.pageType, title: options.title, description: options.description, - keywords: options.keywords, canonical: alternates.canonical!, languageAlternates: alternates.languages, openGraph, @@ -210,44 +200,29 @@ function buildMetadataWithSocial(options: CommonMetadataOptions): Metadata { * Returns complete metadata with robots rules via PageType 'list' */ export async function generateListPageMetadata(options: ListPageMetadataParams): Promise { - const { locale, category, translationNamespace, additionalKeywords = [] } = options + const { locale, category, translationNamespace } = options const tPage = await getTranslations({ locale, namespace: translationNamespace }) const translatedTitle = tPage('title') const description = tPage.has('meta.description') ? tPage('meta.description') : tPage('subtitle') - // Build SEO-optimized title - const categoryKeywords = CATEGORY_SEO_KEYWORDS[category as keyof typeof CATEGORY_SEO_KEYWORDS] - const title = buildListPageTitle({ translatedTitle, year: METADATA_DEFAULTS.currentYear, }) - // Build keywords - const keywords = buildKeywords([ - categoryKeywords ? [...categoryKeywords] : [], - additionalKeywords, - ]) - - // Build social media titles - const displayName = - CATEGORY_DISPLAY_NAMES[category as keyof typeof CATEGORY_DISPLAY_NAMES] || translatedTitle - const socialTitle = `${translatedTitle} - Best ${displayName} ${METADATA_DEFAULTS.currentYear}` - // Use common metadata builder return buildMetadataWithSocial({ locale, pageType: 'list', - canonicalPath: category, + canonicalPath: getCategoryRoutePath(category), title, description, - keywords, - ogTitle: socialTitle, + ogTitle: title, ogDescription: description, ogType: 'website', - twitterTitle: socialTitle, + twitterTitle: title, twitterDescription: description, }) } @@ -280,14 +255,6 @@ export async function generateSoftwareDetailMetadata( license: product.license, }) - // Build keywords - const keywords = buildKeywords([ - product.name, - product.vendor, - [...(CATEGORY_SEO_KEYWORDS[category as keyof typeof CATEGORY_SEO_KEYWORDS] || [])], - platformsStr, - ]) - // Social media titles const socialTitle = `${product.name} - ${typeDescription}` @@ -296,10 +263,9 @@ export async function generateSoftwareDetailMetadata( return buildMetadataWithSocial({ locale, pageType: 'detail', - canonicalPath: `${category}/${slug}`, + canonicalPath: `${getCategoryRoutePath(category)}/${slug}`, title, description, - keywords, ogTitle: socialTitle, ogDescription: product.description, ogType: 'article', @@ -363,15 +329,6 @@ export async function generateModelDetailMetadata( const description = `${model.name} by ${model.vendor}. ${specs.join('. ')}. ${model.description}` - // Build keywords - const keywords = buildKeywords([ - model.name, - model.vendor, - model.size || '', - model.activeParameters || '', - [...CATEGORY_SEO_KEYWORDS.models], - ]) - // Social media titles const socialTitle = `${model.name} - ${tPage('metaTitle')}` @@ -383,7 +340,6 @@ export async function generateModelDetailMetadata( canonicalPath: `models/${slug}`, title, description, - keywords, ogTitle: socialTitle, ogDescription: model.description, ogType: 'article', @@ -401,8 +357,6 @@ export async function generateComparisonMetadata( ): Promise { const { locale, category } = options - const categoryName = CATEGORY_DISPLAY_NAMES[category as keyof typeof CATEGORY_DISPLAY_NAMES] || '' - // Get translations for comparison page const tPage = await getTranslations({ locale, namespace: 'pages.comparison' }) @@ -410,31 +364,17 @@ export async function generateComparisonMetadata( const title = tPage(`${category}.title`) const description = tPage(`${category}.subtitle`) - // Build keywords - const keywords = buildKeywords([ - `${category} comparison`, - 'compare', - 'specifications', - 'pricing', - 'side-by-side', - [...(CATEGORY_SEO_KEYWORDS[category as keyof typeof CATEGORY_SEO_KEYWORDS] || [])], - ]) - - // Social media titles - const socialTitle = `${categoryName} Comparison` - // Use common metadata builder return buildMetadataWithSocial({ locale, pageType: 'comparison', - canonicalPath: `${category}/comparison`, + canonicalPath: `${getCategoryRoutePath(category)}/comparison`, title, description, - keywords, - ogTitle: socialTitle, + ogTitle: title, ogDescription: description, ogType: 'website', - twitterTitle: socialTitle, + twitterTitle: title, twitterDescription: description, }) } @@ -452,9 +392,6 @@ export async function generateArticleMetadata(options: ArticleMetadataParams): P // Build description const description = article.description - // Build keywords - const keywords = buildKeywords([article.title, [...CATEGORY_SEO_KEYWORDS.articles]]) - // Use common metadata builder // Note: OG and Twitter images are automatically detected from opengraph-image.tsx files return buildMetadataWithSocial({ @@ -463,7 +400,6 @@ export async function generateArticleMetadata(options: ArticleMetadataParams): P canonicalPath: `articles/${slug}`, title, description, - keywords, ogTitle: article.title, ogDescription: description, ogType: 'article', @@ -487,9 +423,6 @@ export async function generateDocsMetadata(options: DocsMetadataParams): Promise // Build description const description = doc.description - // Build keywords - const keywords = buildKeywords([doc.title, [...CATEGORY_SEO_KEYWORDS.docs]]) - // Use common metadata builder return buildMetadataWithSocial({ locale, @@ -497,7 +430,6 @@ export async function generateDocsMetadata(options: DocsMetadataParams): Promise canonicalPath: `docs/${slug}`, title, description, - keywords, ogTitle: doc.title, ogDescription: description, ogType: 'article', @@ -516,15 +448,7 @@ export async function generateDocsMetadata(options: DocsMetadataParams): Promise export async function generateStaticPageMetadata( options: StaticPageMetadataParams ): Promise { - const { - locale, - basePath, - title, - description, - keywords, - ogType = 'website', - pageType = 'static', - } = options + const { locale, basePath, title, description, ogType = 'website', pageType = 'static' } = options // Use common metadata builder return buildMetadataWithSocial({ @@ -533,7 +457,6 @@ export async function generateStaticPageMetadata( canonicalPath: basePath, title, description, - keywords, ogTitle: title, ogDescription: description, ogType, diff --git a/src/lib/metadata/helpers.ts b/src/lib/metadata/helpers.ts index 306e7b31..5971c155 100644 --- a/src/lib/metadata/helpers.ts +++ b/src/lib/metadata/helpers.ts @@ -25,6 +25,14 @@ function normalizeRoutePath(path: string): string { return normalized } +/** + * Converts a lower-camel-case category identifier to its public kebab-case route. + * @example 'modelProviders' → 'model-providers' + */ +export function getCategoryRoutePath(category: Category): string { + return category.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase() +} + /** * Maps internal locale format to OpenGraph locale format * @example 'zh-Hans' → 'zh_CN', 'en' → 'en_US' @@ -93,15 +101,6 @@ export function buildDetailPageTitle(options: { return `${productName} - ${typeDescription} | Features & Setup Guide ${year}` } -/** - * Builds keywords array from multiple sources - */ -export function buildKeywords(parts: Array): string { - const flatKeywords = parts.flat().filter(Boolean) - const uniqueKeywords = [...new Set(flatKeywords)] - return uniqueKeywords.join(', ') -} - /** * Builds locale-aware canonical path (not an absolute URL). * Returns a path like "/docs" for default locale or "/zh-Hans/docs" for non-default locale. diff --git a/src/lib/metadata/i18n-validation.ts b/src/lib/metadata/i18n-validation.ts index dda9d1f7..5ea75fac 100644 --- a/src/lib/metadata/i18n-validation.ts +++ b/src/lib/metadata/i18n-validation.ts @@ -23,10 +23,10 @@ export const REQUIRED_TRANSLATION_KEYS = { modelDetail: ['metaTitle', 'contextWindow'] as const, // 文章列表页 - articles: ['meta.title', 'meta.description', 'keywords'] as const, + articles: ['meta.title', 'meta.description'] as const, // 文档列表页 - docs: ['meta.title', 'meta.description', 'keywords'] as const, + docs: ['meta.title', 'meta.description'] as const, // 比较页(每个分类) comparison: (category: Category) => [`${category}.title`, `${category}.subtitle`] as const, diff --git a/src/lib/metadata/index.ts b/src/lib/metadata/index.ts index b8200ef8..c26c73ed 100644 --- a/src/lib/metadata/index.ts +++ b/src/lib/metadata/index.ts @@ -18,11 +18,9 @@ // Export configuration export { - CATEGORY_DISPLAY_NAMES, - CATEGORY_EXAMPLES, - CATEGORY_SEO_KEYWORDS, type Category, type Locale, + METADATA_CATEGORIES, METADATA_DEFAULTS, OG_IMAGE_CONFIG, SEO_CONFIG, @@ -54,7 +52,6 @@ export { buildCanonicalUrl, buildDetailPageTitle, buildFullUrl, - buildKeywords, buildLanguageAlternates, buildListPageTitle, buildOGImage, @@ -65,6 +62,7 @@ export { formatPlatforms, formatPriceForDescription, getAlternateOGLocale, + getCategoryRoutePath, mapLocaleToOG, } from './helpers' export type { PageTranslationRequirement, TranslationValidationError } from './i18n-validation' diff --git a/src/lib/metadata/required-fields.ts b/src/lib/metadata/required-fields.ts index a627e996..d1b8aa6a 100644 --- a/src/lib/metadata/required-fields.ts +++ b/src/lib/metadata/required-fields.ts @@ -60,7 +60,6 @@ export interface RequiredMetadataFields { * Optional but recommended metadata fields */ export interface RecommendedMetadataFields { - keywords?: string authors?: Array<{ name: string }> creator?: string publisher?: string @@ -207,15 +206,6 @@ export function validateMetadataCompleteness( }) } - // Check recommended fields - if (!metadata.keywords) { - errors.push({ - field: 'keywords', - message: 'Missing recommended field: keywords', - severity: 'warning', - }) - } - return errors } @@ -266,5 +256,4 @@ export function logMetadataSummary(metadata: Metadata, pageName: string): void { console.log(` Canonical: ${metadata.alternates?.canonical}`) console.log(` Languages: ${Object.keys(metadata.alternates?.languages || {}).length} locales`) console.log(` OpenGraph: ${(metadata.openGraph as OpenGraphMetadata)?.type}`) - console.log(` Keywords: ${metadata.keywords ? 'Yes' : 'No'}`) } diff --git a/src/lib/metadata/schemas/builders.ts b/src/lib/metadata/schemas/builders.ts index e277049d..09742683 100644 --- a/src/lib/metadata/schemas/builders.ts +++ b/src/lib/metadata/schemas/builders.ts @@ -367,6 +367,7 @@ export function buildArticleSchema(options: ArticleSchemaOptions): SchemaArticle */ export function buildWebSiteSchema(options?: { name?: string + alternateName?: string | string[] url?: string description?: string enableSearch?: boolean @@ -379,6 +380,7 @@ export function buildWebSiteSchema(options?: { url: options?.url || SITE_CONFIG.url, } + if (options?.alternateName) schema.alternateName = options.alternateName if (options?.description) schema.description = options.description if (options?.enableSearch !== false) { diff --git a/src/lib/metadata/schemas/generators.ts b/src/lib/metadata/schemas/generators.ts index 8d4d90aa..66aa408f 100644 --- a/src/lib/metadata/schemas/generators.ts +++ b/src/lib/metadata/schemas/generators.ts @@ -338,6 +338,7 @@ export const generateDocsSchema = cache( export const generateWebSiteSchema = cache(async (): Promise => { return buildWebSiteSchema({ name: SITE_CONFIG.name, + alternateName: SITE_CONFIG.domain, url: SITE_CONFIG.url, description: 'Comprehensive directory for AI coding tools across IDEs, CLIs, MCP servers, models and providers.', diff --git a/src/lib/metadata/schemas/types.ts b/src/lib/metadata/schemas/types.ts index 06d3fd4d..4cb0e184 100644 --- a/src/lib/metadata/schemas/types.ts +++ b/src/lib/metadata/schemas/types.ts @@ -202,6 +202,7 @@ export interface SchemaArticle extends SchemaBase { export interface SchemaWebSite extends SchemaBase { '@type': 'WebSite' name: string + alternateName?: string | string[] url: string description?: string potentialAction?: SchemaSearchAction diff --git a/src/lib/metadata/templates.ts b/src/lib/metadata/templates.ts index e445d7fc..32969e02 100644 --- a/src/lib/metadata/templates.ts +++ b/src/lib/metadata/templates.ts @@ -43,7 +43,6 @@ export interface PageMetadataOptions { pageType: PageType title: string description: string - keywords?: string canonical: string languageAlternates?: Record openGraph?: Metadata['openGraph'] @@ -68,11 +67,6 @@ export function createPageMetadata(options: PageMetadataOptions): Metadata { }, } - // Add optional fields - if (options.keywords) { - metadata.keywords = options.keywords - } - if (options.openGraph) { metadata.openGraph = options.openGraph } @@ -96,7 +90,6 @@ export interface RootLayoutMetadataOptions { locale: Locale title: string description: string - keywords: string canonical: string languageAlternates: Record openGraph: Metadata['openGraph'] @@ -111,7 +104,6 @@ export function createRootLayoutMetadata(options: RootLayoutMetadataOptions): Me template: `%s - ${SITE_CONFIG.name}`, }, description: options.description, - keywords: options.keywords, robots: getPageRobots('home'), // Authors, creator, publisher for better SEO authors: [{ name: `${SITE_CONFIG.name} Team` }], diff --git a/tests/metadata.generators.test.ts b/tests/metadata.generators.test.ts index 7aadd488..738828ae 100644 --- a/tests/metadata.generators.test.ts +++ b/tests/metadata.generators.test.ts @@ -44,7 +44,6 @@ describe('Metadata Generators', () => { expect(metadata).toBeDefined() expect(metadata.title).toBeDefined() expect(metadata.description).toBeDefined() - expect(metadata.keywords).toBeDefined() // Verify alternates expect(metadata.alternates).toBeDefined() @@ -90,7 +89,7 @@ describe('Metadata Generators', () => { expect(metadata).toBeDefined() expect(metadata.title).toContain('Cursor') expect(metadata.description).toContain('Cursor') - expect(metadata.keywords).toContain('Cursor') + expect(metadata.keywords).toBeUndefined() // Verify OpenGraph type is article for detail pages expect((metadata.openGraph as { type?: string })?.type).toBe('article') @@ -98,6 +97,23 @@ describe('Metadata Generators', () => { // Verify canonical includes category and slug expect(metadata.alternates?.canonical).toContain('ides/cursor') }) + + it('derives detail routes from camel-case category identifiers', async () => { + const metadata = await generateSoftwareDetailMetadata({ + locale: 'en', + category: 'modelProviders', + slug: 'openrouter', + product: { + name: 'OpenRouter', + description: 'Multi-model API platform', + vendor: 'OpenRouter', + }, + typeDescription: 'LLM API Provider', + }) + + expect(metadata.alternates?.canonical).toBe('/model-providers/openrouter') + expect(metadata.keywords).toBeUndefined() + }) }) describe('generateModelDetailMetadata', () => { @@ -168,7 +184,6 @@ describe('Metadata Generators', () => { expect(metadata).toBeDefined() expect(metadata.title).toBeDefined() expect(metadata.description).toBeDefined() - expect(metadata.keywords).toContain('comparison') // Verify OpenGraph type is website for comparison pages expect((metadata.openGraph as { type?: string })?.type).toBe('website') @@ -234,7 +249,6 @@ describe('Metadata Generators', () => { basePath: 'about', title: 'About Us', description: 'Learn about our mission', - keywords: 'about, mission, team', ogType: 'website', pageType: 'static', }) @@ -243,7 +257,7 @@ describe('Metadata Generators', () => { expect(metadata).toBeDefined() expect(metadata.title).toBe('About Us') expect(metadata.description).toBe('Learn about our mission') - expect(metadata.keywords).toBe('about, mission, team') + expect(metadata.keywords).toBeUndefined() // Verify OpenGraph type expect((metadata.openGraph as { type?: string })?.type).toBe('website') diff --git a/tests/metadata.list-page.test.ts b/tests/metadata.list-page.test.ts new file mode 100644 index 00000000..c56289eb --- /dev/null +++ b/tests/metadata.list-page.test.ts @@ -0,0 +1,56 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' + +const { getTranslations } = vi.hoisted(() => ({ getTranslations: vi.fn() })) + +vi.mock('next-intl/server', () => ({ getTranslations })) + +import { generateListPageMetadata } from '@/lib/metadata' +import { METADATA_CATEGORIES } from '@/lib/metadata/config' + +describe('list page metadata routes', () => { + beforeEach(() => { + const translate = Object.assign( + (key: string) => + key === 'title' + ? 'LLM API Providers for AI Coding' + : 'Compare LLM API providers for coding.', + { has: (key: string) => key === 'meta.description' } + ) + + getTranslations.mockResolvedValue(translate) + }) + + const categoryRoutes = [ + ['ides', 'ides'], + ['clis', 'clis'], + ['desktops', 'desktops'], + ['extensions', 'extensions'], + ['models', 'models'], + ['modelProviders', 'model-providers'], + ['vendors', 'vendors'], + ['articles', 'articles'], + ['docs', 'docs'], + ] as const + + it('covers every metadata category', () => { + expect(categoryRoutes.map(([category]) => category)).toEqual([...METADATA_CATEGORIES]) + }) + + it.each(categoryRoutes)('maps the %s category to /%s', async (category, publicPath) => { + const metadata = await generateListPageMetadata({ + locale: 'en', + category, + translationNamespace: 'pages.modelProviders', + }) + + expect(metadata.alternates?.canonical).toBe(`/${publicPath}`) + expect(metadata.alternates?.languages).toMatchObject({ + en: `/${publicPath}`, + 'zh-Hans': `/zh-Hans/${publicPath}`, + }) + expect((metadata.openGraph as { url?: string })?.url).toBe( + `https://aicodingstack.io/${publicPath}` + ) + expect(metadata.keywords).toBeUndefined() + }) +}) diff --git a/tests/metadata.no-meta-keywords.test.ts b/tests/metadata.no-meta-keywords.test.ts new file mode 100644 index 00000000..0be0e13e --- /dev/null +++ b/tests/metadata.no-meta-keywords.test.ts @@ -0,0 +1,221 @@ +import { readdirSync, readFileSync } from 'node:fs' +import { join } from 'node:path' +import ts from 'typescript' +import { describe, expect, it } from 'vitest' +import { + createPageMetadata, + createRootLayoutMetadata, + type PageMetadataOptions, + type RootLayoutMetadataOptions, +} from '@/lib/metadata/templates' + +function collectSourceFiles(directory: string): string[] { + return readdirSync(directory, { withFileTypes: true }).flatMap(entry => { + const path = join(directory, entry.name) + + if (entry.isDirectory()) return collectSourceFiles(path) + return /\.(ts|tsx)$/.test(entry.name) ? [path] : [] + }) +} + +function hasExportModifier(node: ts.Node): boolean { + return ts.canHaveModifiers(node) + ? ts.getModifiers(node)?.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword) === + true + : false +} + +function propertyNameIsKeywords(name: ts.PropertyName | undefined): boolean { + if (!name) return false + if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) { + return name.text === 'keywords' + } + return false +} + +function objectDefinesKeywords(node: ts.ObjectLiteralExpression): boolean { + return node.properties.some(property => { + if (ts.isShorthandPropertyAssignment(property)) return property.name.text === 'keywords' + return propertyNameIsKeywords(property.name) + }) +} + +function unwrapExpression(expression: ts.Expression): ts.Expression { + if ( + ts.isAwaitExpression(expression) || + ts.isParenthesizedExpression(expression) || + ts.isAsExpression(expression) || + ts.isSatisfiesExpression(expression) || + ts.isTypeAssertionExpression(expression) + ) { + return unwrapExpression(expression.expression) + } + return expression +} + +function findKeywordDefinitionsInMetadataExpression( + expression: ts.Expression, + initializers: ReadonlyMap, + seenIdentifiers = new Set() +): ts.ObjectLiteralExpression[] { + const unwrapped = unwrapExpression(expression) + + if (ts.isObjectLiteralExpression(unwrapped)) { + return objectDefinesKeywords(unwrapped) ? [unwrapped] : [] + } + + if (ts.isIdentifier(unwrapped)) { + if (seenIdentifiers.has(unwrapped.text)) return [] + const initializer = initializers.get(unwrapped.text) + if (!initializer) return [] + + seenIdentifiers.add(unwrapped.text) + return findKeywordDefinitionsInMetadataExpression(initializer, initializers, seenIdentifiers) + } + + if (ts.isConditionalExpression(unwrapped)) { + return [ + ...findKeywordDefinitionsInMetadataExpression( + unwrapped.whenTrue, + initializers, + new Set(seenIdentifiers) + ), + ...findKeywordDefinitionsInMetadataExpression( + unwrapped.whenFalse, + initializers, + new Set(seenIdentifiers) + ), + ] + } + + if (ts.isCallExpression(unwrapped)) { + return unwrapped.arguments.flatMap(argument => { + const value = unwrapExpression(argument) + return ts.isObjectLiteralExpression(value) && objectDefinesKeywords(value) ? [value] : [] + }) + } + + return [] +} + +function findMetaKeywordLines(sourceText: string, fileName: string): number[] { + const sourceFile = ts.createSourceFile( + fileName, + sourceText, + ts.ScriptTarget.Latest, + true, + fileName.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS + ) + const offenders: ts.ObjectLiteralExpression[] = [] + + for (const statement of sourceFile.statements) { + if ( + ts.isVariableStatement(statement) && + hasExportModifier(statement) && + statement.declarationList.declarations.some( + declaration => ts.isIdentifier(declaration.name) && declaration.name.text === 'metadata' + ) + ) { + for (const declaration of statement.declarationList.declarations) { + if ( + ts.isIdentifier(declaration.name) && + declaration.name.text === 'metadata' && + declaration.initializer + ) { + offenders.push( + ...findKeywordDefinitionsInMetadataExpression(declaration.initializer, new Map()) + ) + } + } + } + + if ( + ts.isFunctionDeclaration(statement) && + hasExportModifier(statement) && + statement.name?.text === 'generateMetadata' && + statement.body + ) { + const initializers = new Map() + + const collectInitializers = (node: ts.Node): void => { + if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) { + initializers.set(node.name.text, node.initializer) + } + ts.forEachChild(node, collectInitializers) + } + collectInitializers(statement.body) + + const inspectReturns = (node: ts.Node): void => { + if (ts.isReturnStatement(node) && node.expression) { + offenders.push( + ...findKeywordDefinitionsInMetadataExpression(node.expression, initializers) + ) + } + ts.forEachChild(node, inspectReturns) + } + inspectReturns(statement.body) + } + } + + return offenders.map(node => sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1) +} + +describe('metadata keyword guardrail', () => { + it('does not define ignored keywords in Next.js route metadata', () => { + const routeFiles = collectSourceFiles(join(process.cwd(), 'src/app')) + const offenders = routeFiles.flatMap(file => + findMetaKeywordLines(readFileSync(file, 'utf8'), file).map( + line => `${file.replace(`${process.cwd()}/`, '')}:${line}` + ) + ) + + expect(offenders).toEqual([]) + }) + + it('allows keywords in unrelated structured data', () => { + const source = ` + export default function Page() { + const articleSchema = { '@type': 'Article', keywords: ['AI coding'] } + return + } + ` + + expect(findMetaKeywordLines(source, 'page.tsx')).toEqual([]) + }) + + it('detects keywords on static and generated route metadata', () => { + const source = `export const metadata = { title: 'Example', keywords: ['ignored'] } + + export function generateMetadata() { + const result = { title: 'Example', keywords: 'ignored' } + return result + } + ` + + expect(findMetaKeywordLines(source, 'page.tsx')).toEqual([1, 4]) + }) + + it('central metadata templates ignore unsupported keyword options', () => { + const pageOptions: PageMetadataOptions & { keywords: string } = { + locale: 'en', + pageType: 'static', + title: 'Example', + description: 'Example description', + canonical: '/example', + keywords: 'ignored', + } + const rootOptions: RootLayoutMetadataOptions & { keywords: string } = { + locale: 'en', + title: 'Example', + description: 'Example description', + canonical: '/', + languageAlternates: { en: '/' }, + openGraph: {}, + twitter: {}, + keywords: 'ignored', + } + + expect(createPageMetadata(pageOptions).keywords).toBeUndefined() + expect(createRootLayoutMetadata(rootOptions).keywords).toBeUndefined() + }) +}) diff --git a/tests/sitemap.test.ts b/tests/sitemap.test.ts index 15f700b0..8b0a98b7 100644 --- a/tests/sitemap.test.ts +++ b/tests/sitemap.test.ts @@ -3,7 +3,8 @@ import { join } from 'node:path' import { describe, expect, it } from 'vitest' import sitemap from '@/app/sitemap' import { locales } from '@/i18n/config' -import { SITE_CONFIG } from '@/lib/metadata/config' +import { METADATA_CATEGORIES, SITE_CONFIG } from '@/lib/metadata/config' +import { getCategoryRoutePath } from '@/lib/metadata/helpers' describe('sitemap', () => { const entries = sitemap() @@ -30,6 +31,23 @@ describe('sitemap', () => { } }) + it('contains every public category route for every locale', () => { + const sitemapUrls = new Set(entries.map(entry => entry.url)) + + for (const category of METADATA_CATEGORIES) { + const publicPath = getCategoryRoutePath(category) + + for (const locale of locales) { + const expectedUrl = + locale === 'en' + ? `${SITE_CONFIG.url}/${publicPath}` + : `${SITE_CONFIG.url}/${locale}/${publicPath}` + + expect(sitemapUrls.has(expectedUrl)).toBe(true) + } + } + }) + it('uses verified article dates and omits synthetic modification dates elsewhere', () => { for (const entry of entries) { if (entry.url.includes('/articles/')) { diff --git a/tests/website-schema.test.ts b/tests/website-schema.test.ts new file mode 100644 index 00000000..bec7d19d --- /dev/null +++ b/tests/website-schema.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from 'vitest' +import { generateWebSiteSchema, validateSchema } from '@/lib/metadata/schemas' + +describe('website structured data', () => { + it('provides the lowercase domain as a fallback site name for search engines', async () => { + const schema = await generateWebSiteSchema() + + expect(schema).toMatchObject({ + '@type': 'WebSite', + name: 'AI Coding Stack', + alternateName: 'aicodingstack.io', + url: 'https://aicodingstack.io', + }) + expect(validateSchema(schema)).toMatchObject({ valid: true, errors: [] }) + }) +}) diff --git a/translations/de/pages/articles.json b/translations/de/pages/articles.json index ae7513fb..d6552c08 100644 --- a/translations/de/pages/articles.json +++ b/translations/de/pages/articles.json @@ -1,5 +1,4 @@ { - "keywords": "Artikel über KI-Programmierung, Tutorials zur KI-Entwicklung, Best Practices für KI-Programmierung, Leitfäden zur Programmierung mit LLMs, Leitfäden zu KI-IDEs", "meta": { "description": "Ausführliche Artikel über KI-Programmierwerkzeuge, Best Practices und die Zukunft der Softwareentwicklung mit KI-Assistenten.", "title": "Artikel und Einblicke zur KI-Programmierung" diff --git a/translations/de/pages/docs.json b/translations/de/pages/docs.json index c2a9db0f..660456d7 100644 --- a/translations/de/pages/docs.json +++ b/translations/de/pages/docs.json @@ -1,5 +1,4 @@ { - "keywords": "Dokumentation zu AI Coding Stack, Leitfaden für KI-Programmierwerkzeuge, IDE-Vergleich, CLI-Vergleich, Spezifikationen von KI-Modellen", "meta": { "description": "Umfassende Dokumentation zu AI Coding Stack. Erfahren Sie, wie Sie die Plattform verwenden, zum Projekt beitragen und Ihren KI-Programmierablauf optimieren.", "title": "Dokumentation – AI Coding Stack" diff --git a/translations/de/pages/model-intelligence-index.json b/translations/de/pages/model-intelligence-index.json index 6b8b9a4a..cc551b60 100644 --- a/translations/de/pages/model-intelligence-index.json +++ b/translations/de/pages/model-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "Umfassender Modellintelligenz-Index", - "description": "Verfolgen Sie umfassende Intelligenzwerte von KI-Modellen im Zeitverlauf, gruppiert nach Anbieter und Modellreihe.", - "keywords": "umfassender Modellintelligenz-Index, Zeitleiste für KI-Modelle, umfassende KI-Benchmarks, Veröffentlichungshistorie" + "description": "Verfolgen Sie umfassende Intelligenzwerte von KI-Modellen im Zeitverlauf, gruppiert nach Anbieter und Modellreihe." }, "title": "Umfassender Modellintelligenz-Index", "description": "Entdecken Sie, wie sich die umfassende Intelligenz von Modellen über ihre Veröffentlichungen hinweg verändert.", diff --git a/translations/de/pages/model-price-intelligence-index.json b/translations/de/pages/model-price-intelligence-index.json index fdae5dc7..28739112 100644 --- a/translations/de/pages/model-price-intelligence-index.json +++ b/translations/de/pages/model-price-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "Modellpreis × Intelligenz", - "description": "Vergleiche gängige KI-Modelle nach API-Tokenpreis und Intelligenzindex.", - "keywords": "KI-Modell Preisvergleich, Modellintelligenz, LLM API Preise, Preis Leistung" + "description": "Vergleiche gängige KI-Modelle nach API-Tokenpreis und Intelligenzindex." }, "title": "Modellpreis × Intelligenz", "description": "Vergleiche gängige Modelle nach API-Preis und Intelligenzleistung.", diff --git a/translations/de/pages/model-providers.json b/translations/de/pages/model-providers.json index 60a6c390..dce7f65c 100644 --- a/translations/de/pages/model-providers.json +++ b/translations/de/pages/model-providers.json @@ -1,4 +1,42 @@ { - "subtitle": "LLM-API-Anbieter, die Zugang zu verschiedenen AI-Modellen bieten", - "title": "AI-Coding-Modellanbieter" + "faq": { + "firstParty": { + "answer": "Wählen Sie eine Erstanbieter-API, wenn Sie die neuesten Funktionen des Anbieters und direkten Support benötigen. Wählen Sie eine Multi-Modell-Plattform, wenn einheitlicher Zugriff, Anbieter-Fallback oder ein einfacher Modellwechsel wichtiger sind.", + "question": "Sollte ich eine Erstanbieter-API oder eine Multi-Modell-API-Plattform verwenden?" + }, + "providerTypes": { + "answer": "Ein Anbieter von Basismodellen entwickelt und betreibt eigene Modelle. Ein Modelldienstanbieter stellt eine API oder Plattform bereit, die Anfragen an Modelle mehrerer Entwickler weiterleitet.", + "question": "Was unterscheidet einen Anbieter von Basismodellen von einem Modelldienstanbieter?" + }, + "selection": { + "answer": "Vergleichen Sie Modellverfügbarkeit, Tokenpreise, Kontext- und Ausgabelimits, Ratenbegrenzungen, Datenverarbeitung, regionalen Zugriff, Latenz, Zuverlässigkeit, SDK-Unterstützung und Produktionsbeobachtbarkeit.", + "question": "Was sollte ich bei der Auswahl eines LLM-API-Anbieters vergleichen?" + }, + "title": "Fragen zu LLM-API-Anbietern" + }, + "guide": { + "browseModels": "AI-Coding-Modelle durchsuchen", + "compareModels": "Coding-Modelle direkt vergleichen", + "criteria": { + "modelAccess": { + "description": "Prüfen Sie, ob der Anbieter die benötigten Coding-Modelle, Versionen, Kontextfenster und Tool-Calling-Funktionen bereitstellt.", + "title": "Modellzugriff" + }, + "pricingLimits": { + "description": "Vergleichen Sie Preise für Eingabe- und Ausgabe-Token sowie Ratenbegrenzungen, regionale Verfügbarkeit, Kontingente und mögliche Cache-Rabatte.", + "title": "Preise und Limits" + }, + "productionFit": { + "description": "Bewerten Sie Datenschutzbedingungen, Datenspeicherung, Zuverlässigkeit, Latenz, SDK-Qualität, Beobachtbarkeit und Fallback-Optionen vor dem Produktionseinsatz.", + "title": "Produktionseignung" + } + }, + "description": "Beginnen Sie mit den Modellen, die Ihr Produkt benötigt, und vergleichen Sie anschließend die tatsächlichen Bereitstellungsbedingungen. Erstanbieter bieten direkten Zugriff auf eigene Modelle; Modelldienstanbieter vereinfachen den Zugriff auf mehrere Modellfamilien über eine API.", + "title": "So wählen Sie einen LLM-API-Anbieter fürs Coding" + }, + "meta": { + "description": "Vergleichen Sie LLM-API-Anbieter fürs AI-Coding nach Modellzugriff, Preisen, Limits, Datenschutz, Zuverlässigkeit und Entwicklerwerkzeugen." + }, + "subtitle": "Erstanbieter von Modellen und Multi-Modell-API-Plattformen vergleichen", + "title": "LLM-API-Anbieter für AI-Coding" } diff --git a/translations/en/pages/articles.json b/translations/en/pages/articles.json index 0244fca0..5f2b4986 100644 --- a/translations/en/pages/articles.json +++ b/translations/en/pages/articles.json @@ -1,5 +1,4 @@ { - "keywords": "AI coding articles, AI development tutorials, AI coding best practices, LLM coding guides, AI IDE guides", "meta": { "description": "In-depth articles about AI coding tools, best practices, and the future of software development with AI assistants.", "title": "AI Coding Articles & Insights" diff --git a/translations/en/pages/docs.json b/translations/en/pages/docs.json index 095144f3..8eafc210 100644 --- a/translations/en/pages/docs.json +++ b/translations/en/pages/docs.json @@ -1,5 +1,4 @@ { - "keywords": "AI coding stack docs, AI coding tools guide, IDE comparison, CLI comparison, AI model specs", "meta": { "description": "Comprehensive documentation for AI Coding Stack. Learn how to use the platform, contribute to the project, and maximize your AI coding workflow.", "title": "Documentation - AI Coding Stack" diff --git a/translations/en/pages/model-intelligence-index.json b/translations/en/pages/model-intelligence-index.json index 6f16c9db..e49221d7 100644 --- a/translations/en/pages/model-intelligence-index.json +++ b/translations/en/pages/model-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "Comprehensive Model Intelligence Index", - "description": "Track comprehensive model intelligence scores over time, grouped by vendor and model series.", - "keywords": "comprehensive model intelligence index, AI model timeline, comprehensive AI benchmarks, model release history" + "description": "Track comprehensive model intelligence scores over time, grouped by vendor and model series." }, "title": "Comprehensive Model Intelligence Index", "description": "Explore how models' comprehensive intelligence changes across release dates.", diff --git a/translations/en/pages/model-price-intelligence-index.json b/translations/en/pages/model-price-intelligence-index.json index 22e79036..a9418db9 100644 --- a/translations/en/pages/model-price-intelligence-index.json +++ b/translations/en/pages/model-price-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "Model Price × Intelligence", - "description": "Compare mainstream AI models by API token price and Intelligence Index score.", - "keywords": "AI model price comparison, model intelligence, LLM API pricing, price performance" + "description": "Compare mainstream AI models by API token price and Intelligence Index score." }, "title": "Model Price × Intelligence", "description": "See how mainstream models compare on API price and intelligence capability.", diff --git a/translations/en/pages/model-providers.json b/translations/en/pages/model-providers.json index 40b4e841..0cca5e7a 100644 --- a/translations/en/pages/model-providers.json +++ b/translations/en/pages/model-providers.json @@ -1,4 +1,42 @@ { - "subtitle": "LLM API providers offering access to various AI models", - "title": "AI Coding Model Providers" + "faq": { + "firstParty": { + "answer": "Choose a first-party API when you need the newest vendor features and direct support. Choose a multi-model platform when unified access, provider fallback, or simpler model switching matters more.", + "question": "Should I use a first-party API or a multi-model API platform?" + }, + "providerTypes": { + "answer": "A foundation model provider develops and serves its own models. A model service provider offers one API or platform that routes requests to models from multiple developers.", + "question": "What is the difference between a foundation model provider and a model service provider?" + }, + "selection": { + "answer": "Compare model availability, token pricing, context and output limits, rate limits, data handling, regional access, latency, reliability, SDK support, and production observability.", + "question": "What should I compare when choosing an LLM API provider?" + }, + "title": "LLM API provider questions" + }, + "guide": { + "browseModels": "Browse AI coding models", + "compareModels": "Compare coding models side by side", + "criteria": { + "modelAccess": { + "description": "Confirm that the provider carries the coding models, versions, context windows, and tool-calling features your application needs.", + "title": "Model access" + }, + "pricingLimits": { + "description": "Compare input and output token prices together with rate limits, regional availability, quotas, and any caching discounts.", + "title": "Pricing and limits" + }, + "productionFit": { + "description": "Review privacy terms, data retention, reliability, latency, SDK quality, observability, and fallback options before production use.", + "title": "Production fit" + } + }, + "description": "Start with the models your product requires, then compare their real delivery conditions. First-party providers offer direct access to their own models; model service providers can simplify access to multiple model families through one API.", + "title": "How to choose an LLM API provider for coding" + }, + "meta": { + "description": "Compare LLM API providers for AI coding by model access, pricing, privacy, reliability, and developer tools across first-party and multi-model platforms." + }, + "subtitle": "Compare first-party model developers and multi-model API platforms", + "title": "LLM API Providers for AI Coding" } diff --git a/translations/es/pages/articles.json b/translations/es/pages/articles.json index c986569c..6815593e 100644 --- a/translations/es/pages/articles.json +++ b/translations/es/pages/articles.json @@ -1,5 +1,4 @@ { - "keywords": "artículos sobre programación con IA, tutoriales de desarrollo con IA, buenas prácticas de programación con IA, guías de programación con LLM, guías de IDE con IA", "meta": { "description": "Artículos detallados sobre herramientas de programación con IA, buenas prácticas y el futuro del desarrollo de software con asistentes de IA.", "title": "Artículos y perspectivas sobre programación con IA" diff --git a/translations/es/pages/docs.json b/translations/es/pages/docs.json index bec72555..53a072e5 100644 --- a/translations/es/pages/docs.json +++ b/translations/es/pages/docs.json @@ -1,5 +1,4 @@ { - "keywords": "documentación de AI Coding Stack, guía de herramientas de programación con IA, comparación de IDE, comparación de CLI, especificaciones de modelos de IA", "meta": { "description": "Documentación completa de AI Coding Stack. Aprende a usar la plataforma, contribuir al proyecto y optimizar tu flujo de programación con IA.", "title": "Documentación – AI Coding Stack" diff --git a/translations/es/pages/model-intelligence-index.json b/translations/es/pages/model-intelligence-index.json index 5458c7ce..403c7417 100644 --- a/translations/es/pages/model-intelligence-index.json +++ b/translations/es/pages/model-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "Índice integral de inteligencia de modelos", - "description": "Sigue las puntuaciones integrales de inteligencia de los modelos de IA a lo largo del tiempo, agrupadas por proveedor y serie.", - "keywords": "índice integral de inteligencia de modelos, cronología de modelos de IA, benchmarks integrales de IA, historial de lanzamientos" + "description": "Sigue las puntuaciones integrales de inteligencia de los modelos de IA a lo largo del tiempo, agrupadas por proveedor y serie." }, "title": "Índice integral de inteligencia de modelos", "description": "Explora cómo cambia la inteligencia integral de los modelos entre lanzamientos.", diff --git a/translations/es/pages/model-price-intelligence-index.json b/translations/es/pages/model-price-intelligence-index.json index 0b2a2c73..bc50444c 100644 --- a/translations/es/pages/model-price-intelligence-index.json +++ b/translations/es/pages/model-price-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "Precio del modelo × Inteligencia", - "description": "Compara modelos de IA populares por precio de tokens de API y puntuación del índice de inteligencia.", - "keywords": "comparación de precios IA, inteligencia de modelos, precios API LLM, precio rendimiento" + "description": "Compara modelos de IA populares por precio de tokens de API y puntuación del índice de inteligencia." }, "title": "Precio del modelo × Inteligencia", "description": "Compara los modelos populares por precio de API y capacidad de inteligencia.", diff --git a/translations/es/pages/model-providers.json b/translations/es/pages/model-providers.json index dee4e450..4955dfc3 100644 --- a/translations/es/pages/model-providers.json +++ b/translations/es/pages/model-providers.json @@ -1,4 +1,42 @@ { - "subtitle": "Proveedores de API de LLM que ofrecen acceso a varios modelos de IA", - "title": "Proveedores de modelos de codificación con IA" + "faq": { + "firstParty": { + "answer": "Elige una API del proveedor original si necesitas sus funciones más recientes y soporte directo. Elige una plataforma multimodelo si son más importantes el acceso unificado, la conmutación por error o cambiar de modelo con facilidad.", + "question": "¿Debo usar una API del proveedor original o una plataforma de API multimodelo?" + }, + "providerTypes": { + "answer": "Un proveedor de modelos fundacionales desarrolla y sirve sus propios modelos. Un proveedor de servicios de modelos ofrece una API o plataforma que dirige solicitudes a modelos de varios desarrolladores.", + "question": "¿Cuál es la diferencia entre un proveedor de modelos fundacionales y uno de servicios de modelos?" + }, + "selection": { + "answer": "Compara la disponibilidad de modelos, el precio por token, los límites de contexto y salida, los límites de uso, el tratamiento de datos, el acceso regional, la latencia, la fiabilidad, los SDK y la observabilidad.", + "question": "¿Qué debo comparar al elegir un proveedor de API de LLM?" + }, + "title": "Preguntas sobre proveedores de API de LLM" + }, + "guide": { + "browseModels": "Explorar modelos de IA para programación", + "compareModels": "Comparar modelos de programación", + "criteria": { + "modelAccess": { + "description": "Confirma que el proveedor ofrece los modelos de programación, versiones, ventanas de contexto y funciones de herramientas que necesita tu aplicación.", + "title": "Acceso a modelos" + }, + "pricingLimits": { + "description": "Compara los precios de tokens de entrada y salida junto con los límites de uso, la disponibilidad regional, las cuotas y los descuentos por caché.", + "title": "Precios y límites" + }, + "productionFit": { + "description": "Revisa la privacidad, la retención de datos, la fiabilidad, la latencia, la calidad de los SDK, la observabilidad y las opciones de respaldo antes de usarlo en producción.", + "title": "Idoneidad para producción" + } + }, + "description": "Empieza por los modelos que necesita tu producto y compara después sus condiciones reales de entrega. Los proveedores originales dan acceso directo a sus modelos; los servicios multimodelo simplifican el acceso a varias familias mediante una sola API.", + "title": "Cómo elegir un proveedor de API de LLM para programación" + }, + "meta": { + "description": "Compara proveedores de API de LLM para programación con IA por modelos, precios, límites, privacidad, fiabilidad y herramientas de desarrollo." + }, + "subtitle": "Compara desarrolladores de modelos y plataformas de API multimodelo", + "title": "Proveedores de API de LLM para programación con IA" } diff --git a/translations/fr/pages/articles.json b/translations/fr/pages/articles.json index 9271466d..39e4a742 100644 --- a/translations/fr/pages/articles.json +++ b/translations/fr/pages/articles.json @@ -1,5 +1,4 @@ { - "keywords": "articles sur le codage avec l’IA, tutoriels de développement avec l’IA, bonnes pratiques du codage avec l’IA, guides de codage avec les LLM, guides des IDE avec IA", "meta": { "description": "Articles approfondis sur les outils de codage avec l’IA, les bonnes pratiques et l’avenir du développement logiciel avec des assistants IA.", "title": "Articles et analyses sur le codage avec l’IA" diff --git a/translations/fr/pages/docs.json b/translations/fr/pages/docs.json index 64264ff1..20f9b515 100644 --- a/translations/fr/pages/docs.json +++ b/translations/fr/pages/docs.json @@ -1,5 +1,4 @@ { - "keywords": "documentation AI Coding Stack, guide des outils de codage avec l’IA, comparaison des IDE, comparaison des CLI, spécifications des modèles d’IA", "meta": { "description": "Documentation complète d’AI Coding Stack. Découvrez comment utiliser la plateforme, contribuer au projet et optimiser votre flux de codage avec l’IA.", "title": "Documentation – AI Coding Stack" diff --git a/translations/fr/pages/model-intelligence-index.json b/translations/fr/pages/model-intelligence-index.json index 5c90abc5..4095dc0b 100644 --- a/translations/fr/pages/model-intelligence-index.json +++ b/translations/fr/pages/model-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "Indice d’intelligence globale des modèles", - "description": "Suivez dans le temps les scores d’intelligence globale des modèles d’IA, regroupés par fournisseur et série.", - "keywords": "indice d’intelligence globale des modèles, chronologie des modèles IA, benchmarks globaux de l’IA, historique des sorties" + "description": "Suivez dans le temps les scores d’intelligence globale des modèles d’IA, regroupés par fournisseur et série." }, "title": "Indice d’intelligence globale des modèles", "description": "Découvrez l’évolution de l’intelligence globale des modèles au fil des sorties.", diff --git a/translations/fr/pages/model-price-intelligence-index.json b/translations/fr/pages/model-price-intelligence-index.json index e4cea7d2..b9999fe4 100644 --- a/translations/fr/pages/model-price-intelligence-index.json +++ b/translations/fr/pages/model-price-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "Prix du modèle × Intelligence", - "description": "Comparez les modèles d’IA courants par prix des jetons API et indice d’intelligence.", - "keywords": "comparaison prix modèles IA, intelligence des modèles, tarifs API LLM, rapport qualité prix" + "description": "Comparez les modèles d’IA courants par prix des jetons API et indice d’intelligence." }, "title": "Prix du modèle × Intelligence", "description": "Comparez les modèles courants selon leur prix API et leur capacité d’intelligence.", diff --git a/translations/fr/pages/model-providers.json b/translations/fr/pages/model-providers.json index 4c6010b9..f24ab771 100644 --- a/translations/fr/pages/model-providers.json +++ b/translations/fr/pages/model-providers.json @@ -1,4 +1,42 @@ { - "subtitle": "Fournisseurs d'API LLM offrant l'accès à divers modèles d'IA", - "title": "Fournisseurs de modèles de codage IA" + "faq": { + "firstParty": { + "answer": "Choisissez une API propriétaire si vous avez besoin des dernières fonctions du fournisseur et d'un support direct. Préférez une plateforme multimodèle si l'accès unifié, le repli entre fournisseurs ou le changement de modèle comptent davantage.", + "question": "Faut-il utiliser une API propriétaire ou une plateforme d'API multimodèle ?" + }, + "providerTypes": { + "answer": "Un fournisseur de modèles de fondation développe et sert ses propres modèles. Un fournisseur de services de modèles propose une API ou une plateforme qui achemine les requêtes vers les modèles de plusieurs développeurs.", + "question": "Quelle est la différence entre un fournisseur de modèles de fondation et un fournisseur de services de modèles ?" + }, + "selection": { + "answer": "Comparez la disponibilité des modèles, les tarifs par jeton, les limites de contexte et de sortie, les quotas, le traitement des données, l'accès régional, la latence, la fiabilité, les SDK et l'observabilité.", + "question": "Que faut-il comparer pour choisir un fournisseur d'API LLM ?" + }, + "title": "Questions sur les fournisseurs d'API LLM" + }, + "guide": { + "browseModels": "Parcourir les modèles d'IA pour le code", + "compareModels": "Comparer les modèles de code", + "criteria": { + "modelAccess": { + "description": "Vérifiez que le fournisseur propose les modèles de code, versions, fenêtres de contexte et fonctions d'appel d'outils dont votre application a besoin.", + "title": "Accès aux modèles" + }, + "pricingLimits": { + "description": "Comparez les tarifs des jetons d'entrée et de sortie, les limites de débit, la disponibilité régionale, les quotas et les remises de mise en cache.", + "title": "Tarifs et limites" + }, + "productionFit": { + "description": "Examinez la confidentialité, la conservation des données, la fiabilité, la latence, la qualité des SDK, l'observabilité et les solutions de repli avant la mise en production.", + "title": "Aptitude à la production" + } + }, + "description": "Commencez par les modèles nécessaires à votre produit, puis comparez leurs conditions réelles de mise à disposition. Les fournisseurs propriétaires donnent un accès direct à leurs modèles ; les services multimodèles simplifient l'accès à plusieurs familles via une seule API.", + "title": "Comment choisir un fournisseur d'API LLM pour le code" + }, + "meta": { + "description": "Comparez les fournisseurs d'API LLM pour le code selon les modèles, les tarifs, les limites, la confidentialité, la fiabilité et les outils de développement." + }, + "subtitle": "Comparez les développeurs de modèles et les plateformes d'API multimodèle", + "title": "Fournisseurs d'API LLM pour le code assisté par IA" } diff --git a/translations/id/pages/articles.json b/translations/id/pages/articles.json index 59e687f0..86960e96 100644 --- a/translations/id/pages/articles.json +++ b/translations/id/pages/articles.json @@ -1,5 +1,4 @@ { - "keywords": "artikel coding AI, tutorial pengembangan AI, praktik terbaik coding AI, panduan coding LLM, panduan IDE AI", "meta": { "description": "Artikel mendalam tentang alat coding AI, praktik terbaik, dan masa depan pengembangan perangkat lunak dengan asisten AI.", "title": "Artikel dan Wawasan Coding AI" diff --git a/translations/id/pages/docs.json b/translations/id/pages/docs.json index 6eb24bf8..9c65b6a2 100644 --- a/translations/id/pages/docs.json +++ b/translations/id/pages/docs.json @@ -1,5 +1,4 @@ { - "keywords": "dokumentasi AI Coding Stack, panduan alat coding AI, perbandingan IDE, perbandingan CLI, spesifikasi model AI", "meta": { "description": "Dokumentasi lengkap untuk AI Coding Stack. Pelajari cara menggunakan platform, berkontribusi pada proyek, dan memaksimalkan alur kerja coding AI Anda.", "title": "Dokumentasi – AI Coding Stack" diff --git a/translations/id/pages/model-intelligence-index.json b/translations/id/pages/model-intelligence-index.json index 141d9fde..1932e367 100644 --- a/translations/id/pages/model-intelligence-index.json +++ b/translations/id/pages/model-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "Indeks Kecerdasan Model Komprehensif", - "description": "Lacak skor kecerdasan komprehensif model AI dari waktu ke waktu, dikelompokkan berdasarkan vendor dan seri model.", - "keywords": "indeks kecerdasan model komprehensif, linimasa model AI, benchmark AI komprehensif, riwayat rilis model" + "description": "Lacak skor kecerdasan komprehensif model AI dari waktu ke waktu, dikelompokkan berdasarkan vendor dan seri model." }, "title": "Indeks Kecerdasan Model Komprehensif", "description": "Jelajahi perubahan kecerdasan komprehensif model di setiap tanggal rilis.", diff --git a/translations/id/pages/model-price-intelligence-index.json b/translations/id/pages/model-price-intelligence-index.json index 0d254e86..8c541be9 100644 --- a/translations/id/pages/model-price-intelligence-index.json +++ b/translations/id/pages/model-price-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "Harga Model × Kecerdasan", - "description": "Bandingkan model AI populer berdasarkan harga token API dan skor Indeks Kecerdasan.", - "keywords": "perbandingan harga model AI, kecerdasan model, harga API LLM, harga kinerja" + "description": "Bandingkan model AI populer berdasarkan harga token API dan skor Indeks Kecerdasan." }, "title": "Harga Model × Kecerdasan", "description": "Lihat perbandingan model populer berdasarkan harga API dan kemampuan kecerdasan.", diff --git a/translations/id/pages/model-providers.json b/translations/id/pages/model-providers.json index 9f156dc1..e49feddc 100644 --- a/translations/id/pages/model-providers.json +++ b/translations/id/pages/model-providers.json @@ -1,4 +1,42 @@ { - "subtitle": "Penyedia API LLM yang menawarkan akses ke berbagai model AI", - "title": "Penyedia Model Coding AI" + "faq": { + "firstParty": { + "answer": "Pilih API pihak pertama jika Anda memerlukan fitur terbaru dari vendor dan dukungan langsung. Pilih platform multimodel jika akses terpadu, pengalihan penyedia, atau pergantian model yang mudah lebih penting.", + "question": "Haruskah saya memakai API pihak pertama atau platform API multimodel?" + }, + "providerTypes": { + "answer": "Penyedia model fondasi mengembangkan dan melayani modelnya sendiri. Penyedia layanan model menawarkan satu API atau platform yang meneruskan permintaan ke model dari beberapa pengembang.", + "question": "Apa perbedaan penyedia model fondasi dan penyedia layanan model?" + }, + "selection": { + "answer": "Bandingkan ketersediaan model, harga token, batas konteks dan keluaran, batas laju, penanganan data, akses regional, latensi, keandalan, dukungan SDK, dan observabilitas produksi.", + "question": "Apa yang perlu dibandingkan saat memilih penyedia API LLM?" + }, + "title": "Pertanyaan tentang penyedia API LLM" + }, + "guide": { + "browseModels": "Jelajahi model AI untuk coding", + "compareModels": "Bandingkan model coding", + "criteria": { + "modelAccess": { + "description": "Pastikan penyedia menawarkan model coding, versi, jendela konteks, dan fitur pemanggilan alat yang dibutuhkan aplikasi Anda.", + "title": "Akses model" + }, + "pricingLimits": { + "description": "Bandingkan harga token input dan output bersama batas laju, ketersediaan regional, kuota, dan diskon caching.", + "title": "Harga dan batas" + }, + "productionFit": { + "description": "Tinjau ketentuan privasi, retensi data, keandalan, latensi, kualitas SDK, observabilitas, dan opsi pengalihan sebelum digunakan di produksi.", + "title": "Kesiapan produksi" + } + }, + "description": "Mulailah dari model yang dibutuhkan produk Anda, lalu bandingkan kondisi penyediaannya. Penyedia pihak pertama memberi akses langsung ke model sendiri; layanan multimodel menyederhanakan akses ke beberapa keluarga model melalui satu API.", + "title": "Cara memilih penyedia API LLM untuk coding" + }, + "meta": { + "description": "Bandingkan penyedia API LLM untuk coding AI berdasarkan akses model, harga, batas, privasi, keandalan, dan alat pengembang." + }, + "subtitle": "Bandingkan pengembang model pihak pertama dan platform API multimodel", + "title": "Penyedia API LLM untuk Coding AI" } diff --git a/translations/ja/pages/articles.json b/translations/ja/pages/articles.json index 2da6494d..93e91caf 100644 --- a/translations/ja/pages/articles.json +++ b/translations/ja/pages/articles.json @@ -1,5 +1,4 @@ { - "keywords": "AIコーディング記事, AI開発チュートリアル, AIコーディングのベストプラクティス, LLMコーディングガイド, AI IDEガイド", "meta": { "description": "AIコーディングツール、ベストプラクティス、AIアシスタントによるソフトウェア開発の未来を詳しく解説する記事です。", "title": "AIコーディングの記事とインサイト" diff --git a/translations/ja/pages/docs.json b/translations/ja/pages/docs.json index a935b47e..52147633 100644 --- a/translations/ja/pages/docs.json +++ b/translations/ja/pages/docs.json @@ -1,5 +1,4 @@ { - "keywords": "AI Coding Stackドキュメント, AIコーディングツールガイド, IDE比較, CLI比較, AIモデル仕様", "meta": { "description": "AI Coding Stackの総合ドキュメントです。プラットフォームの使い方、プロジェクトへの貢献方法、AIコーディングワークフローを最大限に活用する方法を紹介します。", "title": "ドキュメント – AI Coding Stack" diff --git a/translations/ja/pages/model-intelligence-index.json b/translations/ja/pages/model-intelligence-index.json index edb8c2e2..d1d21847 100644 --- a/translations/ja/pages/model-intelligence-index.json +++ b/translations/ja/pages/model-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "モデル総合知能指数", - "description": "AIモデルの総合知能スコアの推移を、ベンダーとモデル系列ごとに追跡します。", - "keywords": "モデル総合知能指数, AIモデル年表, AI総合ベンチマーク, モデルリリース履歴" + "description": "AIモデルの総合知能スコアの推移を、ベンダーとモデル系列ごとに追跡します。" }, "title": "モデル総合知能指数", "description": "リリースごとのモデルの総合知能の変化を確認できます。", diff --git a/translations/ja/pages/model-price-intelligence-index.json b/translations/ja/pages/model-price-intelligence-index.json index 7f9a7545..3cefb546 100644 --- a/translations/ja/pages/model-price-intelligence-index.json +++ b/translations/ja/pages/model-price-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "モデル価格 × インテリジェンス", - "description": "主要なAIモデルをAPIトークン価格と総合知能指数で比較します。", - "keywords": "AIモデル価格比較, モデル知能, LLM API料金, コストパフォーマンス" + "description": "主要なAIモデルをAPIトークン価格と総合知能指数で比較します。" }, "title": "モデル価格 × インテリジェンス", "description": "主要モデルのAPI価格とインテリジェンス能力を比較します。", diff --git a/translations/ja/pages/model-providers.json b/translations/ja/pages/model-providers.json index fa0c5fb2..6cd2c1db 100644 --- a/translations/ja/pages/model-providers.json +++ b/translations/ja/pages/model-providers.json @@ -1,4 +1,42 @@ { - "subtitle": "様々な AI モデルへのアクセスを提供する LLM API プロバイダー", - "title": "AI コーディングモデルプロバイダー" + "faq": { + "firstParty": { + "answer": "ベンダーの最新機能と直接サポートが必要なら、ファーストパーティ API が適しています。統一アクセス、プロバイダーのフォールバック、簡単なモデル切り替えを重視するなら、マルチモデル基盤が適しています。", + "question": "ファーストパーティ API とマルチモデル API 基盤のどちらを使うべきですか?" + }, + "providerTypes": { + "answer": "基盤モデルプロバイダーは自社モデルを開発して提供します。モデルサービスプロバイダーは、複数の開発元のモデルへリクエストを振り分ける単一の API または基盤を提供します。", + "question": "基盤モデルプロバイダーとモデルサービスプロバイダーの違いは何ですか?" + }, + "selection": { + "answer": "モデルの提供状況、トークン料金、コンテキストと出力の上限、レート制限、データの取り扱い、地域対応、レイテンシ、信頼性、SDK、運用時の可観測性を比較してください。", + "question": "LLM API プロバイダーを選ぶとき、何を比較すべきですか?" + }, + "title": "LLM API プロバイダーに関する質問" + }, + "guide": { + "browseModels": "AI コーディングモデルを見る", + "compareModels": "コーディングモデルを比較する", + "criteria": { + "modelAccess": { + "description": "アプリに必要なコーディングモデル、バージョン、コンテキストウィンドウ、ツール呼び出し機能が提供されているか確認します。", + "title": "モデルへのアクセス" + }, + "pricingLimits": { + "description": "入力・出力トークン料金に加えて、レート制限、地域対応、クォータ、キャッシュ割引を比較します。", + "title": "料金と上限" + }, + "productionFit": { + "description": "本番利用の前に、プライバシー条件、データ保持、信頼性、レイテンシ、SDK の品質、可観測性、フォールバック手段を確認します。", + "title": "本番環境への適合性" + } + }, + "description": "まず製品に必要なモデルを決め、その実際の提供条件を比較します。ファーストパーティは自社モデルへ直接アクセスでき、マルチモデルサービスは 1 つの API で複数のモデル群へアクセスしやすくします。", + "title": "コーディング向け LLM API プロバイダーの選び方" + }, + "meta": { + "description": "AI コーディング向け LLM API プロバイダーを、モデル、料金、上限、プライバシー、信頼性、開発ツールで比較できます。" + }, + "subtitle": "モデル開発元とマルチモデル API 基盤を比較", + "title": "AI コーディング向け LLM API プロバイダー" } diff --git a/translations/ko/pages/articles.json b/translations/ko/pages/articles.json index 6dc9fe0d..9bc03a8d 100644 --- a/translations/ko/pages/articles.json +++ b/translations/ko/pages/articles.json @@ -1,5 +1,4 @@ { - "keywords": "AI 코딩 기사, AI 개발 튜토리얼, AI 코딩 모범 사례, LLM 코딩 가이드, AI IDE 가이드", "meta": { "description": "AI 코딩 도구, 모범 사례, AI 어시스턴트와 함께하는 소프트웨어 개발의 미래를 심층적으로 다루는 기사입니다.", "title": "AI 코딩 기사 및 인사이트" diff --git a/translations/ko/pages/docs.json b/translations/ko/pages/docs.json index 31f2df8f..2a736654 100644 --- a/translations/ko/pages/docs.json +++ b/translations/ko/pages/docs.json @@ -1,5 +1,4 @@ { - "keywords": "AI Coding Stack 문서, AI 코딩 도구 가이드, IDE 비교, CLI 비교, AI 모델 사양", "meta": { "description": "AI Coding Stack 종합 문서입니다. 플랫폼 사용법, 프로젝트 기여 방법, AI 코딩 워크플로를 최대한 활용하는 방법을 알아보세요.", "title": "문서 – AI Coding Stack" diff --git a/translations/ko/pages/model-intelligence-index.json b/translations/ko/pages/model-intelligence-index.json index 604897f1..a7b347f5 100644 --- a/translations/ko/pages/model-intelligence-index.json +++ b/translations/ko/pages/model-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "모델 종합 지능 지수", - "description": "AI 모델의 종합 지능 점수 변화를 공급업체 및 모델 계열별로 추적합니다.", - "keywords": "모델 종합 지능 지수, AI 모델 타임라인, 종합 AI 벤치마크, 모델 출시 이력" + "description": "AI 모델의 종합 지능 점수 변화를 공급업체 및 모델 계열별로 추적합니다." }, "title": "모델 종합 지능 지수", "description": "출시 시점에 따른 모델의 종합 지능 변화를 살펴보세요.", diff --git a/translations/ko/pages/model-price-intelligence-index.json b/translations/ko/pages/model-price-intelligence-index.json index c6387a2b..329b5637 100644 --- a/translations/ko/pages/model-price-intelligence-index.json +++ b/translations/ko/pages/model-price-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "모델 가격 × 인텔리전스", - "description": "주요 AI 모델을 API 토큰 가격과 종합 지능 지수로 비교합니다.", - "keywords": "AI 모델 가격 비교, 모델 지능, LLM API 가격, 가격 성능" + "description": "주요 AI 모델을 API 토큰 가격과 종합 지능 지수로 비교합니다." }, "title": "모델 가격 × 인텔리전스", "description": "주요 모델의 API 가격과 인텔리전스 역량을 비교해 보세요.", diff --git a/translations/ko/pages/model-providers.json b/translations/ko/pages/model-providers.json index b10eba1f..5cbd1410 100644 --- a/translations/ko/pages/model-providers.json +++ b/translations/ko/pages/model-providers.json @@ -1,4 +1,42 @@ { - "subtitle": "다양한 AI 모델에 액세스를 제공하는 LLM API 제공업체", - "title": "AI 코딩 모델 제공자" + "faq": { + "firstParty": { + "answer": "공급자의 최신 기능과 직접 지원이 필요하면 자사 API를 선택하세요. 통합 접근, 공급자 장애 조치 또는 간편한 모델 전환이 더 중요하면 멀티 모델 플랫폼이 적합합니다.", + "question": "자사 API와 멀티 모델 API 플랫폼 중 무엇을 사용해야 하나요?" + }, + "providerTypes": { + "answer": "파운데이션 모델 제공자는 자체 모델을 개발하고 서비스합니다. 모델 서비스 제공자는 여러 개발사의 모델로 요청을 전달하는 하나의 API 또는 플랫폼을 제공합니다.", + "question": "파운데이션 모델 제공자와 모델 서비스 제공자의 차이는 무엇인가요?" + }, + "selection": { + "answer": "모델 제공 여부, 토큰 가격, 컨텍스트와 출력 한도, 요청 한도, 데이터 처리, 지역 지원, 지연 시간, 안정성, SDK 지원, 운영 관측성을 비교하세요.", + "question": "LLM API 제공자를 선택할 때 무엇을 비교해야 하나요?" + }, + "title": "LLM API 제공자 관련 질문" + }, + "guide": { + "browseModels": "AI 코딩 모델 둘러보기", + "compareModels": "코딩 모델 비교하기", + "criteria": { + "modelAccess": { + "description": "애플리케이션에 필요한 코딩 모델, 버전, 컨텍스트 창, 도구 호출 기능을 제공하는지 확인하세요.", + "title": "모델 접근" + }, + "pricingLimits": { + "description": "입력·출력 토큰 가격과 함께 요청 한도, 지역별 제공 여부, 할당량, 캐시 할인도 비교하세요.", + "title": "가격과 한도" + }, + "productionFit": { + "description": "프로덕션 사용 전에 개인정보 보호 조건, 데이터 보존, 안정성, 지연 시간, SDK 품질, 관측성, 장애 조치 옵션을 검토하세요.", + "title": "프로덕션 적합성" + } + }, + "description": "제품에 필요한 모델부터 정한 뒤 실제 제공 조건을 비교하세요. 자사 제공자는 자체 모델에 직접 접근하게 하고, 멀티 모델 서비스는 하나의 API로 여러 모델 계열을 간편하게 사용할 수 있게 합니다.", + "title": "코딩용 LLM API 제공자 선택 방법" + }, + "meta": { + "description": "AI 코딩용 LLM API 제공자를 모델 접근, 가격, 한도, 개인정보 보호, 안정성, 개발 도구 기준으로 비교하세요." + }, + "subtitle": "모델 개발사와 멀티 모델 API 플랫폼 비교", + "title": "AI 코딩용 LLM API 제공자" } diff --git a/translations/pt/pages/articles.json b/translations/pt/pages/articles.json index 8ec25ffa..67c2aea2 100644 --- a/translations/pt/pages/articles.json +++ b/translations/pt/pages/articles.json @@ -1,5 +1,4 @@ { - "keywords": "artigos sobre programação com IA, tutoriais de desenvolvimento com IA, boas práticas de programação com IA, guias de programação com LLM, guias de IDE com IA", "meta": { "description": "Artigos aprofundados sobre ferramentas de programação com IA, boas práticas e o futuro do desenvolvimento de software com assistentes de IA.", "title": "Artigos e insights sobre programação com IA" diff --git a/translations/pt/pages/docs.json b/translations/pt/pages/docs.json index 64dafd95..8a12b50f 100644 --- a/translations/pt/pages/docs.json +++ b/translations/pt/pages/docs.json @@ -1,5 +1,4 @@ { - "keywords": "documentação do AI Coding Stack, guia de ferramentas de programação com IA, comparação de IDEs, comparação de CLIs, especificações de modelos de IA", "meta": { "description": "Documentação completa do AI Coding Stack. Aprenda a usar a plataforma, contribuir com o projeto e aproveitar ao máximo seu fluxo de programação com IA.", "title": "Documentação – AI Coding Stack" diff --git a/translations/pt/pages/model-intelligence-index.json b/translations/pt/pages/model-intelligence-index.json index 754b717b..f725bdba 100644 --- a/translations/pt/pages/model-intelligence-index.json +++ b/translations/pt/pages/model-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "Índice abrangente de inteligência dos modelos", - "description": "Acompanhe as pontuações abrangentes de inteligência dos modelos de IA ao longo do tempo, agrupadas por fornecedor e série.", - "keywords": "índice abrangente de inteligência dos modelos, cronologia de modelos de IA, benchmarks abrangentes de IA, histórico de lançamentos" + "description": "Acompanhe as pontuações abrangentes de inteligência dos modelos de IA ao longo do tempo, agrupadas por fornecedor e série." }, "title": "Índice abrangente de inteligência dos modelos", "description": "Explore como a inteligência abrangente dos modelos muda entre lançamentos.", diff --git a/translations/pt/pages/model-price-intelligence-index.json b/translations/pt/pages/model-price-intelligence-index.json index a2fcf33d..4140bb56 100644 --- a/translations/pt/pages/model-price-intelligence-index.json +++ b/translations/pt/pages/model-price-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "Preço do modelo × Inteligência", - "description": "Compare modelos de IA populares por preço de tokens da API e índice de inteligência.", - "keywords": "comparação de preços de IA, inteligência de modelos, preços API LLM, preço desempenho" + "description": "Compare modelos de IA populares por preço de tokens da API e índice de inteligência." }, "title": "Preço do modelo × Inteligência", "description": "Compare modelos populares por preço de API e capacidade de inteligência.", diff --git a/translations/pt/pages/model-providers.json b/translations/pt/pages/model-providers.json index 1ffa3f5d..393e5a83 100644 --- a/translations/pt/pages/model-providers.json +++ b/translations/pt/pages/model-providers.json @@ -1,4 +1,42 @@ { - "subtitle": "Provedores de API LLM que oferecem acesso a vários modelos de IA", - "title": "Provedores de modelos de programação com IA" + "faq": { + "firstParty": { + "answer": "Escolha uma API própria quando precisar dos recursos mais recentes do fornecedor e de suporte direto. Escolha uma plataforma multimodelo quando acesso unificado, contingência entre provedores ou troca simples de modelos forem mais importantes.", + "question": "Devo usar uma API própria ou uma plataforma de API multimodelo?" + }, + "providerTypes": { + "answer": "Um provedor de modelos fundamentais desenvolve e oferece seus próprios modelos. Um provedor de serviços de modelos oferece uma API ou plataforma que encaminha solicitações para modelos de vários desenvolvedores.", + "question": "Qual é a diferença entre um provedor de modelos fundamentais e um provedor de serviços de modelos?" + }, + "selection": { + "answer": "Compare disponibilidade de modelos, preços por token, limites de contexto e saída, limites de uso, tratamento de dados, acesso regional, latência, confiabilidade, suporte a SDK e observabilidade em produção.", + "question": "O que devo comparar ao escolher um provedor de API LLM?" + }, + "title": "Perguntas sobre provedores de API LLM" + }, + "guide": { + "browseModels": "Explorar modelos de IA para programação", + "compareModels": "Comparar modelos de programação", + "criteria": { + "modelAccess": { + "description": "Confirme se o provedor oferece os modelos de programação, versões, janelas de contexto e recursos de chamada de ferramentas necessários ao seu aplicativo.", + "title": "Acesso a modelos" + }, + "pricingLimits": { + "description": "Compare os preços de tokens de entrada e saída com limites de uso, disponibilidade regional, cotas e descontos de cache.", + "title": "Preços e limites" + }, + "productionFit": { + "description": "Revise privacidade, retenção de dados, confiabilidade, latência, qualidade dos SDKs, observabilidade e opções de contingência antes do uso em produção.", + "title": "Adequação à produção" + } + }, + "description": "Comece pelos modelos necessários ao seu produto e compare as condições reais de fornecimento. Provedores próprios dão acesso direto aos seus modelos; serviços multimodelo simplificam o acesso a várias famílias por uma única API.", + "title": "Como escolher um provedor de API LLM para programação" + }, + "meta": { + "description": "Compare provedores de API LLM para programação com IA por modelos, preços, limites, privacidade, confiabilidade e ferramentas de desenvolvimento." + }, + "subtitle": "Compare desenvolvedores de modelos e plataformas de API multimodelo", + "title": "Provedores de API LLM para programação com IA" } diff --git a/translations/ru/pages/articles.json b/translations/ru/pages/articles.json index 4b6983c7..1b87e926 100644 --- a/translations/ru/pages/articles.json +++ b/translations/ru/pages/articles.json @@ -1,5 +1,4 @@ { - "keywords": "статьи о программировании с ИИ, руководства по разработке с ИИ, лучшие практики программирования с ИИ, руководства по программированию с LLM, руководства по ИИ-IDE", "meta": { "description": "Подробные статьи об инструментах программирования с ИИ, лучших практиках и будущем разработки программного обеспечения с ИИ-ассистентами.", "title": "Статьи и аналитика о программировании с ИИ" diff --git a/translations/ru/pages/docs.json b/translations/ru/pages/docs.json index cef2fd70..4c78147b 100644 --- a/translations/ru/pages/docs.json +++ b/translations/ru/pages/docs.json @@ -1,5 +1,4 @@ { - "keywords": "документация AI Coding Stack, руководство по инструментам программирования с ИИ, сравнение IDE, сравнение CLI, характеристики моделей ИИ", "meta": { "description": "Полная документация AI Coding Stack. Узнайте, как пользоваться платформой, участвовать в проекте и максимально эффективно выстроить процесс программирования с ИИ.", "title": "Документация – AI Coding Stack" diff --git a/translations/ru/pages/model-intelligence-index.json b/translations/ru/pages/model-intelligence-index.json index 5e26033d..258bc178 100644 --- a/translations/ru/pages/model-intelligence-index.json +++ b/translations/ru/pages/model-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "Комплексный индекс интеллекта моделей", - "description": "Отслеживайте комплексные оценки интеллекта ИИ-моделей во времени с группировкой по разработчикам и сериям.", - "keywords": "комплексный индекс интеллекта моделей, хронология AI-моделей, комплексные тесты ИИ, история выпусков" + "description": "Отслеживайте комплексные оценки интеллекта ИИ-моделей во времени с группировкой по разработчикам и сериям." }, "title": "Комплексный индекс интеллекта моделей", "description": "Узнайте, как меняется комплексный интеллект моделей от выпуска к выпуску.", diff --git a/translations/ru/pages/model-price-intelligence-index.json b/translations/ru/pages/model-price-intelligence-index.json index c20ef8aa..29c891c1 100644 --- a/translations/ru/pages/model-price-intelligence-index.json +++ b/translations/ru/pages/model-price-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "Цена модели × Интеллект", - "description": "Сравнение популярных моделей ИИ по цене API-токенов и индексу интеллекта.", - "keywords": "сравнение цен моделей ИИ, интеллект моделей, цены API LLM, цена производительность" + "description": "Сравнение популярных моделей ИИ по цене API-токенов и индексу интеллекта." }, "title": "Цена модели × Интеллект", "description": "Сравните популярные модели по цене API и интеллектуальным возможностям.", diff --git a/translations/ru/pages/model-providers.json b/translations/ru/pages/model-providers.json index 681dbcac..bbb908fb 100644 --- a/translations/ru/pages/model-providers.json +++ b/translations/ru/pages/model-providers.json @@ -1,4 +1,42 @@ { - "subtitle": "Поставщики API LLM, предоставляющие доступ к различным моделям ИИ", - "title": "Поставщики моделей ИИ-кодинга" + "faq": { + "firstParty": { + "answer": "Выбирайте API разработчика модели, если нужны новейшие функции и прямая поддержка. Мультимодельная платформа лучше подходит, когда важнее единый доступ, резервирование поставщиков или простое переключение моделей.", + "question": "Что выбрать: API разработчика модели или мультимодельную API-платформу?" + }, + "providerTypes": { + "answer": "Поставщик фундаментальных моделей разрабатывает и обслуживает собственные модели. Поставщик модельных сервисов предлагает единый API или платформу, которая направляет запросы к моделям разных разработчиков.", + "question": "Чем поставщик фундаментальных моделей отличается от поставщика модельных сервисов?" + }, + "selection": { + "answer": "Сравнивайте доступность моделей, цены токенов, ограничения контекста и вывода, лимиты запросов, обработку данных, региональную доступность, задержку, надёжность, поддержку SDK и наблюдаемость.", + "question": "Что сравнивать при выборе поставщика LLM API?" + }, + "title": "Вопросы о поставщиках LLM API" + }, + "guide": { + "browseModels": "Посмотреть модели ИИ для программирования", + "compareModels": "Сравнить модели для программирования", + "criteria": { + "modelAccess": { + "description": "Убедитесь, что поставщик предлагает нужные приложению модели для программирования, их версии, контекстные окна и функции вызова инструментов.", + "title": "Доступ к моделям" + }, + "pricingLimits": { + "description": "Сравните цены входных и выходных токенов, лимиты запросов, региональную доступность, квоты и скидки за кэширование.", + "title": "Цены и ограничения" + }, + "productionFit": { + "description": "До запуска в продакшене проверьте условия конфиденциальности, хранение данных, надёжность, задержку, качество SDK, наблюдаемость и варианты резервирования.", + "title": "Готовность к продакшену" + } + }, + "description": "Сначала определите нужные продукту модели, а затем сравните реальные условия их предоставления. Разработчики дают прямой доступ к своим моделям, а мультимодельные сервисы упрощают работу с несколькими семействами через единый API.", + "title": "Как выбрать поставщика LLM API для программирования" + }, + "meta": { + "description": "Сравните поставщиков LLM API для программирования по моделям, ценам, лимитам, конфиденциальности, надёжности и инструментам разработчика." + }, + "subtitle": "Сравнение разработчиков моделей и мультимодельных API-платформ", + "title": "Поставщики LLM API для ИИ-программирования" } diff --git a/translations/tr/pages/articles.json b/translations/tr/pages/articles.json index ec6ff963..66daaa34 100644 --- a/translations/tr/pages/articles.json +++ b/translations/tr/pages/articles.json @@ -1,5 +1,4 @@ { - "keywords": "Yapay zekâ kodlama makaleleri, yapay zekâ geliştirme eğitimleri, yapay zekâ kodlama en iyi uygulamaları, LLM kodlama rehberleri, yapay zekâ IDE rehberleri", "meta": { "description": "Yapay zekâ kodlama araçları, en iyi uygulamalar ve yapay zekâ asistanlarıyla yazılım geliştirmenin geleceği hakkında ayrıntılı makaleler.", "title": "Yapay Zekâ Kodlama Makaleleri ve Görüşler" diff --git a/translations/tr/pages/docs.json b/translations/tr/pages/docs.json index e9575e81..76b13a63 100644 --- a/translations/tr/pages/docs.json +++ b/translations/tr/pages/docs.json @@ -1,5 +1,4 @@ { - "keywords": "AI Coding Stack belgeleri, yapay zekâ kodlama araçları rehberi, IDE karşılaştırması, CLI karşılaştırması, yapay zekâ modeli özellikleri", "meta": { "description": "AI Coding Stack için kapsamlı belgeler. Platformu nasıl kullanacağınızı, projeye nasıl katkıda bulunacağınızı ve yapay zekâ kodlama iş akışınızdan en iyi şekilde nasıl yararlanacağınızı öğrenin.", "title": "Belgeler – AI Coding Stack" diff --git a/translations/tr/pages/model-intelligence-index.json b/translations/tr/pages/model-intelligence-index.json index 877b9153..ecaf849a 100644 --- a/translations/tr/pages/model-intelligence-index.json +++ b/translations/tr/pages/model-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "Kapsamlı Model Zekâsı Endeksi", - "description": "AI modellerinin kapsamlı zekâ puanlarını sağlayıcı ve model serisine göre zaman içinde izleyin.", - "keywords": "kapsamlı model zekâsı endeksi, AI model zaman çizelgesi, kapsamlı AI benchmarkları, model sürüm geçmişi" + "description": "AI modellerinin kapsamlı zekâ puanlarını sağlayıcı ve model serisine göre zaman içinde izleyin." }, "title": "Kapsamlı Model Zekâsı Endeksi", "description": "Modellerin kapsamlı zekâ puanlarının sürümler arasında nasıl değiştiğini keşfedin.", diff --git a/translations/tr/pages/model-price-intelligence-index.json b/translations/tr/pages/model-price-intelligence-index.json index b4221ec5..917b5ffc 100644 --- a/translations/tr/pages/model-price-intelligence-index.json +++ b/translations/tr/pages/model-price-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "Model Fiyatı × Zekâ", - "description": "Yaygın yapay zekâ modellerini API token fiyatı ve Zekâ Endeksi puanına göre karşılaştırın.", - "keywords": "yapay zekâ model fiyat karşılaştırması, model zekâsı, LLM API fiyatları, fiyat performans" + "description": "Yaygın yapay zekâ modellerini API token fiyatı ve Zekâ Endeksi puanına göre karşılaştırın." }, "title": "Model Fiyatı × Zekâ", "description": "Yaygın modelleri API fiyatı ve zekâ yeteneğine göre karşılaştırın.", diff --git a/translations/tr/pages/model-providers.json b/translations/tr/pages/model-providers.json index 66d11b58..a69ead97 100644 --- a/translations/tr/pages/model-providers.json +++ b/translations/tr/pages/model-providers.json @@ -1,4 +1,42 @@ { - "subtitle": "Çeşitli AI modellerine erişim sunan LLM API sağlayıcıları", - "title": "AI Kodlama Model Sağlayıcıları" + "faq": { + "firstParty": { + "answer": "Sağlayıcının en yeni özelliklerine ve doğrudan desteğe ihtiyacınız varsa birinci taraf API'yi seçin. Birleşik erişim, sağlayıcı yedeklemesi veya kolay model geçişi daha önemliyse çok modelli bir platform seçin.", + "question": "Birinci taraf API mi, çok modelli API platformu mu kullanmalıyım?" + }, + "providerTypes": { + "answer": "Temel model sağlayıcısı kendi modellerini geliştirir ve sunar. Model hizmeti sağlayıcısı ise farklı geliştiricilerin modellerine istek yönlendiren tek bir API veya platform sunar.", + "question": "Temel model sağlayıcısı ile model hizmeti sağlayıcısı arasındaki fark nedir?" + }, + "selection": { + "answer": "Model kullanılabilirliğini, token fiyatlarını, bağlam ve çıktı sınırlarını, hız limitlerini, veri işlemeyi, bölgesel erişimi, gecikmeyi, güvenilirliği, SDK desteğini ve üretim gözlemlenebilirliğini karşılaştırın.", + "question": "Bir LLM API sağlayıcısı seçerken neleri karşılaştırmalıyım?" + }, + "title": "LLM API sağlayıcıları hakkında sorular" + }, + "guide": { + "browseModels": "AI kodlama modellerine göz atın", + "compareModels": "Kodlama modellerini karşılaştırın", + "criteria": { + "modelAccess": { + "description": "Sağlayıcının uygulamanızın ihtiyaç duyduğu kodlama modellerini, sürümleri, bağlam pencerelerini ve araç çağırma özelliklerini sunduğunu doğrulayın.", + "title": "Model erişimi" + }, + "pricingLimits": { + "description": "Girdi ve çıktı token fiyatlarıyla birlikte hız limitlerini, bölgesel kullanılabilirliği, kotaları ve önbellek indirimlerini karşılaştırın.", + "title": "Fiyatlandırma ve sınırlar" + }, + "productionFit": { + "description": "Üretim kullanımı öncesinde gizlilik koşullarını, veri saklamayı, güvenilirliği, gecikmeyi, SDK kalitesini, gözlemlenebilirliği ve yedekleme seçeneklerini inceleyin.", + "title": "Üretime uygunluk" + } + }, + "description": "Önce ürününüzün ihtiyaç duyduğu modelleri belirleyin, ardından gerçek sunum koşullarını karşılaştırın. Birinci taraf sağlayıcılar kendi modellerine doğrudan erişim verir; çok modelli hizmetler tek API ile birden fazla model ailesine erişimi kolaylaştırır.", + "title": "Kodlama için LLM API sağlayıcısı nasıl seçilir?" + }, + "meta": { + "description": "AI kodlama için LLM API sağlayıcılarını model erişimi, fiyat, sınırlar, gizlilik, güvenilirlik ve geliştirici araçlarına göre karşılaştırın." + }, + "subtitle": "Model geliştiricilerini ve çok modelli API platformlarını karşılaştırın", + "title": "AI Kodlama için LLM API Sağlayıcıları" } diff --git a/translations/zh-Hans/pages/articles.json b/translations/zh-Hans/pages/articles.json index aae37807..63916826 100644 --- a/translations/zh-Hans/pages/articles.json +++ b/translations/zh-Hans/pages/articles.json @@ -1,5 +1,4 @@ { - "keywords": "AI 编程文章, AI 开发教程, AI 编程最佳实践, LLM 编程指南, AI IDE 指南", "meta": { "description": "深入介绍 AI 编程工具、最佳实践,以及 AI 助手驱动的软件开发未来。", "title": "AI 编程文章与洞察" diff --git a/translations/zh-Hans/pages/docs.json b/translations/zh-Hans/pages/docs.json index 91978601..962fdf77 100644 --- a/translations/zh-Hans/pages/docs.json +++ b/translations/zh-Hans/pages/docs.json @@ -1,5 +1,4 @@ { - "keywords": "AI Coding Stack 文档, AI 编程工具指南, IDE 对比, CLI 对比, AI 模型规格", "meta": { "description": "AI Coding Stack 完整文档。了解如何使用平台、参与项目,并充分发挥 AI 编程工作流的效能。", "title": "文档 – AI Coding Stack" diff --git a/translations/zh-Hans/pages/model-intelligence-index.json b/translations/zh-Hans/pages/model-intelligence-index.json index fd3d56d9..aa1ee4cc 100644 --- a/translations/zh-Hans/pages/model-intelligence-index.json +++ b/translations/zh-Hans/pages/model-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "模型综合智能指数", - "description": "按厂商和模型系列追踪 AI 模型综合智能指数随时间的变化。", - "keywords": "模型综合智能指数, AI 模型时间线, AI 综合基准, 模型发布历史" + "description": "按厂商和模型系列追踪 AI 模型综合智能指数随时间的变化。" }, "title": "模型综合智能指数", "description": "查看 AI 模型综合智能指数随发布时间的变化。", diff --git a/translations/zh-Hans/pages/model-price-intelligence-index.json b/translations/zh-Hans/pages/model-price-intelligence-index.json index 7afaa989..17ca7820 100644 --- a/translations/zh-Hans/pages/model-price-intelligence-index.json +++ b/translations/zh-Hans/pages/model-price-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "模型价格 × 智能指数", - "description": "按 API Token 价格和智能指数比较各家主流 AI 模型。", - "keywords": "AI 模型价格对比, 模型智能能力, LLM API 定价, 性价比" + "description": "按 API Token 价格和智能指数比较各家主流 AI 模型。" }, "title": "模型价格 × 智能指数", "description": "对比各家主流模型的 API 价格与智能指数。", diff --git a/translations/zh-Hans/pages/model-providers.json b/translations/zh-Hans/pages/model-providers.json index a62fd55d..50140a61 100644 --- a/translations/zh-Hans/pages/model-providers.json +++ b/translations/zh-Hans/pages/model-providers.json @@ -1,4 +1,42 @@ { - "subtitle": "提供各种 AI 模型访问的 LLM API 提供者", - "title": "AI 编码模型提供商" + "faq": { + "firstParty": { + "answer": "如果需要厂商的最新功能和直接支持,选择第一方 API;如果更看重统一接入、供应商故障转移或便捷切换模型,选择多模型 API 平台。", + "question": "应该使用第一方 API 还是多模型 API 平台?" + }, + "providerTypes": { + "answer": "基础模型提供商开发并托管自己的模型;模型服务提供商则通过一个 API 或平台,将请求路由到多个开发者的模型。", + "question": "基础模型提供商和模型服务提供商有什么区别?" + }, + "selection": { + "answer": "应比较模型可用性、输入和输出价格、上下文与输出上限、速率限制、数据处理方式、区域支持、延迟、可靠性、SDK 支持和生产可观测性。", + "question": "选择 LLM API 提供商时应该比较什么?" + }, + "title": "LLM API 提供商常见问题" + }, + "guide": { + "browseModels": "浏览 AI 编码模型", + "compareModels": "并排比较编码模型", + "criteria": { + "modelAccess": { + "description": "确认提供商具备应用所需的编码模型、版本、上下文窗口以及工具调用能力。", + "title": "模型接入" + }, + "pricingLimits": { + "description": "综合比较输入和输出 Token 价格、速率限制、区域可用性、配额以及缓存优惠。", + "title": "价格与限制" + }, + "productionFit": { + "description": "投入生产前,应检查隐私条款、数据保留、可靠性、延迟、SDK 质量、可观测性以及故障转移方案。", + "title": "生产适用性" + } + }, + "description": "先确定产品需要的模型,再比较真实的交付条件。第一方提供商可直接访问自有模型;多模型服务则通过一个 API 简化多个模型家族的接入。", + "title": "如何选择适合编码的 LLM API 提供商" + }, + "meta": { + "description": "按模型接入、价格、限制、隐私、可靠性和开发工具,比较适合 AI 编码的第一方及多模型 LLM API 提供商。" + }, + "subtitle": "比较第一方模型开发商与多模型 API 平台", + "title": "适合 AI 编码的 LLM API 提供商" } diff --git a/translations/zh-Hant/pages/articles.json b/translations/zh-Hant/pages/articles.json index 7e67a3f5..bfff9d04 100644 --- a/translations/zh-Hant/pages/articles.json +++ b/translations/zh-Hant/pages/articles.json @@ -1,5 +1,4 @@ { - "keywords": "AI 程式設計文章, AI 開發教學, AI 程式設計最佳實務, LLM 程式設計指南, AI IDE 指南", "meta": { "description": "深入介紹 AI 程式設計工具、最佳實務,以及 AI 助手驅動的軟體開發未來。", "title": "AI 程式設計文章與洞察" diff --git a/translations/zh-Hant/pages/docs.json b/translations/zh-Hant/pages/docs.json index 30e82edc..65257aea 100644 --- a/translations/zh-Hant/pages/docs.json +++ b/translations/zh-Hant/pages/docs.json @@ -1,5 +1,4 @@ { - "keywords": "AI Coding Stack 文件, AI 程式設計工具指南, IDE 比較, CLI 比較, AI 模型規格", "meta": { "description": "AI Coding Stack 完整文件。瞭解如何使用平台、參與專案,並充分發揮 AI 程式設計工作流程的效能。", "title": "文件 – AI Coding Stack" diff --git a/translations/zh-Hant/pages/model-intelligence-index.json b/translations/zh-Hant/pages/model-intelligence-index.json index c4fe1ac6..69faeaf4 100644 --- a/translations/zh-Hant/pages/model-intelligence-index.json +++ b/translations/zh-Hant/pages/model-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "模型綜合智慧指數", - "description": "依廠商和模型系列追蹤 AI 模型綜合智慧指數隨時間的變化。", - "keywords": "模型綜合智慧指數, AI 模型時間線, AI 綜合基準, 模型發布歷史" + "description": "依廠商和模型系列追蹤 AI 模型綜合智慧指數隨時間的變化。" }, "title": "模型綜合智慧指數", "description": "查看 AI 模型綜合智慧指數隨發布時間的變化。", diff --git a/translations/zh-Hant/pages/model-price-intelligence-index.json b/translations/zh-Hant/pages/model-price-intelligence-index.json index aa15c707..7bc24300 100644 --- a/translations/zh-Hant/pages/model-price-intelligence-index.json +++ b/translations/zh-Hant/pages/model-price-intelligence-index.json @@ -1,8 +1,7 @@ { "meta": { "title": "模型價格 × 智慧指數", - "description": "按 API Token 價格和智慧指數比較各家主流 AI 模型。", - "keywords": "AI 模型價格比較, 模型智能能力, LLM API 定價, 性價比" + "description": "按 API Token 價格和智慧指數比較各家主流 AI 模型。" }, "title": "模型價格 × 智慧指數", "description": "比較各家主流模型的 API 價格與智慧指數。", diff --git a/translations/zh-Hant/pages/model-providers.json b/translations/zh-Hant/pages/model-providers.json index 162324a1..2dd02143 100644 --- a/translations/zh-Hant/pages/model-providers.json +++ b/translations/zh-Hant/pages/model-providers.json @@ -1,4 +1,42 @@ { - "subtitle": "提供各種 AI 模型存取的 LLM API 提供者", - "title": "AI 編碼模型提供者" + "faq": { + "firstParty": { + "answer": "若需要廠商的最新功能和直接支援,請選擇第一方 API;若更重視統一存取、供應商容錯移轉或方便切換模型,請選擇多模型 API 平台。", + "question": "應該使用第一方 API 還是多模型 API 平台?" + }, + "providerTypes": { + "answer": "基礎模型供應商開發並託管自己的模型;模型服務供應商則透過單一 API 或平台,將請求路由至多個開發者的模型。", + "question": "基礎模型供應商和模型服務供應商有什麼差別?" + }, + "selection": { + "answer": "應比較模型可用性、輸入與輸出價格、上下文與輸出上限、速率限制、資料處理方式、區域支援、延遲、可靠性、SDK 支援和正式環境可觀測性。", + "question": "選擇 LLM API 供應商時應該比較什麼?" + }, + "title": "LLM API 供應商常見問題" + }, + "guide": { + "browseModels": "瀏覽 AI 程式設計模型", + "compareModels": "並排比較程式設計模型", + "criteria": { + "modelAccess": { + "description": "確認供應商提供應用程式所需的程式設計模型、版本、上下文視窗及工具呼叫能力。", + "title": "模型存取" + }, + "pricingLimits": { + "description": "綜合比較輸入與輸出 Token 價格、速率限制、區域可用性、配額及快取折扣。", + "title": "價格與限制" + }, + "productionFit": { + "description": "投入正式環境前,請檢查隱私條款、資料保留、可靠性、延遲、SDK 品質、可觀測性及容錯移轉方案。", + "title": "正式環境適用性" + } + }, + "description": "先確定產品需要的模型,再比較實際的交付條件。第一方供應商可直接存取自有模型;多模型服務則透過單一 API 簡化多個模型家族的接入。", + "title": "如何選擇適合程式設計的 LLM API 供應商" + }, + "meta": { + "description": "依模型存取、價格、限制、隱私、可靠性和開發工具,比較適合 AI 程式設計的第一方及多模型 LLM API 供應商。" + }, + "subtitle": "比較第一方模型開發商與多模型 API 平台", + "title": "適合 AI 程式設計的 LLM API 供應商" }