Skip to content

外部設定ファイルによるカスタムローマ字かな変換ルールのサポート (kana-rule.conf)#31

Merged
tamo merged 7 commits into
tamo:masterfrom
AquiTCD:feature/kana-rule
Apr 14, 2026
Merged

外部設定ファイルによるカスタムローマ字かな変換ルールのサポート (kana-rule.conf)#31
tamo merged 7 commits into
tamo:masterfrom
AquiTCD:feature/kana-rule

Conversation

@AquiTCD
Copy link
Copy Markdown
Contributor

@AquiTCD AquiTCD commented Apr 11, 2026

概要

AZIKや各種拡張ローマ字入力体系をサポートするため、外部の設定ファイル(kana-rule.conf)からローマ字かな変換ルールを読み込む機能を追加しました。
これにより、ソースコードを変更することなく、ユーザーが自由に独自の変換テーブルを利用できるようになります。

主な変更点

1. エンジン側の拡張 (RomajiConverter)

  • 動的な変換テーブル管理: mBaseRomajiMap(固定)と mCustomRules(外部読み込み)を合成した mEffectiveMap を導入しました。
  • パフォーマンス最適化: getVowel() で毎回 Map を生成していた処理をキャッシュ化(mVowelMap)し、入力時のオーバーヘッドを O(1) に削減しました。
  • 中間ローマ字の動的判定: カスタムルールで追加された新しいプレフィックス(例: AZIKの qn -> )に対しても、正しく「入力中」として認識し、「っ」の生成ロジックが正常に動作するように mIntermediateRomajiSet を動的に再計算する仕組みを導入しました。

2. ファイル管理ロジック (SKKKanaRule)

  • パースロジック: CSV形式(ローマ字,ひらがな[,カタカナ,半角カナ])のパース処理を実装しました。
  • 堅牢性・安全性:
    • OOMガード: 1MBを超えるファイルについては、読み込み・コピー共に行わない制限を追加しました。
    • SecurityException 対応: SAF (Storage Access Framework) 経由でのファイルアクセス時に、パーミッション失効などで発生する例外を適切にキャッチし、クラッシュを防止しました。

3. 設定画面 UI (SKKKanaRuleManager)

  • ViewBinding 導入: 最新の Android 実装パターンに合わせ、ViewBinding に移行しました。
  • ファイル選択機能: SAF を使用したファイル選択ランチャーを実装し、ユーザーが任意の場所から .conf ファイルをインポートできるようにしました。
  • 設定のリセット: ワンタップでデフォルトの変換ルールに戻せる「クリア」機能を追加しました。

4. サンプルファイルの同梱

  • 動作テストおよびユーザーの利便性のため、macSKK の設定をベースにした AZIK対応サンプル (azik-kana-rule.conf)assets に同梱しました。

検証内容

1. ユニットテストの実施

  • RomajiConverterTest: デフォルト変換、カスタムルールによる上書き、中間ローマ字判定、キャッシュの整合性などを網羅。
  • SKKKanaRuleTest: フォーマット異常時の挙動、重複キーの扱い、コメント行のスキップなどを検証。
  • ./gradlew test により、すべてのテストがパスすることを確認済みです。

2. 動作確認

  • Android 実機にて、独自の設定ファイルを読み込ませ、意図した通りの変換が可能であることを確認しました。

AquiTCD added 7 commits April 9, 2026 19:52
…tom rule support

[Context]
The romaji→kana conversion map was a single immutable val. Adding AZIK and
other custom input styles requires runtime-swappable rules.

[Decision Drivers]
- Keep base rules intact as fallback
- mIntermediateRomajiSet and mVowelMap must stay consistent with the active
  rule set; all three fields are updated atomically on the main thread

[Consequences]
- loadCustomRules() / clearCustomRules() are the only public mutation points
- mEffectiveMap = mBaseRomajiMap + mCustomRules (custom wins on conflict)
- mVowelMap cached to avoid per-keystroke allocation in getVowel()
…torage

Provides parse(), loadFromInternalStorage(), saveFromUri(), exists(), clear().
- parse() handles CSV format: input,hiragana[,katakana]; ignores comments and
  blank lines; later entries override earlier ones for the same key
- saveFromUri() queries OpenableColumns.SIZE before reading to guard against
  OOM; catches both IOException and SecurityException from ContentResolver
- loadFromInternalStorage() checks file.length() against MAX_FILE_SIZE to
  guard against hand-placed oversized files bypassing the SAF size check
