diff --git a/demo/demo.js b/demo/demo.js index 2c7c9de..ecce849 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -88,6 +88,7 @@ function initPlayers() { logo: 'https://i.loli.net/2019/06/06/5cf8c5d94521136430.png', volume: 0.2, mutex: true, + lang: 'zh-cn', video: { url: 'https://api.dogecloud.com/player/get.mp4?vcode=5ac682e6f8231991&userId=17&ext=.mp4', pic: 'https://i.loli.net/2019/06/06/5cf8c5d9c57b510947.png', @@ -140,7 +141,7 @@ function initPlayers() { const eventsEle = document.getElementById('events'); for (let i = 0; i < events.length; i++) { dp2.on(events[i], (info) => { - eventsEle.innerHTML += '

Event: ' + events[i] + '

'; + eventsEle.innerHTML += `

Event: ${events[i]} ${info?`Data: ${JSON.stringify(info)}`:''}

`; eventsEle.scrollTop = eventsEle.scrollHeight; }); } diff --git a/src/js/comment.js b/src/js/comment.js index d299762..d8683da 100644 --- a/src/js/comment.js +++ b/src/js/comment.js @@ -76,7 +76,7 @@ class Comment { // text can't be empty if (!this.player.template.commentInput.value.replace(/^\s+|\s+$/g, '')) { - this.player.notice(this.player.tran('Please input danmaku content!')); + this.player.notice(this.player.tran('please-input-danmaku')); return; } diff --git a/src/js/controller.js b/src/js/controller.js index 9378581..19ee12f 100644 --- a/src/js/controller.js +++ b/src/js/controller.js @@ -366,12 +366,12 @@ class Controller { initSubtitleButton() { if (this.player.options.subtitle) { this.player.events.on('subtitle_show', () => { - this.player.template.subtitleButton.dataset.balloon = this.player.tran('Hide subtitle'); + this.player.template.subtitleButton.dataset.balloon = this.player.tran('hide-subs'); this.player.template.subtitleButtonInner.style.opacity = ''; this.player.user.set('subtitle', 1); }); this.player.events.on('subtitle_hide', () => { - this.player.template.subtitleButton.dataset.balloon = this.player.tran('Show subtitle'); + this.player.template.subtitleButton.dataset.balloon = this.player.tran('show-subs'); this.player.template.subtitleButtonInner.style.opacity = '0.4'; this.player.user.set('subtitle', 0); }); diff --git a/src/js/danmaku.js b/src/js/danmaku.js index fb71ad0..453bc36 100644 --- a/src/js/danmaku.js +++ b/src/js/danmaku.js @@ -70,7 +70,7 @@ class Danmaku { } }, error: (msg) => { - this.options.error(msg || this.options.tran('Danmaku load failed')); + this.options.error(msg || this.options.tran('danmaku-failed')); results[i] = []; ++readCount; @@ -97,7 +97,7 @@ class Danmaku { data: danmakuData, success: callback, error: (msg) => { - this.options.error(msg || this.options.tran('Danmaku send failed')); + this.options.error(msg || this.options.tran('danmaku-failed')); }, }); diff --git a/src/js/i18n.js b/src/js/i18n.js index b849862..cbecd87 100644 --- a/src/js/i18n.js +++ b/src/js/i18n.js @@ -9,139 +9,263 @@ Use this as shown below..... */ function i18n(lang) { this.lang = lang; - this.tran = (text) => { - if (tranTxt[this.lang] && tranTxt[this.lang][text]) { - return tranTxt[this.lang][text]; + // in case someone says en-us, and en is present! + this.fallbackLang = this.lang.includes('-') ? this.lang.split('-')[0] : this.lang; + this.tran = (key) => { + key = key.toLowerCase(); + if (tranTxt[this.lang] && tranTxt[this.lang][key]) { + return tranTxt[this.lang][key]; + } else if (tranTxt[this.fallbackLang] && tranTxt[this.fallbackLang][key]) { + return tranTxt[this.fallbackLang][key]; } else { - return text; + return standard[key]; } }; } +// abstract model for recognizing if valid translations are present +// const model = { +// 'danmaku-loading': [], +// top: [], +// bottom: [], +// rolling: [], +// 'input-danmaku-enter': [], +// 'about-author': [], +// 'dplayer-feedback': [], +// 'about-dplayer': [], +// loop: [], +// speed: [], +// 'opacity-danmaku': [], +// normal: [], +// 'please-input-danmaku': [], +// 'set-danmaku-color': [], +// 'set-danmaku-type': [], +// 'show-danmaku': [], +// 'video-failed': [], +// 'danmaku-failed': [], +// 'danmaku-send-failed': [], +// 'switching-quality': [{ symbol: '%q', name: 'Quality', example: '720p' }], +// 'switched-quality': [{ symbol: '%q', name: 'Quality', example: '720p' }], +// ff: [{ symbol: '%s', name: 'Seconds', example: '5' }], +// rew: [{ symbol: '%s', name: 'Seconds', example: '5' }], +// 'unlimited-danmaku': [], +// 'send-danmaku': [], +// setting: [], +// fullscreen: [], +// 'web-fullscreen': [], +// send: [], +// screenshot: [], +// airplay: [], +// chromecast: [], +// 'show-subs': [], +// 'hide-subs': [], +// volume: [], +// live: [], +// 'video-info': [], +// }; + +// Standard english translations +const standard = { + 'danmaku-loading': 'Danmaku is loading', + top: 'Top', + bottom: 'Bottom', + rolling: 'Rolling', + 'input-danmaku-enter': 'Input danmaku, hit Enter', + 'about-author': 'About author', + 'dplayer-feedback': 'DPlayer feedback', + 'about-dplayer': 'About DPlayer', + loop: 'Loop', + speed: 'Speed', + 'opacity-danmaku': 'Opacity for danmaku', + normal: 'Normal', + 'please-input-danmaku': 'Please input danmaku content!', + 'set-danmaku-color': 'Set danmaku color', + 'set-danmaku-type': 'Set danmaku type', + 'show-danmaku': 'Show danmaku', + 'video-failed': 'Video load failed', + 'danmaku-failed': 'Danmaku load failed', + 'danmaku-send-failed': 'Danmaku send failed', + 'switching-quality': 'Switching to %q quality', + 'switched-quality': 'Switched to %q quality', + ff: 'FF %s s', + rew: 'REW %s s', + 'unlimited-danmaku': 'Unlimited danmaku', + 'send-danmaku': 'Send danmaku', + setting: 'Setting', + fullscreen: 'Full screen', + 'web-fullscreen': 'Web full screen', + send: 'Send', + screenshot: 'Screenshot', + airplay: 'AirPlay', + chromecast: 'ChromeCast', + 'show-subs': 'Show subtitle', + 'hide-subs': 'Hide subtitle', + volume: 'Volume', + live: 'Live', + 'video-info': 'Video info', +}; + // add translation text here const tranTxt = { + en: standard, 'zh-cn': { - 'Danmaku is loading': '弹幕加载中', - Top: '顶部', - Bottom: '底部', - Rolling: '滚动', - 'Input danmaku, hit Enter': '输入弹幕,回车发送', - 'About author': '关于作者', - 'DPlayer feedback': '播放器意见反馈', - 'About DPlayer': '关于 DPlayer 播放器', - Loop: '洗脑循环', - Speed: '速度', - 'Opacity for danmaku': '弹幕透明度', - Normal: '正常', - 'Please input danmaku content!': '要输入弹幕内容啊喂!', - 'Set danmaku color': '设置弹幕颜色', - 'Set danmaku type': '设置弹幕类型', - 'Show danmaku': '显示弹幕', - 'Video load failed': '视频加载失败', - 'Danmaku load failed': '弹幕加载失败', - 'Danmaku send failed': '弹幕发送失败', - 'Switching to': '正在切换至', - 'Switched to': '已经切换至', - quality: '画质', - FF: '快进', - REW: '快退', - 'Unlimited danmaku': '海量弹幕', - 'Send danmaku': '发送弹幕', - Setting: '设置', - 'Full screen': '全屏', - 'Web full screen': '页面全屏', - Send: '发送', - Screenshot: '截图', - AirPlay: '无线投屏', - ChromeCast: 'ChromeCast', - s: '秒', - 'Show subtitle': '显示字幕', - 'Hide subtitle': '隐藏字幕', - Volume: '音量', - Live: '直播', - 'Video info': '视频统计信息', + 'danmaku-loading': '弹幕加载中', + top: '顶部', + bottom: '底部', + rolling: '滚动', + 'input-danmaku-enter': '输入弹幕,回车发送', + 'about-author': '关于作者', + 'dplayer-feedback': '播放器意见反馈', + 'about-dplayer': '关于 DPlayer 播放器', + loop: '洗脑循环', + speed: '速度', + 'opacity-danmaku': '弹幕透明度', + normal: '正常', + 'please-input-danmaku': '要输入弹幕内容啊喂!', + 'set-danmaku-color': '设置弹幕颜色', + 'set-danmaku-type': '设置弹幕类型', + 'show-danmaku': '显示弹幕', + 'video-failed': '视频加载失败', + 'danmaku-failed': '弹幕加载失败', + 'danmaku-send-failed': '弹幕发送失败', + 'switching-quality': '正在切换至 %q 画质', + 'switched-quality': '已经切换至 %q 画质', + ff: '快进 %s 秒', + rew: '快退 %s 秒', + 'unlimited-danmaku': '海量弹幕', + 'send-danmaku': '发送弹幕', + setting: '设置', + fullscreen: '全屏', + 'web-fullscreen': '页面全屏', + send: '发送', + screenshot: '截图', + airplay: '无线投屏', + chromecast: 'ChromeCast', + 'show-subs': '显示字幕', + 'hide-subs': '隐藏字幕', + volume: '音量', + live: '直播', + 'video-info': '视频统计信息', }, 'zh-tw': { - 'Danmaku is loading': '彈幕載入中', - Top: '頂部', - Bottom: '底部', - Rolling: '滾動', - 'Input danmaku, hit Enter': '輸入彈幕,Enter 發送', - 'About author': '關於作者', - 'DPlayer feedback': '播放器意見回饋', - 'About DPlayer': '關於 DPlayer 播放器', - Loop: '循環播放', - Speed: '速度', - 'Opacity for danmaku': '彈幕透明度', - Normal: '正常', - 'Please input danmaku content!': '請輸入彈幕內容啊!', - 'Set danmaku color': '設定彈幕顏色', - 'Set danmaku type': '設定彈幕類型', - 'Show danmaku': '顯示彈幕', - 'Video load failed': '影片載入失敗', - 'Danmaku load failed': '彈幕載入失敗', - 'Danmaku send failed': '彈幕發送失敗', - 'Switching to': '正在切換至', - 'Switched to': '已經切換至', - quality: '畫質', - FF: '快進', - REW: '快退', - 'Unlimited danmaku': '巨量彈幕', - 'Send danmaku': '發送彈幕', - Setting: '設定', - 'Full screen': '全螢幕', - 'Web full screen': '頁面全螢幕', - Send: '發送', - Screenshot: '截圖', - AirPlay: '無線投屏', - ChromeCast: 'ChromeCast', - s: '秒', - 'Show subtitle': '顯示字幕', - 'Hide subtitle': '隱藏字幕', - Volume: '音量', - Live: '直播', - 'Video info': '影片統計訊息', + 'danmaku-loading': '彈幕載入中', + top: '頂部', + bottom: '底部', + rolling: '滾動', + 'input-danmaku-enter': '輸入彈幕,Enter 發送', + 'about-author': '關於作者', + 'dplayer-feedback': '播放器意見回饋', + 'about-dplayer': '關於 DPlayer 播放器', + loop: '循環播放', + speed: '速度', + 'opacity-danmaku': '彈幕透明度', + normal: '正常', + 'please-input-danmaku': '請輸入彈幕內容啊!', + 'set-danmaku-color': '設定彈幕顏色', + 'set-danmaku-type': '設定彈幕類型', + 'show-danmaku': '顯示彈幕', + 'video-failed': '影片載入失敗', + 'danmaku-failed': '彈幕載入失敗', + 'danmaku-send-failed': '彈幕發送失敗', + 'switching-quality': '正在切換至 %q 畫質', + 'switched-quality': '已經切換至 %q 畫質', + ff: '快進 %s 秒', + rew: '快退 %s 秒', + 'unlimited-danmaku': '巨量彈幕', + 'send-danmaku': '發送彈幕', + setting: '設定', + fullscreen: '全螢幕', + 'web-fullscreen': '頁面全螢幕', + send: '發送', + screenshot: '截圖', + airplay: '無線投屏', + chromecast: 'ChromeCast', + 'show-subs': '顯示字幕', + 'hide-subs': '隱藏字幕', + volume: '音量', + live: '直播', + 'video-info': '影片統計訊息', }, 'ko-kr': { - 'Danmaku is loading': 'Danmaku를 불러오는 중입니다.', - Top: 'Top', - Bottom: 'Bottom', - Rolling: 'Rolling', - 'Input danmaku, hit Enter': 'Danmaku를 입력하고 Enter를 누르세요.', - 'About author': '만든이', - 'DPlayer feedback': '피드백 보내기', - 'About DPlayer': 'DPlayer 정보', - Loop: '반복', - Speed: '배속', - 'Opacity for danmaku': 'Danmaku 불투명도', - Normal: '표준', - 'Please input danmaku content!': 'Danmaku를 입력하세요!', - 'Set danmaku color': 'Danmaku 색상', - 'Set danmaku type': 'Danmaku 설정', - 'Show danmaku': 'Danmaku 표시', - 'Video load failed': '비디오를 불러오지 못했습니다.', - 'Danmaku load failed': 'Danmaku를 불러오지 못했습니다.', - 'Danmaku send failed': 'Danmaku 전송에 실패했습니다.', - 'Switching to': '전환', - 'Switched to': '전환 됨', - quality: '화질', - FF: '앞으로', - REW: '뒤로', - 'Unlimited danmaku': '끝없는 Danmaku', - 'Send danmaku': 'Danmaku 보내기', - Setting: '설정', - 'Full screen': '전체 화면', - 'Web full screen': '웹 내 전체화면', - Send: '보내기', - Screenshot: '화면 캡쳐', - AirPlay: '에어플레이', - s: '초', - 'Show subtitle': '자막 보이기', - 'Hide subtitle': '자막 숨기기', + 'danmaku-loading': 'Danmaku를 불러오는 중입니다.', + top: 'Top', + bottom: 'Bottom', + rolling: 'Rolling', + 'input-danmaku-enter': 'Danmaku를 입력하고 Enter를 누르세요.', + 'about-author': '만든이', + 'dplayer-feedback': '피드백 보내기', + 'about-dplayer': 'DPlayer 정보', + loop: '반복', + speed: '배속', + 'opacity-danmaku': 'Danmaku 불투명도', + normal: '표준', + 'please-input-danmaku': 'Danmaku를 입력하세요!', + 'set-danmaku-color': 'Danmaku 색상', + 'set-danmaku-type': 'Danmaku 설정', + 'show-danmaku': 'Danmaku 표시', + 'video-failed': '비디오를 불러오지 못했습니다.', + 'danmaku-failed': 'Danmaku를 불러오지 못했습니다.', + 'danmaku-send-failed': 'Danmaku 전송에 실패했습니다.', + 'Switching to': '', + 'Switched to': '', + 'switching-quality': '전환 %q 화질', + 'switched-quality': '전환 됨 %q 화질', + ff: '앞으로 %s 초', + rew: '뒤로 %s 초', + 'unlimited-danmaku': '끝없는 Danmaku', + 'send-danmaku': 'Danmaku 보내기', + setting: '설정', + fullscreen: '전체 화면', + 'web-fullscreen': '웹 내 전체화면', + send: '보내기', + screenshot: '화면 캡쳐', + airplay: '에어플레이', + chromecast: 'ChromeCast', + 'show-subs': '자막 보이기', + 'hide-subs': '자막 숨기기', Volume: '볼륨', - Live: '생방송', - 'Video info': '비디오 정보', + live: '생방송', + 'video-info': '비디오 정보', + }, + de: { + 'danmaku-loading': 'Danmaku lädt...', + top: 'Oben', + bottom: 'Unten', + rolling: 'Rollend', + 'input-danmaku-enter': 'Drücke Enter nach dem Einfügen vom Danmaku', + 'about-author': 'Über den Autor', + 'dplayer-feedback': 'DPlayer Feedback', + 'about-dplayer': 'Über DPlayer', + loop: 'Wiederholen', + speed: 'Geschwindigkeit', + 'opacity-danmaku': 'Transparenz für Danmaku', + normal: 'Normal', + 'please-input-danmaku': 'Bitte Danmaku Inhalt eingeben!', + 'set-danmaku-color': 'Danmaku Farbe festlegen', + 'set-danmaku-type': 'Danmaku Typ festlegen', + 'show-danmaku': 'Zeige Danmaku', + 'video-failed': 'Das Video konnte nicht geladen werden', + 'danmaku-failed': 'Danmaku konnte nicht geladen werden', + 'danmaku-send-failed': 'Das senden von Danmaku ist fehgeschlagen', + 'switching-quality': 'Wechsle zur %q Qualität', + 'switched-quality': 'Zur %q Qualität gewechselt', + ff: '%s s Vorwärts', + rew: '%s s Zurück', + 'unlimited-danmaku': 'Unlimitiertes Danmaku', + 'send-danmaku': 'Sende Danmaku', + setting: 'Einstellungen', + fullscreen: 'Vollbild', + 'web-fullscreen': 'Browser Vollbild', + send: 'Senden', + screenshot: 'Screenshot', + airplay: 'AirPlay', + 'show-subs': 'Zeige Untertitel', + chromecast: 'ChromeCast', + 'hide-subs': 'Verstecke Untertitel', + volume: 'Lautstärke', + live: 'Live', + 'video-info': 'Video Info', }, }; -export default i18n; +export { i18n }; diff --git a/src/js/options.js b/src/js/options.js index 7563f70..008e084 100644 --- a/src/js/options.js +++ b/src/js/options.js @@ -52,13 +52,13 @@ export default (options) => { options.contextmenu = options.contextmenu.concat([ { - text: 'Video info', + key: 'video-info', click: (player) => { player.infoPanel.triggle(); }, }, { - text: 'About author', + key: 'about-author', link: 'https://diygod.me', }, { diff --git a/src/js/player.js b/src/js/player.js index cdd64cb..1917d1f 100644 --- a/src/js/player.js +++ b/src/js/player.js @@ -2,7 +2,7 @@ import Promise from 'promise-polyfill'; import utils from './utils'; import handleOption from './options'; -import i18n from './i18n'; +import { i18n } from './i18n'; import Template from './template'; import Icons from './icons'; import Danmaku from './danmaku'; @@ -156,9 +156,9 @@ class DPlayer { time = Math.min(time, this.video.duration); } if (this.video.currentTime < time) { - this.notice(`${this.tran('FF')} ${(time - this.video.currentTime).toFixed(0)} ${this.tran('s')}`); + this.notice(`${this.tran('ff').replace('%s', (time - this.video.currentTime).toFixed(0))}`); } else if (this.video.currentTime > time) { - this.notice(`${this.tran('REW')} ${(this.video.currentTime - time).toFixed(0)} ${this.tran('s')}`); + this.notice(`${this.tran('rew').replace('%s', (this.video.currentTime - time).toFixed(0))}`); } this.video.currentTime = time; @@ -255,7 +255,7 @@ class DPlayer { this.user.set('volume', percentage); } if (!nonotice) { - this.notice(`${this.tran('Volume')} ${(percentage * 100).toFixed(0)}%`); + this.notice(`${this.tran('volume')} ${(percentage * 100).toFixed(0)}%`); } this.video.volume = percentage; @@ -468,7 +468,7 @@ class DPlayer { // Not a video load error, may be poster load failed, see #307 return; } - this.tran && this.notice && this.type !== 'webtorrent' && this.notice(this.tran('Video load failed'), -1); + this.tran && this.notice && this.type !== 'webtorrent' && this.notice(this.tran('video-failed'), -1); }); // video end @@ -549,7 +549,7 @@ class DPlayer { this.video = videoEle; this.initVideo(this.video, this.quality.type || this.options.video.type); this.seek(this.prevVideo.currentTime); - this.notice(`${this.tran('Switching to')} ${this.quality.name} ${this.tran('quality')}`, -1); + this.notice(`${this.tran('switching-quality').replace('%q', this.quality.name)}`, -1); this.events.trigger('quality_start', this.quality); this.on('canplay', () => { @@ -564,7 +564,7 @@ class DPlayer { this.video.play(); } this.prevVideo = null; - this.notice(`${this.tran('Switched to')} ${this.quality.name} ${this.tran('quality')}`); + this.notice(`${this.tran('switched-quality').replace('%q', this.quality.name)}`); this.switchingQuality = false; this.events.trigger('quality_end'); diff --git a/src/template/player.art b/src/template/player.art index 8c48601..6f01264 100644 --- a/src/template/player.art +++ b/src/template/player.art @@ -13,7 +13,7 @@
{{ if options.danmaku }} - {{ tran('Danmaku is loading') }} + {{ tran('danmaku-loading') }} {{ /if }} {{@ icons.loading }}
@@ -21,12 +21,12 @@
-
-
{{ tran('Set danmaku color') }}
+
{{ tran('set-danmaku-color') }}
-
{{ tran('Set danmaku type') }}
+
{{ tran('set-danmaku-type') }}
- -
@@ -94,7 +94,7 @@ 0:00 {{ if options.live }} - {{ tran('Live') }} + {{ tran('live') }} {{ /if }}
@@ -111,65 +111,65 @@
{{ /if }} {{ if options.screenshot }} -
+
{{@ icons.camera }}
{{ /if }} {{ if options.airplay }} -
+
{{@ icons.airplay }}
{{ /if }} {{ if options.chromecast }} -
+
{{@ icons.chromecast }}
{{ /if }}
-
{{ if options.subtitle }}
-
{{ /if }}
-
- {{ tran('Speed') }} + {{ tran('speed') }}
{{@ icons.right }}
- {{ tran('Loop') }} + {{ tran('loop') }}
- {{ tran('Show danmaku') }} + {{ tran('show-danmaku') }}
- {{ tran('Unlimited danmaku') }} + {{ tran('unlimited-danmaku') }}
- {{ tran('Opacity for danmaku') }} + {{ tran('opacity-danmaku') }}
@@ -182,17 +182,17 @@
{{ each options.playbackSpeed }}
- {{ $value === 1 ? tran('Normal') : $value }} + {{ $value === 1 ? tran('normal') : $value }}
{{ /each }}
- -
@@ -252,7 +252,7 @@
{{ each options.contextmenu }}
- {{ tran($value.text) }} + {{ if $value.key }} {{ tran($value.key) }}{{ /if }}{{ if $value.text }} {{$value.text}}{{ /if }}
{{ /each }}