هنوز در قرن بیست و یکم چقدر مانع زبانی وجود دارد؟ شما به عنوان خواننده احتمالاً با زبان انگلیسی آشنایی کامل دارید، اما دیگران چطور؟

امروزه اغلب ما اهمیت دسترسی، عملکرد بهتر و UX یا DX بهتر را شنیده ایم. ممکن است درباره i18n در مقایسه با این موضوعات نشنیده باشید یا اغلب آن را نبینید. اما اگر حقایق و اعداد را از آمار مشاهده کنید، ممکن است نتایج شگفت انگیزی در مورد i18n و تأثیر آن پیدا کنید. بیایید با هم در مورد آن پی ببریم.

i18n و l10n

قبل از بررسی تاثیر i18n، بیایید تفاوت بین این دو اصطلاح را بیاموزیم.

  • i18n
    i18n مخفف بین المللی سازی است. بین اولین کاراکتر “i” و آخرین کاراکتر “n” از این کلمه، 18 کاراکتر وجود دارد. i18n پیاده‌سازی ساختارها و ویژگی‌های برنامه‌های شما را توضیح می‌دهد تا آماده بومی‌سازی محتوا باشند.
  • l10n
    l10n مخفف محلی سازی است. بین اولین کاراکتر “l” و آخرین کاراکتر “n” از این کلمه، ده کاراکتر وجود دارد. l10n به معنای ترجمه محتوا به زبان برای کاربرانی است که از مناطق خاصی به آن دسترسی دارند.

در ادامه، i18n شامل یک فرآیند برنامه‌ریزی برای پیاده‌سازی ویژگی‌هایی برای ویرایشگران محتوا و مترجمان است تا بتوانند فرآیند l10n را از رابط کاربری شروع کنند.

چرا i18n اینقدر اهمیت دارد؟

برای درک اهمیت i18n، بیایید به اعداد و آمار برای اطلاعات عینی نگاه کنیم. اعداد برخی از حقایق را در زیر خواهید دید، و قبل از خواندن ادامه مطلب، بیایید حدس بزنیم که این اعداد به چه معنا هستند.

  • 5.07 میلیارد
  • 25.9٪
  • 74.1٪
  • چین
  • آسیا

اولین واقعیت تعداد زیادی از اعداد را نشان می دهد. 5.07 میلیارد تعداد کاربران این جهان در سال 2020 است. جمعیت جهان در سال 2021 7.837 میلیارد نفر بوده است، نزدیک به 8 میلیارد. بیش از نیمی از جمعیت جهان به محتوای اینترنت و اپلیکیشن ها دسترسی دارند.

بر اساس تعداد کاربران این دنیا، تحقیق دیگری در مورد رایج ترین زبان های مورد استفاده در اینترنت وجود دارد. با نگاهی به نمودار، اکثر ما به بالاترین عدد در این نمودار توجه می کنیم: 25.9٪، انگلیسی.

رایج ترین زبان های مورد استفاده در اینترنت از ژانویه 2020، بر اساس سهم کاربران اینترنت
(منبع تصویر: Statista) (پیش نمایش بزرگ)

دومین زبان بقیه با 23.1 درصد بالاترین. همچنین فرض کنید بقیه درصد را به جز انگلیسی جمع آوری کنید. در این صورت، ممکن است متوجه شوید که از بیش از 5 میلیارد کاربر، 74.1٪ از کاربران به محتوا به هر زبان دیگری دسترسی دارند.

پس از بررسی این حقایق، اکنون می‌توانیم در مورد اینکه چرا بین‌المللی کردن و بومی‌سازی محتوای شما برای آسیا و چین بسیار مهم است صحبت کنیم. چین بیشترین کاربران اینترنت را در سراسر جهان دارد. در نتیجه، بیش از نیمی از کل کاربران اینترنت در سراسر جهان از آسیا هستند.

بر اساس آنچه دیدیم، احتمالاً نمی‌توانیم محتوای بومی‌سازی را نادیده بگیریم. اگر تعداد زیادی از کاربران در سراسر جهان بتوانند محتوای بومی سازی شده داشته باشند، UX را بهبود می بخشد. پس از دانستن تأثیر بالقوه i18n مناسب، بیایید به منطق اساسی نگاه کنیم.

چگونه i18n در یک سطح پایه کار می کند

صرف نظر از فناوری پیاده سازی i18n، سه راه برای تعیین زبان ها و مناطق وجود دارد.

  1. محل آدرس IP
  2. پذیرش-زبان سرتیتر/Navigator.languages
  3. شناسه ها در URL ها

