Resource and Culture and UICulture (והשמה אוטומטית של מידע)
קבצי resource הם דרך נהדרת לשמור את ההודעות והטקסט של האפליקצייה לפי ה culture,
הדבר שמעצבן אותי כמפתח, שאני צריך בכל פעם לכתוב את הקוד של ההשמה מתוך קובץ ה resource על ה label המתאים.
אז לפני שאני אראה את הפיתרון שאני משתמש, נעשה מעבר מהיר על הנושא. לטובת אלו שלא יודעים מה זה resource ומה זה culture.
נניח שיש לי אפליקצייה שאני רוצה שתהיה גם באנגלית וגם בעברית, נתעלם לרגע מהעובדה שכיון הכתיבה הוא הפוך, ונתמקד בזה שהטקסט צריך להיות בשתי השפות.
איך נעשה את זה ?
קליק ימין עם העכבר על הפרויקט שלנו, נבחר ב add new item, ונבחר ב Resource
אני נוהג לייצר קובץ עבור כל דף, והשם של ה resource הוא בדרך כלל השם של הדף.
כעת נוסיף עוד קובץ resource שהשם שלו יהיה זהה לשם של הקובץ הקודם, אבל עם סיומת של ה culture, לדוגמא: Default.he-IL.resx,
אחרי שנוסיף את הקבצים, ונתחיל לערוך אותם, נוכל להוסיף ערכים, שהם למעשה key-value, כשה key, יהיה זהה בשני הקבצים, אבל ה value, יהיה שונה, לדוגמא:
(העליון הוא הקובץ עם הסיומת של he-IL)
וכעת בקוד מה שאנחנו צריכים לעשות, זה לתת לכל label, את הkey המתאים,
לדוגמא:
protected void Page_Load(object sender, EventArgs e)
{
lblHello.Text = Default.lblHello;
lblWelcome.Text = Default.lblWelcome;
}
כעת נשאר לנו רק לקבוע, מאיזה קובץ לקחת את הערכים,
אז למעשה יש כמה דרכים, אפשר מקונפיג, אפשר על ידי הגדרות הדף,
כאן אפשר לקרוא מאמר שמסביר את הנושא.
אני בדרך כלל עושה את זה מקוד, צריך לעשות override ל InitializeCulture, ושם להחליט מה ה resource המתאים,
יש לדף שני properties שמחליטים על הסביבה, הראשון, הוא ה Culture, זה אינו קשור אלינו, הוא מחליט באיזה פורמט יוצגו המספרים ותאריכים,
לדוגמא הקוד הבא,
protected void Page_Load(object sender, EventArgs e)
{
lblTime.Text = DateTime.Now.ToLongDateString();
}
protected override void InitializeCulture()
{
Culture = "he-IL";
}
יחזיר את התוצאה: שבת 09 מאי 2009
לעומת זאת הקוד הבא:
protected void Page_Load(object sender, EventArgs e)
{
lblTime.Text = DateTime.Now.ToLongDateString();
}
protected override void InitializeCulture()
{
Culture = "en-US";
}
יחזיר את התוצאה: Saturday, May 09, 2009
אבל כמו שאמרתי, זה לא המאפיין שכרגע אנחנו מדברים עליו, אנחנו מדברים על המאפיין UICulture,
הוא מחליט מאיזה קובץ resource לקחת את הערכים, ולכן הקוד הבא:
protected void Page_Load(object sender, EventArgs e)
{
lblHello.Text = Default.lblHello;
lblWelcome.Text = Default.lblWelcome;
}
protected override void InitializeCulture()
{
UICulture = "he-IL";
}
יחזיר את הערכים מתוך קובץ ה resource עם סיומת ה he-IL,
לעומת זאת הקוד הבא:
protected void Page_Load(object sender, EventArgs e)
{
lblHello.Text = Default.lblHello;
lblWelcome.Text = Default.lblWelcome;
}
protected override void InitializeCulture()
{
UICulture = "en-US";
}
יחזיר את הערכים, מתוך הקובץ הרגיל (ללא סיומת של תרבות כלשהו).
בעייה אחת, יש לזה, שבמידה ואנחנו צריכים גישה לערכים מתוך ה javascript, אין לנו, הפיתרון לזה, נמצא
כאן.
והבעייה האחרונה (שלכבודה נכתב כל הפוסט הזה),
אני לא רוצה כמפתח לדאוג להשמה של כל הערכים על ה labels השונים, אז כדי לפתור את זה כתבתי את הפיתרון הבא:
בקבצי ה resource, השמות שאני נותן ל keys הם שם האובייקט_שם המאפיין לדוגמא:
שני הראשונים, הם שמות של label ומאפיין הטקסט שלהם, האחרון הינו RequiredFieldValidator ומאפיין ErrorMessage.
כעת בקוד אני מפעיל את המתודה הבאה:
private void SetDataFromResource()
{
Type type = typeof(Default);
PropertyInfo[] properties = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Static);
var stringProperties = from property in properties
where property.PropertyType == typeof(string)
select new Pair(property.Name, property.GetValue(null, null));
foreach (var item in stringProperties)
{
string[] arr = item.First.ToString().Split('_');
if (arr.Length == 2)
{
Control ctr = FindControl(arr[0]);
if (ctr != null)
{
PropertyInfo pi = ctr.GetType().GetProperty(arr[1]);
if (pi != null)
{
pi.SetValue(ctr, item.Second.ToString(), null);
}
}
}
}
}
אני מוציא את כל הערכים מתוך קובץ ה resource.
מחפש control עם השם של האובייקט, במידה ואני מוצא, אני משים את הערך למאפיין.
הפיתרון הזה הוא לא כל כך טוב מבחינת ביצועים, אבל במידה והנקודה הזאת לא מפריעה, אז זה פותר לי את הבעייה שאני צריך לדאוג להשים את הערכים למקומות המתאימים