GMT+8 SG --:-- 0000 X 0000 Y /
← 全部文章← All writing

VOLO engineering diary

Engineering Diary, Day 23: Building a Business Aviation Intelligence Center — 103 Insights, 11 Languages, 1,331 New Indexed Pages

Originally published on flyvolo.ai as part of the VOLO Engineering Diary (2026-04-04), documenting the build of the VOLO private-aviation platform between February and May 2026. When VOLO changed direction in September 2026 the 25 entries were moved here as an archive. The text is unchanged; the products, pages and links it mentions no longer exist. Original byline: VOLO CTO.

The Opportunity: Exclusive Data Needs a Home

VOLO's Insights Hub already had 200+ entity pages, 7 monthly reports, and 26 page types covering airports, aircraft, operators, and routes. But all of it was structured market data — tables, rankings, trend charts. What we didn't have was analysis. The kind of deep, cross-referenced intelligence that tells you not just "OMDB traffic dropped 97%" but why, what it means for brokers, and which alternative airports are absorbing the overflow.

Our CEO acquired a dataset of 103 curated business aviation insights — each one cross-validated between AviGo flight data and knowledge graph business events. Every insight has a confidence rating (1-3 stars), a type classification, and entity references. All in Chinese. The directive: "Build an information center with industry-wide data and proactive insights. Make it massive for SEO and GEO."

Architecture: From JSON to 1,331 Pages

The raw data was a single JSON file with 103 entries. The challenge was transforming this into a fully-indexed, multilingual, cross-linked module that integrates seamlessly with our existing Insights infrastructure.

Data Layer (3 files)

We built a three-layer data architecture:

  • types.ts — IndustryInsight, MultilingualInsightText (11 locales), NormalizedEntity, DailyDigest, WeeklyDigest type definitions
  • data.ts — Raw JSON processing with entity normalization (ICAO airport codes, aircraft models, operator names), garbage filtering (Chinese descriptive text fragments mixed in with entities), confidence extraction from star characters, and a 103-entry slug mapping table
  • index.ts — 14 query functions including getInsightBySlug, getInsightsByType, getInsightsForEntity, getDailyDigests, getWeeklyDigests, and getRelatedInsights (scoring algorithm: shared entities 3x weight + same type 1x + date proximity 0-1x)

Entity Normalization: The Hidden Complexity

The raw entities array was messy. Some entries were clean ICAO codes ("KTEB", "OMDB"), some were aircraft names ("Phenom 300", "G700"), but many were garbage — Chinese descriptive text fragments that slipped in from the knowledge graph extraction. Our normalization pipeline:

  1. Filter: Reject any string containing Chinese characters
  2. Filter: Reject strings longer than 30 characters
  3. Deduplicate: "Phenom" and "Phenom 1" both map to "Phenom 300"
  4. Classify: ICAO pattern (/^[A-Z]{4}$/) → airport, known aircraft keywords → aircraft, everything else → operator
  5. Enrich: 80+ ICAO codes mapped to human-readable display names ("KTEB" → "Teterboro Airport")

Semantic URL Slugs

Every insight needed a unique, SEO-optimized English slug. We manually mapped all 103 IDs to descriptive slugs:

  • ID 326 → uae-airports-92pct-decline-iran-war
  • ID 334 → cannes-film-festival-aviation-pulse
  • ID 297 → netjets-global-8000-domestic-route-paradox
  • ID 285 → boao-forum-15-movements-deer-jet-absent

These slugs directly match long-tail search queries. Someone searching "UAE business jet traffic decline Iran war" will find our page.

11 Languages, 1,133 Translations

The CEO's directive was clear: "你有多少语种就翻译多少" (translate into every language you have). We have 11 locales.

We parallelized the translation work into 4 batches:

  1. English — Primary translation from Chinese originals (103 insights)
  2. zh-Hant + Japanese + Korean — CJK languages (parallel agent)
  3. French + Spanish + German — European languages (parallel agent)
  4. Portuguese + Russian + Arabic — Remaining languages (parallel agent)

Each translation preserves all ICAO codes, aircraft model names, operator names, numerical data, percentages, dates, and confidence ratings exactly. The result: 10 translation files (zh-Hant, en, fr, es, pt, ja, ko, de, ru, ar) totaling ~1.2MB of translated content.

The integration uses static imports with a fallback chain: native language → English → Chinese original. This means the system works even if a translation file is missing.

Page Routes: 5 Types, 121 Routes

We created 5 distinct route groups:

RouteCountPurpose
/insights/industry1Hub page with metrics, type filters, featured insights
/insights/industry/[slug]103Individual insight detail pages with full analysis
/insights/industry/type/[type]5Type aggregate pages (risk, opportunity, trend, anomaly, regulatory)
/insights/industry/daily/[date]~10Daily digest pages with prev/next navigation
/insights/industry/weekly/[week]~2Weekly summary with top entity frequency analysis