استفاده از آدرس IP، منطقه کاربران را شناسایی می کند و به آنها امکان می دهد به زبان های منطقه ای خود به محتوا دسترسی داشته باشند. با این حال، آدرس IP کاربران لزوماً با ترجیح زبان آنها مطابقت ندارد. علاوه بر این، تجزیه و تحلیل مکان از خزیدن سایت توسط موتورهای جستجو جلوگیری می کند.

استفاده كردن پذیرش-زبان سرصفحه یا Navigator.languages یکی دیگر از روش های ممکن برای پیاده سازی i18n است. با این حال، این رویکرد اطلاعات زبان را ارائه می دهد اما اطلاعات منطقه ای را ارائه نمی دهد.

i18n فقط برای بومی سازی محتوا نیست. این شامل بهبود UX نیز می شود. به عنوان مثال، ایجاد شناسه در URL ها UX را افزایش می دهد. همچنین به تقسیم محتوای محلی به سیستم اختصاصی کمک می کند. ما چگونگی پیاده سازی چنین سیستمی را در ” توضیح خواهیم داد.ترکیبی از ریمیکس و CMS” بخش.

به طور معمول، شناسه ها در URL ها در سه الگوی مختلف وجود دارند:

  1. تمایز بر اساس دامنه (به عنوان مثال hello.es، hello.jp)
  2. پارامترهای URL (به عنوان مثال hello.com?loc=de)
  3. دایرکتوری های فرعی محلی شده (به عنوان مثال hello.com/es، hello.com/ja)

برای پیروی از سیاست همان مبدأ برای سئوی بهتر، می توان از زیرمجموعه های محلی استفاده کرد.

بر اساس حقایق جالب و منطق اساسی برای پیاده سازی i18n، ما در مورد چارچوب ها و کتابخانه ها صحبت خواهیم کرد، زیرا برخی از آنها از کتابخانه های i18n استفاده می کنند.

کتابخانه ها

برای اینکه هر زمان که می‌خواهیم i18n را در پروژه‌های خود پیاده‌سازی کنیم، چرخ را دوباره اختراع نکنیم، توسعه‌دهندگان کتابخانه‌ها، ابزارها و سرویس‌های مختلفی دارند که می‌توان از آنها برای تسهیل کار استفاده کرد. اگر با چارچوب‌های React یا React کار می‌کنیم، گزینه‌های مختلفی در دسترس داریم. بیایید در مورد برخی از آنها صحبت کنیم.

Format.js

Format.js مجموعه‌ای مدولار از کتابخانه‌های جاوا اسکریپت است که می‌توانیم از آن برای پیاده‌سازی منطق i18n در کلاینت و سرور استفاده کنیم. این گروه از کتابخانه ها بر قالب بندی اعداد، تاریخ ها و رشته ها متمرکز هستند. این قابلیت ها و ابزارهای مختلفی را ارائه می دهد و در مرورگر و همچنین در زمان اجرا Node.js اجرا می شود. با فریمورک های مختلفی مانند Vue و React ادغام می شود تا بتوانیم از قابلیت های آن در پروژه های Remix خود استفاده کنیم. می توانید در مورد آن بیشتر بخوانید اسناد رسمی React Intl.

i18 بعدی

جایگزین دیگری که می توانیم برای پروژه خود ارزیابی کنیم این است i18 بعدی. این کتابخانه جاوا اسکریپت فراتر از ویژگی های استاندارد i18n است و مجموعه کاملی را برای مدیریت i18n در پروژه های ما ارائه می دهد. ما می توانیم زبان کاربران را شناسایی کنیم، ترجمه های حافظه پنهان را ذخیره کنیم و حتی افزونه ها و افزونه ها را نصب کنیم. همانطور که در جاوا اسکریپت ساخته شده است، می توانیم از این ابزار برای وب سایت ها و همچنین برای برنامه های موبایل و دسکتاپ استفاده کنیم.

در مورد ریمیکس چطور؟

هنگام ایجاد یک وب سایت با استفاده از Remix، ما باید گزینه های مختلفی را در نظر بگیریم. از آنجایی که این یک چارچوب مبتنی بر React است، می‌توانیم از هر یک از کتابخانه‌های ذکر شده قبلی استفاده کنیم. با این حال، ما دو رویکرد را بررسی خواهیم کرد که می توانند در پروژه های Remix شما بهتر جا بیفتند. ابتدا، نحوه بومی سازی محتوا را با استفاده از remix-i18next، یک کتابخانه خاص Remix برای i18n خواهیم دید. دوم، ما از یک سیستم مدیریت محتوای بدون سر به عنوان منبع زبان/محل های مختلف محتوای خود استفاده خواهیم کرد.

remix-i18next

بر اساس i18next، سرجیو زالامبری، یکی از مشارکت کنندگان اصلی ریمیکس، ایجاد شد remix-i18next. این کتابخانه ویژگی ها و ماژول های مشابه کتابخانه جاوا اسکریپت را ارائه می دهد اما بر مفاهیم و رویکردهای Remix تمرکز دارد. راه اندازی و استفاده آسان، آماده تولید، و بدون هیچ نیاز یا وابستگی. بیایید نگاهی دقیق‌تر به نحوه پیاده‌سازی i18n در پروژه‌های Remix با استفاده از remix-i18next داشته باشیم.

اول از همه، باید چند بسته npm را نصب کنیم:

npm install remix-i18next i18next react-i18next i18next-browser-languagedetector i18next-http-backend i18next-fs-backend

همه آنها به ما کمک می کنند i18n را هم در سمت سرور و هم در سمت مشتری وب سایت خود مدیریت کنیم. ما همچنین از آنها برای راه اندازی backend خود و تعریف منطقی که زبان کاربر را تشخیص می دهد استفاده خواهیم کرد.

اکنون، باید تنظیماتی را اضافه کنیم که در سرتاسر وب سایت هم از سمت کلاینت و هم از سمت سرور استفاده می شود. بیایید چند فایل JSON با ترجمه رشته های کاراکترهای مختلف که در وب سایت خود استفاده خواهیم کرد ایجاد کنیم:

{
  "intro": "Hello everyone!"
}
{
  "intro": "Hola a todos!"
}

با نام گذاری فایل ها به “common.json”، فضای نام رشته ها را تعریف می کنیم که ما در آنها لیست خواهیم کرد.

حالا بیایید یک فایل به نام بسازیم i18n.js. این فایل حاوی تنظیمات پیکربندی مختلفی است که ما در لحظه شروع به کار سرور i18n خود از آنها استفاده خواهیم کرد.

export default {
  supportedLngs: ["en", "es"],
  fallbackLng: "en",
  defaultNS: "common",
  // Disabling suspense is recommended
  react: { useSuspense: false },
};

می توانید گزینه های پیکربندی بیشتری را در قسمت مشاهده کنید اسناد رسمی i18next.

حال، فایل را ایجاد کنید i18next.server.js، که حاوی منطقی است که در آن استفاده خواهد شد entry.server.jsx فایل پروژه ریمیکس ما

import Backend from "i18next-fs-backend";
    import { resolve } from "node:path";
    import { RemixI18Next } from "remix-i18next";
    import i18n from "~/i18n"; // The configuration file we created
    
    let i18next = new RemixI18Next({
      detection: {
        supportedLanguages: i18n.supportedLngs,
        fallbackLanguage: i18n.fallbackLng,
      },
      i18next: {
        ...i18n,
        backend: {
          loadPath: resolve('./public/locales/{{lng}}/{{ns}}.json'),
        },
      },
      backend: Backend,
    });
    
    export default i18next;

ما اساساً در حال راه اندازی یک سرور جدید i18n هستیم که با باطن Remix ما اجرا می شود. ما محل فایل‌های JSON حاوی ترجمه‌های مورد استفاده را مشخص می‌کنیم. بیایید این ویژگی ها را به فایل های پیکربندی Remix اصلی خود اضافه کنیم. ابتدا مقداری منطق اضافه می کنیم تا بتوانیم سمت مشتری محتوا را ترجمه کنیم. برای انجام این کار، اجازه دهید فایل «entry.client.jsx» خود را ویرایش کنیم:

import i18next from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import Backend from "i18next-http-backend";
import { I18nextProvider, initReactI18next } from "react-i18next";
import { getInitialNamespaces } from "remix-i18next";
import i18n from "./i18n"; // The configuration file we created

i18next
  .use(initReactI18next)
  .use(LanguageDetector)
  .use(Backend)
  .init({
    ...i18n, // The same config we created for the server
    ns: getInitialNamespaces(),
    backend: {
      loadPath: "/locales/{{lng}}/{{ns}}.json",
    },
    detection: {
      order: ["htmlTag"],
      caches: [],
    },
  })
  .then(() => {
    // After i18next init, hydrate the app
    hydrateRoot(
      document,
      // Wrap RemixBrowser in I18nextProvider
      <I18nextProvider i18n={i18next}>
        <RemixBrowser />
      </I18nextProvider>
    );
  });

