MailChimp changes all signup forms to single opt-in

A friend of mine who I set up a WordPress website for and who is using MailChimp for her newsletter just received an email some hours ago she forwarded to me:

In this email, MailChimp was informing her, that starting October 31, all signup forms will be switched to a single opt-in process, rather than the current default of a double opt-in.

A big problem for websites in Europe

She didn’t really knew, what that meant, but when I saw the email, I couldn’t believe what I’ve just read. The double opt-in ensures that anyone who ends up on your email subscribers list confirmed this by clicking a link in an email sent to them after filling out the signup form.

Read more →

Translate Customizer options with Polylang

When it comes to translations, my favorite plugin is MultilingualPress. For a new client project with a small budget and only a couple of static pages, we decided to choose Polylang, which doesn’t require a Multisite.

Issues translating content

One of the main reasons I prefere MultilingualPress over other solutions is the fact, that is uses the Multisite functionality of WordPress. In this case, it’s usually pretty easy to translate any aspect of the website. It’s even possible to use different theme, plugins, widgets, menu item, etc. With a single site installation, some texts are only available ones, like plugin properties and also Customizer options.

In the mentioned project, the theme provided a Customizer option for contact information used on the front page next to the header logo, as well as a textarea for a custom footer. As on a German website you usually link to the “Impressum” and the privacy policy, that was one of the options we needed to be able to translate. But also the contact information at the top had some texts that should be translated, such as dates.

Using a child theme for the translations

Unfortunately, I couldn’t find a way to solve the translation issues with the theme’s codebase. So I decided to implement a child theme, in which I’ve overwritten the header and footer templates. The header for example looked like this:

<div class="intro-wrap">
  <?php echo wp_kses_post( wpautop( get_theme_mod( 'header_intro' ) ) ); ?>
</div><!-- end .intro-wrap -->

The header_intro is a Customizer option that is taken from the option and just printed out. The can not change if with a filter. So how can we still translate it into the different languages? We simple have to wrap it with the pll__ function, the Polylang API is providing:

<div class="intro-wrap">
  <?php echo wp_kses_post( wpautop( pll__( get_theme_mod( 'header_intro' ) ) ) ); ?>
</div><!-- end .intro-wrap -->

Now the text would be translated, if such a translation exists for the current language. But how do you actually translate the option?

Register text for translations

To be able to translate any string, you have to tell Polylang about it. We use the function pll_register_string form the Polylang API to do that. In our functions.php file, we add these lines:

if ( function_exists( 'pll_register_string' ) ) :
	/**
	 * Register some string from the customizer to be translated with Polylang
	 */
	function child_theme_name_pll_register_string() {
		pll_register_string( 'header_intro', get_theme_mod( 'header_intro' ), 'child-theme-name', true );
	}

	add_action( 'after_setup_theme', 'child_theme_name_pll_register_string' );
endif;

First we check, if the functions exsists, as otherwise we might cause a fatal error, if we deactivate Polylang. Then we register a callback function and in this function we use the Polylang function to register the string. The first parameter is just a name to order the translatable string. The second parameter is the actual text we want to translate. In this case, we use the dynamic Customizer option value. The third parameter is to group strings and the last parameter tells the function, that it’s a multiline strings, so in the backend we see a textarea rather than a single line text input.

Doing the translation

All registered string can be translated using the settings page “Polylang -> Strings translations” in the backend. Here we should find the current value of the string an a textarea per language we have configured in Polylang. It’s important to keep in mind, that every time the Customizer option is changed, we have to update the translation as well, as the original string has changed.

Conclusion

As you can see, translating Customizer options per language is also possible when using Polylang. But we need to create a child theme in most cases. Translating options of plugins can be a lot trickier, as we can not simple create a child plugin. We usually have to use the filters, a plugin might provide to change it’s texts and option. This is also the reason why I still prefer a Multisite approach when I need a multilingual website.

Bonus tip

As Polylang is a rather new plugin, much newer then WPML (which I usually don’t use), it support the WPML language configuration file wpml-config.xml, so if your theme says, that it’s WPML ready and has this file, you can probably skip some of the steps I’ve shown you in this blog post, or even all of them.

Global WordPress Translation Day 3

