使用 Chinese_pinyin + Friendly_id 为中文标题生成 Slug

在许多项目中,我们可能都会遇到需要为数据生成 slug 的场景,这些场景类似于:

  • 基于商品名称生成 slug

  • 基于文章标题生成 slug

至于为什么需要生成 slug,而不是使用比如 Rails 中默认自增的主键也就是数据的 id,原因其实很简单:

  • 使用自增 id 容易暴露数据,比如通过订单 id 可能导致遍历所有订单,不信,你看这里就有个例子

  • 增加 URL 友好性,/products/18376 这样的链接肯定没有比 /products/apple-watch-gold 这样的链接更招人喜欢

friendly_id

friendly_id 是用来生成 slug 的 ruby gem,假设我们有一个产品模型 Product,使用 friendly_id 为商品名称(name)生成 slug 的示例代码如下:

class Product < ActiveRecord::Base
  friendly_id :name, use: :slugged
end

上面的代码理论上来说已经完成我们所需要的工作了,但是如果 title 包含中文的话,生成的 slug 就有点类似 30f175f4-1e56-4e3a-823d-a7c1a5d32b29 这样的乱码了,实际上这个 slug 对应的原来的 title 是 测试产品。这样的 slug 虽然避免了自增 id 的弊端,但是却丧失了友好性。如果 slug 能够基于汉语拼音生成,岂不更好?

自己控制 slug 生成方式

阅读 friendly_id 的源码可以找到以下代码:

module FriendlyId
  module Slugged
    # ...

    def normalize_friendly_id(value)
      value.to_s.parameterize
    end

    # ...
  end
end

这段代码便是 friendly_id 基于输入生成 slug 的核心代码,使用 ActiveSupport 扩展后的 String 类的 parameterize 方法,此方法会将除了英文字母、数字、短横线以及下划线之外的字符转换为 -,所以不适用于中文的情况,我们需要重写该方法,以满足我们的需求。

中文拼音利器——chinese_pinyin

中文生成中文拼音的工具,我选择了黄志敏先生写的 chinese_pinyin 这个 gem,推荐理由就是简单够用。

以下是单独使用这个 gem 时的示例:

2.2.0 :009 > Pinyin.t("中国人")
 => "zhong guo ren"
2.2.0 :010 > Pinyin.t("Hello, 李雷")
 => "Hello  li lei"

组装!!!

根据 friendly_id 的注释,如果你只需要为单独一个 model 定制 slug 的生成逻辑,那么建议你只在相关的 model 中定义同名方法即可。但是由于我是需要为多个 model 定制中文的 slug 生成逻辑,所以我选择了直接重定义 FriendlyId::Slugged 模块中的这个方法:

# config/initializers/friendly_id/slugged.rb
module FriendlyId
  module Slugged
    # 重定义 friendly_id 方法,实现 slug 从中文到拼音,非中文不受影响
    def normalize_friendly_id(value)
      Pinyin.t(value.to_s).parameterize
    end
  end
end

这样的定义方式使得新的 normalize_friendly_id 方法对所有依赖 friendly_id 的代码都生效。

最后通过新的方法为我们的产品生成新的 slug,现在“测试产品”得到的 slug 变为 ce-shi-chan-pin 了:

product.update(slug: nil)  # 显式清空 slug, friendly_id 在 save 时会自动重新生成 slug

最后产品的链接已变为 /products/ce-shi-chan-pin,比起 /products/30f175f4-1e56-4e3a-823d-a7c1a5d32b29,可真是叫人心旷神怡。

Pinyin 基于 CC-CEDICT 词典的中文转拼音工具,更准确的支持多音字的汉字转拼音解决方案。 安装 使用 Composer 安装: composer require "overtrue/pinyin:~3.0" 使用 拼音数组 use Overtrue\Pinyin\Pinyin; $pinyin = new Pinyin(); $pinyin->convert('带着希望去旅行,比到达终点更美好'); // ["dai", "zhe", "xi", "wang", "qu", "lu", "xing", "bi", "dao", "da", "zhong", "dian", "geng", "mei", "hao"] $pinyin->convert('带着希望去旅行,比到达终点更美好', PINYIN_UNICODE); // ["dài","zhe","xī","wàng","qù","lǚ","xíng","bǐ","dào","dá","zhōng","diǎn","gèng","měi","hǎo"] $pinyin->convert('带着希望去旅行,比到达终点更美好', PINYIN_ASCII); //["dai4","zhe","xi1","wang4","qu4","lv3","xing2","bi3","dao4","da2","zhong1","dian3","geng4","mei3","hao3"] 选项: 选项 描述 PINYIN_NONE 不带音调输出:mei hao PINYIN_ASCII 带数字式音调:mei3 hao3 PINYIN_UNICODE UNICODE 式音调:měi hǎo 生成用于链接的拼音字符串 $pinyin->permlink('带着希望去旅行'); // dai-zhe-xi-wang-qu-lu-xing $pinyin->permlink('带着希望去旅行', '.'); // dai.zhe.xi.wang.qu.lu.xing 获取首字符字符串 $pinyin->abbr('带着希望去旅行'); // dzxwqlx $pinyin->abbr('带着希望去旅行', '-'); // d-z-x-w-q-l-x 翻译整段文字为拼音 将会保留中文字符:,。 ! ? : “ ” ‘ ’并替换为对应的英文符号。 $pinyin->sentence('带着希望去旅行,比到达终点更美好!'); // dai zhe xi wang qu lv xing, bi dao da zhong dian geng mei hao! $pinyin->sentence('带着希望去旅行,比到达终点更美好!', true); // dài zhe xī wàng qù lǚ xíng, bǐ dào dá zhōng diǎn gèng měi hǎo! 翻译姓名 姓名的姓的读音有些与普通字不一样,比如 ‘单’ 常见的音为dan,而作为姓的时候读shan。 $pinyin->name('单某某'); // ['shan', 'mou', 'mou'] $pinyin->name('单某某', PINYIN_UNICODE); // ["shàn","mǒu","mǒu"] 在 Laravel 中使用 独立的包在这里:overtrue/laravel-pinyin Contribution 欢迎提意见及完善补充词库 tools/patches/ 参考 详细参考资料 License MIT 标签:Pinyin
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值