]> git.openstreetmap.org Git - rails.git/commitdiff
Merge remote-tracking branch 'upstream/pull/5349' master
authorTom Hughes <tom@compton.nu>
Fri, 22 Nov 2024 17:51:49 +0000 (17:51 +0000)
committerTom Hughes <tom@compton.nu>
Fri, 22 Nov 2024 17:51:49 +0000 (17:51 +0000)
27 files changed:
Gemfile.lock
app/abilities/api_capability.rb
app/assets/javascripts/index/note.js
app/assets/javascripts/leaflet.map.js
app/assets/javascripts/osm.js.erb
app/controllers/api/note_subscriptions_controller.rb [new file with mode: 0644]
app/views/notes/show.html.erb
config/layers.yml [new file with mode: 0644]
config/locales/da.yml
config/locales/de.yml
config/locales/el.yml
config/locales/en.yml
config/locales/fr.yml
config/locales/gl.yml
config/locales/he.yml
config/locales/hi.yml
config/locales/it.yml
config/locales/ko.yml
config/locales/lb.yml
config/locales/nl.yml
config/locales/ru.yml
config/locales/scn.yml
config/locales/skr-arab.yml
config/locales/zh-CN.yml
config/routes.rb
test/controllers/api/note_subscriptions_controller_test.rb [new file with mode: 0644]
test/system/note_comments_test.rb

index 5d15e550c55d1dd8bcfdf3bec73091af8ae130bf..f23429253244a3da32469ae3ea07de20545cb629 100644 (file)
@@ -92,16 +92,16 @@ GEM
     autoprefixer-rails (10.4.19.0)
       execjs (~> 2)
     aws-eventstream (1.3.0)
-    aws-partitions (1.1004.0)
-    aws-sdk-core (3.212.0)
+    aws-partitions (1.1009.0)
+    aws-sdk-core (3.213.0)
       aws-eventstream (~> 1, >= 1.3.0)
       aws-partitions (~> 1, >= 1.992.0)
       aws-sigv4 (~> 1.9)
       jmespath (~> 1, >= 1.6.1)
-    aws-sdk-kms (1.95.0)
+    aws-sdk-kms (1.96.0)
       aws-sdk-core (~> 3, >= 3.210.0)
       aws-sigv4 (~> 1.5)
-    aws-sdk-s3 (1.170.1)
+    aws-sdk-s3 (1.172.0)
       aws-sdk-core (~> 3, >= 3.210.0)
       aws-sdk-kms (~> 1)
       aws-sigv4 (~> 1.5)
@@ -265,14 +265,14 @@ GEM
     factory_bot_rails (6.4.4)
       factory_bot (~> 6.5)
       railties (>= 5.0.0)
-    faraday (2.12.0)
-      faraday-net_http (>= 2.0, < 3.4)
+    faraday (2.12.1)
+      faraday-net_http (>= 2.0, < 3.5)
       json
       logger
     faraday-http-cache (2.5.1)
       faraday (>= 0.8)
-    faraday-net_http (3.3.0)
-      net-http
+    faraday-net_http (3.4.0)
+      net-http (>= 0.5.0)
     ffi (1.17.0)
     ffi-compiler (1.3.2)
       ffi (>= 1.15.5)
@@ -339,12 +339,12 @@ GEM
       rails-dom-testing (>= 1, < 3)
       railties (>= 4.2.0)
       thor (>= 0.14, < 2.0)
-    json (2.8.1)
+    json (2.8.2)
     jwt (2.9.3)
       base64
     kgio (2.11.4)
-    kramdown (2.4.0)
-      rexml
+    kramdown (2.5.1)
+      rexml (>= 3.3.9)
     kramdown-parser-gfm (1.1.0)
       kramdown (~> 2.0)
     language_server-protocol (3.17.0.3)
@@ -370,7 +370,7 @@ GEM
     maxminddb (0.1.22)
     mini_magick (4.13.2)
     mini_mime (1.1.5)
-    mini_portile2 (2.8.7)
+    mini_portile2 (2.8.8)
     mini_racer (0.9.0)
       libv8-node (~> 18.19.0.0)
     minitest (5.25.1)
@@ -531,7 +531,7 @@ GEM
     rb-inotify (0.11.1)
       ffi (~> 1.0)
     rchardet (1.8.0)
-    rdoc (6.7.0)
+    rdoc (6.8.1)
       psych (>= 4.0.0)
     regexp_parser (2.9.2)
     reline (0.5.11)
@@ -554,7 +554,7 @@ GEM
       rubocop-ast (>= 1.32.2, < 2.0)
       ruby-progressbar (~> 1.7)
       unicode-display_width (>= 2.4.0, < 3.0)
-    rubocop-ast (1.35.0)
+    rubocop-ast (1.36.1)
       parser (>= 3.3.1.0)
     rubocop-capybara (2.21.0)
       rubocop (~> 1.41)
@@ -563,7 +563,7 @@ GEM
     rubocop-minitest (0.36.0)
       rubocop (>= 1.61, < 2.0)
       rubocop-ast (>= 1.31.1, < 2.0)
-    rubocop-performance (1.22.1)
+    rubocop-performance (1.23.0)
       rubocop (>= 1.48.1, < 2.0)
       rubocop-ast (>= 1.31.1, < 2.0)
     rubocop-rails (2.27.0)
index d8be136438efded684104cbf9dfd0728b27221c8..0e953d50b11c95f2294bb2ec31f752b22a1966dc 100644 (file)
@@ -9,6 +9,7 @@ class ApiCapability
 
       if user&.active?
         can [:create, :comment, :close, :reopen], Note if scope?(token, :write_notes)
+        can [:create, :destroy], NoteSubscription if scope?(token, :write_notes)
         can [:show, :data], Trace if scope?(token, :read_gpx)
         can [:create, :update, :destroy], Trace if scope?(token, :write_gpx)
         can [:details], User if scope?(token, :read_prefs)