On Saturday, the third Global WordPress Translation Day took place all around the world. Starting at midnight UTC, the day was filled with a 24h schedule with 22 presentations.

Local events

Just as on the first two WP Translation Days, there were many local events. On the second WP Translations Day, we had 38 events. This time, we almost doubled the number and had 71 events (maybe even more, which were not announced), six of them in Germany:

The Berlin meetup group, which I am a co-organizer of, participated on all events. This year we were a group of 8 translators and really got some work done. A big thank to the Designerei.berlin who donated us a room for free and some coffee, to get the day started:

https://twitter.com/svenbrier/status/914110427457556480

Learning and translating

After setting up, I gave a quick introduction on how to get started translating WordPress. I first explained, how you get a WordPress.org account and how you connect to the international and German slack teams, to be able to ask questions in the polyplot channels. Then I introduced the glossary, the style guide and the quick start guide. We then worked together on the formal German translation of Contact Form 7. Then everyone worked on another plugin or theme and everyone was helping out each other on questions around translations. One of our attendees got CF7 translated to 100% and I could validate all strings the same day.

We also watched the live streamed session of Brigit Olzem from the official schedule. To round things up, I also gave a small presentation on internationalization, so our attendees also learned how to make you own theme or plugin translatable.

Summary

Unfortunately, I haven’t seen any numbers on how many people attended local events, watched the stream or how many strings, plugins and themes were translated. But our Berlin meetup group alone finished a couple of plugins and themes. On the website of the WP Translation Day, you can find some numbers of what has been achieved since the beginning of WPTD3. You can also watch all sessions from the schedule:

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

This was probably not the last Global WordPress Transation Day and we will also most probably organize a local events on the next iteration. If you haven’t been able to participate (or didn’t even know about it), but you want to learn translating WordPress, find a WordCamp near you and join the polyglot team on it’s Contributor Day. Translating WordPress is really an easy start into contrubitung something back and it can have a big impact on users speaking your language.

Show tags of private posts in the tag cloud

No WordCamp last weekend, so today, you get a regular post 😉 This week, I had an interesting request in a client project. The WordPress website had a FAQ section and some of the questions were set as private posts. The website was using a tag cloud, to make it easier for visitors, to find questions. But even if the user was signed in, he couldn’t see the tags used only on private posts.

How are tags in the tag cloud generated?

When WordPress generates the HTML for the tag cloud, it queries a limited number of tags, ordered by count. But how is this count calculated. You would think, that a database query groups the tags on the posts, then groups by the tag ID and calculates the count. But this is not how it works in WordPress. For performance reasons, the count is stored statically in the database table. But why are private posts not showing up in the tag cloud? Once you change the visibility of a post to private, the posts is not added to the count of the tag. Only public posts are used to calculate the count. So there is not easy way using a filter/action to include the private posts. So how could it be solved?

Altering the query

Almost every query in WordPress, that resolves in the output of “a list”, is using a WP_Query. For the tag cloud, the WP_Term_Query is used. So we simple have to alter this query. But how do we know, that we are in the WP_Term_Query used in the tag cloud? Ther is no is_tag_cloud() or similar conditional tag.

The tag cloud is using two very specific arguments, that are only present in the tag cloud: largest and smallest. These two arguments are used, to set the minimum and maximum font size used in the tag cloud. So we can easily use one of this arguments, to catch the tag cloud’s WP_Term_Query. The rest is rather easy. We use the term_clauses filter to change the SQL query:

function private_posts_in_tc_terms_clauses( $pieces, $taxonomies, $args ) {
	if ( isset( $args['largest'] ) && is_user_logged_in() ) {
		// Count by the grouped term_id.
		$pieces['fields'] .= ', COUNT(t.term_id) AS grouped_count';
		// Join the relationship to the posts.
		$pieces['join'] .= ' INNER JOIN wp_term_relationships AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id';
		// Remove the "redundant" count (only for public posts).
		$pieces['where'] = str_replace( 'AND tt.count > 0', '', $pieces['where'] );
		// Group by the term_id to remove duplicates and calculate the count.
		$pieces['where'] .= ' GROUP BY tt.term_id HAVING grouped_count > 0';
	}

	return $pieces;
}
add_filter( 'terms_clauses', 'private_posts_in_tc_terms_clauses', 10, 3 );

