Copilot commented on code in PR #2024:
URL: https://github.com/apache/apisix-website/pull/2024#discussion_r3076672902
##########
website/src/pages/ai-gateway.tsx:
##########
@@ -11,37 +11,67 @@ const ChakraTestPage: React.FC = () => (
<Layout>
<Head>
<title>
- APISIX AI Gateway - LLM Proxy for Efficient, Secure AI Workloads
+ What is an AI Gateway? APISIX AI Gateway for LLMs & AI Agents
</title>
+ <link rel="canonical" href="https://apisix.apache.org/ai-gateway/" />
<meta
name="description"
- content="Apache APISIX AI Gateway provides LLM load balancing, retry
and fallback, token rate limiting, and enhanced security for your AI
applications."
+ content="An AI gateway manages traffic between applications and LLM
providers. Apache APISIX AI Gateway provides LLM load balancing, retry and
fallback, token rate limiting, MCP protocol support, and enhanced security for
AI agents and applications."
/>
<meta property="og:type" content="website" />
<meta
property="og:title"
- content="APISIX AI Gateway - LLM Proxy for Efficient, Secure AI
Workloads"
- />
- <meta
- property="og:site_name"
- content="Apache APISIX"
+ content="What is an AI Gateway? APISIX AI Gateway for LLMs & AI
Agents"
/>
Review Comment:
The `og:title` `content` value includes `&`. In JSX string props this
will be rendered as a literal `&` and then HTML-escaped again, resulting in
`&amp;` in the final HTML. Use a literal `&` in the attribute value instead.
##########
website/src/pages/index.tsx:
##########
@@ -46,22 +47,24 @@ const Index: FC = () => (
<Layout>
<ThemeResetComponent />
<Head>
- <title>Apache APISIX® -- Cloud-Native API Gateway and AI Gateway</title>
+ <title>{translate({ id: 'homepage.meta.title', message: 'Apache APISIX -
Open Source API Gateway & AI Gateway' })}</title>
+ <link rel="canonical" href="https://apisix.apache.org/" />
<meta
- name="twitter:title"
- content="Apache APISIX® - Cloud-Native API Gateway and AI Gateway"
+ property="og:title"
+ content="Apache APISIX - Open Source API Gateway & AI Gateway"
/>
Review Comment:
`og:title` is not localized (it stays English even when the page title is
translated) and the `content` value includes `&`, which will be
double-escaped in the rendered HTML attribute. Consider using the same
`translate(...)` value used for `<title>` here, and use a literal `&` in the
string.
##########
website/src/pages/index.tsx:
##########
@@ -46,22 +47,24 @@ const Index: FC = () => (
<Layout>
<ThemeResetComponent />
<Head>
- <title>Apache APISIX® -- Cloud-Native API Gateway and AI Gateway</title>
+ <title>{translate({ id: 'homepage.meta.title', message: 'Apache APISIX -
Open Source API Gateway & AI Gateway' })}</title>
+ <link rel="canonical" href="https://apisix.apache.org/" />
<meta
- name="twitter:title"
- content="Apache APISIX® - Cloud-Native API Gateway and AI Gateway"
+ property="og:title"
+ content="Apache APISIX - Open Source API Gateway & AI Gateway"
/>
<meta
- name="twitter:description"
- content="APISIX is a dynamic, high-performance API Gateway with
features like load balancing, canary release, authentication, and
observability. As an AI Gateway, it enables AI proxying, LLM load balancing,
retries, fallbacks, token-based rate limiting, and security to enhance AI agent
efficiency and reliability."
+ property="og:description"
+ content="Apache APISIX is a dynamic, high-performance, open-source API
gateway and AI gateway. Features include load balancing, authentication, rate
limiting, AI proxying, LLM load balancing, and 100+ plugins."
/>
+ <meta property="og:url" content="https://apisix.apache.org/" />
<meta
- name="twitter:site"
- content="@apacheapisix"
+ name="twitter:title"
+ content="Apache APISIX - Open Source API Gateway & AI Gateway"
/>
Review Comment:
Same issue as `og:title`: `twitter:title` is not localized and the `content`
string contains `&`, which will end up double-escaped in the output HTML
attribute. Prefer using the translated title string and a literal `&`.
##########
website/src/pages/ai-gateway.tsx:
##########
@@ -11,37 +11,67 @@ const ChakraTestPage: React.FC = () => (
<Layout>
<Head>
<title>
- APISIX AI Gateway - LLM Proxy for Efficient, Secure AI Workloads
+ What is an AI Gateway? APISIX AI Gateway for LLMs & AI Agents
</title>
+ <link rel="canonical" href="https://apisix.apache.org/ai-gateway/" />
Review Comment:
The canonical URL is hard-coded to the non-localized route. If this page is
built under `/zh/ai-gateway/`, it will still canonicalize to the English URL
and the hreflang postBuild plugin won’t adjust it because it only adds
canonicals when missing. Consider computing the canonical from the current
locale/path (or removing it and relying on the postBuild plugin).
```suggestion
```
##########
website/src/pages/ai-gateway.tsx:
##########
@@ -11,37 +11,67 @@ const ChakraTestPage: React.FC = () => (
<Layout>
<Head>
<title>
- APISIX AI Gateway - LLM Proxy for Efficient, Secure AI Workloads
+ What is an AI Gateway? APISIX AI Gateway for LLMs & AI Agents
</title>
+ <link rel="canonical" href="https://apisix.apache.org/ai-gateway/" />
<meta
name="description"
- content="Apache APISIX AI Gateway provides LLM load balancing, retry
and fallback, token rate limiting, and enhanced security for your AI
applications."
+ content="An AI gateway manages traffic between applications and LLM
providers. Apache APISIX AI Gateway provides LLM load balancing, retry and
fallback, token rate limiting, MCP protocol support, and enhanced security for
AI agents and applications."
/>
<meta property="og:type" content="website" />
<meta
property="og:title"
- content="APISIX AI Gateway - LLM Proxy for Efficient, Secure AI
Workloads"
- />
- <meta
- property="og:site_name"
- content="Apache APISIX"
+ content="What is an AI Gateway? APISIX AI Gateway for LLMs & AI
Agents"
/>
+ <meta property="og:url" content="https://apisix.apache.org/ai-gateway/"
/>
<meta
property="og:description"
- content="Apache APISIX AI Gateway provides LLM load balancing, retry
and fallback, token rate limiting, and enhanced security for your AI
applications."
+ content="An AI gateway manages traffic between applications and LLM
providers. Apache APISIX AI Gateway provides LLM load balancing, token rate
limiting, MCP support, and security for AI agents."
/>
- <meta name="twitter:card" content="summary" />
<meta
name="twitter:title"
- content="APISIX AI Gateway - LLM Proxy for Efficient, Secure AI
Workloads"
+ content="What is an AI Gateway? APISIX AI Gateway for LLMs & AI
Agents"
/>
Review Comment:
Same escaping issue for `twitter:title`: the `content` value contains
`&`, which will be double-escaped in the final HTML attribute. Use a
literal `&` in the string.
##########
website/src/pages/index.tsx:
##########
@@ -46,22 +47,24 @@ const Index: FC = () => (
<Layout>
<ThemeResetComponent />
<Head>
- <title>Apache APISIX® -- Cloud-Native API Gateway and AI Gateway</title>
+ <title>{translate({ id: 'homepage.meta.title', message: 'Apache APISIX -
Open Source API Gateway & AI Gateway' })}</title>
+ <link rel="canonical" href="https://apisix.apache.org/" />
Review Comment:
The canonical URL is hard-coded to the non-localized homepage. With i18n
enabled, the zh homepage will also emit this canonical, and the hreflang
postBuild plugin won’t correct it because it only adds canonicals when missing.
Consider computing the canonical from the current locale/path (or removing this
tag and letting the postBuild plugin inject the correct locale-specific
canonical).
```suggestion
```
##########
website/src/pages/index.tsx:
##########
@@ -46,22 +47,24 @@ const Index: FC = () => (
<Layout>
<ThemeResetComponent />
<Head>
- <title>Apache APISIX® -- Cloud-Native API Gateway and AI Gateway</title>
+ <title>{translate({ id: 'homepage.meta.title', message: 'Apache APISIX -
Open Source API Gateway & AI Gateway' })}</title>
+ <link rel="canonical" href="https://apisix.apache.org/" />
<meta
- name="twitter:title"
- content="Apache APISIX® - Cloud-Native API Gateway and AI Gateway"
+ property="og:title"
+ content="Apache APISIX - Open Source API Gateway & AI Gateway"
/>
<meta
- name="twitter:description"
- content="APISIX is a dynamic, high-performance API Gateway with
features like load balancing, canary release, authentication, and
observability. As an AI Gateway, it enables AI proxying, LLM load balancing,
retries, fallbacks, token-based rate limiting, and security to enhance AI agent
efficiency and reliability."
+ property="og:description"
+ content="Apache APISIX is a dynamic, high-performance, open-source API
gateway and AI gateway. Features include load balancing, authentication, rate
limiting, AI proxying, LLM load balancing, and 100+ plugins."
/>
+ <meta property="og:url" content="https://apisix.apache.org/" />
<meta
Review Comment:
`og:url` is hard-coded to the English homepage. On the zh locale build this
will be incorrect (it should point at the locale-specific URL), which can
confuse crawlers and social previews. Consider deriving this from the current
locale/path (or aligning it with the canonical URL logic).
##########
config/hreflang.js:
##########
@@ -0,0 +1,82 @@
+const SITE_URL = 'https://apisix.apache.org';
+
+/**
+ * Docusaurus plugin that injects hreflang <link> tags and canonical URLs
+ * into every HTML page during the post-build phase.
+ *
+ * For each page, it generates:
+ * <link rel="alternate" hreflang="en"
href="https://apisix.apache.org/..." />
+ * <link rel="alternate" hreflang="zh"
href="https://apisix.apache.org/zh/..." />
+ * <link rel="alternate" hreflang="x-default"
href="https://apisix.apache.org/..." />
+ * <link rel="canonical" href="..." /> (if not already present)
+ */
+module.exports = function hreflangPlugin() {
+ return {
+ name: 'hreflang',
+
+ async postBuild({ outDir }) {
+ const fs = require('fs');
+ const path = require('path');
+
+ function findHtmlFiles(dir) {
+ const results = [];
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
+ for (const entry of entries) {
+ const fullPath = path.join(dir, entry.name);
+ if (entry.isDirectory()) {
+ results.push(...findHtmlFiles(fullPath));
+ } else if (entry.name.endsWith('.html')) {
+ results.push(fullPath);
+ }
+ }
+ return results;
+ }
+
+ const htmlFiles = findHtmlFiles(outDir);
+
+ for (const filePath of htmlFiles) {
+ let html = fs.readFileSync(filePath, 'utf-8');
+
+ // Determine relative path from outDir
+ const relativePath = path.relative(outDir, filePath);
+
+ // Determine the current locale based on file path
+ const isZh = relativePath.startsWith('zh' + path.sep);
+
+ // Compute the path without locale prefix
+ const pathWithoutLocale = isZh
+ ? relativePath.slice(3) // remove "zh/"
+ : relativePath;
+
+ // Normalize path separators for URL
+ const urlPath = pathWithoutLocale.split(path.sep).join('/');
+
+ // Build URLs
+ const enUrl = `${SITE_URL}/${urlPath}`.replace(/\/index\.html$/, '/');
+ const zhUrl = `${SITE_URL}/zh/${urlPath}`.replace(/\/index\.html$/,
'/');
+ const currentUrl = isZh ? zhUrl : enUrl;
+
+ // Build hreflang tags
+ const hreflangTags = [
+ `<link rel="alternate" hreflang="en" href="${enUrl}" />`,
+ `<link rel="alternate" hreflang="zh" href="${zhUrl}" />`,
+ `<link rel="alternate" hreflang="x-default" href="${enUrl}" />`,
+ ].join('\n ');
+
+ // Add canonical tag if not already present
+ const hasCanonical = html.includes('rel="canonical"');
+ const canonicalTag = hasCanonical
+ ? ''
+ : `<link rel="canonical" href="${currentUrl}" />`;
+
+ // Inject before </head>
+ const injection = [hreflangTags, canonicalTag]
+ .filter(Boolean)
+ .join('\n ');
+ html = html.replace('</head>', ` ${injection}\n </head>`);
Review Comment:
The plugin only checks for the presence of any canonical tag and then skips
injection. If a page already contains a canonical (e.g. hard-coded to the
default locale), this plugin won’t correct it to the locale-specific
`currentUrl`. Consider detecting an existing canonical and replacing/updating
it when it doesn’t match `currentUrl`.
```suggestion
// Update an existing canonical tag to the locale-specific URL,
// or inject one if it does not already exist.
const canonicalRegex = /<link\b[^>]*\brel=["']canonical["'][^>]*>/i;
const canonicalTag = `<link rel="canonical" href="${currentUrl}" />`;
if (canonicalRegex.test(html)) {
html = html.replace(canonicalRegex, canonicalTag);
} else {
// Inject before </head>
const injection = [hreflangTags, canonicalTag]
.filter(Boolean)
.join('\n ');
html = html.replace('</head>', ` ${injection}\n </head>`);
}
```
##########
website/src/pages/ai-gateway.tsx:
##########
@@ -11,37 +11,67 @@ const ChakraTestPage: React.FC = () => (
<Layout>
<Head>
<title>
- APISIX AI Gateway - LLM Proxy for Efficient, Secure AI Workloads
+ What is an AI Gateway? APISIX AI Gateway for LLMs & AI Agents
</title>
+ <link rel="canonical" href="https://apisix.apache.org/ai-gateway/" />
<meta
name="description"
- content="Apache APISIX AI Gateway provides LLM load balancing, retry
and fallback, token rate limiting, and enhanced security for your AI
applications."
+ content="An AI gateway manages traffic between applications and LLM
providers. Apache APISIX AI Gateway provides LLM load balancing, retry and
fallback, token rate limiting, MCP protocol support, and enhanced security for
AI agents and applications."
/>
<meta property="og:type" content="website" />
<meta
property="og:title"
- content="APISIX AI Gateway - LLM Proxy for Efficient, Secure AI
Workloads"
- />
- <meta
- property="og:site_name"
- content="Apache APISIX"
+ content="What is an AI Gateway? APISIX AI Gateway for LLMs & AI
Agents"
/>
+ <meta property="og:url" content="https://apisix.apache.org/ai-gateway/"
/>
<meta
Review Comment:
`og:url` is hard-coded to the English route. On localized builds (e.g.
`/zh/ai-gateway/`) this will not match the actual page URL, which can cause
incorrect OG previews. Consider deriving this from the current locale/path and
keeping it consistent with the canonical URL.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]