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 i18n.rs 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
replace.

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

I've also created the spanish translation.

See #61

https://gitlab.gnome.org/World/podcasts/issues/61
This commit is contained in:
Daniel García Moreno 2018-08-02 11:50:09 +02:00
parent f695ba4605
commit bea4915317
22 changed files with 756 additions and 21 deletions

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ build/
vendor/ vendor/
.criterion/ .criterion/
org.gnome.*.json~ org.gnome.*.json~
podcasts-gtk/po/gnome-podcasts.pot

32
Cargo.lock generated
View File

@ -657,6 +657,23 @@ dependencies = [
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "gettext-rs"
version = "0.3.0"
source = "git+https://github.com/danigm/gettext-rs?branch=no-gettext#db8f12e140f0db5aabafe8dd210c7fc1520a55ff"
dependencies = [
"gettext-sys 0.19.8 (git+https://github.com/danigm/gettext-rs?branch=no-gettext)",
"locale_config 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "gettext-sys"
version = "0.19.8"
source = "git+https://github.com/danigm/gettext-rs?branch=no-gettext#db8f12e140f0db5aabafe8dd210c7fc1520a55ff"
dependencies = [
"cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "gio" name = "gio"
version = "0.4.1" version = "0.4.1"
@ -1086,6 +1103,17 @@ dependencies = [
"vcpkg 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "locale_config"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.3.9" version = "0.3.9"
@ -1522,6 +1550,7 @@ dependencies = [
"fragile 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "fragile 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "gdk 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "gdk-pixbuf 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gettext-rs 0.3.0 (git+https://github.com/danigm/gettext-rs?branch=no-gettext)",
"gio 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "gio 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gstreamer 0.11.5 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer 0.11.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2559,6 +2588,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum gdk-pixbuf 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c2d2199eba47ebcb9977ce28179649bdd59305ef465c4e6f9b65aaa41c24e6b5" "checksum gdk-pixbuf 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c2d2199eba47ebcb9977ce28179649bdd59305ef465c4e6f9b65aaa41c24e6b5"
"checksum gdk-pixbuf-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df6a3b73e04fafc07f5ebc083f1096a773412e627828e1103a55e921f81187d8" "checksum gdk-pixbuf-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df6a3b73e04fafc07f5ebc083f1096a773412e627828e1103a55e921f81187d8"
"checksum gdk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3162ff940526ddff71bf1f630facee6b5e05d282d125ba0c4c803842819b80c3" "checksum gdk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3162ff940526ddff71bf1f630facee6b5e05d282d125ba0c4c803842819b80c3"
"checksum gettext-rs 0.3.0 (git+https://github.com/danigm/gettext-rs?branch=no-gettext)" = "<none>"
"checksum gettext-sys 0.19.8 (git+https://github.com/danigm/gettext-rs?branch=no-gettext)" = "<none>"
"checksum gio 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2db9fad8f1b0d4c7338a210a6cbdf081dcc1a3c223718c698c4f313f6c288acb" "checksum gio 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2db9fad8f1b0d4c7338a210a6cbdf081dcc1a3c223718c698c4f313f6c288acb"
"checksum gio-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a57872499171d279f8577ce83837da4cae62b08dd32892236ed67ab7ea61030" "checksum gio-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a57872499171d279f8577ce83837da4cae62b08dd32892236ed67ab7ea61030"
"checksum glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e0be1b1432e227bcd1a9b28db9dc1474a7e7fd4227e08e16f35304f32d09b61" "checksum glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e0be1b1432e227bcd1a9b28db9dc1474a7e7fd4227e08e16f35304f32d09b61"
@ -2597,6 +2628,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1" "checksum libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1"
"checksum libflate 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7d4b4c7aff5bac19b956f693d0ea0eade8066deb092186ae954fa6ba14daab98" "checksum libflate 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7d4b4c7aff5bac19b956f693d0ea0eade8066deb092186ae954fa6ba14daab98"
"checksum libsqlite3-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e9eb7b8e152b6a01be6a4a2917248381875758250dc3df5d46caf9250341dda" "checksum libsqlite3-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e9eb7b8e152b6a01be6a4a2917248381875758250dc3df5d46caf9250341dda"
"checksum locale_config 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "14fbee0e39bc2dd6a2427c4fdea66e9826cc1fd09b0a0b7550359f5f6efe1dab"
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
"checksum log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "61bd98ae7f7b754bc53dca7d44b604f733c6bba044ea6f41bc8d89272d8161d2" "checksum log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "61bd98ae7f7b754bc53dca7d44b604f733c6bba044ea6f41bc8d89272d8161d2"
"checksum loggerv 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ba6b0664956d197c6e0223870c1cd1ec4117aea282b4c0bd5ab01119d31d708d" "checksum loggerv 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ba6b0664956d197c6e0223870c1cd1ec4117aea282b4c0bd5ab01119d31d708d"

View File

@ -127,6 +127,23 @@ There are also some minor tasks tagged with `TODO:` and `FIXME:` in the source c
[contribution-guidelines]: https://gitlab.gnome.org/World/podcasts/blob/master/CONTRIBUTING.md [contribution-guidelines]: https://gitlab.gnome.org/World/podcasts/blob/master/CONTRIBUTING.md
### 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 ## Overview
@ -174,4 +191,4 @@ And almost the entirety of the build system is copied from the [Fractal][fractal
[issues]: https://gitlab.gnome.org/World/podcasts/issues [issues]: https://gitlab.gnome.org/World/podcasts/issues
[new_issue]: https://gitlab.gnome.org/World/podcasts/issues/new [new_issue]: https://gitlab.gnome.org/World/podcasts/issues/new
[builder]: https://wiki.gnome.org/Apps/Builder [builder]: https://wiki.gnome.org/Apps/Builder
[get_builder]: https://wiki.gnome.org/Apps/Builder/Downloads [get_builder]: https://wiki.gnome.org/Apps/Builder/Downloads

View File

@ -15,6 +15,7 @@ podcasts_version_micro = version_array[2].to_int()
podcasts_prefix = get_option('prefix') podcasts_prefix = get_option('prefix')
podcasts_bindir = join_paths(podcasts_prefix, get_option('bindir')) podcasts_bindir = join_paths(podcasts_prefix, get_option('bindir'))
podcasts_localedir = join_paths(podcasts_prefix, get_option('localedir'))
podcasts_conf = configuration_data() podcasts_conf = configuration_data()
podcasts_conf.set('BINDIR', podcasts_bindir) podcasts_conf.set('BINDIR', podcasts_bindir)
@ -23,6 +24,9 @@ datadir = get_option('datadir')
icondir = join_paths(datadir, 'icons') icondir = join_paths(datadir, 'icons')
subdir('podcasts-gtk/resources') subdir('podcasts-gtk/resources')
i18n = import('i18n')
subdir('podcasts-gtk/po')
cargo = find_program('cargo', required: false) cargo = find_program('cargo', required: false)
gresource = find_program('glib-compile-resources', required: false) gresource = find_program('glib-compile-resources', required: false)
cargo_vendor = find_program('cargo-vendor', required: false) cargo_vendor = find_program('cargo-vendor', required: false)
@ -34,8 +38,8 @@ cargo_release = custom_target('cargo-build',
output: ['gnome-podcasts'], output: ['gnome-podcasts'],
install: true, install: true,
install_dir: podcasts_bindir, 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/release.sh', run_target('release', command: ['scripts/release.sh',
meson.project_name() + '-' + podcasts_version meson.project_name() + '-' + podcasts_version
]) ])

View File

@ -28,6 +28,7 @@ reqwest = "0.8.6"
serde_json = "1.0.24" serde_json = "1.0.24"
# html2text = "0.1.8" # html2text = "0.1.8"
html2text = { git = "https://github.com/alatiera/rust-html2text" } html2text = { git = "https://github.com/alatiera/rust-html2text" }
gettext-rs = { git = "https://github.com/danigm/gettext-rs", branch = "no-gettext", features = ["gettext-system"] }
[dependencies.gtk] [dependencies.gtk]
features = ["v3_22"] features = ["v3_22"]

View File

@ -1,3 +1,7 @@
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::process::Command; use std::process::Command;
fn main() { fn main() {
@ -10,4 +14,20 @@ fn main() {
.current_dir("resources") .current_dir("resources")
.status() .status()
.unwrap(); .unwrap();
// 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("build_globals.rs");
let mut f = File::create(&dest_path).unwrap();
let globals = format!(
"
pub static LOCALEDIR: &'static str = \"{}\";
",
localedir
);
f.write_all(&globals.into_bytes()[..]).unwrap();
} }

3
podcasts-gtk/po/LINGUAS Normal file
View File

@ -0,0 +1,3 @@
# please keep this list sorted alphabetically
#
es

View File

@ -0,0 +1,48 @@
# List of source files containing translatable strings.
# Please keep this file sorted alphabetically.
podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml
podcasts-gtk/resources/org.gnome.Podcasts.desktop
podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml
# ui files
podcasts-gtk/resources/gtk/shows_child.ui
podcasts-gtk/resources/gtk/hamburger.ui
podcasts-gtk/resources/gtk/show_widget.ui
podcasts-gtk/resources/gtk/help-overlay.ui
podcasts-gtk/resources/gtk/shows_view.ui
podcasts-gtk/resources/gtk/episode_widget.ui
podcasts-gtk/resources/gtk/prefs.ui
podcasts-gtk/resources/gtk/home_view.ui
podcasts-gtk/resources/gtk/secondary_menu.ui
podcasts-gtk/resources/gtk/empty_view.ui
podcasts-gtk/resources/gtk/show_menu.ui
podcasts-gtk/resources/gtk/player_toolbar.ui
podcasts-gtk/resources/gtk/empty_show.ui
podcasts-gtk/resources/gtk/headerbar.ui
podcasts-gtk/resources/gtk/home_episode.ui
podcasts-gtk/resources/gtk/inapp_notif.ui
# rust files
podcasts-gtk/src/manager.rs
podcasts-gtk/src/app.rs
podcasts-gtk/src/widgets/show_menu.rs
podcasts-gtk/src/widgets/appnotif.rs
podcasts-gtk/src/widgets/empty.rs
podcasts-gtk/src/widgets/mod.rs
podcasts-gtk/src/widgets/shows_view.rs
podcasts-gtk/src/widgets/home_view.rs
podcasts-gtk/src/widgets/show.rs
podcasts-gtk/src/widgets/player.rs
podcasts-gtk/src/widgets/aboutdialog.rs
podcasts-gtk/src/widgets/episode.rs
podcasts-gtk/src/headerbar.rs
podcasts-gtk/src/static_resource.rs
podcasts-gtk/src/utils.rs
podcasts-gtk/src/settings.rs
podcasts-gtk/src/main.rs
podcasts-gtk/src/stacks/content.rs
podcasts-gtk/src/stacks/mod.rs
podcasts-gtk/src/stacks/home.rs
podcasts-gtk/src/stacks/populated.rs
podcasts-gtk/src/stacks/show.rs
podcasts-gtk/src/prefs.rs

432
podcasts-gtk/po/es.po Normal file
View File

@ -0,0 +1,432 @@
# 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 <danigm@wadobo.com>, 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 <danigm@wadobo.com>\n"
"Language-Team: Español; Castellano <info@wadobo.com>\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/show_menu.rs:141
msgid "Marked all episodes as listened"
msgstr "Marcar todos los episodios como escuchados"
#: podcasts-gtk/src/widgets/show_menu.rs:146
msgid "Unsubscribed from {}"
msgstr "Suscripción cancelada para {}"
#. sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
#: podcasts-gtk/src/widgets/player.rs:300
msgid "The media player was unable to execute an action."
msgstr "El reproductor no ha podido reproducir una acción."
#: podcasts-gtk/src/widgets/aboutdialog.rs:26
msgid "Podcast Client for the GNOME Desktop."
msgstr "Aplicación de Podcast para el escritorio GNOME."
#: podcasts-gtk/src/widgets/aboutdialog.rs:27
msgid "© 2017, 2018 Jordan Petridis"
msgstr "© 2017, 2018 Jordan Petridis"
#: podcasts-gtk/src/widgets/aboutdialog.rs:41
msgid "translator-credits"
msgstr "\"Daniel García Moreno <danigm@wadobo.com>\""
#. Set the label and show them.
#: podcasts-gtk/src/widgets/episode.rs:128
msgid "{} min"
msgstr "{} min"
#: podcasts-gtk/src/headerbar.rs:123
msgid "You are already subscribed to this Show"
msgstr "Ya estás suscrito a este programa"
#: podcasts-gtk/src/headerbar.rs:131
msgid "Invalid url"
msgstr "Url no válida"
#: podcasts-gtk/src/utils.rs:330
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/utils.rs:333
msgid "_Import"
msgstr "_Importar"
#: podcasts-gtk/src/utils.rs:342
msgid "OPML file"
msgstr "fichero OPML"
#: podcasts-gtk/src/utils.rs:360
msgid "Failed to parse the Imported file"
msgstr "Fallo al leer el fichero importado"
#: podcasts-gtk/src/utils.rs:365
msgid "Selected File could not be accessed."
msgstr "El fichero seleccionado no es accesible."
#: podcasts-gtk/src/stacks/content.rs:29
msgid "New"
msgstr "Nuevo"
#: podcasts-gtk/src/stacks/content.rs:30
msgid "Shows"
msgstr "Programas"
#: podcasts-gtk/src/prefs.rs:58
msgid "Seconds"
msgstr "Segundos"
#: podcasts-gtk/src/prefs.rs:58
msgid "Minutes"
msgstr "Minutos"
#: podcasts-gtk/src/prefs.rs:58
msgid "Hours"
msgstr "Horas"
#: podcasts-gtk/src/prefs.rs:59
msgid "Days"
msgstr "Días"
#: podcasts-gtk/src/prefs.rs:59
msgid "Weeks"
msgstr "Semanas"
#~ msgid "Jordan Petridis and others"
#~ msgstr "Jordan Petridis y otros"

View File

@ -0,0 +1,4 @@
i18n.gettext(meson.project_name(),
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')

View File

@ -5,6 +5,8 @@ use glib::{self, Variant};
use gtk; use gtk;
use gtk::prelude::*; use gtk::prelude::*;
use gettextrs::{bindtextdomain, setlocale, textdomain, LocaleCategory};
use crossbeam_channel::{unbounded, Receiver, Sender}; use crossbeam_channel::{unbounded, Receiver, Sender};
use fragile::Fragile; use fragile::Fragile;
use podcasts_data::Show; use podcasts_data::Show;
@ -25,6 +27,8 @@ use std::sync::Arc;
pub const APP_ID: &str = "org.gnome.Podcasts"; pub const APP_ID: &str = "org.gnome.Podcasts";
include!(concat!(env!("OUT_DIR"), "/build_globals.rs"));
/// Creates an action named `name` in the action map `T with the handler `F` /// Creates an action named `name` in the action map `T with the handler `F`
fn action<T, F>(thing: &T, name: &str, action: F) fn action<T, F>(thing: &T, name: &str, action: F)
where where
@ -304,6 +308,11 @@ impl App {
} }
pub fn run() { pub fn run() {
// Set up the textdomain for gettext
setlocale(LocaleCategory::LcAll, "");
bindtextdomain("gnome-podcasts", LOCALEDIR);
textdomain("gnome-podcasts");
let application = gtk::Application::new(APP_ID, gio::ApplicationFlags::empty()) let application = gtk::Application::new(APP_ID, gio::ApplicationFlags::empty())
.expect("Application Initialization failed..."); .expect("Application Initialization failed...");

View File

@ -15,6 +15,8 @@ use utils::{itunes_to_rss, refresh};
use std::rc::Rc; use std::rc::Rc;
use i18n::i18n;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
// TODO: Factor out the hamburger menu // TODO: Factor out the hamburger menu
// TODO: Make a proper state machine for the headerbar states // TODO: Make a proper state machine for the headerbar states
@ -118,7 +120,7 @@ impl AddPopover {
} else { } else {
self.add.set_sensitive(false); self.add.set_sensitive(false);
self.result self.result
.set_label("You are already subscribed to this Show"); .set_label(i18n("You are already subscribed to this Show").as_str());
self.result.show(); self.result.show();
} }
Ok(()) Ok(())
@ -126,7 +128,7 @@ impl AddPopover {
Err(err) => { Err(err) => {
self.add.set_sensitive(false); self.add.set_sensitive(false);
if !url.is_empty() { if !url.is_empty() {
self.result.set_label("Invalid url"); self.result.set_label(i18n("Invalid url").as_str());
self.result.show(); self.result.show();
error!("Error: {}", err); error!("Error: {}", err);
} else { } else {

135
podcasts-gtk/src/i18n.rs Normal file
View File

@ -0,0 +1,135 @@
extern crate gettextrs;
extern crate regex;
use self::gettextrs::gettext;
use self::gettextrs::ngettext;
use self::regex::Captures;
use self::regex::Regex;
#[allow(dead_code)]
fn freplace(input: String, args: &[&str]) -> String {
let mut parts = input.split("{}");
let mut output = parts.next().unwrap_or("").to_string();
for (p, a) in parts.zip(args.iter()) {
output += &(a.to_string() + &p.to_string());
}
output
}
#[allow(dead_code)]
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())
.to_string();
}
}
s
}
#[allow(dead_code)]
pub fn i18n(format: &str) -> String {
gettext(format)
}
#[allow(dead_code)]
pub fn i18n_f(format: &str, args: &[&str]) -> String {
let s = gettext(format);
freplace(s, args)
}
#[allow(dead_code)]
pub fn i18n_k(format: &str, kwargs: &[(&str, &str)]) -> String {
let s = gettext(format);
kreplace(s, kwargs)
}
#[allow(dead_code)]
pub fn ni18n(single: &str, multiple: &str, number: u32) -> String {
ngettext(single, multiple, number)
}
#[allow(dead_code)]
pub fn ni18n_f(single: &str, multiple: &str, number: u32, args: &[&str]) -> String {
let s = ngettext(single, multiple, number);
freplace(s, args)
}
#[allow(dead_code)]
pub fn ni18n_k(single: &str, multiple: &str, number: u32, kwargs: &[(&str, &str)]) -> String {
let s = ngettext(single, multiple, number);
kreplace(s, kwargs)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_i18n() {
let out = i18n("translate1");
assert_eq!(out, "translate1");
let out = ni18n("translate1", "translate multi", 1);
assert_eq!(out, "translate1");
let out = ni18n("translate1", "translate multi", 2);
assert_eq!(out, "translate multi");
}
#[test]
fn test_i18n_f() {
let out = i18n_f("{} param", &["one"]);
assert_eq!(out, "one param");
let out = i18n_f("middle {} param", &["one"]);
assert_eq!(out, "middle one param");
let out = i18n_f("end {}", &["one"]);
assert_eq!(out, "end one");
let out = i18n_f("multiple {} and {}", &["one", "two"]);
assert_eq!(out, "multiple one and two");
let out = ni18n_f("singular {} and {}", "plural {} and {}", 2, &["one", "two"]);
assert_eq!(out, "plural one and two");
let out = ni18n_f("singular {} and {}", "plural {} and {}", 1, &["one", "two"]);
assert_eq!(out, "singular one and two");
}
#[test]
fn test_i18n_k() {
let out = i18n_k("{one} param", &[("one", "one")]);
assert_eq!(out, "one param");
let out = i18n_k("middle {one} param", &[("one", "one")]);
assert_eq!(out, "middle one param");
let out = i18n_k("end {one}", &[("one", "one")]);
assert_eq!(out, "end one");
let out = i18n_k("multiple {one} and {two}", &[("one", "1"), ("two", "two")]);
assert_eq!(out, "multiple 1 and two");
let out = i18n_k("multiple {two} and {one}", &[("one", "1"), ("two", "two")]);
assert_eq!(out, "multiple two and 1");
let out = i18n_k("multiple {one} and {one}", &[("one", "1"), ("two", "two")]);
assert_eq!(out, "multiple 1 and 1");
let out = ni18n_k(
"singular {one} and {two}",
"plural {one} and {two}",
1,
&[("one", "1"), ("two", "two")],
);
assert_eq!(out, "singular 1 and two");
let out = ni18n_k(
"singular {one} and {two}",
"plural {one} and {two}",
2,
&[("one", "1"), ("two", "two")],
);
assert_eq!(out, "plural 1 and two");
}
}

View File

@ -73,6 +73,8 @@ extern crate reqwest;
extern crate serde_json; extern crate serde_json;
extern crate url; extern crate url;
extern crate gettextrs;
use log::Level; use log::Level;
use gtk::prelude::*; use gtk::prelude::*;
@ -108,6 +110,8 @@ mod settings;
mod static_resource; mod static_resource;
mod utils; mod utils;
mod i18n;
use app::App; use app::App;
fn main() { fn main() {

View File

@ -3,6 +3,8 @@ use gio::{Settings, SettingsExt};
use gtk; use gtk;
use gtk::prelude::*; use gtk::prelude::*;
use i18n::i18n;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Prefs { pub struct Prefs {
dialog: gtk::Window, dialog: gtk::Window,
@ -53,7 +55,13 @@ impl Prefs {
let cleanup_p = settings.get_string("cleanup-age-period").unwrap(); let cleanup_p = settings.get_string("cleanup-age-period").unwrap();
let mut cleanup_pos = 0; let mut cleanup_pos = 0;
let store = gtk::ListStore::new(&[gtk::Type::String]); let store = gtk::ListStore::new(&[gtk::Type::String]);
for (i, item) in ["Seconds", "Minutes", "Hours", "Days", "Weeks"] for (i, item) in [
i18n("Seconds"),
i18n("Minutes"),
i18n("Hours"),
i18n("Days"),
i18n("Weeks"),
]
.iter() .iter()
.enumerate() .enumerate()
{ {

View File

@ -10,6 +10,8 @@ use stacks::{HomeStack, ShowStack};
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use i18n::i18n;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Content { pub struct Content {
stack: gtk::Stack, stack: gtk::Stack,
@ -24,8 +26,8 @@ impl Content {
let home = Rc::new(RefCell::new(HomeStack::new(sender.clone())?)); let home = Rc::new(RefCell::new(HomeStack::new(sender.clone())?));
let shows = Rc::new(RefCell::new(ShowStack::new(sender.clone()))); let shows = Rc::new(RefCell::new(ShowStack::new(sender.clone())));
stack.add_titled(&home.borrow().get_stack(), "home", "New"); stack.add_titled(&home.borrow().get_stack(), "home", &i18n("New"));
stack.add_titled(&shows.borrow().get_stack(), "shows", "Shows"); stack.add_titled(&shows.borrow().get_stack(), "shows", &i18n("Shows"));
let con = Content { let con = Content {
stack, stack,

View File

@ -29,6 +29,8 @@ use std::sync::{Arc, Mutex, RwLock};
use app::Action; use app::Action;
use i18n::i18n;
/// Lazy evaluates and loads widgets to the parent `container` widget. /// Lazy evaluates and loads widgets to the parent `container` widget.
/// ///
/// Accepts an `IntoIterator`, `data`, as the source from which each widget /// Accepts an `IntoIterator`, `data`, as the source from which each widget
@ -325,10 +327,10 @@ pub fn on_import_clicked(window: &gtk::ApplicationWindow, sender: &Sender<Action
// TODO: It might be better to use a FileChooserNative widget. // TODO: It might be better to use a FileChooserNative widget.
// Create the FileChooser Dialog // Create the FileChooser Dialog
let dialog = FileChooserNative::new( let dialog = FileChooserNative::new(
Some("Select the file from which to you want to Import Shows."), Some(i18n("Select the file from which to you want to Import Shows.").as_str()),
Some(window), Some(window),
FileChooserAction::Open, FileChooserAction::Open,
Some("_Import"), Some(i18n("_Import").as_str()),
None, None,
); );
@ -337,7 +339,7 @@ pub fn on_import_clicked(window: &gtk::ApplicationWindow, sender: &Sender<Action
// Set a filter to show only xml files // Set a filter to show only xml files
let filter = FileFilter::new(); let filter = FileFilter::new();
FileFilterExt::set_name(&filter, Some("OPML file")); FileFilterExt::set_name(&filter, Some(i18n("OPML file").as_str()));
filter.add_mime_type("application/xml"); filter.add_mime_type("application/xml");
filter.add_mime_type("text/xml"); filter.add_mime_type("text/xml");
dialog.add_filter(&filter); dialog.add_filter(&filter);
@ -355,12 +357,12 @@ pub fn on_import_clicked(window: &gtk::ApplicationWindow, sender: &Sender<Action
// Refresh the succesfully parsed feeds to index them // Refresh the succesfully parsed feeds to index them
refresh(Some(sources), sender) refresh(Some(sources), sender)
} else { } else {
let text = String::from("Failed to parse the Imported file"); let text = i18n("Failed to parse the Imported file");
sender.send(Action::ErrorNotification(text)); sender.send(Action::ErrorNotification(text));
} }
})) }))
} else { } else {
let text = String::from("Selected File could not be accessed."); let text = i18n("Selected File could not be accessed.");
sender.send(Action::ErrorNotification(text)); sender.send(Action::ErrorNotification(text));
} }
} }

View File

@ -2,6 +2,8 @@ use app::APP_ID;
use gtk; use gtk;
use gtk::prelude::*; use gtk::prelude::*;
use i18n::i18n;
// Totally copied it from fractal. // Totally copied it from fractal.
// https://gitlab.gnome.org/danigm/fractal/blob/503e311e22b9d7540089d735b92af8e8f93560c5/fractal-gtk/src/app.rs#L1883-1912 // https://gitlab.gnome.org/danigm/fractal/blob/503e311e22b9d7540089d735b92af8e8f93560c5/fractal-gtk/src/app.rs#L1883-1912
/// Given a `window` create and attach an `gtk::AboutDialog` to it. /// Given a `window` create and attach an `gtk::AboutDialog` to it.
@ -21,8 +23,8 @@ pub fn about_dialog(window: &gtk::ApplicationWindow) {
let dialog = gtk::AboutDialog::new(); let dialog = gtk::AboutDialog::new();
dialog.set_logo_icon_name(APP_ID); dialog.set_logo_icon_name(APP_ID);
dialog.set_comments("Podcast Client for the GNOME Desktop."); dialog.set_comments(i18n("Podcast Client for the GNOME Desktop.").as_str());
dialog.set_copyright("© 2017, 2018 Jordan Petridis"); dialog.set_copyright(i18n("© 2017, 2018 Jordan Petridis").as_str());
dialog.set_license_type(gtk::License::Gpl30); dialog.set_license_type(gtk::License::Gpl30);
dialog.set_modal(true); dialog.set_modal(true);
// TODO: make it show it fetches the commit hash from which it was built // TODO: make it show it fetches the commit hash from which it was built
@ -36,6 +38,7 @@ pub fn about_dialog(window: &gtk::ApplicationWindow) {
dialog.set_artists(&["Tobias Bernard", "Sam Hewitt"]); dialog.set_artists(&["Tobias Bernard", "Sam Hewitt"]);
dialog.set_authors(authors); dialog.set_authors(authors);
dialog.set_translator_credits(i18n("translator-credits").as_str());
dialog.connect_response(|dlg, _| dlg.destroy()); dialog.connect_response(|dlg, _| dlg.destroy());

View File

@ -21,6 +21,8 @@ use std::cell::RefCell;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use std::sync::{Arc, Mutex, TryLockError}; use std::sync::{Arc, Mutex, TryLockError};
use i18n::i18n_f;
lazy_static! { lazy_static! {
static ref SIZE_OPTS: Arc<size_opts::FileSizeOpts> = { static ref SIZE_OPTS: Arc<size_opts::FileSizeOpts> = {
// Declare a custom humansize option struct // Declare a custom humansize option struct
@ -123,7 +125,8 @@ impl InfoLabels {
// If the lenght is 1 or more minutes // If the lenght is 1 or more minutes
if minutes != 0 { if minutes != 0 {
// Set the label and show them. // Set the label and show them.
self.duration.set_text(&format!("{} min", minutes)); self.duration
.set_text(&i18n_f("{} min", &[&minutes.to_string()]));
self.duration.show(); self.duration.show();
self.separator1.show(); self.separator1.show();
return; return;

View File

@ -23,6 +23,8 @@ use std::ops::Deref;
use std::path::Path; use std::path::Path;
use std::rc::Rc; use std::rc::Rc;
use i18n::i18n;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
enum SeekDirection { enum SeekDirection {
Backwards, Backwards,
@ -295,7 +297,7 @@ impl PlayerWidget {
// Log gst errors. // Log gst errors.
s.player.connect_error(clone!(sender => move |_, _error| { s.player.connect_error(clone!(sender => move |_, _error| {
// sender.send(Action::ErrorNotification(format!("Player Error: {}", error))); // sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
let s = "The media player was unable to execute an action.".into(); let s = i18n("The media player was unable to execute an action.");
sender.send(Action::ErrorNotification(s)); sender.send(Action::ErrorNotification(s));
})); }));

View File

@ -17,6 +17,8 @@ use widgets::appnotif::{InAppNotification, UndoState};
use std::sync::Arc; use std::sync::Arc;
use i18n::{i18n, i18n_f};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ShowMenu { pub struct ShowMenu {
pub container: gtk::PopoverMenu, pub container: gtk::PopoverMenu,
@ -136,12 +138,12 @@ pub fn mark_all_notif(pd: Arc<Show>, sender: &Sender<Action>) -> InAppNotificati
}); });
let undo_callback = clone!(sender => move || sender.send(Action::RefreshWidgetIfSame(id))); let undo_callback = clone!(sender => move || sender.send(Action::RefreshWidgetIfSame(id)));
let text = "Marked all episodes as listened"; let text = i18n("Marked all episodes as listened");
InAppNotification::new(text, callback, undo_callback, UndoState::Shown) InAppNotification::new(&text, callback, undo_callback, UndoState::Shown)
} }
pub fn remove_show_notif(pd: Arc<Show>, sender: Sender<Action>) -> InAppNotification { pub fn remove_show_notif(pd: Arc<Show>, sender: Sender<Action>) -> InAppNotification {
let text = format!("Unsubscribed from {}", pd.title()); let text = i18n_f("Unsubscribed from {}", &[pd.title()]);
let res = utils::ignore_show(pd.id()); let res = utils::ignore_show(pd.id());
debug_assert!(res.is_ok()); debug_assert!(res.is_ok());

View File

@ -2,6 +2,7 @@
export CARGO_HOME=$1/target/cargo-home export CARGO_HOME=$1/target/cargo-home
export RUSTFLAGS="--cfg rayon_unstable" export RUSTFLAGS="--cfg rayon_unstable"
export PODCASTS_LOCALEDIR="$3"
if [[ $DEBUG = true ]] if [[ $DEBUG = true ]]
then then
@ -10,4 +11,4 @@ then
else else
echo "RELEASE MODE" echo "RELEASE MODE"
cargo build --release -p podcasts-gtk && cp $1/target/release/podcasts-gtk $2 cargo build --release -p podcasts-gtk && cp $1/target/release/podcasts-gtk $2
fi fi