From 759c6b7a962da5b105b7185d8e29e9950c330090 Mon Sep 17 00:00:00 2001 From: Max Niu <53027637+maxx-niu@users.noreply.github.com> Date: Wed, 31 Jan 2024 18:02:58 -0500 Subject: [PATCH 01/15] tray menu options now have translations when a user changes their language (#1545) --- .../app/app/components/Settings/index.tsx | 13 +++++++-- packages/main/src/services/trayMenu/index.ts | 27 ++++++++++++++----- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/packages/app/app/components/Settings/index.tsx b/packages/app/app/components/Settings/index.tsx index 058bf363b4..34c7ef8ad0 100644 --- a/packages/app/app/components/Settings/index.tsx +++ b/packages/app/app/components/Settings/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { remote } from 'electron'; +import { ipcRenderer, remote } from 'electron'; import { Button, Input, Radio, Segment, Icon } from 'semantic-ui-react'; import cx from 'classnames'; import _ from 'lodash'; @@ -99,7 +99,16 @@ const Settings: React.FC = ({ placeholder={t(placeholder)} value={i18n.language} options={options} - onChange={(e, { value }) => i18n.changeLanguage(value as string)} + onChange={(e, { value }) => { + i18n.changeLanguage(value as string); + ipcRenderer.send('tray-menu-translations-update', { + 'play': i18n.t('command-palette:actions.play'), + 'pause': i18n.t('command-palette:actions.pause'), + 'next': i18n.t('command-palette:actions.next'), + 'previous': i18n.t('command-palette:actions.previous'), + 'quit': i18n.t('command-palette:actions.quit') + }); + }} /> ); diff --git a/packages/main/src/services/trayMenu/index.ts b/packages/main/src/services/trayMenu/index.ts index 590e9e352e..2329dac1dd 100644 --- a/packages/main/src/services/trayMenu/index.ts +++ b/packages/main/src/services/trayMenu/index.ts @@ -1,12 +1,11 @@ import { injectable, inject } from 'inversify'; -import { Menu, app, Tray, nativeImage } from 'electron'; +import { Menu, app, Tray, nativeImage, ipcMain } from 'electron'; import { NuclearMeta, IpcEvents} from '@nuclear/core'; import HttpApi from '../http'; import Config from '../config'; import Platform from '../platform'; import Window from '../window'; - type PlayerContext = { isPlaying?: boolean; track?: NuclearMeta @@ -18,6 +17,14 @@ class TrayMenu { private playerContext: PlayerContext; + private trayMenuTranslations = { + 'pause': 'Pause', + 'play': 'Play', + 'next': 'Next', + 'previous': 'Previous', + 'quit': 'Quit' + }; + constructor( @inject(Config) private config: Config, @inject(HttpApi) private httpApi: HttpApi, @@ -39,6 +46,12 @@ class TrayMenu { }); this.tray.setToolTip(this.getToolTipString()); this.tray.setContextMenu(this.getMenu()); + + // apply tray menu translations when user changes language from language settings + ipcMain.on('tray-menu-translations-update', (e, translations) => { + this.trayMenuTranslations = translations; + this.update(); + }); } getMenu() { @@ -69,7 +82,7 @@ class TrayMenu { if (this.playerContext.track) { if (this.playerContext.isPlaying) { template.push({ - label: 'Pause', + label: this.trayMenuTranslations.pause, type: 'normal', click: async () => { this.window.send(IpcEvents.PAUSE); @@ -78,7 +91,7 @@ class TrayMenu { }); } else { template.push({ - label: 'Play', + label: this.trayMenuTranslations.play, type: 'normal', click: async () => { this.window.send(IpcEvents.PLAY); @@ -88,7 +101,7 @@ class TrayMenu { } template.push({ - label: 'Next', + label: this.trayMenuTranslations.next, type: 'normal', click: async () => { this.window.send(IpcEvents.NEXT); @@ -96,7 +109,7 @@ class TrayMenu { }); template.push({ - label: 'Previous', + label: this.trayMenuTranslations.previous, type: 'normal', click: async () => { this.window.send(IpcEvents.PREVIOUS); @@ -110,7 +123,7 @@ class TrayMenu { // Quit button template.push({ - label: 'Quit', + label: this.trayMenuTranslations.quit, type: 'normal', click: async () => { await this.httpApi.close(); From e4abca9c544e22388267c03818c5e5d02caad520 Mon Sep 17 00:00:00 2001 From: nukeop <12746779+nukeop@users.noreply.github.com> Date: Sat, 3 Feb 2024 00:18:27 +0100 Subject: [PATCH 02/15] New Crowdin updates (#1485) * New translations en.json (Romanian) * New translations en.json (French) * New translations en.json (Spanish) * New translations en.json (Arabic) * New translations en.json (Belarusian) * New translations en.json (Czech) * New translations en.json (Danish) * New translations en.json (German) * New translations en.json (Greek) * New translations en.json (Finnish) * New translations en.json (Hebrew) * New translations en.json (Italian) * New translations en.json (Japanese) * New translations en.json (Korean) * New translations en.json (Lithuanian) * New translations en.json (Dutch) * New translations en.json (Norwegian) * New translations en.json (Polish) * New translations en.json (Russian) * New translations en.json (Slovak) * New translations en.json (Albanian) * New translations en.json (Swedish) * New translations en.json (Turkish) * New translations en.json (Ukrainian) * New translations en.json (Chinese Simplified) * New translations en.json (Chinese Traditional) * New translations en.json (Vietnamese) * New translations en.json (Icelandic) * New translations en.json (Portuguese, Brazilian) * New translations en.json (Indonesian) * New translations en.json (Persian) * New translations en.json (Bengali) * New translations en.json (Croatian) * New translations en.json (Latvian) * New translations en.json (Hindi) * New translations en.json (Tagalog) * New translations en.json (Kurmanji (Kurdish)) * New translations en.json (Turkmen) * New translations en.json (Cantonese (Simplified)) * New translations en.json (Turkish) * New translations en.json (Portuguese, Brazilian) * New translations en.json (Ukrainian) * New translations en.json (Ukrainian) * New translations en.json (Czech) * New translations en.json (Belarusian) * New translations en.json (Belarusian) * New translations en.json (Italian) * New translations en.json (Italian) * New translations en.json (German) * New translations en.json (Dutch) * New translations en.json (Croatian) * New translations en.json (Hindi) * New translations en.json (Arabic) * New translations en.json (Arabic) * New translations en.json (Spanish) * New translations en.json (Arabic) * New translations en.json (Arabic) * New translations en.json (Arabic) * New translations en.json (Chinese Simplified) * New translations en.json (Japanese) * New translations en.json (Swedish) * New translations en.json (Swedish) * New translations en.json (French) * New translations en.json (French) * New translations en.json (Chinese Simplified) * New translations en.json (Chinese Traditional) * New translations en.json (Tamil) * New translations en.json (Turkish) * New translations en.json (Turkish) * New translations en.json (Finnish) * New translations en.json (Finnish) * New translations en.json (Indonesian) * New translations en.json (Indonesian) * New translations en.json (Hungarian) * New translations en.json (Hungarian) * New translations en.json (Hungarian) * New translations en.json (Hungarian) * New translations en.json (Hungarian) * New translations en.json (Romanian) * New translations en.json (Romanian) --- packages/i18n/src/locales/ar_SA.json | 291 ++++++++-------- packages/i18n/src/locales/be_BY.json | 35 +- packages/i18n/src/locales/bn_BD.json | 1 + packages/i18n/src/locales/cs.json | 1 + packages/i18n/src/locales/de.json | 37 +- packages/i18n/src/locales/dk.json | 1 + packages/i18n/src/locales/es.json | 37 +- packages/i18n/src/locales/fa_IR.json | 1 + packages/i18n/src/locales/fi.json | 37 +- packages/i18n/src/locales/fr.json | 37 +- packages/i18n/src/locales/gr.json | 1 + packages/i18n/src/locales/he_IL.json | 1 + packages/i18n/src/locales/hi_IN.json | 15 +- packages/i18n/src/locales/hr.json | 37 +- packages/i18n/src/locales/hu_HU.json | 464 ++++++++++++++++++++++++++ packages/i18n/src/locales/id.json | 89 ++--- packages/i18n/src/locales/is.json | 1 + packages/i18n/src/locales/it.json | 7 +- packages/i18n/src/locales/ja_JP.json | 13 +- packages/i18n/src/locales/ko.json | 1 + packages/i18n/src/locales/ku_KMR.json | 1 + packages/i18n/src/locales/lt_LT.json | 1 + packages/i18n/src/locales/lv_LV.json | 1 + packages/i18n/src/locales/nl.json | 39 +-- packages/i18n/src/locales/no_NO.json | 1 + packages/i18n/src/locales/pl.json | 1 + packages/i18n/src/locales/pt_BR.json | 1 + packages/i18n/src/locales/ro_RO.json | 123 +++---- packages/i18n/src/locales/ru.json | 1 + packages/i18n/src/locales/se.json | 107 +++--- packages/i18n/src/locales/sk.json | 1 + packages/i18n/src/locales/sq.json | 1 + packages/i18n/src/locales/ta_IN.json | 464 ++++++++++++++++++++++++++ packages/i18n/src/locales/tk_TM.json | 1 + packages/i18n/src/locales/tl.json | 1 + packages/i18n/src/locales/tr.json | 89 ++--- packages/i18n/src/locales/uk_UA.json | 21 +- packages/i18n/src/locales/vi.json | 1 + packages/i18n/src/locales/yue_CN.json | 1 + packages/i18n/src/locales/zh.json | 11 +- packages/i18n/src/locales/zh_TW.json | 191 +++++------ 41 files changed, 1566 insertions(+), 599 deletions(-) create mode 100644 packages/i18n/src/locales/hu_HU.json create mode 100644 packages/i18n/src/locales/ta_IN.json diff --git a/packages/i18n/src/locales/ar_SA.json b/packages/i18n/src/locales/ar_SA.json index c77d06a0d7..f898a69a2a 100644 --- a/packages/i18n/src/locales/ar_SA.json +++ b/packages/i18n/src/locales/ar_SA.json @@ -19,7 +19,7 @@ "favorite-artists": "الفنانين المفضلين", "favorite-tracks": "الاغاني المفضلة", "library": "اللائحة المحلية", - "listening-history": "Listening History", + "listening-history": "تاريخ الاستماع", "lyrics": "كلمات الأغنية", "main": "رئيسي", "playlists": "قائمة التشغيل", @@ -51,15 +51,15 @@ "genres": "الأنواع", "news": "الأخبَار", "playcounts": "مرات التشغيل", - "playlists": "Trending", + "playlists": "رائجاً", "popular-track-title": "الاكثر تشغيلا من ديزر", "title": "العنوان", "top": "أفضل المقاطع", - "trending-playlists": "Trending playlists", - "trending-artists": "Trending artists", - "trending-albums": "Trending albums", - "filter": "Filter...", - "nothing-found": "Nothing found." + "trending-playlists": "قوائم التشغيل الشائعة", + "trending-artists": "الفنانين المشهورين", + "trending-albums": "ألبومات شائعة", + "filter": "تصفية...", + "nothing-found": "لم يتم العثور على شيء." }, "downloads": { "clear": "مسح الاغاني المنتهية", @@ -97,7 +97,7 @@ "empty": "لا توجد مفضلات مضافة", "empty-help": "حاول إضافة بعض الاغاني إلى المفضلة وسيظهرون هنا!", "header": "الاغاني المفضلين لديك", - "play-random": "Play a random track from your favorites", + "play-random": "تشغيل مسار عشوائي من تفضيلاتك", "title": "العنوان" }, "help": { @@ -127,6 +127,7 @@ "not-found": "لم يتم العثور على أي كلمات لهذه الأغنية." }, "option-control": { + "rate": "سرعة التشغيل", "autoradio": "مذياع تلقائي", "loop": "تكرار", "mini-player": "مشغل صغير", @@ -159,12 +160,12 @@ "spotify-import-placeholder": "لصق رابط المقطوعات الموسيقية بسبوتيفاي هنا", "dialog-placeholder": "اسم قائمة التشغيل...", "dialog-accept": "حفظ", - "dialog-cancel": "Cancel", - "dialog-rename": "Rename", - "dialog-import": "Import", + "dialog-cancel": "إلغاء", + "dialog-rename": "إعادة التسمية", + "dialog-import": "إستيراد", "create-button": "إنشاء قائمة تشغيل جديدة", "new-playlist": "قائمة تشغيل جديدة", - "import-url-button": "Import from url (Spotify)", + "import-url-button": "استيراد من الرابط (Spotify)", "import-button": "استيراد من ملف (JSON)", "import-fail-title": "فشل استيراد قائمة التشغيل", "import-success-title": "تم استيراد قائمة التشغيل بنجاح", @@ -182,9 +183,9 @@ "tracks-plural": "مقاطع", "modified-at": "آخر تعديل: ", "never-modified": "مجهول", - "server-modified-at": "Server modified: ", - "upload-to-server": "Upload to server", - "download-from-server": "Download from server", + "server-modified-at": "تم تعديل الخادم: ", + "upload-to-server": "تحميل إلى الخادم", + "download-from-server": "التنزيل من الخادم", "number-of-tracks": "الاغاني" }, "plugins": { @@ -207,34 +208,34 @@ "dialog-placeholder": "اسم قائمة التشغيل...", "dialog-trigger": "حفظ كقائمة تشغيل", "download": "التنزيل", - "download-toast-content": "{{artist}} - {{title}} has been added to downloads.", + "download-toast-content": "{{artist}} - {{title}} تم إضافته إلى التنزيلات.", "download-toast-title": "تمت اضافة ا الاغنية الى التنزيلات", "favorite-add": "إضافة إلى المفضلات", "header": "قائمة الإنتظار", "header-track": "المقطع الحالي", "copy-track-url": "تم نسخ رابط الاغنية إلى الحافظة", - "loading": "Stream still loading.", + "loading": "لا يزال البث قيد التحميل.", "playlist-add": "إضافة إلى قائمة التشغيل", "playlist-toast-content": "قائمة التشغيل {{name}} تم إنشاؤها.", "playlist-toast-title": "تم إنشاء قائمة التشغيل", - "live": "Live", + "live": "بث مباشر", "stream-verification": { "tooltip": { - "unknown": "The status of this stream is unknown.", - "unverified": "This stream has not yet been verified by the community.", - "weakly-verified": "This stream has been verified by a couple of users.", - "verified": "This stream has been verified by the community.", - "verified-by-user": "This stream has been verified by you." + "unknown": "حالة هذا البث غير معروفة.", + "unverified": "لم يتم التحقق بعد من هذا التيار من قبل المجتمع.", + "weakly-verified": "تم التحقق من هذا البث من قبل اثنين من المستخدمين.", + "verified": "تم التحقق من هذا البث من قبل المجتمع.", + "verified-by-user": "تم التحقق من هذا البث من قبلك." }, "stream-status": { - "unknown": "Unknown", - "unverified": "Unverified", - "weakly-verified": "Weakly verified", - "verified": "Verified", - "verified-by-user": "Verified by you" + "unknown": "غير معروف", + "unverified": "لم يتم التحقق", + "weakly-verified": "تحقق ضعيف", + "verified": "تم التحقق", + "verified-by-user": "تم التحقق من قبلك" }, - "verify": "Verify", - "unverify": "Unverify" + "verify": "تحقق", + "unverify": "لا تحقق" } }, "search": { @@ -245,8 +246,8 @@ "artist_plural": "الفنانين", "clear-history": "مسح التاريخ", "empty": "لا يوجد شيء.", - "last-searches": "Last searches", - "live-stream": "LiveStream", + "last-searches": "عمليات البحث الأخيرة", + "live-stream": "بث مباشر", "placeholder": "بحث...", "playlist": "قائمة التشغيل", "playlist_plural": "قوائم التشغيل", @@ -257,85 +258,85 @@ "you-can-search-for": "يمكنك البحث عن:" }, "seekbar": { - "live": "Live", + "live": "بث مباشر", "segment-popup": "غير الموسيقى" }, "settings": { - "api-port": "Port used by the api", + "api-port": "المنفذ المستخدم من قبل api", "api-url": "", - "audio": "Audio", - "autoradio": "Autoradio", - "autoradio-craziness": "Autoradio craziness", - "autoradio-craziness-description": "Autoradio will select songs that are less similar to the ones already in the queue the crazier it is", + "audio": "صوت", + "autoradio": "مذياع تلقائي", + "autoradio-craziness": "جنون المذياع التلقائي", + "autoradio-craziness-description": "سيختار المذياع التلقائي الأغاني التي لا تشبه الأغاني الموجودة فعلًا في قائمة الانتظار كلما كانت أكثر جنونًا", "autoradio-description": "إضافة اغنية مشابهة تلقائياً عند انتهاء قائمة الانتظار", - "listening-history": "Listening history", - "listening-history-description": "Log the tracks you listen to, a la Last.fm. The history is stored offline.", - "compact-menu-bar": "Use compact style for menu bar", - "compact-queue-bar": "Use compact style for queue bar", - "developer": "Developer settings", - "devtools": "Developer tools", - "disable-gpu": "Disable hardware rendering (might fix issues with dragging elements and flashing screen)", + "listening-history": "تاريخ الاستماع", + "listening-history-description": "سجل المسارات التي تستمع إليها، لا Last.fm. يتم تخزين السجل دون اتصال.", + "compact-menu-bar": "استخدام النمط المدمج لشريط القائمة", + "compact-queue-bar": "استخدام النمط المدمج لقائمة الانتظار", + "developer": "اعدادات المطور", + "devtools": "أدوات المطورين", + "disable-gpu": "تعطيل عرض الأجهزة (قد يصلح المشكلات مع عناصر السحب والشاشة الوامضة)", "display": "الشاشة", "downloads": "التنزيلات", - "downloads-count": "Max simultaneous downloads", - "downloads-dir": "Downloads directory", - "downloads-dir-button": "Choose a directory...", + "downloads-count": "الحد الأقصى للتنزيلات المتزامنة", + "downloads-dir": "مجلد التحميلات", + "downloads-dir-button": "اختر مجلدا...", "enable-api": "تمكين الـAPI", "fmfav-btn": "استيراد", - "fmfav-msg": "Import your Last.fm Favorites", - "frameless-window": "Frameless window (requires restart)", + "fmfav-msg": "استيراد تفضيلاتك من Last.fm", + "frameless-window": "نافذة لا إطار لها (يتطلب إعادة تشغيل)", "github-connect": "تسجيل الدخول باستخدام الـGitHub", - "github-description": "Log in via Github to be able to create and share your playlists online (upcoming feature).", + "github-description": "قم بتسجيل الدخول عبر Github لتتمكن من إنشاء ومشاركة قوائم التشغيل الخاصة بك على الإنترنت (مِيزة قادمة).", "github-title": "Github", "http": "API الـHTTP", - "invidious-url": "Invidious instance url", + "invidious-url": "رابط مخدم Invidious", "language": "اللّغة", "language-placeholder": "اختيار اللغة", - "lastfm-connect": "Connect with Last.fm", - "lastfm-description": "In order to enable scrobbling, you first have to connect and authorize Nuclear on Last.fm, then click log in.", - "lastfm-enable": "Enable scrobbling to Last.fm", + "lastfm-connect": "الاتصال بواسطة Last.fm", + "lastfm-description": "لتفعيل نظام الخربشة، يجب إعطاء أذون Nuclear لاستخدام Last.fm، و من ثم الضغط على \"تسجيل الدخول\".", + "lastfm-enable": "تفعيل نظام الخربشة على Last.fm", "lastfm-title": "Last.fm", "less": "أقل", "login": "تسجيل الدخول", "logout": "تسجيل الخروج", "loop-after-queue-end": "تكرار بعد تشغيل آخر عنصر في قائمة الانتظار", - "mastodon-authorization-token-label": "Authorization token", + "mastodon-authorization-token-label": "رمز التفويض", "mastodon-authorize": "التصريح", - "mastodon-authorized": "Nuclear is authorized to post on ", - "mastodon-awaiting-authorization": "Nuclear is registered on {{instanceUrl}}. Awaiting authorization.", + "mastodon-authorized": "يؤذن للنووي بأن ينشر على ", + "mastodon-awaiting-authorization": "نووي مسجل في {{instanceUrl}}. بانتظار الحصول على ترخيص.", "mastodon-description": "", - "mastodon-instance-label": "Mastodon instance URL", - "mastodon-post-format-description": "Nuclear will post a status on Mastodon after each track completes playing. The above string will be the template for each post, with {{artist}}, {{title}}, and {{url}} replaced with the artist, title, and a link to the track respectively.", - "mastodon-post-format-label": "Post format", + "mastodon-instance-label": "رابط خادم Mastodon", + "mastodon-post-format-description": "سينشر Nuclear حالة على Mastodon بعد الاستماع إلى كل غنية. إن السلسلة المعروضة أعلاه هي قالب لكل منشور، حيث يبدل كل من {{artist}}، {{title}}، و {{url}} بالمطرب، اسم الأغنية، و الرابط لها على التوالي.", + "mastodon-post-format-label": "تنسيق المنشور", "mastodon-title": "", - "normalize": "Normalize volume", - "normalize-description": "Automatically adjust volume of tracks so that they are played at the same level. This needs to fetch the whole track to work, so it may cause a delay between tracks.", - "nuclear-identity-service-url": "Nuclear identity service URL", - "nuclear-playlists-service-url": "Nuclear playlists service URL", - "mini-player": "Use mini player style", + "normalize": "تطبيع مستوى الصوت", + "normalize-description": "ضبط مستوى الصوت حيث يكون متساو في كل الأغاني. هذه المِيزة تحتاج إلى تحميل كل الأغنية لكي تعمل مما يؤدي إلى وجود فاصل زمني بين الأغاني.", + "nuclear-identity-service-url": "رابط خدمة هُوِيَّة Nuclear", + "nuclear-playlists-service-url": "رابط خدمة قوائم تشغيل Nuclear", + "mini-player": "استعمل الشكل المصغر لمشغل الموسيقى", "more": "المزيد", - "discord-rich-presence": "Toggle discord rich presence (requires restart)", - "notification-timeout": "Notification timeout", + "discord-rich-presence": "تفعيل وجود الغني ل Discord (يحتاج إلى إعادة تشغيل البرنامَج)", + "notification-timeout": "مهلة الإشعارات", "notlogged": "ليس مسجيل لالدخول", "playback": "التشغيل", "program-settings": "إعدادات البرنامج", - "promoted-artists": "Show promoted artists in the dashboard", - "promoted-artists-description": "This is a reel of interesting, handpicked indie artists you might like. No one's receiving any compensation for this.", - "saving-in": "Saving in:", - "seek-iteration": "Number of seconds to seek forward/backwards when pressing arrow keys", - "show-tray-icon": "Show tray icon", - "shuffle-queue": "Shuffle songs", - "shuffle-when-going-back": "Shuffle on previous track", - "shuffle-when-going-back-description": "Play a random track when shuffle is active and the previous track button is clicked", + "promoted-artists": "إظهار المطربين المروجون في الواجهة", + "promoted-artists-description": "هذه قائمة مختارة يدوياً لفنانين إيندي (فنانين مستقلين) قد تعجبك. لا يتلقى أحد تعويض عن هذه القائمة.", + "saving-in": "الحفظ في مجلد:", + "seek-iteration": "عدد الثواني المستخدم للتقديم/التأخير عند استعمال مفاتيح الأسهم", + "show-tray-icon": "إظهار الأيقونة المصغرة", + "shuffle-queue": "خلْط الأغاني", + "shuffle-when-going-back": "الخلط على الغنية السابقة", + "shuffle-when-going-back-description": "تشغيل أغنية عشوائية عند تفعيل مِيزة الخلط و النقر على زر تشغيل الأغنية السابقة", "social": "روابط", "skip-sponsorblock": "تخطي القطاع غير الموسيقي", "skip-sponsorblock-description": "تخطي القطاع غير الموسيقي من SponsorBlock (https://sponsor.ajay.app/)", - "streaming": "Streaming", - "track-duration": "Display track duration over the seekbar", - "user": "User:", - "user-id": "User ID", - "use-stream-verification": "Use stream verification", - "use-stream-verification-description": "Use stream verification. Loads the streams the community has verified as being correct for the tracks you play. Also allows you to vote on streams. See documentation for details.", + "streaming": "البث", + "track-duration": "إظهار مدة الأغنية على شريط البحث", + "user": "المستخدم:", + "user-id": "هُوِيَّة المستخدم", + "use-stream-verification": "استعمال تحقق البث", + "use-stream-verification-description": "استعمال تحقق البث. يتم تحميل بث الأغاني المؤكد الاستماع لها من قبل الجُمهور. يمكنك تصويت الأغاني أيضاً. راجع المرجع لمعرفة التفاصيل.", "youtube": "YouTube", "yt-api-key": "مفتاح API تبع اليوتيوب" }, @@ -351,11 +352,11 @@ "add-to-favorite": "إضافة إلى المفضلات", "add-to-playlist": "إضافة إلى قائمة التشغيل", "add-to-queue": "إضافة إلى قائمة الانتظار", - "create-playlist": "Create new playlist", - "create-playlist-dialog-title": "Input playlist name:", - "create-playlist-dialog-placeholder": "Playlist name...", - "create-playlist-dialog-accept": "Save", - "create-playlist-dialog-cancel": "Cancel", + "create-playlist": "إنشاء قائمة تشغيل جديدة", + "create-playlist-dialog-title": "أدخل اسم قائمة الأغاني:", + "create-playlist-dialog-placeholder": "اسم قائمة الأغاني...", + "create-playlist-dialog-accept": "حفظ", + "create-playlist-dialog-cancel": "إلغاء", "download": "تنزيل", "download-toast-body": "{{artist}} - {{track}} تم إضافته إلى التنزيلات.", "download-toast-title": "تمت اضافة ا الاغنية الى التنزيلات", @@ -364,79 +365,79 @@ "play-next": "شغل التالي", "play-now": "شغيل الآن", "playlist-toast-body": "{{artist}} - {{track}} تم إضافته إلى قائمة التشغيل.", - "playlist-toast-title": "Track added to playlist" + "playlist-toast-title": "تم إضافة الأغنية إلى القائمة" }, "track-table": { - "add-selected-tracks-to-queue": "Add selected to queue", - "add-selected-tracks-to-downloads": "Add selected to downloads", - "add-selected-tracks-to-favorites": "Add selected to favorites", - "play-selected-tracks-now": "Play selected now", - "tracks-selected-label-singular": "track selected", - "tracks-selected-label-plural": "tracks selected" + "add-selected-tracks-to-queue": "إضافة المحدد إلى قائمة التشغيل", + "add-selected-tracks-to-downloads": "إضافة المحدد إلى قائمة التنزيلات", + "add-selected-tracks-to-favorites": "إضافة المحدد إلى المفضلة", + "play-selected-tracks-now": "تشغيل المحدد الآن", + "tracks-selected-label-singular": "الأغنية المحددة", + "tracks-selected-label-plural": "الأغاني المحددة" }, "visualizer": { - "exit-fullscreen": "Press ESC to exit full screen" + "exit-fullscreen": "اضغط ESC للخروج من ملء الشاشة" }, "command-palette": { - "search-placeholder": "What would you like to do?", - "empty-state-help": "Can't seem to find what you're looking for? Try the help command.", - "protip-text": "PROTIP:", - "protip-content": "Arrows to navigate, Enter to select, Esc to close", + "search-placeholder": "ماذا تريد أن تفعل؟", + "empty-state-help": "لم تجد الذي تبحث عنه؟ استعمل أمر المساعدة.", + "protip-text": "نصيحة:", + "protip-content": "الأسهم للتنقل، Enter للاختيار، Esc للخروج", "categories": { - "playback": "Playback", - "queue": "Queue", - "navigation": "Navigation", - "application": "Application" + "playback": "التشغيل", + "queue": "قائمة الانتظار", + "navigation": "التنقل", + "application": "التطبيق" }, "actions": { - "play": "Play", - "pause": "Pause", - "next": "Next", - "previous": "Previous", - "go-to-next-page": "Go to next page", - "go-to-previous-page": "Go to previous page", - "shuffle": "Shuffle", - "loop": "Loop", - "autoradio": "Autoradio", - "raise-volume": "Raise volume", - "lower-volume": "Lower volume", - "mute": "Mute", - "unmute": "Unmute", - "quit": "Quit", - "minimize": "Minimize", - "maximize": "Maximize", - "go-to-dashboard": "Go to dashboard", - "go-to-downloads": "Go to downloads", - "go-to-lyrics": "Go to lyrics", - "go-to-plugins": "Go to plugins", - "go-to-search": "Go to search", - "go-to-settings": "Go to settings", - "go-to-equalizer": "Go to equalizer", - "go-to-visualizer": "Go to visualizer", - "go-to-playlists": "Go to playlists", - "go-to-favorite-tracks": "Go to favorite tracks", - "go-to-library": "Go to local library" + "play": "تشغيل", + "pause": "إيقاف مؤقت", + "next": "التالي", + "previous": "السابق", + "go-to-next-page": "اذهب للصفحة التالية", + "go-to-previous-page": "انتقل إلى الصفحة السابقة", + "shuffle": "الخلط", + "loop": "تكرار الأغنية", + "autoradio": "راديو تلقائي", + "raise-volume": "رفع الصوت", + "lower-volume": "خفض الصوت", + "mute": "كتم الصوت", + "unmute": "إلغاء كتم الصوت", + "quit": "اخرج", + "minimize": "صغّر", + "maximize": "املئ الشاشه", + "go-to-dashboard": "اذهب إلى اللوحة الرئيسة", + "go-to-downloads": "انتقل إلى التنزيلات", + "go-to-lyrics": "انتقل إلى كلمات الأغنية", + "go-to-plugins": "اذهب إلى الإضافات", + "go-to-search": "اذهب إلى البحث", + "go-to-settings": "اذهب للإعدادات", + "go-to-equalizer": "اذهب إلى معدِّل الصوت", + "go-to-visualizer": "اذهب إلى المتخيل", + "go-to-playlists": "اذهب إلى قوائم التشغيل", + "go-to-favorite-tracks": "اذهب إلى قائمة الأغاني المفضلة", + "go-to-library": "اذهب إلى المكتبة المحلية" } }, "listening-history": { - "title": "Listening history", - "empty-state": "You haven't listened to anything yet.", - "clear-history": "Clear history", - "clear-history-confirm": "Are you sure you want to clear your listening history?", - "clear-history-confirm-yes": "Confirm", - "clear-history-confirm-no": "Cancel", - "clear-history-toast": "Listening history cleared" + "title": "سجل الاستماع", + "empty-state": "لم تستمع لشيئ بعد.", + "clear-history": "محو السجل", + "clear-history-confirm": "هل ترغب حقّاً في إزالة سجل الاستماع؟", + "clear-history-confirm-yes": "تأكيد", + "clear-history-confirm-no": "إلغاء", + "clear-history-toast": "تم مسح سجل الاستماع" }, "forms": { "nuclear-sign-up": { - "header": "Sign up", - "secondary-header": "Sign up to Nuclear Web Services", - "side-paragraph-1": "NWS enables you to backup your playlists online and share them.", - "side-paragraph-2": "Providing your email is optional; it will allow you to recover your account if you forget your password.", - "username-label": "Username", - "email-label": "Email", - "password-label": "Password", - "sign-up-button": "Sign up", + "header": "إنشاء حساب", + "secondary-header": "سجِّل في خِدْمَات Nuclear الويب", + "side-paragraph-1": "يمكِّنك NWS من النسخ الاحتياطي لقائمة التشغيلات على الإنترنت و مشاركتها.", + "side-paragraph-2": "إدخال بريدك الإلكتروني هو غير إلزامي؛ يتيح لك إدخال بريدك الإلكتروني استعادة حسابك عند نسيان كلمة السر.", + "username-label": "اسم المستخدم", + "email-label": "بريدك الإلكتروني", + "password-label": "كلمة السر", + "sign-up-button": "إنشاء حساب", "validation": { "username": { "length": "يجب أن يكون اسم المستخدم 4 أحرف أو أكثر", diff --git a/packages/i18n/src/locales/be_BY.json b/packages/i18n/src/locales/be_BY.json index 1dfa6c3700..cf4bfdfd39 100644 --- a/packages/i18n/src/locales/be_BY.json +++ b/packages/i18n/src/locales/be_BY.json @@ -97,7 +97,7 @@ "empty": "Выбраныя ня дададзеныя", "empty-help": "Паспрабуйце дадаць некалькі трэкаў у абранае, і яны з'явяцца тут!", "header": "Абраныя трэкі", - "play-random": "Play a random track from your favorites", + "play-random": "Прайграць выпадковы трэк з вашых абраных", "title": "Назва" }, "help": { @@ -127,6 +127,7 @@ "not-found": "Тэкст гэтай песні не быў знойдзены." }, "option-control": { + "rate": "Хуткасць прайгравання", "autoradio": "Аўтарадыё", "loop": "Паўтор", "mini-player": "Міні-плэер", @@ -220,21 +221,21 @@ "live": "У прамым эфіры", "stream-verification": { "tooltip": { - "unknown": "The status of this stream is unknown.", - "unverified": "This stream has not yet been verified by the community.", - "weakly-verified": "This stream has been verified by a couple of users.", - "verified": "This stream has been verified by the community.", - "verified-by-user": "This stream has been verified by you." + "unknown": "Статус гэтага стрыму невядомы.", + "unverified": "Гэты стрым яшчэ не быў пацверджаны супольнасцю.", + "weakly-verified": "Гэты стрым быў правераны нямногімі карыстальнікамі.", + "verified": "Гэты стрым быў пацверджаны супольнасцю.", + "verified-by-user": "Гэты стрым быў пацверджаны вамі." }, "stream-status": { - "unknown": "Unknown", - "unverified": "Unverified", - "weakly-verified": "Weakly verified", - "verified": "Verified", - "verified-by-user": "Verified by you" + "unknown": "Невядома", + "unverified": "Непацверджаны", + "weakly-verified": "Слаба пацверджаны", + "verified": "Пацверджаны", + "verified-by-user": "Пацверджаны вамі" }, - "verify": "Verify", - "unverify": "Unverify" + "verify": "Пацвердзіць", + "unverify": "Прыбраць пацвердженіе" } }, "search": { @@ -323,7 +324,7 @@ "promoted-artists-description": "Гэта падборка цікавых, адабраных ўручную індзі-выканаўцаў, якія могуць вам спадабацца. Ніхто не атрымае за гэта ніякай кампенсацыі.", "saving-in": "Будзе захавана ў:", "seek-iteration": "Колькасць секунд для пошуку наперад / назад пры націску клавіш са стрэлкамі", - "show-tray-icon": "Show tray icon", + "show-tray-icon": "Паказаць значок у латку", "shuffle-queue": "Змяшаць трэкi", "shuffle-when-going-back": "Перакартоўка на папярэднім трэку", "shuffle-when-going-back-description": "Прайграваць выпадковы трэк пры актыўным мяшанні і націску кнопкі папярэдняга трэка", @@ -333,9 +334,9 @@ "streaming": "Трансляцыі", "track-duration": "Адлюстраванне працягласці трэка на панэлі пошуку", "user": "Карыстальнік:", - "user-id": "User ID", - "use-stream-verification": "Use stream verification", - "use-stream-verification-description": "Use stream verification. Loads the streams the community has verified as being correct for the tracks you play. Also allows you to vote on streams. See documentation for details.", + "user-id": "ID Карыстальніка", + "use-stream-verification": "Выкарыстоўваць пацвярджэнне стрыму", + "use-stream-verification-description": "Выкарыстоўваць пацвярджэнне стрыму. Загружае стрымы, якія былі пацверджаны супольнасцю як правільны для трэкаў, якія вы праграваеце. Глядзіце дакументацыю для дэталей.", "youtube": "YouTube", "yt-api-key": "YouTube API ключ" }, diff --git a/packages/i18n/src/locales/bn_BD.json b/packages/i18n/src/locales/bn_BD.json index 05e92690a1..e37f3feed1 100644 --- a/packages/i18n/src/locales/bn_BD.json +++ b/packages/i18n/src/locales/bn_BD.json @@ -127,6 +127,7 @@ "not-found": "এই গানের জন্য কোনো লিরিক্স পাওয়া জায়নি।" }, "option-control": { + "rate": "Playback speed", "autoradio": "অটোরেডিও", "loop": "লুপ", "mini-player": "মিনিপ্লেয়ার", diff --git a/packages/i18n/src/locales/cs.json b/packages/i18n/src/locales/cs.json index 12e3ead0fb..06bd6c8bfa 100644 --- a/packages/i18n/src/locales/cs.json +++ b/packages/i18n/src/locales/cs.json @@ -127,6 +127,7 @@ "not-found": "Pro tuto skladbu nebyly nalezeny žádné texty." }, "option-control": { + "rate": "Rychlost přehrávání", "autoradio": "Autorádio", "loop": "Smyčka", "mini-player": "Minipřehrávač", diff --git a/packages/i18n/src/locales/de.json b/packages/i18n/src/locales/de.json index aea57a453c..7904e41a64 100644 --- a/packages/i18n/src/locales/de.json +++ b/packages/i18n/src/locales/de.json @@ -97,7 +97,7 @@ "empty": "Keine Favoriten hinzugefügt", "empty-help": "Versuche ein paar Lieder hinzuzufügen, dann werden sie hier auftauchen!", "header": "Deine Lieblingslieder", - "play-random": "Play a random track from your favorites", + "play-random": "Wiedergabe eines zufälligen Titels aus Ihren Favoriten", "title": "Titel" }, "help": { @@ -127,6 +127,7 @@ "not-found": "Keine Liedtexte für diesen Song gefunden." }, "option-control": { + "rate": "Wiedergabegeschwindigkeit", "autoradio": "Autoradio", "loop": "Schleife", "mini-player": "Miniplayer", @@ -220,21 +221,21 @@ "live": "Live", "stream-verification": { "tooltip": { - "unknown": "The status of this stream is unknown.", - "unverified": "This stream has not yet been verified by the community.", - "weakly-verified": "This stream has been verified by a couple of users.", - "verified": "This stream has been verified by the community.", - "verified-by-user": "This stream has been verified by you." + "unknown": "Der Status von dieser Stream ist unbekannt.", + "unverified": "Dieser Stream wurde noch nicht von der Gemeinschaft überprüft.", + "weakly-verified": "Dieser Stream wurde von einigen Nutzern verifiziert.", + "verified": "Dieser Stream wurde von der Community überprüft.", + "verified-by-user": "Dieser Stream wurde von Ihnen verifiziert." }, "stream-status": { - "unknown": "Unknown", - "unverified": "Unverified", - "weakly-verified": "Weakly verified", - "verified": "Verified", - "verified-by-user": "Verified by you" + "unknown": "Unbekannt", + "unverified": "Unbestätigt", + "weakly-verified": "Schwach verifiziert", + "verified": "Verifiziert", + "verified-by-user": "Verifiziert durch Sie" }, - "verify": "Verify", - "unverify": "Unverify" + "verify": "Verifizieren", + "unverify": "Bestätigung entfernen" } }, "search": { @@ -305,7 +306,7 @@ "mastodon-awaiting-authorization": "Nuclear ist auf {{instanceUrl}} registriert. Warte auf Autorisierung.", "mastodon-description": "", "mastodon-instance-label": "Mastodon-Instanz-URL", - "mastodon-post-format-description": "Nuclear will post a status on Mastodon after each track completes playing. The above string will be the template for each post, with {{artist}}, {{title}}, and {{url}} replaced with the artist, title, and a link to the track respectively.", + "mastodon-post-format-description": "Nuclear wird einen Status auf Mastodon posten, nachdem jeder Track fertig abgespielt wurde. Die obige Zeichenkette wird die Vorlage für jeden Beitrag sein, wobei {{artist}}, {{title}} und {{url}} jeweils durch den Künstler, den Titel und einen Link zum Track ersetzt werden.", "mastodon-post-format-label": "Beitragsformat", "mastodon-title": "", "normalize": "Lautstärke normalisieren", @@ -323,7 +324,7 @@ "promoted-artists-description": "Dies ist eine Rolle von interessanten, handverlesenen Indie-Künstlern, die Ihnen gefallen könnten. Niemand erhält dafür eine Vergütung.", "saving-in": "Speichern in:", "seek-iteration": "Anzahl der Sekunden, die beim Drücken der Pfeiltasten vorwärts/rückwärts suchen", - "show-tray-icon": "Show tray icon", + "show-tray-icon": "Taskleistensymbol anzeigen", "shuffle-queue": "Titel mischen", "shuffle-when-going-back": "Shuffle von vorherigen Liedern", "shuffle-when-going-back-description": "Spiele zufälliges Lied, wenn Zufallswiedergabe aktiv ist und der vorherige Titel Button geklickt wird", @@ -333,9 +334,9 @@ "streaming": "Streamen", "track-duration": "Laufzeit über der Suchleiste anzeigen", "user": "Benutzer:", - "user-id": "User ID", - "use-stream-verification": "Use stream verification", - "use-stream-verification-description": "Use stream verification. Loads the streams the community has verified as being correct for the tracks you play. Also allows you to vote on streams. See documentation for details.", + "user-id": "Benutzer ID", + "use-stream-verification": "Stream-Überprüfung verwenden", + "use-stream-verification-description": "Stream-Überprüfung verwenden. Lädt die Streams, die von der Community als korrekt für die von Ihnen gespielten Titel bestätigt wurden. Außerdem können Sie über Streams abstimmen. Siehe Dokumentation für Details.", "youtube": "YouTube", "yt-api-key": "YouTube API-Schlüssel" }, diff --git a/packages/i18n/src/locales/dk.json b/packages/i18n/src/locales/dk.json index 528954e446..be3b6e79c9 100644 --- a/packages/i18n/src/locales/dk.json +++ b/packages/i18n/src/locales/dk.json @@ -127,6 +127,7 @@ "not-found": "Ingen sangtekst blev fundet til denne sang." }, "option-control": { + "rate": "Playback speed", "autoradio": "Autoradio", "loop": "Gentag", "mini-player": "Miniafspiller", diff --git a/packages/i18n/src/locales/es.json b/packages/i18n/src/locales/es.json index d479625034..0bd2f1cbd9 100644 --- a/packages/i18n/src/locales/es.json +++ b/packages/i18n/src/locales/es.json @@ -59,7 +59,7 @@ "trending-artists": "Artistas de moda", "trending-albums": "Álbumes populares", "filter": "Filtrar...", - "nothing-found": "Nada encontrado." + "nothing-found": "No se ha encontrado nada." }, "downloads": { "clear": "Borrar pistas completadas", @@ -97,7 +97,7 @@ "empty": "No hay favoritos añadidos", "empty-help": "¡Prueba añadir algunas pistas a favoritos y aparecerán aquí!", "header": "Tus pistas favoritas", - "play-random": "Play a random track from your favorites", + "play-random": "Reproducir una pista aleatoria de tus favoritos", "title": "Título" }, "help": { @@ -127,6 +127,7 @@ "not-found": "No se ha encontrado ninguna letra para esta canción." }, "option-control": { + "rate": "Velocidad de reproducción", "autoradio": "Autoradio", "loop": "Bucle", "mini-player": "Minireproductor", @@ -220,21 +221,21 @@ "live": "En directo", "stream-verification": { "tooltip": { - "unknown": "The status of this stream is unknown.", - "unverified": "This stream has not yet been verified by the community.", - "weakly-verified": "This stream has been verified by a couple of users.", - "verified": "This stream has been verified by the community.", - "verified-by-user": "This stream has been verified by you." + "unknown": "El estado de este origen es desconocido.", + "unverified": "Esta transmisión aún no ha sido verificada por la comunidad.", + "weakly-verified": "Esta transmisión ha sido verificada por un par de usuarios.", + "verified": "Esta transmisión ha sido verificada por la comunidad.", + "verified-by-user": "Esta transmisión ha sido verificada por usted." }, "stream-status": { - "unknown": "Unknown", - "unverified": "Unverified", - "weakly-verified": "Weakly verified", - "verified": "Verified", - "verified-by-user": "Verified by you" + "unknown": "Desconocido", + "unverified": "Sin verificar", + "weakly-verified": "Poco verificado", + "verified": "Verificado", + "verified-by-user": "Verificado por ti" }, - "verify": "Verify", - "unverify": "Unverify" + "verify": "Verificar", + "unverify": "No verificar" } }, "search": { @@ -323,7 +324,7 @@ "promoted-artists-description": "Este es un reel de algunos artistas seleccionados que te podrían gustar. Nadie recibe alguna compensación por ello.", "saving-in": "Guardando en:", "seek-iteration": "Cantidad de segundos a desplazar al presionar las flechas direccionales", - "show-tray-icon": "Show tray icon", + "show-tray-icon": "Mostrar icono de la bandeja", "shuffle-queue": "Reproducción aleatoria", "shuffle-when-going-back": "Aleatorizar en pista previa", "shuffle-when-going-back-description": "Reproduce una pista aleatoria cuando está activada la reproducción aleatoria y se presiona el botón de pista previa", @@ -333,9 +334,9 @@ "streaming": "Transmisión", "track-duration": "Mostrar duración de la pista sobre la barra de búsqueda", "user": "Usuario:", - "user-id": "User ID", - "use-stream-verification": "Use stream verification", - "use-stream-verification-description": "Use stream verification. Loads the streams the community has verified as being correct for the tracks you play. Also allows you to vote on streams. See documentation for details.", + "user-id": "ID de Usuario", + "use-stream-verification": "Usar verificación de transmisión", + "use-stream-verification-description": "Utilizar verificación de flujos. Carga los streams que la comunidad ha verificado como correctos para las pistas que juegas. También le permite votar sobre los streams. Consulte la documentación para más detalles.", "youtube": "YouTube", "yt-api-key": "Clave de la API de YouTube" }, diff --git a/packages/i18n/src/locales/fa_IR.json b/packages/i18n/src/locales/fa_IR.json index 37c2040fd3..a142ca28b2 100644 --- a/packages/i18n/src/locales/fa_IR.json +++ b/packages/i18n/src/locales/fa_IR.json @@ -127,6 +127,7 @@ "not-found": "No lyrics were found for this song." }, "option-control": { + "rate": "Playback speed", "autoradio": "Autoradio", "loop": "Loop", "mini-player": "Miniplayer", diff --git a/packages/i18n/src/locales/fi.json b/packages/i18n/src/locales/fi.json index 42e613549d..a39e739677 100644 --- a/packages/i18n/src/locales/fi.json +++ b/packages/i18n/src/locales/fi.json @@ -97,7 +97,7 @@ "empty": "Ei suosikkeja lisätty", "empty-help": "Kokeile lisätä joitakin kappaleita suosikeihin ja ne näkyvät täällä!", "header": "Suosikki kappaleesi", - "play-random": "Play a random track from your favorites", + "play-random": "Toista satunnainen kappale suosikeistasi", "title": "Nimi" }, "help": { @@ -127,6 +127,7 @@ "not-found": "Sanoituksia ei löytynyt tähän kappaleeseen." }, "option-control": { + "rate": "Toiston nopeus", "autoradio": "Automaattinen radio", "loop": "Uudelleentoisto", "mini-player": "Minisoitin", @@ -220,21 +221,21 @@ "live": "Suora", "stream-verification": { "tooltip": { - "unknown": "The status of this stream is unknown.", - "unverified": "This stream has not yet been verified by the community.", - "weakly-verified": "This stream has been verified by a couple of users.", - "verified": "This stream has been verified by the community.", - "verified-by-user": "This stream has been verified by you." + "unknown": "Tämän streamin tila on tuntematon.", + "unverified": "Tämä stream ei ole vielä yhteisön vahvistama.", + "weakly-verified": "Tämä stream on muutaman käyttäjän vahvistama.", + "verified": "Tämä stream on yhteisön vahvistama.", + "verified-by-user": "Olet vahvistanut tämän streamin." }, "stream-status": { - "unknown": "Unknown", - "unverified": "Unverified", - "weakly-verified": "Weakly verified", - "verified": "Verified", - "verified-by-user": "Verified by you" + "unknown": "Tuntematon", + "unverified": "Vahvistamaton", + "weakly-verified": "Heikosti vahvistettu", + "verified": "Vahvistettu", + "verified-by-user": "Sinun vahvistamasi" }, - "verify": "Verify", - "unverify": "Unverify" + "verify": "Vahvista", + "unverify": "Poista vahvistus" } }, "search": { @@ -305,7 +306,7 @@ "mastodon-awaiting-authorization": "Nuclear on rekisteröity {{instanceUrl}}. Odottaa valtuutusta.", "mastodon-description": "", "mastodon-instance-label": "Mastodon-instanssin osoite", - "mastodon-post-format-description": "Nuclear will post a status on Mastodon after each track completes playing. The above string will be the template for each post, with {{artist}}, {{title}}, and {{url}} replaced with the artist, title, and a link to the track respectively.", + "mastodon-post-format-description": "Nuclear lähettää Mastodoniin tilan jokaisen kappaleen jälkeen. Yllä oleva merkkijono on malli jokaiselle viestille, {{artist}}, {{title}} ja {{url}} korvattiin esittäjällä, kappaleen otsikolla ja linkillä.", "mastodon-post-format-label": "Julkaisun muoto", "mastodon-title": "", "normalize": "Normalisoi äänenvoimakkuus", @@ -323,7 +324,7 @@ "promoted-artists-description": "Tämä on jatkumo kiinnostavia, valikoituja itsenäisiä artisteja joista saattaisit pitää. Kukaan ei saa tästä mitään korvauksia.", "saving-in": "Tallennetaan:", "seek-iteration": "Eteen/taaksepäin etsimisen sekuntien lukumäärä nuolinäppäimiä painettaessa", - "show-tray-icon": "Show tray icon", + "show-tray-icon": "Näytä ilmoitusalueen kuvake", "shuffle-queue": "Sekoita kappaleet", "shuffle-when-going-back": "Sekoita edellinen kappale napilla", "shuffle-when-going-back-description": "Soita satunnainen kappale kun sekoitus on päällä ja edellinen kappale nappia on painettu", @@ -333,9 +334,9 @@ "streaming": "Suoratoisto", "track-duration": "Näytä kappaleen pituus edistymispalkissa", "user": "Käyttäjä:", - "user-id": "User ID", - "use-stream-verification": "Use stream verification", - "use-stream-verification-description": "Use stream verification. Loads the streams the community has verified as being correct for the tracks you play. Also allows you to vote on streams. See documentation for details.", + "user-id": "Käyttäjän ID", + "use-stream-verification": "Käytä streamin vahvistusta", + "use-stream-verification-description": "Käytä streamin varmennusta. Lataa yhteisön varmentamat streamit, jotka ovat osoittautuneet oikeiksi soittamiesi kappaleiden osalta. Mahdollistaa myös äänestämisen streamien suhteen. Katso lisätietoja dokumentaatiosta.", "youtube": "YouTube", "yt-api-key": "Youtube API Avain" }, diff --git a/packages/i18n/src/locales/fr.json b/packages/i18n/src/locales/fr.json index c1847a526a..6c2e99eed5 100644 --- a/packages/i18n/src/locales/fr.json +++ b/packages/i18n/src/locales/fr.json @@ -47,7 +47,7 @@ "artist": "Artiste", "best": "Nouveautés", "best-new-albums": "Meilleurs albums", - "best-new-tracks": "Meilleurs nouveaux morceaux", + "best-new-tracks": "Meilleurs morceaux récents", "genres": "Genres", "news": "Actualités", "playcounts": "Nombre d'écoute", @@ -97,7 +97,7 @@ "empty": "Aucun favoris", "empty-help": "Essayez d'ajouter quelques morceaux à vos favoris et ils apparaîtront ici !", "header": "Vos morceaux favoris", - "play-random": "Play a random track from your favorites", + "play-random": "Jouer un morceau aléatoire à partir de vos favoris", "title": "Titre" }, "help": { @@ -127,6 +127,7 @@ "not-found": "Aucune paroles n'ont été trouvées pour ce morceau." }, "option-control": { + "rate": "Vitesse de lecture", "autoradio": "Auto-radio", "loop": "Boucle", "mini-player": "Mini-lecteur", @@ -220,21 +221,21 @@ "live": "En direct", "stream-verification": { "tooltip": { - "unknown": "The status of this stream is unknown.", - "unverified": "This stream has not yet been verified by the community.", - "weakly-verified": "This stream has been verified by a couple of users.", - "verified": "This stream has been verified by the community.", - "verified-by-user": "This stream has been verified by you." + "unknown": "L'état de ce flux est inconnu.", + "unverified": "Ce flux n'a pas encore été vérifié par la communauté.", + "weakly-verified": "Ce flux n'a été vérifié que par quelques utilisateurs.", + "verified": "Ce flux a été vérifié par la communauté.", + "verified-by-user": "Ce flux a été vérifié par vous." }, "stream-status": { - "unknown": "Unknown", - "unverified": "Unverified", - "weakly-verified": "Weakly verified", - "verified": "Verified", - "verified-by-user": "Verified by you" + "unknown": "Inconnu", + "unverified": "Non vérifié", + "weakly-verified": "Faiblement vérifié", + "verified": "Vérifié", + "verified-by-user": "Vérifié par vous" }, - "verify": "Verify", - "unverify": "Unverify" + "verify": "Vérifier", + "unverify": "Ne plus vérifier" } }, "search": { @@ -323,7 +324,7 @@ "promoted-artists-description": "Il s'agit d'un ensemble d'artistes indie que vous pourriez aimer et choisis sur le volet. Personne ne recevant aucune compensation pour cela.", "saving-in": "Enregistrer dans :", "seek-iteration": "Nombre de seconde à avancer/reculer en appuyant sur les flèches", - "show-tray-icon": "Show tray icon", + "show-tray-icon": "Afficher l'icône dans la barre des tâches", "shuffle-queue": "Ordre aléatoire", "shuffle-when-going-back": "Mélanger les morceaux précédents", "shuffle-when-going-back-description": "Joue un morceau aléatoire lorsque le bouton de morceau précédent est cliqué", @@ -333,9 +334,9 @@ "streaming": "Diffusion", "track-duration": "Afficher la durée du morceau sur la barre d'avancement de lecture", "user": "Utilisateur :", - "user-id": "User ID", - "use-stream-verification": "Use stream verification", - "use-stream-verification-description": "Use stream verification. Loads the streams the community has verified as being correct for the tracks you play. Also allows you to vote on streams. See documentation for details.", + "user-id": "Identifiant de l'utilisateur", + "use-stream-verification": "Utiliser la vérification de flux", + "use-stream-verification-description": "Utilisez la vérification de flux. Charge les flux que la communauté a vérifiés comme étant corrects pour les musiques que vous jouez. Permet également de voter sur les flux. Consultez la documentation pour plus de détails.", "youtube": "YouTube", "yt-api-key": "Clé d'API YouTube" }, diff --git a/packages/i18n/src/locales/gr.json b/packages/i18n/src/locales/gr.json index 21af97397d..b43b41dc77 100644 --- a/packages/i18n/src/locales/gr.json +++ b/packages/i18n/src/locales/gr.json @@ -127,6 +127,7 @@ "not-found": "Δεν βρέθηκαν στοίχοι για αυτό το κομμάτι." }, "option-control": { + "rate": "Playback speed", "autoradio": "Αυτόματο ράδιο", "loop": "Επανάληψη", "mini-player": "Μίνι Παίκτης", diff --git a/packages/i18n/src/locales/he_IL.json b/packages/i18n/src/locales/he_IL.json index da7fe5cb6c..075cbe8cd9 100644 --- a/packages/i18n/src/locales/he_IL.json +++ b/packages/i18n/src/locales/he_IL.json @@ -127,6 +127,7 @@ "not-found": "לא נמצאו מילות שיר לשיר הזה." }, "option-control": { + "rate": "Playback speed", "autoradio": "רדיו אוטומטי", "loop": "חזרה", "mini-player": "נגן ממוזער", diff --git a/packages/i18n/src/locales/hi_IN.json b/packages/i18n/src/locales/hi_IN.json index d3fe952d8b..c4ab813aeb 100644 --- a/packages/i18n/src/locales/hi_IN.json +++ b/packages/i18n/src/locales/hi_IN.json @@ -19,7 +19,7 @@ "favorite-artists": "पसंदीदा कलाकार", "favorite-tracks": "पसंदीदा ट्रैक", "library": "स्थानीय संग्रह", - "listening-history": "Listening History", + "listening-history": "सुनने का इतिहास", "lyrics": "गीतिकाव्य", "main": "प्रमुख", "playlists": "गानों की सूचीयाँ", @@ -51,15 +51,15 @@ "genres": "शैलियाँ", "news": "समाचार", "playcounts": "प्लेकाउंट्स", - "playlists": "Trending", + "playlists": "चर्चित", "popular-track-title": "डीज़र के शीर्ष ट्रैक", "title": "शीर्षक", "top": "शीर्ष ट्रैक्स", - "trending-playlists": "Trending playlists", + "trending-playlists": "ट्रेंडिंग प्लेलिस्ट", "trending-artists": "Trending artists", "trending-albums": "Trending albums", - "filter": "Filter...", - "nothing-found": "Nothing found." + "filter": "फिल्टर...", + "nothing-found": "कुछ भी नहीं मिला" }, "downloads": { "clear": "समाप्त ट्रैक साफ़ करें", @@ -127,6 +127,7 @@ "not-found": "इस गाने के कोई गीतिकाव्य नहीं मिले।" }, "option-control": { + "rate": "Playback speed", "autoradio": "ऑटोरेडियो", "loop": "लूप", "mini-player": "मिनीप्लेयर", @@ -423,8 +424,8 @@ "empty-state": "You haven't listened to anything yet.", "clear-history": "Clear history", "clear-history-confirm": "Are you sure you want to clear your listening history?", - "clear-history-confirm-yes": "Confirm", - "clear-history-confirm-no": "Cancel", + "clear-history-confirm-yes": "पुष्टि करें", + "clear-history-confirm-no": "रद्द करें", "clear-history-toast": "Listening history cleared" }, "forms": { diff --git a/packages/i18n/src/locales/hr.json b/packages/i18n/src/locales/hr.json index de49d33b08..bc6b825349 100644 --- a/packages/i18n/src/locales/hr.json +++ b/packages/i18n/src/locales/hr.json @@ -97,7 +97,7 @@ "empty": "Nemaš omiljenih izvođača", "empty-help": "Probajte dodati nekoliko pjesama u omiljene, i oni će se ovdje pojaviti!", "header": "Tvoje omiljene pjesme", - "play-random": "Play a random track from your favorites", + "play-random": "Pustite razabranu pjesmu iz svojih favorita", "title": "Naslov" }, "help": { @@ -127,6 +127,7 @@ "not-found": "Nije pronađen tekst ove pjesme." }, "option-control": { + "rate": "Brzina reprodukcije", "autoradio": "Autoradio", "loop": "Petlja", "mini-player": "Minipler", @@ -220,21 +221,21 @@ "live": "Uživo", "stream-verification": { "tooltip": { - "unknown": "The status of this stream is unknown.", - "unverified": "This stream has not yet been verified by the community.", - "weakly-verified": "This stream has been verified by a couple of users.", - "verified": "This stream has been verified by the community.", - "verified-by-user": "This stream has been verified by you." + "unknown": "Status ovog stream-a je nepoznat.", + "unverified": "Zajednica još nije potvrdila ovaj stream.", + "weakly-verified": "Ovaj stream je potvrdilo nekoliko korisnika.", + "verified": "Zajednica je potvrdila ovaj stream.", + "verified-by-user": "Vi ste potvrdili ovaj stream." }, "stream-status": { - "unknown": "Unknown", - "unverified": "Unverified", - "weakly-verified": "Weakly verified", - "verified": "Verified", - "verified-by-user": "Verified by you" + "unknown": "Nepoznato", + "unverified": "Neprovjereno", + "weakly-verified": "Slabo provjereno", + "verified": "Potvrđeno", + "verified-by-user": "Potvrđeno od vas" }, - "verify": "Verify", - "unverify": "Unverify" + "verify": "Potvrdi", + "unverify": "Poništi potvrdu" } }, "search": { @@ -305,7 +306,7 @@ "mastodon-awaiting-authorization": "Nuclear je zabilježen na {{instanceUrl}}. Čeka se ovlašćenje.", "mastodon-description": "", "mastodon-instance-label": "URL za instancu Mastodona", - "mastodon-post-format-description": "Nuclear will post a status on Mastodon after each track completes playing. The above string will be the template for each post, with {{artist}}, {{title}}, and {{url}} replaced with the artist, title, and a link to the track respectively.", + "mastodon-post-format-description": "Nuclear će objaviti status na Mastodonu nakon završetka reprodukcije svake pjesme. Gornji niz bit će predložak za svaki post, a {{artist}}, {{title}} i {{url}} zamijenjeni su izvođačem, naslovom i vezom na pjesmu.", "mastodon-post-format-label": "Format objave", "mastodon-title": "", "normalize": "Normalizirajte glasnoću", @@ -323,7 +324,7 @@ "promoted-artists-description": "Ovo je niz zanimljivih, ručno odabranih nezavisnih izvođača koji bi vam se mogli svidjeti. Nitko ne prima nikakvu naknadu za ovo.", "saving-in": "Spremanje u:", "seek-iteration": "Broj preskočenih sekundi naprijed/nazad kada se koriste tipke sa strelicama", - "show-tray-icon": "Show tray icon", + "show-tray-icon": "Prikaži ikonu palete", "shuffle-queue": "Izmješaj redoslijed pjesama", "shuffle-when-going-back": "Izmješaj kada ideš na prethodnu pjesmu", "shuffle-when-going-back-description": "Sviraj nasumičnu pjesmu kada je aktivno mješanje i kada klikneš na gumb za prethodnu pjesmu", @@ -333,9 +334,9 @@ "streaming": "Strimovanje", "track-duration": "Prikaži trajanje pjesme preko vremenske trake", "user": "Korisnik:", - "user-id": "User ID", - "use-stream-verification": "Use stream verification", - "use-stream-verification-description": "Use stream verification. Loads the streams the community has verified as being correct for the tracks you play. Also allows you to vote on streams. See documentation for details.", + "user-id": "Korisnički ID", + "use-stream-verification": "Koristite potvrdu streama", + "use-stream-verification-description": "Koristite potvrdu streama. Učitava streamove koje je zajednica potvrdila kao ispravne za pjesme koje puštate. Također vam omogućuje glasanje na streamovima. Pogledajte dokumentaciju za detalje.", "youtube": "Youtube", "yt-api-key": "Youtube API ključ" }, diff --git a/packages/i18n/src/locales/hu_HU.json b/packages/i18n/src/locales/hu_HU.json new file mode 100644 index 0000000000..160a3a4eb0 --- /dev/null +++ b/packages/i18n/src/locales/hu_HU.json @@ -0,0 +1,464 @@ +{ + "album": { + "download": "Letöltés", + "download-toast-content": "{{artist}} - {{title}} hozzá lett adva a letöltésekhez.", + "download-toast-title": "Album hozzáadva a letöltésekhez", + "error-load": "Sajnáljuk, az albumot nem tudtuk letölteni. Próbáld újra később vagy nyiss új problémat Github-on", + "genre": "Műfaj:", + "play": "Lejátszás", + "queue": "Album hozzáadása a várólistához", + "tracks": "Számok:", + "year": "Év:" + }, + "app": { + "collection": "Gyűjtemény", + "dashboard": "Főoldal", + "downloads": "Letöltések", + "equalizer": "Hangszínszabályozó", + "favorite-albums": "Kedven albumok", + "favorite-artists": "Kedven előadók", + "favorite-tracks": "Kedven számok", + "library": "Helyi könyvtár", + "listening-history": "Hallgatási előzmények", + "lyrics": "Dalszöveg", + "main": "Kezdőlap", + "playlists": "Lejátszási listák", + "plugins": "Bővítmények", + "search": "Keresési eredmények", + "settings": "Beállítások", + "visualizer": "Vizualizátor", + "user-panel": { + "actions-tooltip": "Felhasználói műveletek", + "sign-up": "Regisztáci / Bejelentkezés", + "sign-out": "Kijelentkezés" + }, + "version": "Verzió" + }, + "artist": { + "add-all": "Összes hozzáadása", + "count": "Lejátszások száma", + "queue": "Minden szám hozzáadása a várólistához", + "similar": "Hasonló előadók", + "title": "Cím", + "tour": "Turnén van" + }, + "dashboard": { + "add-all": "Összes hozzáadása", + "artist": "Előadó", + "best": "Legjobb új zenék", + "best-new-albums": "Legjobb új albumok", + "best-new-tracks": "Legjobb új számok", + "genres": "Műfajok", + "news": "Hírek", + "playcounts": "Lejátszások száma", + "playlists": "Felkapott", + "popular-track-title": "Legjobb számok Deezer-ről", + "title": "Cím", + "top": "Legjobb számok", + "trending-playlists": "Felkapott lejátszási listák", + "trending-artists": "Felkapott előadók", + "trending-albums": "Felkapott albumok", + "filter": "Szűrők...", + "nothing-found": "Nincs találat." + }, + "downloads": { + "clear": "Letöltött elemek törlése", + "completion": "Letöltve", + "empty": "Nincsenek letöltések.", + "empty-help": "Adj hozzá valamit a letöltési váró listádhoz és itt látni fogod!", + "header": "Letöltések", + "name": "Név", + "status": "Állapot" + }, + "equalizer": { + "classical": "Klasszikus", + "club": "Klub", + "custom": "Egyedi", + "default": "Alapértelmezett", + "full-bass": "Teljes basszus", + "full-treble": "Teljes Treble", + "pop": "Pop", + "presets": "Hangsémák", + "reggae": "Reggae", + "rock": "Rock" + }, + "favorite-albums": { + "empty": "Nincsenek kedvelt albumok", + "empty-help": "Adj hozzá albumokat és itt látni fogod őket!", + "header": "A kedvenc albumaid" + }, + "favorite-artists": { + "empty": "Nincsenek kedvelt előadók", + "empty-help": "Adj hozzá előadókat és itt látni fogod őket!", + "header": "A kedvenc előadóid" + }, + "favorites": { + "artist": "Előadó", + "empty": "Nincsenek kedvencek hozzáadva", + "empty-help": "Adj hozzá kedvenceket és itt látni fogod őket!", + "header": "Kedvenc számaid", + "play-random": "Random szám lejátszása a kedveltek közül", + "title": "Cím" + }, + "help": { + "about": "A Nuclear Zenelejátszó névjegye", + "contributors": "Top 10 közreműködők", + "header": "Asztali zenelejátszó ingyenes forrásokból", + "released": "kiadva AGPL-3.0 licensz alatt", + "report": "Jelezd a hibákat vagy új ötleteket", + "thanks": "Örök hála a Github-os hozzájárulásokért, a segítségetek nélkül nem jöhetett volna létre ez a program." + }, + "library": { + "add": "Mappák hozzáadása", + "album": "Album", + "artist": "Előadó", + "empty": "A könyvtár üres", + "empty-help": "Try adding some music using the button above.", + "filter-placeholder": "Szűrő...", + "header": "Helyi könyvtár", + "no-search-results": "Nincsenek találatok", + "no-search-results-help": "Nincsenek találatok ezzel a névvel, albummal, vagy előadóval.", + "title": "Cím" + }, + "lyrics": { + "by-artist": "{{artist}} által", + "empty": "Nothing is playing.", + "empty-help": "Adj hozzá zenéket a várólistához, hogy itt láthasd a szövegüket!", + "not-found": "Nincs szöveg találat ehhez a zenéhez." + }, + "option-control": { + "rate": "Lejátszási sebesség", + "autoradio": "Autómata rádió", + "loop": "Ismétlés", + "mini-player": "Kislejátszó", + "shuffle": "Keverés" + }, + "player": { + "next-button": "Következő szám gomb", + "play-pause-button": "Lejátszás/Megállítás gomb", + "previous-button": "Előző szám gomb" + }, + "playlists": { + "add-selected-tracks-to-downloads": "A kiválasztottak hozzáadása a letöltésekhez", + "add-selected-tracks-to-favorites": "A kiválasztottak hozzáadása a kedvencekhez", + "add-selected-tracks-to-queue": "A kiválasztottak hozzáadása a várólistához", + "album": "Album", + "artist": "Előadó", + "delete": "Lejátszási lista törlése", + "duration": "Hossz", + "empty": "Nincsenek lejátszási listák.", + "empty-help": "Try to create some playlists or import by url and they will appear here!", + "error-empty-data": "Nincs adat", + "error-invalid-data": "Érvénytelen adat", + "error-open-file": "A fájl nem nyitható meg", + "error-save-file": "A fájlt nem lehet lementeni", + "export-button": "Lejátszási lista kiírása fájlba (JSON)", + "export-fail-title": "Nem sikerült kiírni a lejátszási listát", + "export-success-title": "Sikeresen fájlba lett írva a lejátszási lista", + "header": "Lejátszási listáid", + "import-progress": "Beolvasás folyamatban...", + "spotify-import-placeholder": "Spotify URL beillesztése ide", + "dialog-placeholder": "Lejátszási lista neve...", + "dialog-accept": "Mentés", + "dialog-cancel": "Mégsem", + "dialog-rename": "Átnevezés", + "dialog-import": "Beolvasás", + "create-button": "Új lejátszási lista létrehozása", + "new-playlist": "Új lejátszási lista", + "import-url-button": "Beolvasás URL-ből (Spotify)", + "import-button": "Beolvasás fájlból (JSON)", + "import-fail-title": "Lejátszási lista beolvasása sikertelen", + "import-success-title": "Sikeresen fájlba lett írva a lejátszási lista", + "play-selected-tracks-now": "A kiválasztott lejátszása most", + "playlist-created": "{{name}} létrehozva", + "playlist-exported": "{{name}} saved to local file", + "queue": "Lejátszási lista hozzáadása a várólistához", + "rename": "Lejátszási lista átnevezése", + "songs": "{{total}} song", + "songs_plural": "{{total}} songs", + "title": "Cím", + "tracks-selected-plural": "számok kiválasztva", + "tracks-selected-singular": "szám kiválasztva", + "tracks-singular": "szám", + "tracks-plural": "számok", + "modified-at": "Utoljára módosítva: ", + "never-modified": "Ismeretlen", + "server-modified-at": "Server modified: ", + "upload-to-server": "Upload to server", + "download-from-server": "Letöltetés a szerverről", + "number-of-tracks": "számok" + }, + "plugins": { + "add-a-plugin": "Bővítmény hozzáadása", + "header": "Bővítmények", + "lyrics-providers": "Lyrics providers", + "meta-providers": "Metadata providers", + "placeholder": "Select the default music source:", + "select-lyrics-provider": "Select the default lyrics provider:", + "select-meta-provider": "Select the default metadata provider:", + "stream-providers": "Stream providers", + "user-plugins": "User plugins", + "user-plugins-warning-desc": "Plugins work by running code on your computer. Load plugins only from sources you trust!", + "user-plugins-warning-title": "Veszélyzóna!" + }, + "queue": { + "clear": "Várólista törlése", + "dialog-accept": "Mentés", + "dialog-cancel": "Mégsem", + "dialog-placeholder": "Lejátszási lista neve...", + "dialog-trigger": "Mentés lejátszási listaként", + "download": "Letöltés", + "download-toast-content": "{{artist}} - {{title}} hozzá lett adva a letöltésekhez.", + "download-toast-title": "Szám hozzáadva a letöltésekhez", + "favorite-add": "Hozzáadás a kedvencekhez", + "header": "Várólista", + "header-track": "Current track", + "copy-track-url": "Copy track url to clipboard", + "loading": "Stream still loading.", + "playlist-add": "Hozzáadás a lejátszási listához", + "playlist-toast-content": "Playlist {{name}} has been created.", + "playlist-toast-title": "Lejátszási lista létrehozva", + "live": "Élő", + "stream-verification": { + "tooltip": { + "unknown": "The status of this stream is unknown.", + "unverified": "Ez a közvetítés még nem lett ellenőrízve a közösség által.", + "weakly-verified": "This stream has been verified by a couple of users.", + "verified": "Ez a közvetítés ellenőrízve lett a közösség által.", + "verified-by-user": "Ez a közvetítés ellenőrízve lett általad." + }, + "stream-status": { + "unknown": "Ismeretlen", + "unverified": "Nem ellenőrzött", + "weakly-verified": "Hetente ellenőrzött", + "verified": "Ellenőrzött", + "verified-by-user": "Ellenőrízve általad" + }, + "verify": "Ellenőrzés", + "unverify": "Nem ellenőrzött" + } + }, + "search": { + "album": "Album", + "album_plural": "Albumok", + "all": "Összes", + "artist": "Előadó", + "artist_plural": "Előadók", + "clear-history": "Előzmények törlése", + "empty": "Nincs találat.", + "last-searches": "Utóbbi keresések", + "live-stream": "Élő adás", + "placeholder": "Keresés...", + "playlist": "Lejátszási lista", + "playlist_plural": "Lejátszási listák", + "podcast": "Podcast", + "queue-add": "Minden szám hozzáadása a várólistához", + "track": "Szám", + "track_plural": "Számok", + "you-can-search-for": "Keresni tudsz:" + }, + "seekbar": { + "live": "Élő", + "segment-popup": "Nem-Zene" + }, + "settings": { + "api-port": "A portot az API használja", + "api-url": "", + "audio": "Hang", + "autoradio": "Autómata rádió", + "autoradio-craziness": "Autómata rádió őrültsége", + "autoradio-craziness-description": "Az autómata rádió olyan zenéket fog kiválasztani, amlyek annál kevésbé hasonlítanak az várólistán levőkre, ammenyivel őrültebb", + "autoradio-description": "Hasonló számok autómatikus hozzáadása, amikor a várólista a végéhez ér", + "listening-history": "Hallgatási előzmények", + "listening-history-description": "Log the tracks you listen to, a la Last.fm. The history is stored offline.", + "compact-menu-bar": "Use compact style for menu bar", + "compact-queue-bar": "Use compact style for queue bar", + "developer": "Developer settings", + "devtools": "Developer tools", + "disable-gpu": "Disable hardware rendering (might fix issues with dragging elements and flashing screen)", + "display": "Display", + "downloads": "Downloads", + "downloads-count": "Max simultaneous downloads", + "downloads-dir": "Downloads directory", + "downloads-dir-button": "Choose a directory...", + "enable-api": "Enable the api", + "fmfav-btn": "Import", + "fmfav-msg": "Import your Last.fm Favorites", + "frameless-window": "Frameless window (requires restart)", + "github-connect": "Log in with Github", + "github-description": "Log in via Github to be able to create and share your playlists online (upcoming feature).", + "github-title": "Github", + "http": "HTTP API", + "invidious-url": "Invidious instance url", + "language": "Language", + "language-placeholder": "Pick a language", + "lastfm-connect": "Connect with Last.fm", + "lastfm-description": "In order to enable scrobbling, you first have to connect and authorize Nuclear on Last.fm, then click log in.", + "lastfm-enable": "Enable scrobbling to Last.fm", + "lastfm-title": "Last.fm", + "less": "Less", + "login": "Log in", + "logout": "Log out", + "loop-after-queue-end": "Loop after playing the last queue item", + "mastodon-authorization-token-label": "Authorization token", + "mastodon-authorize": "Authorize", + "mastodon-authorized": "Nuclear is authorized to post on ", + "mastodon-awaiting-authorization": "Nuclear is registered on {{instanceUrl}}. Awaiting authorization.", + "mastodon-description": "", + "mastodon-instance-label": "Mastodon instance URL", + "mastodon-post-format-description": "Nuclear will post a status on Mastodon after each track completes playing. The above string will be the template for each post, with {{artist}}, {{title}}, and {{url}} replaced with the artist, title, and a link to the track respectively.", + "mastodon-post-format-label": "Post format", + "mastodon-title": "", + "normalize": "Normalize volume", + "normalize-description": "Automatically adjust volume of tracks so that they are played at the same level. This needs to fetch the whole track to work, so it may cause a delay between tracks.", + "nuclear-identity-service-url": "Nuclear identity service URL", + "nuclear-playlists-service-url": "Nuclear playlists service URL", + "mini-player": "Use mini player style", + "more": "More", + "discord-rich-presence": "Toggle discord rich presence (requires restart)", + "notification-timeout": "Notification timeout", + "notlogged": "Not logged in", + "playback": "Playback", + "program-settings": "Program settings", + "promoted-artists": "Show promoted artists in the dashboard", + "promoted-artists-description": "This is a reel of interesting, handpicked indie artists you might like. No one's receiving any compensation for this.", + "saving-in": "Saving in:", + "seek-iteration": "Number of seconds to seek forward/backwards when pressing arrow keys", + "show-tray-icon": "Tálcaikon megjelenítése", + "shuffle-queue": "Dalok keverése", + "shuffle-when-going-back": "Shuffle on previous track", + "shuffle-when-going-back-description": "Play a random track when shuffle is active and the previous track button is clicked", + "social": "Közösség", + "skip-sponsorblock": "Skip non-music segment", + "skip-sponsorblock-description": "Playback skips non-music segment from SponsorBlock (https://sponsor.ajay.app/)", + "streaming": "Közvetítés", + "track-duration": "Display track duration over the seekbar", + "user": "Felhasználó:", + "user-id": "Felhasználói azonosító", + "use-stream-verification": "Use stream verification", + "use-stream-verification-description": "Use stream verification. Loads the streams the community has verified as being correct for the tracks you play. Also allows you to vote on streams. See documentation for details.", + "youtube": "YouTube", + "yt-api-key": "YouTube API-kulcs" + }, + "tags": { + "albums": "Legnépszerűbb albumok", + "artist": "Előadó", + "artists": "Legjobb előadók", + "duration": "Időtartam", + "queue-add": "Minden szám hozzáadása a várólistához", + "title": "Cím" + }, + "track-popup": { + "add-to-favorite": "Hozzáadás a kedvencekhez", + "add-to-playlist": "Hozzáadás a lejátszási listához", + "add-to-queue": "Hozzáadás a várólistához", + "create-playlist": "Új lejátszási lista létrehozása", + "create-playlist-dialog-title": "Input playlist name:", + "create-playlist-dialog-placeholder": "Lejátszási lista neve...", + "create-playlist-dialog-accept": "Mentés", + "create-playlist-dialog-cancel": "Mégsem", + "download": "Letöltés", + "download-toast-body": "{{artist}} - {{track}} hozzá lett adva a letöltésekhez.", + "download-toast-title": "Szám hozzáadva a letöltésekhez", + "favorite-toast-body": "{{artist}} - {{track}} hozzá lett adva a kedvencekhez.", + "favorite-toast-title": "Favorite track added", + "play-next": "A következő lejátszása", + "play-now": "Lejátszás most", + "playlist-toast-body": "{{artist}} - {{track}} hozzá lett adva a lejátszási listához.", + "playlist-toast-title": "Track added to playlist" + }, + "track-table": { + "add-selected-tracks-to-queue": "A kiválasztottak hozzáadása a várólistához", + "add-selected-tracks-to-downloads": "A kiválasztottak hozzáadása a letöltésekhez", + "add-selected-tracks-to-favorites": "A kiválasztottak hozzáadása a kedvencekhez", + "play-selected-tracks-now": "A kiválasztott lejátszása most", + "tracks-selected-label-singular": "Szám kiválasztva", + "tracks-selected-label-plural": "Számok kiválasztva" + }, + "visualizer": { + "exit-fullscreen": "A teljes képernyőből való kilépéshez nyomd meg az ESC gombot" + }, + "command-palette": { + "search-placeholder": "Mit szeretnél csinálni?", + "empty-state-help": "Can't seem to find what you're looking for? Try the help command.", + "protip-text": "Haladó Tanács:", + "protip-content": "A nyilak a navigációhoz, Enter a kiválasztáshoz, Esc a bezáráshoz", + "categories": { + "playback": "Lejátszás", + "queue": "Várólista", + "navigation": "Navigáció", + "application": "Alkalmazás" + }, + "actions": { + "play": "Lejátszás", + "pause": "Szünet", + "next": "Következő", + "previous": "Előző", + "go-to-next-page": "Ugrás a következő oldalra", + "go-to-previous-page": "Ugrás az előző oldalra", + "shuffle": "Keverés", + "loop": "Ismétlés", + "autoradio": "Autómata rádió", + "raise-volume": "Hangerő-emelés", + "lower-volume": "Hangerő-csökkentés", + "mute": "Némítás", + "unmute": "Némítás feloldása", + "quit": "Kilépés", + "minimize": "Kis méret", + "maximize": "Teljes méret", + "go-to-dashboard": "Ugrás a vezérlőputlba", + "go-to-downloads": "Ugrás a letöltésekbe", + "go-to-lyrics": "Ugrás a dalszövegre", + "go-to-plugins": "Ugrás a bővítményekhez", + "go-to-search": "Ugrás a kereséshez", + "go-to-settings": "Ugrás a beállításokhoz", + "go-to-equalizer": "Ugrás a hangerőkeverőhöz", + "go-to-visualizer": "Ugrás a hangerőkeverőhöz", + "go-to-playlists": "Ugrás a lejátszási listákhoz", + "go-to-favorite-tracks": "Ugrás a kedvenc számaidhoz", + "go-to-library": "Ugrás a helyi könyvtárhoz" + } + }, + "listening-history": { + "title": "Hallgatási előzmények", + "empty-state": "Még nem hallgattál semmit.", + "clear-history": "Előzmények törlése", + "clear-history-confirm": "Biztos, hogy törli a hallgatási előzményeit?", + "clear-history-confirm-yes": "Megerősítés", + "clear-history-confirm-no": "Mégsem", + "clear-history-toast": "A hallgatási előzmények törölve lettek" + }, + "forms": { + "nuclear-sign-up": { + "header": "Regisztráció", + "secondary-header": "Bejelentkezés a Nuclear Web Services-be", + "side-paragraph-1": "NWS-el biztonsági mentést készíthetsz a lejátszási listáidról online és megoszthatod őket.", + "side-paragraph-2": "Az E-mail megadása nem kötelező; megadása segít visszaszerezni fiókod, ha elfejejtenéd jelszavad.", + "username-label": "Felhasználónév", + "email-label": "E-mail", + "password-label": "Jelszó", + "sign-up-button": "Regisztráció", + "validation": { + "username": { + "length": "A felhasználónév legalább 4 karakteres kell legyen", + "required": "Felhasználónév megadása kötelező" + }, + "email": { + "invalid": "Egy valós E-mail címet kell megadni" + }, + "password": { + "length": "A jelszó legalább 6 karakteres kell legyen", + "required": "A jelszó kötelező" + } + } + }, + "nuclear-sign-in": { + "header": " Bejelentkezés", + "username-label": "Felhasználónév", + "password-label": "Jelszó", + "no-account-label": "Nincs fiókod?", + "no-account-link": "Regisztráció", + "sign-in-button": "Bejelentkezés" + } + } +} \ No newline at end of file diff --git a/packages/i18n/src/locales/id.json b/packages/i18n/src/locales/id.json index 7c8b9dbf16..e777a85e78 100644 --- a/packages/i18n/src/locales/id.json +++ b/packages/i18n/src/locales/id.json @@ -19,7 +19,7 @@ "favorite-artists": "Musisi Favorit", "favorite-tracks": "Lagu Favorit", "library": "Daftar Koleksi", - "listening-history": "Listening History", + "listening-history": "Riwayat Mendengarkan", "lyrics": "Lirik", "main": "Main", "playlists": "Daftar Putar", @@ -51,15 +51,15 @@ "genres": "Genre", "news": "Berita", "playcounts": "Jumlah pemutaran", - "playlists": "Trending", + "playlists": "Sedang Tren", "popular-track-title": "Lagu Teratas dari Deezer", "title": "Judul", "top": "Lagu Teratas", - "trending-playlists": "Trending playlists", - "trending-artists": "Trending artists", - "trending-albums": "Trending albums", + "trending-playlists": "Daftar Putar yang Lagi Tren", + "trending-artists": "Artis yang sedang hangat", + "trending-albums": "Album yang lagi ngetren", "filter": "Filter...", - "nothing-found": "Nothing found." + "nothing-found": "Tidak ditemukan." }, "downloads": { "clear": "Hapus unduhan yang selesai", @@ -97,7 +97,7 @@ "empty": "Belum ada lagu favorit", "empty-help": "Tambahkan lagu ke dalam daftar favorit agar muncul di halaman ini!", "header": "Lagu favoritmu", - "play-random": "Play a random track from your favorites", + "play-random": "Putar trek acak dari favoritmu", "title": "Judul" }, "help": { @@ -123,10 +123,11 @@ "lyrics": { "by-artist": "oleh {{artist}}", "empty": "Tidak ada lagu yang diputar.", - "empty-help": "Tambahkan lagu ke daftar antrian agar liriknya ditampilkan di sini!", + "empty-help": "Tambahkan beberapa lagu ke daftar antrian agar liriknya ditampilkan di sini!", "not-found": "Lirik untuk lagu ini tidak ditemukan" }, "option-control": { + "rate": "Kecepatan pemutaran", "autoradio": "Autoradio", "loop": "Ulang", "mini-player": "Miniplayer", @@ -220,21 +221,21 @@ "live": "Live", "stream-verification": { "tooltip": { - "unknown": "The status of this stream is unknown.", - "unverified": "This stream has not yet been verified by the community.", - "weakly-verified": "This stream has been verified by a couple of users.", - "verified": "This stream has been verified by the community.", - "verified-by-user": "This stream has been verified by you." + "unknown": "Status stream ini tidak diketahui.", + "unverified": "Stream ini tidak diverifikasi oleh komunitas.", + "weakly-verified": "Stream ini sudah diverifikasi oleh beberapa pengguna.", + "verified": "Stream ini sudah diverifikasi oleh komunitas.", + "verified-by-user": "Stream ini sudah diverifikasi oleh anda." }, "stream-status": { - "unknown": "Unknown", - "unverified": "Unverified", - "weakly-verified": "Weakly verified", - "verified": "Verified", - "verified-by-user": "Verified by you" + "unknown": "Tidak diketahui", + "unverified": "Tidak diverifikasi", + "weakly-verified": "Verifikasi mingguan", + "verified": "Diverifikasi", + "verified-by-user": "Diverifikasi oleh anda" }, - "verify": "Verify", - "unverify": "Unverify" + "verify": "Verifikasi", + "unverify": "Tidak diverifikasi" } }, "search": { @@ -268,8 +269,8 @@ "autoradio-craziness": "Autoradio craziness", "autoradio-craziness-description": "Autoradio akan memilih lagu yang kurang mirip dengan yang sudah masuk ke daftar antrian", "autoradio-description": "Tambahkan lagu yang mirip secara otomatis di akhir daftar antrian", - "listening-history": "Listening history", - "listening-history-description": "Log the tracks you listen to, a la Last.fm. The history is stored offline.", + "listening-history": "Riwayat mendengarkan", + "listening-history-description": "Log trek yang anda dengarkan ke, Last.fm. Riwayat akan disimpan secara luring.", "compact-menu-bar": "Gunakan gaya ringkas untuk papan menu", "compact-queue-bar": "Gunakan gaya ringkas untuk papan antrian", "developer": "Pengaturan pengembang", @@ -305,11 +306,11 @@ "mastodon-awaiting-authorization": "Nuclear telah terdaftar di {{instanceUrl}}. Menunggu otorisasi.", "mastodon-description": "", "mastodon-instance-label": "URL Mastodon", - "mastodon-post-format-description": "Nuclear will post a status on Mastodon after each track completes playing. The above string will be the template for each post, with {{artist}}, {{title}}, and {{url}} replaced with the artist, title, and a link to the track respectively.", + "mastodon-post-format-description": "Nuclear akan memposting status di Mastodon setelah trek lagu selesai diputar. String di atas akan menjadi templat setiap post, dengan {{artist}}, {{title}}, dan {{url}} dan akan ditimpa dengan artist, title, dan tautan trek masing-masing.", "mastodon-post-format-label": "Format post", "mastodon-title": "", - "normalize": "Normalize volume", - "normalize-description": "Automatically adjust volume of tracks so that they are played at the same level. This needs to fetch the whole track to work, so it may cause a delay between tracks.", + "normalize": "Normalkan volume", + "normalize-description": "Otomatis menyesuaikan volume trek yang mereka putar dalam level bersamaan. Ini membutuhkan untuk mengambil keseluruhan trek untuk bekerja, sehingga dapat menyebabkan penundaan antar trek.", "nuclear-identity-service-url": "URL Layanan identitas Nuclear", "nuclear-playlists-service-url": "URL Layanan daftar putar Nuclear", "mini-player": "Gunakan mini player", @@ -319,11 +320,11 @@ "notlogged": "Kamu belum masuk", "playback": "Pemutaran", "program-settings": "Pengaturan program", - "promoted-artists": "Show promoted artists in the dashboard", - "promoted-artists-description": "This is a reel of interesting, handpicked indie artists you might like. No one's receiving any compensation for this.", + "promoted-artists": "Tampilkan artis yang dipromosikan di dasbor", + "promoted-artists-description": "Reel ini ini sangat menarik, pilih artis indie yang anda suka. Tidak ada yang menerima kompensasi untuk ini.", "saving-in": "Simpan di:", "seek-iteration": "Rentang detik yang di-skip/dikembalikan ketika menekan tombol arah", - "show-tray-icon": "Show tray icon", + "show-tray-icon": "Tampilkan ikon baki", "shuffle-queue": "Acak lagu", "shuffle-when-going-back": "Acak lagu sebelumnya", "shuffle-when-going-back-description": "Acak lagu saat mode random aktif dan tombol lagu sebelumnya diklik", @@ -333,8 +334,8 @@ "streaming": "Streaming", "track-duration": "Tampilkan durasi lagu di atas kolom pencarian", "user": "Pengguna:", - "user-id": "User ID", - "use-stream-verification": "Use stream verification", + "user-id": "ID Pengguna", + "use-stream-verification": "Gunakan verifikasi stream", "use-stream-verification-description": "Use stream verification. Loads the streams the community has verified as being correct for the tracks you play. Also allows you to vote on streams. See documentation for details.", "youtube": "Youtube", "yt-api-key": "Kunci API Youtube" @@ -351,11 +352,11 @@ "add-to-favorite": "Tambahkan ke favorit", "add-to-playlist": "Tambahkan ke daftar putar", "add-to-queue": "Tambahkan ke daftar antri", - "create-playlist": "Create new playlist", - "create-playlist-dialog-title": "Input playlist name:", - "create-playlist-dialog-placeholder": "Playlist name...", - "create-playlist-dialog-accept": "Save", - "create-playlist-dialog-cancel": "Cancel", + "create-playlist": "Buat playlist baru", + "create-playlist-dialog-title": "Masukkan nama playlist:", + "create-playlist-dialog-placeholder": "Nama playlist...", + "create-playlist-dialog-accept": "Simpan", + "create-playlist-dialog-cancel": "Batal", "download": "Unduh", "download-toast-body": "{{artist}} - {{track}} berhasil ditambah ke unduhan.", "download-toast-title": "Lagu ditambahkan ke unduhan", @@ -378,10 +379,10 @@ "exit-fullscreen": "Tekan ESC untuk keluar dari layar penuh" }, "command-palette": { - "search-placeholder": "What would you like to do?", + "search-placeholder": "Apa yang ingin anda lakukan?", "empty-state-help": "Tidak dapat menemukan apa yang anda inginkan? Coba dengan perintah bantuan.", "protip-text": "PROTIP:", - "protip-content": "Arrows to navigate, Enter to select, Esc to close", + "protip-content": "Arrows untuk menavigasikan, Enter untuk memilih, Esc untuk menutup", "categories": { "playback": "Pemutaran Belakang", "queue": "Antrian", @@ -419,13 +420,13 @@ } }, "listening-history": { - "title": "Listening history", - "empty-state": "You haven't listened to anything yet.", - "clear-history": "Clear history", - "clear-history-confirm": "Are you sure you want to clear your listening history?", - "clear-history-confirm-yes": "Confirm", - "clear-history-confirm-no": "Cancel", - "clear-history-toast": "Listening history cleared" + "title": "Riwayat mendengarkan", + "empty-state": "Anda belum mendengarkan apapun.", + "clear-history": "Hapus riwayat", + "clear-history-confirm": "Apa anda yakin ingin menghapus riwayat mendengarkanmu?", + "clear-history-confirm-yes": "Konfirmasi", + "clear-history-confirm-no": "Batal", + "clear-history-toast": "Riwayat mendengarkanmu sudah dihapus" }, "forms": { "nuclear-sign-up": { diff --git a/packages/i18n/src/locales/is.json b/packages/i18n/src/locales/is.json index 5bcd8d3f44..5664177978 100644 --- a/packages/i18n/src/locales/is.json +++ b/packages/i18n/src/locales/is.json @@ -127,6 +127,7 @@ "not-found": "Enginn texti fannst fyrir þetta lag." }, "option-control": { + "rate": "Playback speed", "autoradio": "Sjálfvirkt útvarp", "loop": "Lykkja", "mini-player": "Miniplayer", diff --git a/packages/i18n/src/locales/it.json b/packages/i18n/src/locales/it.json index 315afb265f..f7b23da09f 100644 --- a/packages/i18n/src/locales/it.json +++ b/packages/i18n/src/locales/it.json @@ -59,7 +59,7 @@ "trending-artists": "Artisti di tendenza", "trending-albums": "Album di tendenza", "filter": "Filtra...", - "nothing-found": "Non è stato trovato nulla." + "nothing-found": "Nessun risultato." }, "downloads": { "clear": "Cancella tracce completate", @@ -97,7 +97,7 @@ "empty": "Nessun preferito aggiunto", "empty-help": "Prova ad aggiungere alcune tracce ai preferiti e appariranno qui!", "header": "Tue tracce preferite", - "play-random": "Play a random track from your favorites", + "play-random": "Riproduce una traccia casuale dai tuoi preferiti", "title": "Titolo" }, "help": { @@ -127,6 +127,7 @@ "not-found": "Non sono stati trovati testi per questa canzone." }, "option-control": { + "rate": "Velocità di riproduzione", "autoradio": "Radio automatica", "loop": "Ciclo continuo", "mini-player": "Mini lettore", @@ -392,7 +393,7 @@ "play": "Riproduci", "pause": "Pausa", "next": "Successivo", - "previous": "Precendente", + "previous": "Precedente", "go-to-next-page": "Vai alla pagina successiva", "go-to-previous-page": "Vai alla pagina precedente", "shuffle": "Casuale", diff --git a/packages/i18n/src/locales/ja_JP.json b/packages/i18n/src/locales/ja_JP.json index 4a75072fce..d7a33246d4 100644 --- a/packages/i18n/src/locales/ja_JP.json +++ b/packages/i18n/src/locales/ja_JP.json @@ -64,8 +64,8 @@ "downloads": { "clear": "再生済みの曲を消去", "completion": "進捗", - "empty": "ダウンロードは空です。", - "empty-help": "ダウンロード待ちに何かを追加すると、ここに表示されます!", + "empty": "ダウンロードなし。", + "empty-help": "ダウンロード待ちに追加すると、ここに表示されます!", "header": "ダウンロード", "name": "名前", "status": "状態" @@ -112,10 +112,10 @@ "add": "フォルダーを追加", "album": "アルバム", "artist": "アーティスト", - "empty": "ライブラリは空です", + "empty": "ライブラリ内に登録なし", "empty-help": "上のボタンから音楽を追加してみてください。", "filter-placeholder": "絞り込み...", - "header": "ローカルライブラリ", + "header": "端末内のライブラリ", "no-search-results": "検索結果なし", "no-search-results-help": "指定された名前、アルバム、アーティストの曲が見つかりません。", "title": "タイトル" @@ -127,6 +127,7 @@ "not-found": "この曲の歌詞が見つかりませんでした。" }, "option-control": { + "rate": "再生速度", "autoradio": "ラジオ", "loop": "ループ", "mini-player": "ミニプレイヤー", @@ -170,7 +171,7 @@ "import-success-title": "再生リストのインポートに成功", "play-selected-tracks-now": "選択した曲を再生", "playlist-created": "{{name}} を作成しました", - "playlist-exported": "{{name}} をローカルファイルに保存しました", + "playlist-exported": "{{name}} のファイルを端末に保存しました", "queue": "再生リストをキューに追加", "rename": "再生リストの名前を変更", "songs": "{{total}} 曲", @@ -261,7 +262,7 @@ "segment-popup": "音楽以外の部分" }, "settings": { - "api-port": "APIが使用するポート", + "api-port": "API用のポート", "api-url": "", "audio": "音声", "autoradio": "ラジオ", diff --git a/packages/i18n/src/locales/ko.json b/packages/i18n/src/locales/ko.json index 2c12619400..b12201308e 100644 --- a/packages/i18n/src/locales/ko.json +++ b/packages/i18n/src/locales/ko.json @@ -127,6 +127,7 @@ "not-found": "이 음악의 가사를 찾을 수 없습니다." }, "option-control": { + "rate": "Playback speed", "autoradio": "자동 재생", "loop": "반복", "mini-player": "미니 플레이어", diff --git a/packages/i18n/src/locales/ku_KMR.json b/packages/i18n/src/locales/ku_KMR.json index a83640cc33..5d907a2ffc 100644 --- a/packages/i18n/src/locales/ku_KMR.json +++ b/packages/i18n/src/locales/ku_KMR.json @@ -127,6 +127,7 @@ "not-found": "Peyvên stranê ji bo vê stranê nehatin dîtin." }, "option-control": { + "rate": "Playback speed", "autoradio": "Autoradio", "loop": "Dubare bike", "mini-player": "Lêdera piçûk", diff --git a/packages/i18n/src/locales/lt_LT.json b/packages/i18n/src/locales/lt_LT.json index f182b8381f..0befe16a42 100644 --- a/packages/i18n/src/locales/lt_LT.json +++ b/packages/i18n/src/locales/lt_LT.json @@ -127,6 +127,7 @@ "not-found": "Jokių lyrikų šitai dainai nebuvo rasta." }, "option-control": { + "rate": "Playback speed", "autoradio": "Autoradijas", "loop": "Ciklas", "mini-player": "Mini grotuvas", diff --git a/packages/i18n/src/locales/lv_LV.json b/packages/i18n/src/locales/lv_LV.json index 8d39f6d8a5..d75cf9023b 100644 --- a/packages/i18n/src/locales/lv_LV.json +++ b/packages/i18n/src/locales/lv_LV.json @@ -127,6 +127,7 @@ "not-found": "Šai dziesmai netika atrasta lirika." }, "option-control": { + "rate": "Playback speed", "autoradio": "Autoradio", "loop": "Cilpa", "mini-player": "Mini atskaņotājs", diff --git a/packages/i18n/src/locales/nl.json b/packages/i18n/src/locales/nl.json index b636601510..d88ac7d5e2 100644 --- a/packages/i18n/src/locales/nl.json +++ b/packages/i18n/src/locales/nl.json @@ -97,7 +97,7 @@ "empty": "Geen favorieten toegevoegd", "empty-help": "Probeer wat tracks aan je favorieten toe te voegen en ze zullen hier verschijnen!", "header": "Jouw favoriete nummers", - "play-random": "Play a random track from your favorites", + "play-random": "Een willekeurig nummer uit je favorieten afspelen", "title": "Titel" }, "help": { @@ -127,6 +127,7 @@ "not-found": "Geen songtekst gevonden bij dit nummer." }, "option-control": { + "rate": "Afspeelsnelheid", "autoradio": "Automatische radio", "loop": "Herhalen", "mini-player": "Minispeler", @@ -220,21 +221,21 @@ "live": "Live", "stream-verification": { "tooltip": { - "unknown": "The status of this stream is unknown.", - "unverified": "This stream has not yet been verified by the community.", - "weakly-verified": "This stream has been verified by a couple of users.", - "verified": "This stream has been verified by the community.", - "verified-by-user": "This stream has been verified by you." + "unknown": "De status van deze stream is onbekend.", + "unverified": "Deze stream is nog niet gecontroleerd door de gemeenschap.", + "weakly-verified": "Deze stream is geverifieerd door een paar gebruikers.", + "verified": "Deze stream is geverifieerd door de gemeenschap.", + "verified-by-user": "Deze stream is door jou geverifieerd." }, "stream-status": { - "unknown": "Unknown", - "unverified": "Unverified", - "weakly-verified": "Weakly verified", - "verified": "Verified", - "verified-by-user": "Verified by you" + "unknown": "Onbekend", + "unverified": "Niet-geverifieerd", + "weakly-verified": "Zwak geverifieerd", + "verified": "Geverifieerd", + "verified-by-user": "Geverifieerd door u" }, - "verify": "Verify", - "unverify": "Unverify" + "verify": "Verifiëren", + "unverify": "Maak verificatie ongedaan" } }, "search": { @@ -305,7 +306,7 @@ "mastodon-awaiting-authorization": "Nuclear is geregistreerd op {{instanceUrl}}. Wachten op autorisatie.", "mastodon-description": "", "mastodon-instance-label": "Mastodon instantie-URL", - "mastodon-post-format-description": "Nuclear will post a status on Mastodon after each track completes playing. The above string will be the template for each post, with {{artist}}, {{title}}, and {{url}} replaced with the artist, title, and a link to the track respectively.", + "mastodon-post-format-description": "Nuclear zal een status posten op Mastodon nadat elk nummer is afgespeeld. De bovenstaande string wordt het sjabloon voor elke post, met {{artist}}, {{title}} en {{url}} vervangen door respectievelijk de artiest, titel en een link naar de track.", "mastodon-post-format-label": "Post formaat", "mastodon-title": "", "normalize": "Volume normaliseren", @@ -323,7 +324,7 @@ "promoted-artists-description": "Dit is een opeenstapeling van interessante, zorgvuldig uitgekozen indie-artiesten die u zou misschien leuk vinden. Hier krijgt niemand een vergoeding voor.", "saving-in": "Opslaan in:", "seek-iteration": "Aantal seconden om vooruit/achteruit te zoeken bij het indrukken van pijltoetsen", - "show-tray-icon": "Show tray icon", + "show-tray-icon": "Toon ladepictogram", "shuffle-queue": "Nummers willekeurig afspelen", "shuffle-when-going-back": "Shuffle bij het vorige nummer", "shuffle-when-going-back-description": "Speel een willekeurig nummer af wanneer shuffle actief is en de vorige track knop wordt aangeklikt", @@ -333,9 +334,9 @@ "streaming": "Streaming", "track-duration": "Toon de duur van de track over de zoekbalk", "user": "Gebruiker:", - "user-id": "User ID", - "use-stream-verification": "Use stream verification", - "use-stream-verification-description": "Use stream verification. Loads the streams the community has verified as being correct for the tracks you play. Also allows you to vote on streams. See documentation for details.", + "user-id": "Gebruikers ID", + "use-stream-verification": "Gebruik streamverificatie", + "use-stream-verification-description": "Gebruik streamverificatie. Hiermee worden de streams geladen waarvan de community heeft gecontroleerd dat ze correct zijn voor de tracks die je afspeelt. Je kunt ook stemmen op streams. Zie documentatie voor meer informatie.", "youtube": "Youtube", "yt-api-key": "Youtube API-sleutel" }, @@ -452,7 +453,7 @@ } }, "nuclear-sign-in": { - "header": "Aanmelden", + "header": " Aanmelden", "username-label": "Gebruikersnaam", "password-label": "Wachtwoord", "no-account-label": "Hebt u geen account?", diff --git a/packages/i18n/src/locales/no_NO.json b/packages/i18n/src/locales/no_NO.json index 5e290dbbf9..1364e757d8 100644 --- a/packages/i18n/src/locales/no_NO.json +++ b/packages/i18n/src/locales/no_NO.json @@ -127,6 +127,7 @@ "not-found": "Ingen sangtekster ble funnet for denne sangen." }, "option-control": { + "rate": "Playback speed", "autoradio": "Autostart", "loop": "Sløyfe", "mini-player": "Smittespiller", diff --git a/packages/i18n/src/locales/pl.json b/packages/i18n/src/locales/pl.json index 72d82fcb50..f2d8e2aaea 100644 --- a/packages/i18n/src/locales/pl.json +++ b/packages/i18n/src/locales/pl.json @@ -127,6 +127,7 @@ "not-found": "Nie znaleziono tekstu dla tego utworu." }, "option-control": { + "rate": "Playback speed", "autoradio": "Autoradio", "loop": "Powtarzaj", "mini-player": "Miniodtwarzacz", diff --git a/packages/i18n/src/locales/pt_BR.json b/packages/i18n/src/locales/pt_BR.json index 50f0b12bbb..b0a83c656f 100644 --- a/packages/i18n/src/locales/pt_BR.json +++ b/packages/i18n/src/locales/pt_BR.json @@ -127,6 +127,7 @@ "not-found": "Nenhuma letra foi encontrada para esta música." }, "option-control": { + "rate": "Velocidade de reprodução", "autoradio": "Autoradio", "loop": "Loop", "mini-player": "Miniplayer", diff --git a/packages/i18n/src/locales/ro_RO.json b/packages/i18n/src/locales/ro_RO.json index 37c2040fd3..12c3c6a722 100644 --- a/packages/i18n/src/locales/ro_RO.json +++ b/packages/i18n/src/locales/ro_RO.json @@ -1,63 +1,63 @@ { "album": { "download": "Download", - "download-toast-content": "{{artist}} - {{title}} has been added to downloads.", - "download-toast-title": "Album added to downloads", - "error-load": "Sorry, this album could not be loaded. Try again later or open a new issue on Github", - "genre": "Genre:", + "download-toast-content": "{{artist}} - {{title}} a fost adăugat la descărcări.", + "download-toast-title": "Album adăugat la descărcări", + "error-load": "Ne pare rău, acest album nu a putut fi încărcat. Încercați din nou mai târziu sau deschideți un nou subiect pe GitHub", + "genre": "Gen:", "play": "Play", - "queue": "Add album to queue", - "tracks": "Tracks:", - "year": "Year:" + "queue": "Adaugă album la coadă", + "tracks": "Piese:", + "year": "An:" }, "app": { - "collection": "Collection", - "dashboard": "Dashboard", - "downloads": "Downloads", - "equalizer": "Equalizer", - "favorite-albums": "Favorite Albums", - "favorite-artists": "Favorite Artists", - "favorite-tracks": "Favorite Tracks", - "library": "Local Library", - "listening-history": "Listening History", - "lyrics": "Lyrics", - "main": "Main", - "playlists": "Playlists", - "plugins": "Plugins", - "search": "Search Results", - "settings": "Settings", - "visualizer": "Visualizer", + "collection": "Colecție", + "dashboard": "Tablou de bord", + "downloads": "Descărcări", + "equalizer": "Egalizator", + "favorite-albums": "Albume favorite", + "favorite-artists": "Artiști favoriți", + "favorite-tracks": "Piese favorite", + "library": "Biblioteca locală", + "listening-history": "Istoric ascultare", + "lyrics": "Versuri", + "main": "Principal", + "playlists": "Playlist-uri", + "plugins": "Plugin-uri", + "search": "Rezultatele căutării", + "settings": "Setări", + "visualizer": "Vizualizator", "user-panel": { - "actions-tooltip": "User actions", - "sign-up": "Sign up / Sign in", - "sign-out": "Sign out" + "actions-tooltip": "Acțiuni utilizator", + "sign-up": "Înregistrare / Autentificare", + "sign-out": "Deconectare" }, - "version": "Version" + "version": "Versiune" }, "artist": { - "add-all": "Add all", - "count": "Play counts", - "queue": "Add all tracks to queue", - "similar": "Similar artists", - "title": "Title", - "tour": "On tour" + "add-all": "Adăugare toate", + "count": "Număr de redări", + "queue": "Adaugă toate piesele la coadă", + "similar": "Artiști similari", + "title": "Titlu", + "tour": "În turneu" }, "dashboard": { - "add-all": "Add all", + "add-all": "Adaugă toate", "artist": "Artist", - "best": "Best new music", - "best-new-albums": "Best new albums", - "best-new-tracks": "Best new tracks", - "genres": "Genres", - "news": "News", - "playcounts": "Playcounts", - "playlists": "Trending", - "popular-track-title": "Top Tracks from Deezer", - "title": "Title", - "top": "Top Tracks", - "trending-playlists": "Trending playlists", - "trending-artists": "Trending artists", - "trending-albums": "Trending albums", + "best": "Cea mai bună muzică nouă", + "best-new-albums": "Cele mai bune albume noi", + "best-new-tracks": "Cele mai bune piese noi", + "genres": "Genuri", + "news": "Ştiri", + "playcounts": "Număr ascultări", + "playlists": "Populare", + "popular-track-title": "Top piese din Deezer", + "title": "Titlu", + "top": "Piese de top", + "trending-playlists": "Playlist-uri populare", + "trending-artists": "Artiști populari", + "trending-albums": "Albume populare", "filter": "Filter...", "nothing-found": "Nothing found." }, @@ -122,30 +122,31 @@ }, "lyrics": { "by-artist": "by {{artist}}", - "empty": "Nothing is playing.", - "empty-help": "Add some music to the queue to display the lyrics here!", - "not-found": "No lyrics were found for this song." + "empty": "Nu rulează nimic.", + "empty-help": "Adaugă niște muzică la coadă pentru a afișa versurile aici!", + "not-found": "Nu au fost găsite versuri pentru această piesă." }, "option-control": { + "rate": "Viteza de redare", "autoradio": "Autoradio", - "loop": "Loop", + "loop": "Buclă", "mini-player": "Miniplayer", - "shuffle": "Shuffle" + "shuffle": "Amestecare" }, "player": { - "next-button": "Next track button", - "play-pause-button": "Play/Pause button", - "previous-button": "Previous track button" + "next-button": "Butonul Piesa următoare", + "play-pause-button": "Butonul Redare/Pauză", + "previous-button": "Butonul Piesa anterioară" }, "playlists": { - "add-selected-tracks-to-downloads": "Add selected to downloads", - "add-selected-tracks-to-favorites": "Add selected to favorites", - "add-selected-tracks-to-queue": "Add selected to queue", + "add-selected-tracks-to-downloads": "Adaugă selecția la Descărcări", + "add-selected-tracks-to-favorites": "Adaugă selecţia la favorite", + "add-selected-tracks-to-queue": "Adaugă selecția la coadă", "album": "Album", "artist": "Artist", - "delete": "Delete this playlist", - "duration": "Length", - "empty": "No playlists.", + "delete": "Șterge acest playlist", + "duration": "Durată", + "empty": "Niciun playlist.", "empty-help": "Try to create some playlists or import by url and they will appear here!", "error-empty-data": "Empty data", "error-invalid-data": "Invalid data", diff --git a/packages/i18n/src/locales/ru.json b/packages/i18n/src/locales/ru.json index 226dabfce5..241af6a1a0 100644 --- a/packages/i18n/src/locales/ru.json +++ b/packages/i18n/src/locales/ru.json @@ -127,6 +127,7 @@ "not-found": "Текст этой песни не найден." }, "option-control": { + "rate": "Скорость проигрывания", "autoradio": "Автоподборка", "loop": "Повтор", "mini-player": "Мини-проигрыватель", diff --git a/packages/i18n/src/locales/se.json b/packages/i18n/src/locales/se.json index 4396da409e..a6b5796157 100644 --- a/packages/i18n/src/locales/se.json +++ b/packages/i18n/src/locales/se.json @@ -19,7 +19,7 @@ "favorite-artists": "Favoritartister", "favorite-tracks": "Favoritspår", "library": "Lokalt bibliotek", - "listening-history": "Listening History", + "listening-history": "Lyssnings historik", "lyrics": "Låttexter", "main": "Huvudmeny", "playlists": "Spellistor", @@ -28,14 +28,14 @@ "settings": "Inställningar", "visualizer": "Visualiserare", "user-panel": { - "actions-tooltip": "User actions", - "sign-up": "Sign up / Sign in", - "sign-out": "Sign out" + "actions-tooltip": "Användaråtgärder", + "sign-up": "Registrera / Logga in", + "sign-out": "Logga ut" }, "version": "Version" }, "artist": { - "add-all": "Add all", + "add-all": "Lägg till alla", "count": "Antal spelningar", "queue": "Lägg till alla spår i kön", "similar": "Liknande artister", @@ -51,15 +51,15 @@ "genres": "Genrer", "news": "Nyheter", "playcounts": "Antal spelningar", - "playlists": "Trending", + "playlists": "Trendar", "popular-track-title": "Toppspår från Deezer", "title": "Titel", "top": "Toppspår", - "trending-playlists": "Trending playlists", - "trending-artists": "Trending artists", - "trending-albums": "Trending albums", - "filter": "Filter...", - "nothing-found": "Nothing found." + "trending-playlists": "Trendiga spellistor", + "trending-artists": "Trendiga artister", + "trending-albums": "Trendiga album", + "filter": "Filtrera...", + "nothing-found": "Inget kunde hittas." }, "downloads": { "clear": "Rensa lyssnade spår", @@ -71,14 +71,14 @@ "status": "Status" }, "equalizer": { - "classical": "Classical", - "club": "Club", - "custom": "Custom", - "default": "Default", + "classical": "Klassiskt", + "club": "Klubb", + "custom": "Anpassad", + "default": "Standard", "full-bass": "Full bas", "full-treble": "Full diskant", "pop": "Pop", - "presets": "Presets", + "presets": "Förval", "reggae": "Reggae", "rock": "Rock" }, @@ -97,12 +97,12 @@ "empty": "Inga favoritspår", "empty-help": "Försök lägga till några spår till favoriter och de kommer att visas här!", "header": "Dina favoritspår", - "play-random": "Play a random track from your favorites", + "play-random": "Spela ett slumpmässigt spår från dina favoriter", "title": "Titel" }, "help": { "about": "Om Nuclear musikspelare", - "contributors": "Our top 10 Contributors", + "contributors": "Våra 10 bästa bidragsgivare", "header": "Musikspelare som streamar från avgiftsfria källor", "released": "släppt under AGPL-3.0", "report": "Skicka in problem eller nya funktioner", @@ -127,6 +127,7 @@ "not-found": "Ingen låttext hittades för detta spår." }, "option-control": { + "rate": "Uppspelningshastighet", "autoradio": "Autoradio", "loop": "Slinga", "mini-player": "Minispelare", @@ -155,12 +156,12 @@ "export-fail-title": "Export av spellista misslyckades", "export-success-title": "Export av spellista lyckades", "header": "Dina spellistor", - "import-progress": "Import progress", - "spotify-import-placeholder": "Paste a Spotify playlist URL here", + "import-progress": "Importeringsframsteg", + "spotify-import-placeholder": "Klistra in en Spotify-spellistURL här", "dialog-placeholder": "Namn på spellistan...", "dialog-accept": "Spara", - "dialog-cancel": "Cancel", - "dialog-rename": "Rename", + "dialog-cancel": "Avbryt", + "dialog-rename": "Döp om", "dialog-import": "Import", "create-button": "Skapa ny spellista", "new-playlist": "Ny spellista", @@ -178,14 +179,14 @@ "title": "Titel", "tracks-selected-plural": "spår valda", "tracks-selected-singular": "spår vald", - "tracks-singular": "track", - "tracks-plural": "tracks", - "modified-at": "Last modified: ", - "never-modified": "Unknown", - "server-modified-at": "Server modified: ", - "upload-to-server": "Upload to server", - "download-from-server": "Download from server", - "number-of-tracks": "tracks" + "tracks-singular": "spår", + "tracks-plural": "spår", + "modified-at": "Senast modifierad: ", + "never-modified": "Okänd", + "server-modified-at": "Server ändrad: ", + "upload-to-server": "Ladda upp till server", + "download-from-server": "Ladda ned från server", + "number-of-tracks": "spår" }, "plugins": { "add-a-plugin": "Lägg till en insticksmodul", @@ -220,21 +221,21 @@ "live": "Live", "stream-verification": { "tooltip": { - "unknown": "The status of this stream is unknown.", - "unverified": "This stream has not yet been verified by the community.", - "weakly-verified": "This stream has been verified by a couple of users.", - "verified": "This stream has been verified by the community.", - "verified-by-user": "This stream has been verified by you." + "unknown": "Statusen för denna ström är okänd.", + "unverified": "Denna ström har ännu inte verifierats av communityn.", + "weakly-verified": "Denna ström har verifierats av några användare.", + "verified": "Denna ström har blivit verifierad av communityn.", + "verified-by-user": "Denna ström har blivit verifierad av dig." }, "stream-status": { - "unknown": "Unknown", - "unverified": "Unverified", - "weakly-verified": "Weakly verified", - "verified": "Verified", - "verified-by-user": "Verified by you" + "unknown": "Okänd", + "unverified": "Icke verifierad", + "weakly-verified": "Svagt verifierad", + "verified": "Verifierad", + "verified-by-user": "Verifierad av dig" }, - "verify": "Verify", - "unverify": "Unverify" + "verify": "Verifiera", + "unverify": "Avverifiera" } }, "search": { @@ -406,20 +407,20 @@ "minimize": "Minimize", "maximize": "Maximize", "go-to-dashboard": "Go to dashboard", - "go-to-downloads": "Go to downloads", - "go-to-lyrics": "Go to lyrics", - "go-to-plugins": "Go to plugins", - "go-to-search": "Go to search", - "go-to-settings": "Go to settings", - "go-to-equalizer": "Go to equalizer", - "go-to-visualizer": "Go to visualizer", - "go-to-playlists": "Go to playlists", - "go-to-favorite-tracks": "Go to favorite tracks", - "go-to-library": "Go to local library" + "go-to-downloads": "Gå till nerladdningar", + "go-to-lyrics": "Gå till låttext", + "go-to-plugins": "Gå till tillägg", + "go-to-search": "Hoppa till sökrutan", + "go-to-settings": "Gå till inställningar", + "go-to-equalizer": "Gå till equalizer", + "go-to-visualizer": "Gå till visualiserare", + "go-to-playlists": "Gå till spellistor", + "go-to-favorite-tracks": "Gå till favoritspår", + "go-to-library": "Gå till det lokala biblioteket" } }, "listening-history": { - "title": "Listening history", + "title": "Lyssnings historik", "empty-state": "You haven't listened to anything yet.", "clear-history": "Clear history", "clear-history-confirm": "Are you sure you want to clear your listening history?", diff --git a/packages/i18n/src/locales/sk.json b/packages/i18n/src/locales/sk.json index 3f12518d27..a435e79912 100644 --- a/packages/i18n/src/locales/sk.json +++ b/packages/i18n/src/locales/sk.json @@ -127,6 +127,7 @@ "not-found": "Pre túto pieseň neboli nájdené žiadne titulky." }, "option-control": { + "rate": "Playback speed", "autoradio": "Autorádio", "loop": "Slučka", "mini-player": "Miniprehrávač", diff --git a/packages/i18n/src/locales/sq.json b/packages/i18n/src/locales/sq.json index f32c601a9f..c712577442 100644 --- a/packages/i18n/src/locales/sq.json +++ b/packages/i18n/src/locales/sq.json @@ -127,6 +127,7 @@ "not-found": "Nuk u gjet teksti për këtë këngë." }, "option-control": { + "rate": "Playback speed", "autoradio": "Autoradio", "loop": "Cikël i përsëritur", "mini-player": "Miniplayer", diff --git a/packages/i18n/src/locales/ta_IN.json b/packages/i18n/src/locales/ta_IN.json new file mode 100644 index 0000000000..a142ca28b2 --- /dev/null +++ b/packages/i18n/src/locales/ta_IN.json @@ -0,0 +1,464 @@ +{ + "album": { + "download": "Download", + "download-toast-content": "{{artist}} - {{title}} has been added to downloads.", + "download-toast-title": "Album added to downloads", + "error-load": "Sorry, this album could not be loaded. Try again later or open a new issue on Github", + "genre": "Genre:", + "play": "Play", + "queue": "Add album to queue", + "tracks": "Tracks:", + "year": "Year:" + }, + "app": { + "collection": "Collection", + "dashboard": "Dashboard", + "downloads": "Downloads", + "equalizer": "Equalizer", + "favorite-albums": "Favorite Albums", + "favorite-artists": "Favorite Artists", + "favorite-tracks": "Favorite Tracks", + "library": "Local Library", + "listening-history": "Listening History", + "lyrics": "Lyrics", + "main": "Main", + "playlists": "Playlists", + "plugins": "Plugins", + "search": "Search Results", + "settings": "Settings", + "visualizer": "Visualizer", + "user-panel": { + "actions-tooltip": "User actions", + "sign-up": "Sign up / Sign in", + "sign-out": "Sign out" + }, + "version": "Version" + }, + "artist": { + "add-all": "Add all", + "count": "Play counts", + "queue": "Add all tracks to queue", + "similar": "Similar artists", + "title": "Title", + "tour": "On tour" + }, + "dashboard": { + "add-all": "Add all", + "artist": "Artist", + "best": "Best new music", + "best-new-albums": "Best new albums", + "best-new-tracks": "Best new tracks", + "genres": "Genres", + "news": "News", + "playcounts": "Playcounts", + "playlists": "Trending", + "popular-track-title": "Top Tracks from Deezer", + "title": "Title", + "top": "Top Tracks", + "trending-playlists": "Trending playlists", + "trending-artists": "Trending artists", + "trending-albums": "Trending albums", + "filter": "Filter...", + "nothing-found": "Nothing found." + }, + "downloads": { + "clear": "Clear finished tracks", + "completion": "Completion", + "empty": "Downloads are empty.", + "empty-help": "Add something to your download queue and you'll see it here!", + "header": "Downloads", + "name": "Name", + "status": "Status" + }, + "equalizer": { + "classical": "Classical", + "club": "Club", + "custom": "Custom", + "default": "Default", + "full-bass": "Full Bass", + "full-treble": "Full Treble", + "pop": "Pop", + "presets": "Presets", + "reggae": "Reggae", + "rock": "Rock" + }, + "favorite-albums": { + "empty": "No favorite albums", + "empty-help": "Try adding some albums to favorites and they will appear here!", + "header": "Your favorite albums" + }, + "favorite-artists": { + "empty": "No favorite artists", + "empty-help": "Try adding some artists to favorites and they will appear here!", + "header": "Your favorite artists" + }, + "favorites": { + "artist": "Artist", + "empty": "No favorites added", + "empty-help": "Try adding some tracks to favorites and they will appear here!", + "header": "Your favorite tracks", + "play-random": "Play a random track from your favorites", + "title": "Title" + }, + "help": { + "about": "About Nuclear Music Player", + "contributors": "Our top 10 Contributors", + "header": "Desktop music player for streaming from free sources", + "released": "released under AGPL-3.0", + "report": "Submit issues or new features", + "thanks": "Many thanks to our contributors on Github, your help was vital in creating this program." + }, + "library": { + "add": "Add folders", + "album": "Album", + "artist": "Artist", + "empty": "The library is empty", + "empty-help": "Try adding some music using the button above.", + "filter-placeholder": "Filter...", + "header": "Local Library", + "no-search-results": "No search results", + "no-search-results-help": "No tracks found with the given name, album, or artist.", + "title": "Title" + }, + "lyrics": { + "by-artist": "by {{artist}}", + "empty": "Nothing is playing.", + "empty-help": "Add some music to the queue to display the lyrics here!", + "not-found": "No lyrics were found for this song." + }, + "option-control": { + "rate": "Playback speed", + "autoradio": "Autoradio", + "loop": "Loop", + "mini-player": "Miniplayer", + "shuffle": "Shuffle" + }, + "player": { + "next-button": "Next track button", + "play-pause-button": "Play/Pause button", + "previous-button": "Previous track button" + }, + "playlists": { + "add-selected-tracks-to-downloads": "Add selected to downloads", + "add-selected-tracks-to-favorites": "Add selected to favorites", + "add-selected-tracks-to-queue": "Add selected to queue", + "album": "Album", + "artist": "Artist", + "delete": "Delete this playlist", + "duration": "Length", + "empty": "No playlists.", + "empty-help": "Try to create some playlists or import by url and they will appear here!", + "error-empty-data": "Empty data", + "error-invalid-data": "Invalid data", + "error-open-file": "Can not open file", + "error-save-file": "Can not save file", + "export-button": "Export playlist (JSON)", + "export-fail-title": "Playlist export fail", + "export-success-title": "Playlist exported successfully", + "header": "Your playlists", + "import-progress": "Import progress", + "spotify-import-placeholder": "Paste a Spotify playlist URL here", + "dialog-placeholder": "Playlist name...", + "dialog-accept": "Save", + "dialog-cancel": "Cancel", + "dialog-rename": "Rename", + "dialog-import": "Import", + "create-button": "Create new playlist", + "new-playlist": "New playlist", + "import-url-button": "Import from url (Spotify)", + "import-button": "Import from file (JSON)", + "import-fail-title": "Playlist import fail", + "import-success-title": "Playlist imported successfully", + "play-selected-tracks-now": "Play selected now", + "playlist-created": "{{name}} created", + "playlist-exported": "{{name}} saved to local file", + "queue": "Add playlist to queue", + "rename": "Rename this playlist", + "songs": "{{total}} song", + "songs_plural": "{{total}} songs", + "title": "Title", + "tracks-selected-plural": "tracks selected", + "tracks-selected-singular": "track selected", + "tracks-singular": "track", + "tracks-plural": "tracks", + "modified-at": "Last modified: ", + "never-modified": "Unknown", + "server-modified-at": "Server modified: ", + "upload-to-server": "Upload to server", + "download-from-server": "Download from server", + "number-of-tracks": "tracks" + }, + "plugins": { + "add-a-plugin": "Add a plugin", + "header": "Plugins", + "lyrics-providers": "Lyrics providers", + "meta-providers": "Metadata providers", + "placeholder": "Select the default music source:", + "select-lyrics-provider": "Select the default lyrics provider:", + "select-meta-provider": "Select the default metadata provider:", + "stream-providers": "Stream providers", + "user-plugins": "User plugins", + "user-plugins-warning-desc": "Plugins work by running code on your computer. Load plugins only from sources you trust!", + "user-plugins-warning-title": "Danger zone!" + }, + "queue": { + "clear": "Clear queue", + "dialog-accept": "Save", + "dialog-cancel": "Cancel", + "dialog-placeholder": "Playlist name...", + "dialog-trigger": "Save as playlist", + "download": "Download", + "download-toast-content": "{{artist}} - {{title}} has been added to downloads.", + "download-toast-title": "Track added to downloads", + "favorite-add": "Add to favorites", + "header": "Queue", + "header-track": "Current track", + "copy-track-url": "Copy track url to clipboard", + "loading": "Stream still loading.", + "playlist-add": "Add to playlist", + "playlist-toast-content": "Playlist {{name}} has been created.", + "playlist-toast-title": "Playlist created", + "live": "Live", + "stream-verification": { + "tooltip": { + "unknown": "The status of this stream is unknown.", + "unverified": "This stream has not yet been verified by the community.", + "weakly-verified": "This stream has been verified by a couple of users.", + "verified": "This stream has been verified by the community.", + "verified-by-user": "This stream has been verified by you." + }, + "stream-status": { + "unknown": "Unknown", + "unverified": "Unverified", + "weakly-verified": "Weakly verified", + "verified": "Verified", + "verified-by-user": "Verified by you" + }, + "verify": "Verify", + "unverify": "Unverify" + } + }, + "search": { + "album": "Album", + "album_plural": "Albums", + "all": "All", + "artist": "Artist", + "artist_plural": "Artists", + "clear-history": "Clear history", + "empty": "Nothing found.", + "last-searches": "Last searches", + "live-stream": "LiveStream", + "placeholder": "Search...", + "playlist": "Playlist", + "playlist_plural": "Playlists", + "podcast": "Podcast", + "queue-add": "Add all tracks to queue", + "track": "Track", + "track_plural": "Tracks", + "you-can-search-for": "You can search for:" + }, + "seekbar": { + "live": "Live", + "segment-popup": "Non-music" + }, + "settings": { + "api-port": "Port used by the api", + "api-url": "", + "audio": "Audio", + "autoradio": "Autoradio", + "autoradio-craziness": "Autoradio craziness", + "autoradio-craziness-description": "Autoradio will select songs that are less similar to the ones already in the queue the crazier it is", + "autoradio-description": "Add similar tracks automatically when the queue is ending", + "listening-history": "Listening history", + "listening-history-description": "Log the tracks you listen to, a la Last.fm. The history is stored offline.", + "compact-menu-bar": "Use compact style for menu bar", + "compact-queue-bar": "Use compact style for queue bar", + "developer": "Developer settings", + "devtools": "Developer tools", + "disable-gpu": "Disable hardware rendering (might fix issues with dragging elements and flashing screen)", + "display": "Display", + "downloads": "Downloads", + "downloads-count": "Max simultaneous downloads", + "downloads-dir": "Downloads directory", + "downloads-dir-button": "Choose a directory...", + "enable-api": "Enable the api", + "fmfav-btn": "Import", + "fmfav-msg": "Import your Last.fm Favorites", + "frameless-window": "Frameless window (requires restart)", + "github-connect": "Log in with Github", + "github-description": "Log in via Github to be able to create and share your playlists online (upcoming feature).", + "github-title": "Github", + "http": "HTTP API", + "invidious-url": "Invidious instance url", + "language": "Language", + "language-placeholder": "Pick a language", + "lastfm-connect": "Connect with Last.fm", + "lastfm-description": "In order to enable scrobbling, you first have to connect and authorize Nuclear on Last.fm, then click log in.", + "lastfm-enable": "Enable scrobbling to Last.fm", + "lastfm-title": "Last.fm", + "less": "Less", + "login": "Log in", + "logout": "Log out", + "loop-after-queue-end": "Loop after playing the last queue item", + "mastodon-authorization-token-label": "Authorization token", + "mastodon-authorize": "Authorize", + "mastodon-authorized": "Nuclear is authorized to post on ", + "mastodon-awaiting-authorization": "Nuclear is registered on {{instanceUrl}}. Awaiting authorization.", + "mastodon-description": "", + "mastodon-instance-label": "Mastodon instance URL", + "mastodon-post-format-description": "Nuclear will post a status on Mastodon after each track completes playing. The above string will be the template for each post, with {{artist}}, {{title}}, and {{url}} replaced with the artist, title, and a link to the track respectively.", + "mastodon-post-format-label": "Post format", + "mastodon-title": "", + "normalize": "Normalize volume", + "normalize-description": "Automatically adjust volume of tracks so that they are played at the same level. This needs to fetch the whole track to work, so it may cause a delay between tracks.", + "nuclear-identity-service-url": "Nuclear identity service URL", + "nuclear-playlists-service-url": "Nuclear playlists service URL", + "mini-player": "Use mini player style", + "more": "More", + "discord-rich-presence": "Toggle discord rich presence (requires restart)", + "notification-timeout": "Notification timeout", + "notlogged": "Not logged in", + "playback": "Playback", + "program-settings": "Program settings", + "promoted-artists": "Show promoted artists in the dashboard", + "promoted-artists-description": "This is a reel of interesting, handpicked indie artists you might like. No one's receiving any compensation for this.", + "saving-in": "Saving in:", + "seek-iteration": "Number of seconds to seek forward/backwards when pressing arrow keys", + "show-tray-icon": "Show tray icon", + "shuffle-queue": "Shuffle songs", + "shuffle-when-going-back": "Shuffle on previous track", + "shuffle-when-going-back-description": "Play a random track when shuffle is active and the previous track button is clicked", + "social": "Social", + "skip-sponsorblock": "Skip non-music segment", + "skip-sponsorblock-description": "Playback skips non-music segment from SponsorBlock (https://sponsor.ajay.app/)", + "streaming": "Streaming", + "track-duration": "Display track duration over the seekbar", + "user": "User:", + "user-id": "User ID", + "use-stream-verification": "Use stream verification", + "use-stream-verification-description": "Use stream verification. Loads the streams the community has verified as being correct for the tracks you play. Also allows you to vote on streams. See documentation for details.", + "youtube": "Youtube", + "yt-api-key": "Youtube API Key" + }, + "tags": { + "albums": "Top Albums", + "artist": "Artist", + "artists": "Top Artists", + "duration": "Duration", + "queue-add": "Add all tracks to queue", + "title": "Title" + }, + "track-popup": { + "add-to-favorite": "Add to favorites", + "add-to-playlist": "Add to playlist", + "add-to-queue": "Add to queue", + "create-playlist": "Create new playlist", + "create-playlist-dialog-title": "Input playlist name:", + "create-playlist-dialog-placeholder": "Playlist name...", + "create-playlist-dialog-accept": "Save", + "create-playlist-dialog-cancel": "Cancel", + "download": "Download", + "download-toast-body": "{{artist}} - {{track}} has been added to downloads.", + "download-toast-title": "Track added to downloads", + "favorite-toast-body": "{{artist}} - {{track}} has been added to favorites.", + "favorite-toast-title": "Favorite track added", + "play-next": "Play next", + "play-now": "Play now", + "playlist-toast-body": "{{artist}} - {{track}} has been added to playlist.", + "playlist-toast-title": "Track added to playlist" + }, + "track-table": { + "add-selected-tracks-to-queue": "Add selected to queue", + "add-selected-tracks-to-downloads": "Add selected to downloads", + "add-selected-tracks-to-favorites": "Add selected to favorites", + "play-selected-tracks-now": "Play selected now", + "tracks-selected-label-singular": "track selected", + "tracks-selected-label-plural": "tracks selected" + }, + "visualizer": { + "exit-fullscreen": "Press ESC to exit full screen" + }, + "command-palette": { + "search-placeholder": "What would you like to do?", + "empty-state-help": "Can't seem to find what you're looking for? Try the help command.", + "protip-text": "PROTIP:", + "protip-content": "Arrows to navigate, Enter to select, Esc to close", + "categories": { + "playback": "Playback", + "queue": "Queue", + "navigation": "Navigation", + "application": "Application" + }, + "actions": { + "play": "Play", + "pause": "Pause", + "next": "Next", + "previous": "Previous", + "go-to-next-page": "Go to next page", + "go-to-previous-page": "Go to previous page", + "shuffle": "Shuffle", + "loop": "Loop", + "autoradio": "Autoradio", + "raise-volume": "Raise volume", + "lower-volume": "Lower volume", + "mute": "Mute", + "unmute": "Unmute", + "quit": "Quit", + "minimize": "Minimize", + "maximize": "Maximize", + "go-to-dashboard": "Go to dashboard", + "go-to-downloads": "Go to downloads", + "go-to-lyrics": "Go to lyrics", + "go-to-plugins": "Go to plugins", + "go-to-search": "Go to search", + "go-to-settings": "Go to settings", + "go-to-equalizer": "Go to equalizer", + "go-to-visualizer": "Go to visualizer", + "go-to-playlists": "Go to playlists", + "go-to-favorite-tracks": "Go to favorite tracks", + "go-to-library": "Go to local library" + } + }, + "listening-history": { + "title": "Listening history", + "empty-state": "You haven't listened to anything yet.", + "clear-history": "Clear history", + "clear-history-confirm": "Are you sure you want to clear your listening history?", + "clear-history-confirm-yes": "Confirm", + "clear-history-confirm-no": "Cancel", + "clear-history-toast": "Listening history cleared" + }, + "forms": { + "nuclear-sign-up": { + "header": "Sign up", + "secondary-header": "Sign up to Nuclear Web Services", + "side-paragraph-1": "NWS enables you to backup your playlists online and share them.", + "side-paragraph-2": "Providing your email is optional; it will allow you to recover your account if you forget your password.", + "username-label": "Username", + "email-label": "Email", + "password-label": "Password", + "sign-up-button": "Sign up", + "validation": { + "username": { + "length": "Username must be 4 characters or more", + "required": "Username is required" + }, + "email": { + "invalid": "Email must be a valid email" + }, + "password": { + "length": "Password must be 6 characters or more", + "required": "Password is required" + } + } + }, + "nuclear-sign-in": { + "header": " Sign in", + "username-label": "Username", + "password-label": "Password", + "no-account-label": "Don't have an account?", + "no-account-link": "Sign up", + "sign-in-button": "Sign in" + } + } +} \ No newline at end of file diff --git a/packages/i18n/src/locales/tk_TM.json b/packages/i18n/src/locales/tk_TM.json index 37c2040fd3..a142ca28b2 100644 --- a/packages/i18n/src/locales/tk_TM.json +++ b/packages/i18n/src/locales/tk_TM.json @@ -127,6 +127,7 @@ "not-found": "No lyrics were found for this song." }, "option-control": { + "rate": "Playback speed", "autoradio": "Autoradio", "loop": "Loop", "mini-player": "Miniplayer", diff --git a/packages/i18n/src/locales/tl.json b/packages/i18n/src/locales/tl.json index 516afdcd37..289eabaf99 100644 --- a/packages/i18n/src/locales/tl.json +++ b/packages/i18n/src/locales/tl.json @@ -127,6 +127,7 @@ "not-found": "Walang liriko nahanap para sa kantang ito." }, "option-control": { + "rate": "Playback speed", "autoradio": "Autoradio", "loop": "Paulit", "mini-player": "Miniplayer", diff --git a/packages/i18n/src/locales/tr.json b/packages/i18n/src/locales/tr.json index 0bd69ba1a5..e071ab1f0b 100644 --- a/packages/i18n/src/locales/tr.json +++ b/packages/i18n/src/locales/tr.json @@ -19,7 +19,7 @@ "favorite-artists": "Favori Şarkıcılar", "favorite-tracks": "Favori Şarkılar", "library": "Yerel Kütüphane", - "listening-history": "Listening History", + "listening-history": "Dinleme Geçmişi", "lyrics": "Şarkı sözleri", "main": "Ana", "playlists": "Çalma Listeleri", @@ -51,15 +51,15 @@ "genres": "Türler", "news": "Haberler", "playcounts": "Dinlenme sayısı", - "playlists": "Trending", + "playlists": "Öne Çıkanlar", "popular-track-title": "Deezer'de en popüler şarkılar", "title": "Şarkı Adı", "top": "Popüler Şarkılar", - "trending-playlists": "Trending playlists", - "trending-artists": "Trending artists", - "trending-albums": "Trending albums", - "filter": "Filter...", - "nothing-found": "Nothing found." + "trending-playlists": "Trend Oynatma listesi", + "trending-artists": "Trend sanatçılar", + "trending-albums": "Trend albümler", + "filter": "Filtrele...", + "nothing-found": "Bulunamadı." }, "downloads": { "clear": "İndirilenleri temizle", @@ -97,7 +97,7 @@ "empty": "Favori eklenmedi", "empty-help": "Şarkıları favorilerine eklediğinde burada görebilirsin!", "header": "Favori Şarkıların", - "play-random": "Play a random track from your favorites", + "play-random": "Favoriler listenden rastgele bir şarkı çal", "title": "Şarkı Adı" }, "help": { @@ -127,6 +127,7 @@ "not-found": "Şarkı sözleri bulunamadı." }, "option-control": { + "rate": "Oynatma hızı", "autoradio": "Autoradio", "loop": "Tekrarla", "mini-player": "Mini Oynatıcı", @@ -220,21 +221,21 @@ "live": "Canlı", "stream-verification": { "tooltip": { - "unknown": "The status of this stream is unknown.", - "unverified": "This stream has not yet been verified by the community.", - "weakly-verified": "This stream has been verified by a couple of users.", - "verified": "This stream has been verified by the community.", - "verified-by-user": "This stream has been verified by you." + "unknown": "Bu akışın durumu bilinmiyor.", + "unverified": "Bu akış topluluk tarafından henüz doğrulanmadı.", + "weakly-verified": "Bu akış birkaç kullanıcı tarafından doğrulandı.", + "verified": "Bu akış topluluk tarafından doğrulandı.", + "verified-by-user": "Bu akış senin tarafından doğrulandı." }, "stream-status": { - "unknown": "Unknown", - "unverified": "Unverified", - "weakly-verified": "Weakly verified", - "verified": "Verified", - "verified-by-user": "Verified by you" + "unknown": "Bilinmiyor", + "unverified": "Doğrulanmamış", + "weakly-verified": "Zayıf olarak doğrulandı", + "verified": "Doğrulandı", + "verified-by-user": "Senin tarafından doğrulandı" }, - "verify": "Verify", - "unverify": "Unverify" + "verify": "Doğrula", + "unverify": "Doğrulamayı kaldır" } }, "search": { @@ -263,13 +264,13 @@ "settings": { "api-port": "Api tarafından kullanılan port", "api-url": "", - "audio": "Audio", + "audio": "Ses", "autoradio": "Autoradio", "autoradio-craziness": "Autoradio Çılgınlığı", "autoradio-craziness-description": "Autoradio çılgınlaştıkça kuyruktaki şarkılara daha az benzeyen şarkıları seçer", "autoradio-description": "Eğer kuyruk bitiyorsa otomatik olarak benzer şarkılar ekle", - "listening-history": "Listening history", - "listening-history-description": "Log the tracks you listen to, a la Last.fm. The history is stored offline.", + "listening-history": "Dinleme Geçmişi", + "listening-history-description": "Dinlediğin parçaları la Last.fm e kaydet. Geçmiş çevrimdışı kaydedilir.", "compact-menu-bar": "Menü için kompakt stili kullan", "compact-queue-bar": "Kuyruk barı için kompakt stili kullan", "developer": "Geliştirici ayarları", @@ -305,11 +306,11 @@ "mastodon-awaiting-authorization": "Nuclear {{instanceUrl}} ile kaydedildi. Yetkilendirme bekleniyor.", "mastodon-description": "", "mastodon-instance-label": "Mastodon bağlantı URL'niz", - "mastodon-post-format-description": "Nuclear will post a status on Mastodon after each track completes playing. The above string will be the template for each post, with {{artist}}, {{title}}, and {{url}} replaced with the artist, title, and a link to the track respectively.", + "mastodon-post-format-description": "Nuclear her parça çalındıktan sonra Mastodon'a durum gönderir. Yukarıdaki dizi her gönderi için şablon olarak kullanılır. {{artist}}, {{title}}, ve {{url}} kısımları sırasıyla sanatçı, başlık ve bağlantıyla değiştirilir.", "mastodon-post-format-label": "Gönderi biçimi", "mastodon-title": "", - "normalize": "Normalize volume", - "normalize-description": "Automatically adjust volume of tracks so that they are played at the same level. This needs to fetch the whole track to work, so it may cause a delay between tracks.", + "normalize": "Sesi Normalleştir", + "normalize-description": "Aynı ses düzeyinde çalınması için parçaların sesini otomatik olarak ayarla. Bu tüm parçayı indireceğinden parçalar arasında gecikmeye yol açabilir.", "nuclear-identity-service-url": "Nuclear kimlik servis bağlantısı", "nuclear-playlists-service-url": "Nuclear oynatma listesi servis bağlantısı", "mini-player": "Mini-oynatıcıyı kullan", @@ -319,11 +320,11 @@ "notlogged": "Giriş yapılmadı", "playback": "Oynatıcı", "program-settings": "Program Ayarları", - "promoted-artists": "Show promoted artists in the dashboard", - "promoted-artists-description": "This is a reel of interesting, handpicked indie artists you might like. No one's receiving any compensation for this.", + "promoted-artists": "Desteklenen sanatçıları gösterge panelinde göster", + "promoted-artists-description": "Bu gerçekten sevebileceğin, özenle seçilmiş bir sanatçı. Hiç kimse bunun için bir karşılık almıyor.", "saving-in": "Kaydedildiği yer:", "seek-iteration": "Ok tuşlarına basıldığında ileri/geri oynayan saniye sayısı", - "show-tray-icon": "Show tray icon", + "show-tray-icon": "Tepsi simgesini göster", "shuffle-queue": "Şarkıları karıştır", "shuffle-when-going-back": "Önceki şarkıya dönerken de karıştır", "shuffle-when-going-back-description": "Eğer karışık çalma aktifse önceki şarkı butonuna bastığında da karıştır", @@ -333,9 +334,9 @@ "streaming": "Yayın Akışı", "track-duration": "Şarkı uzunluğunu oynatma çubuğu üzerinde göster", "user": "Kullanıcı:", - "user-id": "User ID", - "use-stream-verification": "Use stream verification", - "use-stream-verification-description": "Use stream verification. Loads the streams the community has verified as being correct for the tracks you play. Also allows you to vote on streams. See documentation for details.", + "user-id": "Kullanıcı Kimliği", + "use-stream-verification": "Akış doğrulaması kullan", + "use-stream-verification-description": "Akış doğrulama kullan. Kullanıcıların doğru olduğunu onayladığı akışları yükler. Aynı zamanda akışlara oy vermenizi sağlar. Detaylar için belgelere bakınız.", "youtube": "Youtube", "yt-api-key": "Youtube API Anahtarı" }, @@ -351,11 +352,11 @@ "add-to-favorite": "Favorilere ekle", "add-to-playlist": "Çalma listesine ekle", "add-to-queue": "Kuyruğa ekle", - "create-playlist": "Create new playlist", - "create-playlist-dialog-title": "Input playlist name:", - "create-playlist-dialog-placeholder": "Playlist name...", - "create-playlist-dialog-accept": "Save", - "create-playlist-dialog-cancel": "Cancel", + "create-playlist": "Yeni çalma listesi oluştur", + "create-playlist-dialog-title": "Çalma listesi adı girin:", + "create-playlist-dialog-placeholder": "Çalma listesi adı...", + "create-playlist-dialog-accept": "Kaydet", + "create-playlist-dialog-cancel": "İptal Et", "download": "İndir", "download-toast-body": "{{artist}} - {{track}} indirilenlere eklendi.", "download-toast-title": "Şarkı indirilenlere eklendi", @@ -419,13 +420,13 @@ } }, "listening-history": { - "title": "Listening history", - "empty-state": "You haven't listened to anything yet.", - "clear-history": "Clear history", - "clear-history-confirm": "Are you sure you want to clear your listening history?", - "clear-history-confirm-yes": "Confirm", - "clear-history-confirm-no": "Cancel", - "clear-history-toast": "Listening history cleared" + "title": "Dinleme Geçmişi", + "empty-state": "Henüz bir şey dinlemedin.", + "clear-history": "Geçmişi temizle", + "clear-history-confirm": "Arama geçmişinizi silmek istediğinizden emin misiniz?", + "clear-history-confirm-yes": "Onayla", + "clear-history-confirm-no": "İptal Et", + "clear-history-toast": "Dinleme geçmişi silindi" }, "forms": { "nuclear-sign-up": { diff --git a/packages/i18n/src/locales/uk_UA.json b/packages/i18n/src/locales/uk_UA.json index 4113f67af1..4ccb2941b9 100644 --- a/packages/i18n/src/locales/uk_UA.json +++ b/packages/i18n/src/locales/uk_UA.json @@ -23,7 +23,7 @@ "lyrics": "Слова", "main": "Головна", "playlists": "Грайлисти", - "plugins": "Додатки", + "plugins": "Втулки", "search": "Результати Пошуку", "settings": "Налаштування", "visualizer": "Унаочник", @@ -102,10 +102,10 @@ }, "help": { "about": "Про Nuclear Music Player", - "contributors": "Наші 10 найкращих Співтворців", + "contributors": "Наші 10 найкращих Внесників", "header": "Стільничний програвач музики з відкритих джерел", "released": "випущено під ліцензією AGPL-3.0", - "report": "Заявіть про помилку або нові функції", + "report": "Заявити про помилки або запропонувати нові функції", "thanks": "Безмежна подяка нашим співтворцям на Github, ваш вклад був життєво необхідним у створенні цієї програми." }, "library": { @@ -127,6 +127,7 @@ "not-found": "Слів до цієї пісні не знайдено." }, "option-control": { + "rate": "Швидкість відтворення", "autoradio": "Авторадіо", "loop": "Повтор", "mini-player": "Мініпрогравач", @@ -188,16 +189,16 @@ "number-of-tracks": "доріжки" }, "plugins": { - "add-a-plugin": "Додати додаток", - "header": "Додатки", + "add-a-plugin": "Додати втулок", + "header": "Втулки", "lyrics-providers": "Надавачі слів до пісень", "meta-providers": "Надавачі метаданих", "placeholder": "Обрати джерело музики за замовчуванням:", "select-lyrics-provider": "Обрати надавача слів до пісень за замовчуванням:", "select-meta-provider": "Обрати надавача метаданих за замовчуванням:", "stream-providers": "Надавачі потоків", - "user-plugins": "Користувацькі додатки", - "user-plugins-warning-desc": "Додатки працюють запустивши код на вашому комп'ютері. Додавайте додатки лише з довірених джерел!", + "user-plugins": "Користувацькі втулки", + "user-plugins-warning-desc": "Втулки працюють запустивши код на вашому комп'ютері. Додавайте втулки лише з довірених джерел!", "user-plugins-warning-title": "Зона підвищеної небезпеки!" }, "queue": { @@ -229,7 +230,7 @@ "stream-status": { "unknown": "Невідомо", "unverified": "Непідтверджено", - "weakly-verified": "Слабко підтверджено", + "weakly-verified": "Ледь підтверджено", "verified": "Підтверджено", "verified-by-user": "Підтверджено Вами" }, @@ -292,7 +293,7 @@ "language": "Мова", "language-placeholder": "Вибрати мову", "lastfm-connect": "Увійти за допомогою Last.fm", - "lastfm-description": "Щоб увімкнути скроблинґ, спершу потрібно під'єднатися та авторизувати Nuclear на Last.fm, а потім натиснути Увійти.", + "lastfm-description": "Щоб увімкнути скроблинґ, спершу потрібно під'єднатися й авторизувати Nuclear на Last.fm, а потім натиснути Увійти.", "lastfm-enable": "Увімкнути скроблинґ для Last.fm", "lastfm-title": "Last.fm", "less": "Менше", @@ -408,7 +409,7 @@ "go-to-dashboard": "Перейти на головну", "go-to-downloads": "Перейти до завантажень", "go-to-lyrics": "Перейти до слів", - "go-to-plugins": "Перейти до додатків", + "go-to-plugins": "Перейти до втулків", "go-to-search": "Перейти до пошуку", "go-to-settings": "Перейти до налаштувань", "go-to-equalizer": "Перейти до рівнів", diff --git a/packages/i18n/src/locales/vi.json b/packages/i18n/src/locales/vi.json index 48e3de88f0..6adfd9a8c9 100644 --- a/packages/i18n/src/locales/vi.json +++ b/packages/i18n/src/locales/vi.json @@ -127,6 +127,7 @@ "not-found": "Không tìm thấy lời bài hát." }, "option-control": { + "rate": "Playback speed", "autoradio": "Tự động tìm kiếm bài hát", "loop": "Phát lặp lại", "mini-player": "Thu nhỏ trình phát", diff --git a/packages/i18n/src/locales/yue_CN.json b/packages/i18n/src/locales/yue_CN.json index 91ea98b26e..87b2f6cf40 100644 --- a/packages/i18n/src/locales/yue_CN.json +++ b/packages/i18n/src/locales/yue_CN.json @@ -127,6 +127,7 @@ "not-found": "No lyrics were found for this song." }, "option-control": { + "rate": "Playback speed", "autoradio": "Autoradio", "loop": "Loop", "mini-player": "Miniplayer", diff --git a/packages/i18n/src/locales/zh.json b/packages/i18n/src/locales/zh.json index 3d6026a44f..fea1eaca8f 100644 --- a/packages/i18n/src/locales/zh.json +++ b/packages/i18n/src/locales/zh.json @@ -19,7 +19,7 @@ "favorite-artists": "最喜欢的艺术家", "favorite-tracks": "收藏的曲目", "library": "本地曲库", - "listening-history": "Listening History", + "listening-history": "聆听历史记录", "lyrics": "歌词", "main": "主界面", "playlists": "播放列表", @@ -28,7 +28,7 @@ "settings": "设置", "visualizer": "可视化", "user-panel": { - "actions-tooltip": "User actions", + "actions-tooltip": "用户操作", "sign-up": "注册/登录", "sign-out": "注销" }, @@ -71,14 +71,14 @@ "status": "状态" }, "equalizer": { - "classical": "Classical", + "classical": "古典", "club": "Club", "custom": "自定义", "default": "默认", "full-bass": "全低音", "full-treble": "Full Treble", "pop": "Pop", - "presets": "Presets", + "presets": "默认", "reggae": "Reggae", "rock": "Rock" }, @@ -127,6 +127,7 @@ "not-found": "这首歌未找到歌词" }, "option-control": { + "rate": "Playback speed", "autoradio": "随便听听", "loop": "循环", "mini-player": "迷你版播放器", @@ -250,7 +251,7 @@ "placeholder": "搜索...", "playlist": "播放列表", "playlist_plural": "播放列表", - "podcast": "Podcast", + "podcast": "播客", "queue-add": "将全部歌曲添加到播放列表", "track": "音乐标题", "track_plural": "曲目", diff --git a/packages/i18n/src/locales/zh_TW.json b/packages/i18n/src/locales/zh_TW.json index 9f418cbfd7..479799ec23 100644 --- a/packages/i18n/src/locales/zh_TW.json +++ b/packages/i18n/src/locales/zh_TW.json @@ -12,21 +12,21 @@ }, "app": { "collection": "收藏", - "dashboard": "控制台", + "dashboard": "主頁", "downloads": "下載", "equalizer": "等化器", "favorite-albums": "最喜歡的專輯", "favorite-artists": "最喜歡的藝術家", "favorite-tracks": "我的最愛", - "library": "本機音樂庫", - "listening-history": "Listening History", + "library": "本機媒體庫", + "listening-history": "聆聽紀錄", "lyrics": "歌詞", "main": "主要設定", "playlists": "播放清單", "plugins": "擴充套件", - "search": "搜尋", + "search": "搜尋結果", "settings": "設定", - "visualizer": "視覺化工具", + "visualizer": "視覺效果", "user-panel": { "actions-tooltip": "使用者操作", "sign-up": "註冊/登入", @@ -37,8 +37,8 @@ "artist": { "add-all": "新增全部", "count": "播放次數", - "queue": "將所有歌曲新增到播放清單", - "similar": "相關歌手", + "queue": "將所有歌曲新增到播放佇列", + "similar": "類似的藝人", "title": "音樂標題", "tour": "巡迴演出" }, @@ -64,19 +64,19 @@ "downloads": { "clear": "清除已完成歌曲", "completion": "完成", - "empty": "下載列為空", - "empty-help": "把歌曲下載後,會顯示在這裡。", + "empty": "還沒有下載任何歌曲", + "empty-help": "您下載的曲目將會顯示在這裡", "header": "下載", "name": "名字", "status": "狀態" }, "equalizer": { "classical": "古典", - "club": "Club", - "custom": "Custom", + "club": "俱樂部", + "custom": "自訂", "default": "預設", - "full-bass": "Full Bass", - "full-treble": "Full Treble", + "full-bass": "重低音", + "full-treble": "重高音", "pop": "流行", "presets": "預設值", "reggae": "雷鬼", @@ -97,13 +97,13 @@ "empty": "我的最愛為空", "empty-help": "嘗試將一些藝術家添加到收藏夾,他們會出現在這裡!", "header": "我的最愛", - "play-random": "Play a random track from your favorites", + "play-random": "隨機播放喜好曲目", "title": "音樂標題" }, "help": { - "about": "關於", + "about": "關於 Nuclear 音樂播放器", "contributors": "前十位貢獻者", - "header": "免費的串流播放軟體", + "header": "免費且開源的串流播放媒體(桌面端)", "released": "遵循 AGPL-3.0 發行", "report": "提交issues或新的功能", "thanks": "非常感謝 Github 上貢獻者的貢獻,您的貢獻對本程式的誕生至關重要。" @@ -112,7 +112,7 @@ "add": "新建資料夾", "album": "專輯", "artist": "演出者", - "empty": "資料庫為空", + "empty": "媒體庫是空的", "empty-help": "嘗試使用上方按鈕添加一些音樂", "filter-placeholder": "篩選...", "header": "本機", @@ -127,18 +127,19 @@ "not-found": "這首歌沒有歌詞資料" }, "option-control": { - "autoradio": "自動播放", + "rate": "播放速度", + "autoradio": "自動接續播放", "loop": "循環播放", "mini-player": "最小化播放器介面", "shuffle": "隨機播放" }, "player": { - "next-button": "下一曲", + "next-button": "[下一首曲目] 按鈕", "play-pause-button": "播放/暫停", "previous-button": "上一曲" }, "playlists": { - "add-selected-tracks-to-downloads": "將選中的添加到下載中", + "add-selected-tracks-to-downloads": "下載當前選中的曲目", "add-selected-tracks-to-favorites": "將選中的添加到最喜歡的", "add-selected-tracks-to-queue": "將所選加入至隊列", "album": "專輯", @@ -155,7 +156,7 @@ "export-fail-title": "導出播放列表失敗", "export-success-title": "播放列表導出成功", "header": "你的播放列表", - "import-progress": "Import progress", + "import-progress": "匯入進度", "spotify-import-placeholder": "貼上一個 Spotify 歌單網址", "dialog-placeholder": "播放清單名稱...", "dialog-accept": "保存", @@ -178,18 +179,18 @@ "title": "音樂標題", "tracks-selected-plural": "音軌已選中", "tracks-selected-singular": "音軌已選中", - "tracks-singular": "track", - "tracks-plural": "tracks", + "tracks-singular": "曲目", + "tracks-plural": "曲目", "modified-at": "上次修改時間: ", "never-modified": "未知的", - "server-modified-at": "Server modified: ", - "upload-to-server": "Upload to server", - "download-from-server": "Download from server", - "number-of-tracks": "tracks" + "server-modified-at": "伺服器已編輯: ", + "upload-to-server": "上傳到伺服器", + "download-from-server": "從伺服器下載", + "number-of-tracks": "曲目" }, "plugins": { "add-a-plugin": "新增插件", - "header": "插件", + "header": "擴充套件", "lyrics-providers": "歌詞提供者", "meta-providers": "元數據提供者", "placeholder": "選擇預設音樂串流來源:", @@ -220,21 +221,21 @@ "live": "直播", "stream-verification": { "tooltip": { - "unknown": "The status of this stream is unknown.", - "unverified": "This stream has not yet been verified by the community.", - "weakly-verified": "This stream has been verified by a couple of users.", - "verified": "This stream has been verified by the community.", - "verified-by-user": "This stream has been verified by you." + "unknown": "此串流狀態未知", + "unverified": "此串流尚未得到社群的認證", + "weakly-verified": "此串流已由部分使用者認證", + "verified": "此串流已得到社群的認證", + "verified-by-user": "此串流已由您認證" }, "stream-status": { - "unknown": "Unknown", - "unverified": "Unverified", - "weakly-verified": "Weakly verified", - "verified": "Verified", - "verified-by-user": "Verified by you" + "unknown": "未知的", + "unverified": "未認證", + "weakly-verified": "較為薄弱的驗證", + "verified": "已驗證", + "verified-by-user": "已由您驗證" }, - "verify": "Verify", - "unverify": "Unverify" + "verify": "驗證", + "unverify": "未驗證" } }, "search": { @@ -263,13 +264,13 @@ "settings": { "api-port": "API 網路埠", "api-url": "", - "audio": "Audio", + "audio": "音訊", "autoradio": "自動播放", "autoradio-craziness": "Autoradio瘋狂度", "autoradio-craziness-description": "Autoradio 會自動選擇與crazier中更不相似的歌曲", - "autoradio-description": "當隊列結束時自動添加相似音軌", - "listening-history": "Listening history", - "listening-history-description": "Log the tracks you listen to, a la Last.fm. The history is stored offline.", + "autoradio-description": "當佇列結束時自動添加相似曲目", + "listening-history": "聆聽紀錄", + "listening-history-description": "儲存你的聆聽記錄,就像Last.fm一樣。\n此聆聽記錄是離線儲存的。", "compact-menu-bar": "使用最小化工具列", "compact-queue-bar": "使用最小化播放佇列", "developer": "開發者設定", @@ -288,7 +289,7 @@ "github-description": "登入 GitHub 並將歌曲清單分享到網路 (即將期待).", "github-title": "Github", "http": "HTTP API", - "invidious-url": "有害的url實例", + "invidious-url": "Invidious 事件 URL", "language": "語言", "language-placeholder": "設定語言", "lastfm-connect": "連接到 Last.fm", @@ -298,44 +299,44 @@ "less": "收起", "login": "登入", "logout": "登出", - "loop-after-queue-end": "到達最後曲目後自動循環", + "loop-after-queue-end": "重複播放佇列", "mastodon-authorization-token-label": "授權token", "mastodon-authorize": "授權", "mastodon-authorized": "Nuclear獲授權發表於 ", "mastodon-awaiting-authorization": "Nuclear已於{{instanceUrl}} 註冊。等待授權中。", "mastodon-description": "", "mastodon-instance-label": "Mastodon實例URL", - "mastodon-post-format-description": "Nuclear will post a status on Mastodon after each track completes playing. The above string will be the template for each post, with {{artist}}, {{title}}, and {{url}} replaced with the artist, title, and a link to the track respectively.", + "mastodon-post-format-description": "每首曲目播放完畢後,Nuclear將在Mastodon上顯示狀態。 上述字串將是每篇帖子的範本, {{artist}}、{{title}} 和{{url}} 分別替換為藝術家、標題和曲目連結。", "mastodon-post-format-label": "帖子格式", "mastodon-title": "", - "normalize": "Normalize volume", - "normalize-description": "Automatically adjust volume of tracks so that they are played at the same level. This needs to fetch the whole track to work, so it may cause a delay between tracks.", + "normalize": "音量平衡", + "normalize-description": "自動音量平衡:讓曲目間在相同音量播放。但這需要獲取整個曲目才能工作,因此可能會導致曲目之間的延遲。", "nuclear-identity-service-url": "Nuclear 識別服務網址", "nuclear-playlists-service-url": "Nulear 歌單服務網址", "mini-player": "使用迷你樣式播放器", "more": "更多", - "discord-rich-presence": "Toggle discord rich presence (requires restart)", - "notification-timeout": "超時提醒", + "discord-rich-presence": "在Discord中顯示正在播放的音樂 (需要重新啟動)", + "notification-timeout": "通知逾時", "notlogged": "未登入", - "playback": "播放", + "playback": "重播", "program-settings": "程式設定", - "promoted-artists": "Show promoted artists in the dashboard", - "promoted-artists-description": "This is a reel of interesting, handpicked indie artists you might like. No one's receiving any compensation for this.", + "promoted-artists": "在主頁中顯示推薦的藝術家", + "promoted-artists-description": "這是一系列您可能會感興趣的、精挑細選的獨立藝術家。沒有人會因此得到任何報酬。", "saving-in": "儲存於:", - "seek-iteration": "按下方向鍵時前進/後退的秒數", - "show-tray-icon": "Show tray icon", - "shuffle-queue": "隨機曲目", - "shuffle-when-going-back": "播放先前的曲目", - "shuffle-when-going-back-description": "當隨機播放處於激活狀態且點擊上一曲目按鈕時,播放一個隨機曲目", + "seek-iteration": "按下鍵盤方向鍵時,曲目搜尋快轉/後退的秒數", + "show-tray-icon": "顯示系統匣圖示", + "shuffle-queue": "隨機播放歌曲", + "shuffle-when-going-back": "對之前的曲目進行隨機播放", + "shuffle-when-going-back-description": "當隨機播放啟用時,點擊上一首曲目按鈕,將隨機播放歌曲。", "social": "社交", "skip-sponsorblock": "跳過非音樂片段", - "skip-sponsorblock-description": "Playback skips non-music segment from SponsorBlock (https://sponsor.ajay.app/)", + "skip-sponsorblock-description": "跳過非音樂片段\n由 SponsorBlock (https://sponsor.ajay.app/)提供。", "streaming": "串流", "track-duration": "在進度條上顯示音軌長度", "user": "帳號:", - "user-id": "User ID", - "use-stream-verification": "Use stream verification", - "use-stream-verification-description": "Use stream verification. Loads the streams the community has verified as being correct for the tracks you play. Also allows you to vote on streams. See documentation for details.", + "user-id": "使用者ID", + "use-stream-verification": "使用串流驗證", + "use-stream-verification-description": "使用串流驗證。將您正在播放的歌曲載入社群的正確驗證,並且允許您對串流進行投票。有關詳細訊息,請參閱文件。", "youtube": "YouTube", "yt-api-key": "YouTube API Key" }, @@ -361,13 +362,13 @@ "download-toast-title": "歌曲下載中", "favorite-toast-body": "{{artist}} - {{track}} 已加入最愛.", "favorite-toast-title": "歌曲已新增到我的最愛", - "play-next": "播放下一首", + "play-next": "插播", "play-now": "立即播放", "playlist-toast-body": "{{artist}} - {{track}} 已加入歌單.", "playlist-toast-title": "曲目已加入播放清單" }, "track-table": { - "add-selected-tracks-to-queue": "Add selected to queue", + "add-selected-tracks-to-queue": "將所選加入至佇列", "add-selected-tracks-to-downloads": "將選中的增加到下載項目中", "add-selected-tracks-to-favorites": "將選中的加到我的最愛", "play-selected-tracks-now": "播放所選歌曲", @@ -379,14 +380,14 @@ }, "command-palette": { "search-placeholder": "您想做什麼呢?", - "empty-state-help": "Can't seem to find what you're looking for? Try the help command.", - "protip-text": "PROTIP:", + "empty-state-help": "找不到你要找的東西嗎? 嘗試使用幫助命令。", + "protip-text": "小提示:", "protip-content": "方向鍵瀏覽, Enter 鍵選取, Esc鍵關閉", "categories": { - "playback": "Playback", - "queue": "Queue", - "navigation": "Navigation", - "application": "Application" + "playback": "重播", + "queue": "佇列", + "navigation": "導覽", + "application": "應用程式" }, "actions": { "play": "播放", @@ -408,56 +409,56 @@ "go-to-dashboard": "前往控制臺", "go-to-downloads": "前往下載", "go-to-lyrics": "前往歌詞頁面", - "go-to-plugins": "Go to plugins", + "go-to-plugins": "移至擴充套件", "go-to-search": "前往搜尋", "go-to-settings": "前往設定", "go-to-equalizer": "前往等化器", - "go-to-visualizer": "Go to visualizer", + "go-to-visualizer": "移至視覺效果", "go-to-playlists": "前往播放清單", "go-to-favorite-tracks": "前往我的最愛", - "go-to-library": "Go to local library" + "go-to-library": "移至本地媒體庫" } }, "listening-history": { - "title": "Listening history", - "empty-state": "You haven't listened to anything yet.", - "clear-history": "Clear history", - "clear-history-confirm": "Are you sure you want to clear your listening history?", - "clear-history-confirm-yes": "Confirm", - "clear-history-confirm-no": "Cancel", - "clear-history-toast": "Listening history cleared" + "title": "聆聽紀錄", + "empty-state": "您尚未播放任何歌曲", + "clear-history": "清除歷史紀錄", + "clear-history-confirm": "您確定要清除聆聽記錄嗎?", + "clear-history-confirm-yes": "確認", + "clear-history-confirm-no": "取消", + "clear-history-toast": "聆聽紀錄已清除" }, "forms": { "nuclear-sign-up": { "header": "註冊", - "secondary-header": "Sign up to Nuclear Web Services", - "side-paragraph-1": "NWS enables you to backup your playlists online and share them.", - "side-paragraph-2": "Providing your email is optional; it will allow you to recover your account if you forget your password.", + "secondary-header": "註冊 Nuclear 的網頁服務", + "side-paragraph-1": "使用NWS服務,您可以線上備份和共享播放列表。", + "side-paragraph-2": "提供電子郵件是可選的;如果您忘記密碼,它將允許您恢復您的帳戶。", "username-label": "帳號", "email-label": "電子信箱", "password-label": "密碼", "sign-up-button": "註冊", "validation": { "username": { - "length": "Username must be 4 characters or more", - "required": "Username is required" + "length": "使用者名稱必須為4個字元或以上", + "required": "使用者名稱為必填欄位" }, "email": { - "invalid": "Email must be a valid email" + "invalid": "必須為有效的電子郵件地址" }, "password": { - "length": "Password must be 6 characters or more", - "required": "Password is required" + "length": "密碼必須為 6 個字元或以上", + "required": "密碼為必填欄位" } } }, "nuclear-sign-in": { - "header": " Sign in", - "username-label": "Username", - "password-label": "Password", - "no-account-label": "Don't have an account?", - "no-account-link": "Sign up", - "sign-in-button": "Sign in" + "header": " 登入", + "username-label": "使用者名稱", + "password-label": "密碼", + "no-account-label": "還沒有帳戶嗎?", + "no-account-link": "註冊", + "sign-in-button": "登入" } } } \ No newline at end of file From c7a43c504ebc10640d2822707601a1e48ba9715f Mon Sep 17 00:00:00 2001 From: Cristan Beskid <596554+sferra@users.noreply.github.com> Date: Sat, 10 Feb 2024 23:44:10 +0100 Subject: [PATCH 03/15] Fix Bandcamp plugin search aborting after the first page --- .../src/plugins/stream/BandcampPlugin.test.ts | 60 +++++++++++++++++++ .../core/src/plugins/stream/BandcampPlugin.ts | 2 +- 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/plugins/stream/BandcampPlugin.test.ts diff --git a/packages/core/src/plugins/stream/BandcampPlugin.test.ts b/packages/core/src/plugins/stream/BandcampPlugin.test.ts new file mode 100644 index 0000000000..8f7bbe14e6 --- /dev/null +++ b/packages/core/src/plugins/stream/BandcampPlugin.test.ts @@ -0,0 +1,60 @@ +import {BandcampPlugin} from '.'; +import {Bandcamp} from '../../rest'; +import spyOn = jest.spyOn; +import {BandcampSearchResult} from '../../rest/Bandcamp'; + +describe('Bandcamp plugin tests', () => { + let plugin: BandcampPlugin; + + beforeEach(() => { + plugin = new BandcampPlugin(); + }); + + describe('find track URLs', () => { + const streamQuery = { + artist: 'Artist Name', + track: 'Track Name' + }; + const trackQuery = 'Track Name'; + const bandcampSearch = spyOn(Bandcamp, 'search'); + + afterEach(() => { + bandcampSearch.mockReset(); + }); + + test('finds track', async () => { + const matchingSearchResult: BandcampSearchResult = { + type: 'track', + artist: 'Artist Name', + name: 'Track Name', + url: null, + imageUrl: null, + tags: null + }; + bandcampSearch.mockResolvedValueOnce([]) + .mockResolvedValueOnce([]) + .mockResolvedValueOnce([]) + .mockResolvedValueOnce([]) + .mockResolvedValueOnce([matchingSearchResult]); + + await expect(plugin.findTrackUrls(streamQuery)) + .resolves.toEqual([matchingSearchResult]); + + expect(bandcampSearch).toHaveBeenCalledTimes(5); + expect(bandcampSearch).toHaveBeenNthCalledWith(1, trackQuery, 0); + expect(bandcampSearch).toHaveBeenNthCalledWith(2, trackQuery, 1); + expect(bandcampSearch).toHaveBeenNthCalledWith(3, trackQuery, 2); + expect(bandcampSearch).toHaveBeenNthCalledWith(4, trackQuery, 3); + expect(bandcampSearch).toHaveBeenNthCalledWith(5, trackQuery, 4); + }); + + test('doesn\'t find tracks', async () => { + bandcampSearch.mockResolvedValue([]); + + await expect(plugin.findTrackUrls(streamQuery)) + .resolves.toEqual([]); + + expect(bandcampSearch).toHaveBeenCalledTimes(5); + }); + }); +}); diff --git a/packages/core/src/plugins/stream/BandcampPlugin.ts b/packages/core/src/plugins/stream/BandcampPlugin.ts index 08d48b90cc..6e52e4bc3b 100644 --- a/packages/core/src/plugins/stream/BandcampPlugin.ts +++ b/packages/core/src/plugins/stream/BandcampPlugin.ts @@ -24,7 +24,7 @@ class BandcampPlugin extends StreamProviderPlugin { item.type === 'track' && item.artist.toLowerCase() === query.artist.toLowerCase() ); - if (tracks) { + if (tracks.length > 0) { break; } } From 52827b6e41b4bb8e1401990fcba8432ac36c44f2 Mon Sep 17 00:00:00 2001 From: nukeop <12746779+nukeop@users.noreply.github.com> Date: Sun, 11 Feb 2024 00:18:00 +0100 Subject: [PATCH 04/15] Update review.yml --- .github/workflows/review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/review.yml b/.github/workflows/review.yml index 437de2f174..de416bd8e8 100644 --- a/.github/workflows/review.yml +++ b/.github/workflows/review.yml @@ -1,6 +1,6 @@ name: Code Review on: - pull_request: + pull_request_target: types: [opened, review_requested] jobs: From a67adfb1a6d8102d3b4d18d537e20f7b236ec279 Mon Sep 17 00:00:00 2001 From: Cristan Beskid <596554+sferra@users.noreply.github.com> Date: Sun, 11 Feb 2024 00:56:20 +0100 Subject: [PATCH 05/15] Fix Bandcamp track search caused by imprecise query When the name of a track was very simple, the actual search match could be on a page higher than the configured maximum. This caused the search to fail and the track wouldn't be played. The amount of search results can be greatly reduced by simply adding the artist name to the query. --- .../src/plugins/stream/BandcampPlugin.test.ts | 54 ++++++++++++++++++- .../core/src/plugins/stream/BandcampPlugin.ts | 22 ++++++-- 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/packages/core/src/plugins/stream/BandcampPlugin.test.ts b/packages/core/src/plugins/stream/BandcampPlugin.test.ts index 8f7bbe14e6..713a7cc1c3 100644 --- a/packages/core/src/plugins/stream/BandcampPlugin.test.ts +++ b/packages/core/src/plugins/stream/BandcampPlugin.test.ts @@ -1,4 +1,5 @@ import {BandcampPlugin} from '.'; +import fn = jest.fn; import {Bandcamp} from '../../rest'; import spyOn = jest.spyOn; import {BandcampSearchResult} from '../../rest/Bandcamp'; @@ -15,9 +16,17 @@ describe('Bandcamp plugin tests', () => { artist: 'Artist Name', track: 'Track Name' }; - const trackQuery = 'Track Name'; + const trackQuery = 'Artist Name Track Name'; const bandcampSearch = spyOn(Bandcamp, 'search'); + beforeEach(() => { + spyOn(plugin, 'createTrackQuery') + .mockImplementation(fn(() => trackQuery)); + // 'accept all' matcher + spyOn(plugin, 'createTrackMatcher') + .mockImplementation(fn(query => () => true)); + }); + afterEach(() => { bandcampSearch.mockReset(); }); @@ -57,4 +66,47 @@ describe('Bandcamp plugin tests', () => { expect(bandcampSearch).toHaveBeenCalledTimes(5); }); }); + + test('creates track query', () => { + const streamQuery = { + artist: 'Artist Name', + track: 'Track Name' + }; + const trackQuery = plugin.createTrackQuery(streamQuery); + expect(trackQuery).toBe('Artist Name Track Name'); + }); + + test('creates track matcher', () => { + const streamQuery = { + artist: 'Artist Name', + track: 'Track Name' + }; + const matcher = plugin.createTrackMatcher(streamQuery); + const matchingResult = { + type: 'track', + artist: 'Artist Name', + name: 'Track Name' + }; + const searchResults = [ + matchingResult, // first match + { + type: 'album' // type shouldn't match + }, + { + type: 'artist' // type shouldn't match + }, + { + type: 'track', + artist: 'Wrong Artist' // artist shouldn't match + }, + { + type: 'track', + artist: 'Artist Name', + name: 'Wrong Title' // track name shouldn't match + }, + matchingResult // second match + ]; + const tracks = searchResults.filter(matcher); + expect(tracks).toEqual([matchingResult, matchingResult]); + }); }); diff --git a/packages/core/src/plugins/stream/BandcampPlugin.ts b/packages/core/src/plugins/stream/BandcampPlugin.ts index 6e52e4bc3b..fff2e24587 100644 --- a/packages/core/src/plugins/stream/BandcampPlugin.ts +++ b/packages/core/src/plugins/stream/BandcampPlugin.ts @@ -18,12 +18,11 @@ class BandcampPlugin extends StreamProviderPlugin { const limit = 5; let tracks: BandcampSearchResult[]; + const trackQuery: string = this.createTrackQuery(query); + const isMatchingTrack = this.createTrackMatcher(query); for (let page = 0; page < limit; page++) { - const searchResults = await Bandcamp.search(query.track, page); - tracks = searchResults.filter(item => - item.type === 'track' && - item.artist.toLowerCase() === query.artist.toLowerCase() - ); + const searchResults = await Bandcamp.search(trackQuery, page); + tracks = searchResults.filter(isMatchingTrack); if (tracks.length > 0) { break; } @@ -31,6 +30,19 @@ class BandcampPlugin extends StreamProviderPlugin { return tracks; } + createTrackQuery(query: StreamQuery): string { + return `${query.artist} ${query.track}`; + } + + createTrackMatcher(query: StreamQuery): (BandcampSearchResult) => boolean { + const lowerCaseTrack: string = query.track.toLowerCase(); + const lowerCaseArtist: string = query.artist.toLowerCase(); + return (searchResult) => + searchResult.type === 'track' && + searchResult.artist.toLowerCase() === lowerCaseArtist && + searchResult.name.toLowerCase() === lowerCaseTrack; + } + resultToStream(result: BandcampSearchResult, stream: string, duration: number): StreamData { return { source: this.sourceName, From b8e60f3ea8ecf1b04a35519ffd79382d7b13aa13 Mon Sep 17 00:00:00 2001 From: Cristan Beskid <596554+sferra@users.noreply.github.com> Date: Sun, 11 Feb 2024 17:15:54 +0100 Subject: [PATCH 06/15] Fix formatting and incorrect value types --- .../core/src/plugins/stream/BandcampPlugin.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core/src/plugins/stream/BandcampPlugin.test.ts b/packages/core/src/plugins/stream/BandcampPlugin.test.ts index 8f7bbe14e6..06e59a1c8e 100644 --- a/packages/core/src/plugins/stream/BandcampPlugin.test.ts +++ b/packages/core/src/plugins/stream/BandcampPlugin.test.ts @@ -1,7 +1,7 @@ -import {BandcampPlugin} from '.'; -import {Bandcamp} from '../../rest'; +import { BandcampPlugin } from '.'; +import { Bandcamp } from '../../rest'; import spyOn = jest.spyOn; -import {BandcampSearchResult} from '../../rest/Bandcamp'; +import { BandcampSearchResult } from '../../rest/Bandcamp'; describe('Bandcamp plugin tests', () => { let plugin: BandcampPlugin; @@ -27,9 +27,9 @@ describe('Bandcamp plugin tests', () => { type: 'track', artist: 'Artist Name', name: 'Track Name', - url: null, - imageUrl: null, - tags: null + url: 'URL', + imageUrl: 'image URL', + tags: [] }; bandcampSearch.mockResolvedValueOnce([]) .mockResolvedValueOnce([]) From d175585276b2acccc3b7b097c7bb8519d7f95108 Mon Sep 17 00:00:00 2001 From: Cristan Beskid <596554+sferra@users.noreply.github.com> Date: Sun, 11 Feb 2024 22:18:33 +0100 Subject: [PATCH 07/15] Clean up code --- .../src/plugins/stream/BandcampPlugin.test.ts | 65 ++++++++++++------- .../core/src/plugins/stream/BandcampPlugin.ts | 8 +-- 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/packages/core/src/plugins/stream/BandcampPlugin.test.ts b/packages/core/src/plugins/stream/BandcampPlugin.test.ts index 92a7959c43..021759f789 100644 --- a/packages/core/src/plugins/stream/BandcampPlugin.test.ts +++ b/packages/core/src/plugins/stream/BandcampPlugin.test.ts @@ -16,12 +16,18 @@ describe('Bandcamp plugin tests', () => { artist: 'Artist Name', track: 'Track Name' }; - const trackQuery = 'Artist Name Track Name'; + const searchTerm = 'Search term for Bandcamp'; + // some of the search result attributes are irrelevant for this test case + const irrelevantSearchResultAttributes = { + url: 'not relevant for this test', + imageUrl: 'not relevant for this test', + tags: [] + }; const bandcampSearch = spyOn(Bandcamp, 'search'); beforeEach(() => { - spyOn(plugin, 'createTrackQuery') - .mockImplementation(fn(() => trackQuery)); + spyOn(plugin, 'createSearchTerm') + .mockImplementation(fn(() => searchTerm)); // 'accept all' matcher spyOn(plugin, 'createTrackMatcher') .mockImplementation(fn(query => () => true)); @@ -36,9 +42,7 @@ describe('Bandcamp plugin tests', () => { type: 'track', artist: 'Artist Name', name: 'Track Name', - url: 'URL', - imageUrl: 'image URL', - tags: [] + ...irrelevantSearchResultAttributes }; bandcampSearch.mockResolvedValueOnce([]) .mockResolvedValueOnce([]) @@ -50,11 +54,11 @@ describe('Bandcamp plugin tests', () => { .resolves.toEqual([matchingSearchResult]); expect(bandcampSearch).toHaveBeenCalledTimes(5); - expect(bandcampSearch).toHaveBeenNthCalledWith(1, trackQuery, 0); - expect(bandcampSearch).toHaveBeenNthCalledWith(2, trackQuery, 1); - expect(bandcampSearch).toHaveBeenNthCalledWith(3, trackQuery, 2); - expect(bandcampSearch).toHaveBeenNthCalledWith(4, trackQuery, 3); - expect(bandcampSearch).toHaveBeenNthCalledWith(5, trackQuery, 4); + expect(bandcampSearch).toHaveBeenNthCalledWith(1, searchTerm, 0); + expect(bandcampSearch).toHaveBeenNthCalledWith(2, searchTerm, 1); + expect(bandcampSearch).toHaveBeenNthCalledWith(3, searchTerm, 2); + expect(bandcampSearch).toHaveBeenNthCalledWith(4, searchTerm, 3); + expect(bandcampSearch).toHaveBeenNthCalledWith(5, searchTerm, 4); }); test('doesn\'t find tracks', async () => { @@ -67,42 +71,59 @@ describe('Bandcamp plugin tests', () => { }); }); - test('creates track query', () => { + test('creates search term', () => { const streamQuery = { artist: 'Artist Name', track: 'Track Name' }; - const trackQuery = plugin.createTrackQuery(streamQuery); - expect(trackQuery).toBe('Artist Name Track Name'); + const searchTerm = plugin.createSearchTerm(streamQuery); + expect(searchTerm).toBe('Artist Name Track Name'); }); test('creates track matcher', () => { + // some of the search result attributes are irrelevant for this test case + const irrelevantSearchResultAttributes = { + url: 'irrelevant for this test', + imageUrl: 'irrelevant for this test', + tags: [] + }; const streamQuery = { artist: 'Artist Name', - track: 'Track Name' + track: 'Track Name', + ...irrelevantSearchResultAttributes }; const matcher = plugin.createTrackMatcher(streamQuery); - const matchingResult = { + const matchingResult: BandcampSearchResult = { type: 'track', artist: 'Artist Name', - name: 'Track Name' + name: 'Track Name', + ...irrelevantSearchResultAttributes }; - const searchResults = [ + const searchResults: BandcampSearchResult[] = [ matchingResult, // first match { - type: 'album' // type shouldn't match + type: 'album', // the 'album' type shouldn't match + artist: 'irrelevant for this entry', + name: 'irrelevant for this entry', + ...irrelevantSearchResultAttributes }, { - type: 'artist' // type shouldn't match + type: 'artist', // the 'artist' type shouldn't match + artist: 'irrelevant for this entry', + name: 'irrelevant for this entry', + ...irrelevantSearchResultAttributes }, { type: 'track', - artist: 'Wrong Artist' // artist shouldn't match + artist: 'Wrong Artist', // artist shouldn't match + name: 'irrelevant for this entry', + ...irrelevantSearchResultAttributes }, { type: 'track', artist: 'Artist Name', - name: 'Wrong Title' // track name shouldn't match + name: 'Wrong Title', // track name shouldn't match + ...irrelevantSearchResultAttributes }, matchingResult // second match ]; diff --git a/packages/core/src/plugins/stream/BandcampPlugin.ts b/packages/core/src/plugins/stream/BandcampPlugin.ts index fff2e24587..651e188796 100644 --- a/packages/core/src/plugins/stream/BandcampPlugin.ts +++ b/packages/core/src/plugins/stream/BandcampPlugin.ts @@ -18,10 +18,10 @@ class BandcampPlugin extends StreamProviderPlugin { const limit = 5; let tracks: BandcampSearchResult[]; - const trackQuery: string = this.createTrackQuery(query); + const searchTerm: string = this.createSearchTerm(query); const isMatchingTrack = this.createTrackMatcher(query); for (let page = 0; page < limit; page++) { - const searchResults = await Bandcamp.search(trackQuery, page); + const searchResults = await Bandcamp.search(searchTerm, page); tracks = searchResults.filter(isMatchingTrack); if (tracks.length > 0) { break; @@ -30,11 +30,11 @@ class BandcampPlugin extends StreamProviderPlugin { return tracks; } - createTrackQuery(query: StreamQuery): string { + createSearchTerm(query: StreamQuery): string { return `${query.artist} ${query.track}`; } - createTrackMatcher(query: StreamQuery): (BandcampSearchResult) => boolean { + createTrackMatcher(query: StreamQuery): (item: BandcampSearchResult) => boolean { const lowerCaseTrack: string = query.track.toLowerCase(); const lowerCaseArtist: string = query.artist.toLowerCase(); return (searchResult) => From a93054f0f7d4cf292504d5e822291feee3f92f12 Mon Sep 17 00:00:00 2001 From: Cristan Beskid <596554+sferra@users.noreply.github.com> Date: Fri, 16 Feb 2024 22:53:08 +0100 Subject: [PATCH 08/15] Make bandcamp search result matching more robust Some metadata providers like Discogs provide track names which include traling spaces which caused the search to fail. Removing leading and trailing spaces from track and artist names allows us to find any previously missed matches. --- .../src/plugins/stream/BandcampPlugin.test.ts | 15 +++++++++++---- .../core/src/plugins/stream/BandcampPlugin.ts | 12 ++++++++---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/packages/core/src/plugins/stream/BandcampPlugin.test.ts b/packages/core/src/plugins/stream/BandcampPlugin.test.ts index 021759f789..3d98b28de4 100644 --- a/packages/core/src/plugins/stream/BandcampPlugin.test.ts +++ b/packages/core/src/plugins/stream/BandcampPlugin.test.ts @@ -1,8 +1,8 @@ import { BandcampPlugin } from '.'; -import fn = jest.fn; import { Bandcamp } from '../../rest'; -import spyOn = jest.spyOn; import { BandcampSearchResult } from '../../rest/Bandcamp'; +import fn = jest.fn; +import spyOn = jest.spyOn; describe('Bandcamp plugin tests', () => { let plugin: BandcampPlugin; @@ -88,8 +88,8 @@ describe('Bandcamp plugin tests', () => { tags: [] }; const streamQuery = { - artist: 'Artist Name', - track: 'Track Name', + artist: ' Artist Name', + track: 'Track Name ', ...irrelevantSearchResultAttributes }; const matcher = plugin.createTrackMatcher(streamQuery); @@ -130,4 +130,11 @@ describe('Bandcamp plugin tests', () => { const tracks = searchResults.filter(matcher); expect(tracks).toEqual([matchingResult, matchingResult]); }); + + test('normalizes value for matching search results', () => { + // mixed case search term padded with spaces + const termToNormalize = ' Search Term '; + const normalizedTerm = plugin.normalizeForMatching(termToNormalize); + expect(normalizedTerm).toEqual('search term'); + }); }); diff --git a/packages/core/src/plugins/stream/BandcampPlugin.ts b/packages/core/src/plugins/stream/BandcampPlugin.ts index 651e188796..3dc7751648 100644 --- a/packages/core/src/plugins/stream/BandcampPlugin.ts +++ b/packages/core/src/plugins/stream/BandcampPlugin.ts @@ -35,12 +35,16 @@ class BandcampPlugin extends StreamProviderPlugin { } createTrackMatcher(query: StreamQuery): (item: BandcampSearchResult) => boolean { - const lowerCaseTrack: string = query.track.toLowerCase(); - const lowerCaseArtist: string = query.artist.toLowerCase(); + const normalizedTrack: string = this.normalizeForMatching(query.track); + const normalizedArtist: string = this.normalizeForMatching(query.artist); return (searchResult) => searchResult.type === 'track' && - searchResult.artist.toLowerCase() === lowerCaseArtist && - searchResult.name.toLowerCase() === lowerCaseTrack; + this.normalizeForMatching(searchResult.artist) === normalizedArtist && + this.normalizeForMatching(searchResult.name) === normalizedTrack; + } + + normalizeForMatching(term: string): string { + return term.trim().toLowerCase(); } resultToStream(result: BandcampSearchResult, stream: string, duration: number): StreamData { From 30139bcdb4aae7375e8643726ca904d52ff4b2ba Mon Sep 17 00:00:00 2001 From: Cristan Beskid <596554+sferra@users.noreply.github.com> Date: Sat, 17 Feb 2024 00:54:42 +0100 Subject: [PATCH 09/15] Fix artist search when lastfm information isn't available --- packages/core/src/plugins/meta/discogs.ts | 41 +++++++++++++++-------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/packages/core/src/plugins/meta/discogs.ts b/packages/core/src/plugins/meta/discogs.ts index 3c76153a27..2d60068454 100644 --- a/packages/core/src/plugins/meta/discogs.ts +++ b/packages/core/src/plugins/meta/discogs.ts @@ -16,7 +16,6 @@ import { import { DiscogsReleaseSearchResponse, DiscogsArtistSearchResponse, - DiscogsArtistReleasesSearchResponse, DiscogsArtistInfo, DiscogsArtistSearchResult, DiscogsReleaseSearchResult, @@ -175,29 +174,19 @@ class DiscogsMetaProvider extends MetaProvider { const lastFmInfo: LastFmArtistInfo = (await (await this.lastfm.getArtistInfo(discogsInfo.name)).json()).artist; const lastFmTopTracks: LastfmTopTracks = (await (await this.lastfm.getArtistTopTracks(discogsInfo.name)).json()).toptracks; - const coverImage = this.getCoverImage(discogsInfo); - - const spotifyToken = await getToken(); - const similarArtists = await Promise.all(take(lastFmInfo.similar.artist, 5).map(async artist => { - const similarArtist = await searchArtists(spotifyToken, artist.name); - - return { - name: artist.name, - thumbnail: similarArtist?.images[0]?.url - }; - })); + const similarArtists = await this.getSimilarArtists(lastFmInfo); return { id: discogsInfo.id, name: discogsInfo.name, description: _.get(lastFmInfo, 'bio.summary'), tags: _.map(_.get(lastFmInfo, 'tags.tag'), 'name'), - onTour: lastFmInfo.ontour === '1', + onTour: this.isArtistOnTour(lastFmInfo), coverImage, thumb: coverImage, images: _.map(discogsInfo.images, 'resource_url'), - topTracks: _.map(lastFmTopTracks.track, (track: LastfmTrack) => ({ + topTracks: _.map(lastFmTopTracks?.track, (track: LastfmTrack) => ({ name: track.name, title: track.name, thumb: coverImage, @@ -210,6 +199,30 @@ class DiscogsMetaProvider extends MetaProvider { }; } + async getSimilarArtists(artistInfo: LastFmArtistInfo | undefined) { + if (!artistInfo) { + return []; + } + const spotifyToken = await getToken(); + return await Promise.all( + take(artistInfo.similar.artist, 5) + .map(async artist => { + const similarArtist = await searchArtists(spotifyToken, artist.name); + return { + name: artist.name, + thumbnail: similarArtist?.images[0]?.url + }; + }) + ); + } + + isArtistOnTour(artistInfo: LastFmArtistInfo | undefined): boolean { + if (!artistInfo) { + return false; + } + return artistInfo?.ontour === '1'; + } + async fetchArtistDetailsByName(artistName: string): Promise { const artistSearch = await (await Discogs.search(artistName, 'artist')).json(); const artist: DiscogsArtistSearchResult = _.head(artistSearch.results); From d00639a6cc8c99c133f606a3b0b89bc2b9301781 Mon Sep 17 00:00:00 2001 From: Cristan Beskid <596554+sferra@users.noreply.github.com> Date: Sat, 17 Feb 2024 01:04:08 +0100 Subject: [PATCH 10/15] Remove obsolete check --- packages/core/src/plugins/meta/discogs.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/core/src/plugins/meta/discogs.ts b/packages/core/src/plugins/meta/discogs.ts index 2d60068454..3ec64151e5 100644 --- a/packages/core/src/plugins/meta/discogs.ts +++ b/packages/core/src/plugins/meta/discogs.ts @@ -217,9 +217,6 @@ class DiscogsMetaProvider extends MetaProvider { } isArtistOnTour(artistInfo: LastFmArtistInfo | undefined): boolean { - if (!artistInfo) { - return false; - } return artistInfo?.ontour === '1'; } From f6ff1afc302d73d577c0bae6e5bfd7e518317881 Mon Sep 17 00:00:00 2001 From: Cristan Beskid <596554+sferra@users.noreply.github.com> Date: Mon, 19 Feb 2024 00:25:12 +0100 Subject: [PATCH 11/15] Refactor to allow testing and add tests --- .../core/src/plugins/meta/discogs.test.ts | 164 ++++++++++++++++++ packages/core/src/plugins/meta/discogs.ts | 58 +++++-- packages/core/src/rest/Spotify.ts | 11 +- 3 files changed, 212 insertions(+), 21 deletions(-) create mode 100644 packages/core/src/plugins/meta/discogs.test.ts diff --git a/packages/core/src/plugins/meta/discogs.test.ts b/packages/core/src/plugins/meta/discogs.test.ts new file mode 100644 index 0000000000..f149fbafec --- /dev/null +++ b/packages/core/src/plugins/meta/discogs.test.ts @@ -0,0 +1,164 @@ +import DiscogsMetaProvider from './discogs'; +import { SimilarArtist } from '../plugins.types'; +import * as SpotifyApi from '../../rest/Spotify'; + +describe('Tests for DiscogsMetaProvider', () => { + const provider = new DiscogsMetaProvider(); + + describe('Verify if an artist is on tour', () => { + + test('Should return false if the artist info is missing', () => { + expect(provider.isArtistOnTour(undefined)).toBeFalsy(); + }); + + test('Should return false if the \'ontour\' flag is not \'1\'', () => { + const artistInfo: any = { + ontour: 'something else than 1' + }; + expect(provider.isArtistOnTour(artistInfo)).toBeFalsy(); + }); + + test('Should return false if the \'ontour\' flag is \'1\'', () => { + const artistInfo: any = { + ontour: '1' + }; + expect(provider.isArtistOnTour(artistInfo)).toBeTruthy(); + }); + }); + + describe('Get similar artists', () => { + const spotifyToken = 'spotify token'; + const getToken = jest.spyOn(SpotifyApi, 'getToken'); + const fetchTopSimilarArtistsFromSpotify = jest.spyOn(provider, 'fetchTopSimilarArtistsFromSpotify'); + + afterEach(() => { + getToken.mockReset(); + fetchTopSimilarArtistsFromSpotify.mockReset(); + }); + + afterAll(() => { + getToken.mockRestore(); + fetchTopSimilarArtistsFromSpotify.mockRestore(); + }); + + test('Should return an empty result set if the artist info is missing', async () => { + expect(await provider.getSimilarArtists({ + similar: { + artist: null + } + } as any)).toEqual([]); + }); + + test('Should fetch similar artist', async () => { + const similarArtistsOnLastFm = [{ + name: 'Similar Artist on LastFm' + }]; + const artistInfoFromLastFm = { + name: 'Artist Name', + similar: { + artist: similarArtistsOnLastFm + } + } as any; + + getToken.mockResolvedValueOnce(spotifyToken); + + const similarArtist: SimilarArtist = { + name: 'Similar Artist Name', + thumbnail: 'the thumbnail' + }; + fetchTopSimilarArtistsFromSpotify.mockImplementationOnce((artists, token) => { + expect(token).toEqual(spotifyToken); + expect(artists).toEqual(similarArtistsOnLastFm); + return Promise.resolve([similarArtist]); + }); + expect(await provider.getSimilarArtists(artistInfoFromLastFm)).toEqual([similarArtist]); + }); + + test('Should return empty result set if fetching the Spotify token fails', async () => { + getToken.mockRejectedValueOnce('Failed to fetch Spotify token'); + expect(await provider.getSimilarArtists({ + similar: { + artist: [] + } + } as any)).toEqual([]); + }); + + test('Should return empty result set if fetching the artists from Spotify fails', async () => { + getToken.mockResolvedValueOnce(spotifyToken); + fetchTopSimilarArtistsFromSpotify.mockRejectedValueOnce('Failed to fetch similar artists'); + expect(await provider.getSimilarArtists({ + similar: { + artist: [] + } + } as any)).toEqual([]); + }); + }); + + describe('Fetch top similar artists from Spotify', () => { + + test('Should fetch similar artists from Spotify', async () => { + const fetchSimilarArtistFromSpotify = jest.spyOn(provider, 'fetchSimilarArtistFromSpotify') + .mockImplementation((artistName, token) => { + return Promise.resolve({ + name: `Spotify: ${artistName}` + } as any); + }); + + const spotifyToken = 'Spotify token'; + const artistsFromLastfm = [ + { name: null }, + { name: 'Artist 1' }, + { name: 'Artist 2' }, + { name: 'Artist 3' }, + { name: null }, + { name: 'Artist 4' }, + { name: 'Artist 5' }, + { name: 'Artist 6' } + ] as any; + + expect(await provider.fetchTopSimilarArtistsFromSpotify(artistsFromLastfm, spotifyToken)) + .toEqual([ + { 'name': 'Spotify: Artist 1' }, + { 'name': 'Spotify: Artist 2' }, + { 'name': 'Spotify: Artist 3' }, + { 'name': 'Spotify: Artist 4' }, + { 'name': 'Spotify: Artist 5' } + ]); + expect(fetchSimilarArtistFromSpotify).toHaveBeenCalledTimes(5); + fetchSimilarArtistFromSpotify.mockRestore(); + }); + }); + + describe('Fetch similar artist from Spotify', () => { + const searchArtists = jest.spyOn(SpotifyApi, 'searchArtists'); + + afterEach(() => { + searchArtists.mockReset(); + }); + + afterAll(() => { + searchArtists.mockRestore(); + }); + + test('Should fetch similar artist', async () => { + searchArtists.mockResolvedValueOnce({ + images: [{ url: 'the thumbnail URL' }] + } as any); + expect(await provider.fetchSimilarArtistFromSpotify('Artist Name', 'Spotify token')) + .toEqual({ + name: 'Artist Name', + thumbnail: 'the thumbnail URL' + }); + }); + + test('Should return shallow similar artist if fetching fails', async () => { + searchArtists.mockRejectedValueOnce('Failed to fetch artist'); + expect(await provider.fetchSimilarArtistFromSpotify('Artist Name', 'Spotify token')) + .toEqual({ + name: 'Artist Name', + thumbnail: null + }); + }); + }); + +}); diff --git a/packages/core/src/plugins/meta/discogs.ts b/packages/core/src/plugins/meta/discogs.ts index 3ec64151e5..99376f4873 100644 --- a/packages/core/src/plugins/meta/discogs.ts +++ b/packages/core/src/plugins/meta/discogs.ts @@ -1,4 +1,4 @@ -import _, { take } from 'lodash'; +import _ from 'lodash'; import MetaProvider from '../metaProvider'; import * as Discogs from '../../rest/Discogs'; @@ -11,7 +11,8 @@ import { SearchResultsSource, ArtistDetails, AlbumDetails, - AlbumType + AlbumType, + SimilarArtist } from '../plugins.types'; import { DiscogsReleaseSearchResponse, @@ -23,9 +24,10 @@ import { DiscogsReleaseInfo, DiscogsTrack } from '../../rest/Discogs.types'; -import { LastFmArtistInfo, LastfmTopTracks, LastfmTrack } from '../../rest/Lastfm.types'; +import { LastFmArtistInfo, LastfmTopTracks, LastfmTrack, LastfmArtistShort } from '../../rest/Lastfm.types'; import { cleanName } from '../../structs/Artist'; import { getToken, searchArtists } from '../../rest/Spotify'; +import logger from 'electron-timber'; class DiscogsMetaProvider extends MetaProvider { lastfm: LastFmApi; @@ -199,23 +201,47 @@ class DiscogsMetaProvider extends MetaProvider { }; } - async getSimilarArtists(artistInfo: LastFmArtistInfo | undefined) { - if (!artistInfo) { - return []; + async getSimilarArtists(artist: LastFmArtistInfo | undefined): Promise { + if (!artist?.similar?.artist) { + return Promise.resolve([]); } - const spotifyToken = await getToken(); - return await Promise.all( - take(artistInfo.similar.artist, 5) - .map(async artist => { - const similarArtist = await searchArtists(spotifyToken, artist.name); - return { - name: artist.name, - thumbnail: similarArtist?.images[0]?.url - }; - }) + const similarArtists = artist.similar.artist; + return getToken() + .then(spotifyToken => this.fetchTopSimilarArtistsFromSpotify(similarArtists, spotifyToken)) + .catch(error => { + logger.error(`Failed to fetch similar artists for '${artist.name}'`); + logger.error(error); + return []; + }); + } + + async fetchTopSimilarArtistsFromSpotify(artists: LastfmArtistShort[], spotifyToken: string) { + return Promise.all( + artists + .filter(artist => artist?.name) + .slice(0, 5) + .map(artist => this.fetchSimilarArtistFromSpotify(artist.name, spotifyToken)) ); } + async fetchSimilarArtistFromSpotify(artistName: string, spotifyToken: string): Promise { + return searchArtists(spotifyToken, artistName) + .then(spotifyArtist => { + return { + name: artistName, + thumbnail: spotifyArtist?.images[0]?.url + }; + }) + .catch(error => { + logger.error(`Failed to fetch artist from Spotify: '${artistName}'`); + logger.error(error); + return { + name: artistName, + thumbnail: null + }; + }); + } + isArtistOnTour(artistInfo: LastFmArtistInfo | undefined): boolean { return artistInfo?.ontour === '1'; } diff --git a/packages/core/src/rest/Spotify.ts b/packages/core/src/rest/Spotify.ts index 5b493d4252..cc17ca23fd 100644 --- a/packages/core/src/rest/Spotify.ts +++ b/packages/core/src/rest/Spotify.ts @@ -1,11 +1,12 @@ const SPOTIFY_API_OPEN_URL = 'https://open.spotify.com'; const SPOTIFY_API_URL = 'https://api.spotify.com/v1'; -type SpotifyArtist = { - images: { - height: number; - url: string; - }[]; +export type SpotifyArtist = { + name: string; + images: { + height: number; + url: string; + }[]; } export const getToken = async (): Promise => { From 36f708134f0df7c5b57742f3284ec3f893182678 Mon Sep 17 00:00:00 2001 From: Cristan Beskid <596554+sferra@users.noreply.github.com> Date: Mon, 19 Feb 2024 18:46:47 +0100 Subject: [PATCH 12/15] Cast test data to explicit types --- packages/core/src/plugins/meta/discogs.test.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/core/src/plugins/meta/discogs.test.ts b/packages/core/src/plugins/meta/discogs.test.ts index f149fbafec..f12727aaf1 100644 --- a/packages/core/src/plugins/meta/discogs.test.ts +++ b/packages/core/src/plugins/meta/discogs.test.ts @@ -1,6 +1,8 @@ import DiscogsMetaProvider from './discogs'; import { SimilarArtist } from '../plugins.types'; import * as SpotifyApi from '../../rest/Spotify'; +import { SpotifyArtist } from '../../rest/Spotify'; +import { LastFmArtistInfo, LastfmArtistShort } from '../../rest/Lastfm.types'; describe('Tests for DiscogsMetaProvider', () => { const provider = new DiscogsMetaProvider(); @@ -46,7 +48,7 @@ describe('Tests for DiscogsMetaProvider', () => { similar: { artist: null } - } as any)).toEqual([]); + } as LastFmArtistInfo)).toEqual([]); }); test('Should fetch similar artist', async () => { @@ -58,7 +60,7 @@ describe('Tests for DiscogsMetaProvider', () => { similar: { artist: similarArtistsOnLastFm } - } as any; + } as LastFmArtistInfo; getToken.mockResolvedValueOnce(spotifyToken); @@ -80,7 +82,7 @@ describe('Tests for DiscogsMetaProvider', () => { similar: { artist: [] } - } as any)).toEqual([]); + } as LastFmArtistInfo)).toEqual([]); }); test('Should return empty result set if fetching the artists from Spotify fails', async () => { @@ -90,7 +92,7 @@ describe('Tests for DiscogsMetaProvider', () => { similar: { artist: [] } - } as any)).toEqual([]); + } as LastFmArtistInfo)).toEqual([]); }); }); @@ -101,7 +103,7 @@ describe('Tests for DiscogsMetaProvider', () => { .mockImplementation((artistName, token) => { return Promise.resolve({ name: `Spotify: ${artistName}` - } as any); + } as SimilarArtist); }); const spotifyToken = 'Spotify token'; @@ -114,7 +116,7 @@ describe('Tests for DiscogsMetaProvider', () => { { name: 'Artist 4' }, { name: 'Artist 5' }, { name: 'Artist 6' } - ] as any; + ] as LastfmArtistShort[]; expect(await provider.fetchTopSimilarArtistsFromSpotify(artistsFromLastfm, spotifyToken)) .toEqual([ @@ -143,7 +145,7 @@ describe('Tests for DiscogsMetaProvider', () => { test('Should fetch similar artist', async () => { searchArtists.mockResolvedValueOnce({ images: [{ url: 'the thumbnail URL' }] - } as any); + } as SpotifyArtist); expect(await provider.fetchSimilarArtistFromSpotify('Artist Name', 'Spotify token')) .toEqual({ name: 'Artist Name', From aa253ea8f438928b6daba8ca14a093793577d801 Mon Sep 17 00:00:00 2001 From: Cristan Beskid <596554+sferra@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:50:28 +0100 Subject: [PATCH 13/15] Improve test description --- packages/core/src/plugins/stream/BandcampPlugin.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/plugins/stream/BandcampPlugin.test.ts b/packages/core/src/plugins/stream/BandcampPlugin.test.ts index 3d98b28de4..32c1280a9a 100644 --- a/packages/core/src/plugins/stream/BandcampPlugin.test.ts +++ b/packages/core/src/plugins/stream/BandcampPlugin.test.ts @@ -131,7 +131,7 @@ describe('Bandcamp plugin tests', () => { expect(tracks).toEqual([matchingResult, matchingResult]); }); - test('normalizes value for matching search results', () => { + test('normalizeForMatching trims and converts the term to lowercase', () => { // mixed case search term padded with spaces const termToNormalize = ' Search Term '; const normalizedTerm = plugin.normalizeForMatching(termToNormalize); From 84fa709d0a0a56d05d606a35943b18fa668522d5 Mon Sep 17 00:00:00 2001 From: Cristan Beskid <596554+sferra@users.noreply.github.com> Date: Fri, 23 Feb 2024 17:18:27 +0100 Subject: [PATCH 14/15] Mock the search term normalization --- packages/core/src/plugins/stream/BandcampPlugin.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/core/src/plugins/stream/BandcampPlugin.test.ts b/packages/core/src/plugins/stream/BandcampPlugin.test.ts index 32c1280a9a..6fa8c98016 100644 --- a/packages/core/src/plugins/stream/BandcampPlugin.test.ts +++ b/packages/core/src/plugins/stream/BandcampPlugin.test.ts @@ -87,9 +87,11 @@ describe('Bandcamp plugin tests', () => { imageUrl: 'irrelevant for this test', tags: [] }; + const normalizeForMatching = spyOn(plugin, 'normalizeForMatching'); + normalizeForMatching.mockImplementation(term => term); const streamQuery = { - artist: ' Artist Name', - track: 'Track Name ', + artist: 'Artist Name', + track: 'Track Name', ...irrelevantSearchResultAttributes }; const matcher = plugin.createTrackMatcher(streamQuery); From 26392a81a57c7f5619606bad0617f3c7bf5b5200 Mon Sep 17 00:00:00 2001 From: Cristan Beskid <596554+sferra@users.noreply.github.com> Date: Fri, 23 Feb 2024 18:12:38 +0100 Subject: [PATCH 15/15] Change then/catch sequence to try/await/catch As requested in the review. --- packages/core/src/plugins/meta/discogs.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/core/src/plugins/meta/discogs.ts b/packages/core/src/plugins/meta/discogs.ts index 99376f4873..5a796392b6 100644 --- a/packages/core/src/plugins/meta/discogs.ts +++ b/packages/core/src/plugins/meta/discogs.ts @@ -203,16 +203,17 @@ class DiscogsMetaProvider extends MetaProvider { async getSimilarArtists(artist: LastFmArtistInfo | undefined): Promise { if (!artist?.similar?.artist) { - return Promise.resolve([]); + return []; } const similarArtists = artist.similar.artist; - return getToken() - .then(spotifyToken => this.fetchTopSimilarArtistsFromSpotify(similarArtists, spotifyToken)) - .catch(error => { - logger.error(`Failed to fetch similar artists for '${artist.name}'`); - logger.error(error); - return []; - }); + try { + const spotifyToken = await getToken(); + return await this.fetchTopSimilarArtistsFromSpotify(similarArtists, spotifyToken); + } catch (error) { + logger.error(`Failed to fetch similar artists for '${artist.name}'`); + logger.error(error); + } + return []; } async fetchTopSimilarArtistsFromSpotify(artists: LastfmArtistShort[], spotifyToken: string) {