Index: trunk/data/boundaries.osm
===================================================================
--- trunk/data/boundaries.osm	(revision 15564)
+++ trunk/data/boundaries.osm	(revision 15565)
@@ -25384,4 +25384,5 @@
     <tag k='maxspeed:urban' v='50' />
     <tag k='name:en' v='Czechia' />
+    <tag k='taginfo' v='https://taginfo.openstreetmap.cz' />
   </way>
   <way id='-28998'>
@@ -28995,4 +28996,5 @@
     <tag k='maxspeed:urban' v='50' />
     <tag k='name:en' v='Hungary' />
+    <tag k='taginfo' v='http://taginfo.openstreetmap.hu' />
   </way>
   <way id='-29068'>
@@ -29651,4 +29653,5 @@
     <tag k='driving_side' v='left' />
     <tag k='name:en' v='India' />
+    <tag k='taginfo' v='https://taginfo.openstreetmap.in' />
   </way>
   <way id='-29070'>
@@ -35827,4 +35830,5 @@
     <tag k='maxspeed:urban' v='50' />
     <tag k='name:en' v='Poland' />
+    <tag k='taginfo' v='http://taginfo.openstreetmap.pl' />
   </way>
   <way id='-29170'>
@@ -37952,4 +37956,5 @@
     <tag k='maxspeed:urban' v='50' />
     <tag k='name:en' v='Sweden' />
+    <tag k='taginfo' v='http://se.taginfo.openstreetmap.se' />
   </way>
   <way id='-29212'>
@@ -39569,4 +39574,5 @@
     <tag k='name:en' v='Taiwan' />
     <tag k='note' v='Self admin.; Claimed by China' />
+    <tag k='taginfo' v='https://taginfo.osm.kcwu.csie.org' />
   </way>
   <way id='-29238'>
@@ -42429,4 +42435,5 @@
     <tag k='driving_side' v='left' />
     <tag k='name:en' v='Japan' />
+    <tag k='taginfo' v='http://taginfo.openstreetmap.jp' />
   </way>
   <way id='-29312'>
@@ -58367,4 +58374,5 @@
     <tag k='maxspeed:urban' v='50' />
     <tag k='name:en' v='France' />
+    <tag k='taginfo' v='https://taginfo.openstreetmap.fr' />
     <tag k='type' v='multipolygon' />
   </relation>
@@ -58399,4 +58407,5 @@
     <tag k='maxspeed:urban' v='30 mph' />
     <tag k='name:en' v='United Kingdom' />
+    <tag k='taginfo' v='https://taginfo.openstreetmap.org.uk' />
     <tag k='type' v='multipolygon' />
   </relation>
@@ -58644,4 +58653,5 @@
     <tag k='maxspeed:urban' v='50' />
     <tag k='name:en' v='Switzerland' />
+    <tag k='taginfo' v='https://taginfo.osm.ch' />
     <tag k='type' v='multipolygon' />
   </relation>
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java	(revision 15564)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java	(revision 15565)
@@ -28,8 +28,10 @@
 import java.util.TreeSet;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
 
 import javax.swing.AbstractAction;
 import javax.swing.JComponent;
 import javax.swing.JLabel;
+import javax.swing.JMenuItem;
 import javax.swing.JPanel;
 import javax.swing.JPopupMenu;
@@ -109,4 +111,5 @@
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.Shortcut;
+import org.openstreetmap.josm.tools.Territories;
 import org.openstreetmap.josm.tools.Utils;
 
@@ -172,4 +175,7 @@
     private final transient PopupMenuHandler blankSpaceMenuHandler = new PopupMenuHandler(blankSpaceMenu);
 
