background blurbackground mobile blur

1/1/1970

কীভাবে আমি ৩ দিনে ২০টি ভাষায় i18n বাস্তবায়ন করলাম

হ্যালো! আমি সবেমাত্র একটা বিশাল কাজ শেষ করলাম, যেখানে Foony কে ২০টি ভিন্ন ভাষায় অনুবাদ করেছি। এটা একটা বিশাল কর্মযজ্ঞ ছিল যেখানে কোডবেসের প্রায় প্রতিটি ফাইল স্পর্শ করতে হয়েছে, কিন্তু আমি মাত্র ৩ দিনেই পুরোটা সম্পন্ন করতে পেরেছি।

নিচে আমি বিস্তারিত বলব কীভাবে এটা করেছি, এই পরিবর্তনের পেছনের নির্দিষ্ট সংখ্যাগুলো কী, এবং কেন আমি ইন্ডাস্ট্রি স্ট্যান্ডার্ড ব্যবহার না করে নিজেই নিজের অনুবাদ লাইব্রেরি তৈরি করার সিদ্ধান্ত নিলাম (আবারও)।

i18next কেন নয়?

যখন আমি প্রথম অনুবাদ যোগ করার কথা ভেবেছিলাম, তখন ইন্ডাস্ট্রি স্ট্যান্ডার্ড বিবেচনা করেছিলাম: i18next এবং react-i18next

এর বদলে, আমি AI দ্বারা রক্ষণাবেক্ষণযোগ্যতার জন্য অপ্টিমাইজ করার সিদ্ধান্ত নিলাম। i18next শক্তিশালী, কিন্তু এর API-এর বৈচিত্র্য LLM-গুলোকে হ্যালুসিনেট করতে বা অসামঞ্জস্যপূর্ণ কোড লিখতে বাধ্য করতে পারে। লাইব্রেরিকে শুধু t() এবং interpolate() এ সীমাবদ্ধ রেখে আমি নিশ্চিত করলাম যে ১০+ সমান্তরাল এজেন্ট প্রায় কোনো মানব হস্তক্ষেপ ছাড়াই ১০০% টাইপ-সেফ কোড লিখতে পারে।

আমি একটা বড় ইকোসিস্টেমে আটকে যাওয়া নিয়েও সতর্ক ছিলাম, যা পরে ব্রেকিং চেঞ্জ আনতে পারে। React Router v5 এবং MUI v4 → v5 এর মতো কষ্টকর মাইগ্রেশনের তিক্ত অভিজ্ঞতা থেকে আমি জানি যে JavaScript জগতে ব্যাকওয়ার্ড-কম্প্যাটিবিলিটি দ্রুত ভাঙার ঘটনা খুবই সাধারণ। পরে প্লুরালাইজেশন ফিচার যোগ করার খরচ এখন ১৩৯k লাইন কোড ম্যানুয়ালি মাইগ্রেট করার চেয়ে কম।

আমি এমন কিছু চাইছিলাম যা একদম সরল, অত্যন্ত হালকা, এবং আমার টিমের প্রয়োজন অনুযায়ী তৈরি।

তাই নিজেই লিখে ফেললাম।

আমি ৩ KB এর একটা সীমাবদ্ধ সাবসেট তৈরি করলাম, যা বিশেষভাবে উচ্চ-নির্ভুলতাসম্পন্ন, স্বায়ত্তশাসিত AI রিফ্যাক্টরিং সক্ষম করার জন্য ডিজাইন করা। এটা আমাকে একজন ইঞ্জিনিয়ার হিসেবে ৫ জনের একটা টিমের ৩ সপ্তাহের কাজ মাত্র ৩ দিনে সম্পন্ন করতে দিল।

কাস্টম ইমপ্লিমেন্টেশন

আমি একটা মিনিমাল i18n লাইব্রেরি তৈরি করলাম যা প্রায় ৩ KB gzipped। এটা দুটো প্রধান ফাংশন প্রকাশ করে: নন-React কনটেক্সটের জন্য getTranslation() এবং কম্পোনেন্টের জন্য useTranslation() হুক।

