mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
234 lines
13 KiB
Markdown
234 lines
13 KiB
Markdown
---
|
|||
|
|
description: "21 لغة مدعومة وكيفية إنشاء الترجمات أو تحسينها لـ SnapOtter باستخدام نظام i18n المدعوم بـ TypeScript."
|
||
|
|
i18n_source_hash: 55837d9fdaef
|
||
|
|
i18n_provenance: human
|
||
|
|
i18n_output_hash: a9364069dea0
|
||
|
|
---
|
||
|
|
|
||
|
|
# دليل الترجمة {#translation-guide}
|
||
|
|
|
||
|
|
يأتي SnapOtter مزوّدًا بـ 21 لغة جاهزة للاستخدام. يستخدم نظام i18n وقت تشغيل مخصصًا خفيف الوزن مع اكتمال اللغات المفروض عبر TypeScript وتقسيم الشيفرة الديناميكي.
|
||
|
|
|
||
|
|
## اللغات المدعومة {#supported-languages}
|
||
|
|
|
||
|
|
| الرمز | اللغة | Native Name | Direction |
|
||
|
|
|------|----------|-------------|-----------|
|
||
|
|
| `en` | الإنجليزية | English | LTR |
|
||
|
|
| `zh-CN` | الصينية (المبسطة) | 简体中文 | LTR |
|
||
|
|
| `zh-TW` | الصينية (التقليدية) | 繁體中文 | LTR |
|
||
|
|
| `ja` | اليابانية | 日本語 | LTR |
|
||
|
|
| `ko` | الكورية | 한국어 | LTR |
|
||
|
|
| `es` | الإسبانية | Español | LTR |
|
||
|
|
| `fr` | الفرنسية | Français | LTR |
|
||
|
|
| `it` | الإيطالية | Italiano | LTR |
|
||
|
|
| `pt-BR` | البرتغالية (البرازيل) | Português (Brasil) | LTR |
|
||
|
|
| `de` | الألمانية | Deutsch | LTR |
|
||
|
|
| `nl` | الهولندية | Nederlands | LTR |
|
||
|
|
| `sv` | السويدية | Svenska | LTR |
|
||
|
|
| `ru` | الروسية | Русский | LTR |
|
||
|
|
| `pl` | البولندية | Polski | LTR |
|
||
|
|
| `uk` | الأوكرانية | Українська | LTR |
|
||
|
|
| `ar` | العربية | العربية | RTL |
|
||
|
|
| `tr` | التركية | Türkçe | LTR |
|
||
|
|
| `hi` | الهندية | हिन्दी | LTR |
|
||
|
|
| `vi` | الفيتنامية | Tiếng Việt | LTR |
|
||
|
|
| `id` | الإندونيسية | Bahasa Indonesia | LTR |
|
||
|
|
| `th` | التايلاندية | ไทย | LTR |
|
||
|
|
|
||
|
|
## كيف يعمل اكتشاف اللغة {#how-language-detection-works}
|
||
|
|
|
||
|
|
يستخدم SnapOtter ترتيب حلٍّ من ثلاث طبقات:
|
||
|
|
|
||
|
|
1. **تفضيل المستخدم** - مخزَّن في `localStorage("snapotter-locale")` ومتزامن مع إعدادات المستخدم عند تسجيل الدخول
|
||
|
|
2. **الاكتشاف التلقائي للمتصفح** - يمرّ عبر مصفوفة `navigator.languages` مع مطابقة بادئة BCP 47
|
||
|
|
3. **الافتراضي للنسخة** - متغيّر البيئة `DEFAULT_LOCALE` الخاص بالمشرف (يُجلَب من `GET /api/v1/config/locale`)
|
||
|
|
4. **الرجوع إلى الإنجليزية** - متاح دائمًا
|
||
|
|
|
||
|
|
يمكن للمستخدمين تغيير اللغة من:
|
||
|
|
- **محدِّد الكرة الأرضية في التذييل** (سطح المكتب، ظاهر دائمًا)
|
||
|
|
- محدِّد اللغة في **صفحة تسجيل الدخول** (قبل المصادقة)
|
||
|
|
- قسم **الإعدادات > عام** (تفضيل لكل مستخدم)
|
||
|
|
- قائمة اللغة المنسدلة في **الشريط الجانبي للجوال**
|
||
|
|
- قسم **الإعدادات > النظام** يحدّد الافتراضي على مستوى النسخة (للمشرف فقط)
|
||
|
|
|
||
|
|
## كيف تعمل الترجمات {#how-translations-work}
|
||
|
|
|
||
|
|
توجد جميع سلاسل واجهة المستخدم في `packages/shared/src/i18n/`. الملف المرجعي هو `en.ts`، الذي يصدّر كائنًا مُنمَّطًا يحتوي على كل سلسلة يستخدمها التطبيق (نحو 1500 مفتاح). اللغات الأخرى هي ملفات منفصلة (مثل `de.ts` و `fr.ts`) تصدّر البنية نفسها.
|
||
|
|
|
||
|
|
يستخدم النوع `TranslationKeys` الأداة `DeepStringRecord` لقبول أي قيمة سلسلة مع فرض بنية المفاتيح. يلتقط TypeScript المفاتيح المفقودة في أي ملف ترجمة في وقت التصريف.
|
||
|
|
|
||
|
|
يُحمَّل فقط الإعداد اللغوي النشط في وقت التشغيل عبر `import()` الديناميكي، مما يُبقي الحزمة الرئيسية صغيرة.
|
||
|
|
|
||
|
|
## استخدام الترجمات في المكوّنات {#using-translations-in-components}
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import { useTranslation } from "@/contexts/i18n-context";
|
||
|
|
import { format, plural } from "@/lib/format";
|
||
|
|
|
||
|
|
function MyComponent() {
|
||
|
|
const { t, locale, setLocale } = useTranslation();
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div>
|
||
|
|
<h1>{t.common.settings}</h1>
|
||
|
|
<p>{format(t.settings.people.deleteConfirm, { username: "admin" })}</p>
|
||
|
|
<p>{plural(count, t.automate.fileCount, t.automate.fileCountPlural)}</p>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## المساهمة بترجمة {#contributing-a-translation}
|
||
|
|
|
||
|
|
نرحّب بطلبات سحب الترجمة مباشرة. يمكنك تحسين إعداد لغوي موجود أو إضافة إعداد جديد.
|
||
|
|
|
||
|
|
للإبلاغ عن ترجمة خاطئة دون تقديم شيفرة، افتح [مشكلة على GitHub](https://github.com/snapotter-hq/SnapOtter/issues) مع ذكر اللغة والسلسلة غير الصحيحة والإصلاح المقترح.
|
||
|
|
|
||
|
|
::: tip
|
||
|
|
لا تتطلب طلبات سحب الترجمة موافقة مسبقة. انسخ المستودع (fork)، وأجرِ تغييراتك، وافتح طلب سحب. راجع [دليل المساهمة](/ar/guide/contributing) لمعرفة عملية طلب السحب الكاملة ومتطلب CLA.
|
||
|
|
:::
|
||
|
|
|
||
|
|
## كيفية إنشاء ترجمة أو تحديثها {#how-to-create-or-update-a-translation}
|
||
|
|
|
||
|
|
### 1. انسخ المستودع (fork) واستنسخه {#_1-fork-and-clone}
|
||
|
|
|
||
|
|
```bash
|
||
|
|
git clone https://github.com/<your-username>/snapotter.git
|
||
|
|
cd snapotter
|
||
|
|
pnpm install
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. انسخ الملف المرجعي (للغة جديدة فقط) {#_2-copy-the-reference-file-new-language-only}
|
||
|
|
|
||
|
|
تخطَّ هذه الخطوة إذا كنت تحسّن ترجمة موجودة.
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cp packages/shared/src/i18n/en.ts packages/shared/src/i18n/XX.ts
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. ترجم السلاسل {#_3-translate-the-strings}
|
||
|
|
|
||
|
|
افتح ملفك الجديد وترجم كل قيمة سلسلة. أبقِ بنية الكائن والمفاتيح كما هي تمامًا.
|
||
|
|
|
||
|
|
```ts
|
||
|
|
import type { TranslationKeys } from "./en.js";
|
||
|
|
|
||
|
|
export const xx: TranslationKeys = {
|
||
|
|
common: {
|
||
|
|
upload: "Your translation here",
|
||
|
|
// ... translate all entries
|
||
|
|
},
|
||
|
|
// ... translate all sections
|
||
|
|
} as const;
|
||
|
|
```
|
||
|
|
|
||
|
|
القواعد:
|
||
|
|
- لا تترجم مفاتيح الكائن، بل القيم النصية فقط
|
||
|
|
- أبقِ `as const` في النهاية
|
||
|
|
- استورد `TranslationKeys` من `./en.js` ونمّط تصديرك
|
||
|
|
- أبقِ العناصر النائبة `{variable}` كما هي تمامًا
|
||
|
|
- يجب أن تحتوي المصفوفات (`rotatingPhrases` و `progressMessages`) على العدد نفسه من المدخلات
|
||
|
|
- لا تترجم: SnapOtter و JPEG و PNG و WebP و EXIF و API وغيرها من المصطلحات التقنية
|
||
|
|
|
||
|
|
### 4. سجّل الإعداد اللغوي (للغة جديدة فقط) {#_4-register-the-locale-new-language-only}
|
||
|
|
|
||
|
|
أضف إعدادك اللغوي إلى `SUPPORTED_LOCALES` في `packages/shared/src/i18n/index.ts`:
|
||
|
|
|
||
|
|
```ts
|
||
|
|
{ code: "xx", name: "Language Name", nativeName: "Native Name", dir: "ltr" },
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5. تحقّق {#_5-verify}
|
||
|
|
|
||
|
|
```bash
|
||
|
|
pnpm typecheck # catches missing or mistyped keys
|
||
|
|
pnpm lint # formatting check
|
||
|
|
pnpm dev # manually verify strings appear correctly
|
||
|
|
```
|
||
|
|
|
||
|
|
### 6. أرسل {#_6-submit}
|
||
|
|
|
||
|
|
افتح طلب سحب على `main` بعنوان مثل `feat(i18n): add Swedish translation` أو `fix(i18n): correct German typos`. سيطلب منك روبوت CLA التوقيع عند مساهمتك الأولى.
|
||
|
|
|
||
|
|
## إضافة مفاتيح ترجمة جديدة {#adding-new-translation-keys}
|
||
|
|
|
||
|
|
عند إضافة ميزة جديدة تحتاج إلى سلاسل واجهة مستخدم جديدة:
|
||
|
|
|
||
|
|
1. أضف المفاتيح الجديدة إلى `en.ts` أولًا (الملف المرجعي)
|
||
|
|
2. شغّل `pnpm typecheck` - سيفشل كل ملف إعداد لغوي إذا كان يفتقد المفتاح الجديد
|
||
|
|
3. أضف المفتاح الجديد إلى جميع ملفات الإعداد اللغوي (استخدم الإنجليزية كخيار رجوع مؤقت)
|
||
|
|
|
||
|
|
## الإعداد {#configuration}
|
||
|
|
|
||
|
|
حدّد لغة النسخة الافتراضية عبر متغيّر البيئة:
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
DEFAULT_LOCALE: "de" # German as the default for all new users
|
||
|
|
```
|
||
|
|
|
||
|
|
## مرجع الملفات {#file-reference}
|
||
|
|
|
||
|
|
| الملف | الغرض |
|
||
|
|
|------|---------|
|
||
|
|
| `packages/shared/src/i18n/en.ts` | سلاسل الإنجليزية (الإعداد اللغوي المرجعي، نحو 1500 مفتاح) |
|
||
|
|
| `packages/shared/src/i18n/index.ts` | `SUPPORTED_LOCALES` و `loadTranslations()` وتصديرات الأنواع |
|
||
|
|
| `packages/shared/src/i18n/<locale>.ts` | ملفات الترجمة لكل لغة |
|
||
|
|
| `apps/web/src/contexts/i18n-context.tsx` | خطاف `I18nProvider` و `useTranslation()` |
|
||
|
|
| `apps/web/src/lib/format.ts` | مساعدات `format()` و `plural()` و `formatFileSize()` |
|
||
|
|
| `apps/api/src/routes/config.ts` | نقطة النهاية العامة `GET /api/v1/config/locale` |
|
||
|
|
|
||
|
|
## ترجمة الموقع الإلكتروني والوثائق ومرجع API {#translating-the-web-surfaces}
|
||
|
|
|
||
|
|
يغطي دعم الـ 21 لغة أعلاه **التطبيق**. الموقع العام
|
||
|
|
(snapotter.com)، وموقع الوثائق هذا، ومرجع REST API مترجمة أيضًا
|
||
|
|
إلى جميع الـ 21 لغة، بواسطة مسار منفصل مقيَّد بالتجزئة يعيد استخدام
|
||
|
|
أسماء الأدوات وأوصافها نفسها من `packages/shared/src/i18n`، لذا
|
||
|
|
تبقى المصطلحات متسقة في كل مكان.
|
||
|
|
|
||
|
|
### مترجمة آليًا افتراضيًا {#machine-translated-by-default}
|
||
|
|
|
||
|
|
كل صفحة غير إنجليزية على الموقع والوثائق **مترجمة آليًا** في
|
||
|
|
التمريرة الأولى (بواسطة جلسة Claude Code، وليس خدمة طرف ثالث) وتحمل
|
||
|
|
شعارًا صغيرًا قابلًا للإغلاق يوضّح ذلك، مع رابط للعودة إلى هنا. هذا متعمَّد:
|
||
|
|
فهو يشحن جميع الـ 21 لغة بسرعة وبصدق، ثم يدعو المجتمع إلى
|
||
|
|
تحسين الصفحات الأهم. الترجمة الآلية توصّل المعنى؛
|
||
|
|
والمراجعة البشرية تجعله يُقرأ بشكل طبيعي.
|
||
|
|
|
||
|
|
### كيف يقرّر المسار ما يترجمه {#how-the-web-pipeline-decides}
|
||
|
|
|
||
|
|
تُجزَّأ كل وحدة قابلة للترجمة من المصدر الإنجليزي، وتُخزَّن التجزئة بجوار
|
||
|
|
ترجمتها. في كل تشغيل، يقوم المسار بما يلي:
|
||
|
|
|
||
|
|
- يترجم أي وحدة ليس لها ترجمة بعد،
|
||
|
|
- يتخطى أي وحدة لا يزال تجزئتها المخزَّنة يطابق المصدر الإنجليزي،
|
||
|
|
- يعيد ترجمة وحدة **آلية** عندما يتغير مصدرها الإنجليزي،
|
||
|
|
- ويضع علامة على وحدة **بشرية** مُنقَّحة بوسم `stale` (تحتاج مراجعة) عندما يتغير مصدرها
|
||
|
|
الإنجليزي، بدلًا من الكتابة فوق عملك.
|
||
|
|
|
||
|
|
### تنقيح ترجمة ويب عبر طلب سحب {#refining-a-web-translation-by-pr}
|
||
|
|
|
||
|
|
تحسّن ترجمة موقع أو وثائق أو مرجع API بالطريقة نفسها التي
|
||
|
|
تحسّن بها إعدادًا لغويًا للتطبيق: بتحرير الملف المُولَّد وفتح طلب سحب.
|
||
|
|
|
||
|
|
1. اعثر على الترجمة المُولَّدة للغتك:
|
||
|
|
- سلاسل واجهة الموقع: `apps/landing/src/i18n/<locale>.json`
|
||
|
|
- صفحة وثائق: `apps/docs/<locale>/**.md`
|
||
|
|
- مرجع API: `apps/api/src/openapi.<locale>.yaml`
|
||
|
|
2. حرّر النص. أبقِ الشيفرة والروابط و `{placeholders}` وأي علامات `⸤I18N…⸥`
|
||
|
|
كما هي تمامًا؛ فمُدقّق المسار يرفض أي ترجمة تُسقط
|
||
|
|
هذه العناصر أو تعيد ترتيبها.
|
||
|
|
3. افتح طلب سحب. يقلب تحرير الوحدة مصدرها من `machine` إلى `human`، لذا
|
||
|
|
لن **يكتب المسار فوقها أبدًا** في تشغيل لاحق. إذا تغير المصدر الإنجليزي
|
||
|
|
بعد ذلك، توضَع علامة على وحدتك بوسم `stale` للمراجعة بدلًا من
|
||
|
|
استبدالها بصمت.
|
||
|
|
|
||
|
|
للإبلاغ عن ترجمة خاطئة دون تقديم شيفرة، افتح
|
||
|
|
[مشكلة على GitHub](https://github.com/snapotter-hq/SnapOtter/issues) مع ذكر عنوان
|
||
|
|
URL للصفحة واللغة والنص غير الصحيح وإصلاحك المقترح.
|
||
|
|
|
||
|
|
::: tip
|
||
|
|
يشغّل المشرفون مسار الترجمة؛ لست بحاجة إلى مفتاح API
|
||
|
|
للمساهمة. فقط حرّر الملف المُولَّد وافتح طلب سحب. راجع
|
||
|
|
[`scripts/i18n/README.md`](https://github.com/snapotter-hq/SnapOtter/blob/main/scripts/i18n/README.md)
|
||
|
|
لمعرفة كيفية تشغيل المسار.
|
||
|
|
:::
|