Click here to Skip to main content
Click here to Skip to main content
Go to top

Persian (Farsi) Support for Silverlight 2.0

, 21 Oct 2008
Rate this:
Please Sign up or sign in to vote.
This is an article on how to support Persian language in Microsoft Silverlight 2.0.

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
{
    // Persian character map
    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 

    }

    /// <summary>
    /// Check whether the character belongs to the Persian character list or not.
    /// </summary>
    /// <param name="ch"></param>
    /// <returns>true if the character belongs to the
    /// Persian character list; otherwise, false.</returns>
    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;
    }

    /// <summary>
    /// Check whether the character is from NoLastAppend group or not.
    /// </summary>
    /// <param name="ch"></param>
    /// <returns>true if the character is from NoLastAppend group;
    /// otherwise, false.</returns>
    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;
    }

    /// <summary>
    /// Generates a correct Persian text to be presented
    /// in the silverlight context.
    /// </summary>
    /// <param name="text"></param>
    /// <returns>Persian correct format of the text</returns>
    private string ToPersianString(string text)
    {
        int i = 0;
        string str2 = "";
        while (i < text.Length)
        {
            ECharPossition possition= ECharPossition.StartingChar;
            // get current letter
            if (IsPersianCharacter(Convert.ToChar(text.Substring(i, 1))))
            {
                if (i == 0)// This is the first chraracter of the string
                {
                    possition = ECharPossition.StartingChar;
                }
                else if (i == (text.Length - 1))
                // This is the last character of the string
                {
                    // get letter before
                    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 // the current character is neither first nor last
                {
                    // get letter before
                    if (IsPersianCharacter(
                           Convert.ToChar(text.Substring(i - 1, 1))))
                    {
                        if (IsNoLastAppendCharacter(
                              Convert.ToChar(text.Substring(i - 1, 1))))
                        {
                            // get current letter
                            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;
                    }
                }

                // get current letter
                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;
    
                //Retreive the character set of the current letter
                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
            {
                // The character does not belong to the CharMap
                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;
    }

    /// <summary>
    /// Reverse the specified string to become right to left.
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    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.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

Ali Daneshmandi
Software Developer DAN
Canada Canada
XAML Designer & Developer | Passionate about UX | Founder of www.PortraitPad.com
 
Follow me on Twitter at: @daneshmandi
My website: www.daneshmandi.com
Follow on   Twitter

Comments and Discussions

 
QuestionRight To Left Text Editor with highlighting PinmemberDirkster991-Apr-13 5:44 
GeneralSilverlight 4.0 beta has added BiDi support. PinmemberAli Daneshmandi3-Mar-10 0:51 
GeneralProblems Pinmembermasoudoskuie23-Apr-09 9:42 
GeneralRe: Problems PinmemberAli Daneshmandi24-Apr-09 0:48 
GeneralThank you Pinmemberhasanhd30-Dec-08 12:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140916.1 | Last Updated 22 Oct 2008
Article Copyright 2008 by Ali Daneshmandi
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid