Unicode 用户名以 Σ 作为最后一个字符会导致加载个人资料页面出错

首先,非常感谢您的帮助!不过这确实是一个有趣的 bug。

问题是,我可能需要通过数据库来修改用户名,因为相关的路由不可用::sweat_smile:

您能提供一个查询语句吗?

我认为这不可行,因为那样会禁止希腊语中许多以 Σ 开头的名字(而且实际上会导致这些用户的 Facebook 登录失效)。我能否禁止某个特定的正则表达式模式?这样就能确保 Σ 至少出现在末尾。

1 个赞

另一位成员也注册了完全相同的问题。我想说明的是,对我们而言,这并非边缘案例。实际上,有成千上万个希腊语名字在末尾使用 Σ(或 ς)。

此外,由于我们的主要用户群体年龄超过 40 岁,许多成员在 Facebook 上的名字是全大写的(:sweat_smile:),因此当他们使用 Facebook 登录时,名字会被复制到用户名字段中……

4 个赞

我不必担心 Postgres。在 SQL 中,我们应始终与 username_lower 进行比较,而不依赖 LOWER(),因为 username_lower 不仅仅是用户名的全小写版本。我们还应用了 Unicode 规范化。

我同意。目前为止,为 User.normalize_username 添加一个变通方案就足够了。Ruby 的 bug 讨论中已经有一些相关言论,看来并没有简单的解决方案。不过,我们很幸运,因为我们需要做的只是检查用户名中的最后一个字符。这比在整个句子中进行检查要容易得多。

4 个赞

那么……这个用户名会如何处理呢?

大写:ΧΡΗΣΤΟΣ_ΠΑΝΑΓΙΩΤΑΚΟΠΟΥΛΟΣ
小写:χρηστος_παναγιωτακοπουλος 或 χρηστοσ_παναγιωτακοπουλος

NodeJs 能正确处理这种情况,但如果只检查用户名的最后一个字符,问题仍将存在。


(几乎所有希腊语男性名字——包括名和姓——都以 Σ 结尾)

3 个赞

没错。不过,这应该比完整实现容易得多,因为我们只需要处理某些符号,比如下划线、连字符,可能还有数字?这应该是可行的。

2 个赞

我知道我偏离了关于这个特定 Bug 的讨论,但一想到这个问题,我就无法忽视它本是可以避免的。

我发现 Discourse 中有些 API 路由通过 userId 引用用户,而另一些则通过 username 引用。这难道不应该更加一致吗(倾向于使用 userId)?

或许可以像现在处理分类/标签那样实现:在 URL 中同时包含用户名和 userId,例如:https://meta.discourse.org/u/chrispanag/4387

只是一个小想法 :sweat_smile:

2 个赞

这将需要一次清理工作,我确实见过一些 LOWER() 调用。

4 个赞

多年来这个问题一直存在:Update quotes and mentions when username is changed - #10 by sam

早在 2016 年,@eviltrout 就反对这样做,不确定他现在的立场如何。

无论如何,我在 Discourse 的这个 PR 中提供了一个变通方案:

它将通过将所有以西格玛(sigma)开头的用户名转换为小写,来解决 Facebook 新注册的问题。这意味着你只需要将 Spiros 的用户名以及其他带有词尾西格玛的用户名改为小写,问题就能从长远上得到解决。

(等待合并 PR)

4 个赞

非常感谢 @sam!你的帮助巨大。

有没有办法通过 Rails 控制台来操作?(因为该用户已损坏,无法通过管理面板进行操作……)

1 个赞

是的,这应该能解决问题。

./launcher enter app
rails c
u = User.find_by(username: 'ΣΠΥΡΟΣ')
u.username = 'σπυρος'
u.username_lower = 'σπυρος'
u.save!
4 个赞

我仍然不太喜欢到处使用 ID,但我理解在很多情况下这样做是合理的。

在这些情况下,我更倾向于使用类似 id-username 的格式,其中 username 可以是任何能放在 URL 中的内容。即使路由器忽略它也没关系。但至少当分享链接时,你就能知道链接指向的是什么。

6 个赞

关于 PostgreSQL,看起来设置正确的排序规则可以解决该问题:

➜  discoursesmall git:(master) psql -d discourse_development
psql (13.1 (Ubuntu 13.1-1.pgdg20.10+1))
输入 "help" 以获取帮助。

discourse_development=# SELECT lower('ΣΠΥΡΟΣ');
 lower  
--------
 σπυροσ
(1 行)

discourse_development=# SELECT lower('ΣΠΥΡΟΣ' COLLATE "und-x-icu"); 
 lower  
--------
 σπυρος
(1 行)

来源:PostgreSQL: Re: BUG #15805: Problem with lower function for greek sigma (Σ) letter

7 个赞

显然还有其他 Unicode 字符具有类似的转换,例如:‘ß’ → ‘SS’(德语)。

研究 Discourse 如何处理这些字符会很有趣……

此外,你也可以查看这个资源:

就我个人而言,我非常推崇 Stack Overflow 的用户路由风格:

https://stackoverflow.com/users/17174/sam-saffron

在 Discourse 中,这相当于:

https://meta.discourse.org/u/17174/sam-saffron

这种方式让你鱼与熊掌兼得。不过,我完全理解那些反对意见:“我不希望 URL 中出现 17174,因为用户名才是稳定的”。

话虽如此,我们现有的路由一直运行良好,只是每隔几年才会出现一些边缘情况。

5 个赞

我认为,至少在用户个人资料页面使用 ID 而不是用户名(或者两者都用,但如上文所述,仅依赖 ID)会更有帮助。这样,如果用户名出现问题,管理员就可以通过 Discourse 界面直接修改用户名,而无需在 Rails 控制台中运行命令。

我们只需要担心那些 JavaScript 和 Ruby 在将字符转换为小写时实现方式不同的情况。反之则无需担心。

德语小写字母 “ß” 转换为大写字母并没有统一规则。它可以是 “SS”、“SZ”,甚至是新的大写字母 “ẞ”(是的,这里有细微差别)。只有 “ẞ” 的反向转换是可行的,而 Ruby 和 JavaScript 都能正确处理这一点。


我认为我们应该采取以下措施:

  1. 合并 @sam 的变通方案以解决当前问题。
  2. 从 SQL 查询中移除 LOWER(username),因为这本身就是一个糟糕的做法(例如,会导致 Unicode 规范化缺失)。
  3. 希望 Ruby 能修复底层问题。
  4. 长期来看:考虑在路由中添加用户 ID。我想最困难的部分将是弄清楚如何处理引号和提及(mentions)。
3 个赞

不确定是否与此相关,但我们发现有一些用户的名称包含 Unicode 字符,这些名称分散在 /log 中。

ActionView::Template::Error (No route matches {:action=>"show", :controller=>"users", :username=>"ζηεδψ"}, possible unmatched constraints: [:username])