كيفية تدويل تطبيق AngularJS الخاص بك
نشرت: 2022-03-11يمكن أن يؤدي تدويل تطبيقك إلى جعل تطوير البرامج تجربة مؤلمة ، خاصةً إذا لم تبدأ في القيام بذلك من البداية أو كنت تتبع نهجًا أمنيًا تجاهه.
يمكن أن يكون التعامل مع التطبيقات الحديثة ، حيث تكون الواجهة الأمامية والخلفية منفصلتين بشكل واضح عن بعضهما البعض ، أكثر صعوبة في التعامل معها عندما يتعلق الأمر بالتدويل. فجأة لم يعد بإمكانك الوصول إلى عدد كبير من الأدوات التي تم اختبارها بمرور الوقت والتي ساعدت ذات مرة في تدويل تطبيقات الويب التي تم إنشاؤها على جانب الخادم التقليدي.
وفقًا لذلك ، يتطلب تطبيق AngularJS تسليم بيانات التدويل (i18n) والترجمة (l10n) عند الطلب ليتم تسليمها إلى العميل لتقديم نفسه في الإعدادات المحلية المناسبة. على عكس التطبيقات التقليدية المقدمة من جانب الخادم ، لم يعد بإمكانك الاعتماد على الخادم لتسليم الصفحات المترجمة بالفعل. يمكنك التعرف على كيفية إنشاء تطبيق PHP متعدد اللغات هنا
في هذه المقالة ، ستتعلم كيف يمكنك تدويل تطبيق AngularJS الخاص بك ، وستتعرف على الأدوات التي يمكنك استخدامها لتسهيل العملية. يمكن أن يشكل جعل تطبيق AngularJS متعدد اللغات الخاص بك بعض التحديات المثيرة للاهتمام ، ولكن بعض الأساليب يمكن أن تجعل من السهل التغلب على معظم هذه التحديات.
تطبيق AngularJS بسيط قادر على i18n
للسماح للعميل بتغيير اللغة والإعدادات المحلية بسرعة بناءً على تفضيلات المستخدم ، ستحتاج إلى اتخاذ عدد من قرارات التصميم الرئيسية:
- كيف تصمم تطبيقك بحيث يكون حيادي اللغة والإعدادات المحلية منذ البداية؟
- كيف تقوم ببناء بيانات i18n و l10n؟
- كيف تقدم هذه البيانات بكفاءة للعملاء؟
- كيف تستخلص أكبر قدر من تفاصيل التنفيذ منخفضة المستوى لتبسيط سير عمل المطور؟
يمكن أن تساعد الإجابة على هذه الأسئلة في أقرب وقت ممكن في تجنب العوائق في عملية التطوير في المستقبل. سيتم تناول كل من هذه التحديات في هذه المقالة ؛ بعضها من خلال مكتبات AngularJS القوية ، والبعض الآخر من خلال استراتيجيات وأساليب معينة.
مكتبات التدويل لـ AngularJS
يوجد عدد من مكتبات JavaScript المصممة خصيصًا لتدويل تطبيقات AngularJS.
angular-translate
هي وحدة AngularJS التي توفر عوامل تصفية وتوجيهات ، إلى جانب القدرة على تحميل بيانات i18n بشكل غير متزامن. وهو يدعم الجمع من خلال MessageFormat
، وهو مصمم ليكون قابلاً للتوسعة والتكوين بدرجة عالية.
إذا كنت تستخدم angular-translate
في مشروعك ، فقد تجد بعض الحزم التالية مفيدة للغاية:
-
angular-sanitize
: يمكن استخدامه للحماية من هجمات XSS في الترجمات. - تنسيق
angular-translate-interpolation-messageformat
: الجمع مع دعم تنسيق النص الذي يراعي الفوارق بين الجنسين. -
angular-translate-loader-partial
: تُستخدم لتقديم السلاسل المترجمة للعملاء.
للحصول على تجربة ديناميكية حقًا ، يمكنك إضافة angular-dynamic-locale
إلى المجموعة. تتيح لك هذه المكتبة تغيير الإعدادات المحلية ديناميكيًا - ويتضمن ذلك طريقة تنسيق التواريخ والأرقام والعملات وما إلى ذلك.
الشروع في العمل: تثبيت الحزم ذات الصلة
بافتراض أن لديك بالفعل AngularJS boilerplate جاهزًا ، يمكنك استخدام NPM لتثبيت حزم التدويل:
npm i -S angular-translate angular-translate-interpolation-messageformat angular-translate-loader-partial angular-sanitize messageformat
بمجرد تثبيت الحزم ، لا تنس إضافة الوحدات النمطية باعتبارها تبعيات لتطبيقك:
// /src/app/core/core.module.js app.module('app.core', ['pascalprecht.translate', ...]);
لاحظ أن اسم الوحدة يختلف عن اسم الحزمة.
ترجمة السلسلة الأولى الخاصة بك
لنفترض أن تطبيقك يحتوي على شريط أدوات به بعض النص وحقل به بعض نص العنصر النائب:
<nav class="navbar navbar-default"> <div class="container-fluid"> <div class="navbar-header"> <a class="navbar-brand" href="#">Hello</a> </div> <div class="collapse navbar-collapse"> <form class="navbar-form navbar-left"> <div class="form-group"> <input type="text" class="form-control" ng-model="vm.query" placeholder="Search"> </div> ... </div> </div> </nav>
يحتوي العرض أعلاه على جزأين من النص يمكنك تدويلهما: "مرحبًا" و "بحث". من حيث HTML ، يظهر أحدهما كنص داخلي لعلامة الربط ، بينما يظهر الآخر كقيمة لسمة.
لتدويلها ، سيتعين عليك استبدال كلتا السلاسل الحرفية برموز يمكن لـ AngularJS استبدالها بالسلاسل الفعلية المترجمة ، بناءً على تفضيل المستخدم ، أثناء عرض الصفحة.
يمكن لـ AngularJS القيام بذلك باستخدام الرموز المميزة الخاصة بك لإجراء بحث في جداول الترجمة التي توفرها. تتوقع الوحدة النمطية angular-translate
أن يتم توفير جداول الترجمة هذه ككائنات JavaScript عادية أو ككائنات JSON (إذا تم التحميل عن بُعد).
فيما يلي مثال على الشكل العام لجداول الترجمة هذه:
// /src/app/toolbar/i18n/en.json { "TOOLBAR": { "HELLO": "Hello", "SEARCH": "Search" } } // /src/app/toolbar/i18n/tr.json { "TOOLBAR": { "HELLO": "Merhaba", "SEARCH": "Ara" } }
لإضفاء الطابع الدولي على عرض شريط الأدوات من الأعلى ، تحتاج إلى استبدال سلسلة الأحرف الحرفية بالرموز المميزة التي يمكن أن يستخدمها AngularJS للبحث في جدول الترجمة:
<!-- /src/app/toolbar/toolbar.html --> <a class="navbar-brand" href="#" translate="TOOLBAR.HELLO"></a> <!-- or --> <a class="navbar-brand" href="#">{{'TOOLBAR.HELLO' | translate}}</a>
لاحظ كيف ، بالنسبة للنص الداخلي ، يمكنك إما استخدام توجيه translate
أو مرشح translate
. (يمكنك معرفة المزيد حول توجيه translate
هنا وحول translate
التصفية من هنا.)
مع هذه التغييرات ، عندما يتم تقديم العرض ، ستقوم angular-translate
تلقائيًا بإدراج الترجمة المناسبة المقابلة لـ TOOLBAR.HELLO
في DOM بناءً على اللغة الحالية.
لترميز القيم الحرفية للسلسلة التي تظهر كقيم سمة ، يمكنك استخدام الطريقة التالية:
<!-- /src/app/toolbar/toolbar.html --> <input type="text" class="form-control" ng-model="vm.query" translate translate-attr-placeholder="TOOLBAR.SEARCH">
الآن ، ماذا لو احتوت سلاسل الرموز الخاصة بك على متغيرات؟
للتعامل مع حالات مثل "مرحبًا ، {{اسم}}." ، يمكنك إجراء استبدال متغير باستخدام نفس صيغة الاستيفاء التي يدعمها AngularJS بالفعل:
جدول الترجمة:
// /src/app/toolbar/i18n/en.json { "TOOLBAR": { "HELLO": "Hello, {{name}}." } }
يمكنك بعد ذلك تحديد المتغير بعدة طرق. وهنا عدد قليل:
<!-- /src/app/toolbar/toolbar.html --> <a ... translate="TOOLBAR.HELLO" translate-values='{ name: vm.user.name }'></a> <!-- or --> <a ... translate="TOOLBAR.HELLO" translate-value-name='{{vm.user.name}}'></a> <!-- or --> <a ...>{{'TOOLBAR.HELLO | translate:'{ name: vm.user.name }'}}</a>
التعامل مع التعددية والنوع
تعد التعددية موضوعًا صعبًا جدًا عندما يتعلق الأمر بـ i18n و l10n. للغات والثقافات المختلفة قواعد مختلفة لكيفية تعامل اللغة مع التعددية في المواقف المختلفة.
بسبب هذه التحديات ، لن يقوم مطورو البرامج أحيانًا بمعالجة المشكلة ببساطة (أو على الأقل لن يعالجوها بشكل مناسب) ، مما ينتج عنه برنامج ينتج جمل سخيفة مثل هذه:
He saw 1 person(s) on floor 1. She saw 1 person(s) on floor 3. Number of people seen on floor 2: 2.
لحسن الحظ ، هناك معيار لكيفية التعامل مع هذا ، وتطبيق JavaScript للمعيار متاح كـ MessageFormat.
باستخدام MessageFormat ، يمكنك استبدال الجمل ذات البنية الرديئة أعلاه بما يلي:
He saw 1 person on the 2nd floor. She saw 1 person on the 3rd floor. They saw 2 people on the 5th floor.
يقبل MessageFormat
التعبيرات مثل ما يلي:
var message = [ '{GENDER, select, male{He} female{She} other{They}}', 'saw', '{COUNT, plural, =0{no one} one{1 person} other{# people}}', 'on the', '{FLOOR, selectordinal, one{#st} two{#nd} few{#rd} other{#th}}', 'floor.' ].join(' ');
يمكنك إنشاء مُنسق باستخدام المصفوفة أعلاه ، واستخدامها لإنشاء سلاسل:
var messageFormatter = new MessageFormat('en').compile(message); messageFormatter({ GENDER: 'male', COUNT: 1, FLOOR: 2 }) // 'He saw 1 person on the 2nd floor.' messageFormatter({ GENDER: 'female', COUNT: 1, FLOOR: 3 }) // 'She saw 1 person on the 3rd floor.' messageFormatter({ COUNT: 2, FLOOR: 5 }) // 'They saw 2 people on the 5th floor.'
كيف يمكنك استخدام MessageFormat
مع Angular angular-translate
للاستفادة من وظائفه الكاملة داخل تطبيقاتك؟
في تكوين التطبيق الخاص بك ، يمكنك ببساطة إخبار angular-translate
أن استيفاء تنسيق الرسالة متاح على النحو التالي:
/src/app/core/core.config.js app.config(function ($translateProvider) { $translateProvider.addInterpolation('$translateMessageFormatInterpolation'); });
فيما يلي الشكل الذي قد يبدو عليه إدخال في جدول الترجمة:
// /src/app/main/social/i18n/en.json { "SHARED": "{GENDER, select, male{He} female{She} other{They}} shared this." }
وفي رأي:
<!-- /src/app/main/social/social.html --> <div translate="SHARED" translate-values="{ GENDER: 'male' }" translate-interpolation="messageformat"></div> <div> {{ 'SHARED' | translate:"{ GENDER: 'male' }":'messageformat' }} </div>
هنا يجب أن تشير صراحة إلى أنه يجب استخدام محرف تنسيق الرسالة بدلاً من المحرف الافتراضي في AngularJS. هذا لأن المحرفين يختلفان قليلاً في تركيبهما. تستطيع ان تقرأ المزيد عن هذا هنا.
توفير جداول الترجمة لتطبيقك
الآن بعد أن عرفت كيف يمكن لـ AngularJS البحث عن الترجمات الخاصة بك من جداول الترجمة ، كيف يعرف تطبيقك جداول الترجمة في المقام الأول؟ كيف تخبر تطبيقك باللغة / اللغة التي يجب استخدامها؟
هذا هو المكان الذي تتعرف فيه على $translateProvider
.
يمكنك توفير جداول الترجمة لكل لغة تريد دعمها مباشرةً في ملف core.config.js
الخاص بتطبيقك على النحو التالي:
// /src/app/core/core.config.js app.config(function ($translateProvider) { $translateProvider.addInterpolation('$translateMessageFormatInterpolation'); $translateProvider.translations('en', { TOOLBAR: { HELLO: 'Hello, {{name}}.' } }); $translateProvider.translations('tr', { TOOLBAR: { HELLO: 'Merhaba, {{name}}.' } }); $translateProvider.preferredLanguage('en'); });
هنا تقوم بتوفير جداول الترجمة ككائنات JavaScript للغة الإنجليزية (en) والتركية (tr) ، بينما تعلن أن اللغة الحالية هي الإنجليزية (en). إذا كان المستخدم يرغب في تغيير اللغة ، فيمكنك القيام بذلك من خلال خدمة الترجمة $:

// /src/app/toolbar/toolbar.controller.js app.controller('ToolbarCtrl', function ($scope, $translate) { $scope.changeLanguage = function (languageKey) { $translate.use(languageKey); // Persist selection in cookie/local-storage/database/etc... }; });
لا يزال هناك سؤال حول اللغة التي يجب استخدامها بشكل افتراضي. قد لا يكون الترميز الدقيق للغة الأولية لتطبيقنا مقبولاً دائمًا. في مثل هذه الحالات ، يكون البديل هو محاولة تحديد اللغة تلقائيًا باستخدام $ translateProvider:
// /src/app/core/core.config.js app.config(function ($translateProvider) { ... $translateProvider.determinePreferredLanguage(); });
يبحث determinePreferredLanguage
عن القيم في window.navigator
ويختار افتراضيًا ذكيًا حتى يتم توفير إشارة واضحة من قبل المستخدم.
جداول الترجمة ذات التحميل الكسول
أظهر القسم السابق كيف يمكنك توفير جداول الترجمة مباشرة في التعليمات البرمجية المصدر ككائنات جافا سكريبت. قد يكون هذا مقبولًا للتطبيقات الصغيرة ، لكن الطريقة غير قابلة للتطوير ، ولهذا السبب غالبًا ما يتم تنزيل جداول الترجمة كملفات JSON من خادم بعيد.
يؤدي الاحتفاظ بجداول الترجمة بهذه الطريقة إلى تقليل حجم الحمولة الأولية التي يتم تسليمها إلى العميل ولكنه يؤدي إلى تعقيد إضافي. أنت الآن تواجه تحدي التصميم المتمثل في توصيل بيانات i18n إلى العميل. إذا لم يتم التعامل مع هذا بعناية ، فقد يتأثر أداء التطبيق الخاص بك دون داع.
لماذا هو معقد جدا؟ يتم تنظيم تطبيقات AngularJS في وحدات. في تطبيق معقد ، قد يكون هناك العديد من الوحدات النمطية ، لكل منها بيانات i18n المميزة الخاصة بها. لذلك يجب تجنب نهج ساذج ، مثل تحميل وتوفير بيانات i18n دفعة واحدة.
ما تحتاجه هو طريقة لتنظيم بيانات i18n الخاصة بك حسب الوحدة. سيمكنك هذا من تحميل ما تحتاجه فقط عندما تحتاج إليه ، وتخزين ما تم تحميله مسبقًا لتجنب إعادة تحميل نفس البيانات (على الأقل حتى تصبح ذاكرة التخزين المؤقت غير صالحة).
هذا هو المكان الذي يأتي دور partialLoader
.
لنفترض أن جداول الترجمة الخاصة بتطبيقك منظمة على النحو التالي:
/src/app/main/i18n/en.json /src/app/main/i18n/tr.json /src/app/toolbar/i18n/en.json /src/app/toolbar/i18n/tr.json
يمكنك تكوين $translateProvider
لاستخدام partialLoader
بنمط عنوان URL يطابق هذه البنية:
// /src/app/core/core.config.js app.config(function ($translateProvider) { ... $translateProvider.useLoader('$translatePartialLoader', { urlTemplate: '/src/app/{part}/i18n/{lang}.json' }); });
كما قد يتوقع المرء ، يتم استبدال "lang" برمز اللغة في وقت التشغيل (على سبيل المثال ، "en" أو "tr"). ماذا عن "الجزء"؟ كيف يعرف $ translateProvider أي "جزء" يجب تحميله؟
يمكنك توفير هذه المعلومات داخل وحدات التحكم باستخدام $translatePartialLoader
:
// /src/app/main/main.controller.js app.controller('MainCtrl', function ($translatePartialLoader) { $translatePartialLoader.addPart('main'); }); // /src/app/toolbar/toolbar.config.js app.controller('ToolbarCtrl', function ($translatePartialLoader) { $translatePartialLoader.addPart('toolbar'); });
اكتمل النمط الآن ، ويتم تحميل بيانات i18n لطريقة عرض معينة عند تنفيذ وحدة التحكم الخاصة به لأول مرة ، وهو ما تريده بالضبط.
التخزين المؤقت: تقليل أوقات التحميل
ماذا عن التخزين المؤقت؟
يمكنك تفعيل ذاكرة التخزين المؤقت القياسية في تكوين التطبيق باستخدام $translateProvider
:
// /src/app/core/core.config.js app.config(function ($translateProvider) { ... $translateProvider.useLoaderCache(true); // default is false });
إذا كنت تريد كسر ذاكرة التخزين المؤقت للغة معينة ، فيمكنك استخدام $translate
:
$translate.refresh(languageKey); // omit languageKey to refresh all
مع وجود هذه القطع في مكانها الصحيح ، يصبح تطبيقك دوليًا بالكامل ويدعم لغات متعددة.
تعريب الأرقام والعملات والتواريخ
في هذا القسم ، ستتعلم كيف يمكنك استخدام angular-dynamic-locale
لدعم تنسيق عناصر واجهة المستخدم مثل الأرقام والعملات والتواريخ وما شابه ذلك ، في تطبيق AngularJS.
ستحتاج إلى تثبيت حزمتين أخريين لهذا:
npm i -S angular-dynamic-locale angular-i18n
بمجرد تثبيت الحزم ، يمكنك إضافة الوحدة النمطية إلى تبعيات تطبيقك:
// /src/app/core/core.module.js app.module('app.core', ['tmh.dynamicLocale', ...]);
قواعد اللغة
قواعد اللغة هي ملفات JavaScript بسيطة توفر مواصفات لكيفية تنسيق التواريخ والأرقام والعملات وما شابه ذلك بواسطة المكونات التي تعتمد على خدمة locale $.
قائمة اللغات المدعومة حاليًا متاحة هنا.
إليك مقتطف من angular-locale_en-us.js
يوضح تنسيق الشهر والتاريخ:
... "MONTH": [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], "SHORTDAY": [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], ...
على عكس بيانات i18n ، تكون قواعد الإعدادات المحلية عامة للتطبيق ، وتتطلب تحميل جميع القواعد الخاصة بإحدى اللغات مرة واحدة.
بشكل افتراضي ، تتوقع لغة angular-dynamic-locale
أن تكون ملفات قواعد اللغة موجودة في angular/i18n/angular-locale_{{locale}}.js
. إذا كانت موجودة في مكان آخر ، فيجب استخدام tmhDynamicLocaleProvider
الافتراضي:
// /src/app/core/core.config.js app.config(function (tmhDynamicLocaleProvider) { tmhDynamicLocaleProvider.localeLocationPattern( '/node_modules/angular-i18n/angular-locale_{{locale}}.js'); });
يتم التعامل مع التخزين المؤقت تلقائيًا بواسطة خدمة tmhDynamicLocaleCache
.
لا يمثل إبطال ذاكرة التخزين المؤقت مصدر قلق هنا ، نظرًا لأن احتمال تغيير قواعد اللغة أقل من ترجمات السلاسل.
للتبديل بين اللغات ، توفر angular-dynamic-locale
للزاوية الديناميكية خدمة tmhDynamicLocale
:
// /src/app/toolbar/toolbar.controller.js app.controller('ToolbarCtrl', function ($scope, tmhDynamicLocale) { $scope.changeLocale = function (localeKey) { tmhDynamicLocale.set(localeKey); // Persist selection in cookie/local-storage/database/etc... }; });
توليد جداول الترجمة مع الترجمة الآلية
يتم شحن قواعد الإعدادات المحلية مع حزمة angular-i18n
، لذلك كل ما عليك فعله هو إتاحة محتويات الحزمة لتطبيقك حسب الحاجة. ولكن كيف تنشئ ملفات JSON لجداول الترجمة الخاصة بك؟ لا توجد حزمة بالضبط يمكنك تنزيلها وتوصيلها بتطبيقنا.
يتمثل أحد الخيارات في استخدام واجهات برمجة تطبيقات للترجمة البرمجية ، خاصةً إذا كانت السلاسل في تطبيقك عبارة عن حرفية بسيطة بدون متغيرات أو تعبيرات متعددة.
مع Gulp واثنين من الحزم الإضافية ، يعد طلب الترجمات الآلية لتطبيقك أمرًا في غاية السهولة:
import gulp from 'gulp'; import map from 'map-stream'; import rename from 'gulp-rename'; import traverse from 'traverse'; import transform from 'vinyl-transform'; import jsonFormat from 'gulp-json-format'; function translateTable(to) { return transform(() => { return map((data, done) => { const table = JSON.parse(data); const strings = []; traverse(table).forEach(function (value) { if (typeof value !== 'object') { strings.push(value); } }); Promise.all(strings.map((s) => getTranslation(s, to))) .then((translations) => { let index = 0; const translated = traverse(table).forEach(function (value) { if (typeof value !== 'object') { this.update(translations[index++]); } }); done(null, JSON.stringify(translated)); }) .catch(done); }); }); } function translate(to) { return gulp.src('src/app/**/i18n/en.json') .pipe(translateTable(to)) .pipe(jsonFormat(2)) .pipe(rename({ basename: to })) .pipe(gulp.dest('src/app')); } gulp.task('translate:tr', () => translate('tr')); This task assumes the following folder structure: /src/app/main/i18n/en.json /src/app/toolbar/i18n/en.json /src/app/navigation/i18n/en.json ...
يقرأ البرنامج النصي أولاً جميع جداول الترجمة الإنجليزية ، ويطلب ترجمات غير متزامنة لموارد السلسلة الخاصة بهم ، ثم يستبدل السلاسل الإنجليزية بالسلاسل المترجمة لإنتاج جدول ترجمة بلغة جديدة.
أخيرًا ، تمت كتابة جدول الترجمة الجديد كشقيق لجدول الترجمة الإنجليزية ، مما أسفر عن:
/src/app/main/i18n/en.json /src/app/main/i18n/tr.json /src/app/toolbar/i18n/en.json /src/app/toolbar/i18n/tr.json /src/app/navigation/i18n/en.json /src/app/navigation/i18n/tr.json ...
يعد تطبيق getTranslation
أيضًا:
import bluebird from 'bluebird'; import MicrosoftTranslator from 'mstranslator'; bluebird.promisifyAll(MicrosoftTranslator.prototype); const Translator = new MicrosoftTranslator({ client_id: process.env.MICROSOFT_TRANSLATOR_CLIENT_ID, client_secret: process.env.MICROSOFT_TRANSLATOR_CLIENT_SECRET }, true); function getTranslation(string, to) { const text = string; const from = 'en'; return Translator.translateAsync({ text, from, to }); }
هنا ، نحن نستخدم Microsoft Translate ، ولكن يمكن للمرء بسهولة استخدام مزود آخر مثل Google Translate أو Yandex Translate.
في حين أن الترجمات الآلية مريحة ، إلا أن هناك العديد من العيوب ، بما في ذلك:
- تعتبر الترجمات الآلية مفيدة للسلاسل القصيرة ، ولكن حتى ذلك الحين ، قد تكون هناك مخاطر مع الكلمات التي لها معاني مختلفة في سياقات مختلفة (على سبيل المثال ، يمكن أن تعني كلمة "تجمع" السباحة أو التجمع).
- قد لا تتمكن واجهات برمجة التطبيقات من التعامل مع السلاسل ذات المتغيرات أو السلاسل التي تعتمد على تنسيق الرسالة.
في هذه الحالات وغيرها ، قد تكون هناك حاجة للترجمات البشرية ؛ ومع ذلك ، هذا موضوع لمدونة أخرى.
تدويل الواجهات الأمامية فقط يبدو مخيفًا
في هذه المقالة ، تعلمت كيفية استخدام هذه الحزم لتدويل وترجمة تطبيقات AngularJS.
angular-translate
angular-dynamic-locale
، gulp
هي أدوات فعّالة لتدويل تطبيق AngularJS الذي يغلف تفاصيل التنفيذ المؤلمة منخفضة المستوى.
للحصول على تطبيق تجريبي يوضح الأفكار التي تمت مناقشتها في هذا المنشور ، تحقق من مستودع GitHub هذا.