ما باید منتظر بمانیم تا مطمئن شویم ترجمه‌ها قبل از هیدراتاسیون بارگیری می‌شوند تا برنامه وب خود را تعاملی نگه داریم.

بیایید منطق را به آن اضافه کنیم entry.server.jsx اکنون فایل کنید:

import { createInstance } from "i18next";
import Backend from "i18next-fs-backend";
import { resolve } from "node:path";
import { I18nextProvider, initReactI18next } from "react-i18next";
import i18next from "./i18next.server"; // The backend file we created
import i18n from "./i18n"; // The configuration file we created

...

export default async function handleRequest(
...
) {
  // We create a new instance of i18next
  let instance = createInstance();

  // We can detect the specific locale from each request
  let lng = await i18next.getLocale(request);
  // The namespaces the routes about to render wants to use
  let ns = i18next.getRouteNamespaces(remixContext);

  await instance
    .use(initReactI18next)
    .use(Backend)
    .init({
      ...i18n,// The config we created
      lng, // The locale we detected from the request
      ns,
      backend: {
        loadPath: resolve("./public/locales/{{lng}}/{{ns}}.json"),
      },
    });

  return new Promise((resolve, reject) => {
    ...

    let { pipe, abort } = renderToPipeableStream(
      
        {" "}
      ,
      ...
    );
    ...
  });
}

شناسایی زبان مورد علاقه کاربران به ما امکان می دهد، از جمله موارد دیگر، آنها را به مسیرهای خاصی هدایت کنیم.

اکنون می‌توانیم با استفاده از قابلیت‌های ارائه‌شده توسط remix-i18next برای شناسایی موقعیت کاربر و ارائه محتوای ترجمه‌شده بر اساس آن، شروع کنیم. بیایید ویرایش کنیم root.jsx فایل:

...

import { json } from "@remix-run/node";
import { useChangeLanguage } from "remix-i18next";
import { useTranslation } from "react-i18next";
import i18next from "~/i18next.server";

...

export let loader = async ({ request }) => {
  let locale = await i18next.getLocale(request);
  return json({ locale });
};

export let handle = {
  i18n: "common",
};

export default function App() {
  // Get the locale from the loader
  let { locale } = useLoaderData();
  let { i18n } = useTranslation();

  useChangeLanguage(locale);

  return (
    <html lang={locale} dir={i18n.dir()}>
      ...
    </html>
  );
}

را useChangeLanguage hook زبان نمونه را به محلی شناسایی شده توسط لودر تغییر می دهد. هر زمان که کاری برای تغییر زبان انجام دهیم، این زبان تغییر می‌کند و i18next ترجمه‌های صحیح را بارگیری می‌کند.

اکنون، ما می توانیم محتوا را در هر مسیری ترجمه کنیم:

import { useTranslation } from "react-i18next";

export default function MyPage() {
  let { t } = useTranslation();
  return <h1>{t("intro")}</h1>;
}

ما استفاده می کنیم t() تابعی برای نمایش رشته های ترجمه شده بر اساس لیست پیام هایی که در فایل های JSON خود تعریف کرده ایم.

در این مثال، ما از یک فضای نام پیش‌فرض استفاده می‌کنیم، اما اگر بخواهیم می‌توانیم چندین فضای نام را تنظیم کنیم. می توانید در مورد آن بیشتر بخوانید t() عملکرد در اسناد رسمی i18next.

در صورتی که بخواهیم سمت سرور محتوا را ترجمه کنیم، می توانیم از آن استفاده کنیم getFixedT روش داخل لودرها و اقدامات ما:

import i18next from "~/i18next.server";

...

export let loader = async ({ request }) => {
  let t = await i18next.getFixedT(request);
  let title = t("intro");
  return json({ title });
};

ترکیبی از ریمیکس و CMS

با هم، گزینه های موجود برای پیاده سازی i18n با Remix را بررسی کردیم. در ابتدای این مقاله، متوجه شدیم که i18n می‌تواند منجر به بهبود بسیار زیاد UX و SEOUX و SEO شود. به عنوان بخشی از UX، گنجاندن DX بهتر نیز مهم است.

رویکرد بالا فایل های ترجمه را در سطح کد منبع ایجاد می کند. همچنین، منطق پیاده سازی شناسه ها در URL ها را نداریم. برای رسیدن به این هدف، بیایید به رویکرد یکپارچه سازی CMS نگاه کنیم. در این مقاله، از Storyblok استفاده خواهیم کرد، که سه رویکرد متفاوت برای بومی‌سازی محتوا و دسته‌ها برای تعیین زبان‌ها و مناطق ارائه می‌دهد.

