-
Notifications
You must be signed in to change notification settings - Fork 4
/
multilingual_element.js
156 lines (139 loc) · 6.46 KB
/
multilingual_element.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/**
* multilingual_element.js
*
* check that multiple elements for expressing multilingual values match DVB-I requirments
*/
import { datatypeIs } from "./phlib/phlib.js";
import { tva } from "./TVA_definitions.js";
import { WARNING, APPLICATION } from "./error_list.js";
import { isIn, CountChildElements } from "./utils.js";
import { keys } from "./common_errors.js";
const NO_DOCUMENT_LANGUAGE = "**"; // this should not be needed as @xml:lang is required in <ServiceList> and <TVAMain> root elements
/**
* check a language code and log its result
*
* @param {Object} validator the validation class to use
* @param {string} lang the language to check
* @param {string} loc the 'location' of the element containing the language value
* @param {XMLnode} element the element containing the language value
* @param {Object} errs the class where errors and warnings relating to the service list processing are stored
* @param {String} errCode the error code to be reported
* @returns {boolean} true if the specified language is valid
*/
export function checkLanguage(validator, lang, loc, element, errs, errCode) {
if (!validator) {
errs.addError({ type: APPLICATION, code: "CL000", message: `cannot validate language ${lang.quote()}${loc ? ` for ${loc.elementize()}` : ""}`, key: "no language validator" });
return false;
}
const validatorResp = validator.isKnown(lang);
let langOK = false;
switch (validatorResp.resp) {
case validator.languageKnown:
langOK = true;
break;
case validator.languageUnknown:
errs.addError({ code: `${errCode}-1`, message: `${loc ? loc : "language"} value ${lang.quote()} is invalid`, fragment: element, key: keys.k_InvalidLanguage });
break;
case validator.languageRedundant:
let msg = `${loc ? loc : "language"} value ${lang.quote()} is deprecated`;
if (validatorResp?.pref) msg += ` (use ${validatorResp.pref.quote()} instead)`;
errs.addError({
type: WARNING,
code: `${errCode}-2`,
message: msg,
fragment: element,
key: "deprecated language",
});
break;
case validator.languageNotSpecified:
errs.addError({ code: `${errCode}-3`, message: `${loc ? loc : "language"} value is not provided`, fragment: element, key: keys.k_UnspecifiedLanguage });
break;
case validator.languageInvalidType:
errs.addError({ code: `${errCode}-4`, message: `language is not a String, its "${datatypeIs(lang)}"`, fragment: element, key: keys.k_InvalidLanguage });
break;
}
return langOK;
}
/**
* Recurse up the XML element hierarchy until we find an element with an @xml:lang attribute or return a ficticouus
* value of topmost level element does not contain @xml:lang
*
* @param {XMLNode} node the multilingual element whose language is needed
* @returns {String} the value of the xml:lang attribute for the element, or the teh closest ancestor
*/
export function mlLanguage(node) {
if (node.type() != "element") return NO_DOCUMENT_LANGUAGE;
if (node.attr(tva.a_lang)) return node.attr(tva.a_lang).value();
return mlLanguage(node.parent());
}
/**
* checks that all the @xml:lang values for an element are unique and that only one instace of the element does not contain an xml:lang attribute
*
* @param {String} elementName The multilingual XML element to check
* @param {String} elementLocation The descriptive location of the element being checked (for reporting)
* @param {XMLnode} node The XML tree node containing the element being checked
* @param {Object} errs The class where errors and warnings relating to the service list processing are stored
* @param {String} errCode The error code to be reported
* @param {Object} validator The validation class for check the value of the language, or null if no check is to be performed
*/
export function checkXMLLangs(elementName, elementLocation, node, errs, errCode, validator = null) {
if (!node) {
errs.addError({ type: APPLICATION, code: "XL000", message: "checkXMLLangs() called with node==null" });
return;
}
let childElems = node.childNodes();
if ( CountChildElements(node, elementName) > 1) {
childElems.forEachSubElement((elem) => {
if (elem.name() == elementName) {
if (!elem.attr(tva.a_lang))
errs.addError({
code: `${errCode}-1`,
message: `xml:lang must be declared for each multilingual element for ${elementName.elementize()} in ${elementLocation}`,
fragment: elem,
key: "required @xml:lang",
});
}
});
}
let elementLanguages = [];
childElems.forEachSubElement((elem) => {
if (elem.name() == elementName) {
let lang = mlLanguage(elem);
if (isIn(elementLanguages, lang))
errs.addError({
code: `${errCode}-2`,
message: `${lang == NO_DOCUMENT_LANGUAGE ? "default language" : `xml:lang=${lang.quote()}`} already specifed for ${elementName.elementize()} in ${elementLocation}`,
fragment: elem,
key: "duplicate @xml:lang",
});
else elementLanguages.push(lang);
if (elem.text().length == 0)
errs.addError({
code: `${errCode}-3`,
message: `value must be specified for ${elem.parent().name().elementize()}${elem.name().elementize()}`,
fragment: elem,
key: "empty value",
});
// if lang is specified, validate the format and value of the attribute against BCP47 (RFC 5646)
if (elem.attr(tva.a_lang) && validator && lang != NO_DOCUMENT_LANGUAGE) checkLanguage(validator, lang, `xml:lang in ${elementName}`, elem, errs, `${errCode}-4`);
}
});
}
/**
* validate the language specified record any errors
*
* @param {XMLnode} node the XML node whose @lang attribute should be checked
* @param {boolean} isRequired report an error if @lang is not explicitly stated
* @param {Class} errs errors found in validaton
* @param {string} errCode error number to use
* @param {object} validator the validation class to use
* @returns {string} the @lang attribute of the node element of the parentLang if it does not exist of is not specified
*/
export function GetNodeLanguage(node, isRequired, errs, errCode, validator = null) {
if (!node) return NO_DOCUMENT_LANGUAGE;
if (isRequired && !node.attr(tva.a_lang))
errs.addError({ code: `${errCode}-1`, message: `${tva.a_lang.attribute()} is required for ${node.name().quote()}`, key: "unspecified language", line: node.line() });
let localLang = mlLanguage(node);
if (validator && node.attr(tva.a_lang) && localLang != NO_DOCUMENT_LANGUAGE) checkLanguage(validator, localLang, node.name(), node, errs, `${errCode}-2`);
return localLang;
}