【PowerShell】Google 日本語入力の単語辞書を力業でクラウド同期する
Google 日本語入力でこのような感じの単語登録を無節操に繰り返した結果、気づけばユーザー辞書が1000件を超えていました。
在宅勤務も導入され、会社PCとの間で辞書同期のためにエクスポートとインポートを都度するのも手間なので、色々試行錯誤して PowerShell から無理やり辞書を同期させてみました。
やっていること
- Google 日本語入力の設定ファイルをクラウド(今回の場合は Dropbox)にコピーする
- ローカルにある本来の設定ファイルとの間でタイムスタンプを比較
- もしローカルファイルのほうが新しければ、ローカルをクラウドにアップロードする
- もしクラウドのファイルのほうが新しければ、クラウドをローカルにコピーする
- 上記処理を PowerShell の
prompt
関数に仕込んでコマンドを実行するたびにチェックする
実際のイメージ
前提確認
環境
> $PSVersionTable
Name Value
---- -----
PSVersion 7.1.3
PSEdition Core
GitCommitId 7.1.3
OS Microsoft Windows 10.0.19042
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
Google 日本語入力
- バージョン:
2.25.3700.0
- 辞書等設定ファイルのディレクトリ:
C:\Users\(ユーザー名)\AppData\LocalLow\Google\Google Japanese Input
- 最初、このディレクトリをまるっとクラウドに置いて、シンボリックリンクなりジャンクションなりをローカルにおけば万事解決ではと思ったのですが、実際にやってみたところ「データを読み込めません」的なエラーを吐いてしまいました。無念。
- 辞書と全般的な設定は
config1.db
user_dictionary.db
に格納されている模様。
事前準備
クラウド側
辞書ファイルをアップロードするディレクトリを作っておきます(今回は (Dropbox のパス)\IME_google\db
)。
次に、そのディレクトリ内に .history
というサブディレクトリを用意します。詳細は後述。
クラス作成
$PROFILE
に下記のクラスを書き込んでおきます。
Class GoogleIME {
static [PSCustomObject[]] Check () {
return (@("config1.db", "user_dictionary.db") | ForEach-Object {
$local = "C:\Users\{0}\AppData\LocalLow\Google\Google Japanese Input" -f $env:USERNAME | Join-Path -ChildPath $_ | Get-Item
$cloud = "C:\Users\{0}\Dropbox\IME_google\db" -f $env:USERNAME | Join-Path -ChildPath $_ | Get-Item
$require = "NOTHING"
if ($local.LastWriteTime -gt $cloud.LastWriteTime) {
$require = "UPLOAD"
}
elseif ($local.LastWriteTime -lt $cloud.LastWriteTime) {
$require = "DOWNLOAD"
}
return [PSCustomObject]@{
"Name" = $_;
"Require" = $require;
}
})
}
static [void] Backup ([System.IO.FileInfo]$dbFile) {
$ts = Get-Date -Format "yyyyMMddHHmmssff"
$backupDir = $dbFile.Directory.FullName | Join-Path -ChildPath $(".history\{0}_{1}.db" -f $dbFile.Basename, $ts)
$dbFile | Copy-Item -Destination $backupDir
}
static [void] Refresh () {
Get-Process | Where-Object ProcessName -In @("GoogleIMEJaConverter", "GoogleIMEJaRenderer") | ForEach-Object {
$path = $_.Path
Stop-Process $_
Start-Process $path
}
}
static [void] Sync ([string]$mode, [string]$name) {
$local = "C:\Users\{0}\AppData\LocalLow\Google\Google Japanese Input" -f $env:USERNAME | Join-Path -ChildPath $name | Get-Item
$cloud = "C:\Users\{0}\Dropbox\IME_google\db" -f $env:USERNAME | Join-Path -ChildPath $name | Get-Item
$origin, $dest = switch ($mode) {
"UPLOAD" {
@($local, $cloud); break;
}
"DOWNLOAD" {
@($cloud, $local); break;
}
}
if ($origin.LastWriteTime -gt $dest.LastWriteTime) {
if ($mode -eq "UPLOAD") {
[GoogleIME]::Backup($dest)
}
$origin | Copy-Item -Destination $dest
if ($mode -eq "DOWNLOAD") {
[GoogleIme]::Refresh()
}
}
}
}
別にクラスの静的メソッドの代わりに関数でも良いのですが、メソッドにしておいたほうが目的(クラス名)と処理(メソッド名)が区別しやすくて好きです。
個人的には、
- パイプラインの途中でフィルタ的に使うもの →関数
- 単一処理 →静的メソッド
という感じで使い分けたりしています。
以下、各メソッドの解説。
[GoogleIME]::Check()
static [PSCustomObject[]] Check () {
return (@("config1.db", "user_dictionary.db") | ForEach-Object {
$local = "C:\Users\{0}\AppData\LocalLow\Google\Google Japanese Input" -f $env:USERNAME | Join-Path -ChildPath $_ | Get-Item
$cloud = "C:\Users\{0}\Dropbox\IME_google\db" -f $env:USERNAME | Join-Path -ChildPath $_ | Get-Item
$require = "NOTHING"
if ($local.LastWriteTime -gt $cloud.LastWriteTime) {
$require = "UPLOAD"
}
elseif ($local.LastWriteTime -lt $cloud.LastWriteTime) {
$require = "DOWNLOAD"
}
return [PSCustomObject]@{
"Name" = $_;
"Require" = $require;
}
})
}
config1.db
と user_dictionary.db
それぞれについてクラウドのコピーとタイムスタンプを比較して、必要なのが UPLOAD
か DOWNLOAD
かを Require
の値として返します。
最新の状態に同期されていれば Require
の値は NOTHING
を返すようにしました。
[GoogleIME]::Backup()
static [void] Backup ([System.IO.FileInfo]$dbFile) {
$ts = Get-Date -Format "yyyyMMddHHmmssff"
$backupDir = $dbFile.Directory.FullName | Join-Path -ChildPath $(".history\{0}_{1}.db" -f $dbFile.Basename, $ts)
$dbFile | Copy-Item -Destination $backupDir
}
更新されたローカルファイルをクラウドにアップロードする仕様は、PCを新調したときや Google 日本語入力をインストールし直したときに問題になります。その場合、ローカルのまっさらな辞書データで、これまで育ててきたクラウドの辞書データを上書きして消し飛ばしてしまうので、その対策としてアップロード前に .history
へ直前の内容をコピーするようにしています。
[GoogleIME]::Refresh()
static [void] Refresh () {
Get-Process | Where-Object ProcessName -In @("GoogleIMEJaConverter", "GoogleIMEJaRenderer") | ForEach-Object {
$path = $_.Path
Stop-Process $_
Start-Process $path
}
}
クラウドから最新の設定ファイルを読み込んだ場合も、すぐにはIMEに反映されません。
GoogleIMEJaConverter
と GoogleIMEJaRenderer
のプロセスを再起動して初めて設定ファイルの内容が適用されるようです。この仕様を発見するまでは辞書を同期するたびに PC を再起動していました。
[GoogleIME]::Sync()
static [void] Sync ([string]$mode, [string]$name) {
$local = "C:\Users\{0}\AppData\LocalLow\Google\Google Japanese Input" -f $env:USERNAME | Join-Path -ChildPath $name | Get-Item
$cloud = "C:\Users\{0}\Dropbox\IME_google\db" -f $env:USERNAME | Join-Path -ChildPath $name | Get-Item
$origin, $dest = switch ($mode) {
"UPLOAD" {
@($local, $cloud); break;
}
"DOWNLOAD" {
@($cloud, $local); break;
}
}
if ($origin.LastWriteTime -gt $dest.LastWriteTime) {
if ($mode -eq "UPLOAD") {
[GoogleIME]::Backup($dest)
}
$origin | Copy-Item -Destination $dest
if ($mode -eq "DOWNLOAD") {
[GoogleIme]::Refresh()
}
}
}
これまでの内容を組み合わせて、実際にファイルのコピー操作をするメソッドです。コピー対象のファイル名と操作( UPLOAD
/ DOWNLOAD
)を指定します。
Prompt に組み込む
これまで書いてきたメソッドを Prompt
に組み込みます。
最近ハマっている ANSI エスケープシークエンスで文字に色を付けてみました。PowerShell の Write-Host
で -ForegroundColor
を指定しても同じです。
function prompt {
[GoogleIME]::Check() | Where-Object {$_.Require -ne "NOTHING"} | ForEach-Object {
$ansiIdx = @{
"UPLOAD" = 10 <# Green #>;
"DOWNLOAD" = 6 <# Blue #>;
}[$_.Require]
"[Google IME] `e[38;5;{0}m{1}`e[0m is required on '{2}'!`n==> Execute Sync?" -f $ansiIdx, $_.Require, $_.Name | Write-Host -NoNewline
if ((Read-Host -Prompt "(y/n)") -eq "y") {
[GoogleIME]::Sync($_.Require, $_.Name)
"`e[38;5;{0}m{1} completed: '{2}'`e[0m" -f $ansiIdx, $_.Require, $_.Name | Write-Host
}
else {
"skipped {0} of '{1}'." -f $_.Require, $_.Name | Write-Host -ForegroundColor Magenta
}
}
return "#> "
}
単語登録 Tips
敬語の語幹を単語登録しておくと便利です。「あmす」みたいなタイポもあらかじめ登録しておくと高速化できますね。
丸数字を登録しておくのも便利です。
Google 日本語入力にはコマンドラインオプションがあり、辞書ツールや単語登録ツールを直接呼び出すことができます。
本体のパスは C:\Program Files (x86)\Google\Google Japanese Input\GoogleIMEJaTool.exe
で、オプションは下記の通り。
- 単語登録モードで起動
--mode=word_register_dialog
- 辞書ツールを起動
--mode=dictionary_tool
個人的には keyhac を使ってホットキーを割り当てるようにしたらスムーズでした(そのせいで1000個も辞書登録してしまったわけですが…)。
Discussion