Rails 非拉丁字符的友好 ID
Rails friendly id with non-Latin characters
我有一个模型,我将其友好 ID 用作 slug:
extend FriendlyId
friendly_id :slug_candidates, :use => :scoped, :scope => :account
def slug_candidates
:title_and_sequence
end
def title_and_sequence
slug = normalize_friendly_id(title)
:
# some login to add sequence in case of collision
:
end
我的问题是,当我使用非拉丁字符(阿拉伯语、希伯来语等)时,我得到一个空的 slug。
有什么好用又简单的解决办法吗?
更新
为了让我的问题更清楚,我希望拥有与 WordPress 相同的行为,这意味着:
+--------------------+----------------------------------------------------+
| Title | url |
+--------------------+----------------------------------------------------+
| Hello World!! | /hello-world |
+--------------------+----------------------------------------------------+
| Helló Világ | /hello-vilag |
+--------------------+----------------------------------------------------+
| שלום עולם | /%D7%A9%D7%9C%D7%95%D7%9D-%D7%A2%D7%95%D7%9C%D7%9D |
+--------------------+----------------------------------------------------+
| مرحبا | %D9%85%D8%B1%D8%AD%D8%A8%D8%A7 |
+--------------------+----------------------------------------------------+
(阿拉伯语和希伯来语都在现代浏览器中被翻译成原始和可读的字符)。
有一个 Rails API 方法 transliterate
使用示例:
transliterate('Ãrøskøbing')
# => "AEroskobing"
默认情况下,它仅支持基于拉丁语的语言和俄语,但您应该也能找到其他字母表的规则(如链接文档中所述)
编辑
要实现与 wordpress 相同的行为,您可以简单地使用 url 编码,如下例所示
URI::encode('שלום') => "%D7%A9%D7%9C%D7%95%D7%9D"
感谢@michalszyndel 的笔记和想法,我得到了以下解决方案,希望对更多人有所帮助。
首先,如何在 slug 中制作 non-unicode 个字符:
extend FriendlyId
friendly_id :slug_candidates, :use => :scoped, :scope => :account
def slug_candidates
:title_and_sequence
end
def title_and_sequence
# This line switch all special chars to its unicode
title_unicode = heb_to_unicode(title)
slug = normalize_friendly_id(title_unicode)
:
# some login to add sequence in case of collision
# and whatever you need from your slug
:
end
def heb_to_unicode(str)
heb_chars = 'אבגדהוזחטיכךלמםנןסעפףצץקרשת'
heb_map = {}
heb_chars.split("").each {|c| heb_map.merge!({c => URI::encode(c)})}
# This regex replace all Hebrew letters to their unicode representation
heb_re = Regexp.new(heb_map.keys.map { |x| Regexp.escape(x) }.join('|'))
return str.gsub(heb_re, heb_map)
end
我还需要修改 normalize_friendly_id
以避免它摆脱 %
。
我简单地获取了 parameterize 方法的代码并将 %
添加到正则表达式中:
def normalize_friendly_id(string)
# replace accented chars with their ascii equivalents
parameterized_string = I18n.transliterate(string)
sep = '-'
# Turn unwanted chars into the separator
# We permit % in order to allow unicode in slug
parameterized_string.gsub!(/[^a-zA-Z0-9\-_\%]+/, sep)
unless sep.nil? || sep.empty?
re_sep = Regexp.escape(sep)
# No more than one of the separator in a row.
parameterized_string.gsub!(/#{re_sep}{2,}/, sep)
# Remove leading/trailing separator.
parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/, '')
end
parameterized_string.downcase
end
现在,如果我用标题 שלום
保存模型,它的 slug 将保存为 %D7%A9%D7%9C%D7%95%D7%9D
。
为了使用 friendly
方法找到实例,我需要执行以下操作:
id = URI::encode(params[:id]).downcase
Page.friendly.find(id)
我有一个模型,我将其友好 ID 用作 slug:
extend FriendlyId
friendly_id :slug_candidates, :use => :scoped, :scope => :account
def slug_candidates
:title_and_sequence
end
def title_and_sequence
slug = normalize_friendly_id(title)
:
# some login to add sequence in case of collision
:
end
我的问题是,当我使用非拉丁字符(阿拉伯语、希伯来语等)时,我得到一个空的 slug。 有什么好用又简单的解决办法吗?
更新
为了让我的问题更清楚,我希望拥有与 WordPress 相同的行为,这意味着:
+--------------------+----------------------------------------------------+
| Title | url |
+--------------------+----------------------------------------------------+
| Hello World!! | /hello-world |
+--------------------+----------------------------------------------------+
| Helló Világ | /hello-vilag |
+--------------------+----------------------------------------------------+
| שלום עולם | /%D7%A9%D7%9C%D7%95%D7%9D-%D7%A2%D7%95%D7%9C%D7%9D |
+--------------------+----------------------------------------------------+
| مرحبا | %D9%85%D8%B1%D8%AD%D8%A8%D8%A7 |
+--------------------+----------------------------------------------------+
(阿拉伯语和希伯来语都在现代浏览器中被翻译成原始和可读的字符)。
有一个 Rails API 方法 transliterate
使用示例:
transliterate('Ãrøskøbing')
# => "AEroskobing"
默认情况下,它仅支持基于拉丁语的语言和俄语,但您应该也能找到其他字母表的规则(如链接文档中所述)
编辑
要实现与 wordpress 相同的行为,您可以简单地使用 url 编码,如下例所示
URI::encode('שלום') => "%D7%A9%D7%9C%D7%95%D7%9D"
感谢@michalszyndel 的笔记和想法,我得到了以下解决方案,希望对更多人有所帮助。
首先,如何在 slug 中制作 non-unicode 个字符:
extend FriendlyId
friendly_id :slug_candidates, :use => :scoped, :scope => :account
def slug_candidates
:title_and_sequence
end
def title_and_sequence
# This line switch all special chars to its unicode
title_unicode = heb_to_unicode(title)
slug = normalize_friendly_id(title_unicode)
:
# some login to add sequence in case of collision
# and whatever you need from your slug
:
end
def heb_to_unicode(str)
heb_chars = 'אבגדהוזחטיכךלמםנןסעפףצץקרשת'
heb_map = {}
heb_chars.split("").each {|c| heb_map.merge!({c => URI::encode(c)})}
# This regex replace all Hebrew letters to their unicode representation
heb_re = Regexp.new(heb_map.keys.map { |x| Regexp.escape(x) }.join('|'))
return str.gsub(heb_re, heb_map)
end
我还需要修改 normalize_friendly_id
以避免它摆脱 %
。
我简单地获取了 parameterize 方法的代码并将 %
添加到正则表达式中:
def normalize_friendly_id(string)
# replace accented chars with their ascii equivalents
parameterized_string = I18n.transliterate(string)
sep = '-'
# Turn unwanted chars into the separator
# We permit % in order to allow unicode in slug
parameterized_string.gsub!(/[^a-zA-Z0-9\-_\%]+/, sep)
unless sep.nil? || sep.empty?
re_sep = Regexp.escape(sep)
# No more than one of the separator in a row.
parameterized_string.gsub!(/#{re_sep}{2,}/, sep)
# Remove leading/trailing separator.
parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/, '')
end
parameterized_string.downcase
end
现在,如果我用标题 שלום
保存模型,它的 slug 将保存为 %D7%A9%D7%9C%D7%95%D7%9D
。
为了使用 friendly
方法找到实例,我需要执行以下操作:
id = URI::encode(params[:id]).downcase
Page.friendly.find(id)