LoginSignup
1
0

数値の区切り文字を変更するという対応で躓いた話

Last updated at Posted at 2024-05-20

はじめに

この記事は、ライブラリ「SpreadsheetGear」と「SpreadJS」に関する、備忘録的なものである。
ReferenceとStack Overflowを漁り尽くして、果てには公式に問い合わせながら、ようやく何とかなった内容を忘れるわけにはいかないので、書きます。

背景

Windowsアプリ(SpreadsheetGear)とWebアプリ(SpreadJS)を開発中。
現在はスプレッドに表示される数値の区切り文字が、固定で「,」「.」になっている。
これをユーザの設定と同期させたい!

技術詳細

今回の記事で登場する技術を示す。
使用しているが必要ないと判断したものについては省略している。

  • 開発言語
    • サーバサイド…C# 6.0
    • フロントエンド…TypeScript 4.3
  • フレームワーク… .NET Framework 4.6
  • ライブラリ…SpreadsheetGear2017 Ver 8.6.10.102, SpreadJS 9J SP2 (Ver. 9.20171.0)

実践

実際に区切り文字を変更してみる。

以降は、記述を省略するために以下のように単語を明記する。

  • SpreadsheetGear → Gear
  • SpreadJS → SJS

手順

区切り文字を適用するための簡単な手順は、以下のとおりである。

  • Gearの場合
    1. ユーザ設定(Windows)の区切り文字を取得する
    2. (必要に応じて) 設定対象のWorkbookByteにしてサーバ上で保持しておく
    3. ユーザ設定の区切り文字を適用したCultureInfoを作成する
    4. 作成したCultureInfoをもとにWorkbookSetを作成する
    5. スプレッドのActiveWorkbookSetを4. で作成したWorkbookSetにする
    6. (必要に応じて) WorkbookSet.WorkBooksに2. で保持したWorkbookを読み込む
  • SJSの場合
    1. サーバサイドで、ユーザ設定(ブラウザ)の区切り文字を取得する
    2. 取得した区切り文字を含めたデータを、スプレッド表示メソッドに渡す
    3. フロントでユーザ設定の区切り文字を適用したCultureInfoを作成する
    4. 作成したCultureInfoをSJSのカルチャに追加する
    5. SJSの現在のカルチャを追加したカルチャに変更する

GearはSJSのように安直な設定ができなかった。本当にキレそう。
後述するが、プロパティの操作に難解な部分があったためである。

また、今回の記事の手法は、Microsoftから提供されている標準のカルチャ設定において、区切り文字以外のプロパティを変更する場合を考慮していない。
区切り文字以外のプロパティを操作する場合は、適宜設定を保持した上で区切り文字適用の処理を行うことが望ましい。

Gearの場合

ユーザ設定の区切り文字を取得

//
// 1. ユーザ設定(Windows)の区切り文字を取得する
//
var cultureInfo = System.Globalization.CultureInfo.CurrentCulture;

// 後で使うのでカルチャ名(コード)を保持する
this.m_cultureName = cultureInfo.Name;

// 取得したCultureInfoから、各区切り文字を取得
this.m_cgs = cultureInfo.NumberFormat.CurrencyGroupSeparator;
this.m_nds = cultureInfo.NumberFormat.NumberDecimalSeparator;
.
.
.

WorkbookSet, Workbookの操作、区切り文字の適用

//
// 2. (必要に応じて) 設定対象のWorkbookをByteにしてサーバ上で保持しておく
//
this.m_byteWorkbook = workbook.SaveToMemory(this.m_fileFormat);

.
.
.

//
// 3. ユーザ設定の区切り文字を適用したCultureInfoを作成する
// 「ユーザ設定の区切り文字」で保持したカルチャ名を使用する
//
var currentCulture = new System.Globalization.CultureInfo(CultureName);

var numberFormat = currentCulture.NumberFormat;
numberFormat.CurrencyGroupSeparator = this.m_cgs;
numberFormat.NumberDecimalSeparator = this.m_nds;
.
.
.

//
// 4. 作成したCultureInfoをもとにWorkbookSetを作成する
// 5. スプレッドのActiveWorkbookSetを4. で作成したWorkbookSetにする
//
this.Spread.ActiveWrkbookSet = SpreadsheetGear.Factory.GetWorkbookSet(currentCulture);
//
// 6. (必要に応じて) WorkbookSet.WorkBooksに2. で保持したWorkbookを読み込む
//
this.Spread.ActiveWrkbookSet.Workbooks.OpenFromMemory(this.m_byteWorkbook);

SJSの場合

ユーザ設定の区切り文字を取得

//
// 1. サーバサイドで、ユーザ設定(ブラウザ)の区切り文字を取得する
// C#で実装した、Viewを返すAPIで書く
//
public ActionResult Index(string hoge) {
    .
    .
    .
    
    // ユーザ設定の言語一覧を取得
    // 配列として入っている。優先度が高いものが若いインデックス
    var userLangs = Request.UserLanguages;

    // 取得した言語一覧からSystem.Globalization.CultureInfoを作成
    CultureInfo userCultureInfo = userLangs != null && userLangs.Length > 0
    ? new CultureInfo(userLangs[0])
    : CultureInfo.CurrentCulture;

    // 取得したCultureInfoから、各区切り文字を取得
    this.m_cgs = userCultureInfo.NumberFormat.CurrencyGroupSeparator;
    this.m_nds = userCultureInfo.NumberFormat.NumberDecimalSeparator;
    .
    .
    .
}

