하아찡

[C#/WPF] Language 프로젝트 본문

C#/코인프로그램 - 코드

[C#/WPF] Language 프로젝트

하아찡 2023. 11. 26. 21:45

현재 Language 프로젝트 파일 목록은 위에 사진과 같으며 각종 언어가 추가될경우 폴더를 추가해서 해주면될듯.

기본적으로 기본언어는 지정해두고 언어를 변경했을경우 파일로 저장하여 다음에 프로그램 실행시킬때 해당 파일을 로드시킨 후 저장된 언어로 로드시키는 방식으로 작업했습니다.

각종 단어들은 리소스에 등록하여 불러다 사용했습니다.

 

미리보기(변경되지 않은 텍스들은 영문데이터 등록을 안했습니다.)

 

아래는 Language.cs코드 입니다.

using System.Resources;
using System.Reflection;
using FileIO;

namespace Language
{
    public class Language
    {
        private string DefaultLang = "kr";
        public string LangType { get; private set; } //한글

        //추가된 언어가있을경우 생성자에 추가해줘야함 없을경우 기본설정언어로 출력
        public List<string> Langs;

        private FncFileIO fileIO = new FncFileIO();
        private string FilePath = "Setting";
        private string FileName = "Language";

        #region Event
        public delegate void LangEventHandler();
        public static event LangEventHandler LangChangeEvent;
        #endregion

        private static Language lang;

        public static Language Lang
        {
            get
            {
                if (lang == null) lang = new Language();

                return lang;
            }
        }
        public Language() 
        {

            Langs = new List<string>();
            Langs.Add("kr");
            Langs.Add("en");

            //LangType = DefaultLang;
            if (LoadLang() == false) LangType = DefaultLang;

        }

        #region 리소스 데이터 불러오기
        /// <summary>
        /// 기본적으로 등록된 언어 가져오기
        /// </summary>
        /// <param name="rsname"></param>
        /// <returns></returns>
        private ResourceManager GetResource(string rsname = "")
        {
            string str = "";
            ResourceManager rm = new ResourceManager($"Language.{LangType}.{rsname}", Assembly.GetExecutingAssembly());

            return rm;
        }

        /// <summary>
        /// 공용 언어 가져오기
        /// </summary>
        /// <param name="rsname"></param>
        /// <returns></returns>
        public ResourceManager GetPublicResource(string rsname = "")
        {
            ResourceManager rm = new ResourceManager($"Language.Public.{rsname}", Assembly.GetExecutingAssembly());
            return rm;
        }

        /// <summary>
        /// 언어 변경시 등록된 언어가 없을경우 기본 언어로 로드.
        /// </summary>
        /// <param name="rsname"></param>
        /// <returns></returns>
        private ResourceManager ExceptResource(string rsname = "")
        {
            ResourceManager rm = new ResourceManager($"Language.{DefaultLang}.{rsname}", Assembly.GetExecutingAssembly());
            return rm;
        }

        /// <summary>
        /// 등록된 리소스에서 단어를 가져옴 / 못찾았을경우 해당 리소스 명과 해당 단어 이름을 표시해줌
        /// </summary>
        /// <param name="rsname">리소스명</param>
        /// <param name="word">단어명</param>
        /// <returns></returns>
        public string GetWord(string rsname = "",string word = "")
        {
            string Word = "";
            ResourceManager rm;
            try
            {
                rm = GetResource(rsname);
                Word = rm.GetString(word);
                if(Word is null)
                {
                    rm = ExceptResource(rsname);
                    Word = rm.GetString(word);
                    if (Word is null)
                        Word = $"Error, plz add word, Resource -> {rsname} / Word -> {word}";
                }
            }
            catch (Exception e)
            {
                Word = $"Error, plz add word, Resource -> {rsname} / Word -> {word}";
            }
            
            
            return Word;
        }


        #endregion

        #region Functions
        public void SetLang(string langtype)
        {

            bool Check = Langs.Contains(langtype);

            //선택한 언어가 있을경우
            if (Check)
            {
                this.LangType = langtype;
                SaveLang();

                //언어 변경 이벤트처리
                LangChangeEvent();
            }

            
        }


        /// <summary>
        /// 저장된 언어타입 있을경우 로드 없을경우 기본값으로 설정
        /// </summary>
        /// <returns></returns>
        private bool LoadLang()
        {
            bool b = true;
            try
            {
                string type = fileIO.FileRead<string>(FilePath, FileName);
                if (type is null || type == "")
                {
                    LangType = DefaultLang;
                    SaveLang();
                }
                else
                    LangType = type;

            }
            catch (Exception e) 
            {
                b = false;
            }
            return b;
        }

        /// <summary>
        /// 파일로 언어셋팅저장.
        /// </summary>
        private void SaveLang()
        {
            fileIO.FileWrite<string>(FilePath, FileName, LangType);
        }

        #endregion Functions    
    }
}

 

 

언어 변경시 이벤트처리

위 코드에서 아래 코드는 언어가 변경됐을때 사용하고있는곳에 이벤트를 전달시켜주기위해 사용함.

#region Event
public delegate void LangEventHandler();
public static event LangEventHandler LangChangeEvent;
#endregion

public void SetLang(string langtype)
{

    bool Check = Langs.Contains(langtype);

    //선택한 언어가 있을경우
    if (Check)
    {
        this.LangType = langtype;
        SaveLang();
        //언어 변경 이벤트처리
        LangChangeEvent();
    }

}

/// <summary>
/// 파일로 언어셋팅저장.
/// </summary>
private void SaveLang()
{
    fileIO.FileWrite<string>(FilePath, FileName, LangType);
}

private bool LoadLang()
{
    bool b = true;
    try
    {
        string type = fileIO.FileRead<string>(FilePath, FileName);
        if (type is null || type == "")
        {
            LangType = DefaultLang;
            SaveLang();
        }
        else
            LangType = type;

    }
    catch (Exception e) 
    {
        b = false;
    }
    return b;
}

 

 

리소스안에 데이터 불러오기

리소스안에 있는 데이터를 불러오기위해 ResourceManager를 사용하는데, 사용하기에 앞서 using System.Resources;를 추가해야 사용이 가능해집니다.

여러개의 언어를 등록하기위해서 리소스 경로를 폴더별로 나눠서 등록하는 방식을 사용했습니다.

그래서 아래 코드를 살펴보면

private ResourceManager GetResource(string rsname = "")
{
    string str = "";
    ResourceManager rm = new ResourceManager($"Language.{LangType}.{rsname}", Assembly.GetExecutingAssembly());

    return rm;
}

리소스 이름을 파라미터로 전달받아 사용하는데 LangType에 따라 언어가 변경되어 리소스를 확인해서 값을 전달해줍니다.

 

 

그 후에 GetWord라는 함수를 사용하여 해당 리소스안에있는 텍스트를 불러와서 필요한곳에서 출력 할 수 있게 값을 저장시킵니다.(사실 이거없음 쓰기 매우 귀찮아져서 만듦)

/// <summary>
/// 등록된 리소스에서 단어를 가져옴 / 못찾았을경우 해당 리소스 명과 해당 단어 이름을 표시해줌
/// </summary>
/// <param name="rsname">리소스명</param>
/// <param name="word">단어명</param>
/// <returns></returns>
public string GetWord(string rsname = "",string word = "")
{
    string Word = "";
    ResourceManager rm;
    try
    {
        rm = GetResource(rsname);
        Word = rm.GetString(word);
        if(Word is null)
        {
            rm = ExceptResource(rsname);
            Word = rm.GetString(word);
            if (Word is null)
                Word = $"Error, plz add word, Resource -> {rsname} / Word -> {word}";
        }
    }
    catch (Exception e)
    {
        Word = $"Error, plz add word, Resource -> {rsname} / Word -> {word}";
    }
    
    
    return Word;
}

해당 함수는 내가 불러오는 단어가 해당 리소스에 있을경우엔 불러와서 Return을 시켜주지만 없을경우엔 기본 언어로 불러다사용해주고, 기본 언어리소스에 조차 없을경우엔 Error를 띄워 어떤 리소스에서 어떤 언어를 호출했을때 오류가났는지를 띄워줌.

 

 

 

ILanguage 추가하여 사용한이유

가끔 추가해놓고 이벤트처리를 추가해주지않아서 왜 안될까 해가지고 그냥 추가안했을 경우 오류나게 나만의 족쇄를 걸어버림. 계속 코드에 나올테니 코드는 첨부해두겠습니다.

namespace Language
{
    public interface ILanguage
    {
        //언어 함수를 호출할때 그냥 상속시켜 해당 함수를 필수로 추가하게 만듦. 까먹을까봐
        void SetLanguage();
    }
}

 

 

등록된 리소스데이터 사용

등록된 데이터를 불러다쓰기위해 각 리소스별로 클래스를 만들어두었는데 코드흐름 자체는 똑같아서 예시로 1개만 올리겠습니다.

등록된 리소스데이터

using Prism.Mvvm;

namespace Language.Lang
{
    public class Balance : BindableBase, ILang
    {
        public Balance() 
        {
            UpdateLang();
        }

        public void UpdateLang()
        {
            LBalance = Language.Lang.GetWord("Balance", "_Balance");
            LAvgBuyPrice = Language.Lang.GetWord("Balance", "AvgBuyPrice");
            LCoin = Language.Lang.GetWord("Balance", "Coin");
            LCurrency = Language.Lang.GetWord("Balance", "Currency");
            LLocked = Language.Lang.GetWord("Balance", "Locked");
            LMoney = Language.Lang.GetWord("Balance", "Money");
            LNotAccess = Language.Lang.GetWord("Balance", "NotAccess");
        }

        private string lnotaccess;
        public string LNotAccess
        {
            get { return lnotaccess; }
            set { SetProperty(ref lnotaccess, value); }
        }

        private string lcurrency;
        public string LCurrency
        {
            get { return lcurrency; }
            set { SetProperty(ref lcurrency, value); }
        }

        private string lcoin;
        public string LCoin
        {
            get { return lcoin; }
            set { SetProperty(ref lcoin, value); }
        }
        private string lbalance;
        public string LBalance
        {
            get { return lbalance; }
            set { SetProperty(ref lbalance, value); }
        }
        private string llocked;
        public string LLocked
        {
            get { return llocked; }
            set { SetProperty(ref llocked, value); }
        }
        private string lavgbuyprice;
        public string LAvgBuyPrice
        {
            get { return lavgbuyprice; }
            set { SetProperty(ref lavgbuyprice, value); }
        }

        private string lmoney;
        public string LMoney
        {
            get { return lmoney; }
            set { SetProperty(ref lmoney, value); }
        }
    }
}

 

 public class Balance : BindableBase, ILang 해당 부분에 BindableBase는 Prism에서제공함, 기본 WPF에서 OnPropertyChanged대신 사용하는 아이라고했습니다.

대충 설명하자면 OnPropertyChange -> 해당 데이터가 값이 변경됐을경우 UI쪽에 데이터 변경이 일어난걸 알려줘서 데이터가 갱신되는게 보임. 이게없을경우 아무리 내부에서 데이터값을 바꿔도 UI쪽에 데이터변경이 일어나는게 보이지않기때문에 사용함.(저는 이런식으로 이해하긴했지만 혹시 틀린점이있다면 알려주시면 감사합니다!!)

 

그래서 코드를 보면 해당 리소스에 등록된 데이터를 변수로 만들어두어 해당 단어가 필요한 부분에 클래스를 불러다 사용하는 방식으로 언어를 처리하였습니다.

 

 

 

틀린점이나 혹은 내용에 누락되어 궁굼한점있으시면 댓글남겨주세요!

반응형