এগুলো সরল স্ট্রিং প্রতিস্থাপনের জন্য t() এবং যখন একটা অনুবাদ স্ট্রিংয়ে React কম্পোনেন্ট (যেমন একটা লিংক বা আইকন) ইনজেক্ট করতে চাই তখন interpolate() রিটার্ন করে। উভয় ফাংশনই ভেরিয়েবল প্রতিস্থাপন সমর্থন করে, যেমন "Hello {{thing}}", {thing: 'World'}

কী-গুলো একটা "স্ল্যাশ-ডট" নোটেশন অনুসরণ করে (লোকালাইজেশন ফাইলের পথের জন্য স্ল্যাশ, ফাইলের নেস্টেড অবজেক্টের জন্য ডট)। ইউনিকনেস নিশ্চিত করতে, একটা ফাইলের অনুবাদ কী-তে ফরওয়ার্ড-স্ল্যাশ থাকতে পারে না।

এই হলো মূল t() ফাংশন:

export function t(key: TranslationKeys, values?: Record<string, string | number>, locale?: SupportedLocale): string {
  let namespace: string = '';
  let translationKey: string = key;
  
  // Check if key contains '/' - this indicates a namespace
  const slashIndex = key.indexOf('/');
  if (slashIndex !== -1) {
    const parts = key.split('/');
    namespace = parts.slice(0, -1).join('/');
    translationKey = parts[parts.length - 1];
  }
  
  const targetLocale = locale ?? currentLocale;
  const text = getTranslationValue(targetLocale, namespace, translationKey);
  
  if (values) {
    return interpolateString(text, values);
  }
  
  return text;
}

এবং React হুক:

export function useTranslation() {
  const [language] = useLanguage();

  // Subscribe to locale loading events to trigger re-renders when translations are loaded
  const version = useSyncExternalStore(
    (callback) => LocaleQueryer.onLoad(callback),
    () => LocaleQueryer.getVersion(),
    () => LocaleQueryer.getVersion()
  );

  return useMemo(() => ({
    t: (key: TranslationKeys, values?: Record<string, string | number>) => 
      t(key, values, language),
    interpolate: (key: TranslationKeys, components: Record<string, ReactNode>) => 
      interpolate(key, components, language),
  }), [language, version]);
}

পুরো লাইব্রেরির মূল অংশ মাত্র ৫৮০ লাইন কোড। এটা পরিচালনা করে:

  • লেজি-লোডিং অনুবাদ ফাইল, যাতে আমাদের প্রতিটি ব্যবহারকারীর কাছে ২০টা ভাষা পাঠাতে না হয়।
  • "namespace" দ্বারা অনুবাদ কোড-স্প্লিটিং (যেমন common, misc, games/{gameId})।
  • একটা "debug" লোকেল যা র‌্য কী-গুলো দেখায় যাতে আমি যাচাই করতে পারি সবকিছু সঠিকভাবে যুক্ত হয়েছে কিনা।

সিস্টেমটাকে রক্ষণাবেক্ষণ করা সহজ রাখতে, আমি shared/src/i18n/README.md এ বিস্তারিত ডকুমেন্টেশনও যোগ করলাম, যেখানে ফাইল স্ট্রাকচার থেকে শুরু করে ক্লায়েন্ট ও সার্ভার উভয়ের জন্য ব্যবহারের উদাহরণ সবকিছু কভার করে। যেহেতু আমি স্ট্যান্ডার্ড লাইব্রেরি ব্যবহার করছি না, নতুন টিম মেম্বার অনবোর্ডিং (অথবা শুধু ভবিষ্যতের নিজেকে বা LLM-কে এটা কীভাবে কাজ করে মনে করিয়ে দেওয়ার) জন্য এই রেফারেন্স থাকা অত্যন্ত গুরুত্বপূর্ণ।

