前置き
この最後で紹介した、「翻訳用の文字列を列挙したExcel」から
自動でstrings.xmlを作成するというので
実際どう作ればいいのかというのをPowershellを使って実装してみる
なぜPowerShellなのか
まぁExcel読み込んでテキストファイル吐き出せれば
C#でもPythonでもBrainFuckでもぶっちゃけなんでもいいんだけど
PowerShellはデフォルトでWindowsに搭載されてて
余計な物やエディタをインストールする必要ないので、これがお手軽かなと
macとかは知らん。Python使えばいいんじゃない?(適当)
エディタはサクラエディタ使ってる
(batファイルがctrl+Bで実行できるので)
ファイル構成
LangFileConverterというフォルダを作り、この階層にbatやps1や翻訳用excelをぶち込む
※)hello.ps1はテストで使ったゴミです
想定する翻訳Excel
・app sheet
execute.bat
make_strings_xml.ps1 を実行するための叩き台
PowerShell -ExecutionPolicy RemoteSigned .\make_strings_xml.ps1
pause
make_strings_xml.ps1
まずは全体の処理
# 1つのIDに対する言語データ
class Data
{
[string] $name
[string] $jp
[string] $en
}
# データのリスト
$data_list = New-Object 'System.Collections.Generic.List[Data]'
# 1枚分のシートを読み込む
function ReadSheet( $sheet )
{
# 最終行を取得
$lastRowIndex = $sheet.UsedRange.Rows.Count + 1
# 1行目は無視する
for ($i=2; $i -lt $lastRowIndex; $i++)
{
$id_string = $sheet.name + "_" + $sheet.Cells.Item($i, 1).Text
$data = New-Object Data
$data.name = $id_string
$data.jp = $sheet.Cells.Item($i, 2).Text
$data.en = $sheet.Cells.Item($i, 3).Text
$data_list.Add($data)
}
}
# Excel 読み込み処理
function ReadExcel()
{
$excel = New-Object -ComObject Excel.Application
$book = $null
# 対象となるエクセルの絶対パス
$excel_path = "C:\Users\h\Desktop\LangFileConverter\strings.xlsx"
try
{
$excel.Visible = $false
$excel.DisplayAlerts = $false
# エクセルを開く
$book = $excel.Workbooks.Open($excel_path)
# シートの数だけ処理する
foreach($sheet in $book.Worksheets)
{
ReadSheet $sheet
}
} finally {
if ($book -ne $null) {
[void]$book.Close($false)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($book)
}
# Excelの終了
[void]$excel.Quit()
# オブジェクトの開放
# ApplicationとBookに対して行えばよい
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
}
}
# xml掃き出し処理
function OutputXml()
{
$text_path = "C:\Users\h\Desktop\LangFileConverter\strings.xml"
$output = "<resources>`r`n"
# xml生成
foreach( $d in $data_list )
{
$output += "`t<string-array name=`"$($d.name)`">`r`n"
$output += "`t`t<item>$($d.jp)</item>`r`n"
$output += "`t`t<item>$($d.en)</item>`r`n"
$output += "`t</string-array>`r`n"
}
$output += "</resources>"
#書き出し
Write-Output ( $output | Out-File $text_path -Encoding default )
}
# 実行処理
ReadExcel
OutputXml
一応やろうと思えばオブジェクト指向っぽくかけるとは思うけど
スクリプト言語なので、スクリプト言語っぽく上から下に書きました
# 1つのIDに対する言語データ
class Data
{
[string] $name
[string] $jp
[string] $en
}
# データのリスト
$data_list = New-Object 'System.Collections.Generic.List[Data]'
エクセルそのままでは使いづらいので
使いやすいようにデータ加工するためのクラスとリスト
# Excel 読み込み処理
function ReadExcel()
{
$excel = New-Object -ComObject Excel.Application
$book = $null
# 対象となるエクセルの絶対パス
$excel_path = "C:\Users\h\Desktop\LangFileConverter\strings.xlsx"
try
{
$excel.Visible = $false
$excel.DisplayAlerts = $false
# エクセルを開く
$book = $excel.Workbooks.Open($excel_path)
# シートの数だけ処理する
foreach($sheet in $book.Worksheets)
{
ReadSheet $sheet
}
} finally {
if ($book -ne $null) {
[void]$book.Close($false)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($book)
}
# Excelの終了
[void]$excel.Quit()
# オブジェクトの開放
# ApplicationとBookに対して行えばよい
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
}
}
エクセルを読み込む処理
シートが複数あるので、シート単体への処理を別関数に分ける
# 1枚分のシートを読み込む
function ReadSheet( $sheet )
{
# 最終行を取得
$lastRowIndex = $sheet.UsedRange.Rows.Count + 1
# 1行目は無視する
for ($i=2; $i -lt $lastRowIndex; $i++)
{
$id_string = $sheet.name + "_" + $sheet.Cells.Item($i, 1).Text
$data = New-Object Data
$data.name = $id_string
$data.jp = $sheet.Cells.Item($i, 2).Text
$data.en = $sheet.Cells.Item($i, 3).Text
$data_list.Add($data)
}
}
1枚分のシートを読み込み、DataのListに変換する処理
# xml掃き出し処理
function OutputXml()
{
$text_path = "C:\Users\h\Desktop\LangFileConverter\strings.xml"
$output = "<resources>`r`n"
# xml生成
foreach( $d in $data_list )
{
$output += "`t<string-array name=`"$($d.name)`">`r`n"
$output += "`t`t<item>$($d.jp)</item>`r`n"
$output += "`t`t<item>$($d.en)</item>`r`n"
$output += "`t</string-array>`r`n"
}
$output += "</resources>"
#書き出し
Write-Output ( $output | Out-File $text_path -Encoding default )
}
xmlを吐き出す処理
アプリによっては、xmlではなく、別の形式で文字列を管理する場合もあるので
どういう形式を求めるかによって、ここの処理は別物になると思う
xml形式以外で考えられるのは
C#やJavaとかで「nameをキーにしたDictionary」形式や
json形式での保存とか(webとかで使うのか?そっち方面の知識はない)
toml形式とかもアリかもしれない
実際に吐き出されたxml
<resources>
<string-array name="app_name">
<item>テストアプリケーション</item>
<item>Test Application</item>
</string-array>
<string-array name="system_use_item">
<item>{user}は{itemName}を使った</item>
<item>The {user} used {itemName}.</item>
</string-array>
<string-array name="system_use_item_on_target">
<item>{user}は{target}に{itemName}を使った</item>
<item>The {user} used {itemName} on {target}.</item>
</string-array>
<string-array name="system_player_die">
<item>{player}は力尽きた</item>
<item>The {player} ran out of steam.</item>
</string-array>
<string-array name="npc_tadokoro_koji">
<item>田所浩二</item>
<item>Koji Tadokoro</item>
</string-array>
<string-array name="npc_hasegawa_ryota">
<item>長谷川亮太</item>
<item>Ryota Hasegawa</item>
</string-array>
<string-array name="npc_karasawa_takahiro">
<item>唐澤貴洋</item>
<item>Takahiro Karasawa</item>
</string-array>
<string-array name="npc_iwama_koiti">
<item>岩間好一</item>
<item>Yosikazu Iwama</item>
</string-array>
<string-array name="item_harb">
<item>おハーブ</item>
<item>herb</item>
</string-array>
<string-array name="item_portion">
<item>ポーション</item>
<item>portion</item>
</string-array>
</resources>
あえて実装しなかったもの
新たに言語を追加しても、自動で認識するような設計にはしませんでした
ソースとしてのわかりやすさを優先するため
やろうと思えばできると思うけど、数行処理を追加するだけの手間と天秤にかけて
自動にするための実装コストのほうが高そうなので、個人的にはおすすめできないと思います
参考