{"_id":"@applyseo/translate","_rev":"3-c570016c511b6b1636e535396e978ea0","name":"@applyseo/translate","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@applyseo/translate","version":"0.1.0","keywords":["translation","localization","i18n","seo","hreflang","multilingual","middleware","nextjs","cloudflare-workers","edge"],"license":"MIT","_id":"@applyseo/translate@0.1.0","maintainers":[{"name":"ahmedalshamsi","email":"ahmed@applyseo.com"}],"homepage":"https://github.com/ibilalchaudhary/applyseo/tree/main/applyseo-translate#readme","bugs":{"url":"https://github.com/ibilalchaudhary/applyseo/issues"},"bin":{"applyseo-translate":"dist/cli.js"},"dist":{"shasum":"19a5a3c5241c41f3abcb3e2a1273fb724169f80f","tarball":"https://registry.npmjs.org/@applyseo/translate/-/translate-0.1.0.tgz","fileCount":27,"integrity":"sha512-FK7HComDDFnyXaNwdeMkJcKO7YEZh+mIEgLldPh4JgA6JtTrniADxmd9cdKaMv/gNTJsSoFnyfah9bV3spKq/Q==","signatures":[{"sig":"MEYCIQCXuqU3OwUuMUu5TId6Dml+D6fSWyOFpZKD7b3blPbSQAIhAIG175dFDE+/Yl7BQ8IASfO9EQ3GfWYWFqwYoGhx6SDT","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":87866},"type":"module","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./next":{"types":"./dist/adapters/next.d.ts","default":"./dist/adapters/next.js"},"./node":{"types":"./dist/adapters/node.d.ts","default":"./dist/adapters/node.js"},"./worker":{"types":"./dist/adapters/worker.d.ts","default":"./dist/adapters/worker.js"},"./browser":{"types":"./dist/browser.d.ts","default":"./dist/browser.js"}},"gitHead":"2d742a14f3607578235b9bb5176db4c4ac1fa019","scripts":{"test":"tsc -p tsconfig.build.json && vitest run","build":"tsc -p tsconfig.build.json","types:check":"tsc --noEmit","format:check":"prettier --check .","prepublishOnly":"tsc -p tsconfig.build.json && vitest run"},"_npmUser":{"name":"ahmedalshamsi","email":"ahmed@applyseo.com"},"repository":{"url":"git+https://github.com/ibilalchaudhary/applyseo.git","type":"git","directory":"applyseo-translate"},"_npmVersion":"11.19.0","description":"Serve ApplySEO-approved website translations from your own edge. Applies a signed, immutable translation dictionary to live origin HTML, so translated pages are server-rendered and crawlable without delegating DNS.","directories":{},"sideEffects":false,"_nodeVersion":"24.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.30.1","devDependencies":{"vitest":"3.2.6","prettier":"3.6.2","typescript":"5.9.3","@types/node":"22.19.11"},"_npmOperationalInternal":{"tmp":"tmp/translate_0.1.0_1788188907915_0.13714337119736753","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@applyseo/translate","version":"0.1.1","keywords":["translation","localization","i18n","seo","hreflang","multilingual","middleware","nextjs","cloudflare-workers","edge"],"license":"MIT","_id":"@applyseo/translate@0.1.1","maintainers":[{"name":"ahmedalshamsi","email":"ahmed@applyseo.com"}],"homepage":"https://github.com/ibilalchaudhary/applyseo/tree/main/applyseo-translate#readme","bugs":{"url":"https://github.com/ibilalchaudhary/applyseo/issues"},"bin":{"applyseo-translate":"dist/cli.js"},"dist":{"shasum":"651ac7ffccece11c47513e82d4b292f4fca9dc97","tarball":"https://registry.npmjs.org/@applyseo/translate/-/translate-0.1.1.tgz","fileCount":27,"integrity":"sha512-PLtH3WAv0ei3TP9SEJHfMn/IemR5HSGhZYhqovs1RFBO+TczLLrEiLgZ9Q2uVkUgDxWwT3f2g+8cR064SDUV0A==","signatures":[{"sig":"MEUCIGdKgbVrm2YKGrflpfBxkp604T4WqChSn9MflSvxGCvuAiEAxZE1xI/RYyh25o4oN7PyzSl6OCrTbnSl8CLN7zztPkY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":91010},"type":"module","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./next":{"types":"./dist/adapters/next.d.ts","default":"./dist/adapters/next.js"},"./node":{"types":"./dist/adapters/node.d.ts","default":"./dist/adapters/node.js"},"./worker":{"types":"./dist/adapters/worker.d.ts","default":"./dist/adapters/worker.js"},"./browser":{"types":"./dist/browser.d.ts","default":"./dist/browser.js"}},"gitHead":"d019f7203f1107cdc829561ed7be47296e1fea63","scripts":{"test":"tsc -p tsconfig.build.json && vitest run","build":"tsc -p tsconfig.build.json","types:check":"tsc --noEmit","format:check":"prettier --check .","prepublishOnly":"tsc -p tsconfig.build.json && vitest run"},"_npmUser":{"name":"ahmedalshamsi","email":"ahmed@applyseo.com"},"repository":{"url":"git+https://github.com/ibilalchaudhary/applyseo.git","type":"git","directory":"applyseo-translate"},"_npmVersion":"10.9.7","description":"Add approved, crawlable website translations to Next.js, Node, Workers, static sites, and browser apps without moving DNS or changing hosting.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.30.1","devDependencies":{"vitest":"3.2.6","prettier":"3.6.2","typescript":"5.9.3","@types/node":"22.19.11"},"_npmOperationalInternal":{"tmp":"tmp/translate_0.1.1_1788191855543_0.4370584364418286","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@applyseo/translate","version":"0.1.2","description":"Add approved, crawlable website translations to Next.js, Node, Workers, static sites, and browser apps without moving DNS or changing hosting.","type":"module","license":"MIT","keywords":["translation","localization","i18n","seo","hreflang","multilingual","middleware","nextjs","cloudflare-workers","edge"],"homepage":"https://github.com/Apply-SEO/translate#readme","repository":{"type":"git","url":"git+https://github.com/Apply-SEO/translate.git"},"bugs":{"url":"https://github.com/Apply-SEO/translate/issues"},"publishConfig":{"access":"public"},"sideEffects":false,"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./next":{"types":"./dist/adapters/next.d.ts","default":"./dist/adapters/next.js"},"./node":{"types":"./dist/adapters/node.d.ts","default":"./dist/adapters/node.js"},"./worker":{"types":"./dist/adapters/worker.d.ts","default":"./dist/adapters/worker.js"},"./browser":{"types":"./dist/browser.d.ts","default":"./dist/browser.js"}},"scripts":{"build":"tsc -p tsconfig.build.json","format:check":"prettier --check .","prepublishOnly":"tsc -p tsconfig.build.json && vitest run","test":"tsc -p tsconfig.build.json && vitest run","types:check":"tsc --noEmit"},"engines":{"node":">=20"},"packageManager":"pnpm@10.30.1","devDependencies":{"@types/node":"22.19.11","prettier":"3.6.2","typescript":"5.9.3","vitest":"3.2.6"},"bin":{"applyseo-translate":"dist/cli.js"},"_id":"@applyseo/translate@0.1.2","gitHead":"d7e74d984325a1937991d3a1fdd45e242cabed0e","_nodeVersion":"22.22.2","_npmVersion":"10.9.7","dist":{"integrity":"sha512-fH0kYCNszNAqLnGhSr21tjfbO2qkEPcAHeV0omSYgF0zXBXEJRW7W4FY2941oXncxpn21cis7KHUWjWGSXfhFg==","shasum":"a09a893d75c9d33eba0777ea7a2b41f740284e61","tarball":"https://registry.npmjs.org/@applyseo/translate/-/translate-0.1.2.tgz","fileCount":27,"unpackedSize":90927,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDW92th55SHcLWLJky0sTez9n8NwMhXNNinzOt0CHg5qQIhANluVloMe0Z9jyIdxbfHaZaAIKt5UlhjOf9C7nmXfgky"}]},"_npmUser":{"name":"ahmedalshamsi","email":"ahmed@applyseo.com"},"directories":{},"maintainers":[{"name":"ahmedalshamsi","email":"ahmed@applyseo.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/translate_0.1.2_1788193092681_0.0726230626119988"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-31T15:08:27.596Z","modified":"2026-08-31T16:18:13.075Z","0.1.0":"2026-08-31T15:08:28.059Z","0.1.1":"2026-08-31T15:57:35.680Z","0.1.2":"2026-08-31T16:18:12.853Z"},"bugs":{"url":"https://github.com/Apply-SEO/translate/issues"},"license":"MIT","homepage":"https://github.com/Apply-SEO/translate#readme","keywords":["translation","localization","i18n","seo","hreflang","multilingual","middleware","nextjs","cloudflare-workers","edge"],"repository":{"type":"git","url":"git+https://github.com/Apply-SEO/translate.git"},"description":"Add approved, crawlable website translations to Next.js, Node, Workers, static sites, and browser apps without moving DNS or changing hosting.","maintainers":[{"name":"ahmedalshamsi","email":"ahmed@applyseo.com"}],"readme":"<!-- @applyseo-file ARCH-001 | module=localization | layer=documentation -->\n<!-- @evidence repository:applyseo-translate/src/handler.ts; repository:applyseo-translate/src/cli.ts; repository:docs/architecture/decisions/ADR-0004-TRANSLATE-EDGE-DELIVERY-PACKAGE.md; verified 2026-08-31 -->\n\n# @applyseo/translate\n\nAdd approved website translations without moving your DNS or changing your\nhosting provider.\n\nThis package reads a translation release from ApplySEO and applies it to the\nHTML your website already creates. The translated words can be present in the\nHTML before a visitor receives the page. Search engines and AI crawlers can\nthen read the translated page.\n\n## What happens when you use this package\n\n1. Add your website in ApplySEO.\n2. Choose a language.\n3. Review and approve the translated pages.\n4. Publish a translation release in ApplySEO.\n5. Install this package on your website.\n6. Open a language URL such as `https://example.com/ar/`.\n\nThe package does not ask an AI model to translate a visitor request. It only\nreads text that your team already approved in ApplySEO.\n\n## Before you start\n\nYou need:\n\n- Node.js 20 or newer.\n- A published ApplyTranslate release.\n- Your ApplySEO endpoint.\n- Your site delivery key.\n- Your site connector secret for server and build integrations.\n\nFind these values in **ApplySEO > Translate > Connected site**.\n\n```sh\nAPPLYSEO_ENDPOINT=https://app.applyseo.com\nAPPLYSEO_DELIVERY_KEY=at_...\nAPPLYSEO_CONNECTOR_SECRET=...\n```\n\nKeep `APPLYSEO_CONNECTOR_SECRET` private. Put it in your server environment or\nbuild settings. Never put it in browser code or commit it to Git.\n\n## Choose the setup that matches your website\n\n| Your website                                            | Setup                           | Translation is visible to crawlers |\n| ------------------------------------------------------- | ------------------------------- | ---------------------------------- |\n| Next.js                                                 | Next.js middleware              | Yes                                |\n| Express, Fastify, Koa, or Node HTTP                     | Node middleware                 | Yes                                |\n| Nuxt, Astro, SvelteKit, or Remix with server rendering  | Core handler                    | Yes                                |\n| Cloudflare Workers, Deno, Bun, or another Fetch runtime | Worker adapter                  | Yes                                |\n| Plain HTML or a static build                            | Command line build              | Yes                                |\n| React, Vue, or Svelte app with no server HTML           | Browser runtime                 | No                                 |\n| WordPress                                               | ApplyTranslate Connector plugin | Yes                                |\n\nIf crawler visibility matters, choose a setup marked **Yes**.\n\n## Install the package\n\nRun this command in your website project:\n\n```sh\nnpm install @applyseo/translate\n```\n\nThen follow one setup below.\n\n## Next.js setup\n\n### Step 1: add the private values\n\nPut the three ApplySEO values in `.env.local`.\n\n### Step 2: create `middleware.ts`\n\nCreate this file next to your `package.json`:\n\n```ts\nimport { createTranslateMiddleware } from \"@applyseo/translate/next\";\n\nexport const middleware = createTranslateMiddleware({\n  endpoint: process.env.APPLYSEO_ENDPOINT!,\n  deliveryKey: process.env.APPLYSEO_DELIVERY_KEY!,\n  secret: process.env.APPLYSEO_CONNECTOR_SECRET!,\n  locales: [\"ar\", \"fr\"],\n});\n\nexport const config = {\n  matcher: [\"/ar/:path*\", \"/fr/:path*\"],\n};\n```\n\nReplace `ar` and `fr` with the languages you published.\n\n### Step 3: check the result\n\nStart your site and open `/ar/`. View the page source and confirm that the\ntranslated words are already in the HTML.\n\n## Express or Node setup\n\n### Step 1: add the private values\n\nPut the three ApplySEO values in your server environment.\n\n### Step 2: add the middleware before your routes\n\n```ts\nimport { createTranslateNodeMiddleware } from \"@applyseo/translate/node\";\n\napp.use(\n  createTranslateNodeMiddleware({\n    endpoint: process.env.APPLYSEO_ENDPOINT!,\n    deliveryKey: process.env.APPLYSEO_DELIVERY_KEY!,\n    secret: process.env.APPLYSEO_CONNECTOR_SECRET!,\n    locales: [\"ar\"],\n  }),\n);\n```\n\n### Step 3: check the result\n\nOpen `/ar/` and inspect the response headers. You should see\n`X-ApplyTranslate-Release` and `X-ApplyTranslate-Locale`.\n\n## Cloudflare Worker setup\n\n### Step 1: add the private values\n\nStore the three ApplySEO values as Worker environment bindings or secrets.\n\n### Step 2: add the Worker adapter\n\n```ts\nimport { createTranslateWorker } from \"@applyseo/translate/worker\";\n\ntype Env = {\n  APPLYSEO_ENDPOINT: string;\n  APPLYSEO_DELIVERY_KEY: string;\n  APPLYSEO_CONNECTOR_SECRET: string;\n};\n\nlet translate: ReturnType<typeof createTranslateWorker> | undefined;\n\nexport default {\n  fetch(request: Request, env: Env) {\n    translate ??= createTranslateWorker({\n      endpoint: env.APPLYSEO_ENDPOINT,\n      deliveryKey: env.APPLYSEO_DELIVERY_KEY,\n      secret: env.APPLYSEO_CONNECTOR_SECRET,\n      locales: [\"ar\"],\n    });\n    return translate(request);\n  },\n};\n```\n\nThe adapter handles translated language paths. Other paths continue to your\nnormal origin.\n\n## Nuxt, Astro, SvelteKit, Remix, Deno, or Bun setup\n\nUse the core handler when your framework works with `Request` and `Response`.\n\n```ts\nimport { createTranslateHandler } from \"@applyseo/translate\";\n\nconst translate = createTranslateHandler({\n  endpoint: process.env.APPLYSEO_ENDPOINT!,\n  deliveryKey: process.env.APPLYSEO_DELIVERY_KEY!,\n  secret: process.env.APPLYSEO_CONNECTOR_SECRET!,\n  locales: [\"ar\"],\n});\n\nexport async function handleRequest(request: Request) {\n  const translatedResponse = await translate(request);\n  if (translatedResponse) return translatedResponse;\n  return renderNormally(request);\n}\n```\n\nConnect `handleRequest` to the request hook used by your framework.\n\n## Static HTML setup\n\nUse this path for plain HTML, Hugo, Jekyll, Eleventy, Astro static output,\nVite static output, `next export`, or `nuxt generate`.\n\n### Step 1: build your website normally\n\nFor example:\n\n```sh\nnpm run build\n```\n\n### Step 2: add the private values\n\nPut the three ApplySEO values in your local environment or CI settings.\n\n### Step 3: translate the completed build folder\n\n```sh\nnpx applyseo-translate build \\\n  --dir ./dist \\\n  --site https://example.com \\\n  --locales ar,fr\n```\n\nThe command:\n\n1. Finds every HTML file in `./dist`.\n2. Downloads the approved release for each language.\n3. Writes translated copies to `./dist/ar`, `./dist/fr`, and other requested\n   language folders.\n4. Reports the release number and translation coverage.\n5. Tells you which URL to check after deployment.\n\nThe original HTML files stay unchanged. Deploy the complete `dist` folder as\nyou normally would.\n\n## Browser setup for a client-rendered app\n\nUse this only when visitors need translated app text and crawler visibility is\nnot required. Examples include dashboards, account areas, and private tools.\n\nThe browser runtime uses the public delivery key. It does not use the connector\nsecret.\n\n```html\n<script type=\"module\">\n  import { startBrowserTranslation } from \"https://esm.sh/@applyseo/translate/browser\";\n\n  if (location.pathname.startsWith(\"/ar\")) {\n    startBrowserTranslation({\n      endpoint: \"https://app.applyseo.com\",\n      deliveryKey: \"at_...\",\n      locale: \"ar\",\n    });\n  }\n</script>\n```\n\nPaste the script before `</body>` in your HTML file.\n\nThe script translates text already rendered in the browser and watches for new\ntext added later. Crawlers that do not run JavaScript will not see this\ntranslation.\n\n### Make a client-rendered app crawlable\n\nIf the original page source contains only an empty app container, prerender the\nroutes first. Then run the static HTML command on the prerendered folder.\n\n```sh\nnpx applyseo-translate build \\\n  --dir ./prerendered \\\n  --site https://example.com \\\n  --locales ar\n```\n\nKeep the browser runtime in the prerendered page. The saved HTML helps crawlers.\nThe browser runtime keeps the translation visible after your JavaScript app\nstarts.\n\n## WordPress setup\n\nUse the ApplyTranslate Connector plugin instead of this npm package.\n\n1. Install and activate the plugin in WordPress.\n2. Open **Settings > ApplyTranslate**.\n3. Paste the endpoint, delivery key, locale, and site connector secret.\n4. Save the settings.\n5. Refresh WordPress permalinks once.\n6. Run the plugin health check.\n7. Open the translated URL.\n\n## How to verify any server or static setup\n\nCheck a translated page with:\n\n```sh\ncurl -I https://example.com/ar/\n```\n\nLook for these headers:\n\n| Header                     | What it confirms                     |\n| -------------------------- | ------------------------------------ |\n| `Content-Language`         | The language returned to the visitor |\n| `X-ApplyTranslate-Release` | The approved release version used    |\n| `X-ApplyTranslate-Locale`  | The locale applied to the page       |\n\nThen fetch the HTML itself:\n\n```sh\ncurl -s https://example.com/ar/\n```\n\nConfirm all of the following:\n\n- The translated words are present in the HTML.\n- `<html lang>` contains the target language.\n- `<html dir>` is correct for the language.\n- The canonical URL points to the translated page.\n- `hreflang` links include the source and translated pages.\n- Internal links point to the correct translated paths.\n\n## What the package translates\n\nThe package can translate:\n\n- Visible text.\n- Page titles and supported description metadata.\n- Link labels.\n- Image alternative text.\n- Form placeholders.\n- Accessibility labels.\n- Button and input values.\n\nIt also sets `lang` and `dir`, updates internal links, and writes canonical and\n`hreflang` tags for the translated page.\n\n## Keep specific content unchanged\n\nUse one of these markers:\n\n```html\n<span data-applyseo-notranslate>ACME-1234-XZ</span>\n<span translate=\"no\">Keep this text</span>\n<span class=\"notranslate\">Keep this text</span>\n```\n\nApplySEO ignores marked content during the website scan. It is not stored and\nis not sent to the translation provider.\n\nThe package also leaves `script`, `style`, `noscript`, `template`, `svg`, and\n`canvas` content unchanged.\n\n## What happens when something fails\n\nYour source website stays available.\n\n- If ApplySEO cannot be reached, the source language page is served.\n- If a language has no published release, the source language page is served.\n- If a fresh translation file request fails, a saved translation file can\n  continue to serve the last known approved release.\n- If new source text is not approved, that text stays in the source language.\n- The static build command exits with a clear error and tells you what to fix.\n\nA translation problem does not turn into a website outage.\n\n## Instructions for a coding agent\n\nYou can give the following brief to a coding agent:\n\n```text\nAdd @applyseo/translate to this website.\n\n1. Identify whether the site uses Next.js, Node middleware, a Fetch runtime,\n   static HTML, or a client-rendered browser app.\n2. Use the matching setup from the @applyseo/translate README.\n3. Read APPLYSEO_ENDPOINT, APPLYSEO_DELIVERY_KEY, and\n   APPLYSEO_CONNECTOR_SECRET from server or build environment variables.\n4. Never expose APPLYSEO_CONNECTOR_SECRET in browser code, generated HTML,\n   logs, or Git.\n5. Use only the public delivery key for the browser runtime.\n6. Keep existing routes and source-language pages working.\n7. Verify a translated URL, response headers, HTML lang and dir, canonical,\n   hreflang, internal links, and source-language fallback.\n8. Do not claim crawler visibility if translation happens only in the browser.\n```\n\n## What this package does not do\n\nThis package does not create translations, approve text, publish releases,\nchange DNS, or spend translation credits. Those actions stay inside ApplySEO.\n\nThe package reads one approved release and applies it to your website.\n","readmeFilename":"README.md"}