After the Weekend Sprint: Three Features That Made swisscontract.ai Actually Useful

Published: (February 28, 2026 at 01:18 PM EST)
5 min read
Source: Dev.to

Source: Dev.to

SwissContract.ai – From Weekend Hack to Production‑Ready Tool

I shipped SwissContract.ai in a weekend. Told the story. Thought I was done. Then Monday happened.


The three cracks that showed up

IssueWhy it mattered
Scanned PDFs returned empty analysisThe error “Contract text is too short or empty” is useless when a user is trying to analyse a lease that was scanned.
English‑only UISwitzerland has four official languages. An English‑only contract tool is a bad joke for local users.
Site invisible to search enginesNo og:image, no sitemap, no structured data – the site never appears in search results.

These weren’t nice‑to‑haves; they were blocking real use.


1️⃣ Handling scanned PDFs

Most contracts uploaded are digital PDFs (copy‑pasteable text). A significant chunk, however, are image‑only PDFs.

My original text‑extraction library (unpdf) returns an empty string for those files, causing the “too short” error.

First attempt – OCR in Node.js

  • Render PDF pages to a canvas with pdfjs-dist.
  • Use OCR on the canvas output.

Problem: OffscreenCanvas isn’t available in Node 22 or on Vercel, so the approach that works in a browser dies on a serverless backend.

The solution – Claude’s native PDF support

Claude can accept a raw PDF (base64‑encoded) and perform text extraction internally. No extra canvas, no OCR library, no server‑side dependencies.

// When text extraction fails ( pathname === `/${l}` || pathname.startsWith(`/${l}/`)
  );
  if (pathLocale) {
    const response = NextResponse.next();
    response.cookies.set("locale", pathLocale, { path: "/", maxAge: 31536000 });
    return response;
  }

  // 2️⃣ Root path only → try cookie, then Accept‑Language header
  if (pathname === "/") {
    const cookieLocale = request.cookies.get("locale")?.value;
    if (cookieLocale && cookieLocale !== "en") {
      return NextResponse.redirect(new URL(`/${cookieLocale}`, request.url));
    }

    const detected = detectLocale(request.headers.get("accept-language"));
    if (detected && detected !== "en") {
      const response = NextResponse.redirect(
        new URL(`/${detected}`, request.url)
      );
      response.cookies.set("locale", detected, { path: "/", maxAge: 31536000 });
      return response;
    }
  }

  // 3️⃣ No special handling → continue as‑is
  return NextResponse.next();
}

Result

  • Each language now has a stable, crawlable URL.
  • Google discovers /de, /fr, /it as separate pages → proper hreflang handling.
  • Shared links always point to the intended language.
  • Analytics can segment by pathname.

🎉 Takeaways

  1. Leverage existing AI capabilities – Claude’s PDF handling saved us from building a custom OCR pipeline.
  2. Design i18n with SEO in mind – Locale‑specific URLs are essential for discoverability and analytics.
  3. Middleware can keep the user experience smooth while still serving the correct language version.

SwissContract.ai went from a weekend prototype to a production‑ready, multilingual, SEO‑friendly contract analyser. 🚀

Overview

Problem:

  • Cookie‑based locale handling broke SEO, link sharing, and analytics.
  • The site shipped without essential SEO fundamentals.

Lesson:

  • Path‑based routing is the right default for public‑facing sites.
  • Ship SEO basics before announcing a product.

What Went Wrong

IssueImpact
No og:imageSocial shares displayed a blank card
No sitemapSearch engines couldn’t discover pages efficiently
No robots.txtCrawlers had no guidance
No structured data (FAQPage, Organization)Missed rich‑result opportunities
Thin homepage content (no “How it works”, no FAQ)Poor user experience & lower rankings
Cookie‑based locale (instead of path‑based)SEO, link sharing, and analytics broke

Fixes Implemented (in one session)

1. Open Graph Image

  • Added an SVG (/public/og-image.svg) – 1200 × 630, fully vector, no rasterisation needed.
  • Served directly by Next.js.

2. Sitemap (Next.js 14)

// app/sitemap.ts
import type { MetadataRoute } from 'next';

export default function sitemap(): MetadataRoute.Sitemap {
  return [
    {
      url: "https://swisscontract.ai",
      lastModified: new Date(),
      changeFrequency: "weekly",
      priority: 1,
    },
  ];
}

3. Structured Data (JSON‑LD)

Added FAQPage and Organization schemas to layout.tsx.
Google now can surface FAQ rich results in SERPs.

4. Homepage Content

  • Added a FAQ section and a “How it works” step‑by‑step guide.
  • These appear only when no analysis is active, keeping the UI clean.

5. Locale Routing

  • Switched from cookie‑based locale to path‑based (/de, /fr, …).
  • SEO‑friendly and works as the source of truth for language.

Product Summary

+-------------------------------------------------+
|                swisscontract.ai                 |
|                                                 |
|  Input:  PDF / DOCX / DOC / TXT                 |
|          (text‑based OR scanned)                |
|                                                 |
|  Language: EN / DE / FR / IT                   |
|            (path‑based, SEO‑friendly)           |
|                                                 |
|  Output: Plain‑language AI analysis              |
|          in the user's language                |
|                                                 |
|  Limits: 5 MB, 20 pages, 5 IP/day               |
|  Storage: none                                  |
+-------------------------------------------------+
  • Scanned PDF support – the AI model reads PDFs natively; the simplest solution turned out to be the most powerful.

Takeaway

“Ship SEO basics before you tell anyone about the product.”

Skipping those fundamentals cost early traffic and required a painful retro‑fit. The three focused sessions that followed turned a weekend prototype into a usable, SEO‑friendly service.

Live demo: https://swisscontract.ai – analyse any Swiss contract for free, no account required.

0 views
Back to Blog

Related posts

Read more »

Google Gemini Writing Challenge

What I Built - Where Gemini fit in - Used Gemini’s multimodal capabilities to let users upload screenshots of notes, diagrams, or code snippets. - Gemini gener...