Follows the same SAF + internal-storage copy pattern as SKKDictManager:
- OpenDocument() launcher for file selection (all MIME types)
- onPause() sends COMMAND_READ_PREFS to reload rules in the running service;
  isModified flag is cleared after notification to avoid redundant restarts
- Uses ViewBinding; menu items: select (ifRoom) / clear (never)
- Preference entry added after the dictionary manager entry in prefs_main.xml
In readPrefs(), load kana-rule.conf from internal storage and pass the parsed
map to RomajiConverter.loadCustomRules(). Clear custom rules when no conf file
is present so toggling back to default works correctly after a clear.
Provides a ready-to-use AZIK layout conf that users can copy to their device
and load via KanaRuleManager. Notable mappings: q→い, ;→ー, single-consonant
shortcuts (e.g. bn→びん). Header comment notes the q/katakana-mode conflict
that will be resolved in the follow-up keybind-customization branch.
SKKKanaRuleTest: valid format, invalid/empty lines, duplicate key last-wins,
empty and comment-only input.

RomajiConverterTest: custom rule override and extend, clearCustomRules reverts
to base, second loadCustomRules replaces previous (not appends), custom
intermediate romaji updates isIntermediateRomaji and checkSpecialConsonants
(っ generation), getVowel reflects custom mappings. @after tearDown() prevents
singleton state from leaking between test classes.
@tamo tamo merged commit fa5a7d8 into tamo:master Apr 14, 2026
@tamo
Copy link
Copy Markdown
Owner

tamo commented Apr 14, 2026

なんだかすごい PR をいただきまして、ありがとうございます!
手元で試そうと思ったら、ついうっかり merge してしまいました
これから試そうと思います〜

# 考案者: 木村 清 氏 (http://hp.vector.co.jp/authors/VA002116/azik/azikindex.html)
# CorvusSKKを元にしたAZIK入力ルール (https://github.com/nathancorvussolis/corvusskk/blob/3.3.0/installer/config-sample/config%20-%20azik-uskbd.xml) を元にした
# macSKK (https://github.com/mtgto/macSKK) の設定ファイルを参考に
# AndroidSKK 用のサンプルとして構成したものですが、さらに元は
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

「さらに元は」で終わっていますので、文章が途中でしょうか?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

すみません、編集途中のままpushしてしまって見落としのようです、申し訳ないです 🙇

Comment thread app/src/main/assets/azik-kana-rule.conf
Comment thread app/src/main/assets/azik-kana-rule.conf
Comment thread app/src/main/java/jp/deadend/noname/skk/engine/RomajiConverter.kt

