<commit_before>import { dispatch as d3_dispatch } from 'd3-dispatch';
import { select as d3_select } from 'd3-selection';
import * as countryCoder from '@ideditor/country-coder';

import { presetManager } from '../../presets';
import { fileFetcher } from '../../core/file_fetcher';
import { t, localizer } from '../../core/localizer';
import { services } from '../../services';
import { svgIcon } from '../../svg';
import { uiTooltip } from '../tooltip';
import { uiCombobox } from '../combobox';
import { utilArrayUniq, utilEditDistance, utilGetSetValue, utilNoAuto, utilRebind, utilTotalExtent, utilUniqueDomId } from '../../util';

var _languagesArray = [];


export function uiFieldLocalized(field, context) {
    var dispatch = d3_dispatch('change', 'input');
    var wikipedia = services.wikipedia;
    var input = d3_select(null);
    var localizedInputs = d3_select(null);
    var _countryCode;
    var _tags;


    // A concern here in switching to async data means that _languagesArray will not
    // be available the first time through, so things like the fetchers and
    // the language() function will not work immediately.
    fileFetcher.get('languages')
        .then(loadLanguagesArray)
        .catch(function() { /* ignore */ });

    var _territoryLanguages = {};
    fileFetcher.get('territory_languages')
        .then(function(d) { _territoryLanguages = d; })
        .catch(function() { /* ignore */ });


    var allSuggestions = presetManager.collection.filter(function(p) {
        return p.suggestion === true;
    });

    // reuse these combos
    var langCombo = uiCombobox(context, 'localized-lang')
        .fetcher(fetchLanguages)
        .minItems(0);

    var brandCombo = uiCombobox(context, 'localized-brand')
        .canAutocomplete(false)
        .minItems(1);

    var _selection = d3_select(null);
    var _multilingual = [];
    var _buttonTip = uiTooltip()
        .title(t.html('translate.translate'))
        .placement('left');
    var _wikiTitles;
    var _entityIDs = [];


    function loadLanguagesArray(dataLanguages) {
        if (_languagesArray.length !== 0) return;

        // some conversion is needed to ensure correct OSM tags are used
        var replacements = {
            sr: 'sr-Cyrl',      // in OSM, `sr` implies Cyrillic
            'sr-Cyrl': false    // `sr-Cyrl` isn't used in OSM
        };

        for (var code in dataLanguages) {
            if (replacements[code] === false) continue;
            var metaCode = code;
            if (replacements[code]) metaCode = replacements[code];

            _languagesArray.push({
                localName: localizer.languageName(metaCode, { localOnly: true }),
                nativeName: dataLanguages[metaCode].nativeName,
                code: code,
                label: localizer.languageName(metaCode)
            });
        }
    }


    function calcLocked() {

        // only lock the Name field
        var isLocked = field.id === 'name' &&
            _entityIDs.length &&
            // lock the field if any feature needs it
            _entityIDs.some(function(entityID) {

                var entity = context.graph().hasEntity(entityID);
                if (!entity) return false;

                var original = context.graph().base().entities[_entityIDs[0]];
                var hasOriginalName = original && entity.tags.name && entity.tags.name === original.tags.name;
                // if the name was already edited manually then allow further editing
                if (!hasOriginalName) return false;

                // features linked to Wikidata are likely important and should be protected
                if (entity.tags.wikidata) return true;

                // assume the name has already been confirmed if its source has been researched
                if (entity.tags['name:etymology:wikidata']) return true;

                var preset = presetManager.match(entity, context.graph());
                var isSuggestion = preset && preset.suggestion;
                var showsBrand = preset && preset.originalFields.filter(function(d) {
                    return d.id === 'brand';
                }).length;
                // protect standardized brand names
                return isSuggestion && !showsBrand;
            });

        field.locked(isLocked);
    }


    // update _multilingual, maintaining the existing order
    function calcMultilingual(tags) {
        var existingLangsOrdered = _multilingual.map(function(item) {
            return item.lang;
        });
        var existingLangs = new Set(existingLangsOrdered.filter(Boolean));

        for (var k in tags) {
            var m = k.match(/^(.*):([a-zA-Z_-]+)$/);
            if (m && m[1] === field.key && m[2]) {
                var item = { lang: m[2], value: tags[k] };
                if (existingLangs.has(item.lang)) {
                    // update the value
                    _multilingual[existingLangsOrdered.indexOf(item.lang)].value = item.value;
                    existingLangs.delete(item.lang);
                } else {
                    _multilingual.push(item);
                }
            }
        }

        _multilingual = _multilingual.filter(function(item) {
            return !item.lang || !existingLangs.has(item.lang);
        });
    }


    function localized(selection) {
        _selection = selection;
        calcLocked();
        var isLocked = field.locked();
        var singularEntity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
        var preset = singularEntity && presetManager.match(singularEntity, context.graph());

        var wrap = selection.selectAll('.form-field-input-wrap')
            .data([0]);
<commit_msg>Show multilingual values in the Name field even for keys with spaces or other unexpected characters (re: #8165)<commit_after>import { dispatch as d3_dispatch } from 'd3-dispatch';
import { select as d3_select } from 'd3-selection';
import * as countryCoder from '@ideditor/country-coder';

import { presetManager } from '../../presets';
import { fileFetcher } from '../../core/file_fetcher';
import { t, localizer } from '../../core/localizer';
import { services } from '../../services';
import { svgIcon } from '../../svg';
import { uiTooltip } from '../tooltip';
import { uiCombobox } from '../combobox';
import { utilArrayUniq, utilEditDistance, utilGetSetValue, utilNoAuto, utilRebind, utilTotalExtent, utilUniqueDomId } from '../../util';

var _languagesArray = [];


export function uiFieldLocalized(field, context) {
    var dispatch = d3_dispatch('change', 'input');
    var wikipedia = services.wikipedia;
    var input = d3_select(null);
    var localizedInputs = d3_select(null);
    var _countryCode;
    var _tags;


    // A concern here in switching to async data means that _languagesArray will not
    // be available the first time through, so things like the fetchers and
    // the language() function will not work immediately.
    fileFetcher.get('languages')
        .then(loadLanguagesArray)
        .catch(function() { /* ignore */ });

    var _territoryLanguages = {};
    fileFetcher.get('territory_languages')
        .then(function(d) { _territoryLanguages = d; })
        .catch(function() { /* ignore */ });


    var allSuggestions = presetManager.collection.filter(function(p) {
        return p.suggestion === true;
    });

    // reuse these combos
    var langCombo = uiCombobox(context, 'localized-lang')
        .fetcher(fetchLanguages)
        .minItems(0);

    var brandCombo = uiCombobox(context, 'localized-brand')
        .canAutocomplete(false)
        .minItems(1);

    var _selection = d3_select(null);
    var _multilingual = [];
    var _buttonTip = uiTooltip()
        .title(t.html('translate.translate'))
        .placement('left');
    var _wikiTitles;
    var _entityIDs = [];


    function loadLanguagesArray(dataLanguages) {
        if (_languagesArray.length !== 0) return;

        // some conversion is needed to ensure correct OSM tags are used
        var replacements = {
            sr: 'sr-Cyrl',      // in OSM, `sr` implies Cyrillic
            'sr-Cyrl': false    // `sr-Cyrl` isn't used in OSM
        };

        for (var code in dataLanguages) {
            if (replacements[code] === false) continue;
            var metaCode = code;
            if (replacements[code]) metaCode = replacements[code];

            _languagesArray.push({
                localName: localizer.languageName(metaCode, { localOnly: true }),
                nativeName: dataLanguages[metaCode].nativeName,
                code: code,
                label: localizer.languageName(metaCode)
            });
        }
    }


    function calcLocked() {

        // only lock the Name field
        var isLocked = field.id === 'name' &&
            _entityIDs.length &&
            // lock the field if any feature needs it
            _entityIDs.some(function(entityID) {

                var entity = context.graph().hasEntity(entityID);
                if (!entity) return false;

                var original = context.graph().base().entities[_entityIDs[0]];
                var hasOriginalName = original && entity.tags.name && entity.tags.name === original.tags.name;
                // if the name was already edited manually then allow further editing
                if (!hasOriginalName) return false;

                // features linked to Wikidata are likely important and should be protected
                if (entity.tags.wikidata) return true;

                // assume the name has already been confirmed if its source has been researched
                if (entity.tags['name:etymology:wikidata']) return true;

                var preset = presetManager.match(entity, context.graph());
                var isSuggestion = preset && preset.suggestion;
                var showsBrand = preset && preset.originalFields.filter(function(d) {
                    return d.id === 'brand';
                }).length;
                // protect standardized brand names
                return isSuggestion && !showsBrand;
            });

        field.locked(isLocked);
    }


    // update _multilingual, maintaining the existing order
    function calcMultilingual(tags) {
        var existingLangsOrdered = _multilingual.map(function(item) {
            return item.lang;
        });
        var existingLangs = new Set(existingLangsOrdered.filter(Boolean));

        for (var k in tags) {
            var m = k.match(/^(.*):(.+)$/);
            if (m && m[1] === field.key && m[2]) {
                var item = { lang: m[2], value: tags[k] };
                if (existingLangs.has(item.lang)) {
                    // update the value
                    _multilingual[existingLangsOrdered.indexOf(item.lang)].value = item.value;
                    existingLangs.delete(item.lang);
                } else {
                    _multilingual.push(item);
                }
            }
        }

        _multilingual = _multilingual.filter(function(item) {
            return !item.lang || !existingLangs.has(item.lang);
        });
    }


    function localized(selection) {
        _selection = selection;
        calcLocked();
        var isLocked = field.locked();
        var singularEntity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
        var preset = singularEntity && presetManager.match(singularEntity, context.graph());

        var wrap = selection.selectAll('.form-field-input-wrap')
            .data([0]);

{'fraction_non_alphanumeric': 0.11826931414202722, 'fraction_numerical': 0.003468308332610769, 'mean_word_length': 2.1513661202185794, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4601443', 'n_tokens_mistral': 3239, 'n_tokens_neox': 3066, 'n_words': 815}