ProcessWire 3.0.151 core updates

This week we’ve got a couple of really useful API-side improvements to the core in 3.0.151, among other updates.

First we’ll take a look at a new $config setting that lets you predefine image resize settings, enabling you to isolate them from code that outputs them, not unlike how you isolate CSS from HTML. Following that, we’ll introduce and show you how to use a handy addition to our static language translation functions. If you’ve ever come across the term “abandoned translation”, you’ll really like what this adds.

Predefined image size settings

If you’ve developed any site with ProcessWire, chances are you interact a lot with ProcessWire’s images field. Likewise, chances are that you create a lot of different size variations when producing output in your site. Perhaps something like this:

foreach($page->images as $image) {
  $thumb = $image->size(200, 200);
  echo "<a href='$image->url'><img src='$thumb->url'></a>";
}

With the code above, we are rendering 200x200 clickable thumbnails for all of the images on the page. Perhaps you also use a srcset attribute to deliver two different sizes depending on what the client side supports:

foreach($page->images as $image) {
  $thumb = $image->size(200, 200);
  $thumb2x = $image->size(400, 400);
  echo "
    <a href='$image->url'>
      <img src='$thumb->url' srcset='$thumb2x->url 2x'>
    </a>
  ";
}

Whatever you are doing with images, there’s a good chance that you are using the same image dimensions and/or settings in more than one location (like template files) in your site, simply as a matter of consistency. When working with images from the API, you have to remember those image width/height dimensions every time you need them. And should you ever need to change them, you might need to use your editor’s search function (or a grep of template files) in order to find and adjust all of them.

In a way, all those those specific $image->size() calls are kind of like writing repetitive inline CSS or JS across template files. We use dedicated CSS/JS files to avoid repeating ourselves and add efficiency. Maybe we should be able to do the same with images.

Having worked a lot with a very image-heavy site this week, I came to the conclusion that ProcessWire could benefit from a way to define image sizes/settings in a common location… kind of like how a CSS file provides a common location to define styles that are used in many places in a site, only simpler. Rather than always defining image dimensions and settings inline in the code, it seems worthwhile to be able able to name and refer to these settings in the same way that a CSS class refers to styles.

In ProcessWire 3.0.151, we now have a new $config->imageSizes setting. This is simply an associative array containing named sets of image settings, defined in /site/config.php, like this:

$config->imageSizes = [
  'thumb' => [
    'width' => 200,
    'height' => 200
  ],
  'thumb2x' => [
    'width' => 400,
    'height' => 400,
    'quality' => 50
  ]
];

The benefit is that we can now refer to these predefined image settings anywhere that we use an $image->size() call. For example, here’s our earlier code example updated to use our named image sizes (defined above):

foreach($page->images as $image) {
  $thumb = $image->size('thumb');
  $thumb2x = $image->size('thumb2x');
  echo "
    <a href='$image->url'>
      <img src='$thumb->url' srcset='$thumb2x->url 2x'>
    </a>
  ";
}

Each named entry in the $config->imageSizes array can contain width and height, but it can also contain any other option accepted in the $options argument of the $image->size() call. Meaning, you can specify things like quality, cropping, sharpening, and more. This example below demonstrates a few:

$config->imageSizes = [
  'landscape' => [
    'width' => 600,
    'height' => 300,
  ],
  'portrait' => [
    'width' => 300,
    'height' => 500,
    'quality' => 80,
    'suffix' => 'portrait'
  ],
  'person' => [
    'width' => 400,
    'height' => 550,
    'cropping' => 'north',
    'sharpening' => 'none',
    'suffix' => 'person',
  ],
  'person-large' => [
    'width' => 1600,
    'height' => 0, // proportional
    'upscaling' => false
  ]
];

Let’s say that we were writing code for a company directory that shows photos of all employees and includes an enlargeable portrait photo. Here’s how we could use a couple of the image settings above to abstract away the specific image settings from the code that outputs them:

$people = $pages->get('/company/team/')->children();

foreach($people as $person) {
  $image = $person->images->first();
  if(!$image) continue;
  $sm = $image->size('person');
  $lg = $image->size('person-large');
  echo "
    <a class='person' href='$lg->url'>
      <img src='$sm->url' width='$sm->width' height='$sm->height'>
      $person->title
    </a>
  ";
}

I've found this addition very useful in a site I'm working on now, and no doubt it'll also be very handy for the future maintainability of the site as well. The bigger (and more image intensive) the website, the more useful it will be. But I think it's worthwhile at any scale.

Language translation function improvements

In ProcessWire 3.0.151 there’s now a solution for an issue that keeps arising [for me at least] with multi-language translated text. If you maintain any multi-language websites, chances are you’ve run into this too. In any multi-language website, you are going to have many static translations that look like this submit button below:

<button type="submit" name="submit">
  <?= __('Submit') ?>
</button>

This is an overly simple example, but I think will demonstrate the issue well. That snippet of code above is the submit button for a contact form. And that “Submit” label as been translated into various other languages that the website delivers content in (German, French and Spanish).

