npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@altified/next

v0.5.13

Published

Server-first translation, locale routing, and a dashboard-controlled language switcher for Next.js App Router projects.

Readme

@altified/next

Server-first translation, locale routing, and a dashboard-controlled language switcher for Next.js App Router projects.

Install and initialize

Requires Next.js 15 or newer and React 18 or newer.

pnpm add @altified/next
pnpm exec altified init

You can use npm install @altified/next and npx altified init instead.

The initializer detects JavaScript or TypeScript and whether the project uses a src directory. For a Next.js project it:

  • adds the server-only environment placeholders to .env;
  • wraps the existing Next config with withAltified;
  • creates a server-only client in lib/altified;
  • creates the locale-routing proxy file;
  • keeps the site's existing /sitemap.xml and adds a separate Altified sitemap containing only additional-language URLs;
  • adds /altified-sitemap.xml to an existing robots route or static robots.txt when it can do so safely; otherwise it reports the exact manual change required.

Existing generated files and environment values are preserved. The site's sitemap and robots crawl rules are preserved even with --force; the initializer only merges the Altified sitemap entry into the existing sitemap list. The flag can replace other integration files. Review the reported warning if an unusual Next config or robots implementation cannot be changed safely. The initializer never inserts visual UI into your layout: place the language switcher yourself wherever it belongs. Use --force only when you intentionally want to replace generated files.

The sitemap integration uses the site's existing app/sitemap.js/ts function directly when available (including generateSitemaps shards); for custom XML or static sitemaps it reads the site's published /sitemap.xml, including any sitemap index children. The original site sitemap keeps serving that URL and remains the only source of page selection, including dynamic pages and exclusions. Altified publishes an index at /altified-sitemap.xml with locale-named children such as /altified-sitemaps/fr-0.xml. Those children contain only URLs not already in the site's sitemap. A source /jobs/nurse produces /fr/jobs/nurse, never a second /jobs/nurse entry. Child files split at 50,000 URLs or 50 MB. If the source function fails, a public XML source is unavailable, or Altified routing is unavailable, the Altified route returns 503 rather than an incomplete successful sitemap. An empty source is also an error. The source sitemap must itself successfully include the site's dynamic public pages; Altified cannot recover URLs it omits. The site's own sitemap function also controls its caching and revalidation.

If no sitemap exists, the initializer does not invent URLs or create the Altified sitemap. Add a site-owned sitemap first, then run altified init. Existing robots.js, robots.ts, or robots.txt files keep their crawl rules and receive the Altified sitemap in their sitemap list when the initializer can identify the existing sitemap format. If a static file does not contain an absolute sitemap URL, or a custom route cannot be safely parsed, the initializer prints the exact manual change required. If there is no robots file, the generated one lists both sitemap URLs without changing crawl rules. Submitting the Altified sitemap directly in Search Console remains supported in every case.

The Altified sitemap is for discovery, not hreflang annotation: original and translated pages must expose reciprocal hreflang links in their HTML. The loader wraps static metadata exports automatically; for a custom generateMetadata, call AltifiedMetadata from @altified/next/auto (or the generated translateAltifiedMetadata helper) with the page's metadata and a public origin. Verify on both locale versions that their HTML contains the same reciprocal language links and a self-canonical URL. Also verify that the translated page is actually translated and indexable. The SDK cannot guarantee that arbitrary database-backed content has been translated; translate API fields explicitly where needed.

Upgrading an older initialization: altified init safely restores a dynamic sitemap.source.js/ts to sitemap.js/ts and archives recognized old Altified-generated sitemap routes. If the source still contains the old STATIC_ROUTES fallback, initialization stops: replace that fallback with the site's complete sitemap function first. It will not guess missing dynamic URLs. The public origin is derived from the request unless configured explicitly.

For deployments behind a reverse proxy that gives Next.js an internal request origin, set the public origin explicitly:

NEXT_PUBLIC_SITE_URL=https://your-production-domain.com

The generated sitemap and robots routes use this value when it is present. For custom XML/static sitemaps, set ALTIFIED_SITE_URL or NEXT_PUBLIC_SITE_URL explicitly so the SDK can fetch only the intended public host, rather than trusting an incoming Host header. The source sitemap path defaults to /sitemap.xml; an alternate path can be set with ALTIFIED_SOURCE_SITEMAP_PATH, but it must stay on the same site and must not point to an Altified sitemap.

For a locally running Altified backend:

pnpm exec altified init --local

Dashboard setup

In the Altified dashboard, create or open the project, add its source and target languages, and copy the project ID and server secret into .env:

ALTIFIED_SECRET_KEY=altified_...
ALTIFIED_PROJECT_ID=your-project-uuid

ALTIFIED_SECRET_KEY is server-only. Never rename it to a NEXT_PUBLIC_ variable. The project default language and published locale routing come from the Altified dashboard. ALTIFIED_DEFAULT_LOCALE is an optional local fallback, not a required installation setting. Restart Next.js after changing .env.

