I have a text field in the backend. The text value inside it will be different depends on the products I choose. So, it's a kind of dynamic field.
How can I target that field to make it translatable?
so each time I create an offer WPML can detect the string?
if ( $total_to_add - $total_added > 0 ) {
if ( ! in_array( $rule['key'], $special_offers, true ) ) {
$rule_text = isset( $rule['text_in_modal_special_offer'] ) ? $rule['text_in_modal_special_offer'] : __( 'Get a special discount if you add {{total_to_add}} product(s) to your order.', 'discountText' );
$special_offer = array(
'text' => $rule_text,
'items_in_cart' => $total_added,
'discount' => array(
'type' => $discount['discount_amount']['type'],
'amount' => $discount['discount_amount']['amount'],
),
);
$special_offers[ $rule['key'] ] = $special_offer;
}
}
You need to use the "Auto-register strings for translation" feature of WPML (documentation):
WPML uses static code analysis to locate strings that require translation in the theme and plugins. In some cases, the static code-scan cannot reliably find all strings. This often happens when strings are generated dynamically using code.
Then you need to go to the front end of the site and make sure both variants of your IF statement are executed. Also, I would try to __() the dynamic value as well:
$rule_text = isset( $rule['text_in_modal_special_offer'] ) ? __($rule['text_in_modal_special_offer'], 'discountText') : __( 'Get a special discount if you add {{total_to_add}} product(s) to your order.', 'discountText' );
After that, go to WPML > String Translation, search for both strings, and translate them.
Related
I've been searching for examples of this online and through the WP documentation on filters but I can't find a suitable hook, so apologies for posting a question without a good example of what I'm trying to do!
When you add a link to text or to a button in the editor, you can search for the page/post you want to link to. What you can't search for is a post type archive link.
I want to be able to type the name of a post type into the search box (pictured below), and include the post type archive link in the search results. In this example, I have a post type called members that I'd like to link to.
I find the need to do this a lot, and I always end up just typing /post-type-link into the box and leaving it at that, but I don't think this is an elegant solution and is clunky for users.
I have tried to write some code, but I don't believe I have the right hook:
function include_cpt_search( $query ) {
if ( is_admin() && is_single() ) {
$query->set( 'post_type', array( 'services' ) );
}
return $query;
}
add_filter( 'pre_get_posts', 'include_cpt_search' );
Has anyone done this before? Know of a filter or hook I could work with? Anything really!
The Link format in the RichText toolbar uses the Search REST API endpoint at /wp/v2/search, so although that endpoint doesn't provide a specialized hook for filtering the response, you can use rest_post_dispatch to add custom links to the search results returned via /wp/v2/search.
So in the examples below, I'm checking if the route is /wp/v2/search and if so, then we add the (custom) post type's archive link. Also note that, you should provide an array with the items mentioned here (the LinkControl component used by the Link format).
Basic Example
Includes only the (i.e. one) post type whereby its name matched exactly the search keyword.
add_filter( 'rest_post_dispatch', 'so_62472641', 10, 3 );
function so_62472641( $response, $server, $request ) {
// Don't modify the data if the REST API route is not /wp/v2/search
if ( 'post' !== $request->get_param( 'type' ) ||
'/wp/v2/search' !== $request->get_route() ) {
return $response;
}
// Let's see if there's a post type that matched the search keyword.
$search = $request->get_param( 'search' );
if ( ! $post_type = get_post_type_object( $search ) ) {
return $response;
}
// Now add the post type archive URL, if any, to the response data.
if ( $url = get_post_type_archive_link( $search ) ) {
$data = (array) $response->get_data();
$data[] = [
'id' => 'post_type-' . $search,
'type' => 'Post Type Archive',
'title' => $post_type->label,
'url' => $url,
];
$response->set_data( $data );
}
return $response;
}
Extended Example
Includes all post types whereby the name/label matched the search keyword.
add_filter( 'rest_post_dispatch', 'so_62472641', 10, 3 );
function so_62472641( $response, $server, $request ) {
// Don't modify the data if the REST API route is not /wp/v2/search
if ( 'post' !== $request->get_param( 'type' ) ||
'/wp/v2/search' !== $request->get_route() ) {
return $response;
}
$search = $request->get_param( 'search' );
$post_types = get_post_types( [], 'objects' );
$extra_data = [];
// Let's see if there's a post type that matched the search keyword.
foreach ( $post_types as $obj ) {
if ( $search === $obj->name ||
// look for the search keyword in the post type name/slug and labels (plural & singular)
false !== stripos( "{$obj->name} {$obj->label} {$obj->labels->singular_name}", $search )
) {
if ( $url = get_post_type_archive_link( $obj->name ) ) {
$extra_data[] = [
'id' => 'post_type-' . $obj->name,
'type' => 'Post Type Archive',
'title' => $obj->label,
'url' => $url,
];
}
}
}
// Now add the post type archive links, if any, to the response data.
if ( ! empty( $extra_data ) ) {
$response->set_data( array_merge( (array) $response->get_data(), $extra_data ) );
}
return $response;
}
Sample Output (for the second example above)
Note: The above is a screenshot of a real response, but I deliberately (via PHP) changed the domain name to example.com (i.e. the actual domain name is different).
And the examples were both tried & tested working on WordPress 5.5.1 (latest release as of writing). Also, you can exclude the default post post type, if you want to.
Additional Notes
It should be noted that the examples do not take into account the pagination, which means, if there were 10 post types that matched the search keyword, then they would always be included in the response (on page 1, 2, 3, etc.). So you might want to just use the first example because at least it always includes at most 1 post type only. However, with the second example, you can actually limit the $extra_data to, say, 5 items (per page - but it's up to you on how to distribute the items per page).
You can also use a custom search handler class, e.g. one that extends the default class (WP_REST_Post_Search_Handler) and use the wp_rest_search_handlers hook to add your class to the list. Here's a very basic example...
In your-class.php:
class My_WP_REST_Post_Search_Handler extends WP_REST_Post_Search_Handler {
// ... you'll need to actually write the code on your own..
}
return new My_WP_REST_Post_Search_Handler;
In the theme's functions.php file or somewhere in your plugin:
add_filter( 'wp_rest_search_handlers', 'my_wp_rest_search_handlers' );
function my_wp_rest_search_handlers( $search_handlers ) {
$search_handlers[] = include_once '/path/to/the/your-class.php';
return $search_handlers;
}
I believe I am close but entirely unsure as my limited php knowledge is based on trial and error. I am trying to pull data from a plugin that creates my shipping label into a plugin that tracks shipping. Specifically Elex EasyPost into Advanced Shipping Tracking.
I have an example snippet for shipstation integration from AST here:
<?php
add_action( 'woocommerce_shipstation_shipnotify', 'add_tracking_information_into_order', 10, 2 );
function add_tracking_information_into_order($order, $tracking_details){
$order_id = $order->get_id();
$args = array(
'tracking_provider' => $tracking_details['carrier'],
'tracking_number' => wc_clean( $tracking_details['tracking_number'] ),
'date_shipped' => wc_clean( $tracking_details['ship_date'] ),
'status_shipped' => 1,
);
$ast = new WC_Advanced_Shipment_Tracking_Actions;
$tracking_item = $ast->insert_tracking_item( $order_id, $args );
}
It should be similar but obviously I need to replace the hook and the items in the array. Here is what Elex sent me when asked for the hooks they are using.
get_post_meta($order_id, ‘wf_easypost_labels’);
Sample output:
array(
0=>(‘url’=>label_url,’tracking_number’=> (string)’tracking code’,’integrator_txn_id’=> ‘integerator_id’,’shipment_id’=>’package_shipment_id’,’order_date’=>order_date,’carrier’=>EX:USPS,”link”=>tracking_link,);//First package
1=>(‘url’=>label_url,’tracking_number’=> (string)’tracking code’,’integrator_txn_id’=> ‘integerator_id’,’shipment_id’=>’package_shipment_id’,’order_date’=>order_date,’carrier’=>EX:USPS,”link”=>tracking_link,);//Second package
)
So here is my attempt to use their array in my own snippet:
<?php
add_action( 'woocommerce_checkout_update_order_meta', 'add_tracking_information_into_order', 10, 2 );
function add_tracking_information_into_order($order, $myship_func){
$myship_func = get_post_meta($order_id, 'wf_easypost_labels');
$order_id = $order->get_id();
$args = array(
'tracking_provider' => $myship_func['carrier'],
'tracking_number' => wc_clean($myship_func['tracking code']),
'date_shipped' => wc_clean($myship_func['order_date']),
'status_shipped' => 1,
);
$ast = new WC_Advanced_Shipment_Tracking_Actions;
$tracking_item = $ast->insert_tracking_item( $order_id, $args );
}
When the order is initially filled and the label is generated Elex grabs all the data from and sticks it in the meta. I need to pull that meta either at the time of generation or post generation and stick it in the AST fields.
My main question is thus - Am I on the right track here? Do I just need to figure out the correct hook or is my method for accessing the Elex array incorrect? Again, I usually compare codes, slice and dice, and get things to "look right" and then they work (or not). I myself don't have too deep of knowledge about arrays or class functions (which I believe the Elex one is).
In this case the code is silently failing so I suspect it is the hook involved or a combination of the hook and my code.
Perfect woocommerce brands only allows certain tags to be used and removes the rest via the function
if( isset( $_POST['pwb_brand_description_field'] ) ){
$desc = strip_tags(
wp_unslash( $_POST['pwb_brand_description_field'] ),
'<p><span><a><ul><ol><li><h1><h2><h3><h4><h5><h6><pre><strong><em><blockquote><del><ins><img><code><hr>'
);
global $wpdb;
$wpdb->update( $wpdb->term_taxonomy, [ 'description' => $desc ], [ 'term_id' => $term_id ] );
I am looking for a way to overide this so that I can include all tags, in particular <div>
I was hoping there might be something that I could add to functions.php which would help me achieve this?
Because the plugin author did not add any filters / hooks for this particular function, you have to do a fairly clunky work-around.
Thankfully, this function is called via a couple of actions
add_action( 'edit_pwb-brand', array( $this, 'add_brands_metafields_save' ) );
add_action( 'create_pwb-brand', array( $this, 'add_brands_metafields_save' ) );
That means that we may be able to add our own actions that run after those two, and may be able to do what you want.
Note that this is sub-optimal but cannot be avoided. This function is essentially running twice (the original in the plugin, plus yours), which is not good for performance - however, since this is only happening when an admin saves a brand, it shouldn't be too bad for performance.
First, add your own action(s) with a higher priority, then copy the original function with the desired modifications.
It's often recommended that you do this in your theme's functions.php file, but that's not ideal - what if you want to change themes, or update themes, but keep this functionality? Instead, I strongly recommended you build your own small, light-weight plugin. The complete code for such a plugin is below. (Simply add this code to a PHP file, and place it in your plugins directory).
<?php
/**
* Plugin Name: Override Perfect WooCommerce Brands Meta
* Description: Custom override to permit div tags in brand meta description
* Version: 1.0.0
* Author: SupGen
*/
// Note the higher priority levels - to ensure these run AFTER the main plugin is done
add_action( 'edit_pwb-brand', 'override_add_brands_metafields_save', 9999 );
add_action( 'create_pwb-brand', 'override_add_brands_metafields_save', 9999 );
function override_add_brands_metafields_save( $term_id ) {
// NOTE: hard-coding the file name here in order to verify the nonce. MAY need to be changed
$filename = 'class-brands-custom-fields.php';
if ( ! isset( $_POST[ 'pwb_nonce' ] ) || ! wp_verify_nonce( $_POST[ 'pwb_nonce' ], $filename ) ) {
return;
}
// removed bits you didn't care about, keeping only the relevant part
if ( isset( $_POST[ 'pwb_brand_description_field' ] ) ) {
// added div tag to allowed tags list
$desc = strip_tags(
wp_unslash( $_POST[ 'pwb_brand_description_field' ] ),
'<div><p><span><a><ul><ol><li><h1><h2><h3><h4><h5><h6><pre><strong><em><blockquote><del><ins><img><code><hr>'
);
global $wpdb;
$wpdb->update( $wpdb->term_taxonomy, [ 'description' => $desc ], [ 'term_id' => $term_id ] );
}
}
With the help from the community, I have succeeded to create, save and print the labels and their values to the single product page.
I can also translate the input values into different languages using Polylang, but translating the custom labels (Conditions and Brands) is extremely hard.
Anyone out there can help me with these issues?
I tried to use Polylang, Saywhat...with no success!
Here is the code:
// Enabling and Displaying Fields in backend
add_action( 'woocommerce_product_options_general_product_data', 'woo_add_custom_general_fields' );
function woo_add_custom_general_fields() {
global $woocommerce, $post;
echo '<div class="options_group">';
woocommerce_wp_text_input( array( // Text Field type
'id' => '_conditions',
'label' => __( 'Conditions', 'woocommerce' ),
'placeholder' => 'i.e: brand-new; refurbished; defected...',
'desc_tip' => 'true',
'description' => __( 'Enter the conditions of the products here.', 'woocommerce' )
) );
woocommerce_wp_text_input( array( // Text Field type
'id' => '_brands',
'label' => __( 'Brands', 'woocommerce' ),
'placeholder' => 'i.e: Lacoste; Hugo Boss...etc',
'desc_tip' => 'true',
'description' => __( 'Enter names of the Brands of the products if any.', 'woocommerce' )
));
echo '</div>'; // Closing </div> tag HERE
}
// Save Fields values to database when submitted (Backend)
add_action( 'woocommerce_process_product_meta', 'woo_save_custom_general_fields' );
function woo_save_custom_general_fields( $post_id ){
// Saving "Conditions" field key/value
$conditions_field = $_POST['_conditions'];
if( !empty( $conditions_field ) )
update_post_meta( $post_id, '_conditions', esc_attr( $conditions_field ) );
// Saving "Brands" field key/value
$brands_field = $_POST['_brands'];
if( !empty( $brands_field ) )
update_post_meta( $post_id, '_brands', esc_attr( $brands_field ) );
}
add_action('woocommerce_single_product_summary', 'woo_display_custom_general_fields_values', 20);
function woo_display_custom_general_fields_values() {
global $product;
echo '<p class="custom-conditions">Conditions: ' . get_post_meta( $product->id, '_conditions', true ) . '</p>';
echo '<p class="custom-brands">Brands: ' . get_post_meta( $product->id, '_brands', true ) . '</p>';
}
And here a screenshot:
Thank you.
First, you should change the gettex domain name from 'woocommerce' to the domain name of your theme (or something more custom), as it doesn't make part of woocommerce code, and it's located in your active theme.
1) the Free alternative:
Then as it's not really content that you are trying to translate, but some peaces of code located in the function.php file of your active child theme (or active theme), you should use a specialized free plugin as Loco Translate that will provides in-browser editing of WordPress translation files.
Loco Translate, also provides localization tools for developers, such as extracting strings and generating templates.
Loco Translate features include:
Built-in translation editor within WordPress admin
Create and update language files directly in your theme or plugin
Extraction of translatable strings from your source code
Native MO file compilation without the need for Gettext on your system
Support for PO features including comments, references and plural forms
PO source view with clickable source code references
Protected language directory for saving custom translations
Configurable PO file backups
Built-in WordPress locale codes
2) The commercial complete way (completely compatible with WooCommerce):
The most complete commercial alternative is WPML plugin, that will also handle perfectly and more easily WooCommerce custom localisation and translations content for multilingual web sites. The other free optional plugins are incomplete for WooCommerce and much more difficult to get them work totally well.
So If you are planing to publish a multilingual website, think about it.
I'm a php newbie and a first time poster.
I am working on a wordpress site where I need a discography.
I have successfully:
Created my custom post type : Albums
Added custom meta boxes and custom fields to admin post edit page
Made an archive page and echoed all the custom field meta.
http://squarerecording.com/albums/
One of my custom fields is a series of 5 checkboxes: the last line of each album on that archive page
array(
'name' => 'Services Rendered',
'desc' => 'field description (optional)',
'id' => $prefix . 'services',
'type' => 'multicheck',
'options' => array(
'R' => 'Recorded',
'Mi' => 'Mixed',
'Ma' => 'Mastered',
'P' => 'Produced',
'RMV' => 'Re-mastered for vinyl',
),
),
although I have successfully echoed the "Services Rendered" in a comma separated format, the order is different for each post (P,RMV,R,Mi,Ma-for the first one, P,Ma,Mi,R,RMV-for the second, etc.)
Here is the code for the archive page that outputs "Services Rendered":
<?php $key="sqr_services"; get_post_meta($post->ID, $key);
$sqr_services = get_post_meta( $post->ID, $key );
$comma_sep_services = implode(",", $sqr_services );
echo $comma_sep_services;
?>
My question is: What do I need to do so that they are listed in the same order that they appear on the edit page ( R,Mi,Ma,P,RMV)? Bearing in mind that they will not always ALL be checked.
I have tried messing around with unserializing but i don't know enough about it.
Any help or a point in the right direction would be greatly appreciated!
Thanks
This post might be of interest. It's one of the few solutions I've seen that doesn't involve editing Wordpress core files.
You could also try this modified get_post_custom function that orders the key/value pairs by meta id instead of the "date modified" order returned by get_post_custom.
A third option would be to sift through this answer.
Update: Silly Me! PHP can sort arrays any darn way you want, and it just gets a teeny bit tricky when that's not numerical or alphabetical. If alphabetization works for you, attempt the following:
$key="sqr_services";
get_post_meta($post->ID, $key);
$sqr_services = get_post_meta( $post->ID, $key );
asort($sqr_services);
foreach ($sqr_services as $key => $val) {
echo "$val\n";
}
If you want to learn yourself more complex array sorting, be my guest. Me? I'll stick to ABCs.
Update: Silly You!
In response to your comment about checking for and displaying individual values in your $sqr_services array, alls you needs to do is check in_array():
if (in_array('Recorded', $sqr_services)){echo 'Recorded';}
if (in_array('Mixed',$sqr_services)){echo 'Mixed';}
if (in_array('Mastered',$sqr_services)){echo 'Mastered';}
if (in_array('Produced',$sqr_services)){echo 'Produced';}
if (in_array('Re-mastered for vinyl',$sqr_services)){echo 'Re-mastered for vinyl';}
It's less flexible in that if you add a value to the array (add a new tax term), it won't start showing up until you check for it in your loop. I'm guessing that's not gonna be a problem, though, as this seems like a pretty finite set of options.