+    private final List<JMenuItem> tagMenuTagInfoNatItems = new ArrayList<>();
+    private final List<JMenuItem> membershipMenuTagInfoNatItems = new ArrayList<>();
+
     private final transient Map<String, Map<String, Integer>> valueCount = new TreeMap<>();
     /**
@@ -183,4 +189,5 @@
     private final TaginfoAction taginfoAction = new TaginfoAction(tagTable, editHelper::getDataKey, editHelper::getDataValues,
             membershipTable, x -> (IRelation<?>) membershipData.getValueAt(x, 0));
+    private final Collection<TaginfoAction> taginfoNationalActions = new ArrayList<>();
     private final PasteValueAction pasteValueAction = new PasteValueAction();
     private final CopyValueAction copyValueAction = new CopyValueAction(
@@ -348,4 +355,26 @@
     }
 
+    private void destroyTaginfoNationalActions() {
+        membershipMenuTagInfoNatItems.forEach(membershipMenu::remove);
+        membershipMenuTagInfoNatItems.clear();
+        tagMenuTagInfoNatItems.forEach(tagMenu::remove);
+        tagMenuTagInfoNatItems.clear();
+        taginfoNationalActions.forEach(JosmAction::destroy);
+        taginfoNationalActions.clear();
+    }
+
+    private void setupTaginfoNationalActions(Collection<? extends IPrimitive> newSel) {
+        destroyTaginfoNationalActions();
+        if (!newSel.isEmpty()) {
+            for (Entry<String, String> e : Territories.getNationalTaginfoUrls(
+                    newSel.iterator().next().getBBox().getCenter()).entrySet()) {
+                taginfoNationalActions.add(new TaginfoAction(tagTable, editHelper::getDataKey, editHelper::getDataValues,
+                        membershipTable, x -> (IRelation<?>) membershipData.getValueAt(x, 0), e.getValue(), e.getKey()));
+            }
+            membershipMenuTagInfoNatItems.addAll(taginfoNationalActions.stream().map(membershipMenu::add).collect(Collectors.toList()));
+            tagMenuTagInfoNatItems.addAll(taginfoNationalActions.stream().map(tagMenu::add).collect(Collectors.toList()));
+        }
+    }
+
     /**
      * Creates the popup menu @field membershipMenu and its launcher on membership table.
@@ -538,4 +567,5 @@
     public void destroy() {
         taginfoAction.destroy();
+        destroyTaginfoNationalActions();
         super.destroy();
         Config.getPref().removeKeyPreferenceChangeListener("display.discardable-keys", preferenceListener);
@@ -558,7 +588,7 @@
         // Ignore parameter as we do not want to operate always on real selection here, especially in draw mode
         Collection<? extends IPrimitive> newSel = OsmDataManager.getInstance().getInProgressISelection();
-        String selectedTag;
+        int newSelSize = newSel.size();
         IRelation<?> selectedRelation = null;
-        selectedTag = editHelper.getChangedKey(); // select last added or last edited key by default
+        String selectedTag = editHelper.getChangedKey(); // select last added or last edited key by default
         if (selectedTag == null && tagTable.getSelectedRowCount() == 1) {
             selectedTag = editHelper.getDataKey(tagTable.getSelectedRow());
@@ -598,6 +628,6 @@
                 count += e1.getValue();
             }
-            if (count < newSel.size()) {
-                e.getValue().put("", newSel.size() - count);
+            if (count < newSelSize) {
+                e.getValue().put("", newSelSize - count);
             }
             tagData.addRow(new Object[]{e.getKey(), e.getValue()});
@@ -655,4 +685,5 @@
         pluginHook.setVisible(hasSelection);
 
+        setupTaginfoNationalActions(newSel);
         autoresizeTagTable();
 
@@ -669,7 +700,7 @@
 
         if (tagData.getRowCount() != 0 || membershipData.getRowCount() != 0) {
-            if (newSel.size() > 1) {
+            if (newSelSize > 1) {
                 setTitle(tr("Objects: {2} / Tags: {0} / Memberships: {1}",
-                    tagData.getRowCount(), membershipData.getRowCount(), newSel.size()));
+                    tagData.getRowCount(), membershipData.getRowCount(), newSelSize));
             } else {
                 setTitle(tr("Tags: {0} / Memberships: {1}",
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/properties/TaginfoAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/properties/TaginfoAction.java	(revision 15564)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/properties/TaginfoAction.java	(revision 15565)
@@ -23,5 +23,5 @@
 public class TaginfoAction extends JosmAction {
 
-    final transient StringProperty TAGINFO_URL_PROP = new StringProperty("taginfo.url", "https://taginfo.openstreetmap.org/");
+    private static final StringProperty TAGINFO_URL_PROP = new StringProperty("taginfo.url", "https://taginfo.openstreetmap.org/");
 
     private final JTable tagTable;
@@ -29,4 +29,5 @@
     private final IntFunction<Map<String, Integer>> tagValuesSupplier;
 
+    private final String taginfoUrl;
     private final JTable membershipTable;
     private final IntFunction<IRelation<?>> memberValueSupplier;
@@ -43,5 +44,23 @@
     public TaginfoAction(JTable tagTable, IntFunction<String> tagKeySupplier, IntFunction<Map<String, Integer>> tagValuesSupplier,
             JTable membershipTable, IntFunction<IRelation<?>> memberValueSupplier) {
-        super(tr("Go to Taginfo"), "dialogs/taginfo", tr("Launch browser with Taginfo statistics for selected object"), null, false);
+        this(tagTable, tagKeySupplier, tagValuesSupplier, membershipTable, memberValueSupplier, TAGINFO_URL_PROP.get(), null);
+    }
+
+    /**
+     * Constructs a new {@code TaginfoAction} with a given URL and optional name suffix.
+     * @param tagTable The tag table. Cannot be null
+     * @param tagKeySupplier Finds the key from given row of tag table. Cannot be null
+     * @param tagValuesSupplier Finds the values from given row of tag table (map of values and number of occurrences). Cannot be null
+     * @param membershipTable The membership table. Can be null
+     * @param memberValueSupplier Finds the parent relation from given row of membership table. Can be null
+     * @param taginfoUrl Taginfo URL. Cannot be null
+     * @param suffix Optional name suffix, can be null
+     * @since 15565
+     */
+    public TaginfoAction(JTable tagTable, IntFunction<String> tagKeySupplier, IntFunction<Map<String, Integer>> tagValuesSupplier,
+            JTable membershipTable, IntFunction<IRelation<?>> memberValueSupplier, String taginfoUrl, String suffix) {
+        super(tr("Go to Taginfo") + (suffix != null ? " " + suffix : ""), "dialogs/taginfo",
+                tr("Launch browser with Taginfo statistics for selected object"), null, false);
+        this.taginfoUrl = taginfoUrl.endsWith("/") ? taginfoUrl : taginfoUrl + '/';
         this.tagTable = Objects.requireNonNull(tagTable);
         this.tagKeySupplier = Objects.requireNonNull(tagKeySupplier);
@@ -59,12 +78,12 @@
             Map<String, Integer> values = tagValuesSupplier.apply(row);
             if (values.size() == 1) {
-                url = TAGINFO_URL_PROP.get() + "tags/" + key
+                url = taginfoUrl + "tags/" + key
                         + '=' + Utils.encodeUrl(values.keySet().iterator().next()).replaceAll("\\+", "%20");
             } else {
-                url = TAGINFO_URL_PROP.get() + "keys/" + key;
+                url = taginfoUrl + "keys/" + key;
             }
         } else if (membershipTable != null && membershipTable.getSelectedRowCount() == 1) {
             final String type = (memberValueSupplier.apply(membershipTable.getSelectedRow())).get("type");
-            url = TAGINFO_URL_PROP.get() + "relations/" + type;
+            url = taginfoUrl + "relations/" + type;
         } else {
             return;
Index: trunk/src/org/openstreetmap/josm/gui/layer/AbstractOsmDataLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/AbstractOsmDataLayer.java	(revision 15564)
+++ trunk/src/org/openstreetmap/josm/gui/layer/AbstractOsmDataLayer.java	(revision 15565)
@@ -34,3 +34,14 @@
         return getDataSet().isLocked();
     }
