Compare commits

...

6 Commits

Author SHA1 Message Date
c200e3255e fix(deps): update dependency i18next to v25.8.5 2026-02-11 14:55:34 +00:00
e2f1ff50dd feat(synced-lyrics): Added Hindi & Bengali romanization support for lyrics (#3933)
Co-authored-by: pranjol <pranjol@pranjols-iMac-Pro.local>
2026-02-11 08:00:36 +09:00
3f1ceca39c chore(i18n): Translated using Weblate (Telugu)
Currently translated at 2.7% (13 of 469 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/te/
2026-02-10 10:09:44 +00:00
37277b2764 chore(i18n): Added translation using Weblate (Telugu) 2026-02-09 11:03:37 +01:00
ba89772b09 chore(i18n): Translated using Weblate (Turkish)
Currently translated at 100.0% (469 of 469 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/tr/
2026-02-07 19:09:44 +00:00
13a0e8e2ab chore(i18n): Translated using Weblate (Italian)
Currently translated at 99.7% (468 of 469 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/it/
2026-02-05 18:01:49 +01:00
6 changed files with 127 additions and 24 deletions

View File

@ -77,6 +77,7 @@
"@hono/swagger-ui": "0.5.3", "@hono/swagger-ui": "0.5.3",
"@hono/zod-openapi": "1.2.1", "@hono/zod-openapi": "1.2.1",
"@hono/zod-validator": "0.7.6", "@hono/zod-validator": "0.7.6",
"@indic-transliteration/sanscript": "1.3.3",
"@jellybrick/dbus-next": "0.10.3", "@jellybrick/dbus-next": "0.10.3",
"@jellybrick/electron-better-web-request": "2.0.0", "@jellybrick/electron-better-web-request": "2.0.0",
"@jellybrick/mpris-service": "2.1.5", "@jellybrick/mpris-service": "2.1.5",
@ -108,7 +109,7 @@
"hono": "4.11.7", "hono": "4.11.7",
"howler": "2.2.4", "howler": "2.2.4",
"html-to-text": "9.0.5", "html-to-text": "9.0.5",
"i18next": "25.8.0", "i18next": "25.8.5",
"jimp": "1.6.0", "jimp": "1.6.0",
"keyboardevent-from-electron-accelerator": "2.0.0", "keyboardevent-from-electron-accelerator": "2.0.0",
"keyboardevents-areequal": "0.2.2", "keyboardevents-areequal": "0.2.2",

50
pnpm-lock.yaml generated
View File

@ -78,6 +78,9 @@ importers:
'@hono/zod-validator': '@hono/zod-validator':
specifier: 0.7.6 specifier: 0.7.6
version: 0.7.6(hono@4.11.7)(zod@4.3.6) version: 0.7.6(hono@4.11.7)(zod@4.3.6)
'@indic-transliteration/sanscript':
specifier: 1.3.3
version: 1.3.3
'@jellybrick/dbus-next': '@jellybrick/dbus-next':
specifier: 0.10.3 specifier: 0.10.3
version: 0.10.3 version: 0.10.3
@ -172,8 +175,8 @@ importers:
specifier: 9.0.5 specifier: 9.0.5
version: 9.0.5 version: 9.0.5
i18next: i18next:
specifier: 25.8.0 specifier: 25.8.5
version: 25.8.0(typescript@5.9.3) version: 25.8.5(typescript@5.9.3)
jimp: jimp:
specifier: 1.6.0 specifier: 1.6.0
version: 1.6.0 version: 1.6.0
@ -843,6 +846,12 @@ packages:
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
engines: {node: '>=18.18'} engines: {node: '>=18.18'}
'@indic-transliteration/common_maps@1.0.5':
resolution: {integrity: sha512-XbWDA5AXGE+Nh4uGr/yN9ZM8avRBy4F1KQL+DLgQGOdsQ390lcW4fga0NSjg4C/rOpMd0rHZv2YFV3Bq3UbpkQ==}
'@indic-transliteration/sanscript@1.3.3':
resolution: {integrity: sha512-zNGeARmQTPIlubwgEhl/JumpwTPHrdT/cNsQeCL+G67SQmjJe3qRnMIYghXiVt7+KDso/pU1Ky2ZfD/RBISfJQ==}
'@isaacs/balanced-match@4.0.1': '@isaacs/balanced-match@4.0.1':
resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==}
engines: {node: 20 || >=22} engines: {node: 20 || >=22}
@ -1297,9 +1306,6 @@ packages:
'@types/node@24.10.9': '@types/node@24.10.9':
resolution: {integrity: sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==} resolution: {integrity: sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==}
'@types/node@25.1.0':
resolution: {integrity: sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==}
'@types/node@25.2.0': '@types/node@25.2.0':
resolution: {integrity: sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==} resolution: {integrity: sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==}
@ -2692,16 +2698,16 @@ packages:
glob@11.1.0: glob@11.1.0:
resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==}
engines: {node: 20 || >=22} engines: {node: 20 || >=22}
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
hasBin: true hasBin: true
glob@13.0.0: glob@13.0.0:
resolution: {integrity: sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==} resolution: {integrity: sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==}
engines: {node: 20 || >=22} engines: {node: 20 || >=22}
hasBin: true
glob@7.2.3: glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
deprecated: Glob versions prior to v9 are no longer supported deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
global-agent@3.0.0: global-agent@3.0.0:
resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==} resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==}
@ -2817,8 +2823,8 @@ packages:
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
engines: {node: '>=10.17.0'} engines: {node: '>=10.17.0'}
i18next@25.8.0: i18next@25.8.5:
resolution: {integrity: sha512-urrg4HMFFMQZ2bbKRK7IZ8/CTE7D8H4JRlAwqA2ZwDRFfdd0K/4cdbNNLgfn9mo+I/h9wJu61qJzH7jCFAhUZQ==} resolution: {integrity: sha512-TApjhgqQU6P7BQlpCTv6zQuXrYAP9rjYWgx2Nm8dsq+Zg9yJlAz+iR16/w7uVtTlSoULbqPTfqYjMK/DAQI+Ng==}
peerDependencies: peerDependencies:
typescript: ^5 typescript: ^5
peerDependenciesMeta: peerDependenciesMeta:
@ -4338,6 +4344,9 @@ packages:
resolution: {integrity: sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==} resolution: {integrity: sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
toml@2.3.6:
resolution: {integrity: sha512-gVweAectJU3ebq//Ferr2JUY4WKSDe5N+z0FvjDncLGyHmIDoxgY/2Ie4qfEIDm4IS7OA6Rmdm7pdEEdMcV/xQ==}
totalist@3.0.1: totalist@3.0.1:
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -5206,6 +5215,13 @@ snapshots:
'@humanwhocodes/retry@0.4.3': {} '@humanwhocodes/retry@0.4.3': {}
'@indic-transliteration/common_maps@1.0.5': {}
'@indic-transliteration/sanscript@1.3.3':
dependencies:
'@indic-transliteration/common_maps': 1.0.5
toml: 2.3.6
'@isaacs/balanced-match@4.0.1': {} '@isaacs/balanced-match@4.0.1': {}
'@isaacs/brace-expansion@5.0.0': '@isaacs/brace-expansion@5.0.0':
@ -5697,7 +5713,7 @@ snapshots:
dependencies: dependencies:
'@types/http-cache-semantics': 4.2.0 '@types/http-cache-semantics': 4.2.0
'@types/keyv': 3.1.4 '@types/keyv': 3.1.4
'@types/node': 25.1.0 '@types/node': 25.2.0
'@types/responselike': 1.0.3 '@types/responselike': 1.0.3
'@types/debug@4.1.12': '@types/debug@4.1.12':
@ -5728,7 +5744,7 @@ snapshots:
'@types/keyv@3.1.4': '@types/keyv@3.1.4':
dependencies: dependencies:
'@types/node': 25.1.0 '@types/node': 25.2.0
'@types/ms@2.1.0': {} '@types/ms@2.1.0': {}
@ -5738,10 +5754,6 @@ snapshots:
dependencies: dependencies:
undici-types: 7.16.0 undici-types: 7.16.0
'@types/node@25.1.0':
dependencies:
undici-types: 7.16.0
'@types/node@25.2.0': '@types/node@25.2.0':
dependencies: dependencies:
undici-types: 7.16.0 undici-types: 7.16.0
@ -5754,7 +5766,7 @@ snapshots:
'@types/responselike@1.0.3': '@types/responselike@1.0.3':
dependencies: dependencies:
'@types/node': 25.1.0 '@types/node': 25.2.0
'@types/semver@7.7.1': {} '@types/semver@7.7.1': {}
@ -5771,7 +5783,7 @@ snapshots:
'@types/yauzl@2.10.3': '@types/yauzl@2.10.3':
dependencies: dependencies:
'@types/node': 25.1.0 '@types/node': 25.2.0
optional: true optional: true
'@typescript-eslint/eslint-plugin@8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': '@typescript-eslint/eslint-plugin@8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
@ -7578,7 +7590,7 @@ snapshots:
human-signals@2.1.0: {} human-signals@2.1.0: {}
i18next@25.8.0(typescript@5.9.3): i18next@25.8.5(typescript@5.9.3):
dependencies: dependencies:
'@babel/runtime': 7.28.6 '@babel/runtime': 7.28.6
optionalDependencies: optionalDependencies:
@ -9085,6 +9097,8 @@ snapshots:
'@tokenizer/token': 0.3.0 '@tokenizer/token': 0.3.0
ieee754: 1.2.1 ieee754: 1.2.1
toml@2.3.6: {}
totalist@3.0.1: {} totalist@3.0.1: {}
truncate-utf8-bytes@1.0.2: truncate-utf8-bytes@1.0.2:

View File

@ -161,7 +161,8 @@
"default": "Predefinito", "default": "Predefinito",
"force-show": "Forza la visualizzazione", "force-show": "Forza la visualizzazione",
"hide": "Nascondi", "hide": "Nascondi",
"label": "Pulsanti Like" "label": "Pulsanti Like",
"swap": "Scambia l'ordine dei pulsanti like"
}, },
"remove-upgrade-button": "Rimuovi il pulsante aggiorna", "remove-upgrade-button": "Rimuovi il pulsante aggiorna",
"theme": { "theme": {
@ -412,6 +413,17 @@
"no-captions": "Nessun sottotitolo disponibile per questa canzone" "no-captions": "Nessun sottotitolo disponibile per questa canzone"
} }
}, },
"clock": {
"description": "Aggiungi un orologio alla barra di navigazione",
"menu": {
"format": {
"24-hour-format": "Formato in 24 ore",
"display-seconds": "Mostra Secondi",
"label": "Formato"
}
},
"name": "Orologio"
},
"compact-sidebar": { "compact-sidebar": {
"description": "Imposta sempre la barra laterale in modalità compatta", "description": "Imposta sempre la barra laterale in modalità compatta",
"name": "Barra laterale compatta" "name": "Barra laterale compatta"

View File

@ -0,0 +1,31 @@
{
"common": {
"console": {
"plugins": {
"execute-failed": "{{pluginName}}::{{contextName}} ప్లగిన్‌ను అమలు చేయడంలో విఫలమైంది",
"executed-at-ms": "{{pluginName}}::{{contextName}} ప్లగిన్ అమలు చేయబడిన సమయం {{ms}} మిల్లీసెకను",
"initialize-failed": "\"{{pluginName}}\" ప్లగిన్‌ను ప్రారంభించడంలో విఫలమైంది",
"load-all": "అన్నీ ప్లగిన్లను లోడ్ చెయ్యబడుతోంది",
"load-failed": "\"{{pluginName}}\" ప్లగిన్ లోడ్ చేయడంలో విఫలమైంది",
"loaded": "ప్లగిన్ \"{{pluginName}}\" లోడ్ చేయబడింది",
"unload-failed": "“{{pluginName}}” ప్లగిన్‌ను అన్‌లోడ్ చేయడంలో విఫలమైంది",
"unloaded": "\"{{pluginName}}\" ప్లగిన్ అన్‌లోడ్ చేయబడింది"
}
}
},
"language": {
"code": "te",
"local-name": "తెలుగు",
"name": "Telugu"
},
"main": {
"console": {
"did-finish-load": {
"dev-tools": "లోడ్ చేయడం పూర్తయింది. డెవ్‌టూల్స్ తెరవబడ్డాయి"
},
"i18n": {
"loaded": "i18n లోడ్ చేయబడింది"
}
}
}
}

View File

@ -53,9 +53,9 @@
"later": "Daha Sonra", "later": "Daha Sonra",
"restart-now": "Şimdi yeniden başlat" "restart-now": "Şimdi yeniden başlat"
}, },
"detail": "\"{{pluginName}}\" eklentisinin çalışabilmesi için yeniden başlatman gerekiyor", "detail": "\"{{pluginName}}\" eklentisinin çalışması için uygulamanın yeniden başlatılması gerekiyor",
"message": "\"{{pluginName}}\" için yeniden başlatman gerekiyor", "message": "\"{{pluginName}}\" eklentisi için uygulamanın yeniden başlatılması gerekiyor",
"title": "Uygulamayı yeniden başlatman gerekiyor" "title": "Uygulamanın yeniden başlatılması gerekiyor"
}, },
"unresponsive": { "unresponsive": {
"buttons": { "buttons": {
@ -161,7 +161,8 @@
"default": "Varsayılan", "default": "Varsayılan",
"force-show": "Zorla göster", "force-show": "Zorla göster",
"hide": "Gizle", "hide": "Gizle",
"label": "Beğenme düğmeleri" "label": "Beğenme düğmeleri",
"swap": "Beğenme butonlarının sıralamasını değiştir"
}, },
"remove-upgrade-button": "Yükseltme düğmesini kaldır", "remove-upgrade-button": "Yükseltme düğmesini kaldır",
"theme": { "theme": {
@ -412,6 +413,17 @@
"no-captions": "Bu şarkı için altyazı mevcut değil" "no-captions": "Bu şarkı için altyazı mevcut değil"
} }
}, },
"clock": {
"description": "Navigasyon barına bir saat ekle",
"menu": {
"format": {
"24-hour-format": "24-Saat Formatı, Biçimi",
"display-seconds": "Saniyeleri göster",
"label": "Format, Biçim"
}
},
"name": "Saat"
},
"compact-sidebar": { "compact-sidebar": {
"description": "Her zaman kompakt kenar çubugu", "description": "Her zaman kompakt kenar çubugu",
"name": "Kompakt Kenar Çubuğu" "name": "Kompakt Kenar Çubuğu"

View File

@ -7,6 +7,7 @@ import * as pinyin from 'tiny-pinyin';
import { romanize as romanizeThaiFrag } from '@dehoist/romanize-thai'; import { romanize as romanizeThaiFrag } from '@dehoist/romanize-thai';
import { lazy } from 'lazy-var'; import { lazy } from 'lazy-var';
import { detect } from 'tinyld'; import { detect } from 'tinyld';
import Sanscript from '@indic-transliteration/sanscript';
import { waitForElement } from '@/utils/wait-for-element'; import { waitForElement } from '@/utils/wait-for-element';
import { LyricsRenderer, setIsVisible } from './renderer'; import { LyricsRenderer, setIsVisible } from './renderer';
@ -155,6 +156,12 @@ const hasChinese = (lines: string[]) =>
const hasThai = (lines: string[]) => const hasThai = (lines: string[]) =>
lines.some((line) => /[\u0E00-\u0E7F]+/.test(line)); lines.some((line) => /[\u0E00-\u0E7F]+/.test(line));
const hasBengali = (lines: string[]) =>
lines.some((line) => /[\u0980-\u09FF]+/.test(line));
const hasHindi = (lines: string[]) =>
lines.some((line) => /[\u0900-\u097F]+/.test(line));
export const romanizeJapanese = async (line: string) => export const romanizeJapanese = async (line: string) =>
(await kuroshiro.get()).convert(line, { (await kuroshiro.get()).convert(line, {
to: 'romaji', to: 'romaji',
@ -190,11 +197,35 @@ export const romanizeThai = (line: string) => {
return latin; return latin;
}; };
export const romanizeBengali = (line: string) => {
try {
let out = Sanscript.t(line, 'bengali', 'iast');
out = out.normalize('NFD');
out = out.replace(/[\u0300-\u036f]/g, '');
out = out.replace(/[\u09BC\u09BE-\u09CD]/g, '');
return out.toLowerCase();
} catch {
return line;
}
};
export const romanizeHindi = (line: string) => {
try {
let out = Sanscript.t(line, 'devanagari', 'iast');
out = out.normalize('NFD').replace(/[\u0300-\u036f]/g, ''); // strip accents
return out.replace(/[^a-zA-Z\s]/g, '') || line; // remove any remaining symbols
} catch {
return line;
}
};
const handlers: Record<string, (line: string) => Promise<string> | string> = { const handlers: Record<string, (line: string) => Promise<string> | string> = {
ja: romanizeJapanese, ja: romanizeJapanese,
ko: romanizeHangul, ko: romanizeHangul,
zh: romanizeChinese, zh: romanizeChinese,
th: romanizeThai, th: romanizeThai,
bn: romanizeBengali,
hi: romanizeHindi,
}; };
export const romanize = async (line: string) => { export const romanize = async (line: string) => {
@ -210,6 +241,8 @@ export const romanize = async (line: string) => {
if (hasKorean([line])) line = romanizeHangul(line); if (hasKorean([line])) line = romanizeHangul(line);
if (hasChinese([line])) line = romanizeChinese(line); if (hasChinese([line])) line = romanizeChinese(line);
if (hasThai([line])) line = romanizeThai(line); if (hasThai([line])) line = romanizeThai(line);
if (hasBengali([line])) line = romanizeBengali(line);
if (hasHindi([line])) line = romanizeHindi(line);
return line; return line;
}; };