GN Gerege Nexus
GitHub Нэвтрэх

Орчуулгын гарын авлага · Translation guide

Gerege Nexus-ийн хэрэглэгчид харагдах бүх текст frontend/lib/i18n/ дотор амьдардаг. Бүтэц нь Odoo-гийн орчуулгын загварыг дагасан: addon бүр өөрийн нэр томьёог эзэмшиж, нийтлэг нэр томьёо base дотор нэг л удаа тодорхойлогдоно.


0. Хэлний бодлого

Монгол хэл + НҮБ-ын албан ёсны 6 хэл — араб (ar), хятад (zh), англи (en), франц (fr), орос (ru), испани (es). Нийт 7 хэл. Монгол хэл нь эх сурвалж, англи нь бусад бүх хэлний fallback.

Жагсаалт дур зоргоор биш: "яагаад яг эдгээр хэл вэ?" гэсэн асуултад хэн асуусанаас үл хамаарах хариулт байх ёстой. Шинэ хэл нэмэх нь энэ бодлогыг өөрчлөх шийдвэр — тохиолдлын хүсэлт биш.

Баримт бичиг долоон хэл дээр бүрэн байдаг. Програм хангамж нь монгол, англи хоёрыг л анхнаасаа санал болгоно; үлдсэн тавыг Тохиргоо → Харагдац дотроос төхөөрөмж тус бүрээр асаана. Монгол, англи хоёрыг унтраах боломжгүй — яг үүнээс болж "бүх хэл унтарсан" төлөв үүсэхгүй.

Яагаад бүгдийг нь анхнаасаа асаадаггүй вэ: толь бичиг mn/en дээр бичигдэж, бусад хэл аажмаар дүүрдэг. t() нь түлхүүр тус бүрээр англи руу шилждэг тул хагас орчуулагдсан хэл нь түлхүүр биш, англи үг харуулна — өөрөө сонгосон хүнд боломжийн, бүх хүнд анхдагчаар өгөхөд тохиромжгүй.

1. Файлын бүтэц

frontend/lib/i18n/
  index.tsx          I18nProvider, useI18n, t(), хэлний бүртгэл
  base.ts            Бүх дэлгэцийн хуваалцдаг нэр томьёо  (Odoo "base")
  web.ts             Клиентийн бүрхүүл: цэс, толгой, хайлт (Odoo "web")
  addons/
    access.ts  ai.ts  app_store.ts  appearance.ts  auth.ts
    billing.ts contacts.ts developer.ts documents.ts esign.ts
    gov.ts     integrations.ts inventory.ts products.ts website.ts

App бүр өөрийн файлтай. Нэг л дэлгэц харуулдаг текст тухайн addon-д, хоёроос дээш апп харуулдаг текст base эсвэл web-д очно.

2. Түлхүүрийн бүтэц

<module>.<kind>.<term>

kind нь Odoo-гийн нэр томьёоны ангиллыг дагана:

kind Юу вэ Жишээ
field Өгөгдлийн талбарын шошго base.field.status, gov.field.sla_hours
action Товч, үйлдэл base.action.save, gov.action.delegate
menu Навигацийн бичлэг web.menu.app_store, gov.menu.appointments
state Сонголтын утга (selection value) gov.state.awaiting_verification
view Дэлгэцийн гарчиг, тайлбар, placeholder gov.view.title, access.view.subtitle
message Хэрэглэгчид хэлэх зүйл: алдаа, мэдэгдэл, хоосон төлөв gov.message.no_child_unit

Мөн addon-д хамаарах тусгай ангилал байж болно: gov.stat.* (dashboard тоолуур), documents.category.*, integrations.type.*, appearance.mode.*.

Term нь техникийн нэр — snake_case, англи хэл дээр, өгүүлбэр биш: gov.message.no_child_unit ✓, gov.noChildUnitAvailable ✗.

3. Дүрмүүд

Хоёр хэл заавал. Dictionary нь typed тул mn эсвэл en дутвал TypeScript compile алдаа өгнө. npx tsc --noEmit энэ шалгалтыг гүйцэтгэнэ.

Давхардуулж болохгүй. Нэг нэр томьёог хоёр модульд бүү тодорхойл. "Төлөв" бол base.field.statuscontacts.status, products.status гэж дахин үүсгэхгүй. Ижил үг өөр утгатай бол (жишээ нь base.action.close = харилцах цонх хаах, gov.action.close = хүсэлтийг хаах) тусад нь байх нь зөв.

Ашиглагдахгүй түлхүүр байхгүй. Дэлгэц харуулдаггүй текст dictionary-д байхгүй. Хэрэгтэй болох үед нь нэмнэ.

