Introduction
This project includes a library which you can use to convert Persian raw text to a formatted Persian string that can be showed correctly in Silverlight 2.0. The latest version of the source code of this project is available here.
Background
As everybody knows, Silverlight 2.0 does not have support for right to left languages. Therefore, the first issue that every developer who desires to write applications in RTL languages should deal with is, to find a way to solve this problem. I am aware that a similar project is running to support Arabic and Hebrew languages in Silverlight. The name of this project is "Silverlight 2.0 Hebrew & Arabic Language Support", and it is available in Codeplex too. However, my project is not as professional as their project, it might be useful. You can add your comments here in CodeProject or here.
Code
The PersianCharacter class is used in defining a Persian Character Map.
internal class PersianCharacter
{
public PersianCharacter(string characterName, bool noLastAppend, int
startChar, int middleRightSideBoundedChar, int
middleLeftSideBoundedChar, int middleTwoSideBoundedChar, int
endingBoundedChar, int endingChar)
{
CharacterName = characterName;
NoLastAppend = noLastAppend;
StartChar = startChar;
MiddleRightSideBoundedChar = middleRightSideBoundedChar;
MiddleLeftSideBoundedChar = middleLeftSideBoundedChar;
MiddleTwoSideBoundedChar = middleTwoSideBoundedChar;
EndingBoundedChar = endingBoundedChar;
EndingChar = endingChar;
}
private string _CharacterName = "";
public string CharacterName
{
get
{
return _CharacterName;
}
set
{
_CharacterName = value;
}
}
private bool _NoLastAppend = false;
public bool NoLastAppend
{
get
{
return _NoLastAppend;
}
set
{
_NoLastAppend = value;
}
}
private int _StartChar = 0;
public int StartChar
{
get
{
return _StartChar;
}
set
{
_StartChar = value;
}
}
private int _MiddleRightSideBoundedChar = 0;
public int MiddleRightSideBoundedChar
{
get
{
return _MiddleRightSideBoundedChar;
}
set
{
_MiddleRightSideBoundedChar = value;
}
}
private int _MiddleLeftSideBoundedChar = 0;
public int MiddleLeftSideBoundedChar
{
get
{
return _MiddleLeftSideBoundedChar;
}
set
{
_MiddleLeftSideBoundedChar = value;
}
}
private int _MiddleTwoSideBoundedChar = 0;
public int MiddleTwoSideBoundedChar
{
get
{
return _MiddleTwoSideBoundedChar;
}
set
{
_MiddleTwoSideBoundedChar = value;
}
}
private int _EndingBoundedChar = 0;
public int EndingBoundedChar
{
get
{
return _EndingBoundedChar;
}
set
{
_EndingBoundedChar = value;
}
}
private int _EndingChar = 0;
public int EndingChar
{
get
{
return _EndingChar;
}
set
{
_EndingChar = value;
}
}
}
The PersianMaker class doed the process of formatting a Persian string. It has five methods which I will discuss briefly.
First, I define all Persian characters and add them to the CharacterMap list. There are about 40 characters, even though the actual Persian character count is less than 40. There are some Arabic characters which are used now in Persian writing. After populating the Character Map with the objects of the PersianCharacter class, it is ready to be used. Thanks to LINQ for supporting LINQ to Objects because it reduces the number of loops.
The first method IsPersianCharacter is for checking whether a specified character belongs to the Persian Character Map or not. This will be done by querying the CharacterMap list.
There is another method IsNoLastAppendCharacter which checks if the specified character is a Persian character to which no character can be append to the end, such as Je(ژ) or Re (ر). The ReverseString method will accept a string, and then return it as reverse. We need this method because we should reverse the string to be presented correctly in Silverlight because of the lack of right to left support.
ToPersianString is the heart of the PersianMaker class because it accepts a raw (unformated) text and produces a string that can be presented correctly in Silverlight content. This method iterates the text, character by character, to concatenate Persian characters correctly.
The ToPersian method first seperates words by space, and then passed them to the ToPersianString. Then it concatenates words, and returns the final string that can be presented in Silverlight 2.0 content correctly.
public class PersianMaker
{
private List<PersianCharacter> CharacterMap;
public PersianMaker()
{
CharacterMap = new List<PersianCharacter>();
#region ABaKolah آ
CharacterMap.Add(new PersianCharacter("ABaKolah", true,
0x622, 0x622, 0xfe82, 0xfe81, 0xfe82, 0xfe81));
# endregion
#region Alef ا
CharacterMap.Add(new PersianCharacter("Alef", true, 0x627,
0x627, 0xfe8e, 0xfe8d, 0xfe8e, 0xfe8d));
# endregion
#region AlefWithHamzeAbove أ
CharacterMap.Add(new PersianCharacter("AlefWithHamzeAbove",
true, 0x623, 0x623, 0xfe84, 0xfe83, 0xfe84, 0xfe83));
# endregion
#region AlefWithHamzeBelow إ
CharacterMap.Add(new PersianCharacter("AlefWithHamzeBelow",
true, 0x625, 0x625, 0xfe88, 0xfe87, 0xfe88, 0xfe87));
# endregion
#region Hamze ء
CharacterMap.Add(new PersianCharacter("Hamze", true,
0x621, 0x621, 0xfe80, 0xfe80, 0xfe80, 0xfe7f));
# endregion
#region Be ب
CharacterMap.Add(new PersianCharacter("Be", false,
0x628, 0x628, 0xfe90, 0xfe91, 0xfe92, 0xfe8f));
# endregion
#region Pe پ
CharacterMap.Add(new PersianCharacter("Pe", false,
0x67e, 0x67e, 0xfb57, 0xfb58, 0xfb59, 0xfb56));
# endregion
#region Te ت
CharacterMap.Add(new PersianCharacter("Te", false,
0x62a, 0x62a, 0xfe96, 0xfe97, 0xfe98, 0xfe95));
# endregion
#region Theh ث
CharacterMap.Add(new PersianCharacter("Theh", false,
0x62b, 0x62b, 0xfe9a, 0xfe9b, 0xfe9c, 0xfe99));
# endregion
#region Jeem ج
CharacterMap.Add(new PersianCharacter("Jeem", false,
0x62c, 0x62c, 0xfe9e, 0xfe9f, 0xfea0, 0xfe9d));
# endregion
#region Che چ
CharacterMap.Add(new PersianCharacter("Che", false,
0x686, 0x686, 0xfb7b, 0xfb7c, 0xfb7d, 0xfb7a));
# endregion
#region Hah ح
CharacterMap.Add(new PersianCharacter("Hah", false,
0x62d, 0x62d, 0xfea2, 0xfea3, 0xfea4, 0xfea1));
# endregion
#region Kheh خ
CharacterMap.Add(new PersianCharacter("Kheh", false,
0x62e, 0x62e, 0xfea6, 0xfea7, 0xfea8, 0xfea5));
# endregion
#region Dal د
CharacterMap.Add(new PersianCharacter("Dal", true,
0x62f, 0x62f, 0xfeaa, 0xfea9, 0xfeaa, 0xfea9));
# endregion
#region Zal ذ
CharacterMap.Add(new PersianCharacter("Zal", true,
0x630, 0x630, 0xfeac, 0xfeab, 0xfeac, 0xfeab));
# endregion
#region Re ر
CharacterMap.Add(new PersianCharacter("Re", true, 0x631,
0x631, 0xfeae, 0xfead, 0xfeae, 0xfead));
# endregion
#region Ze ز
CharacterMap.Add(new PersianCharacter("Ze", true,
0x632, 0x632, 0xfeb0, 0xfeaf, 0xfeb0, 0xfeaf));
# endregion
#region Je ژ
CharacterMap.Add(new PersianCharacter("Je", true,
0x698, 0x698, 0xfb8b, 0xfb8a, 0xfb8b, 0xfb8a));
# endregion
#region Seen س
CharacterMap.Add(new PersianCharacter("Seen", false,
0x633, 0x633, 0xfeb2, 0xfeb3, 0xfeb4, 0xfeb1));
# endregion
#region Sheen ش
CharacterMap.Add(new PersianCharacter("Sheen", false,
0x634, 0x634, 0xfeb6, 0xfeb7, 0xfeb8, 0xfeb5));
# endregion
#region Sad ص
CharacterMap.Add(new PersianCharacter("Sad", false,
0x635, 0x635, 0xfeba, 0xfebb, 0xfebc, 0xfeb9));
# endregion
#region Zad ض
CharacterMap.Add(new PersianCharacter("Zad", false,
0x636, 0x636, 0xfebe, 0xfebf, 0xfec0, 0xfebd));
# endregion
#region Ta ط
CharacterMap.Add(new PersianCharacter("Ta", false,
0x637, 0x637, 0xfec2, 0xfec3, 0xfec4, 0xfec1));
# endregion
#region Za ظ
CharacterMap.Add(new PersianCharacter("Za", false,
0x638, 0x638, 0xfec6, 0xfec7, 0xfec8, 0xfec5));
# endregion
#region Ein ع
CharacterMap.Add(new PersianCharacter("Ein", false,
0x639, 0xfeca, 0xfeca, 0xfecb, 0xfecc, 0xfec9));
# endregion
#region Ghein غ
CharacterMap.Add(new PersianCharacter("Ghein", false,
0x63a, 0x63a, 0xfece, 0xfecf, 0xfed0, 0xfecd));
# endregion
#region Fe ف
CharacterMap.Add(new PersianCharacter("Fe", false,
0x641, 0x641, 0xfed2, 0xfed3, 0xfed4, 0xfed1));
# endregion
#region Ghaf ق
CharacterMap.Add(new PersianCharacter("Ghaf", false,
0x642, 0x642, 0xfed6, 0xfed7, 0xfed8, 0xfed5));
# endregion
#region kaf ک
CharacterMap.Add(new PersianCharacter("kaf", false,
0x6a9, 0x6a9, 0xfeda, 0xfedb, 0xfedc, 0xfed9));
# endregion
#region Gaf گ
CharacterMap.Add(new PersianCharacter("Gaf", false,
0x6af, 0x6af, 0xfb93, 0xfb94, 0xfb95, 0xfb92));
# endregion
#region Lam ل
CharacterMap.Add(new PersianCharacter("Lam", false,
0x644, 0x644, 0xfede, 0xfedf, 0xfee0, 0xfedd));
# endregion
#region Meem م
CharacterMap.Add(new PersianCharacter("Meem", false,
0x645, 0x645, 0xfee2, 0xfee3, 0xfee4, 0xfee1));
# endregion
#region Noon ن
CharacterMap.Add(new PersianCharacter("Noon", false,
0x646, 0x646, 0xfee6, 0xfee7, 0xfee8, 0xfee5));
# endregion
#region Vav و
CharacterMap.Add(new PersianCharacter("Vav", true, 0x648,
0x648, 0xfeee, 0xfeed, 0xfeee, 0xfeed));
# endregion
#region VavBaHamze ؤ
CharacterMap.Add(new PersianCharacter("VavBaHamze", true,
0x624, 0x624, 0xfe86, 0xfe85, 0xfe86, 0xfe85));
# endregion
#region Heh ه
CharacterMap.Add(new PersianCharacter("Heh", false,
0x647, 0x647, 0xfeea, 0xfeeb, 0xfeec, 0xfee9));
# endregion
#region TehMarbuta ة
CharacterMap.Add(new PersianCharacter("TehMarbuta",
true, 0x629, 0x629, 0xfe94, 0xfe93, 0xfe93, 0xfe93));
# endregion
#region Ye ی
CharacterMap.Add(new PersianCharacter("Ye", false,
0x6cc, 0x6cc, 0xfef0, 0xfef3, 0xfef4, 0xfeef));
# endregion
#region ArabicYe ي
CharacterMap.Add(new PersianCharacter("ArabicYe", false,
0x64a, 0x64a, 0xfef2, 0xfef3, 0xfef4, 0xfef1));
# endregion
#region YeBaHamze ئ
CharacterMap.Add(new PersianCharacter("YeBaHamze", false,
0x626, 0x626, 0xfe8a, 0xfe8b, 0xfe8c, 0xfe89));
# endregion
}
private bool IsPersianCharacter(char ch)
{
bool result = false;
var query = from cm in CharacterMap
where cm.StartChar == ch ||
cm.MiddleRightSideBoundedChar == ch ||
cm.MiddleLeftSideBoundedChar == ch ||
cm.MiddleTwoSideBoundedChar == ch ||
cm.EndingBoundedChar == ch ||
cm.EndingChar == ch
select cm.CharacterName;
if (query.Count() > 0)
{
result = true;
}
return result;
}
private bool IsNoLastAppendCharacter(char ch)
{
bool result = false;
var query = from cm in CharacterMap
where (cm.StartChar == ch ||
cm.MiddleRightSideBoundedChar == ch ||
cm.MiddleLeftSideBoundedChar == ch ||
cm.MiddleTwoSideBoundedChar == ch ||
cm.EndingBoundedChar == ch ||
cm.EndingChar == ch) & cm.NoLastAppend == true
select cm.NoLastAppend;
if (query.Count() > 0)
{
result = true;
}
return result;
}
private string ToPersianString(string text)
{
int i = 0;
string str2 = "";
while (i < text.Length)
{
ECharPossition possition= ECharPossition.StartingChar;
if (IsPersianCharacter(Convert.ToChar(text.Substring(i, 1))))
{
if (i == 0) {
possition = ECharPossition.StartingChar;
}
else if (i == (text.Length - 1))
{
if (IsPersianCharacter(Convert.ToChar(text.Substring(i - 1, 1))))
{
if (IsNoLastAppendCharacter(
Convert.ToChar(text.Substring(i - 1, 1))))
{
possition = ECharPossition.EndingChar;
}
else
{
possition = ECharPossition.EndingBoundedChar;
}
}
else
{
possition = ECharPossition.EndingBoundedChar;
}
}
else {
if (IsPersianCharacter(
Convert.ToChar(text.Substring(i - 1, 1))))
{
if (IsNoLastAppendCharacter(
Convert.ToChar(text.Substring(i - 1, 1))))
{
if (IsPersianCharacter(
Convert.ToChar(text.Substring(i, 1))))
{
if (!IsNoLastAppendCharacter(
Convert.ToChar(text.Substring(i, 1))))
{
possition =
ECharPossition.MiddleLeftSideBoundedChar;
}
else
{
possition = ECharPossition.StartingChar;
}
}
else
{
possition = ECharPossition.StartingChar;
}
}
else
{
possition = ECharPossition.MiddleTwoSidesBoundedChar;
}
}
else
{
possition = ECharPossition.StartingChar;
}
}
char currentChar = Convert.ToChar(text.Substring(i, 1));
var query = from cm in CharacterMap
where cm.StartChar == currentChar ||
cm.MiddleRightSideBoundedChar == currentChar ||
cm.MiddleLeftSideBoundedChar == currentChar ||
cm.MiddleTwoSideBoundedChar == currentChar ||
cm.EndingBoundedChar == currentChar ||
cm.EndingChar == currentChar
select cm;
PersianCharacter pch = query.First();
if (pch != null)
{
switch (possition)
{
case ECharPossition.StartingChar:
str2 = str2 +
Convert.ToChar(pch.MiddleTwoSideBoundedChar).ToString();
break;
case ECharPossition.MiddleRightSideBoundedChar: ;
str2 = str2 +
Convert.ToChar(pch.EndingBoundedChar).ToString();
break;
case ECharPossition.MiddleLeftSideBoundedChar:
str2 = str2 +
Convert.ToChar(pch.MiddleTwoSideBoundedChar).ToString();
break;
case ECharPossition.MiddleTwoSidesBoundedChar:
str2 = str2 +
Convert.ToChar(pch.EndingBoundedChar).ToString();
break;
case ECharPossition.EndingBoundedChar:
str2 = str2 +
Convert.ToChar(pch.MiddleLeftSideBoundedChar).ToString();
break;
case ECharPossition.EndingChar:
str2 = str2 + Convert.ToChar(pch.EndingChar).ToString();
break;
default:
break;
}
}
}
else
{
str2 = str2 + text.Substring(i, 1);
}
i++;
}
return this.ReverseString(str2);
}
public string ToPersian(string text)
{
string str = "";
string[] strArray0 = text.Split('\n');
for (int j = strArray0.Length - 1; j >= 0; j--)
{
string[] strArray1 = strArray0[j].Split(new char[] { ' ' });
int upperBound = strArray1.GetUpperBound(0);
for (int i = 0; i <= upperBound; i++)
{
str = this.ToPersianString(strArray1[i]) + " " + str;
}
str = "\n" + str;
}
str.Trim();
return str;
}
private string ReverseString(string str)
{
string str2 = "";
for (int i = str.Length - 1; i >= 0; i--)
{
if (IsPersianCharacter(Convert.ToChar(str.Substring(i, 1))))
{
str2 = str2 + str.Substring(i, 1);
}
else
{
str2 = str.Substring(i, 1) + str2;
}
}
return str2;
}
private enum ECharPossition
{
StartingChar,
MiddleRightSideBoundedChar,
MiddleLeftSideBoundedChar,
MiddleTwoSidesBoundedChar,
EndingBoundedChar,
EndingChar
}
}
Using the code
The source code of this project is available here.