index 24d24f8aef082ec3d17a649ec7c2ad328fcdc1ae..4e8f64d09c4684ae1827fabf1b6bef95b628ad6a 100644 (file)
@@ -37,31 +37,35 @@ OSM.Note = function (map) {
   };
 
   function initialize(path, id) {
-    content.find("button[type=submit]").on("click", function (e) {
+    content.find("button[name]").on("click", function (e) {
       e.preventDefault();
       var data = $(e.target).data();
-      var form = e.target.form;
-
-      $(form).find("button[type=submit]").prop("disabled", true);
-
-      $.ajax({
+      var name = $(e.target).attr("name");
+      var ajaxSettings = {
         url: data.url,
         type: data.method,
         oauth: true,
-        data: { text: $(form.text).val() },
-        success: function () {
-          OSM.loadSidebarContent(path, function () {
+        success: () => {
+          OSM.loadSidebarContent(path, () => {
             initialize(path, id);
             moveToNote();
           });
         },
-        error: function (xhr) {
-          $(form).find("#comment-error")
+        error: (xhr) => {
+          content.find("#comment-error")
             .text(xhr.responseText)
-            .prop("hidden", false);
-          updateButtons(form);
+            .prop("hidden", false)
+            .get(0).scrollIntoView({ block: "nearest" });
+          updateButtons();
         }
-      });
+      };
+
+      if (name !== "subscribe" && name !== "unsubscribe") {
+        ajaxSettings.data = { text: $("textarea").val() };
+      }
+
+      content.find("button[name]").prop("disabled", true);
+      $.ajax(ajaxSettings);
     });
 
     content.find("textarea").on("input", function (e) {
@@ -82,14 +86,16 @@ OSM.Note = function (map) {
     }
   }
 
-  function updateButtons(form) {
-    $(form).find("button[type=submit]").prop("disabled", false);
-    if ($(form.text).val() === "") {
-      $(form.close).text($(form.close).data("defaultActionText"));
-      $(form.comment).prop("disabled", true);
+  function updateButtons() {
+    var resolveButton = content.find("button[name='close']");
+    var commentButton = content.find("button[name='comment']");
+
+    content.find("button[name]").prop("disabled", false);
+    if (content.find("textarea").val() === "") {
+      resolveButton.text(resolveButton.data("defaultActionText"));
+      commentButton.prop("disabled", true);
     } else {
-      $(form.close).text($(form.close).data("commentActionText"));
-      $(form.comment).prop("disabled", false);
+      resolveButton.text(resolveButton.data("commentActionText"));
     }
   }
 
index a907de29581c548d004038f6b61cb2520aaebf93..78f43b73a0b7d180eb9288664828db223059ecf2 100644 (file)
@@ -15,106 +15,24 @@ L.OSM.Map = L.Map.extend({
   initialize: function (id, options) {
     L.Map.prototype.initialize.call(this, id, options);
 
-    var copyright_link = $("<a>", {
-      href: "/copyright",
-      text: I18n.t("javascripts.map.openstreetmap_contributors")
-    }).prop("outerHTML");
-    var copyright = I18n.t("javascripts.map.copyright_text", { copyright_link: copyright_link });
-
-    var donate = $("<a>", {
-      "href": "https://supporting.openstreetmap.org",
-      "class": "donate-attr",
-      "text": I18n.t("javascripts.map.make_a_donation")
-    }).prop("outerHTML");
-
-    var terms = $("<a>", {
-      href: "https://wiki.osmfoundation.org/wiki/Terms_of_Use",
-      text: I18n.t("javascripts.map.website_and_api_terms")
-    }).prop("outerHTML");
-
-    var cyclosm_link = $("<a>", {
-      href: "https://www.cyclosm.org",
-      target: "_blank",
-      text: I18n.t("javascripts.map.cyclosm_name")
-    }).prop("outerHTML");
-    var osm_france_link = $("<a>", {
-      href: "https://openstreetmap.fr/",
-      target: "_blank",
-      text: I18n.t("javascripts.map.osm_france")
-    }).prop("outerHTML");
-    var cyclosm = I18n.t("javascripts.map.cyclosm_credit", { cyclosm_link: cyclosm_link, osm_france_link: osm_france_link });
-
-    var thunderforest_link = $("<a>", {
-      href: "https://www.thunderforest.com/",
-      target: "_blank",
-      text: I18n.t("javascripts.map.andy_allan")
-    }).prop("outerHTML");
-    var thunderforest = I18n.t("javascripts.map.thunderforest_credit", { thunderforest_link: thunderforest_link });
-
-    var tracestrack_link = $("<a>", {
-      href: "https://www.tracestrack.com/",
-      target: "_blank",
-      text: I18n.t("javascripts.map.tracestrack")
-    }).prop("outerHTML");
-    var tracestrack = I18n.t("javascripts.map.tracestrack_credit", { tracestrack_link: tracestrack_link });
-
-    var hotosm_link = $("<a>", {
-      href: "https://www.hotosm.org/",
-      target: "_blank",
-      text: I18n.t("javascripts.map.hotosm_name")
-    }).prop("outerHTML");
-    var hotosm = I18n.t("javascripts.map.hotosm_credit", { hotosm_link: hotosm_link, osm_france_link: osm_france_link });
-
     this.baseLayers = [];
 
-    this.baseLayers.push(new L.OSM.Mapnik({
-      attribution: copyright + " &hearts; " + donate + ". " + terms,
-      code: "M",
-      keyid: "mapnik",
-      name: I18n.t("javascripts.map.base.standard")
-    }));
-
-    this.baseLayers.push(new L.OSM.CyclOSM({
-      attribution: copyright + ". " + cyclosm + ". " + terms,
-      code: "Y",
-      keyid: "cyclosm",
-      name: I18n.t("javascripts.map.base.cyclosm")
-    }));
-
-    if (OSM.THUNDERFOREST_KEY) {
-      this.baseLayers.push(new L.OSM.CycleMap({
-        attribution: copyright + ". " + thunderforest + ". " + terms,
-        apikey: OSM.THUNDERFOREST_KEY,
-        code: "C",
-        keyid: "cyclemap",
-        name: I18n.t("javascripts.map.base.cycle_map")
-      }));
-
-      this.baseLayers.push(new L.OSM.TransportMap({
-        attribution: copyright + ". " + thunderforest + ". " + terms,
-        apikey: OSM.THUNDERFOREST_KEY,
-        code: "T",
-        keyid: "transportmap",
-        name: I18n.t("javascripts.map.base.transport_map")
-      }));
-    }
+    for (const layerDefinition of OSM.LAYER_DEFINITIONS) {
+      if (layerDefinition.apiKeyId && !OSM[layerDefinition.apiKeyId]) continue;
 
-    if (OSM.TRACESTRACK_KEY) {
-      this.baseLayers.push(new L.OSM.TracestrackTopo({
-        attribution: copyright + ". " + tracestrack + ". " + terms,
-        apikey: OSM.TRACESTRACK_KEY,
-        code: "P",
-        keyid: "tracestracktopo",
-        name: I18n.t("javascripts.map.base.tracestracktop_topo")
-      }));
-    }
+      const layerOptions = {
+        attribution: makeAttribution(layerDefinition.credit),
+        code: layerDefinition.code,
+        keyid: layerDefinition.keyId,
+        name: I18n.t(`javascripts.map.base.${layerDefinition.nameId}`)
+      };
+      if (layerDefinition.apiKeyId) {
+        layerOptions.apikey = OSM[layerDefinition.apiKeyId];
+      }
 
-    this.baseLayers.push(new L.OSM.HOT({
-      attribution: copyright + ". " + hotosm + ". " + terms,
-      code: "H",
-      keyid: "hot",
-      name: I18n.t("javascripts.map.base.hot")
-    }));
+      const layer = new L.OSM[layerDefinition.leafletOsmId](layerOptions);
+      this.baseLayers.push(layer);
+    }
 
     this.noteLayer = new L.FeatureGroup();
     this.noteLayer.options = { code: "N" };
@@ -132,6 +50,50 @@ L.OSM.Map = L.Map.extend({
         this.setMaxZoom(event.layer.options.maxZoom);
       }
     });
+
+    function makeAttribution(credit) {
+      let attribution = "";
+
+      attribution += I18n.t("javascripts.map.copyright_text", {
+        copyright_link: $("<a>", {
+          href: "/copyright",
+          text: I18n.t("javascripts.map.openstreetmap_contributors")
+        }).prop("outerHTML")
+      });
+
+      attribution += credit.donate ? " &hearts; " : ". ";
+      attribution += makeCredit(credit);
+      attribution += ". ";
+
+      attribution += $("<a>", {
+        href: "https://wiki.osmfoundation.org/wiki/Terms_of_Use",
+        text: I18n.t("javascripts.map.website_and_api_terms")
+      }).prop("outerHTML");
+
+      return attribution;
+    }
+
+    function makeCredit(credit) {
+      const children = {};
+      for (const childId in credit.children) {
+        children[childId] = makeCredit(credit.children[childId]);
+      }
+      const text = I18n.t(`javascripts.map.${credit.id}`, children);
+      if (credit.href) {
+        const link = $("<a>", {
+          href: credit.href,
+          text: text
+        });
+        if (credit.donate) {
+          link.addClass("donate-attr");
+        } else {
+          link.attr("target", "_blank");
+        }
+        return link.prop("outerHTML");
+      } else {
+        return text;
+      }
+    }
   },
 
   updateLayers: function (layerParam) {
index e08528f845da2b2cee72780d3197720fbc42ab02..e9c09c79f9ae995575030ad9231167c64983f974 100644 (file)
@@ -1,5 +1,6 @@
 //= depend_on settings.yml
 //= depend_on settings.local.yml
+//= depend_on layers.yml
 //= depend_on key.yml
 //= require qs/dist/qs
 
@@ -30,6 +31,7 @@ OSM = {
   TRACESTRACK_KEY:         <%= Settings.tracestrack_key.to_json %>,
 <% end %>
 
+  LAYER_DEFINITIONS:       <%= YAML.load_file(Rails.root.join("config/layers.yml")).to_json %>,
   LAYERS_WITH_MAP_KEY:     <%= YAML.load_file(Rails.root.join("config/key.yml")).keys.to_json %>,
 
   MARKER_GREEN:            <%= image_path("marker-green.png").to_json %>,
diff --git a/app/controllers/api/note_subscriptions_controller.rb b/app/controllers/api/note_subscriptions_controller.rb
new file mode 100644 (file)
index 0000000..c416dd8
--- /dev/null
@@ -0,0 +1,27 @@
+module Api
+  class NoteSubscriptionsController < ApiController
+    before_action :check_api_writable
+    before_action :authorize
+
+    authorize_resource
+
+    def create
+      note_id = params[:note_id].to_i
+      note = Note.find(note_id)
+      note.subscribers << current_user
+    rescue ActiveRecord::RecordNotFound
+      report_error "Note #{note_id} not found.", :not_found
+    rescue ActiveRecord::RecordNotUnique
+      report_error "You are already subscribed to note #{note_id}.", :conflict
+    end
+
+    def destroy
+      note_id = params[:note_id].to_i
+      note = Note.find(note_id)
+      count = note.subscriptions.where(:user => current_user).delete_all
+      report_error "You are not subscribed to note #{note_id}.", :not_found if count.zero?
+    rescue ActiveRecord::RecordNotFound
+      report_error "Note #{note_id} not found.", :not_found
+    end
+  end
+end
index 4aa2e7fd345d196b9b3d0f4ad215b9a6950b6432..8dda65c25dc556fa5d095d3492da272fd801fb41 100644 (file)
     <p class='alert alert-warning'><%= t ".anonymous_warning" %></p>
   <% end -%>
 
+  <div class="row">
+    <div class="col">
+      <h4><%= t(".discussion") %></h4>
+    </div>
+
+    <% if current_user %>
+      <div class="col-auto">
+        <% if @note.subscribers.exists?(current_user.id) %>
+          <%= tag.button t(".unsubscribe"),
+                         :type => "button",
+                         :class => "btn btn-sm btn-primary",
+                         :name => "unsubscribe",
+                         :data => { :method => "DELETE",
+                                    :url => api_note_subscription_path(@note) } %>
+        <% else %>
+          <%= tag.button t(".subscribe"),
+                         :type => "button",
+                         :class => "btn btn-sm btn-primary",
+                         :name => "subscribe",
+                         :data => { :method => "POST",
+                                    :url => api_note_subscription_path(@note) } %>
+        <% end %>
+      </div>
+    <% end %>
+  </div>
+
   <% if @note_comments.length > 1 %>
     <div class='note-comments'>
       <ul class="list-unstyled">
diff --git a/config/layers.yml b/config/layers.yml
new file mode 100644 (file)
index 0000000..3b5bc94
--- /dev/null
@@ -0,0 +1,72 @@
+- leafletOsmId: "Mapnik"
+  code: "M"
+  keyId: "mapnik"
+  nameId: "standard"
+  credit:
+    id: "make_a_donation"
+    href: "https://supporting.openstreetmap.org"
+    donate: true
+
+- leafletOsmId: "CyclOSM"
+  code: "Y"
+  keyId: "cyclosm"
+  nameId: "cyclosm"
+  credit:
+    id: "cyclosm_credit"
+    children:
+      cyclosm_link:
+        id: "cyclosm_name"
+        href: "https://www.cyclosm.org"
+      osm_france_link:
+        id: "osm_france"
+        href: "https://openstreetmap.fr/"
+
+- leafletOsmId: "CycleMap"
+  code: "C"
+  keyId: "cyclemap"
+  nameId: "cycle_map"
+  apiKeyId: "THUNDERFOREST_KEY"
+  credit:
+    id: "thunderforest_credit"
+    children:
+      thunderforest_link:
+        id: "andy_allan"
+        href: "https://www.thunderforest.com/"
+
+- leafletOsmId: "TransportMap"
+  code: "T"
+  keyId: "transportmap"
+  nameId: "transport_map"
+  apiKeyId: "THUNDERFOREST_KEY"
+  credit:
+    id: "thunderforest_credit"
+    children:
+      thunderforest_link:
+        id: "andy_allan"
+        href: "https://www.thunderforest.com/"
+
+- leafletOsmId: "TracestrackTopo"
+  code: "P"
+  keyId: "tracestracktopo"
+  nameId: "tracestracktop_topo"
+  apiKeyId: "TRACESTRACK_KEY"
+  credit:
+    id: "tracestrack_credit"
+    children:
+      tracestrack_link:
+        id: "tracestrack"
+        href: "https://www.tracestrack.com/"
+
+- leafletOsmId: "HOT"
+  code: "H"
+  keyId: "hot"
+  nameId: "hot"
+  credit:
+    id: "hotosm_credit"
+    children:
+      hotosm_link:
+        id: "hotosm_name"
+        href: "https://www.hotosm.org/"
+      osm_france_link:
+        id: "osm_france"
+        href: "https://openstreetmap.fr/"
index 6a56a87e922b18a675ef2b8d88276a7646d323a7..285cd0c02485b700ed6092229df08ff82dca8ec7 100644 (file)
@@ -3054,6 +3054,11 @@ da:
       description: Beskrivelse
       created_at: Oprettet den
       last_changed: Sidst ændret
+      apply: Anvend
+      all: Alle
+      open: Åben
+      closed: Lukket
+      status: Status
     show:
       title: 'Bemærkning: %{id}'
       description: Beskrivelse
index d3b2089e29debdc3c4ff9f1ed3084e1044f82ec6..f943e40d91e0e63bad00dec75a0a4c5f3444fa96 100644 (file)
@@ -3216,6 +3216,11 @@ de:
       description: Hinweis
       created_at: Erstellt am
       last_changed: Zuletzt geändert
+      apply: Anwenden
+      all: Alle
+      open: Offen
+      closed: Geschlossen
+      status: Status
     show:
       title: 'Hinweis: %{id}'
       description: Beschreibung
index a1056ee0937d1ada9aa2b6b843d822d57566ac53..b7201fdc1de72fea888542e324a661cec4691a37 100644 (file)
@@ -3140,6 +3140,11 @@ el:
       description: Περιγραφή
       created_at: Δημιουργήθηκε στις
       last_changed: Τελευταία αλλαγή
+      apply: Εφαρμογή
+      all: Όλα
+      open: Ανοιχτές
+      closed: Κλειστές
+      status: Κατάσταση
     show:
       title: 'Σημείωση: %{id}'
       description: Περιγραφή
@@ -3162,7 +3167,7 @@ el:
       resolve: Επιλύστε
       reactivate: Επανενεργοποίηση
       comment_and_resolve: Σχολιάστε και Επιλύστε
-      comment: Σχολιάζω
+      comment: Σχολιάστε
       log_in_to_comment: Συνδεθείτε για να σχολιάσετε σε αυτή την σημείωση
       report_link_html: Εάν αυτή η σημείωση περιέχει ευαίσθητες πληροφορίες που πρέπει
         να αφαιρεθούν, μπορείτε %{link}.
index 3f2a4cb1408c7337ef826ef5b6451154b717ad29..2a92fbb63c65ecf4dda142b567c35943971e4efb 100644 (file)
@@ -2989,6 +2989,9 @@ en:
       report: report this note
       coordinates_html: "%{latitude}, %{longitude}"
       anonymous_warning: This note includes comments from anonymous users which should be independently verified.
+      discussion: Discussion
+      subscribe: Subscribe
+      unsubscribe: Unsubscribe
       hide: Hide
       resolve: Resolve
       reactivate: Reactivate
index b13f6a59a096720e53e0ee02dcff6f2e02e7b94b..3fedf0a7ed1457d3e149dc33ce263a45004e2746 100644 (file)
@@ -3244,6 +3244,11 @@ fr:
       description: Description
       created_at: Créée le
       last_changed: Dernière modification
+      apply: Appliquer
+      all: Tous
+      open: Ouvert
+      closed: Fermé
+      status: État
     show:
       title: 'Note : %{id}'
       description: Description
index daeb120c505da0cc30fd066bafbc8f021ee85dbf..3a30766a6e22c20baacf69fd411f847cf5f8136f 100644 (file)
@@ -1839,7 +1839,7 @@ gl:
       old_messages:
         one: '%{count} mensaxe vella'
         other: '%{count} mensaxes vellas'
-      no_messages_yet_html: Aínda non ten ningunha mensaxe. Por que non se pon en
+      no_messages_yet_html: Aínda non tes ningunha mensaxe. Por que non te pos en
         contacto con algunhas das %{people_mapping_nearby_link}?
       people_mapping_nearby: persoas preto de ti
     messages_table:
@@ -1859,19 +1859,19 @@ gl:
       back_to_inbox: Volver á caixa de entrada
     create:
       message_sent: Mensaxe enviada
-      limit_exceeded: Estivo enviando unha morea de mensaxes recentemente. Agarde
+      limit_exceeded: Enviaches unha morea de mensaxes dun tempo a esta parte. Agarda
         uns intres denantes de tentar enviar máis.
     no_such_message:
       title: Non se atopou a mensaxe
       heading: Non se atopou a mensaxe
-      body: Non hai ningunha mensaxe con esa ID.
+      body: Non hai ningunha mensaxe con ese identificador.
     outbox:
       title: Caixa de saída
       messages:
         one: Enviaches %{count} mensaxe
         other: Enviaches %{count} mensaxes
-      no_sent_messages_html: Aínda non enviou ningunha mensaxe. Por que non se pon
-        en contacto con algunhas das %{people_mapping_nearby_link}?
+      no_sent_messages_html: Aínda non enviaches ningunha mensaxe. Por que non te
+        pos en contacto con algunhas das %{people_mapping_nearby_link}?
       people_mapping_nearby: persoas preto de ti
     muted:
       title: Mensaxes silenciadas
@@ -2942,10 +2942,9 @@ gl:
       not_revoke_admin_current_user: Non se puido revogar os dereitos de administrador
         do usuario actual.
     grant:
-      are_you_sure: Ten a certeza de querer concederlle o rol "%{role}" ó usuario
-        "%{name}"?
+      are_you_sure: Queres concederlle o rol "%{role}" ó usuario "%{name}"?
     revoke:
-      are_you_sure: Ten a certeza de querer revogarlle o rol "%{role}" ó usuario "%{name}"?
+      are_you_sure: Queres quitarlle o rol "%{role}" ó usuario "%{name}"?
   user_blocks:
     model:
       non_moderator_update: Cómpre ser moderador para crear ou actualizar un bloqueo.
@@ -3086,6 +3085,11 @@ gl:
       description: Descrición
       created_at: Creado o
       last_changed: Última modificación
+      apply: Aplicar
+      all: Todas
+      open: Abertas
+      closed: Pechadas
+      status: Estado
     show:
       title: 'Nota: %{id}'
       description: Descrición
index 134a954f7f3e099a86f972a638cc02046067ed2d..0061608fe3e90857a2154f9210420f53faae4317 100644 (file)
@@ -3052,6 +3052,11 @@ he:
       description: תיאור
       created_at: 'יצירה:'
       last_changed: 'שינוי אחרון:'
+      apply: החלה
+      all: הכול
+      open: פתוחה
+      closed: סגורה
+      status: מצב
     show:
       title: 'הערה: %{id}'
       description: תיאור
index 93d0352107eddf76d9a5d6af98c7739cc0c1d58a..c4ba070b964c51541906565e940730606d854f5b 100644 (file)
@@ -1043,6 +1043,11 @@ hi:
     index:
       created_at: 'निर्माण का समय:'
       last_changed: अंतिम परिवर्तन
+      apply: लागू करें
+      all: सभी
+      open: खोलें
+      closed: बंद किया हुआ
+      status: स्थिति
     show:
       title: 'नोट: %{id}'
       description: विवरण
index 07442a854f04209c3a32a410b10be973c4ca9c9f..d450771755639a8180407acf1d372ea14843d977 100644 (file)
@@ -3121,6 +3121,11 @@ it:
       description: Descrizione
       created_at: Creata il
       last_changed: Modificata per l'ultima volta
+      apply: Applica
+      all: Tutto
+      open: Aperto
+      closed: Chiuso
+      status: Stato
     show:
       title: 'Nota: %{id}'
       description: Descrizione
index 10cf572b4951df4a7b68d626ad892f63b7e92d4b..c11e169af8ccf6e6c3f8808a337c9e1229aa52b5 100644 (file)
@@ -624,6 +624,7 @@ ko:
   doorkeeper:
     errors:
       messages:
+        account_selection_required: 인가 서버에는 최종 사용자의 계정 선택이 필요합니다
         consent_required: 인가 서버에는 최종 사용자의 동의가 필요합니다
         interaction_required: 인가 서버에는 최종 사용자의 소통이 필요합니다
         login_required: 인가 서버에는 최종 사용자의 인증이 필요합니다
@@ -650,6 +651,7 @@ ko:
         알려주십시오. 요청의 정확한 URL을 기록해 두십시오.
     bad_request:
       title: 잘못된 요청
+      description: 오픈스트리트맵 서버에서 요청하신 작업이 유효하지 않습니다 (HTTP 400)
     forbidden:
       title: 접근 거부됨
       description: 오픈스트리트맵 서버에 요청한 이 작업은 관리자만 사용할 수 있습니다(HTTP 403).
@@ -2775,6 +2777,11 @@ ko:
       description: 설명
       created_at: 만든 날짜
       last_changed: 마지막으로 바뀜
+      apply: 적용
+      all: 모두
+      open: 열림
+      closed: 닫힘
+      status: 상태
     show:
       title: '참고: %{id}'
       description: 설명
index 8fcd800e14f9dc94640de7edb9fa6cfb10ff45d4..794ad1daeb66c943e1d65ce62845d824004d0f36 100644 (file)
@@ -2354,6 +2354,7 @@ lb:
       no_notes: Keng Notizen
       description: Beschreiwung
       last_changed: Lescht Ännerung
+      status: Status
     show:
       title: 'Notiz: %{id}'
       description: Beschreiwung
index 043e378d1731716533a4157a904614284d50a862..51ece7d7a908ec0ec800c8cadf2ba76120c84813 100644 (file)
@@ -480,15 +480,33 @@ nl:
       introduction: Klik op de kaart om nabije objecten te vinden.
       nearby: Objecten in de buurt
       enclosing: Omsluitende objecten
+  nodes:
+    timeout:
+      sorry: Het ophalen van de gegevens voor het ID %{id} duurde te lang.
   old_nodes:
     not_found:
       sorry: 'Sorry, knooppunt #%{id} versie %{version} kon niet worden gevonden.'
+    timeout:
+      sorry: Het ophalen van de gegevens voor knooppunt met het ID %{id} duurde te
+        lang.
+  ways:
+    timeout:
+      sorry: Het ophalen van de gegevens voor de weg met het ID %{id} duurde te lang.
   old_ways:
     not_found:
       sorry: 'Sorry, weg #%{id} versie %{version} niet kon worden gevonden.'
+    timeout:
+      sorry: Het ophalen van de geschiedenis voor de weg met het ID %{id} duurde te
+        lang.
+  relations:
+    timeout:
+      sorry: Het ophalen van de relatie met het ID %{id} duurde te lang.
   old_relations:
     not_found:
       sorry: 'Sorry, relatie #%{id} versie %{version} kon niet worden gevonden.'
+    timeout:
+      sorry: Het ophalen van de geschiedenis voor de relatie met het ID %{id} duurde
+        te lang.
   changeset_comments:
     feeds:
       comment:
@@ -2482,6 +2500,9 @@ nl:
           hospital: Ziekenhuis
           building: Belangrijk gebouw
           station: Spoorwegstation
+          railway_halt: Spoorweghalte
+          subway_station: Metrostation
+          tram_stop: Tramhalte
           summit: Top
           peak: Piek
           tunnel: Tunnel
@@ -2839,6 +2860,7 @@ nl:
       readable_summary: voor mensen leesbare samenvatting
       informal_translations: informele vertalingen
       continue: Doorgaan
+      cancel: Annuleren
       you need to accept or decline: Lees de nieuwe Bijdragersvoorwaarden en besluit
         daarna deze te accepteren of te verwerpen voordat u door kunt gaan.
       legale_select: 'Selecteer het land waarin u woont:'
@@ -3106,6 +3128,11 @@ nl:
       description: Beschrijving
       created_at: Aangemaakt op
       last_changed: Laatste wijziging
+      apply: Toepassen
+      all: Alle
+      open: Open
+      closed: Gesloten
+      status: Status
     show:
       title: 'Opmerking: %{id}'
       description: Beschrijving
@@ -3153,6 +3180,8 @@ nl:
       add: Opmerking toevoegen
     notes_paging_nav:
       showing_page: Pagina %{page}
+      next: Volgende
+      previous: Vorige
   javascripts:
     close: Sluiten
     share:
@@ -3346,6 +3375,7 @@ nl:
       empty: Geen weer te geven redigeringen.
       heading: Lijst met redigeringen
       title: Lijst met redigeringen
+      new: Nieuwe redactie
     new:
       heading: Gegevens voor nieuwe redigering invoeren
       title: Aanmaak van een nieuwe redigering
index 8b2df06a290b817ea684251f41300db910eb5df5..bab4cc319651c246a3c5502bc0525db6138fca36 100644 (file)
@@ -77,6 +77,7 @@
 # Author: Okras
 # Author: Pacha Tchernof
 # Author: Parukhin
+# Author: Pavel200071
 # Author: Perevod16
 # Author: PlushBoy
 # Author: Pplex.vhs
@@ -1970,18 +1971,18 @@ ru:
     muted:
       title: Заглушённые сообщения
     reply:
-      wrong_user: Ð\92Ñ\8b Ð²Ð¾Ñ\88ли ÐºÐ°Ðº `%{user}', Ð½Ð¾ Ð¾Ñ\82веÑ\82 Ð½Ð° Ð²Ð°Ñ\88 Ð²Ð¾Ð¿Ñ\80оÑ\81 Ð±Ñ\8bл Ð¾Ñ\82пÑ\80авлен Ð½е
-        этому пользователю. Пожалуйста, войдите как соответствующий вашему вопросу
-        Ð¿Ð¾Ð»Ñ\8cзоваÑ\82елÑ\8c, Ñ\87Ñ\82обÑ\8b Ð¿Ñ\80оÑ\87иÑ\82аÑ\82Ñ\8c Ð¾Ñ\82веÑ\82.
+      wrong_user: Ð\92Ñ\8b Ð²Ð¾Ñ\88ли Ð² Ñ\81иÑ\81Ñ\82емÑ\83 Ð¿Ð¾Ð´ Ð¸Ð¼ÐµÐ½ÐµÐ¼ '%{user}', Ð½Ð¾ Ñ\81ообÑ\89ение, Ð½Ð° ÐºÐ¾Ñ\82оÑ\80ое
+        вы просили ответить, не было отправлено этому пользователю. Пожалуйста, войдите
+        Ð² Ñ\81иÑ\81Ñ\82емÑ\83 Ð¿Ð¾Ð´ Ð¿Ñ\80авилÑ\8cнÑ\8bм Ð¸Ð¼ÐµÐ½ÐµÐ¼, Ñ\87Ñ\82обÑ\8b Ð¾Ñ\82веÑ\82иÑ\82Ñ\8c.
     show:
       title: Просмотр сообщения
       reply_button: Ответить
       unread_button: Пометить как непрочитанное
       destroy_button: Удалить
       back: Назад
-      wrong_user: Ð\92Ñ\8b Ð²Ð¾Ñ\88ли ÐºÐ°Ðº Ð¿Ð¾Ð»Ñ\8cзоваÑ\82елÑ\8c `%{user}', Ð½Ð¾ Ð¾Ñ\82веÑ\82 Ð½Ð° Ð²Ð°Ñ\88 Ð²Ð¾Ð¿Ñ\80оÑ\81 Ð±Ñ\8bл
-        Ð¾Ñ\82пÑ\80авлен Ð½Ðµ Ñ\8dÑ\82им Ð¸Ð»Ð¸ Ð½Ðµ Ñ\8dÑ\82омÑ\83 Ð¿Ð¾Ð»Ñ\8cзоваÑ\82елÑ\8e. Ð\9fожалÑ\83йÑ\81Ñ\82а, Ð²Ð¾Ð¹Ð´Ð¸Ñ\82е ÐºÐ°Ðº Ñ\81ооÑ\82веÑ\82Ñ\81Ñ\82вÑ\83Ñ\8eÑ\89ий
-        вашему вопросу пользователь, чтобы прочитать его.
+      wrong_user: Ð\92Ñ\8b Ð²Ð¾Ñ\88ли Ð² Ñ\81иÑ\81Ñ\82емÑ\83 Ð¿Ð¾Ð´ Ð¸Ð¼ÐµÐ½ÐµÐ¼ '%{user}', Ð½Ð¾ Ñ\81ообÑ\89ение, ÐºÐ¾Ñ\82оÑ\80ое Ð²Ñ\8b
+        Ð¿Ñ\80оÑ\81или Ð¿Ñ\80оÑ\87иÑ\82аÑ\82Ñ\8c, Ð½Ðµ Ð±Ñ\8bло Ð¾Ñ\82пÑ\80авлено Ñ\8dÑ\82им Ð¿Ð¾Ð»Ñ\8cзоваÑ\82елем. Ð\9fожалÑ\83йÑ\81Ñ\82а, Ð²Ð¾Ð¹Ð´Ð¸Ñ\82е
+        в систему под правильным именем, чтобы прочитать его.
     sent_message_summary:
       destroy_button: Удалить
     heading:
@@ -2899,7 +2900,7 @@ ru:
         настройках.
   user_role:
     filter:
-      not_a_role: Строка «%{role}» не является допустимой ролью.
+      not_a_role: Строка '%{role}' не является допустимой ролью.
       already_has_role: Пользователь уже имеет роль %{role}.
       doesnt_have_role: У пользователя нет роли %{role}.
       not_revoke_admin_current_user: Невозможно отозвать роль отозвать администратора
index fee42db421f94a5725606a4dded571ac88ef3ad4..a39c4316f7403cb3575b943ecf3ff43d16613645 100644 (file)
@@ -2,6 +2,7 @@
 # Exported from translatewiki.net
 # Export driver: phpyaml
 # Author: Ajeje Brazorf
+# Author: GianAntonucci
 # Author: Gmelfi
 # Author: LametinoWiki
 # Author: Macofe
@@ -15,41 +16,49 @@ scn:
       friendly: '%e %B %Y ê %H:%M'
   helpers:
     file:
-      prompt: Scegghi file
+      prompt: Scarta u pricu
     submit:
       diary_comment:
         create: Cummenta
       diary_entry:
         create: Pùbblica
-        update: Agghiorna
+        update: Attualizza
       issue_comment:
-        create: Agghiunci cummentu
+        create: Agghiunci un cummentu
       message:
         create: Manna
       client_application:
-        create: Riggistra
-        update: Agghiorna
+        create: Riggìstrati
+        update: Attualizza
       oauth2_application:
-        create: Riggistrati
-        update: Aggiorna
+        create: Riggìstrati
+        update: Attualizza
       redaction:
-        create: Crea l'occultamentu
-        update: Sarva occultamentu
+        create: Crea u redact
+        update: Sarba u redact
       trace:
         create: Càrrica
-        update: Sarva li canciamenti
+        update: Sarba i canci
       user_block:
-        create: Crea lu bloccu
-        update: Aggiorna lu bloccu
+        create: Crea u bloccu
+        update: Attualizza u bloccu
   activerecord:
+    errors:
+      messages:
+        display_name_is_user_n: nun po èssiri user_n abbestra ca si n nun è u to ID
+          utenti
+      models:
+        user_mute:
+          is_already_muted: già sta mutu
     models:
-      acl: Lista di cuntrollu d'accessu
-      changeset: Gruppu di canciamenti
-      changeset_tag: Etichetta dû gruppu di canciamenti
-      country: Paisi
-      diary_comment: Cummentu ntô diariu
+      acl: Lista di cuntrollu di l’accessi
+      changeset: Gruppu dî canci
+      changeset_tag: Etichetta dû gruppu dî canci
+      country: Pajisi
+      diary_comment: Cummentu nnô diariu
       diary_entry: Vuci dû diariu
       friend: Amicu
+      issue: Prubblema
       language: Lingua
       message: Missaggiu
       node: Gruppu
@@ -57,87 +66,101 @@ scn:
       old_node: Gruppu vecchiu
       old_node_tag: Etichetta vecchia dû gruppu
       old_relation: Rilazzioni vecchia
-      old_relation_member: Membru vecchiu dâ rilazzioni
+      old_relation_member: Mèmmiru vecchiu dâ rilazzioni
       old_relation_tag: Etichetta vecchia dâ rilazzioni
       old_way: Caminu vecchiu
       old_way_node: Gruppu vecchiu dû caminu
       old_way_tag: Etichetta vecchia dû caminu
       relation: Rilazzioni
-      relation_member: Membru dâ rilazzioni
+      relation_member: Mèmmiru dâ rilazzioni
       relation_tag: Etichetta dâ rilazzioni
+      report: Signalazzioni
       session: Sissioni
-      trace: Tracciatu
-      tracepoint: Puntu dû tracciatu
-      tracetag: Etichetta dû tracciatu
+      trace: Traccia
+      tracepoint: Puntu dâ traccia
+      tracetag: Etichetta dâ traccia
       user: Utenti
       user_preference: Prifirenza di l'utenti
-      user_token: Lassa-passari di l'utenti
+      user_token: Còdici di l'utenti
       way: Caminu
       way_node: Gruppu dû caminu
       way_tag: Etichetta dû caminu
     attributes:
       client_application:
-        callback_url: URL di callback
-        support_url: URL di l'assistenza
-        allow_write_prefs: canciari li mpustazzioni d'utenti.
-        allow_write_diary: criari vuci dû diariu, cummenti e fari amici.
-        allow_write_api: cancia la cartina
-        allow_read_gpx: lèggi li sò tracciati GPS privati.
-        allow_write_gpx: càrrica li tracciati GPS
-        allow_write_notes: cancia li noti
+        name: Nomu (ubblicatoriu)
+        url: Nnirizzu URL mastru di l’applicazzioni (ubblicatoriu)
+        callback_url: Nnirizzu URL di richiamata
+        support_url: Nnirizzu URL pi l’assistenza
+        allow_read_prefs: leggi i so prifirenzi di l’utenti
+        allow_write_prefs: cancia i so mpustazzioni di l’utenti.
+        allow_write_diary: criari pàggini dû diariu, cummenti e fàrisi amici
+        allow_write_api: cancia a carta
+        allow_read_gpx: leggi i so tracci GPS privati
+        allow_write_gpx: càrrica i traccia GPS
+        allow_write_notes: cancia i noti
       diary_comment:
         body: Corpu
       diary_entry:
         user: Utenti
-        title: Oggettu
+        title: Uggettu
         body: Corpu
         latitude: Latitùdini
-        longitude: Loncitùdini
+        longitude: Luncitùdini
         language_code: Lingua
       doorkeeper/application:
         name: Nomu
         redirect_uri: URI di rimannu
+        confidential: Applicazzioni cunfidinziali?
+        scopes: Pirmisi
       friend:
         user: Utenti
         friend: Amicu
       trace:
         user: Utenti
         visible: Visìbbili
-        name: Nomu dû file
+        name: Nomu dû pricu
         size: Grannizza
         latitude: Latitùdini
-        longitude: Loncitùdini
+        longitude: Luncitùdini
         public: Pùbblicu
         description: Discrizzioni
-        gpx_file: Carricari file GPX
+        gpx_file: Scarta u pricu dâ traccia GPS
         visibility: Visibbilità
         tagstring: Etichetti
       message:
         sender: Mittenti
-        title: Oggettu
+        title: Uggettu
         body: Corpu
-        recipient: Distinatariu
+        recipient: Ricàpitu
       redaction:
         title: Tìtulu
         description: Discrizzioni
+      report:
+        category: Scarta na scaciuni pâ to signalazzioni
+        details: Dinni cchiù assai ncapu ô prubblema (ubblicatoriu).
       user:
-        email: Posta elittrònica
-        new_email: Nnirizzu di posta elittrònica novu
+        auth_provider: Tranzuntaturi
+        auth_uid: UID di tranzuntazzioni
+        email: E-mail
+        new_email: Nnirizzu e-mail novu
         active: Attivu
         display_name: Nomu ammustratu
         description: Discrizzioni dû prufilu
         home_lat: Latitùdini
         home_lon: Luncitùdini
         languages: Lingui prifiruti
-        pass_crypt: Palora d'òrdini
-        pass_crypt_confirmation: Cunferma password
+        preferred_editor: Edituri prifirutu
+        pass_crypt: Chiavi d’accessu
+        pass_crypt_confirmation: Cunferma chiavi d’accessu
     help:
+      doorkeeper/application:
+        redirect_uri: Usa un ringu pi ogni URI
       trace:
         tagstring: spartuti câ vìrgula
       user_block:
-        needs_view: Havi a tràsiri l'utenti avanti chi stu bloccu veni livatu?
+        needs_view: A l'utenti serbi di tràsiri prima ca si leva ssu bloccu?
       user:
-        new_email: (nun veni ammustratu mai pubblicamenti)
+        new_email: (mai fattu vìdiri ô pùbblicu)
   datetime:
     distance_in_words_ago:
       x_minutes:
@@ -153,13 +176,13 @@ scn:
         one: 1 annu fa
         other: '%{count} anni fa'
   editor:
-    default: Pridifinutu (com'a ora %{name})
+    default: Pridifinutu (camora %{name})
     id:
       name: iD
-      description: iD (editor ntô browser)
+      description: iD (edituri nnô browser)
     remote:
-      name: Telecumannu
-      description: Telecumannu (JOSM, Potlatch, Merkaartor)
+      name: Cuntrollu di luntanu
+      description: Cuntrollu di luntanu (JOSM, Potlatch, Merkaartor)
   auth:
     providers:
       none: Nuḍḍu
@@ -171,93 +194,92 @@ scn:
   api:
     notes:
       comment:
-        opened_at_html: Criatu %{when}
-        opened_at_by_html: Criatu %{when} di %{user}
-        commented_at_html: Aggiurnatu %{when}
-        commented_at_by_html: Aggiurnatu %{when} di %{user}
-        closed_at_html: Arrisurvuta %{when}
-        closed_at_by_html: Arrisurvuta %{when} di %{user}
-        reopened_at_html: Rigraputa %{when}
-        reopened_at_by_html: Rigraputa %{when} di %{user}
+        opened_at_html: Criata %{when}
+        opened_at_by_html: Criata %{when} di %{user}
+        commented_at_html: Attualizzata %{when}
+        commented_at_by_html: Attualizzata %{when} di %{user}
+        closed_at_html: Risurbuta %{when}
+        closed_at_by_html: Risurbuta %{when} di %{user}
+        reopened_at_html: Graputa arrè %{when}
+        reopened_at_by_html: Graputa arrè %{when} di %{user}
       rss:
-        title: Noti d'OpenStreetMap
-        description_area: N’elencu dî noti signalati, cummintati o arrisurvuti ntâ
-          tò zona [(%{min_lat}|%{min_lon}) -- (%{max_lat}|%{max_lon})]
-        description_item: Un flussu RSS pâ nota %{id}
+        title: Noti dOpenStreetMap
+        description_area: Na lista di noti, signalati, cummintati o risurbuti nnâ
+          to zona [(%{min_lat}|%{min_lon}) -- (%{max_lat}|%{max_lon})]
+        description_item: Un feed RSS pâ nota %{id}
         opened: nota nova (vicinu a %{place})
         commented: cummentu novu (vicinu a %{place})
-        closed: nota arrisurvuta (vicinu a %{place})
-        reopened: nota riattivata (vicinu a %{place})
+        closed: nota risurbuta (vicinu a %{place})
+        reopened: nota graputa arrè (vicinu a %{place})
       entry:
         comment: Cummentu
         full: Nota cumpleta
   account:
     deletions:
       show:
-        title: Cancella lu mè cuntu
-        delete_account: Cancella cuntu
-        confirm_delete: Sî sicuru?
-        cancel: Annulla
+        title: Scancella u me cuntu
+        delete_account: Scancella u cuntu
+        confirm_delete: Sicuru sì?
+        cancel: Sfai
   accounts:
     edit:
-      title: Cancia lu cuntu
-      my settings: Li mè mpustazzioni
-      current email address: Nnirizzu email attuali
-      external auth: Autinticazzioni esterna
+      title: Cancia u cuntu
+      my settings: Mpustazzioni
+      current email address: Nnirizzu e-mail attuali
+      external auth: Tranzuntari di fora
       openid:
-        link: http://wiki.openstreetmap.org/wiki/IT:OpenID
-        link text: chi voli diri?
+        link: http://wiki.openstreetmap.org/wiki/OpenID
+        link text: zoccu è chissu?
       public editing:
-        heading: Canciamenti pùbblici
-        enabled: Attivati. Nun sî anònimu e poi canciari li dati.
-        enabled link text: chi voli diri?
-        disabled: Disattivati, e nun poi canciari li dati, tutti li canciamenti pricidenti
-          sunnu anònimi.
-        disabled link text: pirchì nun pozzu fari canciamenti?
+        heading: Canciu pùbblicu
+        enabled: Abbilitati. Senza anònimu e cû pirmisu pi canciari i dati.
+        enabled link text: zoccu è chissu?
+        disabled: Disabbilitati e senza u pirmisu pi canciari i dati, tutti i canci
+          d’avanti sunnu anònimi.
+        disabled link text: picchì nun pozzu fari canci?
       contributor terms:
-        heading: Cunnizzioni di cuntribbuzzioni
-        agreed: Accittasti li cunnizzioni di cuntribbuzzioni novi.
-        not yet agreed: Ancora nun accittasti li cunnizzioni di cuntribbuzzioni novi.
-        review link text: Quannu voi tu vai nta stu link pi lèggiri e accittari li
-          cunnizzioni di cuntribbuzzioni novi.
-        agreed_with_pd: Dichiarasti macari chi cunzìddiri li tò canciamenti ntô Duminiu
-          Pùbblicu.
-        link text: chi voli diri?
-      save changes button: Sarva li canciamenti
-      delete_account: Cancella cuntu...
+        heading: Règuli pî cuntribbuti
+        agreed: Accittasti i règuli pî cuntribbuti novi.
+        not yet agreed: Ancora nun accittasti i règuli pî cuntribbuti novi.
+        review link text: Quannu rinesci ammacca sta lijami pi ti lèggiri i règuli
+          pî cuntribbuti novi e p’accittalli.
+        agreed_with_pd: Dicisti macari ca cunzìddiri i to canci comu si sunnu di duminiu
+          pùbblicu.
+        link text: zoccu è chissu?
+      save changes button: Sarba i canci
+      delete_account: Scancella u cuntu...
     go_public:
-      heading: Canciamenti pùbblici
-      make_edits_public_button: Arrenni tutti li mè canciamenti pùbblici
+      heading: Canciu pùbblicu
+      make_edits_public_button: Tutti i me canci falli pùbblici
     update:
-      success_confirm_needed: Li nfurmazzioni di l'utenti foru aggiurnati boni. Cuntrolla
-        la tò posta elittrònica chi t'havi a arrivari nu missaggiu pi cunfirmari lu
-        nnirizzu di posta novu.
-      success: Li nfurmazzioni di l'utenti foru aggiurnati boni.
+      success_confirm_needed: Nfurmazzioni di l'utenti attualizzati. Cuntrolla a to
+        e-mail pi cunfirmari u to nnirizzu di posta elittrònica novu.
+      success: Nfurmazzioni di l'utenti attualizzati.
     destroy:
-      success: Cuntu cancillatu.
+      success: Cuntu scancillatu.
   browse:
     version: Virsioni
-    in_changeset: Gruppu di canciamenti
+    in_changeset: Gruppu dî canci
     anonymous: anònimu
-    no_comment: (nuḍḍu cummentu)
-    part_of: Fa parti di
-    download_xml: Scàrrica ntô furmatu XML
-    view_history: Talìa la crunuluggìa
-    view_details: Talìa li dittagghî
+    no_comment: (senza cummentu)
+    part_of: Parti di
+    download_xml: Scàrrica XML
+    view_history: Talìa a storia
+    view_details: Talìa i minutagghi
     location: 'Locu:'
     node:
       title_html: 'Gruppu: %{name}'
-      history_title_html: 'Crunuluggìa dû gruppu: %{name}'
+      history_title_html: 'Storia dû gruppu: %{name}'
     way:
       title_html: 'Caminu: %{name}'
-      history_title_html: 'Crunuluggìa dû caminu: %{name}'
-      nodes: Gruppa
+      history_title_html: 'Storia dû caminu: %{name}'
+      nodes: Gruppi
       also_part_of_html:
         one: parti dû caminu %{related_ways}
         other: parti dî camini %{related_ways}
     relation:
       title_html: 'Rilazzioni: %{name}'
-      history_title_html: 'Crunuluggìa dâ rilazzioni: %{name}'
+      history_title_html: 'Storia dâ rilazzioni: %{name}'
       members: Mèmmiri
       members_count:
         one: 1 mèmmiru
@@ -272,11 +294,11 @@ scn:
       entry_html: Rilazzioni %{relation_name}
       entry_role_html: Rilazzioni %{relation_name} (comu %{relation_role})
     not_found:
-      title: Nun attruvatu
-      sorry: 'Purtroppu, %{type} #%{id} nun si potti attruvari.'
+      title: Senza truvatu
+      sorry: 'Purtroppu, %{type} #%{id} nun si potti truvari.'
       type:
-        node: lu gruppu
-        way: lu caminu
+        node: gruppu
+        way: caminu
         relation: la rilazzioni
         changeset: lu gruppu di canciamenti
         note: nota
@@ -745,8 +767,8 @@ scn:
           "yes": Passu di muntagna
         natural:
           bay: Gulfu
-          beach: Praia
-          cape: Capu
+          beach: Praja
+          cape: Punta
           cave_entrance: Trasuta dâ grutta
           cliff: Sdirrupu
           crater: Crateri
@@ -758,7 +780,7 @@ scn:
           glacier: Ghiacciaiu
           grassland: Pratu
           heath: Brughiera
-          hill: Cullina
+          hill: Muntagnola
           island: Ìsula
           land: Terra
           marsh: Margiu
@@ -786,7 +808,8 @@ scn:
           accountant: Raggiuneri
           administrative: Ufficiu pùbblicu lucali
           architect: Architettu
-          company: Sucità
+          company: Azzienna
+          diplomatic: Ufficiu diplumàticu
           employment_agency: Agginzìa pû travagghiu
           estate_agent: Agginzìa Mmubbiliari
           government: Ufficiu pùbblicu
@@ -1014,15 +1037,17 @@ scn:
     intro_text: OpenStreetMap è na cartina dû munnu, criata di genti comu a tìa e
       lìbbira a adupirari secunnu na licenza graputa.
     partners_partners: cullabburatura
+    tou: Cunnizzioni pi l’usu
     osm_offline: La basi di dati d'OpenStreetMap comu a ora nun è n lìnia pirchì si
       stannu facennu travagghî di manutinzioni funnamintali.
     osm_read_only: La basi di dati d'OpenStreetMap comu a ora è ntâ mudalità di sula
       littura pirchì si stannu facennu travagghî di manutinzioni funnamintali.
+    nothing_to_preview: Nenti di vìdiri.
     help: Guida
     about: Nfurmazzioni
     copyright: Dritti d'auturi
     learn_more: Sapìrinni cchiossai
-    more: Àutri cosi
+    more: Autru
   user_mailer:
     diary_comment_notification:
       subject: '[OpenStreetMap] %{user} cummintau na vuci dû tò diariu'
@@ -1249,11 +1274,15 @@ scn:
       no home location: Ancora nun mpustasti la tò pusizzioni basi.
       update home location on click: Aggiorna la pusizzioni basi quannu cliccu supra
         â carta?
+      show: Fa’ vìdiri
+      delete: Scancella
+    update:
+      success: Cuntu attualizzatu.
   sessions:
     new:
       tab_title: Trasi
       email or username: 'Nnirizzu di posta elittrònica o nomu utenti:'
-      password: 'Palora d''òrdini:'
+      password: 'Chiavi d’accessu:'
       remember: Arricòrdati di mìa
       lost password link: Pirdisti la tò palora d'òrdini?
       login_button: Trasi
@@ -1299,6 +1328,7 @@ scn:
         aggiurnatu.
       community_driven_title: Guidatu dâ cumunità
       open_data_title: Dati graputi
+      open_data_open_data: dati graputi
       legal_title: Noti ligali
       partners_title: Cullabburatura
     copyright:
@@ -1327,6 +1357,10 @@ scn:
         contributors_intro_html: 'Li nostri cuntribbutura sunnu migghiara di genti.
           Pigghiamu macari dati cu licenza graputa furnuti di l''agginzìi cartugràfichi
           nazziunali e d''àutri fonti, tra dî quali:'
+        contributors_nz_new_zealand: Nova Zilanna
+        contributors_rs_serbia: Serbia
+        contributors_si_slovenia: Sluvenia
+        contributors_es_spain: Spagna
         contributors_footer_2_html: La nclusioni dî dati nta OpenStreetMap nun ìmplica
           ca lu sò furnituri origginali susteni OpenStreetMap, furnisci na quarchi
           garanzìa, o accetta na quarchi rispunzabbilità.
@@ -1448,8 +1482,10 @@ scn:
           taxiway: pista di rullaggiu
           apron: Ària di parcheggiu ariupurtuali
           admin: Cunfini amministrativu
+          capital: Capitali
           forest: Furesta
           wood: Voscu
+          sand: Rina
           golf: Campu di golf
           park: Villa
           common: Cumuni
@@ -1973,10 +2009,14 @@ scn:
     create:
       flash: Occultamentu criatu.
     update:
-      flash: Canciamenti sarvati.
+      flash: Canci sarbati.
     destroy:
-      not_empty: St'occultamentu nun è vacanti. Pi favuri leva di l'occultamentu tutti
-        li virsioni chi nni fannu parti avanti chi lu distrudi.
-      flash: Occultamentu distruiutu.
-      error: Ammattìu n'erruri ntô distrùiri st'occultamentu.
+      not_empty: Sta rivisioni nun è vacanti. Leva a rivisioni a tutti i virsioni
+        chi ci appartèninu avanti ca a distrudi.
+      flash: Rivisioni distruduta.
+      error: Cci fu nu sbagghiu ntô mentri ca scancillava ssa rivisioni.
+  validations:
+    leading_whitespace: àvi nu spazziu jancu ô principiu
+    trailing_whitespace: àvi nu spazziu jancu â fini
+    url_characters: àvi caràttari URL spiciali (%{characters})
 ...
index e6c627a4b1c9b5e2428f948dd1c6275d44fbb9ad..ee6a683d21cf6c555488d7cacf280a12035d556d 100644 (file)
@@ -1346,6 +1346,11 @@ skr-arab:
   notes:
     index:
       subheading_submitted: جمع تھی ڳیا
+      apply: لاڳو کرو
+      all: یکے
+      open: کھولو
+      closed: بند تھیا
+      status: حیثیت
     show:
       title: نوٹ:%{id}
       description: تفصیل
index 02729053fe56eeccdf375fc448f6d7fb2d495f7a..ef47978f864edaf9d43b651298b396097cb6932e 100644 (file)
@@ -58,6 +58,7 @@
 # Author: SkyEye FAST
 # Author: StarrySky
 # Author: StephDC
+# Author: TFX202X
 # Author: TianyinLee
 # Author: Tntchn
 # Author: TsuyaMarisa
@@ -2836,6 +2837,11 @@ zh-CN:
       description: 描述
       created_at: 创建于
       last_changed: 最后更改于
+      apply: 应用
+      all: 全部
+      open: 打开
+      closed: 关闭
+      status: 状态
     show:
       title: 注记:%{id}
       description: 描述
index af72c457d25d0b950a52cd8a0a8037fe1f057e9d..f65042dd7d9ab1c9bf83e22b5c2f1c71c5503ff9 100644 (file)
@@ -108,6 +108,8 @@ OpenStreetMap::Application.routes.draw do
         post "close"
         post "reopen"
       end
+
+      resource :subscription, :only => [:create, :destroy], :controller => "note_subscriptions"
     end
 
     resources :user_blocks, :only => :show, :id => /\d+/, :controller => "user_blocks"
diff --git a/test/controllers/api/note_subscriptions_controller_test.rb b/test/controllers/api/note_subscriptions_controller_test.rb
new file mode 100644 (file)
index 0000000..0e38869
--- /dev/null
@@ -0,0 +1,142 @@
+require "test_helper"
+
+module Api
+  class NoteSubscriptionsControllerTest < ActionDispatch::IntegrationTest
+    def test_routes
+      assert_routing(
+        { :path => "/api/0.6/notes/1/subscription", :method => :post },
+        { :controller => "api/note_subscriptions", :action => "create", :note_id => "1" }
+      )
+      assert_routing(
+        { :path => "/api/0.6/notes/1/subscription", :method => :delete },
+        { :controller => "api/note_subscriptions", :action => "destroy", :note_id => "1" }
+      )
+    end
+
+    def test_create
+      user = create(:user)
+      auth_header = bearer_authorization_header user
+      note = create(:note_with_comments)
+      assert_empty note.subscribers
+
+      assert_difference "NoteSubscription.count", 1 do
+        assert_difference "note.subscribers.count", 1 do
+          post api_note_subscription_path(note), :headers => auth_header
+          assert_response :success
+        end
+      end
+      assert_equal user, note.subscribers.last
+    end
+
+    def test_create_fail_anonymous
+      note = create(:note_with_comments)
+
+      assert_no_difference "NoteSubscription.count" do
+        assert_no_difference "note.subscribers.count" do
+          post api_note_subscription_path(note)
+          assert_response :unauthorized
+        end
+      end
+    end
+
+    def test_create_fail_no_scope
+      user = create(:user)
+      auth_header = bearer_authorization_header user, :scopes => %w[read_prefs]
+      note = create(:note_with_comments)
+
+      assert_no_difference "NoteSubscription.count" do
+        assert_no_difference "note.subscribers.count" do
+          post api_note_subscription_path(note), :headers => auth_header
+          assert_response :forbidden
+        end
+      end
+    end
+
+    def test_create_fail_note_not_found
+      user = create(:user)
+      auth_header = bearer_authorization_header user
+
+      assert_no_difference "NoteSubscription.count" do
+        post api_note_subscription_path(999111), :headers => auth_header
+        assert_response :not_found
+      end
+      assert_match "not found", @response.body
+    end
+
+    def test_create_fail_already_subscribed
+      user = create(:user)
+      auth_header = bearer_authorization_header user
+      note = create(:note_with_comments)
+      create(:note_subscription, :user => user, :note => note)
+
+      assert_no_difference "NoteSubscription.count" do
+        assert_no_difference "note.subscribers.count" do
+          post api_note_subscription_path(note), :headers => auth_header
+          assert_response :conflict
+        end
+      end
+      assert_match "already subscribed", @response.body
+    end
+
+    def test_destroy
+      user = create(:user)
+      auth_header = bearer_authorization_header user
+      other_user = create(:user)
+      note = create(:note_with_comments)
+      other_note = create(:note_with_comments)
+      create(:note_subscription, :user => user, :note => note)
+      create(:note_subscription, :user => other_user, :note => note)
+      create(:note_subscription, :user => user, :note => other_note)
+
+      assert_difference "NoteSubscription.count", -1 do
+        assert_difference "note.subscribers.count", -1 do
+          delete api_note_subscription_path(note), :headers => auth_header
+          assert_response :success
+        end
+      end
+      note.reload
+      assert_equal [other_user], note.subscribers
+      assert_equal [user], other_note.subscribers
+    end
+
+    def test_destroy_fail_anonymous
+      note = create(:note_with_comments)
+
+      delete api_note_subscription_path(note)
+      assert_response :unauthorized
+    end
+
+    def test_destroy_fail_no_scope
+      user = create(:user)
+      auth_header = bearer_authorization_header user, :scopes => %w[read_prefs]
+      note = create(:note_with_comments)
+      create(:note_subscription, :user => user, :note => note)
+
+      assert_no_difference "NoteSubscription.count" do
+        assert_no_difference "note.subscribers.count" do
+          delete api_note_subscription_path(note), :headers => auth_header
+          assert_response :forbidden
+        end
+      end
+    end
+
+    def test_destroy_fail_note_not_found
+      user = create(:user)
+      auth_header = bearer_authorization_header user
+
+      delete api_note_subscription_path(999111), :headers => auth_header
+      assert_response :not_found
+      assert_match "not found", @response.body
+    end
+
+    def test_destroy_fail_not_subscribed
+      user = create(:user)
+      auth_header = bearer_authorization_header user
+      note = create(:note_with_comments)
+
+      delete api_note_subscription_path(note), :headers => auth_header
+      assert_response :not_found
+      assert_match "not subscribed", @response.body
+    end
+  end
+end
index 7c830cc373c5d4955f42888216a52b1c5e672942..0577992f277432f631f36e13358f8fc78daaf5ee 100644 (file)
@@ -22,7 +22,7 @@ class NoteCommentsTest < ApplicationSystemTestCase
     end
   end
 
-  def test_add_comment
+  test "can add comment" do
     note = create(:note_with_comments)
     user = create(:user)
     sign_in_as(user)
@@ -125,4 +125,49 @@ class NoteCommentsTest < ApplicationSystemTestCase
       assert_button "Reactivate", :disabled => false
     end
   end
+
+  test "no subscribe button when not logged in" do
+    note = create(:note_with_comments)
+    visit note_path(note)
+
+    within_sidebar do
+      assert_no_button "Subscribe"
+      assert_no_button "Unsubscribe"
+    end
+  end
+
+  test "can subscribe" do
+    note = create(:note_with_comments)
+    user = create(:user)
+    sign_in_as(user)
+    visit note_path(note)
+
+    within_sidebar do
+      assert_button "Subscribe"
+      assert_no_button "Unsubscribe"
+
+      click_on "Subscribe"
+
+      assert_no_button "Subscribe"
+      assert_button "Unsubscribe"
+    end
+  end
+
+  test "can unsubscribe" do
+    note = create(:note_with_comments)
+    user = create(:user)
+    create(:note_subscription, :note => note, :user => user)
+    sign_in_as(user)
+    visit note_path(note)
+
+    within_sidebar do
+      assert_no_button "Subscribe"
+      assert_button "Unsubscribe"
+
+      click_on "Unsubscribe"
+
+      assert_button "Subscribe"
+      assert_no_button "Unsubscribe"
+    end
+  end
 end