توجه داشته باشید: اگر می خواهید بین برنامه Remix خود و Storyblok ارتباط برقرار کنید، وجود دارد یک آموزش 5 دقیقه ای که نحوه انجام آن را توضیح می دهد.

پس از آن، با استفاده از این لینک جادویی می توانید به سرعت یک فضای شروع را کلون کنید تا تمام اجزا و انواع فیلدهای لازم را داشته باشید. این فضای مثال رویکردی به نام ترجمه در سطح پوشه را پوشش می دهد. در بخش بعدی به آن خواهیم پرداخت.
https://app.storyblok.com/#!/build/181387

بین سه رویکرد انتخاب کنید

Storyblok سه راه برای ایجاد طرح بندی برای ذخیره محتوای محلی و تعیین زبان ها و مناطق دارد.

  1. ترجمه در سطح پوشه: محتوای محلی شده را در سطح پوشه تقسیم کنید.
  2. ترجمه در سطح زمینه: ترجمه در سطح نوع زمینه.
  3. ترجمه در سطح فضا: فضاها (محیط ها یا مخازن) را به محتوای محلی خاص اختصاص دهید.

برای پوشاندن شناسه ها در URL ها، ترجمه در سطح پوشه کاملاً کار می کند، زیرا هر پوشه فقط حاوی محتوای محلی مرتبط است.

ترجمه سطح پوشه
(پیش نمایش بزرگ)

همچنین، شناسه ها را می توان از تنظیمات پوشه از طریق Slug تغییر داد.

تنظیمات پوشه
(پیش نمایش بزرگ)

با تغییر Slug از صفحه تنظیمات پوشه، این شناسه محلی سازی شده در URL در تمام داستان های داخل این پوشه ژاپنی ظاهر می شود. به عنوان مثال، صفحه about در داخل پوشه ژاپنی قبلاً یک شناسه محلی در URL دارد.

تیزر عمومی صفحه Storyblok
(پیش نمایش بزرگ)

برای تولید صفحات محتوا به صورت برنامه نویسی، Remix دارای ویژگی به نام است اسپلتس، گرفتن همه راب ها بدون توجه به سطوح تو در تو. نام گذاری یک فایل $.jsx تابع اساسی catch-all slug را فعال می کند.

app
├── root.jsx
└── routes
    ├── files
    │   └── $.jsx
    └── files.jsx

تفاوت میان بخش های پویا از Remix این است که splats هنوز هم در بعدی مطابقت دارد /. بنابراین، splats همه چیز را در مسیر ضبط می کند. اگر مسیر URL است hello.com/ja/about/something، مسیر splat دارای یک پارامتر ویژه برای گرفتن بخش های انتهایی URL است.

export async function loader({ params }) {
  params["*"]; // "ja/about/something"
}

با استفاده از پارامتر ویژه مسیر splat، اجازه دهید ویرایش کنیم $.jsx فایل.

export default function Page() {
 // useLoaderData returns JSON parsed data from loader func
 let story = useLoaderData();
 story = useStoryblokState(story, {
   resolveRelations: ["featured-posts.posts", "selected-posts.posts"]
 });
 return <StoryblokComponent blok={story.content} />
};
// loader is Backend API & Wired up through useLoaderData
export const loader = async ({ params, preview = false }) => {
 let slug = params["*"] ?? "home";
 slug = slug.endsWith("/") ? slug.slice(0, -1) : slug;
 let sbParams = {
   version: "draft",
   resolve_relations: ["featured-posts.posts", "selected-posts.posts"],
 };
 // …
 let { data } = await getStoryblokApi().get(`cdn/stories/${slug}`,
 sbParams);
 return json(data?.story, preview);
};

اشاره: در بخش “انتخاب بین 3 رویکرد”، ما هر سه رویکرد را پوشش ندادیم، اما اگر می‌خواهید بیشتر بدانید، همه رویکردها در زیر مستند شده‌اند.

خلاصه

ما با هم حقایق و آمار را یاد گرفتیم تا تأثیرات و اهمیت i18n را بدانیم و دیدیم که چگونه Remix چندین گزینه را برای پیاده سازی i18n پیشرفته مدیریت می کند. جالب است که تجربه بهتر i18n SEO و UX بهتری را ارائه می دهد. امیدواریم این مقاله دانش جدید و آموخته های روشنگری را در اختیار شما قرار داده باشد.

سرمقاله Smashing
(vf, il)