From f5d77fcebc1d8c90dcb27e51fe2a12dd98977682 Mon Sep 17 00:00:00 2001 From: Chris Duarte Date: Tue, 28 Nov 2017 14:48:51 -0800 Subject: [PATCH] Add Channel Info Edit options to ChannelInfo screen (#1189) * Add Channel Info Edit options to ChannelInfo screen Refactor CreateChannel to CreateChannel and ChannelInfo Add EditChannel screen add updateChannelRequest display header option only if channel is DM or GM Add editing prop to ChannelInfo Add saving state to ChannelInfo Clean up EditChannel and change saving to updating Add channel name (url) field in EditChannel Clean up enableRightButton logic for EditChannel Add error handling for EditChannel fields Remove leftButton declaration from EditChannel Add wrapWithPreventDoubleTap to click handlers in ChannelInfo Move channel info to parent component (CreateChannel) Add editing support for channel info in parent component Remove ownProps from connected components Remove passing parent props using the spread operator * Strip out channel url field from EditChannel * Comment out channelUrl textInput ref Make eslint happy * Remove injectIntl in favor of intl context props * Add mobile prefix to json string * Remove unnecessary actions declaration in ChannelInfo * Refactor creating | editing to saving * Safe check refs when blurring in ChannelInfo * pass channelType for displayingHeaderOnly in EditChannelInfo * Refactor ChannelInfo component to EditChannelInfo * pass 1to1 props instead of object * Add required to certain props in EditChannelInfo * Use Stylesheet for container styles * Add InvalidPermission method when updating channel * Clean up onDisplayNameChangeText and safe check scrollToEnd * Add {deviceWidth, deviceHeight} from redux * Remove style.navTitle * Add wrapper methods for delete and leave channel * remove alertErrorIfInvalidPermissions * Fix mobile localization strings to use prefix mobile.- * Add TODO for channel URL --- app/components/edit_channel_info/index.js | 423 ++++++++ app/constants/view.js | 4 +- app/screens/channel_info/channel_info.js | 67 +- app/screens/channel_info/index.js | 3 +- app/screens/create_channel/create_channel.js | 314 +----- app/screens/create_channel/index.js | 9 +- app/screens/edit_channel/edit_channel.js | 300 ++++++ app/screens/edit_channel/index.js | 39 + app/screens/index.js | 2 + app/utils/latinise.js | 1003 ++++++++++++++++++ app/utils/url.js | 19 + assets/base/i18n/en.json | 9 + 12 files changed, 1914 insertions(+), 278 deletions(-) create mode 100644 app/components/edit_channel_info/index.js create mode 100644 app/screens/edit_channel/edit_channel.js create mode 100644 app/screens/edit_channel/index.js create mode 100644 app/utils/latinise.js diff --git a/app/components/edit_channel_info/index.js b/app/components/edit_channel_info/index.js new file mode 100644 index 000000000..6a04059dd --- /dev/null +++ b/app/components/edit_channel_info/index.js @@ -0,0 +1,423 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import React, {PureComponent} from 'react'; +import PropTypes from 'prop-types'; +import { + Platform, + TouchableWithoutFeedback, + View, + Text, + findNodeHandle +} from 'react-native'; +import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view'; + +import EventEmitter from 'mattermost-redux/utils/event_emitter'; + +import ErrorText from 'app/components/error_text'; +import FormattedText from 'app/components/formatted_text'; +import Loading from 'app/components/loading'; +import StatusBar from 'app/components/status_bar'; +import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder'; + +import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; +import {General} from 'mattermost-redux/constants'; +import {getShortenedURL} from 'app/utils/url'; + +export default class EditChannelInfo extends PureComponent { + static propTypes = { + navigator: PropTypes.object.isRequired, + theme: PropTypes.object.isRequired, + deviceWidth: PropTypes.number.isRequired, + deviceHeight: PropTypes.number.isRequired, + channelType: PropTypes.string, + enableRightButton: PropTypes.func, + saving: PropTypes.bool.isRequired, + editing: PropTypes.bool, + error: PropTypes.string, + displayName: PropTypes.string, + currentTeamUrl: PropTypes.string, + channelURL: PropTypes.string, + purpose: PropTypes.string, + header: PropTypes.string, + onDisplayNameChange: PropTypes.func, + onChannelURLChange: PropTypes.func, + onPurposeChange: PropTypes.func, + onHeaderChange: PropTypes.func, + oldDisplayName: PropTypes.string, + oldChannelURL: PropTypes.string, + oldHeader: PropTypes.string, + oldPurpose: PropTypes.string + }; + + static defaultProps = { + editing: false + }; + + blur = () => { + if (this.nameInput) { + this.nameInput.refs.wrappedInstance.blur(); + } + + // TODO: uncomment below once the channel URL field is added + // if (this.urlInput) { + // this.urlInput.refs.wrappedInstance.blur(); + // } + if (this.purposeInput) { + this.purposeInput.refs.wrappedInstance.blur(); + } + if (this.headerInput) { + this.headerInput.refs.wrappedInstance.blur(); + } + if (this.scroll) { + this.scroll.scrollToPosition(0, 0, true); + } + }; + + channelNameRef = (ref) => { + this.nameInput = ref; + }; + + channelURLRef = (ref) => { + this.urlInput = ref; + }; + + channelPurposeRef = (ref) => { + this.purposeInput = ref; + }; + + channelHeaderRef = (ref) => { + this.headerInput = ref; + }; + + close = (goBack = false) => { + EventEmitter.emit('closing-create-channel', false); + if (goBack) { + this.props.navigator.pop({animated: true}); + } else { + this.props.navigator.dismissModal({ + animationType: 'slide-down' + }); + } + }; + + lastTextRef = (ref) => { + this.lastText = ref; + }; + + canUpdate = (displayName, channelURL, purpose, header) => { + const { + oldDisplayName, + oldChannelURL, + oldPurpose, + oldHeader + } = this.props; + + return displayName !== oldDisplayName || channelURL !== oldChannelURL || + purpose !== oldPurpose || header !== oldHeader; + }; + + enableRightButton = (enable = false) => { + this.props.enableRightButton(enable); + }; + + onDisplayNameChangeText = (displayName) => { + const {editing, onDisplayNameChange} = this.props; + onDisplayNameChange(displayName); + + if (editing) { + const {channelURL, purpose, header} = this.props; + const canUpdate = this.canUpdate(displayName, channelURL, purpose, header); + this.enableRightButton(canUpdate); + return; + } + + const displayNameExists = displayName && displayName.length >= 2; + this.props.enableRightButton(displayNameExists); + }; + + onDisplayURLChangeText = (channelURL) => { + const {editing, onChannelURLChange} = this.props; + onChannelURLChange(channelURL); + + if (editing) { + const {displayName, purpose, header} = this.props; + const canUpdate = this.canUpdate(displayName, channelURL, purpose, header); + this.enableRightButton(canUpdate); + } + }; + + onPurposeChangeText = (purpose) => { + const {editing, onPurposeChange} = this.props; + onPurposeChange(purpose); + + if (editing) { + const {displayName, channelURL, header} = this.props; + const canUpdate = this.canUpdate(displayName, channelURL, purpose, header); + this.enableRightButton(canUpdate); + } + }; + + onHeaderChangeText = (header) => { + const {editing, onHeaderChange} = this.props; + onHeaderChange(header); + + if (editing) { + const {displayName, channelURL, purpose} = this.props; + const canUpdate = this.canUpdate(displayName, channelURL, purpose, header); + this.enableRightButton(canUpdate); + } + }; + + scrollRef = (ref) => { + this.scroll = ref; + }; + + scrollToEnd = () => { + if (this.scroll && this.lastText) { + this.scroll.scrollToFocusedInput(findNodeHandle(this.lastText)); + } + }; + + render() { + const { + theme, + editing, + channelType, + currentTeamUrl, + deviceWidth, + deviceHeight, + displayName, + channelURL, + header, + purpose + } = this.props; + const {error, saving} = this.props; + const fullUrl = currentTeamUrl + '/channels'; + const shortUrl = getShortenedURL(fullUrl, 35); + + const style = getStyleSheet(theme); + + const displayHeaderOnly = channelType === General.DM_CHANNEL || + channelType === General.GM_CHANNEL; + + if (saving) { + return ( + + + + + ); + } + + let displayError; + if (error) { + displayError = ( + + + + + + ); + } + + return ( + + + + {displayError} + + + {!displayHeaderOnly && ( + + + + + + + + + )} + {/*TODO: Hide channel url field until it's added to CreateChannel */} + {false && editing && !displayHeaderOnly && ( + + + + + {shortUrl} + + + + + + + )} + {!displayHeaderOnly && ( + + + + + + + + + + + + + )} + + + + + + + + + + + + + + + ); + } +} + +const getStyleSheet = makeStyleSheetFromTheme((theme) => { + return { + container: { + flex: 1, + backgroundColor: theme.centerChannelBg + }, + scrollView: { + flex: 1, + backgroundColor: changeOpacity(theme.centerChannelColor, 0.03), + paddingTop: 10 + }, + errorContainer: { + backgroundColor: changeOpacity(theme.centerChannelColor, 0.03) + }, + errorWrapper: { + justifyContent: 'center', + alignItems: 'center' + }, + inputContainer: { + marginTop: 10, + backgroundColor: '#fff' + }, + input: { + color: '#333', + fontSize: 14, + height: 40, + paddingHorizontal: 15 + }, + titleContainer30: { + flexDirection: 'row', + marginTop: 30 + }, + titleContainer15: { + flexDirection: 'row', + marginTop: 15 + }, + title: { + fontSize: 14, + color: theme.centerChannelColor, + marginLeft: 15 + }, + optional: { + color: changeOpacity(theme.centerChannelColor, 0.5), + fontSize: 14, + marginLeft: 5 + }, + helpText: { + fontSize: 14, + color: changeOpacity(theme.centerChannelColor, 0.5), + marginTop: 10, + marginHorizontal: 15 + } + }; +}); + diff --git a/app/constants/view.js b/app/constants/view.js index d5a5ee13f..2ccaa3813 100644 --- a/app/constants/view.js +++ b/app/constants/view.js @@ -65,5 +65,7 @@ export default { ...ViewTypes, POST_VISIBILITY_CHUNK_SIZE: 15, FEATURE_TOGGLE_PREFIX: 'feature_enabled_', - EMBED_PREVIEW: 'embed_preview' + EMBED_PREVIEW: 'embed_preview', + MIN_CHANNELNAME_LENGTH: 2, + MAX_CHANNELNAME_LENGTH: 22 }; diff --git a/app/screens/channel_info/channel_info.js b/app/screens/channel_info/channel_info.js index 8ed5500f7..f65cc5173 100644 --- a/app/screens/channel_info/channel_info.js +++ b/app/screens/channel_info/channel_info.js @@ -12,7 +12,7 @@ import { } from 'react-native'; import StatusBar from 'app/components/status_bar'; -import {preventDoubleTap} from 'app/utils/tap'; +import {wrapWithPreventDoubleTap} from 'app/utils/tap'; import {alertErrorWithFallback} from 'app/utils/general'; import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; @@ -35,6 +35,7 @@ class ChannelInfo extends PureComponent { isCurrent: PropTypes.bool.isRequired, isFavorite: PropTypes.bool.isRequired, canManageUsers: PropTypes.bool.isRequired, + canEditChannel: PropTypes.bool.isRequired, actions: PropTypes.shape({ closeDMChannel: PropTypes.func.isRequired, closeGMChannel: PropTypes.func.isRequired, @@ -74,7 +75,7 @@ class ChannelInfo extends PureComponent { } }; - goToChannelAddMembers = () => { + goToChannelAddMembers = wrapWithPreventDoubleTap(() => { const {intl, navigator, theme} = this.props; navigator.push({ backButtonTitle: '', @@ -88,9 +89,9 @@ class ChannelInfo extends PureComponent { screenBackgroundColor: theme.centerChannelBg } }); - }; + }); - goToChannelMembers = () => { + goToChannelMembers = wrapWithPreventDoubleTap(() => { const {canManageUsers, intl, navigator, theme} = this.props; const id = canManageUsers ? 'channel_header.manageMembers' : 'channel_header.viewMembers'; const defaultMessage = canManageUsers ? 'Manage Members' : 'View Members'; @@ -107,9 +108,36 @@ class ChannelInfo extends PureComponent { screenBackgroundColor: theme.centerChannelBg } }); + }); + + handleChannelEdit = wrapWithPreventDoubleTap(() => { + const {intl, navigator, theme} = this.props; + const id = 'mobile.channel_info.edit'; + const defaultMessage = 'Edit Channel'; + + navigator.push({ + backButtonTitle: '', + screen: 'EditChannel', + title: intl.formatMessage({id, defaultMessage}), + animated: true, + navigatorStyle: { + navBarTextColor: theme.sidebarHeaderTextColor, + navBarBackgroundColor: theme.sidebarHeaderBg, + navBarButtonColor: theme.sidebarHeaderTextColor, + screenBackgroundColor: theme.centerChannelBg + } + }); + }); + + handleLeave = () => { + this.handleDeleteOrLeave('leave'); }; - handleDeleteOrLeave(eventType) { + handleDelete = () => { + this.handleDeleteOrLeave('delete'); + }; + + handleDeleteOrLeave = wrapWithPreventDoubleTap((eventType) => { const {formatMessage} = this.props.intl; const channel = this.props.currentChannel; const term = channel.type === General.OPEN_CHANNEL ? @@ -171,9 +199,9 @@ class ChannelInfo extends PureComponent { onPress: onPressAction }], ); - } + }); - handleClose = () => { + handleClose = wrapWithPreventDoubleTap(() => { const {currentChannel, isCurrent, isFavorite} = this.props; const channel = Object.assign({}, currentChannel, {isCurrent}, {isFavorite}); const {closeDMChannel, closeGMChannel} = this.props.actions; @@ -190,7 +218,7 @@ class ChannelInfo extends PureComponent { }); break; } - }; + }); handleFavorite = () => { const {isFavorite, actions, currentChannel} = this.props; @@ -232,6 +260,7 @@ class ChannelInfo extends PureComponent { currentChannelCreatorName, currentChannelMemberCount, canManageUsers, + canEditChannel, navigator, status, theme @@ -299,7 +328,7 @@ class ChannelInfo extends PureComponent { preventDoubleTap(this.goToChannelMembers)} + action={this.goToChannelMembers} defaultMessage={canManageUsers ? 'Manage Members' : 'View Members'} detail={currentChannelMemberCount} icon='users' @@ -312,7 +341,7 @@ class ChannelInfo extends PureComponent { preventDoubleTap(this.goToChannelAddMembers)} + action={this.goToChannelAddMembers} defaultMessage='Add Members' icon='user-plus' textId='channel_header.addMembers' @@ -320,11 +349,23 @@ class ChannelInfo extends PureComponent { /> } + {canEditChannel && ( + + + + + )} {this.renderLeaveOrDeleteChannelRow() && preventDoubleTap(this.handleDeleteOrLeave, this, 'leave')} + action={this.handleLeave} defaultMessage='Leave Channel' icon='sign-out' textId='navbar.leave' @@ -336,7 +377,7 @@ class ChannelInfo extends PureComponent { {this.renderLeaveOrDeleteChannelRow() && canDeleteChannel && preventDoubleTap(this.handleDeleteOrLeave, this, 'delete')} + action={this.handleDelete} defaultMessage='Delete Channel' icon='trash' iconColor='#CA3B27' @@ -349,7 +390,7 @@ class ChannelInfo extends PureComponent { {this.renderCloseDirect() && preventDoubleTap(this.handleClose, this)} + action={this.handleClose} defaultMessage={defaultMessage} icon='times' iconColor='#CA3B27' diff --git a/app/screens/channel_info/index.js b/app/screens/channel_info/index.js index 59999ed4e..37c6216a5 100644 --- a/app/screens/channel_info/index.js +++ b/app/screens/channel_info/index.js @@ -20,7 +20,7 @@ import { canManageChannelMembers } from 'mattermost-redux/selectors/entities/channels'; import {getCurrentUserId, getUser, getStatusForUserId, getCurrentUserRoles} from 'mattermost-redux/selectors/entities/users'; -import {getUserIdFromChannelName, showDeleteOption} from 'mattermost-redux/utils/channel_utils'; +import {getUserIdFromChannelName, showDeleteOption, showManagementOptions} from 'mattermost-redux/utils/channel_utils'; import {isAdmin, isChannelAdmin, isSystemAdmin} from 'mattermost-redux/utils/user_utils'; import ChannelInfo from './channel_info'; @@ -47,6 +47,7 @@ function mapStateToProps(state) { return { canDeleteChannel: showDeleteOption(config, license, currentChannel, isAdmin(roles), isSystemAdmin(roles), isChannelAdmin(roles)), + canEditChannel: showManagementOptions(config, license, currentChannel, isAdmin(roles), isSystemAdmin(roles), isChannelAdmin(roles)), currentChannel, currentChannelCreatorName, currentChannelMemberCount, diff --git a/app/screens/create_channel/create_channel.js b/app/screens/create_channel/create_channel.js index fbc09e46e..d1afc4d22 100644 --- a/app/screens/create_channel/create_channel.js +++ b/app/screens/create_channel/create_channel.js @@ -1,36 +1,25 @@ // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. + import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; -import {injectIntl, intlShape} from 'react-intl'; +import {intlShape} from 'react-intl'; import { - Dimensions, Keyboard, - InteractionManager, - Platform, - TouchableWithoutFeedback, - View, - findNodeHandle + InteractionManager } from 'react-native'; -import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view'; +import EditChannelInfo from 'app/components/edit_channel_info'; import {General, RequestStatus} from 'mattermost-redux/constants'; import EventEmitter from 'mattermost-redux/utils/event_emitter'; -import ErrorText from 'app/components/error_text'; -import FormattedText from 'app/components/formatted_text'; -import Loading from 'app/components/loading'; -import StatusBar from 'app/components/status_bar'; -import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder'; - -import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; - -class CreateChannel extends PureComponent { +export default class CreateChannel extends PureComponent { static propTypes = { - intl: intlShape.isRequired, - createChannelRequest: PropTypes.object.isRequired, - navigator: PropTypes.object, + navigator: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, + deviceWidth: PropTypes.number.isRequired, + deviceHeight: PropTypes.number.isRequired, + createChannelRequest: PropTypes.object.isRequired, channelType: PropTypes.string, closeButton: PropTypes.object, actions: PropTypes.shape({ @@ -38,6 +27,10 @@ class CreateChannel extends PureComponent { }) }; + static contextTypes = { + intl: intlShape + }; + static defaultProps = { channelType: General.OPEN_CHANNEL }; @@ -52,15 +45,18 @@ class CreateChannel extends PureComponent { showAsAction: 'always' }; - constructor(props) { + constructor(props, context) { super(props); this.state = { + error: null, + creating: false, displayName: '', - header: '', - purpose: '' + purpose: '', + header: '' }; - this.rightButton.title = props.intl.formatMessage({id: 'mobile.create_channel', defaultMessage: 'Create'}); + + this.rightButton.title = context.intl.formatMessage({id: 'mobile.create_channel', defaultMessage: 'Create'}); if (props.channelType === General.PRIVATE_CHANNEL) { this.left = {...this.leftButton, icon: props.closeButton}; @@ -107,25 +103,6 @@ class CreateChannel extends PureComponent { } } - blur = () => { - this.nameInput.refs.wrappedInstance.blur(); - this.purposeInput.refs.wrappedInstance.blur(); - this.headerInput.refs.wrappedInstance.blur(); - this.scroll.scrollToPosition(0, 0, true); - }; - - channelNameRef = (ref) => { - this.nameInput = ref; - }; - - channelPurposeRef = (ref) => { - this.purposeInput = ref; - }; - - channelHeaderRef = (ref) => { - this.headerInput = ref; - }; - close = (goBack = false) => { EventEmitter.emit('closing-create-channel', false); if (goBack) { @@ -161,25 +138,12 @@ class CreateChannel extends PureComponent { this.props.navigator.setButtons(buttons); }; - lastTextRef = (ref) => { - this.lastText = ref; - }; - onCreateChannel = () => { Keyboard.dismiss(); const {displayName, purpose, header} = this.state; this.props.actions.handleCreateChannel(displayName, purpose, header, this.props.channelType); }; - onDisplayNameChangeText = (displayName) => { - this.setState({displayName}); - if (displayName && displayName.length >= 2) { - this.emitCanCreateChannel(true); - } else { - this.emitCanCreateChannel(false); - } - }; - onNavigatorEvent = (event) => { if (event.type === 'NavBarButtonPress') { switch (event.id) { @@ -193,220 +157,50 @@ class CreateChannel extends PureComponent { } }; - onPurposeChangeText = (purpose) => { + onDisplayNameChange = (displayName) => { + this.setState({displayName}); + }; + + onPurposeChange = (purpose) => { this.setState({purpose}); }; - onHeaderChangeText = (header) => { + onHeaderChange = (header) => { this.setState({header}); }; - scrollRef = (ref) => { - this.scroll = ref; - }; - - scrollToEnd = () => { - this.scroll.scrollToFocusedInput(findNodeHandle(this.lastText)); - }; - render() { - const {theme} = this.props; - const {creating, displayName, header, purpose, error} = this.state; - const {height, width} = Dimensions.get('window'); - - const style = getStyleSheet(theme); - - if (creating) { - return ( - - - - - ); - } - - let displayError; - if (error) { - displayError = ( - - - - - - ); - } + const { + navigator, + theme, + deviceWidth, + deviceHeight + } = this.props; + const { + error, + creating, + displayName, + purpose, + header + } = this.state; return ( - - - - {displayError} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ); } } -const getStyleSheet = makeStyleSheetFromTheme((theme) => { - return { - container: { - flex: 1, - backgroundColor: theme.centerChannelBg - }, - scrollView: { - flex: 1, - backgroundColor: changeOpacity(theme.centerChannelColor, 0.03), - paddingTop: 10 - }, - errorContainer: { - backgroundColor: changeOpacity(theme.centerChannelColor, 0.03) - }, - errorWrapper: { - justifyContent: 'center', - alignItems: 'center' - }, - inputContainer: { - marginTop: 10, - backgroundColor: '#fff' - }, - input: { - color: '#333', - fontSize: 14, - height: 40, - paddingHorizontal: 15 - }, - titleContainer30: { - flexDirection: 'row', - marginTop: 30 - }, - titleContainer15: { - flexDirection: 'row', - marginTop: 15 - }, - title: { - fontSize: 14, - color: theme.centerChannelColor, - marginLeft: 15 - }, - optional: { - color: changeOpacity(theme.centerChannelColor, 0.5), - fontSize: 14, - marginLeft: 5 - }, - helpText: { - fontSize: 14, - color: changeOpacity(theme.centerChannelColor, 0.5), - marginTop: 10, - marginHorizontal: 15 - }, - navTitle: { - ...Platform.select({ - android: { - fontSize: 18 - }, - ios: { - fontSize: 15, - fontWeight: 'bold' - } - }) - } - }; -}); - -export default injectIntl(CreateChannel); diff --git a/app/screens/create_channel/index.js b/app/screens/create_channel/index.js index 71c37913f..90400719a 100644 --- a/app/screens/create_channel/index.js +++ b/app/screens/create_channel/index.js @@ -7,16 +7,19 @@ import {connect} from 'react-redux'; import {handleCreateChannel} from 'app/actions/views/create_channel'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {getDimensions} from 'app/selectors/device'; import CreateChannel from './create_channel'; -function mapStateToProps(state, ownProps) { +function mapStateToProps(state) { const {createChannel: createChannelRequest} = state.requests.channels; + const {deviceWidth, deviceHeight} = getDimensions(state); return { createChannelRequest, - channelType: ownProps.channelType, - theme: getTheme(state) + theme: getTheme(state), + deviceWidth, + deviceHeight }; } diff --git a/app/screens/edit_channel/edit_channel.js b/app/screens/edit_channel/edit_channel.js new file mode 100644 index 000000000..d208a5d6a --- /dev/null +++ b/app/screens/edit_channel/edit_channel.js @@ -0,0 +1,300 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import React, {PureComponent} from 'react'; +import PropTypes from 'prop-types'; +import {intlShape} from 'react-intl'; +import { + Keyboard, + InteractionManager +} from 'react-native'; + +import EditChannelInfo from 'app/components/edit_channel_info'; +import {RequestStatus} from 'mattermost-redux/constants'; +import EventEmitter from 'mattermost-redux/utils/event_emitter'; +import {ViewTypes} from 'app/constants'; + +import {cleanUpUrlable} from 'app/utils/url'; + +const messages = { + display_name_required: { + id: 'mobile.rename_channel.display_name_required', + defaultMessage: 'Channel name is required' + }, + display_name_maxLength: { + id: 'mobile.rename_channel.display_name_maxLength', + defaultMessage: 'Channel name must be less than {maxLength, number} characters' + }, + display_name_minLength: { + id: 'mobile.rename_channel.display_name_minLength', + defaultMessage: 'Channel name must be {minLength, number} or more characters' + }, + name_required: { + id: 'mobile.rename_channel.name_required', + defaultMessage: 'URL is required' + }, + name_maxLength: { + id: 'mobile.rename_channel.name_maxLength', + defaultMessage: 'URL must be less than {maxLength, number} characters' + }, + name_minLength: { + id: 'mobile.rename_channel.name_minLength', + defaultMessage: 'URL must be {minLength, number} or more characters' + }, + name_lowercase: { + id: 'mobile.rename_channel.name_lowercase', + defaultMessage: 'URL be lowercase alphanumeric characters' + } +}; + +export default class EditChannel extends PureComponent { + static propTypes = { + navigator: PropTypes.object.isRequired, + theme: PropTypes.object.isRequired, + deviceWidth: PropTypes.number.isRequired, + deviceHeight: PropTypes.number.isRequired, + channel: PropTypes.object.isRequired, + currentTeamUrl: PropTypes.string.isRequired, + updateChannelRequest: PropTypes.object.isRequired, + closeButton: PropTypes.object, + actions: PropTypes.shape({ + patchChannel: PropTypes.func.isRequired + }) + }; + + static contextTypes = { + intl: intlShape + }; + + rightButton = { + id: 'edit-channel', + disabled: true, + showAsAction: 'always' + }; + + constructor(props, context) { + super(props); + const { + channel: { + display_name: displayName, + header, + purpose, + name: channelURL + } + } = props; + + this.state = { + error: null, + updating: false, + displayName, + channelURL, + purpose, + header + }; + + this.rightButton.title = context.intl.formatMessage({id: 'mobile.edit_channel', defaultMessage: 'Save'}); + + const buttons = { + rightButtons: [this.rightButton] + }; + + props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); + props.navigator.setButtons(buttons); + } + + componentDidMount() { + this.emitCanUpdateChannel(false); + } + + componentWillReceiveProps(nextProps) { + const {updateChannelRequest} = nextProps; + + if (this.props.updateChannelRequest !== updateChannelRequest) { + switch (updateChannelRequest.status) { + case RequestStatus.STARTED: + this.emitUpdating(true); + this.setState({error: null, updating: true}); + break; + case RequestStatus.SUCCESS: + EventEmitter.emit('close_channel_drawer'); + InteractionManager.runAfterInteractions(() => { + this.emitUpdating(false); + this.setState({error: null, updating: false}); + this.close(); + }); + break; + case RequestStatus.FAILURE: + this.emitUpdating(false); + this.setState({error: updateChannelRequest.error, updating: false}); + break; + } + } + } + + close = () => { + this.props.navigator.pop({animated: true}); + }; + + emitCanUpdateChannel = (enabled) => { + const buttons = { + rightButtons: [{...this.rightButton, disabled: !enabled}] + }; + + this.props.navigator.setButtons(buttons); + }; + + emitUpdating = (loading) => { + const buttons = { + rightButtons: [{...this.rightButton, disabled: loading}] + }; + + this.props.navigator.setButtons(buttons); + }; + + validateDisplayName = (displayName) => { + const {formatMessage} = this.context.intl; + + if (!displayName) { + return {error: formatMessage(messages.display_name_required)}; + } else if (displayName.length > ViewTypes.MAX_CHANNELNAME_LENGTH) { + return {error: formatMessage( + messages.display_name_maxLength, + {maxLength: ViewTypes.MAX_CHANNELNAME_LENGTH} + )}; + } else if (displayName.length < ViewTypes.MIN_CHANNELNAME_LENGTH) { + return {error: formatMessage( + messages.display_name_minLength, + {minLength: ViewTypes.MIN_CHANNELNAME_LENGTH} + )}; + } + + return {error: null}; + }; + + validateChannelURL = (channelURL) => { + const {formatMessage} = this.context.intl; + + if (!channelURL) { + return {error: formatMessage(messages.name_required)}; + } else if (channelURL.length > ViewTypes.MAX_CHANNELNAME_LENGTH) { + return {error: formatMessage( + messages.name_maxLength, + {maxLength: ViewTypes.MAX_CHANNELNAME_LENGTH} + )}; + } + + const cleanedName = cleanUpUrlable(channelURL); + if (cleanedName === channelURL) { + return {error: null}; + } + + return {error: formatMessage(messages.name_lowercase)}; + }; + + onUpdateChannel = () => { + Keyboard.dismiss(); + const {displayName, channelURL, purpose, header} = this.state; + const {channel: {id}} = this.props; + const channel = { + display_name: displayName, + name: channelURL, + purpose, + header + }; + + let result = this.validateDisplayName(displayName.trim()); + if (result.error) { + this.setState({error: result.error}); + return; + } + result = this.validateChannelURL(channelURL.trim()); + if (result.error) { + this.setState({error: result.error}); + return; + } + + this.props.actions.patchChannel(id, channel); + }; + + onNavigatorEvent = (event) => { + if (event.type === 'NavBarButtonPress') { + switch (event.id) { + case 'close-edit-channel': + this.close(); + break; + case 'edit-channel': + this.onUpdateChannel(); + break; + } + } + }; + + onDisplayNameChange = (displayName) => { + this.setState({displayName}); + }; + + onChannelURLChange = (channelURL) => { + this.setState({channelURL}); + }; + + onPurposeChange = (purpose) => { + this.setState({purpose}); + }; + + onHeaderChange = (header) => { + this.setState({header}); + }; + + render() { + const { + channel: { + display_name: oldDisplayName, + name: oldChannelURL, + header: oldHeader, + purpose: oldPurpose, + type + }, + navigator, + theme, + currentTeamUrl, + deviceWidth, + deviceHeight + } = this.props; + const { + error, + updating, + displayName, + channelURL, + purpose, + header + } = this.state; + + return ( + + ); + } +} + diff --git a/app/screens/edit_channel/index.js b/app/screens/edit_channel/index.js new file mode 100644 index 000000000..8076ea471 --- /dev/null +++ b/app/screens/edit_channel/index.js @@ -0,0 +1,39 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import {bindActionCreators} from 'redux'; +import {connect} from 'react-redux'; + +import {getCurrentChannel} from 'mattermost-redux/selectors/entities/channels'; +import {getCurrentTeamUrl} from 'mattermost-redux/selectors/entities/teams'; +import {patchChannel} from 'mattermost-redux/actions/channels'; + +import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {getDimensions} from 'app/selectors/device'; + +import EditChannel from './edit_channel'; + +function mapStateToProps(state) { + const {updateChannel: updateChannelRequest} = state.requests.channels; + const channel = getCurrentChannel(state); + const {deviceWidth, deviceHeight} = getDimensions(state); + + return { + channel, + currentTeamUrl: getCurrentTeamUrl(state), + updateChannelRequest, + theme: getTheme(state), + deviceWidth, + deviceHeight + }; +} + +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + patchChannel + }, dispatch) + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(EditChannel); diff --git a/app/screens/index.js b/app/screens/index.js index 5a719352a..fcda5bd0a 100644 --- a/app/screens/index.js +++ b/app/screens/index.js @@ -11,6 +11,7 @@ import AdvancedSettings from 'app/screens/settings/advanced_settings'; import Channel from 'app/screens/channel'; import ChannelAddMembers from 'app/screens/channel_add_members'; import ChannelInfo from 'app/screens/channel_info'; +import EditChannel from 'app/screens/edit_channel'; import ChannelMembers from 'app/screens/channel_members'; import ChannelPeek from 'app/screens/channel_peek'; import ClientUpgrade from 'app/screens/client_upgrade'; @@ -63,6 +64,7 @@ export function registerScreens(store, Provider) { Navigation.registerComponent('Channel', () => wrapWithContextProvider(Channel, false), store, Provider); Navigation.registerComponent('ChannelAddMembers', () => wrapWithContextProvider(ChannelAddMembers), store, Provider); Navigation.registerComponent('ChannelInfo', () => wrapWithContextProvider(ChannelInfo), store, Provider); + Navigation.registerComponent('EditChannel', () => wrapWithContextProvider(EditChannel), store, Provider); Navigation.registerComponent('ChannelMembers', () => wrapWithContextProvider(ChannelMembers), store, Provider); Navigation.registerComponent('ChannelPeek', () => wrapWithContextProvider(ChannelPeek), store, Provider); Navigation.registerComponent('ClientUpgrade', () => wrapWithContextProvider(ClientUpgrade), store, Provider); diff --git a/app/utils/latinise.js b/app/utils/latinise.js new file mode 100644 index 000000000..5c0059ff1 --- /dev/null +++ b/app/utils/latinise.js @@ -0,0 +1,1003 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. +// Credit to http://semplicewebsites.com/removing-accents-javascript + +var latinMap = { + Á: 'A', // LATIN CAPITAL LETTER A WITH ACUTE + Ă: 'A', // LATIN CAPITAL LETTER A WITH BREVE + Ắ: 'A', // LATIN CAPITAL LETTER A WITH BREVE AND ACUTE + Ặ: 'A', // LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW + Ằ: 'A', // LATIN CAPITAL LETTER A WITH BREVE AND GRAVE + Ẳ: 'A', // LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE + Ẵ: 'A', // LATIN CAPITAL LETTER A WITH BREVE AND TILDE + Ǎ: 'A', // LATIN CAPITAL LETTER A WITH CARON + Â: 'A', // LATIN CAPITAL LETTER A WITH CIRCUMFLEX + Ấ: 'A', // LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE + Ậ: 'A', // LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW + Ầ: 'A', // LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE + Ẩ: 'A', // LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE + Ẫ: 'A', // LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE + Ä: 'A', // LATIN CAPITAL LETTER A WITH DIAERESIS + Ǟ: 'A', // LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON + Ȧ: 'A', // LATIN CAPITAL LETTER A WITH DOT ABOVE + Ǡ: 'A', // LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON + Ạ: 'A', // LATIN CAPITAL LETTER A WITH DOT BELOW + Ȁ: 'A', // LATIN CAPITAL LETTER A WITH DOUBLE GRAVE + À: 'A', // LATIN CAPITAL LETTER A WITH GRAVE + Ả: 'A', // LATIN CAPITAL LETTER A WITH HOOK ABOVE + Ȃ: 'A', // LATIN CAPITAL LETTER A WITH INVERTED BREVE + Ā: 'A', // LATIN CAPITAL LETTER A WITH MACRON + Ą: 'A', // LATIN CAPITAL LETTER A WITH OGONEK + Å: 'A', // LATIN CAPITAL LETTER A WITH RING ABOVE + Ǻ: 'A', // LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE + Ḁ: 'A', // LATIN CAPITAL LETTER A WITH RING BELOW + Ⱥ: 'A', // LATIN CAPITAL LETTER A WITH STROKE + Ã: 'A', // LATIN CAPITAL LETTER A WITH TILDE + Ꜳ: 'AA', // LATIN CAPITAL LETTER AA + Æ: 'AE', // LATIN CAPITAL LETTER AE + Ǽ: 'AE', // LATIN CAPITAL LETTER AE WITH ACUTE + Ǣ: 'AE', // LATIN CAPITAL LETTER AE WITH MACRON + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER AFRICAN D' (Ɖ) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER ALPHA' (Ɑ) + Ꜵ: 'AO', // LATIN CAPITAL LETTER AO + Ꜷ: 'AU', // LATIN CAPITAL LETTER AU + Ꜹ: 'AV', // LATIN CAPITAL LETTER AV + Ꜻ: 'AV', // LATIN CAPITAL LETTER AV WITH HORIZONTAL BAR + Ꜽ: 'AY', // LATIN CAPITAL LETTER AY + Ḃ: 'B', // LATIN CAPITAL LETTER B WITH DOT ABOVE + Ḅ: 'B', // LATIN CAPITAL LETTER B WITH DOT BELOW + Ɓ: 'B', // LATIN CAPITAL LETTER B WITH HOOK + Ḇ: 'B', // LATIN CAPITAL LETTER B WITH LINE BELOW + Ƀ: 'B', // LATIN CAPITAL LETTER B WITH STROKE + Ƃ: 'B', // LATIN CAPITAL LETTER B WITH TOPBAR + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER BROKEN L' (Ꝇ) + Ć: 'C', // LATIN CAPITAL LETTER C WITH ACUTE + Č: 'C', // LATIN CAPITAL LETTER C WITH CARON + Ç: 'C', // LATIN CAPITAL LETTER C WITH CEDILLA + Ḉ: 'C', // LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE + Ĉ: 'C', // LATIN CAPITAL LETTER C WITH CIRCUMFLEX + Ċ: 'C', // LATIN CAPITAL LETTER C WITH DOT ABOVE + Ƈ: 'C', // LATIN CAPITAL LETTER C WITH HOOK + Ȼ: 'C', // LATIN CAPITAL LETTER C WITH STROKE + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER CON' (Ꝯ) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER CUATRILLO' (Ꜭ) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER CUATRILLO WITH COMMA' (Ꜯ) + Ď: 'D', // LATIN CAPITAL LETTER D WITH CARON + Ḑ: 'D', // LATIN CAPITAL LETTER D WITH CEDILLA + Ḓ: 'D', // LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW + Ḋ: 'D', // LATIN CAPITAL LETTER D WITH DOT ABOVE + Ḍ: 'D', // LATIN CAPITAL LETTER D WITH DOT BELOW + Ɗ: 'D', // LATIN CAPITAL LETTER D WITH HOOK + Ḏ: 'D', // LATIN CAPITAL LETTER D WITH LINE BELOW + Dz: 'D', // LATIN CAPITAL LETTER D WITH SMALL LETTER Z + Dž: 'D', // LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON + Đ: 'D', // LATIN CAPITAL LETTER D WITH STROKE + Ƌ: 'D', // LATIN CAPITAL LETTER D WITH TOPBAR + DZ: 'DZ', // LATIN CAPITAL LETTER DZ + DŽ: 'DZ', // LATIN CAPITAL LETTER DZ WITH CARON + É: 'E', // LATIN CAPITAL LETTER E WITH ACUTE + Ĕ: 'E', // LATIN CAPITAL LETTER E WITH BREVE + Ě: 'E', // LATIN CAPITAL LETTER E WITH CARON + Ȩ: 'E', // LATIN CAPITAL LETTER E WITH CEDILLA + Ḝ: 'E', // LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE + Ê: 'E', // LATIN CAPITAL LETTER E WITH CIRCUMFLEX + Ế: 'E', // LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE + Ệ: 'E', // LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW + Ề: 'E', // LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE + Ể: 'E', // LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE + Ễ: 'E', // LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE + Ḙ: 'E', // LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW + Ë: 'E', // LATIN CAPITAL LETTER E WITH DIAERESIS + Ė: 'E', // LATIN CAPITAL LETTER E WITH DOT ABOVE + Ẹ: 'E', // LATIN CAPITAL LETTER E WITH DOT BELOW + Ȅ: 'E', // LATIN CAPITAL LETTER E WITH DOUBLE GRAVE + È: 'E', // LATIN CAPITAL LETTER E WITH GRAVE + Ẻ: 'E', // LATIN CAPITAL LETTER E WITH HOOK ABOVE + Ȇ: 'E', // LATIN CAPITAL LETTER E WITH INVERTED BREVE + Ē: 'E', // LATIN CAPITAL LETTER E WITH MACRON + Ḗ: 'E', // LATIN CAPITAL LETTER E WITH MACRON AND ACUTE + Ḕ: 'E', // LATIN CAPITAL LETTER E WITH MACRON AND GRAVE + Ę: 'E', // LATIN CAPITAL LETTER E WITH OGONEK + Ɇ: 'E', // LATIN CAPITAL LETTER E WITH STROKE + Ẽ: 'E', // LATIN CAPITAL LETTER E WITH TILDE + Ḛ: 'E', // LATIN CAPITAL LETTER E WITH TILDE BELOW + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER EGYPTOLOGICAL AIN' (Ꜥ) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF' (Ꜣ) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER ENG' (Ŋ) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER ESH' (Ʃ) + Ꝫ: 'ET', // LATIN CAPITAL LETTER ET + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER ETH' (Ð) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER EZH' (Ʒ) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER EZH REVERSED' (Ƹ) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER EZH WITH CARON' (Ǯ) + Ḟ: 'F', // LATIN CAPITAL LETTER F WITH DOT ABOVE + Ƒ: 'F', // LATIN CAPITAL LETTER F WITH HOOK + Ǵ: 'G', // LATIN CAPITAL LETTER G WITH ACUTE + Ğ: 'G', // LATIN CAPITAL LETTER G WITH BREVE + Ǧ: 'G', // LATIN CAPITAL LETTER G WITH CARON + Ģ: 'G', // LATIN CAPITAL LETTER G WITH CEDILLA + Ĝ: 'G', // LATIN CAPITAL LETTER G WITH CIRCUMFLEX + Ġ: 'G', // LATIN CAPITAL LETTER G WITH DOT ABOVE + Ɠ: 'G', // LATIN CAPITAL LETTER G WITH HOOK + Ḡ: 'G', // LATIN CAPITAL LETTER G WITH MACRON + Ǥ: 'G', // LATIN CAPITAL LETTER G WITH STROKE + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER GAMMA' (Ɣ) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER GLOTTAL STOP' (Ɂ) + Ḫ: 'H', // LATIN CAPITAL LETTER H WITH BREVE BELOW + Ȟ: 'H', // LATIN CAPITAL LETTER H WITH CARON + Ḩ: 'H', // LATIN CAPITAL LETTER H WITH CEDILLA + Ĥ: 'H', // LATIN CAPITAL LETTER H WITH CIRCUMFLEX + Ⱨ: 'H', // LATIN CAPITAL LETTER H WITH DESCENDER + Ḧ: 'H', // LATIN CAPITAL LETTER H WITH DIAERESIS + Ḣ: 'H', // LATIN CAPITAL LETTER H WITH DOT ABOVE + Ḥ: 'H', // LATIN CAPITAL LETTER H WITH DOT BELOW + Ħ: 'H', // LATIN CAPITAL LETTER H WITH STROKE + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER HALF H' (Ⱶ) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER HENG' (Ꜧ) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER HWAIR' (Ƕ) + Í: 'I', // LATIN CAPITAL LETTER I WITH ACUTE + Ĭ: 'I', // LATIN CAPITAL LETTER I WITH BREVE + Ǐ: 'I', // LATIN CAPITAL LETTER I WITH CARON + Î: 'I', // LATIN CAPITAL LETTER I WITH CIRCUMFLEX + Ï: 'I', // LATIN CAPITAL LETTER I WITH DIAERESIS + Ḯ: 'I', // LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE + İ: 'I', // LATIN CAPITAL LETTER I WITH DOT ABOVE + Ị: 'I', // LATIN CAPITAL LETTER I WITH DOT BELOW + Ȉ: 'I', // LATIN CAPITAL LETTER I WITH DOUBLE GRAVE + Ì: 'I', // LATIN CAPITAL LETTER I WITH GRAVE + Ỉ: 'I', // LATIN CAPITAL LETTER I WITH HOOK ABOVE + Ȋ: 'I', // LATIN CAPITAL LETTER I WITH INVERTED BREVE + Ī: 'I', // LATIN CAPITAL LETTER I WITH MACRON + Į: 'I', // LATIN CAPITAL LETTER I WITH OGONEK + Ɨ: 'I', // LATIN CAPITAL LETTER I WITH STROKE + Ĩ: 'I', // LATIN CAPITAL LETTER I WITH TILDE + Ḭ: 'I', // LATIN CAPITAL LETTER I WITH TILDE BELOW + Ꝺ: 'D', // LATIN CAPITAL LETTER INSULAR D + Ꝼ: 'F', // LATIN CAPITAL LETTER INSULAR F + Ᵹ: 'G', // LATIN CAPITAL LETTER INSULAR G + Ꞃ: 'R', // LATIN CAPITAL LETTER INSULAR R + Ꞅ: 'S', // LATIN CAPITAL LETTER INSULAR S + Ꞇ: 'T', // LATIN CAPITAL LETTER INSULAR T + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER IOTA' (Ɩ) + Ꝭ: 'IS', // LATIN CAPITAL LETTER IS + Ĵ: 'J', // LATIN CAPITAL LETTER J WITH CIRCUMFLEX + Ɉ: 'J', // LATIN CAPITAL LETTER J WITH STROKE + Ḱ: 'K', // LATIN CAPITAL LETTER K WITH ACUTE + Ǩ: 'K', // LATIN CAPITAL LETTER K WITH CARON + Ķ: 'K', // LATIN CAPITAL LETTER K WITH CEDILLA + Ⱪ: 'K', // LATIN CAPITAL LETTER K WITH DESCENDER + Ꝃ: 'K', // LATIN CAPITAL LETTER K WITH DIAGONAL STROKE + Ḳ: 'K', // LATIN CAPITAL LETTER K WITH DOT BELOW + Ƙ: 'K', // LATIN CAPITAL LETTER K WITH HOOK + Ḵ: 'K', // LATIN CAPITAL LETTER K WITH LINE BELOW + Ꝁ: 'K', // LATIN CAPITAL LETTER K WITH STROKE + Ꝅ: 'K', // LATIN CAPITAL LETTER K WITH STROKE AND DIAGONAL STROKE + Ĺ: 'L', // LATIN CAPITAL LETTER L WITH ACUTE + Ƚ: 'L', // LATIN CAPITAL LETTER L WITH BAR + Ľ: 'L', // LATIN CAPITAL LETTER L WITH CARON + Ļ: 'L', // LATIN CAPITAL LETTER L WITH CEDILLA + Ḽ: 'L', // LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW + Ḷ: 'L', // LATIN CAPITAL LETTER L WITH DOT BELOW + Ḹ: 'L', // LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON + Ⱡ: 'L', // LATIN CAPITAL LETTER L WITH DOUBLE BAR + Ꝉ: 'L', // LATIN CAPITAL LETTER L WITH HIGH STROKE + Ḻ: 'L', // LATIN CAPITAL LETTER L WITH LINE BELOW + Ŀ: 'L', // LATIN CAPITAL LETTER L WITH MIDDLE DOT + Ɫ: 'L', // LATIN CAPITAL LETTER L WITH MIDDLE TILDE + Lj: 'L', // LATIN CAPITAL LETTER L WITH SMALL LETTER J + Ł: 'L', // LATIN CAPITAL LETTER L WITH STROKE + LJ: 'LJ', // LATIN CAPITAL LETTER LJ + Ḿ: 'M', // LATIN CAPITAL LETTER M WITH ACUTE + Ṁ: 'M', // LATIN CAPITAL LETTER M WITH DOT ABOVE + Ṃ: 'M', // LATIN CAPITAL LETTER M WITH DOT BELOW + Ɱ: 'M', // LATIN CAPITAL LETTER M WITH HOOK + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER MIDDLE-WELSH LL' (Ỻ) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER MIDDLE-WELSH V' (Ỽ) + Ń: 'N', // LATIN CAPITAL LETTER N WITH ACUTE + Ň: 'N', // LATIN CAPITAL LETTER N WITH CARON + Ņ: 'N', // LATIN CAPITAL LETTER N WITH CEDILLA + Ṋ: 'N', // LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW + Ṅ: 'N', // LATIN CAPITAL LETTER N WITH DOT ABOVE + Ṇ: 'N', // LATIN CAPITAL LETTER N WITH DOT BELOW + Ǹ: 'N', // LATIN CAPITAL LETTER N WITH GRAVE + Ɲ: 'N', // LATIN CAPITAL LETTER N WITH LEFT HOOK + Ṉ: 'N', // LATIN CAPITAL LETTER N WITH LINE BELOW + Ƞ: 'N', // LATIN CAPITAL LETTER N WITH LONG RIGHT LEG + Nj: 'N', // LATIN CAPITAL LETTER N WITH SMALL LETTER J + Ñ: 'N', // LATIN CAPITAL LETTER N WITH TILDE + NJ: 'NJ', // LATIN CAPITAL LETTER NJ + Ó: 'O', // LATIN CAPITAL LETTER O WITH ACUTE + Ŏ: 'O', // LATIN CAPITAL LETTER O WITH BREVE + Ǒ: 'O', // LATIN CAPITAL LETTER O WITH CARON + Ô: 'O', // LATIN CAPITAL LETTER O WITH CIRCUMFLEX + Ố: 'O', // LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE + Ộ: 'O', // LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW + Ồ: 'O', // LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE + Ổ: 'O', // LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE + Ỗ: 'O', // LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE + Ö: 'O', // LATIN CAPITAL LETTER O WITH DIAERESIS + Ȫ: 'O', // LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON + Ȯ: 'O', // LATIN CAPITAL LETTER O WITH DOT ABOVE + Ȱ: 'O', // LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON + Ọ: 'O', // LATIN CAPITAL LETTER O WITH DOT BELOW + Ő: 'O', // LATIN CAPITAL LETTER O WITH DOUBLE ACUTE + Ȍ: 'O', // LATIN CAPITAL LETTER O WITH DOUBLE GRAVE + Ò: 'O', // LATIN CAPITAL LETTER O WITH GRAVE + Ỏ: 'O', // LATIN CAPITAL LETTER O WITH HOOK ABOVE + Ơ: 'O', // LATIN CAPITAL LETTER O WITH HORN + Ớ: 'O', // LATIN CAPITAL LETTER O WITH HORN AND ACUTE + Ợ: 'O', // LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW + Ờ: 'O', // LATIN CAPITAL LETTER O WITH HORN AND GRAVE + Ở: 'O', // LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE + Ỡ: 'O', // LATIN CAPITAL LETTER O WITH HORN AND TILDE + Ȏ: 'O', // LATIN CAPITAL LETTER O WITH INVERTED BREVE + Ꝋ: 'O', // LATIN CAPITAL LETTER O WITH LONG STROKE OVERLAY + Ꝍ: 'O', // LATIN CAPITAL LETTER O WITH LOOP + Ō: 'O', // LATIN CAPITAL LETTER O WITH MACRON + Ṓ: 'O', // LATIN CAPITAL LETTER O WITH MACRON AND ACUTE + Ṑ: 'O', // LATIN CAPITAL LETTER O WITH MACRON AND GRAVE + Ɵ: 'O', // LATIN CAPITAL LETTER O WITH MIDDLE TILDE + Ǫ: 'O', // LATIN CAPITAL LETTER O WITH OGONEK + Ǭ: 'O', // LATIN CAPITAL LETTER O WITH OGONEK AND MACRON + Ø: 'O', // LATIN CAPITAL LETTER O WITH STROKE + Ǿ: 'O', // LATIN CAPITAL LETTER O WITH STROKE AND ACUTE + Õ: 'O', // LATIN CAPITAL LETTER O WITH TILDE + Ṍ: 'O', // LATIN CAPITAL LETTER O WITH TILDE AND ACUTE + Ṏ: 'O', // LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS + Ȭ: 'O', // LATIN CAPITAL LETTER O WITH TILDE AND MACRON + Ƣ: 'OI', // LATIN CAPITAL LETTER OI + Ꝏ: 'OO', // LATIN CAPITAL LETTER OO + Ɛ: 'E', // LATIN CAPITAL LETTER OPEN E + Ɔ: 'O', // LATIN CAPITAL LETTER OPEN O + Ȣ: 'OU', // LATIN CAPITAL LETTER OU + Ṕ: 'P', // LATIN CAPITAL LETTER P WITH ACUTE + Ṗ: 'P', // LATIN CAPITAL LETTER P WITH DOT ABOVE + Ꝓ: 'P', // LATIN CAPITAL LETTER P WITH FLOURISH + Ƥ: 'P', // LATIN CAPITAL LETTER P WITH HOOK + Ꝕ: 'P', // LATIN CAPITAL LETTER P WITH SQUIRREL TAIL + Ᵽ: 'P', // LATIN CAPITAL LETTER P WITH STROKE + Ꝑ: 'P', // LATIN CAPITAL LETTER P WITH STROKE THROUGH DESCENDER + Ꝙ: 'Q', // LATIN CAPITAL LETTER Q WITH DIAGONAL STROKE + Ꝗ: 'Q', // LATIN CAPITAL LETTER Q WITH STROKE THROUGH DESCENDER + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER R ROTUNDA' (Ꝛ) + Ŕ: 'R', // LATIN CAPITAL LETTER R WITH ACUTE + Ř: 'R', // LATIN CAPITAL LETTER R WITH CARON + Ŗ: 'R', // LATIN CAPITAL LETTER R WITH CEDILLA + Ṙ: 'R', // LATIN CAPITAL LETTER R WITH DOT ABOVE + Ṛ: 'R', // LATIN CAPITAL LETTER R WITH DOT BELOW + Ṝ: 'R', // LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON + Ȑ: 'R', // LATIN CAPITAL LETTER R WITH DOUBLE GRAVE + Ȓ: 'R', // LATIN CAPITAL LETTER R WITH INVERTED BREVE + Ṟ: 'R', // LATIN CAPITAL LETTER R WITH LINE BELOW + Ɍ: 'R', // LATIN CAPITAL LETTER R WITH STROKE + Ɽ: 'R', // LATIN CAPITAL LETTER R WITH TAIL + Ꜿ: 'C', // LATIN CAPITAL LETTER REVERSED C WITH DOT + Ǝ: 'E', // LATIN CAPITAL LETTER REVERSED E + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER RUM ROTUNDA' (Ꝝ) + Ś: 'S', // LATIN CAPITAL LETTER S WITH ACUTE + Ṥ: 'S', // LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE + Š: 'S', // LATIN CAPITAL LETTER S WITH CARON + Ṧ: 'S', // LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE + Ş: 'S', // LATIN CAPITAL LETTER S WITH CEDILLA + Ŝ: 'S', // LATIN CAPITAL LETTER S WITH CIRCUMFLEX + Ș: 'S', // LATIN CAPITAL LETTER S WITH COMMA BELOW + Ṡ: 'S', // LATIN CAPITAL LETTER S WITH DOT ABOVE + Ṣ: 'S', // LATIN CAPITAL LETTER S WITH DOT BELOW + Ṩ: 'S', // LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER SALTILLO' (Ꞌ) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER SCHWA' (Ə) + ẞ: 'SS', // LATIN CAPITAL LETTER SHARP S + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL' (Ɋ) + Ť: 'T', // LATIN CAPITAL LETTER T WITH CARON + Ţ: 'T', // LATIN CAPITAL LETTER T WITH CEDILLA + Ṱ: 'T', // LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW + Ț: 'T', // LATIN CAPITAL LETTER T WITH COMMA BELOW + Ⱦ: 'T', // LATIN CAPITAL LETTER T WITH DIAGONAL STROKE + Ṫ: 'T', // LATIN CAPITAL LETTER T WITH DOT ABOVE + Ṭ: 'T', // LATIN CAPITAL LETTER T WITH DOT BELOW + Ƭ: 'T', // LATIN CAPITAL LETTER T WITH HOOK + Ṯ: 'T', // LATIN CAPITAL LETTER T WITH LINE BELOW + Ʈ: 'T', // LATIN CAPITAL LETTER T WITH RETROFLEX HOOK + Ŧ: 'T', // LATIN CAPITAL LETTER T WITH STROKE + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER THORN' (Þ) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER THORN WITH STROKE' (Ꝥ) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER THORN WITH STROKE THROUGH DESCENDER' (Ꝧ) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER TONE FIVE' (Ƽ) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER TONE SIX' (Ƅ) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER TONE TWO' (Ƨ) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER TRESILLO' (Ꜫ) + Ɐ: 'A', // LATIN CAPITAL LETTER TURNED A + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER TURNED INSULAR G' (Ꝿ) + Ꞁ: 'L', // LATIN CAPITAL LETTER TURNED L + Ɯ: 'M', // LATIN CAPITAL LETTER TURNED M + Ʌ: 'V', // LATIN CAPITAL LETTER TURNED V + Ꜩ: 'TZ', // LATIN CAPITAL LETTER TZ + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER U BAR' (Ʉ) + Ú: 'U', // LATIN CAPITAL LETTER U WITH ACUTE + Ŭ: 'U', // LATIN CAPITAL LETTER U WITH BREVE + Ǔ: 'U', // LATIN CAPITAL LETTER U WITH CARON + Û: 'U', // LATIN CAPITAL LETTER U WITH CIRCUMFLEX + Ṷ: 'U', // LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW + Ü: 'U', // LATIN CAPITAL LETTER U WITH DIAERESIS + Ǘ: 'U', // LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE + Ǚ: 'U', // LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON + Ǜ: 'U', // LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE + Ǖ: 'U', // LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON + Ṳ: 'U', // LATIN CAPITAL LETTER U WITH DIAERESIS BELOW + Ụ: 'U', // LATIN CAPITAL LETTER U WITH DOT BELOW + Ű: 'U', // LATIN CAPITAL LETTER U WITH DOUBLE ACUTE + Ȕ: 'U', // LATIN CAPITAL LETTER U WITH DOUBLE GRAVE + Ù: 'U', // LATIN CAPITAL LETTER U WITH GRAVE + Ủ: 'U', // LATIN CAPITAL LETTER U WITH HOOK ABOVE + Ư: 'U', // LATIN CAPITAL LETTER U WITH HORN + Ứ: 'U', // LATIN CAPITAL LETTER U WITH HORN AND ACUTE + Ự: 'U', // LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW + Ừ: 'U', // LATIN CAPITAL LETTER U WITH HORN AND GRAVE + Ử: 'U', // LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE + Ữ: 'U', // LATIN CAPITAL LETTER U WITH HORN AND TILDE + Ȗ: 'U', // LATIN CAPITAL LETTER U WITH INVERTED BREVE + Ū: 'U', // LATIN CAPITAL LETTER U WITH MACRON + Ṻ: 'U', // LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS + Ų: 'U', // LATIN CAPITAL LETTER U WITH OGONEK + Ů: 'U', // LATIN CAPITAL LETTER U WITH RING ABOVE + Ũ: 'U', // LATIN CAPITAL LETTER U WITH TILDE + Ṹ: 'U', // LATIN CAPITAL LETTER U WITH TILDE AND ACUTE + Ṵ: 'U', // LATIN CAPITAL LETTER U WITH TILDE BELOW + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER UPSILON' (Ʊ) + Ꝟ: 'V', // LATIN CAPITAL LETTER V WITH DIAGONAL STROKE + Ṿ: 'V', // LATIN CAPITAL LETTER V WITH DOT BELOW + Ʋ: 'V', // LATIN CAPITAL LETTER V WITH HOOK + Ṽ: 'V', // LATIN CAPITAL LETTER V WITH TILDE + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER VEND' (Ꝩ) + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER VISIGOTHIC Z' (Ꝣ) + Ꝡ: 'VY', // LATIN CAPITAL LETTER VY + Ẃ: 'W', // LATIN CAPITAL LETTER W WITH ACUTE + Ŵ: 'W', // LATIN CAPITAL LETTER W WITH CIRCUMFLEX + Ẅ: 'W', // LATIN CAPITAL LETTER W WITH DIAERESIS + Ẇ: 'W', // LATIN CAPITAL LETTER W WITH DOT ABOVE + Ẉ: 'W', // LATIN CAPITAL LETTER W WITH DOT BELOW + Ẁ: 'W', // LATIN CAPITAL LETTER W WITH GRAVE + Ⱳ: 'W', // LATIN CAPITAL LETTER W WITH HOOK + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER WYNN' (Ƿ) + Ẍ: 'X', // LATIN CAPITAL LETTER X WITH DIAERESIS + Ẋ: 'X', // LATIN CAPITAL LETTER X WITH DOT ABOVE + Ý: 'Y', // LATIN CAPITAL LETTER Y WITH ACUTE + Ŷ: 'Y', // LATIN CAPITAL LETTER Y WITH CIRCUMFLEX + Ÿ: 'Y', // LATIN CAPITAL LETTER Y WITH DIAERESIS + Ẏ: 'Y', // LATIN CAPITAL LETTER Y WITH DOT ABOVE + Ỵ: 'Y', // LATIN CAPITAL LETTER Y WITH DOT BELOW + Ỳ: 'Y', // LATIN CAPITAL LETTER Y WITH GRAVE + Ƴ: 'Y', // LATIN CAPITAL LETTER Y WITH HOOK + Ỷ: 'Y', // LATIN CAPITAL LETTER Y WITH HOOK ABOVE + Ỿ: 'Y', // LATIN CAPITAL LETTER Y WITH LOOP + Ȳ: 'Y', // LATIN CAPITAL LETTER Y WITH MACRON + Ɏ: 'Y', // LATIN CAPITAL LETTER Y WITH STROKE + Ỹ: 'Y', // LATIN CAPITAL LETTER Y WITH TILDE + // CANNOT FIND APPROXIMATION FOR 'LATIN CAPITAL LETTER YOGH' (Ȝ) + Ź: 'Z', // LATIN CAPITAL LETTER Z WITH ACUTE + Ž: 'Z', // LATIN CAPITAL LETTER Z WITH CARON + Ẑ: 'Z', // LATIN CAPITAL LETTER Z WITH CIRCUMFLEX + Ⱬ: 'Z', // LATIN CAPITAL LETTER Z WITH DESCENDER + Ż: 'Z', // LATIN CAPITAL LETTER Z WITH DOT ABOVE + Ẓ: 'Z', // LATIN CAPITAL LETTER Z WITH DOT BELOW + Ȥ: 'Z', // LATIN CAPITAL LETTER Z WITH HOOK + Ẕ: 'Z', // LATIN CAPITAL LETTER Z WITH LINE BELOW + Ƶ: 'Z', // LATIN CAPITAL LETTER Z WITH STROKE + IJ: 'IJ', // LATIN CAPITAL LIGATURE IJ + Œ: 'OE', // LATIN CAPITAL LIGATURE OE + // CANNOT FIND APPROXIMATION FOR 'LATIN CROSS' (✝) + // CANNOT FIND APPROXIMATION FOR 'LATIN EPIGRAPHIC LETTER ARCHAIC M' (ꟿ) + // CANNOT FIND APPROXIMATION FOR 'LATIN EPIGRAPHIC LETTER I LONGA' (ꟾ) + // CANNOT FIND APPROXIMATION FOR 'LATIN EPIGRAPHIC LETTER INVERTED M' (ꟽ) + // CANNOT FIND APPROXIMATION FOR 'LATIN EPIGRAPHIC LETTER REVERSED F' (ꟻ) + // CANNOT FIND APPROXIMATION FOR 'LATIN EPIGRAPHIC LETTER REVERSED P' (ꟼ) + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER AIN' (ᴥ) + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER ALVEOLAR CLICK' (ǂ) + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER BIDENTAL PERCUSSIVE' (ʭ) + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER BILABIAL CLICK' (ʘ) + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER BILABIAL PERCUSSIVE' (ʬ) + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER DENTAL CLICK' (ǀ) + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER GLOTTAL STOP' (ʔ) + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER GLOTTAL STOP WITH STROKE' (ʡ) + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER INVERTED GLOTTAL STOP' (ʖ) + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER INVERTED GLOTTAL STOP WITH STROKE' (ƾ) + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER LATERAL CLICK' (ǁ) + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER PHARYNGEAL VOICED FRICATIVE' (ʕ) + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER RETROFLEX CLICK' (ǃ) + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER REVERSED ESH LOOP' (ƪ) + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER REVERSED GLOTTAL STOP WITH STROKE' (ʢ) + ᴀ: 'A', // LATIN LETTER SMALL CAPITAL A + ᴁ: 'AE', // LATIN LETTER SMALL CAPITAL AE + ʙ: 'B', // LATIN LETTER SMALL CAPITAL B + ᴃ: 'B', // LATIN LETTER SMALL CAPITAL BARRED B + ᴄ: 'C', // LATIN LETTER SMALL CAPITAL C + ᴅ: 'D', // LATIN LETTER SMALL CAPITAL D + ᴇ: 'E', // LATIN LETTER SMALL CAPITAL E + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER SMALL CAPITAL ETH' (ᴆ) + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER SMALL CAPITAL EZH' (ᴣ) + ꜰ: 'F', // LATIN LETTER SMALL CAPITAL F + ɢ: 'G', // LATIN LETTER SMALL CAPITAL G + ʛ: 'G', // LATIN LETTER SMALL CAPITAL G WITH HOOK + ʜ: 'H', // LATIN LETTER SMALL CAPITAL H + ɪ: 'I', // LATIN LETTER SMALL CAPITAL I + ʁ: 'R', // LATIN LETTER SMALL CAPITAL INVERTED R + ᴊ: 'J', // LATIN LETTER SMALL CAPITAL J + ᴋ: 'K', // LATIN LETTER SMALL CAPITAL K + ʟ: 'L', // LATIN LETTER SMALL CAPITAL L + ᴌ: 'L', // LATIN LETTER SMALL CAPITAL L WITH STROKE + ᴍ: 'M', // LATIN LETTER SMALL CAPITAL M + ɴ: 'N', // LATIN LETTER SMALL CAPITAL N + ᴏ: 'O', // LATIN LETTER SMALL CAPITAL O + ɶ: 'OE', // LATIN LETTER SMALL CAPITAL OE + ᴐ: 'O', // LATIN LETTER SMALL CAPITAL OPEN O + ᴕ: 'OU', // LATIN LETTER SMALL CAPITAL OU + ᴘ: 'P', // LATIN LETTER SMALL CAPITAL P + ʀ: 'R', // LATIN LETTER SMALL CAPITAL R + ᴎ: 'N', // LATIN LETTER SMALL CAPITAL REVERSED N + ᴙ: 'R', // LATIN LETTER SMALL CAPITAL REVERSED R + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER SMALL CAPITAL RUM' (ꝶ) + ꜱ: 'S', // LATIN LETTER SMALL CAPITAL S + ᴛ: 'T', // LATIN LETTER SMALL CAPITAL T + ⱻ: 'E', // LATIN LETTER SMALL CAPITAL TURNED E + ᴚ: 'R', // LATIN LETTER SMALL CAPITAL TURNED R + ᴜ: 'U', // LATIN LETTER SMALL CAPITAL U + ᴠ: 'V', // LATIN LETTER SMALL CAPITAL V + ᴡ: 'W', // LATIN LETTER SMALL CAPITAL W + ʏ: 'Y', // LATIN LETTER SMALL CAPITAL Y + ᴢ: 'Z', // LATIN LETTER SMALL CAPITAL Z + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER STRETCHED C' (ʗ) + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER TWO WITH STROKE' (ƻ) + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER VOICED LARYNGEAL SPIRANT' (ᴤ) + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER WYNN' (ƿ) + // CANNOT FIND APPROXIMATION FOR 'LATIN LETTER YR' (Ʀ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL CAPITAL LETTER I WITH STROKE' (ᵻ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL CAPITAL LETTER U WITH STROKE' (ᵾ) + á: 'a', // LATIN SMALL LETTER A WITH ACUTE + ă: 'a', // LATIN SMALL LETTER A WITH BREVE + ắ: 'a', // LATIN SMALL LETTER A WITH BREVE AND ACUTE + ặ: 'a', // LATIN SMALL LETTER A WITH BREVE AND DOT BELOW + ằ: 'a', // LATIN SMALL LETTER A WITH BREVE AND GRAVE + ẳ: 'a', // LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE + ẵ: 'a', // LATIN SMALL LETTER A WITH BREVE AND TILDE + ǎ: 'a', // LATIN SMALL LETTER A WITH CARON + â: 'a', // LATIN SMALL LETTER A WITH CIRCUMFLEX + ấ: 'a', // LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE + ậ: 'a', // LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW + ầ: 'a', // LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE + ẩ: 'a', // LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE + ẫ: 'a', // LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE + ä: 'a', // LATIN SMALL LETTER A WITH DIAERESIS + ǟ: 'a', // LATIN SMALL LETTER A WITH DIAERESIS AND MACRON + ȧ: 'a', // LATIN SMALL LETTER A WITH DOT ABOVE + ǡ: 'a', // LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON + ạ: 'a', // LATIN SMALL LETTER A WITH DOT BELOW + ȁ: 'a', // LATIN SMALL LETTER A WITH DOUBLE GRAVE + à: 'a', // LATIN SMALL LETTER A WITH GRAVE + ả: 'a', // LATIN SMALL LETTER A WITH HOOK ABOVE + ȃ: 'a', // LATIN SMALL LETTER A WITH INVERTED BREVE + ā: 'a', // LATIN SMALL LETTER A WITH MACRON + ą: 'a', // LATIN SMALL LETTER A WITH OGONEK + ᶏ: 'a', // LATIN SMALL LETTER A WITH RETROFLEX HOOK + ẚ: 'a', // LATIN SMALL LETTER A WITH RIGHT HALF RING + å: 'a', // LATIN SMALL LETTER A WITH RING ABOVE + ǻ: 'a', // LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE + ḁ: 'a', // LATIN SMALL LETTER A WITH RING BELOW + ⱥ: 'a', // LATIN SMALL LETTER A WITH STROKE + ã: 'a', // LATIN SMALL LETTER A WITH TILDE + ꜳ: 'aa', // LATIN SMALL LETTER AA + æ: 'ae', // LATIN SMALL LETTER AE + ǽ: 'ae', // LATIN SMALL LETTER AE WITH ACUTE + ǣ: 'ae', // LATIN SMALL LETTER AE WITH MACRON + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER ALPHA' (ɑ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER ALPHA WITH RETROFLEX HOOK' (ᶐ) + ꜵ: 'ao', // LATIN SMALL LETTER AO + ꜷ: 'au', // LATIN SMALL LETTER AU + ꜹ: 'av', // LATIN SMALL LETTER AV + ꜻ: 'av', // LATIN SMALL LETTER AV WITH HORIZONTAL BAR + ꜽ: 'ay', // LATIN SMALL LETTER AY + ḃ: 'b', // LATIN SMALL LETTER B WITH DOT ABOVE + ḅ: 'b', // LATIN SMALL LETTER B WITH DOT BELOW + ɓ: 'b', // LATIN SMALL LETTER B WITH HOOK + ḇ: 'b', // LATIN SMALL LETTER B WITH LINE BELOW + ᵬ: 'b', // LATIN SMALL LETTER B WITH MIDDLE TILDE + ᶀ: 'b', // LATIN SMALL LETTER B WITH PALATAL HOOK + ƀ: 'b', // LATIN SMALL LETTER B WITH STROKE + ƃ: 'b', // LATIN SMALL LETTER B WITH TOPBAR + ɵ: 'o', // LATIN SMALL LETTER BARRED O + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER BOTTOM HALF O' (ᴗ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER BROKEN L' (ꝇ) + ć: 'c', // LATIN SMALL LETTER C WITH ACUTE + č: 'c', // LATIN SMALL LETTER C WITH CARON + ç: 'c', // LATIN SMALL LETTER C WITH CEDILLA + ḉ: 'c', // LATIN SMALL LETTER C WITH CEDILLA AND ACUTE + ĉ: 'c', // LATIN SMALL LETTER C WITH CIRCUMFLEX + ɕ: 'c', // LATIN SMALL LETTER C WITH CURL + ċ: 'c', // LATIN SMALL LETTER C WITH DOT ABOVE + ƈ: 'c', // LATIN SMALL LETTER C WITH HOOK + ȼ: 'c', // LATIN SMALL LETTER C WITH STROKE + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER CLOSED OMEGA' (ɷ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER CLOSED OPEN E' (ʚ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER CLOSED REVERSED OPEN E' (ɞ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER CON' (ꝯ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER CUATRILLO' (ꜭ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER CUATRILLO WITH COMMA' (ꜯ) + ď: 'd', // LATIN SMALL LETTER D WITH CARON + ḑ: 'd', // LATIN SMALL LETTER D WITH CEDILLA + ḓ: 'd', // LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW + ȡ: 'd', // LATIN SMALL LETTER D WITH CURL + ḋ: 'd', // LATIN SMALL LETTER D WITH DOT ABOVE + ḍ: 'd', // LATIN SMALL LETTER D WITH DOT BELOW + ɗ: 'd', // LATIN SMALL LETTER D WITH HOOK + ᶑ: 'd', // LATIN SMALL LETTER D WITH HOOK AND TAIL + ḏ: 'd', // LATIN SMALL LETTER D WITH LINE BELOW + ᵭ: 'd', // LATIN SMALL LETTER D WITH MIDDLE TILDE + ᶁ: 'd', // LATIN SMALL LETTER D WITH PALATAL HOOK + đ: 'd', // LATIN SMALL LETTER D WITH STROKE + ɖ: 'd', // LATIN SMALL LETTER D WITH TAIL + ƌ: 'd', // LATIN SMALL LETTER D WITH TOPBAR + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER DB DIGRAPH' (ȸ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER DELTA' (ẟ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER DEZH DIGRAPH' (ʤ) + ı: 'i', // LATIN SMALL LETTER DOTLESS I + ȷ: 'j', // LATIN SMALL LETTER DOTLESS J + ɟ: 'j', // LATIN SMALL LETTER DOTLESS J WITH STROKE + ʄ: 'j', // LATIN SMALL LETTER DOTLESS J WITH STROKE AND HOOK + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER DUM' (ꝱ) + dz: 'dz', // LATIN SMALL LETTER DZ + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER DZ DIGRAPH' (ʣ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER DZ DIGRAPH WITH CURL' (ʥ) + dž: 'dz', // LATIN SMALL LETTER DZ WITH CARON + é: 'e', // LATIN SMALL LETTER E WITH ACUTE + ĕ: 'e', // LATIN SMALL LETTER E WITH BREVE + ě: 'e', // LATIN SMALL LETTER E WITH CARON + ȩ: 'e', // LATIN SMALL LETTER E WITH CEDILLA + ḝ: 'e', // LATIN SMALL LETTER E WITH CEDILLA AND BREVE + ê: 'e', // LATIN SMALL LETTER E WITH CIRCUMFLEX + ế: 'e', // LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE + ệ: 'e', // LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW + ề: 'e', // LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE + ể: 'e', // LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE + ễ: 'e', // LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE + ḙ: 'e', // LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW + ë: 'e', // LATIN SMALL LETTER E WITH DIAERESIS + ė: 'e', // LATIN SMALL LETTER E WITH DOT ABOVE + ẹ: 'e', // LATIN SMALL LETTER E WITH DOT BELOW + ȅ: 'e', // LATIN SMALL LETTER E WITH DOUBLE GRAVE + è: 'e', // LATIN SMALL LETTER E WITH GRAVE + ẻ: 'e', // LATIN SMALL LETTER E WITH HOOK ABOVE + ȇ: 'e', // LATIN SMALL LETTER E WITH INVERTED BREVE + ē: 'e', // LATIN SMALL LETTER E WITH MACRON + ḗ: 'e', // LATIN SMALL LETTER E WITH MACRON AND ACUTE + ḕ: 'e', // LATIN SMALL LETTER E WITH MACRON AND GRAVE + ⱸ: 'e', // LATIN SMALL LETTER E WITH NOTCH + ę: 'e', // LATIN SMALL LETTER E WITH OGONEK + ᶒ: 'e', // LATIN SMALL LETTER E WITH RETROFLEX HOOK + ɇ: 'e', // LATIN SMALL LETTER E WITH STROKE + ẽ: 'e', // LATIN SMALL LETTER E WITH TILDE + ḛ: 'e', // LATIN SMALL LETTER E WITH TILDE BELOW + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER EGYPTOLOGICAL AIN' (ꜥ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER EGYPTOLOGICAL ALEF' (ꜣ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER ENG' (ŋ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER ESH' (ʃ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER ESH WITH CURL' (ʆ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER ESH WITH PALATAL HOOK' (ᶋ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER ESH WITH RETROFLEX HOOK' (ᶘ) + ꝫ: 'et', // LATIN SMALL LETTER ET + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER ETH' (ð) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER EZH' (ʒ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER EZH REVERSED' (ƹ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER EZH WITH CARON' (ǯ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER EZH WITH CURL' (ʓ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER EZH WITH RETROFLEX HOOK' (ᶚ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER EZH WITH TAIL' (ƺ) + ḟ: 'f', // LATIN SMALL LETTER F WITH DOT ABOVE + ƒ: 'f', // LATIN SMALL LETTER F WITH HOOK + ᵮ: 'f', // LATIN SMALL LETTER F WITH MIDDLE TILDE + ᶂ: 'f', // LATIN SMALL LETTER F WITH PALATAL HOOK + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER FENG DIGRAPH' (ʩ) + ǵ: 'g', // LATIN SMALL LETTER G WITH ACUTE + ğ: 'g', // LATIN SMALL LETTER G WITH BREVE + ǧ: 'g', // LATIN SMALL LETTER G WITH CARON + ģ: 'g', // LATIN SMALL LETTER G WITH CEDILLA + ĝ: 'g', // LATIN SMALL LETTER G WITH CIRCUMFLEX + ġ: 'g', // LATIN SMALL LETTER G WITH DOT ABOVE + ɠ: 'g', // LATIN SMALL LETTER G WITH HOOK + ḡ: 'g', // LATIN SMALL LETTER G WITH MACRON + ᶃ: 'g', // LATIN SMALL LETTER G WITH PALATAL HOOK + ǥ: 'g', // LATIN SMALL LETTER G WITH STROKE + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER GAMMA' (ɣ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER GLOTTAL STOP' (ɂ) + ḫ: 'h', // LATIN SMALL LETTER H WITH BREVE BELOW + ȟ: 'h', // LATIN SMALL LETTER H WITH CARON + ḩ: 'h', // LATIN SMALL LETTER H WITH CEDILLA + ĥ: 'h', // LATIN SMALL LETTER H WITH CIRCUMFLEX + ⱨ: 'h', // LATIN SMALL LETTER H WITH DESCENDER + ḧ: 'h', // LATIN SMALL LETTER H WITH DIAERESIS + ḣ: 'h', // LATIN SMALL LETTER H WITH DOT ABOVE + ḥ: 'h', // LATIN SMALL LETTER H WITH DOT BELOW + ɦ: 'h', // LATIN SMALL LETTER H WITH HOOK + ẖ: 'h', // LATIN SMALL LETTER H WITH LINE BELOW + ħ: 'h', // LATIN SMALL LETTER H WITH STROKE + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER HALF H' (ⱶ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER HENG' (ꜧ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER HENG WITH HOOK' (ɧ) + ƕ: 'hv', // LATIN SMALL LETTER HV + í: 'i', // LATIN SMALL LETTER I WITH ACUTE + ĭ: 'i', // LATIN SMALL LETTER I WITH BREVE + ǐ: 'i', // LATIN SMALL LETTER I WITH CARON + î: 'i', // LATIN SMALL LETTER I WITH CIRCUMFLEX + ï: 'i', // LATIN SMALL LETTER I WITH DIAERESIS + ḯ: 'i', // LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE + ị: 'i', // LATIN SMALL LETTER I WITH DOT BELOW + ȉ: 'i', // LATIN SMALL LETTER I WITH DOUBLE GRAVE + ì: 'i', // LATIN SMALL LETTER I WITH GRAVE + ỉ: 'i', // LATIN SMALL LETTER I WITH HOOK ABOVE + ȋ: 'i', // LATIN SMALL LETTER I WITH INVERTED BREVE + ī: 'i', // LATIN SMALL LETTER I WITH MACRON + į: 'i', // LATIN SMALL LETTER I WITH OGONEK + ᶖ: 'i', // LATIN SMALL LETTER I WITH RETROFLEX HOOK + ɨ: 'i', // LATIN SMALL LETTER I WITH STROKE + ĩ: 'i', // LATIN SMALL LETTER I WITH TILDE + ḭ: 'i', // LATIN SMALL LETTER I WITH TILDE BELOW + ꝺ: 'd', // LATIN SMALL LETTER INSULAR D + ꝼ: 'f', // LATIN SMALL LETTER INSULAR F + ᵹ: 'g', // LATIN SMALL LETTER INSULAR G + ꞃ: 'r', // LATIN SMALL LETTER INSULAR R + ꞅ: 's', // LATIN SMALL LETTER INSULAR S + ꞇ: 't', // LATIN SMALL LETTER INSULAR T + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER IOTA' (ɩ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER IOTA WITH STROKE' (ᵼ) + ꝭ: 'is', // LATIN SMALL LETTER IS + ǰ: 'j', // LATIN SMALL LETTER J WITH CARON + ĵ: 'j', // LATIN SMALL LETTER J WITH CIRCUMFLEX + ʝ: 'j', // LATIN SMALL LETTER J WITH CROSSED-TAIL + ɉ: 'j', // LATIN SMALL LETTER J WITH STROKE + ḱ: 'k', // LATIN SMALL LETTER K WITH ACUTE + ǩ: 'k', // LATIN SMALL LETTER K WITH CARON + ķ: 'k', // LATIN SMALL LETTER K WITH CEDILLA + ⱪ: 'k', // LATIN SMALL LETTER K WITH DESCENDER + ꝃ: 'k', // LATIN SMALL LETTER K WITH DIAGONAL STROKE + ḳ: 'k', // LATIN SMALL LETTER K WITH DOT BELOW + ƙ: 'k', // LATIN SMALL LETTER K WITH HOOK + ḵ: 'k', // LATIN SMALL LETTER K WITH LINE BELOW + ᶄ: 'k', // LATIN SMALL LETTER K WITH PALATAL HOOK + ꝁ: 'k', // LATIN SMALL LETTER K WITH STROKE + ꝅ: 'k', // LATIN SMALL LETTER K WITH STROKE AND DIAGONAL STROKE + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER KRA' (ĸ) + ĺ: 'l', // LATIN SMALL LETTER L WITH ACUTE + ƚ: 'l', // LATIN SMALL LETTER L WITH BAR + ɬ: 'l', // LATIN SMALL LETTER L WITH BELT + ľ: 'l', // LATIN SMALL LETTER L WITH CARON + ļ: 'l', // LATIN SMALL LETTER L WITH CEDILLA + ḽ: 'l', // LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW + ȴ: 'l', // LATIN SMALL LETTER L WITH CURL + ḷ: 'l', // LATIN SMALL LETTER L WITH DOT BELOW + ḹ: 'l', // LATIN SMALL LETTER L WITH DOT BELOW AND MACRON + ⱡ: 'l', // LATIN SMALL LETTER L WITH DOUBLE BAR + ꝉ: 'l', // LATIN SMALL LETTER L WITH HIGH STROKE + ḻ: 'l', // LATIN SMALL LETTER L WITH LINE BELOW + ŀ: 'l', // LATIN SMALL LETTER L WITH MIDDLE DOT + ɫ: 'l', // LATIN SMALL LETTER L WITH MIDDLE TILDE + ᶅ: 'l', // LATIN SMALL LETTER L WITH PALATAL HOOK + ɭ: 'l', // LATIN SMALL LETTER L WITH RETROFLEX HOOK + ł: 'l', // LATIN SMALL LETTER L WITH STROKE + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER LAMBDA WITH STROKE' (ƛ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER LEZH' (ɮ) + lj: 'lj', // LATIN SMALL LETTER LJ + ſ: 's', // LATIN SMALL LETTER LONG S + ẜ: 's', // LATIN SMALL LETTER LONG S WITH DIAGONAL STROKE + ẛ: 's', // LATIN SMALL LETTER LONG S WITH DOT ABOVE + ẝ: 's', // LATIN SMALL LETTER LONG S WITH HIGH STROKE + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER LS DIGRAPH' (ʪ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER LUM' (ꝲ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER LZ DIGRAPH' (ʫ) + ḿ: 'm', // LATIN SMALL LETTER M WITH ACUTE + ṁ: 'm', // LATIN SMALL LETTER M WITH DOT ABOVE + ṃ: 'm', // LATIN SMALL LETTER M WITH DOT BELOW + ɱ: 'm', // LATIN SMALL LETTER M WITH HOOK + ᵯ: 'm', // LATIN SMALL LETTER M WITH MIDDLE TILDE + ᶆ: 'm', // LATIN SMALL LETTER M WITH PALATAL HOOK + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER MIDDLE-WELSH LL' (ỻ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER MIDDLE-WELSH V' (ỽ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER MUM' (ꝳ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER N PRECEDED BY APOSTROPHE' (ʼn) + ń: 'n', // LATIN SMALL LETTER N WITH ACUTE + ň: 'n', // LATIN SMALL LETTER N WITH CARON + ņ: 'n', // LATIN SMALL LETTER N WITH CEDILLA + ṋ: 'n', // LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW + ȵ: 'n', // LATIN SMALL LETTER N WITH CURL + ṅ: 'n', // LATIN SMALL LETTER N WITH DOT ABOVE + ṇ: 'n', // LATIN SMALL LETTER N WITH DOT BELOW + ǹ: 'n', // LATIN SMALL LETTER N WITH GRAVE + ɲ: 'n', // LATIN SMALL LETTER N WITH LEFT HOOK + ṉ: 'n', // LATIN SMALL LETTER N WITH LINE BELOW + ƞ: 'n', // LATIN SMALL LETTER N WITH LONG RIGHT LEG + ᵰ: 'n', // LATIN SMALL LETTER N WITH MIDDLE TILDE + ᶇ: 'n', // LATIN SMALL LETTER N WITH PALATAL HOOK + ɳ: 'n', // LATIN SMALL LETTER N WITH RETROFLEX HOOK + ñ: 'n', // LATIN SMALL LETTER N WITH TILDE + nj: 'nj', // LATIN SMALL LETTER NJ + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER NUM' (ꝴ) + ó: 'o', // LATIN SMALL LETTER O WITH ACUTE + ŏ: 'o', // LATIN SMALL LETTER O WITH BREVE + ǒ: 'o', // LATIN SMALL LETTER O WITH CARON + ô: 'o', // LATIN SMALL LETTER O WITH CIRCUMFLEX + ố: 'o', // LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE + ộ: 'o', // LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW + ồ: 'o', // LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE + ổ: 'o', // LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE + ỗ: 'o', // LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE + ö: 'o', // LATIN SMALL LETTER O WITH DIAERESIS + ȫ: 'o', // LATIN SMALL LETTER O WITH DIAERESIS AND MACRON + ȯ: 'o', // LATIN SMALL LETTER O WITH DOT ABOVE + ȱ: 'o', // LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON + ọ: 'o', // LATIN SMALL LETTER O WITH DOT BELOW + ő: 'o', // LATIN SMALL LETTER O WITH DOUBLE ACUTE + ȍ: 'o', // LATIN SMALL LETTER O WITH DOUBLE GRAVE + ò: 'o', // LATIN SMALL LETTER O WITH GRAVE + ỏ: 'o', // LATIN SMALL LETTER O WITH HOOK ABOVE + ơ: 'o', // LATIN SMALL LETTER O WITH HORN + ớ: 'o', // LATIN SMALL LETTER O WITH HORN AND ACUTE + ợ: 'o', // LATIN SMALL LETTER O WITH HORN AND DOT BELOW + ờ: 'o', // LATIN SMALL LETTER O WITH HORN AND GRAVE + ở: 'o', // LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE + ỡ: 'o', // LATIN SMALL LETTER O WITH HORN AND TILDE + ȏ: 'o', // LATIN SMALL LETTER O WITH INVERTED BREVE + ꝋ: 'o', // LATIN SMALL LETTER O WITH LONG STROKE OVERLAY + ꝍ: 'o', // LATIN SMALL LETTER O WITH LOOP + ⱺ: 'o', // LATIN SMALL LETTER O WITH LOW RING INSIDE + ō: 'o', // LATIN SMALL LETTER O WITH MACRON + ṓ: 'o', // LATIN SMALL LETTER O WITH MACRON AND ACUTE + ṑ: 'o', // LATIN SMALL LETTER O WITH MACRON AND GRAVE + ǫ: 'o', // LATIN SMALL LETTER O WITH OGONEK + ǭ: 'o', // LATIN SMALL LETTER O WITH OGONEK AND MACRON + ø: 'o', // LATIN SMALL LETTER O WITH STROKE + ǿ: 'o', // LATIN SMALL LETTER O WITH STROKE AND ACUTE + õ: 'o', // LATIN SMALL LETTER O WITH TILDE + ṍ: 'o', // LATIN SMALL LETTER O WITH TILDE AND ACUTE + ṏ: 'o', // LATIN SMALL LETTER O WITH TILDE AND DIAERESIS + ȭ: 'o', // LATIN SMALL LETTER O WITH TILDE AND MACRON + ƣ: 'oi', // LATIN SMALL LETTER OI + ꝏ: 'oo', // LATIN SMALL LETTER OO + ɛ: 'e', // LATIN SMALL LETTER OPEN E + ᶓ: 'e', // LATIN SMALL LETTER OPEN E WITH RETROFLEX HOOK + ɔ: 'o', // LATIN SMALL LETTER OPEN O + ᶗ: 'o', // LATIN SMALL LETTER OPEN O WITH RETROFLEX HOOK + ȣ: 'ou', // LATIN SMALL LETTER OU + ṕ: 'p', // LATIN SMALL LETTER P WITH ACUTE + ṗ: 'p', // LATIN SMALL LETTER P WITH DOT ABOVE + ꝓ: 'p', // LATIN SMALL LETTER P WITH FLOURISH + ƥ: 'p', // LATIN SMALL LETTER P WITH HOOK + ᵱ: 'p', // LATIN SMALL LETTER P WITH MIDDLE TILDE + ᶈ: 'p', // LATIN SMALL LETTER P WITH PALATAL HOOK + ꝕ: 'p', // LATIN SMALL LETTER P WITH SQUIRREL TAIL + ᵽ: 'p', // LATIN SMALL LETTER P WITH STROKE + ꝑ: 'p', // LATIN SMALL LETTER P WITH STROKE THROUGH DESCENDER + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER PHI' (ɸ) + ꝙ: 'q', // LATIN SMALL LETTER Q WITH DIAGONAL STROKE + ʠ: 'q', // LATIN SMALL LETTER Q WITH HOOK + ɋ: 'q', // LATIN SMALL LETTER Q WITH HOOK TAIL + ꝗ: 'q', // LATIN SMALL LETTER Q WITH STROKE THROUGH DESCENDER + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER QP DIGRAPH' (ȹ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER R ROTUNDA' (ꝛ) + ŕ: 'r', // LATIN SMALL LETTER R WITH ACUTE + ř: 'r', // LATIN SMALL LETTER R WITH CARON + ŗ: 'r', // LATIN SMALL LETTER R WITH CEDILLA + ṙ: 'r', // LATIN SMALL LETTER R WITH DOT ABOVE + ṛ: 'r', // LATIN SMALL LETTER R WITH DOT BELOW + ṝ: 'r', // LATIN SMALL LETTER R WITH DOT BELOW AND MACRON + ȑ: 'r', // LATIN SMALL LETTER R WITH DOUBLE GRAVE + ɾ: 'r', // LATIN SMALL LETTER R WITH FISHHOOK + ᵳ: 'r', // LATIN SMALL LETTER R WITH FISHHOOK AND MIDDLE TILDE + ȓ: 'r', // LATIN SMALL LETTER R WITH INVERTED BREVE + ṟ: 'r', // LATIN SMALL LETTER R WITH LINE BELOW + ɼ: 'r', // LATIN SMALL LETTER R WITH LONG LEG + ᵲ: 'r', // LATIN SMALL LETTER R WITH MIDDLE TILDE + ᶉ: 'r', // LATIN SMALL LETTER R WITH PALATAL HOOK + ɍ: 'r', // LATIN SMALL LETTER R WITH STROKE + ɽ: 'r', // LATIN SMALL LETTER R WITH TAIL + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER RAMS HORN' (ɤ) + ↄ: 'c', // LATIN SMALL LETTER REVERSED C + ꜿ: 'c', // LATIN SMALL LETTER REVERSED C WITH DOT + ɘ: 'e', // LATIN SMALL LETTER REVERSED E + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER REVERSED OPEN E' (ɜ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER REVERSED OPEN E WITH HOOK' (ɝ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER REVERSED OPEN E WITH RETROFLEX HOOK' (ᶔ) + ɿ: 'r', // LATIN SMALL LETTER REVERSED R WITH FISHHOOK + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER RUM' (ꝵ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER RUM ROTUNDA' (ꝝ) + ś: 's', // LATIN SMALL LETTER S WITH ACUTE + ṥ: 's', // LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE + š: 's', // LATIN SMALL LETTER S WITH CARON + ṧ: 's', // LATIN SMALL LETTER S WITH CARON AND DOT ABOVE + ş: 's', // LATIN SMALL LETTER S WITH CEDILLA + ŝ: 's', // LATIN SMALL LETTER S WITH CIRCUMFLEX + ș: 's', // LATIN SMALL LETTER S WITH COMMA BELOW + ṡ: 's', // LATIN SMALL LETTER S WITH DOT ABOVE + ṣ: 's', // LATIN SMALL LETTER S WITH DOT BELOW + ṩ: 's', // LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE + ʂ: 's', // LATIN SMALL LETTER S WITH HOOK + ᵴ: 's', // LATIN SMALL LETTER S WITH MIDDLE TILDE + ᶊ: 's', // LATIN SMALL LETTER S WITH PALATAL HOOK + ȿ: 's', // LATIN SMALL LETTER S WITH SWASH TAIL + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER SALTILLO' (ꞌ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER SCHWA' (ə) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER SCHWA WITH HOOK' (ɚ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER SCHWA WITH RETROFLEX HOOK' (ᶕ) + ɡ: 'g', // LATIN SMALL LETTER SCRIPT G + ß: 'ss', // LATIN SMALL LETTER SHARP S + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER SIDEWAYS DIAERESIZED U' (ᴞ) + ᴑ: 'o', // LATIN SMALL LETTER SIDEWAYS O + ᴓ: 'o', // LATIN SMALL LETTER SIDEWAYS O WITH STROKE + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER SIDEWAYS OPEN O' (ᴒ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER SIDEWAYS TURNED M' (ᴟ) + ᴝ: 'u', // LATIN SMALL LETTER SIDEWAYS U + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER SQUAT REVERSED ESH' (ʅ) + ť: 't', // LATIN SMALL LETTER T WITH CARON + ţ: 't', // LATIN SMALL LETTER T WITH CEDILLA + ṱ: 't', // LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW + ț: 't', // LATIN SMALL LETTER T WITH COMMA BELOW + ȶ: 't', // LATIN SMALL LETTER T WITH CURL + ẗ: 't', // LATIN SMALL LETTER T WITH DIAERESIS + ⱦ: 't', // LATIN SMALL LETTER T WITH DIAGONAL STROKE + ṫ: 't', // LATIN SMALL LETTER T WITH DOT ABOVE + ṭ: 't', // LATIN SMALL LETTER T WITH DOT BELOW + ƭ: 't', // LATIN SMALL LETTER T WITH HOOK + ṯ: 't', // LATIN SMALL LETTER T WITH LINE BELOW + ᵵ: 't', // LATIN SMALL LETTER T WITH MIDDLE TILDE + ƫ: 't', // LATIN SMALL LETTER T WITH PALATAL HOOK + ʈ: 't', // LATIN SMALL LETTER T WITH RETROFLEX HOOK + ŧ: 't', // LATIN SMALL LETTER T WITH STROKE + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER TAILLESS PHI' (ⱷ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER TC DIGRAPH WITH CURL' (ʨ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER TESH DIGRAPH' (ʧ) + ᵺ: 'th', // LATIN SMALL LETTER TH WITH STRIKETHROUGH + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER THORN' (þ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER THORN WITH STROKE' (ꝥ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER THORN WITH STROKE THROUGH DESCENDER' (ꝧ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER TONE FIVE' (ƽ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER TONE SIX' (ƅ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER TONE TWO' (ƨ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER TOP HALF O' (ᴖ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER TRESILLO' (ꜫ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER TS DIGRAPH' (ʦ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER TUM' (ꝷ) + ɐ: 'a', // LATIN SMALL LETTER TURNED A + ᴂ: 'ae', // LATIN SMALL LETTER TURNED AE + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER TURNED ALPHA' (ɒ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER TURNED DELTA' (ƍ) + ǝ: 'e', // LATIN SMALL LETTER TURNED E + ᵷ: 'g', // LATIN SMALL LETTER TURNED G + ɥ: 'h', // LATIN SMALL LETTER TURNED H + ʮ: 'h', // LATIN SMALL LETTER TURNED H WITH FISHHOOK + ʯ: 'h', // LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL + ᴉ: 'i', // LATIN SMALL LETTER TURNED I + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER TURNED INSULAR G' (ꝿ) + ʞ: 'k', // LATIN SMALL LETTER TURNED K + ꞁ: 'l', // LATIN SMALL LETTER TURNED L + ɯ: 'm', // LATIN SMALL LETTER TURNED M + ɰ: 'm', // LATIN SMALL LETTER TURNED M WITH LONG LEG + ᴔ: 'oe', // LATIN SMALL LETTER TURNED OE + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER TURNED OPEN E' (ᴈ) + ɹ: 'r', // LATIN SMALL LETTER TURNED R + ɻ: 'r', // LATIN SMALL LETTER TURNED R WITH HOOK + ɺ: 'r', // LATIN SMALL LETTER TURNED R WITH LONG LEG + ⱹ: 'r', // LATIN SMALL LETTER TURNED R WITH TAIL + ʇ: 't', // LATIN SMALL LETTER TURNED T + ʌ: 'v', // LATIN SMALL LETTER TURNED V + ʍ: 'w', // LATIN SMALL LETTER TURNED W + ʎ: 'y', // LATIN SMALL LETTER TURNED Y + ꜩ: 'tz', // LATIN SMALL LETTER TZ + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER U BAR' (ʉ) + ú: 'u', // LATIN SMALL LETTER U WITH ACUTE + ŭ: 'u', // LATIN SMALL LETTER U WITH BREVE + ǔ: 'u', // LATIN SMALL LETTER U WITH CARON + û: 'u', // LATIN SMALL LETTER U WITH CIRCUMFLEX + ṷ: 'u', // LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW + ü: 'u', // LATIN SMALL LETTER U WITH DIAERESIS + ǘ: 'u', // LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE + ǚ: 'u', // LATIN SMALL LETTER U WITH DIAERESIS AND CARON + ǜ: 'u', // LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE + ǖ: 'u', // LATIN SMALL LETTER U WITH DIAERESIS AND MACRON + ṳ: 'u', // LATIN SMALL LETTER U WITH DIAERESIS BELOW + ụ: 'u', // LATIN SMALL LETTER U WITH DOT BELOW + ű: 'u', // LATIN SMALL LETTER U WITH DOUBLE ACUTE + ȕ: 'u', // LATIN SMALL LETTER U WITH DOUBLE GRAVE + ù: 'u', // LATIN SMALL LETTER U WITH GRAVE + ủ: 'u', // LATIN SMALL LETTER U WITH HOOK ABOVE + ư: 'u', // LATIN SMALL LETTER U WITH HORN + ứ: 'u', // LATIN SMALL LETTER U WITH HORN AND ACUTE + ự: 'u', // LATIN SMALL LETTER U WITH HORN AND DOT BELOW + ừ: 'u', // LATIN SMALL LETTER U WITH HORN AND GRAVE + ử: 'u', // LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE + ữ: 'u', // LATIN SMALL LETTER U WITH HORN AND TILDE + ȗ: 'u', // LATIN SMALL LETTER U WITH INVERTED BREVE + ū: 'u', // LATIN SMALL LETTER U WITH MACRON + ṻ: 'u', // LATIN SMALL LETTER U WITH MACRON AND DIAERESIS + ų: 'u', // LATIN SMALL LETTER U WITH OGONEK + ᶙ: 'u', // LATIN SMALL LETTER U WITH RETROFLEX HOOK + ů: 'u', // LATIN SMALL LETTER U WITH RING ABOVE + ũ: 'u', // LATIN SMALL LETTER U WITH TILDE + ṹ: 'u', // LATIN SMALL LETTER U WITH TILDE AND ACUTE + ṵ: 'u', // LATIN SMALL LETTER U WITH TILDE BELOW + ᵫ: 'ue', // LATIN SMALL LETTER UE + ꝸ: 'um', // LATIN SMALL LETTER UM + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER UPSILON' (ʊ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER UPSILON WITH STROKE' (ᵿ) + ⱴ: 'v', // LATIN SMALL LETTER V WITH CURL + ꝟ: 'v', // LATIN SMALL LETTER V WITH DIAGONAL STROKE + ṿ: 'v', // LATIN SMALL LETTER V WITH DOT BELOW + ʋ: 'v', // LATIN SMALL LETTER V WITH HOOK + ᶌ: 'v', // LATIN SMALL LETTER V WITH PALATAL HOOK + ⱱ: 'v', // LATIN SMALL LETTER V WITH RIGHT HOOK + ṽ: 'v', // LATIN SMALL LETTER V WITH TILDE + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER VEND' (ꝩ) + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER VISIGOTHIC Z' (ꝣ) + ꝡ: 'vy', // LATIN SMALL LETTER VY + ẃ: 'w', // LATIN SMALL LETTER W WITH ACUTE + ŵ: 'w', // LATIN SMALL LETTER W WITH CIRCUMFLEX + ẅ: 'w', // LATIN SMALL LETTER W WITH DIAERESIS + ẇ: 'w', // LATIN SMALL LETTER W WITH DOT ABOVE + ẉ: 'w', // LATIN SMALL LETTER W WITH DOT BELOW + ẁ: 'w', // LATIN SMALL LETTER W WITH GRAVE + ⱳ: 'w', // LATIN SMALL LETTER W WITH HOOK + ẘ: 'w', // LATIN SMALL LETTER W WITH RING ABOVE + ẍ: 'x', // LATIN SMALL LETTER X WITH DIAERESIS + ẋ: 'x', // LATIN SMALL LETTER X WITH DOT ABOVE + ᶍ: 'x', // LATIN SMALL LETTER X WITH PALATAL HOOK + ý: 'y', // LATIN SMALL LETTER Y WITH ACUTE + ŷ: 'y', // LATIN SMALL LETTER Y WITH CIRCUMFLEX + ÿ: 'y', // LATIN SMALL LETTER Y WITH DIAERESIS + ẏ: 'y', // LATIN SMALL LETTER Y WITH DOT ABOVE + ỵ: 'y', // LATIN SMALL LETTER Y WITH DOT BELOW + ỳ: 'y', // LATIN SMALL LETTER Y WITH GRAVE + ƴ: 'y', // LATIN SMALL LETTER Y WITH HOOK + ỷ: 'y', // LATIN SMALL LETTER Y WITH HOOK ABOVE + ỿ: 'y', // LATIN SMALL LETTER Y WITH LOOP + ȳ: 'y', // LATIN SMALL LETTER Y WITH MACRON + ẙ: 'y', // LATIN SMALL LETTER Y WITH RING ABOVE + ɏ: 'y', // LATIN SMALL LETTER Y WITH STROKE + ỹ: 'y', // LATIN SMALL LETTER Y WITH TILDE + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LETTER YOGH' (ȝ) + ź: 'z', // LATIN SMALL LETTER Z WITH ACUTE + ž: 'z', // LATIN SMALL LETTER Z WITH CARON + ẑ: 'z', // LATIN SMALL LETTER Z WITH CIRCUMFLEX + ʑ: 'z', // LATIN SMALL LETTER Z WITH CURL + ⱬ: 'z', // LATIN SMALL LETTER Z WITH DESCENDER + ż: 'z', // LATIN SMALL LETTER Z WITH DOT ABOVE + ẓ: 'z', // LATIN SMALL LETTER Z WITH DOT BELOW + ȥ: 'z', // LATIN SMALL LETTER Z WITH HOOK + ẕ: 'z', // LATIN SMALL LETTER Z WITH LINE BELOW + ᵶ: 'z', // LATIN SMALL LETTER Z WITH MIDDLE TILDE + ᶎ: 'z', // LATIN SMALL LETTER Z WITH PALATAL HOOK + ʐ: 'z', // LATIN SMALL LETTER Z WITH RETROFLEX HOOK + ƶ: 'z', // LATIN SMALL LETTER Z WITH STROKE + ɀ: 'z', // LATIN SMALL LETTER Z WITH SWASH TAIL + ff: 'ff', // LATIN SMALL LIGATURE FF + ffi: 'ffi', // LATIN SMALL LIGATURE FFI + ffl: 'ffl', // LATIN SMALL LIGATURE FFL + fi: 'fi', // LATIN SMALL LIGATURE FI + fl: 'fl', // LATIN SMALL LIGATURE FL + ij: 'ij', // LATIN SMALL LIGATURE IJ + // CANNOT FIND APPROXIMATION FOR 'LATIN SMALL LIGATURE LONG S T' (ſt) + œ: 'oe', // LATIN SMALL LIGATURE OE + st: 'st', // LATIN SMALL LIGATURE ST + ₐ: 'a', // LATIN SUBSCRIPT SMALL LETTER A + ₑ: 'e', // LATIN SUBSCRIPT SMALL LETTER E + ᵢ: 'i', // LATIN SUBSCRIPT SMALL LETTER I + ⱼ: 'j', // LATIN SUBSCRIPT SMALL LETTER J + ₒ: 'o', // LATIN SUBSCRIPT SMALL LETTER O + ᵣ: 'r', // LATIN SUBSCRIPT SMALL LETTER R + // CANNOT FIND APPROXIMATION FOR 'LATIN SUBSCRIPT SMALL LETTER SCHWA' (ₔ) + ᵤ: 'u', // LATIN SUBSCRIPT SMALL LETTER U + ᵥ: 'v', // LATIN SUBSCRIPT SMALL LETTER V + ₓ: 'x' // LATIN SUBSCRIPT SMALL LETTER X +}; + +export function map(x) { + return latinMap[x] || x; +} + +export function latinise(input) { + return input.replace(/[^A-Za-z0-9]/g, map); +} diff --git a/app/utils/url.js b/app/utils/url.js index dd684024e..7199ef27e 100644 --- a/app/utils/url.js +++ b/app/utils/url.js @@ -1,6 +1,8 @@ // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. +import {latinise} from './latinise.js'; + const ytRegex = /(?:http|https):\/\/(?:www\.|m\.)?(?:(?:youtube\.com\/(?:(?:v\/)|(?:(?:watch|embed\/watch)(?:\/|.*v=))|(?:embed\/)|(?:user\/[^/]+\/u\/[0-9]\/)))|(?:youtu\.be\/))([^#&?]*)/; const imgRegex = /.+\/(.+\.(?:jpg|gif|bmp|png|jpeg))(?:\?.*)?$/i; @@ -56,3 +58,20 @@ export function normalizeProtocol(url) { const protocol = url.substring(0, index); return protocol.toLowerCase() + url.substring(index); } + +export function getShortenedURL(url = '', getLength = 27) { + if (url.length > 35) { + const subLength = getLength - 14; + return url.substring(0, 10) + '...' + url.substring(url.length - subLength, url.length) + '/'; + } + return url + '/'; +} + +export function cleanUpUrlable(input) { + var cleaned = latinise(input); + cleaned = cleaned.trim().replace(/-/g, ' ').replace(/[^\w\s]/gi, '').toLowerCase().replace(/\s/g, '-'); + cleaned = cleaned.replace(/-{2,}/, '-'); + cleaned = cleaned.replace(/^-+/, ''); + cleaned = cleaned.replace(/-+$/, ''); + return cleaned; +} diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 3c1c5f326..7002c9fe3 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -1918,6 +1918,7 @@ "mobile.channel_info.delete_failed": "We couldn't delete the channel {displayName}. Please check your connection and try again.", "mobile.channel_info.privateChannel": "Private Channel", "mobile.channel_info.publicChannel": "Public Channel", + "mobile.channel_info.edit": "Edit Channel", "mobile.channel_list.alertMessageLeaveChannel": "Are you sure you want to leave the {term} {name}?", "mobile.channel_list.alertNo": "No", "mobile.channel_list.alertTitleLeaveChannel": "Leave {term}", @@ -1954,6 +1955,7 @@ "mobile.components.select_server_view.enterServerUrl": "Enter Server URL", "mobile.components.select_server_view.proceed": "Proceed", "mobile.components.select_server_view.siteUrlPlaceholder": "https://mattermost.example.com", + "mobile.edit_channel": "Save", "mobile.create_channel": "Create", "mobile.create_channel.private": "New Private Channel", "mobile.create_channel.public": "New Public Channel", @@ -2061,6 +2063,13 @@ "mobile.post.retry": "Refresh", "mobile.post_info.add_reaction": "Add Reaction", "mobile.post_info.copy_post": "Copy Post", + "mobile.rename_channel.display_name_required": "Channel name is required", + "mobile.rename_channel.display_name_maxLength": "Channel name must be less than {maxLength, number} characters", + "mobile.rename_channel.display_name_minLength": "Channel name must be {minLength, number} or more characters", + "mobile.rename_channel.name_required": "URL is required", + "mobile.rename_channel.name_maxLength": "URL must be less than {maxLength, number} characters", + "mobile.rename_channel.name_minLength": "URL must be {minLength, number} or more characters", + "mobile.rename_channel.name_lowercase": "URL be lowercase alphanumeric characters", "mobile.request.invalid_response": "Received invalid response from the server.", "mobile.retry_message": "Refreshing messages failed. Pull up to try again.", "mobile.routes.channelInfo": "Info",