121 routes × 11 locales = ~1,331 new indexed pages.

Components: 7 New, Reusing the Shell Pattern

We built 7 new components following our established design system (vanta black, gold accents, monospace metadata):

  • InsightTypeBadge — Color-coded type labels (risk=red, opportunity=green, trend=blue, anomaly=orange, regulatory=purple)
  • ConfidenceIndicator — Star rating display with optional label
  • InsightCard — Card component for listing pages with type badge, confidence, entity tags, date
  • InsightEntityTags — Clickable entity chips linking to airport/aircraft/operator pages (with disableLinks prop for use inside link cards)
  • InsightFilterBar — Client component with type tabs and text search
  • RelatedInsights — Grid of related insight cards
  • InsightPageShell — Layout shell following the EntityPageShell pattern (breadcrumbs, JSON-LD, header, analysis, FAQ, related insights)

Cross-Linking: The SEO Force Multiplier

Bidirectional cross-links were critical for this module to have maximum SEO impact:

  • Insight → Entity: Each insight detail page shows entity tags (airports, aircraft, operators) that link to existing entity pages
  • Entity → Insight: We added an optional industryInsights prop to EntityPageShell. Airport pages like KTEB now show 6 related industry insights between the main content and FAQ section
  • Hub → Insight: The main Insights hub page (/insights) now has an "Industry Insights NEW" section with 3 featured cards
  • Header navigation: Added "Industry Insights" link to the About dropdown menu across all 11 locales

This creates a dense internal link mesh: a user on the Teterboro Airport page sees insights mentioning KTEB, clicks through to learn about NetJets' Global 7500 fleet utilization, then navigates to the Global 7500 aircraft page, which links to insights about the ultra-long-range three-way race. Every click deepens engagement and signals topical authority to search engines.

SEO & Structured Data

  • Article JSON-LD on every insight detail page (headline, description, datePublished, author, publisher, keywords, about entities)
  • FAQ JSON-LD on hub, type, and detail pages (auto-generated questions about data sources, confidence, and entities)
  • BreadcrumbList JSON-LD on all pages
  • IndexNow API updated to include all 121 industry insight URLs — submitted within minutes of deployment, notifying Bing, Yandex, Seznam, and Naver

March 2026 Monthly Report

In parallel, we extracted the March 2026 monthly report from 104 HTML pages into a 748-line TypeScript data file. Key figures: 360,597 total flights (+24.82% YoY, +70.34% MoM), 11,632 daily average, NA 75.89% / EU 14.92% / ROW 9.18%. Featured route: Dallas-Houston (987 flights). Most popular model: Phenom 300 (35,225 flights). This is now the 7th monthly report in our system, extending the time series to Oct 2025 – Mar 2026.

Bug Fix: Missing Translation Key

During verification, we discovered a pre-existing bug: the ReportCard component referenced t("fleet") from the common namespace, but no such key existed. This caused the English version of the Insights hub to crash silently. We added "fleet": "Active Fleet" to the common namespace across all 11 locale files.

Technical Challenges

Turbopack vs require()

Our initial approach used try { require('./translations-xx') } catch {} for optional translation loading. This works in Node.js and webpack production builds, but Turbopack in dev mode treats unresolved require() as hard errors even inside try/catch. We switched to static imports, adding each translation file as it became available.

Server Component onClick

The InsightCard component initially had <div onClick={e => e.preventDefault()}> to prevent parent Link navigation when clicking entity tags. Server Components can't have event handlers. We solved this by adding a disableLinks prop to InsightEntityTags, used inside card contexts where the entire card is already a link.

By the Numbers

MetricBeforeAfter
Files changed48 files, +11,518 lines
New page routes106227 (+121)
New indexed pages (est.)8,350~9,700 (+1,331)
New components156163 (+7)
Monthly reports67 (added March 2026)
Industry insights0103
Translations generated1,133 (103 × 11 - zh original)
Translation files10 files, ~1.2MB
Insight types5 (risk/opportunity/trend/anomaly/regulatory)
JSON-LD schema types1718 (+Article for insights)
IndexNow URLs submitted121
Tests passing261261
Build time55s (zero errors)

What's Next

The Industry Insights module is live but still young. Next steps: automate insight ingestion (currently manual JSON), add more entity cross-references (routes, countries), build an insight RSS feed for syndication, and monitor Google indexing velocity for the 1,331 new pages. We should also consider adding visual charts to individual insight pages and expanding the dataset beyond the current March 25 – April 3 window.

The moat is deepening. No competitor in the business aviation space has a multilingual, AI-cross-validated intelligence center with this level of structured data, SEO optimization, and search engine notification infrastructure. The 103 insights we launched today are just the beginning.