Configure and publish the language switcher in the dashboard. The component loads that public configuration, so language order, labels, theme, and routing behavior can be changed without replacing the component.

Automatic JSX translation

The Next config installed by altified init translates literal JSX text in server and client components:

export default function Page({ product }) {
  return (
    <main>
      <h1>Welcome to our website</h1>
      <p>{product.name}</p>
    </main>
  );
}

Welcome to our website receives a stable generated translation key and is resolved for the locale selected by the proxy. The compiler also follows fixed copy held in local constants, objects, and arrays when those values are rendered as text (including common .map() card/list patterns). Runtime values such as {product.name} are intentionally left unchanged unless they are an explicitly recognised copy field. Client components beginning with "use client" are rewritten too, using the browser-safe client entry point from @altified/next/auto.

If the secret is missing or the translation service is unavailable, the source text is rendered. Generated keys use the source file path and literal position; moving a literal or inserting an earlier literal can create a new key.

Translation coverage

Altified uses layered coverage so different Next.js project styles can opt into the safest path for each kind of copy:

  • Automatic JSX: literal text in server-rendered JSX and files with "use client" is translated regardless of tag (h1, p, button, span, and similar). Fixed copy in local variables, objects, and arrays is also translated when it is rendered, including data-driven cards and lists. Runtime expressions whose content cannot be proven remain unchanged.
  • Accessibility attributes: literal alt, aria-label, placeholder, and title values on native HTML elements are translated without changing the component source.
  • Custom component props: recognised copy props, including placeholder and aria-label, are resolved to strings before being passed to components. This keeps custom inputs compatible while their placeholder text is translated.
  • Client components: rewritten client components use the browser-safe @altified/next/auto entry point. You can still use AltifiedClientProvider to preload a translations map from the server and avoid client-side loading flashes.
  • Metadata/head: static metadata exports and object values returned directly from generateMetadata are translated automatically. For custom metadata helpers or non-object returns, use altified.translatedMetadata() for translated title, description, Open Graph, and Twitter text.
  • Backend/API data: translate explicitly by field name. Do not blindly translate whole response objects, because IDs, slugs, emails, prices, statuses, and user content can be damaged.

Client component example:

"use client";

import { AltifiedText } from "@altified/next/auto";

export function Header() {
  return <nav><AltifiedText translationKey="nav.contact" defaultValue="Contact" /></nav>;
}

Wrap that client tree with translations loaded from a server component:

import { AltifiedClientProvider } from "@altified/next/auto";
import { altified } from "@/lib/altified";

const navCopy = await altified.resolve(locale, [
  { key: "nav.contact", defaultValue: "Contact" },
  { key: "nav.pricing", defaultValue: "Pricing" },
]);

<AltifiedClientProvider translations={navCopy}>
  <Header />
</AltifiedClientProvider>

Metadata example:

export async function generateMetadata({ params }) {
  const { locale } = await params;
  return altified.translatedMetadata({
    locale,
    path: "/pricing",
    origin: "https://example.com",
    locales: ["en", "fr"],
    hideDefaultLocale: true,
    title: "Pricing",
    description: "Simple plans for teams.",
    openGraph: {
      title: "Pricing",
      description: "Simple plans for teams.",
    },
  });
}

For the explicit altified.metadata() and altified.translatedMetadata() helpers, keep locales, defaultLocale, and hideDefaultLocale aligned with the published dashboard routing. AltifiedMetadata reads that published routing automatically.

Explicit and batched translation

Use the generated server client when you need named keys, dynamic values, or a single batched request:

import { altified } from "@/lib/altified";

const copy = await altified.resolve(locale, [
  { key: "home.title", defaultValue: "Hello" },
  { key: "home.subtitle", defaultValue: "Welcome to our website" },
]);

Use altified.t(key, options) for one explicit string. Use altified.metadata() inside generateMetadata to build canonical and reciprocal alternate-language URLs.

SEO helpers

The SDK also provides framework-friendly helpers for indexing controls, sitemaps, and structured data. Import them from @altified/next/seo:

import { createRobots } from "@altified/next/seo";

export default function robots() {
  return createRobots({
    origin: "https://example.com",
    disallow: ["/dashboard", "/search"],
  });
}

Build a localized sitemap from your public routes. Each localized entry includes the reciprocal alternates.languages map and x-default URL:

import { createSitemap } from "@altified/next/seo";

export default function sitemap() {
  return createSitemap({
    origin: "https://example.com",
    locales: ["en", "fr"],
    routes: [
      { path: "/", priority: 1 },
      { path: "/pricing", changeFrequency: "monthly" },
    ],
  });
}

