diff --git a/packages/griffith/src/components/PlayerContainer/PlayerContainer.js b/packages/griffith/src/components/PlayerContainer/PlayerContainer.js index 854b6eb7..f35e2fba 100644 --- a/packages/griffith/src/components/PlayerContainer/PlayerContainer.js +++ b/packages/griffith/src/components/PlayerContainer/PlayerContainer.js @@ -9,7 +9,7 @@ import { import {MessageProvider, InternalContext} from '../../contexts/Message' import {PositionProvider} from '../../contexts/Position' import {ObjectFitProvider, VALID_FIT} from '../../contexts/ObjectFit' -import {LanguageContext} from '../../contexts/Language' +import {LanguageProvider} from '../../contexts/Language' const PlayerContainer = ({ standalone, @@ -24,7 +24,7 @@ const PlayerContainer = ({ children, initialObjectFit = 'contain', useMSE, - language = 'en', + language, }) => ( @@ -32,7 +32,7 @@ const PlayerContainer = ({ {({emitEvent, subscribeAction}) => ( - + {({currentSrc}) => ( {children} - + )} diff --git a/packages/griffith/src/components/TranslatedText/strings.js b/packages/griffith/src/components/TranslatedText/strings.js index 716a5cf0..d4dea880 100644 --- a/packages/griffith/src/components/TranslatedText/strings.js +++ b/packages/griffith/src/components/TranslatedText/strings.js @@ -1,5 +1,7 @@ +import SUPPORTED_LANGUAGES from '../../contexts/Language/supportedLanguages' + export default { - en: { + [SUPPORTED_LANGUAGES.EN]: { 'quality-auto': 'Auto', 'quality-ld': 'LD', 'quality-sd': 'SD', @@ -7,7 +9,7 @@ export default { 'action-enter-fullscreen': 'Fullscreen', 'action-exit-fullscreen': 'Exit Fullscreen', }, - ja: { + [SUPPORTED_LANGUAGES.JA]: { 'quality-auto': '自動', 'quality-ld': '低画質', 'quality-sd': '標準画質', @@ -16,7 +18,7 @@ export default { 'action-exit-fullscreen': '全画面終了', }, // covers zh-Hans-CN and zh-Hans-SG - 'zh-Hans': { + [SUPPORTED_LANGUAGES.ZH_HANS]: { 'quality-auto': '自动', 'quality-ld': '低清', 'quality-sd': '标清', @@ -25,7 +27,7 @@ export default { 'action-exit-fullscreen': '退出全屏', }, // covers zh-Hant-HK and zh-Hant-TW - 'zh-Hant': { + [SUPPORTED_LANGUAGES.ZH_HANT]: { 'quality-auto': '自動', 'quality-ld': '低畫質', 'quality-sd': '標準畫質', diff --git a/packages/griffith/src/contexts/Language/LanguageProvider.js b/packages/griffith/src/contexts/Language/LanguageProvider.js new file mode 100644 index 00000000..e151eee0 --- /dev/null +++ b/packages/griffith/src/contexts/Language/LanguageProvider.js @@ -0,0 +1,47 @@ +import React from 'react' +import PropTypes from 'prop-types' +import LanguageContext from './LanguageContext' +import guessLanguage from './guessLanguage' +import SUPPORTED_LANGUAGES from './supportedLanguages' + +const VALID_LANGUAGES = Object.keys(SUPPORTED_LANGUAGES) + +const isValid = language => VALID_LANGUAGES.includes(language) + +function getLanguage(props) { + const {language} = props + if (isValid(language)) { + return language + } else if (typeof document !== 'undefined') { + // detect language from html lang attributes and navigator.languages + const htmlLanguage = document.documentElement.getAttribute('lang') + const navigatorLanguages = navigator.languages || [navigator.language] + const languageList = [htmlLanguage, ...navigatorLanguages] + return guessLanguage(languageList) + } + + return SUPPORTED_LANGUAGES.EN +} + +export default class LanguageProvider extends React.PureComponent { + static propTypes = { + language: PropTypes.oneOf(VALID_LANGUAGES), + } + + state = { + language: SUPPORTED_LANGUAGES.EN, + } + + static getDerivedStateFromProps = (props, state) => { + const language = getLanguage(props) + return language === state.language ? null : {language} + } + + render() { + return ( + + {this.props.children} + + ) + } +} diff --git a/packages/griffith/src/contexts/Language/guessLanguage.js b/packages/griffith/src/contexts/Language/guessLanguage.js new file mode 100644 index 00000000..0dfe1231 --- /dev/null +++ b/packages/griffith/src/contexts/Language/guessLanguage.js @@ -0,0 +1,35 @@ +import SUPPORTED_LANGUAGES from './supportedLanguages' + +const KEY_WORDS = [ + ['en', SUPPORTED_LANGUAGES.EN], + ['ja', SUPPORTED_LANGUAGES.JA], + + ['hant', SUPPORTED_LANGUAGES.ZH_HANT], + ['hans', SUPPORTED_LANGUAGES.ZH_HANS], + + ['hk', SUPPORTED_LANGUAGES.ZH_HANT], + ['tw', SUPPORTED_LANGUAGES.ZH_HANT], + + ['cn', SUPPORTED_LANGUAGES.ZH_HANS], + ['sg', SUPPORTED_LANGUAGES.ZH_HANS], + + ['zh', SUPPORTED_LANGUAGES.ZH_HANS], // prefer Simplified Chinese to Traditional if not specified +] + +function matchKeyword(langCode) { + const found = KEY_WORDS.find(([keyword]) => langCode.includes(keyword)) + return found ? found[1] : null +} + +function guessLanguage(languageList) { + const list = languageList.filter(Boolean).map(s => s.toLocaleLowerCase()) + for (let i = 0; i < list.length; i = i + 1) { + const matchedSupportedLanguage = matchKeyword(list[i]) + if (matchedSupportedLanguage) { + return matchedSupportedLanguage + } + } + return SUPPORTED_LANGUAGES.EN +} + +export default guessLanguage diff --git a/packages/griffith/src/contexts/Language/guessLanguage.spec.js b/packages/griffith/src/contexts/Language/guessLanguage.spec.js new file mode 100644 index 00000000..e6fa367e --- /dev/null +++ b/packages/griffith/src/contexts/Language/guessLanguage.spec.js @@ -0,0 +1,37 @@ +import guessLanguage from './guessLanguage' +import SUPPORTED_LANGUAGES from './supportedLanguages' + +describe('guessLanguage', () => { + it('should return first suppored language in the list', () => { + expect(guessLanguage(['zh-tw', 'en'])).toBe(SUPPORTED_LANGUAGES.ZH_HANT) + expect(guessLanguage(['fr', 'en'])).toBe(SUPPORTED_LANGUAGES.EN) + }) + + it('should return default language if no match found', () => { + expect(guessLanguage(['es', 'de'])).toBe(SUPPORTED_LANGUAGES.EN) + }) + + it('should return default language if provided empty list', () => { + expect(guessLanguage([])).toBe(SUPPORTED_LANGUAGES.EN) + }) + + it('handle various Chinese language codes', () => { + expect(guessLanguage(['zh-CN'])).toBe(SUPPORTED_LANGUAGES.ZH_HANS) + expect(guessLanguage(['zh-SG'])).toBe(SUPPORTED_LANGUAGES.ZH_HANS) + + expect(guessLanguage(['zh-TW'])).toBe(SUPPORTED_LANGUAGES.ZH_HANT) + expect(guessLanguage(['zh-HK'])).toBe(SUPPORTED_LANGUAGES.ZH_HANT) + + expect(guessLanguage(['zh-cmn-Hans'])).toBe(SUPPORTED_LANGUAGES.ZH_HANS) + expect(guessLanguage(['cmn-Hans-CN'])).toBe(SUPPORTED_LANGUAGES.ZH_HANS) + expect(guessLanguage(['zh-cmn-Hans-HK'])).toBe(SUPPORTED_LANGUAGES.ZH_HANS) + + expect(guessLanguage(['zh-cmn-Hant'])).toBe(SUPPORTED_LANGUAGES.ZH_HANT) + expect(guessLanguage(['zh-cmn-Hant'])).toBe(SUPPORTED_LANGUAGES.ZH_HANT) + expect(guessLanguage(['zh-cmn-Hant-CN'])).toBe(SUPPORTED_LANGUAGES.ZH_HANT) + }) + + it('prefer Simplified Chinese to Traditional if not specified', () => { + expect(guessLanguage(['zh'])).toBe(SUPPORTED_LANGUAGES.ZH_HANS) + }) +}) diff --git a/packages/griffith/src/contexts/Language/index.js b/packages/griffith/src/contexts/Language/index.js index abf20939..af7cc4df 100644 --- a/packages/griffith/src/contexts/Language/index.js +++ b/packages/griffith/src/contexts/Language/index.js @@ -1 +1,2 @@ export {default as LanguageContext} from './LanguageContext' +export {default as LanguageProvider} from './LanguageProvider' diff --git a/packages/griffith/src/contexts/Language/supportedLanguages.js b/packages/griffith/src/contexts/Language/supportedLanguages.js new file mode 100644 index 00000000..1f151821 --- /dev/null +++ b/packages/griffith/src/contexts/Language/supportedLanguages.js @@ -0,0 +1,8 @@ +const SUPPORTED_LANGUAGES = { + EN: 'en', + JA: 'hans', + ZH_HANS: 'zh-Hans', + ZH_HANT: 'zh-Hant', +} + +export default SUPPORTED_LANGUAGES