@fedify/vocab-runtime 2.0.31-dev.2297 → 2.0.31-dev.2308
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/deno.json +1 -1
- package/dist/mod.cjs +82 -3
- package/dist/mod.d.cts +29 -2
- package/dist/mod.d.ts +29 -2
- package/dist/mod.js +82 -4
- package/dist/tests/{body-DkPsw4D7.mjs → body-Bx6-vXo_.mjs} +1 -1
- package/dist/tests/{body-B1TX2GGC.cjs → body-DGmcZ1Kz.cjs} +1 -1
- package/dist/tests/body.test.cjs +1 -1
- package/dist/tests/body.test.mjs +1 -1
- package/dist/tests/docloader.test.cjs +113 -2
- package/dist/tests/docloader.test.mjs +113 -2
- package/dist/tests/langstr.test.cjs +12 -2
- package/dist/tests/langstr.test.mjs +13 -3
- package/dist/tests/langtag-Bm8oKhIO.cjs +50 -0
- package/dist/tests/langtag-QZZgy903.mjs +45 -0
- package/dist/tests/langtag.test.cjs +46 -0
- package/dist/tests/langtag.test.d.cts +1 -0
- package/dist/tests/langtag.test.d.mts +1 -0
- package/dist/tests/langtag.test.mjs +46 -0
- package/dist/tests/{request-DlZHvlsq.mjs → request-BV7yaWdl.mjs} +1 -1
- package/dist/tests/{request-ClhOmVbs.cjs → request-Ban3HXuI.cjs} +1 -1
- package/dist/tests/request.test.cjs +1 -1
- package/dist/tests/request.test.mjs +1 -1
- package/package.json +1 -1
- package/src/contexts/fep-22cd.json +14 -0
- package/src/contexts.ts +8 -0
- package/src/docloader.test.ts +82 -0
- package/src/docloader.ts +1 -0
- package/src/langstr.test.ts +18 -1
- package/src/langstr.ts +6 -2
- package/src/langtag.test.ts +53 -0
- package/src/langtag.ts +76 -0
- package/src/mod.ts +1 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-
|
|
2
|
-
import { n as MAX_BODY_SIZE, r as readBoundedText } from "./body-
|
|
1
|
+
import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-BV7yaWdl.mjs";
|
|
2
|
+
import { n as MAX_BODY_SIZE, r as readBoundedText } from "./body-Bx6-vXo_.mjs";
|
|
3
3
|
import { t as HttpHeaderLink } from "./link-NUUWCdnK.mjs";
|
|
4
4
|
import { o as validatePublicUrl, t as UrlError } from "./url-DzlMS01z.mjs";
|
|
5
5
|
import { test } from "node:test";
|
|
@@ -1344,6 +1344,36 @@ var cid_v1_default = { "@context": {
|
|
|
1344
1344
|
}
|
|
1345
1345
|
} };
|
|
1346
1346
|
//#endregion
|
|
1347
|
+
//#region src/contexts/fep-22cd.json
|
|
1348
|
+
var fep_22cd_default = { "@context": {
|
|
1349
|
+
"fep-22cd": "https://w3id.org/fep/22cd#",
|
|
1350
|
+
"schema": "https://schema.org/",
|
|
1351
|
+
"xsd": "http://www.w3.org/2001/XMLSchema#",
|
|
1352
|
+
"translations": {
|
|
1353
|
+
"@id": "fep-22cd:translations",
|
|
1354
|
+
"@container": "@set"
|
|
1355
|
+
},
|
|
1356
|
+
"Translation": "fep-22cd:Translation",
|
|
1357
|
+
"sourceUpdated": {
|
|
1358
|
+
"@id": "fep-22cd:sourceUpdated",
|
|
1359
|
+
"@type": "xsd:dateTime"
|
|
1360
|
+
},
|
|
1361
|
+
"translator": {
|
|
1362
|
+
"@id": "schema:translator",
|
|
1363
|
+
"@type": "@id",
|
|
1364
|
+
"@container": "@set"
|
|
1365
|
+
},
|
|
1366
|
+
"inLanguage": "schema:inLanguage",
|
|
1367
|
+
"translationOfWork": {
|
|
1368
|
+
"@id": "schema:translationOfWork",
|
|
1369
|
+
"@type": "@id"
|
|
1370
|
+
},
|
|
1371
|
+
"isBasedOn": {
|
|
1372
|
+
"@id": "schema:isBasedOn",
|
|
1373
|
+
"@type": "@id"
|
|
1374
|
+
}
|
|
1375
|
+
} };
|
|
1376
|
+
//#endregion
|
|
1347
1377
|
//#region src/contexts/fep-6757.json
|
|
1348
1378
|
var fep_6757_default = { "@context": {
|
|
1349
1379
|
"license": {
|
|
@@ -5657,6 +5687,7 @@ const preloadedContexts = {
|
|
|
5657
5687
|
"https://w3id.org/fep/ef61": fep_ef61_default,
|
|
5658
5688
|
"https://w3id.org/fep/7aa9": fep_7aa9_default,
|
|
5659
5689
|
"https://w3id.org/fep/6757": fep_6757_default,
|
|
5690
|
+
"https://w3id.org/fep/22cd": fep_22cd_default,
|
|
5660
5691
|
"https://join-lemmy.org/context.json": join_lemmy_default,
|
|
5661
5692
|
"http://joinmastodon.org/ns": { "@context": {
|
|
5662
5693
|
"toot": "http://joinmastodon.org/ns#",
|
|
@@ -5799,6 +5830,7 @@ async function getRemoteDocument(url, response, fetch) {
|
|
|
5799
5830
|
* - <https://w3id.org/fep/ef61>
|
|
5800
5831
|
* - <https://w3id.org/fep/7aa9>
|
|
5801
5832
|
* - <https://w3id.org/fep/6757>
|
|
5833
|
+
* - <https://w3id.org/fep/22cd>
|
|
5802
5834
|
* - <https://purl.archive.org/socialweb/webfinger>
|
|
5803
5835
|
* - <http://schema.org/>
|
|
5804
5836
|
* @param options Options for the document loader.
|
|
@@ -6197,6 +6229,85 @@ test("getDocumentLoader()", async (t) => {
|
|
|
6197
6229
|
}]);
|
|
6198
6230
|
deepStrictEqual(esm_default.callHistory.calls(url).length, 0);
|
|
6199
6231
|
});
|
|
6232
|
+
esm_default.get("https://w3id.org/fep/22cd", { status: 502 });
|
|
6233
|
+
await t.test("preloaded FEP-22cd context", async () => {
|
|
6234
|
+
const url = "https://w3id.org/fep/22cd";
|
|
6235
|
+
const articleId = "https://example.com/articles/1";
|
|
6236
|
+
const translatorId = "https://example.com/users/alice";
|
|
6237
|
+
const revisionId = "https://example.com/articles/1/revisions/1";
|
|
6238
|
+
deepStrictEqual(await fetchDocumentLoader(url), {
|
|
6239
|
+
contextUrl: null,
|
|
6240
|
+
documentUrl: url,
|
|
6241
|
+
document: { "@context": {
|
|
6242
|
+
"fep-22cd": "https://w3id.org/fep/22cd#",
|
|
6243
|
+
schema: "https://schema.org/",
|
|
6244
|
+
xsd: "http://www.w3.org/2001/XMLSchema#",
|
|
6245
|
+
translations: {
|
|
6246
|
+
"@id": "fep-22cd:translations",
|
|
6247
|
+
"@container": "@set"
|
|
6248
|
+
},
|
|
6249
|
+
Translation: "fep-22cd:Translation",
|
|
6250
|
+
sourceUpdated: {
|
|
6251
|
+
"@id": "fep-22cd:sourceUpdated",
|
|
6252
|
+
"@type": "xsd:dateTime"
|
|
6253
|
+
},
|
|
6254
|
+
translator: {
|
|
6255
|
+
"@id": "schema:translator",
|
|
6256
|
+
"@type": "@id",
|
|
6257
|
+
"@container": "@set"
|
|
6258
|
+
},
|
|
6259
|
+
inLanguage: "schema:inLanguage",
|
|
6260
|
+
translationOfWork: {
|
|
6261
|
+
"@id": "schema:translationOfWork",
|
|
6262
|
+
"@type": "@id"
|
|
6263
|
+
},
|
|
6264
|
+
isBasedOn: {
|
|
6265
|
+
"@id": "schema:isBasedOn",
|
|
6266
|
+
"@type": "@id"
|
|
6267
|
+
}
|
|
6268
|
+
} }
|
|
6269
|
+
});
|
|
6270
|
+
deepStrictEqual(await jsonld_default.expand({
|
|
6271
|
+
"@context": ["https://www.w3.org/ns/activitystreams", url],
|
|
6272
|
+
id: articleId,
|
|
6273
|
+
type: "Article",
|
|
6274
|
+
contentMap: {
|
|
6275
|
+
en: "Original",
|
|
6276
|
+
ko: "Translation"
|
|
6277
|
+
},
|
|
6278
|
+
translations: [{
|
|
6279
|
+
type: "Translation",
|
|
6280
|
+
inLanguage: "ko",
|
|
6281
|
+
translator: [translatorId],
|
|
6282
|
+
translationOfWork: articleId,
|
|
6283
|
+
sourceUpdated: "2026-09-01T00:00:00Z",
|
|
6284
|
+
isBasedOn: revisionId
|
|
6285
|
+
}]
|
|
6286
|
+
}, { documentLoader: fetchDocumentLoader }), [{
|
|
6287
|
+
"@id": articleId,
|
|
6288
|
+
"@type": ["https://www.w3.org/ns/activitystreams#Article"],
|
|
6289
|
+
"https://www.w3.org/ns/activitystreams#content": [{
|
|
6290
|
+
"@language": "en",
|
|
6291
|
+
"@value": "Original"
|
|
6292
|
+
}, {
|
|
6293
|
+
"@language": "ko",
|
|
6294
|
+
"@value": "Translation"
|
|
6295
|
+
}],
|
|
6296
|
+
"https://w3id.org/fep/22cd#translations": [{
|
|
6297
|
+
"@type": ["https://w3id.org/fep/22cd#Translation"],
|
|
6298
|
+
"https://schema.org/inLanguage": [{ "@value": "ko" }],
|
|
6299
|
+
"https://schema.org/translator": [{ "@id": translatorId }],
|
|
6300
|
+
"https://schema.org/translationOfWork": [{ "@id": articleId }],
|
|
6301
|
+
"https://w3id.org/fep/22cd#sourceUpdated": [{
|
|
6302
|
+
"@type": "http://www.w3.org/2001/XMLSchema#dateTime",
|
|
6303
|
+
"@value": "2026-09-01T00:00:00Z"
|
|
6304
|
+
}],
|
|
6305
|
+
"https://schema.org/isBasedOn": [{ "@id": revisionId }]
|
|
6306
|
+
}]
|
|
6307
|
+
}]);
|
|
6308
|
+
deepStrictEqual(esm_default.callHistory.calls(url).length, 0);
|
|
6309
|
+
deepStrictEqual(esm_default.callHistory.calls("https://www.w3.org/ns/activitystreams").length, 0);
|
|
6310
|
+
});
|
|
6200
6311
|
await t.test("deny non-HTTP/HTTPS", async () => {
|
|
6201
6312
|
await rejects(() => fetchDocumentLoader("ftp://localhost"), UrlError);
|
|
6202
6313
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-Do9eywBl.cjs");
|
|
2
|
+
const require_langtag = require("./langtag-Bm8oKhIO.cjs");
|
|
2
3
|
let node_test = require("node:test");
|
|
3
4
|
let node_assert = require("node:assert");
|
|
4
5
|
let node_util = require("node:util");
|
|
@@ -17,11 +18,13 @@ var LanguageString = class extends String {
|
|
|
17
18
|
* Constructs a new `LanguageString`.
|
|
18
19
|
* @param value A string value written in the given language.
|
|
19
20
|
* @param language The language of the string. If a string is given, it will
|
|
20
|
-
* be parsed as a `Intl.Locale` object.
|
|
21
|
+
* be parsed as a `Intl.Locale` object. Extended language
|
|
22
|
+
* subtags are normalized first, e.g., `zh-yue` is parsed as
|
|
23
|
+
* `yue`.
|
|
21
24
|
*/
|
|
22
25
|
constructor(value, language) {
|
|
23
26
|
super(value);
|
|
24
|
-
this.locale = typeof language === "string" ? new Intl.Locale(language) : language;
|
|
27
|
+
this.locale = typeof language === "string" ? new Intl.Locale(require_langtag.normalizeLanguageTag(language)) : language;
|
|
25
28
|
}
|
|
26
29
|
};
|
|
27
30
|
LanguageString.prototype[Symbol.for("Deno.customInspect")] = function(inspect, options) {
|
|
@@ -38,6 +41,13 @@ LanguageString.prototype[Symbol.for("nodejs.util.inspect.custom")] = function(_d
|
|
|
38
41
|
(0, node_assert.deepStrictEqual)(langStr.locale, new Intl.Locale("en"));
|
|
39
42
|
(0, node_assert.deepStrictEqual)(new LanguageString("Hello", new Intl.Locale("en")), langStr);
|
|
40
43
|
});
|
|
44
|
+
(0, node_test.test)("new LanguageString() with extlang tags", () => {
|
|
45
|
+
(0, node_assert.deepStrictEqual)(new LanguageString("你好", "zh-YUE").locale.baseName, "yue");
|
|
46
|
+
(0, node_assert.deepStrictEqual)(new LanguageString("你好", "zh-yue-HK").locale.baseName, "yue-HK");
|
|
47
|
+
(0, node_assert.deepStrictEqual)(new LanguageString("你好", "zh-cmn-Hant").locale.baseName, new Intl.Locale("cmn-Hant").baseName);
|
|
48
|
+
(0, node_assert.throws)(() => new LanguageString("Hello", "en-USA"), RangeError);
|
|
49
|
+
(0, node_assert.throws)(() => new LanguageString("你好", "zh-yue-"), RangeError);
|
|
50
|
+
});
|
|
41
51
|
(0, node_test.test)("Deno.inspect(LanguageString)", () => {
|
|
42
52
|
const langStr = new LanguageString("Hello, 'world'", "en");
|
|
43
53
|
(0, node_assert.deepStrictEqual)(node_util.default.inspect(langStr, { colors: false }), "<en> \"Hello, 'world'\"");
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { t as normalizeLanguageTag } from "./langtag-QZZgy903.mjs";
|
|
1
2
|
import { test } from "node:test";
|
|
2
|
-
import { deepStrictEqual } from "node:assert";
|
|
3
|
+
import { deepStrictEqual, throws } from "node:assert";
|
|
3
4
|
import util from "node:util";
|
|
4
5
|
//#region src/langstr.ts
|
|
5
6
|
/**
|
|
@@ -15,11 +16,13 @@ var LanguageString = class extends String {
|
|
|
15
16
|
* Constructs a new `LanguageString`.
|
|
16
17
|
* @param value A string value written in the given language.
|
|
17
18
|
* @param language The language of the string. If a string is given, it will
|
|
18
|
-
* be parsed as a `Intl.Locale` object.
|
|
19
|
+
* be parsed as a `Intl.Locale` object. Extended language
|
|
20
|
+
* subtags are normalized first, e.g., `zh-yue` is parsed as
|
|
21
|
+
* `yue`.
|
|
19
22
|
*/
|
|
20
23
|
constructor(value, language) {
|
|
21
24
|
super(value);
|
|
22
|
-
this.locale = typeof language === "string" ? new Intl.Locale(language) : language;
|
|
25
|
+
this.locale = typeof language === "string" ? new Intl.Locale(normalizeLanguageTag(language)) : language;
|
|
23
26
|
}
|
|
24
27
|
};
|
|
25
28
|
LanguageString.prototype[Symbol.for("Deno.customInspect")] = function(inspect, options) {
|
|
@@ -36,6 +39,13 @@ test("new LanguageString()", () => {
|
|
|
36
39
|
deepStrictEqual(langStr.locale, new Intl.Locale("en"));
|
|
37
40
|
deepStrictEqual(new LanguageString("Hello", new Intl.Locale("en")), langStr);
|
|
38
41
|
});
|
|
42
|
+
test("new LanguageString() with extlang tags", () => {
|
|
43
|
+
deepStrictEqual(new LanguageString("你好", "zh-YUE").locale.baseName, "yue");
|
|
44
|
+
deepStrictEqual(new LanguageString("你好", "zh-yue-HK").locale.baseName, "yue-HK");
|
|
45
|
+
deepStrictEqual(new LanguageString("你好", "zh-cmn-Hant").locale.baseName, new Intl.Locale("cmn-Hant").baseName);
|
|
46
|
+
throws(() => new LanguageString("Hello", "en-USA"), RangeError);
|
|
47
|
+
throws(() => new LanguageString("你好", "zh-yue-"), RangeError);
|
|
48
|
+
});
|
|
39
49
|
test("Deno.inspect(LanguageString)", () => {
|
|
40
50
|
const langStr = new LanguageString("Hello, 'world'", "en");
|
|
41
51
|
deepStrictEqual(util.inspect(langStr, { colors: false }), "<en> \"Hello, 'world'\"");
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//#region src/langtag.ts
|
|
2
|
+
const EXTLANG_PAIRS = new Set(Object.entries({
|
|
3
|
+
ar: "aao abh abv acm acq acw acx acy adf aeb aec afb ajp apc apd arb arq ars ary arz auz avl ayh ayl ayn ayp bbz pga shu ssh",
|
|
4
|
+
kok: "gom knn",
|
|
5
|
+
lv: "ltg lvs",
|
|
6
|
+
ms: "bjn btj bve bvu coa dup hji jak jax kvb kvr kxd lce lcf liw max meo mfa mfb min mqg msi mui orn ors pel pse tmw urk vkk vkt xmm zlm zmi zsm",
|
|
7
|
+
sgn: "ads aed aen afg ajs ase asf asp asq asw bfi bfk bog bqn bqy bvl bzs cds csc csd cse csf csg csl csn csq csr csx doq dse dsl dsz dyl ecs ehs esl esn eso eth fcs fse fsl fss gds gse gsg gsm gss gus hab haf hds hks hos hps hsh hsl icl iks ils inl ins ise isg isr jcs jhs jks jls jos jsl jus kgi kvk lbs lgs lls lsb lsc lsg lsl lsn lso lsp lst lsv lsw lsy lws mdl mfs mre msd msr mzc mzg mzy nbs ncs nsi nsl nsp nsr nzs okl pgz pks prl prz psc psd psg psl pso psp psr pys rib rms rnb rsi rsl rsm rsn sdl sfb sfs sgg sgx slf sls sqk sqs sqx ssp ssr svk swl syy szs tse tsm tsq tss tsy tza ugn ugy ukl uks vgt vsi vsl vsv wbs xki xml xms yds ygs yhs ysl ysm zhk zib zsl",
|
|
8
|
+
sw: "swc swh",
|
|
9
|
+
uz: "uzn uzs",
|
|
10
|
+
zh: "cdo cjy cmn cnp cpx csp czh czo gan hak hnm hsn luh lzh mnp nan sjc wuu yue"
|
|
11
|
+
}).flatMap(([prefix, extlangs]) => extlangs.split(" ").map((extlang) => `${prefix}-${extlang}`)));
|
|
12
|
+
const PREFIX_PATTERN = /^[A-Za-z]{2,3}$/;
|
|
13
|
+
const EXTLANG_PATTERN = /^[A-Za-z]{3}$/;
|
|
14
|
+
/**
|
|
15
|
+
* Normalizes a BCP 47 language tag that has an extended language subtag
|
|
16
|
+
* (extlang), such as `zh-yue`, into its canonical form, such as `yue`, as
|
|
17
|
+
* described in [RFC 5646, Section 4.5][1]. `Intl.Locale` rejects extlang
|
|
18
|
+
* tags, so tags should be normalized before being passed to it.
|
|
19
|
+
*
|
|
20
|
+
* Only registered extlangs that follow their registered prefix are removed;
|
|
21
|
+
* any other tag is returned unchanged, so the result still needs to be
|
|
22
|
+
* validated, e.g., by `Intl.Locale`.
|
|
23
|
+
*
|
|
24
|
+
* NOTE: This function is marked as internal in the 2.0.x–2.4.x patch
|
|
25
|
+
* releases. When merging it into the *main* branch, remove the `@internal`
|
|
26
|
+
* tag, formalize it as a public API (`@since 2.5.0`), and add it to
|
|
27
|
+
* the changelog fragment.
|
|
28
|
+
*
|
|
29
|
+
* [1]: https://www.rfc-editor.org/rfc/rfc5646.html#section-4.5
|
|
30
|
+
*
|
|
31
|
+
* @param tag The BCP 47 language tag to normalize.
|
|
32
|
+
* @returns The normalized language tag.
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
function normalizeLanguageTag(tag) {
|
|
36
|
+
const subtags = tag.split("-");
|
|
37
|
+
if (subtags.length < 2) return tag;
|
|
38
|
+
const [prefix, extlang, ...rest] = subtags;
|
|
39
|
+
if (!PREFIX_PATTERN.test(prefix) || !EXTLANG_PATTERN.test(extlang)) return tag;
|
|
40
|
+
const language = extlang.toLowerCase();
|
|
41
|
+
if (!EXTLANG_PAIRS.has(`${prefix.toLowerCase()}-${language}`)) return tag;
|
|
42
|
+
return [language, ...rest].join("-");
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
Object.defineProperty(exports, "normalizeLanguageTag", {
|
|
46
|
+
enumerable: true,
|
|
47
|
+
get: function() {
|
|
48
|
+
return normalizeLanguageTag;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//#region src/langtag.ts
|
|
2
|
+
const EXTLANG_PAIRS = new Set(Object.entries({
|
|
3
|
+
ar: "aao abh abv acm acq acw acx acy adf aeb aec afb ajp apc apd arb arq ars ary arz auz avl ayh ayl ayn ayp bbz pga shu ssh",
|
|
4
|
+
kok: "gom knn",
|
|
5
|
+
lv: "ltg lvs",
|
|
6
|
+
ms: "bjn btj bve bvu coa dup hji jak jax kvb kvr kxd lce lcf liw max meo mfa mfb min mqg msi mui orn ors pel pse tmw urk vkk vkt xmm zlm zmi zsm",
|
|
7
|
+
sgn: "ads aed aen afg ajs ase asf asp asq asw bfi bfk bog bqn bqy bvl bzs cds csc csd cse csf csg csl csn csq csr csx doq dse dsl dsz dyl ecs ehs esl esn eso eth fcs fse fsl fss gds gse gsg gsm gss gus hab haf hds hks hos hps hsh hsl icl iks ils inl ins ise isg isr jcs jhs jks jls jos jsl jus kgi kvk lbs lgs lls lsb lsc lsg lsl lsn lso lsp lst lsv lsw lsy lws mdl mfs mre msd msr mzc mzg mzy nbs ncs nsi nsl nsp nsr nzs okl pgz pks prl prz psc psd psg psl pso psp psr pys rib rms rnb rsi rsl rsm rsn sdl sfb sfs sgg sgx slf sls sqk sqs sqx ssp ssr svk swl syy szs tse tsm tsq tss tsy tza ugn ugy ukl uks vgt vsi vsl vsv wbs xki xml xms yds ygs yhs ysl ysm zhk zib zsl",
|
|
8
|
+
sw: "swc swh",
|
|
9
|
+
uz: "uzn uzs",
|
|
10
|
+
zh: "cdo cjy cmn cnp cpx csp czh czo gan hak hnm hsn luh lzh mnp nan sjc wuu yue"
|
|
11
|
+
}).flatMap(([prefix, extlangs]) => extlangs.split(" ").map((extlang) => `${prefix}-${extlang}`)));
|
|
12
|
+
const PREFIX_PATTERN = /^[A-Za-z]{2,3}$/;
|
|
13
|
+
const EXTLANG_PATTERN = /^[A-Za-z]{3}$/;
|
|
14
|
+
/**
|
|
15
|
+
* Normalizes a BCP 47 language tag that has an extended language subtag
|
|
16
|
+
* (extlang), such as `zh-yue`, into its canonical form, such as `yue`, as
|
|
17
|
+
* described in [RFC 5646, Section 4.5][1]. `Intl.Locale` rejects extlang
|
|
18
|
+
* tags, so tags should be normalized before being passed to it.
|
|
19
|
+
*
|
|
20
|
+
* Only registered extlangs that follow their registered prefix are removed;
|
|
21
|
+
* any other tag is returned unchanged, so the result still needs to be
|
|
22
|
+
* validated, e.g., by `Intl.Locale`.
|
|
23
|
+
*
|
|
24
|
+
* NOTE: This function is marked as internal in the 2.0.x–2.4.x patch
|
|
25
|
+
* releases. When merging it into the *main* branch, remove the `@internal`
|
|
26
|
+
* tag, formalize it as a public API (`@since 2.5.0`), and add it to
|
|
27
|
+
* the changelog fragment.
|
|
28
|
+
*
|
|
29
|
+
* [1]: https://www.rfc-editor.org/rfc/rfc5646.html#section-4.5
|
|
30
|
+
*
|
|
31
|
+
* @param tag The BCP 47 language tag to normalize.
|
|
32
|
+
* @returns The normalized language tag.
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
function normalizeLanguageTag(tag) {
|
|
36
|
+
const subtags = tag.split("-");
|
|
37
|
+
if (subtags.length < 2) return tag;
|
|
38
|
+
const [prefix, extlang, ...rest] = subtags;
|
|
39
|
+
if (!PREFIX_PATTERN.test(prefix) || !EXTLANG_PATTERN.test(extlang)) return tag;
|
|
40
|
+
const language = extlang.toLowerCase();
|
|
41
|
+
if (!EXTLANG_PAIRS.has(`${prefix.toLowerCase()}-${language}`)) return tag;
|
|
42
|
+
return [language, ...rest].join("-");
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { normalizeLanguageTag as t };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require("./chunk-Do9eywBl.cjs");
|
|
2
|
+
const require_langtag = require("./langtag-Bm8oKhIO.cjs");
|
|
3
|
+
let node_test = require("node:test");
|
|
4
|
+
let node_assert = require("node:assert");
|
|
5
|
+
//#region src/langtag.test.ts
|
|
6
|
+
(0, node_test.test)("normalizeLanguageTag() removes extlang subtags", () => {
|
|
7
|
+
for (const [input, expected] of [
|
|
8
|
+
["zh-yue", "yue"],
|
|
9
|
+
["zh-YUE", "yue"],
|
|
10
|
+
["ZH-Yue-hk", "yue-hk"],
|
|
11
|
+
["zh-yue-HK", "yue-HK"],
|
|
12
|
+
["zh-cmn-Hant", "cmn-Hant"],
|
|
13
|
+
["zh-yue-u-nu-hanidec", "yue-u-nu-hanidec"],
|
|
14
|
+
["sgn-ase", "ase"],
|
|
15
|
+
["ar-arz", "arz"],
|
|
16
|
+
["ms-zlm", "zlm"],
|
|
17
|
+
["kok-gom", "gom"],
|
|
18
|
+
["koK-gom", "gom"],
|
|
19
|
+
["lv-ltg", "ltg"],
|
|
20
|
+
["sw-swc", "swc"],
|
|
21
|
+
["uz-uzs", "uzs"],
|
|
22
|
+
["zh-yue-cmn", "yue-cmn"],
|
|
23
|
+
["zh-yue-", "yue-"]
|
|
24
|
+
]) (0, node_assert.deepStrictEqual)(require_langtag.normalizeLanguageTag(input), expected, input);
|
|
25
|
+
});
|
|
26
|
+
(0, node_test.test)("normalizeLanguageTag() leaves other tags unchanged", () => {
|
|
27
|
+
for (const tag of [
|
|
28
|
+
"",
|
|
29
|
+
"en",
|
|
30
|
+
"en-US",
|
|
31
|
+
"en-USA",
|
|
32
|
+
"en-yue",
|
|
33
|
+
"zh",
|
|
34
|
+
"zh-Hant-TW",
|
|
35
|
+
"yue",
|
|
36
|
+
"yue-HK",
|
|
37
|
+
"x-private",
|
|
38
|
+
"i-klingon",
|
|
39
|
+
"zh-min-nan",
|
|
40
|
+
"koK-gom",
|
|
41
|
+
"zh-yuK",
|
|
42
|
+
"constructor-yue",
|
|
43
|
+
"toString-yue"
|
|
44
|
+
]) (0, node_assert.deepStrictEqual)(require_langtag.normalizeLanguageTag(tag), tag, tag);
|
|
45
|
+
});
|
|
46
|
+
//#endregion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { t as normalizeLanguageTag } from "./langtag-QZZgy903.mjs";
|
|
2
|
+
import { test } from "node:test";
|
|
3
|
+
import { deepStrictEqual } from "node:assert";
|
|
4
|
+
//#region src/langtag.test.ts
|
|
5
|
+
test("normalizeLanguageTag() removes extlang subtags", () => {
|
|
6
|
+
for (const [input, expected] of [
|
|
7
|
+
["zh-yue", "yue"],
|
|
8
|
+
["zh-YUE", "yue"],
|
|
9
|
+
["ZH-Yue-hk", "yue-hk"],
|
|
10
|
+
["zh-yue-HK", "yue-HK"],
|
|
11
|
+
["zh-cmn-Hant", "cmn-Hant"],
|
|
12
|
+
["zh-yue-u-nu-hanidec", "yue-u-nu-hanidec"],
|
|
13
|
+
["sgn-ase", "ase"],
|
|
14
|
+
["ar-arz", "arz"],
|
|
15
|
+
["ms-zlm", "zlm"],
|
|
16
|
+
["kok-gom", "gom"],
|
|
17
|
+
["koK-gom", "gom"],
|
|
18
|
+
["lv-ltg", "ltg"],
|
|
19
|
+
["sw-swc", "swc"],
|
|
20
|
+
["uz-uzs", "uzs"],
|
|
21
|
+
["zh-yue-cmn", "yue-cmn"],
|
|
22
|
+
["zh-yue-", "yue-"]
|
|
23
|
+
]) deepStrictEqual(normalizeLanguageTag(input), expected, input);
|
|
24
|
+
});
|
|
25
|
+
test("normalizeLanguageTag() leaves other tags unchanged", () => {
|
|
26
|
+
for (const tag of [
|
|
27
|
+
"",
|
|
28
|
+
"en",
|
|
29
|
+
"en-US",
|
|
30
|
+
"en-USA",
|
|
31
|
+
"en-yue",
|
|
32
|
+
"zh",
|
|
33
|
+
"zh-Hant-TW",
|
|
34
|
+
"yue",
|
|
35
|
+
"yue-HK",
|
|
36
|
+
"x-private",
|
|
37
|
+
"i-klingon",
|
|
38
|
+
"zh-min-nan",
|
|
39
|
+
"koK-gom",
|
|
40
|
+
"zh-yuK",
|
|
41
|
+
"constructor-yue",
|
|
42
|
+
"toString-yue"
|
|
43
|
+
]) deepStrictEqual(normalizeLanguageTag(tag), tag, tag);
|
|
44
|
+
});
|
|
45
|
+
//#endregion
|
|
46
|
+
export {};
|
|
@@ -3,7 +3,7 @@ let node_process = require("node:process");
|
|
|
3
3
|
node_process = require_chunk.__toESM(node_process);
|
|
4
4
|
//#region deno.json
|
|
5
5
|
var name = "@fedify/vocab-runtime";
|
|
6
|
-
var version = "2.0.31-dev.
|
|
6
|
+
var version = "2.0.31-dev.2308+34090b3a";
|
|
7
7
|
//#endregion
|
|
8
8
|
//#region src/request.ts
|
|
9
9
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-Do9eywBl.cjs");
|
|
2
|
-
const require_request = require("./request-
|
|
2
|
+
const require_request = require("./request-Ban3HXuI.cjs");
|
|
3
3
|
let node_test = require("node:test");
|
|
4
4
|
let node_process = require("node:process");
|
|
5
5
|
node_process = require_chunk.__toESM(node_process);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as version, r as getUserAgent } from "./request-
|
|
1
|
+
import { o as version, r as getUserAgent } from "./request-BV7yaWdl.mjs";
|
|
2
2
|
import { test } from "node:test";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { deepStrictEqual } from "node:assert";
|
package/package.json
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@context": {
|
|
3
|
+
"fep-22cd": "https://w3id.org/fep/22cd#",
|
|
4
|
+
"schema": "https://schema.org/",
|
|
5
|
+
"xsd": "http://www.w3.org/2001/XMLSchema#",
|
|
6
|
+
"translations": { "@id": "fep-22cd:translations", "@container": "@set" },
|
|
7
|
+
"Translation": "fep-22cd:Translation",
|
|
8
|
+
"sourceUpdated": { "@id": "fep-22cd:sourceUpdated", "@type": "xsd:dateTime" },
|
|
9
|
+
"translator": { "@id": "schema:translator", "@type": "@id", "@container": "@set" },
|
|
10
|
+
"inLanguage": "schema:inLanguage",
|
|
11
|
+
"translationOfWork": { "@id": "schema:translationOfWork", "@type": "@id" },
|
|
12
|
+
"isBasedOn": { "@id": "schema:isBasedOn", "@type": "@id" }
|
|
13
|
+
}
|
|
14
|
+
}
|
package/src/contexts.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// cSpell: disable
|
|
4
4
|
|
|
5
5
|
import cidV1Context from "./contexts/cid-v1.json" with { type: "json" };
|
|
6
|
+
import fep22cd from "./contexts/fep-22cd.json" with { type: "json" };
|
|
6
7
|
import fep6757 from "./contexts/fep-6757.json" with { type: "json" };
|
|
7
8
|
import fep7aa9 from "./contexts/fep-7aa9.json" with { type: "json" };
|
|
8
9
|
import fepEf61 from "./contexts/fep-ef61.json" with { type: "json" };
|
|
@@ -4353,6 +4354,13 @@ const preloadedContexts: Record<string, unknown> = {
|
|
|
4353
4354
|
// https://github.com/fedify-dev/fedify/issues/1211
|
|
4354
4355
|
"https://w3id.org/fep/6757": fep6757,
|
|
4355
4356
|
|
|
4357
|
+
// Resolve translation metadata locally even on versions without Translation.
|
|
4358
|
+
// Copied verbatim from the upstream context at the pinned revision below.
|
|
4359
|
+
// See: https://w3id.org/fep/22cd
|
|
4360
|
+
// https://github.com/fedify-dev/fedify/issues/1214
|
|
4361
|
+
// Source: https://codeberg.org/fediverse/fep/src/commit/7526048b018ccc0a7d05597a8f1eb33518373066/fep/22cd/fep-22cd.jsonld
|
|
4362
|
+
"https://w3id.org/fep/22cd": fep22cd,
|
|
4363
|
+
|
|
4356
4364
|
// Lemmy's context document is served as application/json without the JSON-LD
|
|
4357
4365
|
// context Link header. The default document loader treats that as a regular
|
|
4358
4366
|
// JSON response instead of a JSON-LD context, so every Lemmy activity that
|
package/src/docloader.test.ts
CHANGED
|
@@ -409,6 +409,88 @@ test("getDocumentLoader()", async (t) => {
|
|
|
409
409
|
deepStrictEqual(fetchMock.callHistory.calls(url).length, 0);
|
|
410
410
|
});
|
|
411
411
|
|
|
412
|
+
// A Codeberg Pages outage must not prevent expanding translation metadata.
|
|
413
|
+
// See: https://github.com/fedify-dev/fedify/issues/1214
|
|
414
|
+
fetchMock.get("https://w3id.org/fep/22cd", { status: 502 });
|
|
415
|
+
await t.test("preloaded FEP-22cd context", async () => {
|
|
416
|
+
const url = "https://w3id.org/fep/22cd";
|
|
417
|
+
const articleId = "https://example.com/articles/1";
|
|
418
|
+
const translatorId = "https://example.com/users/alice";
|
|
419
|
+
const revisionId = "https://example.com/articles/1/revisions/1";
|
|
420
|
+
deepStrictEqual(await fetchDocumentLoader(url), {
|
|
421
|
+
contextUrl: null,
|
|
422
|
+
documentUrl: url,
|
|
423
|
+
document: {
|
|
424
|
+
"@context": {
|
|
425
|
+
"fep-22cd": "https://w3id.org/fep/22cd#",
|
|
426
|
+
schema: "https://schema.org/",
|
|
427
|
+
xsd: "http://www.w3.org/2001/XMLSchema#",
|
|
428
|
+
translations: {
|
|
429
|
+
"@id": "fep-22cd:translations",
|
|
430
|
+
"@container": "@set",
|
|
431
|
+
},
|
|
432
|
+
Translation: "fep-22cd:Translation",
|
|
433
|
+
sourceUpdated: {
|
|
434
|
+
"@id": "fep-22cd:sourceUpdated",
|
|
435
|
+
"@type": "xsd:dateTime",
|
|
436
|
+
},
|
|
437
|
+
translator: {
|
|
438
|
+
"@id": "schema:translator",
|
|
439
|
+
"@type": "@id",
|
|
440
|
+
"@container": "@set",
|
|
441
|
+
},
|
|
442
|
+
inLanguage: "schema:inLanguage",
|
|
443
|
+
translationOfWork: {
|
|
444
|
+
"@id": "schema:translationOfWork",
|
|
445
|
+
"@type": "@id",
|
|
446
|
+
},
|
|
447
|
+
isBasedOn: { "@id": "schema:isBasedOn", "@type": "@id" },
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
});
|
|
451
|
+
deepStrictEqual(
|
|
452
|
+
await jsonld.expand({
|
|
453
|
+
"@context": ["https://www.w3.org/ns/activitystreams", url],
|
|
454
|
+
id: articleId,
|
|
455
|
+
type: "Article",
|
|
456
|
+
contentMap: { en: "Original", ko: "Translation" },
|
|
457
|
+
translations: [{
|
|
458
|
+
type: "Translation",
|
|
459
|
+
inLanguage: "ko",
|
|
460
|
+
translator: [translatorId],
|
|
461
|
+
translationOfWork: articleId,
|
|
462
|
+
sourceUpdated: "2026-09-01T00:00:00Z",
|
|
463
|
+
isBasedOn: revisionId,
|
|
464
|
+
}],
|
|
465
|
+
}, { documentLoader: fetchDocumentLoader }),
|
|
466
|
+
[{
|
|
467
|
+
"@id": articleId,
|
|
468
|
+
"@type": ["https://www.w3.org/ns/activitystreams#Article"],
|
|
469
|
+
"https://www.w3.org/ns/activitystreams#content": [
|
|
470
|
+
{ "@language": "en", "@value": "Original" },
|
|
471
|
+
{ "@language": "ko", "@value": "Translation" },
|
|
472
|
+
],
|
|
473
|
+
"https://w3id.org/fep/22cd#translations": [{
|
|
474
|
+
"@type": ["https://w3id.org/fep/22cd#Translation"],
|
|
475
|
+
"https://schema.org/inLanguage": [{ "@value": "ko" }],
|
|
476
|
+
"https://schema.org/translator": [{ "@id": translatorId }],
|
|
477
|
+
"https://schema.org/translationOfWork": [{ "@id": articleId }],
|
|
478
|
+
"https://w3id.org/fep/22cd#sourceUpdated": [{
|
|
479
|
+
"@type": "http://www.w3.org/2001/XMLSchema#dateTime",
|
|
480
|
+
"@value": "2026-09-01T00:00:00Z",
|
|
481
|
+
}],
|
|
482
|
+
"https://schema.org/isBasedOn": [{ "@id": revisionId }],
|
|
483
|
+
}],
|
|
484
|
+
}],
|
|
485
|
+
);
|
|
486
|
+
deepStrictEqual(fetchMock.callHistory.calls(url).length, 0);
|
|
487
|
+
deepStrictEqual(
|
|
488
|
+
fetchMock.callHistory.calls("https://www.w3.org/ns/activitystreams")
|
|
489
|
+
.length,
|
|
490
|
+
0,
|
|
491
|
+
);
|
|
492
|
+
});
|
|
493
|
+
|
|
412
494
|
await t.test("deny non-HTTP/HTTPS", async () => {
|
|
413
495
|
await rejects(
|
|
414
496
|
() => fetchDocumentLoader("ftp://localhost"),
|
package/src/docloader.ts
CHANGED
|
@@ -281,6 +281,7 @@ export interface GetDocumentLoaderOptions extends DocumentLoaderFactoryOptions {
|
|
|
281
281
|
* - <https://w3id.org/fep/ef61>
|
|
282
282
|
* - <https://w3id.org/fep/7aa9>
|
|
283
283
|
* - <https://w3id.org/fep/6757>
|
|
284
|
+
* - <https://w3id.org/fep/22cd>
|
|
284
285
|
* - <https://purl.archive.org/socialweb/webfinger>
|
|
285
286
|
* - <http://schema.org/>
|
|
286
287
|
* @param options Options for the document loader.
|
package/src/langstr.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { deepStrictEqual } from "node:assert";
|
|
1
|
+
import { deepStrictEqual, throws } from "node:assert";
|
|
2
2
|
import { test } from "node:test";
|
|
3
3
|
import util from "node:util";
|
|
4
4
|
import { LanguageString } from "./langstr.ts";
|
|
@@ -11,6 +11,23 @@ test("new LanguageString()", () => {
|
|
|
11
11
|
deepStrictEqual(new LanguageString("Hello", new Intl.Locale("en")), langStr);
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
+
test("new LanguageString() with extlang tags", () => {
|
|
15
|
+
// Intl.Locale objects have no own enumerable properties, so their base names
|
|
16
|
+
// are compared instead:
|
|
17
|
+
deepStrictEqual(new LanguageString("你好", "zh-YUE").locale.baseName, "yue");
|
|
18
|
+
deepStrictEqual(
|
|
19
|
+
new LanguageString("你好", "zh-yue-HK").locale.baseName,
|
|
20
|
+
"yue-HK",
|
|
21
|
+
);
|
|
22
|
+
// Whether cmn is canonicalized to zh depends on the runtime:
|
|
23
|
+
deepStrictEqual(
|
|
24
|
+
new LanguageString("你好", "zh-cmn-Hant").locale.baseName,
|
|
25
|
+
new Intl.Locale("cmn-Hant").baseName,
|
|
26
|
+
);
|
|
27
|
+
throws(() => new LanguageString("Hello", "en-USA"), RangeError);
|
|
28
|
+
throws(() => new LanguageString("你好", "zh-yue-"), RangeError);
|
|
29
|
+
});
|
|
30
|
+
|
|
14
31
|
test("Deno.inspect(LanguageString)", () => {
|
|
15
32
|
const langStr = new LanguageString("Hello, 'world'", "en");
|
|
16
33
|
deepStrictEqual(
|
package/src/langstr.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { normalizeLanguageTag } from "./langtag.ts";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* A language-tagged string which corresponds to the `rdf:langString` type.
|
|
3
5
|
*/
|
|
@@ -12,12 +14,14 @@ export class LanguageString extends String {
|
|
|
12
14
|
* Constructs a new `LanguageString`.
|
|
13
15
|
* @param value A string value written in the given language.
|
|
14
16
|
* @param language The language of the string. If a string is given, it will
|
|
15
|
-
* be parsed as a `Intl.Locale` object.
|
|
17
|
+
* be parsed as a `Intl.Locale` object. Extended language
|
|
18
|
+
* subtags are normalized first, e.g., `zh-yue` is parsed as
|
|
19
|
+
* `yue`.
|
|
16
20
|
*/
|
|
17
21
|
constructor(value: string, language: Intl.Locale | string) {
|
|
18
22
|
super(value);
|
|
19
23
|
this.locale = typeof language === "string"
|
|
20
|
-
? new Intl.Locale(language)
|
|
24
|
+
? new Intl.Locale(normalizeLanguageTag(language))
|
|
21
25
|
: language;
|
|
22
26
|
}
|
|
23
27
|
}
|