diff --git a/app/controllers/amf_controller.rb b/app/controllers/amf_controller.rb index 1e87e47c2..b9c344771 100644 --- a/app/controllers/amf_controller.rb +++ b/app/controllers/amf_controller.rb @@ -178,6 +178,7 @@ class AmfController < ApplicationController amf_handle_error("'startchangeset'",nil,nil) do user = getuser(usertoken) if !user then return -1,"You are not logged in, so Potlatch can't write any changes to the database." end + unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end # close previous changeset and add comment if closeid @@ -221,23 +222,43 @@ class AmfController < ApplicationController end lang = request.compatible_language_from(getlocales) + (real_lang, localised) = getlocalized(lang) - begin - # if not, try the browser language - localised = YAML::load(File.open("#{RAILS_ROOT}/config/potlatch/localised/#{lang}/localised.yaml")) - rescue - # fall back to hardcoded English text - localised = "" - end + # Tell Potlatch what language it's using + localised["__potlatch_locale"] = real_lang + + # Get help from i18n but delete it so we won't pass it around + # twice for nothing + help = localised["help_html"] + localised.delete("help_html") - begin - help = File.read("#{RAILS_ROOT}/config/potlatch/localised/#{lang}/help.html") - rescue - help = File.read("#{RAILS_ROOT}/config/potlatch/localised/en/help.html") - end return POTLATCH_PRESETS+[localised,help] end + def getlocalized(lang) + # What we end up actually using. Reported in Potlatch's created_by=* string + loaded_lang = 'en' + + # Load English defaults + en = YAML::load(File.open("#{RAILS_ROOT}/config/potlatch/locales/en.yml"))["en"] + + if lang == 'en' + return [real_lang, en] + else + # Use English as a fallback + begin + other = YAML::load(File.open("#{RAILS_ROOT}/config/potlatch/locales/#{lang}.yml"))[lang] + loaded_lang = lang + rescue + other = en + end + + # We have to return a flat list and some of the keys won't be + # translated (probably) + return [loaded_lang, en.merge(other)] + end + end + ## # Find all the ways, POI nodes (i.e. not part of ways), and relations # in a given bounding box. Nodes are returned in full; ways and relations @@ -460,7 +481,8 @@ class AmfController < ApplicationController def findgpx(searchterm, usertoken) amf_handle_error_with_timeout("'findgpx'" ,nil,nil) do user = getuser(usertoken) - if !uid then return -1,"You must be logged in to search for GPX traces.",[] end + if !user then return -1,"You must be logged in to search for GPX traces.",[] end + unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked'),[] end gpxs = [] if searchterm.to_i>0 then @@ -526,6 +548,7 @@ class AmfController < ApplicationController amf_handle_error("'putrelation' #{relid}" ,'relation',relid) do user = getuser(usertoken) if !user then return -1,"You are not logged in, so the relation could not be saved." end + unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end if !tags_ok(tags) then return -1,"One of the tags is invalid. Please pester Adobe to fix Flash on Linux." end tags = strip_non_xml_chars tags @@ -613,6 +636,7 @@ class AmfController < ApplicationController user = getuser(usertoken) if !user then return -1,"You are not logged in, so the way could not be saved." end + unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end if pointlist.length < 2 then return -2,"Server error - way is only #{points.length} points long." end if !tags_ok(attributes) then return -1,"One of the tags is invalid. Please pester Adobe to fix Flash on Linux." end attributes = strip_non_xml_chars attributes @@ -717,6 +741,7 @@ class AmfController < ApplicationController amf_handle_error("'putpoi' #{id}", 'node',id) do user = getuser(usertoken) if !user then return -1,"You are not logged in, so the point could not be saved." end + unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end if !tags_ok(tags) then return -1,"One of the tags is invalid. Please pester Adobe to fix Flash on Linux." end tags = strip_non_xml_chars tags @@ -799,6 +824,7 @@ class AmfController < ApplicationController amf_handle_error("'deleteway' #{way_id}" ,'way',id) do user = getuser(usertoken) unless user then return -1,"You are not logged in, so the way could not be deleted." end + unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end way_id = way_id.to_i nodeversions = {} @@ -871,7 +897,7 @@ class AmfController < ApplicationController end def getlocales - Dir.glob("#{RAILS_ROOT}/config/potlatch/localised/*").collect { |f| File.basename(f) } + Dir.glob("#{RAILS_ROOT}/config/potlatch/locales/*").collect { |f| File.basename(f, ".yml") } end ## diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 6dbe9165c..bcaed4565 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -39,6 +39,19 @@ class ApplicationController < ActionController::Base end end + ## + # require the user to have cookies enabled in their browser + def require_cookies + if request.cookies["_osm_session"].to_s == "" + if params[:cookie_test].nil? + redirect_to params.merge(:cookie_test => "true") + return false + else + @notice = t 'application.require_cookies.cookies_needed' + end + end + end + # Utility methods to make the controller filter methods easier to read and write. def require_allow_read_prefs require_capability(:allow_read_prefs) @@ -78,6 +91,12 @@ class ApplicationController < ActionController::Base @user = User.authenticate(:username => username, :password => passwd) # basic auth end end + + # check if the user has been banned + unless @user.nil? or @user.active_blocks.empty? + # NOTE: need slightly more helpful message than this. + render :text => t('application.setup_user_auth.blocked'), :status => :forbidden + end end def authorize(realm='Web Password', errormessage="Couldn't authenticate you") diff --git a/app/controllers/changeset_controller.rb b/app/controllers/changeset_controller.rb index c67f4abd5..7573a1803 100644 --- a/app/controllers/changeset_controller.rb +++ b/app/controllers/changeset_controller.rb @@ -206,7 +206,7 @@ class ChangesetController < ApplicationController # create the conditions that the user asked for. some or all of # these may be nil. conditions = conditions_bbox(params['bbox']) - conditions = cond_merge conditions, conditions_user(params['user']) + conditions = cond_merge conditions, conditions_user(params['user'], params['display_name']) conditions = cond_merge conditions, conditions_time(params['time']) conditions = cond_merge conditions, conditions_open(params['open']) conditions = cond_merge conditions, conditions_closed(params['closed']) @@ -352,12 +352,23 @@ private ## # restrict changesets to those by a particular user - def conditions_user(user) - unless user.nil? - # user input checking, we don't have any UIDs < 1 - raise OSM::APIBadUserInput.new("invalid user ID") if user.to_i < 1 + def conditions_user(user, name) + unless user.nil? and name.nil? + # shouldn't provide both name and UID + raise OSM::APIBadUserInput.new("provide either the user ID or display name, but not both") if user and name + + # use either the name or the UID to find the user which we're selecting on. + u = if name.nil? + # user input checking, we don't have any UIDs < 1 + raise OSM::APIBadUserInput.new("invalid user ID") if user.to_i < 1 + u = User.find(user.to_i) + else + u = User.find_by_display_name(name) + end + + # make sure we found a user + raise OSM::APINotFoundError.new if u.nil? - u = User.find(user.to_i) # should be able to get changesets of public users only, or # our own changesets regardless of public-ness. unless u.data_public? diff --git a/app/controllers/geocoder_controller.rb b/app/controllers/geocoder_controller.rb index 8195b0d47..556cfb36a 100644 --- a/app/controllers/geocoder_controller.rb +++ b/app/controllers/geocoder_controller.rb @@ -77,7 +77,7 @@ class GeocoderController < ApplicationController data = response.split(/\s*,\s+/) # lat,long,town,state,zip @results.push({:lat => data[0], :lon => data[1], :zoom => APP_CONFIG['postcode_zoom'], - :prefix => "#{data[2]}, #{data[3]}, ", + :prefix => "#{data[2]}, #{data[3]},", :name => data[4]}) end diff --git a/app/controllers/trace_controller.rb b/app/controllers/trace_controller.rb index f06a162fb..c90558269 100644 --- a/app/controllers/trace_controller.rb +++ b/app/controllers/trace_controller.rb @@ -11,7 +11,8 @@ class TraceController < ApplicationController before_filter :check_api_writable, :only => [:api_create] before_filter :require_allow_read_gpx, :only => [:api_details, :api_data] before_filter :require_allow_write_gpx, :only => [:api_create] - + around_filter :api_call_handle_error, :only => [:api_details, :api_data, :api_create] + # Counts and selects pages of GPX traces for various criteria (by user, tags, public etc.). # target_user - if set, specifies the user to fetch traces for. if not set will fetch all traces def list(target_user = nil, action = "list") @@ -128,8 +129,11 @@ class TraceController < ApplicationController if params[:trace] logger.info(params[:trace][:gpx_file].class.name) if params[:trace][:gpx_file].respond_to?(:read) - do_create(params[:trace][:gpx_file], params[:trace][:tagstring], - params[:trace][:description], params[:trace][:visibility]) + begin + do_create(params[:trace][:gpx_file], params[:trace][:tagstring], + params[:trace][:description], params[:trace][:visibility]) + rescue + end if @trace.id logger.info("id is #{@trace.id}") @@ -293,12 +297,16 @@ class TraceController < ApplicationController if request.post? tags = params[:tags] || "" description = params[:description] || "" - visibility = params[:visibility] || false + visibility = params[:visibility] - if params[:public] && !visibility - visibility = "public" + if visibility.nil? + if params[:public] && params[:public].to_i.nonzero? + visibility = "public" + else + visibility = "private" + end end - + if params[:file].respond_to?(:read) do_create(params[:file], tags, description, visibility) @@ -341,20 +349,35 @@ private :timestamp => Time.now.getutc }) - # Save the trace object - if @trace.save - # Rename the temporary file to the final name - FileUtils.mv(filename, @trace.trace_name) + Trace.transaction do + begin + # Save the trace object + @trace.save! - # Clear the inserted flag to make the import daemon load the trace - @trace.inserted = false - @trace.save! - else - # Remove the file as we have failed to update the database - FileUtils.rm_f(filename) + # Rename the temporary file to the final name + FileUtils.mv(filename, @trace.trace_name) + rescue Exception => ex + # Remove the file as we have failed to update the database + FileUtils.rm_f(filename) + + # Pass the exception on + raise + end + + begin + # Clear the inserted flag to make the import daemon load the trace + @trace.inserted = false + @trace.save! + rescue Exception => ex + # Remove the file as we have failed to update the database + FileUtils.rm_f(@trace.trace_name) + + # Pass the exception on + raise + end end - - # Finally save the user's preferred previacy level + + # Finally save the user's preferred privacy level if pref = @user.preferences.find(:first, :conditions => {:k => "gps.trace.visibility"}) pref.v = visibility pref.save diff --git a/app/controllers/user_blocks_controller.rb b/app/controllers/user_blocks_controller.rb new file mode 100644 index 000000000..fd9f03c4a --- /dev/null +++ b/app/controllers/user_blocks_controller.rb @@ -0,0 +1,156 @@ +class UserBlocksController < ApplicationController + layout 'site' + + before_filter :authorize_web + before_filter :set_locale + before_filter :require_user, :only => [:new, :create, :edit, :update, :revoke] + before_filter :require_moderator, :only => [:create, :update, :revoke] + before_filter :lookup_this_user, :only => [:new, :create, :blocks_on, :blocks_by] + before_filter :lookup_user_block, :only => [:show, :edit, :update, :revoke] + before_filter :require_valid_params, :only => [:create, :update] + before_filter :check_database_readable + before_filter :check_database_writable, :only => [:create, :update, :revoke] + + def index + @user_blocks_pages, @user_blocks = paginate(:user_blocks, + :include => [:user, :creator, :revoker], + :order => "user_blocks.ends_at DESC", + :per_page => 20) + end + + def show + if @user and @user.id == @user_block.user_id + @user_block.needs_view = false + @user_block.save! + end + end + + def new + @user_block = UserBlock.new + end + + def edit + params[:user_block_period] = ((@user_block.ends_at - Time.now.getutc) / 1.hour).ceil.to_s + end + + def create + unless @valid_params + redirect_to :action => "new" + return + end + + @user_block = UserBlock.new(:user_id => @this_user.id, + :creator_id => @user.id, + :reason => params[:user_block][:reason], + :ends_at => Time.now.getutc() + @block_period.hours, + :needs_view => params[:user_block][:needs_view]) + + if @user_block.save + flash[:notice] = t('user_block.create.flash', :name => @this_user.display_name) + redirect_to @user_block + else + render :action => "new" + end + end + + def update + unless @valid_params + redirect_to :action => "edit" + return + end + + if @user_block.creator_id != @user.id + flash[:notice] = t('user_block.update.only_creator_can_edit') + redirect_to :action => "edit" + return + end + + if @user_block.update_attributes({ :ends_at => Time.now.getutc() + @block_period.hours, + :reason => params[:user_block][:reason], + :needs_view => params[:user_block][:needs_view] }) + flash[:notice] = t('user_block.update.success') + redirect_to(@user_block) + else + render :action => "edit" + end + end + + ## + # revokes the block, setting the end_time to now + def revoke + if params[:confirm] + if @user_block.revoke! @user + flash[:notice] = t'user_block.revoke.flash' + redirect_to(@user_block) + end + end + end + + ## + # shows a list of all the blocks on the given user + def blocks_on + @user_blocks_pages, @user_blocks = paginate(:user_blocks, + :include => [:user, :creator, :revoker], + :conditions => {:user_id => @this_user.id}, + :order => "user_blocks.ends_at DESC", + :per_page => 20) + end + + ## + # shows a list of all the blocks by the given user. + def blocks_by + @user_blocks_pages, @user_blocks = paginate(:user_blocks, + :include => [:user, :creator, :revoker], + :conditions => {:creator_id => @this_user.id}, + :order => "user_blocks.ends_at DESC", + :per_page => 20) + end + + private + ## + # require that the user is a moderator, or fill out a helpful error message + # and return them to the blocks index. + def require_moderator + unless @user.moderator? + flash[:notice] = t('user_block.filter.not_a_moderator') + redirect_to :action => 'index' + end + end + + ## + # ensure that there is a "this_user" instance variable + def lookup_this_user + @this_user = User.find_by_display_name(params[:display_name]) + rescue ActiveRecord::RecordNotFound + redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name] unless @this_user + end + + ## + # ensure that there is a "user_block" instance variable + def lookup_user_block + @user_block = UserBlock.find(params[:id]) + rescue ActiveRecord::RecordNotFound + render :action => "not_found", :status => :not_found + end + + ## + # check that the input parameters are valid, setting an instance + # variable if not. note that this doesn't do any redirection, as it's + # called before two different actions, each of which should redirect + # to a different place. + def require_valid_params + @block_period = params[:user_block_period].to_i + @valid_params = false + + if !UserBlock::PERIODS.include?(@block_period) + flash[:notice] = t('user_block.filter.block_period') + + elsif @user_block and !@user_block.active? + flash[:notice] = t('user_block.filter.block_expired') + + else + @valid_params = true + end + end + +end diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb index bea700331..df6ca164c 100644 --- a/app/controllers/user_controller.rb +++ b/app/controllers/user_controller.rb @@ -1,5 +1,5 @@ class UserController < ApplicationController - layout 'site' + layout 'site', :except => :api_details before_filter :authorize, :only => [:api_details, :api_gpx_files] before_filter :authorize_web, :except => [:api_details, :api_gpx_files] @@ -10,6 +10,9 @@ class UserController < ApplicationController before_filter :check_api_readable, :only => [:api_details, :api_gpx_files] before_filter :require_allow_read_prefs, :only => [:api_details] before_filter :require_allow_read_gpx, :only => [:api_gpx_files] + before_filter :require_cookies, :only => [:login, :confirm] + before_filter :require_administrator, :only => [:activate, :deactivate, :hide, :unhide, :delete] + before_filter :lookup_this_user, :only => [:activate, :deactivate, :hide, :unhide, :delete] filter_parameter_logging :password, :pass_crypt, :pass_crypt_confirmation @@ -142,9 +145,28 @@ class UserController < ApplicationController end def login + if params[:user] and session[:user].nil? + email_or_display_name = params[:user][:email] + pass = params[:user][:password] + user = User.authenticate(:username => email_or_display_name, :password => pass) + if user + session[:user] = user.id + elsif User.authenticate(:username => email_or_display_name, :password => pass, :inactive => true) + @notice = t 'user.login.account not active' + else + @notice = t 'user.login.auth failure' + end + end + if session[:user] - # The user is logged in already, if the referer param exists, redirect them to that - if params[:referer] + # The user is logged in, if the referer param exists, redirect them to that + # unless they've also got a block on them, in which case redirect them to + # the block so they can clear it. + user = User.find(session[:user]) + block = user.blocked_on_view + if block + redirect_to block, :referrer => params[:referrer] + elsif params[:referer] redirect_to params[:referer] else redirect_to :controller => 'site', :action => 'index' @@ -153,25 +175,6 @@ class UserController < ApplicationController end @title = t 'user.login.title' - - if params[:user] - email_or_display_name = params[:user][:email] - pass = params[:user][:password] - user = User.authenticate(:username => email_or_display_name, :password => pass) - if user - session[:user] = user.id - if params[:referer] - redirect_to params[:referer] - else - redirect_to :controller => 'site', :action => 'index' - end - return - elsif User.authenticate(:username => email_or_display_name, :password => pass, :inactive => true) - @notice = t 'user.login.account not active' - else - @notice = t 'user.login.auth failure' - end - end end def logout @@ -245,10 +248,6 @@ class UserController < ApplicationController redirect_to :controller => 'user', :action => 'view', :display_name => @user.display_name end - def api_details - render :text => @user.to_xml.to_s, :content_type => "text/xml" - end - def api_gpx_files doc = OSM::API.new.get_xml_doc @user.traces.each do |trace| @@ -258,9 +257,10 @@ class UserController < ApplicationController end def view - @this_user = User.find_by_display_name(params[:display_name], :conditions => {:visible => true}) + @this_user = User.find_by_display_name(params[:display_name]) - if @this_user + if @this_user and + (@this_user.visible? or (@user and @user.administrator?)) @title = @this_user.display_name else @title = t 'user.no_such_user.title' @@ -270,7 +270,7 @@ class UserController < ApplicationController end def make_friend - if params[:display_name] + if params[:display_name] name = params[:display_name] new_friend = User.find_by_display_name(name, :conditions => {:visible => true}) friend = Friend.new @@ -292,7 +292,7 @@ class UserController < ApplicationController end def remove_friend - if params[:display_name] + if params[:display_name] name = params[:display_name] friend = User.find_by_display_name(name, :conditions => {:visible => true}) if @user.is_friends_with?(friend) @@ -305,4 +305,57 @@ class UserController < ApplicationController redirect_to :controller => 'user', :action => 'view' end end + + ## + # activate a user, allowing them to log in + def activate + @this_user.update_attributes(:active => true) + redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name] + end + + ## + # deactivate a user, preventing them from logging in + def deactivate + @this_user.update_attributes(:active => false) + redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name] + end + + ## + # hide a user, marking them as logically deleted + def hide + @this_user.update_attributes(:visible => false) + redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name] + end + + ## + # unhide a user, clearing the logically deleted flag + def unhide + @this_user.update_attributes(:visible => true) + redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name] + end + + ## + # delete a user, marking them as deleted and removing personal data + def delete + @this_user.delete + redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name] + end +private + ## + # require that the user is a administrator, or fill out a helpful error message + # and return them to the user page. + def require_administrator + unless @user.administrator? + flash[:notice] = t('user.filter.not_an_administrator') + redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name] + end + end + + ## + # ensure that there is a "this_user" instance variable + def lookup_this_user + @this_user = User.find_by_display_name(params[:display_name]) + rescue ActiveRecord::RecordNotFound + redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name] unless @this_user + end end diff --git a/app/controllers/user_roles_controller.rb b/app/controllers/user_roles_controller.rb new file mode 100644 index 000000000..ee4de7ce0 --- /dev/null +++ b/app/controllers/user_roles_controller.rb @@ -0,0 +1,83 @@ +class UserRolesController < ApplicationController + layout 'site' + + before_filter :authorize_web + before_filter :require_user + before_filter :lookup_this_user + before_filter :require_administrator + before_filter :require_valid_role + before_filter :not_in_role, :only => [:grant] + before_filter :in_role, :only => [:revoke] + around_filter :setup_nonce + + def grant + @this_user.roles.create(:role => @role, :granter_id => @user.id) + redirect_to :controller => 'user', :action => 'view', :display_name => @this_user.display_name + end + + def revoke + UserRole.delete_all({:user_id => @this_user.id, :role => @role}) + redirect_to :controller => 'user', :action => 'view', :display_name => @this_user.display_name + end + + private + def require_administrator + unless @user.administrator? + flash[:notice] = t'user_role.filter.not_an_administrator' + redirect_to :controller => 'user', :action => 'view', :display_name => @this_user.display_name + end + end + + ## + # ensure that there is a "this_user" instance variable + def lookup_this_user + @this_user = User.find_by_display_name(params[:display_name]) + rescue ActiveRecord::RecordNotFound + redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name] unless @this_user + end + + ## + # the random nonce here which isn't predictable, making an CSRF + # procedure much, much more difficult. setup the nonce. if the given + # nonce matches the session nonce then yield into the actual method. + # otherwise, just sets up the nonce for the form. + def setup_nonce + if params[:nonce] and params[:nonce] == session[:nonce] + @nonce = params[:nonce] + yield + else + @nonce = OAuth::Helper.generate_nonce + session[:nonce] = @nonce + render + end + end + + ## + # require that the given role is valid. the role is a URL + # parameter, so should always be present. + def require_valid_role + @role = params[:role] + unless UserRole::ALL_ROLES.include?(@role) + flash[:notice] = t('user_role.filter.not_a_role', :role => @role) + redirect_to :controller => 'user', :action => 'view', :display_name => @this_user.display_name + end + end + + ## + # checks that the user doesn't already have this role + def not_in_role + if @this_user.has_role? @role + flash[:notice] = t('user_role.filter.already_has_role', :role => @role) + redirect_to :controller => 'user', :action => 'view', :display_name => @this_user.display_name + end + end + + ## + # checks that the user already has this role + def in_role + unless @this_user.has_role? @role + flash[:notice] = t('user_role.filter.doesnt_have_role', :role => @role) + redirect_to :controller => 'user', :action => 'view', :display_name => @this_user.display_name + end + end +end diff --git a/app/controllers/way_controller.rb b/app/controllers/way_controller.rb index 1f301e9fb..f2cda21bc 100644 --- a/app/controllers/way_controller.rb +++ b/app/controllers/way_controller.rb @@ -67,7 +67,7 @@ class WayController < ApplicationController user_display_name_cache = {} doc = OSM::API.new.get_xml_doc - way.nodes.each do |node| + way.nodes.uniq.each do |node| if node.visible doc.root << node.to_xml_node(changeset_cache, user_display_name_cache) end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index b28ab7c1a..bee4f9ce2 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -10,4 +10,32 @@ module ApplicationHelper def atom_link_to(*args) return link_to(image_tag("RSS.gif", :size => "16x16", :border => 0), Hash[*args], { :class => "rsssmall" }); end + + def javascript_strings + js = "" + + js << "\n" + + return js + end + +private + + def javascript_strings_for_key(key) + js = "" + value = t(key, :locale => "en") + + if value.is_a?(String) + js << "i18n_strings['#{key}'] = '" << escape_javascript(t(key)) << "';\n" + else + value.each_key do |k| + js << javascript_strings_for_key("#{key}.#{k}") + end + end + + return js + end end diff --git a/app/helpers/browse_helper.rb b/app/helpers/browse_helper.rb index 879d516ef..aed0c518f 100644 --- a/app/helpers/browse_helper.rb +++ b/app/helpers/browse_helper.rb @@ -8,7 +8,9 @@ module BrowseHelper if version name = t 'printable_name.with_version', :id => name, :version => object.version.to_s end - if object.tags.include? 'name' + if object.tags.include? "name:#{I18n.locale}" + name = t 'printable_name.with_name', :name => object.tags["name:#{I18n.locale}"].to_s, :id => name + elsif object.tags.include? 'name' name = t 'printable_name.with_name', :name => object.tags['name'].to_s, :id => name end return name diff --git a/app/helpers/geocoder_helper.rb b/app/helpers/geocoder_helper.rb index f1b4e01dd..e7d0ddf6c 100644 --- a/app/helpers/geocoder_helper.rb +++ b/app/helpers/geocoder_helper.rb @@ -5,6 +5,7 @@ module GeocoderHelper html_options[:href] = "?mlat=#{result[:lat]}&mlon=#{result[:lon]}&zoom=#{result[:zoom]}" html = "" html << result[:prefix] if result[:prefix] + html << " " if result[:prefix] and result[:name] html << link_to_function(result[:name],"setPosition(#{result[:lat]}, #{result[:lon]}, #{result[:zoom]})", html_options) if result[:name] html << result[:suffix] if result[:suffix] return html diff --git a/app/helpers/user_blocks_helper.rb b/app/helpers/user_blocks_helper.rb new file mode 100644 index 000000000..3cd9373c1 --- /dev/null +++ b/app/helpers/user_blocks_helper.rb @@ -0,0 +1,20 @@ +module UserBlocksHelper + ## + # returns a translated string representing the status of the + # user block (i.e: whether it's active, what the expiry time is) + def block_status(block) + if block.active? + if block.needs_view? + I18n.t('user_block.helper.until_login') + else + I18n.t('user_block.helper.time_future', :time => distance_of_time_in_words_to_now(block.ends_at)) + end + else + # the max of the last update time or the ends_at time is when this block finished + # either because the user viewed the block (updated_at) or it expired or was + # revoked (ends_at) + last_time = [block.ends_at, block.updated_at].max + I18n.t('user_block.helper.time_past', :time => distance_of_time_in_words_to_now(last_time)) + end + end +end diff --git a/app/models/language.rb b/app/models/language.rb index a33c76081..7bb0b6e77 100644 --- a/app/models/language.rb +++ b/app/models/language.rb @@ -3,6 +3,22 @@ class Language < ActiveRecord::Base has_many :diary_entries, :foreign_key => 'language' + def self.load(file) + Language.transaction do + YAML.load(File.read(file)).each do |k,v| + begin + Language.update(k, :english_name => v["english"], :native_name => v["native"]) + rescue ActiveRecord::RecordNotFound + Language.create do |l| + l.code = k + l.english_name = v["english"] + l.native_name = v["native"] + end + end + end + end + end + def name name = english_name name += " (#{native_name})" unless native_name.nil? diff --git a/app/models/trace.rb b/app/models/trace.rb index cbfd68996..24f93236a 100644 --- a/app/models/trace.rb +++ b/app/models/trace.rb @@ -26,7 +26,7 @@ class Trace < ActiveRecord::Base def tagstring=(s) if s.include? ',' - self.tags = s.split(/\s*,\s*/).collect {|tag| + self.tags = s.split(/\s*,\s*/).select {|tag| tag !~ /^\s*$/}.collect {|tag| tt = Tracetag.new tt.tag = tag tt diff --git a/app/models/user.rb b/app/models/user.rb index ae5b0b74f..54b3fa371 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -14,6 +14,9 @@ class User < ActiveRecord::Base has_many :client_applications has_many :oauth_tokens, :class_name => "OauthToken", :order => "authorized_at desc", :include => [:client_application] + has_many :active_blocks, :class_name => "UserBlock", :conditions => ['user_blocks.ends_at > \'#{Time.now.getutc.xmlschema(5)}\' or user_blocks.needs_view'] + has_many :roles, :class_name => "UserRole" + validates_presence_of :email, :display_name validates_confirmation_of :email#, :message => ' addresses must match' validates_confirmation_of :pass_crypt#, :message => ' must match the confirmation password' @@ -33,7 +36,7 @@ class User < ActiveRecord::Base file_column :image, :magick => { :geometry => "100x100>" } def after_initialize - self.creation_time = Time.now.getutc if self.creation_time.nil? + self.creation_time = Time.now.getutc unless self.attribute_present?(:creation_time) end def encrypt_password @@ -125,6 +128,31 @@ class User < ActiveRecord::Base return false end + ## + # returns true if the user has the moderator role, false otherwise + def moderator? + has_role? 'moderator' + end + + ## + # returns true if the user has the administrator role, false otherwise + def administrator? + has_role? 'administrator' + end + + ## + # returns true if the user has the requested role + def has_role?(role) + roles.any? { |r| r.role == role } + end + + ## + # returns the first active block which would require users to view + # a message, or nil if there are none. + def blocked_on_view + active_blocks.detect { |b| b.needs_view? } + end + def delete self.active = false self.display_name = "user_#{self.id}" diff --git a/app/models/user_block.rb b/app/models/user_block.rb new file mode 100644 index 000000000..23e1bcab6 --- /dev/null +++ b/app/models/user_block.rb @@ -0,0 +1,35 @@ +class UserBlock < ActiveRecord::Base + validate :moderator_permissions + + belongs_to :user, :class_name => "User", :foreign_key => :user_id + belongs_to :creator, :class_name => "User", :foreign_key => :creator_id + belongs_to :revoker, :class_name => "User", :foreign_key => :revoker_id + + PERIODS = APP_CONFIG['user_block_periods'] + + ## + # returns true if the block is currently active (i.e: the user can't + # use the API). + def active? + needs_view or ends_at > Time.now.getutc + end + + ## + # revokes the block, allowing the user to use the API again. the argument + # is the user object who is revoking the ban. + def revoke!(revoker) + update_attributes({ :ends_at => Time.now.getutc(), + :revoker_id => revoker.id, + :needs_view => false }) + end + + private + ## + # validate that only moderators are allowed to change the + # block. this should be caught and dealt with in the controller, + # but i've also included it here just in case. + def moderator_permissions + errors.add_to_base(I18n.t('user_block.model.non_moderator_update')) if creator_id_changed? and !creator.moderator? + errors.add_to_base(I18n.t('user_block.model.non_moderator_revoke')) unless revoker_id.nil? or revoker.moderator? + end +end diff --git a/app/models/user_role.rb b/app/models/user_role.rb new file mode 100644 index 000000000..fb783281e --- /dev/null +++ b/app/models/user_role.rb @@ -0,0 +1,8 @@ +class UserRole < ActiveRecord::Base + belongs_to :user + + ALL_ROLES = ['administrator', 'moderator'] + + validates_inclusion_of :role, :in => ALL_ROLES + validates_uniqueness_of :role, :scope => :user_id +end diff --git a/app/views/browse/_common_details.html.erb b/app/views/browse/_common_details.html.erb index 179b22f60..0c486e0a7 100644 --- a/app/views/browse/_common_details.html.erb +++ b/app/views/browse/_common_details.html.erb @@ -20,4 +20,11 @@
<%= t'browse.not_found.sorry', :type=> browse_not_found_type[@type] , :id => params[:id] %>
diff --git a/app/views/browse/start.rjs b/app/views/browse/start.rjs index 707054526..220295663 100644 --- a/app/views/browse/start.rjs +++ b/app/views/browse/start.rjs @@ -262,7 +262,7 @@ page << <<%= t'message.outbox.you_have_sent_messages', :sent_count => @user.sent_messages.size %> +
<%= t'message.outbox.you_have_sent_messages', :count => @user.sent_messages.size %> <% if @user.sent_messages.size > 0 %>
-<%= t 'user.view.mapper since' %><%= l @this_user.creation_time %> <%= t 'user.view.ago', :time_in_words_ago => time_ago_in_words(@this_user.creation_time) %> -
+<%= t 'user.view.mapper since' %> <%= l @this_user.creation_time %> <%= t 'user.view.ago', :time_in_words_ago => time_ago_in_words(@this_user.creation_time) %>
+ +<% if @user and @user.administrator? %> +<%= t 'user.view.email address' %> <%= @this_user.email %>
+<%= t 'user.view.created from' %> <%= @this_user.creation_ip %>
<% end %> - +<%= t'user_block.partial.display_name' %> | + <% end %> + <% if show_creator_name %> +<%= t'user_block.partial.creator_name' %> | + <% end %> +<%= t'user_block.partial.reason' %> | +<%= t'user_block.partial.status' %> | +<%= t'user_block.partial.revoker_name' %> | ++ | + <% if show_revoke_link %> + | + <% end %> + |
---|
<%= t "user_block.blocks_by.empty", :name => h(@this_user.display_name) %>
+<% end %> diff --git a/app/views/user_blocks/blocks_on.html.erb b/app/views/user_blocks/blocks_on.html.erb new file mode 100644 index 000000000..2074af433 --- /dev/null +++ b/app/views/user_blocks/blocks_on.html.erb @@ -0,0 +1,8 @@ +<% @title = t('user_block.blocks_on.title', :name => h(@this_user.display_name)) %> +<%= t "user_block.blocks_on.empty", :name => h(@this_user.display_name) %>
+<% end %> diff --git a/app/views/user_blocks/edit.html.erb b/app/views/user_blocks/edit.html.erb new file mode 100644 index 000000000..387335d9d --- /dev/null +++ b/app/views/user_blocks/edit.html.erb @@ -0,0 +1,28 @@ +<% @title = t 'user_block.edit.title', :name => h(@user_block.user.display_name) %> +
+ <%= f.label :reason, t('user_block.edit.reason', :name => h(@user_block.user.display_name)) %>
+ <%= f.text_area :reason, :cols => 80, :rows => 5 %>
+
+ <%= label_tag 'user_block_period', t('user_block.edit.period') %>
+ <%= select_tag('user_block_period', options_for_select(UserBlock::PERIODS.collect { |h| [t('user_block.period', :count => h), h.to_s] }, params[:user_block_period])) %>
+
+ <%= f.check_box :needs_view %> + <%= f.label :needs_view, t('user_block.edit.needs_view') %> +
++ <%= f.submit t('user_block.edit.submit') %> +
+<% end %> + +<%= link_to t('user_block.edit.show'), @user_block %> | +<%= link_to t('user_block.edit.back'), user_blocks_path %> diff --git a/app/views/user_blocks/index.html.erb b/app/views/user_blocks/index.html.erb new file mode 100644 index 000000000..2baf91e5f --- /dev/null +++ b/app/views/user_blocks/index.html.erb @@ -0,0 +1,8 @@ +<% @title = t('user_block.index.title') %> +<%= t "user_block.index.empty" %>
+<% end %> diff --git a/app/views/user_blocks/new.html.erb b/app/views/user_blocks/new.html.erb new file mode 100644 index 000000000..3d3e6855c --- /dev/null +++ b/app/views/user_blocks/new.html.erb @@ -0,0 +1,28 @@ +<% @title = t 'user_block.new.title', :name => h(@this_user.display_name) %> +
+ <%= f.label :reason, t('user_block.new.reason', :name => @this_user.display_name) %>
+ <%= f.text_area :reason, :cols => 80, :rows => 5 %>
+
+ <%= label_tag 'user_block_period', t('user_block.new.period') %>
+ <%= select_tag('user_block_period', options_for_select(UserBlock::PERIODS.collect { |h| [t('user_block.period', :count => h), h.to_s] }, params[:user_block_period] )) %>
+
+ <%= f.check_box :needs_view %> + <%= f.label :needs_view, t('user_block.new.needs_view') %> +
++ <%= hidden_field_tag 'display_name', @this_user.display_name %> + <%= f.submit t('user_block.new.submit') %> +
+<% end %> + +<%= link_to t('user_block.new.back'), user_blocks_path %> diff --git a/app/views/user_blocks/not_found.html.erb b/app/views/user_blocks/not_found.html.erb new file mode 100644 index 000000000..3b5323d72 --- /dev/null +++ b/app/views/user_blocks/not_found.html.erb @@ -0,0 +1,3 @@ +<%= t'user_block.not_found.sorry', :id => params[:id] %>
+ +<%= link_to t('user_block.not_found.back'), user_blocks_path %> diff --git a/app/views/user_blocks/revoke.html.erb b/app/views/user_blocks/revoke.html.erb new file mode 100644 index 000000000..9f142c802 --- /dev/null +++ b/app/views/user_blocks/revoke.html.erb @@ -0,0 +1,32 @@ +<% @title = t('user_block.revoke.title', + :block_on => h(@user_block.user.display_name), + :block_by => h(@user_block.creator.display_name)) %> ++ <%= t('user_block.revoke.time_future', :time => distance_of_time_in_words_to_now(@user_block.ends_at)) %> +
+ +<% form_for :revoke, :url => { :action => "revoke" } do |f| %> + <%= f.error_messages %> ++ <%= check_box_tag 'confirm', 'yes' %> + <%= label_tag 'confirm', t('user_block.revoke.confirm') %> +
++ <%= submit_tag t('user_block.revoke.revoke') %> +
+<% end %> + +<% else %> ++ <%= t('user_block.revoke.past', :time => distance_of_time_in_words_to_now(@user_block.ends_at)) %> +
+<% end %> diff --git a/app/views/user_blocks/show.html.erb b/app/views/user_blocks/show.html.erb new file mode 100644 index 000000000..24a1d1e66 --- /dev/null +++ b/app/views/user_blocks/show.html.erb @@ -0,0 +1,34 @@ +<% @title = t('user_block.show.title', + :block_on => @user_block.user.display_name, + :block_by => @user_block.creator.display_name) %> ++ <%= t'user_block.show.revoker' %> + <%= link_to h(@user_block.revoker.display_name), :controller => 'user', :action => 'view', :display_name => @user_block.revoker.display_name %> +
+<% end %> + +<%= t'user_block.show.status' %>: <%= block_status(@user_block) %>
+ ++ <%= t'user_block.show.reason' %> + <%=h @user_block.reason %> +
+ + +<% if @user_block.ends_at > Time.now.getutc %> +<% if @user and @user.id == @user_block.creator_id %> +<%= link_to t('user_block.show.edit'), edit_user_block_path(@user_block) %> | +<% end %> +<% if @user and @user.moderator? %> +<%= link_to(t('user_block.show.revoke'),{:controller => 'user_blocks', :action => 'revoke', :id => @user_block.id}) %> | +<% end %> +<% end %> +<%= link_to t('user_block.show.back'), user_blocks_path %> diff --git a/app/views/user_roles/grant.html.erb b/app/views/user_roles/grant.html.erb new file mode 100644 index 000000000..cee4112b7 --- /dev/null +++ b/app/views/user_roles/grant.html.erb @@ -0,0 +1,7 @@ +<% form_tag request.request_uri do %> +<%= hidden_field_tag 'nonce', @nonce %> +<% @title = t('user_role.grant.heading') %> +<%= t('user_role.grant.are_you_sure', :name => params[:display_name], :role => params[:role]) %>
+<%= submit_tag t('user_role.grant.confirm') %>
+<% end %> diff --git a/app/views/user_roles/revoke.html.erb b/app/views/user_roles/revoke.html.erb new file mode 100644 index 000000000..e5aadeae0 --- /dev/null +++ b/app/views/user_roles/revoke.html.erb @@ -0,0 +1,7 @@ +<% form_tag request.request_uri do %> +<%= hidden_field_tag 'nonce', @nonce %> +<% @title = t('user_role.revoke.heading') %> +<%= t('user_role.revoke.are_you_sure', :name => params[:display_name], :role => params[:role]) %>
+<%= submit_tag t'user_role.revoke.confirm' %>
+<% end %> diff --git a/config/application.yml b/config/application.yml index 6241fb621..6a67a5123 100644 --- a/config/application.yml +++ b/config/application.yml @@ -13,6 +13,8 @@ standard_settings: &standard_settings geonames_zoom: 12 # Timeout for API calls in seconds api_timeout: 300 + # Periods (in hours) which are allowed for user blocks + user_block_periods: [0, 1, 3, 6, 12, 24, 48, 96] development: <<: *standard_settings diff --git a/config/environment.rb b/config/environment.rb index e35353c3c..12a8b5424 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -5,7 +5,7 @@ ENV['RAILS_ENV'] ||= 'production' # Specifies gem version of Rails to use when vendor/rails is not present -RAILS_GEM_VERSION = '2.3.2' unless defined? RAILS_GEM_VERSION +RAILS_GEM_VERSION = '2.3.4' unless defined? RAILS_GEM_VERSION # Set the server URL SERVER_URL = ENV['OSM_SERVER_URL'] || 'www.openstreetmap.org' diff --git a/config/languages.yml b/config/languages.yml index df0a8329a..df539c972 100644 --- a/config/languages.yml +++ b/config/languages.yml @@ -1,735 +1,582 @@ -aa: +aa: english: Afar native: Afaraf - -ab: +ab: english: Abkhazian native: Аҧсуа - -ae: +ae: english: Avestan native: avesta - -af: +af: english: Afrikaans native: Afrikaans - -ak: +ak: english: Akan native: Akan - -am: +am: english: Amharic native: አማርኛ - -an: +an: english: Aragonese native: Aragonés - -ar: +ar: english: Arabic native: العربية - -as: +as: english: Assamese native: অসমীয়া - -av: +av: english: Avaric - native: авар мацӀ; магӀарул мацӀ - -ay: + native: Авар +ay: english: Aymara - native: aymar aru - -az: + native: Aymar aru +az: english: Azerbaijani - native: azərbaycan dili - -ba: + native: Azərbaycan +ba: english: Bashkir - native: башҡорт теле - -be: + native: Башҡорт +be: english: Belarusian native: Беларуская - -bg: +bg: english: Bulgarian - native: български език - -bh: + native: Български +bh: english: Bihari native: भोजपुरी - -bi: +bi: english: Bislama native: Bislama - -bm: +bm: english: Bambara - native: bamanankan - -bn: + native: Bamanankan +bn: english: Bengali native: বাংলা - -bo: +bo: english: Tibetan native: བོད་ཡིག - -br: +br: english: Breton - native: brezhoneg - -bs: + native: Brezhoneg +bs: english: Bosnian - native: bosanski jezik - -ca: + native: Bosanski +ca: english: Catalan native: Català - -ce: +ce: english: Chechen - native: нохчийн мотт - -ch: + native: Нохчийн +ch: english: Chamorro native: Chamoru - -co: +co: english: Corsican - native: corsu; lingua corsa - -cr: + native: Corsu +cr: english: Cree native: ᓀᐦᐃᔭᐍᐏᐣ - -cs: +cs: english: Czech - native: česky; čeština - -cu: + native: Česky +cu: english: Church Slavic - native: ѩзыкъ словѣньскъ - -cv: + native: Словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ +cv: english: Chuvash - native: чӑваш чӗлхи - -cy: + native: Чӑвашла +cy: english: Welsh native: Cymraeg - -da: +da: english: Danish - native: dansk - -de: + native: Dansk +de: english: German native: Deutsch - -dv: +dv: english: Divehi - native: ދިވެހި - -dz: + native: ދިވެހިބަސް +dz: english: Dzongkha - native: རྫོང་ཁ - -ee: + native: ཇོང་ཁ +ee: english: Ewe - native: Ɛʋɛgbɛ - -el: + native: Eʋegbe +el: english: Greek native: Ελληνικά - -en: +en: english: English native: English - -eo: +eo: english: Esperanto native: Esperanto - -es: +es: english: Spanish - native: Español; castellano - -et: + native: Español +et: english: Estonian - native: eesti; eesti keel - -eu: + native: Eesti +eu: english: Basque - native: euskara; euskera - -fa: + native: Euskara +fa: english: Persian native: فارسی - -ff: +ff: english: Fulah native: Fulfulde - -fi: +fi: english: Finnish - native: suomi; suomen kieli - -fj: + native: Suomi +fj: english: Fijian - native: vosa Vakaviti - -fo: + native: Na Vosa Vakaviti +fo: english: Faroese native: Føroyskt - -fr: +fr: english: French - native: Français; langue française - -fy: + native: Français +fy: english: Western Frisian native: Frysk - -ga: +ga: english: Irish native: Gaeilge - -gd: +gd: english: Scottish Gaelic native: Gàidhlig - -gl: +gl: english: Galician native: Galego - -gn: - english: Guaraní +gn: + english: Guarani native: Avañe'ẽ - -gu: +gsw: + english: Swiss German + native: Alemannisch +gu: english: Gujarati native: ગુજરાતી - -gv: +gv: english: Manx - native: Gaelg; Gailck - -ha: + native: Gaelg +ha: english: Hausa native: هَوُسَ - -he: +he: english: Hebrew native: עברית - -hi: +hi: english: Hindi - native: हिन्दी; हिंदी - -ho: + native: हिन्दी +ho: english: Hiri Motu native: Hiri Motu - -hr: +hr: english: Croatian native: Hrvatski - -ht: +hsb: + english: Upper Sorbian + native: Hornjoserbsce +ht: english: Haitian native: Kreyòl ayisyen - -hu: +hu: english: Hungarian native: Magyar - -hy: +hy: english: Armenian native: Հայերեն - -hz: +hz: english: Herero native: Otjiherero - -ia: - english: Interlingua (International Auxiliary Language Association) +ia: + english: Interlingua native: Interlingua - -id: +id: english: Indonesian native: Bahasa Indonesia - -ie: +ie: english: Interlingue native: Interlingue - -ig: +ig: english: Igbo native: Igbo - -ii: +ii: english: Sichuan Yi native: ꆇꉙ - -ik: +ik: english: Inupiaq - native: Iñupiaq; Iñupiatun - -io: + native: Iñupiak +io: english: Ido native: Ido - -is: +is: english: Icelandic native: Íslenska - -it: +it: english: Italian native: Italiano - -iu: +iu: english: Inuktitut - native: ᐃᓄᒃᑎᑐᑦ - -ja: + native: ᐃᓄᒃᑎᑐᑦ/inuktitut +ja: english: Japanese - native: 日本語 (にほんご/にっぽんご) - -jv: + native: 日本語 +jv: english: Javanese - native: basa Jawa - -ka: + native: Basa Jawa +ka: english: Georgian native: ქართული - -kg: +kg: english: Kongo - native: KiKongo - -ki: + native: Kongo +ki: english: Kikuyu native: Gĩkũyũ - -kj: +kj: english: Kwanyama native: Kuanyama - -kk: +kk: english: Kazakh - native: Қазақ тілі - -kl: + native: Қазақша +kl: english: Kalaallisut - native: kalaallisut; kalaallit oqaasii - -km: + native: Kalaallisut +km: english: Khmer native: ភាសាខ្មែរ - -kn: +kn: english: Kannada native: ಕನ್ನಡ - -ko: +ko: english: Korean - native: 한국어 (韓國語); 조선말 (朝鮮語) - -kr: + native: 한국어 +kr: english: Kanuri native: Kanuri - -ks: +ks: english: Kashmiri - native: कश्मीरी; كشميري - -ku: + native: (كشميري) +ksh: + english: Ripoarisch + native: Ripoarisch +ku: english: Kurdish - native: Kurdî; كوردی - -kv: + native: Kurdî / كوردی +kv: english: Komi - native: коми кыв - -kw: + native: Коми +kw: english: Cornish - native: Kernewek - -ky: + native: Kernowek +ky: english: Kirghiz - native: кыргыз тили - -la: + native: Кыргызча +la: english: Latin - native: latine; lingua latina - -lb: + native: Latina +lb: english: Luxembourgish native: Lëtzebuergesch - -lg: +lg: english: Ganda native: Luganda - -li: +li: english: Limburgish native: Limburgs - -ln: +ln: english: Lingala native: Lingála - -lo: +lo: english: Lao - native: ພາສາລາວ - -lt: + native: ລາວ +lt: english: Lithuanian - native: lietuvių kalba - -lu: + native: Lietuvių +lu: english: Luba-Katanga - native: - -lv: + native: ~ +lv: english: Latvian - native: latviešu valoda - -mg: + native: Latviešu +mg: english: Malagasy - native: Malagasy fiteny - -mh: + native: Malagasy +mh: english: Marshallese native: Kajin M̧ajeļ - -mi: - english: Māori - native: te reo Māori - -mk: +mi: + english: Maori + native: Māori +mk: english: Macedonian - native: македонски јазик - -ml: + native: Македонски +ml: english: Malayalam native: മലയാളം - -mn: +mn: english: Mongolian native: Монгол - -mr: +mo: + english: Moldavian + native: Молдовеняскэ +mr: english: Marathi native: मराठी - -ms: +ms: english: Malay - native: bahasa Melayu; بهاس ملايو - -mt: + native: Bahasa Melayu +mt: english: Maltese native: Malti - -my: +my: english: Burmese - native: ဗမာစာ - -na: + native: မြန်မာဘာသာ +na: english: Nauru - native: Ekakairũ Naoero - -nb: + native: Dorerin Naoero +nb: english: Norwegian Bokmål - native: Norsk bokmål - -nd: + native: Norsk (bokmål) +nd: english: North Ndebele native: isiNdebele - -ne: +nds: + english: Low German + native: Plattdüütsch +ne: english: Nepali native: नेपाली - -ng: +ng: english: Ndonga native: Owambo - -nl: +nl: english: Dutch native: Nederlands - -nn: +nn: english: Norwegian Nynorsk - native: Norsk nynorsk - -no: - english: Norwegian - native: Norsk - -nr: + native: Norsk (nynorsk) +"no": + english: Norwegian (bokmål) + native: Norsk (bokmål) +nr: english: South Ndebele native: isiNdebele - -nv: +nv: english: Navajo - native: Diné bizaad; Dinékʼehǰí - -ny: - english: Chichewa - native: chiCheŵa; chinyanja - -oc: + native: Diné bizaad +ny: + english: Nyanja + native: Chi-Chewa +oc: english: Occitan native: Occitan - -oj: +oj: english: Ojibwa native: ᐊᓂᔑᓈᐯᒧᐎᓐ - -om: +om: english: Oromo - native: Afaan Oromoo - -or: + native: Oromoo +or: english: Oriya native: ଓଡ଼ିଆ - -os: - english: Ossetian - native: Ирон æвзаг - -pa: - english: Panjabi - native: ਪੰਜਾਬੀ; پنجابی - -pi: - english: Pāli - native: पाऴि - -pl: +os: + english: Ossetic + native: Иронау +pa: + english: Punjabi + native: ਪੰਜਾਬੀ +pi: + english: Pali + native: पािऴ +pl: english: Polish - native: polski - -ps: + native: Polski +ps: english: Pashto native: پښتو - -pt: +pt: english: Portuguese native: Português - -qu: +pt-BR: + english: Brazilian Portuguese + native: Português do Brasil +qu: english: Quechua - native: Runa Simi; Kichwa - -rm: - english: Raeto-Romance - native: rumantsch grischun - -rn: + native: Runa Simi +rm: + english: Rhaeto-Romance + native: Rumantsch +rn: english: Kirundi native: kiRundi - -ro: +ro: english: Romanian - native: română - -ru: + native: Română +ru: english: Russian - native: русский язык - -rw: + native: Русский +rw: english: Kinyarwanda native: Ikinyarwanda - -sa: +sa: english: Sanskrit - native: संस्कृतम् - -sc: + native: संस्कृत +sc: english: Sardinian - native: sardu - -sd: + native: Sardu +sd: english: Sindhi - native: सिन्धी; سنڌي، سندھی - -se: + native: سنڌي +se: english: Northern Sami - native: Davvisámegiella - -sg: + native: Sámegiella +sg: english: Sango - native: yângâ tî sängö - -si: + native: Sängö +sh: + english: Serbo-Croatian + native: Srpskohrvatski / Српскохрватски +si: english: Sinhala native: සිංහල - -sk: +sk: english: Slovak - native: slovenčina - -sl: + native: Slovenčina +sl: english: Slovenian - native: slovenščina - -sm: + native: Slovenščina +sm: english: Samoan - native: gagana fa'a Samoa - -sn: + native: Gagana Samoa +sn: english: Shona native: chiShona - -so: +so: english: Somali - native: Soomaaliga; af Soomaali - -sq: + native: Soomaaliga +sq: english: Albanian native: Shqip - -sr: +sr: english: Serbian - native: српски језик - -ss: + native: Српски / Srpski +sr-EC: + english: Serbian Cyrillic ekavian + native: Српски (ћирилица) +ss: english: Swati native: SiSwati - -st: +st: english: Southern Sotho native: Sesotho - -su: +su: english: Sundanese native: Basa Sunda - -sv: +sv: english: Swedish - native: svenska - -sw: + native: Svenska +sw: english: Swahili native: Kiswahili - -ta: +ta: english: Tamil native: தமிழ் - -te: +te: english: Telugu native: తెలుగు - -tg: +tg: english: Tajik - native: тоҷикӣ; toğikī; تاجیکی - -th: + native: Тоҷикӣ +th: english: Thai native: ไทย - -ti: +ti: english: Tigrinya native: ትግርኛ - -tk: +tk: english: Turkmen - native: Türkmen; Түркмен - -tl: + native: Türkmençe +tl: english: Tagalog native: Tagalog - -tn: +tn: english: Tswana native: Setswana - -to: +to: english: Tonga - native: faka Tonga - -tr: + native: lea faka-Tonga +tr: english: Turkish native: Türkçe - -ts: +ts: english: Tsonga native: Xitsonga - -tt: +tt: english: Tatar - native: татарча; tatarça; تاتارچا - -tw: + native: Татарча/Tatarça +tw: english: Twi native: Twi - -ty: +ty: english: Tahitian native: Reo Mā`ohi - -ug: +ug: english: Uighur - native: Uyƣurqə; ئۇيغۇرچە - -uk: + native: Uyghurche / ئۇيغۇرچە +uk: english: Ukrainian native: Українська - -ur: +ur: english: Urdu native: اردو - -uz: +uz: english: Uzbek - native: O'zbek; Ўзбек; أۇزبېك - -ve: + native: O'zbek +ve: english: Venda - native: Tshivenḓa - -vi: + native: Tshivenda +vi: english: Vietnamese native: Tiếng Việt - -vo: +vo: english: Volapük native: Volapük - -wa: +wa: english: Walloon native: Walon - -wo: +wo: english: Wolof - native: Wollof - -xh: + native: Wolof +xh: english: Xhosa native: isiXhosa - -yi: +yi: english: Yiddish native: ייִדיש - -yo: +yo: english: Yoruba native: Yorùbá - -za: +za: english: Zhuang - native: Saɯ cueŋƅ; Saw cuengh - -zh: + native: Vahcuengh +zh: english: Chinese - native: 中文 (Zhōngwén), 汉语, 漢語 - -zu: + native: 中文 +zh-CN: + english: Chinese (China) + native: 中文(中国大陆) +zh-TW: + english: Chinese (Taiwan) + native: 中文(台灣) +zu: english: Zulu native: isiZulu diff --git a/config/locales/af.yml b/config/locales/af.yml new file mode 100644 index 000000000..5e8bae16f --- /dev/null +++ b/config/locales/af.yml @@ -0,0 +1,810 @@ +# Messages for Afrikaans (Afrikaans) +# Exported from translatewiki.net +# Export driver: syck +# Author: Firefishy +# Author: Naudefj +# Author: Nroets +af: + activerecord: + attributes: + diary_comment: + body: Teks + diary_entry: + language: Taal + latitude: Breedtegraad + longitude: Lengtegraad + title: Titel + user: Gebruiker + friend: + friend: Vriend + user: Gebruiker + message: + body: Teks + recipient: Ontvanger + sender: Afsender + title: Titel + trace: + description: Beskrywing + latitude: Breedtegraad + longitude: Lengtegraad + name: Naam + public: Openbaar + size: Grootte + user: Gebruiker + visible: Sigbaar + user: + active: Aktief + description: Beskrywing + display_name: Vertoon Naam + email: E-pos + languages: Tale + pass_crypt: Wagwoord + models: + acl: Toegangsbeheer + changeset: Stel wysigings + country: Land + diary_comment: Dagboekopmerking + diary_entry: Dagboekinskrywing + friend: Vriend + language: Taal + message: Boodskap + node: Node + notifier: Melding + old_node: Ou Node + session: Sessie + trace: Spoor + user: Gebruiker + user_preference: Gebruikersvoorkeure + way: Weg + browse: + changeset_details: + belongs_to: "Behoort aan:" + bounding_box: "Seleksieboks:" + box: boks + closed_at: "Gesluit op:" + created_at: "Geskep op:" + has_nodes: + one: "Het die volgende node:" + other: "Het die volgende {{count}} nodes:" + show_area_box: Wys gebied + changeset_navigation: + all: + next_tooltip: Volgende stel wysigings + prev_tooltip: Vorige stel wysigings + user: + name_tooltip: Wys wysigings deur {{user}} + next_tooltip: Volgende wysiging deur {{user}} + prev_tooltip: Vorige wysiging deur {{user}} + common_details: + changeset_comment: "Opmerking:" + edited_at: "Gewysig op:" + edited_by: "Opgedateer deur:" + version: "Weergawe:" + containing_relation: + entry: Relasie {{relation_name}} + entry_role: Relasie {{relation_name}} (as {{relation_role}}) + map: + deleted: Uitgevee + larger: + area: Besigtig area op groter kaart + node: Besigtig node op groter kaart + relation: Besigtig relasie op groter kaart + way: Besigtig weg op groter kaart + loading: Besig om af te laai... + node: + download: "{{download_xml_link}}, {{view_history_link}} of {{edit_link}}" + download_xml: Laai XML af + edit: wysig + node: Node + node_title: "Node: {{node_name}}" + view_history: wys geskiedenis + node_details: + coordinates: "Koördinate:" + part_of: "Deel af:" + node_history: + download: "{{download_xml_link}} of {{view_details_link}}" + download_xml: Laai XML af + node_history: Nodegeskiedenis + node_history_title: "Nodegeskiedenis: {{node_name}}" + view_details: besigtig besonderhede + not_found: + sorry: Jammer, {{type}} {{id}} kan nie gevind word nie. + type: + changeset: Veranderingstel + node: node + relation: relasie + way: weg + paging_nav: + of: van + showing_page: Bladsy + relation: + download: "{{download_xml_link}} of {{view_history_link}}" + download_xml: Laai XML af + view_history: wys geskiedenis + relation_details: + members: "Lede:" + part_of: "Deel van:" + relation_history: + download: "{{download_xml_link}} of {{view_details_link}}" + download_xml: Laai XML af + relation_history: Relasiegeskiedenis + relation_history_title: "Relasie Geskiedenis: {{relation_name}}" + view_details: besigtig besonderhede + relation_member: + entry_role: "{{type}} {{name}} as {{role}}" + type: + node: Node + relation: Relasie + way: Weg + start: + manually_select: Kies 'n ander gebied handmatig + view_data: Wys data vir die huidige kaart + start_rjs: + data_frame_title: Gegewens + data_layer_name: Data + details: Details + drag_a_box: Trek 'n boks op die kaart om 'n gebied te kies + edited_by_user_at_timestamp: Verander deur [[user]] om [[timestamp]] + history_for_feature: Geskiedenis van [[feature]] + load_data: Laai data + loaded_an_area_with_num_features: Hierdie area het [[num_features]] items. Party webblaaiers mag stadig raak wanneer hulle om soveel data te vertoon. Om nietemin aan te gaan, kies die skakel hier onder. + loading: Laai... + manually_select: Kies 'n ander gebied handmatig + object_list: + api: Verkry die data van hierdie gebied + back: Vertoon objeklys + details: Besonderhede + heading: Objeklys + history: + type: + node: Node [[id]] + way: Weg [[id]] + selected: + type: + node: Node [[id]] + way: Weg [[id]] + type: + node: Node + way: Weg + private_user: private gebruiker + show_history: Wys Geskiedenis + unable_to_load_size: Aflaai van data onmoontlik. Area van grootte [[bbox_size]] is te groot. Moet kleiner as {{max_bbox_size}} wees + wait: Wag asseblief... + zoom_or_select: Zoom in of kies 'n gebied op die kaart om te besigtig + tag_details: + tags: "Etikette:" + way: + download: "{{download_xml_link}} of {{view_history_link}}" + download_xml: Laai XML af + edit: wysig + view_history: besigtig geskiedenis + way: Weg + way_title: "Weg: {{way_name}}" + way_details: + also_part_of: + one: ook deel van weg {{related_ways}} + other: ook deel van weë {{related_ways}} + nodes: "Nodes:" + part_of: "Deel van:" + way_history: + download: "{{download_xml_link}} of {{view_details_link}}" + download_xml: Laai XML af + view_details: besigtig besonderhede + way_history: Weggeskiedenis + way_history_title: "Weggeskiedenis: {{way_name}}" + changeset: + changeset: + anonymous: Anoniem + big_area: (groot) + no_comment: (geen) + no_edits: (geen wysigings) + still_editing: (steeds besig met wysiging) + changeset_paging_nav: + of: van + showing_page: Wys bladsy + changesets: + area: Gebied + comment: Opmerking + id: ID + saved_at: Gestoor op + user: Gebruiker + list: + description: Onlangse wysigings + diary_entry: + diary_comment: + comment_from: Kommentaar van ((link_user)) op ((comment_created_at)) + diary_entry: + comment_count: + one: 1 reaksie + other: "{{count}} reaksies" + comment_link: Lewer kommentaar op hierdie bydrae + edit_link: Wysig hierdie inskrywing + posted_by: Gepos deur {{link_user}} op {{created}} in die {{language_link}} + reply_link: Antwoord op hierdie bydrae + edit: + body: "Teks:" + language: "Taal:" + latitude: "Breedtegraad:" + location: "Ligging:" + longitude: "Lengtegraad:" + marker_text: Ligging van dagboekinskrywing + save_button: Stoor + subject: "Onderwerp:" + title: Wysig dagboekinskrywing + use_map_link: gebruik kaart + feed: + all: + description: Onlangse dagboekinskrywings van OpenStreetMap-gebruikers + title: OpenStreetMap dagboekinskrywings + language: + description: Onlangse dagboekinskrywings van OpenStreetMap-gebruikers in {{language_name}} + title: OpenStreetMap dagboekinskrywings in {{language_name}} + user: + description: Onlangse OpenStreetMap dagboekinskrywings van {{user}} + title: OpenStreetMap dagboekinskrywings van {{user}} + list: + in_language_title: Dagboekinkrywings in {{language}} + new: Nuwe dagboekinskrywing + new_title: Plaas nuwe artikel in u dagboek + newer_entries: Nuwer inskrywings + no_entries: Geen dagboekinskrywings nie + older_entries: Ouer inskrywings + recent_entries: "Onlangse dagboekinskrywings:" + title: Gebruikersdagboeke + user_title: Dagboek van {{user}} + new: + title: Nuwe dagboekinskrywing + no_such_entry: + body: Jammer, daar is geen dagboekinskrywing of kommentaar met die id {{id}} nie. Kontroleer u spelling, of miskien is die skakel waarop u gekliek het verkeerd. + heading: Die inskrywing met id {{id}} bestaan nie + title: Die opgevraagde dagboekinskrywing bestaan nie + no_such_user: + body: Jammer, daar is geen gebruiker met die naam {{user}} nie. Kontroleer u spelling, of miskien is die skakel waarop u gekliek het verkeerd. + heading: Die gebruiker {{user}} bestaan nie + title: Geen sodanige gebruiker nie + view: + leave_a_comment: Los opmerking agter + login: Teken in + login_to_leave_a_comment: U moet eers {{login_link}} alvorens u kommentaar kan lewer + save_button: Stoor + title: Gebruikersdagboeke | {{user}} + user_title: Dagboek van {{user}} + export: + start: + add_marker: Plaas 'n merker op die kaart + area_to_export: Area om te eksporteer + embeddable_html: HTML-kode + export_button: Eksporteer + export_details: OpenStreetMap se data is gelisensieer onder die Creative Commons Erkenning-Insgelyks Deel 2.0 lisensie. + format: Formaat + format_to_export: Lêerformaat + image_size: Prentgrootte + latitude: "Breedte:" + licence: Lisensie + longitude: "Lengte:" + manually_select: Kies 'n ander gebied handmatig + max: max + options: Voorkeure + osm_xml_data: OpenStreetMap XML-data + output: Afvoer + scale: Skaal + zoom: Zoom + start_rjs: + add_marker: Plaas 'n merker op die kaart + change_marker: Verander posisie van merker + click_add_marker: Kliek op die kaart om 'n merker te plaas + drag_a_box: Trek 'n boks op die kaart om 'n gebied te kies + export: Eksporteer + manually_select: Kies 'n ander gebied handmatig + view_larger_map: Wys groter kaart + geocoder: + description: + title: + geonames: Ligging vanaf GeoNames + osm_namefinder: "{{types}} vanaf OpenStreetMap Namefinder" + types: + cities: Stede + places: Plekke + towns: Dorpe + description_osm_namefinder: + prefix: "{{distance}} {{direction}} van {{type}}" + direction: + east: oos + north: noord + north_east: noordoos + north_west: noordwes + south: suid + south_east: suid-oos + south_west: suidwes + west: wes + distance: + one: ongeveer 1km + other: ongeveer {{count}}km + zero: minder as 1km + results: + no_results: Geen resultate gevind nie + search: + title: + ca_postcode: Resultate vanaf Geocoder.CA + geonames: Resultate vanaf GeoNames + latlon: Resultate vanaf intern + osm_namefinder: Resultate vanaf OpenStreetMap Namefinder + uk_postcode: Resultate vanaf NPEMap / FreeThe Postcode + us_postcode: Resultate vanaf Geocoder.us + search_osm_namefinder: + suffix_parent: "{{suffix}} ({{parentdistance}} {{parentdirection}} van {{parentname}})" + suffix_place: ", {{distance}} {{direction}} van {{placename}}" + javascripts: + map: + base: + cycle_map: Fietskaart + noname: GeenNaam + site: + edit_zoom_alert: u moet in zoom om die kaart te wysig + history_zoom_alert: U moet in zoom om die kaart se wysigingsgeskiedenis te sien + layouts: + donate: Ondersteun OpenStreetMap deur aan die Hardeware Opgradeer-fonds te {{link}}. + donate_link_text: skenk + edit: Wysig + edit_tooltip: Wysig kaarte + export: Eksporteer + export_tooltip: Eksporteer kaartdata + gps_traces: GPS-spore + gps_traces_tooltip: Beheer spore + help_wiki: Help & Wiki + help_wiki_tooltip: Help en wiki vir die projek + history: Geskiedenis + home: tuis + home_tooltip: Gaan na tuisligging + inbox: inboks ({{count}}) + inbox_tooltip: + one: U inboks bevat 1 ongeleesde boodskap + other: U inboks bevat {{count}} ongeleesde boodskappe + zero: U inboks bevat geen ongelees boodskappe nie + intro_1: OpenStreetMap is 'n vry bewerkbare kaart van die hele wêreld. Dit word deur mense soos u geskep. + intro_2: Met OpenStreetMap kan u geografiese data van die hele aarde sien, wysig en gebruik. + intro_3: OpenStreetMap se webwerf word deur {{ucl}} en {{bytemark}} ondersteun. + log_in: Teken in + log_in_tooltip: Teken aan met 'n bestaande rekening + logo: + alt_text: OpenStreetMap-logo + logout: teken uit + logout_tooltip: Teken uit + make_a_donation: + text: Maak 'n donasie + title: Ondersteun OpenStreetMap met'n geldelike donasie + news_blog: Nuusjoernale + news_blog_tooltip: Nuusjoernaal oor OpenStreetMap, vrye geografiese data, ensovoorts. + osm_read_only: Die OpenStreetMap-databasis kan op die oomblik slegs gelees word aangesien noodsaaklik onderhoud tans uitgevoer word. + shop: Winkel + shop_tooltip: Winkel met OpenStreetMap-produkte + sign_up: registreer + sign_up_tooltip: Skep 'n rekening vir wysigings + sotm: Kom na die 2009 OpenStreetMap-konferensie, die "State of the Map", op 10-12 Julie in Amsterdam! + tag_line: Die vrye wiki-wêreldkaart + user_diaries: Gebruikersdagboeke + user_diaries_tooltip: Wys gebruikersdagboeke + view: Wys + view_tooltip: Wys kaarte + welcome_user: Welkom, {{user_link}} + welcome_user_link_tooltip: U gebruikersblad + map: + coordinates: "Koördinate:" + edit: Wysig + view: Wys + message: + delete: + deleted: Boodskap is verwyder + inbox: + date: Datum + from: Vanaf + my_inbox: My inboks + no_messages_yet: U het nog geen boodskappe nie. Hoekom kontak u nie sommige van die {{people_mapping_nearby_link}} nie? + outbox: outboks + people_mapping_nearby: nabygeleë karteerders + subject: Onderwerp + title: Inboks + you_have: U het {{new_count}} nuwe boodskappe en {{old_count}} ou boodskappe + mark: + as_read: Boodskap gemerk as gelees + as_unread: Boodskap gemerk as ongelees + message_summary: + delete_button: Verwyder + read_button: Merk as gelees + reply_button: Antwoord + unread_button: Merk as ongelees + new: + back_to_inbox: Terug na inboks + body: Teks + message_sent: Boodskap is gestuur + send_button: Stuur + send_message_to: Stuur 'n nuwe boodskap aan {{name}} + subject: Onderwerp + title: Stuur boodskap + no_such_user: + body: Jammer, daar is geen gebruiker of boodskap met die naam of id nie + heading: Geen sodanige gebruiker of boodskap nie + title: Geen sodanige gebruiker of boodskap nie + outbox: + date: Datum + inbox: inboks + my_inbox: My {{inbox_link}} + no_sent_messages: U het nog geen boodskappe gestuur nie. Hoekom kontak u nie sommige van die {{people_mapping_nearby_link}} nie? + outbox: uitboks + people_mapping_nearby: nabygeleë karteerders + subject: Onderwerp + title: Gestuur + to: Aan + you_have_sent_messages: U het {{count}} gestuurde boodskappe + read: + back_to_inbox: Terug na inboks + back_to_outbox: Terug na uitboks + date: Datum + from: Van + reply_button: Antwoord + subject: Onderwerp + title: Lees boodskap + to: Aan + unread_button: Merk as ongelees + sent_message_summary: + delete_button: Verwyder + notifier: + diary_comment_notification: + hi: Hallo {{to_user}}, + subject: "[OpenStreetMap] {{user}} het kommentaar op u dagboekinskrywing gelewer" + email_confirm: + subject: "[OpenStreetMap] Bevestig u e-posadres" + email_confirm_html: + click_the_link: As dit u is, kliek op die onderstaande skakel om die verandering te bevestig. + greeting: Hallo, + hopefully_you: Iemand (hopelik u) wil graag sy e-posadres op {{server_url}} verander na {{new_address}}. + email_confirm_plain: + greeting: Hallo, + hopefully_you_2: "{{server_url}} na {{new_address}}." + friend_notification: + subject: "[OpenStreetMap] {{user}} het u as 'n vriend bygevoeg" + gpx_notification: + failure: + more_info_2: "hulle kan gevind word by:" + greeting: Hallo, + with_description: met die beskrywing + your_gpx_file: Dit lyk soos jou GPX-lêer + lost_password: + subject: "[OpenStreetMap] Versoek nuwe wagwoord" + lost_password_html: + greeting: Hallo, + lost_password_plain: + greeting: Hallo, + message_notification: + banner1: "* Moet asseblief nie op hierdie e-pos reageer nie. *" + footer1: U kan ook die boodskap by {{readurl}} lees + footer2: en u kan antwoord by {{replyurl}} + hi: Hallo {{to_user}}, + subject: "[OpenStreetMap] {{user}} het 'n nuwe boodskap aan u gestuur" + signup_confirm: + subject: "[OpenStreetMap] Bevestig u e-posadres" + signup_confirm_html: + get_reading: Lees meer oor OpenStreetMap op die wiki of die opengeodata-blog waar u ook na podgooie kan luister! + greeting: Hallo daar! + hopefully_you: Iemand (hopelik u) wil graag 'n rekening skep op + introductory_video: U kan na 'n {{introductory_video_link}} kyk. + more_videos: Daar is {{more_videos_link}}. + more_videos_here: meer video's hier + video_to_openstreetmap: inleidende video oor OpenStreetMap + signup_confirm_plain: + click_the_link_1: As dit u is, welkom! Kliek asseblief op die onderstaande skakel om u rekening te bevestig + greeting: Hallo daar! + more_videos: "Daar is meer videos hier:" + user_wiki_1: Ons beveel aan dat u 'n gebruikersblad op die wiki skep met onder andere + user_wiki_2: kategorie-etikette wat aandui waar u woon, soos [[Category:Users_in_Pretoria]]. + wiki_signup: "U kan ook op die OpenStreetMap-wiki registreer by:" + oauth: + oauthorize: + allow_read_prefs: u gebruikersvoorkeure te lees. + allow_write_api: die kaart te wysig. + allow_write_gpx: Laai GPS-spore op. + allow_write_prefs: verander jou gebruikersvoorkeure. + oauth_clients: + edit: + submit: Wysig + title: Wysig u applikasie + form: + allow_read_prefs: lees hulle gebruikersvoorkeure. + allow_write_diary: skep dagboekinskrywings, lewer kommentaar en maak vriende. + allow_write_prefs: verander hulle gebruikersvoorkeure. + name: Naam + required: Verplig + support_url: Ondersteunings-URL + url: Applikasie-URL + index: + application: Applikasienaam + issued_at: Uitgereik op + register_new: Registreer u applikasie + revoke: Herroep! + new: + submit: Registreer + title: Registreer 'n nuwe applikasie + not_found: + sorry: Jammer, die {{type}} kon nie gevind word nie. + show: + allow_read_prefs: lees hulle gebruikersvoorkeure. + allow_write_api: wysig die kaart. + allow_write_diary: skep dagboekinskrywings, lewer kommentaar en maak vriende. + allow_write_gpx: laai GPS-spore op. + authorize_url: "URL vir magtiging:" + edit: Wysig details + site: + edit: + anon_edits_link_text: Lees waarom dit die geval is. + user_page_link: gebruikersbladsy + index: + license: + license_name: Creative Commons Naamsvermelding 2.0 + notice: Gelisensieer onder die {{license_name}} lisensie deur die {{project_name}} en sy bydraers. + project_name: OpenStreetMap-projek + permalink: Permanente skakel + shortlink: Kort skakel + key: + map_key: Sleutel + table: + entry: + admin: Administratiewe grens + apron: + - Lughaweplatform + - terminaal + bridge: Brug + building: Belangrike gebou + cable: + - Kabelkar + cemetery: Begraafplaas + centre: Sport-sentrum + commercial: Kommersiële gebied + common: + - weide + - weide + construction: Paaie onder konstruksie + cycleway: Fietspad + destination: Bestemmingsverkeer + farm: Plaas + footway: Voetpad + forest: Bos + golf: Gholfbaan + heathland: Heide + industrial: Industriële gebied + lake: + - Meer + - reservoir + military: Militêre gebied + motorway: Snelweg + park: Park + permissive: Beperkte toegang + pitch: Sportveld + primary: Primêre pad + private: Privaat toegang + rail: Spoorweg + reserve: Natuurreservaat + resident: Woongebied + retail: Winkelgebied + runway: + - Lughawe aanloopbaan + - taxibaan + school: + - Skool + - universiteit + secondary: Sekondêre pad + station: Spoorwegstasie + summit: + - Piek + - piek + tourist: Toerisme-trekpleister + track: Spoor + tram: + - Ligte spoor + trunk: Trokpad + tunnel: Tonnel + unclassified: Ongeklassifiseerde pad + unsurfaced: Grondpad + wood: Bos + heading: Sleutel vir z{{zoom_level}} + search: + search: Soek + search_help: "voorbeelde: 'Alkmaar', 'Regent Street, Cambridge', 'CB2 5AQ', or 'post offices near Lünen' meer voorbeelde..." + submit_text: OK + where_am_i: Waar is ek? + sidebar: + close: Sluit + search_results: Soekresultate + trace: + create: + upload_trace: Laai GPS-spore op + delete: + scheduled_for_deletion: Spoor is geskeduleer vir verwydering + edit: + description: "Beskrywing:" + download: aflaai + edit: wysig + filename: "Lêernaam:" + heading: Wysig spoor {{name}} + map: kaart + owner: "Eienaar:" + points: "Punte:" + save_button: Stoor wysigings + start_coord: "Beginkoördinaat:" + tags: "Etikette:" + tags_help: met kommas geskei + title: Wysig spoor {{name}} + uploaded_at: "Opgelaai op:" + visibility: "Sigbaarheid:" + visibility_help: wat beteken dit? + list: + public_traces: Openbare GPS-spore + public_traces_from: Openbare GPS-spore van {{user}} + tagged_with: geëtiketteer met {{tags}} + your_traces: U GPS-spore + make_public: + made_public: Spoor is openbaar gemaak + no_such_user: + heading: Die gebruiker {{user}} bestaan nie + title: Die gebruiker bestaan nie + trace: + ago: "{{time_in_words_ago}} gelede" + by: deur + count_points: "{{count}} punte" + edit: wysig + edit_map: Kaart bewysig + in: in + map: kaart + more: meer + pending: BESIG + private: PRIVAAT + public: OPENBAAR + trace_details: Wys spoor besonderhede + view_map: Wys kaart + trace_form: + description: Beskrywing + help: Hulp + tags: Etikette + tags_help: met kommas geskei + upload_button: Laai op + upload_gpx: Laai GPX-lêer op + visibility: Sigbaarheid + visibility_help: wat beteken dit? + trace_header: + see_all_traces: Wys alle spore + see_just_your_traces: Sien slegs u spore, of laai 'n spoor op + see_your_traces: Sien al u spore + trace_optionals: + tags: Etikette + trace_paging_nav: + of: van + showing: Wys bladsy + view: + delete_track: Verwyder hierdie spoor + description: "Beskrywing:" + download: laai af + edit: wysig + edit_track: Wysig hierdie spoor + filename: "Lêernaam:" + heading: Besigtig spoor {{name}} + map: kaart + none: Geen + owner: "Eienaar:" + pending: BESIG + points: "Punte:" + start_coordinates: "Beginkoördinaat:" + tags: "Etikette:" + title: Besigting spoor {{name}} + trace_not_found: Spoor nie gevind nie! + uploaded: "Opgelaai op:" + visibility: "Sigbaarheid:" + user: + account: + email never displayed publicly: (word nie openbaar gemaak nie) + flash update success: U gebruikersinligting is verander. + home location: "Tuisligging:" + latitude: "Breedtegraad:" + longitude: "Lengtegraad:" + make edits public button: Maak al my wysigings openbaar + my settings: My voorkeure + no home location: U het nog nie u huis se ligging ingevoer nie. + preferred languages: "Voorkeur tale:" + profile description: "Profielbeskrywing:" + public editing: + disabled link text: hoekom kan ek niks wysig nie? + enabled: Geaktiveer. U is nie anoniem nie en kan inligting wysig. + enabled link text: wat is dit? + heading: "Openbaar wysigings:" + return to profile: Terug na profiel + save changes button: Stoor wysigings + title: Wysig rekening + update home location on click: Opdateer tuisligging wanneer ek op die kaart kliek? + confirm: + button: Bevestig + heading: Bevestig 'n gebruiker se rekening + press confirm button: Kliek op "Bevestig" hieronder om u rekening aktiveer. + confirm_email: + button: Bevestig + success: U e-posadres is bevestig, dankie dat u geregistreer het! + friend_map: + nearby mapper: "Nabygeleë karteerder: [[nearby_user]]" + your location: U ligging + login: + auth failure: Jammer, kon nie met hierdie inligting aanteken nie. + create_account: registreer + email or username: "E-posadres of gebruikersnaam:" + heading: Teken in + login_button: Teken in + lost password link: Wagwoord vergeet? + password: "Wagwoord:" + please login: Teken in of {{create_user_link}}. + title: Teken in + lost_password: + email address: "E-posadres:" + heading: Wagwoord vergeet? + new password button: Herstel wagwoord + notice email cannot find: Kon nie die e-posadres vind nie, jammer. + title: Wagwoord vergeet + make_friend: + already_a_friend: U is reeds met {{name}} bevriend. + failed: Jammer, kon nie {{name}} as 'n vriend byvoeg nie. + success: "{{name}} is nou u vriend." + new: + confirm email address: "Bevestig E-posadres:" + confirm password: "Bevestig wagwoord:" + display name: "Vertoon naam:" + email address: "E-posadres:" + fill_form: Vul die vorm in en ons stuur so spoedig moontlik aan u 'n e-pos om u rekening te aktiveer. + flash create success message: U rekening is suksesvol geskep. Kontroleer u e-pos vir 'n bevestigingsboodskap.The operation you requested on the OpenStreetMap server is only available to administrators (HTTP 403)
+Feel free to contact the OpenStreetMap community if you have found a broken link / bug. Make a note of the exact URL of your request.
+