Como tener un WordPress Theme multilenguaje con POedit

Respuesta rápida

Para tener un WP Theme en varios idiomas es necesario el programa POedit. A su vez, es necesario declarar la funcion load_theme_textdomain($id, $carpeta_idiomas) y cambiar todas las palabras y frases estáticas dentro del WP Theme por __($frase, $id) o _e($frase, $id).

Respuesta completa

Requisitos:

Tener un Theme de WordPress -a partir de ahora WP- multilenguaje, obviamente, permite a tu theme estar en varios idiomas.

Es importante aclarar que un WP Theme multilenguaje no es lo mismo que tener una página multilenguaje.

PERO, para tener una página multilenguaje es necesario tener un WP Theme multilenguaje y un plugin multilenguaje. No conozco ningún plugin multilenguaje gratuito que me guste, así que no voy a mojarme en recomendar alguno.

¿Cuántos idiomas puede tener un WP Theme?

Se pueden tener todos los idiomas que se quieran.

¿Cómo elijo el idioma de mi WP Theme?

En la carpeta raíz de WP se encuentra el archivo ‘wp-config.php’ y dentro se define el idioma de WP.

define('WPLANG', 'es_ES');

¿Y si mi WP Theme no tiene un idioma?

Pues utilizará el que tengas por defecto. Por eso, es aconsejable que el idioma por defecto sea el inglés, y luego usaremos el POedit para traducirlo al español.

¿Cómo puedo traducir mi WP Theme a un idioma que no conozco?

Siempre puedes encontrar apoyo en la comunidad de WP, o pagando a alguien para que lo haga.

Solo tienes que pasarle el archivo .po y el podrá traducir las frases del WP Theme a partir del idioma por defecto.

Más abajo se ve como editar el archivo .po

Estructura de ficheros

Los archivos a editar pueden ser bastantes, ya que habrá que actualizar todos los templates que muestren texto, como por ejemplo, «Error 404. Not found» – «Error 404. No se ha encontrado nada».

Personalmente, hago traducciones muy libres.

alisios/
 |- framework/
 |   |- extensions/
 |   |   |- languages/
 |   |       |- functions.php
 |   |       |- init.php
 |   | 
 |   |- alisios-functions-init.php
 | 
 |- languages/
 |   |- es_ES.mo
 |   |- es_ES.po
 |
 |- {templates que tienen textos, así como 404.php y archive.php}

Para inicializar la funcionalidad usaré la estructura de functions.php extensions, pero las traducciones se encuentran fuera del framework.

Nuestro alisios-functions-init.php llamará a languages/init.php

/* Languages */
require_once( $extensionsDirectory . '/languages/init.php' );

Nuestros languages/init.phplanguages/functions.php habilitarán el funcionamiento de las traducciones.

<?php
/**
 * Languages, I18n
 * Hooks & Filters
 */

require_once('functions.php');

add_action('after_setup_theme', array('AlisiosLanguage', 'load_i18n'));
<?php
class AlisiosLanguage {
    /*
     * Hook
     * Add in wp_fotter our bootstrap.min.js
     */
    public static function load_i18n() {
        load_theme_textdomain( 'alisios', get_template_directory() . '/languages' );
    }
}

I18n es el diminutivo de Internationalization porque hay 18 letras entre la i) y la (n.

La función load_theme_textdomain() recoge los parámetros.

El primero es el $id, el cual haremos referencia en cada texto que encontremos.

El segundo es el directorio donde nuestro WP deberá buscar las traducciones.

Ahora hay que modificar los templates para permitir las traducciones.

Los textos tienen distintas maneras de permitir la traducción. Por ejemplo:

<?php
__('Hello World!', 'alisios');
// no muestra nada, actúa como un get

echo __('Hello World!', 'alisios');
// muestra: Hola Mundo!

_e('Hello World!', 'alisios'); 
// muestra: Hola Mundo!, actúa como un echo 

printf(__('Hello %s!', 'alisios'), 'World');
// muestra: Hola World!, útil para meter un texto aleatorio de por medio.

Todas son igual de válidas.

Para mi Theme Alisios he tenido que modificar los templates 404.php, archive.php, loop-empty.php, search.php y searchform.php.

Como ejemplo, voy a mostrar los templates 404.php y archive.php:

<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>

<?php get_header(); ?>

    <section id="primary" class="content col-xs-12 col-sm-8" role="main">

        <article id="page-404" class="page not-found">

            <header class="entry-header">

                <h1 class="entry-title"><?php _e('Not Found. Error 404', 'alisios'); ?></h1>

            </header><!-- .entry-header -->

            <section class="article-content">

                <?php _e( "<p>I'm sorry, at this location there are nothing and what you looking for no longer (or indeed never did) exists.</p><p>However, there are cool stuff here. Take a look!</p>", 'alisios' ); ?>

                <?php
                get_search_form();

                the_widget( 'WP_Widget_Recent_Posts' );
                ?>

            </section><!-- .entry-content -->

        </article><!-- #page-404 -->

    </section>

<?php get_footer(); ?>
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>

<?php get_header(); ?>

    <section id="primary" class="content col-xs-12 col-sm-8" role="main">

        <header class="page-header">

            <h1 class="archive-title">
                <?php
                if ( is_category() ) :
                    printf( __( 'Category Archives: %s', 'alisios' ), '<span>' . single_cat_title( '', false ) . '</span>' );

                elseif ( is_tag() ) :
                    printf( __( 'Tag Archives: %s', 'alisios' ), '<span>' . single_tag_title( '', false ) . '</span>' );

                elseif ( is_author() ) :
                    /* Queue the first post, that way we know
                     * what author we're dealing with (if that is the case).
                    */
                    the_post();
                    printf( __( 'Author Archives: %s', 'alisios' ), '<span class="vcard"><a class="url fn n" href="' . esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ) . '" title="' . esc_attr( get_the_author() ) . '" rel="me">' . get_the_author() . '</a></span>' );
                    /* Since we called the_post() above, we need to
                     * rewind the loop back to the beginning that way
                     * we can run the loop properly, in full.
                     */
                    rewind_posts();

                elseif ( is_day() ) :
                    printf( __( 'Daily Archives: %s', 'alisios' ), '<span>' . get_the_date() . '</span>' );

                elseif ( is_month() ) :
                    printf( __( 'Monthly Archives: %s', 'alisios' ), '<span>' . get_the_date( 'F Y' ) . '</span>' );

                elseif ( is_year() ) :
                    printf( __( 'Yearly Archives: %s', 'alisios' ), '<span>' . get_the_date( 'Y' ) . '</span>' );

                else :
                    _e( 'Archives', 'alisios' );

                endif;
                ?>
            </h1>

            <?php
            if ( is_category() ) :
                // show an optional category description
                $category_description = category_description();
                if ( ! empty( $category_description ) ) :
                    echo apply_filters( 'category_archive_meta', '<div class="taxonomy-description">' . $category_description . '</div>' );
                endif;

            elseif ( is_tag() ) :
                // show an optional tag description
                $tag_description = tag_description();
                if ( ! empty( $tag_description ) ) :
                    echo apply_filters( 'tag_archive_meta', '<div class="taxonomy-description">' . $tag_description . '</div>' );
                endif;

            endif;
            ?>

        </header><!-- /.page-header -->

        <?php if( have_posts() ) : ?>

            <?php get_template_part( 'loop' ); ?>

        <?php else : ?>

            <?php get_template_part( 'loop', 'empty' ); ?>

        <?php endif; ?>

    </section>

<?php get_footer(); ?>

Ya tenemos el código preparado. Ahora, ¡A traducir!

¿Cómo se utiliza el POedit?

En realidad es sencillo, una vez sabes como hacerlo. (Que frase más tocahuevos)

Abrimos el POedit.

POedit - Abrir Nuevo
Nos dirigimos a Archivo > Nuevo (Ctrl + N).
POedit - Seleccionar idioma
Seleccionamos el idioma de la traducción.
POedit - Ir a propiedades
Vamos a Catágolo > Propiedades.
POedit - Editar Propiedades
Editamos las propiedades poniendo nuestra información.
POedit - Palabras claves
Las palabras claves son aquellas que usaremos para traducir (__, _e, _n).
POedit - Directorio Fuente bloqueado
El directorio fuente estará bloqueado hasta que guardemos y ubiquemos el archivo. Lo guardaremos en alisios/languages.
POedit - Editar directorio raíz
Un solo punto .) significa que la carpeta raíz donde buscará las palabras claves es la actual.
Dos puntos ..) significa que la carpeta raíz donde buscará las palabras claves es la carpeta padre.
Hace búsqueda recursiva.
POedit - Hacer click en actualizar
Con la vista de contenido vacía solo hay que darle a actualizar y buscará las palabras claves en todos los archivos del directorio raíz.
POedit - Muestra nuevos y obsoletos
Las frases nuevas están pendientes de traducirse.
Las obsoletas son aquellas que dejan de existir. De éstas no te preocupes
POedit - A traducir
Ahora solo queda traducir todo y darle a Guardar

Cuando se añadan nuevos textos, o se modifiquen los que ya están, tan solo hay que darle a actualizar y POedit hará la búsqueda y la ayuda en las traducciones.

¿Qué es eso de archivo POT?

Se puede entender como archivo POT que es un archivo .po estático, es decir, no se hace búsqueda ni hay directorio raíz ni nadená, sencillamente tiene las ristras para traducir.

Los archivos .pot se pueden encontrar en formato powerpoint. Eso es debido a la extensión, si se habré con el Notepad++ o el bloc de notas verás que tiene el mismo formato que el .po

¡Ya está!

Ver Alisios v0.3