mirror of
https://github.com/fergalmoran/picard.git
synced 2026-01-06 16:44:06 +00:00
Add ability to use multiple translation locales:
- Add new `artist_locales` setting and remove old `artist_locale`. - Add new translation locales selector and ordering screen. - Cycle through selected translation locales until a match is found. - Update tests.
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
# Copyright (C) 2016 Suhas
|
||||
# Copyright (C) 2016-2017 Sambhav Kothari
|
||||
# Copyright (C) 2021 Gabriel Ferreira
|
||||
# Copyright (C) 2021 Bob Swift
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
@@ -428,6 +429,10 @@ def upgrade_to_v2_7_0_dev_4(config):
|
||||
if _s["artist_script_exception"]:
|
||||
_s["artist_script_exceptions"] = [_s["artist_script_exception"]]
|
||||
_s.remove("artist_script_exception")
|
||||
ListOption("setting", "artist_locales", ['en'])
|
||||
if _s["artist_locale"]:
|
||||
_s["artist_locales"] = [_s["artist_locale"]]
|
||||
_s.remove("artist_locale")
|
||||
|
||||
|
||||
def rename_option(config, old_opt, new_opt, option_type, default):
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
# Copyright (C) 2020 David Kellner
|
||||
# Copyright (C) 2020 dukeyin
|
||||
# Copyright (C) 2021 Vladislav Karbovskii
|
||||
# Copyright (C) 2021 Bob Swift
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
@@ -192,9 +193,6 @@ def _translate_artist_node(node):
|
||||
config = get_config()
|
||||
transl, translsort = None, None
|
||||
if config.setting['translate_artist_names']:
|
||||
locale = config.setting["artist_locale"]
|
||||
lang = locale.split("_")[0]
|
||||
|
||||
if config.setting['translate_artist_names_script_exception']:
|
||||
threshhold = config.setting["artist_script_exception_weighting"] / 100
|
||||
detected_scripts = list_script_weighted(node["name"], threshhold)
|
||||
@@ -202,39 +200,45 @@ def _translate_artist_node(node):
|
||||
if script_id in detected_scripts:
|
||||
return node['name'], node['sort-name']
|
||||
|
||||
if "aliases" in node:
|
||||
result = (-1, (None, None))
|
||||
for alias in node['aliases']:
|
||||
if not alias["primary"]:
|
||||
continue
|
||||
if "locale" not in alias:
|
||||
continue
|
||||
parts = []
|
||||
if alias['locale'] == locale:
|
||||
score = 0.8
|
||||
elif alias['locale'] == lang:
|
||||
score = 0.6
|
||||
elif alias['locale'].split("_")[0] == lang:
|
||||
score = 0.4
|
||||
else:
|
||||
continue
|
||||
parts.append((score, 5))
|
||||
if alias["type"] == "Artist name":
|
||||
score = 0.8
|
||||
elif alias["type"] == "Legal Name":
|
||||
score = 0.5
|
||||
else:
|
||||
# as 2014/09/19, only Artist or Legal names should have the
|
||||
# Primary flag
|
||||
score = 0.0
|
||||
parts.append((score, 5))
|
||||
comb = linear_combination_of_weights(parts)
|
||||
if comb > result[0]:
|
||||
result = (comb, (alias['name'], alias["sort-name"]))
|
||||
transl, translsort = result[1]
|
||||
if not transl:
|
||||
translsort = node['sort-name']
|
||||
transl = translate_from_sortname(node['name'] or "", translsort)
|
||||
for locale in config.setting["artist_locales"]:
|
||||
lang = locale.split("_")[0]
|
||||
|
||||
if "aliases" in node:
|
||||
result = (-1, (None, None))
|
||||
for alias in node['aliases']:
|
||||
if not alias["primary"]:
|
||||
continue
|
||||
if "locale" not in alias:
|
||||
continue
|
||||
parts = []
|
||||
if alias['locale'] == locale:
|
||||
score = 0.8
|
||||
elif alias['locale'] == lang:
|
||||
score = 0.6
|
||||
elif alias['locale'].split("_")[0] == lang:
|
||||
score = 0.4
|
||||
else:
|
||||
continue
|
||||
parts.append((score, 5))
|
||||
if alias["type"] == "Artist name":
|
||||
score = 0.8
|
||||
elif alias["type"] == "Legal Name":
|
||||
score = 0.5
|
||||
else:
|
||||
# as 2014/09/19, only Artist or Legal names should have the
|
||||
# Primary flag
|
||||
score = 0.0
|
||||
parts.append((score, 5))
|
||||
comb = linear_combination_of_weights(parts)
|
||||
if comb > result[0]:
|
||||
result = (comb, (alias['name'], alias["sort-name"]))
|
||||
transl, translsort = result[1]
|
||||
if transl:
|
||||
# Matching locale found. Skip further processing.
|
||||
return (transl, translsort)
|
||||
if not transl:
|
||||
translsort = node['sort-name']
|
||||
transl = translate_from_sortname(node['name'] or "", translsort)
|
||||
else:
|
||||
transl, translsort = node['name'], node['sort-name']
|
||||
return (transl, translsort)
|
||||
|
||||
@@ -43,7 +43,7 @@ class UserProfileGroups():
|
||||
SettingDesc("va_name", N_("Various Artists name"), ["va_name"]),
|
||||
SettingDesc("nat_name", N_("Non-album tracks name"), ["nat_name"]),
|
||||
SettingDesc("translate_artist_names", N_("Translate artist names"), ["translate_artist_names"]),
|
||||
SettingDesc("artist_locale", N_("Translation locale"), ["artist_locale"]),
|
||||
SettingDesc("artist_locales", N_("Translation locales"), ["selected_locales"]),
|
||||
SettingDesc("translate_artist_names_script_exception", N_("Translate artist names exception"), ["translate_artist_names_script_exception"]),
|
||||
SettingDesc("artist_script_exceptions", N_("Translation script exceptions"), ["ignore_tx_scripts"]),
|
||||
SettingDesc("artist_script_exception_weighting", N_("Exception script weighting"), ["minimum_weighting"]),
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
# Copyright (C) 2014 Wieland Hoffmann
|
||||
# Copyright (C) 2017 Sambhav Kothari
|
||||
# Copyright (C) 2021 Vladislav Karbovskii
|
||||
# Copyright (C) 2021 Bob Swift
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
@@ -43,10 +44,13 @@ from picard.const import (
|
||||
SCRIPTS,
|
||||
)
|
||||
|
||||
from picard.ui import PicardDialog
|
||||
from picard.ui.moveable_list_view import MoveableListView
|
||||
from picard.ui.options import (
|
||||
OptionsPage,
|
||||
register_options_page,
|
||||
)
|
||||
from picard.ui.ui_multi_locale_selector import Ui_MultiLocaleSelector
|
||||
from picard.ui.ui_options_metadata import Ui_MetadataOptionsPage
|
||||
|
||||
|
||||
@@ -79,7 +83,7 @@ class MetadataOptionsPage(OptionsPage):
|
||||
options = [
|
||||
TextOption("setting", "va_name", "Various Artists"),
|
||||
TextOption("setting", "nat_name", "[non-album tracks]"),
|
||||
TextOption("setting", "artist_locale", "en"),
|
||||
ListOption("setting", "artist_locales", ["en"]),
|
||||
BoolOption("setting", "translate_artist_names", False),
|
||||
BoolOption("setting", "translate_artist_names_script_exception", False),
|
||||
ListOption("setting", "artist_script_exceptions", []),
|
||||
@@ -98,21 +102,15 @@ class MetadataOptionsPage(OptionsPage):
|
||||
self.ui.setupUi(self)
|
||||
self.ui.va_name_default.clicked.connect(self.set_va_name_default)
|
||||
self.ui.nat_name_default.clicked.connect(self.set_nat_name_default)
|
||||
self.ui.select_locales.clicked.connect(self.open_locale_selector)
|
||||
self.ui.translate_artist_names.stateChanged.connect(self.set_enabled_states)
|
||||
self.ui.translate_artist_names_script_exception.stateChanged.connect(self.set_enabled_states)
|
||||
|
||||
def load(self):
|
||||
config = get_config()
|
||||
self.ui.translate_artist_names.setChecked(config.setting["translate_artist_names"])
|
||||
|
||||
combo_box = self.ui.artist_locale
|
||||
current_locale = config.setting["artist_locale"]
|
||||
for i, (locale, name, level) in enumerate(iter_sorted_locales(ALIAS_LOCALES)):
|
||||
label = " " * level + name
|
||||
combo_box.addItem(label, locale)
|
||||
if locale == current_locale:
|
||||
combo_box.setCurrentIndex(i)
|
||||
|
||||
self.current_locales = config.setting["artist_locales"]
|
||||
self.make_locales_text()
|
||||
self.ui.translate_artist_names_script_exception.setChecked(config.setting["translate_artist_names_script_exception"])
|
||||
self.ui.ignore_tx_scripts.clear()
|
||||
for script_id in SCRIPTS:
|
||||
@@ -135,10 +133,14 @@ class MetadataOptionsPage(OptionsPage):
|
||||
|
||||
self.set_enabled_states()
|
||||
|
||||
def make_locales_text(self):
|
||||
locales = list(ALIAS_LOCALES[key] for key in self.current_locales)
|
||||
self.ui.selected_locales.setText('; '.join(locales))
|
||||
|
||||
def save(self):
|
||||
config = get_config()
|
||||
config.setting["translate_artist_names"] = self.ui.translate_artist_names.isChecked()
|
||||
config.setting["artist_locale"] = self.ui.artist_locale.itemData(self.ui.artist_locale.currentIndex())
|
||||
config.setting["artist_locales"] = self.current_locales
|
||||
config.setting["translate_artist_names_script_exception"] = self.ui.translate_artist_names_script_exception.isChecked()
|
||||
script_exceptions = []
|
||||
for idx in range(self.ui.ignore_tx_scripts.count()):
|
||||
@@ -171,9 +173,78 @@ class MetadataOptionsPage(OptionsPage):
|
||||
def set_enabled_states(self):
|
||||
translate_checked = self.ui.translate_artist_names.isChecked()
|
||||
translate_exception_checked = self.ui.translate_artist_names_script_exception.isChecked()
|
||||
self.ui.artist_locale.setEnabled(translate_checked)
|
||||
self.ui.select_locales.setEnabled(translate_checked)
|
||||
self.ui.selected_locales.setEnabled(translate_checked)
|
||||
self.ui.translate_artist_names_script_exception.setEnabled(translate_checked)
|
||||
self.ui.ignore_script_frame.setEnabled(translate_checked and translate_exception_checked)
|
||||
|
||||
def open_locale_selector(self):
|
||||
dialog = MultiLocaleSelector(self)
|
||||
dialog.show()
|
||||
|
||||
|
||||
class MultiLocaleSelector(PicardDialog):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.ui = Ui_MultiLocaleSelector()
|
||||
self.ui.setupUi(self)
|
||||
self.ui.button_box.accepted.connect(self.save_changes)
|
||||
self.ui.button_box.rejected.connect(self.reject)
|
||||
self.move_view = MoveableListView(self.ui.selected_locales, self.ui.move_up, self.ui.move_down)
|
||||
self.ui.add_locale.clicked.connect(self.add_locale)
|
||||
self.ui.remove_locale.clicked.connect(self.remove_locale)
|
||||
self.ui.selected_locales.currentRowChanged.connect(self.set_button_state)
|
||||
self.load()
|
||||
|
||||
def load(self):
|
||||
self.ui.selected_locales.clear()
|
||||
for locale in self.parent().current_locales:
|
||||
label = (" " if '_' in locale else "") + ALIAS_LOCALES[locale]
|
||||
item = QtWidgets.QListWidgetItem(label)
|
||||
item.setData(QtCore.Qt.UserRole, locale)
|
||||
self.ui.selected_locales.addItem(item)
|
||||
self.ui.selected_locales.setCurrentRow(0)
|
||||
|
||||
self.ui.available_locales.clear()
|
||||
for (locale, name, level) in iter_sorted_locales(ALIAS_LOCALES):
|
||||
label = " " * level + name
|
||||
item = QtWidgets.QListWidgetItem(label)
|
||||
item.setData(QtCore.Qt.UserRole, locale)
|
||||
self.ui.available_locales.addItem(item)
|
||||
self.ui.available_locales.setCurrentRow(0)
|
||||
|
||||
self.set_button_state()
|
||||
|
||||
def add_locale(self):
|
||||
item = self.ui.available_locales.currentItem()
|
||||
if item is None:
|
||||
return
|
||||
locale = item.data(QtCore.Qt.UserRole)
|
||||
for row in range(self.ui.selected_locales.count()):
|
||||
selected_item = self.ui.selected_locales.item(row)
|
||||
if selected_item.data(QtCore.Qt.UserRole) == locale:
|
||||
return
|
||||
new_item = item.clone()
|
||||
new_item.setText(new_item.text().strip())
|
||||
self.ui.selected_locales.addItem(new_item)
|
||||
self.ui.selected_locales.setCurrentRow(self.ui.selected_locales.count() - 1)
|
||||
|
||||
def remove_locale(self):
|
||||
row = self.ui.selected_locales.currentRow()
|
||||
self.ui.selected_locales.takeItem(row)
|
||||
|
||||
def set_button_state(self):
|
||||
enabled = self.ui.selected_locales.count() > 1
|
||||
self.ui.remove_locale.setEnabled(enabled)
|
||||
|
||||
def save_changes(self):
|
||||
locales = []
|
||||
for row in range(self.ui.selected_locales.count()):
|
||||
selected_item = self.ui.selected_locales.item(row)
|
||||
locales.append(selected_item.data(QtCore.Qt.UserRole))
|
||||
self.parent().current_locales = locales
|
||||
self.parent().make_locales_text()
|
||||
self.accept()
|
||||
|
||||
|
||||
register_options_page(MetadataOptionsPage)
|
||||
|
||||
@@ -27,9 +27,17 @@ class Ui_MetadataOptionsPage(object):
|
||||
self.translate_artist_names = QtWidgets.QCheckBox(self.metadata_groupbox)
|
||||
self.translate_artist_names.setObjectName("translate_artist_names")
|
||||
self.verticalLayout_3.addWidget(self.translate_artist_names)
|
||||
self.artist_locale = QtWidgets.QComboBox(self.metadata_groupbox)
|
||||
self.artist_locale.setObjectName("artist_locale")
|
||||
self.verticalLayout_3.addWidget(self.artist_locale)
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout.setContentsMargins(-1, -1, -1, 0)
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.selected_locales = QtWidgets.QLineEdit(self.metadata_groupbox)
|
||||
self.selected_locales.setReadOnly(True)
|
||||
self.selected_locales.setObjectName("selected_locales")
|
||||
self.horizontalLayout.addWidget(self.selected_locales)
|
||||
self.select_locales = QtWidgets.QPushButton(self.metadata_groupbox)
|
||||
self.select_locales.setObjectName("select_locales")
|
||||
self.horizontalLayout.addWidget(self.select_locales)
|
||||
self.verticalLayout_3.addLayout(self.horizontalLayout)
|
||||
self.translate_artist_names_script_exception = QtWidgets.QCheckBox(self.metadata_groupbox)
|
||||
self.translate_artist_names_script_exception.setObjectName("translate_artist_names_script_exception")
|
||||
self.verticalLayout_3.addWidget(self.translate_artist_names_script_exception)
|
||||
@@ -147,8 +155,7 @@ class Ui_MetadataOptionsPage(object):
|
||||
|
||||
self.retranslateUi(MetadataOptionsPage)
|
||||
QtCore.QMetaObject.connectSlotsByName(MetadataOptionsPage)
|
||||
MetadataOptionsPage.setTabOrder(self.translate_artist_names, self.artist_locale)
|
||||
MetadataOptionsPage.setTabOrder(self.artist_locale, self.translate_artist_names_script_exception)
|
||||
MetadataOptionsPage.setTabOrder(self.translate_artist_names, self.translate_artist_names_script_exception)
|
||||
MetadataOptionsPage.setTabOrder(self.translate_artist_names_script_exception, self.standardize_artists)
|
||||
MetadataOptionsPage.setTabOrder(self.standardize_artists, self.standardize_instruments)
|
||||
MetadataOptionsPage.setTabOrder(self.standardize_instruments, self.convert_punctuation)
|
||||
@@ -163,7 +170,8 @@ class Ui_MetadataOptionsPage(object):
|
||||
def retranslateUi(self, MetadataOptionsPage):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
self.metadata_groupbox.setTitle(_("Metadata"))
|
||||
self.translate_artist_names.setText(_("Translate artist names to this locale where possible:"))
|
||||
self.translate_artist_names.setText(_("Translate artist names to these locales where possible:"))
|
||||
self.select_locales.setText(_("Select"))
|
||||
self.translate_artist_names_script_exception.setText(_("Ignore artist name translation for these scripts:"))
|
||||
self.label_2.setText(_("<html><head/><body><p>Only scripts where the percentage of the artist name that is written using that script are above the specified minimum weighting will be considered.</p></body></html>"))
|
||||
self.label.setText(_("Minimum Weighting:"))
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
# Copyright (C) 2018 Wieland Hoffmann
|
||||
# Copyright (C) 2019-2020 Philipp Wolfer
|
||||
# Copyright (C) 2020 Ray Bouchard
|
||||
# Copyright (C) 2021 Bob Swift
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
@@ -38,7 +39,7 @@ settings = {
|
||||
"standardize_artists": False,
|
||||
"standardize_releases": False,
|
||||
"translate_artist_names": True,
|
||||
"artist_locale": 'en',
|
||||
"artist_locales": ['en'],
|
||||
"translate_artist_names_script_exception": False,
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
# Copyright (C) 2017, 2019 Laurent Monin
|
||||
# Copyright (C) 2018 Wieland Hoffmann
|
||||
# Copyright (C) 2018-2021 Philipp Wolfer
|
||||
# Copyright (C) 2021 Bob Swift
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
@@ -55,7 +56,7 @@ settings = {
|
||||
"standardize_instruments": True,
|
||||
"release_ars": True,
|
||||
"preferred_release_countries": [],
|
||||
"artist_locale": 'en',
|
||||
"artist_locales": ['en'],
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -35,12 +35,30 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="translate_artist_names">
|
||||
<property name="text">
|
||||
<string>Translate artist names to this locale where possible:</string>
|
||||
<string>Translate artist names to these locales where possible:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="artist_locale"/>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="selected_locales">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="select_locales">
|
||||
<property name="text">
|
||||
<string>Select</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="translate_artist_names_script_exception">
|
||||
@@ -289,7 +307,6 @@
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>translate_artist_names</tabstop>
|
||||
<tabstop>artist_locale</tabstop>
|
||||
<tabstop>translate_artist_names_script_exception</tabstop>
|
||||
<tabstop>standardize_artists</tabstop>
|
||||
<tabstop>standardize_instruments</tabstop>
|
||||
|
||||
Reference in New Issue
Block a user