× [PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。 |
以下のようにして例外を出力できる
また、ポインタ変数を確認したいときは以下のようにすればよい。
辿ってみるとわかるが、jstringとかjobjectは全部 _jobject クラスの子孫になっている。 PR |
参考ページは以下。
http://qiita.com/gabu/items/673288c3a5b39f89aa92 twitterのアプリケーション登録ページは以下 https://apps.twitter.com/app 基本的に参考ページのコードを書き写せば良いが、 気をつける点いくつか。 ・CallbackURLも設定する CallbackURLとは、OAuth認証が済んだ後に戻る先のURLである。 androidアプリの場合設定は不要なのだが、(コードから設定するので)何かしら書いておく必要がある。何も書いていないと、コールバック自体をしなくなってしまうため。 何でもいいから適当に書いておく マニフェストのTwitterOAuthActivityの中で、 コールバックURLをhost,schemeの形で指定する。 これが scheme://host の形でコールバックURLとして使われることになる。(スキームが先) |
以下のようなエラーが出た。
Unable to execute dex: Multiple dex files define ****; Conversion to Dalvik format failed: Unable to execute dex: Multiple dex files define ****; つまり、同名のファイルが複数存在していることが問題。 今回の場合、 Lcom/android/vending/billing/IInAppBillingService がmultipleだというエラー。 元々自分でこのファイルを追加していた。 android公式の課金サンプルからコピペした src/com/android/vending/billing/IInAppBillingService.aidl をプロジェクトに追加していた。 で、その後 appCCloudのjarファイルを追加したところこのエラー。 appCCloudには課金機能も含まれているので、jar内にIInAppBillingServiceクラスの定義があったのだろう。 ◯対処 そこで、自分でファイル追加していたsrc/以下のaidlファイルを削除。 aidlはインターフェース定義だけなので、実体は元々別にあるものなので ここで削除しても問題ないはず。 で、やってみたらエラーが消えた。 |
大前提
google play developer console と google developers console は別物。 とても紛らわしい google play developer console https://play.google.com/ google developers console https://console.developers.google.com/ 0. google play developer consoleで、アプリを作成 これは済んでいる前提。 アプリ名もストアに並ぶものそのままで書いている。 1. google developers consoleで、プロジェクトを作成。プロジェクト名はアプリ名と同じが良いだろう。ただし英数字しか使えない 2. PUSH通知サービスを有効にする。 左タブ「APIと認証」を選択→APIを選択→「Google Cloud Messaging for Android」を有効にする 3. 左タブ「APIと認証」→認証情報から、「公開 API へのアクセス」の「新しいキーを作成」をクリック。 テスト用にIPアドレスは 「0.0.0.0/0」としておく。 すると、「API キー」が発行される。 4. google play developer console に移動 5. アプリの「サービスとAPI」を選択→「GOOGLE クラウド メッセージング(GCM)」を作成。 先ほどの「APIキー」を入力。 すると「リンクしている送信者ID」が作成される (appCCloud使用の場合は不要の手順) appCCloudの場合 1. google developers consoleのみ使用。 appCCloudの管理画面に行き APIキーに「APIキー」を、 SenderIDに「プロジェクトID」(consoleの「概要」タブから確認)を入力 2. appc_cloud_x.x.x.jarファイルをプロジェクトに追加 3. AndroidManifest.xmlにappCCloud周りの必要事項を追記。 管理画面に書き方参考があるので、それを見てそのまま追記すればよい 4. コード記述。 MainActivity.javaに以下の3行を追記するだけ
5. 一度実機で起動。 アプリ起動のタイミングでデバイスがpush対象として登録される。 6. 管理画面からpush送信予約をする 7. 待つ。5分前後。 端末がpush通知受け取れる設定になってるか確認 8. 届けば成功 注意点 android-support-v4.jarが必要。エラーにはならないが実行時にワーニングが出る |
ご多分に漏れずいろいろ罠がある。
1. AndroidManifest.xmlに課金のパーミッション追加
2. この状態でまずapkを一度作る。(課金部分は実装していなくてよい) 3. 作ったapkをgoogle play developer console から、ベータ版としてアップロード なぜかというと、課金パーミッション付きのapkをアップロードしないと、商品の登録ができないからだ。 4. 課金アイテム他、各種情報を記入。 すべてを記入。 課金アイテムについて。 これは見れば分かる。 「有効」にするのを忘れずに。 プロダクトID(SKUとも言う)はアプリ内でユニークであればよい。なお大文字アルファベットは使えない。 一度使ったIDは、その後二度と使えなくなるので注意。 すべて記入を済ますことで、アプリのベータ公開ができるようになる。 5. ベータ版を公開する ここが必須!(これに気付かず数時間無駄にした) 公開していないと、この後課金処理を動かしたときに課金アイテム情報が取得できない。 ベータ版を公開しても、google playに公開される訳ではないので安心しよう。(google+でグループに招待したメンバーにだけ見えるようになる。その方法はここでは関係ないので触れない。) 公開には時間がかかる。consoleで「公開済み」の表示が出るまで待とう(数十分〜数時間) (ベータ公開しないで実行すると、「指定したアイテムは購入できません」というエラーが出る) 6. AndroidStudioを開く。 ProjectStructureのDependenciesから、play-servicesを選択する。 以下、この記事を参考。 AndroidStudioでは、この記事も参考になった。 7. $ANDROID_SDK_ROOT/extras/google/play_billing/IInAppBillingService.aidl を自分のプロジェクトにコピーする。 AndroidStudioでは、以下の場所に変更になっている $PROJECT_DIR/app/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl 8. $ANDROID_SDK_ROOT/extras/google/play_billing/samples/ 以下にサンプルコードがあり、ほぼそのまま使える。 $ANDROID_SDK_ROOT/extras/google/play_billing/samples/TrivialDrive/src/com/example/android/trivialdrivesample/util/*.java を丸ごと自分のプロジェクトのsrc以下にコピー。package名だけ変える $ANDROID_SDK_ROOT/extras/google/play_billing/samples/TrivialDrive/src/com/example/android/trivialdrivesample/MainActivity.java を参考にしながら自分のプロジェクトのアクティビティクラスに書く。 サンプルには多くのファイルがあるが、直接使うのはIabHelper.javaだけ。 このファイルが IInAppBillingService への処理を肩代わりしている。 あとは簡単なデータ構造体として 全体的な情報(登録アイテム情報や購入済み=非消費型アイテム情報)を格納しているInventory.java, 1つの課金アイテム情報を格納しているSkuDetails.java, 1つの課金処理トランザクション情報を格納しているPurchase.javaなど。 9. ライセンスキーをコードに記述。 base64形式で google play developer consoleに書かれているので、コピーしてコード中に記述。 サンプルコードでは、IabHelperのインスタンシエート時に必要。 10. 先ほどgoogle play developer consoleで作成した課金アイテムのプロダクトIDを適宜コードに記載。 プロダクトIDはSKUとも呼ばれる。 SkuDetails.getSku()などで取得できる。 IabHelper.queryInventoryAsync()のmoreSkusなどもこれ。 11. 適宜IabHelperを改造する。 例えば、登録している課金アイテム情報を取得する方法が無いので、 Inventory.getAllSkus() を public にしたりとか。 以上で実装は完了。引き続き動作確認に入る 12. テストアカウントを作成。 google play developer consoleの設定→アカウント詳細に ライセンステスト という項目があり、そこにメールアドレス(google playアカウント)を記述する。これがテスト購入できるアカウントとなる。 なお、このconsole自体のgoogleアカウントはテストには使えないようになっている。デベロッパー自身が課金購入できないようになっているからだと思われる。 13. 実機端末で、先ほどのアカウントでgoogle playにログイン。 実機のgoogle playは複数アカウントでログインできるものもあるが、「その端末で最初にログインしたアカウント」でしかテスト実行ができないという記事もあった。 その場合、端末を初期化して再度テストアカウントでログインする、という大変なことになるとか。 で、ベータ公開設定画面の「オプトインURL」にアクセス。 すると、「テスターになる」ボタンがあるのでクリック。 これでテストユーザになれる。 これを忘れると、apkのベータ公開後でも「指定したアイテムは購入できません」と出てしまう。 14. 署名付きでapkを作成する 署名が無いと課金購入テストができない。 (署名無しのアプリでテストアカウントで課金購入すると、「このバージョンのアプリは、Google Playを通じたお支払いはご利用になれません。詳しくはヘルプセンターをご覧ください。」というエラーが出る。) (また、署名つまりkey_storeが異なっていても上記のエラーが出る。そもそも署名が変わるとandroid consoleに提出できなくなる。くれぐれもkey_storeのパスワードなど忘れないように) (また、課金テストしているバージョンと、ベータ公開しているバージョンが同一である必要がある。公開したら開発中のバージョンは上げたくなるものだが、そこはこらえて) eclipseでプロジェクトを右クリック→Android tools→Export Signed Android Packageをクリック なお、ここでLintまわりのエラーが起きることがある。 その場合の対処はこの記事などを参照。 「◯◯の文字列のベラルーシ語が無い」のようなローカライズ周りのエラーの場合、プロジェクト設定→Android Lint Preferences→Correctness:Messages:MissingTranslation をwarningに変更することになる。 15. 出力したapkにzipalign 16. $adb install コマンドで実機転送 17. 動作確認。 当たり前だがネットワークが繋がっていることを確認のこと。 購入するとき、「これはテスト用の注文です。課金は発生しません。」と表示されるので安心。 その他注意点。 onIabPurchaseFinished や onConsumeFinished コールバックの引数は、 結果を表す IabResult と、アイテム情報を渡す Purchase があるが IabResultが失敗の場合、Purchaseがnullになる。 参考 参考 参考 チェック項目一覧 参考URL https://groups.google.com/forum/#!topic/android-group-japan/hOFJKNFDCe8 --- AndroidManifestに必要な権限等を設定している。 APKには自分の証明書をセットしている(debug証明書ではない) デベロッパーコンソールにテストアカウントのメールアドレスを登録している。 確認に使用するAPKと同じVersionCodeのAPKをデベロッパーコンソールへアップロードしている。 テストアカウントはGoogleCheckoutの設定を正しくおこなっている。 テストアカウントがプライマリアカウントとして端末に設定して稼働させている。 GooglePlayアプリで使用許諾に同意している。 署名の確認をおこなうならばアプリの公開鍵は正しいものを使用している。 --- |
AdColonyのSDKを導入していて、エラーが出た。
こんな感じ。 同じ問題にぶつかっている人も→いた adcというのはおそらく AdColony の略だし、要するに、NSDataの拡張(Extension)が読み込まれていないということだろう。 それに対応するため、 Other Linker Flagsに
を追加しろ、とAdColonyのマニュアルには書いてある。ここ これはObjective-Cのイマイチなところだ。 しかしこれで別のところに問題が出た。
どうやらcocos2d-xのどこかに、未定義の変数が残っているようだ。 GCController*** という名前のものらしい。 該当するソースを読んでみると、どうもコントローラー対応周りの実装だ。 というわけで、 GameController.framework をプロジェクトに追加して再ビルドしてみる。 …するとうまく行った。 同じ要領で、 GoogleAnalytics周りでもエラーが起きた。 これは libsqlite3.dylib libAdIdAccess.a を追加することで治る。 ◯まとめ ・実装があるはずなのにリンクエラーになるメソッド、変数などは -all_load(または-ObjC)を付加するとうまくいく ・-all_loadをつけたことでエラーになる場合は、実装がプロジェクトに追加されていない。 実装がありそうなライブラリ、フレームワークを予想して追加してみる 本当は、-all_loadってむやみに使いたくない。実行ファイルが大きくなるから。 各社SDKは考慮しておいてほしいところ。 |
デバイスによって画面の縦横比は違う。
これをどう対応するかというと、 AppDelegate.cppの bool AppDelegate::applicationDidFinishLaunching(); 関数の中で、以下の記述をする。 glview->setDesignResolutionSize(SCREEN_WIDTH, SCREEN_HEIGHT, ResolutionPolicy::SHOW_ALL); ここで、SCREEN_WIDTH, SCREEN_HEIGHTは自分で決めた値。 この値でcocos2d-xは画面を作る。 ゲームの実装部分は、この画面サイズと見なして作ればいい。 このサイズはいわば仮想画面のサイズで、デバイスの画面サイズとは違う。 この2つの画面をどう合わせるかが第3引数での指定になっている。 以下、指定できる値を列挙。
デフォルトはEXACT_FITになっている。 ぴったり描画するのは良いのだが、縦横比が変わるのはやはりよくない。 SHOW_ALLを使うのが良いと思う。 |
Androidアプリを作るときは、
必要に応じてスレッドを使いながら実装しないといけない。 とくに描画周りの処理を行うなら、必ず runOnUiThread の中に実装する。そうでないとすぐ落ちる。 cocos2d-xで動かしている場合、 mainLoopから呼び出される Layer::update() メソッドは、描画スレッドの内部であるようで、描画処理(リソースの読み込みやラベルの表示等)をしてもよい(当たり前。いつもそうしてる) だが、たまにupdate()でないところから描画処理を呼び出す流れになる場合がある。 例えば ・JNI経由でJavaからC++側を呼び出したとき ・・アラートのコールバックからゲーム側を呼び出す ・・シェア結果のコールバックからゲーム側を呼び出す ・・など ・AppDelegateの中から呼び出す ・・applicationDidFinishLaunching ・・applicationWillEnterForeground ・・など ・RootViewControllerからゲーム側を呼び出す ・AppControllerからゲーム側を呼び出す など こういうとき、描画処理を行うと、 フリーズ(描画スレッドの停止。タッチには反応し、音楽も鳴るなど) 表示くずれ などの現象が起きる 上記のような流れのときは、フラグを建てるだけにしておいて、 実際の描画処理はLayer::update()からフラグをチェックして行う のように実装する。 |
Qiitaのまとめ
修正した結果のファイル LINEツムツムとかも動かなくなったらしい Eclipseにおいては、cocos2d-xは外部ライブラリとしてリンクしているので、 修正する場合は、外部ライブラリのソースを書き換える。 ゲームディレクトリに内包されているほうのソースではない。 外部ライブラリのソースを変更し、外部ライブラリをビルドしてから ゲームのプロジェクトをビルドすると、cocos2d-xへの変更が反映される。 |
App内課金。
なかなか大変だが避けて通れない道。 手順を追っていこう。 ◯契約 1. iTunes Connect へ行く。 「契約/税金/口座情報」のページへ行き、必要事項を記入。これについては長くなるので、ググって別記事を参照のこと。 これをしていなくても以降の手順は行えるが、実際に端末でテストした時に課金アイテム情報が取得できず、詰まることになるので注意! ◯アイテム登録 1. iTunes Connect へ行く。 最近また変わったので、スクリーンショットも付けて。 まずアプリのページを開き、「App内課金」を選択。 2. App内課金の画面。 以下のようになっている(これはすでにいくつか課金アイテムを登録した後の画面) Create New をクリック。 3. 課金タイプを選択 消耗型(Consumable)…何度でも購入できるアイテム。 非消耗型(Non-Consumable)…一度しか購入できないアイテム。アプリを削除しても、ユーザがリストアできるようになっていなければならない(後述) その他は定期購読雑誌などに使うタイプの物。省略 4. 情報を登録 アイテム名、ID、価格、説明文を記述する。 またスクリーンショットも登録する。これは必須。無いとテストもできない。何度でも差し替えられるので、とりあえず何でもいいから画像を登録しておく。 5. 登録できたことを確認 Doneをクリックすると、App内課金のトップ画面に戻り、今つくったアイテムが表示される。 ステータスが「Ready to Submit」になっていればOK。 ◯実装 Xcode側の実装 1. ライブラリ登録 StoreKit.framework を追加 2. とりあえずRootViewController.mmに実装したものが以下。 外部から呼び出されて駆動するイメージで書いている。 (本筋と関係のない一部メソッドは省略)
以下、解説 ◯SKProductsRequest ◯SKProductsResponse プロダクト情報をAppleに問い合わせるためのクラス。 initWithProductIdentifiers: メソッドでプロダクトIDの配列を渡してインスタンスを作成する。 start: メソッドを呼ぶと問い合わせを開始する。 delegateをセットしたインスタンス(id ・問い合わせに成功した場合 - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response; responseには、正常に情報が取れたSKProductの配列(products)と、情報が取れなかったIDの配列(invalidProductIdentifiers)が入っている。 ・失敗したとき場合 - (void)request:(SKRequest *)request didFailWithError:(NSError *)error; ・完了した場合(成功したあとにも呼ばれる) - (void)requestDidFinish:(SKRequest *)request; ◯SKProduct 課金アイテムの情報を持つクラス。メソッドは持っていない。 主なプロパティは以下。
国ごとの金額を文字列で取得する場合(日本では120円、アメリカでは$1、のようなこと) priceとpriceLocaleを組み合わせて以下のように記述する。
◯SKPayment Apple側に、購入する内容を伝えるためのクラス。プロダクトIDや数量をプロパティに持つ。 インスタンスを生成するときは、SKProductを引数に取る。 SKPaymentをSKPaymentQueueにaddPaymentすることで、購入トランザクション(SKPaymentTransaction)が作成される。 ◯SKPaymentTransaction 1つの購入トランザクション。 メソッドは持っておらず、プロパティの集合体のクラス。 主なプロパティは以下。
◯SKPaymentQueue 購入トランザクション(SKPaymentTransaction)を管理するクラス。 ・購入可能かのチェック [SKPaymentQueue canMakePayments] で、このアプリが課金アイテム購入ができるかどうかチェック。 ・オブザーバー登録 [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; でオブザーバーを登録する。トランザクションに変化が起きたとき、オブザーバーに対して通知が行く(メソッドが呼ばれる)。delegateのようなもの。 selfは id オブザーバーがいないと、例えば購入が完了してもそれを関知できない。つまり課金だけしてその結果をアプリ内に反映できなかったりする。 だから必ずオブザーバーはつけておく。 バックグラウンドに行ったときにオブザーバーを削除するような実装もあるが、購入トランザクションの途中でバックグラウンドに行き、バックグラウンドの状態で購入完了してしまうと、まさに上記の現象になってしまう。 だからバックグラウンドに行ってもオブザーバーを削除しない方がいい。どうしても削除するなら、SKPaymentQueue内のトランザクションをすべて終了させてから。(ただし購入途中のトランザクションに対してfinishTransaction:を実行すると例外が投げられるので注意) ・購入トランザクションの開始 購入を始めるとき(ユーザが購入ボタンを押したとき)は、 [[SKPaymentQueue defaultQueue] addPayment:payment]; とする。paymentはSKPaymentクラスのインスタンス。 ・リストアを行う [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]; これですべての購入済み非消耗型アイテムのトランザクションを元に購入情報をAppleに問い合わせる。 ・購入トランザクションの終了 トランザクションを終了するとき(正常に購入できた、正常にリストアできた、エラーが起きて終了するとき)は [[SKPaymentQueue defaultQueue] finishTransaction: transaction]; とする。 ・購入トランザクションに変化が起きたとき - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions; メソッドが呼ばれる。これはSKPaymentTransactionObserver プロトコルのメソッド。 何が起きたかは、各トランザクションのtransactionStateを見れば分かる。 SKPaymentTransactionState 列挙子の値一覧
それぞれの状態に応じた処理を行う。 成功ならアプリ内に反映。アイテム付与など。トランザクションは終了する。 リストアならアプリ内に反映。アイテム数を確認し、正常な値にセット。トランザクションは終了する。 失敗ならエラー表示。トランザクションは終了する。 許可待ちなら、メッセージ表示。 購入中なら何もしないで待つ。 ・購入トランザクションが削除されたとき - (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions; が呼び出される。 finishTransaction: の結果として呼び出される。 ・リストアがすべて終了したとき - (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue; が呼び出される。 1つ1つのアイテムのリストアは - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions; において transaction.transactionState == SKPaymentTransactionStateRestored で呼び出される。 すべてのアイテムが終わったら、このメソッドが呼び出されることになる。 ・リストアが失敗したとき - (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error; これが呼ばれる。どのような対処が有効なのだろうか… ◯ 流れで言うと、 iTunes Connectで設定したプロダクトID(前述のスクショで言うと、TEST_ITEM。com.yourcompany.app.TEST_ITEM とかではない。むしろこれだとSKProductResponseで情報が取れない)を起点に、 起動時(情報取得) 全プロダクトID →SKProductRequest (initWithProductIdentifiers: PRODUCT_ID_LIST) →SKProductResponse (- (void)productsRequest: didReceiveResponse:) →SKProduct (response.products) 購入時 プロダクトID →SKProduct (保持SKProductから検索) →SKPayment ([SKPayment paymentWithProduct: product]) →SKPaymentQueueに追加 ([[SKPaymentQueue defaultQueue] addPayment:payment];) →購入処理 (- (void)paymentQueue: updatedTransactions: で transactionState == SKPaymentTransactionStatePurchased) →失敗処理(- (void)paymentQueue: updatedTransactions: で transactionState == SKPaymentTransactionStateFailed) →許可待ち処理(- (void)paymentQueue: updatedTransactions: で transactionState == SKPaymentTransactionStateDeferred) リストア時 [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]; →各リストア処理 (- (void)paymentQueue: updatedTransactions: で transactionState == SKPaymentTransactionStateRestored) →全リストア完了通知 (paymentQueueRestoreCompletedTransactionsFinished:) ・どんなときリストアするか 消耗型アイテム以外を購入できる場合は、必ずリストア機能が必要だ。 リストアボタンを置く必要がある。(ないとリジェクト) ゲームではリストアボタンを置く必要があるのか。 一度しか買えないけど、リストアする(端末間共有)することが必要かどうかはまた別問題。 例えば、取得経験値が永久2倍になるようなアイテム。ゲーム的に一度しか買えないようになっているものだが、これは非消耗型アイテムなのか。それとも、消耗型アイテムとして登録し、アプリ側で一度しか買えないように制限するのか。 非消耗型アイテムを「端末間共有されると想定される」ものとして定義するなら、この永久2倍アイテムは非消耗型アイテムではない。 消耗型アイテムとして登録するのが相応しい。 購入できる回数によって分けるということでは必ずしも無い。 非消耗型アイテムとして登録すると、ゲームの購入画面にリストアボタン(復元ボタン)を置かなければならなくなる。しかしそうすると、他の「消耗型アイテム」も復元されるはずではないか?とユーザは思ってしまう可能性がある(ユーザには消耗型、非消耗型の区別は見えないのだから当然だ)。そう考えると、すべてのアイテムを消耗型として作っておく方がユーザにとっても誤解がないということがあると思われる。 ◯レシート検証 検証ロジックは開発者が各自で行うべきという考えになっている。 クラックされないようにするためだ。 レシートデータは、iOS7以前だと購入トランザクションの中に入っていて、SKPaymentTransactionStatePurchased だと正常なものが格納されている。以下で取得できる。 NSData* receiptData = transaction.transactionReceipt iOS8以降だと、transactionReceiptは非推奨になっている。 どうするかというと、バンドル内に保存されているのでそれを読み出す。 NSBundle の appStoreReceiptURL というメソッドでパスが取得できる。 以下の流れになる。 NSURL *url = [[NSBundle mainBundle] appStoreReceiptURL]; NSData *receiptData = [[NSFileManager defaultManager] contentsAtPath:url.path]; 検証のために参考になるページ Qiitaの記事 公式ドキュメント ◯動作確認の手順 まず、実機端末で行う事(シミューレーターでは不可) 1. メアドを作成し(gmailの+付きメアドでもいいかも)、iTunesConnectでsandboxテスターとして登録しておく 2. 実機端末で、AppStoreからサインアウトしておく 3. Xcodeからバイナリを転送。この時、開発用provisioningで作成したバイナリであること。本番用だとテスト用課金はできない 4. アプリを起動し、課金購入を行う。この時、アカウント情報を求められるので、さっき作ったアカウントを入力 5. 購入ダイアログが出る。[Environment: sandbox]と出ていれば、テスト環境での課金になっている 公式ドキュメント(日本語) |