1
0
mirror of https://invent.kde.org/system/dolphin synced 2024-07-04 17:30:55 +00:00

Allow for more explicit button labels

This commit allows us to very explicitly refer to any set of items
in text. This way buttons don't need to be labeled generically
like "Permanently Delete" but can be enriched to be labeled
"Permanently Delete "FileName"" or "Copy 7 Selected Folders" or
"Copy 6 Files" or "Rename "file1", "file2", "file3", "file4" and
"folder5"".

This commit tries to save translators a lot of work by using a
translation puzzle. This might be problematic for some languages.
The alternative on the other hand would mean that any label which
wants to be explicit would need to have over 10 translations just
for one label which seems quite bad as well.

A fallback is to be implemented for languages that can't really
accommodate for any specific word puzzle. This is explained in the
documentation.
This commit is contained in:
Felix Ernst 2022-03-23 15:03:32 +00:00
parent 49726ad591
commit 45af4bc0e0
4 changed files with 153 additions and 37 deletions

View File

@ -61,6 +61,7 @@ add_library(dolphinprivate SHARED)
target_sources(dolphinprivate PRIVATE
kitemviews/kfileitemlistview.cpp
kitemviews/kfileitemlistwidget.cpp
kitemviews/kfileitemlisttostring.cpp
kitemviews/kfileitemmodel.cpp
kitemviews/kfileitemmodelrolesupdater.cpp
kitemviews/kitemlistcontainer.cpp

View File

@ -0,0 +1,82 @@
/*
This file is part of the KDE project
SPDX-FileCopyrightText: 2022 Felix Ernst <felixernst@zohomail.eu>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "kfileitemlisttostring.h"
#include <KFileItem>
#include <KFileItemListProperties>
#include <KLocalizedString>
#include <QFontMetrics>
#include <QString>
QString fileItemListToString(KFileItemList items, int maximumTextWidth, QFontMetrics fontMetrics, ItemsState itemsState)
{
QString text;
switch (items.count()) {
case 1:
text = i18nc("Textual representation of a file. %1 is the name of the file/folder.",
"\"%1\"", items.first().name());
break;
case 2:
text = i18nc("Textual representation of two files. %1 and %2 are names of files/folders.",
"\"%1\" and \"%2\"", items.first().name(), items.last().name());
break;
case 3:
text = i18nc("Textual representation of three files. %1, %2 and %3 are names of files/folders.",
"\"%1\", \"%2\" and \"%3\"",
items.first().name(), items.at(1).name(), items.last().name());
break;
case 4:
text = i18nc("Textual representation of four files. %1, %2, %3 and %4 are names of files/folders.",
"\"%1\", \"%2\", \"%3\" and \"%4\"",
items.first().name(), items.at(1).name(), items.at(2).name(), items.last().name());
break;
case 5:
text = i18nc("Textual representation of five files. %1, %2, %3, %4 and %5 are names of files/folders.",
"\"%1\", \"%2\", \"%3\", \"%4\" and \"%5\"",
items.first().name(), items.at(1).name(), items.at(2).name(), items.at(3).name(), items.last().name());
break;
default:
text = QString();
break;
}
// At some point the added clarity from the text starts being less important than the text width.
if (!text.isEmpty() && fontMetrics.horizontalAdvance(text) <= maximumTextWidth) {
return text;
}
const KFileItemListProperties properties(items);
if (itemsState == Selected) {
if (properties.isFile()) {
text = i18ncp("Textual representation of selected files. %1 is the number of files.",
"One Selected File", "%1 Selected Files", items.count());
} else if (properties.isDirectory()) {
text = i18ncp("Textual representation of selected folders. %1 is the number of folders.",
"One Selected Folder", "%1 Selected Folders", items.count());
} else {
text = i18ncp("Textual representation of selected fileitems. %1 is the number of files/folders.",
"One Selected Item", "%1 Selected Items", items.count());
}
if (fontMetrics.horizontalAdvance(text) <= maximumTextWidth) {
return text;
}
}
if (properties.isFile()) {
return i18ncp("Textual representation of files. %1 is the number of files.",
"One File", "%1 Files", items.count());
} else if (properties.isDirectory()) {
return i18ncp("Textual representation of folders. %1 is the number of folders.",
"One Folder", "%1 Folders", items.count());
} else {
return i18ncp("Textual representation of fileitems. %1 is the number of files/folders.",
"One Item", "%1 Items", items.count());
}
}

View File

@ -0,0 +1,56 @@
/*
This file is part of the KDE project
SPDX-FileCopyrightText: 2022 Felix Ernst <felixernst@zohomail.eu>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#ifndef KFILEITEMLISTTOSTRING_H
#define KFILEITEMLISTTOSTRING_H
class KFileItemList;
class QFontMetrics;
class QString;
enum ItemsState {
None,
Selected
};
/**
* @brief Generates a textual representation of the given list of KFileItems.
*
* This method is especially useful to be very explicit about which items will be affected by an action.
* For example can a label for a delete button be enriched to say "Permanantly Delete `picture1` and `picture2`"
* but also "Permanently Delete 7 Selected Folders" without requiring a huge amount of new translations for this.
*
* Unfortunately this doesn't always work.
*
* For some texts and some languages this wide range of words cannot be inserted into a text while staying
* grammatically correct. Because of this you will probably need to write a fallback for these languages.
* Something like this:
* \code
* // i18n: @action:inmenu menu with actions like copy, paste, rename.
* // %2 is a textual representation of the currently selected files or folders. This can be the name of
* // the file/files like "file1" or "file1, file2 and file3" or an aggregate like "8 Selected Folders".
* // If this sort of word puzzle can not be correctly translated in your language, translate it as "NULL" (without the quotes)
* // and a fallback will be used.
* auto buttonText = i18ncp("@action A more elaborate and clearly worded version of the Delete action", "Permanently Delete %2", "Permanently Delete %2", items.count(), fileItemListToString(items, fontMetrics.averageCharWidth() * 20, fontMetrics));
* if (buttonText == QStringLiteral("NULL")) {
* buttonText = i18ncp("@action Delete button label. %1 is the number of items to be deleted.",
* "Permanently Delete %1 Item", "Permanently Delete %1 Items", items.count())
* }
* \endcode
* (The i18n call should be completely in the line following the i18n: comment without any line breaks within the i18n call or the comment might not be correctly extracted. See: https://commits.kde.org/kxmlgui/a31135046e1b3335b5d7bbbe6aa9a883ce3284c1 )
*
* @param items The KFileItemList for which a QString should be generated.
* @param maximumTextWidth The maximum width/horizontalAdvance the QString should have. Keep scaling in mind.
* @param fontMetrics the fontMetrics for the font that is used to calculate the maximumTextWidth.
* @param itemsState A state of the @p items that should be spelled out in the returned QString.
* @returns the names of the @p items separated by commas if that is below the @p maximumCharacterWidth.
* Otherwise returns something like "5 Files", "8 Selected Folders" or "60 Items"
* while being as specific as possible.
*/
QString fileItemListToString(KFileItemList items, int maximumTextWidth, QFontMetrics fontMetrics, ItemsState itemsState = ItemsState::None);
#endif // KFILEITEMLISTTOSTRING_H

View File

@ -8,6 +8,7 @@
#include "dolphinviewactionhandler.h"
#include "dolphindebug.h"
#include "kitemviews/kfileitemlisttostring.h"
#include "kitemviews/kfileitemmodel.h"
#include "settings/viewpropertiesdialog.h"
#include "views/zoomlevelinfo.h"
@ -763,49 +764,25 @@ void DolphinViewActionHandler::slotCopyPath()
void DolphinViewActionHandler::slotSelectionChanged(const KFileItemList& selection)
{
QString basicActionsMenuText;
switch (selection.count()) {
case 0:
if (selection.isEmpty()) {
basicActionsMenuText =
i18nc("@action:inmenu menu with actions like copy, paste, rename. The user's selection is empty when this text is shown.",
"Actions for Current View");
break;
case 1:
basicActionsMenuText =
i18nc("@action:inmenu menu with actions like copy, paste, rename. %1 is the name of the singular selected file/folder.",
"Actions for \"%1\"", selection.first().name());
break;
case 2:
basicActionsMenuText =
i18nc("@action:inmenu menu with actions like copy, paste, rename. %1 and %2 are names of files/folders.",
"Actions for \"%1\" and \"%2\"", selection.first().name(), selection.last().name());
break;
case 3:
basicActionsMenuText =
i18nc("@action:inmenu menu with actions like copy, paste, rename. %1, %2 and %3 are names of files/folders.",
"Actions for \"%1\", \"%2\" and \"%3\"",
selection.first().name(), selection.at(1).name(), selection.last().name());
break;
default:
basicActionsMenuText = QString();
break;
} else {
QFontMetrics fontMetrics = QMenu().fontMetrics();
// i18n: @action:inmenu menu with actions like copy, paste, rename.
// %1 is a textual representation of the currently selected files or folders. This can be the name of
// the file/files like "file1" or "file1, file2 and file3" or an aggregate like "8 Selected Folders".
// If this sort of word puzzle can not be correctly translated in your language, translate it as "NULL" (without the quotes)
// and a fallback will be used.
basicActionsMenuText = i18n("Actions for %1", fileItemListToString(selection, fontMetrics.averageCharWidth() * 40, fontMetrics, ItemsState::Selected));
}
// At some point the added clarity from the text starts being less important than the menu width.
if (basicActionsMenuText.isEmpty() || basicActionsMenuText.length() > 40) {
if (basicActionsMenuText == QStringLiteral("NULL")) {
const KFileItemListProperties properties(selection);
if (properties.isFile()) {
basicActionsMenuText =
i18ncp("@action:inmenu menu with actions like copy, paste, rename. %1 is the amount of selected files/folders.",
"Actions for One Selected File", "Actions for %1 Selected Files", selection.count());
} else if (properties.isDirectory()) {
basicActionsMenuText =
i18ncp("@action:inmenu menu with actions like copy, paste, rename. %1 is the amount of selected files/folders.",
"Actions for One Selected Folder", "Actions for %1 Selected Folders", selection.count());
} else {
basicActionsMenuText =
i18ncp("@action:inmenu menu with actions like copy, paste, rename. %1 is the amount of selected files/folders.",
"Actions for One Selected Item", "Actions for %1 Selected Items", selection.count());
}
basicActionsMenuText =
i18ncp("@action:inmenu menu with actions like copy, paste, rename. %1 is the amount of selected files/folders.",
"Actions for One Selected Item", "Actions for %1 Selected Items", selection.count());
}
QAction *basicActionsMenu = m_actionCollection->action(QStringLiteral("basic_actions"));