The client has just decided that “Submit” is not clear enough, and that now it must say “Click here to send now”. We oblige, and now the code looks like this:

<button type="submit" name="submit">
  <?= __('Click here to send now') ?>
</button>

But now we’ve got a small problem— all the translated “Submit” text is now considered abandoned since the text has changed, and there is now no translation for our new text in any of the languages. What that means is that all of our other languages now also see the “Click here to send now” text in English, rather than in German, French and Spanish.

In the past, the only way to solve this problem was for the people translating to go in and provide a translation for the new text. But who knows if they are available or not. Usually this takes time, sometimes lots of it.

We’d much rather just have it continue showing the translated “Submit” text for the other languages, rather than showing something in English that many users may not understand. In ProcessWire 3.0.151, this is now possible. We can now specify that we’ll accept a translation for 2 or more different phrases, and it’ll use the first one that a translation is available for. We do this by providing an array of acceptable translation labels, in order of preference:

<button type="submit" name="submit">
  <?= __([ 'Click here to send now', 'Submit' ]) ?>
</button>

The above basically says this: Use the “Click here…” label if a translation is available, otherwise use the translation for “Submit”.

With that in place, now our English users see the new “Click here…” label, but our German, French and Spanish language users will continue to see the translated “Submit” text in their language. Of course, once our translators have a chance to translate the new text, then they will see the newly translated "Click here..." text. But until then, “Submit” is just fine.

Things to keep in mind:

  • As always, the entire __('text') function call must be on a single line. It may be tempting to use multiple lines when an ['a', 'b', 'c'] array is present, but you must keep it on 1 line still.

  • You must use PHP bracket array syntax. This means that ['a', 'b'] will work but array('a',' b') will not. The reason for this is that these function calls are not just executed, but also parsed directly from the code (when translating). Sure, we could support array() syntasx, but as a matter of keeping things simple, I decided just to support bracket array syntax for now.

  • You may also use the contextual _x(…) function with the array syntax, i.e. _x(['great', 'good'], 'quality');

  • In classes (like modules or your own Wire derived classes), you may also use the array syntax with any $this->_(…) or $this->_x(…) call. For example: $this->_(['Send', 'Submit']);

  • When editing translations in the admin (Setup > Languages > Translate File), fallback translations do not show as "abandoned" and instead show below the input for the new translation. Specifically, it indicates what translated text will be used while the new phrase remains untranslated for a particular language.

A couple weeks pass and we hear from the client again. They’ve decided that “Click here to send now” is too long, and they instead want it to just say “SEND NOW” in uppercase. “Sure, No problem” we say:

<button type="submit" name="submit">
  <?= __([ 'SEND NOW', 'Click here to send now', 'Submit' ]) ?>
</button>

No longer do we have to break static translations every time a change is needed. The end result is happier site users, more relaxed translators, and more professional output.

But let's not forget, there's a very good reason for static translations to break (by design) when the source text changes. If the change is a label like "Turn Right" that has now been changed to "Turn Left", then we don't want to keep saying "Turn Right" in any language. So you should only use this new translation feature on text phrases that contain inconsequential changes, where the previously translated label would be preferable to an untranslated label. But the nice thing is that now you can make the call as to whether the change is consequential or not.

That's all for this week — thanks for reading, and have a great weekend! For more ProcessWire news and updates head on over to the ProcessWire Weekly.

Comments

  • HMCB

    HMCB

    • 4 years ago
    • 32

    Standing ovation for the predefined image sizes! Working with images is the worst part of my job. Now if there was a simple PW tag that could output all the sourceset stuff based on image configure settings.

    • Tomas Kostadinov

      That is something that you could improve by either
      - usign field templates
      - create a hook that hooks into outputting of the field
      - using a custom image field module that extends the processwire default image field
      - or, just write a function that takes an image and config array and returns the output

 

PrevDriving around a DDOS attack

7

Last Saturday we started getting hit with heavy traffic at the processwire.com support forums, and it soon became a full blown DDOS frenzy. This post describes all the fun, how we got it back under control, and what we learned along the way. More 

NextProcessWire 3.0.152 core updates

6

This week we have some major improvements to our core date/time Inputfield, as well as a new ability to specify your own custom classes for Page objects. Read on for all the details and examples.  More 

Latest news

  • ProcessWire Weekly #520
    In the 520th issue of ProcessWire Weekly we'll check out some of the latest additions to the ProcessWire module's directory, share some highlights from the latest weekly update from Ryan, and more. Read on!
    Weekly.pw / 27 April 2024
  • ProFields Table Field with Actions support
    This week we have some updates for the ProFields table field (FieldtypeTable). These updates are primarily focused on adding new tools for the editor to facilitate input and management of content in a table field.
    Blog / 12 April 2024
  • Subscribe to weekly ProcessWire news

“To Drupal, or to ProcessWire? The million dollar choice. We decided to make an early switch to PW. And in retrospect, ProcessWire was probably the best decision we made. Thanks are due to ProcessWire and the amazing system and set of modules that are in place.” —Unni Krishnan, Founder of PigtailPundits