Changeset be5d86c in Klonkt


Ignore:
Timestamp:
06/28/2026 02:06:48 PM (2 months ago)
Author:
Robin Genis <roboburr@…>
Branches:
main
Children:
a8b4f10
Parents:
73abbfd
Message:

feat(i18n): Unicode-aware hashtags + mentions (all scripts, not just ASCII)

The hashtag and @mention regexes were [A-Za-z0-9_] only, so Japanese/Cyrillic/Arabic/etc.
hashtags and handles weren't detected, linked or federated. Now \p{L}\p{M}\p{N} with the u flag
— UTF-8 content works across all languages. IDN domains left as-is (Node URL/fetch do punycode).

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/services/ActivityPubService.js

    r73abbfd rbe5d86c  
    949949// Turn #hashtags in reply text into Mastodon-style hashtag links (clickable + federated).
    950950function linkHashtags(base, html) {
    951   return String(html || '').replace(/(^|[\s>])#([A-Za-z0-9_]+)/g, (m, pre, tag) =>
     951  return String(html || '').replace(/(^|[\s>])#([\p{L}\p{M}\p{N}_]+)/gu, (m, pre, tag) =>
    952952    `${pre}<a href="${base}/tag/${encodeURIComponent(tag.toLowerCase())}" class="mention hashtag" rel="tag">#${tag}</a>`);
    953953}
     
    955955function hashtagTags(base, content) {
    956956  const tags = [], seen = new Set();
    957   const re = /class="[^"]*\bhashtag\b[^"]*"[^>]*>#([A-Za-z0-9_]+)</gi;
     957  const re = /class="[^"]*\bhashtag\b[^"]*"[^>]*>#([\p{L}\p{M}\p{N}_]+)</giu;
    958958  let m;
    959959  while ((m = re.exec(content || ''))) {
     
    979979// slug/href stays lowercase ("livemusic").
    980980function tagParts(raw) {
    981   const words = String(raw || '').trim().split(/[\s_]+/).map((w) => w.replace(/[^A-Za-z0-9]/g, '')).filter(Boolean);
     981  const words = String(raw || '').trim().split(/[\s_]+/).map((w) => w.replace(/[^\p{L}\p{M}\p{N}]/gu, '')).filter(Boolean);
    982982  if (!words.length) return null;
    983983  const slug = words.join('').toLowerCase();
     
    10191019  const inboxes = [];
    10201020  const handles = new Set();
    1021   const re = /(^|[\s>])@([A-Za-z0-9_.-]+@[A-Za-z0-9.-]+)/g;
     1021  const re = /(^|[\s>])@([\p{L}\p{M}\p{N}_.-]+@[\p{L}\p{M}\p{N}.-]+)/gu;
    10221022  let m;
    10231023  while ((m = re.exec(html || ''))) handles.add(m[2]);
     
    10321032    const profileUrl = actorInfo(actor, actorUri).url || actorUri; // human profile page → the link href
    10331033    const esc = h.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    1034     out = out.replace(new RegExp('(^|[\\s>])@' + esc + '(?![A-Za-z0-9_.-])', 'g'),
     1034    out = out.replace(new RegExp('(^|[\\s>])@' + esc + '(?![\\p{L}\\p{M}\\p{N}_.-])', 'gu'),
    10351035      (full, pre) => `${pre}<a href="${profileUrl}" class="u-url mention" data-actor="${actorUri}">@${h}</a>`);
    10361036  }
Note: See TracChangeset for help on using the changeset viewer.