カルチャの区切り文字を変更、そのカルチャを適用

public loadSpread(data: any) {
    //
    // 2. フロントでユーザ設定の区切り文字を適用したCultureInfoを作成する
    //
    // 現在のCultureInfoを取得
    var cultureInfo = GC.Spread.Common.CultureManager.getCultureInfo(GC.Spread.Common.CultureManager.culture());
    // CultureInfoのdeepCopyを作成
    // 今回の範囲に影響が無いため、undefinedは考慮しないコピー方法を用いる
    var usrCulture: GC.Spread.Common.CultureInfo = JSON.parse(JSON.stringify(cultureInfo));
    
    var numberFormat: any = usrCulture.NumberFormat;
    numberFormat.currencyGroupSeparator = data.Separator.CurrencyGroup;
    numberFormat.numberDecimalSeparator = data.Separator.NumberDecimal;
    .
    .
    .
    
    //
    // 3. 作成したCultureInfoをSJSのカルチャに追加する
    //
    GC.Spread.Common.CultureManager.addCultureInfo("lo-cl", usrCulture);
    //
    // 4. SJSの現在のカルチャを追加したカルチャに変更する
    //
    GC.Spread.Common.CultureManager.culture("lo-cl");
}

要素の解説

機能実現に必要な要素を簡単に説明する。
分かっている人にとっては別にいらないセクションかと。

書式設定

今回はコードを掲載していないが、必要な要素のため説明する。
そもそもセルの書式で「桁区切り」を指定しないと、見た目では小数点だけしか変化しない。
一つのセルの書式設定は、以下の方法で取得できる。

  • Gearの場合…string IRange.NumberFormat
  • SJSの場合…GC.Spread.Sheets.CellRange.formatter(value: undefined): any

ここに#,##.####のような数値書式を指定することで、セルに入力された数値は桁区切りを伴って表示される。

ユーザ設定の区切り文字

区切り文字のプロパティは、WinもWebも言語設定に含まれている。
WindowsアプリではWindowsの言語設定から取得するようにし、Webアプリではブラウザの言語設定から取得する。

それぞれのプラットフォームで読み書きできる区切り文字の種類は、公式のドキュメントを参考にする。

(Gear)WorkbookSet, Workbookの操作

Gearに限り、変更されたカルチャを定義して、そのカルチャを適用したWorkbookSetを作成して、現在のWorkbookSetにする、というあほ作業がある。

この手法を取る理由は、直接NumberFormatを指定しても区切り文字が変化しないといった仕様が原因である。
この仕様について簡単に説明する。

直接NumberFormatを指定しても区切り文字が変化しない

今回の実装を行っていて、一番頭を悩まされた点である。

上記のReferenceにもある通り、CultureInfogetのみ付いている。
CultureInfoが取得できればその中にあるNumberFormatも操作できてしまう。NumberFormatgetsetがついているためである。
そのため、ActiveWorkbookSet.Culture.NumberFormat.○○○に直接文字を指定すれば、区切り文字が変わるだろう、と思っていた。

実際は、表示される区切り文字が変化していなかった。

加えて、プロパティのウォッチではActiveWorkbookSet.Culture.NumberFormat.○○○は指定通りに変更されていたのである。本当に意味が分からない。

この仕様に対する考察

CultureInfoWorkbookSetの作成時に指定するものである、という思想を体現するために、CultureInfogetのみ付けていると思われる。(というかそう考えないと腑に落ちない)

実はSJSにおいても、CurrentCultureNumberFormatを直接指定しても区切り文字が変化しない、という挙動が存在する。SJSの適用の番のコードが、暫定的な回避策である。
この辺りからも怪しいと疑うべきだったか...
この挙動については問い合わせていないため、詳しいことは不明であるが、とにかく不便。

まとめ

いかがでしたか!?

多言語対応(狭義)はもう懲り懲りです。あとTS触らせないでください。僕はC#に籠ります。

この記事が、僕と同じくSpreadsheetGearとSpreadJSの仕様に悩む人々に届くことを祈っています。

追記

2024/05/21

ActiveWorkbookSetFactoryで作成したWorkbookSetを直接指定する方法でも区切り文字を変更することができたため、一部コードを修正。

付録

SpreadJSの仕様についてMescius社への問い合わせ

はじめにのセクションで述べた、公式への問い合わせである。
区切り文字を変更するという目的には関係ないが、見逃せない挙動であることと自己解決が厳しいと判断したため、公式に問い合わせた。

挙動を要約すると、

  • 区切り文字を変更すると、小数点以下の数値に桁区切り文字が表示されてしまう

というものである。

結論としては、

  • SpreadJS(v17.0.5)以前で発生する既知の不具合

であった。散々悩まされた結果が、過去バージョンに存在する既知のバグだとわかったときは、今年一番萎えた。

興味があれば、以下リンクを参考いただきたい。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0