The map sheets are generated once, in English, so in an Arabic or Chinese packet page 1's map labelled every country in Latin capitals, and its privacy line was English. The labels are now replaced as the sheet is lifted into the packet. They are recognised by the generator's own rule rather than a list: generate.mjs draws each label as esc(ISO3_NAME[iso3].toUpperCase()) from the zone data's ISO3_NAME table, so inverting that table under that transform names the country behind every label it can draw. The inverse is unambiguous (162 labels, no two countries sharing one), so a label is known exactly or left as drawn. The map's privacy line is word for word the packet's privacy_note, so it takes this language's. A width guard: the generator placed every label and then dropped any that would collide, measuring the English name, so a translation is fitted into that same box - estimated wider, it shrinks in proportion and cannot reach a neighbour the English name did not. On the Sudan poster, six Arabic names shrink (the longest, Saudi Arabia, from 15px to 10px); none of the narrower Chinese ones do. Capitals' tracking is dropped for scripts without capitals, where it breaks Arabic joining. English is untouched: its relabeller is null, and the lifted sheet is byte-identical with and without it. No sheet or poster is regenerated - the change is in the lift, so the tracked 3x4 posters page 1 now uses are relabelled as they are. Both print paths supply the relabeller: the UPG 100 profile from its own ISO3_NAME and the library's country-name resolver, the offline generator from the same table and the injected ISO-3 to ISO-2 table. check-map-labels (gates:pre), on the tracked Sudan poster: English byte-identical; in ar and zh no known English name left, halo matching fill, privacy localized; the guard fires for Arabic, not Chinese, and grows nothing; Arabic tracking off, Spanish kept; an unknown label left alone. Mutation-tested five ways, each caught.
39 lines
2.4 KiB
JavaScript
39 lines
2.4 KiB
JavaScript
/**
|
|
* mapLabels — name the countries on a generated map sheet in the packet's language, at print time.
|
|
*
|
|
* The map sheets (posters, and the map-only packet page) are generated once, in English: every country
|
|
* on the map is labelled with its English name in capitals. In an Arabic or Chinese packet those labels
|
|
* printed in Latin capitals. Regenerating a sheet per language would multiply the artwork by eleven; so
|
|
* instead the label is recognised on the way into the packet and replaced.
|
|
*
|
|
* ★ RECOGNISED BY THE GENERATOR'S OWN RULE, NOT A HAND-MADE LIST. generate.mjs writes each label as
|
|
* esc(ISO3_NAME[iso3].toUpperCase()), from the ISO3_NAME table in the zone data. Inverting that same
|
|
* table under that same transform gives, for every label the generator can draw, the country it names.
|
|
* The inverse is unambiguous (no two countries share a label), so a label is either known exactly or
|
|
* left alone — never guessed.
|
|
*
|
|
* Pure and zero-import: the caller injects the table, the country-name resolver its runtime can load,
|
|
* and the language, so the browser print and the offline generator run the same code.
|
|
*
|
|
* @param {{iso3Name: Object<string,string>, countryName: (iso3:string, lang:string)=>string, lang: string}} o
|
|
* @returns {null | ((labelText:string) => (string|null))}
|
|
* null for English — the sheets are already English, and returning no relabeller is what keeps an
|
|
* English packet's output byte-identical. Otherwise a function from a label as it appears in the
|
|
* sheet to the localized name, or null for a label it does not know (which is then left as drawn).
|
|
*/
|
|
export function makeCountryLabelFor({ iso3Name, countryName, lang } = {}) {
|
|
const lc = String(lang || 'en').split('-')[0].toLowerCase()
|
|
if (lc === 'en' || !iso3Name || typeof countryName !== 'function') return null
|
|
const esc = (s) => String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"')
|
|
const byLabel = new Map()
|
|
for (const [iso3, name] of Object.entries(iso3Name)) byLabel.set(esc(String(name).toUpperCase()), iso3)
|
|
return (labelText) => {
|
|
const iso3 = byLabel.get(String(labelText))
|
|
if (!iso3) return null
|
|
const name = countryName(iso3, lc)
|
|
if (!name || name === iso3) return null
|
|
// Capitals as the sheet draws them, in the language's own casing rules; scripts without case
|
|
// (Arabic, Devanagari, Chinese) come back unchanged.
|
|
return esc(name.toLocaleUpperCase(lc))
|
|
}
|
|
}
|