+
+    /**
+     * Clears the data backing this layer, unless if locked.
+     * @since 15565
+     */
+    public void clear() {
+        OsmData<?, ?, ?, ?> data = getDataSet();
+        if (data != null && !data.isLocked()) {
+            data.clear();
+        }
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/layer/LayerManager.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/LayerManager.java	(revision 15564)
+++ trunk/src/org/openstreetmap/josm/gui/layer/LayerManager.java	(revision 15565)
@@ -12,5 +12,4 @@
 import java.util.function.Consumer;
 
-import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.util.GuiHelper;
@@ -480,7 +479,5 @@
         }
         if (layer instanceof OsmDataLayer) {
-            DataSet data = ((OsmDataLayer) layer).data;
-            if (data != null && !data.isLocked())
-                data.clear();
+            ((OsmDataLayer) layer).clear();
         }
         return e.scheduleForRemoval;
Index: trunk/src/org/openstreetmap/josm/gui/preferences/advanced/AdvancedPreference.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/advanced/AdvancedPreference.java	(revision 15564)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/advanced/AdvancedPreference.java	(revision 15565)
@@ -40,8 +40,11 @@
 import org.openstreetmap.josm.data.Preferences;
 import org.openstreetmap.josm.data.PreferencesUtils;
+import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.dialogs.LogShowDialog;
 import org.openstreetmap.josm.gui.help.HelpUtil;
 import org.openstreetmap.josm.gui.io.CustomConfigurator;