সংখ্যায় হিসেব

এই আপডেটের পরিধি বোঝাতে, কোডবেসে যা যা পরিবর্তিত হয়েছে:

  • ২০টি ভাষা সাপোর্টেড (এছাড়াও dev-এর জন্য একটা debug লোকেল)।
  • ৩৬০টি লোকেল ফাইল তৈরি।
  • ১৩৯,০৩১ লাইন অনুবাদ কোড।
  • ক্লায়েন্ট জুড়ে t() এর ৩,৯৩৮টি কল যোগ করা।
  • ৭২৮টি সোর্স ফাইল মডিফাই করা।
  • ১৮টি ইংরেজি সোর্স ফাইল যা সত্যের উৎস হিসেবে কাজ করে (১৬টি গেম + common + misc)।

এজেন্ট দিয়ে অর্কেস্ট্রেট করা

এটা ম্যানুয়ালি করতে গেলে মাসের পর মাস একঘেয়ে, যান্ত্রিক কাজের প্রয়োজন হত। এর বদলে, আমি একসাথে এক ডজনেরও বেশি Cursor এজেন্ট অর্কেস্ট্রেট করে ভারী কাজটা করিয়ে নিলাম।

আমি কোডবেসকে ফোল্ডারের ভিত্তিতে "সেকশনে" ভাগ করে শুরু করলাম। Foony-র প্রতিটা গেম পেল নিজস্ব ফোল্ডার ও নিজস্ব ট্রান্সলেশন namespace। এতে প্রাথমিক লোডের সাইজ ছোট থাকে কারণ আপনি যে গেমটা খেলছেন শুধু সেটারই অনুবাদ লোড হয়।

আমি একসাথে একাধিক Cursor এজেন্ট চালালাম। প্রতিটা এজেন্টকে একটা নির্দিষ্ট সেকশন দিলাম, যেমন "Chess গেমকে অনুবাদ ব্যবহার করতে কনভার্ট করো," এবং সেটা ফাইল ধরে ধরে ইউজার-ফেসিং স্ট্রিং খুঁজে বের করে t('games/chess/some.key') দিয়ে প্রতিস্থাপন করতে লাগল।

তারপর এজেন্ট সেই কী-টা উপযুক্ত ইংরেজি লোকেল ফাইলে যোগ করত একটা JSDoc কমেন্ট সহ যেখানে স্ট্রিংয়ের "কী" এবং "কোথায়" ব্যাখ্যা করা থাকত। অন্যান্য ভাষার জন্য অনুবাদ তৈরি করার সময় এই কনটেক্সট গুরুত্বপূর্ণ, কারণ এটা LLM-কে বুঝতে সাহায্য করে "Save" মানে "Save Game Configuration" নাকি "Save Your Draw & Guess Drawing"।

কোয়ালিটি কন্ট্রোল

আমি জেনারেট হওয়া সব কোড দ্রুত রিভিউ করলাম। এজেন্টগুলো অবাক করার মতো ভালো ছিল, তবে মাঝে মাঝে ভুল করত, যেমন একটা early return স্টেটমেন্টের পরে useTranslation হুক বসানো।

স্ট্রংলি-টাইপড অনুবাদ অসাধারণ সাহায্য করল। এটা নিশ্চিত করল যে প্রতিটা লোকেলের সব অনুবাদে সঠিক কী-গুলো আছে (এবং ভুল কোনোটা নেই)। এটা আরও নিশ্চিত করল যে t() এবং interpolate() কলগুলো বাস্তব অনুবাদ স্ট্রিং ব্যবহার করছে যেগুলো আসলেই বিদ্যমান।

টাইপ সিস্টেম ইংরেজি সোর্স ফাইল থেকে সব সম্ভাব্য অনুবাদ কী এক্সট্র্যাক্ট করে:

