mirror of
https://github.com/strawberrymusicplayer/strawberry
synced 2025-01-31 01:29:41 +01:00
Add translations support (#82)
* Add translations support * Update .gitignore
This commit is contained in:
parent
034b032cfa
commit
954e0e8a59
2
.gitignore
vendored
2
.gitignore
vendored
@ -106,6 +106,8 @@ PKGBUILD
|
||||
|
||||
# Translations
|
||||
translations.pot
|
||||
zanata.xml
|
||||
.zanata-cache/
|
||||
|
||||
# Snap
|
||||
parts/
|
||||
|
@ -114,6 +114,7 @@ pkg_check_modules(LIBUSBMUXD libusbmuxd)
|
||||
pkg_check_modules(LIBPLIST libplist)
|
||||
pkg_check_modules(LIBDEEZER libdeezer)
|
||||
pkg_check_modules(LIBDZMEDIA libdzmedia)
|
||||
find_package(Gettext)
|
||||
|
||||
if(WIN32)
|
||||
find_package(ZLIB REQUIRED)
|
||||
@ -151,6 +152,11 @@ if(WIN32)
|
||||
set(QT_LIBRARIES ${QT_LIBRARIES} Qt5::WinExtras)
|
||||
endif()
|
||||
|
||||
find_package(Qt5LinguistTools CONFIG)
|
||||
if (Qt5LinguistTools_FOUND)
|
||||
set(QT_LCONVERT_EXECUTABLE Qt5::lconvert)
|
||||
endif()
|
||||
|
||||
if(X11_FOUND)
|
||||
find_path(KEYSYMDEF_H NAMES "keysymdef.h" PATHS "${X11_INCLUDE_DIR}" PATH_SUFFIXES "X11")
|
||||
find_path(XF86KEYSYM_H NAMES "XF86keysym.h" PATHS "${XCB_INCLUDEDIR}" PATH_SUFFIXES "X11")
|
||||
@ -346,6 +352,11 @@ optional_component(SPARKLE ON "Sparkle integration"
|
||||
DEPENDS "Sparkle" SPARKLE
|
||||
)
|
||||
|
||||
optional_component(TRANSLATIONS OFF "Translations (No languages included yet)"
|
||||
DEPENDS "gettext" GETTEXT_FOUND
|
||||
DEPENDS "Qt5LinguistTools" Qt5LinguistTools_FOUND
|
||||
)
|
||||
|
||||
optional_component(STREAM_TIDAL ON "Streaming: Tidal support")
|
||||
|
||||
if (LIBDZMEDIA_FOUND OR LIBDEEZER_FOUND)
|
||||
|
77
cmake/Translations.cmake
Normal file
77
cmake/Translations.cmake
Normal file
@ -0,0 +1,77 @@
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
|
||||
find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext)
|
||||
if(NOT GETTEXT_XGETTEXT_EXECUTABLE)
|
||||
message(FATAL_ERROR "Could not find xgettext executable")
|
||||
endif(NOT GETTEXT_XGETTEXT_EXECUTABLE)
|
||||
|
||||
set (XGETTEXT_OPTIONS
|
||||
--qt
|
||||
--keyword=tr:1,2c
|
||||
--keyword=tr --flag=tr:1:pass-c-format --flag=tr:1:pass-qt-format
|
||||
--keyword=trUtf8 --flag=tr:1:pass-c-format --flag=tr:1:pass-qt-format
|
||||
--keyword=translate:2,3c
|
||||
--keyword=translate:2 --flag=translate:2:pass-c-format --flag=translate:2:pass-qt-format
|
||||
--keyword=QT_TR_NOOP --flag=QT_TR_NOOP:1:pass-c-format --flag=QT_TR_NOOP:1:pass-qt-format
|
||||
--keyword=QT_TRANSLATE_NOOP:2 --flag=QT_TRANSLATE_NOOP:2:pass-c-format --flag=QT_TRANSLATE_NOOP:2:pass-qt-format
|
||||
--keyword=_ --flag=_:1:pass-c-format --flag=_:1:pass-qt-format
|
||||
--keyword=N_ --flag=N_:1:pass-c-format --flag=N_:1:pass-qt-format
|
||||
--from-code=utf-8
|
||||
)
|
||||
|
||||
macro(add_pot outfiles header pot)
|
||||
# Make relative filenames for all source files
|
||||
set(add_pot_sources)
|
||||
foreach(_filename ${ARGN})
|
||||
get_filename_component(_absolute_filename ${_filename} ABSOLUTE)
|
||||
file(RELATIVE_PATH _relative_filename ${CMAKE_CURRENT_SOURCE_DIR} ${_absolute_filename})
|
||||
list(APPEND add_pot_sources ${_relative_filename})
|
||||
endforeach(_filename)
|
||||
|
||||
# Generate the .pot
|
||||
add_custom_command(
|
||||
OUTPUT ${pot}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} ${XGETTEXT_OPTIONS} -s -C --omit-header --output=${CMAKE_CURRENT_BINARY_DIR}/pot.temp ${add_pot_sources}
|
||||
COMMAND cat ${header} ${CMAKE_CURRENT_BINARY_DIR}/pot.temp > ${pot}
|
||||
DEPENDS ${add_pot_sources} ${header}
|
||||
)
|
||||
|
||||
list(APPEND ${outfiles} ${pot})
|
||||
endmacro(add_pot)
|
||||
|
||||
# Syntax is:
|
||||
# add_po(sources_var po_prefix LANGUAGES language1 language2 ... DIRECTORY dir)
|
||||
|
||||
macro(add_po outfiles po_prefix)
|
||||
parse_arguments(ADD_PO
|
||||
"LANGUAGES;DIRECTORY"
|
||||
""
|
||||
${ARGN}
|
||||
)
|
||||
|
||||
foreach (_lang ${ADD_PO_LANGUAGES})
|
||||
set(_po_filename "${_lang}.po")
|
||||
set(_po_filepath "${CMAKE_CURRENT_SOURCE_DIR}/${ADD_PO_DIRECTORY}/${_po_filename}")
|
||||
set(_qm_filename "strawberry_${_lang}.qm")
|
||||
set(_qm_filepath "${CMAKE_CURRENT_BINARY_DIR}/${ADD_PO_DIRECTORY}/${_qm_filename}")
|
||||
|
||||
# Convert the .po files to .qm files
|
||||
add_custom_command(
|
||||
OUTPUT ${_qm_filepath}
|
||||
COMMAND ${QT_LCONVERT_EXECUTABLE} ARGS ${_po_filepath} -o ${_qm_filepath} -of qm
|
||||
DEPENDS ${_po_filepath} ${_po_filepath}
|
||||
)
|
||||
|
||||
list(APPEND ${outfiles} ${_qm_filepath})
|
||||
endforeach (_lang)
|
||||
|
||||
# Generate a qrc file for the translations
|
||||
set(_qrc ${CMAKE_CURRENT_BINARY_DIR}/${ADD_PO_DIRECTORY}/translations.qrc)
|
||||
file(WRITE ${_qrc} "<RCC><qresource prefix=\"/${ADD_PO_DIRECTORY}\">")
|
||||
foreach(_lang ${ADD_PO_LANGUAGES})
|
||||
file(APPEND ${_qrc} "<file>${po_prefix}${_lang}.qm</file>")
|
||||
endforeach(_lang)
|
||||
file(APPEND ${_qrc} "</qresource></RCC>")
|
||||
qt5_add_resources(${outfiles} ${_qrc})
|
||||
endmacro(add_po)
|
@ -1,18 +1,18 @@
|
||||
/* This file is part of Clementine.
|
||||
/* This file is part of Strawberry.
|
||||
Copyright 2016, John Maguire <john.maguire@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
Strawberry is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
Strawberry is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LAZY_H
|
||||
|
@ -26,6 +26,10 @@ if(BUILD_WERROR)
|
||||
endif (LINUX)
|
||||
endif(BUILD_WERROR)
|
||||
|
||||
if(HAVE_TRANSLATIONS)
|
||||
include(../cmake/Translations.cmake)
|
||||
endif(HAVE_TRANSLATIONS)
|
||||
|
||||
# Set up definitions and paths
|
||||
|
||||
include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
@ -904,6 +908,37 @@ qt5_wrap_cpp(MOC ${HEADERS})
|
||||
qt5_wrap_ui(UIC ${UI})
|
||||
qt5_add_resources(QRC ${RESOURCES})
|
||||
|
||||
if(HAVE_TRANSLATIONS)
|
||||
|
||||
set(LINGUAS "All" CACHE STRING "A space-seperated list of translations to compile in to Strawberry, or \"None\".")
|
||||
if (LINGUAS STREQUAL "All")
|
||||
# build LANGUAGES from all existing .po files
|
||||
file(GLOB pofiles translations/*.po)
|
||||
foreach(pofile ${pofiles})
|
||||
get_filename_component(lang ${pofile} NAME_WE)
|
||||
list(APPEND LANGUAGES ${lang})
|
||||
endforeach(pofile)
|
||||
else (LINGUAS STREQUAL "All")
|
||||
if (NOT LINGUAS OR LINGUAS STREQUAL "None")
|
||||
set (LANGUAGES "")
|
||||
else (NOT LINGUAS OR LINGUAS STREQUAL "None")
|
||||
string(REGEX MATCHALL [a-zA-Z_@]+ LANGUAGES ${LINGUAS})
|
||||
endif (NOT LINGUAS OR LINGUAS STREQUAL "None")
|
||||
endif (LINGUAS STREQUAL "All")
|
||||
|
||||
add_pot(POT
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/translations/header
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/translations/translations.pot
|
||||
${SOURCES}
|
||||
${MOC}
|
||||
${UIC}
|
||||
${OTHER_SOURCES}
|
||||
../data/html/oauthsuccess.html
|
||||
)
|
||||
add_po(PO strawberry_ LANGUAGES ${LANGUAGES} DIRECTORY translations)
|
||||
|
||||
endif(HAVE_TRANSLATIONS)
|
||||
|
||||
add_library(strawberry_lib STATIC
|
||||
${SOURCES}
|
||||
${MOC}
|
||||
|
@ -60,5 +60,6 @@
|
||||
|
||||
#define USE_BUNDLE_DIR "${USE_BUNDLE_DIR}"
|
||||
|
||||
#endif // CONFIG_H_IN
|
||||
#cmakedefine HAVE_TRANSLATIONS
|
||||
|
||||
#endif // CONFIG_H_IN
|
||||
|
@ -232,9 +232,9 @@ void ContextView::NoSong() {
|
||||
"font-weight: Regular;"
|
||||
);
|
||||
|
||||
ui_->label_stop_top->setText("No song playing");
|
||||
ui_->label_stop_top->setText(tr("No song playing"));
|
||||
|
||||
QString html = QString(
|
||||
QString html = tr(
|
||||
"%1 songs<br />\n"
|
||||
"%2 artists<br />\n"
|
||||
"%3 albums<br />\n"
|
||||
@ -404,7 +404,7 @@ void ContextView::SetSong(const Song &song) {
|
||||
if (albumlist.count() > 1) {
|
||||
ui_->label_play_albums->setVisible(true);
|
||||
ui_->label_play_albums->setMinimumSize(0, 20);
|
||||
ui_->label_play_albums->setText(QString("<b>Albums by %1</b>").arg( song.artist().toHtmlEscaped()));
|
||||
ui_->label_play_albums->setText(tr("<b>Albums by %1</b>").arg( song.artist().toHtmlEscaped()));
|
||||
ui_->label_play_albums->setStyleSheet("background-color: #3DADE8; color: rgb(255, 255, 255); font: 11pt;");
|
||||
for (CollectionBackend::Album album : albumlist) {
|
||||
SongList songs = app_->collection_backend()->GetSongs(song.artist(), album.album_name, opt);
|
||||
|
@ -1,18 +1,18 @@
|
||||
/* This file is part of Clementine.
|
||||
/* This file is part of Strawberry.
|
||||
Copyright 2015, David Sansome <me@davidsansome.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
Strawberry is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
Strawberry is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
@ -1,22 +1,22 @@
|
||||
/* This file is part of Clementine.
|
||||
/* This file is part of Strawberry.
|
||||
Copyright 2015, David Sansome <me@davidsansome.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
Strawberry is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
Strawberry is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CORE_THREAD_H_
|
||||
#define CORE_THREAD_H_
|
||||
#ifndef THREAD_H
|
||||
#define THREAD_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
@ -40,4 +40,4 @@ class Thread : public QThread {
|
||||
Utilities::IoPriority io_priority_;
|
||||
};
|
||||
|
||||
#endif // CORE_THREAD_H_
|
||||
#endif // THREAD_H
|
||||
|
@ -58,6 +58,9 @@
|
||||
#include <QSettings>
|
||||
#include <QtEvents>
|
||||
#include <QtDebug>
|
||||
#ifdef HAVE_TRANSLATIONS
|
||||
# include <QTranslator>
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
# include <unistd.h>
|
||||
@ -96,6 +99,10 @@
|
||||
# include "scoped_cftyperef.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TRANSLATIONS
|
||||
# include "potranslator.h"
|
||||
#endif
|
||||
|
||||
namespace Utilities {
|
||||
|
||||
static QString tr(const char *str) {
|
||||
@ -764,6 +771,29 @@ QString DesktopEnvironment() {
|
||||
|
||||
}
|
||||
|
||||
#ifdef HAVE_TRANSLATIONS
|
||||
|
||||
QString SystemLanguageName() {
|
||||
|
||||
QString system_language = QLocale::system().uiLanguages().empty() ? QLocale::system().name() : QLocale::system().uiLanguages().first();
|
||||
// uiLanguages returns strings with "-" as separators for language/region; however QTranslator needs "_" separators
|
||||
system_language.replace("-", "_");
|
||||
|
||||
return system_language;
|
||||
|
||||
}
|
||||
|
||||
void LoadTranslation(const QString &prefix, const QString &path, const QString &language) {
|
||||
|
||||
QTranslator *t = new PoTranslator;
|
||||
if (t->load(prefix + "_" + language, path))
|
||||
QCoreApplication::installTranslator(t);
|
||||
else
|
||||
delete t;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Utilities
|
||||
|
||||
ScopedWCharArray::ScopedWCharArray(const QString &str)
|
||||
|
@ -154,6 +154,11 @@ QString GetRandomString(const int len, const QString &UseCharacters);
|
||||
|
||||
QString DesktopEnvironment();
|
||||
|
||||
#ifdef HAVE_TRANSLATIONS
|
||||
QString SystemLanguageName();
|
||||
void LoadTranslation(const QString &prefix, const QString &path, const QString &language);
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
class ScopedWCharArray {
|
||||
|
@ -460,7 +460,7 @@ void DeezerService::ClearSearch() {
|
||||
|
||||
void DeezerService::SendSearch() {
|
||||
|
||||
emit UpdateStatus("Searching...");
|
||||
emit UpdateStatus(tr("Searching..."));
|
||||
|
||||
QList<Param> parameters;
|
||||
parameters << Param("q", search_text_);
|
||||
@ -503,7 +503,7 @@ void DeezerService::SearchFinished(QNetworkReply *reply, int id) {
|
||||
|
||||
QJsonArray json_data = json_value.toArray();
|
||||
if (json_data.isEmpty()) {
|
||||
Error("No match.");
|
||||
Error(tr("No match."));
|
||||
CheckFinish();
|
||||
return;
|
||||
}
|
||||
@ -588,7 +588,8 @@ void DeezerService::SearchFinished(QNetworkReply *reply, int id) {
|
||||
}
|
||||
|
||||
if (albums_requested_ > 0) {
|
||||
emit UpdateStatus(QString("Retrieving %1 album%2...").arg(albums_requested_).arg(albums_requested_ == 1 ? "" : "s"));
|
||||
if (albums_requested_ == 1) emit UpdateStatus(tr("Retrieving %1 album...").arg(albums_requested_));
|
||||
else emit UpdateStatus(tr("Retrieving %1 albums...").arg(albums_requested_));
|
||||
emit ProgressSetMaximum(albums_requested_);
|
||||
emit UpdateProgress(0);
|
||||
}
|
||||
|
@ -1,19 +1,22 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2016, Valeriy Malov <jazzvoid@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2016, Valeriy Malov <jazzvoid@gmail.com>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Strawberry is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
|
@ -1,19 +1,22 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2016, Valeriy Malov <jazzvoid@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2016, Valeriy Malov <jazzvoid@gmail.com>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Strawberry is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef UDISKS2LISTER_H
|
||||
#define UDISKS2LISTER_H
|
||||
|
@ -75,7 +75,7 @@ About::About(QWidget *parent):QDialog(parent) {
|
||||
<< Person("Valeriy Malov", "jazzvoid@gmail.com")
|
||||
<< Person("Nick Lanham", "nick@afternight.org");
|
||||
|
||||
QString Title("About Strawberry");
|
||||
QString Title(tr("About Strawberry"));
|
||||
|
||||
QFont title_font;
|
||||
title_font.setBold(true);
|
||||
@ -99,28 +99,27 @@ QString About::MainHtml() const {
|
||||
|
||||
QString ret;
|
||||
|
||||
ret = QString("<p>Version %1</p>").arg(QCoreApplication::applicationVersion());
|
||||
ret = tr("<p>Version %1</p>").arg(QCoreApplication::applicationVersion());
|
||||
|
||||
ret += QString("<p>");
|
||||
ret += QString("Strawberry is a audio player and music collection organizer.<br />");
|
||||
ret += QString("It is a fork of Clementine released in 2018 aimed at music collectors, audio enthusiasts and audiophiles.<br />");
|
||||
ret += QString("The name is inspired by the band Strawbs. It's based on a heavily modified version of Clementine created in 2012-2013. It's written in C++ and Qt 5.");
|
||||
ret += QString("</p>");
|
||||
//ret += QString("<p>Website: <a href=\"http://www.strawbs.org/licenses/\">http://www.strawbs.org/</a></p>");
|
||||
ret += QString("<p>");
|
||||
ret += QString("Strawberry is free software: you can redistribute it and/or modify<br />");
|
||||
ret += QString("it under the terms of the GNU General Public License as published by<br />");
|
||||
ret += QString("the Free Software Foundation, either version 3 of the License, or<br />");
|
||||
ret += QString("(at your option) any later version.<br />");
|
||||
ret += QString("<br />");
|
||||
ret += QString("Strawberry is distributed in the hope that it will be useful,<br />");
|
||||
ret += QString("but WITHOUT ANY WARRANTY; without even the implied warranty of<br />");
|
||||
ret += QString("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br />");
|
||||
ret += QString("GNU General Public License for more details.<br />");
|
||||
ret += QString("<br />");
|
||||
ret += QString("You should have received a copy of the GNU General Public License<br />");
|
||||
ret += QString("along with Strawberry. If not, see <a href=\"http://www.gnu.org/licenses/\">http://www.gnu.org/licenses/</a>.");
|
||||
ret += QString("</p>");
|
||||
ret += "<p>";
|
||||
ret += tr("Strawberry is a audio player and music collection organizer.<br />");
|
||||
ret += tr("It is a fork of Clementine released in 2018 aimed at music collectors, audio enthusiasts and audiophiles.<br />");
|
||||
ret += tr("The name is inspired by the band Strawbs. It's based on a heavily modified version of Clementine created in 2012-2013. It's written in C++ and Qt 5.");
|
||||
ret += "</p>";
|
||||
ret += "<p>";
|
||||
ret += tr("Strawberry is free software: you can redistribute it and/or modify<br />");
|
||||
ret += tr("it under the terms of the GNU General Public License as published by<br />");
|
||||
ret += tr("the Free Software Foundation, either version 3 of the License, or<br />");
|
||||
ret += tr("(at your option) any later version.<br />");
|
||||
ret += "<br />";
|
||||
ret += tr("Strawberry is distributed in the hope that it will be useful,<br />");
|
||||
ret += tr("but WITHOUT ANY WARRANTY; without even the implied warranty of<br />");
|
||||
ret += tr("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br />");
|
||||
ret += tr("GNU General Public License for more details.<br />");
|
||||
ret += "<br />";
|
||||
ret += tr("You should have received a copy of the GNU General Public License<br />");
|
||||
ret += tr("along with Strawberry. If not, see <a href=\"http://www.gnu.org/licenses/\">http://www.gnu.org/licenses/</a>.");
|
||||
ret += "</p>";
|
||||
|
||||
return ret;
|
||||
|
||||
@ -130,25 +129,25 @@ QString About::ContributorsHtml() const {
|
||||
|
||||
QString ret;
|
||||
|
||||
ret += QString("<p><b>Strawberry Authors</b>");
|
||||
ret += tr("<p><b>Strawberry Authors</b>");
|
||||
for (const Person &person : strawberry_authors_) {
|
||||
ret += "<br />" + PersonToHtml(person);
|
||||
}
|
||||
ret += QString("</p>");
|
||||
ret += "</p>";
|
||||
|
||||
ret += QString("<p><b>Clementine Authors</b>");
|
||||
ret += tr("<p><b>Clementine Authors</b>");
|
||||
for (const Person &person : clementine_authors_) {
|
||||
ret += "<br />" + PersonToHtml(person);
|
||||
}
|
||||
ret += QString("</p>");
|
||||
ret += "</p>";
|
||||
|
||||
ret += QString("<p><b>Clementine Contributors</b>");
|
||||
ret += tr("<p><b>Clementine Contributors</b>");
|
||||
for (const Person &person : constributors_) {
|
||||
ret += "<br />" + PersonToHtml(person);
|
||||
}
|
||||
ret += QString("</p>");
|
||||
ret += "</p>";
|
||||
|
||||
ret += QString("<p>Thanks to all the Amarok and Clementine contributors.</p>");
|
||||
ret += tr("<p>Thanks to all the Amarok and Clementine contributors.</p>");
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
@ -899,7 +899,7 @@ void EditTagDialog::SongSaveComplete(TagReaderReply *reply, const QString filena
|
||||
pending_--;
|
||||
|
||||
if (!reply->message().save_file_response().success()) {
|
||||
QString message = QString("An error occurred writing metadata to '%1'").arg(filename);
|
||||
QString message = tr("An error occurred writing metadata to '%1'").arg(filename);
|
||||
emit Error(message);
|
||||
}
|
||||
else if (song.directory_id() != -1) {
|
||||
|
@ -17,7 +17,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -141,7 +141,7 @@ InternetSearchView::InternetSearchView(Application *app, InternetSearch *engine,
|
||||
QMenu *settings_menu = new QMenu(this);
|
||||
settings_menu->addActions(group_by_actions_->actions());
|
||||
settings_menu->addSeparator();
|
||||
settings_menu->addAction(IconLoader::Load("configure"), QString("Configure %1...").arg(Song::TextForSource(engine->source())), this, SLOT(OpenSettingsDialog()));
|
||||
settings_menu->addAction(IconLoader::Load("configure"), tr("Configure %1...").arg(Song::TextForSource(engine->source())), this, SLOT(OpenSettingsDialog()));
|
||||
ui_->settings->setMenu(settings_menu);
|
||||
|
||||
connect(ui_->radiobutton_search_artists, SIGNAL(clicked(bool)), SLOT(SearchArtistsClicked(bool)));
|
||||
@ -430,7 +430,7 @@ bool InternetSearchView::ResultsContextMenuEvent(QContextMenuEvent *event) {
|
||||
context_menu_->addSeparator();
|
||||
context_menu_->addMenu(tr("Group by"))->addActions(group_by_actions_->actions());
|
||||
|
||||
context_menu_->addAction(IconLoader::Load("configure"), QString("Configure %1...").arg(Song::TextForSource(engine_->source())), this, SLOT(OpenSettingsDialog()));
|
||||
context_menu_->addAction(IconLoader::Load("configure"), tr("Configure %1...").arg(Song::TextForSource(engine_->source())), this, SLOT(OpenSettingsDialog()));
|
||||
|
||||
const bool enable_context_actions = ui_->results->selectionModel() && ui_->results->selectionModel()->hasSelection();
|
||||
|
||||
|
29
src/main.cpp
29
src/main.cpp
@ -51,11 +51,13 @@
|
||||
#include <QApplication>
|
||||
#include <QCoreApplication>
|
||||
#include <QStandardPaths>
|
||||
#include <QLibraryInfo>
|
||||
#include <QFileDevice>
|
||||
#include <QIODevice>
|
||||
#include <QByteArray>
|
||||
#include <QNetworkProxy>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
#include <QImage>
|
||||
#include <QSettings>
|
||||
@ -64,6 +66,9 @@
|
||||
#ifdef HAVE_DBUS
|
||||
# include <QDBusArgument>
|
||||
#endif
|
||||
#ifdef HAVE_TRANSLATIONS
|
||||
# include <QTranslator>
|
||||
#endif
|
||||
|
||||
#include "main.h"
|
||||
|
||||
@ -84,6 +89,10 @@
|
||||
#include "core/application.h"
|
||||
#include "core/networkproxyfactory.h"
|
||||
#include "core/scangiomodulepath.h"
|
||||
#ifdef HAVE_TRANSLATIONS
|
||||
# include "core/potranslator.h"
|
||||
#endif
|
||||
#include "settings/behavioursettingspage.h"
|
||||
|
||||
#include "widgets/osd.h"
|
||||
|
||||
@ -206,11 +215,31 @@ int main(int argc, char* argv[]) {
|
||||
// Resources
|
||||
Q_INIT_RESOURCE(data);
|
||||
Q_INIT_RESOURCE(icons);
|
||||
#ifdef HAVE_TRANSLATIONS
|
||||
Q_INIT_RESOURCE(translations);
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
QLoggingCategory::defaultCategory()->setEnabled(QtDebugMsg, true);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TRANSLATIONS
|
||||
QString override_language = options.language();
|
||||
if (override_language.isEmpty()) {
|
||||
QSettings s;
|
||||
s.beginGroup(BehaviourSettingsPage::kSettingsGroup);
|
||||
override_language = s.value("language").toString();
|
||||
s.endGroup();
|
||||
}
|
||||
|
||||
const QString language = override_language.isEmpty() ? Utilities::SystemLanguageName() : override_language;
|
||||
|
||||
Utilities::LoadTranslation("qt", QLibraryInfo::location(QLibraryInfo::TranslationsPath), language);
|
||||
Utilities::LoadTranslation("strawberry", ":/translations", language);
|
||||
Utilities::LoadTranslation("strawberry", a.applicationDirPath(), language);
|
||||
Utilities::LoadTranslation("strawberry", QDir::currentPath(), language);
|
||||
#endif
|
||||
|
||||
Application app;
|
||||
|
||||
// Network proxy
|
||||
|
@ -55,7 +55,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+↑</string>
|
||||
<string>Ctrl+Up</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -74,7 +74,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+↓</string>
|
||||
<string>Ctrl+Down</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -141,7 +141,7 @@ void ListenBrainzScrobbler::Authenticate() {
|
||||
|
||||
bool result = QDesktopServices::openUrl(url);
|
||||
if (!result) {
|
||||
QMessageBox messagebox(QMessageBox::Information, "ListenBrainz Authentication", QString("Please open this URL in your browser:<br /><a href=\"%1\">%1</a>").arg(url.toString()), QMessageBox::Ok);
|
||||
QMessageBox messagebox(QMessageBox::Information, tr("ListenBrainz Authentication"), tr("Please open this URL in your browser:<br /><a href=\"%1\">%1</a>").arg(url.toString()), QMessageBox::Ok);
|
||||
messagebox.setTextFormat(Qt::RichText);
|
||||
messagebox.exec();
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ void ScrobblingAPI20::Authenticate() {
|
||||
url_query.addQueryItem("cb", redirect_url.toString());
|
||||
url.setQuery(url_query);
|
||||
|
||||
QMessageBox messagebox(QMessageBox::Information, QString("%1 Scrobbler Authentication").arg(name_), QString("Open URL in web browser?<br /><a href=\"%1\">%1</a><br />Press \"Save\" to copy the URL to clipboard and manually open it in a web browser.").arg(url.toString()), QMessageBox::Open|QMessageBox::Save|QMessageBox::Cancel);
|
||||
QMessageBox messagebox(QMessageBox::Information, tr("%1 Scrobbler Authentication").arg(name_), tr("Open URL in web browser?<br /><a href=\"%1\">%1</a><br />Press \"Save\" to copy the URL to clipboard and manually open it in a web browser.").arg(url.toString()), QMessageBox::Open|QMessageBox::Save|QMessageBox::Cancel);
|
||||
messagebox.setTextFormat(Qt::RichText);
|
||||
int result = messagebox.exec();
|
||||
switch (result) {
|
||||
@ -141,7 +141,7 @@ void ScrobblingAPI20::Authenticate() {
|
||||
if (openurl_result) {
|
||||
break;
|
||||
}
|
||||
QMessageBox messagebox_error(QMessageBox::Warning, QString("%1 Scrobbler Authentication").arg(name_), QString("Could not open URL. Please open this URL in your browser:<br /><a href=\"%1\">%1</a>").arg(url.toString()), QMessageBox::Ok);
|
||||
QMessageBox messagebox_error(QMessageBox::Warning, tr("%1 Scrobbler Authentication").arg(name_), tr("Could not open URL. Please open this URL in your browser:<br /><a href=\"%1\">%1</a>").arg(url.toString()), QMessageBox::Ok);
|
||||
messagebox_error.setTextFormat(Qt::RichText);
|
||||
messagebox_error.exec();
|
||||
}
|
||||
@ -149,7 +149,7 @@ void ScrobblingAPI20::Authenticate() {
|
||||
QApplication::clipboard()->setText(url.toString());
|
||||
break;
|
||||
case QMessageBox::Cancel:
|
||||
AuthError("Authentication was cancelled.");
|
||||
AuthError(tr("Authentication was cancelled."));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -436,7 +436,7 @@ void ScrobblingAPI20::Scrobble(const Song &song) {
|
||||
if (app_->scrobbler()->IsOffline()) return;
|
||||
|
||||
if (!IsAuthenticated()) {
|
||||
emit ErrorMessage(QString("Scrobbler %1 is not authenticated!").arg(name_));
|
||||
emit ErrorMessage(tr("Scrobbler %1 is not authenticated!").arg(name_));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,10 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QDir>
|
||||
#include <QLocale>
|
||||
#include <QSettings>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QCheckBox>
|
||||
@ -37,6 +41,14 @@ class SettingsDialog;
|
||||
|
||||
const char *BehaviourSettingsPage::kSettingsGroup = "Behaviour";
|
||||
|
||||
#ifdef HAVE_TRANSLATIONS
|
||||
namespace {
|
||||
bool LocaleAwareCompare(const QString &a, const QString &b) {
|
||||
return a.localeAwareCompare(b) < 0;
|
||||
}
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
BehaviourSettingsPage::BehaviourSettingsPage(SettingsDialog *dialog) : SettingsPage(dialog), ui_(new Ui_BehaviourSettingsPage) {
|
||||
|
||||
ui_->setupUi(this);
|
||||
@ -71,6 +83,43 @@ BehaviourSettingsPage::BehaviourSettingsPage(SettingsDialog *dialog) : SettingsP
|
||||
ui_->combobox_menuplaymode->setItemData(1, MainWindow::PlayBehaviour_IfStopped);
|
||||
ui_->combobox_menuplaymode->setItemData(2, MainWindow::PlayBehaviour_Always);
|
||||
|
||||
#ifdef HAVE_TRANSLATIONS
|
||||
// Populate the language combo box. We do this by looking at all the compiled in translations.
|
||||
QDir dir(":/translations/");
|
||||
QStringList codes(dir.entryList(QStringList() << "*.qm"));
|
||||
QRegExp lang_re("^strawberry_(.*).qm$");
|
||||
for (const QString &filename : codes) {
|
||||
|
||||
// The regex captures the "ru" from "strawberry_ru.qm"
|
||||
if (!lang_re.exactMatch(filename)) continue;
|
||||
|
||||
QString code = lang_re.cap(1);
|
||||
QString lookup_code = QString(code)
|
||||
.replace("@latin", "_Latn")
|
||||
.replace("_CN", "_Hans_CN")
|
||||
.replace("_TW", "_Hant_TW");
|
||||
|
||||
QString language_name = QLocale::languageToString(QLocale(lookup_code).language());
|
||||
QString native_name = QLocale(lookup_code).nativeLanguageName();
|
||||
if (!native_name.isEmpty()) {
|
||||
language_name = native_name;
|
||||
}
|
||||
QString name = QString("%1 (%2)").arg(language_name, code);
|
||||
|
||||
language_map_[name] = code;
|
||||
}
|
||||
|
||||
language_map_["English (en)"] = "en";
|
||||
|
||||
// Sort the names and show them in the UI
|
||||
QStringList names = language_map_.keys();
|
||||
std::stable_sort(names.begin(), names.end(), LocaleAwareCompare);
|
||||
ui_->combobox_language->addItems(names);
|
||||
#else
|
||||
ui_->groupbox_language->setEnabled(false);
|
||||
ui_->groupbox_language->setVisible(false);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
BehaviourSettingsPage::~BehaviourSettingsPage() {
|
||||
@ -114,6 +163,12 @@ void BehaviourSettingsPage::Load() {
|
||||
|
||||
ui_->spinbox_seekstepsec->setValue(s.value("seek_step_sec", 10).toInt());
|
||||
|
||||
QString name = language_map_.key(s.value("language").toString());
|
||||
if (name.isEmpty())
|
||||
ui_->combobox_language->setCurrentIndex(0);
|
||||
else
|
||||
ui_->combobox_language->setCurrentIndex(ui_->combobox_language->findText(name));
|
||||
|
||||
s.endGroup();
|
||||
|
||||
}
|
||||
@ -141,6 +196,9 @@ void BehaviourSettingsPage::Save() {
|
||||
s.setValue("doubleclick_playmode", doubleclick_playmode);
|
||||
s.setValue("menu_playmode", menu_playmode);
|
||||
s.setValue("seek_step_sec", ui_->spinbox_seekstepsec->value());
|
||||
|
||||
s.setValue("language", language_map_.contains(ui_->combobox_language->currentText()) ? language_map_[ui_->combobox_language->currentText()] : QString());
|
||||
|
||||
s.endGroup();
|
||||
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
|
||||
#include "settingspage.h"
|
||||
@ -49,6 +50,7 @@ private slots:
|
||||
|
||||
private:
|
||||
Ui_BehaviourSettingsPage *ui_;
|
||||
QMap<QString, QString> language_map_;
|
||||
|
||||
};
|
||||
|
||||
|
@ -101,6 +101,38 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupbox_language">
|
||||
<property name="title">
|
||||
<string>Language</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_13">
|
||||
<item>
|
||||
<widget class="QComboBox" name="combobox_language">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Use the system default</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_language">
|
||||
<property name="text">
|
||||
<string>You will need to restart Strawberry if you change the language.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_1">
|
||||
<property name="title">
|
||||
@ -243,7 +275,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_3">
|
||||
<widget class="Line" name="line_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
@ -237,7 +237,7 @@ void GlobalShortcutsSettingsPage::OpenGnomeKeybindingProperties() {
|
||||
|
||||
if (!QProcess::startDetached("gnome-keybinding-properties")) {
|
||||
if (!QProcess::startDetached("gnome-control-center", QStringList() << "keyboard")) {
|
||||
QMessageBox::warning(this, "Error", QString("The \"%1\" command could not be started.").arg("gnome-keybinding-properties"));
|
||||
QMessageBox::warning(this, "Error", tr("The \"%1\" command could not be started.").arg("gnome-keybinding-properties"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,7 +148,7 @@ void TidalService::SendLogin() {
|
||||
|
||||
void TidalService::SendLogin(const QString &username, const QString &password) {
|
||||
|
||||
if (search_id_ != 0) emit UpdateStatus("Authenticating...");
|
||||
if (search_id_ != 0) emit UpdateStatus(tr("Authenticating..."));
|
||||
|
||||
login_sent_ = true;
|
||||
login_attempts_++;
|
||||
@ -471,7 +471,7 @@ int TidalService::Search(const QString &text, InternetSearch::SearchType type) {
|
||||
void TidalService::StartSearch() {
|
||||
|
||||
if (username_.isEmpty() || password_.isEmpty()) {
|
||||
emit SearchError(pending_search_id_, "Missing username and/or password.");
|
||||
emit SearchError(pending_search_id_, tr("Missing username and/or password."));
|
||||
next_pending_search_id_ = 1;
|
||||
ShowConfig();
|
||||
return;
|
||||
@ -509,7 +509,7 @@ void TidalService::ClearSearch() {
|
||||
|
||||
void TidalService::SendSearch() {
|
||||
|
||||
emit UpdateStatus("Searching...");
|
||||
emit UpdateStatus(tr("Searching..."));
|
||||
|
||||
switch (pending_search_type_) {
|
||||
case InternetSearch::SearchType_Artists:
|
||||
@ -582,7 +582,7 @@ void TidalService::ArtistsReceived(QNetworkReply *reply, int search_id) {
|
||||
QJsonArray json_items = json_value.toArray();
|
||||
if (json_items.isEmpty()) {
|
||||
artist_search_ = false;
|
||||
Error("No match.");
|
||||
Error(tr("No match."));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -610,7 +610,8 @@ void TidalService::ArtistsReceived(QNetworkReply *reply, int search_id) {
|
||||
}
|
||||
|
||||
if (artist_albums_requested_ > 0) {
|
||||
emit UpdateStatus(QString("Retrieving albums for %1 artist%2...").arg(artist_albums_requested_).arg(artist_albums_requested_ == 1 ? "" : "s"));
|
||||
if (artist_albums_requested_ == 1) emit UpdateStatus(tr("Retrieving albums for %1 artist...").arg(artist_albums_requested_));
|
||||
else emit UpdateStatus(tr("Retrieving albums for %1 artists...").arg(artist_albums_requested_));
|
||||
emit ProgressSetMaximum(artist_albums_requested_);
|
||||
emit UpdateProgress(0);
|
||||
}
|
||||
@ -802,7 +803,8 @@ void TidalService::AlbumsFinished(const int artist_id, const int offset_requeste
|
||||
}
|
||||
|
||||
if (album_songs_requested_ > 0) {
|
||||
emit UpdateStatus(QString("Retrieving songs for %1 album%2...").arg(album_songs_requested_).arg(album_songs_requested_ == 1 ? "" : "s"));
|
||||
if (album_songs_requested_ == 1) emit UpdateStatus(tr("Retrieving songs for %1 album...").arg(album_songs_requested_));
|
||||
else emit UpdateStatus(tr("Retrieving songs for %1 albums...").arg(album_songs_requested_));
|
||||
emit ProgressSetMaximum(album_songs_requested_);
|
||||
emit UpdateProgress(0);
|
||||
}
|
||||
|
8
src/translations/header
Normal file
8
src/translations/header
Normal file
@ -0,0 +1,8 @@
|
||||
# Strawberry Music Player
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
Loading…
x
Reference in New Issue
Block a user