+import org.openstreetmap.josm.gui.layer.MainLayerManager;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting;
 import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
@@ -56,4 +59,5 @@
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Territories;
 import org.openstreetmap.josm.tools.Utils;
 
@@ -70,4 +74,55 @@
         public PreferenceSetting createPreferenceSetting() {
             return new AdvancedPreference();
+        }
+    }
+
+    private static class UnclearableOsmDataLayer extends OsmDataLayer {
+        UnclearableOsmDataLayer(DataSet data, String name) {
+            super(data, name, null);
+        }
+
+        @Override
+        public void clear() {
+            // Do nothing
+        }
+    }
+
+    private final class EditBoundariesAction extends AbstractAction {
+        EditBoundariesAction() {
+            super(tr("Edit boundaries"));
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent ae) {
+            DataSet dataSet = Territories.getOriginalDataSet();
+            MainLayerManager layerManager = MainApplication.getLayerManager();
+            if (layerManager.getLayersOfType(OsmDataLayer.class).stream().noneMatch(l -> dataSet.equals(l.getDataSet()))) {
+                layerManager.addLayer(new UnclearableOsmDataLayer(dataSet, tr("Internal JOSM boundaries")));
+            }
+        }
+    }
+
+    private final class ResetPreferencesAction extends AbstractAction {
+        ResetPreferencesAction() {
+            super(tr("Reset preferences"));
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent ae) {
+            if (!GuiHelper.warnUser(tr("Reset preferences"),
+                    "<html>"+
+                    tr("You are about to clear all preferences to their default values<br />"+
+                    "All your settings will be deleted: plugins, imagery, filters, toolbar buttons, keyboard, etc. <br />"+
+                    "Are you sure you want to continue?")
+                    +"</html>", null, "")) {
+                Preferences.main().resetToDefault();
+                try {
+                    Preferences.main().save();
+                } catch (IOException | InvalidPathException e) {
+                    Logging.log(Logging.LEVEL_WARN, "Exception while saving preferences:", e);
+                }
+                readPreferences(Preferences.main());
+                applyFilter();
+            }
         }
     }