We also make sure, that we only show the private tags to logged in users. In the altered SQL query, we join the term_relationship table, group by the term ID and count on the terms. This way, we get the correct count including the private posts. The query itself is a bit slower than the original one, as it is not using the “cached” count from the table. But every query to the WP_Term_Query is cached in a transient anyways, so the performance impact should not be too high.

Conclusion

It’s not always as easy as using a simple hook, to change a query in WordPress. But as usually, there is always a way to get the desired result. If you want to use this function yourself, you can find the code as a plugin in a GIST. So simply download it as a ZIP file and install it as a plugin on your site.

WordCamp Zagreb – My journey to the Croatian community

Another weekend, another WordCamp, another new city … well, actually another new country in Europe I visited. Only two weeks after my vitsit to WordCamp Brigthon, I traveled to Zagreb, the capital of Croatia, to meet a new european community.

Thursday: Arriving in Zagreb and warm up

I took a flight from Berlin to Vienna, back to the city of WordCamp Europe 2016, and then another flight to the new Zagreb airport. The temperature was pretty high and I was very thankful to Lucijan and Emanuel for picking me up and driving me to the city. First I checked in to my special accomadation 🙂

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

In the evening the organizing team reserved some places in a nice restaurant in the city center, so I met the first other attendees and enjoyed a nice little inofficial warm up event.

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Friday: Workshop Day

Having workshops on a WordCamp is becoming more popular lately. Not having signed up for any of the workshops before Friday, I just showed up and attended two English workshops. Most of the other workshops have been in Croatian. Only another one was in English, a workshop for children. I really liked that idea.

The Art of Public Speaking

The first workshop was held by Luca Sartoni a members from the Italian WordPress community, who is well-known for his great talks at WordCamps. In his workshop, he was giving some great tips on how to prepare better talks. He not only concentrated on the slides and the talk itself, but also all the research, preparation and steps you have to do before the actual event. After this workshop, I had some work to do on my talk for Saturday 🙂

The Irresistible Power of Strategic Storytelling

My second workshop was not necessarily one, you would expect me as a developer in. But as I am also a regular blogger, the topic storytelling is not that uninteresting for me 😉 Nevena Tomović really made this a great workshop. After she gave an introduction into the topic, we had to form groups and work on any random story we wanted to tell about a place we wanted to convince people to visit. As I was the only one not from Croatia, the rest of our team came up with some ideas about a mountain near Zagreb. I was quite surprised, how creativ I was, when telling a story about the quick notes I have taken. This really was a great experience 🙂

Speakers dinner

At the end of workshop day, I attended the speakers dinner. I enjoyed a second day with some amazing Croation specialities and many interesting conversations with organizers, speakers, sponsors and volunteers.

Saturday: Conference Day

Following the workshop day, the “main event” was taking place in the Museum of Contemporary Art. The WordCamp had two tracks.

The Human-Centered Brand

In the first slot, there was only one session. The keynote was held by Nela Dunato who has been voted best speaker of last years WordCamp in Croatia. She gave an inspiring talk on branding in a personal context.

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Plugin development

I spent most of the day in track B, which was packed with talks for developers. The first three were focusing on plugins. First, Ante Šepić talked about his plugin developement setup. It was interesting to see, that he is not using the WordPress Coding Standards, but the PSR standards, used by many PHP frameworks. The second talk from Ratko Šolaja also focused on code quality and the In’s and out’s of plugin creation. The third talk regarding plugin development by Goran Jakovljević focused on bulding and selling premium plugins. Goran gave some great tips ony pricing models, service and support. I still have no plans to publish a premium plugins, but after this talk, I have some new insights on how to get started.

DeSign everything

Just after the third talk on plugin developement, Anastasios Manoloudis was giving an inspirational talk on “design“. But he not only focused on visual design, but also all the other things around design, like the language we use.

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Why bother with code checking?

One thing I really liked at this WordCamp was the fact, that every talk on development mentioned the WordPress Coding standards. Denis Žoljom was no exception to that, but his focus was on code checking in general. He presrented multiple tools developers can use to check not only their PHP code, but also the CSS and JavaScript of your code.

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Vue.js with WordPress

