Skip to content

Commit d2b9975

Browse files
Merge pull request #113 from 26B/feature/acf-set-fields-as-translatable
ACF - Set fields as translatable
2 parents f01e5c4 + e8c305b commit d2b9975

File tree

3 files changed

+238
-2
lines changed

3 files changed

+238
-2
lines changed

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Setting Advanced Custom Fields fields as translatable automatically when they are registered.
13+
14+
### Changed
15+
16+
- Registering Advanced Custom Fields integration hooks immediately on Unbabble register instead of waiting for `admin_init`.
17+
1018
## [0.5.7] - 2024-12-19
1119

1220
### Fixed

lib/Integrations/AdvancedCustomFieldsPro.php

+213-1
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
namespace TwentySixB\WP\Plugin\Unbabble\Integrations;
44

5-
use TwentySixB\WP\Plugin\Unbabble\Options;
5+
use TwentySixB\WP\Plugin\Unbabble\LangInterface;
66

77
class AdvancedCustomFieldsPro {
88
public function register() {
99
add_filter( 'option_acf_pro_license', [ $this, 'change_home_url' ] );
10+
add_filter( 'acf/prepare_fields_for_import', [ $this, 'set_fields_as_translatable' ], PHP_INT_MAX - 10, 1 );
1011
}
1112

1213
/**
@@ -28,4 +29,215 @@ public function change_home_url( $pro_license ) {
2829
$decoded['url'] = get_home_url();
2930
return base64_encode( maybe_serialize( $decoded ) );
3031
}
32+
33+
/**
34+
* Sets ACF fields as translatable according to their type.
35+
*
36+
* @since Unreleased
37+
*
38+
* @param array $fields
39+
* @param string $prefix
40+
*
41+
* @return array
42+
*/
43+
public function set_fields_as_translatable( array $fields, string $prefix = '' ) : array {
44+
$field_keys = [];
45+
46+
/**
47+
* Store layouts for flexible content fields. Some layout types are not stored inside
48+
* the flexible content field, but in a separate entry. This is the case for the block
49+
* layout type.
50+
*/
51+
$layouts_to_look_for = [];
52+
53+
// Loop all the fields being imported.
54+
foreach ( $fields as $field ) {
55+
$prefix_value = $prefix;
56+
57+
/**
58+
* Ignore sub fields since we deal with top fields only (aggregators included),
59+
* unless they are a flexible content layout.
60+
*/
61+
if (
62+
empty( $prefix )
63+
&& (
64+
! isset( $field['parent_layout'] )
65+
|| ! isset( $layouts_to_look_for[ $field['parent_layout'] ] )
66+
)
67+
&& (
68+
! isset( $field['parent' ] )
69+
|| str_starts_with( $field['parent'], 'field_' )
70+
)
71+
) {
72+
continue;
73+
}
74+
75+
// If the field is a layout, fetch the prefix value.
76+
if ( isset( $field['parent_layout'] ) ) {
77+
$prefix_value = $layouts_to_look_for[ $field['parent_layout'] ];
78+
}
79+
80+
// If the field is a repeater, check it subfields and add the prefix value.
81+
if ( $field['type'] === 'repeater' ) {
82+
$this->set_fields_as_translatable( $field['sub_fields'] ?? [], $prefix_value . $field['name'] . '_%_' );
83+
continue;
84+
}
85+
86+
// If the field is a block or a group, check it subfields and add the prefix value.
87+
if ( $field['type'] === 'block' || $field['type'] === 'group' ) {
88+
$this->set_fields_as_translatable( $field['sub_fields'] ?? [], $prefix_value . $field['name'] . '_' );
89+
continue;
90+
}
91+
92+
// If the field is a flexible content, add its layouts to the variable with the correct prefix value to be checked later.
93+
if ( $field['type'] === 'flexible_content' ) {
94+
foreach ( $field['layouts'] ?? [] as $layout ) {
95+
$layouts_to_look_for[ $layout['key'] ] = $prefix_value . $field['name'] . '_%_';
96+
}
97+
continue;
98+
}
99+
100+
// Check for field types that have post/term identifiers, and if they are translatable.
101+
$object_type = null;
102+
switch ( $field['type'] ) {
103+
// case 'page_link': TODO: can contain ids but also archive urls so can break unbabble.
104+
case 'relationship':
105+
$object_type = $this->check_relationship( $field );
106+
break;
107+
case 'post_object':
108+
$object_type = $this->check_post_object( $field );
109+
break;
110+
case 'image':
111+
$object_type = $this->check_image( $field );
112+
break;
113+
case 'file':
114+
$object_type = $this->check_file( $field );
115+
break;
116+
case 'gallery':
117+
$object_type = $this->check_gallery( $field );
118+
break;
119+
case 'taxonomy':
120+
$object_type = $this->check_taxonomy( $field );
121+
break;
122+
}
123+
124+
// If the field is not translatable, skip it.
125+
if ( empty( $object_type ) ) {
126+
continue;
127+
}
128+
129+
// Add to the field keys array.
130+
$field_keys[ $prefix_value . $field['name'] ] = $object_type;
131+
}
132+
133+
// Register the field keys to be translated.
134+
if ( ! empty( $field_keys ) ) {
135+
add_filter( 'ubb_change_language_post_meta_translate_keys', fn( $meta_keys ) => array_merge( $meta_keys, $field_keys ) );
136+
add_filter( 'ubb_yoast_duplicate_post_meta_translate_keys', fn( $meta_keys ) => array_merge( $meta_keys, $field_keys ) );
137+
}
138+
139+
return $fields;
140+
}
141+
142+
/**
143+
* Check if a relationship field is translatable.
144+
*
145+
* @since Unreleased
146+
*
147+
* @param array $field
148+
*
149+
* @return string|null
150+
*/
151+
private function check_relationship( array $field ) : ?string {
152+
// TODO: How does ACF handle when only some of these are translatable?
153+
foreach ( $field['post_type'] as $post_types ) {
154+
if ( LangInterface::is_post_type_translatable( $post_types ) ) {
155+
return 'post';
156+
}
157+
}
158+
return null;
159+
}
160+
161+
/**
162+
* Check if a post object field is translatable.
163+
*
164+
* @since Unreleased
165+
*
166+
* @param array $field
167+
*
168+
* @return string|null
169+
*/
170+
private function check_post_object( array $field ) : ?string {
171+
// TODO: How does ACF handle when only some of these are translatable?
172+
foreach ( $field['post_type'] as $post_type ) {
173+
if ( LangInterface::is_post_type_translatable( $post_type ) ) {
174+
return 'post';
175+
}
176+
}
177+
return null;
178+
}
179+
180+
/**
181+
* Check if an image field is translatable.
182+
*
183+
* @since Unreleased
184+
*
185+
* @param array $field
186+
*
187+
* @return string|null
188+
*/
189+
private function check_image( array $field ) : ?string {
190+
if ( LangInterface::is_post_type_translatable( 'attachment' ) ) {
191+
return 'post';
192+
}
193+
return null;
194+
}
195+
196+
/**
197+
* Check if a file field is translatable.
198+
*
199+
* @since Unreleased
200+
*
201+
* @param array $field
202+
*
203+
* @return string|null
204+
*/
205+
private function check_file( array $field ) : ?string {
206+
if ( LangInterface::is_post_type_translatable( 'attachment' ) ) {
207+
return 'post';
208+
}
209+
return null;
210+
}
211+
212+
/**
213+
* Check if a gallery field is translatable.
214+
*
215+
* @since Unreleased
216+
*
217+
* @param array $field
218+
*
219+
* @return string|null
220+
*/
221+
private function check_gallery( array $field ) : ?string {
222+
if ( LangInterface::is_post_type_translatable( 'attachment' ) ) {
223+
return 'post';
224+
}
225+
return null;
226+
}
227+
228+
/**
229+
* Check if a taxonomy field is translatable.
230+
*
231+
* @since Unreleased
232+
*
233+
* @param array $field
234+
*
235+
* @return string|null
236+
*/
237+
private function check_taxonomy( array $field ) : ?string {
238+
if ( LangInterface::is_taxonomy_translatable( $field['taxonomy'] ) ) {
239+
return 'term';
240+
}
241+
return null;
242+
}
31243
}