Дэлгэц дээр текст hardcode хийхгүй. locale === "en" ? "Save" : "Хадгалах" гэж бичихгүй — t("base.action.save"). Ингэснээр хэл солиход бүх дэлгэц зэрэг солигдоно.

Fallback нь англи. Түлхүүр олдоогүй бол t() англи эх текстийг буцаана (gettext-ийн адил), түлхүүрийн нэрийг биш.

4. Хувьсагч дамжуулах

t("base.message.page_of", { page: 2, total: 7 })   // "2 / 7 хуудас"
t("access.message.confirm_delete", { name: role.name })

Текст дотор {name} хэлбэрээр бичнэ.

5. Динамик түлхүүр

API-аас ирсэн утгаар түлхүүр угсрахдаа техникийн хэлбэрт нь буулгана:

// API "AWAITING_VERIFICATION" илгээдэг, dictionary-д lower snake_case байдаг
t(`gov.state.${status.toLowerCase()}` as never);

Ийм тохиолдолд as never шаардлагатай — TypeScript template literal-ыг түлхүүр гэж таних боломжгүй. Тиймээс энэ хэв маягийг зөвхөн бүх утга нь dictionary-д баттай байгаа үед хэрэглэнэ (gov.state.*, gov.action.*, gov.menu.*).

6. Шинэ апп нэмэх үед

  1. frontend/lib/i18n/addons/<app>.ts файл үүсгэнэ.
  2. export const <app> = { ... } as const; гэж бичнэ.
  3. index.tsx-д import хийж dictionary дотор spread хийнэ.
  4. npx tsc --noEmit ажиллуулна.

7. Сервер талын текст

Дараах зүйлс dictionary-д байхгүй — эдгээрийг сервер орчуулна:

Одоогийн цоорхой: зөвшөөрлийн нэр, тайлбар (internal.PermissionDefinition) зөвхөн англиар зарлагддаг тул /settings/access дэлгэц дээр монгол горимд англиар харагдана. Засах бол PermissionDefinitionLabels талбар нэмж, апп бүрийн Permissions()-д монгол нэр бичих хэрэгтэй — MenuDefinition яг ийм хэв маягтай.

8. Gemini AI-гаар хэл нэмэх

Платформ Gemini-г AI туслах болон орчуулгын самбарт аль хэдийн ашигладаг. Мөн интерфэйсийн текстийг өөрийг нь орчуулахад — гэхдээ хүсэлт бүрд биш, урьдчилан нэг удаа — ашиглана.

cd frontend
npm run i18n:translate -- --locale fr             # юу өөрчлөгдөхийг харуулна
npm run i18n:translate -- --locale fr --write     # lib/i18n/locales/fr.ts бичнэ
npm run i18n:translate -- --locale fr --limit 40  # багахан хэсгээр туршина

GEMINI_API_KEY (мөн сонголтоор GEMINI_MODEL, GEMINI_API_BASE) орчны хувьсагч шаардлагатай — сервертэйгээ ижил түлхүүр.

Яагаад build-time вэ

Ажиллах үед орчуулбал дэлгэц бүр дээр саатал, төлбөр гарах ба орох бүрд өөр өөр үг гарна. Интерфэйсийн текст ховор өөрчлөгддөг, харин тогтвортой байх ёстой. Тиймээс нэг удаа үүсгээд, хянаад, commit хийнэ.

Гурван дүрэм

  1. Дарж бичихгүй. Зөвхөн overlay дотор байхгүй түлхүүрийг илгээнэ. Хүний гараар засварласан үг үүрд хэвээр үлдэнэ, дахин ажиллуулахад аюулгүй.
  2. Placeholder-ыг шалгана. {name}, {count} зэрэг хувьсагч алга болсон эсвэл нэр нь өөрчлөгдсөн орчуулгыг хаяна — тэр нь хэрэглэгчид хаалт болж харагдана.
  3. Техникийн нэр томьёог хөндөхгүй. tenant, RBAC, OAuth2, eID, SKU, e-Barimt, Gerege Nexus — эдгээр нь бүтээгдэхүүний үг сан.

Overlay гэж юу вэ

Үүсгэсэн орчуулга frontend/lib/i18n/locales/<code>.ts дотор, хэл тус бүрд нэг файл болж хадгалагдана. mn/en толь бичгээс тусад нь:

t()-ийн хайлтын дараалал: overlay → тухайн түлхүүрийн locale → англи.

Хамгийн чухал нь

Гарсан үр дүн бол ноорог. Машин орчуулга нь хянахад тохирох хэмжээний сайн, харин уншилгүй нийтлэхэд тохиромжгүй хэмжээний алдаатай байдаг. Overlay файл бүрийн толгойд ч мөн үүнийг бичсэн байгаа. PR-д оруулахын өмнө тухайн хэлээр уншиж чаддаг хүн хянана.