I am not really a JavaScript framework developer, so the talk on Vue.js from Marko Banušić was quite interesting for me. He showed some basic examples on how you can replace dynamic parts of your website with Vue.js templates.

WordCamps and why are they important

As a regular WordCamp attendees I had to leave track B for the session of Milan Ivanović about the importance of WordCamps. I could really identify with many of the stories, Milan was telling the audience.

Developing a new default WordCamp theme

Finally, at the seventh WordCamp this year, I had my vist talk. The lead organizer Lucijan Blagonić, who was members of the design team of WordCamp Europe, which I was also part of, asked me to talk about the process of developing the new WordCamp theme, so I did 🙂

Open Sources Of Inspiration

In the last slot, there was no talk in track B, so I attended the session from Dario Jazbec Hrvatin, which was really inspirational, not only in the title 😉

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

After party

Have I mentioned the great food in Croatia yet? 😉 At the small after party in the same venue, there was even more of it. On the three days of the event, I tried to get something from almost eveything they had and I really liked it.

Sunday: Contribtor Day

The WordCamp Zagreb had it’s Contributor Day on Sunday. Around 30 people attended and split into four groups: Core, Localization, Support and Community. While the community team discussed about different topic not only aroud the Croatian community, but also the one in Serbia and other countries in the area, I helped new Core contributors getting started, as the team lead for the Core team couldn’t be there.

Walking tour

My first WordCamp in Croatia ended with a 2h walking tour in the city center of Zagreb, were I learned a bit about the history of the city and the country.

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Conclusion

I really enjoyed my first trip to Croatia and my first WordCamp Zagreb. Not only the food was amazing, also the quailty of the talks and the welcoming community. The fact, that all session were in English really helped to enjoy the event. For a local WordCamp, this was something I didn’t expected. I would really like to attend another WordCamp in Croatia, maybe in a new city 🙂

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

 

My first WordCamp Brighton

About 20 years ago, I first visited the UK, when I’ve spent 3 weeks at the coast in Sussex. There was a day trip to Brighton, but I was invited to a weeding by my host family. But after missing the WordCamp last year, I finally made it to Brighton. And it’s been really a nice one.

Thursday: Arriving in Brighton and warm up

Just like last year, there have been a couple of German attendees at WordCamp Brighton. I flew in from Berlin pretty early at 6:30 in the morning. At Stansted airpot, I met two other German attendees, Thomas Brühl and Matthias Kurz from the Cologne Meetup group. We had a nice breakfast in London and took a train to Brighton, in the afternoon. There we met with my very good friend Maja Benke from Berlin. We took a nice walk at the beach and enjoyed the nice, but pretty windy weather.

Later that day, there was a little speakers dinner and I was a backup speaker for WordCamp Brighton, I enjoyed a nice evening with some new members from the WordCamp community. One of them came all the way from the US to Brighton.

Friday: Workshop Day

WordCamp Brighton was a three day event. It started on Friday, with a schedule that not only had regular session, but also some “workshops” in which attendees were asked to do some “tasks”. With only one track, I had the chance to see every session on the two days. So I want to highlight a few from the schedule.

Things you didn’t know you need to know about databases and WordPress

The first talk I attended was a session about database optimizationGabor Javorszky explained in detail, how indexes are working. That was really a very interresting talk, as some of the details of compound keys were new to me. I hope, that with this new knowledge, I can write even faster queries in upcoming projects.

Workshop 2 – The A to Z of WordPress Multisite

John Blackbourn gave a brief intoduction into Multisite in the second workshop of the day. It was not really a workshop were atteeds had tasks to do, but I think that many of them learned, which benefits a Multisite can have and which things are working differently to a single site installation.

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Easing the anxious mind: dealing with anxiety in the workplace

I really like talks like these. We often forget about those topics in our daily jobs, but anxiety is not something that is rare. And having someone talking about this on a WordCamp is really important. Laura Nelson had a wonderful job in her talk about anxiety in the workplace.

Accessible design

The last lightnight talk of the Workshop Day was from Maja Benke. She gave a brief overview on the most important things you have to take into account, when making sure that your website is accessible. She focused on design aspects, rather than technical details, which are usually coverd in accessibility talks.

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Saturday: Conference Day

The second day of the WordCamp was a traditional conference day with sessions and lightning talks.

