Commit bea49153 authored by Daniel García Moreno's avatar Daniel García Moreno

Translation support and initial spanish translation

Added translation support based on the Fractal i18n. To do this I've
added the gettext-rs crate dep. I'm using my own fork because the
official gettext-rs release includes the gettext source files and that
increase the distribution package a lot and for distribution with
flatkap we don't need to build gettext, the lib is in the gnome sdk. So
this gettext-rs fork is the same, but removing the not needed gettext
source files.

The file adds some useful functions to translate strings. These
functions wraps the original gettext and adds more functionality, to be
able to translate compound strings, something that's not supported by
the gettext function.

The 'i18n' function works like the gettext, receives a plain string
without params.

The 'i18n_f' function receives a string with "{}" and a ref to an array
of &str with substitutions for the "{}" in the original string. The
substitution is done by order.

The 'i18n_k' function receives a string with "{named}" and a ref to an
array of (&str, &str) with substitutions for the "{named}" in the
original string. The substitution is done by name, where the first &str
in the tuple is the name and the second the string to use for the

This mod also include ni18n variants of the three functions for plural
and singular translations.

I've also created the spanish translation.

See #61

parent f695ba46
......@@ -8,3 +8,4 @@ build/
......@@ -657,6 +657,23 @@ dependencies = [
"pkg-config 0.3.12 (registry+",
name = "gettext-rs"
version = "0.3.0"
source = "git+"
dependencies = [
"gettext-sys 0.19.8 (git+",
"locale_config 0.2.2 (registry+",
name = "gettext-sys"
version = "0.19.8"
source = "git+"
dependencies = [
"cc 1.0.18 (registry+",
name = "gio"
version = "0.4.1"
......@@ -1086,6 +1103,17 @@ dependencies = [
"vcpkg 0.2.4 (registry+",
name = "locale_config"
version = "0.2.2"
source = "registry+"
dependencies = [
"kernel32-sys 0.2.2 (registry+",
"lazy_static 0.2.11 (registry+",
"regex 0.2.11 (registry+",
"winapi 0.2.8 (registry+",
name = "log"
version = "0.3.9"
......@@ -1522,6 +1550,7 @@ dependencies = [
"fragile 0.2.1 (registry+",
"gdk 0.8.0 (registry+",
"gdk-pixbuf 0.4.0 (registry+",
"gettext-rs 0.3.0 (git+",
"gio 0.4.1 (registry+",
"glib 0.5.0 (registry+",
"gstreamer 0.11.5 (registry+",
......@@ -2559,6 +2588,8 @@ source = "registry+"
"checksum gdk-pixbuf 0.4.0 (registry+" = "c2d2199eba47ebcb9977ce28179649bdd59305ef465c4e6f9b65aaa41c24e6b5"
"checksum gdk-pixbuf-sys 0.6.0 (registry+" = "df6a3b73e04fafc07f5ebc083f1096a773412e627828e1103a55e921f81187d8"
"checksum gdk-sys 0.6.0 (registry+" = "3162ff940526ddff71bf1f630facee6b5e05d282d125ba0c4c803842819b80c3"
"checksum gettext-rs 0.3.0 (git+" = "<none>"
"checksum gettext-sys 0.19.8 (git+" = "<none>"
"checksum gio 0.4.1 (registry+" = "2db9fad8f1b0d4c7338a210a6cbdf081dcc1a3c223718c698c4f313f6c288acb"
"checksum gio-sys 0.6.0 (registry+" = "2a57872499171d279f8577ce83837da4cae62b08dd32892236ed67ab7ea61030"
"checksum glib 0.5.0 (registry+" = "5e0be1b1432e227bcd1a9b28db9dc1474a7e7fd4227e08e16f35304f32d09b61"
......@@ -2597,6 +2628,7 @@ source = "registry+"
"checksum libc 0.2.42 (registry+" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1"
"checksum libflate 0.1.16 (registry+" = "7d4b4c7aff5bac19b956f693d0ea0eade8066deb092186ae954fa6ba14daab98"
"checksum libsqlite3-sys 0.9.1 (registry+" = "0e9eb7b8e152b6a01be6a4a2917248381875758250dc3df5d46caf9250341dda"
"checksum locale_config 0.2.2 (registry+" = "14fbee0e39bc2dd6a2427c4fdea66e9826cc1fd09b0a0b7550359f5f6efe1dab"
"checksum log 0.3.9 (registry+" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
"checksum log 0.4.3 (registry+" = "61bd98ae7f7b754bc53dca7d44b604f733c6bba044ea6f41bc8d89272d8161d2"
"checksum loggerv 0.7.1 (registry+" = "ba6b0664956d197c6e0223870c1cd1ec4117aea282b4c0bd5ab01119d31d708d"
......@@ -127,6 +127,23 @@ There are also some minor tasks tagged with `TODO:` and `FIXME:` in the source c
### Translations
If you want to add a new language you should update the file
`podcasts-gtk/po/LINUGAS` and add the new lang to the list.
To generate .pot files you should run:
ninja -C _build gnome-podcasts-pot
To generate .po files you should run:
ninja -C _build gnome-podcasts-update-po
## Overview
......@@ -174,4 +191,4 @@ And almost the entirety of the build system is copied from the [Fractal][fractal
\ No newline at end of file
......@@ -15,6 +15,7 @@ podcasts_version_micro = version_array[2].to_int()
podcasts_prefix = get_option('prefix')
podcasts_bindir = join_paths(podcasts_prefix, get_option('bindir'))
podcasts_localedir = join_paths(podcasts_prefix, get_option('localedir'))
podcasts_conf = configuration_data()
podcasts_conf.set('BINDIR', podcasts_bindir)
......@@ -23,6 +24,9 @@ datadir = get_option('datadir')
icondir = join_paths(datadir, 'icons')
i18n = import('i18n')
cargo = find_program('cargo', required: false)
gresource = find_program('glib-compile-resources', required: false)
cargo_vendor = find_program('cargo-vendor', required: false)
......@@ -34,8 +38,8 @@ cargo_release = custom_target('cargo-build',
output: ['gnome-podcasts'],
install: true,
install_dir: podcasts_bindir,
command: [cargo_script, '@CURRENT_SOURCE_DIR@', '@OUTPUT@'])
command: [cargo_script, '@CURRENT_SOURCE_DIR@', '@OUTPUT@', podcasts_localedir])
run_target('release', command: ['scripts/',
meson.project_name() + '-' + podcasts_version
\ No newline at end of file
......@@ -28,6 +28,7 @@ reqwest = "0.8.6"
serde_json = "1.0.24"
# html2text = "0.1.8"
html2text = { git = "" }
gettext-rs = { git = "", branch = "no-gettext", features = ["gettext-system"] }
features = ["v3_22"]
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::process::Command;
fn main() {
......@@ -10,4 +14,20 @@ fn main() {
// Generating build globals
let default_locales = "./podcasts-gtk/po".to_string();
let out_dir = env::var("OUT_DIR").unwrap();
let localedir = env::var("PODCASTS_LOCALEDIR").unwrap_or(default_locales);
let dest_path = Path::new(&out_dir).join("");
let mut f = File::create(&dest_path).unwrap();
let globals = format!(
pub static LOCALEDIR: &'static str = \"{}\";
# please keep this list sorted alphabetically
# List of source files containing translatable strings.
# Please keep this file sorted alphabetically.
# ui files
# rust files
# Spanish translations for gnome-podcasts package.
# Copyright (C) 2018 THE gnome-podcasts'S COPYRIGHT HOLDER
# This file is distributed under the same license as the gnome-podcasts package.
# Automatically generated, 2018.
# Daniel Garcia Moreno <>, 2018.
msgid ""
msgstr ""
"Project-Id-Version: gnome-podcasts\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-08-02 11:42+0200\n"
"PO-Revision-Date: 2018-08-02 11:46+0200\n"
"Last-Translator: Daniel Garcia Moreno <>\n"
"Language-Team: Español; Castellano <>\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Gtranslator 2.91.7\n"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:15
msgid "Top position of the last open main window"
msgstr "Posición superior de la última ventana abierta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:19
msgid "Left position of the last open main window"
msgstr "Posición izquierda de la última ventana abierta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:23
msgid "Height of the last open main window"
msgstr "Alto de la última ventana abierta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:27
msgid "Width of the last open main window"
msgstr "Ancho de la última ventana abierta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:31
msgid "Maximized state of the last open main window"
msgstr "Estado de maximizado de la última ventana abierta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:36
msgid "Enable or disable dark theme"
msgstr "Activar o desactivar el tema oscuro"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:41
msgid "Whether to periodically refresh content"
msgstr "Si refrescar el contenido periódicamente"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:46
msgid "How many periods of time to wait between automatic refreshes"
msgstr "Cuántos periodos de tiempo a esperar entre refrescos automáticos"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:50
msgid "What period of time to wait between automatic refreshes"
msgstr "Qué periodo de tiempo hay que esperar entre refrescos automáticos"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:54
msgid "Whether to refresh content after startup"
msgstr "Si refrescar el contenido al iniciar"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:60
msgid "How many periods of time to wait between automatic cleanups"
msgstr "Cuántos periodos de tiempo hay que esperar entre limpiados automáticos"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:64
msgid "What period of time to wait between automatic cleanups"
msgstr "Cuántos periodos de tiempo hay que esperar entre limpiados automáticos"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop:3
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml:4
msgid "Podcasts"
msgstr "Podcasts"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop:4
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml:11
msgid "Listen to your favorite podcasts, right from your desktop."
msgstr "Escucha tus podcasts favoritos, directamente desde tu escritorio."
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop:5
msgid "org.gnome.Podcasts"
msgstr "org.gnome.Podcasts"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop:11
msgid "Podcast;RSS;"
msgstr "Podcast;RSS;"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml:9
msgid "Podcast app for GNOME"
msgstr "Aplicación de Podcast para GNOME"
#: podcasts-gtk/resources/gtk/hamburger.ui:7
msgid "_Check for new episodes"
msgstr "_Comprobar si hay nuevos episodios"
#: podcasts-gtk/resources/gtk/hamburger.ui:12
msgid "_Import Shows"
msgstr "_Importar programas"
#: podcasts-gtk/resources/gtk/hamburger.ui:22
msgid "_Preferences"
msgstr "_Preferencias"
#: podcasts-gtk/resources/gtk/hamburger.ui:29
msgid "_Keyboard Shortcuts"
msgstr "Atajos de _teclado"
#: podcasts-gtk/resources/gtk/hamburger.ui:37
msgid "_About"
msgstr "_Acerca de"
#: podcasts-gtk/resources/gtk/show_widget.ui:220
msgid "Mark all episodes as listened"
msgstr "Marcar todos los episodios como escuchados"
#: podcasts-gtk/resources/gtk/help-overlay.ui:12
msgid "General"
msgstr "General"
#: podcasts-gtk/resources/gtk/help-overlay.ui:18
msgctxt "shortcut window"
msgid "Check for new episodes"
msgstr "Comprobar si hay nuevos episodios"
#: podcasts-gtk/resources/gtk/help-overlay.ui:25
msgctxt "shortcut window"
msgid "Preferences"
msgstr "Preferencias"
#: podcasts-gtk/resources/gtk/help-overlay.ui:32
msgctxt "shortcut window"
msgid "Quit the application"
msgstr "Salir de la aplicación"
#: podcasts-gtk/resources/gtk/episode_widget.ui:55
#: podcasts-gtk/resources/gtk/player_toolbar.ui:160
msgid "Episode Title"
msgstr "Título del episodio"
#: podcasts-gtk/resources/gtk/episode_widget.ui:78
msgid "3 Jan"
msgstr "3 Jan"
#: podcasts-gtk/resources/gtk/episode_widget.ui:95
#: podcasts-gtk/resources/gtk/episode_widget.ui:128
msgid "·"
msgstr "·"
#: podcasts-gtk/resources/gtk/episode_widget.ui:111
msgid "42 min"
msgstr "42 min"
#: podcasts-gtk/resources/gtk/episode_widget.ui:144
msgid "0 MB"
msgstr "0 MB"
#: podcasts-gtk/resources/gtk/episode_widget.ui:161
msgid "/"
msgstr "/"
#: podcasts-gtk/resources/gtk/episode_widget.ui:178
msgid "Calculating episode size..."
msgstr "Calculando tamaño del episodio..."
#: podcasts-gtk/resources/gtk/episode_widget.ui:213
msgid "Cancel"
msgstr "Cancelar"
#: podcasts-gtk/resources/gtk/episode_widget.ui:231
msgid "Download this episode"
msgstr "Descargar este episodio"
#: podcasts-gtk/resources/gtk/episode_widget.ui:255
msgid "Play this episode"
msgstr "Reproducir este episodio"
#: podcasts-gtk/resources/gtk/prefs.ui:14
msgid "Preferences"
msgstr "Preferencias"
#: podcasts-gtk/resources/gtk/prefs.ui:48
msgid "Appearance"
msgstr "Aspecto"
#: podcasts-gtk/resources/gtk/prefs.ui:92
msgid "Dark Theme"
msgstr "Tema oscuro"
#: podcasts-gtk/resources/gtk/prefs.ui:138
msgid "Delete played episodes"
msgstr "Eliminar episodios reproducidos"
#: podcasts-gtk/resources/gtk/prefs.ui:183
msgid "After"
msgstr "Después"
#: podcasts-gtk/resources/gtk/home_view.ui:94
msgid "Today"
msgstr "Hoy"
#: podcasts-gtk/resources/gtk/home_view.ui:150
msgid "Yesterday"
msgstr "Ayer"
#: podcasts-gtk/resources/gtk/home_view.ui:206
msgid "This Week"
msgstr "Esta semana"
#: podcasts-gtk/resources/gtk/home_view.ui:262
msgid "This Month"
msgstr "Este mes"
#: podcasts-gtk/resources/gtk/home_view.ui:319
msgid "Older"
msgstr "Antiguo"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:7
msgid "_Mark all episodes as played"
msgstr "_Marcar todos los episodios como reproducidos"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:11
msgid "_Website"
msgstr "Página _Web"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:15
msgid "_Unsubscribe"
msgstr "_Cancelar suscripción"
#: podcasts-gtk/resources/gtk/empty_view.ui:66
msgid "Get some shows"
msgstr "Consigue algún programa"
#: podcasts-gtk/resources/gtk/empty_view.ui:103
msgid "Add new shows via feed URL"
msgstr "Añadir nuevo programa a través de canal URL"
#: podcasts-gtk/resources/gtk/empty_view.ui:132
msgid "Import shows from another device"
msgstr "Importar programas de otro dispositivo"
#: podcasts-gtk/resources/gtk/show_menu.ui:23
msgid "Open Website"
msgstr "Abrir Web"
#: podcasts-gtk/resources/gtk/show_menu.ui:36
msgid "Mark all as played"
msgstr "Marcar como reproducido"
#: podcasts-gtk/resources/gtk/show_menu.ui:61
msgid "Unsubscribe"
msgstr "Cancelar suscripción"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:44
msgid "Rewind 10 seconds"
msgstr "Atrás 10 segundos"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:59
msgid "Play"
msgstr "Reproducir"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:75
msgid "Pause"
msgstr "Pausa"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:91
msgid "Fast Forward 10 seconds"
msgstr "Adelante 10 segundos"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:141
#: podcasts-gtk/resources/gtk/headerbar.ui:247
msgid "Show Title"
msgstr "Título de Programa"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:257
msgid "Change the Playback speed"
msgstr "Cambiar velocidad de reproducción"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:272
#: podcasts-gtk/resources/gtk/player_toolbar.ui:352
msgid "1.00x"
msgstr "1.00x"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:316
msgid "1.50x"
msgstr "1.50x"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:320
msgid "1.5 speed rate"
msgstr "velocidad 1.5"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:334
msgid "1.25x"
msgstr "1.25x"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:338
msgid "1.25 speed rate"
msgstr "velocidad 1.25"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:356
msgid "Normal speed"
msgstr "Velocidad normal"
#: podcasts-gtk/resources/gtk/empty_show.ui:35
msgid "This show does not have any episodes"
msgstr "Este programa no tiene episodios todavía"
#: podcasts-gtk/resources/gtk/empty_show.ui:51
msgid "If you think this is an Error, Plese consider opening a bug report."
msgstr ""
"Si crees que esto es un error, por favor, considera abrir un bug report."
#: podcasts-gtk/resources/gtk/headerbar.ui:35
#: podcasts-gtk/resources/gtk/headerbar.ui:155
msgid "Add a new feed"
msgstr "Añadir un nuevo canal"
#: podcasts-gtk/resources/gtk/headerbar.ui:56
msgid "Enter feed address to add"
msgstr "Introduce la dirección del canal a añadir"
#: podcasts-gtk/resources/gtk/headerbar.ui:92
msgid "Add"
msgstr "Añadir"
#: podcasts-gtk/resources/gtk/headerbar.ui:136
msgid "You are already subscribed to that feed!"
msgstr "¡Ya estás suscrito a este canal!"
#: podcasts-gtk/resources/gtk/headerbar.ui:176
msgid "Back"
msgstr "Atrás"
#: podcasts-gtk/resources/gtk/headerbar.ui:212
msgid "Fetching new episodes"
msgstr "Obteniendo nuevos episodios"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:35
msgid "An in-app action notification"
msgstr "Una notificación de acción in-app"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:72
msgid "Undo"
msgstr "Deshacer"
#: podcasts-gtk/src/widgets/
msgid "Marked all episodes as listened"
msgstr "Marcar todos los episodios como escuchados"
#: podcasts-gtk/src/widgets/
msgid "Unsubscribed from {}"
msgstr "Suscripción cancelada para {}"
#. sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
#: podcasts-gtk/src/widgets/
msgid "The media player was unable to execute an action."
msgstr "El reproductor no ha podido reproducir una acción."
#: podcasts-gtk/src/widgets/
msgid "Podcast Client for the GNOME Desktop."
msgstr "Aplicación de Podcast para el escritorio GNOME."
#: podcasts-gtk/src/widgets/
msgid "© 2017, 2018 Jordan Petridis"
msgstr "© 2017, 2018 Jordan Petridis"
#: podcasts-gtk/src/widgets/
msgid "translator-credits"
msgstr "\"Daniel García Moreno <>\""
#. Set the label and show them.
#: podcasts-gtk/src/widgets/
msgid "{} min"
msgstr "{} min"
#: podcasts-gtk/src/
msgid "You are already subscribed to this Show"
msgstr "Ya estás suscrito a este programa"
#: podcasts-gtk/src/
msgid "Invalid url"
msgstr "Url no válida"
#: podcasts-gtk/src/
msgid "Select the file from which to you want to Import Shows."
msgstr "Selecciona el fichero desde el cual quieres importar programas."
#: podcasts-gtk/src/
msgid "_Import"
msgstr "_Importar"
#: podcasts-gtk/src/
msgid "OPML file"
msgstr "fichero OPML"
#: podcasts-gtk/src/
msgid "Failed to parse the Imported file"
msgstr "Fallo al leer el fichero importado"
#: podcasts-gtk/src/
msgid "Selected File could not be accessed."
msgstr "El fichero seleccionado no es accesible."
#: podcasts-gtk/src/stacks/
msgid "New"
msgstr "Nuevo"
#: podcasts-gtk/src/stacks/
msgid "Shows"
msgstr "Programas"
#: podcasts-gtk/src/
msgid "Seconds"
msgstr "Segundos"
#: podcasts-gtk/src/
msgid "Minutes"
msgstr "Minutos"
#: podcasts-gtk/src/
msgid "Hours"
msgstr "Horas"
#: podcasts-gtk/src/
msgid "Days"
msgstr "Días"
#: podcasts-gtk/src/
msgid "Weeks"
msgstr "Semanas"
#~ msgid "Jordan Petridis and others"
#~ msgstr "Jordan Petridis y otros"
args: ['--keyword=i18n', '--keyword=i18n_f', '--keyword=i18n_k',
'--keyword=ni18n:1,2', '--keyword=ni18n_f:1,2', '--keyword=ni18n_k:1,2'],
preset: 'glib')
......@@ -5,6 +5,8 @@ use glib::{self, Variant};
use gtk;
use gtk::prelude::*;
use gettextrs::{bindtextdomain, setlocale, textdomain, LocaleCategory};
use crossbeam_channel::{unbounded, Receiver, Sender};
use fragile::Fragile;
use podcasts_data::Show;
......@@ -25,6 +27,8 @@ use std::sync::Arc;
pub const APP_ID: &str = "org.gnome.Podcasts";
include!(concat!(env!("OUT_DIR"), "/"));
/// Creates an action named `name` in the action map `T with the handler `F`
fn action<T, F>(thing: &T, name: &str, action: F)
......@@ -304,6 +308,11 @@ impl App {
pub fn run() {
// Set up the textdomain for gettext
setlocale(LocaleCategory::LcAll, "");
bindtextdomain("gnome-podcasts", LOCALEDIR);
let application = gtk::Application::new(APP_ID, gio::ApplicationFlags::empty())
.expect("Application Initialization failed...");
......@@ -15,6 +15,8 @@ use utils::{itunes_to_rss, refresh};
use std::rc::Rc;
use i18n::i18n;
#[derive(Debug, Clone)]
// TODO: Factor out the hamburger menu
// TODO: Make a proper state machine for the headerbar states
......@@ -118,7 +120,7 @@ impl AddPopover {
} else {
.set_label("You are already subscribed to this Show");
.set_label(i18n("You are already subscribed to this Show").as_str());;
......@@ -126,7 +128,7 @@ impl AddPopover {
Err(err) => {
if !url.is_empty() {
self.result.set_label("Invalid url");
self.result.set_label(i18n("Invalid url").as_str());;
error!("Error: {}", err);
} else {
extern crate gettextrs;
extern crate regex;
use self::gettextrs::gettext;
use self::gettextrs::ngettext;
use self::regex::Captures;
use self::regex::Regex;
fn freplace(input: String, args: &[&str]) -> String {
let mut parts = input.split("{}");
let mut output ="").to_string();
for (p, a) in {
output += &(a.to_string() + &p.to_string());
fn kreplace(input: String, kwargs: &[(&str, &str)]) -> String {
let mut s = input.clone();
for (k, v) in kwargs {
if let Ok(re) = Regex::new(&format!("\\{{{}\\}}", k)) {
s = re
.replace_all(&s, |_: &Captures| v.to_string().clone())
pub fn i18n(format: &str) -> String {
pub fn i18n_f(format: &str, args: &[&str]) -> String {
let s = gettext(format);
freplace(s, args)
pub fn i18n_k(format: &str, kwargs: &[(&str, &str)]) -> String {
let s = gettext(format);
kreplace(s, kwargs)
pub fn ni18n(single: &str, multiple: &str, number: u32) -> String {
ngettext(single, multiple, number)
pub fn ni18n_f(single: &str, multiple: &str, number: u32, args: &[&str]) -> String {
let s = ngettext(single, multiple, number);