Skip to content

Instantly share code, notes, and snippets.

@Osinko
Last active October 14, 2018 14:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Osinko/97b8f69797ddafbfe1c380c2c8a79976 to your computer and use it in GitHub Desktop.
Save Osinko/97b8f69797ddafbfe1c380c2c8a79976 to your computer and use it in GitHub Desktop.
HatInTimeの翻訳を支援するunity用のプログラム
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Text.RegularExpressions;
using System.Linq;
public class CharSetHelper : MonoBehaviour
{
void Start()
{
string reffolder = @"C:\Users\owner_pc3\Desktop\labo\ref"; //参照フォルダ(この場合、HatInTimeの元データがあるフォルダ。オリジナル保護の為intフォルダのコピーを推奨)
string workfolder = @"C:\Users\owner_pc3\Desktop\labo\work"; //翻訳をある程度進めているデータを収めたフォルダ
string outputfolder = @"C:\Users\owner_pc3\Desktop\labo\output"; //出力先のフォルダ
string serchfolder = @"C:\Users\owner_pc3\Desktop\labo\serch";
List<string> debugLog = new List<string>();
List<string> serchLog = new List<string>();
List<string> refList = new List<string>();
List<string> workList = new List<string>();
List<string> serchList = new List<string>();
//フォルダ内のファイルをリスト化
refList.AddRange(System.IO.Directory.GetFiles(reffolder, "*", System.IO.SearchOption.AllDirectories));
workList.AddRange(System.IO.Directory.GetFiles(workfolder, "*", System.IO.SearchOption.AllDirectories));
serchList.AddRange(System.IO.Directory.GetFiles(serchfolder, "*", System.IO.SearchOption.AllDirectories));
//refフォルダ内の該当ファイルがwork側にあるか確認。ない場合はoutputにref側のファイルをコピーする。
debugLog.AddRange(refList);
debugLog.AddRange(workList);
foreach (string refItem in refList.ToArray()) //Tips:コピー済みのファイルはrefListから除外するためIEnumerableの複製を渡している
{
bool onMatch = false;
foreach (string workItem in workList)
{
if (refItem.Remove(0, reffolder.Length + 1).Contains(workItem.Remove(0, workfolder.Length + 1)))
{
debugLog.Add(string.Format("マッチした。{0} {1}", refItem, workItem));
onMatch = true;
break;
}
}
if (!onMatch)
{
//切り貼りして加工
string filename = string.Format(outputfolder + refItem.Remove(0, reffolder.Length));
//フルファイル名からのファイル名抽出の正規表現。$は改行前の末尾。\w+はワードの塊。拡張子付きのファイル名前提
string folder = Regex.Replace(filename, @"\w+.\w+$", "");
//フォルダ作成
if (!Directory.Exists(folder))
{
DirectoryInfo di = new DirectoryInfo(folder);
di.Create();
}
//ファイルコピー
FileInfo fi = new FileInfo(refItem);
fi.CopyTo(filename, true);
debugLog.Add("ワークフォルダに同名ファイルを見つけられなかったので " + refItem + " を出力フォルダにコピーしました");
//除外リストを作成
refList.Remove(refItem);
}
}
//各正規表現の意味
//文字列の先頭で[と一致。この場合マルチラインモードにしておく必要がある。[はエスケープシーケンスの\で認識させる必要がある。
//()でグループ化する。wで広範囲なワードキャラクタを指し+で無制限に文字数を許可。
//]の後ろに必ずキャリッジリターンがある事を指定。
var headRegex = new Regex(@"^\[(?<head>\w+|\w+\.\w+)\]\r\n", RegexOptions.Multiline);
var tagCodeRegex = new Regex(@"(?<tag>\w+)\s?=\s?(?<code>.*$)", RegexOptions.Multiline);
//正規表現内で記号をエスケープシーケンスする為のもの
//正規表現内で()で囲むことによりグループ化して$1で取り出している。正規表現[]内で1文字として?や\[、\]等をターゲットにしている
var escSQ = new Regex(@"([?\[\]\(\)\*\+\,])", RegexOptions.Multiline);
string escReplace = @"\$1";
//リファレンスファイルの内容を元にワークのcodeに置き換え保存していく。workにあってrefに無いものは無視する
foreach (string refFileName in refList)
{
//テキスト・ファイル全体を1つの文字列に読み込む(改行コードも中に内包する)
string refText = File.ReadAllText(refFileName, System.Text.Encoding.GetEncoding("Shift_JIS"));
string workText = File.ReadAllText(
workList[workList.FindIndex(s => s.EndsWith(refFileName.Remove(0, reffolder.Length)))],
System.Text.Encoding.GetEncoding("Shift_JIS"));
MatchCollection refHeadMatchCollection = headRegex.Matches(refText);
MatchCollection refTagCodeMatchCollection = tagCodeRegex.Matches(refText);
MatchCollection workHeadMatchCollection = headRegex.Matches(workText);
MatchCollection workTagCodeMatchCollection = tagCodeRegex.Matches(workText);
//扱いやすいようにコレクション化
List<ScriptCollection> refCollect = Collect(refText, refHeadMatchCollection, refTagCodeMatchCollection);
List<ScriptCollection> workCollect = Collect(workText, workHeadMatchCollection, workTagCodeMatchCollection);
string cloneStr = string.Copy(refText);
//書き換え処理
foreach (ScriptCollection workItem in workCollect)
{
//ヘッドタグの英字の大文字、小文字の違いを無視して照合
ScriptCollection refItem = refCollect.Find(x => x.header.Value == workItem.header.Value);
if (refItem != null)
{
foreach (Match workMc in workItem.IE_tag)
{
Match refMc = refItem.IE_tag.Find(x => x.Groups["tag"].Value == workMc.Groups["tag"].Value);
if (refMc != null)
{
//どうしても正規表現の[^]内に否定の一文字を設定する必要があるため、ほとんど利用しないロシア文字「Ѭ」を選んで設定している
//正規表現内で?や[等を\?等にする必要があるのでリプレース
Regex rx = new Regex(
@"\[" +
escSQ.Replace(workItem.header.Value, escReplace) + //取り出したデーターをエスケープシーケンス処理
@"\][^Ѭ]+" +
escSQ.Replace(refMc.Groups["tag"].Value, escReplace) +
@"\s?=\s?" +
escSQ.Replace(refMc.Groups["code"].Value, escReplace));
//ヘッダを親としたタグ+コードのテキスト部分を抽出(ヘッダ違いの同名タグの書き換えを防ぐ為)
string parts = rx.Match(cloneStr).Value;
if (parts == null)
{
debugLog.Add("正規表現エラー:" + refText);
}
//ref→workに置換
Regex rx2 = new Regex(
escSQ.Replace(refMc.Groups["tag"].Value, escReplace) +
@"\s?=\s?" +
escSQ.Replace(refMc.Groups["code"].Value, escReplace));
string repParts = rx2.Replace(parts, refMc.Groups["tag"].Value + " = " + workMc.Groups["code"].Value);
if (repParts == null)
{
debugLog.Add("正規表現エラー:" + refText);
}
cloneStr = rx.Replace(cloneStr, repParts);
}
else
{
debugLog.Add("tag抜けアリ:workにあってrefに無いtagを見つけました。workの内容はoutputに反映されない:" + workMc.Groups["tag"].Value);
}
}
}
else
{
debugLog.Add("[head]抜けアリ:workにあってrefに無い[head]を見つけました。workの内容はoutputに反映されない:[" + workItem.header.Value + "]");
}
}
//変換したテキストを出力フォルダに保存
string fullFilename = string.Format(outputfolder + refFileName.Remove(0, reffolder.Length));
string folder = Regex.Replace(fullFilename, @"\w+.\w+$", "");
//フォルダが無ければ作成
if (!Directory.Exists(folder))
{
DirectoryInfo di = new DirectoryInfo(folder);
di.Create();
}
//変換後のファイルセーブ
SaveText(folder, fullFilename.Remove(0, folder.Length), cloneStr);
debugLog.Add("変換後ファイルセーブ:" + fullFilename);
}
//デバッグログ出力
SaveText(outputfolder, @"\debuglog.txt", debugLog);
//機能追加:台詞表示が化ける事を防いでデバッグしやすくするためのログを吐き出す機能を追加 2018_10_14
//検索文字列の中から不要なタグを省くためのものを機能を追加した
var cutRegex = new Regex(@"\[(\/?\w+\:?\w+)\]");
//HatInTimeのフォントとして組み込まれているキャラセット。それら以外を検出するので反転「[^]」を入れて正規表現する
//キャリッジリターンと記号類の扱いに注意が必要。キャリッジリターンは必ず組み込む必要があり、記号は適時エスケープシーケンスで表現する必要がある
string charset = "[^" + "\r\n\"::.  !\"#$%&’()*+,-./:;<=>?@_‘{|}~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzこの望遠鏡でくま見えるようになぞ!" +
"ハードウス鳥たちが、光モノを盗むんだバはっもすぐ気つけろ中古い橋あ。ボク前登時かり傷作らそ頼きねやどて子べ場所お来頂上行道ホルン続ほ宝砂計落と思卵何ヘじゃ?" +
"吹新しト開一番ラケキご熱右ばれ体黄昏鐘届さ風車大変方向角持神々怒立天へ追返勇敢英雄眠音ひづめ塔彼寄せ人間永張終わ…空澄ヤギ正取戻みミ感謝花他探言手伝辺紫色咲距離置" +
"イ山知近絡少厄介自分高薄呼吸辛建物誰明チカ最降ぶ目敵全員倒今回部げ売度嬢私国世界各地発歩ッジセ名通買帽似合強力アビリティプグレ代ナ有益限品悪着治撮影タオ我ぎヴ映画本" +
"ムペ騒書パ野郎躍小銭稼ヒマ孫会年ワ飛び入師フロ警備無能聞者察突出具練習日遅刻厳禁ベ急列西劇ず掌デエ照輝欲家ピ製実内面ふ美ゴソ女待非根役ネ話助必要敗勝違絶対主演決任ポ" +
"下重写真シ顔笑動ァ修昔法使ャョブ仕台先巧妙“ガ”ェ銃燻ニ虚偽箱煙枠語進震岐点割磁石紙枚留量購領収証『コ・』注文紛込配送完了受魚程簡単運術多未払請求惑星数後破壊肖像商" +
"用利許可覚契約サ詳細支並ダ盛意味不切制葉ぜ転競同士協以外特メ乗客早事ょ停職輩付長別守起指鳴共図「」質問答傑直好得魅経営ぱヨ保険賠償責生忍考捕済借金背負腹座巻第位過譲" +
"悔歴史記録元屋ズ若爆弾電驚室冷怖奪加玉ぉ汚慢健闘閉懸命働係口周臭漂困堪準拠工積清誠頑固拝秘密解除失娘土産消忌赤奴律相評価ゲ頭巾城住愚舞狙弱ツ汗調理料仲溶岩犯罪説弁護" +
"団集ぼ扉糸引暴燃激灰焦緒歓迎邪当島覆頃ゆ暮安連殴救心兄弟拳慮暑水蒸党帰戦情泣次魔死親友腐義教順結果論投替現信魂百優痛普段権威的足恐抑圧耐率訪月楽宇宙船*池嫌―泳景丸勘" +
"初額抱確響与良殺瓶暖街休余裕諦状態獲片悟撃胸店賢男飼渡凍庫白止闇市専予由町広観旅洞令構乱昨芽組吐Z雨傘冗談食巨欠泉基残念避忘ザ誇活貢献朝砲学卒業博号攻海捨ぬ恰樽両ヌ尊" +
"規模移民題舟毎射越疑始息族懇願愉快徹都潜艦飲触暗裂拾夢純奥去験漫塊肉然板議粘放焼ゥ粗末夜飯栄半個性埋即興喜化奇校侵洗呂貨陸迷俺流儀従眺速犬幕恥牛乳丈夫喋泡反坊噛尾危志" +
"関噴途幸祈怪ぁ略券定火御扱牢鎖週母王継承僕姫様公平講愛勉束短暇淑髪毛在秀援績読浮努礼遣退北報酬期歌<>狂包衆披露才ュ恵軍隣堂帯踏科〇炎視聴型維M老姿挑字想覧逃打機服ゼ" +
"精僚太陽絵件導隠滅剣減満犠牲害設輪際唯羽社交差襲横血飢獣偵N妹詰黒式裏諸条醜P局例嬉旧姓幼抜医診兆候十ゅ刺素声複雑標成ォ称駅監深縄査寒妻苦遊静和寂透掃駄戚排潰況木偶歯参" +
"提供類脚薬編走比派貼身衣装選緊迫押青惜瞬呪煩森窒虫匹駆識井戸管昇格ゾ択資哀憑履務判断難署罠項黙沸ぽ異唱屍挽窓常喰召募首叫延荷締沼底郵便達殊効表示勤霊村ざ匂嗅更源復章致" +
"認居冒繰球系草技緑形獄貸()奏曲応耳漠温狡猾ぴ彷彿鮮棚育環境版斎整算ユ膨核争存到補告挙幻極康潔側改善試値検索邦審委申訳波低縛路東南吠葛藤揺微再渓谷適描鍛冶己紹原囲拡釈" +
"武器案乾燥隔七秒往布超鋭趣促拍跡統損壁飾属鉄隙腕振慣階床祭壇宗雰伸晴研究誉棄訴爪裁倍罰席ぷ宿軽遺換染独蔵絞乞叩骸椅遭功縫創尽逝吊倫費曰柔墓掘館枝沈仮茂臆病春接跳&氷捜" +
"剤栓掛衝著縮亡ぺ謎帳畑侮辱塗為希封区担授賞蔓揚鎮祝☆俳陣敏慈悲因脅哨錬丘推奨種操軸容縦狭脈滑酔三等線符%含左垂象討彩盲幅被該蝶傾斜依増遮節歪縁処剛級般誤削珍#踊妨督誘粋" +
"造猫給礁夷納林厚忙植寝械" + "]";
var charCheckRegex = new Regex(charset);
//後日、機能追加部分
//キャラセットから文字化けが出る台詞を検出する(翻訳後の台詞表示バグのデバッグ用)
foreach (string serchFileName in serchList)
{
//テキスト・ファイル全体を1つの文字列に読み込む(改行コードも中に内包する)
string serchText = File.ReadAllText(serchFileName, System.Text.Encoding.GetEncoding("Shift_JIS"));
MatchCollection serchHeadMatchCollection = headRegex.Matches(serchText);
MatchCollection serchTagCodeMatchCollection = tagCodeRegex.Matches(serchText);
//扱いやすいようにコレクション化
List<ScriptCollection> serchCollect = Collect(serchText, serchHeadMatchCollection, serchTagCodeMatchCollection);
foreach (ScriptCollection serchItem in serchCollect)
{
if (serchItem != null)
{
foreach (Match serchMc in serchItem.IE_tag)
{
string code = cutRegex.Replace(serchMc.Groups["code"].Value, "");
if (charCheckRegex.IsMatch(code))
{
Match match = charCheckRegex.Match(code);
serchLog.Add(string.Format("ファイル:{0}\nタグ:{1}\n検出文字:{2}\n", serchFileName, serchMc.Groups["tag"].Value, match.Value));
}
}
}
}
SaveText(outputfolder, @"\serchlog.txt", serchLog);
}
}
private static List<ScriptCollection> Collect(string text, MatchCollection HeadMatchCollection, MatchCollection TagCodeMatchCollection)
{
List<ScriptCollection> temp = new List<ScriptCollection>();
//workファイル内のスクリプトコレクションリストを作成する。Headを親としてtagとcodeを子に関連付ける
for (int i = 0; i < HeadMatchCollection.Count; i++)
{
//ヘッダの有効範囲のインデックスを作成
int forwardIndex = HeadMatchCollection[i].Groups["head"].Index;
int backIndex = (HeadMatchCollection.Count - 1 == i) ? text.Length : HeadMatchCollection[i + 1].Groups["head"].Index;
//ヘッダ毎に従ってコレクションを収納
ScriptCollection scriptCollection = new ScriptCollection();
scriptCollection.header = HeadMatchCollection[i].Groups["head"];
scriptCollection.IE_tag = TagCodeMatchCollection.Cast<Match>().Where(x => x.Groups["tag"].Index > forwardIndex && x.Groups["tag"].Index < backIndex).ToList();
temp.Add(scriptCollection);
}
return temp;
}
//テキストファイルとしてセーブ
public void SaveText(string fileFolder, string filename, IEnumerable dataStr)
{
using (StreamWriter w = new StreamWriter(fileFolder + filename, false, System.Text.Encoding.GetEncoding("Shift_JIS")))
{
foreach (var item in dataStr)
{
w.WriteLine(item.ToString());
}
}
}
public void SaveText(string fileFolder, string filename, string dataStr)
{
using (StreamWriter w = new StreamWriter(fileFolder + filename, false, System.Text.Encoding.GetEncoding("Shift_JIS")))
{
w.WriteLine(dataStr);
}
}
public class ScriptCollection
{
public Group header { get; set; }
public List<Match> IE_tag { get; set; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment