Merge branch 'i18n' into 'master'

Translation support and initial spanish translation

See merge request World/podcasts!46
This commit is contained in:
Jordan Petridis 2018-08-02 14:54:41 +00:00
commit bcd739da76
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

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,7 +38,7 @@ 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