Tales Saga Chronicle Blast 개발일지 23 - 일본어 로컬라이징
현재 개발한 부분에서 어떤 활동을 할 수 있을지 고민해보았다. 일본어 로컬라이징을 계획했었기 때문에 그 부분을 구현해보기로 했다.
private void InitializeDialog()
{
if(PlayerPrefs.GetString("Language") == "Korean")
{
// 한국어 스토리 내용 추가
_storyManager.AppendDialog("???", "", "........", 0.25f); // 0
_storyManager.AppendDialog("???", "", "......님!", 0.5f);
_storyManager.AppendDialog("???", "", "선생님!", 1f);
// 이하 생략
}
else if (PlayerPrefs.GetString("Language") == "Japanese")
{
// 일본어 스토리 내용 추가
_storyManager.AppendDialog("???", "", "........", 0.25f); // 0
_storyManager.AppendDialog("???", "", "......生!", 0.5f);
_storyManager.AppendDialog("???", "", "先生!", 1f);
// 이하 생략
}
}
기존에 로컬라이징을 염두에 두고 만들었기 때문에 스토리 수정은 크게 어렵지 않았다. DialogManager.cs에서 Japanese 부분에 일본어로 번역하여 텍스트를 넣어주기만 하면 되었다. 단, 스크립트 자체에 일본어를 넣으면서 깨지는 글자가 발생해 스크립트를 UTF-8로 인코딩하여 저장해주어야 했다.
LikeFont-オンライン画像フォント識別サイト
ゲスト、こんにちは。 識別記録を保存するために、 - 第三者登録 - - おすすめツール -
ja.likefont.com
이후에는 블루 아카이브의 폰트가 어떤 것인지 찾아보았다. 일본에도 텍스트 이미지를 통해 폰트를 찾아주는 사이트가 있었다. 약 85%의 가장 높은 일치율을 가진 X-ShinYuan-Medium 라는 폰트를 사용했다.
極限新圓 M Preview
Full name:X-ShinYuan-Medium,極限新圓-Medium;Font family:X-ShinYuan,X-ShinYuan M,極限新圓 M;Style:M,Bold;Version:Preview;PostScript name:X-ShinYuan-Medium,極限新圓-Medium;Unique font identifier:X-ShinYuan-Medium,極限新圓-Medium
eng.m.fontke.com
이후, 한국어와 동일하게 TextMeshoPro의 기능을 사용해서 SDF 파일을 만들고 적용했다. 찾아보니 한국어를 기본으로 사용하고, 없는 글자는 서브 폰트에 있는 글자를 가져오는 기능도 있었는데, 한국어 범위 내에 히라가나가 포함되어 있어 폰트가 일정하지 않은 문제나, 글씨체에 따라 글자의 위치가 바뀌는 등의 이슈가 있어서 폰트는 따로 적용하기로 하였다.
private void DoLocalizing(TMP_Text textObject, TMP_FontAsset font, string text = "", float yPosition = -10)
{
textObject.font = font;
textObject.text = text;
textObject.GetComponent<RectTransform>().anchoredPosition = new Vector2(textObject.GetComponent<RectTransform>().anchoredPosition.x, textObject.GetComponent<RectTransform>().anchoredPosition.y + yPosition);
}
이를 위해 DoLocalizing()이라는 함수를 만들어서 TMP_Text 객체, 폰트를 받아서 적용하고, 필요에 따라 변경할 텍스트나 텍스트의 y포지션 값을 조정하도록 했다.
// 게임이 처음부터 시작할 때 오프닝을 보여줌
private IEnumerator DoOpening()
{
// 생략
if (PlayerPrefs.HasKey("Language"))
{
if (PlayerPrefs.GetString("Language").Equals("Korean"))
{
_canvas.transform.Find("Option/OptionWindow/Language/KoreanBtn/Selected").gameObject.SetActive(true);
_canvas.transform.Find("Option/OptionWindow/Language/JapaneseBtn/Selected").gameObject.SetActive(false);
_canvas.transform.Find("Story/Episode/Dialog/CharacterName").GetComponent<RectTransform>().anchoredPosition = new Vector2(-622, -266);
_canvas.transform.Find("Story/Episode/Dialog/DepartmentName").GetComponent<RectTransform>().anchoredPosition = new Vector2(-405, -279);
_canvas.transform.Find("Story/Episode/Dialog/DialogText").GetComponent<RectTransform>().anchoredPosition = new Vector2(-14, -433);
}
else if (PlayerPrefs.GetString("Language").Equals("Japanese"))
{
_canvas.transform.Find("Option/OptionWindow/Language/KoreanBtn/Selected").gameObject.SetActive(false);
_canvas.transform.Find("Option/OptionWindow/Language/JapaneseBtn/Selected").gameObject.SetActive(true);
GameObject selection1Btn = _storyManager._selection1Btn.transform.GetChild(0).gameObject;
GameObject selection1_1Btn = _storyManager._selection1_1Btn.transform.GetChild(0).gameObject;
GameObject selection1_2Btn = _storyManager._selection1_2Btn.transform.GetChild(0).gameObject;
// 스토리 로컬라이징
DoLocalizing(_storyManager._characterName.GetComponent<TMP_Text>(), _japaneseFontWithUnderlay, "", -24);
_storyManager._characterName.GetComponent<TMP_Text>().characterSpacing = -4.5f;
DoLocalizing(_storyManager._departmentName.GetComponent<TMP_Text>(), _japaneseFontWithUnderlay, "", -19);
_storyManager._departmentName.GetComponent<TMP_Text>().characterSpacing = -4.5f;
DoLocalizing(_storyManager._dialogText.GetComponent<TMP_Text>(), _japaneseFontWithUnderlay, "", -17);
_storyManager._dialogText.GetComponent<TMP_Text>().characterSpacing = -4.5f;
_storyManager._dialogText.GetComponent<TMP_Text>().lineSpacing = 0;
DoLocalizing(selection1Btn.GetComponent<TMP_Text>(), _japaneseFont);
DoLocalizing(selection1_1Btn.GetComponent<TMP_Text>(), _japaneseFont);
DoLocalizing(selection1_2Btn.GetComponent<TMP_Text>(), _japaneseFont);
DoLocalizing(_storyManager._windowText, _japaneseFont);
DoLocalizing(_storyManager._gameOver2Text, _japaneseFont, "プニプニ:どれだけ剣術を鍛えたところで、我が銃の前では無力……ふっ。");
// 인트로 ~ 메인화면 로컬라이징
DoLocalizing(_guideText, _japaneseFont, "このゲームはNexon Gamesで開発したブルーアーカイブの\nファンメイドゲームで、公式コンテンツではありません。");
DoLocalizing(_startBtn.transform.GetChild(0).GetComponent<TMP_Text>(), _japaneseFontWithUnderlay, "スタート");
DoLocalizing(_optionBtn.transform.GetChild(0).GetComponent<TMP_Text>(), _japaneseFontWithUnderlay, "オプション");
DoLocalizing(_gameQuitBtn.transform.GetChild(0).GetComponent<TMP_Text>(), _japaneseFontWithUnderlay, "ゲーム終了");
DoLocalizing(_option.transform.Find("OptionWindow/Language/Text").GetComponent<TMP_Text>(), _japaneseFont, "言語", -13);
DoLocalizing(_option.transform.Find("OptionWindow/OptionText").GetComponent<TMP_Text>(), _japaneseFont, "オプション", -20);
DoLocalizing(_option.transform.Find("OptionWindow/SFXSlider/Text").GetComponent<TMP_Text>(), _japaneseFont, "効果音", -13);
DoLocalizing(_selectStage.transform.Find("SelectStageWindow/ChapterName").GetComponent<TMP_Text>(), _japaneseFont, "レトロチック・ロマン");
DoLocalizing(_selectStage.transform.Find("SelectStageWindow/ChapterContents").GetComponent<TMP_Text>(), _japaneseFont,
"ゲーム開発部で開発したテイルズ・サガ・クロニクルをプレイすることになりました。「今年のクソゲーランキング1位」のゲームを無事にクリアできましょうか?");
DoLocalizing(_selectStage.transform.Find("SelectStageWindow/ChapterNumber").GetComponent<TMP_Text>(), _japaneseFont, "1章");
DoLocalizing(_selectStage.transform.Find("SelectStageWindow/EpisodeList").GetComponent<TMP_Text>(), _japaneseFont, "エピソードリスト");
DoLocalizing(_selectStage.transform.Find("SelectStageWindow/StageAllClear").GetComponent<TMP_Text>(), _japaneseFont, "すべてのエピソードをクリアしました。");
_selectStage.transform.Find("SelectStageWindow/ChapterContents").GetComponent<TMP_Text>().lineSpacing = 0;
_selectStage.transform.Find("SelectStageWindow/ChapterContents").GetComponent<TMP_Text>().characterSpacing = -5;
DoLocalizing(_selectStage.transform.Find("SelectStageWindow/Stage1/StageName").GetComponent<TMP_Text>(), _japaneseFont, "テイルズ・サガ・クロニクル");
DoLocalizing(_selectStage.transform.Find("SelectStageWindow/Stage2/StageName").GetComponent<TMP_Text>(), _japaneseFont, "???");
DoLocalizing(_selectStage.transform.Find("SelectStageWindow/Stage3/StageName").GetComponent<TMP_Text>(), _japaneseFont, "???");
DoLocalizing(_selectStage.transform.Find("SelectStageWindow/Stage4/StageName").GetComponent<TMP_Text>(), _japaneseFont, "???");
_selectStage.transform.Find("SelectStageWindow/Stage1/StageName").GetComponent<TMP_Text>().characterSpacing = -13;
_selectStage.transform.Find("SelectStageWindow/Stage1/Stage1Enter").GetComponent<Button>().image.sprite = _stageEnterImageJapanese;
_selectStage.transform.Find("SelectStageWindow/Stage2/Stage2Enter").GetComponent<Button>().image.sprite = _stageEnterLockedImageJapanse;
_selectStage.transform.Find("SelectStageWindow/Stage3/Stage3Enter").GetComponent<Button>().image.sprite = _stageEnterLockedImageJapanse;
_selectStage.transform.Find("SelectStageWindow/Stage4/Stage4Enter").GetComponent<Button>().image.sprite = _stageEnterLockedImageJapanse;
DoLocalizing(_canvas.transform.Find("Story/EpisodeStart/Text/ChapterNumber").GetComponent<TMP_Text>(), _japaneseFont, "第1話");
DoLocalizing(_canvas.transform.Find("Story/EpisodeStart/Text/ChapterName").GetComponent<TMP_Text>(), _japaneseFont, "テイルズ・サガ・クロニクル");
}
}
// 이하 생략
기본 설정이 한국어로 되어있어서 이런식으로 처음 시작할 때 일본어로 설정이 되어있으면 각종 텍스트들의 폰트와 내용, 위치 등을 변경해주었다.
이외에도 스토리를 시작할 때 나오는 텍스트라든지, 선택지 등은 언어 설정을 확인해서 내용을 변경하는 식으로 적용했다.
작업하면서 한국어로 실행할 때도 일본어 관련 정보를 가지고 있고, 체크를 해야하는 등의 문제가 있어서 별로 효율적이지 않다고 느꼈다. 내가 개발한 프로그램은 볼륨이 그렇게 크지 않고 용량을 줄일 필요성이 크지 않기 때문에 따로 작업을 하진 않았지만, 실제로 게임을 개발해서 출시한다면 이런 부분도 고려하고 수정해서 최대한 용량이나 번거로운 작업을 줄여야겠다고 생각했다.
따로 LanguageManager.cs 혹은 JapaneseManager.cs 등의 파일을 만들어서 시작할 때 한 번만 체크하고, 이 안에서 로컬라이징 관련 작업을 수행하도록 하면 될 것 같다. 다만 지금은 소요되는 시간에 비해 이런 작업의 필요성이나 효용성이 낮다고 생각하여 작업하지 않기로 했다.
작업을 하면서 이미지 리소스를 일본어로 교체해야 하는 경우가 있었는데, 바로 스테이지 입장 버튼이다. 다행히 2개의 입장 버튼을 제외하면 다른 부분은 전부 텍스트로만 되어있어 쉽게 적용할 수 있었다. 로컬라이징을 계획하고 있을 때는 글자가 박혀있는 이미지 리소스를 사용하면 작업이 굉장히 번거로워진다고 들었는데, 실제로 그런 것 같다. 앞으로도 이런 부분을 주의해야겠다고 느꼈다.
이외에는 언어 설정이 시작할 때 적용되는 것이기 때문에 언어 설정을 변경하면 게임을 재시작해야한다고 알려줄 필요성이 있었는데, 우선은 게임을 종료하도록 하였다. 나중에 적절한 UI를 찾아서 플레이어에게 알려주도록 개선해야겠다.
결과적으로 완성된 부분은 다음과 같다.
한국어 -> 일본어로 설정을 변경하는 부분도 담았다. 현재 1장까지 번역을 완료했고, 2장은 추후 번역 예정이다. 때문에 일본어 설정으로 시작하면 2장은 진입할 수 없도록 막아놓았다.
다음에는 커스텀 에디터 툴을 만들어보면 좋을 것 같다. 근데 어디에 적용할지가 살짝 애매하다. 처음엔 스토리에 적용할까 생각했었는데, 이미 작업해놓은 부분이 있어서 혹시 그 부분과 충돌이 나지는 않을까 걱정이다. 한 번 건드려봐야겠다.