mirror of
https://github.com/FreeTubeApp/FreeTube.git
synced 2025-12-05 01:10:31 +00:00
Fix some template strings in locale files, add script to find problematic values (#7519)
* Fix template strings * add script to make it easier to find issues with template strings * switch to sync fs, exclude non yaml files, add jsdoc comment * Don't fail on duplicate templates in the same string We should only need to validate if the key is in the string, not how many times the key is in the string
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -28,3 +28,5 @@ debug/
|
||||
|
||||
# Lefthook
|
||||
lefthook-local.yml
|
||||
|
||||
locale-errors.json
|
||||
|
||||
86
_scripts/findMissingTemplates.mjs
Normal file
86
_scripts/findMissingTemplates.mjs
Normal file
@@ -0,0 +1,86 @@
|
||||
import { readdirSync, readFileSync, writeFileSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import { load as loadYaml } from 'js-yaml'
|
||||
|
||||
const localesPath = join(import.meta.dirname, '..', 'static', 'locales')
|
||||
const defaultLocale = 'en-US.yaml'
|
||||
|
||||
const errors = [
|
||||
|
||||
]
|
||||
|
||||
const defaultData = loadYaml(readFileSync(`${localesPath}/${defaultLocale}`, { encoding: 'utf-8' }))
|
||||
const defaultKeys = Object.keys(defaultData)
|
||||
|
||||
const filesInLocaleDir = readdirSync(localesPath)
|
||||
|
||||
for (const file of filesInLocaleDir) {
|
||||
if (file !== defaultLocale && file.endsWith('.yaml')) {
|
||||
const fileData = loadYaml(readFileSync(`${localesPath}/${file}`, { encoding: 'utf-8' }))
|
||||
const fileDataKeys = Object.keys(fileData)
|
||||
addErrors(defaultData, fileData, defaultKeys, fileDataKeys, file)
|
||||
}
|
||||
}
|
||||
|
||||
writeFileSync('locale-errors.json', JSON.stringify(errors, null, 2))
|
||||
|
||||
if (errors.length > 0) {
|
||||
console.error(errors)
|
||||
} else {
|
||||
console.log('no issues found')
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {unknown} originalData - data from en-US converted to a JavaScript object
|
||||
* @param {unknown} newData - data from the file we are analyzing converted to a JavaScript object
|
||||
* @param {string[]} originalKeys - keys from en-US file
|
||||
* @param {string[]} newKeys - keys from the file we are currently analyzing
|
||||
* @param {string} file - the file we are currently analyzing
|
||||
*/
|
||||
function addErrors(originalData, newData, originalKeys, newKeys, file) {
|
||||
newKeys.forEach(newKey => {
|
||||
if (originalKeys.includes(newKey)) {
|
||||
if (typeof originalData[newKey] === 'object') {
|
||||
addErrors(originalData[newKey], newData[newKey], Object.keys(originalData[newKey]), Object.keys(newData[newKey]), file)
|
||||
} else if (isMissingInterpolation(originalData[newKey], newData[newKey], file)) {
|
||||
errors.push({ fileName: file, error: 'value is missing a template or has an extra template', key: newKey, defaultValue: originalData[newKey], value: newData[newKey] })
|
||||
}
|
||||
} else {
|
||||
// The key doesn't exist in the en-US file but exists in current yaml file.
|
||||
// We should go through this eventually but it's not as important as invalid templates
|
||||
|
||||
// errors.push({ fileName: file, error: 'extra key found', key: fdk })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} defaultValue
|
||||
* @param {String} otherValue
|
||||
*/
|
||||
function isMissingInterpolation(defaultValue, otherValue, filename) {
|
||||
if (otherValue === '') {
|
||||
// not translated yet, we don't care
|
||||
return false
|
||||
}
|
||||
const defaultMatches = Array.from(new Set(defaultValue.match(/{[^}]*}/g)))
|
||||
const otherMatches = Array.from(new Set(otherValue.match(/{[^}]*}/g)))
|
||||
|
||||
if (defaultMatches) {
|
||||
if (!otherMatches) {
|
||||
// no templates found.
|
||||
return true
|
||||
}
|
||||
defaultMatches.sort()
|
||||
otherMatches.sort()
|
||||
|
||||
const defaultMatchesStringified = JSON.stringify(defaultMatches)
|
||||
const otherMatchesStringified = JSON.stringify(otherMatches)
|
||||
// check if templates match.
|
||||
return defaultMatchesStringified !== otherMatchesStringified
|
||||
} else if (otherMatches) {
|
||||
// extra template found
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,7 @@
|
||||
"pack:web": "webpack --mode=production --node-env=production --config _scripts/webpack.web.config.js",
|
||||
"pack:botGuardScript": "webpack --config _scripts/webpack.botGuardScript.config.js",
|
||||
"postinstall": "yarn run --silent patch-shaka",
|
||||
"checkforbadtemplates": "node _scripts/findMissingTemplates.mjs",
|
||||
"ci": "yarn install --silent --frozen-lockfile --network-concurrency 1"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -27,7 +27,7 @@ Close: 'suilt'
|
||||
Back: 'Terug'
|
||||
Forward: 'vorentoe'
|
||||
Open New Window: 'Maak nuwe venster oop'
|
||||
Go to page: 'Gaan na {bladsy}'
|
||||
Go to page: 'Gaan na {page}'
|
||||
Close Banner: 'Sluit baniere'
|
||||
|
||||
Version {versionNumber} is now available! Click for more details: 'Weergawe {versionNumber}
|
||||
@@ -1171,7 +1171,7 @@ Screenshot Error: 'Skermkiekie het misluk. {error}'
|
||||
Channel Hidden: '{channel} tot kanaalfilter toegevoeg'
|
||||
Channel Unhidden: '{channel} verwyder uit kanaalfilter'
|
||||
Trimmed input must be at least N characters long: 'Bygesnyde invoer moet ten minste
|
||||
1 karakter lank wees | Bygesnyde invoer moet ten minste {lentgh} karakters lank
|
||||
1 karakter lank wees | Bygesnyde invoer moet ten minste {length} karakters lank
|
||||
wees'
|
||||
Tag already exists: 'Etiket “{tagName}” bestaan reeds'
|
||||
|
||||
|
||||
@@ -29,13 +29,13 @@ Global:
|
||||
Live: লাইভ
|
||||
Sort By: বস্তু অনুযায়ী সজাওক
|
||||
Counts:
|
||||
Comment Count: ১ টা মন্তব্য | {গণনা} মন্তব্য
|
||||
Like Count: ১ ৰ দৰে | {গণনা} লাইক
|
||||
Subscriber Count: ১ জন গ্ৰাহক | {গণনা} গ্ৰাহক
|
||||
Watching Count: ১ চাই থকা | {গণনা} চাই আছে
|
||||
View Count: ১ দৰ্শন | {গণনা} দৰ্শন
|
||||
Video Count: ১ ভিডিঅ' | {গণনা} ভিডিঅ'
|
||||
Channel Count: ১ চেনেল | {গণনা} চেনেল
|
||||
Comment Count: ১ টা মন্তব্য | {count} মন্তব্য
|
||||
Like Count: ১ ৰ দৰে | {count} লাইক
|
||||
Subscriber Count: ১ জন গ্ৰাহক | {count} গ্ৰাহক
|
||||
Watching Count: ১ চাই থকা | {count} চাই আছে
|
||||
View Count: ১ দৰ্শন | {count} দৰ্শন
|
||||
Video Count: ১ ভিডিঅ' | {count} ভিডিঅ'
|
||||
Channel Count: ১ চেনেল | {count} চেনেল
|
||||
Shorts: শ্বৰ্ট
|
||||
Posts: পোষ্ট
|
||||
Search Listing:
|
||||
|
||||
@@ -368,8 +368,8 @@ Global:
|
||||
Video Count: 1 video | {count} video
|
||||
Watching Count: 1 baxır | {count} baxır
|
||||
Channel Count: 1 kanal | {count} kanal
|
||||
Like Count: 1 bəyəni | {sayı} bəyənmələr
|
||||
Comment Count: 1 şərh | {sayı} şərhlər
|
||||
Like Count: 1 bəyəni | {count} bəyənmələr
|
||||
Comment Count: 1 şərh | {count} şərhlər
|
||||
Videos: Videolar
|
||||
Live: Canlı
|
||||
Posts: Postlar
|
||||
@@ -396,7 +396,7 @@ Feed:
|
||||
Feed Last Updated: '{feedName} axının son yeniləmə: {date}'
|
||||
Refresh Feed: '{subscriptionName} / yenilə'
|
||||
Search character limit: Axtarış sorğusu {searchCharacterLimit} simvol həddindən çoxdur
|
||||
Go to page: '{Page} səhifəyə keç'
|
||||
Go to page: '{page} səhifəyə keç'
|
||||
Close Banner: Banneri Bağla
|
||||
Display Label: '{label}: {value}'
|
||||
Yes, Delete: Bəli, Sil
|
||||
|
||||
@@ -124,13 +124,13 @@ Global:
|
||||
Live: লাইভ
|
||||
Sort By: 'সাজানোর পদ্ধতি'
|
||||
Counts:
|
||||
Video Count: ১ ভিডিও |{সমষ্টি }ভিডিও
|
||||
Subscriber Count: ১ সাবস্ক্রাইবার |{সমষ্টি }সাবস্ক্রাইবার
|
||||
View Count: ১ দেখা হয়েছে |{সমষ্টি }দর্শন সংখ্যা
|
||||
Watching Count: ১ দেখছি |{গণনা }দেখছেন
|
||||
Channel Count: ১ চ্যানেল |{সমষ্টি }চ্যানেল সমূহ
|
||||
Like Count: ১টি পছন্দ | {গণনা} পছন্দ করা হয়েছে
|
||||
Comment Count: ১টি মন্তব্য | {গণনা} মন্তব্য করা হয়েছে
|
||||
Video Count: ১ ভিডিও |{count}ভিডিও
|
||||
Subscriber Count: ১ সাবস্ক্রাইবার |{count}সাবস্ক্রাইবার
|
||||
View Count: ১ দেখা হয়েছে |{count}দর্শন সংখ্যা
|
||||
Watching Count: ১ দেখছি |{count}দেখছেন
|
||||
Channel Count: ১ চ্যানেল |{count}চ্যানেল সমূহ
|
||||
Like Count: ১টি পছন্দ | {count} পছন্দ করা হয়েছে
|
||||
Comment Count: ১টি মন্তব্য | {count} মন্তব্য করা হয়েছে
|
||||
Posts: পোস্ট
|
||||
External link opening has been disabled in the general settings: সাধারণ পছন্দসমূহে
|
||||
বহিঃসংযোগ খোলা নিষ্ক্রিয় রাখা হয়েছে
|
||||
@@ -191,7 +191,7 @@ User Playlists:
|
||||
Are you sure you want to delete this playlist? This cannot be undone: আপনি কি নিশ্চিত
|
||||
যে আপনি চালুতালিকাটি মুছতে চান? এটি পূর্বাবস্থায় ফেরানো সম্ভব নয়।
|
||||
More: অতিরিক্ত
|
||||
Go to page: পরবর্তী {পেইজ}
|
||||
Go to page: পরবর্তী {page}
|
||||
Search character limit: সার্চ কোয়েরি {searchCharacterLimit} অক্ষরের সীমা অতিক্রম
|
||||
করেছে
|
||||
Close Banner: ব্যানারটি বন্ধ করুন
|
||||
|
||||
@@ -1175,7 +1175,7 @@ Screenshot Error: 'Tapadenn skramm c''hwitet. {error}'
|
||||
Channel Hidden: '{channel} ouzhpennet da sil ar chadennoù'
|
||||
Channel Unhidden: '{channel} dilamet deus sil ar chadennoù'
|
||||
Trimmed input must be at least N characters long: 'An destenn bevennet a rank bezañ
|
||||
1 arouezenn enni da nebeutañ | An destenn bevennet a rank bezañ {number} arouezenn
|
||||
1 arouezenn enni da nebeutañ | An destenn bevennet a rank bezañ {length} arouezenn
|
||||
enni da nebeutañ'
|
||||
Tag already exists: 'Bez ez eus dija deus an dikedenn "{tagName}"'
|
||||
|
||||
|
||||
@@ -478,7 +478,7 @@ Settings:
|
||||
Default Viewing Mode:
|
||||
Theater: Teatro
|
||||
Default Viewing Mode: Vista predeterminada
|
||||
External Player: Reproductor externo ({nombreReproductorExterno})
|
||||
External Player: Reproductor externo ({externalPlayerName})
|
||||
Full Screen: Pantalla Completa
|
||||
Picture in Picture: Imagen en imagen
|
||||
Privacy Settings:
|
||||
|
||||
@@ -865,7 +865,7 @@ Video:
|
||||
CodecAudio: 'کدک: {audioCodec} ({audioItag})'
|
||||
Dropped Frames / Total Frames: 'فریمهای رد شده: {droppedFrames} / مجموع فریمها:
|
||||
{totalFrames}'
|
||||
CodecsVideoAudio: 'کدکها: {videoCodec} ({videotag}) / {audioCodec} ({audioItag})'
|
||||
CodecsVideoAudio: 'کدکها: {videoCodec} ({videoItag}) / {audioCodec} ({audioItag})'
|
||||
CodecsVideoAudioNoItags: 'کدکها: {videoCodec} / {audioCodec}'
|
||||
Take Screenshot: گرفتن عکس صفحه
|
||||
Show Stats: نمایش اطلاعات
|
||||
|
||||
@@ -219,7 +219,7 @@ User Playlists:
|
||||
Enable Quick Bookmark With This Playlist: Ota nopea kirjanmerkki käyttöön tällä
|
||||
soittolistalla
|
||||
Remove Duplicate Videos: Poista päällekkäiset videot
|
||||
TotalTimePlaylist: 'Kokonaisaika: {kesto}'
|
||||
TotalTimePlaylist: 'Kokonaisaika: {duration}'
|
||||
Are you sure you want to remove {playlistItemCount} duplicate videos from this playlist? This cannot be undone: Haluatko
|
||||
varmasti poistaa 1 päällekkäisen videon tästä soittolistasta? Tätä ei voi peruuttaa.
|
||||
| Haluatko varmasti poistaa {playlistItemCount} päällekkäiset videot tästä soittolistasta?
|
||||
|
||||
@@ -824,7 +824,7 @@ Channel:
|
||||
Video hidden by FreeTube: Video disembunyikan oleh FreeTube
|
||||
This channel currently does not have any posts: Kanal ini belum memiliki pos apa
|
||||
pun
|
||||
votes: '{Votes} memilih'
|
||||
votes: '{votes} memilih'
|
||||
View Full Post: Lihat Postingan Lengkap
|
||||
Viewing Posts Only Supported By Invidious: Melihat Postingan hanya didukung oleh
|
||||
Invidious. Buka tab komunitas saluran untuk melihat konten di sana tanpa Invidious.
|
||||
@@ -939,7 +939,7 @@ Video:
|
||||
Show Original Details: Tampilkan Detail Asli
|
||||
Show Modified Details: Tampilkan Detail yang Dimodifikasi
|
||||
Player:
|
||||
TranslatedCaptionTemplate: '{bahasa} (diterjemahkan dari "{bahasaasli}")'
|
||||
TranslatedCaptionTemplate: '{language} (diterjemahkan dari "{originalLanguage}")'
|
||||
Audio Tracks: Trek Audio
|
||||
Autoplay is off: Putar otomatis mati
|
||||
Autoplay is on: Putar otomatis hidup
|
||||
@@ -947,12 +947,12 @@ Video:
|
||||
Stats:
|
||||
Video ID: 'ID Video: {videoId}'
|
||||
Bitrate: 'Kecepatan bit: {bitrate} kbps'
|
||||
Volume: 'Volume: {volumePersentase}%'
|
||||
Volume: 'Volume: {volumePercentage}%'
|
||||
Buffered: 'Di-buffer: {bufferedPercentage}%'
|
||||
CodecAudio: 'Kodek: {audioCodec} ({audioItag})'
|
||||
Dropped Frames / Total Frames: 'Frame yang Dihilangkan: {droppedFrames} / Jumlah
|
||||
Frame: {totalFrames}'
|
||||
Media Formats: 'Format Media: {format}'
|
||||
Media Formats: 'Format Media: {formats}'
|
||||
CodecsVideoAudio: 'Kodek: {videoCodec} ({videoItag}) / {audioCodec} ({audioItag})'
|
||||
Resolution: 'Resolusi: {width}x{height}@{frameRate}'
|
||||
Player Dimensions: 'Dimensi Pemain: {width}x{height}'
|
||||
@@ -1288,7 +1288,7 @@ Hashtag:
|
||||
This hashtag does not currently have any videos: Tagar ini saat ini tidak memiliki
|
||||
video apa pun
|
||||
Hashtag: Hastage
|
||||
Display Label: '{label}: {nilai}'
|
||||
Display Label: '{label}: {value}'
|
||||
Channel Hidden: '{channel} ditambahkan ke filter saluran'
|
||||
Age Restricted:
|
||||
This video is age restricted: Video ini dibatasi usia
|
||||
@@ -1296,7 +1296,7 @@ Age Restricted:
|
||||
Trimmed input must be at least N characters long: Input yang dipangkas harus memiliki
|
||||
panjang minimal 1 karakter | Input yang dipangkas harus memiliki panjang minimal
|
||||
{length} karakter
|
||||
KeyboardShortcutTemplate: '{label} ({pintasan})'
|
||||
KeyboardShortcutTemplate: '{label} ({shortcut})'
|
||||
shortcutJoinOperator: +
|
||||
Chapters:
|
||||
Key Moments: Momen-momen penting
|
||||
|
||||
@@ -196,7 +196,7 @@ User Playlists:
|
||||
Are you sure you want to remove {playlistItemCount} watched videos from this playlist? This cannot be undone: Sei sicuro di voler rimuovere 1 video guardato da questa playlist? Questa operazione non può essere annullata. | Vuoi rimuovere {playlistItemCount} video guardati da questa playlist? Questa operazione non può essere annullata.
|
||||
Export Playlist: Esporta questa playlist
|
||||
The playlist has been successfully exported: La playlist è stata esportata correttamente
|
||||
TotalTimePlaylist: 'Tempo totale: {durata}'
|
||||
TotalTimePlaylist: 'Tempo totale: {duration}'
|
||||
History:
|
||||
# On History Page
|
||||
History: 'Cronologia'
|
||||
|
||||
@@ -158,7 +158,7 @@ Settings:
|
||||
Middle: 'შუაში'
|
||||
End: 'ბოლოში'
|
||||
Current Invidious Instance: 'Invidious-ის მიმდინარე ასლი'
|
||||
The currently set default instance is {instance}: 'ამჟამად დაყენებული ასლია {number}'
|
||||
The currently set default instance is {instance}: 'ამჟამად დაყენებული ასლია {instance}'
|
||||
No default instance has been set: 'სტანდარტული ასლი არ არის დაყენებული'
|
||||
Current instance will be randomized on startup: 'ჩართვისას მიმდინარე ასლი იქნება
|
||||
შემთხვევით არჩეული'
|
||||
|
||||
@@ -138,7 +138,7 @@ User Playlists:
|
||||
Allow Adding Duplicate Video(s): ڕێگە بدە بە زیادکردنی ڤیدیۆ(ەکان)ی دووبارە
|
||||
"{videoCount}/{totalVideoCount} Videos Already Added": '{videoCount}/{totalVideoCount}
|
||||
ڤیدیۆکان پێشتر زیادکراون'
|
||||
Added {count} Times: “پێشتر زیاد کراوە | زیادکرا {ژمارە} کاتەکان
|
||||
Added {count} Times: “پێشتر زیاد کراوە | زیادکرا {count} کاتەکان
|
||||
Save: هەڵگرتن
|
||||
N playlists selected: '{playlistCount} هەڵبژێردراوە'
|
||||
Search in Playlists: لە پلەی لیستەکاندا بگەڕێ
|
||||
@@ -225,7 +225,7 @@ User Playlists:
|
||||
EarliestCreatedFirst: سەرەتاییترین دروستکراوە
|
||||
EarliestUpdatedFirst: زووترین نوێکراوەتەوە
|
||||
EarliestPlayedFirst: زووترین یاری
|
||||
TotalTimePlaylist: 'کۆی گشتی کات: {ماوە}'
|
||||
TotalTimePlaylist: 'کۆی گشتی کات: {duration}'
|
||||
Playlists with Matching Videos: پلەی لیستەکان لەگەڵ ڤیدیۆی هاوتا
|
||||
Edit Playlist Info: دەستکاریکردنی زانیاری پلەی لیست
|
||||
Delete Playlist: سڕینەوەی پلەی لیست
|
||||
@@ -512,7 +512,7 @@ Channels:
|
||||
Unsubscribe Prompt: ئایا دڵنیای کە دەتەوێت بەشداریکردن لە "{channelName}" هەڵبوەشێنیتەوە؟
|
||||
Channels: کەناڵەکان
|
||||
Title: لیستی کەناڵەکان
|
||||
Count: '{ژمارە} کەناڵ(ەکان) دۆزرایەوە.'
|
||||
Count: '{number} کەناڵ(ەکان) دۆزرایەوە.'
|
||||
Right-click or hold to see history: کلیک لە ڕاست یان هەڵبژاردن بەدوورەوە بۆ بینینی
|
||||
مێژووی
|
||||
Search Listing:
|
||||
@@ -535,8 +535,8 @@ KeyboardShortcutPrompt:
|
||||
Zoom Out: زووم کردنە دەرەوە
|
||||
Fullscreen: شاشەکەت پرکەرەوە
|
||||
Feed:
|
||||
Feed Last Updated: '{feedName} فید دوایین نوێکردنەوە: {بەروار}'
|
||||
Refresh Feed: نوێکردنەوە {ناوی بەشداریکردن}
|
||||
Feed Last Updated: '{feedName} فید دوایین نوێکردنەوە: {date}'
|
||||
Refresh Feed: نوێکردنەوە {subscriptionName}
|
||||
More: زیاتر
|
||||
Search character limit: پرسیاری گەڕان لە سەرووی سنووری کاراکتەری {searchCharacterLimit}
|
||||
دایە
|
||||
|
||||
@@ -834,7 +834,7 @@ Video:
|
||||
CodecsVideoAudioNoItags: 'Codecuri: {videoCodec} / {audioCodec}'
|
||||
Stats: Statistici
|
||||
Video ID: 'ID videoclip: {videoId}'
|
||||
Media Formats: 'Formate media: {formate}'
|
||||
Media Formats: 'Formate media: {formats}'
|
||||
Bitrate: 'Rată de biți: {bitrate} kbps'
|
||||
Volume: 'Volum: {volumePercentage}%'
|
||||
Bandwidth: 'Lățime de bandă: {bandwidth} kbps'
|
||||
@@ -1113,7 +1113,7 @@ Keys:
|
||||
checkmark: ✓
|
||||
Yes, Delete: da, șterge
|
||||
Yes, Open Link: Da, Link deschis
|
||||
Display Label: '{label}: {valoare}'
|
||||
Display Label: '{label}: {value}'
|
||||
KeyboardShortcutTemplate: '{label} ({shortcut})'
|
||||
Yes, Restart: Da, repornire
|
||||
shortcutJoinOperator: +
|
||||
|
||||
@@ -241,7 +241,7 @@ User Playlists:
|
||||
பிளேலிச்ட் இப்போது {oldPlaylistName} க்கு க்கு பதிலாக விரைவான புக்மார்க்குக்கு
|
||||
பயன்படுத்தப்படுகிறது. செயல்தவிர்க்க இங்கே சொடுக்கு செய்க'
|
||||
Reverted to use {oldPlaylistName} for quick bookmark: 'விரைவான புக்மார்க்குக்கு
|
||||
{oldPlayListName} bea ஐப் பயன்படுத்த மாற்றப்பட்டது'
|
||||
{oldPlaylistName} bea ஐப் பயன்படுத்த மாற்றப்பட்டது'
|
||||
|
||||
Some videos in the playlist are not loaded yet. Click here to copy anyway.: 'பிளேலிச்ட்டில்
|
||||
உள்ள சில வீடியோக்கள் இன்னும் ஏற்றப்படவில்லை. எப்படியும் நகலெடுக்க இங்கே சொடுக்கு
|
||||
|
||||
Reference in New Issue
Block a user