Git commit bc64b83fb19171a2cf3d420304c612f192a29c5f by Konstantinos Smanis. Committed on 24/10/2013 at 11:47. Pushed by ksmanis into branch 'master'. *ADDED: Translation support for the GRUB menu. There are two translatable points in GRUB: 1. The GRUB menu which is shown when booting. To be more specific, this includes /only/ the instructions shown below the rectangular frame containing the menu. This translation is triggered with a 'set lang=3Dfoobar' command in the GRUB menu file (/boot/grub/grub.cfg by default), where foobar is a translation catalog (*.mo) contained in the GRUB locale dir (/boot/grub/locale by default). The above command is usually generated by scripts in the /etc/grub.d/ directory which process the 'LANG' [environment] variable. 2. The strings found in the GRUB menu file. To be more specific, this includes entries' titles, echo commands etc. These strings are generated by grub-mkconfig. This translation is triggered with the 'LANGUAGE' [environment] variable. The above variables can be either set in the environment running grub-mkconfig or in GRUB's configuration file (/etc/default/grub by default) for a more permanent effect. Unfortunately, after extensive testing in various distributions (Debian, Kubuntu, Fedora, openSUSE) there are some limitations concerning the use of these variables: 1. LANGUAGE should be set exclusively from the list of translation catalogs in GRUB's locale dir. 2. LANG should be set exclusively from the list of locales as returned by 'locale -a'. If LANG is not an available locale, LANGUAGE will be cancelled out. So, in order for a full translation of the GRUB boot menu, we set LANGUAGE to a proper locale, use the user's current locale as LANG (it will be later manually overwritten so we don't mind; we just want a locale that truely exists) and finally 'set lang=3D' with the same locale we used in LANGUAGE. This is not a very clean solution, but up to this point this is the best I could make out from this situation that could work on as many distros as possible. If anyone has more information on this, please let me know. M +4 -0 cmake/modules/GRUBPaths.cmake M +1 -0 config.h.cmake M +2 -1 src/common.h M +39 -0 src/helper/helper.cpp M +1 -0 src/helper/helper.h M +51 -2 src/kcm_grub2.cpp M +4 -0 src/kcm_grub2.h M +70 -56 ui/kcm_grub2.ui http://commits.kde.org/kcm-grub2/bc64b83fb19171a2cf3d420304c612f192a29c5f diff --git a/cmake/modules/GRUBPaths.cmake b/cmake/modules/GRUBPaths.cmake index 7c30e1d..1ab4b6d 100644 --- a/cmake/modules/GRUBPaths.cmake +++ b/cmake/modules/GRUBPaths.cmake @@ -10,6 +10,7 @@ elseif(NOT (GRUB_INSTALL_EXE OR GRUB_MKCONFIG_EXE OR GRUB= _PROBE_EXE OR GRUB_SET_ set(GRUB_CONFIG "/etc/default/grub" CACHE FILEPATH "GRUB configura= tion file path.") set(GRUB_ENV "/boot/grub/grubenv" CACHE FILEPATH "GRUB environment= file path.") set(GRUB_MEMTEST "/etc/grub.d/20_memtest86+" CACHE FILEPATH "GRUB = memtest file path.") + set(GRUB_LOCALE "/boot/grub/locale/" CACHE PATH "GRUB locale path.= ") else(GRUB_INSTALL_EXE AND GRUB_MKCONFIG_EXE AND GRUB_PROBE_EXE AND GRU= B_SET_DEFAULT_EXE) unset(GRUB_INSTALL_EXE CACHE) unset(GRUB_MKCONFIG_EXE CACHE) @@ -24,6 +25,7 @@ elseif(NOT (GRUB_INSTALL_EXE OR GRUB_MKCONFIG_EXE OR GRUB= _PROBE_EXE OR GRUB_SET_ set(GRUB_CONFIG "/etc/default/grub" CACHE FILEPATH "GRUB confi= guration file path.") set(GRUB_ENV "/boot/grub2/grubenv" CACHE FILEPATH "GRUB enviro= nment file path.") set(GRUB_MEMTEST "/etc/grub.d/20_memtest86+" CACHE FILEPATH "G= RUB memtest file path.") + set(GRUB_LOCALE "/boot/grub2/locale/" CACHE PATH "GRUB locale = path.") else(GRUB_INSTALL_EXE AND GRUB_MKCONFIG_EXE AND GRUB_PROBE_EXE AND= GRUB_SET_DEFAULT_EXE) unset(GRUB_INSTALL_EXE CACHE) unset(GRUB_MKCONFIG_EXE CACHE) @@ -38,6 +40,7 @@ elseif(NOT (GRUB_INSTALL_EXE OR GRUB_MKCONFIG_EXE OR GRUB= _PROBE_EXE OR GRUB_SET_ set(GRUB_CONFIG "/etc/default/burg" CACHE FILEPATH "GRUB c= onfiguration file path.") set(GRUB_ENV "/boot/burg/burgenv" CACHE FILEPATH "GRUB env= ironment file path.") set(GRUB_MEMTEST "/etc/burg.d/20_memtest86+" CACHE FILEPAT= H "GRUB memtest file path.") + set(GRUB_LOCALE "/boot/burg/locale/" CACHE PATH "GRUB loca= le path.") else(GRUB_INSTALL_EXE AND GRUB_MKCONFIG_EXE AND GRUB_PROBE_EXE= AND GRUB_SET_DEFAULT_EXE) message(FATAL_ERROR "Could not automatically resolve GRUB = paths. Please specify all of them manually.") endif(GRUB_INSTALL_EXE AND GRUB_MKCONFIG_EXE AND GRUB_PROBE_EX= E AND GRUB_SET_DEFAULT_EXE) @@ -56,4 +59,5 @@ message(STATUS "GRUB_MENU: ${GRUB_MENU}") message(STATUS "GRUB_CONFIG: ${GRUB_CONFIG}") message(STATUS "GRUB_ENV: ${GRUB_ENV}") message(STATUS "GRUB_MEMTEST: ${GRUB_MEMTEST}") +message(STATUS "GRUB_LOCALE: ${GRUB_LOCALE}") message(STATUS "----------------------------------------------------------= ----------------") diff --git a/config.h.cmake b/config.h.cmake index 2270142..f5db630 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -17,5 +17,6 @@ #define GRUB_CONFIG "@GRUB_CONFIG@" #define GRUB_ENV "@GRUB_ENV@" #define GRUB_MEMTEST "@GRUB_MEMTEST@" +#define GRUB_LOCALE "@GRUB_LOCALE@" = #endif diff --git a/src/common.h b/src/common.h index 153d876..92412da 100644 --- a/src/common.h +++ b/src/common.h @@ -27,7 +27,8 @@ enum LoadOperation { ConfigurationFile =3D 0x2, EnvironmentFile =3D 0x4, MemtestFile =3D 0x8, - Vbe =3D 0x10 + Vbe =3D 0x10, + Locales =3D 0x20 }; Q_DECLARE_FLAGS(LoadOperations, LoadOperation) Q_DECLARE_OPERATORS_FOR_FLAGS(LoadOperations) diff --git a/src/helper/helper.cpp b/src/helper/helper.cpp index a40f879..34cb3a7 100644 --- a/src/helper/helper.cpp +++ b/src/helper/helper.cpp @@ -67,6 +67,33 @@ ActionReply Helper::executeCommand(const QStringList &co= mmand) reply.addData("output", process.readAll()); return reply; } +bool Helper::setLang(const QString &lang) +{ + QFile file(GRUB_MENU); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + kError() << "Failed to open file for reading:" << GRUB_MENU; + kError() << "Error code:" << file.error(); + kError() << "Error description:" << file.errorString(); + return false; + } + QString fileContents =3D QString::fromUtf8(file.readAll().constData()); + file.close(); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + kError() << "Failed to open file for writing:" << GRUB_MENU; + kError() << "Error code:" << file.error(); + kError() << "Error description:" << file.errorString(); + return false; + } + fileContents.replace(QRegExp(QLatin1String("(\\n\\s*set\\s+lang=3D)\\S= *\\n")), QString(QLatin1String("\\1%1\n")).arg(lang)); + if (file.write(fileContents.toUtf8()) =3D=3D -1) { + kError() << "Failed to write data to file:" << GRUB_MENU; + kError() << "Error code:" << file.error(); + kError() << "Error description:" << file.errorString(); + return false; + } + file.close(); + return true; +} = ActionReply Helper::defaults(QVariantMap args) { @@ -182,6 +209,9 @@ ActionReply Helper::load(QVariantMap args) reply.addData("gfxmodes", gfxmodes); } #endif + if (operations.testFlag(Locales)) { + reply.addData(QLatin1String("locales"), QDir(GRUB_LOCALE).entryLis= t(QStringList() << QLatin1String("*.mo"), QDir::Files).replaceInStrings(QRe= gExp(QLatin1String("\\.mo$")), QString())); + } return reply; } ActionReply Helper::save(QVariantMap args) @@ -213,10 +243,19 @@ ActionReply Helper::save(QVariantMap args) QFile::setPermissions(GRUB_MEMTEST, permissions); } = + if (args.contains(QLatin1String("LANG"))) { + qputenv("LANG", args.value(QLatin1String("LANG")).toByteArray()); + } ActionReply grub_mkconfigReply =3D executeCommand(QStringList() << GRU= B_MKCONFIG_EXE << "-o" << GRUB_MENU); if (grub_mkconfigReply.failed()) { return grub_mkconfigReply; } + if (args.contains(QLatin1String("LANGUAGE"))) { + if (!setLang(args.value(QLatin1String("LANGUAGE")).toString())) { + kError() << "An error occured while setting the language for t= he GRUB menu."; + kError() << "The GRUB menu will not be properly translated!"; + } + } = ActionReply grub_set_defaultReply =3D executeCommand(QStringList() << = GRUB_SET_DEFAULT_EXE << rawDefaultEntry); if (grub_set_defaultReply.failed()) { diff --git a/src/helper/helper.h b/src/helper/helper.h index 11e8935..e63b41f 100644 --- a/src/helper/helper.h +++ b/src/helper/helper.h @@ -29,6 +29,7 @@ public: Helper(); private: ActionReply executeCommand(const QStringList &command); + bool setLang(const QString &lang); public Q_SLOTS: ActionReply defaults(QVariantMap args); ActionReply install(QVariantMap args); diff --git a/src/kcm_grub2.cpp b/src/kcm_grub2.cpp index 68a0ef9..87c7cc5 100644 --- a/src/kcm_grub2.cpp +++ b/src/kcm_grub2.cpp @@ -185,6 +185,13 @@ void KCMGRUB2::load() kWarning() << "Invalid GRUB_TIMEOUT value"; } = + showLocales(); + int languageIndex =3D ui->kcombobox_language->findData(unquoteWord(m_s= ettings.value(QLatin1String("LANGUAGE")))); + if (languageIndex !=3D -1) { + ui->kcombobox_language->setCurrentIndex(languageIndex); + } else { + kWarning() << "Invalid LANGUAGE value"; + } ui->checkBox_recovery->setChecked(unquoteWord(m_settings.value("GRUB_D= ISABLE_RECOVERY")).compare("true") !=3D 0); ui->checkBox_memtest->setVisible(m_memtest); ui->checkBox_memtest->setChecked(m_memtestOn); @@ -305,6 +312,14 @@ void KCMGRUB2::save() m_settings["GRUB_TIMEOUT"] =3D "-1"; } } + if (m_dirtyBits.testBit(grubLocaleDirty)) { + int langIndex =3D ui->kcombobox_language->currentIndex(); + if (langIndex > 0) { + m_settings[QLatin1String("LANGUAGE")] =3D ui->kcombobox_langua= ge->itemData(langIndex).toString(); + } else { + m_settings.remove(QLatin1String("LANGUAGE")); + } + } if (m_dirtyBits.testBit(grubDisableRecoveryDirty)) { if (ui->checkBox_recovery->isChecked()) { m_settings.remove("GRUB_DISABLE_RECOVERY"); @@ -453,6 +468,10 @@ void KCMGRUB2::save() saveAction.setHelperID("org.kde.kcontrol.kcmgrub2"); saveAction.addArgument("rawConfigFileContents", configFileContents.toL= ocal8Bit()); saveAction.addArgument("rawDefaultEntry", !m_entries.isEmpty() ? grubD= efault : m_settings.value("GRUB_DEFAULT").toLocal8Bit()); + if (ui->kcombobox_language->currentIndex() > 0) { + saveAction.addArgument(QLatin1String("LANG"), qgetenv("LANG")); + saveAction.addArgument(QLatin1String("LANGUAGE"), m_settings.value= (QLatin1String("LANGUAGE"))); + } if (m_dirtyBits.testBit(memtestDirty)) { saveAction.addArgument("memtest", ui->checkBox_memtest->isChecked(= )); } @@ -529,6 +548,11 @@ void KCMGRUB2::slotGrubTimeoutChanged() m_dirtyBits.setBit(grubTimeoutDirty); emit changed(true); } +void KCMGRUB2::slotGrubLanguageChanged() +{ + m_dirtyBits.setBit(grubLocaleDirty); + emit changed(true); +} void KCMGRUB2::slotGrubDisableRecoveryChanged() { m_dirtyBits.setBit(grubDisableRecoveryDirty); @@ -880,6 +904,7 @@ void KCMGRUB2::setupConnections() connect(ui->radioButton_timeout, SIGNAL(clicked(bool)), this, SLOT(slo= tGrubTimeoutChanged())); connect(ui->spinBox_timeout, SIGNAL(valueChanged(int)), this, SLOT(slo= tGrubTimeoutChanged())); = + connect(ui->kcombobox_language, SIGNAL(activated(int)), this, SLOT(slo= tGrubLanguageChanged())); connect(ui->checkBox_recovery, SIGNAL(clicked(bool)), this, SLOT(slotG= rubDisableRecoveryChanged())); connect(ui->checkBox_memtest, SIGNAL(clicked(bool)), this, SLOT(slotMe= mtestChanged())); connect(ui->checkBox_osProber, SIGNAL(clicked(bool)), this, SLOT(slotG= rubDisableOsProberChanged())); @@ -926,7 +951,7 @@ bool KCMGRUB2::readFile(const QString &fileName, QByteA= rray &fileContents) { QFile file(fileName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - kDebug() << "Failed to read file:" << fileName; + kDebug() << "Failed to open file for reading:" << fileName; kDebug() << "Error code:" << file.error(); kDebug() << "Error description:" << file.errorString(); kDebug() << "The helper will now attempt to read this file."; @@ -964,6 +989,11 @@ void KCMGRUB2::readAll() #if HAVE_HD operations |=3D Vbe; #endif + if (QFileInfo(GRUB_LOCALE).isReadable()) { + m_locales =3D QDir(GRUB_LOCALE).entryList(QStringList() << QLatin1= String("*.mo"), QDir::Files).replaceInStrings(QRegExp(QLatin1String("\\.mo$= ")), QString()); + } else { + operations |=3D Locales; + } = if (operations) { Action loadAction("org.kde.kcontrol.kcmgrub2.load"); @@ -1018,9 +1048,28 @@ void KCMGRUB2::readAll() if (operations.testFlag(Vbe)) { m_resolutions =3D reply.data().value(QLatin1String("gfxmodes")= ).toStringList(); } + if (operations.testFlag(Locales)) { + m_locales =3D reply.data().value(QLatin1String("locales")).toS= tringList(); + } } } = +void KCMGRUB2::showLocales() +{ + ui->kcombobox_language->clear(); + ui->kcombobox_language->addItem(i18nc("@item:inlistbox", "No translati= on"), QString()); + + Q_FOREACH(const QString &locale, m_locales) { + QString language =3D KGlobal::locale()->languageCodeToName(locale); + if (language.isEmpty()) { + language =3D KGlobal::locale()->languageCodeToName(locale.spli= t('@').first()); + if (language.isEmpty()) { + language =3D KGlobal::locale()->languageCodeToName(locale.= split('@').first().split('_').first()); + } + } + ui->kcombobox_language->addItem(QString("%1 (%2)").arg(language, l= ocale), locale); + } +} void KCMGRUB2::sortResolutions() { for (int i =3D 0; i < m_resolutions.size(); i++) { @@ -1212,7 +1261,7 @@ void KCMGRUB2::parseSettings(const QString &config) m_settings.clear(); while (!stream.atEnd()) { line =3D stream.readLine().trimmed(); - if (line.startsWith(QLatin1String("GRUB_"))) { + if (line.contains(QRegExp(QLatin1String("^(GRUB_|LANGUAGE=3D)"))))= { m_settings[line.section('=3D', 0, 0)] =3D line.section('=3D', = 1); } } diff --git a/src/kcm_grub2.h b/src/kcm_grub2.h index 014847f..92b39ab 100644 --- a/src/kcm_grub2.h +++ b/src/kcm_grub2.h @@ -56,6 +56,7 @@ private Q_SLOTS: void slotGrubHiddenTimeoutQuietChanged(); void slotGrubTimeoutToggled(bool checked); void slotGrubTimeoutChanged(); + void slotGrubLanguageChanged(); void slotGrubDisableRecoveryChanged(); void slotMemtestChanged(); void slotGrubDisableOsProberChanged(); @@ -87,6 +88,7 @@ private: bool readFile(const QString &fileName, QByteArray &fileContents); void readAll(); = + void showLocales(); void sortResolutions(); void showResolutions(); = @@ -103,6 +105,7 @@ private: grubHiddenTimeoutDirty, grubHiddenTimeoutQuietDirty, grubTimeoutDirty, + grubLocaleDirty, grubDisableRecoveryDirty, memtestDirty, grubDisableOsProberDirty, @@ -132,6 +135,7 @@ private: bool m_memtestOn; QHash m_devices; QStringList m_resolutions; + QStringList m_locales; }; = #endif diff --git a/ui/kcm_grub2.ui b/ui/kcm_grub2.ui index 8a4bc41..1d84e8c 100644 --- a/ui/kcm_grub2.ui +++ b/ui/kcm_grub2.ui @@ -20,7 +20,48 @@ General - + + + + + Default Entry + + + + + + Default Entry: + + + + + + + + + QComboBox::AdjustToContents + + + + + + + Remove Old Entries + + + + + + + + + The next booted entry will = become default + + + + + + @@ -153,40 +194,41 @@ - - - - Qt::Vertical - - - - 20 - 43 - - - - Generated Entries - + + + + Language: + + + + + + + QComboBox::AdjustToContents + + + + Generate recovery entries - + Generate memtest entries - + Probe for operating systems= @@ -196,46 +238,18 @@ - - - - Default Entry + + + + Qt::Vertical - - - - - Default Entry: - - - - - - - - - QComboBox::AdjustToContents - - - - - - - Remove Old Entries - - - - - - - - - The next booted entry will = become default - - - - - + + + 20 + 43 + + +