@@ -333,24 +388,7 @@
         menu.add(getProfileMenu());
         menu.addSeparator();
-        menu.add(new AbstractAction(tr("Reset preferences")) {
-            @Override
-            public void actionPerformed(ActionEvent ae) {
-                if (!GuiHelper.warnUser(tr("Reset preferences"),
-                        "<html>"+
-                        tr("You are about to clear all preferences to their default values<br />"+
-                        "All your settings will be deleted: plugins, imagery, filters, toolbar buttons, keyboard, etc. <br />"+
-                        "Are you sure you want to continue?")
-                        +"</html>", null, "")) {
-                    Preferences.main().resetToDefault();
-                    try {
-                        Preferences.main().save();
-                    } catch (IOException | InvalidPathException e) {
-                        Logging.log(Logging.LEVEL_WARN, "Exception while saving preferences:", e);
-                    }
-                    readPreferences(Preferences.main());
-                    applyFilter();
-                }
-            }
-        });
+        menu.add(new EditBoundariesAction());
+        menu.addSeparator();
+        menu.add(new ResetPreferencesAction());
         return menu;
     }
Index: trunk/src/org/openstreetmap/josm/tools/Territories.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/Territories.java	(revision 15564)
+++ trunk/src/org/openstreetmap/josm/tools/Territories.java	(revision 15565)
@@ -11,5 +11,8 @@
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
 
 import org.openstreetmap.josm.data.coor.LatLon;
@@ -32,8 +35,10 @@
     private static final String ISO3166_1 = "ISO3166-1:alpha2";
     private static final String ISO3166_2 = "ISO3166-2";
+    private static final String TAGINFO = "taginfo";
 
     private static DataSet dataSet;
 
     private static volatile Map<String, GeoPropertyIndex<Boolean>> iso3166Cache;
+    private static volatile Map<String, String> taginfoCache;
 
     private Territories() {
@@ -78,6 +83,15 @@
 
     /**
-     * Returns the territories dataset.
-     * @return the territories dataset
+     * Returns the original territories dataset. Be extra cautious when manipulating it!
+     * @return the original territories dataset
+     * @since 15565
+     */
+    public static synchronized DataSet getOriginalDataSet() {
+        return dataSet;
+    }
+
+    /**
+     * Returns a copy of the territories dataset.
+     * @return a copy of the territories dataset
      */
     public static synchronized DataSet getDataSet() {
@@ -91,4 +105,5 @@
     public static synchronized void initialize() {
         iso3166Cache = new HashMap<>();
+        taginfoCache = new TreeMap<>();
         try (CachedFile cf = new CachedFile("resource://data/" + FILENAME);
                 InputStream is = cf.getInputStream()) {
@@ -109,4 +124,8 @@
                     if (iso1 != null) {
                         iso3166Cache.put(iso1, gpi);
+                        String taginfo = osm.get(TAGINFO);
+                        if (taginfo != null) {
+                            taginfoCache.put(iso1, taginfo);
+                        }
                     }
                     if (iso2 != null) {
@@ -119,3 +138,23 @@
         }
     }
+
+    /**
+     * Returns a map of national taginfo instances for the given location.
+     * @param ll lat/lon where to look.
+     * @return a map of national taginfo instances for the given location (code / url)
+     * @since 15565
+     */
+    public static Map<String, String> getNationalTaginfoUrls(LatLon ll) {
+        Map<String, String> result = new TreeMap<>();
+        for (String code : iso3166Cache.entrySet().parallelStream().distinct()
+            .filter(e -> Boolean.TRUE.equals(e.getValue().get(ll)))
+            .map(Entry<String, GeoPropertyIndex<Boolean>>::getKey)
+            .collect(Collectors.toSet())) {
+            String taginfo = taginfoCache.get(code);
+            if (taginfo != null) {
+                result.put(code, taginfo);
+            }
+        }
+        return result;
+    }
 }