lib/Plugin.php

+17-1
Original file line numberDiff line numberDiff line change
@@ -231,11 +231,21 @@ private function define_commands() : void {
231231
} );
232232
}
233233

234+
/**
235+
* Define the integrations with other plugins.
236+
*
237+
* @since Unreleased Register ACF integration immediatly for field registration.
238+
* @since 0.0.1
239+
*
240+
* @return void
241+
*/
234242
private function define_integrations() : void {
235243
$this->define_integration_migrators();
244+
$immediate_integrations = [
245+
AdvancedCustomFieldsPro::class => 'advanced-custom-fields-pro/acf.php',
246+
];
236247
$admin_integrations = [
237248
YoastDuplicatePost::class => 'duplicate-post/duplicate-post.php',
238-
AdvancedCustomFieldsPro::class => 'advanced-custom-fields-pro/acf.php',
239249
];
240250

241251
// TODO: shouldn't happen but we should make sure that integration classes are not registed twice.
@@ -254,6 +264,12 @@ private function define_integrations() : void {
254264
SearchWP::class => 'searchwp/index.php',
255265
];
256266

267+
foreach ( $immediate_integrations as $integration_class => $plugin_name ) {
268+
if ( \is_plugin_active( $plugin_name ) ) {
269+
( new $integration_class() )->register();
270+
}
271+
}
272+
257273
\add_action( 'admin_init', function() use ( $admin_integrations ) {
258274
foreach ( $admin_integrations as $integration_class => $plugin_name ) {
259275
if ( \is_plugin_active( $plugin_name ) ) {

0 commit comments

Comments
 (0)