<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: T Narasimha</title>
    <description>The latest articles on DEV Community by T Narasimha (@nanilm10).</description>
    <link>https://dev.to/nanilm10</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3786459%2F90c7b57c-5261-4c91-b0bd-3fd527af9629.png</url>
      <title>DEV Community: T Narasimha</title>
      <link>https://dev.to/nanilm10</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nanilm10"/>
    <language>en</language>
    <item>
      <title>How i built a Real-Time Multilingual Social Media Platform using Lingo.dev</title>
      <dc:creator>T Narasimha</dc:creator>
      <pubDate>Mon, 23 Feb 2026 15:51:59 +0000</pubDate>
      <link>https://dev.to/nanilm10/how-i-built-a-real-time-multilingual-social-media-platform-using-lingodev-4l8b</link>
      <guid>https://dev.to/nanilm10/how-i-built-a-real-time-multilingual-social-media-platform-using-lingodev-4l8b</guid>
      <description>&lt;p&gt;When we started building Nativly, we didn’t set out to “add translation.”&lt;/p&gt;

&lt;p&gt;We were trying to solve something more fundamental:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why should language decide who gets to participate in an online community?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most community platforms assume English. If you don’t speak it comfortably, you adapt — or you leave.&lt;/p&gt;

&lt;p&gt;We didn’t want adaptation.&lt;br&gt;
We wanted inclusion by default.&lt;/p&gt;

&lt;p&gt;This is the story of how we built a real-time multilingual community platform using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js&lt;/li&gt;
&lt;li&gt;Supabase&lt;/li&gt;
&lt;li&gt;Lingo.dev SDK
And how we made translation feel invisible.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  The Problem We Faced
&lt;/h1&gt;

&lt;p&gt;Static UI translation is easy.&lt;/p&gt;

&lt;p&gt;You use i18n.&lt;br&gt;
You add JSON files.&lt;br&gt;
You translate buttons.&lt;/p&gt;

&lt;p&gt;But Nativly isn’t just UI.&lt;br&gt;
It’s:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Posts&lt;/li&gt;
&lt;li&gt;Comments&lt;/li&gt;
&lt;li&gt;User bios&lt;/li&gt;
&lt;li&gt;Community discussions
All user-generated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And here’s the real challenge:&lt;br&gt;
If a Hindi user writes a post,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a Spanish user should read it in Spanish.&lt;/li&gt;
&lt;li&gt;An English user should see it in English.&lt;/li&gt;
&lt;li&gt;A Tamil user should see it in Tamil.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Same content Different viewer Real-time&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Pre-translating every post into 20 languages? Not scalable.&lt;br&gt;
Storing 20 columns per post? Messy.&lt;br&gt;
Translating on the client? Unsafe.&lt;/p&gt;

&lt;p&gt;We needed something smarter.&lt;/p&gt;
&lt;h1&gt;
  
  
  Our Core Idea: Store Once, Translate on View
&lt;/h1&gt;

&lt;p&gt;Instead of storing translations in the database, we made a design decision that changed everything.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Store the original content only. Translate at render time based on the viewer.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;
  
  
  Our Architecture (Simple but Powerful)
&lt;/h1&gt;

&lt;p&gt;Here’s how Nativly works internally:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User writes post in their native language.&lt;/li&gt;
&lt;li&gt;We store:

&lt;ul&gt;
&lt;li&gt;Original text&lt;/li&gt;
&lt;li&gt;Source language&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;When another user views the post:

&lt;ul&gt;
&lt;li&gt;We check their preferred language.&lt;/li&gt;
&lt;li&gt;If it differs → we translate in real time using Lingo.dev.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We cache the result.&lt;/li&gt;
&lt;li&gt;We render translated content.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s it.&lt;/p&gt;

&lt;p&gt;No duplicated database rows.&lt;br&gt;
No bloated schema.&lt;br&gt;
No language-specific columns.&lt;/p&gt;

&lt;p&gt;Just intelligent translation at the edge of interaction.&lt;/p&gt;
&lt;h1&gt;
  
  
  Our Database Design (Supabase)
&lt;/h1&gt;