return first to (when (type) {
SKKEngine.LAST_CONVERSION_SMALL -> (mSmallKanaMap + mReversedSmallKanaMap)[kana]
SKKEngine.LAST_CONVERSION_SMALL -> (mSmallKanaMap + mSmallKMap + mReversedSmallKanaMap)[kana]
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これは私が入れ忘れていただけかな?

Copy link
Copy Markdown
Owner

@tamo tamo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これ、テストするとすれば、普段の SKK のルールをこの形式で作って読み込んで普段使いしてみるのがいいんですかね

tamo added a commit that referenced this pull request Apr 16, 2026
* 消せない baseRomajiMap の上にカスタムを重ねるのではなく、ゼロベースで設定
  * 個人的には dhi のようなローマ字を使わないので、dh の時点で d を捨ててほしいから
* ローマ字かな変換ルールはハードウェアキーボードのための機能なので少し隠す
  * 設定を一気に確認できるよう EditText で表示・編集

- app/build.gradle: テストが asset 内の skk-kana-rule.conf を使用できるようにする
- app/src/androidTest/.../SKKSettingsActivityUITest.kt: ソフトウェアキーボード設定が画面の下に隠れていたのでスワイプしておく
- app/src/main/AndroidManifest.xml: SKKKanaRuleManager ダークテーマ対応
- assets/azik-kana-rule.conf:
  - リンク切れに archive.org で対応
  - 途中ぽい文書にブツ切りで対応
  - 実装に合わせて説明を調整
    - 2要素のみ対応
    - 未確定ローマ字の残しは非対応
    - okuri 非対応
    - ; は対応
    - `z,,‥` は `z,,‥` の誤り
- SKKKanaRule.kt:
  - 既定で skk-kana-rule.conf を読む
  - 関数直前のコメントの行頭が # だとホバーツールチップで大きく表示されるので括弧に
- SKKKanaRuleManager.kt:
  - レイアウト調整 (新しめの Android 用?)
  - ルールを TextEdit にして、onPause で保存
- engine/RomajiConverter.kt:
  - mBaseRomajiMap を廃止して外部 asset の skk-kana-rule.conf に
  - それに合わせて mEffectiveMap は mRomajiMap という名前に戻す
  - skkPrefs.useSmallK は既定で false なので skkPrefs==null でも false 動作に (よく分かっていない)
- engine/SKKHiraganaState.kt: rule.conf の `キー,<shift>別キー` 書式に対応
- layout/activity_kana_rule_manager.xml: 常に conf を使用するので TextView を廃止、EditText 追加
- xml/prefs_hard_key.xml, xml/prefs_main.xml: かなルール管理をハードキー部門下に移動
- test/RomajiConverterTest.kt:
  - tearDown で skk-kana-rule.conf を読むように (これでいいのか分かっていない)
  - useSmallK は false 前提
  - 設定されていないと何も変換しない
@AquiTCD
Copy link
Copy Markdown
Contributor Author

AquiTCD commented Apr 17, 2026

はい、自分の環境では元PRの状態でビルドして macSKK で使ってるのをそのまま読ませて期待どおり動いたのは確認できております
自分の使い方がハードウェアキー利用がメインになるので、ソフトウェアキー側はあまり試せておりません。

あと各コメンントいただいたものもすみません、自分の環境でキーバインド変更や、Ctrl+P, N B, F でキャレット移動などもやってまして、それと合わせた状態のものでPRに入れてしまってレビューにお手間をかけてしまいました。
すでにtamoさんのほうで改変&試そうとしていただいてるようなのでtamoさんの開発・設計方針に合うように改変していただければ助かります 🙇

@tamo
Copy link
Copy Markdown
Owner

tamo commented Apr 30, 2026

では https://github.com/AquiTCD/AndroidSKK/branches にある feature ブランチ群も確認して、入れていけばいいですね〜
ありがとうございます

tamo added a commit that referenced this pull request Apr 30, 2026
* 消せない baseRomajiMap の上にカスタムを重ねるのではなく、ゼロベースで設定
  * 個人的には dhi のようなローマ字を使わないので、dh の時点で d を捨ててほしいから
* ローマ字かな変換ルールはハードウェアキーボードのための機能なので少し隠す
  * 設定を一気に確認できるよう EditText で表示・編集

- app/build.gradle: テストが asset 内の skk-kana-rule.conf を使用できるようにする
- app/src/androidTest/.../SKKSettingsActivityUITest.kt: ソフトウェアキーボード設定が画面の下に隠れていたのでスワイプしておく
- app/src/main/AndroidManifest.xml: SKKKanaRuleManager ダークテーマ対応
- assets/azik-kana-rule.conf:
  - リンク切れに archive.org で対応
  - 途中ぽい文書にブツ切りで対応
  - 実装に合わせて説明を調整
    - 2要素のみ対応
    - 未確定ローマ字の残しは非対応
    - okuri 非対応
    - ; は対応
    - `z,,‥` は `z&comma;,‥` の誤り
- SKKKanaRule.kt:
  - 既定で skk-kana-rule.conf を読む
  - 関数直前のコメントの行頭が # だとホバーツールチップで大きく表示されるので括弧に
- SKKKanaRuleManager.kt:
  - レイアウト調整 (新しめの Android 用?)
  - ルールを TextEdit にして、onPause で保存
- engine/RomajiConverter.kt:
  - mBaseRomajiMap を廃止して外部 asset の skk-kana-rule.conf に
  - それに合わせて mEffectiveMap は mRomajiMap という名前に戻す
  - skkPrefs.useSmallK は既定で false なので skkPrefs==null でも false 動作に (よく分かっていない)
- engine/SKKHiraganaState.kt: rule.conf の `キー,<shift>別キー` 書式に対応
- layout/activity_kana_rule_manager.xml: 常に conf を使用するので TextView を廃止、EditText 追加
- xml/prefs_hard_key.xml, xml/prefs_main.xml: かなルール管理をハードキー部門下に移動
- test/RomajiConverterTest.kt:
  - tearDown で skk-kana-rule.conf を読むように (これでいいのか分かっていない)
  - useSmallK は false 前提
  - 設定されていないと何も変換しない
tamo added a commit that referenced this pull request Apr 30, 2026
* 消せない baseRomajiMap の上にカスタムを重ねるのではなく、ゼロベースで設定
  * 個人的には dhi のようなローマ字を使わないので、dh の時点で d を捨ててほしいから
* ローマ字かな変換ルールはハードウェアキーボードのための機能なので少し隠す
  * 設定を一気に確認できるよう EditText で表示・編集

- app/build.gradle: テストが asset 内の skk-kana-rule.conf を使用できるようにする
- app/src/androidTest/.../SKKSettingsActivityUITest.kt: ソフトウェアキーボード設定が画面の下に隠れていたのでスワイプしておく
- app/src/main/AndroidManifest.xml: SKKKanaRuleManager ダークテーマ対応
- assets/azik-kana-rule.conf:
  - リンク切れに archive.org で対応
  - 途中ぽい文書にブツ切りで対応
  - 実装に合わせて説明を調整
    - 2要素のみ対応
    - 未確定ローマ字の残しは非対応
    - okuri 非対応
    - ; は対応
    - `z,,‥` は `z&comma;,‥` の誤り
- SKKKanaRule.kt:
  - 既定で skk-kana-rule.conf を読む
  - 関数直前のコメントの行頭が # だとホバーツールチップで大きく表示されるので括弧に
- SKKKanaRuleManager.kt:
  - レイアウト調整 (新しめの Android 用?)
  - ルールを TextEdit にして、onPause で保存
- engine/RomajiConverter.kt:
  - mBaseRomajiMap を廃止して外部 asset の skk-kana-rule.conf に
  - それに合わせて mEffectiveMap は mRomajiMap という名前に戻す
  - skkPrefs.useSmallK は既定で false なので skkPrefs==null でも false 動作に (よく分かっていない)
- engine/SKKHiraganaState.kt: rule.conf の `キー,<shift>別キー` 書式に対応
- layout/activity_kana_rule_manager.xml: 常に conf を使用するので TextView を廃止、EditText 追加
- xml/prefs_hard_key.xml, xml/prefs_main.xml: かなルール管理をハードキー部門下に移動
- test/RomajiConverterTest.kt:
  - tearDown で skk-kana-rule.conf を読むように (これでいいのか分かっていない)
  - useSmallK は false 前提
  - 設定されていないと何も変換しない
tamo added a commit that referenced this pull request Apr 30, 2026
* 消せない baseRomajiMap の上にカスタムを重ねるのではなく、ゼロベースで設定
  * 個人的には dhi のようなローマ字を使わないので、dh の時点で d を捨ててほしいから
* ローマ字かな変換ルールはハードウェアキーボードのための機能なので少し隠す
  * 設定を一気に確認できるよう EditText で表示・編集

- app/build.gradle: テストが asset 内の skk-kana-rule.conf を使用できるようにする
- app/src/androidTest/.../SKKSettingsActivityUITest.kt: ソフトウェアキーボード設定が画面の下に隠れていたのでスワイプしておく
- app/src/main/AndroidManifest.xml: SKKKanaRuleManager ダークテーマ対応
- assets/azik-kana-rule.conf:
  - リンク切れに archive.org で対応
  - 途中ぽい文書にブツ切りで対応
  - 実装に合わせて説明を調整
    - 2要素のみ対応
    - 未確定ローマ字の残しは非対応
    - okuri 非対応
    - ; は対応
    - `z,,‥` は `z&comma;,‥` の誤り
- SKKKanaRule.kt:
  - 既定で skk-kana-rule.conf を読む
  - 関数直前のコメントの行頭が # だとホバーツールチップで大きく表示されるので括弧に
- SKKKanaRuleManager.kt:
  - レイアウト調整 (新しめの Android 用?)
  - ルールを TextEdit にして、onPause で保存
- engine/RomajiConverter.kt:
  - mBaseRomajiMap を廃止して外部 asset の skk-kana-rule.conf に
  - それに合わせて mEffectiveMap は mRomajiMap という名前に戻す
  - skkPrefs.useSmallK は既定で false なので skkPrefs==null でも false 動作に (よく分かっていない)
- engine/SKKHiraganaState.kt: rule.conf の `キー,<shift>別キー` 書式に対応
- layout/activity_kana_rule_manager.xml: 常に conf を使用するので TextView を廃止、EditText 追加
- xml/prefs_hard_key.xml, xml/prefs_main.xml: かなルール管理をハードキー部門下に移動
- test/RomajiConverterTest.kt:
  - tearDown で skk-kana-rule.conf を読むように (これでいいのか分かっていない)
  - useSmallK は false 前提
  - 設定されていないと何も変換しない
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants