Cómo traducir de forma fácil una aplicación Golang

Translation into Spanish of an interesting article by Vic Shóstak, software engineer with over 13 years of practical experience (Go, Python, Kotlin, TypeScript), UX Consultant, and Open Source Supporter.

APPgolangtranslation into spanish
23 May, 2022 Translate your Golang App.
23 May, 2022 Translate your Golang App.

A free translation by Chema, a Spanish translator who loves to translate apps into Spanish

An original text written by Vic Shóstak, originally published in
https://dev.to/koddr/an-easy-way-to-translate-your-golang-application-5ege

* * *

 TUTORIAL SOBRE GOLANG (una serie de 13 partes)
1. La forma más fácil de incrustar archivos estáticos en un archivo binario en una aplicación Golang (sin dependencias externas)
2. Escribamos la configuración para su aplicación web Golang de la manera correcta: YAML...
3. ¿Cómo actualizar la caché de la versión en pkg.go.dev?
4. ¿Cómo implementar Golang en cualquier sistema GNU/Linux, pero sin Docker?
5. Una potente CLI para crear un nuevo proyecto listo para producción con backend, frontend y automatización de implementación
6. ¿Qué hay de nuevo y especial en Create Go App CLI v1.7.0?
7. Trabajando con RabbitMQ en Golang, con ejemplos
8. Asynq: cola de tareas distribuida simple, fiable y eficiente para tu próximo proyecto Go
9. Una manera fácil de traducir una aplicación Golang
10. El proyecto Create Go App ha pasado a v2, aún es más fácil, mejor, más rápido y más fuerte
11. ¿Cómo enviar mensajes de error claros y bonitos desde el backend de Go a su frontend?
12. Optimización de consultas PostgreSQL para Gophers: ¡es mucho más fácil de lo que parece!
13. Herramientas útiles de Golang para que tu código vuelva a ser genial

 

Hablemos de uno de un tema muy importantes si estás preparando una aplicación Go para una audiencia multilingüe o simplemente necesitas soporte de traducción en diferentes idiomas con las APIs REST.

Introducción

El tema de la traducción no es tan simple como parece, porque cada idioma tiene sus propios elementos especiales especialmente cuando se usan números. Por ejemplo, en ruso hay 3 variantes diferentes para cantidades de artículos:

  • one, 1 articulo;
  • few, 2 artículos;
  • many, +3 artículos;

🤔 ¡Hay que saber esto a la hora de traducir una aplicación!

No te preocupes, pronto explicaremos cómo conseguirlo!

📝 Índice

Código fuente del proyecto

Para aquellos a quienes les gusta ver el código antes de antes, creé un repositorio en GitHub:

koddr tutorial-go-i18n
📖 Tutorial: Una manera fácil de traducir tu aplicación Golang

Preparar el proyecto para la traducción.

He probado muchos paquetes (incluido el integrado en el núcleo de Go), y de todos nicksnyder/go-i18n ha sido el único que realmente me ha gustado. Crearemos nuestra aplicación de demostración usando este paquete en particular.

👋 ¡Escribe en los comentarios qué paquete de i18n usas y por qué!

Aplicación web

Usaremos el framework de Fiber como núcleo de nuestra aplicación, ya que tiene una excelente compatibilidad con plantillas (con una función de recarga suave) y permite leer y escribir código de manera sencilla.

🔥 ¡No dejes de leer los comentarios en el código!

// ./main.go

package main

import (
    "log"
    "strconv"

    "github.com/BurntSushi/toml"
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/template/html"
    "github.com/nicksnyder/go-i18n/v2/i18n"
    "golang.org/x/text/language"
)

func main() {
    // Create a new i18n bundle with default language.
    bundle := i18n.NewBundle(language.English)

    // Register a toml unmarshal function for i18n bundle.
    bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)

    // Load translations from toml files for non-default languages.
    bundle.MustLoadMessageFile("./lang/active.es.toml")
    bundle.MustLoadMessageFile("./lang/active.ru.toml")

    // Create a new engine by passing the template folder
    // and template extension.
    engine := html.New("./templates", ".html")

    // Reload the templates on each render, good for development.
    // Optional, default is false.
    engine.Reload(true)

    // After you created your engine, you can pass it
    // to Fiber's Views Engine.
    app := fiber.New(fiber.Config{
        Views: engine,
    })

    // Register a new route.
    app.Get("/", func(c *fiber.Ctx) error {
        lang := c.Query("lang")            // parse language from query
        accept := c.Get("Accept-Language") // or, parse from Header

        // Create a new localizer.
        localizer := i18n.NewLocalizer(bundle, lang, accept)

        // Set title message.
        helloPerson := localizer.MustLocalize(&i18n.LocalizeConfig{
            DefaultMessage: &i18n.Message{
                ID:    "HelloPerson",     // set translation ID
                Other: "Hello {{.Name}}", // set default translation
            },
            TemplateData: &fiber.Map{
                "Name": "John",
            },
        })

        // Parse and set unread count of emails.
        unreadEmailCount, _ := strconv.ParseInt(c.Query("unread"), 10, 64)

        // Config for translation of email count.
        unreadEmailConfig := &i18n.LocalizeConfig{
            DefaultMessage: &i18n.Message{
                ID:    "MyUnreadEmails",
                One:   "You have {{.PluralCount}} unread email.",
                Other: "You have {{.PluralCount}} unread emails.",
            },
            PluralCount: unreadEmailCount,
        }

        // Set localizer for unread emails.
        unreadEmails := localizer.MustLocalize(unreadEmailConfig)

        // Return data as JSON.
        if c.Query("format") == "json" {
            return c.JSON(&fiber.Map{
                "name":          helloPerson,
                "unread_emails": unreadEmails,
            })
        }

        // Return rendered template.
        return c.Render("index", fiber.Map{
            "Title":        helloPerson,
            "UnreadEmails": unreadEmails,
        })
    })

    // Start server on port 3000.
    log.Fatal(app.Listen(":3000"))
}

Plantilla para mostrar

Normalmente, no me gusta usar bibliotecas CSS prefabricadas, pero para esta demostración, por simplicidad y facilidad, he preferido usar la biblioteca Bootstrap 5 (v5.0.0-beta3):

<!-- ./templates/index.html -->

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>{{.Title}}</title>
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6"
      crossorigin="anonymous"
    />
    <style>
      * {
        font-family: sans-serif;
        color: #333333;
      }
    </style>
  </head>
  <body>
    <div class="col-lg-8 mx-auto p-3 py-md-5">
      <h1>{{.Title}}</h1>
      <br />
      <div class="row g-5">
        <div class="col-md-6">
          <ul class="icon-list">
            <li>{{.UnreadEmails}}</li>
          </ul>
        </div>
      </div>
      <footer class="pt-5 my-5 text-muted border-top">
        Switch to 🇬🇧 <a href="/">English</a>, 🇪🇸
        <a href="/?lang=es">Español</a>, 🇷🇺 <a href="/?lang=ru">Русский</a>.
      </footer>
    </div>
  </body>
</html>

Extraer el idioma original

  • Primero, instala goi18n:
go get -u github.com/nicksnyder/go-i18n/v2/goi18n
  • Exporta todos los literales i18n.Message de archivos fuente de Go y pásalos a un archivo de mensaje para traducir (de forma predeterminada, active.en.toml):
goi18n extract
# ./active.en.toml

HelloPerson = "Hello {{.Name}}"

[MyUnreadEmails]
one = "You have {{.PluralCount}} unread email."
other = "You have {{.PluralCount}} unread emails."
  • Crea archivos de mensajes vacíos para el idioma que deseas traducir (en este ejemplo, translate.es.tomltranslate.ru.toml).
touch translate.es.toml translate.ru.toml
  • Ejecuta el comando goi18n merge con estos archivos de mensajes a traducir:
# For Español:
goi18n merge active.en.toml translate.es.toml

# For Russian:
goi18n merge active.en.toml translate.ru.toml
  • Abre archivos de mensajes y haz la traducción de líneas. Como hemso indicado antes, el idioma ruso tiene peculiaridades al respecto de cantidades. Aquí dejo un ejemplo de traducción para este idioma:
# ./translate.ru.toml

[HelloPerson]
hash = "sha1-5b49bfdad81fedaeefb224b0ffc2acc58b09cff5"
other = "Привет, {{.Name}}"

[MyUnreadEmails]
hash = "sha1-6a65d17f53981a3657db1897630e9cb069053ea8"
one = "У вас есть {{.PluralCount}} непрочитанное письмо."
other = "У вас есть {{.PluralCount}} непрочитанных писем."
few = "У вас есть {{.PluralCount}} непрочитанных письма." # <-- new row for "few" count
many = "У вас есть {{.PluralCount}} непрочитанных писем." # <-- new row for "many" count
  • Cuando se hayan traducido todos, cámbiales el nombre a active.es.tomlactive.ru.tomlcolóquelos en la ./langcarpeta.
  • ¡Eso es todo!

Iniciar la aplicación y jugar con los idiomas.

Finalmente estamos listos para lanzar nuestra aplicación:

go run main.go

# ┌───────────────────────────────────────────────────┐ 
# │                    Fiber v2.7.1                   │ 
# │               http://127.0.0.1:3000               │ 
# │       (bound on host 0.0.0.0 and port 3000)       │ 
# │                                                   │ 
# │ Handlers ............. 2  Processes ........... 1 │ 
# │ Prefork ....... Disabled  PID ............. 64479 │ 
# └───────────────────────────────────────────────────┘

ESTÁ BIEN. Abrir http://localhost:3000/:

go i18n es

Como puedes ver, por defecto, el sitio web siempre se abrirá en 🇬🇧 inglés , como se especifica en la configuración de la aplicación.

💡 En Golang int, los valores no establecidos siempre tendrán 0, no nullNonecomo en JavaScript o Python. Por eso, si no especificamos el
parámetro  unread en una consulta, la plantilla se establecerá en 0.

A continuación, cambiemos el idioma a 🇪🇸 Español. Haz clic en el enlace en la parte inferior de la página y agrega el parámetro de consulta unreadcon algún número entero:

ir i18n es

Ve a otro idioma, por ejemplo 🇷🇺 Ruso :

ir i18n ru

👆 Puedes jugar con el valor de unreadpara ver cómo la forma de la palabra cambia automáticamente después de un número.

Para ver cómo funciona JSON, añade un parámetro format=json a la consulta: Fiber te mostrará el mismo contenido, pero en formato JSON:

ir i18n json

Epílogo

En aplicaciones web reales, puedes crear diferentes variantes de los métodos de la API REST para entregar traducciones a la interfaz. Pero lo más importante a recordar si realizas proyectos internacionales, es que debes pensar antes de nada en las particularidades de cada idioma.

¡Golang ayudará con todo lo demás! 🎉

Fotos y videos de

PD

Si deseas más artículos como este, publica un comentario a continuación y suscríbete. ¡Gracias! 😘

Y, por supuesto, puedes apoyarme donando en LiberaPayCada donación ayuda a publicar nuevos artículos y desarrollar proyectos de código abierto sin fines de lucro para la comunidad.

Valora este artículo