The initializer also exposes the sitemap integration helpers from @altified/next/sitemap. The generated Altified routes use createAltifiedTranslationSitemapFromEntries() for a standard Next sitemap, or createAltifiedTranslationSitemapFromSite() for public XML. For a custom integration that already has an array of pages, the lower-level helper can still wrap a standard Next sitemap function:

import { createAltifiedSitemapFromSource } from "@altified/next/sitemap";

async function buildLocalizedSitemap(request) {
  return createAltifiedSitemapFromSource({
    source: getMyExistingSitemapEntries,
    request,
  });
}

The source must return the standard Next.js sitemap array. This lower-level helper returns original and translated entries; filter the original entries if you are building a translated-only sitemap yourself. The default initializer does this filtering and leaves the site's /sitemap.xml untouched.

Use noIndexMetadata() for not-found, private, search, or filter pages:

import { noIndexMetadata } from "@altified/next/seo";

export async function generateMetadata() {
  return noIndexMetadata();
}

Structured data helpers cover common content types and can be rendered safely with AltifiedJsonLd:

import {
  AltifiedJsonLd,
  createArticleJsonLd,
  createBreadcrumbJsonLd,
} from "@altified/next/seo";

export default function Article({ article }) {
  return (
    <>
      <AltifiedJsonLd data={createArticleJsonLd({
        headline: article.title,
        description: article.description,
        url: article.url,
        image: article.image,
        datePublished: article.publishedAt,
        dateModified: article.updatedAt,
        author: article.author,
        inLanguage: article.locale,
      })} />
      <AltifiedJsonLd data={createBreadcrumbJsonLd(article.breadcrumbs)} />
      <article>{article.content}</article>
    </>
  );
}

These helpers do not fetch articles or decide whether a slug exists. The app must still load its content on the server and call Next's notFound() for a missing record so the response has the correct HTTP status.

Language switcher and locale routing

Initialization does not add a switcher to your UI. Import it in the header, navigation, or other component where you want the control to appear:

import { AltifiedLanguageSwitcher } from "@altified/next/switcher";

<AltifiedLanguageSwitcher
  projectId={process.env.ALTIFIED_PROJECT_ID}
  locale={locale}
  apiUrl={process.env.ALTIFIED_API_URL}
/>

The switcher only fetches the project's published public configuration; it does not expose the server secret. Its options open directly below the control on desktop and mobile.

The generated proxy.js or proxy.ts uses the same configuration:

import { createAltifiedProxy } from "@altified/next/proxy";

export const proxy = createAltifiedProxy({
  projectId: process.env.ALTIFIED_PROJECT_ID,
});

export const config = {
  matcher: ["/((?!api|_next/static|_next/image|favicon.ico|.*\\..*).*)"],
};

It caches public routing configuration for five minutes, uses stale configuration when refresh fails, and falls back to ALTIFIED_DEFAULT_LOCALE before configuration has been loaded. It also remembers the visitor's selected locale. For example, after selecting French, an ordinary internal link to /terms is redirected to /fr/terms, so existing Next.js Link components do not need locale-specific href values. The switcher does not intercept global anchor clicks. Use transformed static links or localizePath for dynamic hrefs; direct window.location assignments remain full browser navigations by design.

Forward locale to backend APIs

If your Next app calls a separate backend, use altifiedFetch in your shared API client so the active Altified locale is sent as Accept-Language.

import { altifiedFetch } from "@altified/next/fetch";

const response = await altifiedFetch(`${BACKEND_URL}/api/products/`, {
  credentials: "include",
});

In client components, altifiedFetch detects the locale from the Altified cookie, the document language, or the locale prefix in the URL. In server components, pass the locale explicitly when you already have it:

const response = await altifiedFetch(`${BACKEND_URL}/api/products/`, {
  locale,
  cache: "no-store",
});

This is the recommended way to make a Django backend using django_altified receive the same locale as the current Next route.\n\nFor Axios, attach the interceptor to your shared Axios instance:\n\njs\nimport axios from "axios";\nimport { altifiedAxiosInterceptor } from "@altified/next/axios";\n\nconst api = axios.create({\n baseURL: BACKEND_URL,\n withCredentials: true,\n});\n\naltifiedAxiosInterceptor(api);\n

Production workflow

  1. Add source copy as server-rendered JSX literals or explicit named keys.
  2. Visit the pages in each configured locale so missing strings are registered and translated.
  3. Review and edit translations in the Altified dashboard.
  4. Add glossary terms for product names, technical terms, or words whose translation must stay consistent, then regenerate affected translations.
  5. Publish the translations and switcher configuration.
  6. Set ALTIFIED_SECRET_KEY, ALTIFIED_PROJECT_ID, and ALTIFIED_DEFAULT_LOCALE in the production environment and deploy.

A glossary tells the translation engine how specific terms should be translated—or that they must not be translated. It does not replace the translation editor: the editor controls a particular string, while a glossary applies terminology consistently across many strings and future translations.