mirror of
https://github.com/discourse/discourse.git
synced 2025-03-06 11:19:51 +00:00
FEATURE: Allow plugins to add custom emoji translations
FIX: buildTranslationTree was erroring when translations overlapped (ie. ":-)" and ":-))") FIX: emoji translations wasn't working properly when translations overlapped
This commit is contained in:
parent
88e861e895
commit
95e5f8380d
@ -1,32 +1,5 @@
|
|||||||
export const emojis = <%= Emoji.standard.map(&:name).flatten.inspect %>;
|
export const emojis = <%= Emoji.standard.map(&:name).flatten.inspect %>;
|
||||||
export const tonableEmojis = <%= Emoji.tonable_emojis.flatten.inspect %>;
|
export const tonableEmojis = <%= Emoji.tonable_emojis.flatten.inspect %>;
|
||||||
export const aliases = <%= Emoji.aliases.inspect.gsub("=>", ":") %>;
|
export const aliases = <%= Emoji.aliases.inspect.gsub("=>", ":") %>;
|
||||||
export const searchAliases = <%= Emoji.searchAliases.inspect.gsub("=>", ":") %>;
|
export const searchAliases = <%= Emoji.search_aliases.inspect.gsub("=>", ":") %>;
|
||||||
export const translations = {
|
export const translations = <%= Emoji.translations.inspect.gsub("=>", ":") %>;
|
||||||
':)' : 'slight_smile',
|
|
||||||
':-)' : 'slight_smile',
|
|
||||||
'^_^' : 'slight_smile',
|
|
||||||
'^__^' : 'slight_smile',
|
|
||||||
':(' : 'frowning',
|
|
||||||
':-(' : 'frowning',
|
|
||||||
';)' : 'wink',
|
|
||||||
';-)' : 'wink',
|
|
||||||
':\'(' : 'cry',
|
|
||||||
':\'-(': 'cry',
|
|
||||||
':-\'(': 'cry',
|
|
||||||
':p' : 'stuck_out_tongue',
|
|
||||||
':P' : 'stuck_out_tongue',
|
|
||||||
':-P' : 'stuck_out_tongue',
|
|
||||||
':O' : 'open_mouth',
|
|
||||||
':-O' : 'open_mouth',
|
|
||||||
':D' : 'smiley',
|
|
||||||
':-D' : 'smiley',
|
|
||||||
':|' : 'expressionless',
|
|
||||||
':-|' : 'expressionless',
|
|
||||||
':/' : 'confused',
|
|
||||||
'8-)' : 'sunglasses',
|
|
||||||
";P" : 'stuck_out_tongue_winking_eye',
|
|
||||||
";-P" : 'stuck_out_tongue_winking_eye',
|
|
||||||
":$" : 'blush',
|
|
||||||
":-$" : 'blush'
|
|
||||||
};
|
|
||||||
|
@ -9,22 +9,18 @@ let translationTree = null;
|
|||||||
// We build a data structure that allows us to quickly
|
// We build a data structure that allows us to quickly
|
||||||
// search through our N next chars to see if any match
|
// search through our N next chars to see if any match
|
||||||
// one of our alias emojis.
|
// one of our alias emojis.
|
||||||
//
|
|
||||||
function buildTranslationTree() {
|
function buildTranslationTree() {
|
||||||
let tree = [];
|
let tree = [];
|
||||||
let lastNode;
|
let lastNode;
|
||||||
|
|
||||||
Object.keys(translations).forEach(function(key) {
|
Object.keys(translations).forEach(key => {
|
||||||
let i;
|
|
||||||
let node = tree;
|
let node = tree;
|
||||||
|
|
||||||
for (i = 0; i < key.length; i++) {
|
for (let i = 0; i < key.length; i++) {
|
||||||
let code = key.charCodeAt(i);
|
let code = key.charCodeAt(i);
|
||||||
let j;
|
|
||||||
|
|
||||||
let found = false;
|
let found = false;
|
||||||
|
|
||||||
for (j = 0; j < node.length; j++) {
|
for (let j = 0; j < node.length; j++) {
|
||||||
if (node[j][0] === code) {
|
if (node[j][0] === code) {
|
||||||
node = node[j][1];
|
node = node[j][1];
|
||||||
found = true;
|
found = true;
|
||||||
@ -33,7 +29,7 @@ function buildTranslationTree() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
// token, children, value
|
// code, children, value
|
||||||
let tmp = [code, []];
|
let tmp = [code, []];
|
||||||
node.push(tmp);
|
node.push(tmp);
|
||||||
lastNode = tmp;
|
lastNode = tmp;
|
||||||
@ -41,7 +37,7 @@ function buildTranslationTree() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lastNode[1] = translations[key];
|
lastNode[2] = translations[key];
|
||||||
});
|
});
|
||||||
|
|
||||||
return tree;
|
return tree;
|
||||||
@ -121,28 +117,22 @@ function getEmojiTokenByName(name, state) {
|
|||||||
function getEmojiTokenByTranslation(content, pos, state) {
|
function getEmojiTokenByTranslation(content, pos, state) {
|
||||||
translationTree = translationTree || buildTranslationTree();
|
translationTree = translationTree || buildTranslationTree();
|
||||||
|
|
||||||
let currentTree = translationTree;
|
let t = translationTree;
|
||||||
|
|
||||||
let i;
|
|
||||||
let search = true;
|
|
||||||
let found = false;
|
|
||||||
let start = pos;
|
let start = pos;
|
||||||
|
let found = null;
|
||||||
|
|
||||||
while (search) {
|
while (t.length > 0 && pos < content.length) {
|
||||||
search = false;
|
|
||||||
let code = content.charCodeAt(pos);
|
let code = content.charCodeAt(pos);
|
||||||
|
|
||||||
for (i = 0; i < currentTree.length; i++) {
|
for (let i = 0; i < t.length; i++) {
|
||||||
if (currentTree[i][0] === code) {
|
if (t[i][0] === code) {
|
||||||
currentTree = currentTree[i][1];
|
found = t[i][2];
|
||||||
pos++;
|
t = t[i][1];
|
||||||
search = true;
|
|
||||||
if (typeof currentTree === "string") {
|
|
||||||
found = currentTree;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pos++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
@ -174,17 +164,9 @@ function getEmojiTokenByTranslation(content, pos, state) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyEmoji(
|
function applyEmoji(content, state, emojiUnicodeReplacer, enableShortcuts, inlineEmoji) {
|
||||||
content,
|
|
||||||
state,
|
|
||||||
emojiUnicodeReplacer,
|
|
||||||
enableShortcuts,
|
|
||||||
inlineEmoji
|
|
||||||
) {
|
|
||||||
let i;
|
|
||||||
let result = null;
|
let result = null;
|
||||||
let contentToken = null;
|
let contentToken = null;
|
||||||
|
|
||||||
let start = 0;
|
let start = 0;
|
||||||
|
|
||||||
if (emojiUnicodeReplacer) {
|
if (emojiUnicodeReplacer) {
|
||||||
@ -193,10 +175,10 @@ function applyEmoji(
|
|||||||
|
|
||||||
let endToken = content.length;
|
let endToken = content.length;
|
||||||
|
|
||||||
for (i = 0; i < content.length - 1; i++) {
|
for (let i = 0; i < content.length - 1; i++) {
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
const emojiName = getEmojiName(content, i, state, inlineEmoji);
|
|
||||||
let token = null;
|
let token = null;
|
||||||
|
const emojiName = getEmojiName(content, i, state, inlineEmoji);
|
||||||
|
|
||||||
if (emojiName) {
|
if (emojiName) {
|
||||||
token = getEmojiTokenByName(emojiName, state);
|
token = getEmojiTokenByName(emojiName, state);
|
||||||
@ -207,8 +189,7 @@ function applyEmoji(
|
|||||||
|
|
||||||
if (enableShortcuts && !token) {
|
if (enableShortcuts && !token) {
|
||||||
// handle aliases (note: we can't do this in inline cause ; is not a split point)
|
// handle aliases (note: we can't do this in inline cause ; is not a split point)
|
||||||
//
|
const info = getEmojiTokenByTranslation(content, i, state);
|
||||||
let info = getEmojiTokenByTranslation(content, i, state);
|
|
||||||
|
|
||||||
if (info) {
|
if (info) {
|
||||||
offset = info.pos - i;
|
offset = info.pos - i;
|
||||||
@ -225,7 +206,8 @@ function applyEmoji(
|
|||||||
}
|
}
|
||||||
|
|
||||||
result.push(token);
|
result.push(token);
|
||||||
endToken = start = i + offset;
|
i += offset;
|
||||||
|
endToken = start = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,10 +25,14 @@ class Emoji
|
|||||||
Discourse.cache.fetch(cache_key("aliases_emojis")) { db['aliases'] }
|
Discourse.cache.fetch(cache_key("aliases_emojis")) { db['aliases'] }
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.searchAliases
|
def self.search_aliases
|
||||||
Discourse.cache.fetch(cache_key("search_aliases_emojis")) { db['searchAliases'] }
|
Discourse.cache.fetch(cache_key("search_aliases_emojis")) { db['searchAliases'] }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.translations
|
||||||
|
Discourse.cache.fetch(cache_key("translations_emojis")) { load_translations }
|
||||||
|
end
|
||||||
|
|
||||||
def self.custom
|
def self.custom
|
||||||
Discourse.cache.fetch(cache_key("custom_emojis")) { load_custom }
|
Discourse.cache.fetch(cache_key("custom_emojis")) { load_custom }
|
||||||
end
|
end
|
||||||
@ -63,13 +67,13 @@ class Emoji
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.clear_cache
|
def self.clear_cache
|
||||||
%w{custom standard aliases search_aliases all tonable}.each do |key|
|
%w{custom standard aliases search_aliases translations all tonable}.each do |key|
|
||||||
Discourse.cache.delete(cache_key("#{key}_emojis"))
|
Discourse.cache.delete(cache_key("#{key}_emojis"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.db_file
|
def self.db_file
|
||||||
"#{Rails.root}/lib/emoji/db.json"
|
@db_file ||= "#{Rails.root}/lib/emoji/db.json"
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.db
|
def self.db
|
||||||
@ -101,6 +105,10 @@ class Emoji
|
|||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.load_translations
|
||||||
|
db["translations"].merge(Plugin::CustomEmoji.translations)
|
||||||
|
end
|
||||||
|
|
||||||
def self.base_directory
|
def self.base_directory
|
||||||
"public#{base_url}"
|
"public#{base_url}"
|
||||||
end
|
end
|
||||||
@ -117,9 +125,8 @@ class Emoji
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.unicode_replacements
|
def self.unicode_replacements
|
||||||
return @unicode_replacements if @unicode_replacements
|
@unicode_replacements ||= begin
|
||||||
|
replacements = {}
|
||||||
@unicode_replacements = {}
|
|
||||||
is_tonable_emojis = Emoji.tonable_emojis
|
is_tonable_emojis = Emoji.tonable_emojis
|
||||||
fitzpatrick_scales = FITZPATRICK_SCALE.map { |scale| scale.to_i(16) }
|
fitzpatrick_scales = FITZPATRICK_SCALE.map { |scale| scale.to_i(16) }
|
||||||
|
|
||||||
@ -130,22 +137,23 @@ class Emoji
|
|||||||
code = replacement_code(e['code'])
|
code = replacement_code(e['code'])
|
||||||
next unless code
|
next unless code
|
||||||
|
|
||||||
@unicode_replacements[code] = name
|
replacements[code] = name
|
||||||
if is_tonable_emojis.include?(name)
|
if is_tonable_emojis.include?(name)
|
||||||
fitzpatrick_scales.each_with_index do |scale, index|
|
fitzpatrick_scales.each_with_index do |scale, index|
|
||||||
toned_code = code.codepoints.insert(1, scale).pack("U*".freeze)
|
toned_code = code.codepoints.insert(1, scale).pack("U*".freeze)
|
||||||
@unicode_replacements[toned_code] = "#{name}:t#{index + 2}"
|
replacements[toned_code] = "#{name}:t#{index + 2}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@unicode_replacements["\u{2639}"] = 'frowning'
|
replacements["\u{2639}"] = 'frowning'
|
||||||
@unicode_replacements["\u{263A}"] = 'slight_smile'
|
replacements["\u{263A}"] = 'slight_smile'
|
||||||
@unicode_replacements["\u{263B}"] = 'slight_smile'
|
replacements["\u{263B}"] = 'slight_smile'
|
||||||
@unicode_replacements["\u{2661}"] = 'heart'
|
replacements["\u{2661}"] = 'heart'
|
||||||
@unicode_replacements["\u{2665}"] = 'heart'
|
replacements["\u{2665}"] = 'heart'
|
||||||
|
|
||||||
@unicode_replacements
|
replacements
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.unicode_unescape(string)
|
def self.unicode_unescape(string)
|
||||||
|
@ -7115,5 +7115,33 @@
|
|||||||
"cry": [
|
"cry": [
|
||||||
"sob"
|
"sob"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"translations": {
|
||||||
|
":)" : "slight_smile",
|
||||||
|
":-)" : "slight_smile",
|
||||||
|
"^_^" : "slight_smile",
|
||||||
|
"^__^": "slight_smile",
|
||||||
|
":(" : "frowning",
|
||||||
|
":-(" : "frowning",
|
||||||
|
";)" : "wink",
|
||||||
|
";-)" : "wink",
|
||||||
|
":'(" : "cry",
|
||||||
|
":'-(": "cry",
|
||||||
|
":-'(": "cry",
|
||||||
|
":p" : "stuck_out_tongue",
|
||||||
|
":P" : "stuck_out_tongue",
|
||||||
|
":-P" : "stuck_out_tongue",
|
||||||
|
":O" : "open_mouth",
|
||||||
|
":-O" : "open_mouth",
|
||||||
|
":D" : "smiley",
|
||||||
|
":-D" : "smiley",
|
||||||
|
":|" : "expressionless",
|
||||||
|
":-|" : "expressionless",
|
||||||
|
":/" : "confused",
|
||||||
|
"8-)" : "sunglasses",
|
||||||
|
";P" : "stuck_out_tongue_winking_eye",
|
||||||
|
";-P" : "stuck_out_tongue_winking_eye",
|
||||||
|
":$" : "blush",
|
||||||
|
":-$" : "blush"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,6 +16,15 @@ class Plugin::CustomEmoji
|
|||||||
@@cache_key = Digest::SHA1.hexdigest(cache_key + name)[0..10]
|
@@cache_key = Digest::SHA1.hexdigest(cache_key + name)[0..10]
|
||||||
emojis[name] = url
|
emojis[name] = url
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.translations
|
||||||
|
@@translations ||= {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.translate(from, to)
|
||||||
|
@@cache_key = Digest::SHA1.hexdigest(cache_key + from)[0..10]
|
||||||
|
translations[from] = to
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Plugin::Instance
|
class Plugin::Instance
|
||||||
@ -429,6 +438,10 @@ class Plugin::Instance
|
|||||||
Plugin::CustomEmoji.register(name, url)
|
Plugin::CustomEmoji.register(name, url)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def translate_emoji(from, to)
|
||||||
|
Plugin::CustomEmoji.translate(from, to)
|
||||||
|
end
|
||||||
|
|
||||||
def automatic_assets
|
def automatic_assets
|
||||||
css = styles.join("\n")
|
css = styles.join("\n")
|
||||||
js = javascripts.join("\n")
|
js = javascripts.join("\n")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user