Hari ini, pada hari Jumat, saya ingin berbicara tentang salah satu proyek hewan peliharaan saya, hal-hal menarik apa yang harus saya lakukan saat mengerjakannya dan masalah apa yang tidak dapat saya selesaikan untuk pengembangan lebih lanjut.
Jadi, saya memiliki beberapa proyek hewan peliharaan dengan berbagai tingkat penyelesaian. Diantaranya: jejaring sosial untuk penulis, generator sprite CSS, bot Telegram untuk berkencan berdasarkan minat, dan banyak lagi. Hari ini kita akan berbicara tentang perkembangan terakhir saya.
Seperti banyak orang saat ini, saya sedang belajar bahasa Inggris. Saya pikir banyak orang juga tahu bahwa pendekatan yang efektif dalam hal ini adalah pencelupan maksimum dalam lingkungan. Antarmuka telepon dalam bahasa Inggris, catatan di buku catatan dalam bahasa Inggris, menonton film dalam bahasa Inggris dengan teks bahasa Inggris. Menonton film dalam bahasa aslinya, cepat atau lambat ada kebutuhan untuk menerjemahkan ini atau itu kata atau frase yang berkedip di layar setiap beberapa menit. Tanpa mereka, tidak ada yang jelas sama sekali.
Ide proyek
Jadi saya mendapatkan ide tentang pemutar video dengan teks yang dapat diterjemahkan. Aplikasi ini memungkinkan Anda menerjemahkan kata dan seluruh frasa saat menonton film. Dengannya, tidak perlu beralih antar aplikasi atau mengambil smartphone. Temui LinguaPlayer .
Skema kerjanya sederhana. Pengguna membuka file film dan file subtitle. Tonton filmnya seperti biasa. Namun, sekarang, selain hotkey standar, ia memiliki kunci untuk menerjemahkan setiap kata secara terpisah, menerjemahkan seluruh kalimat, memutar ulang dari satu baris ke baris lainnya. Ada juga terjemahan dengan mengarahkan kursor mouse ke atas kata-kata atau menyorot bagian teks yang diinginkan. Aplikasi ini tersedia untuk Windows dan MacOS. Semua detail dapat ditemukan di halaman aplikasi .
Tumpukan teknologi
Electron, . . Chromium, -. – . Visual Studio Code, Skype, Slack. Electron API, JavaScript, . . – , . JavaScript, Angular, jQuery, Vue – .
LinguaPlayer , : TypeScript, React, MobX, Webpack. , : , . . , , . . , DOM . , , , .
, . — srt- . – , .
node-webvtt. « ». video- «timeupdate», . , «timeupdate» , . .
hash map. (, ), – , . :
{
// 2
5: [1, 2]
// 3
7: [3, 4, 5]
}
0 4 — . , , — hash map. , . , , . 4 , . , :
// : , ( ), ,
class Cue {
public readonly index: number;
public readonly startTime: number;
public readonly endTime: number;
public readonly text: string;
constructor(index: number, startTime: number, endTime: number, text: string) {
this.index = index;
this.startTime = startTime;
this.endTime = endTime;
this.text = text;
}
}
interface CueIndex {
// ( ) ,
//
[key: number]: number[];
}
class SubtitlesTrack {
private readonly cues: Cue[];
private index: CueIndex = {};
constructor(cues: Cue[]) {
this.cues = cues;
// ,
this.indexCues();
}
private indexCues() {
this.cues.forEach((cue: Cue) => {
//
const startSecond = Math.floor(cue.startTime / 1000);
const endSecond = Math.floor(cue.endTime / 1000);
// ( )
this.addToIndex(startSecond, cue);
// , ,
//
if (endSecond !== startSecond) {
this.addToIndex(endSecond, cue);
}
});
}
private addToIndex(secondNumber: number, cue: Cue): void {
// ,
if (!this.index[secondNumber]) {
this.index[secondNumber] = [];
}
//
this.index[secondNumber].push(cue.index);
}
//
public findCueForTime(timeInSeconds: number): Cue|null {
// timeupdate
//
const flooredTime = Math.floor(timeInSeconds);
//
const cues = this.index[flooredTime];
let currentCue = null;
//
if (cues) {
//
for (let index of cues) {
const cue = this.cues[index];
// ,
if (this.isCueInTime(timeInSeconds, cue)) {
// ,
currentCue = cue;
break;
}
}
}
// null,
return currentCue;
}
public isCueInTime(timeInSeconds: number, cue: Cue): boolean {
const timeInMilliseconds: number = timeInSeconds * 1000;
return timeInMilliseconds >= cue.startTime && timeInMilliseconds <= cue.endTime;
}
}
, , 4 , , 1 4.
node-sentence-tokenizer. div sentence word , . :
import Tokenizer from 'sentence-tokenizer';
function formatCue(text: string): string {
const brMark: string = '[br]';
const tokenizer = new Tokenizer();
//
text = text
.replace(/\r\n/g, ` ${brMark}`)
.replace(/\r/g, ` ${brMark}`)
.replace(/\n/g, ` ${brMark}`);
// text
tokenizer.setEntry(text);
//
const sentenceTokens: string[] = tokenizer.getSentences();
//
const sentencesHtml: string[] = sentenceTokens.map((sentenceToken: string, index: number) => {
//
const wordTokens: string[] = tokenizer.getTokens(index);
//
const wordsHtml: string[] = wordTokens.map((wordToken: string) => {
let brTag: string = '';
// , html
if (wordToken.includes(brMark)) {
wordToken = wordToken.replace(brMark, '');
brTag = '<br/>';
}
// span word br,
return `${brTag}<span class="word">${wordToken}</span>`;
});
// , , span sentence
return `<span class="sentence">${wordsHtml.join(' ')}</span>`;
});
//
const html: string = sentencesHtml.join(' ');
return html;
}
,
, MVP, proof of concept. . , -, Urban Dictionary , . , LinguaLeo Skyeng. . Anki. .
, , . , , . , – Chromium. , , H.264 FLAC MP3. , . – . , , .
Jadi, faktor pemblokiran utama saat ini adalah konten. Itu harus dimainkan tanpa masalah dalam aplikasi, itu harus bisa mendapatkannya dengan mudah dan cepat, dan juga, tidak boleh melanggar lisensi dan hak cipta. Segera setelah masalah konten teratasi, saya dengan senang hati akan terus mengerjakan proyek tersebut. Sementara itu, jika ada yang tertarik, Anda dapat mengunduh dan mencoba versi konsep aplikasi tersebut.