Beyond responsiveness: creating truly fluid themes

The second talk of the day was from Michael Burridge and he was giving an interresting talk on responsive design. After he gave a small introduction into responsive design in general, he showed a different approach as the usual design with fixed breakpoints. Instead, he used the CSS sizes vw to produce a truly fluid theme, were all elements resized just like in a PDF file.

Pursuing your creative passion: turn your side hustle into a full-time gig

Katie Elenberger gave an inspirational talk on her way to become a successful freelancer. She encouraged the audience to take a little risk and follow their passions. A really great talk!

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

REST APIs for absolute beginners

Right after this talk, Tom J Nowell gave a beginner talk on the REST API. He explained the fundamentals of the API and how it can be used. He then explained how you can write custom endpoints to use the REST API with you own custom post types.

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Web accessibility, where to start?

The last talk of WordCamp Brighton was from Rian Rietveld. She talked about the basics of accessibility, not only from a coder point of view but also on how to involve designers and projects managers into the process. It was a perfect match to Maja’s talk from the day before.

After party

At the end of the Conference, we had a little after party with dinner and drinks. The attendees could also put songs on a public playlist, which resulted in a very interresting choice of music from on that evening.

Sunday: Contributor Day

WordCamp Brighton had it’s Contributor Day on Sunday after the two days with the sessions. Around 35 members attended the Contributor Day. I helped out in the Accessibility Team, where I worked on the new WordCamp Theme and wrote some patches to the plugin/theme repository and the make handbook.

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Conclusion

This was my first WordCamp in the UK outside of London and I really enjoyed it. Not only was it nice to see some other people from the British community, but I finally had the chance to visit Brighton. Now I only hope, that the weather will be a bit better tomorrow, so I can take the change to go swimming a bit 🙂

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Fixing SSL on sites using iThemes Security

In a Facebook group, we had a question this week, which was a bit tricky to solve. The member had migrated the site to HTTPS. After the migration, some images were not loading anymore. First we asked, if the search and replace on the database was done, so the paths were updated. This was done, but still some images were not loading.

Wrong domain for SSL certifitcate

So I checked the website as a visitor and inspected the certificate. It was issued for the .com TLD but some files were served from a .de TLD, which was a bit strange. My first guess was a theme with static paths to the domain. So I asked to send me the functions.php and header.php file from the theme to check them. Nothing static there.

Read more →

Fixing broken plugin translation in WPML

This week in a project, I had the issue, that for plugin was not loaded in the correct language. The project war multilingual with three languages. The default language was German, one of the other two languages was English. Usually when you experience broken translations, all texts are shown in English only. But not this time. The strings from the plugin were always translated to German, the default language, even on the English site.

The project was using WPML to translate the website into two other languages. For all other plugins, the theme and the content, the translation was working as expected. Just not for this one plugin. Finding the issue was not easy, but once found, the fix was rather easy.

The problematic code

Two years ago at WordCamp Berlin 2015, I gave a talk on how to initialitze a plugin correctly. I had a similar issue with missing translation. But in this case, it was always showing the English original strings.

<?php
/**
 * Plugin Name: Broken Plugin
 */
    
// Load translation files.
load_plugin_textdomain( 'broken-plugin', false, 'broken-plugin/languages' );
    
// Initialize plugin.
add_action( 'after_setup_theme', 'broken_plugin_theme_setup', 12 );
    
function broken_plugin_theme_setup() {
	// Some code ...
}
&#91;/php&#93;

I don't want to blame the plugin author for the small issue, so I anonymized the name. But above you can see the code, how it looked like in the plugin. As you can see in this short snippet, the language file is loaded right at beginnig of the plugin file. The plugin has a callback function registered to the <code>after_setup_theme</code> hook, which would be the perfect place to load the translation.
<h2>The reason it is broken</h2>
But why is the translation not working? As I told you, the translation files must must be loaded, otherwise the strings would not be in the default language German, but in English. The reason for that is quite simple. The plugin name started with the letter B (not only my anonymized example, but also the real one) and as plugins are usually loaded in an alphabetic order and the folder name of WPML is "sitepress-multilingual-cms", the plugin and it's code just came too early. The switch to the correct language was only done after WPML was fully loaded. But at this time, the translated strings of the broken plugin were already in the translations array (with the German string) and not overwritten with the correct language.
<h2>Fixing the plugin</h2>
So fixing the plugin would be easy, right? I just have to move the function call of `load_plugin_textdomain` into the callback function. That would work. But what would happen on the next update? Correct! The translation would be broken again.