/**
 * Extracts all possible paths from a nested object type, creating dot-notation keys.
 * Example: {a: string, b: {c: string, d: {e: string}}} → 'a' | 'b.c' | 'b.d.e'
 */
type ExtractPaths<T, Prefix extends string = ''> = T extends string
  ? Prefix extends '' ? never : Prefix
  : T extends object
  ? {
      [K in keyof T]: K extends string | number
        ? T[K] extends string
          ? Prefix extends '' ? `${K}` : `${Prefix}.${K}`
          : ExtractPaths<T[K], Prefix extends '' ? `${K}` : `${Prefix}.${K}`>
        : never
    }[keyof T]
  : never;

export type TranslationKeys = 
  | ExtractPaths<typeof import('./locales/en/index').default>
  | `misc/${ExtractPaths<typeof import('./locales/en/misc').default>}`
  | `games/chess/${ExtractPaths<typeof import('./locales/en/games/chess').default>}`
  | `games/pool/${ExtractPaths<typeof import('./locales/en/games/pool').default>}`
  // ... and so on for all games

এটা পারফেক্ট TypeScript অটোকমপ্লিট দেয়, এবং অনুবাদ কী-তে যেকোনো টাইপো কম্পাইল টাইমেই ধরা পড়ে। এজেন্টরা t('games/ches/name') এর মতো ভুল করতে পারে না কারণ TypeScript সাথে সাথে সেটা ফ্ল্যাগ করে।

লোকালাইজেশন

ইংরেজি কনভার্সন শেষ হওয়ার পর, আমি বাকি লোকেল কাজগুলো ভাগ করে নিলাম। প্রতিটা এজেন্টকে একটা নির্দিষ্ট ভাষায় একটা একক ইংরেজি লোকেল ফাইল রূপান্তর করার দায়িত্ব দিলাম।

উদাহরণস্বরূপ, এজেন্টদের আমি এরকম একটা প্রম্পট দিলাম:

Please ensure that ar/games/dinomight.ts has all the translations from en/games/dinomight.ts.
Use `export const account: DinomightTranslations = {`.
Iterate until there are no more type errors for your translation file (if you see errors for other files, ignore them--you are running in parallel with other agents that are responsible for those other files).
Your translations must be excellent and correct for the jsdoc context provided in en.
You must do this manually and without writing "helper" scripts, and with no shortcuts.

আমি ভেবেছিলাম Cursor দিয়ে একটা স্ক্রিপ্ট তৈরি করব যেটা প্রতিটা ফাইল LLM-এ ফিড করে জিনিসগুলো জেনারেট করবে, কিন্তু LLM খরচে একটু সাশ্রয় করতে চেয়েছিলাম। শুধু মিসিং অনুবাদ আপডেট করতে স্ক্রিপ্ট ব্যবহার করা একটা উন্নত পন্থা ছিল, এবং ভবিষ্যতে আমি সম্ভবত একই ধরনের সমাধান ব্যবহার করব। কোন স্ট্রিংগুলোর আপডেট / অনুবাদ দরকার তা ট্র্যাক করতে চাই, কিন্তু জিনিসগুলো সরল রাখতে চাই। হয়তো অনুবাদের কাজটা একটা ডেটাবেস বা এরকম কিছুতে নিয়ে যাব।

আমি একটা "debug" লোকেলও যোগ করলাম যেটা শুধু ডেভেলপমেন্টে পাওয়া যায়। এটা আমাকে সব প্রতিস্থাপিত স্ট্রিং দেখতে দেয় যাতে যাচাই করতে পারি সব ঠিকঠাক কাজ করছে কিনা (এছাড়াও এটা আমার কাছে বেশ কুল লাগে)। যখন আপনি debug লোকেল ব্যবহার করেন, t() কী-টাকে ব্র্যাকেটে মুড়ে রিটার্ন করে:

if (targetLocale === 'debug') {
  return `⟦${key}⟧`;
}

