15-08-2010, 12:23 AM (آخر تعديل لهذه المشاركة: 21-08-2010, 11:41 PM بواسطة kachwahed.)
بسم الله الرحمن الرحيم
السلام عليكم ورحمة الله
في هذا الدرس مثال بسيط حول استخدام مصادر التطبيق (Resources) لتغيير لغة الواجهة حسب الإعدادات الإقليمية للنظام بشكل تلقائي.
String Table:
يمكن باستخدام أي محرر Resource تخزين عدة جداول من النصوص تسمى StringTable حيث يحمل كل جدول رقم تعريفي للغة ورقم تعريفي لإقليم اللغة. مثال:
في هذا المثال نقوم بإنشاء جدولين من النصوص بشكل يدوي (دون استخدام برامج)
حيث في الجدول الأول نخزن النصوص باللغة العربية (إقليم سورية)
وفي الجدول الثاني نخزن النصوص بلغة أجنبية (الفرنسية إقليم قياسي)
افتح ملف نصي واكتب فيه:
كود :
// --------- resources of type String -----------
STRINGTABLE
LANGUAGE LANG_ARABIC, SUBLANG_ARABIC_SYRIA
{
1001, "موافق"
1002, "إلغاء الأمر"
1003, "لغات:"
1004, "تطبيق متعدد اللغات"
1005, "حول التطبيق"
}
STRINGTABLE
LANGUAGE LANG_FRENCH, SUBLANG_DEFAULT
{
1001, "&oK"
1002, "&Annuler"
1003, "Langues:"
1004, "Multi-langue avec Resources"
1005, "&A Propos..."
}
// --------- resources of type Icon -----------
MAINICON Icon "MAINICON.ico"
أعددنا جدولين الأول للغة العربية إقليم سورية وتوجد قيم أخرى:
SUBLANG_ARABIC_LIBYA، SUBLANG_ARABIC_EGYPT
والجدول الثاني للغة الفرنسية-افتراضي، وكان من الممكن استخدام أي من القيم: SUBLANG_FRENCH أو SUBLANG_FRENCH_CANADIAN... أوغيرها
كل هذه القيم وغيرها متوفرة كثوابت في المكتبة Windows.pas.
الآن احفظ الملف باسم Lang.rc مثلا.
قم بإنتاج ملف Resource من خلال سطر الأوامر:
كود :
BRCC32 Lang.rc
الملف الناتج -> Lang.RES
نغير اسم ملف الـ Resource في التطبيق:
كود :
uses
Forms,
uMain in 'uMain.pas' {Form1};
[color=Red]{$R Lang.res}[/color]
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
الآن تطبيقنا يحتوي على مصادر نصية للغتين
لاستدعاء نص الزر OK نكتب:
كود :
btOk.Caption:= LoadStr(1001);
حيث 1001 هو الرقم التعريفي لنص الزر OK
ملاحظة: LoadStr من SysUtils وهي تستدعي LoadString من API بما يشبه الآتي:
كود :
function GetResString(uID: Cardinal): string;
begin
SetLength(Result , MAX_PATH);
LoadString(HInstance, uID, PChar(Result), MAX_PATH);
end;
لكن... كيف يحدد البرنامج أي لغة؟
بعبارة أخرى كيف نعرف أن البرنامج سيستدعي كلمة "موافق" أو "OK"؟؟؟ ... هنا مربط الفرس!
البرنامج لا يعرف ذلك وليس بحاجة لمعرفة ذلك...
عند تشغيل البرنامج في Thread يحصل هذا الأخير على قيمة Cardinal تدعى Locale
تمثل هذه القيمة الإعدادات الإقليمية المطبقة في هذا الـ Thread
للحصول على هذه القيمة نستخدم GetThreadLocale تابع API لنشاهد هذه القيمة:
كود :
ShowMessage(IntToHex(GetThreadLocale, 8));
بماذا تذكرك هذه القيمة ؟
مرر هذه القيمة لهذا التابع لنشاهد الصورة بوضوح:
كود :
function GetLanguage(wLang: Cardinal): string;
var
szLang: array[0..MAX_PATH] of Char;
begin
VerLanguageName(wLang, szLang, 50);
Result := szLang;
end;
ولتغيير هذه القيمة نمرر للتابع SetThreadLocale قيمة (مثل التي حصلنا عليها بـ GetThreadLocale).
يقوم التطبيق عند إقلاعه بقراءة مصادره (الـ Resources) وفق هذه القيمة (Locale) التي تمثل اللغة الإقليمية المحددة.
الآن إقرأ عائد التابع GetSystemDefaultLCID باستخدام GetLanguage التي رأيناها آنفا.
هذا التابع يعيد اللغة الإقليمية للنظام. وهو اللغة التي تستخدم لفك ترميز Unicode (موجودة في الإعدادات الإقليمية التبويب الثالث في XP).
الخلاصة:
نكتب تابع يقوم بتطبيق لغة ممررة عبرة معلمة إلى الـ Thread الحالي للتطبيق، ثم يغير نصوص المكونات:
PHP كود :
procedure TForm1.SetLanguage(LangID: Cardinal); begin SetThreadLocale(LangID); btOk.Caption:= LoadStr(1001); btCancel.Caption:= LoadStr(1002); end;
يمكن المرور على المكونات بحلقة...
لتطبيق أية لغة:
PHP كود :
SetLanguage(LangID);
حيث LangID هو الرقم التعريفي للغة الإقليمية ويمكن الحصول عليه باستخدام Macro يدعى MAKELANGID يترجم بدلفي كما يلي:
PHP كود :
function MAKELANGID(usPrimaryLanguage, usSubLanguage: LANGID): WORD; begin Result := ((usSubLanguage shl 10) + usPrimaryLanguage); end;
عند إقلاع النظام نجعله يأخذ اللغة الإقليمية للنظام:
PHP كود :
procedure TForm1.FormCreate(Sender: TObject); begin SetThreadLocale(GetSystemDefaultLCID); end;
وبالتالي نتفادى جانب من مشكلة عدم تغيير الإعدادات الإقليمية والرموز الغريبة!
سورس المثال مرفق في المشاركة #4 (غير لغة الإقليم في الملف Lang.rc حسب إعدادات جهازك).
إستفسارات،ملاحظات، إنتقادات، إضافات أو تعديلات، اقتراحات، مظاهرات، مفاوضات، إنقلابات... مرحبا
كل من 1 user says قال شكرا ل kachwahed على المشاركة المفيدة1 user says Thank You to kachwahed for this post • Eli Said
21-08-2010, 11:39 PM (آخر تعديل لهذه المشاركة: 28-09-2010, 10:31 PM بواسطة kachwahed.)
السلام عليكم...
الآن، لنجعل الأمور تتم بشكل أكثر ديناميكية نضيف تابع API آخر لقراءة جداول النصوص StringTable من Resources ثم إضافتها إلى TComboBox
التابع EnumResourceNames يقوم بقراءة محتويات Resources من أجل نمط محدد (هو RT_STRING في مثالنا) ويرسلها إلى تابع آخر (CallBack Function) ليقوم هذا الأخير (وهو تابع API آخر EnumResourceLanguages) بتولي مهمة البحث عن اللغات المستخدمة وإضافتها إلى TComboBox بهذا الشكل:
PHP كود :
//Enumerate LanguagesID from resources... function EnumFunc(hModule: THandle; lpszType: PChar; lpszName: PChar; lParam: TStrings): boolean; stdcall;
function EnumLangs(hModule: HWND; lpszType: PChar; lpszName: PChar; wIDLanguage: word; lParam: TStrings): Boolean; stdcall; begin lParam.AddObject(GetLanguage(wIDLanguage), TObject(wIDLanguage)); Result := True; end;
begin EnumResourceLanguages(hModule, lpszType, lpszName, @EnumLangs, Integer(lParam)); Result := False; end;
procedure TMain.FormCreate(Sender: TObject); begin //Enumerate LanguagesID in TComboBox... EnumResourceNames(HInstance, RT_STRING, @EnumFunc, Integer(cbxLang.Items));
تمثل القيمة RT_STRING نوع Resource وهي جدول نصوص StringTable
التابع GetLanguage نفسه الذي رأينا سابقا.
الآن نقوم بتحديد اللغة بهذا الشكل:
كود :
with cbxLang do SetLanguage(Word(Items.Objects[ItemIndex]));
إقتباس :تنبيه: في السورس المرفق يوجد ملف CompileRes.bat يتم تشغيله كلما أردنا توليد ملف ريسورس.