So how to do it better? The best way to fix it, would be a bug report to the plugin author. This is what I did. I found the plugin on GitHub, fixed the bug on a fork and opened a pull request. But the plugin has some pending PRs and no new release for over two years. So I didn't expected my fix to be released any time soon. So we can now fix it in the plugin ourselves? We could, but there is another way. Why not fixing the plugin from the outwise. Doing this is pretty easy.

Many developers know the function <code>load_plugin_textdomain</code>, but as fix many functions named like this, there is usually a reverse function. In this case <code>unload_plugin_textdomain</code> which we could use:

[php gutter="false"]
function broken_plugin_trail_wpml_fix() {
	unload_textdomain( 'broken-plugin' );
	load_plugin_textdomain( 'broken-plugin', false, 'broken-plugin/languages' );
}
add_action( 'after_setup_theme', 'broken_plugin_trail_wpml_fix', 13 );

So within another callback function to the hook after_setup_theme we just first unload the translation and then load the file again using the exact same call. We use priority higher than the one in the plugin, wo even if the fix is being merged and we don’t recognize it right away, our fix will still work.

This code can be dropped in a small plugin (or temporarily in the functions.php file of the theme) and once the original plugin is fixed, we can remove it.

Conclusion

As you can see from the easy example, you can sometime fix issues in a plugin without changing it’s code, so on a plugin update, the fix will still work. I would still highly recommend, that you always try to provide a fix for the issue to the plugin author first. Some of them are really quick with releasing a patched version of the plugin, so you don’t have to find a way aroud. And even more important: this way you not only fix the issue for your site, but for all the other users who experince that issue.

A successful eighth year comes to an end

On June 21st, my blog had it’s 8th birthday. I had it bilinugal quite from the start but in the first few years I haven’t translated all of the blog posts. But starting this year, I wrote almost all of tmy blog posts in English first, followed by the German translation a week later. The anually “birthday blog post”, I wrote in German first though. But now here it comes also in English. A bit belated birthday celebrations.

As always (what you migh not yet know), I start with some statistics. I want to present you the Top 3 blog posts of the last year. In my German yearly review I also have the Top 3 since the start of my blog, but as they are the same for my English blost posts, I recude them to one Top 3 list:

Top 3 blog posts

  1. Free Cisco VPN Client alternative for Windows 7 x64 (64 bit)
  2. Shortcut of the month: CTRL + ALT + J (eclipse)
  3. Form preview with jQuery Thickbox

Those are some pretty only blog posts 🙂 The one on place 4 was my 5th overall, written on August 20th, 2009. In total, my blog has 55 blog posts in English (including this one), 27 written only this year and 264 blog posts in German published. So on my English blog, I am “slightly” getting a better number after 8 years. But it’s still a lot less. That’s mybe why the top 3 are so old.

I also posts some stats about the visitors of my blogs every year. The most active day was December 22nd, 2016 with 662 page views. There were also 188 new comments in the past year, 71 written by myself.

What’s next?

I hope it’s going on just the way it has in the last 27 weeks of the year 🙂 I’ll try to write a blog post until the end of each week. I might even try to write an advent calender for the third year in a row. This year maybe even in German AND English. But let’s see how that will work out 🙂

I am also thinking about working on the theme a bit. I might just improve it a bit or even switch to a new theme. But that would probably require me to reproduce all the perfectly scaled screenshots, which I would rather avoid. So stay tuned 🙂

I’ve always ended my birthday blog posts with a little video. They were usually funny, but for this year, I couldn’t decide on a funny one. Than I thaught I’ll just pick a video form WordCamp Europe some weeks ago, but there were so many good talks, that I couldn’t choose a single one. So instead I want to end this blog post with a more serious one, which has an important topic. I hope you’ll like it, even though the message might not be easy to digest. So sorry if you hoped for a funny one.

Click here to display content from YouTube.
Learn more in YouTube’s privacy policy.