তাই "Welcome to Foony!" দেখার বদলে আপনি দেখবেন ⟦welcome⟧, যা মিসিং অনুবাদ চিহ্নিত করা সহজ করে দেয়।

সবশেষে, আরেকটা এজেন্ট /{locale}/** রাউটিং বাস্তবায়ন করল যাতে /ja/games/chess এর মতো জিনিস সঠিক ভাষায় (এক্ষেত্রে জাপানিজ) রাউট হয়।

ব্লগ অনুবাদ করা

UI স্ট্রিং অনুবাদ একটা কথা, কিন্তু ব্লগ পোস্টের কী হবে? আমি আমার সব ব্লগ পোস্ট অনুবাদ করার জন্য আরও এজেন্ট চালু ও পরিচালনা করতে চাইনি।

আমি এটা সমাধান করলাম একটা এজেন্টকে দিয়ে একটা স্ক্রিপ্ট (scripts/src/generateBlogTranslations.ts) তৈরি করিয়ে যা পুরো প্রক্রিয়াটাকে স্বয়ংক্রিয় করে।

এটা যেভাবে কাজ করে:

  1. এটা ইংরেজি MDX ফাইলের জন্য client/src/posts/en ডিরেক্টরি স্ক্যান করে।
  2. এটা অন্যান্য লোকেল ফোল্ডারে (যেমন posts/ja, posts/es) মিসিং অনুবাদ চেক করে।
  3. যদি কোনো অনুবাদ মিসিং থাকে, এটা ইংরেজি কনটেন্ট পড়ে এবং Markdown ফরম্যাটিং সংরক্ষণ করে কনটেন্ট অনুবাদের জন্য একটা নির্দিষ্ট প্রম্পট সহ Gemini 3 Pro Preview তে ফিড করে।
  4. এটা নতুন ফাইলটি সঠিক জায়গায় সেভ করে।

ফ্রন্টএন্ডে, আমি import.meta.glob ব্যবহার করে এই সব MDX ফাইল ডাইনামিকভাবে ইমপোর্ট করি। আমার PostPage কম্পোনেন্ট তখন শুধু ব্যবহারকারীর বর্তমান লোকেল চেক করে এবং সঠিক MDX ফাইলটা লেজি-লোড করে। যদি একটা অনুবাদ মিসিং থাকে (কারণ আমি এখনো স্ক্রিপ্টটা চালাইনি), এটা মসৃণভাবে ইংরেজিতে ফিরে যায়।

৪র্থ দিন: স্বয়ংক্রিয় অনুবাদ জেনারেশন

আমি জানতাম মূল সমাধানটা স্কেল করবে না। তাই, এখন যেহেতু i18n বের হয়ে গেছে, এটাকে একটা ডেটাবেস-চালিত পদ্ধতি দিয়ে আরও মজবুত করার সময় এসেছে।

সংক্ষেপে: যখন ইংরেজি টেক্সট বা JSDoc কমেন্ট পরিবর্তিত হয়, অনুবাদগুলো পুনরায় তৈরি করা দরকার ছিল। কী আপডেট করতে হবে তা ম্যানুয়ালি ট্র্যাক করাটা ভুলপ্রবণ হত এবং ডেভেলপারের সময়ের অপচয় হত।

তাই আমি আগে যে সমাধান পরিকল্পনা করেছিলাম সেটাই বানালাম: একটা PostgreSQL-ভিত্তিক অনুবাদ জেনারেশন সিস্টেম।

ডেটাবেস স্কিমা

আমি আমাদের PostgreSQL ডেটাবেসে নিম্নলিখিত স্ট্রাকচারের একটা translations টেবিল যোগ করলাম:

  • key: "স্ল্যাশ-ডট" নোটেশনে অনুবাদ কী (যেমন, "games/yacht/nested.name", "config.timeLimit.label")।
  • en_value: ইংরেজি সোর্স মান
  • target_locale: টার্গেট লোকেল কোড (যেমন, "es", "fr", "zh")
  • target_value: অনুবাদিত মান
  • context: একটা JSONB ফিল্ড যাতে এই কী এবং তার সব পূর্বপুরুষ কী-এর জন্য JSDoc থাকে
  • created_at এবং updated_at: ট্র্যাকিংয়ের জন্য টাইমস্ট্যাম্প

ইউনিক ইনডেক্সটা (key, target_locale, en_value, context) এর উপর। এটা গুরুত্বপূর্ণ: ইউনিক কনস্ট্রেইন্টে context অন্তর্ভুক্ত করে আমরা স্বয়ংক্রিয়ভাবে শনাক্ত করতে পারি কখন JSDoc কমেন্ট পরিবর্তিত হয় এবং অনুবাদ পুনরায় তৈরি করতে পারি। পুরনো অনুবাদগুলো ঐতিহাসিক রেফারেন্সের জন্য রাখা হয়।

জেনারেশন স্ক্রিপ্ট

আমি scripts/src/generateLocalizations.ts তৈরি করলাম যা পুরো অনুবাদ ওয়ার্কফ্লোকে স্বয়ংক্রিয় করে:

  1. ইংরেজি কী এক্সট্র্যাক্ট করে: AST পার্সিং (ts-morph) ব্যবহার করে shared/src/i18n/locales/en/** ফাইল থেকে সব অনুবাদ কী এক্সট্র্যাক্ট করে, শুধু default exports প্রসেস করে
  2. JSDoc কনটেক্সট এক্সট্র্যাক্ট করে: প্রতিটা কী এবং সব পূর্বপুরুষ কী (প্যারেন্ট অবজেক্ট) এর জন্য JSDoc কমেন্ট পার্স করে সমৃদ্ধ কনটেক্সট প্রদান করে
  3. ডেটাবেস কোয়েরি করে: PostgreSQL-এ বিদ্যমান অনুবাদ চেক করে, key, target_locale, en_value, এবং context মিলিয়ে - এর কোনোটা পরিবর্তিত হলে অনুবাদ পুনরায় তৈরি হয়।
  4. মিসিং/পরিবর্তিত কী চিহ্নিত করে: এমন কী খুঁজে বের করে যেগুলোর অনুবাদ দরকার বা যাদের ইংরেজি মান/কমেন্ট পরিবর্তিত হয়েছে
  5. অনুবাদ ব্যাচ করে: আরও দক্ষ LLM কলের জন্য লোকেল ও namespace প্রিফিক্স অনুযায়ী গ্রুপ করে (অনুবাদকেও দ্রুত করে)। তবে ব্যাচ যদি বেশি বড় হয়, অনুবাদের গুণগত মান খারাপ হয়ে যায়।
  6. অনুবাদ তৈরি করে: ব্যাপক কনটেক্সট (JSDoc, ভাষা+অঞ্চল, টোন, glossary, উদাহরণ) সহ GPT 5.1 ব্যবহার করে। আমি পড়েছি ৫.১ লেখার জন্য ৫.২ এর চেয়ে ভালো (একঘেয়ে শোনায় না), তবে নিশ্চিত করিনি।
  7. QA চেক: প্লেসহোল্ডার সংরক্ষণ যাচাই করে, যেমন {{name}}, কী ইন্টিগ্রিটি, JSON ফরম্যাট
  8. ডেটাবেসে সংরক্ষণ করে: পূর্ণ কনটেক্সট (JSDoc + পূর্বপুরুষ JSDoc) সহ অনুবাদ সেভ করে
  9. লোকেল ফাইল তৈরি করে: ডেটাবেস থেকে পড়ে এবং RecursivePartial টাইপ সহ সঠিকভাবে ফরম্যাট করা TypeScript লোকেল ফাইল লেখে

মূল সুবিধা

এই পদ্ধতি আমাদের বেশ কয়েকটা DevEx উন্নতি দেয়:

  • স্বয়ংক্রিয় পুনঃজেনারেশন: যখন ইংরেজি টেক্সট অথবা JSDoc কমেন্ট পরিবর্তিত হয়, অনুবাদ স্বয়ংক্রিয়ভাবে পুনরায় তৈরি হয়। তাই কেউ যদি বলে একটা অনুবাদ খারাপ, তাহলে কমেন্ট হিসেবে আরও কনটেক্সট দিয়ে অনুবাদ পুনরায় তৈরি করা সত্যিই সহজ।
  • সমৃদ্ধ কনটেক্সট: JSDoc কমেন্ট অনুবাদের কনটেক্সট দেয় (যেমন, "প্লেয়ারদের দেখানো এরর মেসেজ, সর্বোচ্চ ১৫ অক্ষর"), যা LLM-কে আরও সঠিক অনুবাদ তৈরি করতে সাহায্য করে
  • পূর্বপুরুষ কনটেক্সট: প্যারেন্ট অবজেক্ট JSDoc namespace কনটেক্সট দেয় (যেমন, "এমন গেমে থাকার অর্জন যেখানে সব ডিম ধ্বংস হয়েছে"), যা একটু বেশি স্পষ্টতা দেয়
  • ঐতিহাসিক ট্র্যাকিং: পুরনো অনুবাদ ডেটাবেসে সংরক্ষিত হয়। তারা বেশি জায়গা নেয় না, তাই আপাতত ডিলিট করার বেশি কারণ দেখি না, এবং ইতিহাস দেখাটা মজার।

টেকনিক্যাল ডিটেইলস

বাস্তবায়নে নির্ভরযোগ্যতা ও দক্ষতা নিশ্চিত করতে কয়েকটা কৌশল ব্যবহার করা হয়েছে:

  • সঠিক কমেন্ট পেতে AST-ভিত্তিক এক্সট্র্যাকশন
  • সমান্তরাল ব্যাচ অনুবাদের জন্য Semaphore ব্যবহার করে প্যারালাল প্রসেসিং
  • API ব্যর্থতার জন্য এক্সপোনেনশিয়াল ব্যাকঅফ রিট্রাই লজিক। LLM কল কুখ্যাতভাবে অস্থির।

স্ক্রিপ্টটা scripts ডিরেক্টরি থেকে npm run generate-localizations দিয়ে চালানো যায়। চালানো হলে এটা PostgreSQL-এ সংযোগ করে এবং সব সাপোর্টেড লোকেলের জন্য সব মিসিং বা পরিবর্তিত অনুবাদ প্রসেস করে।

উপসংহার

এই পর্যায়ে এসে, আমার কাছে ২০টি লোকেলে অনুবাদ করা একটা সম্পূর্ণ কার্যকর সাইট ছিল!

এটা একটা পাগল করা ৩ দিন ছিল, কিন্তু ফলাফল হলো একটা সম্পূর্ণ লোকালাইজড সাইট যা বিশ্বজুড়ে ব্যবহারকারীদের কাছে (বেশিরভাগ ক্ষেত্রে) দেশীয় মনে হয়। একটা কাস্টম, হালকা লাইব্রেরি বানিয়ে এবং বিরক্তিকর রিফ্যাক্টরিং কাজের জন্য AI এজেন্ট ব্যবহার করে, আমি এমন কিছু করতে পারলাম যা মাত্র এক বছর আগে অসম্ভব ছিল: একজন ইঞ্জিনিয়ার দ্বারা একটা জটিল ওয়েবসাইটের জন্য ৩ দিনে পূর্ণ i18n। প্রোগ্রামিংয়ের ভবিষ্যৎ দ্রুত কোড লেখার ব্যাপারে নয়। এটা AI এজেন্ট অর্কেস্ট্রেট করার এবং তাদের আউটপুট যাচাই করার জন্য গভীর ডোমেইন এক্সপার্টিজ ধারণ করার ব্যাপার।

8 Ball Pool online multiplayer billiards icon