&lt;p&gt;Our &lt;code&gt;posts&lt;/code&gt; table looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;create table posts (
  id uuid primary key default uuid_generate_v4(),
  user_id uuid references profiles(id),
  original_text text not null,
  source_language varchar(10) not null,
  created_at timestamp with time zone default now()
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s all.&lt;/p&gt;

&lt;p&gt;We don’t store text_hi, text_bn, text_ta.&lt;/p&gt;

&lt;p&gt;Because translation is not storage.&lt;br&gt;
It’s transformation.&lt;/p&gt;
&lt;h1&gt;
  
  
  Integrating Lingo.dev SDK (Server-Side)
&lt;/h1&gt;

&lt;p&gt;We integrated Lingo.dev in our Next.js backend layer (Route Handlers).&lt;/p&gt;

&lt;p&gt;We do not call it from the client. Why?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keeps API key secure&lt;/li&gt;
&lt;li&gt;Allows caching logic&lt;/li&gt;
&lt;li&gt;Reduces abuse&lt;/li&gt;
&lt;li&gt;Keeps translation consistent&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Step 1: Installing SDK
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @lingo-dev/sdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 2: Creating a Translation Service &lt;code&gt;/lib/lingo.ts&lt;/code&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Lingo from "@lingo-dev/sdk";

const lingo = new Lingo({
  apiKey: process.env.LINGO_API_KEY!,
});

export async function translateText(
  text: string,
  sourceLanguage: string,
  targetLanguage: string
) {
  if (sourceLanguage === targetLanguage) {
    return text;
  }

  const response = await lingo.translate({
    text,
    sourceLanguage,
    targetLanguage,
  });

  return response.translatedText;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Simple.&lt;br&gt;
No overengineering.&lt;/p&gt;
&lt;h1&gt;
  
  
  Real-Time Translation in Our API Route
&lt;/h1&gt;

&lt;p&gt;When fetching posts for a user: &lt;br&gt;
&lt;code&gt;/app/api/feed/route.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { translateText } from "@/lib/lingo";
import { createClient } from "@/lib/supabase/server";

export async function GET() {
  const supabase = createClient();

  const {
    data: { user },
  } = await supabase.auth.getUser();

  const { data: profile } = await supabase
    .from("profiles")
    .select("preferred_language")
    .eq("id", user?.id)
    .single();

  const viewerLanguage = profile?.preferred_language || "en";

  const { data: posts } = await supabase
    .from("posts")
    .select("*")
    .order("created_at", { ascending: false });

  const translatedPosts = await Promise.all(
    posts.map(async (post) =&amp;gt; {
      const translated = await translateText(
        post.original_text,
        post.source_language,
        viewerLanguage
      );

      return {
        ...post,
        display_text: translated,
      };
    })
  );

  return Response.json(translatedPosts);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what we’re doing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We don’t mutate stored content.&lt;/li&gt;
&lt;li&gt;We compute display_text dynamically.&lt;/li&gt;
&lt;li&gt;Translation depends entirely on who is viewing.
That’s the key difference.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Making It Feel Instant (Caching Strategy)
&lt;/h1&gt;

&lt;p&gt;Real-time translation is powerful.&lt;br&gt;
But it can be expensive and slow if done naively.&lt;/p&gt;

&lt;p&gt;So we added caching.&lt;/p&gt;
&lt;h2&gt;
  
  
  Our Strategy
&lt;/h2&gt;

&lt;p&gt;If:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Post A (Hindi)&lt;/li&gt;
&lt;li&gt;Viewed by 10 English users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We shouldn’t translate it 10 times.&lt;/p&gt;

&lt;p&gt;We cache translations like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;translation_cache:
post_id
target_language
translated_text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before calling Lingo.dev, we check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const cached = await supabase
  .from("translation_cache")
  .select("translated_text")
  .eq("post_id", post.id)
  .eq("target_language", viewerLanguage)
  .single();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If exists → return cached.&lt;br&gt;
If not → call Lingo → store → return.&lt;/p&gt;

&lt;p&gt;This reduced API calls drastically.&lt;/p&gt;
&lt;h1&gt;
  
  
  Why We Didn’t Pre-Translate
&lt;/h1&gt;

&lt;p&gt;We tested pre-translation into 5 languages at post creation.&lt;/p&gt;

&lt;p&gt;Problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increased post creation latency.&lt;/li&gt;
&lt;li&gt;Wasteful if no one reads in certain languages.&lt;/li&gt;
&lt;li&gt;Hard to scale beyond fixed languages.
Translation should happen when needed, not before.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  UI Integration (Next.js)
&lt;/h1&gt;

&lt;p&gt;Frontend is simple.&lt;br&gt;
We just render:&lt;br&gt;
&lt;code&gt;&amp;lt;p&amp;gt;{post.display_text}&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Users never see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;loading spinners&lt;/li&gt;
&lt;li&gt;raw language switches&lt;/li&gt;
&lt;li&gt;language mismatch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It just works.&lt;br&gt;
And that’s the goal.&lt;/p&gt;
&lt;h1&gt;
  
  
  Handling User Language Preferences
&lt;/h1&gt;

&lt;p&gt;Our &lt;code&gt;profiles&lt;/code&gt; table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;preferred_language varchar(10) default 'en'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Users can select:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;English&lt;/li&gt;
&lt;li&gt;Hindi&lt;/li&gt;
&lt;li&gt;Dutch&lt;/li&gt;
&lt;li&gt;Japanese&lt;/li&gt;
&lt;li&gt;French&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Changing this setting instantly changes how the entire feed renders.&lt;/p&gt;

&lt;p&gt;No reload hacks.&lt;br&gt;
No separate routes.&lt;br&gt;
Just dynamic translation.&lt;/p&gt;

&lt;h1&gt;
  
  
  What &lt;a href="https://lingo.dev/en" rel="noopener noreferrer"&gt;Lingo.dev&lt;/a&gt; made possible
&lt;/h1&gt;

&lt;p&gt;Without Lingo:&lt;br&gt;
We would need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manual translation pipelines&lt;/li&gt;
&lt;li&gt;Separate language services&lt;/li&gt;
&lt;li&gt;Heavy infra&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead:&lt;br&gt;
We get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean SDK&lt;/li&gt;
&lt;li&gt;Server-side integration&lt;/li&gt;
&lt;li&gt;High-quality translations&lt;/li&gt;
&lt;li&gt;Scalable API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It let us focus on product,&lt;br&gt;
not language mechanics.&lt;/p&gt;

&lt;h1&gt;
  
  
  What We Learned
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;1.Translation is a Product Feature, Not a Utility&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When users read content in their language:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They comment more.&lt;/li&gt;
&lt;li&gt;They stay longer.&lt;/li&gt;
&lt;li&gt;They engage deeper.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It changes their entire behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.Server-Side Translation is Cleaner&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Doing translation inside API routes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keeps logic centralized&lt;/li&gt;
&lt;li&gt;Avoids duplicated client calls&lt;/li&gt;
&lt;li&gt;Makes caching easier&lt;/li&gt;
&lt;li&gt;Protects your SDK key&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3.Real-Time &amp;gt; Static for Communities&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Static UI translation is necessary.&lt;br&gt;
But it doesn’t solve community inclusion.&lt;/p&gt;

&lt;p&gt;User-generated content must be dynamic.&lt;/p&gt;

&lt;p&gt;That’s where Lingo.dev became essential for us.&lt;/p&gt;

&lt;h1&gt;
  
  
  Performance Considerations
&lt;/h1&gt;

&lt;p&gt;We optimized:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only translate when languages differ&lt;/li&gt;
&lt;li&gt;Cache aggressively&lt;/li&gt;
&lt;li&gt;Batch translation calls where possible&lt;/li&gt;
&lt;li&gt;Use Promise.all for concurrency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Result:&lt;br&gt;
Feed feels native.&lt;/p&gt;

&lt;p&gt;Even when languages are mixed.&lt;/p&gt;

&lt;h1&gt;
  
  
  What Makes This Different
&lt;/h1&gt;

&lt;p&gt;Many multilingual apps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Translate UI&lt;/li&gt;
&lt;li&gt;Ignore user content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nativly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Translates conversations.&lt;/li&gt;
&lt;li&gt;Translates community interaction.&lt;/li&gt;
&lt;li&gt;Makes cross-language dialogue natural.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It doesn’t just display languages.&lt;br&gt;
It bridges them.&lt;/p&gt;

&lt;h1&gt;
  
  
  Where We’re Taking This Next
&lt;/h1&gt;

&lt;p&gt;Future improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Translation confidence scoring&lt;/li&gt;
&lt;li&gt;Context-aware translation (community-specific tone)&lt;/li&gt;
&lt;li&gt;Real-time comment streaming with translation&lt;/li&gt;
&lt;li&gt;Language auto-detection for posts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the foundation is stable.&lt;br&gt;
And it’s simple.&lt;/p&gt;

&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;We didn’t build translation because it was trendy.&lt;/p&gt;

&lt;p&gt;We built it because communities shouldn’t fragment along language lines.&lt;/p&gt;

&lt;p&gt;With:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js for structure&lt;/li&gt;
&lt;li&gt;Supabase for data&lt;/li&gt;
&lt;li&gt;Lingo.dev for intelligent real-time translation
We turned a local community idea into a borderless platform.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same database.&lt;br&gt;
Same post.&lt;br&gt;
Different language.&lt;br&gt;
Different experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That’s the power &lt;a href="https://lingo.dev/en" rel="noopener noreferrer"&gt;Lingo.dev&lt;/a&gt; brings to the table.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If this article helped you, share it with your friends or someone who's building cool stuff.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>linodehackathon</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
