
# WPML

WPMLは、言語設定ファイルで宣言された範囲内でのみ、カスタムブロックの属性を翻訳します。Blockstudioは各フィールドの値をブロックコメントの`blockstudio.attributes`に保存するため、各ブロックには、テキストフィールドと添付ファイルIDをWPMLに指定するエントリーが必要です。`wp bs wpml config`コマンドは、登録済みのすべてのブロックについてこれらのエントリーを書き込みます。

## ブロックに設定が必要な理由

WPMLは、アクティブなテーマと各アクティブなプラグインのルートフォルダーにある`wpml-config.xml`という名前のファイルから設定を読み込みます。単一ファイルの必須プラグインの場合、フォルダー名はファイル名から`.php`を除いたものになります。[言語設定ファイル](https://wpml.org/documentation/support/language-configuration-files/)と[カスタムGutenbergブロックを翻訳可能にする](https://wpml.org/documentation/support/language-configuration-files/make-custom-gutenberg-blocks-translatable/)を参照してください。

エントリーのないブロックはWPMLの自動文字列検出に任され、ブロックのすべての文字列属性が翻訳対象として提示されます。Blockstudioブロックには、セレクト値、ファイル参照、URL、CSSクラスが含まれます。翻訳者には実際のテキストと並んでこうした技術的な値も表示されるため、セレクト値やクラスを翻訳するとブロックが壊れることがあります。

## 設定を生成する

設定を出力し、ブロックを提供するテーマまたはプラグインのルートフォルダーに保存します。

```bash
wp bs wpml config > wp-content/themes/my-theme/wpml-config.xml
```

または、コマンドにファイルを書き込ませます。

```bash
wp bs wpml config --file=wp-content/themes/my-theme/wpml-config.xml
```

テキストフィールド、画像、リンク、リピーターを含むブロック：

```json title="block.json"
{
  "name": "my-theme/cards",
  "title": "Cards",
  "blockstudio": {
    "attributes": [
      { "id": "heading", "type": "text", "label": "Heading" },
      { "id": "gallery", "type": "files", "label": "Gallery", "multiple": true },
      {
        "id": "style",
        "type": "select",
        "label": "Style",
        "options": [
          { "value": "light", "label": "Light" },
          { "value": "dark", "label": "Dark" }
        ]
      },
      {
        "id": "cards",
        "type": "repeater",
        "label": "Cards",
        "attributes": [
          { "id": "title", "type": "text", "label": "Title" },
          { "id": "image", "type": "files", "label": "Image" },
          { "id": "link", "type": "link", "label": "Button" }
        ]
      }
    ]
  }
}
```

次のようになります：

```xml title="wpml-config.xml"
<wpml-config>
	<gutenberg-blocks>
		<!-- blockstudio:start -->
		<!-- Generated by wp bs wpml config. Run it again after changing block fields; edits between the markers are replaced. -->
		<gutenberg-block type="my-theme/cards" translate="1" label="Cards">
			<key name="blockstudio">
				<key name="attributes">
					<key name="heading" label="Heading" />
					<key name="gallery">
						<key name="*" type="post-ids" sub-type="attachment" />
					</key>
					<key name="cards">
						<key name="*">
							<key name="title" label="Cards: Title" />
							<key name="image" type="post-ids" sub-type="attachment" />
							<key name="link">
								<key name="title" label="Cards: Button: Link text" />
								<key name="url" type="link" label="Cards: Button: URL" />
							</key>
						</key>
					</key>
				</key>
			</key>
		</gutenberg-block>
		<!-- blockstudio:end -->
	</gutenberg-blocks>
</wpml-config>
```

`style`値は宣言されていないため、WPMLはそのまま翻訳にコピーします。`gallery`と`image`のIDもコピーされ、ブロックのレンダリング時にWPMLがそれぞれページの言語に対応する添付ファイルに置き換えるため、翻訳した画像には独自の代替テキストを設定できます。「ブロック内のIDを自動変換する」については、[カスタムGutenbergブロックを翻訳可能にする](https://wpml.org/documentation/support/language-configuration-files/make-custom-gutenberg-blocks-translatable/)を参照してください。
添付ファイルに翻訳がない場合、WPMLは元のIDを保持します。

## 宣言される内容

このコマンドは、`custom/`フィールドとブロックフィールドが展開され、オーバーライドと属性フィルターが適用された後、登録済みブロックを読み込みます。Blockstudioブロックに[拡張機能](https://blockstudio.dev/ja/docs/extensions/)が追加するフィールドも含まれます。拡張機能がコアブロックや他のプラグインのブロックに追加するフィールドは含まれません。これらのブロックはWPMLまたは各プラグイン独自の設定を使用するためです。

| フィールド | 宣言 |
| --- | --- |
| `text`、`textarea`、`richtext`、`wysiwyg` | 翻訳可能なテキスト。 |
| `link` | テキストとしての`title`と、`type="link"`を伴う`url`。WPMLは、利用可能な場合にリンクをその翻訳に置き換えます。 |
| `files` | `type="post-ids"`と`sub-type="attachment"`を伴う、保存された添付ファイルID。`multiple`の場合は、リスト用に`*`という名前の階層を設けます。これは、ファイルを1つ選択したときにフィールドが保存する単一IDにも一致します。 |
| `repeater` | すべての行に一致する`*`という名前の階層と、行フィールドの宣言。ネストされたリピーターも同様に機能します。 |
| `id`を持つ`group` | グループのフィールド。グループIDをキーの接頭辞として使います（グループ`cta`内のフィールド`text`の場合は`cta_text`）。 |
| `id`のない`group`、`tabs` | 独自の階層を設けないフィールド。 |
| `true`に設定された`translate`で登録されたカスタムフィールドタイプ | `text`フィールドと同様に、翻訳可能なテキスト。 |
| その他すべての組み込みタイプ | 宣言されません。WPMLは値をそのままコピーします。 |

宣言されたテキストフィールドがないブロックには、`translate="0"`が設定されます。するとWPMLはすべての値をコピーし、そのブロックから翻訳対象を提示しません。ブロックのレンダリング時にWPMLが`translate`の設定にかかわらず添付ファイルIDを変換するため、ブロック内の`files`フィールドはエントリー内で引き続き宣言されます。内部ブロックは通常どおり翻訳されます。

宣言されない項目：

- 値がエディターによって書き込まれないため、`hidden`を持つフィールド。
- `"translate": false`を持つフィールド（下記参照）。
- `translate`が`true`に設定されていない[カスタムフィールドタイプ](https://blockstudio.dev/ja/docs/blocks/attributes/custom-field-types/)のフィールド。値にテキストが含まれるかどうかはフィールドタイプだけが判断できるため、コマンドは各フィールドについて警告を出力します。フィールドに明示的な`"translate": false`を設定すると、そのフィールドの警告も抑制されます。下記を参照してください。
- Blockstudioフィールドではなく、ブロックがネイティブの`attributes`で宣言している属性。
- 型の異なるフィールドが使われているストレージキー（下記参照）。

`"translate": false`が設定されていない限り、技術的な値であるCSSクラスを保持するものも含め、すべてのテキストフィールドが宣言されます。

### フィールドを除外する

テキストフィールドには、すべての言語で同じ値を使う必要があるアカウント名、メールアドレス、アンカー、CSSクラスなどを格納できます。WPMLは他のテキストと同様に翻訳対象として提示するため、値を翻訳するとブロックが壊れることがあります。フィールドを設定から除外するには、`translate`を`false`に設定します。するとWPMLは値をそのまま翻訳にコピーします。

```json title="block.json"
{
  "id": "account",
  "type": "text",
  "label": "Account handle",
  "translate": false
}
```

`group`、`tabs`、`repeater`では、`"translate": false`がネストされたコンテナーも含むすべての内部フィールドに適用されます。除外されたコンテナー内の子フィールドは、`"translate": true`で設定を上書きできません。`files`フィールドでは、`false`によって添付ファイルIDも除外され、翻訳後も元の添付ファイルが保持されます。

`translate`を省略するか`true`に設定すると、既存の動作が維持され、フィールドタイプが判断します。隠しフィールドや`select`のような値タイプが翻訳可能になるわけではありません。このオプションはブロックと拡張機能のフィールドに適用され、エディターのコントロールを非表示または無効にすることも、保存された値を変更することもありません。設定を変更した後、`wp bs wpml config`を再度実行し、生成された設定を使って`wpml-config.xml`を更新してください。

各キーの`label`はフィールドラベルです。IDを持つ親リピーターやグループのラベルがその前に付きます。WPMLは翻訳エディターでこれをフィールド名として表示します。`link`フィールドの2つのキーでは、ラベルに`Link text`と`URL`が追加されます。これらの接尾辞はサイトの言語に翻訳されるため、翻訳者が使うロケールを指定してコマンドを実行してください。

### カスタムフィールドタイプ

プレーンテキスト文字列を保存するフィールドタイプは、登録時にそのことを示します。定義で`translate`を`true`に設定します。これには`attribute`が`string`である必要があります。

```php title="functions.php"
bs_register_field_type('acme/headline', [
  'attribute' => 'string',
  'translate' => true,
]);
```

このコマンドを実行すると、そのタイプのすべてのフィールドが翻訳可能なテキストとして宣言されます。フィールドごとに`"translate": false`を設定すれば、そのフィールドを除外できます。このオプションがない場合は、従来どおりそのタイプのフィールドが除外され、報告されます。

### ラベルを変更する

フィールドラベルはブロックエディター向けに書かれており、翻訳エディターでは意味をなさない「画像（任意）」のようなヒントを含むことがあります。`blockstudio/wpml/config/label`フィルターは、キーのコンテキストとともに、書き込まれる前の各ラベルを受け取ります。

- `block`: ブロック名。
- `path`: `blockstudio.attributes`の下にあるフィールドの保存パス。リピーターの各行には`*`の階層が付きます。例: `cards/*/link`。
- `field`: フィールド定義。
- `parents`: IDを持つ、外側のリピーターとグループのラベル。
- `part`: テキストフィールドの場合は`text`、リンクフィールドの2つのキーの場合は`title`または`url`。

```php title="functions.php"
add_filter('blockstudio/wpml/config/label', function ($label, $context) {
  return str_replace(' (optional)', '', $label);
}, 10, 2);
```

キーのラベルを除外するには空の文字列を返します。そうすると、WPMLには代わりにブロックのラベルが表示されます。文字列以外の戻り値を指定すると、元のラベルがそのまま使われます。フィルターを変更したら、コマンドを再実行してください。

### 同じ保存キーを持つフィールド

たとえば、ブロックフィールドと同じ`id`を持つ拡張フィールドや、IDが`cta`のグループ内にあるフィールド`text`とIDが`cta_text`のフィールドなど、複数のフィールドが同じ保存キーになることがあります。保存される値が明確な場合に限り、コマンドはそのキーを宣言します。

- リピーターの行の外にあるブロックのフィールドでは、Blockstudioは保存キーごとに1つの属性を登録します。そのため、後のフィールドが先のフィールドを、タイプにかかわらず置き換えます。ブロックの拡張フィールドも同様で、後のフィールド、または後から登録された拡張のフィールドが先のフィールドを置き換えます。
- 同じキーを持つブロックフィールドと拡張フィールドは、どちらも有効です。ブロックテンプレートには、ブロック側のフィールド定義に従った値が渡され、拡張には拡張側のフィールドが適用されます。両方のフィールドで同じ宣言が必要な場合、キーは1回だけ宣言されます。たとえば、両方がテキストフィールド（`text`、`textarea`、`richtext`、または`wysiwyg`）、両方がリンク、両方が`files`フィールド（いずれかに`multiple`がある場合、キーには`*`レベルが適用される）、またはどちらも宣言されない場合（`select`と`number`など）です。2つのリピーターは、行内のフィールドごとに比較されます。ラベルにはブロック側のフィールドのものが使われます。
- 1つのリピーターの行内で同じ`id`を持つ2つのフィールドも、どちらも有効であり、同じ方法で比較されます。

ブロックの`text`フィールドと拡張の`select`フィールドのようにタイプが異なる場合、保存値は、その値を書き込んだコントロールに応じてテキストまたは選択肢になります。コマンドはキーを宣言せず、次の警告を表示します。

```bash
Warning: Not declared, the fields that store this key have different types: my-theme/cards: tone (text in the block, select in an extension)
```

キーを宣言するには、いずれかのフィールド名を変更するか、両方のタイプを同じにしてください。

## 設定を最新の状態に保つ

フィールドを追加、削除、または名前変更したら、コマンドを再実行し、更新したファイルをテーマまたはプラグインに含めてください。7.7.5より前に作成した設定を再生成し、添付ファイルIDの宣言を含めてください。既存のコンテンツに新しいフィールドが翻訳用として表示されるのは、元のコンテンツを更新した後です。詳しくは上記のWPMLガイドをご覧ください。

`--file`を指定すると、コマンドはファイルがない場合に作成します。既存のファイルでは、`<!-- blockstudio:start -->`と`<!-- blockstudio:end -->`の間のテキストだけを置き換えるため、他のセクションや別のブロックのエントリーはそのまま残ります。

```xml title="wpml-config.xml"
<wpml-config>
	<custom-types>
		<custom-type translate="1">book</custom-type>
	</custom-types>
	<gutenberg-blocks>
		<gutenberg-block type="my-plugin/map" translate="1">
			<key name="caption" />
		</gutenberg-block>
		<!-- blockstudio:start -->
		<!-- blockstudio:end -->
	</gutenberg-blocks>
</wpml-config>
```

マーカーがまだない既存ファイルを使うには、その`gutenberg-blocks`要素内に2つのマーカー行を一度追加するか、ファイルに`gutenberg-blocks`要素がない場合は、マーカーを含む要素を追加してください。マーカーがない場合や、結果が有効な設定にならない場合、コマンドはファイルを変更せず、エラー終了します。

手作業で記述したBlockstudioブロックのエントリーは、`translate="0"`のように技術的な値を隠すエントリーも含め、生成されたセクションがそのブロックを網羅すれば不要になります。コマンドは重複して宣言されたブロックをすべて警告するため、これらのエントリーを削除できます。

## 翻訳ジョブ内の画像テキスト

WPMLは、ページで使用されている画像のテキスト（タイトル、代替テキスト、キャプション、説明）をページの翻訳ジョブに追加します。ジョブが完了すると、WPMLは翻訳済みのテキストを翻訳先言語の添付ファイルに書き込み、まだ添付ファイルがない場合は作成します。[メディアの翻訳方法](https://wpml.org/documentation/translating-your-contents/media/)をご覧ください。
WPMLは、コンテンツ内の`<img>`タグ、ギャラリー、アイキャッチ画像、および認識対象のブロックから画像を検出します。Blockstudioブロックは添付ファイルIDをブロックコメントに保存するため、WPMLでは検出できません。

WPMLが有効な場合、Blockstudioはページ内のブロックのフィールドに保存された添付ファイルを、検出された画像WPMLに追加します。

- 非表示のものも含む`files`フィールド、および`media`を持つ`attributes`フィールド。これらはブロックエディターがメディアとして読み込むフィールドと同じです。
- グループ、タブ、リピーターの行、内部ブロックのフィールド。
- [拡張](https://blockstudio.dev/ja/docs/extensions/)のフィールド。拡張先ブロックの属性に値が保存されるため、コアブロックや他のプラグインのブロックも対象です。

添付ファイルのIDではない値は除外されます。設定は不要で、`wpml-config.xml`も関与しません。WPMLがなければ、翻訳ジョブ用のスキャンは行われず、メディア連携クラスも読み込まれません。

これは添付ファイルIDの変換とは別の機能です。生成されたXMLは、どの翻訳済み添付ファイルを表示するかをWPMLに伝えます。一方、この連携機能は、翻訳ジョブに含める添付ファイルのテキストを指定します。添付ファイル以外の投稿に関する文字列参照とIDは、メディアジョブに追加されません。Blockstudioブロックでは、レンダリング時と同様に、保存済みのネストされたフィールド値がトップレベルの値より優先されます。

画像テキストのオプションのいずれかが有効な場合に限り、テキストはジョブに含まれます。コアブロック内の画像と同様に、WPML > Settings > Media Translationにある「画像テキストの翻訳に最適なオプションを自動検出」または「コンテンツの翻訳時にメディアライブラリのテキストを翻訳」を有効にしてください。

Blockstudio はフィルター `wpml_ids_of_media_used_in_post` を通じて画像を報告します。このフィルターは、`PostWithMediaFiles` における投稿のメディアに WPML が適用されます。画像をジョブから除外するには、優先度を 10 より高くして、同じフィルターのコールバックでその ID を削除します:

```php
add_filter(
	'wpml_ids_of_media_used_in_post',
	function ( $ids ) {
		return array_values( array_diff( $ids, array( 42 ) ) );
	},
	20
);
```
